From 6dbe3af945a63f025561abb83275cee9ff06c57b Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Thu, 7 Dec 2006 00:25:32 +0100 Subject: [PATCH 1/1] Imported from util-linux-2.2 tarball. --- ANNOUNCE | 67 + COPYING.GPL | 339 ++++ COPYING.UCB | 32 + LSM | 23 + MCONFIG | 79 + Makefile | 54 + Notes.pre1995 | 386 ++++ README | 566 ++++++ bsd/Makefile | 20 + bsd/err.c | 189 ++ bsd/err.h | 70 + bsd/getopt.3 | 210 +++ bsd/getopt.c | 120 ++ bsd/pathnames.h | 90 + disk-utils/Makefile | 57 + disk-utils/README.bootutils-0.1 | 104 ++ disk-utils/README.cfdisk | 45 + disk-utils/README.fdisk | 577 ++++++ disk-utils/cfdisk.8 | 407 +++++ disk-utils/cfdisk.c | 2330 +++++++++++++++++++++++++ disk-utils/fdformat.8 | 59 + disk-utils/fdformat.c | 109 ++ disk-utils/fdisk.8 | 166 ++ disk-utils/fdisk.c | 1339 ++++++++++++++ disk-utils/fdprm | 26 + disk-utils/frag.8 | 47 + disk-utils/frag.c | 311 ++++ disk-utils/fsck.minix.8 | 125 ++ disk-utils/fsck.minix.c | 862 +++++++++ disk-utils/llseek.c | 84 + disk-utils/mkfs.8 | 133 ++ disk-utils/mkfs.c | 297 ++++ disk-utils/mkfs.minix.8 | 88 + disk-utils/mkfs.minix.c | 533 ++++++ disk-utils/mkswap.8 | 86 + disk-utils/mkswap.c | 212 +++ disk-utils/setfdprm.8 | 66 + disk-utils/setfdprm.c | 149 ++ example.files/fstab | 22 + example.files/inittab | 10 + example.files/issue | 3 + example.files/motd | 10 + example.files/rc | 79 + example.files/rc.local | 87 + example.files/rc.serial | 194 ++ example.files/securetty | 5 + example.files/shells | 4 + example.files/syslog.conf | 12 + example.files/syslog.conf.alt | 22 + games/Makefile | 35 + games/banner.6 | 72 + games/banner.c | 1165 +++++++++++++ games/ddate.6 | 17 + games/ddate.c | 171 ++ getpoe.sh | 56 + historic/makehole.8 | 44 + historic/makehole.c | 140 ++ historic/makeinfo.sh | 2 + historic/selection/Makefile | 58 + historic/selection/README.selection | 151 ++ historic/selection/mouse.c | 367 ++++ historic/selection/mouse.h | 34 + historic/selection/selection.1 | 142 ++ historic/selection/selection.c | 237 +++ historic/selection/test-mouse.c | 67 + historic/sync.c | 14 + historic/update.8 | 25 + historic/update.c | 45 + login-utils/Makefile | 112 ++ login-utils/README.admutil | 162 ++ login-utils/README.getty | 26 + login-utils/README.poeigl | 440 +++++ login-utils/agetty.8 | 241 +++ login-utils/agetty.c | 1099 ++++++++++++ login-utils/chfn.1 | 66 + login-utils/chfn.c | 414 +++++ login-utils/chsh.1 | 51 + login-utils/chsh.c | 313 ++++ login-utils/fastboot.8 | 1 + login-utils/fasthalt.8 | 1 + login-utils/halt.8 | 1 + login-utils/islocal.c | 34 + login-utils/last.1 | 59 + login-utils/last.c | 438 +++++ login-utils/login.1 | 131 ++ login-utils/login.c | 1007 +++++++++++ login-utils/mesg.1 | 24 + login-utils/mesg.c | 44 + login-utils/newgrp.1 | 24 + login-utils/newgrp.c | 95 + login-utils/passwd.1 | 36 + login-utils/passwd.c | 193 ++ login-utils/reboot.8 | 1 + login-utils/setpwnam.c | 209 +++ login-utils/shutdown.8 | 112 ++ login-utils/shutdown.c | 435 +++++ login-utils/simpleinit.8 | 142 ++ login-utils/simpleinit.c | 445 +++++ login-utils/ttymsg.c | 173 ++ login-utils/vipw.8 | 72 + login-utils/vipw.c | 252 +++ login-utils/wall.1 | 65 + login-utils/wall.c | 202 +++ makedev-1.4.1/LEGAL.NOTICE | 22 + makedev-1.4.1/MAKEDEV-C.8 | 80 + makedev-1.4.1/Makefile | 43 + makedev-1.4.1/README.MAKEDEV-C | 36 + makedev-1.4.1/THIS_VERSION_IS_ALTERED | 6 + makedev-1.4.1/devinfo | 343 ++++ makedev-1.4.1/devinfo.5 | 122 ++ makedev-1.4.1/makedev.c | 2296 ++++++++++++++++++++++++ makedev-1.4.1/makedev.cfg | 34 + makedev-1.4.1/makedev.cfg.5 | 50 + makedev-1.4.1/makedev.h | 78 + makedev-1.4.1/makedev.syn | 994 +++++++++++ misc-utils/Makefile | 71 + misc-utils/README.cal | 42 + misc-utils/README.hostname | 29 + misc-utils/README.namei | 31 + misc-utils/README.script | 7 + misc-utils/README1.namei | 14 + misc-utils/cal.1 | 81 + misc-utils/cal.c | 450 +++++ misc-utils/clear.1 | 25 + misc-utils/clear.sh | 2 + misc-utils/dnsdomainname.1 | 1 + misc-utils/domainname.1 | 43 + misc-utils/domainname.c | 29 + misc-utils/dsplit.1 | 46 + misc-utils/dsplit.c | 271 +++ misc-utils/getoptprog.1 | 104 ++ misc-utils/getoptprog.c | 30 + misc-utils/hostid.1 | 23 + misc-utils/hostid.c | 36 + misc-utils/hostname.1 | 77 + misc-utils/hostname.c | 184 ++ misc-utils/kill.1 | 49 + misc-utils/kill.c | 272 +++ misc-utils/logger.1 | 100 ++ misc-utils/logger.c | 192 ++ misc-utils/look.1 | 109 ++ misc-utils/look.c | 365 ++++ misc-utils/mcookie.1 | 17 + misc-utils/mcookie.c | 44 + misc-utils/md5.c | 253 +++ misc-utils/md5.h | 27 + misc-utils/md5sum.1 | 64 + misc-utils/md5sum.c | 243 +++ misc-utils/namei.1 | 60 + misc-utils/namei.c | 341 ++++ misc-utils/procs.c | 113 ++ misc-utils/reset.1 | 29 + misc-utils/reset.sh | 13 + misc-utils/script.1 | 123 ++ misc-utils/script.c | 294 ++++ misc-utils/setterm.1 | 88 + misc-utils/setterm.c | 1137 ++++++++++++ misc-utils/tsort.1 | 74 + misc-utils/tsort.c | 395 +++++ misc-utils/whereis.1 | 198 +++ misc-utils/whereis.c | 458 +++++ misc-utils/write.1 | 110 ++ misc-utils/write.c | 347 ++++ mount/Makefile | 93 + mount/README.mount | 147 ++ mount/fstab.5 | 168 ++ mount/fstab.c | 92 + mount/fstab.h | 25 + mount/lomount.c | 223 +++ mount/loop.h | 77 + mount/mount.8 | 589 +++++++ mount/mount.c | 734 ++++++++ mount/mount.h | 208 +++ mount/mount.x | 161 ++ mount/mount_clnt.c | 94 + mount/mount_xdr.c | 150 ++ mount/nfs.5 | 209 +++ mount/nfsmount.c | 475 +++++ mount/realpath.c | 178 ++ mount/rpcsvc/mount.h | 208 +++ mount/rpcsvc/mount.x | 161 ++ mount/rpcsvc/mount_clnt.c | 94 + mount/rpcsvc/mount_xdr.c | 150 ++ mount/sundries.c | 283 +++ mount/sundries.h | 90 + mount/swapoff.8 | 1 + mount/swapon.8 | 94 + mount/swapon.c | 109 ++ mount/umount.8 | 1 + mount/umount.c | 353 ++++ mount/version.c | 1 + sys-utils/MAKEDEV | 486 ++++++ sys-utils/MAKEDEV.8 | 147 ++ sys-utils/Makefile | 88 + sys-utils/README.MAKEDEV | 22 + sys-utils/README.setserial | 73 + sys-utils/arch.1 | 18 + sys-utils/arch.c | 35 + sys-utils/chroot.8 | 16 + sys-utils/chroot.c | 25 + sys-utils/clock.8 | 108 ++ sys-utils/clock.c | 490 ++++++ sys-utils/ctrlaltdel.8 | 38 + sys-utils/ctrlaltdel.c | 38 + sys-utils/dmesg.8 | 49 + sys-utils/dmesg.c | 88 + sys-utils/ipc.info | 1106 ++++++++++++ sys-utils/ipc.texi | 1310 ++++++++++++++ sys-utils/ipcrm.8 | 15 + sys-utils/ipcrm.c | 50 + sys-utils/ipcs.8 | 58 + sys-utils/ipcs.c | 551 ++++++ sys-utils/kbdrate.8 | 57 + sys-utils/kbdrate.c | 130 ++ sys-utils/lpcntl.8 | 30 + sys-utils/lpcntl.c | 54 + sys-utils/ramsize.8 | 1 + sys-utils/rdev.8 | 166 ++ sys-utils/rdev.c | 244 +++ sys-utils/readprofile.1 | 159 ++ sys-utils/readprofile.c | 223 +++ sys-utils/renice.8 | 131 ++ sys-utils/renice.c | 128 ++ sys-utils/rootflags.8 | 1 + sys-utils/setserial.8 | 392 +++++ sys-utils/setserial.c | 436 +++++ sys-utils/setsid.8 | 15 + sys-utils/setsid.c | 25 + sys-utils/sln.c | 72 + sys-utils/swapdev.8 | 1 + sys-utils/sync.8 | 38 + sys-utils/sync.S | 21 + sys-utils/tunelp.8 | 84 + sys-utils/tunelp.c | 248 +++ sys-utils/update_state.8 | 35 + sys-utils/update_state.sh | 41 + sys-utils/vidmode.8 | 1 + syslogd/Makefile | 35 + syslogd/syslog.conf.5 | 235 +++ syslogd/syslogd.8 | 122 ++ syslogd/syslogd.c | 1377 +++++++++++++++ syslogd/syslogd.c.bsd | 1120 ++++++++++++ syslogd/ttymsg.c | 154 ++ text-utils/Makefile | 79 + text-utils/README.col | 48 + text-utils/col.1 | 126 ++ text-utils/col.c | 529 ++++++ text-utils/colcrt.1 | 108 ++ text-utils/colcrt.c | 251 +++ text-utils/colrm.1 | 63 + text-utils/colrm.c | 126 ++ text-utils/column.1 | 99 ++ text-utils/column.c | 311 ++++ text-utils/conv.c | 124 ++ text-utils/display.c | 364 ++++ text-utils/hexdump.1 | 324 ++++ text-utils/hexdump.c | 79 + text-utils/hexdump.h | 76 + text-utils/hexsyntax.c | 130 ++ text-utils/more.1 | 196 +++ text-utils/more.c | 1832 +++++++++++++++++++ text-utils/more.help | 24 + text-utils/od.1 | 76 + text-utils/odsyntax.c | 255 +++ text-utils/parse.c | 511 ++++++ text-utils/rev.1 | 51 + text-utils/rev.c | 159 ++ text-utils/strings.1 | 96 + text-utils/strings.c | 222 +++ text-utils/ul.1 | 107 ++ text-utils/ul.c | 508 ++++++ time/Makefile | 318 ++++ time/README.time | 73 + time/Theory | 120 ++ time/africa | 603 +++++++ time/antarctica | 19 + time/asctime.c | 56 + time/asia | 803 +++++++++ time/australasia | 783 +++++++++ time/backward | 75 + time/date.1 | 153 ++ time/date.c | 900 ++++++++++ time/difftime.c | 74 + time/emkdir.c | 85 + time/etcetera | 54 + time/europe | 2072 ++++++++++++++++++++++ time/factory | 8 + time/getopt.c | 93 + time/ialloc.c | 103 ++ time/leapseconds | 41 + time/localtime.c | 1569 +++++++++++++++++ time/logwtmp.c | 65 + time/newctime.3 | 220 +++ time/newtzset.3 | 236 +++ time/northamerica | 953 ++++++++++ time/optind.c | 10 + time/pacificnew | 26 + time/private.h | 210 +++ time/scheck.c | 62 + time/solar87 | 386 ++++ time/solar88 | 386 ++++ time/solar89 | 391 +++++ time/southamerica | 397 +++++ time/strftime.c | 576 ++++++ time/systemv | 35 + time/time2posix.3 | 119 ++ time/tzfile.5 | 123 ++ time/tzfile.h | 170 ++ time/usno1988 | 111 ++ time/usno1989 | 452 +++++ time/usno1989a | 452 +++++ time/yearistype.sh | 26 + time/zdump.8 | 40 + time/zdump.c | 331 ++++ time/zic.8 | 406 +++++ time/zic.c | 1956 +++++++++++++++++++++ util-linux-2.2.bin.Notes | 1 + 317 files changed, 70853 insertions(+) create mode 100644 ANNOUNCE create mode 100644 COPYING.GPL create mode 100644 COPYING.UCB create mode 100644 LSM create mode 100644 MCONFIG create mode 100644 Makefile create mode 100644 Notes.pre1995 create mode 100644 README create mode 100644 bsd/Makefile create mode 100644 bsd/err.c create mode 100644 bsd/err.h create mode 100644 bsd/getopt.3 create mode 100644 bsd/getopt.c create mode 100644 bsd/pathnames.h create mode 100644 disk-utils/Makefile create mode 100644 disk-utils/README.bootutils-0.1 create mode 100644 disk-utils/README.cfdisk create mode 100644 disk-utils/README.fdisk create mode 100644 disk-utils/cfdisk.8 create mode 100644 disk-utils/cfdisk.c create mode 100644 disk-utils/fdformat.8 create mode 100644 disk-utils/fdformat.c create mode 100644 disk-utils/fdisk.8 create mode 100644 disk-utils/fdisk.c create mode 100644 disk-utils/fdprm create mode 100644 disk-utils/frag.8 create mode 100644 disk-utils/frag.c create mode 100644 disk-utils/fsck.minix.8 create mode 100644 disk-utils/fsck.minix.c create mode 100644 disk-utils/llseek.c create mode 100644 disk-utils/mkfs.8 create mode 100644 disk-utils/mkfs.c create mode 100644 disk-utils/mkfs.minix.8 create mode 100644 disk-utils/mkfs.minix.c create mode 100644 disk-utils/mkswap.8 create mode 100644 disk-utils/mkswap.c create mode 100644 disk-utils/setfdprm.8 create mode 100644 disk-utils/setfdprm.c create mode 100644 example.files/fstab create mode 100644 example.files/inittab create mode 100644 example.files/issue create mode 100644 example.files/motd create mode 100644 example.files/rc create mode 100644 example.files/rc.local create mode 100644 example.files/rc.serial create mode 100644 example.files/securetty create mode 100644 example.files/shells create mode 100644 example.files/syslog.conf create mode 100644 example.files/syslog.conf.alt create mode 100644 games/Makefile create mode 100644 games/banner.6 create mode 100644 games/banner.c create mode 100644 games/ddate.6 create mode 100644 games/ddate.c create mode 100644 getpoe.sh create mode 100644 historic/makehole.8 create mode 100644 historic/makehole.c create mode 100644 historic/makeinfo.sh create mode 100644 historic/selection/Makefile create mode 100644 historic/selection/README.selection create mode 100644 historic/selection/mouse.c create mode 100644 historic/selection/mouse.h create mode 100644 historic/selection/selection.1 create mode 100644 historic/selection/selection.c create mode 100644 historic/selection/test-mouse.c create mode 100644 historic/sync.c create mode 100644 historic/update.8 create mode 100644 historic/update.c create mode 100644 login-utils/Makefile create mode 100644 login-utils/README.admutil create mode 100644 login-utils/README.getty create mode 100644 login-utils/README.poeigl create mode 100644 login-utils/agetty.8 create mode 100644 login-utils/agetty.c create mode 100644 login-utils/chfn.1 create mode 100644 login-utils/chfn.c create mode 100644 login-utils/chsh.1 create mode 100644 login-utils/chsh.c create mode 100644 login-utils/fastboot.8 create mode 100644 login-utils/fasthalt.8 create mode 100644 login-utils/halt.8 create mode 100644 login-utils/islocal.c create mode 100644 login-utils/last.1 create mode 100644 login-utils/last.c create mode 100644 login-utils/login.1 create mode 100644 login-utils/login.c create mode 100644 login-utils/mesg.1 create mode 100644 login-utils/mesg.c create mode 100644 login-utils/newgrp.1 create mode 100644 login-utils/newgrp.c create mode 100644 login-utils/passwd.1 create mode 100644 login-utils/passwd.c create mode 100644 login-utils/reboot.8 create mode 100644 login-utils/setpwnam.c create mode 100644 login-utils/shutdown.8 create mode 100644 login-utils/shutdown.c create mode 100644 login-utils/simpleinit.8 create mode 100644 login-utils/simpleinit.c create mode 100644 login-utils/ttymsg.c create mode 100644 login-utils/vipw.8 create mode 100644 login-utils/vipw.c create mode 100644 login-utils/wall.1 create mode 100644 login-utils/wall.c create mode 100644 makedev-1.4.1/LEGAL.NOTICE create mode 100644 makedev-1.4.1/MAKEDEV-C.8 create mode 100644 makedev-1.4.1/Makefile create mode 100644 makedev-1.4.1/README.MAKEDEV-C create mode 100644 makedev-1.4.1/THIS_VERSION_IS_ALTERED create mode 100644 makedev-1.4.1/devinfo create mode 100644 makedev-1.4.1/devinfo.5 create mode 100644 makedev-1.4.1/makedev.c create mode 100644 makedev-1.4.1/makedev.cfg create mode 100644 makedev-1.4.1/makedev.cfg.5 create mode 100644 makedev-1.4.1/makedev.h create mode 100644 makedev-1.4.1/makedev.syn create mode 100644 misc-utils/Makefile create mode 100644 misc-utils/README.cal create mode 100644 misc-utils/README.hostname create mode 100644 misc-utils/README.namei create mode 100644 misc-utils/README.script create mode 100644 misc-utils/README1.namei create mode 100644 misc-utils/cal.1 create mode 100644 misc-utils/cal.c create mode 100644 misc-utils/clear.1 create mode 100644 misc-utils/clear.sh create mode 100644 misc-utils/dnsdomainname.1 create mode 100644 misc-utils/domainname.1 create mode 100644 misc-utils/domainname.c create mode 100644 misc-utils/dsplit.1 create mode 100644 misc-utils/dsplit.c create mode 100644 misc-utils/getoptprog.1 create mode 100644 misc-utils/getoptprog.c create mode 100644 misc-utils/hostid.1 create mode 100644 misc-utils/hostid.c create mode 100644 misc-utils/hostname.1 create mode 100644 misc-utils/hostname.c create mode 100644 misc-utils/kill.1 create mode 100644 misc-utils/kill.c create mode 100644 misc-utils/logger.1 create mode 100644 misc-utils/logger.c create mode 100644 misc-utils/look.1 create mode 100644 misc-utils/look.c create mode 100644 misc-utils/mcookie.1 create mode 100644 misc-utils/mcookie.c create mode 100644 misc-utils/md5.c create mode 100644 misc-utils/md5.h create mode 100644 misc-utils/md5sum.1 create mode 100644 misc-utils/md5sum.c create mode 100644 misc-utils/namei.1 create mode 100644 misc-utils/namei.c create mode 100644 misc-utils/procs.c create mode 100644 misc-utils/reset.1 create mode 100644 misc-utils/reset.sh create mode 100644 misc-utils/script.1 create mode 100644 misc-utils/script.c create mode 100644 misc-utils/setterm.1 create mode 100644 misc-utils/setterm.c create mode 100644 misc-utils/tsort.1 create mode 100644 misc-utils/tsort.c create mode 100644 misc-utils/whereis.1 create mode 100644 misc-utils/whereis.c create mode 100644 misc-utils/write.1 create mode 100644 misc-utils/write.c create mode 100644 mount/Makefile create mode 100644 mount/README.mount create mode 100644 mount/fstab.5 create mode 100644 mount/fstab.c create mode 100644 mount/fstab.h create mode 100644 mount/lomount.c create mode 100644 mount/loop.h create mode 100644 mount/mount.8 create mode 100644 mount/mount.c create mode 100644 mount/mount.h create mode 100644 mount/mount.x create mode 100644 mount/mount_clnt.c create mode 100644 mount/mount_xdr.c create mode 100644 mount/nfs.5 create mode 100644 mount/nfsmount.c create mode 100644 mount/realpath.c create mode 100644 mount/rpcsvc/mount.h create mode 100644 mount/rpcsvc/mount.x create mode 100644 mount/rpcsvc/mount_clnt.c create mode 100644 mount/rpcsvc/mount_xdr.c create mode 100644 mount/sundries.c create mode 100644 mount/sundries.h create mode 100644 mount/swapoff.8 create mode 100644 mount/swapon.8 create mode 100644 mount/swapon.c create mode 100644 mount/umount.8 create mode 100644 mount/umount.c create mode 100644 mount/version.c create mode 100644 sys-utils/MAKEDEV create mode 100644 sys-utils/MAKEDEV.8 create mode 100644 sys-utils/Makefile create mode 100644 sys-utils/README.MAKEDEV create mode 100644 sys-utils/README.setserial create mode 100644 sys-utils/arch.1 create mode 100644 sys-utils/arch.c create mode 100644 sys-utils/chroot.8 create mode 100644 sys-utils/chroot.c create mode 100644 sys-utils/clock.8 create mode 100644 sys-utils/clock.c create mode 100644 sys-utils/ctrlaltdel.8 create mode 100644 sys-utils/ctrlaltdel.c create mode 100644 sys-utils/dmesg.8 create mode 100644 sys-utils/dmesg.c create mode 100644 sys-utils/ipc.info create mode 100644 sys-utils/ipc.texi create mode 100644 sys-utils/ipcrm.8 create mode 100644 sys-utils/ipcrm.c create mode 100644 sys-utils/ipcs.8 create mode 100644 sys-utils/ipcs.c create mode 100644 sys-utils/kbdrate.8 create mode 100644 sys-utils/kbdrate.c create mode 100644 sys-utils/lpcntl.8 create mode 100644 sys-utils/lpcntl.c create mode 100644 sys-utils/ramsize.8 create mode 100644 sys-utils/rdev.8 create mode 100644 sys-utils/rdev.c create mode 100644 sys-utils/readprofile.1 create mode 100644 sys-utils/readprofile.c create mode 100644 sys-utils/renice.8 create mode 100644 sys-utils/renice.c create mode 100644 sys-utils/rootflags.8 create mode 100644 sys-utils/setserial.8 create mode 100644 sys-utils/setserial.c create mode 100644 sys-utils/setsid.8 create mode 100644 sys-utils/setsid.c create mode 100644 sys-utils/sln.c create mode 100644 sys-utils/swapdev.8 create mode 100644 sys-utils/sync.8 create mode 100644 sys-utils/sync.S create mode 100644 sys-utils/tunelp.8 create mode 100644 sys-utils/tunelp.c create mode 100644 sys-utils/update_state.8 create mode 100644 sys-utils/update_state.sh create mode 100644 sys-utils/vidmode.8 create mode 100644 syslogd/Makefile create mode 100644 syslogd/syslog.conf.5 create mode 100644 syslogd/syslogd.8 create mode 100644 syslogd/syslogd.c create mode 100644 syslogd/syslogd.c.bsd create mode 100644 syslogd/ttymsg.c create mode 100644 text-utils/Makefile create mode 100644 text-utils/README.col create mode 100644 text-utils/col.1 create mode 100644 text-utils/col.c create mode 100644 text-utils/colcrt.1 create mode 100644 text-utils/colcrt.c create mode 100644 text-utils/colrm.1 create mode 100644 text-utils/colrm.c create mode 100644 text-utils/column.1 create mode 100644 text-utils/column.c create mode 100644 text-utils/conv.c create mode 100644 text-utils/display.c create mode 100644 text-utils/hexdump.1 create mode 100644 text-utils/hexdump.c create mode 100644 text-utils/hexdump.h create mode 100644 text-utils/hexsyntax.c create mode 100644 text-utils/more.1 create mode 100644 text-utils/more.c create mode 100644 text-utils/more.help create mode 100644 text-utils/od.1 create mode 100644 text-utils/odsyntax.c create mode 100644 text-utils/parse.c create mode 100644 text-utils/rev.1 create mode 100644 text-utils/rev.c create mode 100644 text-utils/strings.1 create mode 100644 text-utils/strings.c create mode 100644 text-utils/ul.1 create mode 100644 text-utils/ul.c create mode 100644 time/Makefile create mode 100644 time/README.time create mode 100644 time/Theory create mode 100644 time/africa create mode 100644 time/antarctica create mode 100644 time/asctime.c create mode 100644 time/asia create mode 100644 time/australasia create mode 100644 time/backward create mode 100644 time/date.1 create mode 100644 time/date.c create mode 100644 time/difftime.c create mode 100644 time/emkdir.c create mode 100644 time/etcetera create mode 100644 time/europe create mode 100644 time/factory create mode 100644 time/getopt.c create mode 100644 time/ialloc.c create mode 100644 time/leapseconds create mode 100644 time/localtime.c create mode 100644 time/logwtmp.c create mode 100644 time/newctime.3 create mode 100644 time/newtzset.3 create mode 100644 time/northamerica create mode 100644 time/optind.c create mode 100644 time/pacificnew create mode 100644 time/private.h create mode 100644 time/scheck.c create mode 100644 time/solar87 create mode 100644 time/solar88 create mode 100644 time/solar89 create mode 100644 time/southamerica create mode 100644 time/strftime.c create mode 100644 time/systemv create mode 100644 time/time2posix.3 create mode 100644 time/tzfile.5 create mode 100644 time/tzfile.h create mode 100644 time/usno1988 create mode 100644 time/usno1989 create mode 100644 time/usno1989a create mode 100644 time/yearistype.sh create mode 100644 time/zdump.8 create mode 100644 time/zdump.c create mode 100644 time/zic.8 create mode 100644 time/zic.c create mode 120000 util-linux-2.2.bin.Notes diff --git a/ANNOUNCE b/ANNOUNCE new file mode 100644 index 00000000..4323bf5c --- /dev/null +++ b/ANNOUNCE @@ -0,0 +1,67 @@ + +[Please note that the latest fdisk is "v2.0a (>2GB)" and the latest cfdisk +is "0.8a BETA (>2GB)". The cannonical versions of both are in this +package, and both should work with disks > 2GB if you have Linux 1.1.46 or +above.] + +util-linux-2.2.tar.gz (source distribution) +util-linux-2.2.bin.tar.gz (binary distribution) + + WARNING: THIS COLLECTION DOES *NOT* SUPPORT SHADOW PASSWORDS. + + WARNING: THIS COLLECTION DOES *NOT* SUPPORT SYSTEM V INITTAB. + + WARNING: USE GNU TAR -- OTHER TARS WILL FAIL SILENTLY! + + WARNING: DO *NOT* INSTALL WITHOUT THINKING. + + WARNING: *READ* the util-linux-2.2.bin.Notes file *BEFORE* and *AFTER* + installation: there are a few links you must make by hand. + + This is a collection of many assorted utilities for Linux. Some are + system utilities that are not easily available anywhere elsewhere + (e.g., mkfs.minix, mkswap); others are BSD ports of common utilities + that are not yet contained in any FSF package (e.g., col); others are + non-System-V alternatives to common utilities (e.g., simpleinit, + agetty, login, passwd). + + The arrangement, as nearly as I can determine, conforms to the Linux + Filesystem Structure, Interim Release 1.1, October 9, 1994, with *NO* + exceptions. A copy of the standards document can be found at + tsx-11.mit.edu:/pub/linux/docs/linux-standards/fsstnd/*. + + Many people provided patches and suggestions. I deeply appreciate + this. + + +HIGHLIGHTS for version 2.2: +1) This is primarily a quick bug-fix release for version 2.1 +2) mkfs wrapper added back in, since e2fsprogs only supplies an fsck wrapper +3) selection removed, since someone appears to be maintaining it now. See + sunsite.unc.edu:/pub/linux/kernel/patches/console for recent sources. + For the time being, I'm keeping a copy in the historic subdirectory of + util-linux. A "make install" should work find from within that + directory. +4) Note that other floppy utilities are available from: + ftp.imag.fr:pub/Linux/ZLIBC/fdutils/fdutils-4.1.src.tar.gz + sunsite.unc.edu:/pub/Linux/system/Misc/fdutils-4.1.src.tar.gz + tsx-11.mit.edu:/pub/linux/sources/sbin/fdutils-4.1.src.tar.gz + + +HIGHLIGHTS for version 2.1: + +1) Directory structure rearrange, with configuration support for those who + use shadow passwords and System V init (no support is provided for these + things, but your utilities won't get overwritten if you do a "make + install" after you properly edit MCONFIG). +2) fdisk and cfdisk should work as expected with 2GB+ disk drives +3) As usual, lots of stuff was updated and added, including mount, vipw, + readprofile +4) Some stuff was also deleted, and can now be found elsewhere: + fsck wrapper: tsx-11.mit.edu:/pub/linux/ALPHA/ext2fs/e2fsprogs* + pwd, su: prep.ai.mit.edu:/pub/gnu/sh-utils* + ed: prep.ai.mit.edu:/pub/gnu/ed* + od: prep.ai.mit.edu:/pub/gnu/textutils* + uudecode/uuencode: prep.ai.mit.edu:/pub/gnu/sharutils* + bdflush/update: ftp.funet.fi:/pub/OS/Linux/PEOPLE/Linus/v1.1/bdflush* + diff --git a/COPYING.GPL b/COPYING.GPL new file mode 100644 index 00000000..a43ea212 --- /dev/null +++ b/COPYING.GPL @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/COPYING.UCB b/COPYING.UCB new file mode 100644 index 00000000..9abbf241 --- /dev/null +++ b/COPYING.UCB @@ -0,0 +1,32 @@ +/* + * 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. + */ diff --git a/LSM b/LSM new file mode 100644 index 00000000..3b0a1cb3 --- /dev/null +++ b/LSM @@ -0,0 +1,23 @@ +Begin3 +Title: util-linux: Miscellaneous utilities for Linux +Version: 2.2 +Entered-date: Sun Feb 26 16:05:47 1995 +Description: reboot shutdown simpleinit sln swapoff swapon cal chfn chsh + clear col colcrt colrm column dnsdomainname domainname + dsplit fdformat getopt hexdump hostid hostname ipcrm ipcs + last logger look lpcntl mcookie md5sum mesg namei newgrp + passwd ramsize rdev readprofile renice reset rev rootflags + script selection setfdprm setsid setterm strings swapdev + tsort tunelp ul vidmode wall whereis write chroot + ctrlaltdel frag syslogd update_state vipw zdump zic +Keywords: essential utilities +Author: several +Maintained-by: faith@cs.unc.edu (Rik Faith) +Primary-site: ftp.cs.unc.edu /pub/users/faith/linux + 592k util-linux-2.2.tar.gz + 571k util-linux-2.2.bin.tar.gz +Alternate-site: tsx-11.mit.edu /pub/linux/packages/utils +Alternate-site: sunsite.unc.edu /pub/Linux/system/Misc +Platforms: Linux 1.1.9x, GNU tar +Copying-policy: GPL, BSD, others +End diff --git a/MCONFIG b/MCONFIG new file mode 100644 index 00000000..ee661cfe --- /dev/null +++ b/MCONFIG @@ -0,0 +1,79 @@ +# MCONFIG -- Configuration stuff for util-linux +# Created: Sat Feb 4 15:50:30 1995 +# Revised: Sun Feb 26 21:33:32 1995 by faith@cs.unc.edu +# Copyright 1995 Rickard E. Faith (faith@cs.unc.edu) +# + +# If HAVE_SHADOW is set to "yes", then login, chfn, chsh, newgrp, passwd, and +# vipw will not be built or installed from the login-utils subdirectory. +HAVE_SHADOW=no +#HAVE_SHADOW=yes + +# If HAVE_SYSVINIT is set to "yes", then agetty, simpleinit, shutdown, +# last, mesg, and wall will not be built or installed from the login-utils +# subdirectory. +HAVE_SYSVINIT=no +#HAVE_SYSVINIT=yes + +# If HAVE_STRINGS is set to "yes", then strings won't be installed. This +# is the quick fix until the strings in GNU binutils is in wide use and has +# international support. +HAVE_STRINGS=no +#HAVE_STRINGS=yes + +CC= gcc +OPT= -pipe -O2 -m486 -fomit-frame-pointer +WFLAGS= -Wall +LDFLAGS= -s -N +CFLAGS= $(OPT) -I. -I$(BSD) \ + -DSBINDIR=\"$(SBINDIR)\" \ + -DUSRSBINDIR=\"$(USRSBINDIR)\" \ + -DLOGDIR=\"$(LOGDIR)\" \ + -DVARPATH=\"$(VARPATH)\" + +DEVDIR= $(DESTDIR)/dev +ETCDIR= $(DESTDIR)/etc +SBINDIR= $(DESTDIR)/sbin +USRSBINDIR= $(DESTDIR)/usr/sbin +USRBINDIR= $(DESTDIR)/usr/bin +USRGAMESDIR= $(DESTDIR)/usr/games +BINDIR= $(DESTDIR)/bin +VARPATH= $(DESTDIR)/var +LOGDIR= $(DESTDIR)/var/adm +MANDIR= $(DESTDIR)/usr/man +MAN1DIR= $(DESTDIR)/usr/man/man1 +MAN3DIR= $(DESTDIR)/usr/man/man3 +MAN5DIR= $(DESTDIR)/usr/man/man5 +MAN6DIR= $(DESTDIR)/usr/man/man6 +MAN8DIR= $(DESTDIR)/usr/man/man8 +INFODIR= $(DESTDIR)/usr/info + +# Directory for shutdown, halt, reboot, etc. +SHUTDOWNDIR= $(SBINDIR) + +# Directory for fsck +FSCKDIR= $(SBINDIR) + +# Directory for rdev, vidmode, etc. +RDEVDIR= $(USRBINDIR) + +# Directory for passwd +PASSWDDIR= $(USRBINDIR) + +# Modes +DIRMODE= 755 +BINMODE= 755 +MANMODE= 644 +DATMODE= 644 +INFOMODE= 644 +SUIDMODE= 4755 + +CHMOD= chmod +INSTALL= install +INSTALLDIR= $(INSTALL) -d -m $(DIRMODE) +INSTALLBIN= $(INSTALL) -m $(BINMODE) +INSTALLMAN= $(INSTALL) -m $(MANMODE) +INSTALLDAT= $(INSTALL) -m $(DATMODE) +INSTALLSUID= $(INSTALL) -m $(SUIDMODE) -o root + +BSD= ../bsd diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..9fb40527 --- /dev/null +++ b/Makefile @@ -0,0 +1,54 @@ +# Makefile -- Makefile for util-linux Linux utilities +# Created: Sat Dec 26 20:09:40 1992 +# Revised: Sun Feb 26 17:29:25 1995 by faith@cs.unc.edu +# Copyright 1992, 1993, 1994, 1995 Rickard E. Faith (faith@cs.unc.edu) +# + +VERSION=2.2 +include MCONFIG + +SUBDIRS= bsd \ + disk-utils \ + games \ + login-utils \ + makedev-1.4.1 \ + misc-utils \ + mount \ + sys-utils \ + syslogd \ + text-utils \ + time + + +.PHONEY: all install clean +all: + @for subdir in $(SUBDIRS); do \ + (cd $$subdir && $(MAKE) $@) || exit 1; \ + done + +install: + @if [ "`whoami`" = "root" ]; then umask 022; fi + @for subdir in $(SUBDIRS); do \ + (cd $$subdir && $(MAKE) $@) || exit 1; \ + done + +clean: + -rm -f *.o *~ core poe.diffs + @for subdir in $(SUBDIRS) historic/selection; do \ + (cd $$subdir && $(MAKE) $@) || exit 1; \ + done + +dist: + (cd /tmp; \ + rm -rf /tmp/util-linux-$(VERSION); \ + cvs export -fNd util-linux-$(VERSION) -r HEAD util-linux; \ + cd util-linux-$(VERSION); \ + ln -s README util-linux-$(VERSION).bin.Notes; \ + find -type d | xargs chmod 755; \ + find -type f | xargs chmod 644; \ + find -type d | xargs chown root:root; \ + find -type f | xargs chown root:root; \ + cd ..; \ + tar zcvvf util-linux-$(VERSION).tar.gz util-linux-$(VERSION); \ + cp -p util-linux-$(VERSION)/README util-linux-$(VERSION).bin.Notes; \ + echo Done.) diff --git a/Notes.pre1995 b/Notes.pre1995 new file mode 100644 index 00000000..cb9ed444 --- /dev/null +++ b/Notes.pre1995 @@ -0,0 +1,386 @@ +HIGHLIGHTS for version 1.10: +1) domainname is no longer installed. The source for domainname is still + included in the source distribution, as is the hostname-1.5 package, by + Peter Tobias (tobias@server.et-inf.fho-emden.de), which contains a new + hostname and a dnsdomainname program. If you have any questions read + the documentation in hostname-1.5 and NetKit-A. +2) Alan Modra (alan@spri.levels.unisa.edu.au) updated syslogd and clock. +3) Joe Ragland (jrr@interpath.net) updated whereis.1 +4) Kai Petzke (wpp@marie.physik.tu-berlin.de) updated setserial.8 +5) Michael K. Johnson (johnsonm@nigel.vnet.net) updated tunelp +6) Carl Christofferson (cchris@connected.com) updated col +7) bjdouma@xs4all.nl updated rev +8) Lots of updates to mount: without -t, (null) is no longer entered in + mtab; readonly file systems are now remounted readonly if they weren't + the first time; you can mount loop devices; umount will send RPC calls + to the NFS server. +9) agetty, login, hostid updated. + + +HIGHLIGHTS for version 1.9: + +1) Miscellaneous bug fixes by Dave Gentzel (gentzel@nova.enet.dec.com) and + Sander van Malssen (svm@kozmix.hacktic.nl) +2) tunelp has been added +3) selection now allows the mouse pointer to wrap (this is off by default) + (Thanks to Sander van Malssen (svm@kozmix.hacktic.nl).) +4) Many old versions have been removed, making the source distribution + smaller. + + +HIGHLIGHTS for version 1.8: + +1) bdflush is now installed as update (WARNING!). +2) MAKEDEV was updated. This version uses /proc/devices. +3) Minor corrections (thanks to Dave Gentzel). +4) Nigel Gamble's lpcntl is included. + + +HIGHLIGHTS for version 1.7: + +0) A small, static sln (ln substitute) and a small, static sync(1) are + now included. +1) The mkswap(8) man page was fixed (wpp@marie.physik.tu-berlin.de (Kai + Petzke)) +2) hostname and pwd are no longer installed -- they are in FSF's + sh-util-1.10 +3) uuencode and uudecode are no longer installed -- they are in FSF's + uuencode-1.0 +4) ed is no longer installed -- it is in FSF's ed-0.1 +5) The C version of sync was replaced by an assembly version (by Nick + Holloway) +6) setterm was updated to work with dosemu +7) Various security holes were fixed (login, passwd, agetty, etc.) +8) A few other random things were updated. +9) Many 4.3BSD-reno (NET-2) utilities were replaced with the 4.4BSD-Lite + versions. +10) update has been removed. /sbin/update is now a link to /sbin/bdflush. +11) syslogd moved form /sbin to /usr/sbin, to conform to the FSSTND. +12) mount will use /proc/filesystems if no -t option is given (from + Adam J. Richter (adam@adam.yggdrasil.com)). + + +HIGHLIGHTS for version 1.6: + +Additions: + +1) Kevin Martin's cfdisk: a curses based fdisk! +2) Eric Youngdale's bdflush +3) sln: a statically linked (and very stupid) ln +4) getopt(1) + +Deletions: +1) doshell hasn't been needed for years (since before 0.98 when getty + didn't exist). I have deleted it. +2) To avoid horrible confusion, ldd and ldconfig have been removed. Find + them in David Engel's ldso package. + +Updates: +1) Softlinks are now relative. +2) The backspace problem with agetty is fixed. +3) "maintenance" is now spelled correctly. +4) The example files have been updated. +5) Per Kang-Jin Lee's (lee@tengu.in-berlin.de) suggestion, there is now an + "install.shadow" target that will *NOT* overwrite chsh, login, newgrp, + and passwd. There is no other shadow password support. +6) Timezone support totally updated (zic and zdump moved to /usr/sbin) +7) mount man page updated per Remy Card (Remy.Card@masi.ibp.fr) +8) MAKEDEV has been updated +9) sync is now statically linked +10) fdisk 1.5 was patched to support DOS and OS/2 partitions. + + + + +Notes: + +0) This package is the union of my util-etc, util-bin, and util-usrbin + packages. Trying to comply with the draft file system standard was too + much of a headache when these utilities were all in different pacakges. + +1) The clock program from the timesrc-1.2.tar.Z package is included. The + rest of this distribution has been replaced by the ado@elsie.nci.nih.gov + version. See below for details. + + Patches from Hamish Coleman (hamish@zot.apana.org.au) have been applied + to the clock program, making it version 1.2a. See clock.c for details. + These patches "stuff things up" if your cmos clock is not in universal + time, so they have been removed. + + Version 1.3 is updates from Alan Modra (alan@spri.levels.unisa.edu.au). + These were also reverted because they break if your cmos clock is not in + universal time. Version 1.3 source is in the broken subdirectory in + case anyone wants to fix this. + +2) The time directory contains tzcode94e.tar.gz and tzdata94d.tar.gz from + elsie.nci.nih.gov. + +3) Peter Orbaek (poe@daimi.aau.dk) put together the admutil-1.11.tar.gz + package. The following are from that collection: + + ctrlaltdel (by Peter Orbaek) + + shutdown (by Peter Orbaek, + with new modifications by Stephen Tweedie and Rik Faith) + + passwd (by Peter Orbaek) + + newgrp (by Michael Haardt with modifications by Peter Orbaek) + + chsh (by Peter Orbaek) + + last (BSD 5.11 6/29/88) Port by Michael Haardt with changes by + Peter Orbaek. + + I applied a patch to passwd from Markus Armbruster + which allows non-lettters to be used in the + password instead of digits. + +4) Peter Orbaek (poe@daimi.aau.dk) put together the poeigl-1.29.tar.gz + package. The following are from that collection: + + agetty (by W.Z. Venema ) + + simpleinit (by Peter Orbaek) + + domainname (by Peter Orbaek) + + login (BSD 5.40 5/9/89) Ported to HP-UX by Michael Glad, + ported to Linux by Peter Orbaek) + + hostid (by Mitchum DSouza) + + Thanks to Christian von Roques (roques@juliet.ka.sub.org), Bill + Reynolds (bill@goshawk.lanl.gov), Sander van Malssen + (svm@kozmix.hacktic.nl), David A. Holland (dholland@husc.harvard.edu) + and others who sent in several patches. These were forwarded to Peter. + +5) Jim Winstead Jr. (jwinstea@fenris.claremont.edu) put together the + system-0.98.tar.Z package. The following are from that collection: + + doshell (by Jim Wiegand, + with modifications by Marcel Mol (marcel@dutecad.et.tudelft.nl)) + + fdformat (by Werner Almesberger (almesber@nessie.cs.id.ethz.ch), + with modifications by Marcel Mol (marcel@dutecad.et.tudelft.nl)) + -- Actually, updated with a September 1992 version by Werner. + + frag (by Werner Almesberger (V1.0), with modifications + by Steffen Zahn (V1.1), + by Rob Hooft (V1.2), + and by Steffen Zahn (szahn%masterix@emndev.siemens.co.at)) + + setfdprm (by Werner Almesberger (almesber@nessie.cs.id.ethz.ch)) + + sync (by Nick Holloway, with thanks to James Bonfield) -- a small, + assembly language version replaces the old C language version by + Linus Torvalds (torvalds@cs.helsinki.fi) + + ed.old (by Brian Beattie, Kees Bot, and others; with changes by + W. Metzenthen) -- For utilb, this was edited to provide larger + constants (4096 characters per line, etc.) which are needed by + X11R5 for make depend. + + more (BSD 5.19 6/28/88) by Eric Shienbrood, with + modifications by Geoff Peck and John Foderaro) + + kill (by Peter MacDonald) + +6) Rick Sladkey put together the mount-0.99.6.tar.Z package, and Stephen + Tweedie provided updates. The following are from that package (all + appear to be by Doug Quale (quale@saavik.cs.wisc.edu), with + modifications by H. J. Lu (hlu@eecs.wsu.edu) on 11/25/92; Rick Sladkey + (jrs@world.std.com) in January 1993; and Stephen Tweedie + on 8 October 1993: + + mount + umount + swapon + + This distribution mount now supports NFS stuff. I have modified the man + pages. I have also added a small patch from Hamish Glen Coleman + (t933093@minyos.xx.rmit.OZ.AU) which restores the -o semantics. + + Updated with Rick Sladkey's mount-0.99.14.tar.gz package, and with + extra patches from Rick. + + Adam J. Richter allowed -t option to be optional. + + Patrick J. Volkerding (volkerdi@mhd1.moorhead.msus.edu) and Mitchum + DSouza both provided patches that fixed the (null) problem when not + using -t. + + Mitchum DSouza (mitch@mrc-applied-psychology.cambridge.ac.uk) added + support for loop device mounts. + + Sebastian Lederer (lederer@next-pc.informatik.uni-bonn.de) added + support for sending an unmount RPC call to the server when an + NFS-filesystem is unmounted. + + Sander van Malssen (svm@kozmix.hacktic.nl) added support for remounting + readonly file systems readonly. + +7) The rdev program is original work by Werner Almesberger + (almesber@nessie.cs.id.ethz.ch), modified by Peter MacDonald and Stephen + Tweedie. + +8) I (Rik Faith) wrote: + + kbdrate + clear + reset (updated to call 'stty sane' first) + look + + most of the man pages + +9) Linus Torvalds (torvalds@cs.helsinki.fi) released new versions of + fsck.c, mkfs.c, and mkswap.c in February 1993. This fsck and mkfs + support 14 *and* 30 character minux filesystems! + + fsck HAS BEEN RENAMED TO fsck.minix! TAKE NOTE! This change is for + compatibility with the fsutil package. The return codes have also been + fixed for compatibility with the fsutil package. + + fsck.minix and mkfs.minix have been updated by Rik Faith + (faith@cs.unc.edu), Scott Heavner (sdh@po.cwru.edu), and Dr. Wettstein + (greg%wind.uucp@plains.nodak.edu). + +10) David Engel (david@ods.com) put together the fsutil-1.8.tar.gz package, + which contains a generic front-end for fsck and mkfs. This package has + been included in this release. He also did lfconfig, which is from his + ldso-1.3.tar.z package. + +11) Michael K. Johnson (johnsonm@stolaf.edu) re-released Rick Sladkey's + setserial in January 1993, with changes by Theodore Ts'o + (tytso@mit.edu). I think that Theodore also did extensive changes for + version 2.01, I can't find any notes about this in the documentation. + However, Theodore Ts'o (tytso@ATHENA.MIT.EDU) released version 2.10, + and that is now included. + +12) I applied enhancments and bug fixes to the fdisk (by A. V. Le Blanc + (LeBlanc@mcc.ac.uk)) in Jim Winstead Jr.'s + (jwinstea@fenris.claremont.edu) system-0.98.tar.Z package. Owen + (LeBlanc) then re-enhanced the version and added bug fixes. He also + gave me a copy of the excellent documentation: see README.fdisk. I + have replaced this old version with Owen's fdisk 1.5 release, with + Kevin Martin's patches for DOS and OS/2 compatibility. I've called + this version 1.5a. Then I changed a few partition names, and called it + 1.5b. Since Kevin's changes were significant, it should probably have + been called 1.6. . . + +13) Added ipcs and ipcrm from the ipcdelta.tar.z distribution by krishna + balasub@cis.ohio-state.edu on 3/15/93. I also took the ipc.info and + ipc.texi files from that distribution. I wrote short man pages for the + binaries. + +14) The new dmesg program from Theodore Ts'o is also included, with a man + page that I wrote, and changes from Rick Sladkey. + +15) The complete selection-1.5 package, by Andrew Haylett + , 17th June 1993, is included. Kernel patches are + no longer necessary for modern kernels, but these were tiny so I left + them in for historical reasons. The Makefile was modified for this + distribution. With changes from Rick Sladkey. + +16) A posix-compliant ed is now in ed.posix, and is used by default. See + the README and source for authorship information and other credits, + including The Regents of the University of California; Rodney Ruddock + of the University of Guelph, Guelph, Ontario; Matt Bishop of Dartmouth + College, Hanover, NH; and Addison-Wesley Publishing Company. The code + is based on B. W. Kernighan and P. J. Plauger, SOFTWARE TOOLS IN + PASCAL, Addison-Wesley, 1981. + +17) Gordon Irlam (gordoni@cs.ua.oz.au) did setterm, which was adapted to + Linux by Peter MacDonald and enhanced by Mika Liljeberg + (liljeber@cs.Helsinki.FI). A bunch of patches from John Walder + (j-walder@uiuc.edu) were applied so that setterm will work with dosemu. + +18) Several utilities are from the BSD NET-2 (4.3bsd-reno) distribution: + + col (5.3 (Berkeley) 2/2/91) + [See README.col for comments, and differences other cols] + hexdump (5.5 (Berkeley) 6/1/90) + rev (5.2 (Berkeley) 3/21/92, with modifications for Linux by + Charles Hannum (mycroft@gnu.ai.mit.edu) and Brian + Koehmstedt (bpk@gnu.ai.mit.edu)) + strings (5.10 (Berkeley) 5/23/91) + syslogd (5.45 (Berkeley) 3/2/91) [with ttymsg; see below for changes] + tsort (5.3 (Berkeley) 6/1/90) + wall (5.14 (Berkeley) 3/2/91) + whereis (5.5 (Berkeley) 4/18/91) + write (4.22 (Berkeley) 6/1/90) + + Most of the changes for syslogd come from Rick Sladkey + (jrs@world.std.com), but I'd like to thank other people who sent in + changes (which usually got forwarded to Rick): Carsten Paeth + (calle@calle.in-berlin.de) and Kaz Sasayama (kaz@lilia.iijnet.or.jp). + + Original NET-2 source is currently available at + wuarchive.wustl.edu:/mirrors/4.3-reno/{bin,usr.bin}. The only changes + that where made to these sources were that more reasonable paths were + placed in the whereis program and that internationalization support was + added to some programs. These changes can be found by grep'ing for + "linux" in the source file, or by comparing the source file with the + original source. Other patches have been applied as they became + available. The best way to find out how the programs were patched is + to get the original source and do a diff. It is far too much overhead + for me to track these diffs individually. + + The getopt(3) from the NET-2 distribution is included, and is linked + with BSD NET-2 programs that use getopt(3). The BSD getopt behaves + differently from the standard GNU getopt. Please do *NOT* try to use + the GNU getopt for programs which require BSD getopt, since this may + change the program's behavior when a single '-' is given as an option. + The man page for getopt(3) is included for reference in the source + distribution, but is *NOT* installed in /usr/man/man3. + + Other changes that seemed significant: + + string.c needed a ':' after the 'n' in the getopt call. + +19) Several utilities are from the 4.4BSD-Lite distribution: + + banner (8.3 (Berkeley) 4/2/94) + column (8.3 (Berkeley) 4/2/94) + colcrt (8.1 (Berkeley) 6/6/93) + cal (8.4 (Berkeley) 4/2/94) [See README.cal for algorithm details] + logger (8.1 (Berkeley) 6/6/93) + look (8.1 (Berkeley) 6/14/93) + renice (8.1 (Berkeley) 6/9/93) + ul (8.1 (Berkeley) 6/6/93) + + To find the modifications, look for __linux__. The original sources + are available for ftp from: + ftp.cdrom.com:/pub/bsd-sources/4.4BSD-Lite/usr/src/usr.bin + +20) Rick Sladkey (jrs@world.std.com) ported: + + script (BSD 5.13 3/5/91) + + with a small patch from Harald Koenig + (koenig@nova.tat.physik.uni-tuebingen.de) to fixes the problem of + script terminating unexpectedly. + +21) Miquel van Smoorenburg (miquels@htsa.aha.nl, + miquels@drinkel.nl.mugnet.org) put together a sysvinit.tar.Z package. + One utility was taken from that collection: + + mesg + +22) MAKEDEV is Nick Holloway 's latest, version + 2.0, with patches from Dave Gentzel (gentzel@nova.enet.dec.com). + +23) sln by Mike Parker and David MacKenzie (from Linux's libc) + +24) bdflush 1.4, by Eric Youngdale. + +25) getopt is from the NetBSD distribution on + jhunix.hcf.jhu.edu + (/pub/public_domain_software/NetBSD/usr/src/usr.bin/getopt) + +26) cfdisk is from Kevin Martin's cfdisk-0.8.tar.gz *BETA* distribution. + +27) lpcntl from Nigel Gamble (nigel@gate.net), Mon, 18 Jul 94 20:17:35 EDT. + +28) tunelp (by Michael K. Johnson (johnsonm@sunsite.unc.edu)) added from + tunelp-1.1 diff --git a/README b/README new file mode 100644 index 00000000..b8260998 --- /dev/null +++ b/README @@ -0,0 +1,566 @@ +util-linux: Miscellaneous utilities for Linux +%n util-linux +%v 2.2 +%c * +%l * +%b * +%d * +%f ftp.cs.unc.edu:/pub/users/faith/linux +%t util-linux-2.2.tar.gz +%w utils +%% +# These lines describe an automated build procedure, please ignore them. +%setup +make +%doc ANNOUNCE LSM README +%doc COPYING.GPL COPYING.UCB +%doc ./time/README.time ./disk-utils/README.cfdisk +%doc ./disk-utils/README.fdisk ./disk-utils/README.bootutils-0.1 +%doc ./sys-utils/README.setserial ./makedev-1.4.1/README.MAKEDEV-C +%doc ./misc-utils/README.script ./misc-utils/README.hostname +%doc ./misc-utils/README.namei ./misc-utils/README.cal +%doc ./misc-utils/README1.namei ./text-utils/README.col +%doc ./mount/README.mount +%doc ./login-utils/README.getty ./login-utils/README.admutil +%doc ./login-utils/README.poeigl +cp -a $BUILDDIR/$NAME-$VERSION/example.files /usr/doc/$WHERE/$NAME-$VERSION +* rm -rf /usr/lib/zoneinfo +* make install +%i set -x +%i /usr/sbin/zic -l US/Eastern +%i /usr/sbin/zic -p America/New_York +%i set +x +%i echo 'WARNING: Check time zone! (If necessary, change with zic).' +%i echo 'WARNING: Check /etc/rc for initalization of /var/adm/utmp' +%i echo 'WARNING: /etc/rc should run /sbin/update, *NOT* /sbin/bdflush' +exit +# Please ignore the previous lines. . . +# The informative part of the notes file starts here: + +WARNING: THE PROGRAMS IN THIS SUITE DO *NOT* SUPPORT SHADOW PASSWORD FILES! + +WARNING: THIS COLLECTION DOES *NOT* SUPPORT SYSTEM V INITTAB. + +WARNING: USE GNU TAR -- OTHER TARS WILL FAIL SILENTLY! + +WARNING: DO *NOT* INSTALL WITHOUT THINKING. + +WARNING: Read the util-linux-2.2.bin.Notes file *BEFORE* and *AFTER* + installation: there are a few links you must make by hand. + +WARNING: The agetty, simpleinit, login, passwd, and other programs in this + package are *NOT* System V compliant. These utilities are meant + to be used by people who build their own systems. If you are not + a wizard, do *NOT* blindly install these utilities: they could + prevent you from logging into your system. Have a boot floppy + ready, especially if you don't know what you are doing. + +WARNING: The binary distribution was tarred using GNU TAR AND THE -S OPTION! + This means that holes will be preserved, but that ONLY GNU TAR + WILL WORK ON THE BINARY DISTRIBUTION (in fact, other, inferior, + tar programs will fail silently). YOU HAVE BEEN WARNED! + +WARNING: localtime and posixtime default to US/Eastern -- change these now. + + + +To install from the binary distribution: + +1) Get binary distribution (see the .lsm file for locations) +2) cd / +3) gtar zpxvf util-linux-2.2.bin.tar.gz + (or: pms -i util-linux-2.2.bin.tar.gz) +4) *IF* you want to use agetty and simpleinit, then make softlinks from + /sbin/init to simpleinit and from /sbin/getty to agetty, but make sure + that your /etc/inittab is set up right (this is *NOT* the System V + compatible init!), or you will be hosed. +5) Run zic -l and/or zic -p to set your timezone. The distribution is set + up to use /usr/lib/zoneinfo/US/Eastern as the default. This was + installed with "zic -l US/Eastern" +6) Remove all the old binaries from previous locations. + + + +To install from source: + +1) Get source distribution (see the .lsm file for locations) +2) Untar util-linux-2.2.tar.gz in /usr/src +3) cd util-linux-2.2 +4) Edit MCONFIG: + + If you use the shadow password suite and do _not_ want to install + programs like login and passwd that do not support shadow passwords, + then set HAVE_SHADOW to yes + + If you use the System V init suite and do _not_ want to install programs + like agetty, simpleinit, and shutdown, then set HAVE_SYSVINIT to yes + + If you don't like the compile-time options or the directories, then + change them. Note that you also can say something like "make OPT=-g + LDFLAGS=" in order to make a complete debugging version without editing + the MCONFIG at all. + +5) make +6) make install +7) If you want to use simpleinit and agetty, then make softlinks from + /sbin/init to simpleinit and from /sbin/getty to agetty, but make sure + that your /etc/inittab is set up right (this is *NOT* the System V + compatible init!), or you will be hosed. If you are using the SysV + init and/or some other getty, they you can keep using those. +8) Run zic -l and/or zic -p to set your timezone. The distribution is set + up to use /usr/lib/zoneinfo/US/Eastern as the default. This was + installed with "zic -l US/Eastern" +9) Remove all the old binaries from previous locations. + + + +HIGHLIGHTS for version 2.2: +1) This is primarily a quick bug-fix release for version 2.1 +2) mkfs wrapper added back in, since e2fsprogs only supplies an fsck wrapper +3) selection removed, since someone appears to be maintaining it now. See + sunsite.unc.edu:/pub/linux/kernel/patches/console for recent sources. + For the time being, I'm keeping a copy in the historic subdirectory of + util-linux. A "make install" should work find from within that + directory. +4) Note that other floppy utilities are available from: + ftp.imag.fr:pub/Linux/ZLIBC/fdutils/fdutils-4.1.src.tar.gz + sunsite.unc.edu:/pub/Linux/system/Misc/fdutils-4.1.src.tar.gz + tsx-11.mit.edu:/pub/linux/sources/sbin/fdutils-4.1.src.tar.gz + + +HIGHLIGHTS for version 2.1: + +1) Directory structure rearrange, with configuration support for those who + use shadow passwords and System V init (no support is provided for these + things, but your utilities won't get overwritten if you do a "make + install" after you properly edit MCONFIG). +2) fdisk and cfdisk should work as expected with 2GB+ disk drives +3) As usual, lots of stuff was updated and added, including mount, vipw, + readprofile +4) Some stuff was also deleted, and can now be found elsewhere: + fsck wrapper: tsx-11.mit.edu:/pub/linux/ALPHA/ext2fs/e2fsprogs* + pwd, su: prep.ai.mit.edu:/pub/gnu/sh-utils* + ed: prep.ai.mit.edu:/pub/gnu/ed* + od: prep.ai.mit.edu:/pub/gnu/textutils* + uudecode/uuencode: prep.ai.mit.edu:/pub/gnu/sharutils* + bdflush/update: ftp.funet.fi:/pub/OS/Linux/PEOPLE/Linus/v1.1/bdflush* + + + +PARTIAL HISTORY OF UTIL-LINUX: + +bsd: + Nothing in this directory gets installed, but some BSD programs need + this support: + err.c: 8.1 (Berkeley) 6/4/93 + err.h: 8.1 (Berkeley) 6/2/93 + getopt.c: 4.13 (Berkeley) 2/23/91 + pathnames.h: 5.3 (Berkeley) 5/9/89 with extensive modifications for + Linux + +disk-utils: + cfdisk: 0.8 BETA (>2GB) from Kevin E. Martin (martin@cs.unc.edu) with + modifications for disks > 2GB. + ftp.cs.unc.edu:/pub/users/martin/linux/cfdisk-0.8.tar.gz + fdformat: Werner Almesberger (almesber@nessie.cs.id.ethz.ch), with + modifications by Marcel Mol (marcel@dutecad.et.tudelft.nl)). + Later, updated with a September 1992 version by Werner. + fdisk: A. V. Le Blanc (LeBlanc@mcc.ac.uk) fdisk 1.5 release, with + patched from Kevin Martin for DOS and OS/2 compatibility (1.5a); + Rik Faith (1.5b, 2.0). + frag: Werner Almesberger (1.0), Steffen Zahn (1.1), Rob Hooft (1.2), + Steffen Zahn (szahn%masterix@emndev.siemens.co.at) (1.3), Michael + Bischoff (1.4). + fsck.minix, mkfs.minix: Linus Torvalds, with modifications by: Rik + Faith (faith@cs.unc.edu), Scott Heavner + (sdh@po.cwru.edu), Dr. Wettstein + (greg%wind.uucp@plains.nodak.edu), Daniel + Quinlan (quinlan@yggdrasil.com). + mkswap: Linus Torvalds, with modifications by Mike Jagdis + (jaggy@purplet.demon.co.uk. ) + setfdprm: Werner Almesberger (almesber@nessie.cs.id.ethz.ch) + Note that more floppy utilities are available from: + ftp.imag.fr:pub/Linux/ZLIBC/fdutils/fdutils-4.1.src.tar.gz + sunsite.unc.edu:/pub/Linux/system/Misc/fdutils-4.1.src.tar.gz + tsx-11.mit.edu:/pub/linux/sources/sbin/fdutils-4.1.src.tar.gz + llseek.c: from Remy Card's e2fsprogs-0.5b-WIP.tar.gz + +games: + banner: (8.3 (Berkeley) 4/2/94) + ddate: Druel the Chaotic aka Jeremy Johnson aka mpython@gnu.ai.mit.edu, + with modifications by Lee Harvey Oswald Smith, K.S.C. + +login-utils: + agetty: W. Z. Venema, ported by Peter Orbaek . + ftp.daimi.aau.dk:/pub/linux/poe/poeigl-1.32.tar.gz + chfn: Salvatore Valente + chsh: Salvatore Valente + last: 5.11 w/year (Berkeley) 6/29/88; Port by Michael Haardt with + changes by Peter Orbaek. + ftp://ftp.daimi.aau.dk/pub/linux/poe/admutil-1.14.tar.gz + login: 5.40 (Berkeley) 5/9/89; with ports by Michael Glad and Peter Orbaek + ftp.daimi.aau.dk:/pub/linux/poe/poeigl-1.32.tar.gz + mesg: Miquel van Smoorenburg (miquels@htsa.aha.nl, + miquels@drinkel.nl.mugnet.org). From his sysvinit.tar.Z package. + newgrp: Michael Haardt, with modifications by Peter Orbaek. + ftp://ftp.daimi.aau.dk/pub/linux/poe/admutil-1.14.tar.gz + passwd: Peter Orbaek, with yp modifications by Alvaro Martinez + Echevarria (alvaro@enano.etsit.upm.es) + ftp://ftp.daimi.aau.dk/pub/linux/poe/admutil-1.14.tar.gz + shutdown: Peter Orbaek, with new modifications by Stephen Tweedie, Rik + Faith, and Dave (gentzel@nova.enet.dec.com). + ftp://ftp.daimi.aau.dk/pub/linux/poe/admutil-1.14.tar.gz + simpleinit: Peter Orbaek + ftp.daimi.aau.dk:/pub/linux/poe/poeigl-1.32.tar.gz + vipw: 5.16 (Berkeley) 3/3/91, with modifications by Mike Grupenhoff + + wall: 5.14 (Berkeley) 3/2/91 [From the BSD NET-2 (4.3bsd-reno) + distribution at wuarchive.wustl.edu:/mirrors/4.3-reno] + +makedev-1.4: + MAKEDEV-C: David A. Holland (dholland@husc.harvard.edu) + This version MODIFIED by Rik Faith (faith@cs.unc.edu) + sunsite.unc.edu:/pub/Linux/system/Admin/MAKEDEV-C-1.4.tar.gz + + +misc-utils: + cal: 8.4 (Berkeley) 4/2/94, with modifications by Rik Faith and + Hein@student.tu-clausthal.de (Jochen Hein). + ftp.cdrom.com:/pub/bsd-sources/4.4BSD-Lite/usr/src/usr.bin + clear: Rik Faith + domainname: Peter Orbaek + ftp.daimi.aau.dk:/pub/linux/poe/poeigl-1.32.tar.gz + dsplit: David Arnstein (arnstein@netcom.com) + gatekeeper.dec.com:/pub/usenet/comp.sources.misc/volume40/dsplit + getopt (getoptprog): jhunix.hcf.jhu.edu: + /pub/public_domain_software/NetBSD/usr/src/usr.bin/getopt + hostid: Mitch DSouza (m.dsouza@mrc-apu.cam.ac.uk) + ftp.daimi.aau.dk:/pub/linux/poe/poeigl-1.32.tar.gz + hostname/dnsdomainname: Peter Tobias + This version (1.6) should also be available soon in: + nic.funet.fi:/pub/OS/Linux/PEOPLE/Linus/net-source/base/NetKit-A* + kill: BSD version, modified by Salvatore Valente + logger: 8.1 (Berkeley) 6/6/93, with modifications by Rik Faith + ftp.cdrom.com:/pub/bsd-sources/4.4BSD-Lite/usr/src/usr.bin + look.c: 8.1 (Berkeley) 6/14/93, with modifications by Rik Faith + ftp.cdrom.com:/pub/bsd-sources/4.4BSD-Lite/usr/src/usr.bin + mcookie: Rik Faith (faith@cs.unc.edu) + md5sum: Branki Lankester and Colin Plumb. The MD5 message-digest + algorithm is in the Public Domain. This implementation + calculates message-digest information only, and can NOT be used + for encryption. Therefore it is exportable from the USA. + Original sources in the MIT version of PGP 2.6.2. + namei: Roger S. Southwick, with modifications by Steve Tell. + reset: Rik Faith + script: 5.13 (Berkeley) 3/5/91, with modifications by Rick Sladkey + (jrs@world.std.com), Harald Koenig + (koenig@nova.tat.physik.uni-tuebingen.de). + setterm: Gordon Irlam (gordoni@cs.ua.oz.au), with modifications by + Peter MacDonald, Mika Liljeberg (liljeber@cs.Helsinki.FI), + John Walder (j-walder@uiuc.edu) [for dosemu]. + tsort: 5.3 (Berkeley) 6/1/90 + wuarchive.wustl.edu:/mirrors/4.3-reno + whereis: 5.5 (Berkeley) 4/18/91 + wuarchive.wustl.edu:/mirrors/4.3-reno + write: 4.22 (Berkeley) 6/1/90, with modifications by Mike Grupenhoff + (kashmir@umiacs.umd.edu) . + wuarchive.wustl.edu:/mirrors/4.3-reno + +mount: + mount, umount, swapon + + Rick Sladkey put together the mount-0.99.6.tar.Z package, and Stephen + Tweedie provided updates. The utilities were originally from that + package (all appear to be by Doug Quale (quale@saavik.cs.wisc.edu), + with modifications by H. J. Lu (hlu@eecs.wsu.edu) on 11/25/92; Rick + Sladkey (jrs@world.std.com) in January 1993; and Stephen Tweedie + on 8 October 1993. This distribution mount now + supports NFS stuff. I have modified the man pages. I have also added + a small patch from Hamish Glen Coleman (t933093@minyos.xx.rmit.OZ.AU) + which restores the -o semantics. + + Updated with Rick Sladkey's mount-0.99.14.tar.gz package, and with + extra patches from Rick. Adam J. Richter allowed -t option to be + optional. Patrick J. Volkerding (volkerdi@mhd1.moorhead.msus.edu) and + Mitchum DSouza both provided patches that fixed the (null) problem when + not using -t. Mitchum DSouza + (mitch@mrc-applied-psychology.cambridge.ac.uk) added support for loop + device mounts. Sebastian Lederer + (lederer@next-pc.informatik.uni-bonn.de) added support for sending an + unmount RPC call to the server when an NFS-filesystem is unmounted. + Sander van Malssen (svm@kozmix.hacktic.nl) added support for remounting + readonly file systems readonly. Mike Grupenhoff + added a probe of the superblock for the type + before /proc/filesystems is checked. Andries.Brouwer@cwi.nl fixed up + error reporting. + +historic/selection: The complete selection-1.5 package, by Andrew Haylett + , 17th June 1993, is included in the historic tree. + Kernel patches are no longer necessary for modern kernels, but these + were tiny so I left them in for historical reasons. The Makefile was + modified for this distribution. With changes from Rick Sladkey. + +sys-utils: + MAKEDEV: Nick Holloway + arch: Rik Faith + chroot: Rick Sladkey + clock: Originally from the timesrc-1.2.tar.Z package, Charles Hedrick, + hedrick@cs.rutgers.edu (V1.0); Rob Hooft, hooft@chem.ruu.nl + (V1.1); Harald Koenig (koenig@nova.tat.physik.uni-tuebingen.de) + (V1.2). With additional changes: Hamish Coleman + (hamish@zot.apana.org.au) (V1.2a); Alan Modra + (alan@spri.levels.unisa.edu.au (V1.3, V1.4). + ctrlaltdel: Peter Orbaek + ftp://ftp.daimi.aau.dk/pub/linux/poe/admutil-1.14.tar.gz + dmesg: Theodore Ts'o (tytso@athena.mit.edu); Rick Sladkey + (jrs@world.std.com) + ipcrm: From the ipcdelta.tar.z distribution by krishna + balasub@cis.ohio-state.edu on 3/15/93. ipc.info and ipc.texi + are also from that distribution. + ipcs: Also from the ipcdelta.tar.z distribution by krishna + balasub@cis.ohio-state.edu, with patches from Mike Jagdis + (jaggy@purplet.demon.co.uk) + kbdrate: Rik Faith (faith@cs.unc.edu), with patches from + Andries.Brouwer@cwi.nl and John Bowman + (bowman@hagar.ph.utexas.edu) + lpcntl: Nigel Gamble (nigel@gate.net) + rdev: almesber@nessie.cs.id.ethz.ch (Werner Almesberger), with + modifications from Peter MacDonald, Stephen Tweedie + (sct@dcs.ed.ac.uk), and Dave (gentzel@nova.enet.dec.com) + readprofile: Alessandro Rubini + renice: 8.1 (Berkeley) 6/9/93 + ftp.cdrom.com:/pub/bsd-sources/4.4BSD-Lite/usr/src/usr.bin + setserial: Michael K. Johnson (johnsonm@stolaf.edu) re-released Rick + Sladkey's setserial in January 1993, with changes by + Theodore Ts'o (tytso@mit.edu). I think that Theodore also + did extensive changes for version 2.01, I can't find any + notes about this in the documentation. However, Theodore + Ts'o (tytso@ATHENA.MIT.EDU) released version 2.10, and that + is now included. + setsid: Rick Sladkey + sln: Mike Parker and David MacKenzie (from Linux's libc) + sync: Nick Holloway, with thanks to James Bonfield + tunelp: Michael K. Johnson (johnsonm@nigel.vnet.net) + update_state: Rik Faith (faith@cs.unc.edu) + +syslogd: + 5.45 (Berkeley) 3/2/91 + + Most of the changes for syslogd come from Rick Sladkey + (jrs@world.std.com), but I'd like to thank other people who sent in + changes (which usually got forwarded to Rick): Carsten Paeth + (calle@calle.in-berlin.de) and Kaz Sasayama (kaz@lilia.iijnet.or.jp). + +text-utils: + col: 5.3 (Berkeley) 2/2/91; with patches from Andries.Brouwer@cwi.nl + and Carl Christofferson (cchris@connected.com) + wuarchive.wustl.edu:/mirrors/4.3-reno/{bin,usr.bin} + colcrt: 8.1 (Berkeley) 6/6/93 (Bill Joy) + ftp.cdrom.com:/pub/bsd-sources/4.4BSD-Lite/usr/src/usr.bin + colrm: 5.4 (Berkeley) 6/1/90 (Jeff Schriebman) + column: 8.3 (Berkeley) 4/2/94 + ftp.cdrom.com:/pub/bsd-sources/4.4BSD-Lite/usr/src/usr.bin + hexdump: 5.5 (Berkeley) 6/1/90 + wuarchive.wustl.edu:/mirrors/4.3-reno/{bin,usr.bin} + more: 5.19 (Berkeley) 6/29/88 (Eric Shienbrood, Geoff Peck, John Foderaro) + rev: 5.2 (Berkeley) 3/21/92; with modifications by Charles Hannum + (mycroft@gnu.ai.mit.edu), Brian Koehmstedt (bpk@gnu.ai.mit.edu), + bjdouma@xs4all.nl + wuarchive.wustl.edu:/mirrors/4.3-reno/{bin,usr.bin} + strings: 5.10 (Berkeley) 5/23/91; with patches from Vitor Duarte + + wuarchive.wustl.edu:/mirrors/4.3-reno/{bin,usr.bin} + ul: 8.1 (Berkeley) 6/6/93 + ftp.cdrom.com:/pub/bsd-sources/4.4BSD-Lite/usr/src/usr.bin + +time: + elsie.nci.nih.gov:/pub/classictzcode.tar.gz + elsie.nci.nih.gov:/pub/classictzdata.tar.gz + (The zoneinfo database was updated Dec 1994.) + +%% +* bin/arch +* bin/dmesg +* bin/dnsdomainname +* bin/domainname +* bin/hostname +* bin/kill +* bin/login +* bin/more +* bin/mount +* bin/setserial +* bin/sync +* bin/umount +* dev/MAKEDEV +* dev/MAKEDEV-C +* etc/devinfo +* etc/makedev.cfg +* etc/fdprm +* sbin/agetty +* sbin/cfdisk +* sbin/clock +* sbin/fastboot +* sbin/fasthalt +* sbin/fdisk +* sbin/fsck.minix +* sbin/halt +* sbin/kbdrate +* sbin/mkfs +* sbin/mkfs.minix +* sbin/mkswap +* sbin/reboot +* sbin/shutdown +* sbin/simpleinit +* sbin/sln +* sbin/swapoff +* sbin/swapon +* usr/bin/cal +* usr/bin/chfn +* usr/bin/chsh +* usr/bin/clear +* usr/bin/col +* usr/bin/colcrt +* usr/bin/colrm +* usr/bin/column +* usr/bin/dsplit +* usr/bin/fdformat +* usr/bin/getopt +* usr/bin/hexdump +* usr/bin/hostid +* usr/bin/ipcrm +* usr/bin/ipcs +* usr/bin/last +* usr/bin/logger +* usr/bin/look +* usr/bin/lpcntl +* usr/bin/mcookie +* usr/bin/md5sum +* usr/bin/mesg +* usr/bin/namei +* usr/bin/newgrp +* usr/bin/passwd +* usr/bin/ramsize +* usr/bin/rdev +* usr/bin/readprofile +* usr/bin/renice +* usr/bin/reset +* usr/bin/rev +* usr/bin/rootflags +* usr/bin/script +* usr/bin/setfdprm +* usr/bin/setsid +* usr/bin/setterm +* usr/bin/strings +* usr/bin/swapdev +* usr/bin/tsort +* usr/bin/tunelp +* usr/bin/ul +* usr/bin/vidmode +* usr/bin/wall +* usr/bin/whereis +* usr/bin/write +* usr/info/ipc.info +* usr/lib/libz.a +* usr/lib/more.help +* usr/lib/zoneinfo +* usr/man/man1/arch.1 +* usr/man/man1/cal.1 +* usr/man/man1/chfn.1 +* usr/man/man1/chsh.1 +* usr/man/man1/clear.1 +* usr/man/man1/col.1 +* usr/man/man1/colcrt.1 +* usr/man/man1/colrm.1 +* usr/man/man1/column.1 +* usr/man/man1/dnsdomainname.1 +* usr/man/man1/domainname.1 +* usr/man/man1/dsplit.1 +* usr/man/man1/getopt.1 +* usr/man/man1/hexdump.1 +* usr/man/man1/hostid.1 +* usr/man/man1/hostname.1 +* usr/man/man1/kill.1 +* usr/man/man1/last.1 +* usr/man/man1/logger.1 +* usr/man/man1/login.1 +* usr/man/man1/look.1 +* usr/man/man1/md5sum.1 +* usr/man/man1/mesg.1 +* usr/man/man1/more.1 +* usr/man/man1/namei.1 +* usr/man/man1/newgrp.1 +* usr/man/man1/passwd.1 +* usr/man/man1/readprofile.1 +* usr/man/man1/reset.1 +* usr/man/man1/rev.1 +* usr/man/man1/script.1 +* usr/man/man1/setterm.1 +* usr/man/man1/strings.1 +* usr/man/man1/tsort.1 +* usr/man/man1/ul.1 +* usr/man/man1/wall.1 +* usr/man/man1/whereis.1 +* usr/man/man1/write.1 +* usr/man/man3/newctime.3 +* usr/man/man3/newtzset.3 +* usr/man/man5/devinfo.5 +* usr/man/man5/fstab.5 +* usr/man/man5/makedev.cfg.5 +* usr/man/man5/nfs.5 +* usr/man/man5/syslog.conf.5 +* usr/man/man5/tzfile.5 +* usr/man/man6/banner.6 +* usr/man/man6/ddate.6 +* usr/man/man8/MAKEDEV-C.8 +* usr/man/man8/MAKEDEV.8 +* usr/man/man8/agetty.8 +* usr/man/man8/cfdisk.8 +* usr/man/man8/chroot.8 +* usr/man/man8/clock.8 +* usr/man/man8/ctrlaltdel.8 +* usr/man/man8/dmesg.8 +* usr/man/man8/fastboot.8 +* usr/man/man8/fasthalt.8 +* usr/man/man8/fdformat.8 +* usr/man/man8/fdisk.8 +* usr/man/man8/frag.8 +* usr/man/man8/fsck.minix.8 +* usr/man/man8/halt.8 +* usr/man/man8/ipcrm.8 +* usr/man/man8/ipcs.8 +* usr/man/man8/kbdrate.8 +* usr/man/man8/lpcntl.8 +* usr/man/man8/mkfs.8 +* usr/man/man8/mkfs.minix.8 +* usr/man/man8/mkswap.8 +* usr/man/man8/mount.8 +* usr/man/man8/ramsize.8 +* usr/man/man8/rdev.8 +* usr/man/man8/reboot.8 +* usr/man/man8/renice.8 +* usr/man/man8/rootflags.8 +* usr/man/man8/setfdprm.8 +* usr/man/man8/setserial.8 +* usr/man/man8/setsid.8 +* usr/man/man8/shutdown.8 +* usr/man/man8/simpleinit.8 +* usr/man/man8/swapdev.8 +* usr/man/man8/swapoff.8 +* usr/man/man8/swapon.8 +* usr/man/man8/sync.8 +* usr/man/man8/syslogd.8 +* usr/man/man8/tunelp.8 +* usr/man/man8/umount.8 +* usr/man/man8/update_state.8 +* usr/man/man8/vidmode.8 +* usr/man/man8/vipw.8 +* usr/man/man8/zdump.8 +* usr/man/man8/zic.8 +* usr/sbin/chroot +* usr/sbin/ctrlaltdel +* usr/sbin/frag +* usr/sbin/syslogd +* usr/sbin/update_state +* usr/sbin/vipw +* usr/sbin/zdump +* usr/sbin/zic diff --git a/bsd/Makefile b/bsd/Makefile new file mode 100644 index 00000000..6baf09d5 --- /dev/null +++ b/bsd/Makefile @@ -0,0 +1,20 @@ +# Makefile -- Makefile for util-linux Linux utilities +# Created: Sat Dec 26 20:09:40 1992 +# Revised: Sat Feb 4 19:35:59 1995 by faith@cs.unc.edu +# Copyright 1992, 1993, 1994, 1995 Rickard E. Faith (faith@cs.unc.edu) +# + +include ../MCONFIG + +%.o: %.c + $(CC) -c $(CFLAGS) $< -o $@ + +all: err.o getopt.o +err.o: err.c +getopt.o: getopt.c + +.PHONY: clean distclean +clean: + -rm -f *.o *~ core + +install install.shadow: diff --git a/bsd/err.c b/bsd/err.c new file mode 100644 index 00000000..d171530e --- /dev/null +++ b/bsd/err.c @@ -0,0 +1,189 @@ +/*- + * 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)err.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include +#include +#include +#include + +#ifdef __STDC__ +#include +#else +#include +#endif + +extern char *__progname; /* Program name, from crt0. */ +#ifdef __linux__ +char *__progname; +#endif + +__dead void +#ifdef __STDC__ +err(int eval, const char *fmt, ...) +#else +err(eval, fmt, va_alist) + int eval; + const char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + verr(eval, fmt, ap); + va_end(ap); +} + +__dead void +verr(eval, fmt, ap) + int eval; + const char *fmt; + va_list ap; +{ + int sverrno; + + sverrno = errno; + (void)fprintf(stderr, "%s: ", __progname); + if (fmt != NULL) { + (void)vfprintf(stderr, fmt, ap); + (void)fprintf(stderr, ": "); + } + (void)fprintf(stderr, "%s\n", strerror(sverrno)); + exit(eval); +} + +__dead void +#if __STDC__ +errx(int eval, const char *fmt, ...) +#else +errx(eval, fmt, va_alist) + int eval; + const char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + verrx(eval, fmt, ap); + va_end(ap); +} + +__dead void +verrx(eval, fmt, ap) + int eval; + const char *fmt; + va_list ap; +{ + (void)fprintf(stderr, "%s: ", __progname); + if (fmt != NULL) + (void)vfprintf(stderr, fmt, ap); + (void)fprintf(stderr, "\n"); + exit(eval); +} + +void +#if __STDC__ +warn(const char *fmt, ...) +#else +warn(fmt, va_alist) + const char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + vwarn(fmt, ap); + va_end(ap); +} + +void +vwarn(fmt, ap) + const char *fmt; + va_list ap; +{ + int sverrno; + + sverrno = errno; + (void)fprintf(stderr, "%s: ", __progname); + if (fmt != NULL) { + (void)vfprintf(stderr, fmt, ap); + (void)fprintf(stderr, ": "); + } + (void)fprintf(stderr, "%s\n", strerror(sverrno)); +} + +void +#ifdef __STDC__ +warnx(const char *fmt, ...) +#else +warnx(fmt, va_alist) + const char *fmt; + va_dcl +#endif +{ + va_list ap; +#ifdef __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + vwarnx(fmt, ap); + va_end(ap); +} + +void +vwarnx(fmt, ap) + const char *fmt; + va_list ap; +{ + (void)fprintf(stderr, "%s: ", __progname); + if (fmt != NULL) + (void)vfprintf(stderr, fmt, ap); + (void)fprintf(stderr, "\n"); +} diff --git a/bsd/err.h b/bsd/err.h new file mode 100644 index 00000000..da4be150 --- /dev/null +++ b/bsd/err.h @@ -0,0 +1,70 @@ +/*- + * 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. + * + * @(#)err.h 8.1 (Berkeley) 6/2/93 + */ + +#ifndef _ERR_H_ +#define _ERR_H_ + +#ifdef __linux__ +#include +#define _BSD_VA_LIST_ va_list +#define __dead /* */ +#else +/* + * Don't use va_list in the err/warn prototypes. Va_list is typedef'd in two + * places ( and ), so if we include one + * of them here we may collide with the utility's includes. It's unreasonable + * for utilities to have to include one of them to include err.h, so we get + * _BSD_VA_LIST_ from and use it. + */ +#include +#endif +#include + +__BEGIN_DECLS +__dead void err __P((int, const char *, ...)); +__dead void verr __P((int, const char *, _BSD_VA_LIST_)); +__dead void errx __P((int, const char *, ...)); +__dead void verrx __P((int, const char *, _BSD_VA_LIST_)); +void warn __P((const char *, ...)); +void vwarn __P((const char *, _BSD_VA_LIST_)); +void warnx __P((const char *, ...)); +void vwarnx __P((const char *, _BSD_VA_LIST_)); +__END_DECLS + +#ifdef __linux__ +#undef _BSD_VA_LIST_ +#endif + +#endif /* !_ERR_H_ */ diff --git a/bsd/getopt.3 b/bsd/getopt.3 new file mode 100644 index 00000000..c21a0a72 --- /dev/null +++ b/bsd/getopt.3 @@ -0,0 +1,210 @@ +.\" Copyright (c) 1988, 1991 Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)getopt.3 6.16 (Berkeley) 4/19/91 +.\" +.Dd April 19, 1991 +.Dt GETOPT 3 +.Os BSD 4.3 +.Sh NAME +.Nm getopt +.Nd get option letter from argv +.Sh SYNOPSIS +.Fd #include +.Vt extern char *optarg +.Vt extern int optind +.Vt extern int opterr +.Ft int +.Fn getopt "int argc" "char * const *argv" "const char *optstring" +.Sh DESCRIPTION +The +.Fn getopt +function gets +the next +.Em known +option character from +.Fa argv . +An option character is +.Em known +if it has been specified in the string of accepted option characters, +.Fa optstring . +.Pp +The option string +.Fa optstring +may contain the following characters; letters and +letters followed by a colon to indicate an option argument +is to follow. It does not matter to +.Fn getopt +if a following argument has leading white space. +.Pp +On return from +.Fn getopt , +.Va optarg +points to an option argument, if it is anticipated, +and the variable +.Va optind +contains the index to the next +.Fa argv +argument for a subsequent call +to +.Fn getopt . +.Pp +The variable +.Va opterr +and +.Va optind +are both initialized to 1. +In order to use +.Fn getopt +to evaluate multiple sets of arguments, or to evaluate a single set of +arguments multiple times, +.Va optind +must be initialized to the number of argv entries to be skipped in each +evaluation. +.Pp +The +.Fn getopt +function +returns an +.Dv EOF +when the argument list is exhausted, or a non-recognized +option is encountered. +The interpretation of options in the argument list may be cancelled +by the option +.Ql -- +(double dash) which causes +.Fn getopt +to signal the end of argument processing and return an +.Dv EOF . +When all options have been processed (i.e., up to the first non-option +argument), +.Fn getopt +returns +.Dv EOF . +.Sh DIAGNOSTICS +If the +.Fn getopt +function encounters a character not found in the string +.Va optarg +or detects +a missing option argument +it writes error message +.Ql ? +to the +.Em stderr . +Setting +.Va opterr +to a zero will disable these error messages. +.Sh EXAMPLE +.Bd -literal -compact +extern char *optarg; +extern int optind; +int bflag, ch, fd; + +bflag = 0; +while ((ch = getopt(argc, argv, "bf:")) != EOF) + switch(ch) { + case 'b': + bflag = 1; + break; + case 'f': + if ((fd = open(optarg, O_RDONLY, 0)) < 0) { + (void)fprintf(stderr, + "myname: unable to read file %s.\en", optarg); + exit(1) ; + } + break; + case '?': + default: + usage(); +} +argc -= optind; +argv += optind; +.Ed +.Sh HISTORY +The +.Fn getopt +function appeared +.Bx 4.3 . +.Sh BUGS +Option arguments are allowed to begin with +.Dq Li \- ; +this is reasonable but +reduces the amount of error checking possible. +.Pp +A single dash +.Dq Li - +may be specified as an character in +.Fa optstring , +however it should +.Em never +have an argument associated with it. +This allows +.Fn getopt +to be used with programs that expect +.Dq Li - +as an option flag. +This practice is wrong, and should not be used in any current development. +It is provided for backward compatibility +.Em only . +By default, a single dash causes +.Fn getopt +to return +.Dv EOF . +This is, we believe, compatible with System V. +.Pp +It is also possible to handle digits as option letters. +This allows +.Fn getopt +to be used with programs that expect a number +.Pq Dq Li \&-\&3 +as an option. +This practice is wrong, and should not be used in any current development. +It is provided for backward compatibility +.Em only . +The following code fragment works fairly well. +.Bd -literal -offset indent +int length; +char *p; + +while ((c = getopt(argc, argv, "0123456789")) != EOF) + switch (c) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + p = argv[optind - 1]; + if (p[0] == '-' && p[1] == ch && !p[2]) + length = atoi(++p); + else + length = atoi(argv[optind] + 1); + break; + } +} +.Ed diff --git a/bsd/getopt.c b/bsd/getopt.c new file mode 100644 index 00000000..7126cc1d --- /dev/null +++ b/bsd/getopt.c @@ -0,0 +1,120 @@ +/* + * Copyright (c) 1987 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)getopt.c 4.13 (Berkeley) 2/23/91"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include +#include + +/* + * get option letter from argument vector + */ +int opterr = 1, /* if error message should be printed */ + optind = 1, /* index into parent argv vector */ + optopt; /* character checked for validity */ +char *optarg; /* argument associated with option */ + +#define BADCH (int)'?' +#define EMSG "" + +int +getopt(nargc, nargv, ostr) + int nargc; + char * const *nargv; + const char *ostr; +{ + static char *place = EMSG; /* option letter processing */ + register char *oli; /* option letter list index */ + char *p; + + if (!*place) { /* update scanning pointer */ + if (optind >= nargc || *(place = nargv[optind]) != '-') { + place = EMSG; + return(EOF); + } + if (place[1] && *++place == '-') { /* found "--" */ + ++optind; + place = EMSG; + return(EOF); + } + } /* option letter okay? */ + if ((optopt = (int)*place++) == (int)':' || + !(oli = index(ostr, optopt))) { + /* + * if the user didn't specify '-' as an option, + * assume it means EOF. + */ + if (optopt == (int)'-') + return(EOF); + if (!*place) + ++optind; + if (opterr) { + if (!(p = rindex(*nargv, '/'))) + p = *nargv; + else + ++p; + (void)fprintf(stderr, "%s: illegal option -- %c\n", + p, optopt); + } + return(BADCH); + } + if (*++oli != ':') { /* don't need argument */ + optarg = NULL; + if (!*place) + ++optind; + } + else { /* need an argument */ + if (*place) /* no white space */ + optarg = place; + else if (nargc <= ++optind) { /* no arg */ + place = EMSG; + if (!(p = rindex(*nargv, '/'))) + p = *nargv; + else + ++p; + if (opterr) + (void)fprintf(stderr, + "%s: option requires an argument -- %c\n", + p, optopt); + return(BADCH); + } + else /* white space */ + optarg = nargv[optind]; + place = EMSG; + ++optind; + } + return(optopt); /* dump back option letter */ +} diff --git a/bsd/pathnames.h b/bsd/pathnames.h new file mode 100644 index 00000000..7dca388e --- /dev/null +++ b/bsd/pathnames.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 1989 The 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 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * @(#)pathnames.h 5.3 (Berkeley) 5/9/89 + * + * Changed: Sun Nov 21 12:30:54 1993 by faith@cs.unc.edu + * Changed: Wed Jun 22 20:47:27 1994 by faith@cs.unc.edu, based on changes + * from poe@daimi.aau.dk + * Changed: Wed Jun 22 22:50:13 1994 by faith@cs.unc.edu + * Changed: Sat Feb 4 16:02:10 1995 by faith@cs.unc.edu + */ + +#ifndef __STDC__ +# error "we need an ANSI compiler" +#endif + +/* The paths for some of these are wrong in /usr/include/paths.h, but we + re-define them here. */ + +#undef _PATH_UTMP +#undef _PATH_WTMP +#undef _PATH_DEFPATH +#undef _PATH_DEFPATH_ROOT +#undef _PATH_LASTLOG +#undef _PATH_MAILDIR + +#ifndef SBINDIR +#define SBINDIR "/etc" +#endif + +#ifndef USRSBINDIR +#define USRSBINDIR "/etc" +#endif + +#ifndef LOGDIR +#define LOGDIR "/etc" +#endif + +#ifndef VARPATH +#define VARPATH "/usr" +#endif + +#define _PATH_BSHELL "/bin/sh" +#define _PATH_CSHELL "/bin/csh" +#define UT_NAMESIZE 8 +#define _PATH_TTY "/dev/tty" +#define TTYTYPES "/etc/ttytype" +#define SECURETTY "/etc/securetty" +#define _PATH_UTMP LOGDIR "/utmp" +#define _PATH_WTMP LOGDIR "/wtmp" + +#define _PATH_DEFPATH "/usr/local/bin:/bin:/usr/bin:." +#define _PATH_DEFPATH_ROOT SBINDIR ":/bin:" USRSBINDIR ":/usr/bin" +#define _PATH_HUSHLOGIN ".hushlogin" +#define _PATH_LASTLOG LOGDIR "/lastlog" +#define _PATH_MAILDIR VARPATH "/spool/mail" +#define _PATH_MOTDFILE "/etc/motd" +#define _PATH_NOLOGIN "/etc/nologin" + +#define _PATH_LOGIN "/bin/login" +#define _PATH_INITTAB "/etc/inittab" +#define _PATH_RC "/etc/rc" +#define _PATH_REBOOT SBINDIR "/reboot" +#define _PATH_SINGLE "/etc/singleboot" +#define _PATH_SECURE "/etc/securesingle" +#define _PATH_USERTTY "/etc/usertty" + +#define _PATH_MTAB "/etc/mtab" +#define _PATH_UMOUNT "/bin/umount" +#define UMOUNT_ARGS "umount", "-a" + +#define _PATH_PASSWD "/etc/passwd" +#define _PATH_PTMP "/etc/ptmp" +#define _PATH_PTMPTMP "/etc/ptmptmp" + +#define _PATH_WORDS "/usr/dict/words" +#define _PATH_WORDS_ALT "/usr/dict/web2" diff --git a/disk-utils/Makefile b/disk-utils/Makefile new file mode 100644 index 00000000..854e1ec9 --- /dev/null +++ b/disk-utils/Makefile @@ -0,0 +1,57 @@ +# Makefile -- Makefile for util-linux Linux utilities +# Created: Sat Dec 26 20:09:40 1992 +# Revised: Wed Feb 22 16:09:35 1995 by faith@cs.unc.edu +# Copyright 1992, 1993, 1994, 1995 Rickard E. Faith (faith@cs.unc.edu) +# + +include ../MCONFIG + +# Where to put man pages? + +MAN8= cfdisk.8 fdformat.8 fdisk.8 frag.8 fsck.minix.8 \ + mkfs.8 mkfs.minix.8 mkswap.8 setfdprm.8 + +# Where to put binaries? +# See the "install" rule for the links. . . + +SBIN= cfdisk fdisk fsck.minix mkfs mkfs.minix mkswap + +USRSBIN= frag + +USRBIN= fdformat setfdprm + +# Where to put datebase files? + +ETC= fdprm + +all: $(SBIN) $(USRSBIN) $(USRBIN) + +cfdisk: cfdisk.c llseek.o + $(CC) $(CFLAGS) $(LDFLAGS) $< llseek.o -o $@ -lcurses -ltermcap -lm + +%.o: %.c + $(CC) -c $(CFLAGS) $< -o $@ + +# Rules for everything else + +fdformat: fdformat.o +fdisk: fdisk.o llseek.o +frag: frag.o +fsck.minix: fsck.minix.o +mkfs: mkfs.o +mkfs.minix: mkfs.minix.o +mkswap: mkswap.o +setfdprm: setfdprm.o + +install: all + $(INSTALLDIR) $(SBINDIR) $(USRSBINDIR) $(ETCDIR) + $(INSTALLBIN) $(SBIN) $(SBINDIR) + $(INSTALLBIN) $(USRSBIN) $(USRSBINDIR) + $(INSTALLBIN) $(USRBIN) $(USRBINDIR) + $(INSTALLDAT) $(ETC) $(ETCDIR) + $(INSTALLDIR) $(MAN8DIR) + $(INSTALLMAN) $(MAN8) $(MAN8DIR) + +.PHONY: clean +clean: + -rm -f *.o *~ core $(SBIN) $(USRSBIN) $(USRBIN) diff --git a/disk-utils/README.bootutils-0.1 b/disk-utils/README.bootutils-0.1 new file mode 100644 index 00000000..d87437e8 --- /dev/null +++ b/disk-utils/README.bootutils-0.1 @@ -0,0 +1,104 @@ +bootutils-0.1 + +* ALPHA RELEASE: Use at your own risk! * + +* You MUST have 0.99pl10 or later kernel to make use of all of the + facilities of this package. If you can live without the unmount-root + feature, then 0.99pl9 will work. * + +This is the first release of a set of utilities designed to automate +the management and checking of filesystems at boot time and shutdown. +It supports automatic and safe 'fsck' of all filesystems (including +root) at boot time by booting with root readonly; if the fsck succeeds +then root is remounted read-write and booting can continue. + +Why bother? + +Well, many people like to have a safe and reliable check of all their +filesystems during boot. This is especially true for ext2fs, because +all ext2fs filesystems have a special 'clean' flag which gets set when +the filesystem is cleaned (by e2fsck) or is unmounted cleanly, and +which gets unset when the filesystem is active. e2fsck can sense this +flag, and will skip over filesystems which are clean. + +This means that e2fsck won't bother you with a laborious filesystem +check at each startup, as long as you always shut down cleanly; but it +will check your filesystems automatically if you ever have a crash, +because afterwards the filesystem 'clean' flags will not be set. You +*can* still mount an unclean filesystem, but ext2fs will give you a +warning and will not mark it clean when it gets unmounted. + +One of the problems with automatic fsck'ing is that it is unsafe to +check mounted, active filesystems. The solution is to initially mount +only the root filesystem, and to mount it in readonly mode. In this +situation, fsck can run safely on all filesystems, without the danger +that the kernel might start conflicting with the repairs being done to +the filesystem. + +If any repairs were done, it is unsafe to proceed any further because +the kernel might have cached old information about the filesystems +which has been updated by fsck. However, if the fsck succeeded, then +we can remount the root filesystem in read-write mode and proceed to +mount all of the other filesystems. + +Finally, in order to ensure that filesystems are correctly tidied up +on shutdown, we need to unmount the root at shutdown. This is usually +done automatically; the standard Linux shutdown programs do a 'umount +- -a' command to unmount all mounted filesystems. You MUST have a +0.99pl10 or later kernel for this to work. Many versions of umount +explicitly do not try to unmount the root, since pre-99pl10 kernels +forbade this. The umount included here will unmount even the root +filesystem. (A special kernel trick in pl10 allows this to work by +keeping the filesystem alive in readonly mode after it has been tidied +up.) + +The bootup operation of this package is invoked by the /etc/rc shell +script, an example of which is in mount/etc/rc. It contains the +following important lines: + + # Check the integrity of all filesystems + /bin/fsck -A -a + # If there was a failure, drop into single-user mode. + if [ $? -gt 1 ] ; then + echo fsck failed. Please reboot. + sh + fi + + # Remount the root filesystem in read-write mode + /etc/mount -n -o remount /dev/hda3 / + + # remove /etc/mtab* so that mount will create it with a root entry + /bin/rm -f /etc/mtab* /etc/nologin /etc/utmp + + # mount file systems in fstab (and create an entry for /) + # but not NFS because TCP/IP is not yet configured + /etc/mount -avt nonfs + + +This is the first attempt at a complete package for automated clean +fsck support, so you may well find that you would like a slightly +different behaviour. Please feel free to send me comments, bug +reports and improvements! + + +This package includes three separate items, shamelessly adapted from +other, more or less standard Linux programs. + +* rdev.c: a modified rdev which is extended to allow the + readonly/readwrite status of the kernel image to be altered. Use + rdev -R 1 + to make the kernel mount in readonly mode. This can be overridden + by the use of the 'read-only' or 'read-write' keywords of the most + recent version of LILO. + +* Mount/umount package: This was recently posted to the net, and + implements the '-o remount' mount option which allows filesystems to + be remounted. Unlike the previous post, the version included here + also attempts to unmount the root filesystem on 'umount -a'. I have + also tried to clean up the man-pages. + +* fsck package: David Engel's fsck front-end. Read the README for it. + This package implements the 'fsck -A' command which will check all + filesystems in /etc/fstab automatically. + +Stephen Tweedie diff --git a/disk-utils/README.cfdisk b/disk-utils/README.cfdisk new file mode 100644 index 00000000..5241ad13 --- /dev/null +++ b/disk-utils/README.cfdisk @@ -0,0 +1,45 @@ +Announcing the new curses based fdisk program... cfdisk + +cfdisk is a curses based disk drive partitioning program that can +create partitions for a wide variety of operating systems including +Linux, MS-DOS and OS/2. cfdisk was inspired by the fdisk program, by +A. V. Le Blanc (LeBlanc@mcc.ac.uk). I hope that this program will be +useful to both new and old Linux users, and I hope it will make the +installation process easier. + + + **** WARNING **** +If you write a bad partition table to disk, it may destroy data and +partitions. + + +You can FTP cfdisk from ftp.cs.unc.edu in the /pub/martin/linux +directory. + +I would also like comments (good and bad) on the user interface, logic +and ease of use. If you have any suggestions for improvements, I +would be happy to hear them. + +My e-mail address is martin@cs.unc.edu. + +------------------------------------------------------------------- + + Copyright (C) 1994 Kevin E. Martin (martin@cs.unc.edu) + +cfdisk is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2 of the License, or (at your +option) any later version. + +cfdisk 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 cfdisk; if not, write to the Free Software Foundation, +Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +___ +Kevin E. Martin University of North Carolina at Chapel Hill +martin@cs.unc.edu Department of Computer Science diff --git a/disk-utils/README.fdisk b/disk-utils/README.fdisk new file mode 100644 index 00000000..9e64508e --- /dev/null +++ b/disk-utils/README.fdisk @@ -0,0 +1,577 @@ +`fdisk': the Linux partition table editor +========================================= + +`fdisk' is the Linux partition table editor. In this section we +examine this utility and try to describe it thoroughly enough so that +anyone can use it. + +* Contents: + +* Disks and how they are described. +* Dividing up your disk. +* The `fdisk' command. +* Deleting and adding partitions. +* Active flags and system types. +* Extra commands for experts. +* Warnings for `fdisk' users. + + +Disks and how they are described +-------------------------------- + +A typical disk consists physically of one or more circular objects +called "platters", which rotate about a central axis. Devices called +"heads" move to specified places on the disk surface to read or write +information. There is usually one head on each side of every platter, +and all these heads are attached to a comb-like controller arm which +moves all of them at the same time, either closer to the centre of the +disk, or closer to the outer edge. + +Suppose the arm is in one position, putting an area of the disk +surface within reach of one or another of the heads. This total area, +everything that is accessible without moving the arm, is called a +"cylinder". (A cylinder is a barrel-shaped cross section of a disk, +consisting of a circular strip from each side of each platter.) The +part of a cylinder that one head can read or write without moving is +called a "track". + +Each track is divided into several pie-shaped slices called +"sectors", which are the smallest parts of the disk which can be read +or written at a time. The sectors on one disk are usually all the same +size. + +In fact, there are not always two heads to every platter, there are +some disks which do not have the same amount of data in every cylinder, +and there may be disks which do not have the same amount of data in +every sector. These features are usually hidden on PCs by the +controller card or the BIOS, which map the physical geometry of a disk +onto a logical geometry, which is what is actually used to access the +disk. + +The numbers which describe the "geometry" of a disk are + + 1. The number of cylinders it contains. + + 2. The number of tracks per cylinder, which is the number of heads. + + 3. The number of sectors per track. + + 4. The number of bytes per sector. + +These numbers vary from disk to disk, but a typical PC disk might +have about 1000 cylinders, half a dozen heads, and 15 or 20 sectors per +track, with each sector containing 512 bytes or characters; such a disk +contains 40 to 60 megabytes of data. A "double density" floppy disk +contains 40 cylinders, with 2 heads (2 tracks per cylinder), and with 9 +sectors per track; such a disk contains 360 kilobytes, or 360 * 1024 +characters. A "high density" 3.5 inch floppy contains 80 cylinders, +with 2 heads and 18 sectors per track, or 1.44 megabytes, or 1440 * +1024 characters. + +The exact size of a track or cylinder in bytes varies from one disk +to another. This `fdisk' for Linux deals mainly with cylinders, since +this is the best unit to use when allocating space for partitions. It +reports partition sizes in "blocks" of 1024 bytes, or 2 sectors, since +`mkswap' and the various `mkfs' programs require this number. A block +is the smallest amount of space which can be set aside for a file in +the current file systems. + +An operating system, such as Linux or DOS or OS/2, may use a disk in +any way that it wishes, but if two operating systems share the same +disk, they must agree on who owns what, or else one will interfere with +the other (that is, by damaging the other's files). A "partition" is a +section of a hard disk which is handled as a unit by all operating +systems which can access the disk. The standard way to define +partitions (for the moment) is the "partition table", a list of +information which is stored in parts of the disk that don't belong to +any of the systems using the disk. The beginning of the partition +table is stored in the disk's primary boot sector, and the rest is +stored in a chain of sectors scattered throughout the disk. + +The first sector on the disk is called the "primary boot block" or +"primary boot sector" because (1) it comes first, before other, similar +sectors; (2) it tells where the other, similar sectors are found, so +that it is logically `prior' to them; and (3) it usually contains code +which is executed when the system boots up. This sector contains a +table describing at most four partitions. These areas are called +"primary partitions". + +The partition table in the primary boot sector may also describe at +most one "extended partition". This is a large area of the disk, +usually containing all the space which is not in any primary partition. +Within this space we can set aside other areas which are called +"logical partitions", because they look almost exactly like primary +partitions. In fact, the main difference between them is that we can +boot from primary partitions, while we cannot boot from logical +partitions. This happens because the address of a primary partition is +in a fixed place, whereas the address of a secondary partition is not, +so we require a more complicated process to discover it, one which is +too difficult for most primary boot programs. + + +Dividing up your disk +--------------------- + +It is a good idea to plan ahead before you start creating partitions +on your disk. If you set aside a partition for some purpose, it is not +easy to change its size: you must all the data from the partition, +whether to floppies, to another partition, to another hard disk, or +somewhere else; then you must edit the table which describes this +partition, so changing its size; then you must reboot and initialise +the new partition, formatting it, for example, under DOS, or running +`mkfs' under Linux; finally you can copy all the data back. It is +possible, if you have several partitions, to copy data back and forth +between them while you change their sizes, but this is a bit risky and +time consuming. It is better to plan ahead what you will need, since +it is hard to change it afterwards. + +Many people with large disks and recent versions of DOS have their +entire file system on one large partition. They usually ask, `Isn't +there any way I can reformat my disk without copying everything off?' +There is no way to do it using standard DOS utilities, and there is no +truly safe way to do it using commercial software, because, if you make +a mistake, you will lose the entire contents of your disk. If you are +going to back up your disk anyway, you might as well copy the data back +safely. The Linux FAQ contains references to tools and procedures +which will allow you to do this, if you dare. + +DOS and Linux both allow you to access several partitions on a +single disk; on DOS these are treated as if they were separate disks or +drives, and under Linux they are treated as different "devices". + +You can have up to 64 partitions on a single IDE disk, or up to 16 +partitions on a single SCSI disk, at least as far as Linux is +concerned; in practice you will rarely want so many. The maximum size +of a Linux file system on a single partition depends on the type of +file system you use. Minix file systems are limited to 64 megabytes. +You may have all of your Linux files in a single partition, or you may +have two, three, or more Linux file systems. Similarly you may have +one or more DOS partitions. If you have several small partitions, you +run much less risk of losing all your files if your disk gets +corrupted. On the other hand, you may run out of space on a small +partition more easily. + +Under DOS, you must refer to each partition by a separate drive +letter, but all partitions are automatically accessible. Under Linux +only the root partition is automatically accessible, but once we mount +another partition, it is indistinguishable from the rest of the file +system. Disks are usually mounted by a command in one of the system +startup files, `/etc/rc', so you need not worry about having to do it +yourself whenever you boot the system. But even ordinary users may +be allowed to mount removable hard disks and floppy disks. + +Linux requires at least one partition, which is the `root' of the +file system. You may prefer to have a separate partition for `/usr', +which contains most of the executable files, or for `/home', which +contains most of your private files. You may also wish to set aside a +partition to use for swap space, depending on the amount of memory your +PC has. You will certainly need swap space if you have less than 4 Mb +of RAM and wish to compile anything substantial. You can reserve swap +space in a file, but you need a partition big enough to hold it, and +this will probably be less efficient than having a partition devoted to +swap. + +The disk space you need for Linux is discussed in README.prepare. + +Are you going to boot Linux from the hard disk, or will you boot +from a floppy? Some boot programs place severe restrictions on where +the boot partition can be. LILO is more relaxed about this, but does +require either the Master Boot Record on your first hard disk, or the +boot record on one of the first four partitions on your first hard disk. + +If you have an extended partition with logical partitions in it, you +can have only three primary partitions containing data. + + +The `fdisk' command +------------------- + +Every operating system, whether DOS, OS/2, or Linux, should provide +its own utility for editing hard disk partition tables. At least four +of these utilities have been called `fdisk', for `Fixed DISK setup +program', where `fixed' means `not removable'. I believe the first PC +program named `fdisk' came from Microsoft in about 1985; before that +time disks were too small to divide into separate sections. + +Every operating system has its own peculiarities. Normally you +should set up a partition for the use of one operating system by using +its own `fdisk' program. Do not use the Linux `fdisk' to create +partitions for DOS or for any system other than Linux; otherwise you +may have problems. + +An `fdisk' program performs two functions: it reports how the disk is +configured, and it changes that configuration by adding or deleting +partitions. Most `fdisk' programs can also change other information in +partition tables. + +This `fdisk' for Linux operates on one hard disk at a time. If you +give the command + + fdisk + +it reports on, and is able to change, `/dev/hda', the first hard +disk. (If you have no `/dev/hda', `fdisk' uses `/dev/sda' as the +default device.) To look at or change the second hard disk, `/dev/hdb', +give the command + + fdisk /dev/hdb + +To look at or change the first SCSI disk, give the command + + fdisk /dev/sda + +There are some special forms of the `fdisk' command. One of them, +suggested by Jim Winstead, simply lists all partitions on all available +disks: + + fdisk -l (where `l' is a letter, not the digit `1') + +The option `-v' is provided to list the current version of the +`fdisk' command. Finally, there is an option `-s' which is not really +intended for interactive use. It causes fdisk to print the size of a +partition in blocks of 1024 bytes as follows: + + fdisk -s /dev/hda7 + 39934 + +Because this is intended to be used by `mkfs' and `mkswap' programs, +it does not return the size of extended partitions or of partitions +whose system type code is less than 10 (hexadecimal a). If you start +`fdisk' without using one of these special options, it responds by +asking for a command: + + Command (m for help): _ + +Each `fdisk' command consists of a single letter, which must be +followed by before it is obeyed. Upper and lower case are not +distinguished. Anything you type after the first character is ignored. +Give the command `m', and you should see this menu: + Command action + a toggle a bootable flag + d delete a partition + l list known partition types + m print this menu + n add a new partition + p print the partition table + q quit without saving changes + t change a partition's system id + u change display/entry units + v verify the partition table + w write table to disk and exit + x extra functionality (experts only) + + Command (m for help): _ + +The simplest commands are Print, Verify, and Quit. On a small disk, the +Print command might produce a display like this one: + + Disk /dev/hda: 5 heads, 17 sectors, 977 cylinders + Units = cylinders of 85 * 512 bytes + + Device Boot Begin Start End Blocks Id System + /dev/hda1 * 1 1 236 10021+ 1 DOS 12-bit FAT + /dev/hda2 837 837 977 5992+ 5 Extended + /dev/hda3 * 237 237 836 25500 83 Linux native + /dev/hda5 837 837 936 4249+ 82 Linux swap + /dev/hda6 942 942 977 1522 1 DOS 12-bit FAT + +There are 5 partitions reported; `/dev/hda4' does not appear because +it is not allocated. Partitions 1 and 3 are flagged as bootable. The +size of each partition is reported in 1 kilobyte blocks; hence the +primary Linux partition, partition 3, is 25 1/2 megabytes in size. The +`+' after three of the sizes warns that these partitions contain an odd +number of sectors: Linux normally allocates filespace in 1 kilobyte +blocks, so the extra sector in partition 5 is wasted. Id numbers are +reported in hexadecimal and explained in English. + +The display/entry units may be either cylinders or sectors. The +default is cylinders, but changing the units makes the print command +display the following table for the system reported above: + + Disk /dev/hda: 5 heads, 17 sectors, 977 cylinders + Units = sectors of 1 * 512 bytes + + Device Boot Begin Start End Blocks Id System + /dev/hda1 * 1 17 20059 10021+ 1 DOS 12-bit FAT + /dev/hda2 71060 71060 83044 5992+ 5 Extended + /dev/hda3 * 20060 20060 71059 25500 83 Linux native + /dev/hda5 71061 71061 79559 4249+ 82 Linux swap + /dev/hda6 79985 80001 83044 1522 1 DOS 12-bit FAT + +The start of data in both DOS partitions is 16 sectors after the +beginning of the partition: this is one reason why you should use DOS's +own `FDISK' to create DOS partitions. Changing the units to sectors +also affects the way in which the new partition command asks for the +beginning and end of a new partition. + +*Warning*: it is dangerous to create a new partition when the +display/entry units are sectors. + +The Verify command is useful because + + 1. It warns you if anything is wrong. *Always* give a Verify command + before writing any changes to disk. + + 2. It reports how many unallocated sectors there are on the disk. + +The Quit command is also useful. `fdisk' does not actually change +any data on your disk unless you give a Write command. If you are +unhappy about any changes you may have made, give the Quit command, and +your disk will remain as it was before you ran `fdisk'. You can also +interrupt `fdisk' with `CTRL-C'. + + +Deleting and adding partitions +------------------------------ + +Deleting a partition is simple. Give the Delete command by typing +`d'. `fdisk' asks: + + Partition number (1-6): _ + +Once you get this far, you must either delete a partition or +interrupt the program with `CTRL-C' (or whatever your current interrupt +character is). Note: + + 1. You may delete a nonexistent partition. You will get a warning + message. + + 2. You may delete an extended partition. This has the side effect of + deleting all partitions greater than or equal to 5. + + 3. You may delete a logical partition. In that case, all partitions + above it are renumbered at once. For example, if you delete + partition 5, then partition 6 becomes known as partition 5, and + partition 7 as partition 6. + +Adding a partition is just a bit more complicated. Give the New +command by typing `n'. `fdisk' allows you to + + 1. Create a primary partition, if there is a free slot in the primary + partition table. + + 2. Create an extended partition if there is a free slot in the + primary partition table, and if there is no extended partition. + + 3. Create a logical partition if an extended partition exists. + +If more than one of these actions is possible, you will be asked to +select Primary, Extended, or Logical, depending on what is currently +permissible. Before you create a primary or an extended partition, you +are asked what slot it is to have in the table (1-4). + +You may not add a primary or an extended partition if the selected +slot in the primary partition table is already occupied: in that case +you simply return to the main menu. You are not allowed to add a new +primary partition unless there are sectors available outside the +extended partition. You are not allowed to add a new logical partition +unless there are sectors available inside the extended partition. + +If space is available, you are prompted for the first cylinder: + + First sector (237-977): _ + +The limits are the lowest and the highest cylinders in which sectors +are available in the appropriate part of the disk. Not all numbers in +this range are necessarily available: they may fall inside an existing +partition. If you select a cylinder which is already in use, you are +told off and prompted again for the first cylinder. After selecting the +first cylinder, you are prompted again: + + Last cylinder or +size or +sizeM or +sizeK (237-836): _ + +The limits are the cylinder you have chosen as the first cylinder, +and the highest cylinder which contains a legitimate upper boundary for +the new partition. In other words, all numbers in the given range are +legitimate, unlike those in the first range of cylinders. You may also +specify the size of a partition in megabytes, kilobytes, or in the +current units (cylinders or sectors). A plus sign `+' indicates that +your answer is a size rather than a boundary, and the suffix `m' or `k' +(upper or lower case) indicates that the size is not given in units of +sectors or cyliners, but in megabytes or kilobytes respectively. Thus +possible answers to the last cylinder request above are + +700 + Make cylinder 700 the last cylinder in the partition. + ++300 + Make cylinder 537 the last cylinder in the partition. + ++15m + Make the partition at least 15 megabytes in size. + ++12500k + Make the partition at least 12,500 kilobytes in size. + +If you specify a size which is too large or an end which is out of +range, the prompt is simply repeated. + +Adding or deleting partitions has no effect unless you subsequently +give the Write command. Please remember to give the Verify command +first, just before giving the Write command: this is a safety +precaution. After giving the Write command, you will see this message: + + The partition table has been altered! + Calling ioctl() to re-read partition table. + Syncing disks. + +If there are no further messages, the kernel has successfully copied +the information from the partition table into its own internal table. +But sometimes you will see a message like this one: + + Re-read table failed with error 16: Device or resource busy. + Reboot your system to ensure the partition table is updated. + +In this case, depending on what you have changed in the partition +table, it may be dangerous to continue working without rebooting, +since you may lose or corrupt your data. + + +Here are some important things to note: + + 1. Before you reboot, you *may* run `fdisk' again, either to manage + another disk, or to make additional changes to the same disk, or + just to check that the changes have been made as you expected. + This is true even after you receive the message warning you to + reboot. + + 2. It is not a good idea to run any of the programs `mkfs', `mkswap', + `mount', or `swapon' if you have received the warning message but + have not rebooted. In this case it is dangerous to run any program, + but these in particular may cause serious damage to the data on your + disk, including the partition tables themselves. + + +Active flags and system types +----------------------------- + +The active flag is a bit in the partition table entry which marks a +partition as bootable. This is important to some primary boot sector +programs, which will not boot from an unflagged partition. Other such +programs do not allow more than one partition to be flagged. Some, +like LILO, ignore the flags completely. I prefer to flag all bootable +partitions as active so that they stand out on the menu which `fdisk' +lists. Fdisk prints a star after the name of a partition's device file +if its active flag is set. + +The Active command changes, or toggles, a partition's active flag. +Give the Active command, and select a partition by number. If it was +marked inactive, it will be flagged as active; if it was flagged as +active, it will be marked inactive. You may set the active flag on an +extended or logical partition, though the meaning of such a flag is by +no means clear. This can be used to install LILO as a secondary boot +loader to boot a Linux which lives on a second hard disk. + +The Type command changes the ID number which describes what type a +partition is. `fdisk' currently recognises 30 system IDs, in the sense +that it prints a string for each of them, but it allows you to change +any system ID to any other, with the following exceptions: you may not +change any partition to or from the type Extended, and you may not +change a partition whose type is Empty (0) to any other type. You may, +however, change the type of any data partition to 0, which is +equivalent to deleting it. + +The new system ID or type code is a hexadecimal number. There are +two ways of listing the numbers which `fdisk' recognises: use the List +command, which prints the list, or use the Type command, which, when it +prompts you for the code, says + + Hex code (type L to list codes): _ + +where the upper case `L' is used for clarity. The codes printed are: +Some of these numbers are a trifle uncertain. By default `fdisk' uses +a type of 83. It used to use 81, the type code used by the MINIX +`fdisk'. It seemed prudent to change the default since (a) many Linux +`minix' file systems are no longer compatible with MINIX, (b) the ext2 +file system, a native Linux file system, is fairly stable, as is the +Xia file system, and (c) the number 81 causes problems with DR-DOS. +Linux does not usually care what values you use for type codes, but +other systems, in particular DOS, OS/2, and DR-DOS, may. + +The value of 82 for Linux swap partitions is my own invention, and +is intended to give some recognisable distinction to the partitions +when the values are displayed in hexadecimal. + +New active flags and new system type codes are not written to the +disk until you exit from `fdisk' with the Write command, as described +above, in the section on deleting and adding partitions. + + +Extra commands for experts +-------------------------- + +The eXtra command `x' puts `fdisk' into `expert' mode, in which a +slightly different set of commands is available. The Active, Delete, +List, New, Type, Verify, and `eXpert' commands are not available in +expert mode. The commands Write and Quit are available as in ordinary +mode, the Print command is available, but produces output in a slightly +different format, and of course the Menu command prints the expert +menu. There are several new commands. + + 1. The Return command brings you back to the main menu. + + 2. The Extended command prints the list of table entries which point + to other tables. Ordinary users do not need this information. + The data is shown as it is stored. The same format is used for + the expert Print command. + + 3. The dangerous Begin command allows you to move the start of data + in a partition away from its beginning. Other systems create + partitions with this format, and it is sometimes useful to be able + to reproduce it. + + 4. The slightly dangerous Cylinders command allows you to change the + available number of cylinders. For SCSI disk owners, note that we + require not the actual number of physical cylinders, but the + number of logical cylinders used by DOS and other operating + systems. + + 5. The extremely dangerous Heads and Sectors commands allow you to + change the number of heads and sectors. It should not be + necessary to use these commands unless you have a SCSI disk, whose + geometry Linux is not always able to determine. SCSI disk owners + note that we need not the actual number of heads or of sectors per + track, but the number believed to exist by DOS and other operating + systems. *Warning*: If you set either of these numbers to a bad + value, you may lose all data on your disk. + +Always, after giving any of the commands Begin, Cylinder, Heads, or +Sectors, you should Return to the main menu and give the Verify command. + + +Warnings for `fdisk' users +-------------------------- + +In general, you should not use this `fdisk' program to create +partitions for other operating systems, only for Linux. Nor should you +use `fdisk' commands from other operating systems do create partitions +for Linux. + +DR-DOS 5.0 and 6.0 are reported to have difficulties with partition +ID codes of 80 or more. The Linux `fdisk' used to set the system type +of new partitions to hexadecimal 81. DR-DOS seems to confuse this with +hexadecimal 1, a DOS code. The values 82 for swap and 83 for file +systems should not cause problems with DR-DOS. If they do, you may use +the `fdisk' command `t' to change the system code of any Linux +partitions to some number less than hexadecimal 80; I suggest 42 and 43 +for the moment. + +Partitioning a hard disk may destroy data which is on that disk if you +are not careful. Go slowly, write down a description of the partition +tables before you changed them, and always verify before you write. + +Most operating systems and utilities expect that all partitions begin and +end at cylinder boundaries. This version of `fdisk' does so by default, +but you can use it to create partitions which begin or end anywhere. +This does not normally affect Linux, but it is very dangerous, as other +operating systems (including DOS) may try to `correct' the partition +boundaries. + +It is dangerous to create a new partition when the display/entry +units are sectors. + +The Verify command warns you if anything is wrong. *Always* give a +Verify command before writing any changes to disk. + +If you set the disk geometry (tracks per cylinder, or sectors per +track) to an incorrect value, you may lose all data on your disk. diff --git a/disk-utils/cfdisk.8 b/disk-utils/cfdisk.8 new file mode 100644 index 00000000..cb23149f --- /dev/null +++ b/disk-utils/cfdisk.8 @@ -0,0 +1,407 @@ +.\" cfdisk.8 -- man page for cfdisk +.\" Copyright 1994 Kevin E. Martin (martin@cs.unc.edu) +.\" +.\" Permission is granted to make and distribute verbatim copies of this +.\" manual provided the copyright notice and this permission notice are +.\" preserved on all copies. +.\" +.\" Permission is granted to copy and distribute modified versions of this +.\" manual under the conditions for verbatim copying, provided that the +.\" entire resulting derived work is distributed under the terms of a +.\" permission notice identical to this one. +.\" +.\" " for hilit mode +.TH CFDISK 8 "25 April 1994" "The BOGUS Linux Release" "Linux Programmer's Manual" +.SH NAME +cfdisk \- Curses based disk partition table manipulator for Linux +.SH SYNOPSIS +.BI "cfdisk [ \-avz ] [ \-c " cylinders " ] [ \-h " heads " ]" +.BI "[ \-s " sectors-per-track " ] [ -P " opt " ] [ " device " ]" +.SH DESCRIPTION +.B cfdisk +is a curses based program for partitioning a hard disk drive. The +.I device +can be any one of the following: +.sp +.nf +.RS +/dev/hda [default] +/dev/hdb +/dev/sda +/dev/sdb +/dev/sdc +/dev/sdd +.RE +.fi + +.B cfdisk +first tries to read the geometry of the hard disk. If it fails, an +error message is displayed and +.B cfdisk +exits. This should only happen when partitioning a SCSI drive on an +adapter without a BIOS. To correct this problem, you can set the +.IR cylinders ", " heads " and " sectors-per-track +on the command line. Next, +.B cfdisk +tries to read the current partition table from the disk drive. If it +is unable to figure out the partition table, an error is displayed and +the program will exit. This might also be caused by incorrect +geometry information, and can be overridden on the command line. +Another way around this problem is with the +.B \-z +option. This will ignore the partition table on the disk. + +The main display is composed of four sections, from top to bottom: the +header, the partitions, the command line and a warning line. The +header contains the program name and version number followed by the +disk drive and its geometry. The partitions section always displays +the current partition table. The command line is the place where +commands and text are entered. The available commands are usually +displayed in brackets. The warning line is usually empty except when +there is important information to be displayed. The current partition +is highlighted with reverse video (or an arrow if the +.B \-a +option is given). All partition specific commands apply to the +current partition. + +The format of the partition table in the partitions section is, from +left to right: Name, Flags, Partition Type, Filesystem Type and Size. +The name is the partition device name. The flags can be +.IR Boot , +which designates a bootable partition or +.IR NC , +which stands for "Not Compatible with DOS or OS/2". DOS, OS/2 and +possibly other operating systems require the first sector of the first +partition on the disk and all logical partitions to begin on the +second head. This wastes the second through the last sector of the +first track of the first head (the first sector is taken by the +partition table itself). +.B cfdisk +allows you to recover these "lost" sectors with the maximize command +.RB ( m ). +.I Note: +.BR fdisk (8) +and some early versions of DOS create all partitions with the number +of sectors already maximized. For more information, see the maximize +command below. The partition type can be one of +.IR Primary " or " Logical . +For unallocated space on the drive, the partition type can also be +.IR Pri/Log , +or empty (if the space is unusable). The filesystem type section +displays the name of the filesystem used on the partition, if known. +If it is unknown, then +.I Unknown +and the hex value of the filesystem type are displayed. A special +case occurs when there are sections of the disk drive that cannot be +used (because all of the primary partitions are used). When this is +detected, the filesystem type is displayed as +.IR Unusable . +The size field displays the size of the partition in megabytes (by +default). It can also display the size in sectors and cylinders (see +the change units command below). If an asterisks +.RB ( * ) +appears after the size, this means that the partition is not aligned +on cylinder boundaries. +.SH "DOS 6.x WARNING" + +The DOS 6.x FORMAT command looks for some information in the first +sector of the data area of the partition, and treats this information +as more reliable than the information in the partition table. DOS +FORMAT expects DOS FDISK to clear the first 512 bytes of the data area +of a partition whenever a size change occurs. DOS FORMAT will look at +this extra information even if the /U flag is given -- we consider +this a bug in DOS FORMAT and DOS FDISK. + +The bottom line is that if you use cfdisk or fdisk to change the size of a +DOS partition table entry, then you must also use +.B dd +to zero the first 512 bytes of that partition before using DOS FORMAT to +format the partition. For example, if you were using cfdisk to make a DOS +partition table entry for /dev/hda1, then (after exiting fdisk or cfdisk +and rebooting Linux so that the partition table information is valid) you +would use the command "dd if=/dev/zero of=/dev/hda1 bs=512 count=1" to zero +the first 512 bytes of the partition. +.B BE EXTREMELY CAREFUL +if you use the +.B dd +command, since a small typo can make all of the data on your disk useless. + +.B BE EXTREMELY CAREFUL +if you use the +.B dd +command, since a small typo can make all of the data on your disk useless. + +For best resutls, you should always use an OS-specific partition table +program. For example, you should make DOS partitions with the DOS FDISK +program and Linux partitions with the Linux fdisk or Linux cfdisk program. + +.SH COMMANDS +.B cfdisk +commands can be entered by pressing the desired key (pressing +.I Enter +after the command is not necessary). Here is a list of the available +commands: +.TP +.B b +Toggle bootable flag of the current partition. This allows you to +select which primary partition is bootable on the drive. +.TP +.B d +Delete the current partition. This will convert the current partition +into free space and merge it with any free space immediately +surrounding the current partition. A partition already marked as free +space or marked as unusable cannot be deleted. +.TP +.B g +Change the disk geometry (cylinders, heads, or sectors-per-track). +.B WARNING: +This option should only be used by people who know what they are +doing. A command line option is also available to change the disk +geometry. While at the change disk geometry command line, you can +choose to change cylinders +.RB ( c ), +heads +.RB ( h ), +and sectors per track +.RB ( s ). +The default value will be printed at the prompt which you can accept +by simply pressing the +.I Enter +key, or you can exit without changes by pressing the +.I ESC +key. If you want to change the default value, simply enter the +desired value and press +.IR Enter . +The altered disk parameter values do not take effect until you return +the main menu (by pressing +.IR Enter " or " ESC +at the change disk geometry command line. If you change the geometry +such that the disk appears larger, the extra sectors are added at the +end of the disk as free space. If the disk appears smaller, the +partitions that are beyond the new last sector are deleted and the +last partition on the drive (or the free space at the end of the +drive) is made to end at the new last sector. +.TP +.B h +Print the help screen. +.TP +.B m +Maximize disk usage of the current partition. This command will +recover the the unused space between the partition table and the +beginning of the partition, but at the cost of making the partition +incompatible with DOS, OS/2 and possibly other operating systems. +This option will toggle between maximal disk usage and DOS, OS/2, +etc. compatible disk usage. The default when creating a partition is +to create DOS, OS/2, etc. compatible partitions. +.TP +.B n +Create new partition from free space. If the partition type is +.IR Primary " or " Logical , +a partition of that type will be created, but if the partition type is +.IR Pri/Log , +you will be prompted for the type you want to create. Be aware that +(1) there are only four slots available for primary partitions and (2) +since there can be only one extended partition, which contains all of +the logical drives, all of the logical drives must be contiguous (with +no intervening primary partition). +.B cfdisk +next prompts you for the size of the partition you want to create. +The default size, equal to the entire free space of the current +partition, is display in megabytes. You can either press the +.I Enter +key to accept the default size or enter a different size at the +prompt. +.B cfdisk +accepts size entries in megabytes +.RB ( M ) +[default], kilobytes +.RB ( K ), +cylinders +.RB ( C ) +and sectors +.RB ( S ) +by entering the number immediately followed by one of +.RB ( M ", " K ", " C " or " S ). +If the partition fills the free space available, the partition is +created and you are returned to the main command line. Otherwise, the +partition can be created at the beginning or the end of the free +space, and +.B cfdisk +will ask you to choose where to place the partition. After the +partition is created, +.B cfdisk +automatically adjusts the other partition's partition types if all of +the primary partitions are used. +.TP +.B p +Print the partition table to the screen or to a file. There are +several different formats for the partition that you can choose from: +.sp +.RS +.TP +.B r +Raw data format (exactly what would be written to disk) +.TP +.B s +Partition table in sector order format +.TP +.B t +Partition table in raw format +.RE + +.RS +The +.I raw data format +will print the sectors that would be written to disk if a +.BR w rite +command is selected. First, the primary partition table is printed, +followed by the partition tables associated with each logical +partition. The data is printed in hex byte by byte with 16 bytes per +line. + +The +.I partition table in sector order format +will print the partition table ordered by sector number. The fields, +from left to right, are the number of the partition, the partition +type, the first sector, the last sector, the offset from the first +sector of the partition to the start of the data, the length of the +partition, the filesystem type (with the hex value in parenthesis), +and the flags (with the hex value in parenthesis). In addition to the +primary and logical partitions, free and unusable space is printed and +the extended partition is printed before the first logical partition. + +If a partition does not start or end on a cylinder boundary or if the +partition length is not divisible by the cylinder size, an asterisks +.RB ( * ) +is printed after the non-aligned sector number/count. This usually +indicates that a partition was created by an operating system that +either does not align partitions to cylinder boundaries or that used +different disk geometry information. If you know the disk geometry of +the other operating system, you could enter the geometry information +with the change geometry command +.RB ( g ). + +For the first partition on the disk and for all logical partitions, if +the offset from the beginning of the partition is not equal to the +number of sectors per track (i.e., the data does not start on the +first head), a number sign +.RB ( # ) +is printed after the offset. For the remaining partitions, if the +offset is not zero, a number sign will be printed after the offset. +This corresponds to the +.I NC +flag in the partitions section of the main display. + +The +.I partition table in raw format +will print the partition table ordered by partition number. It will +leave out all free and unusable space. The fields, from left to +right, are the number of the partition, the flags (in hex), the +starting head, sector and cylinder, the filesystem ID (in hex), the +ending head, sector and cylinder, the starting sector in the partition +and the number of sectors in the partition. The information in this +table can be directly translated to the +.IR "raw data format" . + +The partition table entries only have 10 bits available to represent +the starting and ending cylinders. Thus, when the absolute starting +(ending) sector number is on a cylinder greater than 1023, the maximal +values for starting (ending) head, sector and cylinder are printed. +This is the method used by OS/2, and thus fixes the problems +associated with OS/2's fdisk rewriting the partition table when it is +not in this format. Since Linux and OS/2 use absolute sector counts, +the values in the starting and ending head, sector and cylinder are +not used. +.RE +.TP +.B q +Quit program. This will exit the program without writing any data to +disk. +.TP +.B t +Change the filesystem type. By default, new partitions are created as +.I Linux +partitions, but since +.B cfdisk +can create partitions for other operating systems, change partition +type allows you to enter the hex value of the filesystem you desire. +A list of the know filesystem types is displayed. You can type in the +filesystem type at the prompt or accept the default filesystem type +.RI [ Linux ]. +.TP +.B u +Change units of the partition size display. It will rotate through +megabytes, sectors and cylinders. +.TP +.B W +Write partition table to disk (must enter an upper case W). Since +this might destroy data on the disk, you must either confirm or deny +the write by entering `yes' or `no'. If you enter `yes', +.B cfdisk +will write the partition table to disk and the tell the kernel to +re-read the partition table from the disk. The re-reading of the +partition table works is most cases, but I have seen it fail. Don't +panic. It will be correct after you reboot the system. In all cases, +I still recommend rebooting the system--just to be safe. +.TP +.I Up Arrow +.TP +.I Down Arrow +Move cursor to the previous or next partition. If there are more +partitions than can be displayed on a screen, you can display the next +(previous) set of partitions by moving down (up) at the last (first) +partition displayed on the screen. +.TP +.I CTRL-L +Redraws the screen. In case something goes wrong and you cannot read +anything, you can refresh the screen from the main command line. +.TP +.B ? +Print the help screen. + +.RE +All of the commands can be entered with either upper or lower case +letters (except for +.BR W rites). +When in a sub-menu or at a prompt to enter a filename, you can hit the +.I ESC +key to return to the main command line. +.SH OPTIONS +.TP +.B \-a +Use an arrow cursor instead of reverse video for highlighting the +current partition. +.TP +.B \-v +Print the version number and copyright. +.TP +.B \-z +Start with zeroed partition table. This option is useful when you +want to repartition your entire disk. +.I Note: +this option does not zero the partition table on the disk; rather, it +simply starts the program without reading the existing partition +table. +.TP +.BI \-c " cylinders" +.TP +.BI \-h " heads" +.TP +.BI \-s " sectors-per-track" +Override the number of cylinders, heads and sectors per track read +from the BIOS. If your BIOS or adapter does not supply this +information or if it supplies incorrect information, use these options +to set the disk geometry values. +.TP +.BI \-P " opt" +Prints the partition table in specified formats. +.I opt +can be one or more of "r", "s" or "t". See the +.BR p rint +command (above) for more information on the print formats. +.SH "SEE ALSO" +fdisk(8) +.SH BUGS +The current version does not support multiple disks (future addition). +.SH AUTHOR +Kevin E. Martin (martin@cs.unc.edu) diff --git a/disk-utils/cfdisk.c b/disk-utils/cfdisk.c new file mode 100644 index 00000000..6c42f9c0 --- /dev/null +++ b/disk-utils/cfdisk.c @@ -0,0 +1,2330 @@ +/**************************************************************************** + * + * CFDISK + * + * cfdisk is a curses based disk drive partitioning program that can + * create partitions for a wide variety of operating systems including + * Linux, MS-DOS and OS/2. + * + * cfdisk was inspired by the fdisk program, by A. V. Le Blanc + * (LeBlanc@mcc.ac.uk). + * + * Copyright (C) 1994 Kevin E. Martin (martin@cs.unc.edu) + * + * cfdisk is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * cfdisk 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 cfdisk; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Created: Fri Jan 28 22:46:58 1994, martin@cs.unc.edu + * >2GB patches: Sat Feb 11 09:08:10 1995, faith@cs.unc.edu + * Prettier menus: Sat Feb 11 09:08:25 1995, Janne Kukonlehto + * + * + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for BLKRRPART */ + +#if defined(__GNUC__) || defined(HAS_LONG_LONG) +typedef long long ext2_loff_t; +#else +typedef long ext2_loff_t; +#endif + +extern ext2_loff_t ext2_llseek(unsigned int fd, + ext2_loff_t offset, + unsigned int origin); + + +#define VERSION "0.8a BETA (>2GB)" + +#define DEFAULT_DEVICE "/dev/hda" +#define ALTERNATE_DEVICE "/dev/sda" + +#define LINE_LENGTH 80 +#define MAXIMUM_PARTS 60 + +#define SECTOR_SIZE 512 + +#define MAX_CYLINDERS 65535 +#define MAX_HEADS 255 +#define MAX_SECTORS 63 + +#define ACTIVE_FLAG 0x80 +#define PART_TABLE_FLAG 0xAA55 + +#define UNUSABLE -1 +#define FREE_SPACE 0x00 +#define EXTENDED 0x05 +#define LINUX_MINIX 0x81 +#define LINUX_SWAP 0x82 +#define LINUX 0x83 + +#define ADD_EXISTS "This partition is already in use" +#define ADD_UNUSABLE "This partition is unusable" +#define DEL_EMPTY "Cannot delete an empty partition" +#define ID_EMPTY "Cannot change FS Type to empty" +#define ID_EXT "Cannot change FS Type to extended" +#define NEED_EXT "No room to create the extended partition" +#define NO_FLAGS "Cannot make this partition bootable" +#define NO_MORE_PARTS "No more partitions" +#define PRINT_OPEN_ERR "Cannot open file '%s'" +#define TWO_EXTENDEDS "Cannot create logical drive here -- would create two extended partitions" +#define TYPE_EMPTY "Cannot change the type of an empty partition" +#define BAD_COMMAND "Illegal command" +#define MAX_UNMAXABLE "Cannot maximize this partition" +#define BAD_OPEN "Cannot open disk drive" +#define BAD_SEEK "Cannot seek on disk drive" +#define BAD_READ "Cannot read disk drive" +#define BAD_WRITE "Cannot write disk drive" +#define BAD_GEOMETRY "Cannot read disk drive geometry" +#define BAD_PRIMARY "Bad primary partition" +#define BAD_LOGICAL "Bad logical partition" +#define BAD_CYLINDERS "Illegal cylinders value" +#define BAD_HEADS "Illegal heads value" +#define BAD_SECTORS "Illegal sectors value" +#define WRITE_WARN "Warning!! This may destroy data on your disk!" +#define YES_NO "Please enter `yes' or `no'" +#define WRITING_PART "Writing partition table to disk..." +#define YES_WRITE "Wrote partition table to disk" +#define NO_WRITE "Did not write partition table to disk" +#define RRPART_FAILED "Wrote partition table, but re-read table failed. Reboot to update table." + +#define PRI_OR_LOG -1 +#define PRIMARY -2 +#define LOGICAL -3 + +#define COL_ID_WIDTH 20 + +#define CR '\015' +#define ESC '\033' +#define DEL '\177' +#define BELL '\007' +/* '\014' == ^L */ +#define REDRAWKEY '\014' + +/* Display units */ +#define MEGABYTES 1 +#define SECTORS 2 +#define CYLINDERS 3 + +#define GS_DEFAULT -1 +#define GS_ESCAPE -2 + +#define PRINT_RAW_TABLE 1 +#define PRINT_SECTOR_TABLE 2 +#define PRINT_PARTITION_TABLE 4 + +#define IS_PRIMARY(p) ((p) >= 0 && (p) < 4) +#define IS_LOGICAL(p) ((p) > 3) + +#define round_int(d) ((double)((int)(d+0.5))) +#define ceiling(d) ((double)(((d) != (int)(d)) ? (int)(d+1.0) : (int)(d))) + +#define set_hsc(h,s,c,sector) \ +{ \ + s = sector % sectors + 1; \ + sector /= sectors; \ + h = sector % heads; \ + sector /= heads; \ + c = sector & 0xFF; \ + s |= (sector >> 2) & 0xC0;\ +} + +#define ALIGNMENT 2 +typedef union { + struct { + unsigned char align[ALIGNMENT]; + unsigned char b[SECTOR_SIZE]; + } c; + struct { + unsigned char align[ALIGNMENT]; + unsigned char buffer[0x1BE]; + struct partition part[4]; + unsigned short flag; + } p; +} partition_table; + +typedef struct { + int first_sector; /* first sector in partition */ + int last_sector; /* last sector in partition */ + int offset; /* offset from first sector to start of data */ + int flags; /* active == 0x80 */ + int id; /* filesystem type */ + int num; /* number of partition -- primary vs. logical */ +} partition_info; + +char *disk_device = DEFAULT_DEVICE; +int fd; +int heads = 0; +int sectors = 0; +int cylinders = 0; +int changed = FALSE; +int opened = FALSE; + +partition_info p_info[MAXIMUM_PARTS]; +partition_info ext_info; +int num_parts = 0; + +int logical = 0; +int logical_sectors[MAXIMUM_PARTS]; + +__sighandler_t old_SIGINT, old_SIGTERM; + +int arrow_cursor = FALSE; +int display_units = MEGABYTES; +int zero_table = FALSE; +int print_only = 0; + +/* Curses screen information */ +int cur_part = 0; +int warning_last_time = FALSE; +int defined = FALSE; +int COLUMNS = 80; +int NUM_ON_SCREEN = 1; + +/* Y coordinates */ +int HEADER_START = 0; +int DISK_TABLE_START = 5; +int WARNING_START = 23; +int COMMAND_LINE_Y = 21; + +/* X coordinates */ +int NAME_START = 4; +int FLAGS_START = 16; +int PTYPE_START = 30; +int FSTYPE_START = 45; +int SIZE_START = 70; +int COMMAND_LINE_X = 5; + +#define NUM_PART_TYPES 256 +char *partition_type[NUM_PART_TYPES] = { + [LINUX_MINIX] = "Linux/MINIX", + [LINUX_SWAP] = "Linux Swap", + [LINUX] = "Linux", + [FREE_SPACE] = "Free Space", + [EXTENDED] = "Extended", + [0x01] = "DOS 12-bit FAT", + [0x04] = "DOS 16-bit < 32Mb", + [0x06] = "DOS 16-bit >=32Mb", + [0x07] = "OS/2 HPFS", + [0x0A] = "OS/2 Boot Manager", + [0xA5] = "BSD/386", + +/* The rest of these are taken from A. V. Le Blanc's (LeBlanc@mcc.ac.uk) + * fdisk program. I do not know where they came from, but I include + * them for completeness. + */ + + [0x02] = "XENIX root", + [0x03] = "XENIX usr", + [0x08] = "AIX", + [0x09] = "AIX bootable", + [0x40] = "Venix 80286", + [0x51] = "Novell?", + [0x52] = "Microport", + [0x63] = "GNU HURD", + [0x64] = "Novell", + [0x75] = "PC/IX", + [0x80] = "Old MINIX", + [0x93] = "Amoeba", + [0x94] = "Amoeba BBT", + [0xB7] = "BSDI fs", + [0xB8] = "BSDI swap", + [0xC7] = "Syrinx", + [0xDB] = "CP/M", + [0xE1] = "DOS access", + [0xE3] = "DOS R/O", + [0xF2] = "DOS secondary", + [0xFF] = "BBT" +}; + +void fdexit(int ret) +{ + if (opened) + close(fd); + + if (changed) { + fprintf(stderr, "Disk has been changed.\n"); + fprintf(stderr, "Reboot the system to ensure the partition " + "table is correctly updated.\n"); + + fprintf( stderr, "\nWARNING: If you have created or modified any\n" + "DOS 6.x partitions, please see the cfdisk manual\n" + "page for additional information.\n" ); + } + + + exit(ret); +} + +int get_string(char *str, int len, char *def) +{ + char c; + int i = 0; + int x, y; + int use_def = FALSE; + + getyx(stdscr, y, x); + clrtoeol(); + + str[i] = 0; + + if (def != NULL) { + mvaddstr(y, x, def); + move(y, x); + use_def = TRUE; + } + + refresh(); + while ((c = getch()) != '\n' && c != CR) { + switch (c) { + case ESC: + move(y, x); + clrtoeol(); + refresh(); + return GS_ESCAPE; + case DEL: + case '\b': + if (i > 0) { + str[--i] = 0; + mvaddch(y, x+i, ' '); + move(y, x+i); + } else if (use_def) { + clrtoeol(); + use_def = FALSE; + } else + putchar(BELL); + break; + default: + if (i < len && isprint(c)) { + mvaddch(y, x+i, c); + if (use_def) { + clrtoeol(); + use_def = FALSE; + } + str[i++] = c; + str[i] = 0; + } else + putchar(BELL); + } + refresh(); + } + + if (use_def) + return GS_DEFAULT; + else + return i; +} + +void clear_warning(void) +{ + int i; + + if (!warning_last_time) + return; + + move(WARNING_START,0); + for (i = 0; i < COLS; i++) + addch(' '); + + warning_last_time = FALSE; +} + +void print_warning(char *s) +{ + mvaddstr(WARNING_START, (COLS-strlen(s))/2, s); + putchar(BELL); /* CTRL-G */ + + warning_last_time = TRUE; +} + +void fatal(char *s) +{ + char str[LINE_LENGTH]; + + sprintf(str, "FATAL ERROR: %s", s); + mvaddstr(WARNING_START, (COLS-strlen(str))/2, str); + sprintf(str, "Press any key to exit fdisk"); + mvaddstr(WARNING_START+1, (COLS-strlen(str))/2, str); + putchar(BELL); /* CTRL-G */ + + refresh(); + + (void)getch(); + + signal(SIGINT, old_SIGINT); + signal(SIGTERM, old_SIGTERM); + mvcur(0, COLS-1, LINES-1, 0); + nl(); + endwin(); + fdexit(1); +} + +void read_sector(char *buffer, int sect_num) +{ + if (ext2_llseek(fd, sect_num*SECTOR_SIZE, SEEK_SET) < 0) + fatal(BAD_SEEK); + if (read(fd, buffer, SECTOR_SIZE) != SECTOR_SIZE) + fatal(BAD_READ); +} + +void write_sector(char *buffer, int sect_num) +{ + if (ext2_llseek(fd, sect_num*SECTOR_SIZE, SEEK_SET) < 0) + fatal(BAD_SEEK); + if (write(fd, buffer, SECTOR_SIZE) != SECTOR_SIZE) + fatal(BAD_WRITE); +} + +void check_part_info(void) +{ + int i, pri = 0, log = 0; + + for (i = 0; i < num_parts; i++) + if (p_info[i].id > 0 && IS_PRIMARY(p_info[i].num)) + pri++; + else if (p_info[i].id > 0 && IS_LOGICAL(p_info[i].num)) + log++; + if (ext_info.id == EXTENDED) + if (log > 0) + pri++; + else { + ext_info.first_sector = 0; + ext_info.last_sector = 0; + ext_info.offset = 0; + ext_info.flags = 0; + ext_info.id = FREE_SPACE; + ext_info.num = PRIMARY; + } + + if (pri >= 4) + for (i = 0; i < num_parts; i++) + if (p_info[i].id == FREE_SPACE || p_info[i].id == UNUSABLE) + if (ext_info.id == EXTENDED) + if (p_info[i].first_sector >= ext_info.first_sector && + p_info[i].last_sector <= ext_info.last_sector) { + p_info[i].id = FREE_SPACE; + p_info[i].num = LOGICAL; + } else if (i > 0 && + p_info[i-1].first_sector >= + ext_info.first_sector && + p_info[i-1].last_sector <= + ext_info.last_sector) { + p_info[i].id = FREE_SPACE; + p_info[i].num = LOGICAL; + } else if (i < num_parts-1 && + p_info[i+1].first_sector >= + ext_info.first_sector && + p_info[i+1].last_sector <= + ext_info.last_sector) { + p_info[i].id = FREE_SPACE; + p_info[i].num = LOGICAL; + } else + p_info[i].id = UNUSABLE; + else /* if (ext_info.id != EXTENDED) */ + p_info[i].id = UNUSABLE; + else /* if (p_info[i].id > 0) */ + while (0); /* Leave these alone */ + else /* if (pri < 4) */ + for (i = 0; i < num_parts; i++) { + if (p_info[i].id == UNUSABLE) + p_info[i].id = FREE_SPACE; + if (p_info[i].id == FREE_SPACE) + if (ext_info.id == EXTENDED) + if (p_info[i].first_sector >= ext_info.first_sector && + p_info[i].last_sector <= ext_info.last_sector) + p_info[i].num = LOGICAL; + else if (i > 0 && + p_info[i-1].first_sector >= + ext_info.first_sector && + p_info[i-1].last_sector <= + ext_info.last_sector) + p_info[i].num = PRI_OR_LOG; + else if (i < num_parts-1 && + p_info[i+1].first_sector >= + ext_info.first_sector && + p_info[i+1].last_sector <= + ext_info.last_sector) + p_info[i].num = PRI_OR_LOG; + else + p_info[i].num = PRIMARY; + else /* if (ext_info.id != EXTENDED) */ + p_info[i].num = PRI_OR_LOG; + else /* if (p_info[i].id > 0) */ + while (0); /* Leave these alone */ + } +} + +void remove_part(int i) +{ + int p; + + for (p = i; p < num_parts; p++) + p_info[p] = p_info[p+1]; + + num_parts--; +} + +void insert_part(int i, int num, int id, int flags, int first, int last, + int offset) +{ + int p; + + for (p = num_parts; p > i; p--) + p_info[p] = p_info[p-1]; + + p_info[i].first_sector = first; + p_info[i].last_sector = last; + p_info[i].offset = offset; + p_info[i].flags = flags; + p_info[i].id = id; + p_info[i].num = num; + + num_parts++; +} + +void del_part(int i) +{ + int num = p_info[i].num; + + if (i > 0 && (p_info[i-1].id == FREE_SPACE || + p_info[i-1].id == UNUSABLE)) { + /* Merge with previous partition */ + p_info[i-1].last_sector = p_info[i].last_sector; + remove_part(i--); + } + + if (i < num_parts - 1 && (p_info[i+1].id == FREE_SPACE || + p_info[i+1].id == UNUSABLE)) { + /* Merge with next partition */ + p_info[i+1].first_sector = p_info[i].first_sector; + remove_part(i); + } + + if (i > 0) + p_info[i].first_sector = p_info[i-1].last_sector + 1; + else + p_info[i].first_sector = 0; + + if (i < num_parts - 1) + p_info[i].last_sector = p_info[i+1].first_sector - 1; + else + p_info[i].last_sector = sectors*heads*cylinders - 1; + + p_info[i].offset = 0; + p_info[i].flags = 0; + p_info[i].id = FREE_SPACE; + p_info[i].num = PRI_OR_LOG; + + if (IS_LOGICAL(num)) { + /* We have a logical partition --> shrink the extended partition + * if (1) this is the first logical drive, or (2) this is the + * last logical drive; and if there are any other logical drives + * then renumber the ones after "num". + */ + if (i == 0 || (i > 0 && IS_PRIMARY(p_info[i-1].num))) + ext_info.first_sector = p_info[i].last_sector + 1; + if (i == num_parts-1 || + (i < num_parts-1 && IS_PRIMARY(p_info[i+1].num))) + ext_info.last_sector = p_info[i].first_sector - 1; + for (i = 0; i < num_parts; i++) + if (p_info[i].num > num) + p_info[i].num--; + } + + /* Clean up the rest of the partitions */ + check_part_info(); +} + +int add_part(int num, int id, int flags, int first, int last, int offset) +{ + int i, pri = 0, log = 0; + + if (num_parts == MAXIMUM_PARTS || + first < 0 || + first >= cylinders*heads*sectors || + last < 0 || + last >= cylinders*heads*sectors) + return -1; + + for (i = 0; i < num_parts; i++) + if (p_info[i].id > 0 && IS_PRIMARY(p_info[i].num)) + pri++; + else if (p_info[i].id > 0 && IS_LOGICAL(p_info[i].num)) + log++; + if (ext_info.id == EXTENDED && log > 0) + pri++; + + if (IS_PRIMARY(num)) + if (pri >= 4) + return -1; + else + pri++; + + for (i = 0; p_info[i].last_sector < first; i++); + + if (p_info[i].id != FREE_SPACE || last > p_info[i].last_sector) + return -1; + + if (id == EXTENDED) + if (ext_info.id != FREE_SPACE) + return -1; + else if (IS_PRIMARY(num)) { + ext_info.first_sector = first; + ext_info.last_sector = last; + ext_info.offset = offset; + ext_info.flags = flags; + ext_info.id = EXTENDED; + ext_info.num = num; + + return 0; + } else + return -1; + + if (IS_LOGICAL(num)) { + if (ext_info.id != EXTENDED) { + print_warning("!!!! Internal error creating logical " + "drive with no extended partition !!!!"); + } else { + /* We might have a logical partition outside of the extended + * partition's range --> we have to extend the extended + * partition's range to encompass this new partition, but we + * must make sure that there are no primary partitions between + * it and the closest logical drive in extended partition. + */ + if (first < ext_info.first_sector) { + if (i < num_parts-1 && IS_PRIMARY(p_info[i+1].num)) { + print_warning(TWO_EXTENDEDS); + return -1; + } else { + if (first == 0) { + ext_info.first_sector = 0; + ext_info.offset = first = offset; + } else + ext_info.first_sector = first; + } + } else if (last > ext_info.last_sector) { + if (i > 0 && IS_PRIMARY(p_info[i-1].num)) { + print_warning(TWO_EXTENDEDS); + return -1; + } else + ext_info.last_sector = last; + } + } + } + + if (first != p_info[i].first_sector && + !(IS_LOGICAL(num) && first == offset)) { + insert_part(i, PRI_OR_LOG, FREE_SPACE, 0, + p_info[i].first_sector, first-1, 0); + i++; + } + + if (last != p_info[i].last_sector) + insert_part(i+1, PRI_OR_LOG, FREE_SPACE, 0, + last+1, p_info[i].last_sector, 0); + + p_info[i].first_sector = first; + p_info[i].last_sector = last; + p_info[i].offset = offset; + p_info[i].flags = flags; + p_info[i].id = id; + p_info[i].num = num; + + check_part_info(); + + return 0; +} + +int find_primary(void) +{ + int num = 0, cur = 0; + + while (cur < num_parts && IS_PRIMARY(num)) + if ((p_info[cur].id > 0 && p_info[cur].num == num) || + (ext_info.id == EXTENDED && ext_info.num == num)) { + num++; + cur = 0; + } else + cur++; + + if (!IS_PRIMARY(num)) + return -1; + else + return num; +} + +int find_logical(int i) +{ + int num = -1; + int j; + + for (j = i; j < num_parts && num == -1; j++) + if (p_info[j].id > 0 && IS_LOGICAL(p_info[j].num)) + num = p_info[j].num; + + if (num == -1) { + num = 4; + for (j = 0; j < num_parts; j++) + if (p_info[j].id > 0 && p_info[j].num == num) + num++; + } + + return num; +} + +void inc_logical(int i) +{ + int j; + + for (j = i; j < num_parts; j++) + if (p_info[j].id > 0 && IS_LOGICAL(p_info[j].num)) + p_info[j].num++; +} + +/* Command menu support by Janne Kukonlehto September 1994 */ + +/* Constants for menuType parameter of menuSelect function */ +#define MENU_HORIZ 1 +#define MENU_VERT 2 +#define MENU_ACCEPT_OTHERS 4 +#define MENU_BUTTON 8 +/* Miscellenous constants */ +#define MENU_SPACING 2 +#define MENU_MAX_ITEMS 256 /* for simpleMenu function */ +#define MENU_UP 1 +#define MENU_DOWN 2 +#define MENU_RIGHT 3 +#define MENU_LEFT 4 + +struct MenuItem +{ + char key; /* Keyboard shortcut; if zero, then there is no more items in the menu item table */ + char *name; /* Item name, should be eight characters with current implementation */ + char *desc; /* Item description to be printed when item is selected */ +}; + +/* Actual function which prints the button bar and highlights the active button * + * Should not be called directly. Call function menuSelect instead. */ + +int menuUpdate( int y, int x, struct MenuItem *menuItems, int itemLength, char *available, int menuType, int current ) +{ + int i, lmargin = x, ymargin = y; + /* Print available buttons */ + move( y, x ); clrtoeol(); + for( i = 0; menuItems[i].key; i++ ) + { + char buff[20]; + int lenName; + /* Search next available button */ + while( menuItems[i].key && !strchr(available, menuItems[i].key) ) + { + i++; + } + if( !menuItems[i].key ) break; /* No more menu items */ + /* If selected item is not available and we have bypassed it, make current item selected */ + if( current < i && menuItems[current].key < 0 ) current = i; + /* If current item is selected, highlight it */ + if( current == i ) /*attron( A_REVERSE )*/ standout (); + /* Print item */ + lenName = strlen( menuItems[i].name ); + if(lenName > itemLength) + print_warning("Menu item too long. Menu may look odd."); + if( menuType & MENU_BUTTON ) + sprintf( buff, "[%*s%-*s]", (itemLength - lenName) / 2, "", + (itemLength - lenName + 1) / 2 + lenName, menuItems[i].name ); + else + sprintf( buff, "%*s%-*s", (itemLength - lenName) / 2, "", + (itemLength - lenName + 1) / 2 + lenName, menuItems[i].name ); + mvaddstr( y, x, buff ); + /* Lowlight after selected item */ + if( current == i ) /*attroff( A_REVERSE )*/ standend (); + /* Calculate position for the next item */ + if( menuType & MENU_VERT ) + { + y += 1; + if( y >= WARNING_START ) + { + y = ymargin; + x += itemLength + MENU_SPACING; + if( menuType & MENU_BUTTON ) x += 2; + } + } + else + { + x += itemLength + MENU_SPACING; + if( menuType & MENU_BUTTON ) x += 2; + if( x > COLUMNS - lmargin - 12 ) + { + x = lmargin; + y ++ ; + } + } + } + /* Print the description of selected item */ + mvaddstr( WARNING_START + 1, (COLUMNS - strlen( menuItems[current].desc )) / 2, menuItems[current].desc ); + return y; +} + +/* This function takes a list of menu items, lets the user choose one of them * + * and returns the value keyboard shortcut of the selected menu item */ + +int menuSelect( int y, int x, struct MenuItem *menuItems, int itemLength, char *available, int menuType, int menuDefault ) +{ + int i, ylast = y, key = 0, current = menuDefault; + if( !( menuType & ( MENU_HORIZ | MENU_VERT ) ) ) + { + print_warning("Menu without direction. Defaulting horizontal."); + menuType |= MENU_HORIZ; + } + /* Make sure that the current is one of the available items */ + while( !strchr(available, menuItems[current].key) ) + { + current ++ ; + if( !menuItems[current].key ) current = 0; + } + /* Repeat until allowable choice has been made */ + while( !key ) + { + /* Display the menu */ + ylast = menuUpdate( y, x, menuItems, itemLength, available, menuType, current ); + refresh(); + key = getch(); + /* Clear out all prompts and such */ + clear_warning(); + for( i = y; i < ylast; i ++ ) + { + move( i, x ); + clrtoeol(); + } + move( WARNING_START + 1, 0 ); + clrtoeol(); + /* Cursor keys */ + if( key == ESC ) + { + /* Check whether this is a real ESC or one of extended keys */ + /*nodelay(stdscr, TRUE);*/ + key = getch(); + /*nodelay(stdscr, FALSE);*/ + if( key == /*ERR*/ ESC ) + { + /* This is a real ESC */ + key = ESC; + } + if( key == '[' ) + { + /* This is one extended keys */ + switch( getch() ) + { + case 'A': /* Up arrow */ + if( menuType & MENU_VERT ) + { + do { + current -- ; + if( current < 0 ) while( menuItems[current+1].key ) current ++ ; + } while( !strchr( available, menuItems[current].key ) ); + key = 0; + } + else + key = MENU_UP; + break; + case 'B': /* Down arrow */ + if( menuType & MENU_VERT ) + { + do { + current ++ ; + if( !menuItems[current].key ) current = 0 ; + } while( !strchr( available, menuItems[current].key ) ); + key = 0; + } + else + key = MENU_DOWN; + break; + case 'C': /* Right arrow */ + if( menuType & MENU_HORIZ ) + { + do { + current ++ ; + if( !menuItems[current].key ) + { + current = 0 ; + } + } while( !strchr( available, menuItems[current].key ) ); + key = 0; + } + else + key = MENU_RIGHT; + break; + case 'D': /* Left arrow */ + if( menuType & MENU_HORIZ ) + { + do { + current -- ; + if( current < 0 ) + { + while( menuItems[current + 1].key ) current ++ ; + } + } while( !strchr( available, menuItems[current].key ) ); + key = 0; + } + else + key = MENU_LEFT; + break; + } + } + } + /* Enter equals to the keyboard shortcut of current menu item */ + if( key == 13 ) + { + key = menuItems[current].key; + } + /* Should all keys to be accepted? */ + if( key && (menuType & MENU_ACCEPT_OTHERS) ) break; + /* Is pressed key among acceptable ones */ + if( key && (strchr(available, tolower(key)) || strchr(available, key)) ) break; + /* The key has not been accepted so far -> let's reject it */ + if( key ) + { + key = 0; + putchar( BELL ); + print_warning("Illegal key"); + } + } + /* Clear out prompts and such */ + clear_warning(); + for( i = y; i <= ylast; i ++ ) + { + move( i, x ); + clrtoeol(); + } + move( WARNING_START + 1, 0 ); + clrtoeol(); + return key; +} + +/* A function which displays "Press a key to continue" and waits for a keypress * + * Perhaps calling function menuSelect is a bit overkill but who cares? */ + +void menuContinue(void) +{ + static struct MenuItem menuContinueBtn[]= + { + { 'c', "", "Press a key to continue" }, + { 0, NULL, NULL } + }; + + menuSelect(COMMAND_LINE_Y, COMMAND_LINE_X, + menuContinueBtn, 0, "c", MENU_HORIZ | MENU_ACCEPT_OTHERS, 0 ); +} + +/* Function menuSelect takes way too many parameters * + * Luckily, most of time we can do with this function */ + +int menuSimple(struct MenuItem *menuItems, int menuDefault) +{ + int i, j, itemLength = 0; + char available[MENU_MAX_ITEMS]; + for(i = 0; menuItems[i].key; i++) + { + j = strlen( menuItems[i].name ); + if( j > itemLength ) itemLength = j; + available[i] = menuItems[i].key; + } + available[i] = 0; + return menuSelect(COMMAND_LINE_Y, COMMAND_LINE_X, menuItems, itemLength, + available, MENU_HORIZ | MENU_BUTTON, menuDefault); +} + +/* End of command menu support code */ + +void new_part(int i) +{ + char response[LINE_LENGTH], def[LINE_LENGTH]; + char c; + int first = p_info[i].first_sector; + int last = p_info[i].last_sector; + int offset = 0; + int flags = 0; + int id = LINUX; + int num = -1; + int num_sects = last - first + 1; + int len, ext, j; + + if (p_info[i].num == PRI_OR_LOG) { + static struct MenuItem menuPartType[]= + { + { 'p', "Primary", "Create a new primary partition" }, + { 'l', "Logical", "Create a new logical partition" }, + { ESC, "Cancel", "Don't create a partition" }, + { 0, NULL, NULL } + }; + + c = menuSimple( menuPartType, 0 ); + if (toupper(c) == 'P') + num = find_primary(); + else if (toupper(c) == 'L') + num = find_logical(i); + else + return; + } else if (p_info[i].num == PRIMARY) + num = find_primary(); + else if (p_info[i].num == LOGICAL) + num = find_logical(i); + else + print_warning("!!! Internal error !!!"); + + sprintf(def, "%.2f", ceiling(num_sects/20.48)/100); + mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X, "Size (in MB): "); + if ((len = get_string(response, LINE_LENGTH, def)) <= 0 && + len != GS_DEFAULT) + return; + else if (len > 0) { +#define num_cyls(bytes) (round_int(bytes/SECTOR_SIZE/(sectors*heads))) + for (j = 0; + j < len-1 && (isdigit(response[j]) || response[j] == '.'); + j++); + if (toupper(response[j]) == 'K') { + num_sects = num_cyls(atof(response)*1024)*sectors*heads; + } else if (toupper(response[j]) == 'M') { + num_sects = num_cyls(atof(response)*1024*1024)*sectors*heads; + } else if (toupper(response[j]) == 'C') { + num_sects = round_int(atof(response))*sectors*heads; + } else if (toupper(response[j]) == 'S') { + num_sects = round_int(atof(response)); + } else { + num_sects = num_cyls(atof(response)*1024*1024)*sectors*heads; + } + } + + if (num_sects <= 0 || + num_sects > p_info[i].last_sector - p_info[i].first_sector + 1) + return; + + move( COMMAND_LINE_Y, COMMAND_LINE_X ); clrtoeol(); + if (num_sects < p_info[i].last_sector - p_info[i].first_sector + 1) { + /* Determine where inside free space to put partition. + */ + static struct MenuItem menuPlace[]= + { + { 'b', "Beginning", "Add partition at beginning of free space" }, + { 'e', "End", "Add partition at end of free space" }, + { ESC, "Cancel", "Don't create a partition" }, + { 0, NULL, NULL } + }; + c = menuSimple( menuPlace, 0 ); + if (toupper(c) == 'B') + last = first + num_sects - 1; + else if (toupper(c) == 'E') + first = last - num_sects + 1; + else + return; + } + + if (IS_LOGICAL(num) && ext_info.id != EXTENDED) { + /* We want to add a logical partition, but need to create an + * extended partition first. + */ + if ((ext = find_primary()) < 0) { + print_warning(NEED_EXT); + return; + } + (void)add_part(ext, EXTENDED, 0, first, last, + (first == 0 ? sectors : 0)); + } + + if (IS_LOGICAL(num)) + inc_logical(i); + + /* Now we have a complete partition to ourselves */ + if (first == 0 || IS_LOGICAL(num)) + offset = sectors; + + (void)add_part(num, id, flags, first, last, offset); +} + +void clear_p_info(void) +{ + num_parts = 1; + p_info[0].first_sector = 0; + p_info[0].last_sector = sectors*heads*cylinders - 1; + p_info[0].offset = 0; + p_info[0].flags = 0; + p_info[0].id = FREE_SPACE; + p_info[0].num = PRI_OR_LOG; + + ext_info.first_sector = 0; + ext_info.last_sector = 0; + ext_info.offset = 0; + ext_info.flags = 0; + ext_info.id = FREE_SPACE; + ext_info.num = PRIMARY; +} + +void fill_p_info(void) +{ + int p, i; + struct hd_geometry geometry; + partition_table buffer; + partition_info tmp_ext = { 0, 0, 0, 0, FREE_SPACE, PRIMARY }; + + if ((fd = open(disk_device, O_RDWR)) < 0) + fatal(BAD_OPEN); + read_sector(buffer.c.b, 0); + + if (!ioctl(fd, HDIO_GETGEO, &geometry)) { + if (!heads) + heads = geometry.heads; + if (!sectors) + sectors = geometry.sectors; + if (!cylinders) + cylinders = geometry.cylinders; + } + + if (!heads || !sectors || !cylinders) + fatal(BAD_GEOMETRY); + + clear_p_info(); + + if (!zero_table) { + for (i = 0; i < 4; i++) { + if (buffer.p.part[i].sys_ind > 0 && + add_part(i, + buffer.p.part[i].sys_ind, + buffer.p.part[i].boot_ind, + ((buffer.p.part[i].start_sect <= sectors) ? + 0 : buffer.p.part[i].start_sect), + buffer.p.part[i].start_sect + + buffer.p.part[i].nr_sects - 1, + ((buffer.p.part[i].start_sect <= sectors) ? + buffer.p.part[i].start_sect : 0))) { + fatal(BAD_PRIMARY); + } + if (buffer.p.part[i].sys_ind == EXTENDED) + tmp_ext = ext_info; + } + + if (tmp_ext.id == EXTENDED) { + ext_info = tmp_ext; + logical_sectors[logical] = ext_info.first_sector; + read_sector(buffer.c.b, logical_sectors[logical++]); + i = 4; + do { + for (p = 0; + p < 4 && (!buffer.p.part[p].sys_ind || + buffer.p.part[p].sys_ind == 5); + p++); + if (p > 3) + fatal(BAD_LOGICAL); + + if (add_part(i++, + buffer.p.part[p].sys_ind, + buffer.p.part[p].boot_ind, + logical_sectors[logical-1], + logical_sectors[logical-1] + + buffer.p.part[p].start_sect + + buffer.p.part[p].nr_sects - 1, + buffer.p.part[p].start_sect)) { + fatal(BAD_LOGICAL); + } + + for (p = 0; + p < 4 && buffer.p.part[p].sys_ind != 5; + p++); + if (p < 4) { + logical_sectors[logical] = + ext_info.first_sector + buffer.p.part[p].start_sect; + read_sector(buffer.c.b, logical_sectors[logical++]); + } + } while (p < 4 && logical < MAXIMUM_PARTS-4); + } + } +} + +void fill_part_table(struct partition *p, partition_info *pi) +{ + int sects; + + p->boot_ind = pi->flags; + p->sys_ind = pi->id; + if (IS_LOGICAL(pi->num)) + p->start_sect = pi->offset; + else + p->start_sect = pi->first_sector + pi->offset; + p->nr_sects = pi->last_sector - (pi->first_sector+pi->offset) + 1; + sects = (((pi->first_sector+pi->offset)/(sectors*heads) > 1023) ? + heads*sectors*1024 - 1 : pi->first_sector+pi->offset); + set_hsc(p->head, p->sector, p->cyl, sects); + sects = ((pi->last_sector/(sectors*heads) > 1023) ? + heads*sectors*1024 - 1 : pi->last_sector); + set_hsc(p->end_head, p->end_sector, p->end_cyl, sects); +} + +void fill_primary_table(partition_table *buffer) +{ + int i; + + /* Zero out existing table */ + for (i = 0x1BE; i < SECTOR_SIZE; i++) + buffer->c.b[i] = 0; + + for (i = 0; i < num_parts; i++) + if (IS_PRIMARY(p_info[i].num)) + fill_part_table(&(buffer->p.part[p_info[i].num]), &(p_info[i])); + + if (ext_info.id == EXTENDED) + fill_part_table(&(buffer->p.part[ext_info.num]), &ext_info); + + buffer->p.flag = PART_TABLE_FLAG; +} + +void fill_logical_table(partition_table *buffer, partition_info *pi) +{ + struct partition *p; + int i, sects; + + for (i = 0; i < logical && pi->first_sector != logical_sectors[i]; i++); + if (i == logical || buffer->p.flag != (unsigned short)PART_TABLE_FLAG) + for (i = 0; i < SECTOR_SIZE; i++) + buffer->c.b[i] = 0; + + /* Zero out existing table */ + for (i = 0x1BE; i < SECTOR_SIZE; i++) + buffer->c.b[i] = 0; + + fill_part_table(&(buffer->p.part[0]), pi); + + for (i = 0; + i < num_parts && pi->num != p_info[i].num - 1; + i++); + + if (i < num_parts) { + p = &(buffer->p.part[1]); + pi = &(p_info[i]); + + p->boot_ind = 0; + p->sys_ind = 5; + p->start_sect = pi->first_sector - ext_info.first_sector; + p->nr_sects = pi->last_sector - pi->first_sector + 1; + sects = ((pi->first_sector/(sectors*heads) > 1023) ? + heads*sectors*1024 - 1 : pi->first_sector); + set_hsc(p->head, p->sector, p->cyl, sects); + sects = ((pi->last_sector/(sectors*heads) > 1023) ? + heads*sectors*1024 - 1 : pi->last_sector); + set_hsc(p->end_head, p->end_sector, p->end_cyl, sects); + } + + buffer->p.flag = PART_TABLE_FLAG; +} + +void write_part_table(void) +{ + int i, done = FALSE, len; + partition_table buffer; + char response[LINE_LENGTH]; + + print_warning(WRITE_WARN); + + while (!done) { + mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X, + "Are you sure you want write the partition table to disk? (yes or no): "); + + len = get_string(response, LINE_LENGTH, NULL); + + clear_warning(); + + if (len == GS_ESCAPE) + return; + else if (len == 2 && + toupper(response[0]) == 'N' && + toupper(response[1]) == 'O') { + print_warning(NO_WRITE); + return; + } else if (len == 3 && + toupper(response[0]) == 'Y' && + toupper(response[1]) == 'E' && + toupper(response[2]) == 'S') + done = TRUE; + else + print_warning(YES_NO); + } + + clear_warning(); + print_warning(WRITING_PART); + refresh(); + + read_sector(buffer.c.b, 0); + fill_primary_table(&buffer); + write_sector(buffer.c.b, 0); + + for (i = 0; i < num_parts; i++) + if (IS_LOGICAL(p_info[i].num)) { + /* Read the extended partition table from disk ??? KEM */ + read_sector(buffer.c.b, p_info[i].first_sector); + fill_logical_table(&buffer, &(p_info[i])); + write_sector(buffer.c.b, p_info[i].first_sector); + } + + sync(); + sleep(2); + if (!ioctl(fd,BLKRRPART)) + changed = TRUE; + sync(); + sleep(4); + + clear_warning(); + if (changed) + print_warning(YES_WRITE); + else + print_warning(RRPART_FAILED); +} + +void fp_printf(FILE *fp, char *format, ...) +{ + va_list args; + char buf[1024]; + int y, x; + + va_start(args, format); + vsprintf(buf, format, args); + va_end(args); + + if (fp == NULL) { + /* The following works best if the string to be printed has at + most only one newline. */ + printw("%s", buf); + getyx(stdscr, y, x); + if (y >= COMMAND_LINE_Y-2) { + menuContinue(); + erase(); + move(0, 0); + } + } else + fprintf(fp, "%s", buf); +} + +#define MAX_PER_LINE 16 +void print_file_buffer(FILE *fp, char *buffer) +{ + int i,l; + + for (i = 0, l = 0; i < SECTOR_SIZE; i++, l++) { + if (l == 0) + fp_printf(fp, "0x%03X:", i); + fp_printf(fp, " %02X", (unsigned char) buffer[i]); + if (l == MAX_PER_LINE - 1) { + fp_printf(fp, "\n"); + l = -1; + } + } + if (l > 0) + fp_printf(fp, "\n"); + fp_printf(fp, "\n"); +} + +void print_raw_table(void) +{ + int i, to_file; + partition_table buffer; + char fname[LINE_LENGTH]; + FILE *fp; + + if (print_only) { + fp = stdout; + to_file = TRUE; + } else { + mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X, + "Enter filename or press RETURN to display on screen: "); + + if ((to_file = get_string(fname, LINE_LENGTH, NULL)) < 0) + return; + + if (to_file) { + if ((fp = fopen(fname, "w")) == NULL) { + char errstr[LINE_LENGTH]; + sprintf(errstr, PRINT_OPEN_ERR, fname); + print_warning(errstr); + return; + } + } else { + fp = NULL; + erase(); + move(0, 0); + } + } + + fp_printf(fp, "Disk Drive: %s\n", disk_device); + + read_sector(buffer.c.b, 0); + fill_primary_table(&buffer); + print_file_buffer(fp, buffer.c.b); + + for (i = 0; i < num_parts; i++) + if (IS_LOGICAL(p_info[i].num)) { + read_sector(buffer.c.b, p_info[i].first_sector); + fill_logical_table(&buffer, &(p_info[i])); + print_file_buffer(fp, buffer.c.b); + } + + if (to_file) { + if (!print_only) + fclose(fp); + } else { + menuContinue(); + } +} + +void print_p_info_entry(FILE *fp, partition_info *p) +{ + int size; + char part_str[21]; + + if (p->id == UNUSABLE) + fp_printf(fp, " None "); + else if (p->id == FREE_SPACE && p->num == PRI_OR_LOG) + fp_printf(fp, " Pri/Log"); + else if (p->id == FREE_SPACE && p->num == PRIMARY) + fp_printf(fp, " Primary"); + else if (p->id == FREE_SPACE && p->num == LOGICAL) + fp_printf(fp, " Logical"); + else + fp_printf(fp, "%2d %-7.7s", p->num+1, + IS_LOGICAL(p->num) ? "Logical" : "Primary"); + + fp_printf(fp, " "); + + fp_printf(fp, "%7d%c", p->first_sector, + ((p->first_sector/(sectors*heads)) != + ((float)p->first_sector/(sectors*heads)) ? + '*' : ' ')); + + fp_printf(fp, " "); + + fp_printf(fp, "%7d%c", p->last_sector, + (((p->last_sector+1)/(sectors*heads)) != + ((float)(p->last_sector+1)/(sectors*heads)) ? + '*' : ' ')); + + fp_printf(fp, " "); + + fp_printf(fp, "%6d%c", p->offset, + ((((p->first_sector == 0 || IS_LOGICAL(p->num)) && + (p->offset != sectors)) || + (p->first_sector != 0 && IS_PRIMARY(p->num) && + p->offset != 0)) ? + '#' : ' ')); + + fp_printf(fp, " "); + + size = p->last_sector - p->first_sector + 1; + fp_printf(fp, "%7d%c", size, + ((size/(sectors*heads)) != ((float)size/(sectors*heads)) ? + '*' : ' ')); + + fp_printf(fp, " "); + + if (p->id == UNUSABLE) + sprintf(part_str, "%.16s", "Unusable"); + else if (p->id == FREE_SPACE) + sprintf(part_str, "%.16s", "Free Space"); + else if (partition_type[p->id]) + sprintf(part_str, "%.16s (%02X)", partition_type[p->id], p->id); + else + sprintf(part_str, "%.16s (%02X)", "Unknown", p->id); + fp_printf(fp, "%-21.21s", part_str); + + fp_printf(fp, " "); + + if (p->flags == ACTIVE_FLAG) + fp_printf(fp, "Boot (%02X)", p->flags); + else if (p->flags != 0) + fp_printf(fp, "Unknown (%02X)", p->flags); + else + fp_printf(fp, "None (%02X)", p->flags); + + fp_printf(fp, "\n"); +} + +void print_p_info(void) +{ + char fname[LINE_LENGTH]; + FILE *fp; + int i, to_file, pext = (ext_info.id == EXTENDED); + + if (print_only) { + fp = stdout; + to_file = TRUE; + } else { + mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X, + "Enter filename or press RETURN to display on screen: "); + + if ((to_file = get_string(fname, LINE_LENGTH, NULL)) < 0) + return; + + if (to_file) { + if ((fp = fopen(fname, "w")) == NULL) { + char errstr[LINE_LENGTH]; + sprintf(errstr, PRINT_OPEN_ERR, fname); + print_warning(errstr); + return; + } + } else { + fp = NULL; + erase(); + move(0, 0); + } + } + + fp_printf(fp, "Partition Table for %s\n", disk_device); + fp_printf(fp, "\n"); + fp_printf(fp, " First Last\n"); + fp_printf(fp, " # Type Sector Sector Offset Length Filesystem Type (ID) Flags\n"); + fp_printf(fp, "-- ------- -------- -------- ------- -------- --------------------- ---------\n"); + + for (i = 0; i < num_parts; i++) { + if (pext && (p_info[i].first_sector >= ext_info.first_sector)) { + print_p_info_entry(fp,&ext_info); + pext = FALSE; + } + print_p_info_entry(fp, &(p_info[i])); + } + + if (to_file) { + if (!print_only) + fclose(fp); + } else { + menuContinue(); + } +} + +void print_part_entry(FILE *fp, int num, partition_info *pi) +{ + int first = 0, start = 0, end = 0, size = 0; + int ss = 0, sh = 0, sc = 0; + int es = 0, eh = 0, ec = 0; + int flags = 0, id = 0; + + if (pi != NULL) { + flags = pi->flags; + id = pi->id; + + if (IS_LOGICAL(num)) + first = pi->offset; + else + first = pi->first_sector + pi->offset; + + start = pi->first_sector + pi->offset; + end = pi->last_sector; + size = end - start + 1; + if ((start/(sectors*heads)) > 1023) + start = heads*sectors*1024 - 1; + if ((end/(sectors*heads)) > 1023) + end = heads*sectors*1024 - 1; + + ss = start % sectors + 1; + start /= sectors; + sh = start % heads; + sc = start / heads; + + es = end % sectors + 1; + end /= sectors; + eh = end % heads; + ec = end / heads; + } + + fp_printf(fp, "%2d 0x%02X %4d %4d %4d 0x%02X %4d %4d %4d %7d %7d\n", + num+1, flags, sh, ss, sc, id, eh, es, ec, first, size); +} + + +void print_part_table(void) +{ + int i, j, to_file; + char fname[LINE_LENGTH]; + FILE *fp; + + if (print_only) { + fp = stdout; + to_file = TRUE; + } else { + mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X, + "Enter filename or press RETURN to display on screen: "); + + if ((to_file = get_string(fname, LINE_LENGTH, NULL)) < 0) + return; + + if (to_file) { + if ((fp = fopen(fname, "w")) == NULL) { + char errstr[LINE_LENGTH]; + sprintf(errstr, PRINT_OPEN_ERR, fname); + print_warning(errstr); + return; + } + } else { + fp = NULL; + erase(); + move(0, 0); + } + } + + fp_printf(fp, "Partition Table for %s\n", disk_device); + fp_printf(fp, "\n"); + fp_printf(fp, " ---Starting--- ----Ending---- Start Number\n"); + fp_printf(fp, " # Flags Head Sect Cyl ID Head Sect Cyl Sector Sectors\n"); + fp_printf(fp, "-- ----- ---- ---- ---- ---- ---- ---- ---- ------- -------\n"); + + for (i = 0; i < 4; i++) { + for (j = 0; + j < num_parts && (p_info[j].id <= 0 || p_info[j].num != i); + j++); + if (j < num_parts) { + print_part_entry(fp, i, &(p_info[j])); + } else if (ext_info.id == EXTENDED && ext_info.num == i) { + print_part_entry(fp, i, &ext_info); + } else { + print_part_entry(fp, i, NULL); + } + } + + for (i = 0; i < num_parts; i++) + if (IS_LOGICAL(p_info[i].num)) + print_part_entry(fp, p_info[i].num, &(p_info[i])); + + if (to_file) { + if (!print_only) + fclose(fp); + } else { + menuContinue(); + } +} + +void print_tables(void) +{ + int done = FALSE; + + static struct MenuItem menuFormat[]= + { + { 'r', "Raw", "Print the table using raw data format" }, + { 's', "Sectors", "Print the table ordered by sectors" }, + { 't', "Table", "Just print the partition table" }, + { ESC, "Cancel", "Don't print the table" }, + { 0, NULL, NULL } + }; + + while (!done) + switch ( toupper(menuSimple( menuFormat, 2)) ) { + case 'R': + print_raw_table(); + done = TRUE; + break; + case 'S': + print_p_info(); + done = TRUE; + break; + case 'T': + print_part_table(); + done = TRUE; + break; + case ESC: + done = TRUE; + break; + } +} + +#define END_OF_HELP "EOHS!" +#define NEW_HELP_SCREEN "SNHS!" +void display_help() +{ + char *help_text[] = { + "Help Screen for cfdisk " VERSION, + "", + "This is cfdisk, a curses based disk partitioning programs, which", + "allows you to create, delete and modify partitions on your hard", + "disk drive.", + "", + "Copyright (C) 1994 Kevin E. Martin", + "", + "Command Meaning", + "------- -------", + " b Toggle bootable flag of the current partition", + " d Delete the current partition", + " g Change cylinders, heads, sectors-per-track parameters", + " WARNING: This option should only be used by people who", + " know what they are doing.", + " h Print this screen", + " m Maximize disk usage of the current partition", + " Note: This may make the partition incompatible with", + " DOS, OS/2, ...", + " n Create new partition from free space", + " p Print partition table to the screen or to a file", + " There are several different formats for the partition", + " that you can choose from:", + " r - Raw data (exactly what would be written to disk)", + " s - Table ordered by sectors", + " t - Table in raw format", + " q Quit program without writing partition table", + " t Change the filesystem type", + " u Change units of the partition size display", + " Rotates through Mb, sectors and cylinders", + " W Write partition table to disk (must enter upper case W)", + " Since this might destroy data on the disk, you must", + " either confirm or deny the write by entering `yes' or", + " `no'", + "Up Arrow Move cursor to the previous partition", + "Down Arrow Move cursor to the next partition", + "CTRL-L Redraws the screen", + " ? Print this screen", + "", + "Note: All of the commands can be entered with either upper or lower", + "case letters (except for Writes).", + END_OF_HELP + }; + + int cur_line = 0; + FILE *fp = NULL; + + erase(); + move(0, 0); + while (strcmp(help_text[cur_line], END_OF_HELP)) + if (!strcmp(help_text[cur_line], NEW_HELP_SCREEN)) { + menuContinue(); + erase(); + move(0, 0); + cur_line++; + } else + fp_printf(fp, "%s\n", help_text[cur_line++]); + + menuContinue(); +} + +int change_geometry(void) +{ + int ret_val = FALSE; + int done = FALSE; + char def[LINE_LENGTH]; + char response[LINE_LENGTH]; + int tmp_val; + + while (!done) { + static struct MenuItem menuGeometry[]= + { + { 'c', "Cylinders", "Change cylinder geometry" }, + { 'h', "Heads", "Change head geometry" }, + { 's', "Sectors", "Change sector geometry" }, + { 'd', "Done", "Done with changing geometry" }, + { 0, NULL, NULL } + }; + move(COMMAND_LINE_Y, COMMAND_LINE_X); + clrtoeol(); + refresh(); + + clear_warning(); + + switch (toupper( menuSimple(menuGeometry, 3) )) { + case 'C': + sprintf(def, "%d", cylinders); + mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X, + "Enter the number of cylinders: "); + if (get_string(response, LINE_LENGTH, def) > 0) { + tmp_val = atoi(response); + if (tmp_val > 0 && tmp_val <= MAX_CYLINDERS) { + cylinders = tmp_val; + ret_val = TRUE; + } else + print_warning(BAD_CYLINDERS); + } + break; + case 'H': + sprintf(def, "%d", heads); + mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X, + "Enter the number of heads: "); + if (get_string(response, LINE_LENGTH, def) > 0) { + tmp_val = atoi(response); + if (tmp_val > 0 && tmp_val <= MAX_HEADS) { + heads = tmp_val; + ret_val = TRUE; + } else + print_warning(BAD_HEADS); + } + break; + case 'S': + sprintf(def, "%d", sectors); + mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X, + "Enter the number of sectors per track: "); + if (get_string(response, LINE_LENGTH, def) > 0) { + tmp_val = atoi(response); + if (tmp_val > 0 && tmp_val <= MAX_SECTORS) { + sectors = tmp_val; + ret_val = TRUE; + } else + print_warning(BAD_SECTORS); + } + break; + case ESC: + case 'D': + done = TRUE; + break; + default: + putchar(BELL); + break; + } + } + + if (ret_val) { + if (p_info[num_parts-1].last_sector > heads*sectors*cylinders-1) { + while (p_info[num_parts-1].first_sector > heads*sectors*cylinders-1) { + if (p_info[num_parts-1].id == FREE_SPACE || + p_info[num_parts-1].id == UNUSABLE) + remove_part(num_parts-1); + else + del_part(num_parts-1); + } + + p_info[num_parts-1].last_sector = heads*sectors*cylinders - 1; + + if (ext_info.last_sector > heads*sectors*cylinders-1) + ext_info.last_sector = heads*sectors*cylinders - 1; + } else if (p_info[num_parts-1].last_sector < heads*sectors*cylinders-1) { + if (p_info[num_parts-1].id == FREE_SPACE || + p_info[num_parts-1].id == UNUSABLE) { + p_info[num_parts-1].last_sector = heads*sectors*cylinders - 1; + } else { + insert_part(num_parts, PRI_OR_LOG, FREE_SPACE, 0, + p_info[num_parts-1].last_sector+1, + heads*sectors*cylinders-1, 0); + } + } + + /* Make sure the partitions are correct */ + check_part_info(); + } + + return ret_val; +} + +void change_id(int i) +{ + char id[LINE_LENGTH], def[LINE_LENGTH]; + int num_types = 0; + int num_across, num_down; + int len, new_id = LINUX; + int y_start, y_end; + int j, pos; + + for (num_types = 0, j = 1; j < NUM_PART_TYPES; j++) + if (partition_type[j]) + num_types++; + + num_across = COLS/COL_ID_WIDTH; + num_down = (((float)num_types)/num_across + 1); + y_start = COMMAND_LINE_Y - 1 - num_down; + if (y_start > DISK_TABLE_START+cur_part+4) + y_start = DISK_TABLE_START+cur_part+4; + y_end = y_start + num_down - 1; + + for (j = y_start - 1; j <= y_end + 1; j++) { + move(j, 0); + clrtoeol(); + } + + for (pos = 0, j = 1; j < NUM_PART_TYPES; j++) + if (partition_type[j]) { + move(y_start + pos % num_down, (pos/num_down)*COL_ID_WIDTH + 1); + printw("%02X %-16.16s", j, partition_type[j]); + pos++; + } + + sprintf(def, "%02X", new_id); + mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X, "Enter filesystem type: "); + if ((len = get_string(id, 2, def)) <= 0 && len != GS_DEFAULT) + return; + + if (len != GS_DEFAULT) { + if (!isxdigit(id[0])) + return; + new_id = (isdigit(id[0]) ? id[0] - '0' : tolower(id[0]) - 'a' + 10); + if (len == 2) + if (isxdigit(id[1])) + new_id = new_id*16 + + (isdigit(id[1]) ? id[1] - '0' : tolower(id[1]) - 'a' + 10); + else + return; + } + + if (new_id == 0) + print_warning(ID_EMPTY); + else if (new_id == EXTENDED) + print_warning(ID_EXT); + else + p_info[i].id = new_id; +} + +void draw_partition(int i) +{ + int size, j; + int y = i + DISK_TABLE_START + 2 - (cur_part/NUM_ON_SCREEN)*NUM_ON_SCREEN; + + if (!arrow_cursor) { + move(y, 0); + for (j = 0; j < COLS; j++) + addch(' '); + } + + if (p_info[i].id > 0) { + mvprintw(y, NAME_START, + "%s%d", disk_device, p_info[i].num+1); + if (p_info[i].flags) { + if (p_info[i].flags == ACTIVE_FLAG) + mvaddstr(y, FLAGS_START, "Boot"); + else + mvprintw(y, FLAGS_START, "Unk(%02X)", p_info[i].flags); + if (p_info[i].first_sector == 0 || IS_LOGICAL(p_info[i].num)) { + if (p_info[i].offset != sectors) + addstr(", NC"); + } else { + if (p_info[i].offset != 0) + addstr(", NC"); + } + } else { + if (p_info[i].first_sector == 0 || IS_LOGICAL(p_info[i].num)) { + if (p_info[i].offset != sectors) + mvaddstr(y, FLAGS_START, "NC"); + } else { + if (p_info[i].offset != 0) + mvaddstr(y, FLAGS_START, "NC"); + } + } + } + mvaddstr(y, PTYPE_START, + (p_info[i].id == UNUSABLE ? "" : + (IS_LOGICAL(p_info[i].num) ? "Logical" : + (p_info[i].num >= 0 ? "Primary" : + (p_info[i].num == PRI_OR_LOG ? "Pri/Log" : + (p_info[i].num == PRIMARY ? "Primary" : "Logical")))))); + if (p_info[i].id == UNUSABLE) + mvaddstr(y, FSTYPE_START, "Unusable"); + else if (p_info[i].id == FREE_SPACE) + mvaddstr(y, FSTYPE_START, "Free Space"); + else if (partition_type[p_info[i].id]) + mvaddstr(y, FSTYPE_START, partition_type[p_info[i].id]); + else + mvprintw(y, FSTYPE_START, "Unknown (%02X)", p_info[i].id); + + size = p_info[i].last_sector - p_info[i].first_sector + 1; + if (display_units == SECTORS) + mvprintw(y, SIZE_START, "%9d", size); + else if (display_units == CYLINDERS) + mvprintw(y, SIZE_START, "%9d", size/(sectors*heads)); + else + mvprintw(y, SIZE_START, "%9.2f", ceiling(size/20.48)/100); + if (((size/(sectors*heads)) != ceiling(size/(sectors*(float)heads))) || + ((p_info[i].first_sector/(sectors*heads)) != + ceiling(p_info[i].first_sector/(sectors*heads)))) + mvprintw(y, COLUMNS-1, "*"); +} + +void init_const(void) +{ + if (!defined) { + NAME_START = (((float)NAME_START)/COLUMNS)*COLS; + FLAGS_START = (((float)FLAGS_START)/COLUMNS)*COLS; + PTYPE_START = (((float)PTYPE_START)/COLUMNS)*COLS; + FSTYPE_START = (((float)FSTYPE_START)/COLUMNS)*COLS; + SIZE_START = (((float)SIZE_START)/COLUMNS)*COLS; + COMMAND_LINE_X = (((float)COMMAND_LINE_X)/COLUMNS)*COLS; + + COMMAND_LINE_Y = LINES - 4; + WARNING_START = LINES - 2; + + if ((NUM_ON_SCREEN = COMMAND_LINE_Y - DISK_TABLE_START - 3) <= 0) + NUM_ON_SCREEN = 1; + + COLUMNS = COLS; + defined = TRUE; + } +} + +void draw_screen(void) +{ + int i; + char *line; + + line = (char *)malloc((COLS+1)*sizeof(char)); + + if (warning_last_time) { + for (i = 0; i < COLS; i++) { + move(WARNING_START, i); + line[i] = inch(); + } + line[COLS] = 0; + } + + erase(); + + if (warning_last_time) + mvaddstr(WARNING_START, 0, line); + + + sprintf(line, "cfdisk %s", VERSION); + mvaddstr(HEADER_START, (COLS-strlen(line))/2, line); + sprintf(line, "Disk Drive: %s", disk_device); + mvaddstr(HEADER_START+2, (COLS-strlen(line))/2, line); + sprintf(line, "Heads: %d Sectors per Track: %d Cylinders: %d", + heads, sectors, cylinders); + mvaddstr(HEADER_START+3, (COLS-strlen(line))/2, line); + + mvaddstr(DISK_TABLE_START, NAME_START, "Name"); + mvaddstr(DISK_TABLE_START, FLAGS_START, "Flags"); + mvaddstr(DISK_TABLE_START, PTYPE_START, "Part Type"); + mvaddstr(DISK_TABLE_START, FSTYPE_START, "FS Type"); + if (display_units == SECTORS) + mvaddstr(DISK_TABLE_START, SIZE_START, " Sectors"); + else if (display_units == CYLINDERS) + mvaddstr(DISK_TABLE_START, SIZE_START, "Cylinders"); + else + mvaddstr(DISK_TABLE_START, SIZE_START, "Size (MB)"); + + move(DISK_TABLE_START+1, 1); + for (i = 1; i < COLS-1; i++) + addch('-'); + + if (NUM_ON_SCREEN >= num_parts) + for (i = 0; i < num_parts; i++) + draw_partition(i); + else + for (i = (cur_part/NUM_ON_SCREEN)*NUM_ON_SCREEN; + i < NUM_ON_SCREEN + (cur_part/NUM_ON_SCREEN)*NUM_ON_SCREEN && + i < num_parts; + i++) + draw_partition(i); + + free(line); +} + +int draw_cursor(int move) +{ + if (move != 0 && (cur_part + move < 0 || cur_part + move >= num_parts)) + return -1; + + if (arrow_cursor) + mvaddstr(DISK_TABLE_START + cur_part + 2 + - (cur_part/NUM_ON_SCREEN)*NUM_ON_SCREEN, 0, " "); + else + draw_partition(cur_part); + + cur_part += move; + + if (((cur_part - move)/NUM_ON_SCREEN)*NUM_ON_SCREEN != + (cur_part/NUM_ON_SCREEN)*NUM_ON_SCREEN) + draw_screen(); + + if (arrow_cursor) + mvaddstr(DISK_TABLE_START + cur_part + 2 + - (cur_part/NUM_ON_SCREEN)*NUM_ON_SCREEN, 0, "-->"); + else { + standout(); + draw_partition(cur_part); + standend(); + } + + return 0; +} + +void die(int dummy) +{ + signal(SIGINT, old_SIGINT); + signal(SIGTERM, old_SIGTERM); + mvcur(0, COLS-1, LINES-1, 0); + nl(); + endwin(); + fdexit(0); +} + +void do_curses_fdisk(void) +{ + int done = FALSE; + char command; + + static struct MenuItem menuMain[]= + { + { 'b', "Bootable", "Toggle bootable flag of the current partition" }, + { 'd', "Delete", "Delete the current partition" }, + { 'g', "Geometry", "Change disk geometry (experts only)" }, + { 'h', "Help", "Print help screen" }, + { 'm', "Maximize", "Maximize disk usage of the current partition (experts only)" }, + { 'n', "New", "Create new partition from free space" }, + { 'p', "Print", "Print partition table to the screen or to a file" }, + { 'q', "Quit", "Quit program without writing partition table" }, + { 't', "Type", "Change the filesystem type (DOS, Linux, OS/2 and so on)" }, + { 'u', "Units", "Change units of the partition size display (MB, sect, cyl)" }, + { 'W', "Write", "Write partition table to disk (this might destroy data)" }, + { 0, NULL, NULL } + }; + + initscr(); + old_SIGINT = signal(SIGINT, die); + old_SIGTERM = signal(SIGTERM, die); +#ifdef DEBUG + signal(SIGINT, old_SIGINT); + signal(SIGTERM, old_SIGTERM); +#endif + + cbreak(); + noecho(); + nonl(); + + init_const(); + + fill_p_info(); + + draw_screen(); + + while (!done) { + (void)draw_cursor(0); + + if (p_info[cur_part].id == FREE_SPACE) + command = menuSelect(COMMAND_LINE_Y, COMMAND_LINE_X, menuMain, 8, "hnpquW", + MENU_HORIZ | MENU_BUTTON | MENU_ACCEPT_OTHERS, 0); + else if (p_info[cur_part].id > 0) + command = menuSelect(COMMAND_LINE_Y, COMMAND_LINE_X, menuMain, 8, "bdhmpqtuW", + MENU_HORIZ | MENU_BUTTON | MENU_ACCEPT_OTHERS, 0); + else + command = menuSelect(COMMAND_LINE_Y, COMMAND_LINE_X, menuMain, 8, "hpquW", + MENU_HORIZ | MENU_BUTTON | MENU_ACCEPT_OTHERS, 0); + + switch ( command ) { + case 'B': + case 'b': + if (p_info[cur_part].id > 0) + p_info[cur_part].flags ^= 0x80; + else + print_warning(NO_FLAGS); + break; + case 'D': + case 'd': + if (p_info[cur_part].id > 0) { + del_part(cur_part); + if (cur_part >= num_parts) + cur_part = num_parts - 1; + draw_screen(); + } else + print_warning(DEL_EMPTY); + break; + case 'G': + case 'g': + if (change_geometry()) + draw_screen(); + break; + case 'M': + case 'm': + if (p_info[cur_part].id > 0) { + if (p_info[cur_part].first_sector == 0 || + IS_LOGICAL(p_info[cur_part].num)) { + if (p_info[cur_part].offset == sectors) + p_info[cur_part].offset = 1; + else + p_info[cur_part].offset = sectors; + draw_screen(); + } else if (p_info[cur_part].offset != 0) + p_info[cur_part].offset = 0; + else + print_warning(MAX_UNMAXABLE); + } else + print_warning(MAX_UNMAXABLE); + break; + case 'N': + case 'n': + if (p_info[cur_part].id == FREE_SPACE) { + new_part(cur_part); + draw_screen(); + } else if (p_info[cur_part].id == UNUSABLE) + print_warning(ADD_UNUSABLE); + else + print_warning(ADD_EXISTS); + break; + case 'P': + case 'p': + print_tables(); + draw_screen(); + break; + case 'Q': + case 'q': + done = TRUE; + break; + case 'T': + case 't': + if (p_info[cur_part].id > 0) { + change_id(cur_part); + draw_screen(); + } else + print_warning(TYPE_EMPTY); + break; + case 'U': + case 'u': + if (display_units == MEGABYTES) + display_units = SECTORS; + else if (display_units == SECTORS) + display_units = CYLINDERS; + else if (display_units == CYLINDERS) + display_units = MEGABYTES; + draw_screen(); + break; + case 'W': + write_part_table(); + break; + case 'H': + case 'h': + case '?': + display_help(); + draw_screen(); + break; + case MENU_UP : /* Up arrow */ + if (!draw_cursor(-1)) + command = 0; + else + print_warning(NO_MORE_PARTS); + break; + case MENU_DOWN : /* Down arrow */ + if (!draw_cursor(1)) + command = 0; + else + print_warning(NO_MORE_PARTS); + break; + case REDRAWKEY: + clear(); + draw_screen(); + break; + default: + print_warning(BAD_COMMAND); + putchar(BELL); /* CTRL-G */ + } + } + + signal(SIGINT, old_SIGINT); + signal(SIGTERM, old_SIGTERM); + mvcur(0, COLS-1, LINES-1, 0); + nl(); + endwin(); + fdexit(0); +} + +void copyright(void) +{ + fprintf(stderr, "Copyright (C) 1994 Kevin E. Martin\n"); +} + +void usage(char *prog_name) +{ + fprintf(stderr, + "%s: [-avz] [-c # cylinders] [-h # heads] [-s # sectors/track]\n", + prog_name); + fprintf(stderr, + "[ -P opt ] device\n"); + copyright(); +} + +void main(int argc, char **argv) +{ + char c; + int i, len; + + while ((c = getopt(argc, argv, "ac:h:s:vzP:")) != EOF) + switch (c) { + case 'a': + arrow_cursor = TRUE; + break; + case 'c': + cylinders = atoi(optarg); + if (cylinders <= 0 || cylinders > MAX_CYLINDERS) { + fprintf(stderr, "%s: %s\n", argv[0], BAD_CYLINDERS); + exit(1); + } + break; + case 'h': + heads = atoi(optarg); + if (heads <= 0 || heads > MAX_HEADS) { + fprintf(stderr, "%s: %s\n", argv[0], BAD_HEADS); + exit(1); + } + break; + case 's': + sectors = atoi(optarg); + if (sectors <= 0 || sectors > MAX_SECTORS) { + fprintf(stderr, "%s: %s\n", argv[0], BAD_SECTORS); + exit(1); + } + break; + case 'v': + fprintf(stderr, "cfdisk %s\n", VERSION); + copyright(); + exit(0); + case 'z': + zero_table = TRUE; + break; + case 'P': + len = strlen(optarg); + for (i = 0; i < len; i++) { + switch (optarg[i]) { + case 'r': + print_only |= PRINT_RAW_TABLE; + break; + case 's': + print_only |= PRINT_SECTOR_TABLE; + break; + case 't': + print_only |= PRINT_PARTITION_TABLE; + break; + default: + usage(argv[0]); + break; + } + } + break; + default: + usage(argv[0]); + exit(1); + } + + if (argc-optind == 1) + disk_device = argv[optind]; + else if (argc-optind != 0) { + usage(argv[0]); + exit(1); + } else if ((fd = open(DEFAULT_DEVICE, O_RDWR)) < 0) + disk_device = ALTERNATE_DEVICE; + else close(fd); + + if (print_only) { + fill_p_info(); + if (print_only & PRINT_RAW_TABLE) + print_raw_table(); + if (print_only & PRINT_SECTOR_TABLE) + print_p_info(); + if (print_only & PRINT_PARTITION_TABLE) + print_part_table(); + } else + do_curses_fdisk(); +} diff --git a/disk-utils/fdformat.8 b/disk-utils/fdformat.8 new file mode 100644 index 00000000..9e24b0ae --- /dev/null +++ b/disk-utils/fdformat.8 @@ -0,0 +1,59 @@ +.\" Copyright 1992, 1993 Rickard E. Faith (faith@cs.unc.edu) +.\" May be distributed under the GNU General Public License +.TH FDFORMAT 8 "1 February 1993" "Linux 0.99" "Linux Programmer's Manual" +.SH NAME +fdformat \- Low-level formats a floppy disk +.SH SYNOPSIS +.B fdformat +.B "[ \-n ]" +device +.SH DESCRIPTION +.B fdformat +does a low level format on a floppy disk. +.I device +is usually one of the following (for floppy devices, the major = 2, and the +minor is shown for informational purposes only): +.sp +.nf +.RS +/dev/fd0d360 (minor = 4) +/dev/fd0h1200 (minor = 8) +/dev/fd0D360 (minor = 12) +/dev/fd0H360 (minor = 12) +/dev/fd0D720 (minor = 16) +/dev/fd0H720 (minor = 16) +/dev/fd0h360 (minor = 20) +/dev/fd0h720 (minor = 24) +/dev/fd0H1440 (minor = 28) + +/dev/fd1d360 (minor = 5) +/dev/fd1h1200 (minor = 9) +/dev/fd1D360 (minor = 13) +/dev/fd1H360 (minor = 13) +/dev/fd1D720 (minor = 17) +/dev/fd1H720 (minor = 17) +/dev/fd1h360 (minor = 21) +/dev/fd1h720 (minor = 25) +/dev/fd1H1440 (minor = 29) +.RE +.fi + +The generic floppy devices, /dev/fd0 and /dev/fd1, will fail to work with +.B fdformat +when a non-standard format is being used, or if the format has not been +autodetected earlier. In this case, use +.BR setfdprm (8) +to load the disk parameters. + +.SH OPTIONS +.TP +.B \-n +No verify. This option will disable the verification that is performed +after the format. +.SH "SEE ALSO" +.BR fd (4), +.BR setfdprm (8), +.BR mkfs (8), +.BR emkfs (8) +.SH AUTHOR +Werner Almesberger (almesber@nessie.cs.id.ethz.ch) diff --git a/disk-utils/fdformat.c b/disk-utils/fdformat.c new file mode 100644 index 00000000..7fb78af6 --- /dev/null +++ b/disk-utils/fdformat.c @@ -0,0 +1,109 @@ +/* fdformat.c - Low-level formats a floppy disk. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int ctrl; +struct floppy_struct param; + +#define FLOPPY_MAJOR 2 +#define SECTOR_SIZE 512 +#define PERROR(msg) { perror(msg); exit(1); } + +static void format_disk(char *name) +{ + struct format_descr descr; + int track; + + printf("Formatting ... "); + fflush(stdout); + if (ioctl(ctrl,FDFMTBEG,NULL) < 0) PERROR("\nioctl(FDFMTBEG)"); + for (track = 0; track < param.track; track++) { + descr.track = track; + descr.head = 0; + if (ioctl(ctrl,FDFMTTRK,(int) &descr) < 0) PERROR("\nioctl(FDFMTTRK)"); + printf("%3d\b\b\b",track); + fflush(stdout); + if (param.head == 2) { + descr.head = 1; + if (ioctl(ctrl,FDFMTTRK,(int) &descr) < 0) + PERROR("\nioctl(FDFMTTRK)"); + } + } + if (ioctl(ctrl,FDFMTEND,NULL) < 0) PERROR("\nioctl(FDFMTEND)"); + printf("done\n"); +} + + +static void verify_disk(char *name) +{ + unsigned char *data; + int fd,cyl_size,cyl,count; + + cyl_size = param.sect*param.head*512; + if ((data = (unsigned char *) malloc(cyl_size)) == NULL) PERROR("malloc"); + printf("Verifying ... "); + fflush(stdout); + if ((fd = open(name,O_RDONLY)) < 0) PERROR(name); + for (cyl = 0; cyl < param.track; cyl++) { + printf("%3d\b\b\b",cyl); + fflush(stdout); + if (read(fd,data,cyl_size) != cyl_size) PERROR("read"); + for (count = 0; count < cyl_size; count++) + if (data[count] != FD_FILL_BYTE) { + printf("bad data in cyl %d\nContinuing ... ",cyl); + fflush(stdout); + break; + } + } + printf("done\n"); + if (close(fd) < 0) PERROR("close"); +} + + +static void usage(char *name) +{ + char *this; + + if (this = strrchr(name,'/')) name = this+1; + fprintf(stderr,"usage: %s [ -n ] device\n",name); + exit(1); +} + + +int main(int argc,char **argv) +{ + int verify; + char *name; + struct stat st; + + name = argv[0]; + verify = 1; + if (argc > 1 && argv[1][0] == '-') { + if (argv[1][1] != 'n') usage(name); + verify = 0; + argc--; + argv++; + } + if (argc != 2) usage(name); + if (lstat(argv[1],&st) < 0) PERROR(argv[1]); + if (!S_ISBLK(st.st_mode) || MAJOR(st.st_rdev) != FLOPPY_MAJOR) { + fprintf(stderr,"%s: not a floppy device\n",argv[1]); + exit(1); + } + if (access(argv[1],W_OK) < 0) PERROR(argv[1]); + if ((ctrl = open(argv[1],3)) < 0) PERROR(argv[1]); + if (ioctl(ctrl,FDGETPRM,(int) ¶m) < 0) PERROR("ioctl(FDGETPRM)"); + printf("%sle-sided, %d tracks, %d sec/track. Total capacity %d kB.\n", + param.head ? "Doub" : "Sing",param.track,param.sect,param.size >> 1); + format_disk(argv[1]); + if (verify) verify_disk(argv[1]); + return 0; +} diff --git a/disk-utils/fdisk.8 b/disk-utils/fdisk.8 new file mode 100644 index 00000000..d1891bb2 --- /dev/null +++ b/disk-utils/fdisk.8 @@ -0,0 +1,166 @@ +.\" Copyright 1992, 1993 Rickard E. Faith (faith@cs.unc.edu) +.\" May be distributed under the GNU General Public License +.TH FDISK 8 "Tue Mar 22 01:00:00 1994" "Linux 1.0" "Linux Programmer's Manual" +.SH NAME +fdisk \- Partition table manipulator for Linux +.SH SYNOPSIS +.B fdisk +.B "[ \-l ] [ \-v ] [ \-s partition] [" +device +.B ] +.SH DESCRIPTION +.B fdisk +is a menu driven program for manipulation of the hard disk partition table. +The +.I device +is usually one of the following: +.sp +.nf +.RS +/dev/hda +/dev/hdb +/dev/sda +/dev/sdb +.RE +.fi +The +.I partition +is a +.I device +name followed by a partition number. For example, +.B /dev/hda1 +is the first partition on the first hard disk in the system. + +If possible, +.B fdisk +will obtain the disk geometry automatically. This is +.I not +necessarily the +.I physical +disk geometry, but is the disk geometry that MS-DOS uses for the partition +table. If +.B fdisk +warns you that you need to set the disk geometry, please believe this +statement, and set the geometry. This should only be necessary with +certain SCSI host adapters (the drivers for which are rapidly being +modified to provide geometry information automatically). + +Whenever a partition table is printed out, a consistency check is performed +on the partition table entries. This check verifies that the physical and +logical start and end points are identical, and that the partition starts +and ends on a cylinder boundary (except for the first partition). + +Old versions of fdisk (all versions prior to 1.1r [including 0.93]) +incorrectly mapped the cylinder/head/sector specification onto absolute +sectors. This may result in the first partition on a drive failing the +consistency check. If you use LILO to boot, this situation can be ignored. +However, there are reports that the OS/2 boot manager will not boot a +partition with inconsistent data. + +Some versions of MS-DOS create a first partition which does not begin +on a cylinder boundary, but on sector 2 of the first cylinder. +Partitions beginning in cylinder 1 cannot begin on a cylinder boundary, but +this is unlikely to cause difficulty unless you have OS/2 on your machine. + +In version 1.1r, a BLKRRPART ioctl() is performed before exiting when the +partition table is updated. This is primarily to ensure that removable +SCSI disks have their partition table information updated. If the kernel +does not update its partition table information, fdisk warns you to +reboot. If you do not reboot your system after receiving such a warning, +you may lose or corrupt the data on the disk. Sometimes BLKRRPART fails +silently, when installing Linux, you should +.I always +reboot after editing the partition table. + +.SH "DOS 6.x WARNING" + +The DOS 6.x FORMAT command looks for some information in the first +sector of the data area of the partition, and treats this information +as more reliable than the information in the partition table. DOS +FORMAT expects DOS FDISK to clear the first 512 bytes of the data area +of a partition whenever a size change occurs. DOS FORMAT will look at +this extra information even if the /U flag is given -- we consider +this a bug in DOS FORMAT and DOS FDISK. + +The bottom line is that if you use cfdisk or fdisk to change the size of a +DOS partition table entry, then you must also use +.B dd +to zero the first 512 bytes of that partition before using DOS FORMAT to +format the partition. For example, if you were using cfdisk to make a DOS +partition table entry for /dev/hda1, then (after exiting fdisk or cfdisk +and rebooting Linux so that the partition table information is valid) you +would use the command "dd if=/dev/zero of=/dev/hda1 bs=512 count=1" to zero +the first 512 bytes of the partition. +.B BE EXTREMELY CAREFUL +if you use the +.B dd +command, since a small typo can make all of the data on your disk useless. + +.B BE EXTREMELY CAREFUL +if you use the +.B dd +command, since a small typo can make all of the data on your disk useless. + +For best resutls, you should always use an OS-specific partition table +program. For example, you should make DOS partitions with the DOS FDISK +program and Linux partitions with the Linux fdisk or Linux cfdisk program. + +.SH OPTIONS +.TP +.B \-v +Prints version number of +.B fdisk +program. +.TP +.B \-l +Lists the partition tables for +.BR /dev/hda , +.BR /dev/hdb , +.BR /dev/sda , +.BR /dev/sdb , +.BR /dev/sdc , +.BR /dev/sdd , +.BR /dev/sde , +.BR /dev/sdf , +.BR /dev/sdg , +.BR /dev/sdh , +and then exits. +.TP +.BI \-s partition +If the +.I partition +is not a DOS partition (i.e., the partition id is greater than 10), then +the +.I size +of that partition is printed on the standard output. This value is +normally used as an argument to the +.BR mkfs (8) +program to specify the size of the partition which will be formatted. +.SH BUGS +Although this man page (written by faith@cs.unc.edu) is poor, there is +.I excellent +documentation in the README.fdisk file (written by LeBlanc@mcc.ac.uk) that +should always be with the fdisk distribution. If you cannot find this file +in the +.I util-linux-* +directory or with the +.I fdisk.c +source file, then you should write to the distributor of your version of +.B fdisk +and complain that you do not have all of the available documentation. +.SH AUTHOR +A. V. Le Blanc (LeBlanc@mcc.ac.uk) +.br +v1.0r: SCSI and extfs support added by Rik Faith (faith@cs.unc.edu) +.br +v1.1r: Bug fixes and enhancements by Rik Faith (faith@cs.unc.edu), with +special thanks to Michael Bischoff (i1041905@ws.rz.tu-bs.de or +mbi@mo.math.nat.tu-bs.de). +.br +v1.3: Latest enhancements and bug fixes by A. V. Le Blanc, including the +addition of the +.B \-s +option. +.bt +v2.0: Disks larger than 2GB are now fully supported, thanks to Remy Card's +llseek support. diff --git a/disk-utils/fdisk.c b/disk-utils/fdisk.c new file mode 100644 index 00000000..3c0a328d --- /dev/null +++ b/disk-utils/fdisk.c @@ -0,0 +1,1339 @@ +/* fdisk.c -- Partition table manipulator for Linux. + * + * Copyright (C) 1992 A. V. Le Blanc (LeBlanc@mcc.ac.uk) + * + * 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 1 or + * (at your option) any later version. + * + * Before Linux version 0.95c, this program requires a kernel patch. + * + * Modified, Tue Feb 2 18:46:49 1993, faith@cs.unc.edu to better support SCSI. + * Modified, Sat Feb 27 18:11:27 1993, faith@cs.unc.edu: added extfs support. + * Modified, Sat Mar 6 10:14:12 1993, faith@cs.unc.edu: added more comments. + * Modified, Sat Mar 6 12:25:45 1993, faith@cs.unc.edu: + * Added patches from Michael Bischoff (i1041905@ws.rz.tu-bs.de + * or mbi@mo.math.nat.tu-bs.de) to fix the following problems: + * 1) Incorrect mapping of head/sector/cylinder to absolute sector + * 2) Odd sector count causes one sector to be lost + * Modified, Sat Mar 6 12:25:52 1993, faith@cs.unc.edu: improved verification. + * Modified, Sat Apr 17 15:00:00 1993, LeBlanc@mcc.ac.uk: add -s, fix -l. + * Modified, Sat Apr 24 10:00:00 1993, LeBlanc@mcc.ac.uk: fix overlap bug. + * Modified, Wed May 5 21:30:00 1993, LeBlanc@mcc.ac.uk: errors to stderr. + * Modified, Mon Mar 21 20:00:00 1994, LeBlanc@mcc.ac.uk: + * more stderr for messages, avoid division by 0, and + * give reboot message only if ioctl(fd, BLKRRPART) fails. + * Modified, Mon Apr 25 01:01:05 1994, martin@cs.unc.edu: + * 1) Added support for DOS, OS/2, ... compatibility. We should be able + * use this fdisk to partition our drives for other operating systems. + * 2) Added a print the raw data in the partition table command. + * Modified, Wed Jun 22 21:05:30 1994, faith@cs.unc.edu: + * Added/changed a few partition type names to conform to cfdisk. + * (suggested by Sujal, smpatel@wam.umd.edu) + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#if defined(__GNUC__) || defined(HAS_LONG_LONG) +typedef long long ext2_loff_t; +#else +typedef long ext2_loff_t; +#endif + +extern ext2_loff_t ext2_llseek(unsigned int fd, + ext2_loff_t offset, + unsigned int origin); + +#define hex_val(c) ({ \ + char _c = (c); \ + isdigit(_c) ? _c - '0' : \ + tolower(_c) + 10 - 'a'; \ + }) + + +#define VERSION "2.0a (>2GB)" + +#define DEFAULT_DEVICE "/dev/hda" +#define ALTERNATE_DEVICE "/dev/sda" +#define LINE_LENGTH 80 +#define MAXIMUM_PARTS 60 +#define SECTOR_SIZE 512 +#define PART_TABLE_FLAG 0xaa55 +#define table_check(b) ((unsigned short *)((b) + 0x1fe)) +#define offset(b, n) ((struct partition *)((b) + 0x1be + \ + (n) * sizeof(struct partition))) +#define sector(s) ((s) & 0x3f) +#define cylinder(s, c) ((c) | (((s) & 0xc0) << 2)) + +#define calculate(h,s,c) (sector(s) - 1 + sectors * \ + ((h) + heads * cylinder(s,c))) +#define set_hsc(h,s,c,sector) { \ + s = sector % sectors + 1; \ + sector /= sectors; \ + h = sector % heads; \ + sector /= heads; \ + c = sector & 0xff; \ + s |= (sector >> 2) & 0xc0; \ + } + +#define cround(n) (((n) + display_factor * unit_flag) / display_factor) +#define ACTIVE_FLAG 0x80 +#define EXTENDED 5 + +#define LINUX_PARTITION 0x81 +#define LINUX_SWAP 0x82 +#define LINUX_NATIVE 0x83 + +enum failure {usage, unable_to_open, unable_to_read, unable_to_seek, + unable_to_write, out_of_memory}; + +char *disk_device = DEFAULT_DEVICE, /* hda, unless specified */ + *line_ptr, /* interactive input */ + line_buffer[LINE_LENGTH], + changed[MAXIMUM_PARTS], /* marks changed buffers */ + buffer[SECTOR_SIZE], /* first four partitions */ + *buffers[MAXIMUM_PARTS] /* pointers to buffers */ + = {buffer, buffer, buffer, buffer}; + +int fd, /* the disk */ + ext_index, /* the prime extended partition */ + listing = 0, /* no aborts for fdisk -l */ + size_flag = 0, + dos_compatible_flag = ~0, + partitions = 4; /* maximum partition + 1 */ + +uint heads, + sectors, + cylinders, + sector_offset = 1, + display_factor = 1, /* in units/sector */ + unit_flag = 1, + full_bits = 0, /* 1024 cylinders in sectors */ + extended_offset = 0, /* offset of link pointers */ + offsets[MAXIMUM_PARTS] = {0, 0, 0, 0}; + +struct partition *part_table[MAXIMUM_PARTS] /* partitions */ + = {offset(buffer, 0), offset(buffer, 1), + offset(buffer, 2), offset(buffer, 3)}, + *ext_pointers[MAXIMUM_PARTS] /* link pointers */ + = {NULL, NULL, NULL, NULL}; + +struct systypes { + unsigned char index; + char *name; + } sys_types[] = { + {0, "Empty"}, + {1, "DOS 12-bit FAT"}, + {2, "XENIX root"}, + {3, "XENIX usr"}, + {4, "DOS 16-bit <32M"}, + {EXTENDED, "Extended"}, + {6, "DOS 16-bit >=32M"}, + {7, "OS/2 HPFS"}, /* or QNX? */ + {8, "AIX"}, + {9, "AIX bootable"}, + {10, "OS/2 Boot Manager"}, + {0x40, "Venix 80286"}, + {0x51, "Novell?"}, + {0x52, "Microport"}, /* or CPM? */ + {0x63, "GNU HURD"}, /* or System V/386? */ + {0x64, "Novell"}, + {0x75, "PC/IX"}, + {0x80, "Old MINIX"}, /* Minix 1.4a and earlier */ + + {LINUX_PARTITION, "Linux/MINIX"}, /* Minix 1.4b and later */ + {LINUX_SWAP, "Linux swap"}, + {LINUX_NATIVE, "Linux native"}, + + {0x93, "Amoeba"}, + {0x94, "Amoeba BBT"}, /* (bad block table) */ + {0xa5, "BSD/386"}, + {0xb7, "BSDI fs"}, + {0xb8, "BSDI swap"}, + {0xc7, "Syrinx"}, + {0xdb, "CP/M"}, /* or Concurrent DOS? */ + {0xe1, "DOS access"}, + {0xe3, "DOS R/O"}, + {0xf2, "DOS secondary"}, + {0xff, "BBT"} /* (bad track table) */ + }; + +jmp_buf listingbuf; + +void fatal(enum failure why) +{ + char error[LINE_LENGTH], + *message = error; + + if (listing) { + close(fd); + longjmp(listingbuf, 1); + } + + switch (why) { + case usage: message = + "Usage: fdisk [-l] [-v] [-s /dev/hdxn] [/dev/hdx]\n"; + break; + case unable_to_open: + sprintf(error, "Unable to open %s\n", disk_device); + break; + case unable_to_read: + sprintf(error, "Unable to read %s\n", disk_device); + break; + case unable_to_seek: + sprintf(error, "Unable to seek on %s\n", disk_device); + break; + case unable_to_write: + sprintf(error, "Unable to write %s\n", disk_device); + break; + case out_of_memory: + message = "Unable to allocate any more memory\n"; + break; + default: message = "Fatal error\n"; + } + + fputc('\n', stderr); + fputs(message, stderr); + exit(1); +} + +void menu(void) +{ + puts("Command action\n" + " a toggle a bootable flag\n" + " c toggle the dos compatiblity flag\n" + " d delete a partition\n" + " l list known partition types\n" + " m print this menu\n" + " n add a new partition\n" + " p print the partition table\n" + " q quit without saving changes\n" + " t change a partition's system id\n" + " u change display/entry units\n" + " v verify the partition table\n" + " w write table to disk and exit\n" + " x extra functionality (experts only)" + ); +} + +void xmenu(void) +{ + puts("Command action\n" + " b move beginning of data in a partition\n" + " c change number of cylinders\n" + " d print the raw data in the partition table\n" + " e list extended partitions\n" + " h change number of heads\n" + " m print this menu\n" + " p print the partition table\n" + " q quit without saving changes\n" + " r return to main menu\n" + " s change number of sectors\n" + " w write table to disk and exit" + ); +} + +char *partition_type(unsigned char type) +{ + int high = sizeof(sys_types) / sizeof(struct systypes), + low = 0, mid; + uint tmp; + + while (high >= low) { + mid = (high + low) >> 1; + if ((tmp = sys_types[mid].index) == type) + return sys_types[mid].name; + else if (tmp < type) + low = mid + 1; + else high = mid - 1; + } + return NULL; +} + +void list_types(void) +{ + uint last[4], done = 0, next = 0, + size = sizeof(sys_types) / sizeof(struct systypes); + int i; + + for (i = 3; i >= 0; i--) + last[3 - i] = done += (size + i - done) / (i + 1); + i = done = 0; + + do { + printf("%c%2x %-15.15s", i ? ' ' : '\n', + sys_types[next].index, sys_types[next].name); + next = last[i++] + done; + if (i > 3 || next >= last[i]) { + i = 0; + next = ++done; + } + } while (done < last[0]); + putchar('\n'); +} + +void clear_partition(struct partition *p) +{ + p->boot_ind = 0; + p->head = 0; + p->sector = 0; + p->cyl = 0; + p->sys_ind = 0; + p->end_head = 0; + p->end_sector = 0; + p->end_cyl = 0; + p->start_sect = 0; + p->nr_sects = 0; +} + +void set_partition(int i, struct partition *p, uint start, uint stop, + int sys, uint offset) +{ + p->boot_ind = 0; + p->sys_ind = sys; + p->start_sect = start - offset; + p->nr_sects = stop - start + 1; + if (dos_compatible_flag && (start/(sectors*heads) > 1023)) + start = heads*sectors*1024 - 1; + set_hsc(p->head, p->sector, p->cyl, start); + if (dos_compatible_flag && (stop/(sectors*heads) > 1023)) + stop = heads*sectors*1024 - 1; + set_hsc(p->end_head, p->end_sector, p->end_cyl, stop); + changed[i] = 1; +} + +int test_c(char **m, char *mesg) +{ + int val = 0; + if (!*m) + fprintf(stderr, "You must set"); + else { + fprintf(stderr, " %s", *m); + val = 1; + } + *m = mesg; + return val; +} + +int warn_geometry(void) +{ + char *m = NULL; + int prev = 0; + if (!heads) + prev = test_c(&m, "heads"); + if (!sectors) + prev = test_c(&m, "sectors"); + if (!cylinders) + prev = test_c(&m, "cylinders"); + if (!m) + return 0; + fprintf(stderr, + "%s%s.\nYou can do this from the extra functions menu.\n", + prev ? " and " : " ", m); + return 1; +} + +uint rounded(uint calcul, uint start) +{ + uint i; + if (!full_bits) + return calcul; + while ((i = calcul + full_bits) <= start) + calcul = i; + return calcul; +} + +void update_units(void) +{ + full_bits = 1024 * heads * sectors; + if (unit_flag && full_bits) + display_factor = full_bits >> 10; + else display_factor = 1; +} + +void warn_cylinders(void) +{ + update_units(); + if (cylinders > 1024) + fprintf(stderr, "The number of cylinders for this disk is " + "set to %d.\nThis is larger than 1024, and may cause " + "problems with:\n" + "1) software that runs at boot time (e.g., LILO)\n" + "2) booting and partitioning software form other OSs\n" + " (e.g., DOS FDISK, OS/2 FDISK)\n", + cylinders); +} + +void read_extended(struct partition *p) +{ + int i; + struct partition *q; + + ext_pointers[ext_index] = part_table[ext_index]; + if (!p->start_sect) + fprintf(stderr, "Bad offset in primary extended partition\n"); + else while (p->sys_ind == EXTENDED) { + if (partitions >= MAXIMUM_PARTS) { + fprintf(stderr, + "Warning: deleting partitions after %d\n", + partitions); + clear_partition(ext_pointers[partitions - 1]); + changed[partitions - 1] = 1; + return; + } + offsets[partitions] = extended_offset + p->start_sect; + if (!extended_offset) + extended_offset = p->start_sect; + if (ext2_llseek(fd, offsets[partitions] + * SECTOR_SIZE, SEEK_SET) < 0) + fatal(unable_to_seek); + if (!(buffers[partitions] = (char *) malloc(SECTOR_SIZE))) + fatal(out_of_memory); + if (SECTOR_SIZE != read(fd, buffers[partitions], SECTOR_SIZE)) + fatal(unable_to_read); + part_table[partitions] = ext_pointers[partitions] = NULL; + q = p = offset(buffers[partitions], 0); + for (i = 0; i < 4; i++, p++) { + if (p->sys_ind == EXTENDED) + if (ext_pointers[partitions]) + fprintf(stderr, "Warning: extra link " + "pointer in partition table " + "%d\n", partitions + 1); + else + ext_pointers[partitions] = p; + else if (p->sys_ind) + if (part_table[partitions]) + fprintf(stderr, + "Warning: ignoring extra data " + "in partition table %d\n", + partitions + 1); + else + part_table[partitions] = p; + } + if (!part_table[partitions]) + if (q != ext_pointers[partitions]) + part_table[partitions] = q; + else part_table[partitions] = q + 1; + if (!ext_pointers[partitions]) + if (q != part_table[partitions]) + ext_pointers[partitions] = q; + else ext_pointers[partitions] = q + 1; + p = ext_pointers[partitions++]; + } +} + +void get_boot(void) +{ + int i; + struct hd_geometry geometry; + + partitions = 4; + if ((fd = open(disk_device, O_RDWR)) < 0) + fatal(unable_to_open); + if (SECTOR_SIZE != read(fd, buffer, SECTOR_SIZE)) + fatal(unable_to_read); + if (!ioctl(fd, HDIO_REQ, &geometry)) { + heads = geometry.heads; + sectors = geometry.sectors; + cylinders = geometry.cylinders; + if (dos_compatible_flag) + sector_offset = sectors; + warn_cylinders(); + } + else update_units(); + warn_geometry(); + + for (i = 0; i < 4; i++) + if(part_table[i]->sys_ind == EXTENDED) + if (partitions != 4) + fprintf(stderr, "Ignoring extra extended " + "partition %d\n", i + 1); + else read_extended(part_table[ext_index = i]); + + for (i = 3; i < partitions; i++) + if (*table_check(buffers[i]) != PART_TABLE_FLAG) { + fprintf(stderr, "Warning: invalid flag %04x of parti" + "tion table %d will be corrected by w(rite)\n", + *table_check(buffers[i]), i + 1); + changed[i] = 1; + } +} + +int read_line(void) +{ + if (!fgets(line_buffer, LINE_LENGTH, stdin)) + return 0; + line_ptr = line_buffer; + while (*line_ptr && !isgraph(*line_ptr)) + line_ptr++; + return *line_ptr; +} + +char read_char(char *mesg) +{ + do + fputs(mesg, stdout); + while (!read_line()); + return *line_ptr; +} + +uint read_int(uint low, uint high, char *mesg) +{ + uint i; + char ms[70]; + sprintf(ms, "%s (%d-%d): ", mesg, low, high); + + while (1) { + while (!isdigit(read_char(ms)) && + (!size_flag || *line_ptr != '+')); + if (*line_ptr == '+') { + i = atoi(++line_ptr); + while (isdigit(*line_ptr)) + line_ptr++; + switch (*line_ptr) { + case 'c': + case 'C': if (!unit_flag) + i *= heads * sectors; + break; + case 'k': + case 'K': i *= 2; + i /= display_factor; + break; + case 'm': + case 'M': i *= 2048; + i /= display_factor; + break; + default: break; + } + i += low; + } + else i = atoi(line_ptr); + if (i >= low && i <= high) + break; + } + size_flag = 0; + return i; +} + +int get_partition(int warn, int max) +{ + int i = read_int(1, max, "Partition number") - 1; + + if (warn && !part_table[i]->sys_ind) + fprintf(stderr, "Warning: partition %d has empty type\n", + i + 1); + return i; +} + +char *const str_units(void) +{ + return unit_flag ? "cylinder" : "sector"; +} + +void change_units(void) +{ + if (unit_flag = !unit_flag) + display_factor = 1; + else display_factor = heads * sectors; + update_units(); + printf("Changing display/entry units to %ss\n", + str_units()); +} + +void toggle_active(int i) +{ + struct partition *p = part_table[i]; + + if (p->sys_ind == EXTENDED && !p->boot_ind) + fprintf(stderr, + "WARNING: Partition %d is an extended partition\n", + i + 1); + if (p->boot_ind) + p->boot_ind = 0; + else p->boot_ind = ACTIVE_FLAG; + changed[i] = 1; +} + +void toggle_dos(void) +{ + dos_compatible_flag = ~dos_compatible_flag; + printf("DOS Compatibility flag is "); + if (dos_compatible_flag) + sector_offset = sectors; + else { + sector_offset = 1; + printf("not "); + } + printf("set\n"); +} + +void delete_partition(int i) +{ + struct partition *p = part_table[i], *q = ext_pointers[i]; + +/* Note that for the fifth partition (i == 4) we don't actually + * decrement partitions. + */ + + if (warn_geometry()) + return; + changed[i] = 1; + if (i < 4) { + if (p->sys_ind == EXTENDED && i == ext_index) { + while (partitions > 4) + free(buffers[--partitions]); + ext_pointers[ext_index] = NULL; + extended_offset = 0; + } + clear_partition(p); + } + else if (!q->sys_ind && i > 4) { + free(buffers[--partitions]); + clear_partition(ext_pointers[--i]); + } + else if (i > 3) { + if (i > 4) { + p = ext_pointers[i - 1]; + p->boot_ind = 0; + p->head = q->head; + p->sector = q->sector; + p->cyl = q->cyl; + p->sys_ind = EXTENDED; + p->end_head = q->end_head; + p->end_sector = q->end_sector; + p->end_cyl = q->end_cyl; + p->start_sect = q->start_sect; + p->nr_sects = q->nr_sects; + changed[i - 1] = 1; + } + else { + part_table[5]->start_sect += + offsets[5] - extended_offset; + offsets[5] = extended_offset; + changed[5] = 1; + } + if (partitions > 5) { + partitions--; + free(buffers[i]); + while (i < partitions) { + changed[i] = changed[i + 1]; + buffers[i] = buffers[i + 1]; + offsets[i] = offsets[i + 1]; + part_table[i] = part_table[i + 1]; + ext_pointers[i] = ext_pointers[i + 1]; + i++; + } + } + else + clear_partition(part_table[i]); + } +} + +void change_sysid(void) +{ + char *temp; + int i = get_partition(0, partitions), sys; + struct partition *p = part_table[i]; + + if ((sys = p->sys_ind) == EXTENDED) + printf("Partition %d is extended. Delete it\n", i + 1); + else if (!sys) + printf("Partition %d does not exist yet!\n", i + 1); + else while (1) { + read_char("Hex code (type L to list codes): "); + if (tolower(*line_ptr) == 'l') + list_types(); + else if (isxdigit(*line_ptr)) { + sys = 0; + do + sys = sys << 4 | hex_val(*line_ptr++); + while (isxdigit(*line_ptr)); + if (!sys) { + delete_partition(i); + break; + } + else if (sys == EXTENDED) { + printf("You may not change a partition " + "to be an extended partition\n"); + break; + } + else if (sys < 256) { + if (sys == p->sys_ind) + break; + part_table[i]->sys_ind = sys; + printf ("Changed system type of partition %d " + "to %x (%s)\n", i + 1, sys, + (temp = partition_type(sys)) ? temp : + "Unknown"); + changed[i] = 1; + break; + } + } + } +} + +/* check_consistency() and long2chs() added Sat Mar 6 12:28:16 1993, + * faith@cs.unc.edu, based on code fragments from pfdisk by Gordon W. Ross, + * Jan. 1990 (version 1.2.1 by Gordon W. Ross Aug. 1990; Modified by S. + * Lubkin Oct. 1991). */ + +static void long2chs(ulong ls, uint *c, uint *h, uint *s) +{ + int spc = heads * sectors; + + *c = ls / spc; + ls = ls % spc; + *h = ls / sectors; + *s = ls % sectors + 1; /* sectors count from 1 */ +} + +static void check_consistency(struct partition *p, int partition) +{ + uint pbc, pbh, pbs; /* physical beginning c, h, s */ + uint pec, peh, pes; /* physical ending c, h, s */ + uint lbc, lbh, lbs; /* logical beginning c, h, s */ + uint lec, leh, les; /* logical ending c, h, s */ + + if (!heads || !sectors || (partition >= 4)) + return; /* do not check extended partitions */ + +/* physical beginning c, h, s */ + pbc = p->cyl & 0xff | (p->sector << 2) & 0x300; + pbh = p->head; + pbs = p->sector & 0x3f; + +/* physical ending c, h, s */ + pec = p->end_cyl & 0xff | (p->end_sector << 2) & 0x300; + peh = p->end_head; + pes = p->end_sector & 0x3f; + +/* compute logical beginning (c, h, s) */ + long2chs(p->start_sect, &lbc, &lbh, &lbs); + +/* compute logical ending (c, h, s) */ + long2chs(p->start_sect + p->nr_sects - 1, &lec, &leh, &les); + +/* Same physical / logical beginning? */ + if (cylinders <= 1024 && (pbc != lbc || pbh != lbh || pbs != lbs)) { + printf("Partition %d has different physical/logical " + "beginnings (non-Linux?):\n", partition + 1); + printf(" phys=(%d, %d, %d) ", pbc, pbh, pbs); + printf("logical=(%d, %d, %d)\n",lbc, lbh, lbs); + } + +/* Same physical / logical ending? */ + if (cylinders <= 1024 && (pec != lec || peh != leh || pes != les)) { + printf("Partition %d has different physical/logical " + "endings:\n", partition + 1); + printf(" phys=(%d, %d, %d) ", pec, peh, pes); + printf("logical=(%d, %d, %d)\n",lec, leh, les); + } + +/* Beginning on cylinder boundary? */ + if (pbh != !pbc || pbs != 1) { + printf("Partition %i does not start on cylinder " + "boundary:\n", partition + 1); + printf(" phys=(%d, %d, %d) ", pbc, pbh, pbs); + printf("should be (%d, %d, 1)\n", pbc, !pbc); + } + +/* Ending on cylinder boundary? */ + if (peh != (heads - 1) || pes != sectors) { + printf("Partition %i does not end on cylinder boundary:\n", + partition + 1); + printf(" phys=(%d, %d, %d) ", pec, peh, pes); + printf("should be (%d, %d, %d)\n", + pec, heads - 1, sectors); + } +} + +void list_table(void) +{ + struct partition *p; + char *type; + int i, w = strlen(disk_device); + + printf("\nDisk %s: %d heads, %d sectors, %d cylinders\nUnits = " + "%ss of %d * 512 bytes\n\n", disk_device, heads, sectors, + cylinders, str_units(), display_factor); + if (w < 5) + w = 5; + printf("%*s Boot Begin Start End Blocks Id System\n", + w + 1, "Device"); + for (i = 0 ; i < partitions; i++) + if ((p = part_table[i])->sys_ind) { + printf("%*s%-2d %c%8d%8d%8d%8d%c %2x %s\n", w, + disk_device, i + 1, + !p->boot_ind ? ' ' : p->boot_ind == ACTIVE_FLAG + ? '*' : '?', + cround(rounded( calculate(p->head, p->sector, p->cyl), + p->start_sect + offsets[i])), + cround(p->start_sect + offsets[i]), + cround(p->start_sect + offsets[i] + p->nr_sects + - (p->nr_sects ? 1: 0)), + p->nr_sects / 2, p->nr_sects & 1 ? '+' : ' ', + p->sys_ind, + (type = partition_type(p->sys_ind)) ? + type : "Unknown"); + check_consistency(p, i); + } + +} + +void x_list_table(int extend) +{ + struct partition *p, **q; + int i; + + if (extend) + q = ext_pointers; + else + q = part_table; + printf("\nDisk %s: %d heads, %d sectors, %d cylinders\n\n", + disk_device, heads, sectors, cylinders); + printf("Nr AF Hd Sec Cyl Hd Sec Cyl Start Size ID\n"); + for (i = 0 ; i < partitions; i++) + if (p = q[i]) { + printf("%2d %02x%4d%4d%5d%4d%4d%5d%8d%8d %02x\n", + i + 1, p->boot_ind, p->head, + sector(p->sector), + cylinder(p->sector, p->cyl), p->end_head, + sector(p->end_sector), + cylinder(p->end_sector, p->end_cyl), + p->start_sect, p->nr_sects, p->sys_ind); + if (p->sys_ind) + check_consistency(p, i); + } +} + +void check_bounds(uint *first, uint *last) +{ + int i; + uint max = 256 * 63 * 1024; + struct partition *p = part_table[0]; + + for (i = 0; i < partitions; p = part_table[++i]) + if (!p->sys_ind || p->sys_ind == EXTENDED) { + first[i] = max; + last[i] = 0; + } + else { + first[i] = rounded(calculate(p->head, p->sector, + p->cyl), p->start_sect + offsets[i]); + last[i] = p->start_sect + offsets[i] + p->nr_sects - 1; + } +} + +void check(int n, uint h, uint s, uint c, uint start) +{ + uint total, real_s, real_c, i; + + real_s = sector(s) - 1; + real_c = cylinder(s, c); + total = (real_c * sectors + real_s) * heads + h; + if (full_bits) + while ((i = total + full_bits) <= start) { + real_c += 1024; + total = i; + } + if (!total) + fprintf(stderr, "Warning: partition %d contains sector 0\n", n); + if (h >= heads) + fprintf(stderr, + "Partition %d: head %d greater than maximum %d\n", + n, h + 1, heads); + if (real_s >= sectors) + fprintf(stderr, "Partition %d: sector %d greater than " + "maximum %d\n", n, s, sectors); + if (real_c >= cylinders) + fprintf(stderr, "Partitions %d: cylinder %d greater than " + "maximum %d\n", n, real_c + 1, cylinders); + if (start != total) + fprintf(stderr, + "Partition %d: previous sectors %d disagrees with " + "total %d\n", n, start, total); +} + + +void verify(void) +{ + int i, j; + uint total = 1, + first[partitions], last[partitions]; + struct partition *p = part_table[0]; + + if (warn_geometry()) + return; + + check_bounds(first, last); + for (i = 0; i < partitions; p = part_table[++i]) + if (p->sys_ind && (p->sys_ind != EXTENDED)) { + check_consistency(p, i); + if (p->start_sect + offsets[i] < first[i]) + printf("Warning: bad start-of-data in " + "partition %d\n", i + 1); + check(i + 1, p->end_head, p->end_sector, p->end_cyl, + last[i]); + total += last[i] + 1 - first[i]; + for (j = 0; j < i; j++) + if (first[i] >= first[j] && first[i] <= last[j] + || (last[i] <= last[j] && + last[i] >= first[j])) { + printf("Warning: partition %d overlaps " + "partition %d.\n", j + 1, i + 1); + total += first[i] >= first[j] ? + first[i] : first[j]; + total -= last[i] <= last[j] ? + last[i] : last[j]; + } + } + + if (extended_offset) { + uint e_last = part_table[ext_index]->start_sect + + part_table[ext_index]->nr_sects - 1; + + for (p = part_table[i = 4]; i < partitions; + p = part_table[++i]) { + total++; + if (!p->sys_ind) { + if (i != 4 || i + 1 < partitions) + printf("Warning: partition %d " + "is empty\n", i + 1); + } + else if (first[i] < extended_offset || + last[i] > e_last) + printf("Logical partition %d not entirely in " + "partition %d\n", i + 1, ext_index + 1); + } + } + + if (total > heads * sectors * cylinders) + printf("Total allocated sectors %d greater than the maximum " + "%d\n", total, heads * sectors * cylinders); + else if (total = heads * sectors * cylinders - total) + printf("%d unallocated sectors\n", total); +} + +void add_partition(int n, int sys) +{ + char mesg[48]; + int i, read = 0; + struct partition *p = part_table[n], *q = part_table[ext_index]; + uint start, stop = 0, limit, temp, + first[partitions], last[partitions]; + + if (p->sys_ind) { + printf("Partition %d is already defined. Delete " + "it before re-adding it.\n", n + 1); + return; + } + check_bounds(first, last); + if (n < 4) { + start = sector_offset; + limit = heads * sectors * cylinders - 1; + if (extended_offset) { + first[ext_index] = extended_offset; + last[ext_index] = q->start_sect + q->nr_sects - 1; + } + } + else { + start = extended_offset + sector_offset; + limit = q->start_sect + q->nr_sects - 1; + } + if (unit_flag) + for (i = 0; i < partitions; i++) + first[i] = (cround(first[i]) - 1) * display_factor; + + sprintf(mesg, "First %s", str_units()); + do { + temp = start; + for (i = 0; i < partitions; i++) { + if (start == offsets[i]) + start += sector_offset; + if (start >= first[i] && start <= last[i]) + if (n < 4) + start = last[i] + 1; + else + start = last[i] + sector_offset; + } + if (start > limit) + break; + if (start != temp && read) { + printf("Sector %d is already allocated\n", temp); + temp = start = stop; + read = 0; + } + if (!read && start == temp) { + uint i; + temp = 0; + start = read_int(cround(i = (stop = start) + (n > 4)), + cround(limit), mesg); + if (unit_flag) { + start = (start - 1) * display_factor; + if (start < i) start = i; + } + read = 1; + } + } while (start != temp || !read); + if (n > 4) /* NOT for fifth partition */ + offsets[n] = start - sector_offset; + + for (i = 0; i < partitions; i++) { + if (start < offsets[i] && limit >= offsets[i]) + limit = offsets[i] - 1; + if (start < first[i] && limit >= first[i]) + limit = first[i] - 1; + } + if (start > limit) { + printf("No free sectors available\n"); + if (n > 4) { + free(buffers[n]); + partitions--; + } + return; + } + if (cround(start) == cround(limit)) + stop = start; + else { + sprintf(mesg, "Last %s or +size or +sizeM or +sizeK", + str_units()); + size_flag = 1; + stop = read_int(cround(start), cround(limit), mesg); + if (unit_flag) { + stop = stop * display_factor - 1; + if (stop >limit) + stop = limit; + } + } + + set_partition(n, p, start, stop, sys, offsets[n]); + + if (sys == EXTENDED) { + ext_index = n; + offsets[4] = extended_offset = start; + ext_pointers[n] = p; + if (!(buffers[4] = calloc(1, SECTOR_SIZE))) + fatal(out_of_memory); + part_table[4] = offset(buffers[4], 0); + ext_pointers[4] = part_table[4] + 1; + changed[4] = 1; + partitions = 5; + } + else { + if (n > 4) + set_partition(n - 1, ext_pointers[n - 1], + start - sector_offset, stop, EXTENDED, + extended_offset); +#if 0 + if ((limit = p->nr_sects) & 1) + printf("Warning: partition %d has an odd " + "number of sectors.\n", n + 1); +#endif + } +} + +void add_logical(void) +{ + if (partitions > 5 || part_table[4]->sys_ind) { + if (!(buffers[partitions] = calloc(1, SECTOR_SIZE))) + fatal(out_of_memory); + part_table[partitions] = offset(buffers[partitions], 0); + ext_pointers[partitions] = part_table[partitions] + 1; + offsets[partitions] = 0; + partitions++; + } + add_partition(partitions - 1, LINUX_NATIVE); +} + +void new_partition(void) +{ + int i, free_primary = 0; + + if (warn_geometry()) + return; + if (partitions >= MAXIMUM_PARTS) { + printf("The maximum number of partitions has been created\n"); + return; + } + + for (i = 0; i < 4; i++) + free_primary += !part_table[i]->sys_ind; + if (!free_primary) + if (extended_offset) + add_logical(); + else + printf("You must delete some partition and add " + "an extended partition first\n"); + else { + char c, line[LINE_LENGTH]; + sprintf(line, "Command action\n %s\n p primary " + "partition (1-4)\n", extended_offset ? + "l logical (5 or over)" : "e extended"); + while (1) + if ((c = tolower(read_char(line))) == 'p') { + add_partition(get_partition(0, 4), + LINUX_NATIVE); + return; + } + else if (c == 'l' && extended_offset) { + add_logical(); + return; + } + else if (c == 'e' && !extended_offset) { + add_partition(get_partition(0, 4), + EXTENDED); + return; + } + } +} + +void write_table(void) +{ + int i, error = 0; + + changed[3] = changed[0] || changed[1] || changed[2] || changed[3]; + for (i = 3; i < partitions; i++) + if (changed[i]) { + *table_check(buffers[i]) = PART_TABLE_FLAG; + if (ext2_llseek(fd, offsets[i] + * SECTOR_SIZE, SEEK_SET) < 0) + fatal(unable_to_seek); + if (write(fd, buffers[i], SECTOR_SIZE) != SECTOR_SIZE) + fatal(unable_to_write); + } + + printf("The partition table has been altered!\n\n"); + + printf("Calling ioctl() to re-read partition table.\n" + "(Reboot to ensure the partition table has been updated.)\n"); + sync(); + sleep(2); + if (i = ioctl(fd, BLKRRPART)) + error = errno; + close(fd); + + printf("Syncing disks.\n"); + sync(); + sleep(4); /* for sync() */ + + if (i) + printf("Re-read table failed with error %d: %s.\nReboot your " + "system to ensure the partition table is updated.\n", + error, strerror(error)); + + printf( "\nWARNING: If you have created or modified any DOS 6.x\n" + "partitions, please see the fdisk manual page for additional\n" + "information.\n" ); + + exit(0); +} + +#define MAX_PER_LINE 16 +void print_buffer(char buffer[]) +{ + int i, + l; + + for (i = 0, l = 0; i < SECTOR_SIZE; i++, l++) { + if (l == 0) + printf("0x%03X:", i); + printf(" %02X", (unsigned char) buffer[i]); + if (l == MAX_PER_LINE - 1) { + printf("\n"); + l = -1; + } + } + if (l > 0) + printf("\n"); + printf("\n"); +} + +void print_raw(void) +{ + int i; + + printf("Device: %s\n", disk_device); + for (i = 3; i < partitions; i++) + print_buffer(buffers[i]); +} + +void move_begin(int i) +{ + struct partition *p = part_table[i]; + uint new, first; + + if (warn_geometry()) + return; + if (!p->sys_ind || !p->nr_sects || p->sys_ind == EXTENDED) { + printf("Partition %d has no data area\n", i + 1); + return; + } + first = rounded(calculate(p->head, p->sector, p->cyl), p->start_sect + + offsets[i]); + new = read_int(first, p->start_sect + p->nr_sects + offsets[i] - 1, + "New beginning of data") - offsets[i]; + + if (new != p->nr_sects) { + first = p->nr_sects + p->start_sect - new; + p->nr_sects = first; + p->start_sect = new; + changed[i] = 1; + } +} + +void xselect(void) +{ + while(1) { + putchar('\n'); + switch (tolower(read_char("Expert command (m for help): "))) { + case 'b': move_begin(get_partition(0, partitions)); + break; + case 'c': cylinders = read_int(1, 65535, + "Number of cylinders"); + warn_cylinders(); + break; + case 'd': print_raw(); + break; + case 'e': x_list_table(1); + break; + case 'h': heads = read_int(1, 256, "Number of heads"); + update_units(); + break; + case 'p': x_list_table(0); + break; + case 'q': close(fd); + exit(0); + case 'r': return; + case 's': sectors = read_int(1, 63, + "Number of sectors"); + if (dos_compatible_flag) { + sector_offset = sectors; + fprintf(stderr, "Warning: setting " + "sector offset for DOS " + "compatiblity\n"); + } + update_units(); + break; + case 'w': write_table(); + default: xmenu(); + } + } +} + +void try(char *device) +{ + disk_device = device; + if (!setjmp(listingbuf)) + if ((fd = open(disk_device, O_RDWR)) >= 0) { + close(fd); + get_boot(); + list_table(); + if (partitions > 4) + delete_partition(ext_index); + } +} + +void main(int argc, char **argv) +{ + if (argc > 3) + fatal(usage); + if (argc > 1 && *argv[1] == '-') { + switch (*(argv[1] + 1)) { + case 'v': + printf("fdisk v" VERSION "\n"); + exit(0); + case 'l': + listing = 1; + try("/dev/hda"); + try("/dev/hdb"); + try("/dev/hdc"); + try("/dev/hdd"); + try("/dev/sda"); + try("/dev/sdb"); + try("/dev/sdc"); + try("/dev/sdd"); + try("/dev/sde"); + try("/dev/sdf"); + try("/dev/sdg"); + try("/dev/sdh"); + exit(0); + case 's': { + int i; + if (argc < 3) + fatal(usage); + if (!(i = atoi(argv[2] + 8))) + fatal(usage); + disk_device = (char *) malloc(9); + strncpy(disk_device, argv[2], 8); + if ((fd = open(disk_device, O_RDWR)) >= 0) { + close(fd); + get_boot(); + if (i > partitions) exit(1); + if (part_table[--i]->sys_ind > 10) + printf("%d\n", + part_table[i]->nr_sects / 2); + else exit(1); + exit(0); + } + } + default: + fatal(usage); + } + } + if (argc > 1) + disk_device = argv[argc - 1]; + else if ((fd = open(DEFAULT_DEVICE, O_RDWR)) < 0) + disk_device = ALTERNATE_DEVICE; + else close(fd); + + get_boot(); + if (argc == 1) + printf("Using %s as default device!\n", disk_device); + + while (1) { + putchar('\n'); + switch (tolower(read_char("Command (m for help): "))) { + case 'a': toggle_active(get_partition(1, partitions)); + break; + case 'c': + toggle_dos(); + break; + case 'd': delete_partition( + get_partition(1, partitions)); + break; + case 'l': list_types(); + break; + case 'n': new_partition(); + break; + case 'p': list_table(); + break; + case 'q': close(fd); + exit(0); + case 't': change_sysid(); + break; + case 'u': change_units(); + break; + case 'v': verify(); + break; + case 'w': write_table(); + case 'x': xselect(); + break; + default: menu(); + } + } +} diff --git a/disk-utils/fdprm b/disk-utils/fdprm new file mode 100644 index 00000000..2a59da01 --- /dev/null +++ b/disk-utils/fdprm @@ -0,0 +1,26 @@ +# /etc/fdprm - floppy disk parameter table + +# Common disk formats. Names are of the form +# actual media capacity/maximum drive capacity +# (Note: although 5.25" HD drives can format disks at 1.44M, they're listed +# as 1200 because that's the common maximum size.) + +# size sec/t hds trk stre gap rate spec1 fmt_gap +360/360 720 9 2 40 0 0x2A 0x02 0xDF 0x50 +1200/1200 2400 15 2 80 0 0x1B 0x00 0xDF 0x54 +360/720 720 9 2 40 1 0x2A 0x02 0xDF 0x50 +720/720 1440 9 2 80 0 0x2A 0x02 0xDF 0x50 +720/1440 1440 9 2 80 0 0x2A 0x02 0xDF 0x50 +360/1200 720 9 2 40 1 0x23 0x01 0xDF 0x50 +720/1200 1440 9 2 80 0 0x23 0x01 0xDF 0x50 +1440/1440 2880 18 2 80 0 0x1B 0x00 0xCF 0x6C + +# Non-standard disk formats: + +# BEWARE: They're incomplete and possibly incorrect. The only reason why +# they are in this file is to show how such formats are added. + +1440/1200 2880 18 2 80 0 ???? ???? ???? ???? # ????? +1680/1440 3360 21 2 80 0 0x0C 0x00 0xCF 0x6C # ????? + +# Add user-specific formats here diff --git a/disk-utils/frag.8 b/disk-utils/frag.8 new file mode 100644 index 00000000..c2f67b58 --- /dev/null +++ b/disk-utils/frag.8 @@ -0,0 +1,47 @@ +.\" Copyright 1992,1993,1994 Rickard E. Faith (faith@cs.unc.edu) +.\" May be distributed under the GNU General Public License +.TH FRAG 8 "8 January 1994" "Linux 0.99" "Linux Programmer's Manual" +.SH NAME +frag \- simple fragmentation checker +.SH SYNOPSIS +.B /usr/sbin/frag +.B "[ \-s [ \-s ]]" +filename ... +.SH DESCRIPTION +.B frag +will report the file system fragmentation on a specified +.IR filename . +If the +.I filename +is a directory, +.B frag +will recursively descend the directory. +.SH OPTIONS +.TP +.B \-s +Silent (may be set to 1 or 2). The first +.B \-s +eliminates the file by file statistics, and just prints the +examined directories and a summary. The second +.B \-s +eliminates the printing of the directories, and just prints a +summary. This option is useful when +.B frag +is used on a directory. +.SH "SEE ALSO" +.BR mkfs (8), +.BR fsck (8), +.BR mkefs (8), +.BR efsck (8) +.SH BUGS +.B frag +will get caught in an infinite loop in the /proc filesystem. +.SH AUTHORS +V1.0 by Werner Almesberger +.br +V1.1 by Steffen Zahn, adding directory recursion +.br +V1.2 by Rob Hooft, adding hole counts +.br +V1.3 by Steffen Zahn, ignore symlinks, +don't cross filesys borders, get filesystem block size at runtime diff --git a/disk-utils/frag.c b/disk-utils/frag.c new file mode 100644 index 00000000..0098e02f --- /dev/null +++ b/disk-utils/frag.c @@ -0,0 +1,311 @@ +/* frag.c - simple fragmentation checker + V1.0 by Werner Almesberger + V1.1 by Steffen Zahn, adding directory recursion + V1.2 by Rob Hooft, adding hole counts + V1.3 by Steffen Zahn, email: szahn%masterix@emndev.siemens.co.at + 14 Nov 93 + - ignore symlinks, + - don't cross filesys borders + - get filesystem block size at runtime + V1.4 by Michael Bischoff to handle + indirect blocks better, but only for ext2fs + (applied by faith@cs.unc.edu, Sat Feb 4 22:06:27 1995) + + TODO: - handle hard links + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for FIBMAP */ + +typedef struct StackElem { + struct StackElem *backref, *next; + char name[NAME_MAX]; + char dir_seen; + char from_cmd_line; +} StackElem; + +StackElem *top = NULL; + + +void discard( void ) +{ + StackElem *se = top; + if( se == NULL ) + return ; + top = se->next; + free(se); +} + +void push( StackElem * se ) +{ + se -> next = top; + top = se; +} + +char *p2s( StackElem *se, char *path ) +{ + char *s; + if( se->backref!=NULL ) { + path = p2s( se->backref, path ); + if( path[-1]!='/' ) + *path++ = '/'; + } + s = se->name; + while( *s ) + *path++ = *s++; + return path; +} + +char *path2str( StackElem *se, char *path ) +{ + *(p2s( se, path ))=0; + return path; +} + +void *xmalloc( size_t size ) +{ + void *p; + if( (p=malloc(size))==NULL ) { + fprintf(stderr,"\nvirtual memory exhausted.\n"); + exit(1); + } + return p; +} + +int main(int argc,char **argv) +{ + int fd,last_phys_block, + fragments_in_file, blocks_in_file, + blocks,current_phys_block, + this_fragment, largest_fragment, i; + long sum_blocks=0, sum_frag_blocks=0, sum_files=0, sum_frag_files=0; + long num_hole=0, sum_hole=0, hole; + struct stat st; + struct statfs stfs; + StackElem *se, *se1; + char path[PATH_MAX], pathlink[PATH_MAX], *p; + DIR *dir; + struct dirent *de; + char silent_flag=0; + dev_t local_fs; + int block_size; + + if (argc < 2) + { + fprintf(stderr,"usage: %s [-s [-s]] filename ...\n",argv[0]); + exit(1); + } + argc--; argv++; + while (argc>0) + { + p = *argv; + if( *p=='-' ) + while( *++p ) + switch( *p ) + { + case 's': + silent_flag++; /* may be 1 or 2 */ + break; + default: + fprintf(stderr,"\nunknown flag %c\n", *p ); + exit(1); + } + else + { + se = xmalloc( sizeof(StackElem) ); + se->backref=NULL; se->dir_seen=0; se->from_cmd_line=1; + strcpy( se->name, p ); + push(se); + } + argc--; argv++; + } + while ( top != NULL) + { + se = top; + if( se->dir_seen ) + discard(); + else + { + path2str( se, path ); + if( readlink( path, pathlink, sizeof(pathlink) )>=0 ) + { /* ignore symlinks */ + if(silent_flag<1) + { + printf("symlink %s\n", path ); + } + discard(); + } + else if( stat( path,&st) < 0) + { + perror( path ); + discard(); + } + else if( !se->from_cmd_line && (local_fs!=st.st_dev) ) + { /* do not cross filesystem borders */ + if(silent_flag<2) + { + printf("different filesystem %s\n", path ); + } + discard(); + } + else + { + if( se->from_cmd_line ) + { + local_fs = st.st_dev; + if ( statfs( path, &stfs )<0 ) + { + perror( path ); + block_size = 1024; + } + else + block_size = stfs.f_bsize; + } + if( S_ISREG(st.st_mode)) /* regular file */ + { + if ( (fd = open( path ,O_RDONLY)) < 0 ) + { + perror( path ); + discard(); + } + else + { + last_phys_block = -1; + fragments_in_file = 0; + hole = 0; this_fragment=0; + largest_fragment=0; + blocks_in_file = (st.st_size+block_size-1)/block_size; + for (blocks = 0; blocks < blocks_in_file; blocks++) + { + current_phys_block = blocks; + if (ioctl(fd,FIBMAP,¤t_phys_block) < 0) + { + perror(path); + break; + } + if (current_phys_block) { /* no hole here */ + int indirect; + /* indirect is the number of indirection */ + /* blocks which must be skipped */ + indirect = 0; + /* every 256 blocks there is an indirect block, + the first of these is before block 12 */ + if (blocks >= 12 && (blocks-12) % 256 == 0) + ++indirect; + /* there is a block pointing to the indirect + blocks every 64K blocks */ + if (blocks >= 256+12 && (blocks-256-12) % 65536 == 0) + ++indirect; /* 2nd indirect block */ + /* there is a single triple indirect block */ + if (blocks == 65536 + 256 + 12) + ++indirect; + if (last_phys_block == current_phys_block-1-indirect) + this_fragment++; + else { /* start of first or new fragment */ + if( largest_fragment1 ) + { + sum_frag_blocks+=blocks_in_file-largest_fragment; + sum_frag_files++; + } + discard(); + close(fd); + } + } + else if( S_ISDIR( st.st_mode ) ) /* push dir contents */ + { + if( (dir=opendir( path ))==NULL ) + { + perror(path); + discard(); + } + else + { + if( silent_flag<2 ) + printf("reading %s\n", path); + while( (de=readdir(dir))!=NULL ) + { + if( (strcmp(de->d_name,".")!=0) + && (strcmp(de->d_name,"..")!=0) ) + { + se1 = xmalloc( sizeof(StackElem) ); + se1->backref=se; se1->dir_seen=0; + se1->from_cmd_line=0; + strcpy( se1->name, de->d_name ); + push(se1); + } + } + closedir( dir ); + se->dir_seen=1; + } + } + else /* if( S_ISREG(st.st_mode)) */ + discard(); + } + } /* if( se->dir_seen ) */ + } /* while ( top != NULL) */ + if (sum_files>1) + { + printf("\nsummary:\n"); + printf(" %3ld%% file fragmentation (%ld of %ld files contain fragments)\n", + sum_files<1 ? 0L : sum_frag_files*100/sum_files, + sum_frag_files, sum_files); + printf(" %3ld%% block fragmentation (%ld of %ld blocks are in fragments)\n", + sum_blocks<1 ? 0L : sum_frag_blocks*100/sum_blocks, + sum_frag_blocks, sum_blocks); + if (num_hole>1) + printf(" %ld files contain %ld blocks in holes\n", + num_hole,sum_hole); + } + exit(0); +} diff --git a/disk-utils/fsck.minix.8 b/disk-utils/fsck.minix.8 new file mode 100644 index 00000000..32bbe488 --- /dev/null +++ b/disk-utils/fsck.minix.8 @@ -0,0 +1,125 @@ +.\" Copyright 1992, 1993, 1994 Rickard E. Faith (faith@cs.unc.edu) +.\" May be freely distributed. +.\" " for hilit19 +.TH FSCK 8 "10 January 1994" "Linux 0.99" "Linux Programmer's Manual" +.SH NAME +fsck.minix \- a file system consistency checker for Linux +.SH SYNOPSIS +.B "fsck.minix [ \-larvsmf ]" +device +.SH DESCRIPTION +.B fsck.minix +performs a consistency check for the Linux MINIX filesystem. The current +version supports the 14 character and 30 character filename options. + +The program +assumes the file system is quiescent. +.B fsck.minix +should not be used on a mounted device unless you can be sure nobody is +writing to it (and remember that the kernel can write to it when it +searches for files). + +The device will usually have the following form: +.nf +.RS +/dev/hda[1-8] +/dev/hdb[1-8] +/dev/sda[1-8] +/dev/sdb[1-8] +.RE +.fi + +If the file system was changed (i.e., repaired), then +.B fsck.minix +will print "FILE SYSTEM HAS CHANGED" and will +.BR sync (2) +three times before exiting. Since Linux does not currently have raw +devices, there is +.I no +need to reboot at this time (versus a system which +.I does +have raw devices). +.SH WARNING +.B fsck.minix +should +.B not +be used on a mounted filesystem. Using +.B fsck.minix +on a mounted filesystem is very dangerous, due to the possibility that +deleted files are still in use, and can seriously damage a perfectly good +filesystem! If you absolutely have to run +.B fsck.minix +on a mounted filesystem (i.e., the root filesystem), make sure nothing is +writing to the disk, and that no files are "zombies" waiting for deletion. +.SH OPTIONS +.TP +.B \-l +Lists all filenames +.TP +.B \-r +Performs interactive repairs +.TP +.B \-a +Performs automatic repairs (this option implies +.BR \-r ), +and serves to answer all of the questions asked with the default. Note +that this can be extremely dangerous in the case of extensive file system +damage. +.TP +.B \-v +Verbose +.TP +.B \-s +Outputs super-block information +.TP +.B \-m +Activates MINIX-like "mode not cleared" warnings +.TP +.B \-f +Force file system check even if the file system was marked as valid (this +marking is done by the kernel when the file system is unmounted). +.SH "SEE ALSO" +.BR fsck (8), +.BR fsck.ext (8), +.BR fsck.ext2 (8), +.BR fsck.xiafs (8), +.BR mkfs (8), +.BR mkfs.minix (8), +.BR mkfs.ext (8), +.BR mkfs.ext2 (8), +.BR mkfs.xiafs (8). +.BR reboot (8) +.SH DIAGNOSTICS +There are numerous diagnostic messages. The ones mentioned here are the +most commonly seen in normal usage. + +If the device does not exist, +.B fsck.minix +will print "unable to read super block". If the device exists, but is not +a MINIX file system, +.B fsck.minix +will print "bad magic number in super-block". +.SH "EXIT CODES" +The exit code returned by +.B fsck.minix +is the sum of the following: +.IP 0 +No errors +.IP 3 +File system errors corrected, system should be rebooted if file system was +mounted +.IP 4 +File system errors left uncorrected +.IP 8 +Operational error +.IP 16 +Usage or syntax error +.PP +In point of fact, only 0, 3, 4, 7, 8, and 16 can ever be returned. +.SH AUTHOR +Linus Torvalds (torvalds@cs.helsinki.fi) +.br +Error code values by Rik Faith (faith@cs.unc.edu) +.br +Added support for file system valid flag: Dr. Wettstein +(greg%wind.uucp@plains.nodak.edu) diff --git a/disk-utils/fsck.minix.c b/disk-utils/fsck.minix.c new file mode 100644 index 00000000..209f9ce7 --- /dev/null +++ b/disk-utils/fsck.minix.c @@ -0,0 +1,862 @@ +/* + * fsck.c - a file system consistency checker for Linux. + * + * (C) 1991, 1992 Linus Torvalds. This file may be redistributed + * as per the GNU copyleft. + */ + +/* + * 09.11.91 - made the first rudimetary functions + * + * 10.11.91 - updated, does checking, no repairs yet. + * Sent out to the mailing-list for testing. + * + * 14.11.91 - Testing seems to have gone well. Added some + * correction-code, and changed some functions. + * + * 15.11.91 - More correction code. Hopefully it notices most + * cases now, and tries to do something about them. + * + * 16.11.91 - More corrections (thanks to Mika Jalava). Most + * things seem to work now. Yeah, sure. + * + * + * 19.04.92 - Had to start over again from this old version, as a + * kernel bug ate my enhanced fsck in february. + * + * 28.02.93 - added support for different directory entry sizes.. + * + * Sat Mar 6 18:59:42 1993, faith@cs.unc.edu: Output namelen with + * super-block information + * + * Sat Oct 9 11:17:11 1993, faith@cs.unc.edu: make exit status conform + * to that required by fsutil + * + * Mon Jan 3 11:06:52 1994 - Dr. Wettstein (greg%wind.uucp@plains.nodak.edu) + * Added support for file system valid flag. Also + * added program_version variable and output of + * program name and version number when program + * is executed. + * + * 10.12.94 - added test to prevent checking of mounted fs adapted + * from Theodore Ts'o's (tytso@athena.mit.edu) e2fsck + * program. (Daniel Quinlan, quinlan@yggdrasil.com) + * + * I've had no time to add comments - hopefully the function names + * are comments enough. As with all file system checkers, this assumes + * the file system is quiescent - don't use it on a mounted device + * unless you can be sure nobody is writing to it (and remember that the + * kernel can write to it when it searches for files). + * + * Usuage: fsck [-larvsm] device + * -l for a listing of all the filenames + * -a for automatic repairs (not implemented) + * -r for repairs (interactive) (not implemented) + * -v for verbose (tells how many files) + * -s for super-block info + * -m for minix-like "mode not cleared" warnings + * -f force filesystem check even if filesystem marked as valid + * + * The device may be a block device or a image of one, but this isn't + * enforced (but it's not much fun on a character device :-). + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifndef __GNUC__ +#error "needs gcc for the bitop-__asm__'s" +#endif + +#ifndef __linux__ +#define volatile +#endif + +#define ROOT_INO 1 + +#define UPPER(size,n) ((size+((n)-1))/(n)) +#define INODE_SIZE (sizeof(struct minix_inode)) +#define INODE_BLOCKS UPPER(INODES,MINIX_INODES_PER_BLOCK) +#define INODE_BUFFER_SIZE (INODE_BLOCKS * BLOCK_SIZE) + +#define BITS_PER_BLOCK (BLOCK_SIZE<<3) + +static char * program_name = "fsck.minix"; +static char * program_version = "1.0 - 12/30/93"; +static char * device_name = NULL; +static int IN; +static int repair=0, automatic=0, verbose=0, list=0, show=0, warn_mode=0, + force=0; +static int directory=0, regular=0, blockdev=0, chardev=0, links=0, + symlinks=0, total=0; + +static int changed = 0; /* flags if the filesystem has been changed */ +static int errors_uncorrected = 0; /* flag if some error was not corrected */ +static int dirsize = 16; +static int namelen = 14; + +/* File-name data */ +#define MAX_DEPTH 50 +static int name_depth = 0; +static char name_list[MAX_DEPTH][NAME_MAX+1]; + +static char * inode_buffer = NULL; +#define Inode (((struct minix_inode *) inode_buffer)-1) +static char super_block_buffer[BLOCK_SIZE]; +#define Super (*(struct minix_super_block *)super_block_buffer) +#define INODES ((unsigned long)Super.s_ninodes) +#define ZONES ((unsigned long)Super.s_nzones) +#define IMAPS ((unsigned long)Super.s_imap_blocks) +#define ZMAPS ((unsigned long)Super.s_zmap_blocks) +#define FIRSTZONE ((unsigned long)Super.s_firstdatazone) +#define ZONESIZE ((unsigned long)Super.s_log_zone_size) +#define MAXSIZE ((unsigned long)Super.s_max_size) +#define MAGIC (Super.s_magic) +#define NORM_FIRSTZONE (2+IMAPS+ZMAPS+INODE_BLOCKS) + +static char inode_map[BLOCK_SIZE * MINIX_I_MAP_SLOTS]; +static char zone_map[BLOCK_SIZE * MINIX_Z_MAP_SLOTS]; + +static unsigned char * inode_count = NULL; +static unsigned char * zone_count = NULL; + +void recursive_check(unsigned int ino); + +#define bitop(name,op) \ +static inline int name(char * addr,unsigned int nr) \ +{ \ +int __res; \ +__asm__ __volatile__("bt" op "l %1,%2; adcl $0,%0" \ +:"=g" (__res) \ +:"r" (nr),"m" (*(addr)),"0" (0)); \ +return __res; \ +} + +bitop(bit,"") +bitop(setbit,"s") +bitop(clrbit,"r") + +#define inode_in_use(x) (bit(inode_map,(x))) +#define zone_in_use(x) (bit(zone_map,(x)-FIRSTZONE+1)) + +#define mark_inode(x) (setbit(inode_map,(x)),changed=1) +#define unmark_inode(x) (clrbit(inode_map,(x)),changed=1) + +#define mark_zone(x) (setbit(zone_map,(x)-FIRSTZONE+1),changed=1) +#define unmark_zone(x) (clrbit(zone_map,(x)-FIRSTZONE+1),changed=1) + +/* + * Volatile to let gcc know that this doesn't return. When trying + * to compile this under minix, volatile gives a warning, as + * exit() isn't defined as volatile under minix. + */ +volatile void fatal_error(const char * fmt_string, int status) +{ + fprintf(stderr,fmt_string,program_name,device_name); + exit(status); +} + +#define usage() fatal_error("Usage: %s [-larvsmf] /dev/name\n",16) +#define die(str) fatal_error("%s: " str "\n",8) + +/* + * This simply goes through the file-name data and prints out the + * current file. + */ +void print_current_name(void) +{ + int i=0; + + while (imnt_fsname) == 0) + break; + endmntent (f); + if (!mnt) + return; + + /* + * If the root is mounted read-only, then /etc/mtab is + * probably not correct; so we won't issue a warning based on + * it. + */ + fd = open(MOUNTED, O_RDWR); + if (fd < 0 && errno == EROFS) + return; + else + close(fd); + + printf ("%s is mounted. ", device_name); + if (isatty(0) && isatty(1)) + cont = ask("Do you really want to continue", 0); + else + cont = 0; + if (!cont) { + printf ("check aborted.\n"); + exit (0); + } + return; +} + +/* + * check_zone_nr checks to see that *nr is a valid zone nr. If it + * isn't, it will possibly be repaired. Check_zone_nr sets *corrected + * if an error was corrected, and returns the zone (0 for no zone + * or a bad zone-number). + */ +int check_zone_nr(unsigned short * nr, int * corrected) +{ + if (!*nr) + return 0; + if (*nr < FIRSTZONE) + printf("Zone nr < FIRSTZONE in file `"); + else if (*nr >= ZONES) + printf("Zone nr >= ZONES in file `"); + else + return *nr; + print_current_name(); + printf("'."); + if (ask("Remove block",1)) { + *nr = 0; + *corrected = 1; + } + return 0; +} + +/* + * read-block reads block nr into the buffer at addr. + */ +void read_block(unsigned int nr, char * addr) +{ + if (!nr) { + memset(addr,0,BLOCK_SIZE); + return; + } + if (BLOCK_SIZE*nr != lseek(IN, BLOCK_SIZE*nr, SEEK_SET)) { + printf("Read error: unable to seek to block in file '"); + print_current_name(); + printf("'\n"); + memset(addr,0,BLOCK_SIZE); + errors_uncorrected = 1; + } else if (BLOCK_SIZE != read(IN, addr, BLOCK_SIZE)) { + printf("Read error: bad block in file '"); + print_current_name(); + printf("'\n"); + memset(addr,0,BLOCK_SIZE); + errors_uncorrected = 1; + } +} + +/* + * write_block writes block nr to disk. + */ +void write_block(unsigned int nr, char * addr) +{ + if (!nr) + return; + if (nr < FIRSTZONE || nr >= ZONES) { + printf("Internal error: trying to write bad block\n" + "Write request ignored\n"); + errors_uncorrected = 1; + return; + } + if (BLOCK_SIZE*nr != lseek(IN, BLOCK_SIZE*nr, SEEK_SET)) + die("seek failed in write_block"); + if (BLOCK_SIZE != write(IN, addr, BLOCK_SIZE)) { + printf("Write error: bad block in file '"); + print_current_name(); + printf("'\n"); + errors_uncorrected = 1; + } +} + +/* + * map-block calculates the absolute block nr of a block in a file. + * It sets 'changed' if the inode has needed changing, and re-writes + * any indirect blocks with errors. + */ +int map_block(struct minix_inode * inode, unsigned int blknr) +{ + unsigned short ind[BLOCK_SIZE>>1]; + unsigned short dind[BLOCK_SIZE>>1]; + int blk_chg, block, result; + + if (blknr<7) + return check_zone_nr(inode->i_zone + blknr, &changed); + blknr -= 7; + if (blknr<512) { + block = check_zone_nr(inode->i_zone + 7, &changed); + read_block(block, (char *) ind); + blk_chg = 0; + result = check_zone_nr(blknr + ind, &blk_chg); + if (blk_chg) + write_block(block, (char *) ind); + return result; + } + blknr -= 512; + block = check_zone_nr(inode->i_zone + 8, &changed); + read_block(block, (char *) dind); + blk_chg = 0; + result = check_zone_nr(dind + (blknr/512), &blk_chg); + if (blk_chg) + write_block(block, (char *) dind); + block = result; + read_block(block, (char *) ind); + blk_chg = 0; + result = check_zone_nr(ind + (blknr%512), &blk_chg); + if (blk_chg) + write_block(block, (char *) ind); + return result; +} + +void write_super_block(void) +{ + /* + * Set the state of the filesystem based on whether or not there + * are uncorrected errors. The filesystem valid flag is + * unconditionally set if we get this far. + */ + Super.s_state |= MINIX_VALID_FS; + if ( errors_uncorrected ) + Super.s_state |= MINIX_ERROR_FS; + else + Super.s_state &= ~MINIX_ERROR_FS; + + if (BLOCK_SIZE != lseek(IN, BLOCK_SIZE, SEEK_SET)) + die("seek failed in write_super_block"); + if (BLOCK_SIZE != write(IN, super_block_buffer, BLOCK_SIZE)) + die("unable to write super-block"); + + return; +} + +void write_tables(void) +{ + write_super_block(); + + if (IMAPS*BLOCK_SIZE != write(IN,inode_map,IMAPS*BLOCK_SIZE)) + die("Unable to write inode map"); + if (ZMAPS*BLOCK_SIZE != write(IN,zone_map,ZMAPS*BLOCK_SIZE)) + die("Unable to write zone map"); + if (INODE_BUFFER_SIZE != write(IN,inode_buffer,INODE_BUFFER_SIZE)) + die("Unable to write inodes"); +} + +void read_tables(void) +{ + memset(inode_map,0,sizeof(inode_map)); + memset(zone_map,0,sizeof(zone_map)); + if (BLOCK_SIZE != lseek(IN, BLOCK_SIZE, SEEK_SET)) + die("seek failed"); + if (BLOCK_SIZE != read(IN, super_block_buffer, BLOCK_SIZE)) + die("unable to read super block"); + if (MAGIC == MINIX_SUPER_MAGIC) { + namelen = 14; + dirsize = 16; + } else if (MAGIC == MINIX_SUPER_MAGIC2) { + namelen = 30; + dirsize = 32; + } else + die("bad magic number in super-block"); + if (ZONESIZE != 0 || BLOCK_SIZE != 1024) + die("Only 1k blocks/zones supported"); + if (!IMAPS || IMAPS > MINIX_I_MAP_SLOTS) + die("bad s_imap_blocks field in super-block"); + if (!ZMAPS || ZMAPS > MINIX_Z_MAP_SLOTS) + die("bad s_zmap_blocks field in super-block"); + inode_buffer = malloc(INODE_BUFFER_SIZE); + if (!inode_buffer) + die("Unable to allocate buffer for inodes"); + inode_count = malloc(INODES); + if (!inode_count) + die("Unable to allocate buffer for inode count"); + zone_count = malloc(ZONES); + if (!zone_count) + die("Unable to allocate buffer for zone count"); + if (IMAPS*BLOCK_SIZE != read(IN,inode_map,IMAPS*BLOCK_SIZE)) + die("Unable to read inode map"); + if (ZMAPS*BLOCK_SIZE != read(IN,zone_map,ZMAPS*BLOCK_SIZE)) + die("Unable to read zone map"); + if (INODE_BUFFER_SIZE != read(IN,inode_buffer,INODE_BUFFER_SIZE)) + die("Unable to read inodes"); + if (NORM_FIRSTZONE != FIRSTZONE) { + printf("Warning: Firstzone != Norm_firstzone\n"); + errors_uncorrected = 1; + } + if (show) { + printf("%d inodes\n",INODES); + printf("%d blocks\n",ZONES); + printf("Firstdatazone=%d (%d)\n",FIRSTZONE,NORM_FIRSTZONE); + printf("Zonesize=%d\n",BLOCK_SIZE<= INODES) + return NULL; + total++; + inode = Inode + nr; + if (!inode_count[nr]) { + if (!inode_in_use(nr)) { + printf("Inode %d marked not used, but used for file '", + nr); + print_current_name(); + printf("'\n"); + if (repair) + if (ask("Mark in use",1)) + mark_inode(nr); + else + errors_uncorrected = 1; + } + if (S_ISDIR(inode->i_mode)) + directory++; + else if (S_ISREG(inode->i_mode)) + regular++; + else if (S_ISCHR(inode->i_mode)) + chardev++; + else if (S_ISBLK(inode->i_mode)) + blockdev++; + else if (S_ISLNK(inode->i_mode)) + symlinks++; + else if (S_ISSOCK(inode->i_mode)) + ; + else if (S_ISFIFO(inode->i_mode)) + ; + else { + print_current_name(); + printf(" has mode %05o\n",inode->i_mode); + } + + } else + links++; + if (!++inode_count[nr]) { + printf("Warning: inode count too big.\n"); + inode_count[nr]--; + errors_uncorrected = 1; + } + return inode; +} + +void check_root(void) +{ + struct minix_inode * inode = Inode + ROOT_INO; + + if (!inode || !S_ISDIR(inode->i_mode)) + die("root inode isn't a directory"); +} + +static int add_zone(unsigned short * znr, int * corrected) +{ + int result; + int block; + + result = 0; + block = check_zone_nr(znr, corrected); + if (!block) + return 0; + if (zone_count[block]) { + printf("Block has been used before. Now in file `"); + print_current_name(); + printf("'."); + if (ask("Clear",1)) { + *znr = 0; + block = 0; + *corrected = 1; + } + } + if (!block) + return 0; + if (!zone_in_use(block)) { + printf("Block %d in file `",block); + print_current_name(); + printf("' is marked not in use."); + if (ask("Correct",1)) + mark_zone(block); + } + if (!++zone_count[block]) + zone_count[block]--; + return block; +} + +static void add_zone_ind(unsigned short * znr, int * corrected) +{ + static char blk[BLOCK_SIZE]; + int i, chg_blk=0; + int block; + + block = add_zone(znr, corrected); + if (!block) + return; + read_block(block, blk); + for (i=0 ; i < (BLOCK_SIZE>>1) ; i++) + add_zone(i + (unsigned short *) blk, &chg_blk); + if (chg_blk) + write_block(block, blk); +} + +static void add_zone_dind(unsigned short * znr, int * corrected) +{ + static char blk[BLOCK_SIZE]; + int i, blk_chg=0; + int block; + + block = add_zone(znr, corrected); + if (!block) + return; + read_block(block, blk); + for (i=0 ; i < (BLOCK_SIZE>>1) ; i++) + add_zone_ind(i + (unsigned short *) blk, &blk_chg); + if (blk_chg) + write_block(block, blk); +} + +void check_zones(unsigned int i) +{ + struct minix_inode * inode; + + if (!i || i >= INODES) + return; + if (inode_count[i] > 1) /* have we counted this file already? */ + return; + inode = Inode + i; + if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode) && + !S_ISLNK(inode->i_mode)) + return; + for (i=0 ; i<7 ; i++) + add_zone(i + inode->i_zone, &changed); + add_zone_ind(7 + inode->i_zone, &changed); + add_zone_dind(8 + inode->i_zone, &changed); +} + +void check_file(struct minix_inode * dir, unsigned int offset) +{ + static char blk[BLOCK_SIZE]; + struct minix_inode * inode; + int ino; + char * name; + int block; + + block = map_block(dir,offset/BLOCK_SIZE); + read_block(block, blk); + name = blk + (offset % BLOCK_SIZE) + 2; + ino = * (unsigned short *) (name-2); + if (ino >= INODES) { + print_current_name(); + printf(" contains a bad inode number for file '"); + printf("%.*s'.",namelen,name); + if (ask(" Remove",1)) { + *(unsigned short *)(name-2) = 0; + write_block(block, blk); + } + ino = 0; + } + inode = get_inode(ino); + if (!offset) + if (!inode || strcmp(".",name)) { + print_current_name(); + printf(": bad directory: '.' isn't first\n"); + errors_uncorrected = 1; + } else return; + if (offset == dirsize) + if (!inode || strcmp("..",name)) { + print_current_name(); + printf(": bad directory: '..' isn't second\n"); + errors_uncorrected = 1; + } else return; + if (!inode) + return; + if (name_depth < MAX_DEPTH) + strncpy(name_list[name_depth],name,namelen); + name_depth++; + if (list) { + if (verbose) + printf("%6d %07o %3d ",ino,inode->i_mode,inode->i_nlinks); + print_current_name(); + if (S_ISDIR(inode->i_mode)) + printf(":\n"); + else + printf("\n"); + } + check_zones(ino); + if (inode && S_ISDIR(inode->i_mode)) + recursive_check(ino); + name_depth--; + return; +} + +void recursive_check(unsigned int ino) +{ + struct minix_inode * dir; + unsigned int offset; + + dir = Inode + ino; + if (!S_ISDIR(dir->i_mode)) + die("internal error"); + if (dir->i_size < 32) { + print_current_name(); + printf(": bad directory: size<32"); + errors_uncorrected = 1; + } + for (offset = 0 ; offset < dir->i_size ; offset += dirsize) + check_file(dir,offset); +} + +int bad_zone(int i) +{ + char buffer[1024]; + + if (BLOCK_SIZE*i != lseek(IN, BLOCK_SIZE*i, SEEK_SET)) + die("seek failed in bad_zone"); + return (BLOCK_SIZE != read(IN, buffer, BLOCK_SIZE)); +} + +void check_counts(void) +{ + int i; + + for (i=1 ; i < INODES ; i++) { + if (!inode_in_use(i) && Inode[i].i_mode && warn_mode) { + printf("Inode %d mode not cleared.",i); + if (ask("Clear",1)) { + Inode[i].i_mode = 0; + changed = 1; + } + } + if (!inode_count[i]) { + if (!inode_in_use(i)) + continue; + printf("Inode %d not used, marked used in the bitmap.",i); + if (ask("Clear",1)) + unmark_inode(i); + continue; + } + if (!inode_in_use(i)) { + printf("Inode %d used, marked unused in the bitmap.", + i); + if (ask("Set",1)) + mark_inode(i); + } + if (Inode[i].i_nlinks != inode_count[i]) { + printf("Inode %d (mode = %07o), i_nlinks=%d, counted=%d.", + i,Inode[i].i_mode,Inode[i].i_nlinks,inode_count[i]); + if (ask("Set i_nlinks to count",1)) { + Inode[i].i_nlinks=inode_count[i]; + changed=1; + } + } + } + for (i=FIRSTZONE ; i < ZONES ; i++) { + if (zone_in_use(i) == zone_count[i]) + continue; + if (!zone_count[i]) { + if (bad_zone(i)) + continue; + printf("Zone %d: marked in use, no file uses it.",i); + if (ask("Unmark",1)) + unmark_zone(i); + continue; + } + printf("Zone %d: %sin use, counted=%d\n", + i,zone_in_use(i)?"":"not ",zone_count[i]); + } +} + +void check(void) +{ + memset(inode_count,0,INODES*sizeof(*inode_count)); + memset(zone_count,0,ZONES*sizeof(*zone_count)); + check_zones(ROOT_INO); + recursive_check(ROOT_INO); + check_counts(); +} + +int main(int argc, char ** argv) +{ + struct termios termios,tmp; + int count; + int retcode = 0; + + if (argc && *argv) + program_name = *argv; + if (INODE_SIZE * MINIX_INODES_PER_BLOCK != BLOCK_SIZE) + die("bad inode size"); + while (argc-- > 1) { + argv++; + if (argv[0][0] != '-') + if (device_name) + usage(); + else + device_name = argv[0]; + else while (*++argv[0]) + switch (argv[0][0]) { + case 'l': list=1; break; + case 'a': automatic=1; repair=1; break; + case 'r': automatic=0; repair=1; break; + case 'v': verbose=1; break; + case 's': show=1; break; + case 'm': warn_mode=1; break; + case 'f': force=1; break; + default: usage(); + } + } + if (!device_name) + usage(); + check_mount(); /* trying to check a mounted filesystem? */ + if (repair && !automatic) { + if (!isatty(0) || !isatty(1)) + die("need terminal for interactive repairs"); + tcgetattr(0,&termios); + tmp = termios; + tmp.c_lflag &= ~(ICANON|ECHO); + tcsetattr(0,TCSANOW,&tmp); + } + IN = open(device_name,repair?O_RDWR:O_RDONLY); + if (IN < 0) + die("unable to open '%s'"); + for (count=0 ; count<3 ; count++) + sync(); + read_tables(); + + /* + * Determine whether or not we should continue with the checking. + * This is based on the status of the filesystem valid and error + * flags and whether or not the -f switch was specified on the + * command line. + */ + printf("%s, %s\n", program_name, program_version); + if ( !(Super.s_state & MINIX_ERROR_FS) && + (Super.s_state & MINIX_VALID_FS) && + !force ) { + if (repair) + printf("%s is clean, no check.\n", device_name); + if (repair && !automatic) + tcsetattr(0,TCSANOW,&termios); + return retcode; + } + else if (force) + printf("Forcing filesystem check on %s.\n", device_name); + else if (repair) + printf("Filesystem on %s is dirty, needs checking.\n",\ + device_name); + + check_root(); + check(); + if (verbose) { + int i, free; + + for (i=1,free=0 ; i < INODES ; i++) + if (!inode_in_use(i)) + free++; + printf("\n%6d inodes used (%d%%)\n",(INODES-free-1), + 100*(INODES-free-1)/(INODES-1)); + for (i=FIRSTZONE,free=0 ; i < ZONES ; i++) + if (!zone_in_use(i)) + free++; + printf("%6d zones used (%d%%)\n",(ZONES-free), + 100*(ZONES-free)/ZONES); + printf("\n%6d regular files\n" + "%6d directories\n" + "%6d character device files\n" + "%6d block device files\n" + "%6d links\n" + "%6d symbolic links\n" + "------\n" + "%6d files\n", + regular,directory,chardev,blockdev, + links-2*directory+1,symlinks,total-2*directory+1); + } + if (changed) { + write_tables(); + printf( "----------------------------\n" + "FILE SYSTEM HAS BEEN CHANGED\n" + "----------------------------\n"); + for (count=0 ; count<3 ; count++) + sync(); + } + else if ( repair ) + write_super_block(); + + if (repair && !automatic) + tcsetattr(0,TCSANOW,&termios); + + if (changed) + retcode += 3; + if (errors_uncorrected) + retcode += 4; + return retcode; +} diff --git a/disk-utils/llseek.c b/disk-utils/llseek.c new file mode 100644 index 00000000..66166d3a --- /dev/null +++ b/disk-utils/llseek.c @@ -0,0 +1,84 @@ +/* + * llseek.c -- stub calling the llseek system call + * + * Copyright (C) 1994 Remy Card. This file may be redistributed + * under the terms of the GNU Public License. + */ + +#include + +#include +#include +#include +#if 0 +#include "et/com_err.h" +#include "ext2fs/io.h" +#endif + +#if defined(__GNUC__) || defined(HAS_LONG_LONG) +typedef long long ext2_loff_t; +#else +typedef long ext2_loff_t; +#endif + +extern ext2_loff_t ext2_llseek(unsigned int fd, + ext2_loff_t offset, + unsigned int origin); + +#ifdef __linux__ + +#ifndef __NR__llseek +#define __NR__llseek 140 +#endif + +static int _llseek (unsigned int, unsigned long, + unsigned long, ext2_loff_t *, unsigned int); + +static _syscall5(int,_llseek,unsigned int,fd,unsigned long,offset_high, + unsigned long, offset_low,ext2_loff_t *,result, + unsigned int, origin) + +ext2_loff_t ext2_llseek (unsigned int fd, ext2_loff_t offset, + unsigned int origin) +{ + unsigned long offset_high; + unsigned long offset_low; + ext2_loff_t result; + int retval; + static int do_compat = 0; + + if (do_compat) + return lseek (fd, (off_t) offset, origin); + + offset_high = ((unsigned long long) offset) >> 32; + offset_low = ((unsigned long long) offset) & 0xffffffff; + retval = _llseek (fd, offset_high, offset_low, &result, origin); + if (retval == -1 && errno == ENOSYS) { + /* + * Just in case this code runs on top of an old kernel + * which does not support the llseek system call + */ + do_compat++; + return lseek (fd, (off_t) offset, origin); + } + if (retval == -1) + result = -1; + return result; +} + +#else + +ext2_loff_t ext2_llseek (unsigned int fd, ext2_loff_t offset, + unsigned int origin) +{ + if ((sizeof(off_t) < sizeof(ext2_loff_t)) && + (offset >= ((ext2_loff_t) 1 << ((sizeof(off_t)*8) -1)))) { + errno = -EINVAL; + return -1; + } + return lseek (fd, (off_t) offset, origin); +} + +#endif + + diff --git a/disk-utils/mkfs.8 b/disk-utils/mkfs.8 new file mode 100644 index 00000000..48b22342 --- /dev/null +++ b/disk-utils/mkfs.8 @@ -0,0 +1,133 @@ +.\" -*- nroff -*- +.TH FSCK 8 "Jul 1993" "Version 1.8" +.SH NAME +fsck \- check and repair a Linux file system +.SH SYNOPSIS +.B fsck +[ +.B \-A +] +[ +.B \-V +] +[ +.B \-t +.I fstype +] +[ +.B fs-options +] +.I filesys +.SH DESCRIPTION +.B fsck +is used to check and optionally repair a Linux file system. +.I filesys +is either the device name (e.g. /dev/hda1, /dev/sdb2) or +the mount point (e.g. /, /usr, /home) for the file system. +.PP +The exit code returned by +.B fsck +is the sum of the following conditions: +.br +\ 0\ \-\ No errors +.br +\ 1\ \-\ File system errors corrected +.br +\ 2\ \-\ File system errors corrected, system should +.br +\ \ \ \ be rebooted if file system was mounted +.br +\ 4\ \-\ File system errors left uncorrected +.br +\ 8\ \-\ Operational error +.br +\ 16\ \-\ Usage or syntax error +.br +\ 128\ \-\ Shared library error +.br +The exit code returned when all file systems are checked using the +.B -A +option is the bit-wise OR of the exit codes for each +file system that is checked. +.PP +In actuality, +.B fsck +is simply a front-end for the various file system checkers +(\fBfsck\fR.\fIfstype\fR) +available under Linux. +The file system-specific checker is searched for in /etc/fs first, +then in /etc and finally in the directories listed in the PATH +environment variable. +Please see the file system-specific checker manual pages for +further details. +.SH OPTIONS +.TP +.B -A +Walk through the +.I /etc/fstab +file and try to check all file systems in one run. This option is +typically used from the +.I /etc/rc +system initalization file, instead of multiple commands for checking +a single file system. Note, that with this option, you cannot give +the +.I filesys +argument as well. +.TP +.B -V +Produce verbose output, including all file system-specific commands +that are executed. +Specifying this option more than once inhibits execution of any +file system-specific commands. +This is really only useful for testing. +.TP +.BI -t \ fstype +Specifies the type of file system to be checked. +If not specified, the type is deduced by searching for +.I filesys +in +.I /etc/fstab +and using the corresponding entry. +If the type can not be deduced, the default file system type +(currently minix) is used. +.TP +.B fs-options +File system-specific options to be passed to the real file +system checker. +Although not guaranteed, the following options are supported +by most file system checkers. +.TP +.I -a +Automatically repair the file system without any questions (use +this option with caution). +.TP +.I -l +List all the file names in the file system. +.TP +.I -r +Interactively repair the file system (ask for confirmations). +.TP +.I -s +List the super block before checking the file system. +.TP +.I -v +Produce verbose output. +.SH BUGS +All generic options must precede and not be combined with +file system-specific options. +Some file system-specific programs do not support the +.I -v +(verbose) option, nor return meaningful exit codes. +.SH AUTHORS +David Engel (david@ods.com) +.br +Fred N. van Kempen (waltje@uwalt.nl.mugnet.org) +.br +The manual page was shamelessly adapted from Remy Card's version +for the ext2 file system. +.SH SEE ALSO +.BR mkfs (8), +.BR fsck.minix (8), +.BR fsck.ext (8), +.BR fsck.ext2 (8), +.BR fsck.xiafs (8). diff --git a/disk-utils/mkfs.c b/disk-utils/mkfs.c new file mode 100644 index 00000000..018a5384 --- /dev/null +++ b/disk-utils/mkfs.c @@ -0,0 +1,297 @@ +/* + * fs-util A simple generic frontend for the for the fsck and mkfs + * programs under Linux. See the manual pages for details. + * + * Usage: fsck [-AV] [-t fstype] [fs-options] device + * mkfs [-V] [-t fstype] [fs-options] device< [size] + * + * Authors: David Engel, + * Fred N. van Kempen, + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#ifndef DEFAULT_FSTYPE +# define DEFAULT_FSTYPE "minix" +#endif + +#define _PATH_PROG "%s.%s" +#define _PROG_FSCK "fsck" + +#define EXIT_OK 0 +#define EXIT_NONDESTRUCT 1 +#define EXIT_DESTRUCT 2 +#define EXIT_UNCORRECTED 4 +#define EXIT_ERROR 8 +#define EXIT_USAGE 16 +#define EXIT_LIBRARY 128 + +static char *Version = "1.8"; +static char *ignored_types[] = { + "ignore", + "iso9660", + "msdos", + "nfs", + "proc", + "sw", + "swap", + NULL +}; + + +/* Execute a program. */ +int do_exec(char *prog, char **argv, int verbose) +{ + char *args[33]; + register int i; + int pid, status; + + /* Build the vector. */ + i = 0; + args[i++] = prog; + while(*argv != NULL && i < 32) + args[i++] = *argv++; + args[i] = NULL; + + if (verbose) { + i = 0; + while(args[i] != NULL) { + printf("%s ", args[i]); + i++; + } + printf("\n"); + if (verbose > 1) + return EXIT_OK; + } + + /* Fork and execute the correct program. */ + if ((pid = fork()) < 0) { + perror("fork"); + status = EXIT_ERROR; + } else if (pid == 0) { + (void) execvp(args[0], args); + perror(args[0]); + exit(EXIT_ERROR); + } else { + while(wait(&status) != pid) + ; + status = WEXITSTATUS(status); + } + + return status; +} + + +/* Check if we have to ignore a file system type. */ +int ignore(char *type, char *opts) +{ + char *cp; + char **ip; + + ip = ignored_types; + while (*ip != NULL) { + if (!strcmp(type, *ip)) + return 1; + ip++; + } + + for (cp = strtok(opts, ","); cp != NULL; cp = strtok(NULL, ",")) { + if (!strcmp(cp, "noauto")) + return 1; + } + + return 0; +} + + +/* Check all file systems, using the /etc/fstab table. */ +int check_all(int verbose, char **argv) +{ + char path[PATH_MAX]; + char *args[33]; + FILE *mntfile; + struct mntent *mp; + register int i; + int status = EXIT_OK; + + if (verbose) + printf("Checking all file systems.\n"); + + /* Create an array of arguments. */ + i = 0; + while (*argv != NULL && i < 32) + args[i++] = *argv++; + args[i] = NULL; + args[i + 1] = NULL; + + /* Open the mount table. */ + if ((mntfile = setmntent(MNTTAB, "r")) == NULL) { + perror(MNTTAB); + exit(EXIT_ERROR); + } + + /* Walk through the /etc/fstab file. */ + while ((mp = getmntent(mntfile)) != NULL) { + if (verbose) + printf("%-7s %-15s %-15s ", mp->mnt_type, + mp->mnt_fsname, mp->mnt_dir); + if (ignore(mp->mnt_type, mp->mnt_opts)) { + if (verbose) + printf("(ignored)\n"); + continue; + } + + /* Build program name. */ + sprintf(path, _PATH_PROG, _PROG_FSCK, mp->mnt_type); + args[i] = mp->mnt_fsname; + status |= do_exec(path, args, verbose); + } + + (void) endmntent(mntfile); + + return status; +} + + +/* Lookup filesys in /etc/fstab and return the corresponding entry. */ +struct mntent *lookup(char *filesys) +{ + FILE *mntfile; + struct mntent *mp; + + /* No filesys name given. */ + if (filesys == NULL) + return NULL; + + /* Open the mount table. */ + if ((mntfile = setmntent(MNTTAB, "r")) == NULL) { + perror(MNTTAB); + exit(EXIT_ERROR); + } + + while ((mp = getmntent(mntfile)) != NULL) { + if (!strcmp(filesys, mp->mnt_fsname) || + !strcmp(filesys, mp->mnt_dir)) + break; + } + + (void) endmntent(mntfile); + + return mp; +} + + +void usage(int fsck, char *prog) +{ + if (fsck) { + fprintf(stderr, "Usage: fsck [-AV] [-t fstype] [fs-options] filesys\n"); + } else { + fprintf(stderr, "Usage: mkfs [-V] [-t fstype] [fs-options] filesys [size]\n"); + } + + exit(EXIT_USAGE); +} + + +void main(int argc, char *argv[]) +{ + char path[PATH_MAX]; + char *oldpath, newpath[PATH_MAX]; + register char *sp; + struct mntent *fsent; + char *fstype = NULL; + int verbose = 0; + int doall = 0; + int i, fsck, more; + + /* Must be 1 for "fsck" and 0 for "mkfs". */ + if ((sp = strrchr(argv[0], '/')) != NULL) + sp++; + else + sp = argv[0]; + if (!strcmp(sp, _PROG_FSCK)) + fsck = 1; + else + fsck = 0; + + /* Check commandline options. */ + opterr = 0; + more = 0; + while ((more == 0) && ((i = getopt(argc, argv, "AVt:")) != EOF)) + switch(i) { + case 'A': + doall++; + break; + case 'V': + verbose++; + break; + case 't': + if (optarg == NULL) + usage(fsck, sp); + fstype = optarg; + break; + default: + more = 1; + break; /* start of specific arguments */ + } + + /* Did we get any specific arguments? */ + if (more) + optind--; + + /* Print our version number if requested. */ + if (verbose) + printf("%s (fsutil) version %s (%s)\n", argv[0], + Version, __DATE__); + + /* Update our PATH to include /etc/fs and /etc. */ + strcpy(newpath, "PATH=/etc/fs:/etc:"); + if ((oldpath = getenv("PATH")) != NULL) + strcat(newpath, oldpath); + putenv(newpath); + + /* If -A was specified ("check all"), double-check. */ + if (doall) { + if (!fsck || (fstype != NULL)) + usage(fsck, sp); + exit(check_all(verbose, &argv[optind])); + } else { + /* If -t wasn't specified, we must deduce fstype. */ + if (fstype == NULL) { + /* make sure that "filesys" was specified */ + if (optind >= argc) + usage(fsck, sp); + /* then try looking for it in /etc/fstab */ + if ((fsent = lookup(argv[argc - 1])) != NULL) { + argv[argc - 1] = fsent->mnt_fsname; + fstype = fsent->mnt_type; + } else { + if (!fsck && optind < argc-1) { + if ((fsent = lookup(argv[argc - 2])) != NULL) { + argv[argc - 2] = fsent->mnt_fsname; + fstype = fsent->mnt_type; + } + } + } + /* if we still don't know, use the default */ + if (fstype == NULL) fstype = DEFAULT_FSTYPE; + } + + /* Build program name. */ + sprintf(path, _PATH_PROG, sp, fstype); + exit(do_exec(path, &argv[optind], verbose)); + } + /*NOTREACHED*/ +} diff --git a/disk-utils/mkfs.minix.8 b/disk-utils/mkfs.minix.8 new file mode 100644 index 00000000..cbfb1cf4 --- /dev/null +++ b/disk-utils/mkfs.minix.8 @@ -0,0 +1,88 @@ +.\" Copyright 1992, 1993, 1994 Rickard E. Faith (faith@cs.unc.edu) +.\" May be freely distributed. +.\" " for emacs hilit19 mode +.TH MKFS 8 "10 January 1994" "Linux 0.99" "Linux Programmer's Manual" +.SH NAME +mkfs \- make a Linux MINIX filesystem +.SH SYNOPSIS +.BR "mkfs [ \-c ] [ \-n" +namelength +.B ] [ \-i +inodecount +.B ] +device size-in-blocks +.br +.B "mkfs [ \-l" +filename +.B ] +device size-in-blocks +.SH DESCRIPTION +.B mkfs +creates a Linux MINIX file-system on a device (usually a disk partition). + +The +.I device +is usually of the following form: + +.nf +.RS +/dev/hda[1-8] +/dev/hdb[1-8] +/dev/sda[1-8] +/dev/sdb[1-8] +.RE +.fi + +The +.I size-in-blocks +parameter is the desired size of the file system, in blocks. This +information can be determined from the +.BR fdisk (8) +program. Only block counts strictly greater than 10 and strictly less than +65536 are allowed. +.SH OPTIONS +.TP +.B \-c +Check the device for bad blocks before creating the file system. If any +are found, the count is printed. +.TP +.BI \-n " namelength" +Specify the maximum length of filenames. No space is allowed between the +.B \-n +and the +.IR namelength. Currently, the only allowable +values are 14 and 30. +.B 30 is the default. +.TP +.BI \-i " inodecount" +Specify the number of inodes for the filesystem. +.TP +.BI \-l " filename" +Read the bad blocks list from +.IR filename . +The file has one bad block number per line. The count of bad blocks read +is printed. +.SH "EXIT CODES" +The exit code returned by +.B mkfs.minix +is one of the following: +.IP 0 +No errors +.IP 8 +Operational error +.IP 16 +Usage or syntax error +.SH "SEE ALSO" +.BR fsck (8), +.BR mkefs (8), +.BR efsck (8), +.BR reboot (8) +.SH AUTHOR +Linus Torvalds (torvalds@cs.helsinki.fi) +.br +Error code values by Rik Faith (faith@cs.unc.edu) +.br +Inode request feature by Scott Heavner (sdh@po.cwru.edu) +.br +Support for the file system valid flag by Dr. Wettstein +(greg%wind.uucp@plains.nodak.edu) diff --git a/disk-utils/mkfs.minix.c b/disk-utils/mkfs.minix.c new file mode 100644 index 00000000..b2dcf789 --- /dev/null +++ b/disk-utils/mkfs.minix.c @@ -0,0 +1,533 @@ +/* + * mkfs.c - make a linux (minix) file-system. + * + * (C) 1991 Linus Torvalds. This file may be redistributed as per + * the Linux copyright. + */ + +/* + * 24.11.91 - time began. Used the fsck sources to get started. + * + * 25.11.91 - corrected some bugs. Added support for ".badblocks" + * The algorithm for ".badblocks" is a bit weird, but + * it should work. Oh, well. + * + * 25.01.92 - Added the -l option for getting the list of bad blocks + * out of a named file. (Dave Rivers, rivers@ponds.uucp) + * + * 28.02.92 - added %-information when using -c. + * + * 28.02.93 - added support for other namelengths than the original + * 14 characters so that I can test the new kernel routines.. + * + * Sat Oct 9 11:48:31 1993, faith@cs.unc.edu: make exit status conform + * to that required by fsutil + * + * 31.10.93 - added inode request feature, for backup floppies: use + * 32 inodes, for a news partition use more. + * (Scott Heavner, sdh@po.cwru.edu) + * + * Mon Jan 3 11:08:49 1994, Dr. Wettstein (greg%wind.uucp@plains.nodak.edu). + * Added support for file system valid flag. + * + * 9.11.94 - added test to prevent overwrite of mounted fs adapted + * from Theodore Ts'o's (tytso@athena.mit.edu) mke2fs + * program. (Daniel Quinlan, quinlan@yggdrasil.com) + * + * Usage: mkfs [-c] [-nXX] [-iXX] device size-in-blocks + * mkfs [-l filename ] device size-in-blocks + * + * -c for readablility checking (SLOW!) + * -l for getting a list of bad blocks from a file. + * -n for namelength (currently the kernel only uses 14 or 30) + * -i for number of inodes + * + * The device may be a block device or a image of one, but this isn't + * enforced (but it's not much fun on a character device :-). + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifndef __GNUC__ +#error "needs gcc for the bitop-__asm__'s" +#endif + +#ifndef __linux__ +#define volatile +#endif + +#define MINIX_ROOT_INO 1 +#define MINIX_BAD_INO 2 + +#define TEST_BUFFER_BLOCKS 16 +#define MAX_GOOD_BLOCKS 512 + +#define UPPER(size,n) ((size+((n)-1))/(n)) +#define INODE_SIZE (sizeof(struct minix_inode)) +#define INODE_BLOCKS UPPER(INODES,MINIX_INODES_PER_BLOCK) +#define INODE_BUFFER_SIZE (INODE_BLOCKS * BLOCK_SIZE) + +#define BITS_PER_BLOCK (BLOCK_SIZE<<3) + +static char * program_name = "mkfs"; +static char * device_name = NULL; +static int DEV = -1; +static long BLOCKS = 0; +static int check = 0; +static int badblocks = 0; +static int namelen = 30; /* default (changed to 30, per Linus's + suggestion, Sun Nov 21 08:05:07 1993) */ +static int dirsize = 16; +static int magic = MINIX_SUPER_MAGIC; + +static char root_block[BLOCK_SIZE] = "\0"; + +static char * inode_buffer = NULL; +#define Inode (((struct minix_inode *) inode_buffer)-1) +static char super_block_buffer[BLOCK_SIZE]; +#define Super (*(struct minix_super_block *)super_block_buffer) +#define INODES ((unsigned long)Super.s_ninodes) +#define ZONES ((unsigned long)Super.s_nzones) +#define IMAPS ((unsigned long)Super.s_imap_blocks) +#define ZMAPS ((unsigned long)Super.s_zmap_blocks) +#define FIRSTZONE ((unsigned long)Super.s_firstdatazone) +#define ZONESIZE ((unsigned long)Super.s_log_zone_size) +#define MAXSIZE ((unsigned long)Super.s_max_size) +#define MAGIC (Super.s_magic) +#define NORM_FIRSTZONE (2+IMAPS+ZMAPS+INODE_BLOCKS) + +static char inode_map[BLOCK_SIZE * MINIX_I_MAP_SLOTS]; +static char zone_map[BLOCK_SIZE * MINIX_Z_MAP_SLOTS]; + +static unsigned short good_blocks_table[MAX_GOOD_BLOCKS]; +static int used_good_blocks = 0; +static unsigned long req_nr_inodes = 0; + +#define bitop(name,op) \ +static inline int name(char * addr,unsigned int nr) \ +{ \ +int __res; \ +__asm__ __volatile__("bt" op " %1,%2; adcl $0,%0" \ +:"=g" (__res) \ +:"r" (nr),"m" (*(addr)),"0" (0)); \ +return __res; \ +} + +bitop(bit,"") +bitop(setbit,"s") +bitop(clrbit,"r") + +#define inode_in_use(x) (bit(inode_map,(x))) +#define zone_in_use(x) (bit(zone_map,(x)-FIRSTZONE+1)) + +#define mark_inode(x) (setbit(inode_map,(x))) +#define unmark_inode(x) (clrbit(inode_map,(x))) + +#define mark_zone(x) (setbit(zone_map,(x)-FIRSTZONE+1)) +#define unmark_zone(x) (clrbit(zone_map,(x)-FIRSTZONE+1)) + +/* + * Volatile to let gcc know that this doesn't return. When trying + * to compile this under minix, volatile gives a warning, as + * exit() isn't defined as volatile under minix. + */ +volatile void fatal_error(const char * fmt_string,int status) +{ + fprintf(stderr,fmt_string,program_name,device_name); + exit(status); +} + +#define usage() fatal_error("Usage: %s [-c | -l filename] [-nXX] [-iXX] /dev/name blocks\n",16) +#define die(str) fatal_error("%s: " str "\n",8) + +/* + * Check to make certain that our new filesystem won't be created on + * an already mounted partition. Code adapted from mke2fs, Copyright + * (C) 1994 Theodore Ts'o. Also licensed under GPL. + */ +static void check_mount(void) +{ + FILE * f; + struct mntent * mnt; + + if ((f = setmntent (MOUNTED, "r")) == NULL) + return; + while ((mnt = getmntent (f)) != NULL) + if (strcmp (device_name, mnt->mnt_fsname) == 0) + break; + endmntent (f); + if (!mnt) + return; + + die("%s is mounted; will not make a filesystem here!"); +} + +void write_tables(void) +{ + /* Mark the super block valid. */ + Super.s_state |= MINIX_VALID_FS; + Super.s_state &= ~MINIX_ERROR_FS; + + if (BLOCK_SIZE != lseek(DEV, BLOCK_SIZE, SEEK_SET)) + die("seek failed in write_tables"); + if (BLOCK_SIZE != write(DEV, super_block_buffer, BLOCK_SIZE)) + die("unable to write super-block"); + if (IMAPS*BLOCK_SIZE != write(DEV,inode_map,IMAPS*BLOCK_SIZE)) + die("Unable to write inode map"); + if (ZMAPS*BLOCK_SIZE != write(DEV,zone_map,ZMAPS*BLOCK_SIZE)) + die("Unable to write zone map"); + if (INODE_BUFFER_SIZE != write(DEV,inode_buffer,INODE_BUFFER_SIZE)) + die("Unable to write inodes"); +} + +void write_block(int blk, char * buffer) +{ + if (blk*BLOCK_SIZE != lseek(DEV, blk*BLOCK_SIZE, SEEK_SET)) + die("seek failed in write_block"); + if (BLOCK_SIZE != write(DEV, buffer, BLOCK_SIZE)) + die("write failed in write_block"); +} + +int get_free_block(void) +{ + int blk; + + if (used_good_blocks+1 >= MAX_GOOD_BLOCKS) + die("too many bad blocks"); + if (used_good_blocks) + blk = good_blocks_table[used_good_blocks-1]+1; + else + blk = FIRSTZONE; + while (blk < ZONES && zone_in_use(blk)) + blk++; + if (blk >= ZONES) + die("not enough good blocks"); + good_blocks_table[used_good_blocks] = blk; + used_good_blocks++; + return blk; +} + +void mark_good_blocks(void) +{ + int blk; + + for (blk=0 ; blk < used_good_blocks ; blk++) + mark_zone(good_blocks_table[blk]); +} + +inline int next(int zone) +{ + if (!zone) + zone = FIRSTZONE-1; + while (++zone < ZONES) + if (zone_in_use(zone)) + return zone; + return 0; +} + +void make_bad_inode(void) +{ + struct minix_inode * inode = &Inode[MINIX_BAD_INO]; + int i,j,zone; + int ind=0,dind=0; + unsigned short ind_block[BLOCK_SIZE>>1]; + unsigned short dind_block[BLOCK_SIZE>>1]; + +#define NEXT_BAD (zone = next(zone)) + + if (!badblocks) + return; + mark_inode(MINIX_BAD_INO); + inode->i_nlinks = 1; + inode->i_time = time(NULL); + inode->i_mode = S_IFREG + 0000; + inode->i_size = badblocks*BLOCK_SIZE; + zone = next(0); + for (i=0 ; i<7 ; i++) { + inode->i_zone[i] = zone; + if (!NEXT_BAD) + goto end_bad; + } + inode->i_zone[7] = ind = get_free_block(); + memset(ind_block,0,BLOCK_SIZE); + for (i=0 ; i<512 ; i++) { + ind_block[i] = zone; + if (!NEXT_BAD) + goto end_bad; + } + inode->i_zone[8] = dind = get_free_block(); + memset(dind_block,0,BLOCK_SIZE); + for (i=0 ; i<512 ; i++) { + write_block(ind,(char *) ind_block); + dind_block[i] = ind = get_free_block(); + memset(ind_block,0,BLOCK_SIZE); + for (j=0 ; j<512 ; j++) { + ind_block[j] = zone; + if (!NEXT_BAD) + goto end_bad; + } + } + die("too many bad blocks"); +end_bad: + if (ind) + write_block(ind, (char *) ind_block); + if (dind) + write_block(dind, (char *) dind_block); +} + +void make_root_inode(void) +{ + struct minix_inode * inode = &Inode[MINIX_ROOT_INO]; + + mark_inode(MINIX_ROOT_INO); + inode->i_zone[0] = get_free_block(); + inode->i_nlinks = 2; + inode->i_time = time(NULL); + if (badblocks) + inode->i_size = 3*dirsize; + else { + root_block[2*dirsize] = '\0'; + root_block[2*dirsize+1] = '\0'; + inode->i_size = 2*dirsize; + } + inode->i_mode = S_IFDIR + 0755; + write_block(inode->i_zone[0],root_block); +} + +void setup_tables(void) +{ + int i; + + memset(inode_map,0xff,sizeof(inode_map)); + memset(zone_map,0xff,sizeof(zone_map)); + memset(super_block_buffer,0,BLOCK_SIZE); + MAGIC = magic; + ZONESIZE = 0; + MAXSIZE = (7+512+512*512)*1024; + ZONES = BLOCKS; +/* some magic nrs: 1 inode / 3 blocks */ + if ( req_nr_inodes == 0 ) + INODES = BLOCKS/3; + else + INODES = req_nr_inodes; +/* I don't want some off-by-one errors, so this hack... */ + if ((INODES & 8191) > 8188) + INODES -= 5; + if ((INODES & 8191) < 10) + INODES -= 20; + IMAPS = UPPER(INODES,BITS_PER_BLOCK); + ZMAPS = 0; + while (ZMAPS != UPPER(BLOCKS - NORM_FIRSTZONE,BITS_PER_BLOCK)) + ZMAPS = UPPER(BLOCKS - NORM_FIRSTZONE,BITS_PER_BLOCK); + FIRSTZONE = NORM_FIRSTZONE; + for (i = FIRSTZONE ; i= ZONES) + return; + signal(SIGALRM,alarm_intr); + alarm(5); + if (!currently_testing) + return; + printf("%d ...", currently_testing); + fflush(stdout); +} + +void check_blocks(void) +{ + int try,got; + static char buffer[BLOCK_SIZE * TEST_BUFFER_BLOCKS]; + + currently_testing=0; + signal(SIGALRM,alarm_intr); + alarm(5); + while (currently_testing < ZONES) { + if (lseek(DEV,currently_testing*BLOCK_SIZE,SEEK_SET) != + currently_testing*BLOCK_SIZE) + die("seek failed in check_blocks"); + try = TEST_BUFFER_BLOCKS; + if (currently_testing + try > ZONES) + try = ZONES-currently_testing; + got = do_check(buffer, try, currently_testing); + currently_testing += got; + if (got == try) + continue; + if (currently_testing < FIRSTZONE) + die("bad blocks before data-area: cannot make fs"); + mark_zone(currently_testing); + badblocks++; + currently_testing++; + } + if (badblocks) + printf("%d bad block%s\n",badblocks,(badblocks>1)?"s":""); +} + +void get_list_blocks(filename) +char *filename; +{ + FILE *listfile; + unsigned long blockno; + + listfile=fopen(filename,"r"); + if(listfile == (FILE *)NULL) { + die("Can't open file of bad blocks"); + } + while(!feof(listfile)) { + fscanf(listfile,"%d\n", &blockno); + mark_zone(blockno); + badblocks++; + } + if(badblocks) { + printf("%d bad block%s\n", badblocks, (badblocks>1)?"s":""); + } +} + +int main(int argc, char ** argv) +{ + int i; + char * tmp; + struct stat statbuf; + char * listfile = NULL; + + if (argc && *argv) + program_name = *argv; + if (INODE_SIZE * MINIX_INODES_PER_BLOCK != BLOCK_SIZE) + die("bad inode size"); + while (argc-- > 1) { + argv++; + if (argv[0][0] != '-') + if (device_name) { + BLOCKS = strtol(argv[0],&tmp,0); + if (*tmp) { + printf("strtol error: number of" + " blocks not specified"); + usage(); + } + } else + device_name = argv[0]; + else { + if(argv[0][1] == 'l') { + listfile = argv[1]; + argv++; + if (!(argc--)) + usage(); + } else { + if(argv[0][1] == 'i') { + req_nr_inodes + = (unsigned long)atol(argv[1]); + argv++; + if (!(argc--)) + usage(); + } else while (*(++argv[0])) { + switch (argv[0][0]) { + case 'c': check=1; break; + case 'n': + i = strtoul(argv[0]+1,&tmp,0); + if (*tmp) + usage(); + argv[0][1] = '\0'; + if (i == 14) + magic = MINIX_SUPER_MAGIC; + else if (i == 30) + magic = MINIX_SUPER_MAGIC2; + else + usage(); + namelen = i; + dirsize = i+2; + break; + default: usage(); + } + } + } + } + } + if (!device_name || BLOCKS<10 || BLOCKS > 65536) { + usage(); + } + check_mount(); /* is it already mounted? */ + tmp = root_block; + tmp[0] = 1; + tmp[1] = 0; + strcpy(tmp+2,"."); + tmp += dirsize; + tmp[0] = 1; + tmp[1] = 0; + strcpy(tmp+2,".."); + tmp += dirsize; + tmp[0] = 2; + tmp[1] = 0; + strcpy(tmp+2,".badblocks"); + DEV = open(device_name,O_RDWR ); + if (DEV<0) + die("unable to open %s"); + if (fstat(DEV,&statbuf)<0) + die("unable to stat %s"); + if (!S_ISBLK(statbuf.st_mode)) + check=0; + else if (statbuf.st_rdev == 0x0300 || statbuf.st_rdev == 0x0340) + die("Will not try to make filesystem on '%s'"); + setup_tables(); + if (check) + check_blocks(); + else if (listfile) + get_list_blocks(listfile); + make_root_inode(); + make_bad_inode(); + mark_good_blocks(); + write_tables(); + return 0; +} diff --git a/disk-utils/mkswap.8 b/disk-utils/mkswap.8 new file mode 100644 index 00000000..2c02fbbe --- /dev/null +++ b/disk-utils/mkswap.8 @@ -0,0 +1,86 @@ +.\" Copyright 1992, 1993 Rickard E. Faith (faith@cs.unc.edu) +.\" May be distributed under the GNU General Public License +.\" Modified with suggestions from Linus, Mon Feb 1 21:40:49 1993 +.\" Modified with patches from Kai, Wed Jun 22 21:54:56 1994 +.\" Patches from jaggy@purplet.demon.co.uk (Mike Jagdis), Wed Feb 8 1995 +.\" Added comments from Nick Holloway, Sat Feb 11 1995, faith@cs.unc.edu +.\" " +.TH MKSWAP 8 "8 February 1995" "Linux 1.0" "Linux Programmer's Manual" +.SH NAME +mkswap \- set up a Linux swap device +.SH SYNOPSIS +.B "mkswap [ \-c ]" +.IB device " [" size-in-blocks "]" +.SH DESCRIPTION +.B mkswap +sets up a Linux swap area on a device (usually a disk partition). + +The +.I device +is usually of the following form: + +.nf +.RS +/dev/hda[1-8] +/dev/hdb[1-8] +/dev/sda[1-8] +/dev/sdb[1-8] +.RE +.fi + +The +.I size-in-blocks +parameter is the desired size of the file system, in blocks. This +information is determined automatically by mkswap if it is omitted. Block +counts are rounded down to pages of 4 kB each. Only block counts equal to +or greater than 40 and equal to or less than 131072 are allowed. Block +counts greater than 130752 are (silently) rounded down to 130752. + +As Nick Holloway explains, the actual maximum for each swap file/partition +is: +.RS +(4096 - 10) * 8 * 4096 = 133890048 bytes = 130752 blocks = 127.6875 Mb +.RE +This is because a single page is used to hold the swap bitmap at the +start of the partition, where each bit is a single 4K page. The reason +for the -10, is that the signature is "SWAP-SPACE" -- 10 characters. + +.B mkswap +can also set up swap files, although the file has to be created first. A +sequence of commands similar to the following is reasonable for this +purpose: + +.nf +.RS +# dd if=/dev/zero of=swapfile bs=1024 count=8192 +# mkswap swapfile 8192 +# sync +# swapon swapfile +.RE +.fi + +Note that the regular file has to be created before running +.B mkswap +on the file, and that the file must not contain any holes (so, using +.BR cp (1) +to create the file is not acceptable). + +.SH OPTIONS +.TP +.B \-c +Check the device for bad blocks before creating the file system. If any +are found, the count is printed. This option is meant to be used for swap +partitions +.BR only , +and should +.B not +be used for regular files! To make sure that regular files do not contain +bad blocks, the partition that contains the regular file should have been +created with +.BR "mkfs -c" . +.SH "SEE ALSO" +.BR fsck (8), +.BR mkfs (8), +.BR fdisk (8) +.SH AUTHOR +Linus Torvalds (torvalds@cs.helsinki.fi) diff --git a/disk-utils/mkswap.c b/disk-utils/mkswap.c new file mode 100644 index 00000000..bb4e2249 --- /dev/null +++ b/disk-utils/mkswap.c @@ -0,0 +1,212 @@ +/* + * mkswap.c - set up a linux swap device + * + * (C) 1991 Linus Torvalds. This file may be redistributed as per + * the Linux copyright. + */ + +/* + * 20.12.91 - time began. Got VM working yesterday by doing this by hand. + * + * Usuage: mkswap [-c] device [size-in-blocks] + * + * -c for readablility checking (use it unless you are SURE!) + * + * The device may be a block device or a image of one, but this isn't + * enforced (but it's not much fun on a character device :-). + * + * Patches from jaggy@purplet.demon.co.uk (Mike Jagdis) to make the + * size-in-blocks parameter optional added Wed Feb 8 10:33:43 1995. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#ifndef __GNUC__ +#error "needs gcc for the bitop-__asm__'s" +#endif + +#ifndef __linux__ +#define volatile +#endif + +#define TEST_BUFFER_PAGES 8 + +static char * program_name = "mkswap"; +static char * device_name = NULL; +static int DEV = -1; +static long PAGES = 0; +static int check = 0; +static int badpages = 0; + +static char signature_page[PAGE_SIZE]; + +#define bitop(name,op) \ +static inline int name(char * addr,unsigned int nr) \ +{ \ +int __res; \ +__asm__ __volatile__("bt" op " %1,%2; adcl $0,%0" \ +:"=g" (__res) \ +:"r" (nr),"m" (*(addr)),"0" (0)); \ +return __res; \ +} + +bitop(bit,"") +bitop(setbit,"s") +bitop(clrbit,"r") + +/* + * Volatile to let gcc know that this doesn't return. When trying + * to compile this under minix, volatile gives a warning, as + * exit() isn't defined as volatile under minix. + */ +volatile void fatal_error(const char * fmt_string) +{ + fprintf(stderr,fmt_string,program_name,device_name); + exit(1); +} + +#define usage() fatal_error("Usage: %s [-c] /dev/name [blocks]\n") +#define die(str) fatal_error("%s: " str "\n") + +void check_blocks(void) +{ + unsigned int current_page; + int do_seek = 1; + static char buffer[PAGE_SIZE]; + + current_page = 0; + while (current_page < PAGES) { + if (!check) { + setbit(signature_page,current_page++); + continue; + } + if (do_seek && lseek(DEV,current_page*PAGE_SIZE,SEEK_SET) != + current_page*PAGE_SIZE) + die("seek failed in check_blocks"); + if (do_seek = (PAGE_SIZE != read(DEV, buffer, PAGE_SIZE))) { + clrbit(signature_page,current_page++); + badpages++; + continue; + } + setbit(signature_page,current_page++); + } + if (badpages) + printf("%d bad page%s\n",badpages,(badpages>1)?"s":""); +} + +static long valid_offset (int fd, int offset) +{ + char ch; + + if (lseek (fd, offset, 0) < 0) + return 0; + if (read (fd, &ch, 1) < 1) + return 0; + return 1; +} + +static int count_blocks (int fd) +{ + int high, low; + + low = 0; + for (high = 1; valid_offset (fd, high); high *= 2) + low = high; + while (low < high - 1) + { + const int mid = (low + high) / 2; + + if (valid_offset (fd, mid)) + low = mid; + else + high = mid; + } + valid_offset (fd, 0); + return (low + 1); +} + +static int get_size(const char *file) +{ + int fd; + int size; + + fd = open(file, O_RDWR); + if (fd < 0) { + perror(file); + exit(1); + } + if (ioctl(fd, BLKGETSIZE, &size) >= 0) { + close(fd); + return (size * 512); + } + + size = count_blocks(fd); + close(fd); + return size; +} + +int main(int argc, char ** argv) +{ + char * tmp; + struct stat statbuf; + int goodpages; + + memset(signature_page,0,PAGE_SIZE); + if (argc && *argv) + program_name = *argv; + while (argc-- > 1) { + argv++; + if (argv[0][0] != '-') + if (device_name) { + PAGES = strtol(argv[0],&tmp,0)>>2; + if (*tmp) + usage(); + } else + device_name = argv[0]; + else while (*++argv[0]) + switch (argv[0][0]) { + case 'c': check=1; break; + default: usage(); + } + } + if (device_name && !PAGES) { + PAGES = get_size(device_name) / PAGE_SIZE; + } + if (!device_name || PAGES<10) + usage(); + if (PAGES > 32688) /* 130752 blocks */ + PAGES=32688; +#if 0 + if (PAGES > 32768) + PAGES=32768; +#endif + DEV = open(device_name,O_RDWR); + if (DEV < 0 || fstat(DEV, &statbuf) < 0) { + perror(device_name); + exit(1); + } + if (!S_ISBLK(statbuf.st_mode)) + check=0; + else if (statbuf.st_rdev == 0x0300 || statbuf.st_rdev == 0x0340) + die("Will not try to make swapdevice on '%s'"); + check_blocks(); + if (!clrbit(signature_page,0)) + die("fatal: first page unreadable"); + goodpages = PAGES - badpages - 1; + if (!goodpages) + die("Unable to set up swap-space: unreadable"); + printf("Setting up swapspace, size = %d bytes\n",goodpages*PAGE_SIZE); + strncpy(signature_page+PAGE_SIZE-10,"SWAP-SPACE",10); + if (lseek(DEV, 0, SEEK_SET)) + die("unable to rewind swap-device"); + if (PAGE_SIZE != write(DEV, signature_page, PAGE_SIZE)) + die("unable to write signature page"); + return 0; +} diff --git a/disk-utils/setfdprm.8 b/disk-utils/setfdprm.8 new file mode 100644 index 00000000..63143256 --- /dev/null +++ b/disk-utils/setfdprm.8 @@ -0,0 +1,66 @@ +.\" Copyright 1992 Rickard E. Faith (faith@cs.unc.edu) +.\" May be distributed under the GNU General Public License +.TH SETFDPRM 8 "20 November 1993" "Linux 0.99" "Linux Programmer's Manual" +.SH NAME +setfdprm \- sets user-provided floppy disk parameters +.SH SYNOPSIS +.B "setfdprm [ \-p ]" +device name +.br +.B "setfdprm [ \-p ]" +device size sectors heads tracks stretch gap rate spec1 fmt_gap +.br +.B "setfdprm [ \-c ]" +device +.br +.B "setfdprm [ \-y ]" +device +.br +.B "setfdprm [ \-n ]" +device +.SH DESCRIPTION +.B setfdprm +is a utility that can be used to load disk parameters into the +auto-detecting floppy devices, to clear old parameter sets and to disable +or enable diagnostic messages. + +Without any options, +.B setfdprm +loads the +.I device +(usually +.I /dev/fd0 +or +.IR /dev/fd1 ) +with a new parameter set with the +.I name +entry found in +.I /etc/fdprm +(usually named 360/360, etc.). These parameters stay in effect until the +media is changed. +.OPTIONS +.TP +.BI \-p " device name" +Permanently loads a new parameter set for the specified auto-configuring +floppy device for the configuration with +.I name +in +.IR /etc/fdprm . +Alternatively, the parameters can be given directly from the command line. +.TP +.BI \-c " device" +Clears the parameter set of the specified auto-configuring floppy device. +.TP +.BI -y " device" +Enables format detection messages for the specified auto-configuring floppy +device. +.TP +.BI -n " device" +Disables format detection messages for the specified auto-configuring +floppy device. +.SH BUGS +This documentation is grossly incomplete. +.SH FILES +.I /etc/fdprm +.SH AUTHOR +Werner Almesberger (almesber@nessie.cs.id.ethz.ch) diff --git a/disk-utils/setfdprm.c b/disk-utils/setfdprm.c new file mode 100644 index 00000000..bd98f5a2 --- /dev/null +++ b/disk-utils/setfdprm.c @@ -0,0 +1,149 @@ +/* setfdprm.c - Sets user-provided floppy disk parameters, re-activates + autodetection and switches diagnostic messages. */ + +#include +#include +#include +#include +#include +#include +#include + +#define FDPRMFILE "/etc/fdprm" +#define MAXLINE 200 + + +static int convert(char *arg) +{ + long result; + char *end; + + result = strtol(arg,&end,0); + if (!*end) return (int) result; + fprintf(stderr,"Invalid number: %s\n",arg); + exit(1); +} + + +static void cmd_without_param(int cmd,int fd) +{ + if (ioctl(fd,cmd,NULL) >= 0) exit(0); + perror("ioctl"); + exit(1); +} + + +static void set_params(int cmd,int fd,char **params) +{ + struct floppy_struct ft; + + ft.size = convert(params[0]); + ft.sect = convert(params[1]); + ft.head = convert(params[2]); + ft.track = convert(params[3]); + ft.stretch = convert(params[4]); + ft.gap = convert(params[5]); + ft.rate = convert(params[6]); + ft.spec1 = convert(params[7]); + ft.fmt_gap = convert(params[8]); + ft.name = NULL; + if (ioctl(fd,cmd,&ft) >= 0) exit(0); + perror("ioctl"); + exit(1); +} + + +static void find_params(int cmd,int fd,char *name) +{ + FILE *file; + char line[MAXLINE+2],this[MAXLINE+2],param[9][MAXLINE+2]; + char *params[9],*start; + int count; + + if ((file = fopen(FDPRMFILE,"r")) == NULL) { + perror(FDPRMFILE); + exit(1); + } + while (fgets(line,MAXLINE,file)) { + for (start = line; *start == ' ' || *start == '\t'; start++); + if (*start && *start != '\n' && *start != '#') { + if (sscanf(start,"%s %s %s %s %s %s %s %s %s %s",this,param[0], + param[1],param[2],param[3],param[4],param[5],param[6],param[7], + param[8]) != 10) { + fprintf(stderr,"Syntax error: '%s'\n",line); + exit(1); + } + if (!strcmp(this,name)) { + for (count = 0; count < 9; count++) + params[count] = param[count]; + set_params(cmd,fd,params); + } + } + } + fprintf(stderr,"No such parameter set: '%s'\n",name); + exit(1); +} + + +static void usage(char *name) +{ + char *this; + + if (this = strrchr(name,'/')) name = this+1; + fprintf(stderr,"usage: %s [ -p ] dev name\n",name); + fprintf(stderr," %s [ -p ] dev size sect heads tracks stretch \ +gap rate spec1 fmt_gap\n",name); +#ifdef FDMEDCNG + fprintf(stderr," %s [ -c | -y | -n | -d ] dev\n",name); +#else + fprintf(stderr," %s [ -c | -y | -n ] dev\n",name); +#endif + exit(1); +} + + +main(int argc,char **argv) +{ + int cmd,fd; + char *name; + + name = argv[0]; + if (argc < 3) usage(name); + cmd = FDSETPRM; + if (*argv[1] == '-') { + switch (argv[1][1]) { + case 'c': + cmd = FDCLRPRM; + break; + case 'p': + cmd = FDDEFPRM; + break; + case 'y': + cmd = FDMSGON; + break; + case 'n': + cmd = FDMSGOFF; + break; +#ifdef FDMEDCNG + case 'd': + cmd = FDMEDCNG; + break; +#endif + default: + usage(name); + } + argc--; + argv++; + } + if ((fd = open(argv[1],3)) < 0) { /* 3 == no access at all */ + perror(argv[1]); + exit(1); + } + if (cmd != FDSETPRM && cmd != FDDEFPRM) { + if (argc != 2) usage(name); + cmd_without_param(cmd,fd); + } + if (argc != 11 && argc != 3) usage(name); + if (argc == 11) set_params(cmd,fd,&argv[2]); + else find_params(cmd,fd,argv[2]); +} diff --git a/example.files/fstab b/example.files/fstab new file mode 100644 index 00000000..9f169dcd --- /dev/null +++ b/example.files/fstab @@ -0,0 +1,22 @@ +# /etc/fstab +# static file system information +# +# This file is not used by the kernel, but rather by mount(8) and umount(8) +# (and some day fsck(8)). Comment lines have "#" in the first column. +# Entries that are to be ignored should have "none" in the directory field, +# and have type "ignore" or options "xx". Frequency and pass are numeric +# fields for dump(8) and fsck(8) that are not used yet in Linux. You can +# leave them empty if want. + +# device directory type options freq pass +/dev/sda2 / ext2 defaults 1 1 +none /proc proc defaults 0 0 +/dev/sdb1 none swap sw 0 0 +/dev/sdb2 /var ext2 defaults 1 1 +/dev/sdc1 /usr ext2 defaults 1 1 +/dev/sdc3 /usr/src ext2 defaults 1 1 +/dev/sdc2 /home ext2 defaults 1 1 +/dev/sdc4 /playpen ext2 defaults 1 1 + +/dev/sda1 /dos msdos noexec,conv=binary,gid=20,umask=007 0 0 +/dev/sdb3 /dos/usr msdos noexec,conv=binary,gid=20,umask=007 0 0 diff --git a/example.files/inittab b/example.files/inittab new file mode 100644 index 00000000..d4a2177d --- /dev/null +++ b/example.files/inittab @@ -0,0 +1,10 @@ +# inittab +# Format: +# ttyline:termcap-entry:getty-command +tty1:linux:/sbin/getty 9600 tty1 +tty2:console:/sbin/getty 9600 tty2 +tty3:console:/sbin/getty 9600 tty3 +tty4:console:/sbin/getty 9600 tty4 +tty5:console:/sbin/getty 9600 tty5 +tty6:console:/sbin/getty 9600 tty6 +ttyS1:vt100:/sbin/getty -Lh 19200 ttyS1 diff --git a/example.files/issue b/example.files/issue new file mode 100644 index 00000000..4ad1ec5a --- /dev/null +++ b/example.files/issue @@ -0,0 +1,3 @@ + +Welcome to winter, an i486 running BOGUS Release 1.0.1 Linux 1.1.90 + diff --git a/example.files/motd b/example.files/motd new file mode 100644 index 00000000..842a6f3b --- /dev/null +++ b/example.files/motd @@ -0,0 +1,10 @@ + + |^^^^^^| + | | _____________________ + | | / \ + | (o)(o) | | + @ _) | BOGUS man!! | + | ,___| ,,| | + | / ..'' | | + /____\ \_____________________/ + diff --git a/example.files/rc b/example.files/rc new file mode 100644 index 00000000..232afc35 --- /dev/null +++ b/example.files/rc @@ -0,0 +1,79 @@ +# rc file for The Linux BOGUS Release, version 1.0.1 + + echo "Running /etc/rc. . ." + +PATH=/sbin:/usr/sbin:/bin:/usr/bin; export PATH + +# Update (bdflush) performs flushes dirty buffers back to disk and +# performs a sync every 30 seconds. + echo "Starting update" +update & + +if [ -f /fastboot ] +then + echo "Skipping file system check" +else + echo "Performing file system check" + fsck -AV -a + # If there was a failure, drop into single-user mode. + # NOTE: This is not secure. If security is needed, make sure than + # /etc/securesingle exists and "reboot single". simipleinit + # will then prompt for root's password. + if [ $? -gt 1 ] + then + echo "Warning: fsck FAILED" + echo " Starting single user shell" + echo " Fix filesystem and REBOOT" + sh -si < /dev/console > /dev/console 2>&1 + fi +fi + + echo "Mounting file systems" +# Remount the root filesystem in read-write mode +mount -n -o rw,remount / + +# Remove the fastboot check file +rm -f /fastboot + +# Remove /etc/mtab* so mount creates the /etc/mtab file +rm -f /etc/mtab* + +# Mount all partitions specified in /etc/fstab +mount -avt nonfs,nomsdos,nohpfs + +# Mount swap partition specified in /etc/fstab +swapon -a + + echo -n "Initialization: " +# Update ps database +if [ -f /usr/sbin/psupdate -a -f /usr/src/linux/vmlinux ]; then + echo -n "psdatabase " + /usr/sbin/psupdate /usr/src/linux/vmlinux +fi + +# tmp and usr may be mounted, so we do these rm's after mount + echo -n "locks " +rm -f /var/lock/LCK* + echo -n "pids " +rm -f /var/run/* + echo -n "/tmp " +rm -rf /tmp; mkdir /tmp; chmod 1777 /tmp + +# Remove /var/adm/utmp and touch it + echo -n "utmp " +if [ ! -e /var ]; then mkdir /var; chmod 755 /var; fi +if [ ! -e /var/adm ]; then mkdir /var/adm; chmod 755 /var/adm; fi +rm -f /var/adm/utmp +touch /var/adm/utmp +chmod 644 /var/adm/utmp + + echo "" + echo "Running rc.local. . ." +sh /etc/rc.local + +# Allow logins + echo "Allowing logins" +rm -f /etc/nologin + +date +exit 0 diff --git a/example.files/rc.local b/example.files/rc.local new file mode 100644 index 00000000..190242d5 --- /dev/null +++ b/example.files/rc.local @@ -0,0 +1,87 @@ +# rc.local file for The Linux BOGUS Release, version 1.0.1 + + echo -n "Local initialization: " + +# Set the host name. You will probably want to change this. + echo -n "hostname " +hostname winter + +# Update the system time from the CMOS clock. +# This assume that the CMOS clock is in local time, and that you have the +# correct links in /usr/src/zoneinfo (see zic(8) for details. (For example, +# for the east coast of the US, do: zic -l US/Eastern -p America/NewYork) +# Note: mount /usr BEFORE running rc.local! + echo -n "clock " +clock -a + +# Set screen blanker to 5 minutes. + echo -n "screen-blanking " +setterm -blank 5 + +# Set modem for 57600 bps +if [ -e /dev/modem ] +then + echo -n "high-speed-modem " + setserial /dev/modem spd_hi +fi + +# Make the keyboard repeat rate and delay reasonable. + echo -n "keyboard " +kbdrate -r 24 -d 250 >& /dev/null + +# Make CTRL-ALT-DEL do a controlled reboot (i.e., call reboot(8)) + echo -n "reboot " +ctrlaltdel soft + +# Preserve elvis files in case of a crash + echo -n "vi-files " +elvprsv "-the system went down" /tmp/elv_*.* + + echo + + + echo -n "Starting daemons: " + +# Make sure log files exist +if [ -d /var/adm ]; then + if [ ! -e /var/adm/kernlog ]; then touch /var/adm/kernlog; fi + if [ ! -e /var/adm/syslog ]; then touch /var/adm/syslog; fi + if [ ! -e /var/adm/maillog ]; then touch /var/adm/maillog; fi + if [ ! -e /var/adm/authlog ]; then touch /var/adm/authlog; fi + if [ ! -e /var/adm/news ]; then touch /var/adm/news; fi + if [ ! -e /var/adm/daemon ]; then touch /var/adm/daemon; fi + if [ ! -e /var/adm/lpd-errs ]; then touch /var/adm/lpd-errs; fi + if [ ! -e /var/adm/sysdebug ]; then touch /var/adm/sysdebug; fi + + # start up syslogd if it exists, but wait until AFTER HOSTNAME SET + if [ -f /usr/sbin/syslogd ]; then + echo -n "syslogd " + /usr/sbin/syslogd + fi +fi + +# Start up cron if it exists +if [ -f /usr/sbin/cron ] +then + echo -n "cron " + /usr/sbin/cron +fi + + echo + +# If you want networking turned on, then uncomment the following lines. +if [ -f /etc/NETWORKING_IS_ON ] +then + if [ -f /etc/rc.net ] + then + /bin/sh /etc/rc.net + fi +fi + + echo "Mounting foreign file systems" +mount -avt nfs,msdos,hpfs + +# Create a new issue file +echo > /etc/issue +echo "Welcome to `hostname`, an `uname -m` running BOGUS Release 1.0.1 `uname` `uname -r`" >> /etc/issue +echo >> /etc/issue diff --git a/example.files/rc.serial b/example.files/rc.serial new file mode 100644 index 00000000..1f3b07d0 --- /dev/null +++ b/example.files/rc.serial @@ -0,0 +1,194 @@ +# +# /etc/rc.serial +# Initializes the serial ports on your system +# +# Distributed with setserial version 2.10 +# + +# Standard flags you want your serial devices to have +# Examples: SAK, pgrp_lockout, session_lockout +# +STD_FLAGS="session_lockout" + +SETSERIAL=/sbin/setserial + +echo -n "Configuring serial ports...." + +# Do wild interrupt detection +# +${SETSERIAL} -W /dev/cua0 + +############################################################### +# +# AUTOMATIC CONFIGURATION +# +# Uncomment the appropriate lines below to enable auto-configuration +# of a particular board. Or comment them out to disable them.... +# +# NOTE! Although the automatic configuration is enabled by default, +# I strongly suggest that you comment out this section and use the +# manual configuration section instead. It's more work to set up, but +# it's more reliable. +# +############################################################### + +# Do AUTOMATIC_IRQ probing +# +AUTO_IRQ=auto_irq + +# These are the standard COM1 through COM4 devices +# +# If you have an internal modeme with a Rockwell Chipset, add a "skip_test" +# to the /dev/cua3 line below. (It's not added by default because it will +# screw up people with 8514 displays). +# +${SETSERIAL} /dev/cua0 ${AUTO_IRQ} skip_test autoconfig ${STD_FLAGS} +${SETSERIAL} /dev/cua1 ${AUTO_IRQ} skip_test autoconfig ${STD_FLAGS} +${SETSERIAL} /dev/cua2 ${AUTO_IRQ} skip_test autoconfig ${STD_FLAGS} +${SETSERIAL} /dev/cua3 ${AUTO_IRQ} autoconfig ${STD_FLAGS} + +# These are for the first AST Fourport board (base address 0x1A0) +# +${SETSERIAL} /dev/cua4 ${AUTO_IRQ} autoconfig ${STD_FLAGS} +${SETSERIAL} /dev/cua5 ${AUTO_IRQ} autoconfig ${STD_FLAGS} +${SETSERIAL} /dev/cua6 ${AUTO_IRQ} autoconfig ${STD_FLAGS} +${SETSERIAL} /dev/cua7 ${AUTO_IRQ} autoconfig ${STD_FLAGS} + +# These are for the second AST Fourport board (base address 0x2A0) +# +${SETSERIAL} /dev/cua8 ${AUTO_IRQ} autoconfig ${STD_FLAGS} +${SETSERIAL} /dev/cua9 ${AUTO_IRQ} autoconfig ${STD_FLAGS} +${SETSERIAL} /dev/cua10 ${AUTO_IRQ} autoconfig ${STD_FLAGS} +${SETSERIAL} /dev/cua11 ${AUTO_IRQ} autoconfig ${STD_FLAGS} + +# These are the 3rd and 4th ports on the Accent Async board. +# +#${SETSERIAL} /dev/cua12 ${AUTO_IRQ} autoconfig ${STD_FLAGS} +#${SETSERIAL} /dev/cua13 ${AUTO_IRQ} autoconfig ${STD_FLAGS} + +# Usenet Serial Board II (base address 0x100) +# +#${SETSERIAL} /dev/cua16 ${AUTO_IRQ} autoconfig ${STD_FLAGS} +#${SETSERIAL} /dev/cua17 ${AUTO_IRQ} autoconfig ${STD_FLAGS} +#${SETSERIAL} /dev/cua18 ${AUTO_IRQ} autoconfig ${STD_FLAGS} +#${SETSERIAL} /dev/cua19 ${AUTO_IRQ} autoconfig ${STD_FLAGS} + + +# BocaBoard 4 port (BB-1004) (base address 0x100) +# +#${SETSERIAL} /dev/cua16 ${AUTO_IRQ} autoconfig ${STD_FLAGS} +#${SETSERIAL} /dev/cua17 ${AUTO_IRQ} autoconfig ${STD_FLAGS} +#${SETSERIAL} /dev/cua18 ${AUTO_IRQ} autoconfig ${STD_FLAGS} +#${SETSERIAL} /dev/cua19 ${AUTO_IRQ} autoconfig ${STD_FLAGS} + +# BocaBoard 8 port (BB-1008) (base address 0x100), +# or two BB-1004's (base addresses 0x100 and 0x120) +# +#${SETSERIAL} /dev/cua16 ${AUTO_IRQ} autoconfig ${STD_FLAGS} +#${SETSERIAL} /dev/cua17 ${AUTO_IRQ} autoconfig ${STD_FLAGS} +#${SETSERIAL} /dev/cua18 ${AUTO_IRQ} autoconfig ${STD_FLAGS} +#${SETSERIAL} /dev/cua19 ${AUTO_IRQ} autoconfig ${STD_FLAGS} +#${SETSERIAL} /dev/cua20 ${AUTO_IRQ} autoconfig ${STD_FLAGS} +#${SETSERIAL} /dev/cua21 ${AUTO_IRQ} autoconfig ${STD_FLAGS} +#${SETSERIAL} /dev/cua22 ${AUTO_IRQ} autoconfig ${STD_FLAGS} +#${SETSERIAL} /dev/cua23 ${AUTO_IRQ} autoconfig ${STD_FLAGS} + +# BocaBoard 16 port (BB-1008), (base address 0x100), +# or two BB-1008's (base addresses 0x100 and 0x140), +# or four BB-1004's (base address 0x100, 0x120, 0x140, and 0x160) +# +# Warning --- some of these ports may conflict with the Future Domain +# SCSI controller. If you want to run both the BocaBoards and the +# Future Domain controller, you may need to change the port assignment +# of the Bocaboards -- see below in the section on manual configuration. +# +${SETSERIAL} /dev/cua16 ${AUTO_IRQ} autoconfig ${STD_FLAGS} +${SETSERIAL} /dev/cua17 ${AUTO_IRQ} autoconfig ${STD_FLAGS} +${SETSERIAL} /dev/cua18 ${AUTO_IRQ} autoconfig ${STD_FLAGS} +${SETSERIAL} /dev/cua19 ${AUTO_IRQ} autoconfig ${STD_FLAGS} +${SETSERIAL} /dev/cua20 ${AUTO_IRQ} autoconfig ${STD_FLAGS} +${SETSERIAL} /dev/cua21 ${AUTO_IRQ} autoconfig ${STD_FLAGS} +${SETSERIAL} /dev/cua22 ${AUTO_IRQ} autoconfig ${STD_FLAGS} +${SETSERIAL} /dev/cua23 ${AUTO_IRQ} autoconfig ${STD_FLAGS} +${SETSERIAL} /dev/cua24 ${AUTO_IRQ} autoconfig ${STD_FLAGS} +${SETSERIAL} /dev/cua25 ${AUTO_IRQ} autoconfig ${STD_FLAGS} +${SETSERIAL} /dev/cua26 ${AUTO_IRQ} autoconfig ${STD_FLAGS} +${SETSERIAL} /dev/cua27 ${AUTO_IRQ} autoconfig ${STD_FLAGS} +${SETSERIAL} /dev/cua28 ${AUTO_IRQ} autoconfig ${STD_FLAGS} +${SETSERIAL} /dev/cua29 ${AUTO_IRQ} autoconfig ${STD_FLAGS} +${SETSERIAL} /dev/cua30 ${AUTO_IRQ} autoconfig ${STD_FLAGS} +${SETSERIAL} /dev/cua31 ${AUTO_IRQ} autoconfig ${STD_FLAGS} + +############################################################### +# +# MANUAL CONFIGURATION +# +# If you want to do manual configuration of one or more of your +# serial ports, uncomment and modify the relevant lines. +# +############################################################### + +# These are the standard COM1 through COM4 devices +# +#${SETSERIAL} /dev/cua0 uart 16450 port 0x3F8 irq 4 ${STD_FLAGS} +#${SETSERIAL} /dev/cua1 uart 16450 port 0x2F8 irq 3 ${STD_FLAGS} +#${SETSERIAL} /dev/cua2 uart 16450 port 0x3E8 irq 4 ${STD_FLAGS} +#${SETSERIAL} /dev/cua3 uart 16450 port 0x2E8 irq 3 ${STD_FLAGS} + +# These are the first set of AST Fourport ports +# +#${SETSERIAL} /dev/cua4 uart 16450 port 0x1A0 irq 9 fourport ${STD_FLAGS} +#${SETSERIAL} /dev/cua5 uart 16450 port 0x1A8 irq 9 fourport ${STD_FLAGS} +#${SETSERIAL} /dev/cua6 uart 16450 port 0x1B0 irq 9 fourport ${STD_FLAGS} +#${SETSERIAL} /dev/cua7 uart 16450 port 0x1B8 irq 9 fourport ${STD_FLAGS} + +# These are the second set of AST Fourport ports +# +#${SETSERIAL} /dev/cua8 uart 16450 port 0x2A0 irq 5 fourport ${STD_FLAGS} +#${SETSERIAL} /dev/cua9 uart 16450 port 0x2A8 irq 5 fourport ${STD_FLAGS} +#${SETSERIAL} /dev/cua10 uart 16450 port 0x2B0 irq 5 fourport ${STD_FLAGS} +#${SETSERIAL} /dev/cua11 uart 16450 port 0x2B8 irq 5 fourport ${STD_FLAGS} + +# These are the 3rd and 4th ports on the Accent Async board. +# +#${SETSERIAL} /dev/cua12 uart 16450 port 0x330 irq 4 ${STD_FLAGS} +#${SETSERIAL} /dev/cua13 uart 16450 port 0x338 irq 4 ${STD_FLAGS} + +# These are two spare devices you can use to customize for +# some board which is not supported above.... +#${SETSERIAL} /dev/cua14 uart XXXXX port XXXX irq X ${STD_FLAGS} +#${SETSERIAL} /dev/cua15 uart XXXXX port XXXX irq X ${STD_FLAGS} + +# These are the ports used for either the Usenet Serial II +# board, or the Boca Board 4, 8, or 16 port boards. +# +# Uncomment only the first 4 lines for the Usenet Serial II board, +# and uncomment the first 4, 8, or all 16 lines for the +# Boca Board BB-1004, BB-1008, and BB-2016 respectively. +# +#${SETSERIAL} /dev/cua16 uart 16550A port 0x100 irq 12 ${STD_FLAGS} +#${SETSERIAL} /dev/cua17 uart 16550A port 0x108 irq 12 ${STD_FLAGS} +#${SETSERIAL} /dev/cua18 uart 16550A port 0x110 irq 12 ${STD_FLAGS} +#${SETSERIAL} /dev/cua19 uart 16550A port 0x118 irq 12 ${STD_FLAGS} +#${SETSERIAL} /dev/cua20 uart 16550A port 0x120 irq 12 ${STD_FLAGS} +#${SETSERIAL} /dev/cua21 uart 16550A port 0x128 irq 12 ${STD_FLAGS} +#${SETSERIAL} /dev/cua22 uart 16550A port 0x130 irq 12 ${STD_FLAGS} +#${SETSERIAL} /dev/cua23 uart 16550A port 0x138 irq 12 ${STD_FLAGS} +#${SETSERIAL} /dev/cua24 uart 16550A port 0x140 irq 12 ${STD_FLAGS} +#${SETSERIAL} /dev/cua25 uart 16550A port 0x148 irq 12 ${STD_FLAGS} +#${SETSERIAL} /dev/cua26 uart 16550A port 0x150 irq 12 ${STD_FLAGS} +#${SETSERIAL} /dev/cua27 uart 16550A port 0x158 irq 12 ${STD_FLAGS} +#${SETSERIAL} /dev/cua28 uart 16550A port 0x160 irq 12 ${STD_FLAGS} +#${SETSERIAL} /dev/cua29 uart 16550A port 0x168 irq 12 ${STD_FLAGS} +#${SETSERIAL} /dev/cua30 uart 16550A port 0x170 irq 12 ${STD_FLAGS} +#${SETSERIAL} /dev/cua31 uart 16550A port 0x178 irq 12 ${STD_FLAGS} + +########################################################### +# +# Print the results of the serial configuration process +# +########################################################### + +echo "done." + +${SETSERIAL} -bg /dev/cua? /dev/cua?? diff --git a/example.files/securetty b/example.files/securetty new file mode 100644 index 00000000..d874765d --- /dev/null +++ b/example.files/securetty @@ -0,0 +1,5 @@ +tty1 +tty2 +tty3 +tty4 +ttyS1 diff --git a/example.files/shells b/example.files/shells new file mode 100644 index 00000000..14b99f1a --- /dev/null +++ b/example.files/shells @@ -0,0 +1,4 @@ +/bin/sh +/bin/bash +/bin/csh +/bin/tcsh diff --git a/example.files/syslog.conf b/example.files/syslog.conf new file mode 100644 index 00000000..66e436b4 --- /dev/null +++ b/example.files/syslog.conf @@ -0,0 +1,12 @@ +kern.* /var/adm/kernlog +mark.info;daemon.notice;mail.crit /var/adm/syslog +mail.info;mail.debug /var/adm/maillog +daemon.info /var/adm/daemon +lpr.info /var/adm/lpd-errs +auth.notice /var/adm/authlog +*.err /var/adm/syslog +*.notice;auth.debug /var/adm/sysdebug +*.alert /dev/console +news.alert;news.crit;news.err /var/adm/news +news.warning;news.notice /var/adm/news +news.info;news.debug /var/adm/news diff --git a/example.files/syslog.conf.alt b/example.files/syslog.conf.alt new file mode 100644 index 00000000..c5923c4c --- /dev/null +++ b/example.files/syslog.conf.alt @@ -0,0 +1,22 @@ +# Log all kernel messages to the console. +# Logging much else clutters up the screen. +kern.* /dev/console +#kern.* /dev/cua1 + +# Log anything (except mail) of level info or higher. +# Don't log private authentication messages! +*.info;mail.none;authpriv.none /var/adm/messages + +# The authpriv file has restricted access. +authpriv.* /var/adm/secure + +# Log all the mail messages in one place. +mail.* /var/adm/maillog + +# Everybody gets emergency messages, plus log them on another +# machine. +*.emerg * + +# Save mail and news errors of level err and higher in a +# special file. +uucp,news.crit /var/adm/spooler diff --git a/games/Makefile b/games/Makefile new file mode 100644 index 00000000..0f7bc6e7 --- /dev/null +++ b/games/Makefile @@ -0,0 +1,35 @@ +# Makefile -- Makefile for util-linux Linux utilities +# Created: Sat Dec 26 20:09:40 1992 +# Revised: Wed Feb 8 20:30:16 1995 by faith@cs.unc.edu +# Copyright 1992, 1993, 1994, 1995 Rickard E. Faith (faith@cs.unc.edu) +# + +include ../MCONFIG + +# Where to put man pages? + +MAN6= banner.6 ddate.6 + +# Where to put binaries? +# See the "install" rule for the links. . . + +USRGAMES= banner ddate + +all: $(USRGAMES) + +%.o: %.c + $(CC) -c $(CFLAGS) $< -o $@ + +# Rules for everything else + +banner: banner.o $(BSD)/getopt.o $(BSD)/err.o +ddate: ddate.o + +install: all + $(INSTALLDIR) $(USRGAMESDIR) + $(INSTALLBIN) $(USRGAMES) $(USRGAMESDIR) + $(INSTALLDIR) $(MAN6DIR) + $(INSTALLMAN) $(MAN6) $(MAN6DIR) + +clean: + -rm -f *.o *~ core $(USRGAMES) diff --git a/games/banner.6 b/games/banner.6 new file mode 100644 index 00000000..3427a669 --- /dev/null +++ b/games/banner.6 @@ -0,0 +1,72 @@ +.\" 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. +.\" +.\" @(#)banner.6 8.1 (Berkeley) 6/6/93 +.\" +.TH BANNER 6 "June 6, 1993" +.UC +.SH NAME +banner \- print large banner on printer +.SH SYNOPSIS +.B /usr/games/banner +[ +.BI \-w n +] +message ... +.SH DESCRIPTION +.I Banner +prints a large, high quality banner on the standard output. +If the message is omitted, it prompts for and +reads one line of its standard input. If +.B \-w +is given, the output is scrunched down from a width of 132 to +.I n , +suitable for a narrow terminal. If +.I n +is omitted, it defaults to 80. +.PP +The output should be printed on a hard-copy device, up to 132 columns wide, +with no breaks between the pages. The volume is great enough that you +may want +a printer or a fast hardcopy terminal, but if you are patient, a +decwriter or other 300 baud terminal will do. +.SH BUGS +Several ASCII characters are not defined, notably <, >, [, ], \\, +^, _, {, }, |, and ~. Also, the characters ", ', and & are funny +looking (but in a useful way.) +.PP +The +.B \-w +option is implemented by skipping some rows and columns. +The smaller it gets, the grainier the output. +Sometimes it runs letters together. +.SH AUTHOR +Mark Horton diff --git a/games/banner.c b/games/banner.c new file mode 100644 index 00000000..65d04e5d --- /dev/null +++ b/games/banner.c @@ -0,0 +1,1165 @@ +/* + * Copyright (c) 1980, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1980, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)banner.c 8.3 (Berkeley) 4/2/94"; +#endif /* not lint */ + +/* + * banner - prints large signs + * banner [-w#] [-d] [-t] message ... + */ + +#include +#include +#include +#include +#include + +#define MAXMSG 1024 +#define DWIDTH 132 +#define NCHARS 128 +#define NBYTES 9271 + +/* Pointers into data_table for each ASCII char */ +int asc_ptr[NCHARS] = { +/* ^@ */ 0, 0, 0, 0, 0, 0, 0, 0, +/* ^H */ 0, 0, 0, 0, 0, 0, 0, 0, +/* ^P */ 0, 0, 0, 0, 0, 0, 0, 0, +/* ^X */ 0, 0, 0, 0, 0, 0, 0, 0, +/* */ 1, 3, 50, 81, 104, 281, 483, 590, +/* ( */ 621, 685, 749, 851, 862, 893, 898, 921, +/* 0 */1019, 1150, 1200, 1419, 1599, 1744, 1934, 2111, +/* 8 */2235, 2445, 2622, 2659, 0, 2708, 0, 2715, +/* @ */2857, 3072, 3273, 3403, 3560, 3662, 3730, 3785, +/* H */3965, 4000, 4015, 4115, 4281, 4314, 4432, 4548, +/* P */4709, 4790, 4999, 5188, 5397, 5448, 5576, 5710, +/* X */5892, 6106, 6257, 0, 0, 0, 0, 0, +/* ` */ 50, 6503, 6642, 6733, 6837, 6930, 7073, 7157, +/* h */7380, 7452, 7499, 7584, 7689, 7702, 7797, 7869, +/* p */7978, 8069, 8160, 8222, 8381, 8442, 8508, 8605, +/* x */8732, 8888, 9016, 0, 0, 0, 0, 0 +}; + +/* + * Table of stuff to print. Format: + * 128+n -> print current line n times. + * 64+n -> this is last byte of char. + * else, put m chars at position n (where m + * is the next elt in array) and goto second + * next element in array. + */ +char data_table[NBYTES] = { +/* 0 1 2 3 4 5 6 7 8 9 */ +/* 0 */ 129, 227, 130, 34, 6, 90, 19, 129, 32, 10, +/* 10 */ 74, 40, 129, 31, 12, 64, 53, 129, 30, 14, +/* 20 */ 54, 65, 129, 30, 14, 53, 67, 129, 30, 14, +/* 30 */ 54, 65, 129, 31, 12, 64, 53, 129, 32, 10, +/* 40 */ 74, 40, 129, 34, 6, 90, 19, 129, 194, 130, +/* 50 */ 99, 9, 129, 97, 14, 129, 96, 18, 129, 95, +/* 60 */ 22, 129, 95, 16, 117, 2, 129, 95, 14, 129, +/* 70 */ 96, 11, 129, 97, 9, 129, 99, 6, 129, 194, +/* 80 */ 129, 87, 4, 101, 4, 131, 82, 28, 131, 87, +/* 90 */ 4, 101, 4, 133, 82, 28, 131, 87, 4, 101, +/* 100 */ 4, 131, 193, 129, 39, 1, 84, 27, 129, 38, +/* 110 */ 3, 81, 32, 129, 37, 5, 79, 35, 129, 36, +/* 120 */ 5, 77, 38, 129, 35, 5, 76, 40, 129, 34, +/* 130 */ 5, 75, 21, 103, 14, 129, 33, 5, 74, 19, +/* 140 */ 107, 11, 129, 32, 5, 73, 17, 110, 9, 129, +/* 150 */ 32, 4, 73, 16, 112, 7, 129, 31, 4, 72, +/* 160 */ 15, 114, 6, 129, 31, 4, 72, 14, 115, 5, +/* 170 */ 129, 30, 4, 71, 15, 116, 5, 129, 27, 97, +/* 180 */ 131, 30, 4, 69, 14, 117, 4, 129, 30, 4, +/* 190 */ 68, 15, 117, 4, 132, 30, 4, 68, 14, 117, +/* 200 */ 4, 129, 27, 97, 131, 30, 5, 65, 15, 116, +/* 210 */ 5, 129, 31, 4, 65, 14, 116, 4, 129, 31, +/* 220 */ 6, 64, 15, 116, 4, 129, 32, 7, 62, 16, +/* 230 */ 115, 4, 129, 32, 9, 61, 17, 114, 5, 129, +/* 240 */ 33, 11, 58, 19, 113, 5, 129, 34, 14, 55, +/* 250 */ 21, 112, 5, 129, 35, 40, 111, 5, 129, 36, +/* 260 */ 38, 110, 5, 129, 37, 35, 109, 5, 129, 38, +/* 270 */ 32, 110, 3, 129, 40, 27, 111, 1, 129, 193, +/* 280 */ 129, 30, 4, 103, 9, 129, 30, 7, 100, 15, +/* 290 */ 129, 30, 10, 99, 17, 129, 33, 10, 97, 6, +/* 300 */ 112, 6, 129, 36, 10, 96, 5, 114, 5, 129, +/* 310 */ 39, 10, 96, 4, 115, 4, 129, 42, 10, 95, +/* 320 */ 4, 116, 4, 129, 45, 10, 95, 3, 117, 3, +/* 330 */ 129, 48, 10, 95, 3, 117, 3, 129, 51, 10, +/* 340 */ 95, 4, 116, 4, 129, 54, 10, 96, 4, 115, +/* 350 */ 4, 129, 57, 10, 96, 5, 114, 5, 129, 60, +/* 360 */ 10, 97, 6, 112, 6, 129, 63, 10, 99, 17, +/* 370 */ 129, 66, 10, 100, 15, 129, 69, 10, 103, 9, +/* 380 */ 129, 39, 9, 72, 10, 129, 36, 15, 75, 10, +/* 390 */ 129, 35, 17, 78, 10, 129, 33, 6, 48, 6, +/* 400 */ 81, 10, 129, 32, 5, 50, 5, 84, 10, 129, +/* 410 */ 32, 4, 51, 4, 87, 10, 129, 31, 4, 52, +/* 420 */ 4, 90, 10, 129, 31, 3, 53, 3, 93, 10, +/* 430 */ 129, 31, 3, 53, 3, 96, 10, 129, 31, 4, +/* 440 */ 52, 4, 99, 10, 129, 32, 4, 51, 4, 102, +/* 450 */ 10, 129, 32, 5, 50, 5, 105, 10, 129, 33, +/* 460 */ 6, 48, 6, 108, 10, 129, 35, 17, 111, 10, +/* 470 */ 129, 36, 15, 114, 7, 129, 40, 9, 118, 4, +/* 480 */ 129, 193, 129, 48, 18, 129, 43, 28, 129, 41, +/* 490 */ 32, 129, 39, 36, 129, 37, 40, 129, 35, 44, +/* 500 */ 129, 34, 46, 129, 33, 13, 68, 13, 129, 32, +/* 510 */ 9, 73, 9, 129, 32, 7, 75, 7, 129, 31, +/* 520 */ 6, 77, 6, 129, 31, 5, 78, 5, 129, 30, +/* 530 */ 5, 79, 5, 129, 20, 74, 132, 30, 4, 80, +/* 540 */ 4, 129, 31, 3, 79, 4, 129, 31, 4, 79, +/* 550 */ 4, 129, 32, 3, 78, 4, 129, 32, 4, 76, +/* 560 */ 6, 129, 33, 4, 74, 7, 129, 34, 4, 72, +/* 570 */ 8, 129, 35, 5, 72, 7, 129, 37, 5, 73, +/* 580 */ 4, 129, 39, 4, 74, 1, 129, 129, 193, 130, +/* 590 */ 111, 6, 129, 109, 10, 129, 108, 12, 129, 107, +/* 600 */ 14, 129, 97, 2, 105, 16, 129, 99, 22, 129, +/* 610 */ 102, 18, 129, 105, 14, 129, 108, 9, 129, 194, +/* 620 */ 130, 63, 25, 129, 57, 37, 129, 52, 47, 129, +/* 630 */ 48, 55, 129, 44, 63, 129, 41, 69, 129, 38, +/* 640 */ 75, 129, 36, 79, 129, 34, 83, 129, 33, 28, +/* 650 */ 90, 28, 129, 32, 23, 96, 23, 129, 32, 17, +/* 660 */ 102, 17, 129, 31, 13, 107, 13, 129, 30, 9, +/* 670 */ 112, 9, 129, 30, 5, 116, 5, 129, 30, 1, +/* 680 */ 120, 1, 129, 194, 130, 30, 1, 120, 1, 129, +/* 690 */ 30, 5, 116, 5, 129, 30, 9, 112, 9, 129, +/* 700 */ 31, 13, 107, 13, 129, 32, 17, 102, 17, 129, +/* 710 */ 32, 23, 96, 23, 129, 33, 28, 90, 28, 129, +/* 720 */ 34, 83, 129, 36, 79, 129, 38, 75, 129, 41, +/* 730 */ 69, 129, 44, 63, 129, 48, 55, 129, 52, 47, +/* 740 */ 129, 57, 37, 129, 63, 25, 129, 194, 129, 80, +/* 750 */ 4, 130, 80, 4, 129, 68, 2, 80, 4, 94, +/* 760 */ 2, 129, 66, 6, 80, 4, 92, 6, 129, 67, +/* 770 */ 7, 80, 4, 90, 7, 129, 69, 7, 80, 4, +/* 780 */ 88, 7, 129, 71, 6, 80, 4, 87, 6, 129, +/* 790 */ 72, 20, 129, 74, 16, 129, 76, 12, 129, 62, +/* 800 */ 40, 131, 76, 12, 129, 74, 16, 129, 72, 20, +/* 810 */ 129, 71, 6, 80, 4, 87, 6, 129, 69, 7, +/* 820 */ 80, 4, 88, 7, 129, 67, 7, 80, 4, 90, +/* 830 */ 7, 129, 66, 6, 80, 4, 92, 6, 129, 68, +/* 840 */ 2, 80, 4, 94, 2, 129, 80, 4, 130, 193, +/* 850 */ 129, 60, 4, 139, 41, 42, 131, 60, 4, 139, +/* 860 */ 193, 130, 34, 6, 129, 32, 10, 129, 31, 12, +/* 870 */ 129, 30, 14, 129, 20, 2, 28, 16, 129, 22, +/* 880 */ 22, 129, 24, 19, 129, 27, 15, 129, 31, 9, +/* 890 */ 129, 194, 129, 60, 4, 152, 193, 130, 34, 6, +/* 900 */ 129, 32, 10, 129, 31, 12, 129, 30, 14, 131, +/* 910 */ 31, 12, 129, 32, 10, 129, 34, 6, 129, 194, +/* 920 */ 129, 30, 4, 129, 30, 7, 129, 30, 10, 129, +/* 930 */ 33, 10, 129, 36, 10, 129, 39, 10, 129, 42, +/* 940 */ 10, 129, 45, 10, 129, 48, 10, 129, 51, 10, +/* 950 */ 129, 54, 10, 129, 57, 10, 129, 60, 10, 129, +/* 960 */ 63, 10, 129, 66, 10, 129, 69, 10, 129, 72, +/* 970 */ 10, 129, 75, 10, 129, 78, 10, 129, 81, 10, +/* 980 */ 129, 84, 10, 129, 87, 10, 129, 90, 10, 129, +/* 990 */ 93, 10, 129, 96, 10, 129, 99, 10, 129, 102, +/* 1000 */ 10, 129, 105, 10, 129, 108, 10, 129, 111, 10, +/* 1010 */ 129, 114, 7, 129, 117, 4, 129, 193, 129, 60, +/* 1020 */ 31, 129, 53, 45, 129, 49, 53, 129, 46, 59, +/* 1030 */ 129, 43, 65, 129, 41, 69, 129, 39, 73, 129, +/* 1040 */ 37, 77, 129, 36, 79, 129, 35, 15, 101, 15, +/* 1050 */ 129, 34, 11, 106, 11, 129, 33, 9, 109, 9, +/* 1060 */ 129, 32, 7, 112, 7, 129, 31, 6, 114, 6, +/* 1070 */ 129, 31, 5, 115, 5, 129, 30, 5, 116, 5, +/* 1080 */ 129, 30, 4, 117, 4, 132, 30, 5, 116, 5, +/* 1090 */ 129, 31, 5, 115, 5, 129, 31, 6, 114, 6, +/* 1100 */ 129, 32, 7, 112, 7, 129, 33, 9, 109, 9, +/* 1110 */ 129, 34, 11, 106, 11, 129, 35, 15, 101, 15, +/* 1120 */ 129, 36, 79, 129, 37, 77, 129, 39, 73, 129, +/* 1130 */ 41, 69, 129, 43, 65, 129, 46, 59, 129, 49, +/* 1140 */ 53, 129, 53, 45, 129, 60, 31, 129, 193, 129, +/* 1150 */ 30, 4, 129, 30, 4, 100, 1, 129, 30, 4, +/* 1160 */ 100, 3, 129, 30, 4, 100, 5, 129, 30, 76, +/* 1170 */ 129, 30, 78, 129, 30, 80, 129, 30, 82, 129, +/* 1180 */ 30, 83, 129, 30, 85, 129, 30, 87, 129, 30, +/* 1190 */ 89, 129, 30, 91, 129, 30, 4, 132, 193, 129, +/* 1200 */ 30, 3, 129, 30, 7, 129, 30, 10, 112, 1, +/* 1210 */ 129, 30, 13, 112, 2, 129, 30, 16, 112, 3, +/* 1220 */ 129, 30, 18, 111, 5, 129, 30, 21, 111, 6, +/* 1230 */ 129, 30, 23, 112, 6, 129, 30, 14, 47, 8, +/* 1240 */ 113, 6, 129, 30, 14, 49, 8, 114, 5, 129, +/* 1250 */ 30, 14, 51, 8, 115, 5, 129, 30, 14, 53, +/* 1260 */ 8, 116, 4, 129, 30, 14, 55, 8, 116, 5, +/* 1270 */ 129, 30, 14, 56, 9, 117, 4, 129, 30, 14, +/* 1280 */ 57, 9, 117, 4, 129, 30, 14, 58, 10, 117, +/* 1290 */ 4, 129, 30, 14, 59, 10, 117, 4, 129, 30, +/* 1300 */ 14, 60, 11, 117, 4, 129, 30, 14, 61, 11, +/* 1310 */ 116, 5, 129, 30, 14, 62, 11, 116, 5, 129, +/* 1320 */ 30, 14, 63, 12, 115, 6, 129, 30, 14, 64, +/* 1330 */ 13, 114, 7, 129, 30, 14, 65, 13, 113, 8, +/* 1340 */ 129, 30, 14, 65, 15, 111, 9, 129, 30, 14, +/* 1350 */ 66, 16, 109, 11, 129, 30, 14, 67, 17, 107, +/* 1360 */ 12, 129, 30, 14, 68, 20, 103, 16, 129, 30, +/* 1370 */ 14, 69, 49, 129, 30, 14, 70, 47, 129, 30, +/* 1380 */ 14, 71, 45, 129, 30, 14, 73, 42, 129, 30, +/* 1390 */ 15, 75, 38, 129, 33, 12, 77, 34, 129, 36, +/* 1400 */ 10, 79, 30, 129, 40, 6, 82, 23, 129, 44, +/* 1410 */ 3, 86, 15, 129, 47, 1, 129, 193, 129, 129, +/* 1420 */ 38, 3, 129, 37, 5, 111, 1, 129, 36, 7, +/* 1430 */ 111, 2, 129, 35, 9, 110, 5, 129, 34, 8, +/* 1440 */ 110, 6, 129, 33, 7, 109, 8, 129, 32, 7, +/* 1450 */ 110, 8, 129, 32, 6, 112, 7, 129, 31, 6, +/* 1460 */ 113, 6, 129, 31, 5, 114, 6, 129, 30, 5, +/* 1470 */ 115, 5, 129, 30, 5, 116, 4, 129, 30, 4, +/* 1480 */ 117, 4, 131, 30, 4, 117, 4, 129, 30, 4, +/* 1490 */ 79, 2, 117, 4, 129, 30, 5, 78, 4, 117, +/* 1500 */ 4, 129, 30, 5, 77, 6, 116, 5, 129, 30, +/* 1510 */ 6, 76, 8, 115, 6, 129, 30, 7, 75, 11, +/* 1520 */ 114, 6, 129, 30, 8, 73, 15, 112, 8, 129, +/* 1530 */ 31, 9, 71, 19, 110, 9, 129, 31, 11, 68, +/* 1540 */ 26, 107, 12, 129, 32, 13, 65, 14, 82, 36, +/* 1550 */ 129, 32, 16, 61, 17, 83, 34, 129, 33, 44, +/* 1560 */ 84, 32, 129, 34, 42, 85, 30, 129, 35, 40, +/* 1570 */ 87, 27, 129, 36, 38, 89, 23, 129, 38, 34, +/* 1580 */ 92, 17, 129, 40, 30, 95, 11, 129, 42, 26, +/* 1590 */ 129, 45, 20, 129, 49, 11, 129, 193, 129, 49, +/* 1600 */ 1, 129, 49, 4, 129, 49, 6, 129, 49, 8, +/* 1610 */ 129, 49, 10, 129, 49, 12, 129, 49, 14, 129, +/* 1620 */ 49, 17, 129, 49, 19, 129, 49, 21, 129, 49, +/* 1630 */ 23, 129, 49, 14, 65, 9, 129, 49, 14, 67, +/* 1640 */ 9, 129, 49, 14, 69, 9, 129, 49, 14, 71, +/* 1650 */ 10, 129, 49, 14, 74, 9, 129, 49, 14, 76, +/* 1660 */ 9, 129, 49, 14, 78, 9, 129, 49, 14, 80, +/* 1670 */ 9, 129, 49, 14, 82, 9, 129, 49, 14, 84, +/* 1680 */ 9, 129, 30, 4, 49, 14, 86, 10, 129, 30, +/* 1690 */ 4, 49, 14, 89, 9, 129, 30, 4, 49, 14, +/* 1700 */ 91, 9, 129, 30, 4, 49, 14, 93, 9, 129, +/* 1710 */ 30, 74, 129, 30, 76, 129, 30, 78, 129, 30, +/* 1720 */ 81, 129, 30, 83, 129, 30, 85, 129, 30, 87, +/* 1730 */ 129, 30, 89, 129, 30, 91, 129, 30, 4, 49, +/* 1740 */ 14, 132, 193, 129, 37, 1, 129, 36, 3, 77, +/* 1750 */ 3, 129, 35, 5, 78, 11, 129, 34, 7, 78, +/* 1760 */ 21, 129, 33, 7, 79, 29, 129, 32, 7, 79, +/* 1770 */ 38, 129, 32, 6, 80, 4, 92, 29, 129, 31, +/* 1780 */ 6, 80, 5, 102, 19, 129, 31, 5, 80, 6, +/* 1790 */ 107, 14, 129, 31, 4, 81, 5, 107, 14, 129, +/* 1800 */ 30, 5, 81, 6, 107, 14, 129, 30, 4, 81, +/* 1810 */ 6, 107, 14, 130, 30, 4, 81, 7, 107, 14, +/* 1820 */ 129, 30, 4, 80, 8, 107, 14, 130, 30, 5, +/* 1830 */ 80, 8, 107, 14, 129, 30, 5, 79, 9, 107, +/* 1840 */ 14, 129, 31, 5, 79, 9, 107, 14, 129, 31, +/* 1850 */ 6, 78, 10, 107, 14, 129, 32, 6, 76, 11, +/* 1860 */ 107, 14, 129, 32, 8, 74, 13, 107, 14, 129, +/* 1870 */ 33, 10, 71, 16, 107, 14, 129, 33, 15, 67, +/* 1880 */ 19, 107, 14, 129, 34, 51, 107, 14, 129, 35, +/* 1890 */ 49, 107, 14, 129, 36, 47, 107, 14, 129, 37, +/* 1900 */ 45, 107, 14, 129, 39, 41, 107, 14, 129, 41, +/* 1910 */ 37, 107, 14, 129, 44, 32, 107, 14, 129, 47, +/* 1920 */ 25, 111, 10, 129, 51, 16, 115, 6, 129, 119, +/* 1930 */ 2, 129, 193, 129, 56, 39, 129, 51, 49, 129, +/* 1940 */ 47, 57, 129, 44, 63, 129, 42, 67, 129, 40, +/* 1950 */ 71, 129, 38, 75, 129, 37, 77, 129, 35, 81, +/* 1960 */ 129, 34, 16, 74, 5, 101, 16, 129, 33, 11, +/* 1970 */ 76, 5, 107, 11, 129, 32, 9, 77, 5, 110, +/* 1980 */ 9, 129, 32, 7, 79, 4, 112, 7, 129, 31, +/* 1990 */ 6, 80, 4, 114, 6, 129, 31, 5, 81, 4, +/* 2000 */ 115, 5, 129, 30, 5, 82, 4, 116, 5, 129, +/* 2010 */ 30, 4, 82, 4, 116, 5, 129, 30, 4, 82, +/* 2020 */ 5, 117, 4, 131, 30, 5, 82, 5, 117, 4, +/* 2030 */ 129, 31, 5, 81, 6, 117, 4, 129, 31, 6, +/* 2040 */ 80, 7, 117, 4, 129, 32, 7, 79, 8, 117, +/* 2050 */ 4, 129, 32, 9, 77, 9, 116, 5, 129, 33, +/* 2060 */ 11, 75, 11, 116, 4, 129, 34, 16, 69, 16, +/* 2070 */ 115, 5, 129, 35, 49, 114, 5, 129, 37, 46, +/* 2080 */ 113, 5, 129, 38, 44, 112, 6, 129, 40, 41, +/* 2090 */ 112, 5, 129, 42, 37, 113, 3, 129, 44, 33, +/* 2100 */ 114, 1, 129, 47, 27, 129, 51, 17, 129, 193, +/* 2110 */ 129, 103, 2, 129, 103, 6, 129, 104, 9, 129, +/* 2120 */ 105, 12, 129, 106, 15, 129, 107, 14, 135, 30, +/* 2130 */ 10, 107, 14, 129, 30, 17, 107, 14, 129, 30, +/* 2140 */ 25, 107, 14, 129, 30, 31, 107, 14, 129, 30, +/* 2150 */ 37, 107, 14, 129, 30, 42, 107, 14, 129, 30, +/* 2160 */ 46, 107, 14, 129, 30, 50, 107, 14, 129, 30, +/* 2170 */ 54, 107, 14, 129, 30, 58, 107, 14, 129, 59, +/* 2180 */ 32, 107, 14, 129, 64, 30, 107, 14, 129, 74, +/* 2190 */ 23, 107, 14, 129, 81, 18, 107, 14, 129, 86, +/* 2200 */ 16, 107, 14, 129, 91, 14, 107, 14, 129, 96, +/* 2210 */ 25, 129, 100, 21, 129, 104, 17, 129, 107, 14, +/* 2220 */ 129, 111, 10, 129, 114, 7, 129, 117, 4, 129, +/* 2230 */ 120, 1, 129, 193, 129, 48, 13, 129, 44, 21, +/* 2240 */ 129, 42, 26, 129, 40, 30, 92, 12, 129, 38, +/* 2250 */ 34, 88, 20, 129, 36, 37, 86, 25, 129, 35, +/* 2260 */ 39, 84, 29, 129, 34, 13, 63, 12, 82, 33, +/* 2270 */ 129, 33, 11, 67, 9, 80, 36, 129, 32, 9, +/* 2280 */ 70, 7, 79, 38, 129, 31, 8, 72, 46, 129, +/* 2290 */ 30, 7, 74, 22, 108, 11, 129, 30, 6, 75, +/* 2300 */ 19, 111, 9, 129, 30, 5, 75, 17, 113, 7, +/* 2310 */ 129, 30, 5, 74, 16, 114, 6, 129, 30, 4, +/* 2320 */ 73, 16, 115, 6, 129, 30, 4, 72, 16, 116, +/* 2330 */ 5, 129, 30, 4, 72, 15, 117, 4, 129, 30, +/* 2340 */ 4, 71, 16, 117, 4, 129, 30, 5, 70, 16, +/* 2350 */ 117, 4, 129, 30, 5, 70, 15, 117, 4, 129, +/* 2360 */ 30, 6, 69, 15, 116, 5, 129, 30, 7, 68, +/* 2370 */ 17, 115, 5, 129, 30, 9, 67, 19, 114, 6, +/* 2380 */ 129, 30, 10, 65, 22, 113, 6, 129, 31, 12, +/* 2390 */ 63, 27, 110, 9, 129, 32, 14, 60, 21, 84, +/* 2400 */ 9, 106, 12, 129, 33, 47, 85, 32, 129, 34, +/* 2410 */ 45, 86, 30, 129, 35, 43, 88, 26, 129, 36, +/* 2420 */ 40, 90, 22, 129, 38, 36, 93, 17, 129, 40, +/* 2430 */ 32, 96, 10, 129, 42, 28, 129, 44, 23, 129, +/* 2440 */ 48, 15, 129, 193, 129, 83, 17, 129, 77, 27, +/* 2450 */ 129, 36, 1, 74, 33, 129, 35, 3, 72, 37, +/* 2460 */ 129, 34, 5, 70, 41, 129, 33, 6, 69, 44, +/* 2470 */ 129, 33, 5, 68, 46, 129, 32, 5, 67, 49, +/* 2480 */ 129, 31, 5, 66, 17, 101, 16, 129, 31, 5, +/* 2490 */ 66, 11, 108, 10, 129, 30, 4, 65, 9, 110, +/* 2500 */ 9, 129, 30, 4, 64, 8, 112, 7, 129, 30, +/* 2510 */ 4, 64, 7, 114, 6, 129, 30, 4, 64, 6, +/* 2520 */ 115, 5, 129, 30, 4, 64, 5, 116, 5, 129, +/* 2530 */ 30, 4, 64, 5, 117, 4, 131, 30, 4, 65, +/* 2540 */ 4, 117, 4, 129, 30, 5, 65, 4, 116, 5, +/* 2550 */ 129, 31, 5, 66, 4, 115, 5, 129, 31, 6, +/* 2560 */ 67, 4, 114, 6, 129, 32, 7, 68, 4, 112, +/* 2570 */ 7, 129, 32, 9, 69, 5, 110, 9, 129, 33, +/* 2580 */ 11, 70, 5, 107, 11, 129, 34, 16, 72, 5, +/* 2590 */ 101, 16, 129, 35, 81, 129, 37, 77, 129, 38, +/* 2600 */ 75, 129, 40, 71, 129, 42, 67, 129, 44, 63, +/* 2610 */ 129, 47, 57, 129, 51, 49, 129, 56, 39, 129, +/* 2620 */ 193, 130, 34, 6, 74, 6, 129, 32, 10, 72, +/* 2630 */ 10, 129, 31, 12, 71, 12, 129, 30, 14, 70, +/* 2640 */ 14, 131, 31, 12, 71, 12, 129, 32, 10, 72, +/* 2650 */ 10, 129, 34, 6, 74, 6, 129, 194, 130, 34, +/* 2660 */ 6, 74, 6, 129, 32, 10, 72, 10, 129, 31, +/* 2670 */ 12, 71, 12, 129, 30, 14, 70, 14, 129, 20, +/* 2680 */ 2, 28, 16, 70, 14, 129, 22, 22, 70, 14, +/* 2690 */ 129, 24, 19, 71, 12, 129, 27, 15, 72, 10, +/* 2700 */ 129, 31, 9, 74, 6, 129, 194, 129, 53, 4, +/* 2710 */ 63, 4, 152, 193, 130, 99, 7, 129, 97, 13, +/* 2720 */ 129, 96, 16, 129, 96, 18, 129, 96, 19, 129, +/* 2730 */ 97, 19, 129, 99, 6, 110, 7, 129, 112, 6, +/* 2740 */ 129, 114, 5, 129, 34, 6, 57, 5, 115, 4, +/* 2750 */ 129, 32, 10, 54, 12, 116, 4, 129, 31, 12, +/* 2760 */ 53, 16, 117, 3, 129, 30, 14, 52, 20, 117, +/* 2770 */ 4, 129, 30, 14, 52, 23, 117, 4, 129, 30, +/* 2780 */ 14, 52, 25, 117, 4, 129, 31, 12, 52, 27, +/* 2790 */ 117, 4, 129, 32, 10, 53, 10, 70, 11, 116, +/* 2800 */ 5, 129, 34, 6, 55, 5, 73, 10, 115, 6, +/* 2810 */ 129, 74, 11, 114, 7, 129, 75, 12, 112, 9, +/* 2820 */ 129, 76, 13, 110, 10, 129, 77, 16, 106, 14, +/* 2830 */ 129, 78, 41, 129, 80, 38, 129, 81, 36, 129, +/* 2840 */ 82, 34, 129, 84, 30, 129, 86, 26, 129, 88, +/* 2850 */ 22, 129, 92, 14, 129, 194, 129, 55, 15, 129, +/* 2860 */ 50, 25, 129, 47, 32, 129, 45, 13, 70, 12, +/* 2870 */ 129, 43, 9, 76, 10, 129, 42, 6, 79, 8, +/* 2880 */ 129, 41, 5, 81, 7, 129, 40, 4, 84, 6, +/* 2890 */ 129, 39, 4, 59, 12, 85, 6, 129, 38, 4, +/* 2900 */ 55, 19, 87, 5, 129, 37, 4, 53, 23, 88, +/* 2910 */ 4, 129, 36, 4, 51, 8, 71, 6, 89, 4, +/* 2920 */ 129, 36, 4, 51, 6, 73, 4, 89, 4, 129, +/* 2930 */ 36, 4, 50, 6, 74, 4, 90, 3, 129, 35, +/* 2940 */ 4, 50, 5, 75, 3, 90, 4, 129, 35, 4, +/* 2950 */ 50, 4, 75, 4, 90, 4, 131, 35, 4, 50, +/* 2960 */ 5, 75, 4, 90, 4, 129, 36, 4, 51, 5, +/* 2970 */ 75, 4, 90, 4, 129, 36, 4, 51, 6, 75, +/* 2980 */ 4, 90, 4, 129, 36, 4, 53, 26, 90, 4, +/* 2990 */ 129, 37, 4, 54, 25, 90, 4, 129, 37, 4, +/* 3000 */ 52, 27, 90, 3, 129, 38, 4, 52, 4, 89, +/* 3010 */ 4, 129, 39, 4, 51, 4, 88, 4, 129, 40, +/* 3020 */ 4, 50, 4, 87, 5, 129, 41, 4, 50, 4, +/* 3030 */ 86, 5, 129, 42, 4, 50, 4, 85, 5, 129, +/* 3040 */ 43, 3, 50, 4, 83, 6, 129, 44, 2, 51, +/* 3050 */ 5, 80, 7, 129, 46, 1, 52, 6, 76, 9, +/* 3060 */ 129, 54, 28, 129, 56, 23, 129, 60, 16, 129, +/* 3070 */ 193, 129, 30, 4, 132, 30, 5, 129, 30, 8, +/* 3080 */ 129, 30, 12, 129, 30, 16, 129, 30, 4, 37, +/* 3090 */ 12, 129, 30, 4, 41, 12, 129, 30, 4, 44, +/* 3100 */ 13, 129, 30, 4, 48, 13, 129, 52, 13, 129, +/* 3110 */ 56, 12, 129, 58, 14, 129, 58, 4, 64, 12, +/* 3120 */ 129, 58, 4, 68, 12, 129, 58, 4, 72, 12, +/* 3130 */ 129, 58, 4, 75, 13, 129, 58, 4, 79, 13, +/* 3140 */ 129, 58, 4, 83, 13, 129, 58, 4, 87, 13, +/* 3150 */ 129, 58, 4, 91, 12, 129, 58, 4, 95, 12, +/* 3160 */ 129, 58, 4, 96, 15, 129, 58, 4, 93, 22, +/* 3170 */ 129, 58, 4, 89, 30, 129, 58, 4, 85, 36, +/* 3180 */ 129, 58, 4, 81, 38, 129, 58, 4, 77, 38, +/* 3190 */ 129, 58, 4, 73, 38, 129, 58, 4, 70, 37, +/* 3200 */ 129, 58, 4, 66, 37, 129, 58, 41, 129, 58, +/* 3210 */ 37, 129, 54, 38, 129, 30, 4, 50, 38, 129, +/* 3220 */ 30, 4, 46, 38, 129, 30, 4, 42, 38, 129, +/* 3230 */ 30, 4, 38, 39, 129, 30, 43, 129, 30, 39, +/* 3240 */ 129, 30, 35, 129, 30, 31, 129, 30, 27, 129, +/* 3250 */ 30, 24, 129, 30, 20, 129, 30, 16, 129, 30, +/* 3260 */ 12, 129, 30, 8, 129, 30, 5, 129, 30, 4, +/* 3270 */ 132, 193, 129, 30, 4, 117, 4, 132, 30, 91, +/* 3280 */ 137, 30, 4, 80, 4, 117, 4, 138, 30, 4, +/* 3290 */ 80, 5, 116, 5, 129, 30, 5, 79, 6, 116, +/* 3300 */ 5, 130, 30, 6, 78, 8, 115, 6, 129, 31, +/* 3310 */ 6, 77, 9, 115, 6, 129, 31, 7, 76, 11, +/* 3320 */ 114, 6, 129, 31, 8, 75, 14, 112, 8, 129, +/* 3330 */ 32, 8, 74, 16, 111, 9, 129, 32, 9, 73, +/* 3340 */ 19, 109, 10, 129, 33, 10, 71, 24, 106, 13, +/* 3350 */ 129, 33, 13, 68, 12, 83, 35, 129, 34, 16, +/* 3360 */ 64, 15, 84, 33, 129, 35, 43, 85, 31, 129, +/* 3370 */ 36, 41, 86, 29, 129, 37, 39, 88, 25, 129, +/* 3380 */ 38, 37, 90, 21, 129, 40, 33, 93, 15, 129, +/* 3390 */ 42, 29, 96, 9, 129, 45, 24, 129, 49, 16, +/* 3400 */ 129, 193, 129, 63, 25, 129, 57, 37, 129, 53, +/* 3410 */ 45, 129, 50, 51, 129, 47, 57, 129, 45, 61, +/* 3420 */ 129, 43, 65, 129, 41, 69, 129, 39, 73, 129, +/* 3430 */ 38, 25, 92, 21, 129, 36, 21, 97, 18, 129, +/* 3440 */ 35, 18, 102, 14, 129, 34, 16, 106, 11, 129, +/* 3450 */ 33, 14, 108, 10, 129, 32, 12, 111, 8, 129, +/* 3460 */ 32, 10, 113, 6, 129, 31, 10, 114, 6, 129, +/* 3470 */ 31, 8, 115, 5, 129, 30, 8, 116, 5, 129, +/* 3480 */ 30, 7, 116, 5, 129, 30, 6, 117, 4, 130, +/* 3490 */ 30, 5, 117, 4, 131, 31, 4, 116, 5, 129, +/* 3500 */ 32, 4, 116, 4, 129, 32, 5, 115, 5, 129, +/* 3510 */ 33, 4, 114, 5, 129, 34, 4, 112, 6, 129, +/* 3520 */ 35, 4, 110, 7, 129, 37, 4, 107, 9, 129, +/* 3530 */ 39, 4, 103, 12, 129, 41, 4, 103, 18, 129, +/* 3540 */ 43, 4, 103, 18, 129, 45, 5, 103, 18, 129, +/* 3550 */ 48, 5, 103, 18, 129, 51, 1, 129, 193, 129, +/* 3560 */ 30, 4, 117, 4, 132, 30, 91, 137, 30, 4, +/* 3570 */ 117, 4, 135, 30, 5, 116, 5, 130, 30, 6, +/* 3580 */ 115, 6, 130, 31, 6, 114, 6, 129, 31, 7, +/* 3590 */ 113, 7, 129, 32, 7, 112, 7, 129, 32, 8, +/* 3600 */ 111, 8, 129, 33, 9, 109, 9, 129, 33, 12, +/* 3610 */ 106, 12, 129, 34, 13, 104, 13, 129, 35, 15, +/* 3620 */ 101, 15, 129, 36, 19, 96, 19, 129, 37, 24, +/* 3630 */ 90, 24, 129, 39, 73, 129, 40, 71, 129, 42, +/* 3640 */ 67, 129, 44, 63, 129, 46, 59, 129, 49, 53, +/* 3650 */ 129, 52, 47, 129, 56, 39, 129, 61, 29, 129, +/* 3660 */ 193, 129, 30, 4, 117, 4, 132, 30, 91, 137, +/* 3670 */ 30, 4, 80, 4, 117, 4, 140, 30, 4, 79, +/* 3680 */ 6, 117, 4, 129, 30, 4, 77, 10, 117, 4, +/* 3690 */ 129, 30, 4, 73, 18, 117, 4, 132, 30, 4, +/* 3700 */ 117, 4, 130, 30, 5, 116, 5, 130, 30, 7, +/* 3710 */ 114, 7, 129, 30, 8, 113, 8, 129, 30, 11, +/* 3720 */ 110, 11, 129, 30, 18, 103, 18, 132, 193, 129, +/* 3730 */ 30, 4, 117, 4, 132, 30, 91, 137, 30, 4, +/* 3740 */ 80, 4, 117, 4, 132, 80, 4, 117, 4, 136, +/* 3750 */ 79, 6, 117, 4, 129, 77, 10, 117, 4, 129, +/* 3760 */ 73, 18, 117, 4, 132, 117, 4, 130, 116, 5, +/* 3770 */ 130, 114, 7, 129, 113, 8, 129, 110, 11, 129, +/* 3780 */ 103, 18, 132, 193, 129, 63, 25, 129, 57, 37, +/* 3790 */ 129, 53, 45, 129, 50, 51, 129, 47, 57, 129, +/* 3800 */ 45, 61, 129, 43, 65, 129, 41, 69, 129, 39, +/* 3810 */ 73, 129, 38, 25, 92, 21, 129, 36, 21, 97, +/* 3820 */ 18, 129, 35, 18, 102, 14, 129, 34, 16, 106, +/* 3830 */ 11, 129, 33, 14, 108, 10, 129, 32, 12, 111, +/* 3840 */ 8, 129, 32, 10, 113, 6, 129, 31, 10, 114, +/* 3850 */ 6, 129, 31, 8, 115, 5, 129, 30, 8, 116, +/* 3860 */ 5, 129, 30, 7, 116, 5, 129, 30, 6, 117, +/* 3870 */ 4, 130, 30, 5, 117, 4, 131, 30, 5, 75, +/* 3880 */ 4, 116, 5, 129, 31, 5, 75, 4, 116, 4, +/* 3890 */ 129, 31, 6, 75, 4, 115, 5, 129, 32, 7, +/* 3900 */ 75, 4, 114, 5, 129, 32, 9, 75, 4, 112, +/* 3910 */ 6, 129, 33, 11, 75, 4, 110, 7, 129, 34, +/* 3920 */ 15, 75, 4, 107, 9, 129, 35, 44, 103, 12, +/* 3930 */ 129, 36, 43, 103, 18, 129, 38, 41, 103, 18, +/* 3940 */ 129, 39, 40, 103, 18, 129, 41, 38, 103, 18, +/* 3950 */ 129, 44, 35, 129, 48, 31, 129, 52, 27, 129, +/* 3960 */ 61, 18, 129, 193, 129, 30, 4, 117, 4, 132, +/* 3970 */ 30, 91, 137, 30, 4, 80, 4, 117, 4, 132, +/* 3980 */ 80, 4, 140, 30, 4, 80, 4, 117, 4, 132, +/* 3990 */ 30, 91, 137, 30, 4, 117, 4, 132, 193, 129, +/* 4000 */ 30, 4, 117, 4, 132, 30, 91, 137, 30, 4, +/* 4010 */ 117, 4, 132, 193, 129, 44, 7, 129, 40, 13, +/* 4020 */ 129, 37, 17, 129, 35, 20, 129, 34, 22, 129, +/* 4030 */ 33, 23, 129, 32, 24, 129, 32, 23, 129, 31, +/* 4040 */ 6, 41, 13, 129, 31, 5, 42, 11, 129, 30, +/* 4050 */ 5, 44, 7, 129, 30, 4, 132, 30, 5, 130, +/* 4060 */ 31, 5, 129, 31, 6, 117, 4, 129, 31, 8, +/* 4070 */ 117, 4, 129, 32, 9, 117, 4, 129, 33, 11, +/* 4080 */ 117, 4, 129, 34, 87, 129, 35, 86, 129, 36, +/* 4090 */ 85, 129, 37, 84, 129, 38, 83, 129, 40, 81, +/* 4100 */ 129, 42, 79, 129, 45, 76, 129, 50, 71, 129, +/* 4110 */ 117, 4, 132, 193, 129, 30, 4, 117, 4, 132, +/* 4120 */ 30, 91, 137, 30, 4, 76, 8, 117, 4, 129, +/* 4130 */ 30, 4, 73, 13, 117, 4, 129, 30, 4, 70, +/* 4140 */ 18, 117, 4, 129, 30, 4, 67, 23, 117, 4, +/* 4150 */ 129, 65, 26, 129, 62, 31, 129, 59, 35, 129, +/* 4160 */ 56, 29, 89, 7, 129, 53, 29, 91, 7, 129, +/* 4170 */ 50, 29, 93, 7, 129, 47, 29, 95, 6, 129, +/* 4180 */ 30, 4, 45, 29, 96, 7, 129, 30, 4, 42, +/* 4190 */ 29, 98, 7, 129, 30, 4, 39, 30, 100, 6, +/* 4200 */ 129, 30, 4, 36, 30, 101, 7, 129, 30, 33, +/* 4210 */ 103, 7, 117, 4, 129, 30, 30, 105, 6, 117, +/* 4220 */ 4, 129, 30, 27, 106, 7, 117, 4, 129, 30, +/* 4230 */ 25, 108, 7, 117, 4, 129, 30, 22, 110, 11, +/* 4240 */ 129, 30, 19, 111, 10, 129, 30, 16, 113, 8, +/* 4250 */ 129, 30, 13, 115, 6, 129, 30, 11, 116, 5, +/* 4260 */ 129, 30, 8, 117, 4, 129, 30, 5, 117, 4, +/* 4270 */ 129, 30, 4, 117, 4, 130, 30, 4, 130, 193, +/* 4280 */ 129, 30, 4, 117, 4, 132, 30, 91, 137, 30, +/* 4290 */ 4, 117, 4, 132, 30, 4, 144, 30, 5, 130, +/* 4300 */ 30, 7, 129, 30, 8, 129, 30, 11, 129, 30, +/* 4310 */ 18, 132, 193, 129, 30, 4, 117, 4, 132, 30, +/* 4320 */ 91, 132, 30, 4, 103, 18, 129, 30, 4, 97, +/* 4330 */ 24, 129, 30, 4, 92, 29, 129, 30, 4, 87, +/* 4340 */ 34, 129, 81, 40, 129, 76, 45, 129, 70, 49, +/* 4350 */ 129, 65, 49, 129, 60, 49, 129, 55, 49, 129, +/* 4360 */ 50, 48, 129, 44, 49, 129, 39, 48, 129, 33, +/* 4370 */ 49, 129, 30, 47, 129, 34, 37, 129, 40, 26, +/* 4380 */ 129, 46, 19, 129, 52, 19, 129, 58, 19, 129, +/* 4390 */ 64, 19, 129, 70, 19, 129, 76, 19, 129, 82, +/* 4400 */ 19, 129, 30, 4, 88, 18, 129, 30, 4, 94, +/* 4410 */ 18, 129, 30, 4, 100, 18, 129, 30, 4, 106, +/* 4420 */ 15, 129, 30, 91, 137, 30, 4, 117, 4, 132, +/* 4430 */ 193, 129, 30, 4, 117, 4, 132, 30, 91, 132, +/* 4440 */ 30, 4, 107, 14, 129, 30, 4, 104, 17, 129, +/* 4450 */ 30, 4, 101, 20, 129, 30, 4, 99, 22, 129, +/* 4460 */ 96, 25, 129, 93, 28, 129, 91, 28, 129, 88, +/* 4470 */ 29, 129, 85, 29, 129, 82, 29, 129, 79, 29, +/* 4480 */ 129, 76, 29, 129, 74, 29, 129, 71, 29, 129, +/* 4490 */ 68, 29, 129, 65, 29, 129, 62, 29, 129, 60, +/* 4500 */ 29, 129, 57, 29, 129, 54, 29, 129, 51, 29, +/* 4510 */ 129, 49, 28, 129, 46, 29, 129, 43, 29, 129, +/* 4520 */ 40, 29, 117, 4, 129, 37, 29, 117, 4, 129, +/* 4530 */ 35, 29, 117, 4, 129, 32, 29, 117, 4, 129, +/* 4540 */ 30, 91, 132, 117, 4, 132, 193, 129, 63, 25, +/* 4550 */ 129, 57, 37, 129, 53, 45, 129, 50, 51, 129, +/* 4560 */ 47, 57, 129, 45, 61, 129, 43, 65, 129, 41, +/* 4570 */ 69, 129, 39, 73, 129, 38, 21, 92, 21, 129, +/* 4580 */ 36, 18, 97, 18, 129, 35, 14, 102, 14, 129, +/* 4590 */ 34, 11, 106, 11, 129, 33, 10, 108, 10, 129, +/* 4600 */ 32, 8, 111, 8, 129, 32, 6, 113, 6, 129, +/* 4610 */ 31, 6, 114, 6, 129, 31, 5, 115, 5, 129, +/* 4620 */ 30, 5, 116, 5, 130, 30, 4, 117, 4, 132, +/* 4630 */ 30, 5, 116, 5, 130, 31, 5, 115, 5, 129, +/* 4640 */ 31, 6, 114, 6, 129, 32, 6, 113, 6, 129, +/* 4650 */ 32, 8, 111, 8, 129, 33, 10, 108, 10, 129, +/* 4660 */ 34, 11, 106, 11, 129, 35, 14, 102, 14, 129, +/* 4670 */ 36, 18, 97, 18, 129, 38, 21, 92, 21, 129, +/* 4680 */ 39, 73, 129, 41, 69, 129, 43, 65, 129, 45, +/* 4690 */ 61, 129, 47, 57, 129, 50, 51, 129, 53, 45, +/* 4700 */ 129, 57, 37, 129, 63, 25, 129, 193, 129, 30, +/* 4710 */ 4, 117, 4, 132, 30, 91, 137, 30, 4, 80, +/* 4720 */ 4, 117, 4, 132, 80, 4, 117, 4, 134, 80, +/* 4730 */ 5, 116, 5, 131, 80, 6, 115, 6, 130, 81, +/* 4740 */ 6, 114, 6, 129, 81, 8, 112, 8, 129, 81, +/* 4750 */ 9, 111, 9, 129, 82, 10, 109, 10, 129, 82, +/* 4760 */ 13, 106, 13, 129, 83, 35, 129, 84, 33, 129, +/* 4770 */ 85, 31, 129, 86, 29, 129, 88, 25, 129, 90, +/* 4780 */ 21, 129, 93, 15, 129, 96, 9, 129, 193, 129, +/* 4790 */ 63, 25, 129, 57, 37, 129, 53, 45, 129, 50, +/* 4800 */ 51, 129, 47, 57, 129, 45, 61, 129, 43, 65, +/* 4810 */ 129, 41, 69, 129, 39, 73, 129, 38, 21, 92, +/* 4820 */ 21, 129, 36, 18, 97, 18, 129, 35, 14, 102, +/* 4830 */ 14, 129, 34, 11, 106, 11, 129, 33, 10, 108, +/* 4840 */ 10, 129, 32, 8, 111, 8, 129, 32, 6, 113, +/* 4850 */ 6, 129, 31, 6, 114, 6, 129, 31, 5, 115, +/* 4860 */ 5, 129, 30, 5, 116, 5, 130, 30, 4, 39, +/* 4870 */ 2, 117, 4, 129, 30, 4, 40, 4, 117, 4, +/* 4880 */ 129, 30, 4, 41, 5, 117, 4, 129, 30, 4, +/* 4890 */ 41, 6, 117, 4, 129, 30, 5, 40, 8, 116, +/* 4900 */ 5, 129, 30, 5, 39, 10, 116, 5, 129, 31, +/* 4910 */ 5, 38, 11, 115, 5, 129, 31, 18, 114, 6, +/* 4920 */ 129, 32, 17, 113, 6, 129, 32, 16, 111, 8, +/* 4930 */ 129, 33, 15, 108, 10, 129, 33, 14, 106, 11, +/* 4940 */ 129, 32, 17, 102, 14, 129, 31, 23, 97, 18, +/* 4950 */ 129, 31, 28, 92, 21, 129, 30, 82, 129, 30, +/* 4960 */ 80, 129, 30, 11, 43, 65, 129, 30, 10, 45, +/* 4970 */ 61, 129, 31, 8, 47, 57, 129, 32, 6, 50, +/* 4980 */ 51, 129, 33, 5, 53, 45, 129, 35, 4, 57, +/* 4990 */ 37, 129, 38, 2, 63, 25, 129, 193, 129, 30, +/* 5000 */ 4, 117, 4, 132, 30, 91, 137, 30, 4, 76, +/* 5010 */ 8, 117, 4, 129, 30, 4, 73, 11, 117, 4, +/* 5020 */ 129, 30, 4, 70, 14, 117, 4, 129, 30, 4, +/* 5030 */ 67, 17, 117, 4, 129, 65, 19, 117, 4, 129, +/* 5040 */ 62, 22, 117, 4, 129, 59, 25, 117, 4, 129, +/* 5050 */ 56, 28, 117, 4, 129, 53, 31, 117, 4, 129, +/* 5060 */ 50, 34, 117, 4, 129, 47, 29, 80, 5, 116, +/* 5070 */ 5, 129, 30, 4, 45, 29, 80, 5, 116, 5, +/* 5080 */ 129, 30, 4, 42, 29, 80, 5, 116, 5, 129, +/* 5090 */ 30, 4, 39, 30, 80, 6, 115, 6, 129, 30, +/* 5100 */ 4, 36, 30, 80, 6, 115, 6, 129, 30, 33, +/* 5110 */ 81, 6, 114, 6, 129, 30, 30, 81, 8, 112, +/* 5120 */ 8, 129, 30, 27, 81, 9, 111, 9, 129, 30, +/* 5130 */ 25, 82, 10, 109, 10, 129, 30, 22, 82, 13, +/* 5140 */ 106, 13, 129, 30, 19, 83, 35, 129, 30, 16, +/* 5150 */ 84, 33, 129, 30, 13, 85, 31, 129, 30, 11, +/* 5160 */ 86, 29, 129, 30, 8, 88, 25, 129, 30, 5, +/* 5170 */ 90, 21, 129, 30, 4, 93, 15, 129, 30, 4, +/* 5180 */ 96, 9, 129, 30, 4, 130, 193, 129, 30, 18, +/* 5190 */ 130, 30, 18, 89, 15, 129, 30, 18, 85, 23, +/* 5200 */ 129, 34, 11, 83, 27, 129, 34, 9, 81, 31, +/* 5210 */ 129, 33, 8, 79, 35, 129, 33, 6, 78, 16, +/* 5220 */ 106, 9, 129, 32, 6, 77, 15, 109, 7, 129, +/* 5230 */ 32, 5, 76, 14, 111, 6, 129, 31, 5, 75, +/* 5240 */ 14, 113, 5, 129, 31, 4, 74, 15, 114, 5, +/* 5250 */ 129, 31, 4, 74, 14, 115, 4, 129, 30, 4, +/* 5260 */ 73, 15, 116, 4, 129, 30, 4, 73, 14, 116, +/* 5270 */ 4, 129, 30, 4, 73, 14, 117, 4, 129, 30, +/* 5280 */ 4, 72, 15, 117, 4, 130, 30, 4, 71, 15, +/* 5290 */ 117, 4, 130, 30, 4, 70, 15, 117, 4, 129, +/* 5300 */ 30, 5, 70, 15, 117, 4, 129, 30, 5, 69, +/* 5310 */ 15, 116, 5, 129, 30, 6, 68, 16, 115, 5, +/* 5320 */ 129, 31, 6, 67, 16, 114, 6, 129, 31, 7, +/* 5330 */ 66, 17, 113, 6, 129, 32, 7, 64, 18, 111, +/* 5340 */ 8, 129, 32, 8, 62, 19, 109, 9, 129, 33, +/* 5350 */ 9, 60, 20, 107, 10, 129, 34, 11, 57, 22, +/* 5360 */ 103, 13, 129, 35, 43, 103, 18, 129, 36, 41, +/* 5370 */ 103, 18, 129, 38, 38, 103, 18, 129, 39, 35, +/* 5380 */ 103, 18, 129, 41, 31, 129, 43, 27, 129, 46, +/* 5390 */ 22, 129, 49, 14, 129, 193, 129, 103, 18, 132, +/* 5400 */ 110, 11, 129, 113, 8, 129, 114, 7, 129, 116, +/* 5410 */ 5, 130, 117, 4, 132, 30, 4, 117, 4, 132, +/* 5420 */ 30, 91, 137, 30, 4, 117, 4, 132, 117, 4, +/* 5430 */ 132, 116, 5, 130, 114, 7, 129, 113, 8, 129, +/* 5440 */ 110, 11, 129, 103, 18, 132, 193, 129, 117, 4, +/* 5450 */ 132, 56, 65, 129, 50, 71, 129, 46, 75, 129, +/* 5460 */ 44, 77, 129, 42, 79, 129, 40, 81, 129, 38, +/* 5470 */ 83, 129, 36, 85, 129, 35, 86, 129, 34, 20, +/* 5480 */ 117, 4, 129, 33, 17, 117, 4, 129, 32, 15, +/* 5490 */ 117, 4, 129, 32, 13, 117, 4, 129, 31, 12, +/* 5500 */ 129, 31, 10, 129, 31, 9, 129, 30, 9, 129, +/* 5510 */ 30, 8, 130, 30, 7, 132, 31, 6, 130, 31, +/* 5520 */ 7, 129, 32, 6, 129, 32, 7, 129, 33, 7, +/* 5530 */ 129, 34, 7, 129, 35, 8, 129, 36, 9, 117, +/* 5540 */ 4, 129, 38, 9, 117, 4, 129, 40, 10, 117, +/* 5550 */ 4, 129, 42, 12, 117, 4, 129, 44, 77, 129, +/* 5560 */ 46, 75, 129, 50, 71, 129, 56, 43, 100, 21, +/* 5570 */ 129, 117, 4, 132, 193, 129, 117, 4, 132, 115, +/* 5580 */ 6, 129, 110, 11, 129, 105, 16, 129, 101, 20, +/* 5590 */ 129, 96, 25, 129, 92, 29, 129, 87, 34, 129, +/* 5600 */ 83, 38, 129, 78, 43, 129, 74, 47, 129, 70, +/* 5610 */ 42, 117, 4, 129, 65, 42, 117, 4, 129, 60, +/* 5620 */ 43, 117, 4, 129, 56, 42, 129, 51, 42, 129, +/* 5630 */ 46, 43, 129, 42, 43, 129, 37, 44, 129, 33, +/* 5640 */ 43, 129, 30, 42, 129, 33, 34, 129, 38, 25, +/* 5650 */ 129, 42, 16, 129, 47, 15, 129, 52, 15, 129, +/* 5660 */ 57, 15, 129, 61, 16, 129, 66, 16, 129, 71, +/* 5670 */ 16, 129, 76, 16, 129, 80, 16, 129, 85, 16, +/* 5680 */ 117, 4, 129, 90, 16, 117, 4, 129, 95, 16, +/* 5690 */ 117, 4, 129, 100, 21, 129, 105, 16, 129, 110, +/* 5700 */ 11, 129, 114, 7, 129, 117, 4, 132, 193, 129, +/* 5710 */ 117, 4, 132, 115, 6, 129, 110, 11, 129, 105, +/* 5720 */ 16, 129, 101, 20, 129, 96, 25, 129, 92, 29, +/* 5730 */ 129, 87, 34, 129, 83, 38, 129, 78, 43, 129, +/* 5740 */ 74, 47, 129, 70, 42, 117, 4, 129, 65, 42, +/* 5750 */ 117, 4, 129, 60, 43, 117, 4, 129, 56, 42, +/* 5760 */ 129, 51, 42, 129, 46, 43, 129, 42, 43, 129, +/* 5770 */ 37, 44, 129, 33, 43, 129, 30, 42, 129, 33, +/* 5780 */ 34, 129, 38, 25, 129, 42, 16, 129, 47, 15, +/* 5790 */ 129, 52, 15, 129, 57, 15, 129, 61, 16, 129, +/* 5800 */ 65, 17, 129, 60, 27, 129, 56, 36, 129, 51, +/* 5810 */ 42, 129, 46, 43, 129, 42, 43, 129, 37, 44, +/* 5820 */ 129, 33, 43, 129, 30, 42, 129, 33, 34, 129, +/* 5830 */ 38, 25, 129, 42, 16, 129, 47, 15, 129, 52, +/* 5840 */ 15, 129, 57, 15, 129, 61, 16, 129, 66, 16, +/* 5850 */ 129, 71, 16, 129, 76, 16, 129, 80, 16, 129, +/* 5860 */ 85, 16, 117, 4, 129, 90, 16, 117, 4, 129, +/* 5870 */ 95, 16, 117, 4, 129, 100, 21, 129, 105, 16, +/* 5880 */ 129, 110, 11, 129, 114, 7, 129, 117, 4, 132, +/* 5890 */ 193, 129, 30, 4, 117, 4, 132, 30, 4, 115, +/* 5900 */ 6, 129, 30, 4, 112, 9, 129, 30, 6, 109, +/* 5910 */ 12, 129, 30, 9, 106, 15, 129, 30, 11, 103, +/* 5920 */ 18, 129, 30, 14, 100, 21, 129, 30, 4, 38, +/* 5930 */ 9, 98, 23, 129, 30, 4, 40, 10, 95, 26, +/* 5940 */ 129, 30, 4, 43, 9, 92, 29, 129, 46, 9, +/* 5950 */ 89, 32, 129, 49, 8, 86, 28, 117, 4, 129, +/* 5960 */ 51, 9, 83, 28, 117, 4, 129, 54, 9, 80, +/* 5970 */ 28, 117, 4, 129, 57, 8, 77, 28, 117, 4, +/* 5980 */ 129, 59, 9, 74, 28, 129, 62, 37, 129, 64, +/* 5990 */ 33, 129, 66, 28, 129, 63, 28, 129, 60, 28, +/* 6000 */ 129, 57, 28, 129, 54, 33, 129, 51, 39, 129, +/* 6010 */ 48, 29, 83, 9, 129, 30, 4, 45, 29, 86, +/* 6020 */ 9, 129, 30, 4, 42, 29, 89, 9, 129, 30, +/* 6030 */ 4, 39, 29, 92, 8, 129, 30, 4, 36, 29, +/* 6040 */ 94, 9, 129, 30, 32, 97, 9, 129, 30, 29, +/* 6050 */ 100, 8, 117, 4, 129, 30, 26, 103, 8, 117, +/* 6060 */ 4, 129, 30, 23, 105, 9, 117, 4, 129, 30, +/* 6070 */ 20, 108, 13, 129, 30, 18, 111, 10, 129, 30, +/* 6080 */ 15, 113, 8, 129, 30, 12, 116, 5, 129, 30, +/* 6090 */ 9, 117, 4, 129, 30, 6, 117, 4, 129, 30, +/* 6100 */ 4, 117, 4, 132, 193, 129, 117, 4, 132, 114, +/* 6110 */ 7, 129, 111, 10, 129, 108, 13, 129, 105, 16, +/* 6120 */ 129, 102, 19, 129, 100, 21, 129, 96, 25, 129, +/* 6130 */ 93, 28, 129, 90, 31, 129, 87, 34, 129, 84, +/* 6140 */ 30, 117, 4, 129, 30, 4, 81, 30, 117, 4, +/* 6150 */ 129, 30, 4, 78, 30, 117, 4, 129, 30, 4, +/* 6160 */ 75, 30, 117, 4, 129, 30, 4, 72, 30, 129, +/* 6170 */ 30, 69, 129, 30, 66, 129, 30, 63, 129, 30, +/* 6180 */ 60, 129, 30, 57, 129, 30, 54, 129, 30, 51, +/* 6190 */ 129, 30, 48, 129, 30, 51, 129, 30, 4, 73, +/* 6200 */ 12, 129, 30, 4, 76, 12, 129, 30, 4, 80, +/* 6210 */ 12, 129, 30, 4, 83, 12, 129, 87, 12, 129, +/* 6220 */ 90, 12, 117, 4, 129, 94, 11, 117, 4, 129, +/* 6230 */ 97, 12, 117, 4, 129, 101, 12, 117, 4, 129, +/* 6240 */ 104, 17, 129, 108, 13, 129, 111, 10, 129, 115, +/* 6250 */ 6, 129, 117, 4, 134, 193, 129, 30, 1, 103, +/* 6260 */ 18, 129, 30, 4, 103, 18, 129, 30, 7, 103, +/* 6270 */ 18, 129, 30, 9, 103, 18, 129, 30, 12, 110, +/* 6280 */ 11, 129, 30, 15, 113, 8, 129, 30, 18, 114, +/* 6290 */ 7, 129, 30, 21, 116, 5, 129, 30, 24, 116, +/* 6300 */ 5, 129, 30, 27, 117, 4, 129, 30, 30, 117, +/* 6310 */ 4, 129, 30, 33, 117, 4, 129, 30, 4, 37, +/* 6320 */ 28, 117, 4, 129, 30, 4, 40, 28, 117, 4, +/* 6330 */ 129, 30, 4, 42, 29, 117, 4, 129, 30, 4, +/* 6340 */ 45, 29, 117, 4, 129, 30, 4, 48, 29, 117, +/* 6350 */ 4, 129, 30, 4, 51, 29, 117, 4, 129, 30, +/* 6360 */ 4, 54, 29, 117, 4, 129, 30, 4, 57, 29, +/* 6370 */ 117, 4, 129, 30, 4, 59, 30, 117, 4, 129, +/* 6380 */ 30, 4, 62, 30, 117, 4, 129, 30, 4, 65, +/* 6390 */ 30, 117, 4, 129, 30, 4, 68, 30, 117, 4, +/* 6400 */ 129, 30, 4, 71, 30, 117, 4, 129, 30, 4, +/* 6410 */ 74, 30, 117, 4, 129, 30, 4, 77, 30, 117, +/* 6420 */ 4, 129, 30, 4, 80, 30, 117, 4, 129, 30, +/* 6430 */ 4, 83, 30, 117, 4, 129, 30, 4, 86, 35, +/* 6440 */ 129, 30, 4, 89, 32, 129, 30, 4, 91, 30, +/* 6450 */ 129, 30, 4, 94, 27, 129, 30, 5, 97, 24, +/* 6460 */ 129, 30, 5, 100, 21, 129, 30, 7, 103, 18, +/* 6470 */ 129, 30, 8, 106, 15, 129, 30, 11, 109, 12, +/* 6480 */ 129, 30, 18, 112, 9, 129, 30, 18, 115, 6, +/* 6490 */ 129, 30, 18, 117, 4, 129, 30, 18, 120, 1, +/* 6500 */ 129, 193, 129, 42, 8, 129, 38, 16, 129, 36, +/* 6510 */ 20, 129, 34, 24, 71, 5, 129, 33, 26, 69, +/* 6520 */ 10, 129, 32, 28, 68, 13, 129, 31, 30, 68, +/* 6530 */ 14, 129, 31, 9, 52, 9, 68, 15, 129, 30, +/* 6540 */ 8, 54, 8, 69, 14, 129, 30, 7, 55, 7, +/* 6550 */ 71, 4, 78, 6, 129, 30, 6, 56, 6, 79, +/* 6560 */ 5, 129, 30, 6, 56, 6, 80, 4, 130, 31, +/* 6570 */ 5, 56, 5, 80, 4, 129, 31, 5, 56, 5, +/* 6580 */ 79, 5, 129, 32, 5, 55, 5, 78, 6, 129, +/* 6590 */ 33, 5, 54, 5, 77, 7, 129, 34, 6, 52, +/* 6600 */ 6, 74, 9, 129, 35, 48, 129, 33, 49, 129, +/* 6610 */ 32, 49, 129, 31, 49, 129, 30, 49, 129, 30, +/* 6620 */ 47, 129, 30, 45, 129, 30, 41, 129, 30, 6, +/* 6630 */ 129, 30, 4, 129, 30, 3, 129, 30, 2, 129, +/* 6640 */ 193, 129, 30, 4, 117, 4, 130, 31, 90, 136, +/* 6650 */ 37, 5, 72, 5, 129, 35, 5, 74, 5, 129, +/* 6660 */ 33, 5, 76, 5, 129, 32, 5, 77, 5, 129, +/* 6670 */ 31, 5, 78, 5, 129, 31, 4, 79, 4, 129, +/* 6680 */ 30, 5, 79, 5, 131, 30, 6, 78, 6, 129, +/* 6690 */ 30, 7, 77, 7, 129, 31, 8, 75, 8, 129, +/* 6700 */ 31, 11, 72, 11, 129, 32, 15, 67, 15, 129, +/* 6710 */ 33, 48, 129, 34, 46, 129, 35, 44, 129, 37, +/* 6720 */ 40, 129, 39, 36, 129, 42, 30, 129, 46, 22, +/* 6730 */ 129, 193, 129, 48, 18, 129, 43, 28, 129, 41, +/* 6740 */ 32, 129, 39, 36, 129, 37, 40, 129, 35, 44, +/* 6750 */ 129, 34, 46, 129, 33, 13, 68, 13, 129, 32, +/* 6760 */ 9, 73, 9, 129, 32, 7, 75, 7, 129, 31, +/* 6770 */ 6, 77, 6, 129, 31, 5, 78, 5, 129, 30, +/* 6780 */ 5, 79, 5, 129, 30, 4, 80, 4, 133, 31, +/* 6790 */ 3, 79, 4, 129, 31, 4, 79, 4, 129, 32, +/* 6800 */ 3, 78, 4, 129, 32, 4, 76, 6, 129, 33, +/* 6810 */ 4, 74, 7, 129, 34, 4, 72, 8, 129, 35, +/* 6820 */ 5, 72, 7, 129, 37, 5, 73, 4, 129, 39, +/* 6830 */ 4, 74, 1, 129, 129, 193, 129, 46, 22, 129, +/* 6840 */ 42, 30, 129, 39, 36, 129, 37, 40, 129, 35, +/* 6850 */ 44, 129, 34, 46, 129, 33, 48, 129, 32, 15, +/* 6860 */ 67, 15, 129, 31, 11, 72, 11, 129, 31, 8, +/* 6870 */ 75, 8, 129, 30, 7, 77, 7, 129, 30, 6, +/* 6880 */ 78, 6, 129, 30, 5, 79, 5, 131, 31, 4, +/* 6890 */ 79, 4, 129, 31, 5, 78, 5, 129, 32, 5, +/* 6900 */ 77, 5, 129, 33, 5, 76, 5, 129, 35, 5, +/* 6910 */ 74, 5, 117, 4, 129, 37, 5, 72, 5, 117, +/* 6920 */ 4, 129, 30, 91, 136, 30, 4, 130, 193, 129, +/* 6930 */ 48, 18, 129, 43, 28, 129, 41, 32, 129, 39, +/* 6940 */ 36, 129, 37, 40, 129, 35, 44, 129, 34, 46, +/* 6950 */ 129, 33, 13, 55, 4, 68, 13, 129, 32, 9, +/* 6960 */ 55, 4, 73, 9, 129, 32, 7, 55, 4, 75, +/* 6970 */ 7, 129, 31, 6, 55, 4, 77, 6, 129, 31, +/* 6980 */ 5, 55, 4, 78, 5, 129, 30, 5, 55, 4, +/* 6990 */ 79, 5, 129, 30, 4, 55, 4, 80, 4, 132, +/* 7000 */ 30, 4, 55, 4, 79, 5, 129, 31, 3, 55, +/* 7010 */ 4, 78, 5, 129, 31, 4, 55, 4, 77, 6, +/* 7020 */ 129, 32, 3, 55, 4, 75, 7, 129, 32, 4, +/* 7030 */ 55, 4, 73, 9, 129, 33, 4, 55, 4, 68, +/* 7040 */ 13, 129, 34, 4, 55, 25, 129, 35, 5, 55, +/* 7050 */ 24, 129, 37, 5, 55, 22, 129, 39, 4, 55, +/* 7060 */ 20, 129, 55, 18, 129, 55, 16, 129, 55, 11, +/* 7070 */ 129, 193, 129, 80, 4, 129, 30, 4, 80, 4, +/* 7080 */ 130, 30, 78, 129, 30, 82, 129, 30, 85, 129, +/* 7090 */ 30, 87, 129, 30, 88, 129, 30, 89, 129, 30, +/* 7100 */ 90, 130, 30, 4, 80, 4, 115, 6, 129, 30, +/* 7110 */ 4, 80, 4, 117, 4, 129, 80, 4, 105, 6, +/* 7120 */ 117, 4, 129, 80, 4, 103, 10, 116, 5, 129, +/* 7130 */ 80, 4, 102, 19, 129, 80, 4, 101, 19, 129, +/* 7140 */ 101, 19, 129, 101, 18, 129, 102, 16, 129, 103, +/* 7150 */ 12, 129, 105, 6, 129, 193, 129, 12, 10, 59, +/* 7160 */ 11, 129, 9, 16, 55, 19, 129, 7, 20, 53, +/* 7170 */ 23, 129, 6, 7, 23, 5, 32, 6, 51, 27, +/* 7180 */ 129, 4, 7, 25, 16, 50, 29, 129, 3, 6, +/* 7190 */ 27, 16, 49, 31, 129, 2, 6, 28, 16, 48, +/* 7200 */ 33, 129, 1, 6, 27, 18, 47, 35, 129, 1, +/* 7210 */ 6, 27, 31, 71, 12, 129, 1, 5, 26, 15, +/* 7220 */ 44, 10, 75, 8, 129, 1, 5, 25, 14, 45, +/* 7230 */ 7, 77, 7, 129, 1, 5, 25, 13, 45, 5, +/* 7240 */ 79, 5, 129, 1, 5, 24, 14, 45, 4, 80, +/* 7250 */ 4, 129, 1, 5, 24, 13, 45, 4, 80, 4, +/* 7260 */ 129, 1, 5, 23, 14, 45, 4, 80, 4, 129, +/* 7270 */ 1, 5, 23, 13, 45, 4, 80, 4, 129, 1, +/* 7280 */ 6, 22, 13, 45, 5, 79, 5, 129, 1, 6, +/* 7290 */ 21, 14, 45, 7, 77, 7, 129, 1, 7, 21, +/* 7300 */ 13, 46, 8, 75, 8, 129, 1, 8, 20, 13, +/* 7310 */ 46, 12, 71, 12, 129, 1, 10, 18, 15, 47, +/* 7320 */ 35, 129, 2, 30, 48, 33, 129, 3, 29, 49, +/* 7330 */ 32, 129, 4, 27, 50, 31, 129, 5, 25, 51, +/* 7340 */ 27, 80, 2, 86, 4, 129, 7, 21, 53, 23, +/* 7350 */ 80, 3, 85, 6, 129, 9, 17, 55, 19, 80, +/* 7360 */ 12, 129, 12, 12, 59, 11, 81, 11, 129, 82, +/* 7370 */ 10, 129, 84, 7, 129, 86, 4, 129, 193, 129, +/* 7380 */ 30, 4, 117, 4, 130, 30, 91, 136, 30, 4, +/* 7390 */ 72, 5, 129, 30, 4, 74, 5, 129, 75, 5, +/* 7400 */ 129, 76, 5, 129, 76, 6, 129, 77, 6, 130, +/* 7410 */ 77, 7, 130, 76, 8, 129, 30, 4, 75, 9, +/* 7420 */ 129, 30, 4, 72, 12, 129, 30, 54, 129, 30, +/* 7430 */ 53, 130, 30, 52, 129, 30, 51, 129, 30, 49, +/* 7440 */ 129, 30, 46, 129, 30, 42, 129, 30, 4, 130, +/* 7450 */ 193, 129, 30, 4, 80, 4, 129, 30, 4, 80, +/* 7460 */ 4, 100, 6, 129, 30, 54, 98, 10, 129, 30, +/* 7470 */ 54, 97, 12, 129, 30, 54, 96, 14, 131, 30, +/* 7480 */ 54, 97, 12, 129, 30, 54, 98, 10, 129, 30, +/* 7490 */ 54, 100, 6, 129, 30, 4, 130, 193, 129, 7, +/* 7500 */ 6, 129, 4, 11, 129, 3, 13, 129, 2, 14, +/* 7510 */ 129, 1, 15, 130, 1, 3, 6, 9, 129, 1, +/* 7520 */ 3, 7, 6, 129, 1, 3, 130, 1, 4, 129, +/* 7530 */ 1, 5, 80, 4, 129, 1, 7, 80, 4, 100, +/* 7540 */ 6, 129, 2, 82, 98, 10, 129, 3, 81, 97, +/* 7550 */ 12, 129, 4, 80, 96, 14, 129, 5, 79, 96, +/* 7560 */ 14, 129, 7, 77, 96, 14, 129, 10, 74, 97, +/* 7570 */ 12, 129, 14, 70, 98, 10, 129, 19, 65, 100, +/* 7580 */ 6, 129, 193, 129, 30, 4, 117, 4, 130, 30, +/* 7590 */ 91, 136, 30, 4, 57, 9, 129, 30, 4, 55, +/* 7600 */ 12, 129, 52, 17, 129, 50, 20, 129, 48, 24, +/* 7610 */ 129, 46, 27, 129, 44, 21, 69, 6, 129, 41, +/* 7620 */ 22, 70, 6, 80, 4, 129, 30, 4, 39, 21, +/* 7630 */ 72, 6, 80, 4, 129, 30, 4, 36, 22, 73, +/* 7640 */ 11, 129, 30, 26, 75, 9, 129, 30, 23, 76, +/* 7650 */ 8, 129, 30, 21, 78, 6, 129, 30, 19, 79, +/* 7660 */ 5, 129, 30, 16, 80, 4, 129, 30, 14, 80, +/* 7670 */ 4, 129, 30, 12, 129, 30, 10, 129, 30, 7, +/* 7680 */ 129, 30, 5, 129, 30, 4, 130, 193, 129, 30, +/* 7690 */ 4, 117, 4, 130, 30, 91, 136, 30, 4, 130, +/* 7700 */ 193, 129, 30, 4, 80, 4, 130, 30, 54, 136, +/* 7710 */ 30, 4, 72, 5, 129, 30, 4, 74, 5, 129, +/* 7720 */ 75, 5, 129, 76, 5, 129, 30, 4, 75, 7, +/* 7730 */ 129, 30, 4, 74, 9, 129, 30, 54, 132, 30, +/* 7740 */ 53, 129, 30, 52, 129, 30, 51, 129, 30, 48, +/* 7750 */ 129, 30, 4, 72, 5, 129, 30, 4, 74, 5, +/* 7760 */ 129, 75, 5, 129, 76, 5, 129, 30, 4, 75, +/* 7770 */ 7, 129, 30, 4, 74, 9, 129, 30, 54, 132, +/* 7780 */ 30, 53, 129, 30, 52, 129, 30, 51, 129, 30, +/* 7790 */ 48, 129, 30, 4, 130, 193, 129, 30, 4, 80, +/* 7800 */ 4, 130, 30, 54, 136, 30, 4, 72, 5, 129, +/* 7810 */ 30, 4, 74, 5, 129, 75, 5, 129, 76, 5, +/* 7820 */ 129, 76, 6, 129, 77, 6, 130, 77, 7, 130, +/* 7830 */ 76, 8, 129, 30, 4, 75, 9, 129, 30, 4, +/* 7840 */ 72, 12, 129, 30, 54, 129, 30, 53, 130, 30, +/* 7850 */ 52, 129, 30, 51, 129, 30, 49, 129, 30, 46, +/* 7860 */ 129, 30, 42, 129, 30, 4, 130, 193, 129, 48, +/* 7870 */ 18, 129, 43, 28, 129, 41, 32, 129, 39, 36, +/* 7880 */ 129, 37, 40, 129, 35, 44, 129, 34, 46, 129, +/* 7890 */ 33, 13, 68, 13, 129, 32, 9, 73, 9, 129, +/* 7900 */ 32, 7, 75, 7, 129, 31, 6, 77, 6, 129, +/* 7910 */ 31, 5, 78, 5, 129, 30, 5, 79, 5, 129, +/* 7920 */ 30, 4, 80, 4, 132, 30, 5, 79, 5, 130, +/* 7930 */ 31, 5, 78, 5, 129, 31, 6, 77, 6, 129, +/* 7940 */ 32, 7, 75, 7, 129, 32, 9, 73, 9, 129, +/* 7950 */ 33, 13, 68, 13, 129, 34, 46, 129, 35, 44, +/* 7960 */ 129, 37, 40, 129, 39, 36, 129, 41, 32, 129, +/* 7970 */ 43, 28, 129, 48, 18, 129, 193, 129, 1, 3, +/* 7980 */ 80, 4, 130, 1, 83, 137, 37, 5, 72, 5, +/* 7990 */ 129, 35, 5, 74, 5, 129, 33, 5, 76, 5, +/* 8000 */ 129, 32, 5, 77, 5, 129, 31, 5, 78, 5, +/* 8010 */ 129, 31, 4, 79, 4, 129, 30, 5, 79, 5, +/* 8020 */ 131, 30, 6, 78, 6, 129, 30, 7, 77, 7, +/* 8030 */ 129, 31, 8, 75, 8, 129, 31, 11, 72, 11, +/* 8040 */ 129, 32, 15, 67, 15, 129, 33, 48, 129, 34, +/* 8050 */ 46, 129, 35, 44, 129, 37, 40, 129, 39, 36, +/* 8060 */ 129, 42, 30, 129, 46, 22, 129, 193, 129, 46, +/* 8070 */ 22, 129, 42, 30, 129, 39, 36, 129, 37, 40, +/* 8080 */ 129, 35, 44, 129, 34, 46, 129, 33, 48, 129, +/* 8090 */ 32, 15, 67, 15, 129, 31, 11, 72, 11, 129, +/* 8100 */ 31, 8, 75, 8, 129, 30, 7, 77, 7, 129, +/* 8110 */ 30, 6, 78, 6, 129, 30, 5, 79, 5, 131, +/* 8120 */ 31, 4, 79, 4, 129, 31, 5, 78, 5, 129, +/* 8130 */ 32, 5, 77, 5, 129, 33, 5, 76, 5, 129, +/* 8140 */ 35, 5, 74, 5, 129, 37, 5, 72, 5, 129, +/* 8150 */ 1, 83, 136, 1, 3, 80, 4, 130, 193, 129, +/* 8160 */ 30, 4, 80, 4, 130, 30, 54, 136, 30, 4, +/* 8170 */ 68, 6, 129, 30, 4, 70, 6, 129, 71, 7, +/* 8180 */ 129, 72, 7, 129, 73, 7, 129, 74, 7, 129, +/* 8190 */ 74, 8, 129, 75, 8, 130, 69, 15, 129, 67, +/* 8200 */ 17, 129, 66, 18, 129, 65, 19, 130, 65, 18, +/* 8210 */ 130, 66, 16, 129, 67, 13, 129, 69, 8, 129, +/* 8220 */ 193, 129, 30, 13, 64, 8, 129, 30, 13, 61, +/* 8230 */ 14, 129, 30, 13, 59, 18, 129, 30, 13, 57, +/* 8240 */ 22, 129, 33, 8, 56, 24, 129, 32, 7, 55, +/* 8250 */ 26, 129, 32, 6, 54, 28, 129, 31, 6, 53, +/* 8260 */ 16, 77, 6, 129, 31, 5, 53, 14, 79, 4, +/* 8270 */ 129, 30, 5, 52, 14, 80, 4, 129, 30, 5, +/* 8280 */ 52, 13, 80, 4, 129, 30, 4, 52, 13, 80, +/* 8290 */ 4, 129, 30, 4, 52, 12, 80, 4, 129, 30, +/* 8300 */ 4, 51, 13, 80, 4, 130, 30, 4, 50, 13, +/* 8310 */ 79, 5, 129, 30, 4, 50, 13, 78, 5, 129, +/* 8320 */ 30, 5, 49, 14, 77, 6, 129, 31, 4, 49, +/* 8330 */ 13, 76, 6, 129, 31, 5, 48, 14, 75, 7, +/* 8340 */ 129, 32, 5, 47, 14, 73, 8, 129, 32, 6, +/* 8350 */ 45, 16, 71, 13, 129, 33, 27, 71, 13, 129, +/* 8360 */ 34, 26, 71, 13, 129, 35, 24, 71, 13, 129, +/* 8370 */ 37, 20, 129, 39, 16, 129, 43, 9, 129, 193, +/* 8380 */ 129, 80, 4, 131, 41, 56, 129, 37, 60, 129, +/* 8390 */ 35, 62, 129, 33, 64, 129, 32, 65, 129, 31, +/* 8400 */ 66, 129, 30, 67, 130, 30, 11, 80, 4, 129, +/* 8410 */ 30, 9, 80, 4, 129, 30, 8, 80, 4, 129, +/* 8420 */ 31, 7, 80, 4, 129, 31, 6, 129, 32, 5, +/* 8430 */ 129, 33, 5, 129, 35, 4, 129, 38, 3, 129, +/* 8440 */ 193, 129, 80, 4, 130, 42, 42, 129, 38, 46, +/* 8450 */ 129, 35, 49, 129, 33, 51, 129, 32, 52, 129, +/* 8460 */ 31, 53, 130, 30, 54, 129, 30, 12, 129, 30, +/* 8470 */ 9, 129, 30, 8, 129, 30, 7, 130, 31, 6, +/* 8480 */ 130, 32, 6, 129, 33, 5, 129, 34, 5, 129, +/* 8490 */ 35, 5, 80, 4, 129, 37, 5, 80, 4, 129, +/* 8500 */ 30, 54, 136, 30, 4, 130, 193, 129, 80, 4, +/* 8510 */ 130, 77, 7, 129, 74, 10, 129, 70, 14, 129, +/* 8520 */ 66, 18, 129, 62, 22, 129, 59, 25, 129, 55, +/* 8530 */ 29, 129, 51, 33, 129, 47, 37, 129, 44, 32, +/* 8540 */ 80, 4, 129, 40, 32, 80, 4, 129, 36, 32, +/* 8550 */ 129, 32, 33, 129, 30, 31, 129, 33, 24, 129, +/* 8560 */ 36, 17, 129, 40, 12, 129, 44, 12, 129, 48, +/* 8570 */ 12, 129, 51, 13, 129, 55, 13, 129, 59, 13, +/* 8580 */ 80, 4, 129, 63, 13, 80, 4, 129, 67, 17, +/* 8590 */ 129, 71, 13, 129, 74, 10, 129, 78, 6, 129, +/* 8600 */ 80, 4, 131, 193, 129, 80, 4, 130, 77, 7, +/* 8610 */ 129, 74, 10, 129, 70, 14, 129, 66, 18, 129, +/* 8620 */ 62, 22, 129, 59, 25, 129, 55, 29, 129, 51, +/* 8630 */ 33, 129, 47, 37, 129, 44, 32, 80, 4, 129, +/* 8640 */ 40, 32, 80, 4, 129, 36, 32, 129, 32, 33, +/* 8650 */ 129, 30, 31, 129, 33, 24, 129, 36, 17, 129, +/* 8660 */ 40, 12, 129, 44, 12, 129, 47, 13, 129, 44, +/* 8670 */ 20, 129, 40, 28, 129, 36, 31, 129, 32, 32, +/* 8680 */ 129, 30, 30, 129, 33, 24, 129, 36, 17, 129, +/* 8690 */ 40, 12, 129, 44, 12, 129, 48, 12, 129, 51, +/* 8700 */ 13, 129, 55, 13, 129, 59, 13, 80, 4, 129, +/* 8710 */ 63, 13, 80, 4, 129, 67, 17, 129, 71, 13, +/* 8720 */ 129, 74, 10, 129, 78, 6, 129, 80, 4, 131, +/* 8730 */ 193, 129, 30, 4, 80, 4, 130, 30, 4, 79, +/* 8740 */ 5, 129, 30, 5, 77, 7, 129, 30, 6, 74, +/* 8750 */ 10, 129, 30, 8, 72, 12, 129, 30, 11, 69, +/* 8760 */ 15, 129, 30, 13, 67, 17, 129, 30, 4, 37, +/* 8770 */ 8, 64, 20, 129, 30, 4, 39, 8, 62, 22, +/* 8780 */ 129, 41, 8, 59, 25, 129, 43, 8, 57, 27, +/* 8790 */ 129, 45, 8, 55, 22, 80, 4, 129, 47, 27, +/* 8800 */ 80, 4, 129, 49, 23, 129, 47, 22, 129, 44, +/* 8810 */ 23, 129, 42, 22, 129, 30, 4, 39, 27, 129, +/* 8820 */ 30, 4, 37, 31, 129, 30, 27, 62, 8, 129, +/* 8830 */ 30, 25, 64, 8, 129, 30, 22, 66, 8, 80, +/* 8840 */ 4, 129, 30, 20, 68, 8, 80, 4, 129, 30, +/* 8850 */ 17, 70, 8, 80, 4, 129, 30, 15, 73, 11, +/* 8860 */ 129, 30, 12, 75, 9, 129, 30, 10, 77, 7, +/* 8870 */ 129, 30, 7, 79, 5, 129, 30, 5, 80, 4, +/* 8880 */ 129, 30, 4, 80, 4, 130, 193, 129, 4, 5, +/* 8890 */ 80, 4, 129, 2, 9, 80, 4, 129, 1, 11, +/* 8900 */ 77, 7, 129, 1, 12, 74, 10, 129, 1, 12, +/* 8910 */ 70, 14, 129, 1, 12, 66, 18, 129, 1, 11, +/* 8920 */ 62, 22, 129, 2, 9, 59, 25, 129, 4, 11, +/* 8930 */ 55, 29, 129, 7, 12, 51, 33, 129, 10, 12, +/* 8940 */ 47, 37, 129, 14, 12, 44, 32, 80, 4, 129, +/* 8950 */ 17, 13, 40, 32, 80, 4, 129, 21, 13, 36, +/* 8960 */ 32, 129, 25, 40, 129, 29, 32, 129, 33, 24, +/* 8970 */ 129, 36, 17, 129, 40, 12, 129, 44, 12, 129, +/* 8980 */ 48, 12, 129, 51, 13, 129, 55, 13, 129, 59, +/* 8990 */ 13, 80, 4, 129, 63, 13, 80, 4, 129, 67, +/* 9000 */ 17, 129, 71, 13, 129, 74, 10, 129, 78, 6, +/* 9010 */ 129, 80, 4, 131, 193, 129, 30, 1, 71, 13, +/* 9020 */ 129, 30, 3, 71, 13, 129, 30, 6, 71, 13, +/* 9030 */ 129, 30, 9, 75, 9, 129, 30, 11, 77, 7, +/* 9040 */ 129, 30, 14, 79, 5, 129, 30, 17, 79, 5, +/* 9050 */ 129, 30, 19, 80, 4, 129, 30, 22, 80, 4, +/* 9060 */ 129, 30, 25, 80, 4, 129, 30, 27, 80, 4, +/* 9070 */ 129, 30, 4, 36, 24, 80, 4, 129, 30, 4, +/* 9080 */ 38, 25, 80, 4, 129, 30, 4, 41, 24, 80, +/* 9090 */ 4, 129, 30, 4, 44, 24, 80, 4, 129, 30, +/* 9100 */ 4, 46, 25, 80, 4, 129, 30, 4, 49, 25, +/* 9110 */ 80, 4, 129, 30, 4, 52, 24, 80, 4, 129, +/* 9120 */ 30, 4, 54, 30, 129, 30, 4, 57, 27, 129, +/* 9130 */ 30, 4, 59, 25, 129, 30, 4, 62, 22, 129, +/* 9140 */ 30, 4, 65, 19, 129, 30, 5, 67, 17, 129, +/* 9150 */ 30, 5, 70, 14, 129, 30, 7, 73, 11, 129, +/* 9160 */ 30, 9, 76, 8, 129, 30, 13, 78, 6, 129, +/* 9170 */ 30, 13, 81, 3, 129, 30, 13, 129, 193, 2, +/* 9180 */ 9, 59, 25, 129, 4, 11, 55, 29, 129, 7, +/* 9190 */ 12, 51, 33, 129, 10, 12, 47, 37, 129, 14, +/* 9200 */ 12, 44, 32, 80, 4, 129, 17, 13, 40, 32, +/* 9210 */ 80, 4, 129, 21, 13, 36, 32, 129, 25, 40, +/* 9220 */ 129, 29, 32, 129, 33, 24, 129, 36, 17, 129, +/* 9230 */ 40, 12, 129, 44, 12, 129, 48, 12, 129, 51, +/* 9240 */ 13, 129, 55, 13, 129, 59, 13, 80, 4, 129, +/* 9250 */ 63, 13, 80, 4, 129, 67, 17, 129, 71, 13, +/* 9260 */ 129, 74, 10, 129, 78, 6, 129, 80, 4, 131, +/* 9270 */ 193 +}; + +char line[DWIDTH]; +char message[MAXMSG]; +char print[DWIDTH]; +int debug, i, j, linen, max, nchars, pc, term, trace, x, y; +int width = DWIDTH; /* -w option: scrunch letters to 80 columns */ + +int +main(argc, argv) + int argc; + char **argv; +{ + int ch; + +#ifdef __linux__ + extern char *__progname; + __progname = argv[0]; +#endif + + while ((ch = getopt(argc, argv, "w:td")) != EOF) + switch(ch) { + case 'w': + width = atoi(optarg); + if (width <= 0) + width = 80; + break; + case 'd': + debug = 1; + break; + case 't': + trace = 1; + break; + case '?': + default: + fprintf(stderr, "usage: banner [-w width]\n"); + exit(1); + } + argc -= optind; + argv += optind; + + for (i = 0; i < width; i++) { + j = i * 132 / width; + print[j] = 1; + } + + /* Have now read in the data. Next get the message to be printed. */ + if (*argv) { + strcpy(message, *argv); + while (*++argv) { + strcat(message, " "); + strcat(message, *argv); + } + nchars = strlen(message); + } else { + fprintf(stderr,"Message: "); + (void)fgets(message, sizeof(message), stdin); + nchars = strlen(message); + message[nchars--] = '\0'; /* get rid of newline */ + } + + /* some debugging print statements */ + if (debug) { + printf("int asc_ptr[128] = {\n"); + for (i = 0; i < 128; i++) { + printf("%4d, ",asc_ptr[i]); + if ((i+1) % 8 == 0) + printf("\n"); + } + printf("};\nchar data_table[NBYTES] = {\n"); + printf(" /* "); + for (i = 0; i < 10; i++) printf(" %3d ",i); + printf(" */\n"); + for (i = 0; i < NBYTES; i += 10) { + printf("/* %4d */ ",i); + for (j = i; j < i+10; j++) { + x = data_table[j] & 0377; + printf(" %3d, ",x); + } + putchar('\n'); + } + printf("};\n"); + } + + /* check message to make sure it's legal */ + j = 0; + for (i = 0; i < nchars; i++) + if ((u_char) message[i] >= NCHARS || + asc_ptr[(u_char) message[i]] == 0) { + warnx("The character '%c' is not in my character set", + message[i]); + j++; + } + if (j) + exit(1); + + if (trace) + printf("Message '%s' is OK\n",message); + /* Now have message. Print it one character at a time. */ + + for (i = 0; i < nchars; i++) { + if (trace) + printf("Char #%d: %c\n", i, message[i]); + for (j = 0; j < DWIDTH; j++) line[j] = ' '; + pc = asc_ptr[(u_char) message[i]]; + term = 0; + max = 0; + linen = 0; + while (!term) { + if (pc < 0 || pc > NBYTES) { + printf("bad pc: %d\n",pc); + exit(1); + } + x = data_table[pc] & 0377; + if (trace) + printf("pc=%d, term=%d, max=%d, linen=%d, x=%d\n",pc,term,max,linen,x); + if (x >= 128) { + if (x>192) term++; + x = x & 63; + while (x--) { + if (print[linen++]) { + for (j=0; j <= max; j++) + if (print[j]) + putchar(line[j]); + putchar('\n'); + } + } + for (j = 0; j < DWIDTH; j++) line[j] = ' '; + pc++; + } + else { + y = data_table[pc+1]; + /* compensate for narrow teminals */ +#ifdef notdef + x = (x*width + (DWIDTH/2)) / DWIDTH; + y = (y*width + (DWIDTH/2)) / DWIDTH; +#endif + max = x+y; + while (x < max) line[x++] = '#'; + pc += 2; + if (trace) + printf("x=%d, y=%d, max=%d\n",x,y,max); + } + } + } + + exit(0); +} diff --git a/games/ddate.6 b/games/ddate.6 new file mode 100644 index 00000000..6499c620 --- /dev/null +++ b/games/ddate.6 @@ -0,0 +1,17 @@ +.\" All Rites Reversed. This file is in the PUBLIC DOMAIN. +.\" Kallisti. +.TH DDATE 6 "55 Confusion 3160" "" "Linux Programmer's Manual" +.SH NAME +ddate \- converts boring normal dates to fun Discordian Dates +.SH SYNOPSIS +.B ddate +.SH DESCRIPTION +.B ddate +prints the date in Discordian Date format. +.SH AUTHOR +.nf +Druel the Chaotic aka Jeremy Johnson (mpython@gnu.ai.mit.edu) +.br +Modifications for Unix by Lee Harvey Oswald Smith, K.S.C. +.br +Five tons of flax. diff --git a/games/ddate.c b/games/ddate.c new file mode 100644 index 00000000..33ce386f --- /dev/null +++ b/games/ddate.c @@ -0,0 +1,171 @@ +/* ddate.c .. converts boring normal dates to fun Discordian Date -><- + written the 65th day of The Aftermath in the Year of Our Lady of + Discord 3157 by Druel the Chaotic aka Jeremy Johnson aka + mpython@gnu.ai.mit.edu + + and I'm not responsible if this program messes anything up (except your + mind, I'm responsible for that) + + Modifications for Unix by Lee Harvey Oswald Smith, K.S.C. + Five tons of flax. +*/ + +#include +#include +#include + +struct disc_time +{int season; /* 0-4 */ + int day; /* 0-72 */ + int yday; /* 0-365 */ + int year; /* 3066- */ +}; + +char *ending(int); +void print(struct disc_time,char **); +struct disc_time convert(int,int); +struct disc_time makeday(int,int,int); + +main (int argc,char **argv) +{long t; + struct tm *eris; + int bob,raw; + struct disc_time hastur; + if (argc==4) + { int moe,larry,curly; + moe=atoi(argv[1]); + larry=atoi(argv[2]); + curly=atoi(argv[3]); + hastur=makeday(moe,larry,curly); + } + else if (argc!=1) + { fprintf(stderr,"Syntax: DiscDate [month day year]"); + exit(1); + } + else + { + t= time(NULL); + eris=localtime(&t); + bob=eris->tm_yday; /* days since Jan 1. */ + raw=eris->tm_year; /* years since 1980 */ + hastur=convert(bob,raw); + } + print(hastur,argv); +} + +struct disc_time makeday(int imonth,int iday,int iyear) /*i for input */ +{ struct disc_time funkychickens; + + int cal[12] = + { + 31,28,31,30,31,30,31,31,30,31,30,31 + }; + int dayspast=0; + + imonth--; + funkychickens.year= iyear+1166; + while(imonth>0) + { + dayspast+=cal[--imonth]; + } + funkychickens.day=dayspast+iday-1; + funkychickens.season=0; + if((funkychickens.year%4)==2) + { + if (funkychickens.day==59) + funkychickens.day=-1; + } + funkychickens.yday=funkychickens.day; +/* note: EQUAL SIGN...hopefully that fixes it */ + while(funkychickens.day>=73) + { + funkychickens.season++; + funkychickens.day-=73; + } + return funkychickens; +} + +char *ending(int num) +{ + int temp; + char *funkychickens; + + funkychickens=(char *)malloc(sizeof(char)*3); + + temp=num%10; /* get 0-9 */ + switch (temp) + { case 1: + strcpy(funkychickens,"st"); + break; + case 2: + strcpy(funkychickens,"nd"); + break; + case 3: + strcpy(funkychickens,"rd"); + break; + default: + strcpy(funkychickens,"th"); + } + return funkychickens; +} + +struct disc_time convert(int nday, int nyear) +{ struct disc_time funkychickens; + + funkychickens.year = nyear+3066; + funkychickens.day=nday; + funkychickens.season=0; + if ((funkychickens.year%4)==2) + {if (funkychickens.day==59) + funkychickens.day=-1; + else if (funkychickens.day >59) + funkychickens.day-=1; + } + funkychickens.yday=funkychickens.day; + while (funkychickens.day>=73) + { funkychickens.season++; + funkychickens.day-=73; + } + return funkychickens; + + } + +void print(struct disc_time tick, char **args) +{ char *days[5] = { "Sweetmorn", + "Boomtime", + "Pungenday", + "Prickle-Prickle", + "Setting Orange" + }; + char *seasons[5] = { "Chaos", + "Discord", + "Confusion", + "Bureaucracy", + "The Aftermath" + }; + char *holidays[5][2] = { "Mungday", "Chaoflux", + "Mojoday", "Discoflux", + "Syaday", "Confuflux", + "Zaraday", "Bureflux", + "Maladay", "Afflux" + }; + if (args[1]==NULL) + printf("Today is "); + else + printf("%s-%s-%s is ",args[1],args[2],args[3]); + if (tick.day==-1) printf("St. Tib's Day!"); + else + { tick.day++; + printf("%s",days[tick.yday%5]); + printf(", the %d", tick.day); + printf("%s day of %s",ending(tick.day),seasons[tick.season]) ; + } + printf(" in the YOLD %d\n",tick.year); + if ((tick.day==5)||(tick.day==50)) + { printf("Celebrate "); + if (tick.day==5) + printf("%s\n",holidays[tick.season][0]); + else + printf("%s\n",holidays[tick.season][1]); + } +} diff --git a/getpoe.sh b/getpoe.sh new file mode 100644 index 00000000..6c808946 --- /dev/null +++ b/getpoe.sh @@ -0,0 +1,56 @@ +#!/bin/bash -x +adm=/tmp/admutil +poe=/tmp/poeigl +tmp=/tmp/$$ +diffs=poe.diffs + +if [ -e $diffs ]; then rm $diffs; fi +if [ ! -d $tmp ]; then mkdir $tmp; fi + +function cmpandcp () { + dir=$1; + i=$2; + name=${i#$poe/}; + name=${name#$adm/}; + target=$dir/$name; + diff -u $target.c $i.c >> $diffs; + mv $target.c $tmp/$name.c; + mv $i.c $target.c; + for k in man 1 8; do + if [ -e $i.$k ]; then + for j in 1 8; do + if [ -e $target.$j ]; then + diff -u $target.$j $i.$k >> $diffs; + mv $target.$j $tmp/$name.$j; + mv $i.$k $target.$j; + fi + done + fi + done +} + + +# login-utils +for i in $poe/agetty $adm/last $poe/login $adm/newgrp $adm/passwd \ + $adm/shutdown $poe/simpleinit; do + cmpandcp login-utils $i; +done + +# misc-utils +cmpandcp misc-utils $poe/hostid; +cmpandcp misc-utils $poe/domainname; + +# sys-utils +cmpandcp sys-utils $adm/ctrlaltdel; + +# READMEs +diff -u $adm/README login-utils/README.admutil >> $diffs +mv $adm/README login-utils/README.admutil + +diff -u $poe/README login-utils/README.poeigl >> $diffs +mv $poe/README login-utils/README.poeigl + +diff -u $poe/README.getty login-utils >> $diffs +mv $poe/README.getty login-utils + +exit diff --git a/historic/makehole.8 b/historic/makehole.8 new file mode 100644 index 00000000..5b5c63be --- /dev/null +++ b/historic/makehole.8 @@ -0,0 +1,44 @@ +.\" Copyright 1992 Rickard E. Faith (faith@cs.unc.edu) +.\" May be distributed under the GNU General Public License +.TH MAKEHOLE 8 "20 November 1993" "Linux 0.99" "Linux Programmer's Manual" +.SH NAME +makehole \- a program to make filesystem "holes" in pure executables +.SH SYNOPSIS +.B makehole +Imagefile +.SH DESCRIPTION +.B makehole +copies the +.IR Imagefile , +using +.BR lseek (2) +to skip over sections of the file which contain all zeros. If the file +system is smart enough to recognize this use of +.BR lseek (2), +then it will store the file in a more efficient fashion. + +The logical length of the file will +.I not +be changed, only the way it is stored in the filesystem. This can save a +lot of space if the file contains large blocks of zeros. +.BR cp (3) +will not similar "hole creation," but it does not seem to be as extensive +(see the GNU source code for details). +.BR dd (3) +will +.I not +create holes, and should be used when holes are not desired (i.e., for the +.BR shoelace (8) +boot image). +.SH "SEE ALSO" +.BR lseek (2), +.BR cp (3), +.BR dd (3) +.SH BUGS +Must be root to run. +.br +The +.I Imagefile +must be a pure exectuable. +.SH AUTHOR +HJ Lu diff --git a/historic/makehole.c b/historic/makehole.c new file mode 100644 index 00000000..6c337db0 --- /dev/null +++ b/historic/makehole.c @@ -0,0 +1,140 @@ +/* makehole.c - original by HJ Lu */ + +/* Patched by faith@cs.unc.edu, Wed Oct 6 18:01:39 1993 based on + information from Michael Bischoff (Fri, 18 + Jun 93 10:10:19 +0200). */ + +#include +#include +#include +#include +#include +#include + +#define BUFSIZE 1024 +#undef DEBUG + +void usage(char *name, char *message) +{ + if (message) + fprintf(stderr, "%s: %s\n", name, message); + + if (errno) + perror(name); + + fprintf(stderr, "Usage:%s Imagefile\n", name); + exit(1); +} + +int ishole(char *buf, int size) +{ + int i; + + for (i = 0; i < size; i++) + if (buf[i]) + return 0; + + return 1; +} + +void main(int argc, char *argv[]) +{ + char buf[BUFSIZE]; + char tmp_file[64]; + int fdin, fdout; + int ret; + int abs_offset; + int hole; + struct exec *header = (struct exec *) buf; + +#ifndef DEBUG + if (geteuid()) { + fprintf(stderr, "%s: must be root to run!\n", *argv); + exit(1); + } +#endif + + switch (argc) { + case 2: + break; + default: + usage(*argv, NULL); + } + + errno = 0; + + sprintf( tmp_file, "hole%d", getpid() ); + if (tmp_file == NULL) { + usage(*argv, "Unable to get a temporary image filename!"); + } +#ifdef DEBUG + else { + fprintf(stderr, "Temparory image file: %s\n", tmp_file); + } +#endif + + errno = 0; + fdin = open(argv[1], O_RDONLY); + if (fdin == -1) { + usage(*argv, "unable to open file."); + } + fprintf(stderr, "Making holes in %s...\n", argv[1]); + + errno = 0; + + if ((ret = read(fdin, header, BUFSIZE)) != BUFSIZE + || N_MAGIC(*header) != ZMAGIC) { + usage(*argv, "file must be pure executable."); + } + + fdout = creat(tmp_file, 0555); + if (fdout == -1) { + perror("Unable to create the temparory image file!"); + exit(1); + } + if (write(fdout, header, ret) != ret) { + perror("Fail to write header to the temparory image file!"); + unlink(tmp_file); + exit(1); + } + abs_offset = ret; + hole = 0; + while ((ret = read(fdin, buf, BUFSIZE)) > 0) { + abs_offset += ret; + if (ishole(buf, ret)) { +#ifdef DEBUG + fprintf(stderr, "There is a %d byte hole from 0x%x to 0x%x.\n", ret, abs_offset - ret, abs_offset); +#endif + hole += ret; + if (lseek(fdout, abs_offset, SEEK_SET) != abs_offset) { + perror("Fail to make a hole in the temparory image file!"); + unlink(tmp_file); + exit(1); + } + } else { +#ifdef DEBUG + fprintf(stderr, "Writing %d bytes from 0x%x to 0x%x.\n", ret, abs_offset - ret, abs_offset); +#endif + if (write(fdout, buf, ret) != ret) { + perror("Fail to write the temparory image file!"); + unlink(tmp_file); + exit(1); + } + } + } + + if (ftruncate(fdout, abs_offset)) { + perror("Fail to truncate the temparory image file!"); + unlink(tmp_file); + exit(1); + } + close(fdout); + close(fdin); + + if (rename(tmp_file, argv[1])) { + perror("Fail to rename the temparory image file to the old image file!"); + unlink(tmp_file); + exit(1); + } + fprintf(stderr, "There are %d byte holes out of %d bytes in `%s'.\n", hole, abs_offset, argv[1]); +} diff --git a/historic/makeinfo.sh b/historic/makeinfo.sh new file mode 100644 index 00000000..9061c833 --- /dev/null +++ b/historic/makeinfo.sh @@ -0,0 +1,2 @@ +#!/bin/sh +emacs -batch $* -f texinfo-format-buffer -f save-buffer diff --git a/historic/selection/Makefile b/historic/selection/Makefile new file mode 100644 index 00000000..ec78fbcd --- /dev/null +++ b/historic/selection/Makefile @@ -0,0 +1,58 @@ +# Makefile for selection utility +# Andrew Haylett, 17th June 1993 +# Minor modifications by Rik Faith (faith@cs.unc.edu), Sat Nov 20 09:47:59 1993 + +# bump .., since we live in historic now +include ../../MCONFIG +BINDIR = $(USRBINDIR) +MANEXT = 1 + +all: selection test-mouse + +selection: selection.o mouse.o + $(CC) $(LDFLAGS) -o selection selection.o mouse.o + +test-mouse: test-mouse.o mouse.o + $(CC) $(LDFLAGS) -o test-mouse test-mouse.o mouse.o + +mouse.o: mouse.c mouse.h + +selection.o: selection.c mouse.h Makefile + +test-mouse.o: test-mouse.c mouse.h + +selection.man: selection.1 + nroff -man selection.1 > selection.man + +install: selection # selection.man + install -m 755 selection $(BINDIR)/selection + install -m 644 selection.1 $(MANDIR)/man$(MANEXT)/selection.$(MANEXT) + +DIST = selection-1.5 +DATE = 17th June 1993 +PATCH = patch-0.99.10 +SRC = README Makefile selection.1 mouse.c mouse.h selection.c test-mouse.c +DFILES = $(SRC) selection.man $(PATCH) +DIFF = diff +DFLAGS = -u + +patch: + (cd /usr/src/linux; \ + $(DIFF) -c0 $(DFLAGS) config.in~ config.in; \ + $(DIFF) $(DFLAGS) kernel/chr_drv/tty_ioctl.c~ kernel/chr_drv/tty_ioctl.c; \ + $(DIFF) $(DFLAGS) kernel/chr_drv/console.c~ kernel/chr_drv/console.c) \ + > $(PATCH); true + +update: + perl -pi -e 's/\d+\S+ \S+ 199[3]/$(DATE)/' $(SRC) + +dist: update patch $(DFILES) + rm -fr $(DIST) + mkdir $(DIST) + cp $(DFILES) $(DIST) + tar cf - $(DIST) | gzip -c > $(DIST).tar.z + rm -fr $(DIST) + +clean: + rm -f selection.o mouse.o test-mouse.o selection test-mouse \ + selection.man *~ diff --git a/historic/selection/README.selection b/historic/selection/README.selection new file mode 100644 index 00000000..7435161d --- /dev/null +++ b/historic/selection/README.selection @@ -0,0 +1,151 @@ + selection 1.5: Copy and paste for Linux Virtual Consoles using mouse + -------------------------------------------------------------------- + +This package implements mouse-driven selection of text from a VC and pasting +of the text into the same or a different VC, the user interface being based +loosely on the equivalent xterm facility. + +Version 1.5 +----------- + - fixed support for bus mice. + - added support for PS/2 and Mouse Systems 3-byte mice. + - command line options added. + - updated for kernel version 0.99.pl10. + - cooperates with XFree86 1.2, for serial mice at least. + - enabled as part of normal kernel configuration process. + +Version 1.4 +----------- + - added manual page. + - updated for kernel version 0.99.pl0. + +Version 1.3 +----------- + - improved support for Logitech mice (speed set correctly). + - optional flag for left-handed users. + - corrected bug in Mouse Systems handling code. + +Version 1.2 +----------- + - disabled when console in graphics mode, eg. under X11 or MGR. + - uses default screen size if ioctl TIOCGWINSZ fails. + +Version 1.1 +----------- + - support for some common mouse types. + - selection by word or line as well as by character. + - changes in the interface to make it behave more like xterm. + +Manifest +-------- + README + Makefile + selection.1 manual page + selection.man formatted manual page + patch-0.99.10 patches to kernel + mouse.c source for mouse driver + mouse.h mouse driver interface + selection.c source for selection manager + test-mouse.c test code for mouse compatibility + +Mouse support +------------- + +The following types of mouse are supported. + + - Microsoft + - MouseSystems 3-byte and 5-byte + - MM Series + - Logitech + - BusMouse + - PS/2 + +The code has been tested with various types of mice, including +Microsoft-compatible and Logitech, a three-button Mouse Systems, and with bus +and PS/2 mice; please tell me if it doesn't work with yours and you think it +ought to. + +Installation +------------ + +1. Check it out +--------------- + + - Make the mouse device. If you have a serial mouse, either use `mknod' to + make /dev/mouse reference the appropriate serial driver or create a + symbolic link from /dev/ttys? to /dev/mouse. If you have a bus mouse, + use `mknod' to create the bus mouse device if it doesn't already exist. + Make sure that your kernel is configured to support the appropriate + bus mouse device (specified during `make config'). + +e.g. mknod /dev/mouse c 4 64 +or ln -s /dev/ttys1 /dev/mouse (for serial mouse) + + mknod /dev/busmouse c 10 0 (for Logitech bus mouse) + + - Test your mouse for compatibility by using the test-mouse facility + supplied. Build it by typing `make test-mouse', then run `test-mouse'. + You may need to supply it with certain options; try `test-mouse -?'. + If your mouse device is not /dev/mouse, use the -m option. You should + be able to move the cursor over the entire screen, and draw + asterisks in different colours by moving the mouse while pressing + different buttons. Press both the left and right buttons while the mouse + is not moving to quit the program. The options that you find work with + `test-mouse' should also work with `selection'. + +2. Patch the kernel +------------------- + + [ NOTE: Precompiled versions of the kernel supplied with the SLS + package should already have the patch applied, in which case this + section may be skipped. ] + + - Apply the kernel patches, by going into the directory in which the + kernel source is located (eg. /usr/src/linux) and typing: + + patch < patch-0.99.10 + + The patches were generated against the standard 0.99.pl10 kernel. + +The following files are patched: + + config.in to add the selection mechanism as a + configuration option. + + kernel/chr_drv/tty_ioctl.c to provide the interface to the selection + mechanism via ioctl(..., TCIOLINUX, ...). + + kernel/chr_drv/console.c to implement the selection mechanism itself. + + - Reconfigure the kernel by typing 'make config', remembering to include + the selection mechanism by answering 'y' to the appropriate question. + + - To be safe, rebuild the kernel dependencies using 'make dep'. + + - Rebuild the kernel and reboot. + + - Make sure you have the /dev/tty0 (current VC) device. If not, make it using + + mknod /dev/tty0 c 4 0 + +3. Build the program +-------------------- + + - Type `make' in the directory in which you unpacked the selection code; + this will build the `selection' executable. It has been tested with + gcc 2.3.3 and libc.so.4.3.3. + + - Run `selection &' to test it out. Use `selection -?' to see what + options are supported. Then type `make install', which installs the + executable in /etc and the manual page in /usr/man, and start it up + from /etc/rc.local. Consult the manual page for usage. It should + work with text screens of various sizes, e.g. 80x28, 80x50, etc. + +The default size of the paste buffer is 2048 bytes. This may be changed by +altering the value of SEL_BUFFER_SIZE in kernel/chr_drv/console.c. + +And that's all there is to it, hopefully. See the manual page for a more +detailed description of operation. Please let me know of any problems, +suggestions for enhancements, etc, etc. + +Andrew Haylett , 17th June 1993 diff --git a/historic/selection/mouse.c b/historic/selection/mouse.c new file mode 100644 index 00000000..87e9d06e --- /dev/null +++ b/historic/selection/mouse.c @@ -0,0 +1,367 @@ +/* simple driver for serial mouse */ +/* Andrew Haylett, 17th June 1993 */ + +#include +#include +#include +#include +#include +#include + +#include "mouse.h" + +#define DEF_MDEV "/dev/mouse" +#define DEF_MTYPE P_MS +#define DEF_MBAUD 1200 +#define DEF_MSAMPLE 100 +#define DEF_MDELTA 25 +#define DEF_MACCEL 2 +#define DEF_SLACK -1 + +/* thse settings may be altered by the user */ +static char *mdev = DEF_MDEV; /* mouse device */ +static mouse_type mtype = DEF_MTYPE; /* mouse type */ +static int mbaud = DEF_MBAUD; /* mouse device baud rate */ +static int msample = DEF_MSAMPLE; /* sample rate for Logitech mice */ +static int mdelta = DEF_MDELTA; /* x+y movements more than mdelta pixels..*/ +static int maccel = DEF_MACCEL; /* ..are multiplied by maccel. */ +static int slack = DEF_SLACK; /* < 0 ? no wraparound : amount of slack */ +int ms_copy_button = MS_BUTLEFT, + ms_paste_button = MS_BUTRIGHT; + +static char *progname; + +static void +ms_usage() +{ + printf( + "Selection version 1.5, 17th June 1993\n" + "Usage: %s [-a accel] [-b baud-rate] [-c l|m|r] [-d delta]\n" + " [-m mouse-device] [-p l|m|r] [-s sample-rate] [-t mouse-type]\n" + " [-w slack]\n\n", progname); + printf( + " -a accel sets the acceleration (default %d)\n" + " -b baud-rate sets the baud rate (default %d)\n" + " -c l|m|r sets the copy button (default `l')\n" + " -d delta sets the delta value (default %d)\n" + " -m mouse-device sets mouse device (default `%s')\n" + " -p l|m|r sets the paste button (default `r')\n" + " -s sample-rate sets the sample rate (default %d)\n" + " -t mouse-type sets mouse type (default `ms')\n" + " Microsoft = `ms', Mouse Systems Corp = `msc',\n" + " MM Series = `mm', Logitech = `logi', BusMouse = `bm',\n" + " MSC 3-bytes = `sun', PS/2 = `ps2')\n" + " -w slack turns on wrap-around and specifies slack (default off)\n", + DEF_MACCEL, DEF_MBAUD, DEF_MDELTA, DEF_MDEV, DEF_MSAMPLE); + exit(1); +} + +extern int optind; +extern char *optarg; + +void +ms_params(int argc, char *argv[]) +{ + int opt; + + progname = (rindex(argv[0], '/')) ? rindex(argv[0], '/') + 1 : argv[0]; + while ((opt = getopt(argc, argv, "a:b:c:d:m:p:s:t:w:")) != -1) + { + switch (opt) + { + case 'a': + maccel = atoi(optarg); + if (maccel < 2) + ms_usage(); + break; + case 'b': + mbaud = atoi(optarg); + break; + case 'c': + switch (*optarg) + { + case 'l': ms_copy_button = MS_BUTLEFT; break; + case 'm': ms_copy_button = MS_BUTMIDDLE; break; + case 'r': ms_copy_button = MS_BUTRIGHT; break; + default: ms_usage(); break; + } + break; + case 'd': + mdelta = atoi(optarg); + if (mdelta < 2) + ms_usage(); + break; + case 'm': + mdev = optarg; + break; + case 'p': + switch (*optarg) + { + case 'l': ms_paste_button = MS_BUTLEFT; break; + case 'm': ms_paste_button = MS_BUTMIDDLE; break; + case 'r': ms_paste_button = MS_BUTRIGHT; break; + default: ms_usage(); break; + } + break; + case 's': + msample = atoi(optarg); + break; + case 't': + if (!strcmp(optarg, "ms")) + mtype = P_MS; + else if (!strcmp(optarg, "sun")) + mtype = P_SUN; + else if (!strcmp(optarg, "msc")) + mtype = P_MSC; + else if (!strcmp(optarg, "mm")) + mtype = P_MM; + else if (!strcmp(optarg, "logi")) + mtype = P_LOGI; + else if (!strcmp(optarg, "bm")) + mtype = P_BM; + else if (!strcmp(optarg, "ps2")) + mtype = P_PS2; + else + ms_usage(); + break; + case 'w': + slack = atoi (optarg); + break; + default: + ms_usage(); + break; + } + } +} + +#define limit(n,l,u,s) n = ((s) < 0 ? \ + (((n) < (l) ? (l) : ((n) > (u) ? (u) : (n)))) : \ + (((n) < (l-s) ? (u) : ((n) > (u+s) ? (l) : (n))))) + +static int mx = 32767; +static int my = 32767; +static int x, y; +static int mfd = -1; + +static const unsigned short cflag[NR_TYPES] = +{ + (CS7 | CREAD | CLOCAL | HUPCL ), /* MicroSoft */ + (CS8 | CSTOPB | CREAD | CLOCAL | HUPCL ), /* MouseSystems 3 */ + (CS8 | CSTOPB | CREAD | CLOCAL | HUPCL ), /* MouseSystems 5 */ + (CS8 | PARENB | PARODD | CREAD | CLOCAL | HUPCL ), /* MMSeries */ + (CS8 | CSTOPB | CREAD | CLOCAL | HUPCL ), /* Logitech */ + 0, /* BusMouse */ + 0 /* PS/2 */ +}; + +static const unsigned char proto[NR_TYPES][5] = +{ + /* hd_mask hd_id dp_mask dp_id nobytes */ + { 0x40, 0x40, 0x40, 0x00, 3 }, /* MicroSoft */ + { 0xf8, 0x80, 0x00, 0x00, 3 }, /* MouseSystems 3 (Sun) */ + { 0xf8, 0x80, 0x00, 0x00, 5 }, /* MouseSystems 5 */ + { 0xe0, 0x80, 0x80, 0x00, 3 }, /* MMSeries */ + { 0xe0, 0x80, 0x80, 0x00, 3 }, /* Logitech */ + { 0xf8, 0x80, 0x00, 0x00, 5 }, /* BusMouse */ + { 0xcc, 0x00, 0x00, 0x00, 3 } /* PS/2 */ +}; + +static void +ms_setspeed(const int old, const int new, + const unsigned short c_cflag) +{ + struct termios tty; + char *c; + + tcgetattr(mfd, &tty); + + tty.c_iflag = IGNBRK | IGNPAR; + tty.c_oflag = 0; + tty.c_lflag = 0; + tty.c_line = 0; + tty.c_cc[VTIME] = 0; + tty.c_cc[VMIN] = 1; + + switch (old) + { + case 9600: tty.c_cflag = c_cflag | B9600; break; + case 4800: tty.c_cflag = c_cflag | B4800; break; + case 2400: tty.c_cflag = c_cflag | B2400; break; + case 1200: + default: tty.c_cflag = c_cflag | B1200; break; + } + + tcsetattr(mfd, TCSAFLUSH, &tty); + + switch (new) + { + case 9600: c = "*q"; tty.c_cflag = c_cflag | B9600; break; + case 4800: c = "*p"; tty.c_cflag = c_cflag | B4800; break; + case 2400: c = "*o"; tty.c_cflag = c_cflag | B2400; break; + case 1200: + default: c = "*n"; tty.c_cflag = c_cflag | B1200; break; + } + + write(mfd, c, 2); + usleep(100000); + tcsetattr(mfd, TCSAFLUSH, &tty); +} + +int +ms_init(const int maxx, const int maxy) +{ + if ((mfd = open(mdev, O_RDWR)) < 0) + { + char buf[32]; + sprintf(buf, "ms_init: %s", mdev); + perror(buf); + return -1; + } + + if (mtype != P_BM && mtype != P_PS2) + { + ms_setspeed(9600, mbaud, cflag[mtype]); + ms_setspeed(4800, mbaud, cflag[mtype]); + ms_setspeed(2400, mbaud, cflag[mtype]); + ms_setspeed(1200, mbaud, cflag[mtype]); + + if (mtype == P_LOGI) + { + write(mfd, "S", 1); + ms_setspeed(mbaud, mbaud, cflag[P_MM]); + } + + if (msample <= 0) write(mfd, "O", 1); + else if (msample <= 15) write(mfd, "J", 1); + else if (msample <= 27) write(mfd, "K", 1); + else if (msample <= 42) write(mfd, "L", 1); + else if (msample <= 60) write(mfd, "R", 1); + else if (msample <= 85) write(mfd, "M", 1); + else if (msample <= 125) write(mfd, "Q", 1); + else write(mfd, "N", 1); + } + + mx = maxx; + my = maxy; + x = mx / 2; + y = my / 2; + return 0; +} + +int +get_ms_event(struct ms_event *ev) +{ + unsigned char buf[5]; + char dx, dy; + int i, acc; + + if (mfd == -1) + return -1; + if (mtype != P_BM) + { + if (read(mfd, &buf[0], 1) != 1) + return -1; +restart: + /* find a header packet */ + while ((buf[0] & proto[mtype][0]) != proto[mtype][1]) + { + if (read(mfd, &buf[0], 1) != 1) + { + perror("get_ms_event: read"); + return -1; + } + } + + /* read in the rest of the packet */ + for (i = 1; i < proto[mtype][4]; ++i) + { + if (read(mfd, &buf[i], 1) != 1) + { + perror("get_ms_event: read"); + return -1; + } + /* check whether it's a data packet */ + if (mtype != P_PS2 && ((buf[i] & proto[mtype][2]) != proto[mtype][3] + || buf[i] == 0x80)) + goto restart; + } + } + else /* bus mouse */ + { + while ((i = read(mfd, buf, 3)) != 3 && errno == EAGAIN) + usleep(40000); + if (i != 3) + { + perror("get_ms_event: read"); + return -1; + } + } + +/* construct the event */ + switch (mtype) + { + case P_MS: /* Microsoft */ + default: + ev->ev_butstate = ((buf[0] & 0x20) >> 3) | ((buf[0] & 0x10) >> 4); + dx = (char)(((buf[0] & 0x03) << 6) | (buf[1] & 0x3F)); + dy = (char)(((buf[0] & 0x0C) << 4) | (buf[2] & 0x3F)); + break; + case P_SUN: /* Mouse Systems 3 byte as used in Sun workstations */ + ev->ev_butstate = (~buf[0]) & 0x07; + dx = (char)(buf[1]); + dy = -(char)(buf[2]); + break; + case P_MSC: /* Mouse Systems Corp (5 bytes, PC) */ + ev->ev_butstate = (~buf[0]) & 0x07; + dx = (char)(buf[1]) + (char)(buf[3]); + dy = - ((char)(buf[2]) + (char)(buf[4])); + break; + case P_MM: /* MM Series */ + case P_LOGI: /* Logitech */ + ev->ev_butstate = buf[0] & 0x07; + dx = (buf[0] & 0x10) ? buf[1] : - buf[1]; + dy = (buf[0] & 0x08) ? - buf[2] : buf[2]; + break; + case P_BM: /* BusMouse */ + ev->ev_butstate = (~buf[0]) & 0x07; + dx = (char)buf[1]; + dy = - (char)buf[2]; + break; + case P_PS2: /* PS/2 Mouse */ + ev->ev_butstate = 0; + if (buf[0] & 0x01) + ev->ev_butstate |= MS_BUTLEFT; + if (buf[0] & 0x02) + ev->ev_butstate |= MS_BUTRIGHT; + dx = (buf[0] & 0x10) ? buf[1]-256 : buf[1]; + dy = - ((buf[0] & 0x20) ? buf[2]-256 : buf[2]); + break; + } + + acc = (abs(ev->ev_dx) + abs(ev->ev_dy) > mdelta) ? maccel : 1; + ev->ev_dx = dx * acc; + ev->ev_dy = dy * acc; + x += ev->ev_dx; + y += ev->ev_dy; + limit(x, 0, mx, (int) (slack * mx / my)); + limit(y, 0, my, slack); + ev->ev_x = x; + ev->ev_y = y; + limit(ev->ev_x, 0, mx, -1); + limit(ev->ev_y, 0, my, -1); + if (dx || dy) + { + if (ev->ev_butstate) + ev->ev_code = MS_DRAG; + else + ev->ev_code = MS_MOVE; + } + else + { + if (ev->ev_butstate) + ev->ev_code = MS_BUTDOWN; + else + ev->ev_code = MS_BUTUP; + } + return 0; +} diff --git a/historic/selection/mouse.h b/historic/selection/mouse.h new file mode 100644 index 00000000..b8014dbd --- /dev/null +++ b/historic/selection/mouse.h @@ -0,0 +1,34 @@ +/* interface file for mouse driver */ +/* Andrew Haylett, 17th June 1993 */ + +#ifndef MOUSE_H +#define MOUSE_H + +#define MS_BUTLEFT 4 +#define MS_BUTMIDDLE 2 +#define MS_BUTRIGHT 1 + +typedef enum { + P_MS = 0, + P_SUN = 1, + P_MSC = 2, + P_MM = 3, + P_LOGI = 4, + P_BM = 5, + P_PS2 = 6 +} mouse_type; + +#define NR_TYPES 7 /* keep in step with mouse_type! */ + +struct ms_event { + enum { MS_NONE, MS_BUTUP, MS_BUTDOWN, MS_MOVE, MS_DRAG } ev_code; + char ev_butstate; + int ev_x, ev_y; + int ev_dx, ev_dy; +}; + +void ms_params(int argc, char *argv[]); +int ms_init(const int maxx, const int maxy); +int get_ms_event(struct ms_event *ev); + +#endif /* MOUSE_H */ diff --git a/historic/selection/selection.1 b/historic/selection/selection.1 new file mode 100644 index 00000000..450c9fab --- /dev/null +++ b/historic/selection/selection.1 @@ -0,0 +1,142 @@ +.\" +.\" selection.1 - the cut and paste utility for Linux virtual consoles +.\" +.\" Modified by faith@cs.unc.edu +.\" +.TH SELECTION 1 "20 November 1993" "Linux 0.99" "Linux Programmer's Manual" +.SH NAME +selection - the cut and paste utility for Linux virtual consoles +.SH SYNTAX +\fBselection [-a accel] [-b baud-rate] [-c l|m|r] [-d delta] [-m mouse-device] [-p l|m|r] [-s sample-rate] [-t mouse-type] [-r slack]\fR +.SH DESCRIPTION +\fBselection\fR is a utility that allows characters to be selected from the +current Linux virtual console using the mouse and pasted into the current +console. \fBselection\fR is normally invoked at boot time from /etc/rc.local, +and runs as a background process. +.SH OPTIONS +.IP \fB\-a\fP\fIaccel\fP +movements of more than \fIdelta\fP pixels are multiplied by \fIaccel\fP (default 2) +.IP \fB\-b\fP\fIbaud-rate\fP +set the baud rate of the mouse (default 1200 baud) +.IP \fB\-c\fP\fIl|m|r\fP +set the copy button to be the left, middle or right button (default left) +.IP \fB\-d\fP\fIdelta\fP +movements of more than \fIdelta\fP pixels are multiplied by \fIaccel\fP +(default 25) +.IP \fB\-m\fP\fImouse-device\fP +specify the mouse device (default /dev/mouse) +.IP \fB\-p\fP\fIl|m|r\fP +set the paste button to be the left, middle or right button (default right) +.IP \fB\-s\fP\fIsample-rate\fP +set the sample rate of the mouse (default 100) +.IP \fB\-t\fP\fImouse-type\fP +specify the mouse type (Microsoft = `ms', Mouse Systems Corp = `msc', +MM Series = `mm', Logitech = `logi', BusMouse = `bm', +MSC 3-bytes = `sun', PS/2 = `ps2'; default = ms) +.IP \fB\-w\fP\fIslack\fP +turn on wrap-around, specifying the amount of slack before the pointer +reappears at the other side of the screen (default off) +.SH OPERATION +To invoke the selection mechanism, press and release the copy button +(the meaning of the buttons may be set at startup as above). A highlighted +block will start moving around the screen, correlated with the movement of the +mouse. +.PP +Move the block to the first character of the selection, then press and hold +down the copy button. +.PP +Drag out the selection; the selected text will be highlighted. Then release +the copy button. You can take the end of the selection to before the start of +the selection if necessary. +.PP +Double-clicking the copy button while the highlighted block is on the +screen selects text by word boundaries; treble-clicking selects by entire +lines. If the button is held down after double- or treble-clicking, multiple +words or lines may be selected. A word consists of a set of alphanumeric +characters and underscores. +.PP +If a trailing space after the contents of a line is highlighted, and if there +is no other text on the remainder of the line, the rest of the line will be +selected automatically. If a number of lines are selected, highlighted +trailing spaces on each line will be removed from the selection buffer. +.PP +Pressing the paste button in any virtual console pastes the +selected text into the read queue of the associated tty. +.PP +Any output on the virtual console holding the selection will clear the +highlighted selection from the screen, to maintain integrity of the display, +although the contents of the paste buffer will be unaffected. +.PP +The selection mechanism is disabled if the controlling virtual console is +placed in graphics mode, for example when running X11, and is re-enabled when +text mode is resumed. (But see BUGS section below.) +.SH FILES +/dev/mouse - default mouse device +.br +/dev/console - current VC device +.SH DIAGNOSTICS +\fBselection\fR complains if any of the devices it requires cannot be located. +.SH BUGS +The size of the paste buffer is set at 2048 bytes by default. This may be +changed at compile time; consult the installation notes. +.PP +The selection mechanism doesn't work very well with graphics characters, or +indeed with any characters where a mapping between the typed character and +the displayed character is performed by the console driver. The selection +mechanism pastes into the input buffer the character codes as they are +displayed on the screen, not those originally typed in by the user. +.PP +Because of the way that the kernel bus mouse drivers work, allowing only one +process to have the mouse device open at once, \fBselection\fR cannot +co-exist with X11 using ATI XL, Logitech and Microsoft bus mice or with a +PS/2 mouse. The X server will not start while \fBselection\fR is running. +This problem is not present with serial mice. +.SH AUTHOR +.nf +Andrew Haylett +.SH ACKNOWLEDGEMENTS +.nf +Lefty patches originally suggested by: +.ti +4 +Sotiris C. Vassilopoulos +.br +Logitech patches from: +.ti +4 +Jim Winstead Jr +.br +Command line options based on those from: +.ti +4 +Peter Macdonald +.br +Patches for bus mouse from: +.br +.ti +4 +Erik Troan +.br +.ti +4 +Christoph Niemann +.br +.ti +4 +Koen Gadeyne +.br +Patches for PS/2 mouse from: +.br +.ti +4 +Hans D. Fink +.br +Patches for Sun mouse from: +.br +.ti +4 +Michael Haardt +.br +Run-time configurable mouse buttons suggested by: +.br +.ti +4 +Charlie Brady +.br +Setsid patches by: +.bt +.ti +4 +Rick Sladkey +.sp +Apologies to any contributors whose names I have omitted. diff --git a/historic/selection/selection.c b/historic/selection/selection.c new file mode 100644 index 00000000..058936fc --- /dev/null +++ b/historic/selection/selection.c @@ -0,0 +1,237 @@ +/* implement copying and pasting in Linux virtual consoles */ +/* Andrew Haylett, 17th June 1993 */ +/* Wed Feb 15 09:33:16 1995, faith@cs.unc.edu changed tty0 to console, since + most systems don't have a tty0 any more. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mouse.h" + +extern int ms_copy_button, ms_paste_button; + +static const int SCALE = 10; +static const long CLICK_INTERVAL = 250; /* msec */ +static const char *console = "/dev/console"; + +typedef enum { character = 0, word = 1, line = 2 } sel_mode; + +static int open_console(const int mode); +static void set_sel(const int xs, const int ys, const int xe, + const int ye, const sel_mode mode); +static void paste(void); +static long interval(const struct timeval *t1, const struct timeval *t2); +static int check_mode(void); + +int +main(int argc, char *argv[]) +{ + struct ms_event ev; + struct winsize win; + struct timeval tv1, tv2; + int xs, ys, xe, ye, x1, y1, fd, clicks = 0; + sel_mode mode; + + fd = open_console(O_RDONLY); + ioctl(fd, TIOCGWINSZ, &win); + close(fd); + if (! win.ws_col || ! win.ws_row) + { + fprintf(stderr, "selection: zero screen dimension, assuming 80x25.\n"); + win.ws_col = 80; + win.ws_row = 25; + } + + ms_params(argc, argv); + + if (ms_init(win.ws_col * SCALE - 1, win.ws_row * SCALE - 1)) + exit(1); + + if (fork() > 0) + exit(0); + setsid(); + + gettimeofday(&tv1, (struct timezone *)NULL); + +restart: + while (1) + { + if (check_mode()) + goto restart; + if (get_ms_event(&ev)) + exit(1); + if (ev.ev_butstate == ms_copy_button) + { + ++clicks; + gettimeofday(&tv2, (struct timezone *)NULL); + xs = ev.ev_x / SCALE + 1; + ys = ev.ev_y / SCALE + 1; + if (interval(&tv1, &tv2) < CLICK_INTERVAL && clicks == 1) + { + mode = word; + set_sel(xs, ys, xs, ys, mode); + } + else if (interval(&tv1, &tv2) < CLICK_INTERVAL && clicks == 2) + { + mode = line; + set_sel(xs, ys, xs, ys, mode); + } + else + { + mode = character; + clicks = 0; + do /* wait for left button up */ + { + if (check_mode()) + goto restart; + if (get_ms_event(&ev)) + exit(1); + } while (ev.ev_butstate); + x1 = y1 = 0; + do /* track start selection until left button down */ + { + xs = ev.ev_x / SCALE + 1; + ys = ev.ev_y / SCALE + 1; + if (xs != x1 || ys != y1) + { + set_sel(xs, ys, xs, ys, mode); + x1 = xs; y1 = ys; + } + if (check_mode()) + goto restart; + if (get_ms_event(&ev)) + exit(1); + } while (ev.ev_butstate != ms_copy_button); + } + x1 = y1 = 0; + gettimeofday(&tv1, (struct timezone *)NULL); + do /* track end selection until left button up */ + { + xe = ev.ev_x / SCALE + 1; + ye = ev.ev_y / SCALE + 1; + if (xe != x1 || ye != y1) + { + set_sel(xs, ys, xe, ye, mode); + x1 = xe; y1 = ye; + } + if (check_mode()) + goto restart; + if (get_ms_event(&ev)) + exit(1); + } while (ev.ev_butstate == ms_copy_button); + } else if (ev.ev_butstate == ms_paste_button) + { /* paste selection */ + paste(); + do /* wait for right button up */ + { + if (check_mode()) + goto restart; + if (get_ms_event(&ev)) + exit(1); + } while (ev.ev_butstate); + gettimeofday(&tv1, (struct timezone *)NULL); + clicks = 0; + } + } +} + +/* We have to keep opening and closing the console because (a) /dev/tty0 + changed its behaviour at some point such that the current VC is fixed + after the open(), rather than being re-evaluated at each write(), and (b) + because we seem to lose our grip on /dev/tty? after someone logs in if + this is run from /etc/rc. */ + +static int +open_console(const int mode) +{ + int fd; + + if ((fd = open(console, mode)) < 0) + { + perror("selection: open_console()"); + exit(1); + } + return fd; +} + +/* mark selected text on screen. */ +static void +set_sel(const int xs, const int ys, + const int xe, const int ye, const sel_mode mode) +{ + unsigned char buf[sizeof(char) + 5 * sizeof(short)]; + unsigned short *arg = (unsigned short *)(buf + 1); + int fd; + + buf[0] = 2; + + arg[0] = xs; + arg[1] = ys; + arg[2] = xe; + arg[3] = ye; + arg[4] = mode; + + fd = open_console(O_WRONLY); + if (ioctl(fd, TIOCLINUX, buf) < 0) + { + perror("selection: ioctl(..., TIOCLINUX, ...)"); + exit(1); + } + close(fd); +} + +/* paste contents of selection buffer into console. */ +static void +paste(void) +{ + char c = 3; + int fd; + + fd = open_console(O_WRONLY); + if (ioctl(fd, TIOCLINUX, &c) < 0) + { + perror("selection: ioctl(..., TIOCLINUX, ...)"); + exit(1); + } + close(fd); +} + +/* evaluate interval between times. */ +static long +interval(const struct timeval *t1, const struct timeval *t2) +{ + return (t2->tv_sec - t1->tv_sec) * 1000 + + (t2->tv_usec - t1->tv_usec) / 1000; +} + +/* Check whether console is in graphics mode; if so, wait until it isn't. */ +static int +check_mode(void) +{ + int fd, ch = 0; + long kd_mode; + + do + { + fd = open_console(O_RDONLY); + if (ioctl(fd, KDGETMODE, &kd_mode) < 0) + { + perror("selection: ioctl(..., KDGETMODE, ...)"); + exit(1); + } + close(fd); + if (kd_mode != KD_TEXT) + { + ++ch; + sleep(2); + } + } while (kd_mode != KD_TEXT); + return (ch > 0); +} diff --git a/historic/selection/test-mouse.c b/historic/selection/test-mouse.c new file mode 100644 index 00000000..fd80989b --- /dev/null +++ b/historic/selection/test-mouse.c @@ -0,0 +1,67 @@ +/* + * test-mouse: exercise rodent to test compatibility. + * Any button to draw asterisks of different + * colour. Left and right buttons (while mouse is stationary) to quit. + * Andrew Haylett, 17th June 1993 + */ + +#include +#include +#include +#include +#include + +#include "mouse.h" + +#define SCALE 10 + +int +main(int argc, char *argv[]) +{ + struct ms_event ev; + struct winsize win; + + ms_params(argc, argv); + ioctl(fileno(stdout), TIOCGWINSZ, &win); + if (! win.ws_col || ! win.ws_row) + { + fprintf(stderr, "selection: zero screen dimension: assuming 80x25.\n"); + win.ws_col = 80; + win.ws_row = 25; + } + + printf("\033[2J\033[%d;%dH", win.ws_row / 2, win.ws_col / 2); + fflush(stdout); + if (ms_init((win.ws_col + 1) * SCALE - 1, (win.ws_row + 1) * SCALE - 1)) + { + perror("ms_init"); + exit(1); + } + while (1) + { + if (get_ms_event(&ev)) + { + perror("get_ms_event"); + exit(1); + } + if (ev.ev_code == MS_BUTDOWN && ev.ev_butstate == (MS_BUTLEFT | MS_BUTRIGHT)) + { + printf("\033[;H\033[2J\033[m"); + exit(0); + } + else if (ev.ev_code == MS_MOVE || ev.ev_code == MS_DRAG) + { + printf("\033[%d;%dH", ev.ev_y / SCALE, ev.ev_x / SCALE); + if (ev.ev_code == MS_DRAG) + { + if (ev.ev_butstate == MS_BUTLEFT) + printf("\033[31m*\033[D"); /* red */ + else if (ev.ev_butstate == MS_BUTRIGHT) + printf("\033[35m*\033[D"); /* purple */ + else + printf("\033[34m*\033[D"); /* blue */ + } + } + fflush(stdout); + } +} diff --git a/historic/sync.c b/historic/sync.c new file mode 100644 index 00000000..76e0b622 --- /dev/null +++ b/historic/sync.c @@ -0,0 +1,14 @@ +/* + * sync.c - flush Linux filesystem buffers + * + * Copyright 1992 Linus Torvalds. + * This file may be redistributed as per the GNU copyright. + */ + +#include +#include + +int main(int argc, char **argv) { + sync(); + return 0; +} diff --git a/historic/update.8 b/historic/update.8 new file mode 100644 index 00000000..e6887a73 --- /dev/null +++ b/historic/update.8 @@ -0,0 +1,25 @@ +.\" Copyright 1992 Rickard E. Faith (faith@cs.unc.edu) +.\" May be distributed under the GNU General Public License +.TH UPDATE 8 "20 November 1993" "Linux 0.99" "Linux Programmer's Manual" +.SH NAME +update \- periodically flush Linux filesystem buffers +.SH SYNOPSIS +.B update [ interval ] +.SH DESCRIPTION +.B update +executes +.BR sync (2) +every +.I interval +seconds. By default, the +.I interval +is 30 seconds. It is generally started at boot time in +.IR /etc/rc . +.SH "SEE ALSO" +.BR sync (2), +.BR sync (8), +.BR init (8) +.SH AUTHOR +Linus Torvalds (torvalds@cs.helsinki.fi) +.br +With modifications by Rick Sladkey (jrs@world.std.com) diff --git a/historic/update.c b/historic/update.c new file mode 100644 index 00000000..0506df87 --- /dev/null +++ b/historic/update.c @@ -0,0 +1,45 @@ +/* + * update.c -- periodically sync the filesystems to disk + */ + +#include +#include +#include +#include + +void alarm_handler(int sig) +{ +} + +int main(int argc, char *argv[]) +{ + int i; + int interval; + struct sigaction sa; + sigset_t empty_set; + sigset_t alarm_set; + + interval = (argc > 1) ? atoi(argv[1]) : 30; + if (fork() > 0) + exit(0); + chdir("/"); + for (i = 0; i < OPEN_MAX; i++) + close(i); + setsid(); + sa.sa_handler = SIG_IGN; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + sa.sa_handler = alarm_handler; + sigaction(SIGALRM, &sa, NULL); + sigemptyset(&empty_set); + sigemptyset(&alarm_set); + sigaddset(&alarm_set, SIGALRM); + sigprocmask(SIG_BLOCK, &alarm_set, NULL); + for (;;) { + alarm(interval); + sigsuspend(&empty_set); + sync(); + } +} diff --git a/login-utils/Makefile b/login-utils/Makefile new file mode 100644 index 00000000..88e0b822 --- /dev/null +++ b/login-utils/Makefile @@ -0,0 +1,112 @@ +# Makefile -- Makefile for util-linux Linux utilities +# Created: Sat Dec 26 20:09:40 1992 +# Revised: Wed Feb 22 16:09:31 1995 by faith@cs.unc.edu +# Copyright 1992, 1993, 1994, 1995 Rickard E. Faith (faith@cs.unc.edu) +# +# Suggested changed from Bauke Jan Douma have been +# implemented to handle shadow and sysvinit systems + +include ../MCONFIG + +# Where to put man pages? + +MAN1= last.1 mesg.1 wall.1 + +MAN1.NONSHADOW= chfn.1 chsh.1 login.1 newgrp.1 passwd.1 + +MAN8= agetty.8 fastboot.8 fasthalt.8 halt.8 reboot.8 simpleinit.8 \ + shutdown.8 + +MAN8.NONSHADOW= vipw.8 + +# Where to put binaries? +# See the "install" rule for the links. . . + +SBIN= agetty simpleinit shutdown + +BIN.NONSHADOW= login + +USRBIN= last mesg wall + +USRBIN.NONSHADOW= chfn chsh newgrp passwd + +USRSBIN.NONSHADOW= vipw + +PASSWDDIR= /usr/bin + +ifeq "$(HAVE_SHADOW)" "no" +WHAT_TO_BUILD:=$(WHAT_TO_BUILD) all-nonshadow +WHAT_TO_INSTALL:=$(WHAT_TO_INSTALL) install-nonshadow +endif + +ifeq "$(HAVE_SYSVINIT)" "no" +WHAT_TO_BUILD:=$(WHAT_TO_BUILD) all-nonsysvinit +WHAT_TO_INSTALL:=$(WHAT_TO_INSTALL) install-nonsysvinit +endif + +all: $(WHAT_TO_BUILD) +all-nonshadow: $(BIN.NONSHADOW) $(USRBIN.NONSHADOW) $(USRSBIN.NONSHADOW) +all-nonsysvinit: $(USRBIN) $(SBIN) + +%.o: %.c + $(CC) -c $(CFLAGS) $< -o $@ + +# Rules for everything else + +agetty.o: $(BSD)/pathnames.h +agetty: agetty.o +chfn: chfn.o setpwnam.o +chsh: chsh.o setpwnam.o +last.o: $(BSD)/pathnames.h +last: last.o $(BSD)/getopt.o +login.o: $(BSD)/pathnames.h +login: login.o +mesg: mesg.o +newgrp.o: $(BSD)/pathnames.h +newgrp: newgrp.o +passwd: passwd.o islocal.o +shutdown.o: $(BSD)/pathnames.h +shutdown: shutdown.o +simpleinit.o: $(BSD)/pathnames.h +simpleinit: simpleinit.o +vipw.o: $(BSD)/pathnames.h +vipw: vipw.o +wall: wall.o ttymsg.o + +install: all $(WHAT_TO_INSTALL) + +install-nonshadow: + $(INSTALLDIR) $(SBINDIR) $(BINDIR) $(USRBINDIR) + $(INSTALLBIN) $(BIN.NONSHADOW) $(BINDIR) + $(INSTALLBIN) $(USRBIN.NONSHADOW) $(USRBINDIR) + $(INSTALLBIN) $(USRSBIN.NONSHADOW) $(USRSBINDIR) + $(INSTALLDIR) $(MAN1DIR) $(MAN8DIR) + $(INSTALLMAN) $(MAN1.NONSHADOW) $(MAN1DIR) + $(INSTALLMAN) $(MAN8.NONSHADOW) $(MAN8DIR) + chown root $(USRBINDIR)/chsh + chmod u+s $(USRBINDIR)/chsh + chown root $(USRBINDIR)/chfn + chmod u+s $(USRBINDIR)/chfn + chown root $(USRBINDIR)/newgrp + chmod u+s $(USRBINDIR)/newgrp + chown root $(PASSWDDIR)/passwd + chmod u+s $(PASSWDDIR)/passwd + chown root $(BINDIR)/login + chmod u+s $(BINDIR)/login + +install-nonsysvinit: + $(INSTALLDIR) $(SBINDIR) $(BINDIR) $(USRBINDIR) + $(INSTALLBIN) $(SBIN) $(SBINDIR) + (cd $(SHUTDOWNDIR); ln -sf shutdown reboot) + (cd $(SHUTDOWNDIR); ln -sf shutdown fastboot) + (cd $(SHUTDOWNDIR); ln -sf shutdown halt) + (cd $(SHUTDOWNDIR); ln -sf shutdown fasthalt) + $(INSTALLBIN) $(USRBIN) $(USRBINDIR) + $(INSTALLDIR) $(MAN1DIR) $(MAN8DIR) + $(INSTALLMAN) $(MAN1) $(MAN1DIR) + $(INSTALLMAN) $(MAN8) $(MAN8DIR) + +.PHONY: clean +clean: + -rm -f *.o *~ core $(SBIN) $(BIN) $(BIN.NONSHADOW) $(USRBIN) \ + $(USRBIN.NONSHADOW) $(USRSBIN.NONSHADOW) diff --git a/login-utils/README.admutil b/login-utils/README.admutil new file mode 100644 index 00000000..789252d1 --- /dev/null +++ b/login-utils/README.admutil @@ -0,0 +1,162 @@ +README file for the admutils V1.14 for Linux. + +See installation instructions at the bottom. Currently the latest versions +of this software is maintained at ftp://ftp.daimi.aau.dk/pub/linux/poe/ + +LICENSE: +This software is distributed as is without any warranty what so ever. +With respect to copyrights it is covered by the GNU Public License. + +Version 1.14 (12-Feb-95): + Added options -l, -y, -i to last.c. See last.man + +Version 1.13d (26-Jan-95): + Added some comments on request from Rik Faith. Compiled succesfully + on Linux 1.1.73, GCC 2.5.8, libc 4.5.26 + +Version 1.13c (6-Dec-94): + New versions of passwd and chsh due to Alvaro Martinez Echevarria + , so they will coexist with YP/NIS + passwords. + +Version 1.13b (7-Nov-94): + Use fgets() + atoi() in chsh.c instead of scanf(). + +Version 1.12 (17-Sep-94): + Rik Faith provided patches for passwd.c to let non-alphabetics count + as digits as well, allows more obscure passwords. + + Applied patches from Dave Gentzel + to prevent dereferencing a NULL pointer, and turn off accounting + in shutdown.c + +Version 1.11 (18-Aug-94): + Finally got around to making it a non-alpha version. Just a + little cleanup in Makefile + +Version 1.10b (8-Jun-94): + David A. Holland made me aware of a + security leak in passwd and chsh. /etc/ptmp could be forced to + be world-writeable. Fixed by hardwiring an umask of 022 into + passwd and chsh. + + Vesa Ruokonen sent me a new pathnames.h + file that shouldn't conflict with paths.h. + + Cleaned the source a bit for -Wall + +Version 1.10a (31-May-94): + Vesa Ruokonen provided a patch for + passwd.c such that it will work for multiple usernames for + the same uid. I mimicked his actions on chsh.c. In both cases + I added a check to ensure that even if utmp is hacked, one can + only change the password for users with the same uid. + +Version 1.9 (9-Feb-94): + Vesa Ruokonen suggested that newgrp should support passwords in + /etc/group. It now does. I mostly rewrote newgrp to make it + cleaner. + +Version 1.8 (19-Jan-94): + Rik Faith provided several patches, especially for passwd.c and + some man-pages. + +Version 1.7 (3-Nov-93): changes since 1.6 + Shutdown can now be used as a login shell. I forget who sent me the + patch. Example /etc/passwd entry: + + shutdown:dLbVbIMx7bVHw:0:0:Stopper:/:/etc/halt + + The package should now be prepared to have shutdown in /sbin as well + as in /etc. utmp and wtmp are allowed in /usr/adm too. Both things + are configurable in the Makefile. + + Olaf Flebbe provided a + patch for chsh.c to make it work. + + This version is built under linux 0.99.13 with gcc 2.4.3 and + libc 4.4.1 + +Version 1.6 (1-Jun-93) + Shutdown now looks more like shutdown on SunOS, but not quite. Most + of this was done by Scott Telford (s.telford@ed.ac.uk), but I + butchered his patches somewhat. This version was built under Linux + 0.99.9 with GCC 2.3.3 and libc 4.3.3. + + "make install" will now install shutdown in /etc instead of /usr/bin + +Version 1.5 (13-Dec-92) + This version is tested and built under Linux 0.98P6 with gcc-2.2.2d7 + You will have a hard time making it work with the older compilers and + libraries. + + Su is now deprecated. I believe that the GNU/FSF version is better. + +CONTENTS. +last - A new and better last command, a port from BSD done by + Michael Haardt. + I put a couple of if's in so LOGIN_PROCESS entries in wtmp + are not printed. + +chsh - CHangeSHell changes the shell entry in the passwd file. + Written from scratch by me. + +passwd - Changes the password in the passwd file. + Also done from scratch by me. + +su - A su(1) command by me. + +newgrp - Sets the gid if possible, ala su(1), written by Michael + Haardt. + +shutdown - Shuts down linux. Supports timed shutdowns, and sends + warnings to all users currently logged in. It then + kills all processes and unmounts file-systems etc. + + Shutdown also doubles as halt and reboot commands. + + Shutdown leaves the file /etc/nologin behind after shutdown, + it is wise to have a "rm -f /etc/nologin" in ones /etc/rc + + Shutdown now supports a -s switch, that works in connection + with the init program in poeigl-1.7 or later, so a singleuser + reboot is possible. + + Rick Sladkey provided patches for better + umounting code, needed in connection with NFS. + + Remy Card provided patches for support for + fastboot/fasthalt. These create a /fastboot file on shutdown, + and /etc/rc may check for the existance of this file, to + optionally skip fsck. + +example.rc An example of an /etc/rc file. Edit it to suit your own setup. + +ctrlaltdel - Sets the behaviour of the Ctrl-Alt-Del combination. + "ctrlaltdel hard" makes the key-combination instantly reboot + the machine without syncing the disk or anything. This may + very well corrupt the data on the disk. + + "ctrlaltdel soft" makes the key-combination send a SIGINT to + the init process. Such a command would typically be in /etc/rc. + For this to make sense you must run the init from the + poeigl-1.4 package or later. The System V compatible init in + this package won't reboot the machine when it gets a SIGINT. + Linux version 0.96b-PL1 or later is also needed for this + feature to work. + +init is gone as of V1.5, it was outdated and buggy. If you want a +SYSV compatible init get the newest one from Mike Smoorenburg, called +sysvinit.tar.Z + +INSTALLATION. +Simply do a + + make + +and then (optionally) as root: + + make install + + + - Peter (poe@daimi.aau.dk) diff --git a/login-utils/README.getty b/login-utils/README.getty new file mode 100644 index 00000000..4e32faa0 --- /dev/null +++ b/login-utils/README.getty @@ -0,0 +1,26 @@ +@(#) README 1.8 9/1/91 23:32:37 + +This is a SYSV/SunOS4 getty program with useful features for hardwired +and dial-in tty lines. + +- The program adapts the tty modes to parity bits and to erase, kill +end-of-line and upper-case characters when it reads a login name. + +- The baud rate can be established by BREAK character processing and by +parsing the CONNECT status messages from Hayes-compatible modems. + +Other features: RTS/CTS flow control (SunOS4, suggested by John Harkin, +), alternate login program, does not use /etc/gettytab +or /etc/gettydefs. + +The program works without modification under System V release 2 and +SunOS 4.1/4.1.1. It probably also works with later System V releases. + +In the Makefile you will have to specify whether diagnostics should be +reported through the syslog daemon; the alternative is that all error +reports go directly to /dev/console. + +The command-line interface was cleaned up a bit; it is slightly +incompatible with earlier agetty versions. + + Wietse Venema (wietse@wzv.win.tue.nl) diff --git a/login-utils/README.poeigl b/login-utils/README.poeigl new file mode 100644 index 00000000..f6f8933c --- /dev/null +++ b/login-utils/README.poeigl @@ -0,0 +1,440 @@ +README for init/getty/login, by poe@daimi.aau.dk + +This package contains init, getty, and login programs for Linux. +Additional utilities included are: hostname, who, write, wall, users +domainname, hostid, cage and mesg. + +Most of this software has been contributed by others, I basically just +ported the things to Linux. + +About installation: See the bottom of this file. Check the Makefile! +Be sure you know what you are doing! You may well be able to lock +yourself out from your machine. + +If you are uncertain whether you got the latest version, check out + + ftp://ftp.daimi.aau.dk:/pub/linux/poe/ + +Version 1.32 + Login now logs the ip-address of the connecting host to utmp as it + should. + +Version 1.31b (2-Feb-95): + Daniel Quinlan and Ross Biro + suggested a patch to login.c that allows for + shell scripts in the shell field of /etc/passwd, so one can now + have (as a line in /etc/passwd): + bye::1000:1000:Outlogger:/bin:echo Bye + Logging in as "bye" with no password simply echoes Bye on the screen. + This has applications for pppd/slip. + +Version 1.31a (28-Oct-94): + Scott Telford provided a patch for simpleinit, so executing reboot + from singleuser mode won't partially execute /etc/rc before + the reboot. + +Version 1.30 (17-Sep-94): + tobias@server.et-inf.fho-emden.de (Peter Tobias) has made a more + advanced hostname command that understands some options such as + -f for FQDN etc. I'll not duplicate his work. Use his hostname + package if you wish. + + svm@kozmix.xs4all.nl (Sander van Malssen) provided more features + for the /etc/issue file in agetty. \U and \u now expand to the + number of current users. + + It is now possible to state the value of TERM on the agetty command + line. This was also provided by Sander. + + This has been built under Linux 1.1.42 with gcc 2.5.8 and libc 4.5.26. + +Version 1.29 (18-Aug-94): + Finally got around to making a real version after the numerous + alpha versions of 1.28. Scott Telford provided + a patch for write(1) to make it look more like BSD write. + + Fixed login so that the .hushlogin feature works even with real + protective users mounted via NFS (ie. where root can't access + the user's .hushlogin file). + + Cleaned up the code to make -Wall bearable. + +Version 1.28c (21-Jul-94): + Rik Faith reminded me that agetty should use the syslog + facility. It now does. + +Version 1.28b (30-May-94): + On suggestion from Jeremy Fitzhardinge + I added -- as option delimiter on args passed from agetty to + login. Fixes -froot hole for other login programs. The login + program in this package never had that hole. + +Version 1.28a (16-May-94): + bill@goshawk.lanl.gov provided a couple of patches, one fixing + terminal setup in agetty, and reboot is now supposed to be + in /sbin according to FSSTND. + +Version 1.27 (10-May-94): + Changed login.c, so all bad login attempts are logged, and added + usertty security feature. See about.usertty for an explanation. + There's no longer a limit of 20 chars in the TERM environment + variable. Suggested by Nicolai Langfeldt + + Added #ifdef HAVE_QUOTA around quota checks. Enable them if + you have quota stuff in your libraries and kernel. + Also re-enabled set/getpriority() calls as we now have them, + and have had for a long time... + + Now wtmp is locked and unlocked around writes to avoid mangling. + Due to Jaakko Hyv{tti . + + Wrt. agetty: A \o in /etc/issue now inserts the domainname, as + set by domainname(1). Sander van Malssen provided this. + This is being used under Linux 1.1.9 + + Beefed up the agetty.8 man-page to describe the /etc/issue + options. Added man-pages for wall, cage, who. + +Version 1.26 alpha (25-Apr-94): + Added patch from Bill Reynolds to + simpleinit, so it will drop into single user if /etc/rc + fails, eg. from fsck. + +Version 1.25 (9-Feb-94): + Agetty should now work with the Linux 0.99pl15a kernel. + ECHOCTL and ECHOPRT are no longer set in the termios struct. + Also made agetty accept both "tty baudrate" and "baudrate tty" + arguments. + +Version 1.24 (23-Jan-94): changes since 1.22 + Christian von Roques provided a patch + that cleans up the handling of the -L option on agetty. + Rik Faith enhanced several man-pages... + +Version 1.23 (11-Dec-93): changes since 1.21 + Mitchum DSouza provided the hostid(1) code. It needs libc 4.4.4 or + later and a Linux 0.99.14 kernel or later. It can set and print + the world unique hostid of the machine. This may be used in + connection with commercial software licenses. God forbid! + I added the -v option, and munged the code a bit, so don't blame + Mitch if you don't like it. + + I made the "cage" program. Using this as a shell in the passwd + file, enables one to let users log into a chroot'ed environment. + For those that have modem logins and are concerned about security. + Read the source for further info. + + "who am i" now works. + + The login program works with Yellow Pages (aka NIS) simply by + linking with an appropriate library containing a proper version + of getpwnam() and friends. + +Version 1.21 (30-Oct-93): changes since 1.20 + In simpleinit.c: The boottime wtmp record is now written *after* + /etc/rc is run, to put a correct timestamp on it. + Daniel Thumim suggested this fix. + + The source and Makefile is prepared for optional installation of + binaries in /sbin instead of /etc, and logfiles in /usr/adm instead + of /etc. See and change the Makefile to suit your preferences. + Rik Faith and Stephen Tweedie inspired this change. + +Version 1.20 (30-Jul-93): changes since 1.17: + Versions 1.18 and 1.19 were never made publically available. + Agetty now supports a -L switch that makes it force the CLOCAL flag. + This is useful if you have a local terminal attached with a partly + wired serial cable that does not pass on the Carrier Detect signal. + + There's a domainname program like the hostname program; contributed + by Lars Wirzenius. + + Simpleinit will now write a REBOOT record to wtmp on boot up. Time- + zone support is now optional in simpleinit. Both of these patches + were made by Scott Telford . + + This is for Linux 0.99.11 or later. + +Version 1.17 (19-May-93): changes since 1.16: + Login, simpleinit and write should now work with shadow passwords + too. See the Makefile. Thanks to Anders Buch who let me have an + account on his SLS based Linux box on the Internet, so I could test + this. I should also thank jmorriso@rflab.ee.ubc.ca (John Paul Morrison) + who sent me the shadow patch to login.c + +Version 1.16 (24-Apr-93): changes since 1.15a: + Simpleinit now clears the utmp entry associated with the pid's that + it reaps if there is one. A few are still using simpleinit and this + was a popular demand. It also appends an entry to wtmp + +Version 1.15a (15-Mar-93): changes since 1.13a: + junio@shadow.twinsun.com (Jun Hamano) sent me a one-line fix + for occasional mangled issue-output from agetty. + +Version 1.13a (2-Mar-93): changes since 1.12a: + With the new LILO (0.9), there are more than one possible arg + to init, so Werner Almesberger + suggested that a loop over argv[] was made in boot_single() in + simpleinit.c + +Version 1.12a (24-Feb-93): changes since 1.11: + This is for Linux 0.99.6 or later. Built with gcc 2.3.3 and libc4.2 + jrs@world.std.com (Rick Sladkey) told me that the setenv("TZ",..) + in login.c did more harm than good, so I commented it out. + +Version 1.11a (16-Feb-93): changes since 1.9a: + This is for Linux 0.99.5 or later. + Anthony Rumble made me avare that + the patches for vhangup() from Steven S. Dick didn't quite work, + so I changed it. + + Linus Torvalds provided another patch relating to vhangup, since + in newer Linuxen vhangup() doesn't really close all files, so we + can't just open the tty's again. + +Version 1.9a (18-Jan-93): changes since 1.8a: + Rick Faith sent me man-pages for most of the untilities in this + package. They are now included. + + Steven S. Dick sent me a patch for login.c + so DTR won't drop during vhangup() on a modemline. + + This is completely untested!! I haven't even had the time to + compile it yet. + +Version 1.8a (13-Dec-92): changes since 1.7: + This is for Linux 0.98.6 or later. Compiles with gcc2.2.2d7 and libc4.1 + + Bettered write/wall after fix from I forget who. Now wall can have + commandline args. + + Fixed bug in who.c + + Patched simpleinit.c with patch from Ed Carp, so it sets the timezone + from /etc/TZ. Should probably by be /etc/timezone. + + Sander Van Malssen provided a patch + for getty, so it can understand certain escapecodes in /etc/issue. + + I hacked up a very simple substitute for a syslog() call, to try out + the logging. If you have a real syslog() and syslogd then use that! + + The special vhangup.c file is out, it's in the official libc by now. + (and even in the libc that I have :-) + + who, and write are now deprecated, get the better ones from one of + the GNU packages, shellutils I think. + + Some people think that the simple init provided in this package is too + spartan, if you think the same, then get the SYSV compatible init + from Miquel van Smoorenburg + Simpleinit will probably be deprecated in the future. + +Version 1.7: 26-Oct-92 changes since 1.6: + This is for Linux 0.97PL4 or later. + + Thanks to Werner Almesberger, init now has support for a + singleuser mode. + + Login now supports the -h option, used in connection + with TCP/IP. (rlogin/telnet) + + Getty writes an entry to /etc/wtmp when started, so last won't report + "still logged in" for tty's that have not been logged into since + the last user of that tty logged out. This patch was inspired by + Mitchum DSouza. To gain the full benefit of this, get the newest + last from the admutils-1.4.tar.Z package or later. + +Version 1.6 (29-Aug-92): changes since 1.5: + This is for Linux 0.97P1+ or later. + + Login now uses the newly implemented vhangup() sys-call, to prevent + snooping on the tty. + An alternative getpass() function is now provided with login, because + I was told that the old one in libc didn't work with telnet and + or rlogin. I don't have a network or a kernel with TCP/IP so I haven't + tested the new one with telnet, but it is derived from BSD sources + that are supposed to work with networking. + +Version 1.5 (12-Aug-92): changes since 1.4 + This is for Linux 0.97 or later, and has been built with gcc2.2.2 + + This release just puts in a few bugfixes in login.c and simpleinit.c + +Version 1.4 (4-Jul-92): changes since 1.3: + This is for Linux 0.96b, and has been built and tested with gcc 2.2.2. + + Init now handles the SIGINT signal. When init gets a SIGINT it will + call /usr/bin/reboot and thereby gently reboot the machine. This + makes sense because after Linux 0.96B-PL1 the key-combination + Ctrl-Alt-Del may send a SIGINT to init instead of booting the + machine the hard way without syncing or anything. + + You may want to get the admutils-1.1 package which includes a program + that will instruct the kernel to use the "gentle-reboot" procedure. + +Version 1.3 (14-Jun-92): changes since 1.2: + This is for Linux 0.96A. + + The ioctl(TIOCSWINSZ) has been removed from login.c because it now + works :-). + + login.c now supports a lastlog database. + + Several programs and pieces of source that were included in the 1.2 + package has been *removed* as they are incorporated into the new + libc. Other omitted parts such as last(1) has been replaced by + better versions, and can be found in the admutils package. + + Agetty is now called getty and will be placed in /etc. + + A few changes has been made to make it possible to compile the + stuff with GCC 2.x. + +Version 1.2 (28-Feb-92): changes since 1.1: + This is for Linux 0.12. + + A couple of problems with simpleinit.c has been solved, thanks to + Humberto Zuazaga. So now init groks comments in /etc/inittab, and + handles the HUP and TSTP signals properly. + + I added two small scripts to the distribution: users and mesg. + + TERM is now carried through from /etc/inittab all the way to the + shell. Console tty's are special-cased, so the termcap entry in + /etc/inittab is overridden by the setting given at boot-time. + This requires a different patch to the kernel than that distributed + with version 1.1 + + Login no more sends superfluous chars from a password to the + shell. It also properly prints a NL after the password. + + Agetty didn't set the erase character properly, it does now. + + A few extra defines has been added to utmp.h + + Several netters helped discover the bugs in 1.1. Thanks to them + all. + +Version 1.1 (released 19-Feb-92): Changes since 1.0: + A bug in simpleinit.c has been fixed, thanks to Pietro Castelli. + The definition of the ut_line field has been changed to track the + USG standard more closely, we now strip "/dev/" off the front. + Thanks to: Douglas E. Quale and Stephen Gallimore. + + I have added a getlogin.c library routine, and a write(1) command. + I removed the qpl-init stuff. If people want to use it, they should + get it from the source. I don't want to hack on it anymore. + + A couple of people reported problems with getty having problems + with serial terminals. That was correct. I borrowed a null-modem + from Tommy Thorn, and now the problems should be fixed. It seems + that there is kept a lot of garbage in the serial buffers, flush + them and it works like a charm. Getty does an ioctl(0, TCFLSH, 2) + for this. + + The write.c code now doubles as code for a wall(1) program. + +Description of the various files: + +login.c The login program. This is a portation of BSD login, first + to HP-UX 8.0 by Michael Glad (glad@daimi.aau.dk), and + to Linux (initially to 0.12) by me. + +who.c A simple who(1) util. to list utmp. Done by me. + You may prefer the GNU who util. with more options + and features. + +hostname.c A hostname(1) command to get and set the hostname. I did + this too. + +domainname.c Like hostname, only reads out or sets the domainname. + +agetty.c The getty program. From comp.sources.misc, by W.Z. Venema. + Hacked a bit by me. + +simpleinit.c A simple init program, written by me. Uses /etc/inittab + + A "kill -HUP" to init makes it re-read /etc/inittab. + A "kill -TSTP" to init makes it stop spawning gettys on the + ttys. A second "kill -TSTP" starts it again. + A kill -INT to init makes it attempt a reboot of the machine. + this works in connection with kernel support for softboot + when Ctrl-Alt-Del is pressed. + + Init will start up in singleuser mode if /etc/singleboot + exists at boottime, or if it is given an argument of "single" + via eg. LILO. If /etc/securesingle exists it will ask for the + root password before starting single user. + +write.c A write(1) command, used to pass messages between users + at different terminals. This code doubles as code for + a wall(1) command. Make a symlink: /usr/bin/wall -> + /usr/bin/write for this. + +mesg A tiny shellscript, so you can avoid that other people write + to your shell. + +users Another script that uses awk(1) and tr(1) to process the + output from who(1) into a one-liner. + If you don't have awk, but have Perl, this does the same: + + who | perl -ane 'print "$F[0] "'; echo "" + +pathnames.h: + Header. + +param.h + Header, extended with getdtablesize() macro, should go + in /usr/include/sys + +Building. +--------- +A "make all" should do. At least it does for me. + +Installation: +------------- + +login should go in /bin, if you don't like this change + pathnames.h and recompile at least agetty. + +getty, init Put them in SBINDIR + +who, hostname, write, wall, mesg, users: + /usr/bin + +securetty login needs this in /etc, defines which ttys that root + can login on. This should *never* include ttys{1,2} + +inittab the simpleinit code needs this in /etc. Note that the syntax + of /etc/inittab has little to do with the syntax of a real + SysV inittab. Edit this one for your local setup. + +shells The chsh program will use this if it's placed in /etc. It + defines the valid shell-programs. Have one abs. path on + each line. + +You can also do a "make install" as root, but don't just do it because I +say so, check the Makefile first. + +"Make install" will install only the new binaries, and not motd, inittab, +securetty and issue. To install these configuration files, do a +"make Install". + +Getty requires a /dev/console to write errors to. I just made it a symlink +to /dev/tty1. Because of a bug in the tty driver this errorlogging may +cause the shell on tty1 to logout. + +Getty will print the contents of /etc/issue if it's present before asking +for username. Login will print the contents of /etc/motd after successful +login. Login doesn't print /etc/motd, and doesn't check for mail if +~/.hushlogin is present and world readable. + +If /etc/nologin is present then login will print its contents and disallow +any logins except root. +It might be a good idea to have a "rm -f /etc/nologin" line in one's +/etc/rc file. + +If /etc/securetty is present it defines which tty's that root can login on. + + - Peter (poe@daimi.aau.dk) diff --git a/login-utils/agetty.8 b/login-utils/agetty.8 new file mode 100644 index 00000000..3f3cf6ad --- /dev/null +++ b/login-utils/agetty.8 @@ -0,0 +1,241 @@ +.TH AGETTY 8 +.ad +.fi +.SH NAME +agetty \- alternative Linux getty +.SH SYNOPSIS +.na +.nf +agetty [-ihL] [-l login_program] [-m] [-t timeout] port baud_rate,... [term] +agetty [-ihL] [-l login_program] [-m] [-t timeout] baud_rate,... port [term] +.SH DESCRIPTION +.ad +.fi +\fIagetty\fP opens a tty port, prompts for a login name and invokes +the /bin/login command. It is normally invoked by \fIinit(8)\fP. + +\fIagetty\fP has several \fInon-standard\fP features that are useful +for hard-wired and for dial-in lines: +.IP o +Adapts the tty settings to parity bits and to erase, kill, +end-of-line and uppercase characters when it reads a login name. +The program can handle 7-bit characters with even, odd, none or space +parity, and 8-bit characters with no parity. The following special +characters are recognized: @ and Control-U (kill); #, DEL and +back space (erase); carriage return and line feed (end of line). +.IP o +Optionally deduces the baud rate from the CONNECT messages produced by +Hayes(tm)-compatible modems. +.IP o +Optionally does not hang up when it is given an already opened line +(useful for call-back applications). +.IP o +Optionally does not display the contents of the \fI/etc/issue\fP file +(System V only). +.IP o +Optionally invokes a non-standard login program instead of +\fI/bin/login\fP. +.IP o +Optionally turns on hard-ware flow control +.IP o +Optionally forces the line to be local with no need for carrier detect. +.PP +This program does not use the \fI/etc/gettydefs\fP (System V) or +\fI/etc/gettytab\fP (SunOS 4) files. +.SH ARGUMENTS +.na +.nf +.fi +.ad +.TP +port +A path name relative to the \fI/dev\fP directory. If a "-" is +specified, \fIagetty\fP assumes that its standard input is +already connected to a tty port and that a connection to a +remote user has already been established. +.sp +Under System V, a "-" \fIport\fP argument should be preceded +by a "--". +.TP +baud_rate,... +A comma-separated list of one or more baud rates. Each time +\fIagetty\fP receives a BREAK character it advances through +the list, which is treated as if it were circular. +.sp +Baud rates should be specified in descending order, so that the +null character (Ctrl-@) can also be used for baud rate switching. +.TP +term +The value to be used for the TERM environment variable. This overrides +whatever init(8) may have set, and is inherited by login and the shell. +.SH OPTIONS +.na +.nf +.fi +.ad +.TP +-h +Enable hardware (RTS/CTS) flow control. It is left up to the +application to disable software (XON/XOFF) flow protocol where +appropriate. +.TP +-i +Do not display the contents of \fI/etc/issue\fP before writing the +login prompt. Terminals or communications hardware may become confused +when receiving lots of text at the wrong baud rate; dial-up scripts +may fail if the login prompt is preceded by too much text. +.TP +-l login_program +Invoke the specified \fIlogin_program\fP instead of /bin/login. +This allows the use of a non-standard login program (for example, +one that asks for a dial-up password or that uses a different +password file). +.TP +-m +Try to extract the baud rate the \fIconnect\fP status message +produced by some Hayes(tm)-compatible modems. These status +messages are of the form: "". +\fIagetty\fP assumes that the modem emits its status message at +the same speed as specified with (the first) \fIbaud_rate\fP value +on the command line. +.sp +Since the \fI-m\fP feature may fail on heavily-loaded systems, +you still should enable BREAK processing by enumerating all +expected baud rates on the command line. +.TP +-t timeout +Terminate if no user name could be read within \fItimeout\fP +seconds. This option should probably not be used with hard-wired +lines. +.TP +-L +Force the line to be local line with no need for carrier detect. This can +be useful when you have locally attached terminal where the serial line +does not set the carrier detect signal. + +.SH EXAMPLES +.na +.nf +This section shows sample entries for the \fI/etc/inittab\fP file. + +For a hard-wired line: +.ti +5 +tty1:con80x60:/sbin/agetty 9600 tty1 + +For a dial-in line with a 9600/2400/1200 baud modem: +.ti +5 +ttyS1:dumb:/sbin/agetty -mt60 ttyS1 9600,2400,1200 + +These examples assume you use the simpleinit(8) init program for Linux. +If you use a SysV like init (does /etc/inittab mention "respawn"?), refer +to the appropriate manual page. + +.SH ISSUE ESCAPES +The \fI/etc/issue\fP file may contain certain escape codes to display the +system name, date and time etc. All escape codes consist of a backslash +(\\) immediately followed by one of the letters explained below. + +.TP +b +Insert the baudrate of the current line. +.TP +d +Insert the current date. +.TP +s +Insert the system name, the name of the operating system. +.TP +l +Insert the name of the current tty line. +.TP +m +Insert the architecture identifier of the machine, eg. i486 +.TP +n +Insert the nodename of the machine, also known as the hostname. +.TP +o +Insert the domainname of the machine. +.TP +r +Insert the release number of the OS, eg. 1.1.9. +.TP +t +Insert the current time. +.TP +u +Insert the number of current users logged in. +.TP +U +Insert the string "1 user" or " users" where is the number of current +users logged in. +.TP +v +Insert the version of the OS, eg. the build-date etc. +.TP +Example: On my system, the following \fI/etc/issue\fP file: + +.na +.nf +.ti +.5 +This is \\n.\\o (\\s \\m \\r) \\t +.TP +displays as + +.ti +.5 +This is thingol.orcan.dk (Linux i386 1.1.9) 18:29:30 + +.fi + +.SH FILES +.na +.nf +/etc/utmp, the system status file (System V only). +/etc/issue, printed before the login prompt (System V only). +/dev/console, problem reports (if syslog(3) is not used). +/etc/inittab (Linux simpleinit(8) configuration file). +.SH BUGS +.ad +.fi +The baud-rate detection feature (the \fI-m\fP option) requires that +\fIagetty\fP be scheduled soon enough after completion of a dial-in +call (within 30 ms with modems that talk at 2400 baud). For robustness, +always use the \fI-m\fP option in combination with a multiple baud +rate command-line argument, so that BREAK processing is enabled. + +The text in the /etc/issue file and the login prompt +are always output with 7-bit characters and space parity. + +The baud-rate detection feature (the \fI-m\fP option) requires that +the modem emits its status message \fIafter\fP raising the DCD line. +.SH DIAGNOSTICS +.ad +.fi +Depending on how the program was configured, all diagnostics are +written to the console device or reported via the syslog(3) facility. +Error messages are produced if the \fIport\fP argument does not +specify a terminal device; if there is no /etc/utmp entry for the +current process (System V only); and so on. +.SH AUTHOR(S) +.na +.nf +W.Z. Venema +Eindhoven University of Technology +Department of Mathematics and Computer Science +Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands + +Peter Orbaek +Linux port. + +.SH CREATION DATE +.na +.nf +Sat Nov 25 22:51:05 MET 1989 +.SH LAST MODIFICATION +.na +.nf +91/09/01 23:22:00 +.SH VERSION/RELEASE +.na +.nf +1.29 diff --git a/login-utils/agetty.c b/login-utils/agetty.c new file mode 100644 index 00000000..a8cd45db --- /dev/null +++ b/login-utils/agetty.c @@ -0,0 +1,1099 @@ +/* agetty.c - another getty program for Linux. By W. Z. Venema 1989 + Ported to Linux by Peter Orbaek + This program is freely distributable. The entire man-page used to + be here. Now read the real man-page agetty.8 instead. +*/ + +#ifndef lint +char sccsid[] = "@(#) agetty.c 1.29 9/1/91 23:22:00"; +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef linux +#include "pathnames.h" +#include +#define USE_SYSLOG +#endif + + /* If USE_SYSLOG is undefined all diagnostics go directly to /dev/console. */ + +#ifdef USE_SYSLOG +#include +extern void closelog(); +#endif + + /* + * Some heuristics to find out what environment we are in: if it is not + * System V, assume it is SunOS 4. + */ + +#ifdef LOGIN_PROCESS /* defined in System V utmp.h */ +#define SYSV_STYLE /* select System V style getty */ +#endif + + /* + * Things you may want to modify. + * + * If ISSUE is not defined, agetty will never display the contents of the + * /etc/issue file. You will not want to spit out large "issue" files at the + * wrong baud rate. Relevant for System V only. + * + * You may disagree with the default line-editing etc. characters defined + * below. Note, however, that DEL cannot be used for interrupt generation + * and for line editing at the same time. + */ + +#ifdef SYSV_STYLE +#define ISSUE "/etc/issue" /* displayed before the login prompt */ +#include +#include +#endif + +#define LOGIN " login: " /* login prompt */ + +/* Some shorthands for control characters. */ + +#define CTL(x) (x ^ 0100) /* Assumes ASCII dialect */ +#define CR CTL('M') /* carriage return */ +#define NL CTL('J') /* line feed */ +#define BS CTL('H') /* back space */ +#define DEL CTL('?') /* delete */ + +/* Defaults for line-editing etc. characters; you may want to change this. */ + +#define DEF_ERASE DEL /* default erase character */ +#define DEF_INTR CTL('C') /* default interrupt character */ +#define DEF_QUIT CTL('\\') /* default quit char */ +#define DEF_KILL CTL('U') /* default kill char */ +#define DEF_EOF CTL('D') /* default EOF char */ +#define DEF_EOL 0 +#define DEF_SWITCH 0 /* default switch char */ + + /* + * SunOS 4.1.1 termio is broken. We must use the termios stuff instead, + * because the termio -> termios translation does not clear the termios + * CIBAUD bits. Therefore, the tty driver would sometimes report that input + * baud rate != output baud rate. I did not notice that problem with SunOS + * 4.1. We will use termios where available, and termio otherwise. + */ + +/* linux 0.12 termio is broken too, if we use it c_cc[VERASE] isn't set + properly, but all is well if we use termios?! */ + +#ifdef TCGETS +#undef TCGETA +#undef TCSETA +#undef TCSETAW +#define termio termios +#define TCGETA TCGETS +#define TCSETA TCSETS +#define TCSETAW TCSETSW +#endif + + /* + * This program tries to not use the standard-i/o library. This keeps the + * executable small on systems that do not have shared libraries (System V + * Release <3). + */ + +#define BUFSIZ 1024 + + /* + * When multiple baud rates are specified on the command line, the first one + * we will try is the first one specified. + */ + +#define FIRST_SPEED 0 + +/* Storage for command-line options. */ + +#define MAX_SPEED 10 /* max. nr. of baud rates */ + +struct options { + int flags; /* toggle switches, see below */ + int timeout; /* time-out period */ + char *login; /* login program */ + int numspeed; /* number of baud rates to try */ + int speeds[MAX_SPEED]; /* baud rates to be tried */ + char *tty; /* name of tty */ +}; + +#define F_PARSE (1<<0) /* process modem status messages */ +#define F_ISSUE (1<<1) /* display /etc/issue */ +#define F_RTSCTS (1<<2) /* enable RTS/CTS flow control */ +#define F_LOCAL (1<<3) /* force local */ + +/* Storage for things detected while the login name was read. */ + +struct chardata { + int erase; /* erase character */ + int kill; /* kill character */ + int eol; /* end-of-line character */ + int parity; /* what parity did we see */ + int capslock; /* upper case without lower case */ +}; + +/* Initial values for the above. */ + +struct chardata init_chardata = { + DEF_ERASE, /* default erase character */ + DEF_KILL, /* default kill character */ + 0, /* always filled in at runtime */ + 0, /* space parity */ + 0, /* always filled in at runtime */ +}; + +#define P_(s) () +void parse_args P_((int argc, char **argv, struct options *op)); +void parse_speeds P_((struct options *op, char *arg)); +void update_utmp P_((char *line)); +void open_tty P_((char *tty, struct termio *tp, int local)); +void termio_init P_((struct termio *tp, int speed, int local)); +void auto_baud P_((struct termio *tp)); +void do_prompt P_((struct options *op, struct termio *tp)); +void next_speed P_((struct termio *tp, struct options *op)); +char *get_logname P_((struct options *op, struct chardata *cp, struct termio *tp)); +void termio_final P_((struct options *op, struct termio *tp, struct chardata *cp)); +int caps_lock P_((char *s)); +int bcode P_((char *s)); +void usage P_((void)); +void error P_((int va_alist)); +#undef P_ + +/* The following is used for understandable diagnostics. */ + +char *progname; + +/* ... */ +#ifdef DEBUGGING +#define debug(s) fprintf(dbf,s); fflush(dbf) +FILE *dbf; +#else +#define debug(s) /* nothing */ +#endif + +int +main(argc, argv) + int argc; + char **argv; +{ + char *logname; /* login name, given to /bin/login */ + char *get_logname(); + struct chardata chardata; /* set by get_logname() */ + struct termio termio; /* terminal mode bits */ + static struct options options = { + F_ISSUE, /* show /etc/issue (SYSV_STYLE) */ + 0, /* no timeout */ + _PATH_LOGIN, /* default login program */ + 0, /* no baud rates known yet */ + }; + + /* The BSD-style init command passes us a useless process name. */ + +#ifdef SYSV_STYLE + progname = argv[0]; +#else + progname = "agetty"; +#endif + +#ifdef DEBUGGING + dbf = fopen("/dev/tty1", "w"); + + { int i; + + for(i = 1; i < argc; i++) { + debug(argv[i]); + } + } +#endif + + /* Parse command-line arguments. */ + + parse_args(argc, argv, &options); + +#ifdef linux + setsid(); +#endif + + /* Update the utmp file. */ + +#ifdef SYSV_STYLE + update_utmp(options.tty); +#endif + + /* Open the tty as standard { input, output, error }. */ + open_tty(options.tty, &termio, options.flags & F_LOCAL); + +#ifdef linux + { + int iv; + + iv = getpid(); + (void) ioctl(0, TIOCSPGRP, &iv); + } +#endif + /* Initialize the termio settings (raw mode, eight-bit, blocking i/o). */ + + termio_init(&termio, options.speeds[FIRST_SPEED], options.flags & F_LOCAL); + + /* Optionally detect the baud rate from the modem status message. */ + + if (options.flags & F_PARSE) + auto_baud(&termio); + + /* Set the optional timer. */ + + if (options.timeout) + (void) alarm((unsigned) options.timeout); + + /* Read the login name. */ + + while ((logname = get_logname(&options, &chardata, &termio)) == 0) + next_speed(&termio, &options); + + /* Disable timer. */ + + if (options.timeout) + (void) alarm(0); + + /* Finalize the termio settings. */ + + termio_final(&options, &termio, &chardata); + + /* Now the newline character should be properly written. */ + + (void) write(1, "\n", 1); + + /* Let the login program take care of password validation. */ + + (void) execl(options.login, options.login, "--", logname, (char *) 0); + error("%s: can't exec %s: %m", options.tty, options.login); + exit(0); /* quiet GCC */ +} + +/* parse-args - parse command-line arguments */ + +void +parse_args(argc, argv, op) + int argc; + char **argv; + struct options *op; +{ + extern char *optarg; /* getopt */ + extern int optind; /* getopt */ + int c; + + while (isascii(c = getopt(argc, argv, "Lhil:mt:"))) { + switch (c) { + case 'L': /* force local */ + op->flags |= F_LOCAL; + break; + case 'h': /* enable h/w flow control */ + op->flags |= F_RTSCTS; + break; + case 'i': /* do not show /etc/issue */ + op->flags &= ~F_ISSUE; + break; + case 'l': + op->login = optarg; /* non-default login program */ + break; + case 'm': /* parse modem status message */ + op->flags |= F_PARSE; + break; + case 't': /* time out */ + if ((op->timeout = atoi(optarg)) <= 0) + error("bad timeout value: %s", optarg); + break; + default: + usage(); + } + } + debug("after getopt loop\n"); + if (argc < optind + 2) /* check parameter count */ + usage(); + + /* we loosen up a bit and accept both "baudrate tty" and "tty baudrate" */ + if('0' <= argv[optind][0] && argv[optind][0] <= '9') { + /* a number first, assume it's a speed (BSD style) */ + parse_speeds(op, argv[optind++]); /* baud rate(s) */ + op->tty = argv[optind]; /* tty name */ + } else { + op->tty = argv[optind++]; /* tty name */ + parse_speeds(op, argv[optind]); /* baud rate(s) */ + } + + optind++; + if (argc > optind && argv[optind]) + setenv ("TERM", argv[optind], 1); + + debug("exiting parseargs\n"); +} + +/* parse_speeds - parse alternate baud rates */ + +void +parse_speeds(op, arg) + struct options *op; + char *arg; +{ + char *strtok(); + char *cp; + + debug("entered parse_speeds\n"); + for (cp = strtok(arg, ","); cp != 0; cp = strtok((char *) 0, ",")) { + if ((op->speeds[op->numspeed++] = bcode(cp)) <= 0) + error("bad speed: %s", cp); + if (op->numspeed > MAX_SPEED) + error("too many alternate speeds"); + } + debug("exiting parsespeeds\n"); +} + +#ifdef SYSV_STYLE + +/* update_utmp - update our utmp entry */ +void +update_utmp(line) + char *line; +{ + struct utmp ut; + long ut_size = sizeof(ut); /* avoid nonsense */ + int ut_fd; + int mypid = getpid(); + long time(); + long lseek(); + char *strncpy(); + + /* + * The utmp file holds miscellaneous information about things started by + * /etc/init and other system-related events. Our purpose is to update + * the utmp entry for the current process, in particular the process type + * and the tty line we are listening to. Return successfully only if the + * utmp file can be opened for update, and if we are able to find our + * entry in the utmp file. + */ + +#ifdef linux + utmpname(_PATH_UTMP); + memset(&ut, 0, sizeof(ut)); + (void) strncpy(ut.ut_user, "LOGIN", sizeof(ut.ut_user)); + (void) strncpy(ut.ut_line, line, sizeof(ut.ut_line)); + (void) strncpy(ut.ut_id, line + 3, sizeof(ut.ut_id)); + (void) time(&ut.ut_time); + ut.ut_type = LOGIN_PROCESS; + ut.ut_pid = mypid; + + pututline(&ut); + endutent(); + + if((ut_fd = open(_PATH_WTMP, O_APPEND|O_WRONLY)) >= 0) { + flock(ut_fd, LOCK_EX); + write(ut_fd, &ut, sizeof(ut)); + flock(ut_fd, LOCK_UN); + close(ut_fd); + } +#else + if ((ut_fd = open(UTMP_FILE, 2)) < 0) { + error("%s: open for update: %m", UTMP_FILE); + } else { + while (read(ut_fd, (char *) &ut, sizeof(ut)) == sizeof(ut)) { + if (ut.ut_type == INIT_PROCESS && ut.ut_pid == mypid) { + ut.ut_type = LOGIN_PROCESS; + ut.ut_time = time((long *) 0); + (void) strncpy(ut.ut_name, "LOGIN", sizeof(ut.ut_name)); + (void) strncpy(ut.ut_line, line, sizeof(ut.ut_line)); + (void) lseek(ut_fd, -ut_size, 1); + (void) write(ut_fd, (char *) &ut, sizeof(ut)); + (void) close(ut_fd); + return; + } + } + error("%s: no utmp entry", line); + } +#endif /* linux */ +} + +#endif + +/* open_tty - set up tty as standard { input, output, error } */ +void +open_tty(tty, tp, local) + char *tty; + struct termio *tp; + int local; +{ + /* Get rid of the present standard { output, error} if any. */ + + (void) close(1); + (void) close(2); + errno = 0; /* ignore above errors */ + + /* Set up new standard input, unless we are given an already opened port. */ + + if (strcmp(tty, "-")) { + struct stat st; + + /* Sanity checks... */ + + if (chdir("/dev")) + error("/dev: chdir() failed: %m"); + if (stat(tty, &st) < 0) + error("/dev/%s: %m", tty); + if ((st.st_mode & S_IFMT) != S_IFCHR) + error("/dev/%s: not a character device", tty); + + /* Open the tty as standard input. */ + + (void) close(0); + errno = 0; /* ignore close(2) errors */ + + if (open(tty, (local ? O_RDWR|O_NONBLOCK : O_RDWR), 0) != 0) + error("/dev/%s: cannot open as standard input: %m", tty); + + } else { + + /* + * Standard input should already be connected to an open port. Make + * sure it is open for read/write. + */ + + if ((fcntl(0, F_GETFL, 0) & O_RDWR) != O_RDWR) + error("%s: not open for read/write", tty); + } + + /* Set up standard output and standard error file descriptors. */ + + if (dup(0) != 1 || dup(0) != 2) /* set up stdout and stderr */ + error("%s: dup problem: %m", tty); /* we have a problem */ + + /* + * The following ioctl will fail if stdin is not a tty, but also when + * there is noise on the modem control lines. In the latter case, the + * common course of action is (1) fix your cables (2) give the modem more + * time to properly reset after hanging up. SunOS users can achieve (2) + * by patching the SunOS kernel variable "zsadtrlow" to a larger value; + * 5 seconds seems to be a good value. + */ + + if (ioctl(0, TCGETA, tp) < 0) + error("%s: ioctl: %m", tty); + + /* + * It seems to be a terminal. Set proper protections and ownership. Mode + * 0622 is suitable for SYSV <4 because /bin/login does not change + * protections. SunOS 4 login will change the protections to 0620 (write + * access for group tty) after the login has succeeded. + */ + + (void) chown(tty, 0, 0); /* root, sys */ + (void) chmod(tty, 0622); /* crw--w--w- */ + errno = 0; /* ignore above errors */ +} + +/* termio_init - initialize termio settings */ + +char gbuf[1024]; +char area[1024]; + +void +termio_init(tp, speed, local) + struct termio *tp; + int speed; + int local; +{ + + /* + * Initial termio settings: 8-bit characters, raw-mode, blocking i/o. + * Special characters are set after we have read the login name; all + * reads will be done in raw mode anyway. Errors will be dealt with + * lateron. + */ +#ifdef linux + /* flush input and output queues, important for modems! */ + (void) ioctl(0, TCFLSH, 2); +#endif + + tp->c_cflag = CS8 | HUPCL | CREAD | speed; + if (local) { + tp->c_cflag |= CLOCAL; + } + + tp->c_iflag = tp->c_lflag = tp->c_oflag = tp->c_line = 0; + tp->c_cc[VMIN] = 1; + tp->c_cc[VTIME] = 0; + (void) ioctl(0, TCSETA, tp); + if (local) { + (void) fcntl(0, F_SETFL, fcntl(0, F_GETFL, 0) & ~O_NDELAY); + } + debug("term_io 2\n"); +} + +/* auto_baud - extract baud rate from modem status message */ +void +auto_baud(tp) + struct termio *tp; +{ + int speed; + int vmin; + unsigned iflag; + char buf[BUFSIZ]; + char *bp; + int nread; + + /* + * This works only if the modem produces its status code AFTER raising + * the DCD line, and if the computer is fast enough to set the proper + * baud rate before the message has gone by. We expect a message of the + * following format: + * + * + * + * The number is interpreted as the baud rate of the incoming call. If the + * modem does not tell us the baud rate within one second, we will keep + * using the current baud rate. It is advisable to enable BREAK + * processing (comma-separated list of baud rates) if the processing of + * modem status messages is enabled. + */ + + /* + * Use 7-bit characters, don't block if input queue is empty. Errors will + * be dealt with lateron. + */ + + iflag = tp->c_iflag; + tp->c_iflag |= ISTRIP; /* enable 8th-bit stripping */ + vmin = tp->c_cc[VMIN]; + tp->c_cc[VMIN] = 0; /* don't block if queue empty */ + (void) ioctl(0, TCSETA, tp); + + /* + * Wait for a while, then read everything the modem has said so far and + * try to extract the speed of the dial-in call. + */ + + (void) sleep(1); + if ((nread = read(0, buf, sizeof(buf) - 1)) > 0) { + buf[nread] = '\0'; + for (bp = buf; bp < buf + nread; bp++) { + if (isascii(*bp) && isdigit(*bp)) { + if (speed = bcode(bp)) { + tp->c_cflag &= ~CBAUD; + tp->c_cflag |= speed; + } + break; + } + } + } + /* Restore terminal settings. Errors will be dealt with lateron. */ + + tp->c_iflag = iflag; + tp->c_cc[VMIN] = vmin; + (void) ioctl(0, TCSETA, tp); +} + +/* do_prompt - show login prompt, optionally preceded by /etc/issue contents */ +void +do_prompt(op, tp) + struct options *op; + struct termio *tp; +{ +#ifdef ISSUE + FILE *fd; + int oflag; + char c; + struct utsname uts; + + (void) uname(&uts); +#endif + + (void) write(1, "\r\n", 2); /* start a new line */ +#ifdef ISSUE /* optional: show /etc/issue */ + if ((op->flags & F_ISSUE) && (fd = fopen(ISSUE, "r"))) { + oflag = tp->c_oflag; /* save current setting */ + tp->c_oflag |= (ONLCR | OPOST); /* map NL in output to CR-NL */ + (void) ioctl(0, TCSETAW, tp); + + + while ((c = getc(fd)) != EOF) + { + if (c == '\\') + { + c = getc(fd); + + switch (c) + { + case 's': + (void) printf ("%s", uts.sysname); + break; + + case 'n': + (void) printf ("%s", uts.nodename); + break; + + case 'r': + (void) printf ("%s", uts.release); + break; + + case 'v': + (void) printf ("%s", uts.version); + break; + + case 'm': + (void) printf ("%s", uts.machine); + break; + + case 'o': + (void) printf ("%s", uts.domainname); + break; + + case 'd': + case 't': + { + char *weekday[] = { "Sun", "Mon", "Tue", "Wed", "Thu", + "Fri", "Sat" }; + char *month[] = { "Jan", "Feb", "Mar", "Apr", "May", + "Jun", "Jul", "Aug", "Sep", "Oct", + "Nov", "Dec" }; + time_t now; + struct tm *tm; + + (void) time (&now); + tm = localtime(&now); + + if (c == 'd') + (void) printf ("%s %s %d %d", + weekday[tm->tm_wday], month[tm->tm_mon], + tm->tm_mday, + tm->tm_year < 70 ? tm->tm_year + 2000 : + tm->tm_year + 1900); + else + (void) printf ("%02d:%02d:%02d", + tm->tm_hour, tm->tm_min, tm->tm_sec); + + break; + } + + case 'l': + (void) printf ("%s", op->tty); + break; + + case 'b': + { + char *zpeedz[] = { "0", "50", "75", "110", "134.5", + "150", "200", "300", "600", "1200", + "1800", "2400", "4800", "9600", + "19200", "38400" }; + + (void) printf ("%s", zpeedz[tp->c_cflag & CBAUD]); + break; + } + case 'u': + case 'U': + { + int users = 0; + struct utmp *ut; + setutent(); + while (ut = getutent()) + if (ut->ut_type == USER_PROCESS) + users++; + endutent(); + printf ("%d", users); + if (c == 'U') + printf (" user%s", users == 1 ? "" : "s"); + break; + } + default: + (void) putchar(c); + } + } + else + (void) putchar(c); + } + fflush(stdout); + + tp->c_oflag = oflag; /* restore settings */ + (void) ioctl(0, TCSETAW, tp); /* wait till output is gone */ + (void) fclose(fd); + } +#endif +#ifdef linux + { + char hn[MAXHOSTNAMELEN+1]; + + (void) gethostname(hn, MAXHOSTNAMELEN); + write(1, hn, strlen(hn)); + } +#endif + (void) write(1, LOGIN, sizeof(LOGIN) - 1); /* always show login prompt */ +} + +/* next_speed - select next baud rate */ +void +next_speed(tp, op) + struct termio *tp; + struct options *op; +{ + static int baud_index = FIRST_SPEED;/* current speed index */ + + baud_index = (baud_index + 1) % op->numspeed; + tp->c_cflag &= ~CBAUD; + tp->c_cflag |= op->speeds[baud_index]; + (void) ioctl(0, TCSETA, tp); +} + +/* get_logname - get user name, establish parity, speed, erase, kill, eol */ + +char *get_logname(op, cp, tp) + struct options *op; + struct chardata *cp; + struct termio *tp; +{ + char logname[BUFSIZ]; + char *bp; + char c; /* input character, full eight bits */ + char ascval; /* low 7 bits of input character */ + int bits; /* # of "1" bits per character */ + int mask; /* mask with 1 bit up */ + static char *erase[] = { /* backspace-space-backspace */ + "\010\040\010", /* space parity */ + "\010\040\010", /* odd parity */ + "\210\240\210", /* even parity */ + "\210\240\210", /* no parity */ + }; + + /* Initialize kill, erase, parity etc. (also after switching speeds). */ + + *cp = init_chardata; + + /* Flush pending input (esp. after parsing or switching the baud rate). */ + + (void) sleep(1); + (void) ioctl(0, TCFLSH, (struct termio *) 0); + + /* Prompt for and read a login name. */ + + for (*logname = 0; *logname == 0; /* void */ ) { + + /* Write issue file and prompt, with "parity" bit == 0. */ + + do_prompt(op, tp); + + /* Read name, watch for break, parity, erase, kill, end-of-line. */ + + for (bp = logname, cp->eol = 0; cp->eol == 0; /* void */ ) { + + /* Do not report trivial EINTR/EIO errors. */ + + if (read(0, &c, 1) < 1) { + if (errno == EINTR || errno == EIO) + exit(0); + error("%s: read: %m", op->tty); + } + /* Do BREAK handling elsewhere. */ + + if ((c == 0) && op->numspeed > 1) + return (0); + + /* Do parity bit handling. */ + + if (c != (ascval = (c & 0177))) { /* "parity" bit on ? */ + for (bits = 1, mask = 1; mask & 0177; mask <<= 1) + if (mask & ascval) + bits++; /* count "1" bits */ + cp->parity |= ((bits & 1) ? 1 : 2); + } + /* Do erase, kill and end-of-line processing. */ + + switch (ascval) { + case CR: + case NL: + *bp = 0; /* terminate logname */ + cp->eol = ascval; /* set end-of-line char */ + break; + case BS: + case DEL: + case '#': + cp->erase = ascval; /* set erase character */ + if (bp > logname) { + (void) write(1, erase[cp->parity], 3); + bp--; + } + break; + case CTL('U'): + case '@': + cp->kill = ascval; /* set kill character */ + while (bp > logname) { + (void) write(1, erase[cp->parity], 3); + bp--; + } + break; + case CTL('D'): + exit(0); + default: + if (!isascii(ascval) || !isprint(ascval)) { + /* ignore garbage characters */ ; + } else if (bp - logname >= sizeof(logname) - 1) { + error("%s: input overrun", op->tty); + } else { + (void) write(1, &c, 1); /* echo the character */ + *bp++ = ascval; /* and store it */ + } + break; + } + } + } + /* Handle names with upper case and no lower case. */ + + if (cp->capslock = caps_lock(logname)) { + for (bp = logname; *bp; bp++) + if (isupper(*bp)) + *bp = tolower(*bp); /* map name to lower case */ + } + return (logname); +} + +/* termio_final - set the final tty mode bits */ +void +termio_final(op, tp, cp) + struct options *op; + struct termio *tp; + struct chardata *cp; +{ + /* General terminal-independent stuff. */ + + tp->c_iflag |= IXON | IXOFF; /* 2-way flow control */ + tp->c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK| ECHOKE; + /* no longer| ECHOCTL | ECHOPRT*/ + tp->c_oflag |= OPOST; + /* tp->c_cflag = 0; */ + tp->c_cc[VINTR] = DEF_INTR; /* default interrupt */ + tp->c_cc[VQUIT] = DEF_QUIT; /* default quit */ + tp->c_cc[VEOF] = DEF_EOF; /* default EOF character */ + tp->c_cc[VEOL] = DEF_EOL; +#ifdef linux + tp->c_cc[VSWTC] = DEF_SWITCH; /* default switch character */ +#else + tp->c_cc[VSWTCH] = DEF_SWITCH; /* default switch character */ +#endif + + /* Account for special characters seen in input. */ + + if (cp->eol == CR) { + tp->c_iflag |= ICRNL; /* map CR in input to NL */ + tp->c_oflag |= ONLCR; /* map NL in output to CR-NL */ + } + tp->c_cc[VERASE] = cp->erase; /* set erase character */ + tp->c_cc[VKILL] = cp->kill; /* set kill character */ + + /* Account for the presence or absence of parity bits in input. */ + + switch (cp->parity) { + case 0: /* space (always 0) parity */ + break; + case 1: /* odd parity */ + tp->c_cflag |= PARODD; + /* FALLTHROUGH */ + case 2: /* even parity */ + tp->c_cflag |= PARENB; + tp->c_iflag |= INPCK | ISTRIP; + /* FALLTHROUGH */ + case (1 | 2): /* no parity bit */ + tp->c_cflag &= ~CSIZE; + tp->c_cflag |= CS7; + break; + } + /* Account for upper case without lower case. */ + + if (cp->capslock) { + tp->c_iflag |= IUCLC; + tp->c_lflag |= XCASE; + tp->c_oflag |= OLCUC; + } + /* Optionally enable hardware flow control */ + +#ifdef CRTSCTS + if (op->flags & F_RTSCTS) + tp->c_cflag |= CRTSCTS; +#endif + + /* Finally, make the new settings effective */ + + if (ioctl(0, TCSETA, tp) < 0) + error("%s: ioctl: TCSETA: %m", op->tty); +} + +/* caps_lock - string contains upper case without lower case */ +int +caps_lock(s) + char *s; +{ + int capslock; + + for (capslock = 0; *s; s++) { + if (islower(*s)) + return (0); + if (capslock == 0) + capslock = isupper(*s); + } + return (capslock); +} + +/* bcode - convert speed string to speed code; return 0 on failure */ +int +bcode(s) + char *s; +{ + struct Speedtab { + long speed; + int code; + }; + static struct Speedtab speedtab[] = { + 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, +#ifdef B19200 + 19200, B19200, +#endif +#ifdef B38400 + 38400, B38400, +#endif +#ifdef EXTA + 19200, EXTA, +#endif +#ifdef EXTB + 38400, EXTB, +#endif + 0, 0, + }; + struct Speedtab *sp; + long speed = atol(s); + + for (sp = speedtab; sp->speed; sp++) + if (sp->speed == speed) + return (sp->code); + return (0); +} + +/* usage - explain */ + +void +usage() +{ +#if defined(SYSV_STYLE) && !defined(linux) + static char msg[] = + "[-i] [-l login_program] [-m] [-t timeout] line baud_rate,..."; +#else + static char msg[] = + "[-h] [-l login_program] [-m] [-t timeout] baud_rate,... line"; +#endif + + error("usage: %s %s", progname, msg); +} + +/* error - report errors to console or syslog; only understands %s and %m */ + +#define str2cpy(b,s1,s2) strcat(strcpy(b,s1),s2) + +/* VARARGS */ +void +error(va_alist) + va_dcl +{ + va_list ap; + char *fmt; +#ifndef USE_SYSLOG + int fd; +#endif + char buf[BUFSIZ]; + char *bp; + + char *strcpy(); + char *strcat(); + + /* + * If the diagnostic is reported via syslog(3), the process name is + * automatically prepended to the message. If we write directly to + * /dev/console, we must prepend the process name ourselves. + */ + +#ifdef USE_SYSLOG + buf[0] = '\0'; + bp = buf; +#else + (void) str2cpy(buf, progname, ": "); + bp = buf + strlen(buf); +#endif + + /* + * %s expansion is done by hand. On a System V Release 2 system without + * shared libraries and without syslog(3), linking with the the stdio + * library would make the program three times as big... + * + * %m expansion is done here as well. Too bad syslog(3) does not have a + * vsprintf() like interface. + */ + + va_start(ap); + fmt = va_arg(ap, char *); + while (*fmt) { + if (strncmp(fmt, "%s", 2) == 0) { + (void) strcpy(bp, va_arg(ap, char *)); + bp += strlen(bp); + fmt += 2; + } else if (strncmp(fmt, "%m", 2) == 0) { + (void) strcpy(bp, sys_errlist[errno]); + bp += strlen(bp); + fmt += 2; + } else { + *bp++ = *fmt++; + } + } + *bp = 0; + va_end(ap); + + /* + * Write the diagnostic directly to /dev/console if we do not use the + * syslog(3) facility. + */ + +#ifdef USE_SYSLOG + (void) openlog(progname, LOG_PID, LOG_AUTH); + (void) syslog(LOG_ERR, "%s", buf); + closelog(); +#else + /* Terminate with CR-LF since the console mode is unknown. */ + (void) strcat(bp, "\r\n"); + if ((fd = open("/dev/console", 1)) >= 0) { + (void) write(fd, buf, strlen(buf)); + (void) close(fd); + } +#endif + (void) sleep((unsigned) 10); /* be kind to init(8) */ + exit(1); +} diff --git a/login-utils/chfn.1 b/login-utils/chfn.1 new file mode 100644 index 00000000..9be9fff0 --- /dev/null +++ b/login-utils/chfn.1 @@ -0,0 +1,66 @@ +.\" +.\" chfn.1 -- change your finger information +.\" (c) 1994 by salvatore valente +.\" +.\" this program is free software. you can redistribute it and +.\" modify it under the terms of the gnu general public license. +.\" there is no warranty. +.\" +.\" faith +.\" 1.1.1.1 +.\" 1995/02/22 19:09:24 +.\" +.TH CHFN 1 "October 13 1994" "chfn" "Linux Reference Manual" +.SH NAME +chfn \- change your finger information +.SH SYNOPSIS +.B chfn +[\ \-f\ full-name\ ] [\ \-o\ office\ ] [\ \-p\ office-phone\ ] +[\ \-h\ home-phone\ ] [\ \-u\ ] [\ \-v\ ] [\ username\ ] +.SH DESCRIPTION +.B chfn +is used to change your finger information. This information is +stored in the +.I /etc/passwd +file, and is displayed by the +.B finger +program. The Linux +.B finger +command will display four pieces of information that can be changed by +.B chfn +: your real name, your work room and phone, and your home phone. +.SS COMMAND LINE +Any of the four pieces of information can be specified on the command +line. If no information is given on the command line, +.B chfn +enters interactive mode. +.SS INTERACTIVE MODE +In interactive mode, +.B chfn +will prompt for each field. At a prompt, you can enter the new information, +or just press return to leave the field unchanged. Enter the keyword +"none" to make the field blank. +.SH OPTIONS +.TP +.I "\-f, \-\-full-name" +Specify your real name. +.TP +.I "\-o, \-\-office" +Specify your office room number. +.TP +.I "\-p, \-\-office-phone" +Specify your office phone number. +.TP +.I "\-h, \-\-home-phone" +Specify your home phone number. +.TP +.I "\-u, \-\-help" +Print a usage message and exit. +.TP +.I "-v, \-\-version" +Print version information and exit. +.SH "SEE ALSO" +.BR finger (1), +.BR passwd (5) +.SH AUTHOR +Salvatore Valente diff --git a/login-utils/chfn.c b/login-utils/chfn.c new file mode 100644 index 00000000..2effa85d --- /dev/null +++ b/login-utils/chfn.c @@ -0,0 +1,414 @@ +/* + * chfn.c -- change your finger information + * (c) 1994 by salvatore valente + * + * this program is free software. you can redistribute it and + * modify it under the terms of the gnu general public license. + * there is no warranty. + * + * faith + * 1.1.1.1 + * 1995/02/22 19:09:24 + * + */ + +#define _POSIX_SOURCE 1 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef P +#if __STDC__ +#define P(foo) foo +#else +#define P(foo) () +#endif + +typedef unsigned char boolean; +#define false 0 +#define true 1 + +static char *version_string = "chfn 0.9 beta"; +static char *whoami; + +static char buf[1024]; + +struct finfo { + struct passwd *pw; + char *username; + char *full_name; + char *office; + char *office_phone; + char *home_phone; + char *other; +}; + +static boolean parse_argv P((int argc, char *argv[], struct finfo *pinfo)); +static void usage P((FILE *fp)); +static void parse_passwd P((struct passwd *pw, struct finfo *pinfo)); +static void ask_info P((struct finfo *oldfp, struct finfo *newfp)); +static char *prompt P((char *question, char *def_val)); +static int check_gecos_string P((char *msg, char *gecos)); +static boolean set_changed_data P((struct finfo *oldfp, struct finfo *newfp)); +static int save_new_data P((struct finfo *pinfo)); +static void *xmalloc P((int bytes)); +extern int strcasecmp P((char *, char *)); +extern int setpwnam P((struct passwd *pwd)); +#define memzero(ptr, size) memset((char *) ptr, 0, size) + +int main (argc, argv) + int argc; + char *argv[]; +{ + char *cp; + uid_t uid; + struct finfo oldf, newf; + boolean interactive; + int status; + extern int errno; + + /* whoami is the program name for error messages */ + whoami = argv[0]; + if (! whoami) whoami = "chfn"; + for (cp = whoami; *cp; cp++) + if (*cp == '/') whoami = cp + 1; + + umask (022); + + /* + * "oldf" contains the users original finger information. + * "newf" contains the changed finger information, and contains NULL + * in fields that haven't been changed. + * in the end, "newf" is folded into "oldf". + * the reason the new finger information is not put _immediately_ into + * "oldf" is that on the command line, new finger information can + * be specified before we know what user the information is being + * specified for. + */ + uid = getuid (); + memzero (&oldf, sizeof (oldf)); + memzero (&newf, sizeof (newf)); + + interactive = parse_argv (argc, argv, &newf); + if (! newf.username) { + parse_passwd (getpwuid (uid), &oldf); + if (! oldf.username) { + fprintf (stderr, "%s: you (user %d) don't exist.\n", whoami, uid); + return (-1); } + } + else { + parse_passwd (getpwnam (newf.username), &oldf); + if (! oldf.username) { + cp = newf.username; + fprintf (stderr, "%s: user \"%s\" does not exist.\n", whoami, cp); + return (-1); } + } + + /* reality check */ + if (uid != 0 && uid != oldf.pw->pw_uid) { + errno = EACCES; + perror (whoami); + return (-1); + } + + if (interactive) ask_info (&oldf, &newf); + + if (! set_changed_data (&oldf, &newf)) { + printf ("Finger information not changed.\n"); + return 0; + } + status = save_new_data (&oldf); + return status; +} + +/* + * parse_argv () -- + * parse the command line arguments. + * returns true if no information beyond the username was given. + */ +static boolean parse_argv (argc, argv, pinfo) + int argc; + char *argv[]; + struct finfo *pinfo; +{ + int index, c, status; + boolean info_given; + + static struct option long_options[] = { + { "full-name", required_argument, 0, 'f' }, + { "office", required_argument, 0, 'o' }, + { "office-phone", required_argument, 0, 'p' }, + { "home-phone", required_argument, 0, 'h' }, + { "help", no_argument, 0, 'u' }, + { "version", no_argument, 0, 'v' }, + { NULL, no_argument, 0, '0' }, + }; + + optind = 0; + info_given = false; + while (true) { + c = getopt_long (argc, argv, "f:r:p:h:o:uv", long_options, &index); + if (c == EOF) break; + /* version? output version and exit. */ + if (c == 'v') { + printf ("%s\n", version_string); + exit (0); + } + if (c == 'u') { + usage (stdout); + exit (0); + } + /* all other options must have an argument. */ + if (! optarg) { + usage (stderr); + exit (-1); + } + /* ok, we were given an argument */ + info_given = true; + status = 0; + strcpy (buf, whoami); strcat (buf, ": "); + + /* now store the argument */ + switch (c) { + case 'f': + pinfo->full_name = optarg; + strcat (buf, "full name"); + status = check_gecos_string (buf, optarg); + break; + case 'o': + pinfo->office = optarg; + strcat (buf, "office"); + status = check_gecos_string (buf, optarg); + break; + case 'p': + pinfo->office_phone = optarg; + strcat (buf, "office phone"); + status = check_gecos_string (buf, optarg); + break; + case 'h': + pinfo->home_phone = optarg; + strcat (buf, "home phone"); + status = check_gecos_string (buf, optarg); + break; + default: + usage (stderr); + status = (-1); + } + if (status < 0) exit (status); + } + /* done parsing arguments. check for a username. */ + if (optind < argc) { + if (optind + 1 < argc) { + usage (stderr); + exit (-1); + } + pinfo->username = argv[optind]; + } + return (! info_given); +} + +/* + * usage () -- + * print out a usage message. + */ +static void usage (fp) + FILE *fp; +{ + fprintf (fp, "Usage: %s [ -f full-name ] [ -o office ] ", whoami); + fprintf (fp, "[ -p office-phone ]\n [ -h home-phone ] "); + fprintf (fp, "[ --help ] [ --version ]\n"); +} + +/* + * parse_passwd () -- + * take a struct password and fill in the fields of the + * struct finfo. + */ +static void parse_passwd (pw, pinfo) + struct passwd *pw; + struct finfo *pinfo; +{ + char *cp; + + if (pw) { + pinfo->pw = pw; + pinfo->username = pw->pw_name; + /* use pw_gecos */ + cp = pw->pw_gecos; + pinfo->full_name = cp; + cp = strchr (cp, ','); + if (cp) { *cp = 0, cp++; } else return; + pinfo->office = cp; + cp = strchr (cp, ','); + if (cp) { *cp = 0, cp++; } else return; + pinfo->office_phone = cp; + cp = strchr (cp, ','); + if (cp) { *cp = 0, cp++; } else return; + pinfo->home_phone = cp; + /* extra fields contain site-specific information, and + * can not be changed by this version of chfn. */ + cp = strchr (cp, ','); + if (cp) { *cp = 0, cp++; } else return; + pinfo->other = cp; + } +} + +/* + * ask_info () -- + * prompt the user for the finger information and store it. + */ +static void ask_info (oldfp, newfp) + struct finfo *oldfp; + struct finfo *newfp; +{ + printf ("Changing finger information for %s.\n", oldfp->username); + newfp->full_name = prompt ("Name", oldfp->full_name); + newfp->office = prompt ("Office", oldfp->office); + newfp->office_phone = prompt ("Office Phone", oldfp->office_phone); + newfp->home_phone = prompt ("Home Phone", oldfp->home_phone); + printf ("\n"); +} + +/* + * prompt () -- + * ask the user for a given field and check that the string is legal. + */ +static char *prompt (question, def_val) + char *question; + char *def_val; +{ + static char *blank = "none"; + int len; + char *ans, *cp; + + while (true) { + if (! def_val) def_val = ""; + printf("%s [%s]: ", question, def_val); + *buf = 0; + if (fgets (buf, sizeof (buf), stdin) == NULL) { + printf ("\nAborted.\n"); + exit (-1); + } + /* remove the newline at the end of buf. */ + ans = buf; + while (isspace (*ans)) ans++; + len = strlen (ans); + while (len > 0 && isspace (ans[len-1])) len--; + if (len <= 0) return NULL; + ans[len] = 0; + if (! strcasecmp (ans, blank)) return ""; + if (check_gecos_string (NULL, ans) >= 0) break; + } + cp = (char *) xmalloc (len + 1); + strcpy (cp, ans); + return cp; +} + +/* + * check_gecos_string () -- + * check that the given gecos string is legal. if it's not legal, + * output "msg" followed by a description of the problem, and + * return (-1). + */ +static int check_gecos_string (msg, gecos) + char *msg; + char *gecos; +{ + int i, c; + + for (i = 0; i < strlen (gecos); i++) { + c = gecos[i]; + if (c == ',' || c == ':' || c == '=' || c == '"' || c == '\n') { + if (msg) printf ("%s: ", msg); + printf ("'%c' is not allowed.\n", c); + return (-1); + } + if (iscntrl (c)) { + if (msg) printf ("%s: ", msg); + printf ("Control characters are not allowed.\n"); + return (-1); + } + } + return (0); +} + +/* + * set_changed_data () -- + * incorporate the new data into the old finger info. + */ +static boolean set_changed_data (oldfp, newfp) + struct finfo *oldfp; + struct finfo *newfp; +{ + boolean changed = false; + + if (newfp->full_name) { + oldfp->full_name = newfp->full_name; changed = true; } + if (newfp->office) { + oldfp->office = newfp->office; changed = true; } + if (newfp->office_phone) { + oldfp->office_phone = newfp->office_phone; changed = true; } + if (newfp->home_phone) { + oldfp->home_phone = newfp->home_phone; changed = true; } + + return changed; +} + +/* + * save_new_data () -- + * save the given finger info in /etc/passwd. + * return zero on success. + */ +static int save_new_data (pinfo) + struct finfo *pinfo; +{ + char *gecos; + int len; + + /* null fields will confuse printf(). */ + if (! pinfo->full_name) pinfo->full_name = ""; + if (! pinfo->office) pinfo->office = ""; + if (! pinfo->office_phone) pinfo->office_phone = ""; + if (! pinfo->home_phone) pinfo->home_phone = ""; + if (! pinfo->other) pinfo->other = ""; + + /* create the new gecos string */ + len = (strlen (pinfo->full_name) + strlen (pinfo->office) + + strlen (pinfo->office_phone) + strlen (pinfo->home_phone) + + strlen (pinfo->other) + 4); + gecos = (char *) xmalloc (len + 1); + sprintf (gecos, "%s,%s,%s,%s,%s", pinfo->full_name, pinfo->office, + pinfo->office_phone, pinfo->home_phone, pinfo->other); + + /* write the new struct passwd to the passwd file. */ + pinfo->pw->pw_gecos = gecos; + if (setpwnam (pinfo->pw) < 0) { + perror ("setpwnam"); + return (-1); + } + printf ("Finger information changed.\n"); + return 0; +} + +/* + * xmalloc () -- malloc that never fails. + */ +static void *xmalloc (bytes) + int bytes; +{ + void *vp; + + vp = malloc (bytes); + if (! vp && bytes > 0) { + perror ("malloc failed"); + exit (-1); + } + return vp; +} diff --git a/login-utils/chsh.1 b/login-utils/chsh.1 new file mode 100644 index 00000000..ec278fb4 --- /dev/null +++ b/login-utils/chsh.1 @@ -0,0 +1,51 @@ +.\" +.\" chsh.1 -- change your login shell +.\" (c) 1994 by salvatore valente +.\" +.\" this program is free software. you can redistribute it and +.\" modify it under the terms of the gnu general public license. +.\" there is no warranty. +.\" +.\" faith +.\" 1.1.1.1 +.\" 1995/02/22 19:09:23 +.\" +.TH CHSH 1 "October 13 1994" "chsh" "Linux Reference Manual" +.SH NAME +chsh \- change your login shell +.SH SYNOPSIS +.B chsh +[\ \-s\ shell\ ] [\ \-l\ ] [\ \-u\ ] [\ \-v\ ] [\ username\ ] +.SH DESCRIPTION +.B chsh +is used to change your login shell. +If a shell is not given on the command line, +.B chsh +prompts for one. +.SS VALID SHELLS +.B chsh +will accept the full pathname of any executable file on the system. +However, it will issue a warning if the shell is not listed in the +.I /etc/shells +file. +.SH OPTIONS +.TP +.I "\-s, \-\-shell" +Specify your login shell. +.TP +.I "\-l, \-\-list-shells" +Print the list of shells listed in +.I /etc/shells +and exit. +.TP +.I "\-u, \-\-help" +Print a usage message and exit. +.TP +.I "-v, \-\-version" +Print version information and exit. +.SH "SEE ALSO" +.BR login (1), +.BR passwd (5), +.BR shells (5) +.SH AUTHOR +Salvatore Valente diff --git a/login-utils/chsh.c b/login-utils/chsh.c new file mode 100644 index 00000000..9a9a52e8 --- /dev/null +++ b/login-utils/chsh.c @@ -0,0 +1,313 @@ +/* + * chsh.c -- change your login shell + * (c) 1994 by salvatore valente + * + * this program is free software. you can redistribute it and + * modify it under the terms of the gnu general public license. + * there is no warranty. + * + * faith + * 1.1.1.1 + * 1995/02/22 19:09:23 + * + */ + +#define _POSIX_SOURCE 1 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef P +#if __STDC__ +#define P(foo) foo +#else +#define P(foo) () +#endif + +typedef unsigned char boolean; +#define false 0 +#define true 1 + +static char *version_string = "chsh 0.9 beta"; +static char *whoami; + +static char buf[FILENAME_MAX]; + +struct sinfo { + char *username; + char *shell; +}; + +static void parse_argv P((int argc, char *argv[], struct sinfo *pinfo)); +static void usage P((FILE *fp)); +static char *prompt P((char *question, char *def_val)); +static int check_shell P((char *shell)); +static boolean get_shell_list P((char *shell)); +static void *xmalloc P((int bytes)); +extern int setpwnam P((struct passwd *pwd)); +#define memzero(ptr, size) memset((char *) ptr, 0, size) + +int main (argc, argv) + int argc; + char *argv[]; +{ + char *cp, *shell; + uid_t uid; + struct sinfo info; + struct passwd *pw; + extern int errno; + + /* whoami is the program name for error messages */ + whoami = argv[0]; + if (! whoami) whoami = "chsh"; + for (cp = whoami; *cp; cp++) + if (*cp == '/') whoami = cp + 1; + + umask (022); + + uid = getuid (); + memzero (&info, sizeof (info)); + + parse_argv (argc, argv, &info); + pw = NULL; + if (! info.username) { + pw = getpwuid (uid); + if (! pw) { + fprintf (stderr, "%s: you (user %d) don't exist.\n", whoami, uid); + return (-1); } + } + else { + pw = getpwnam (info.username); + if (! pw) { + cp = info.username; + fprintf (stderr, "%s: user \"%s\" does not exist.\n", whoami, cp); + return (-1); } + } + + /* reality check */ + if (uid != 0 && uid != pw->pw_uid) { + errno = EACCES; + perror (whoami); + return (-1); + } + + shell = info.shell; + if (! shell) { + printf ("Changing shell for %s.\n", pw->pw_name); + shell = prompt ("New shell", pw->pw_shell); + if (! shell) return 0; + } + if (check_shell (shell) < 0) return (-1); + + if (! strcmp (pw->pw_shell, shell)) { + printf ("Shell not changed.\n"); + return 0; + } + pw->pw_shell = shell; + if (setpwnam (pw) < 0) { + perror ("setpwnam"); + return (-1); + } + printf ("Shell changed.\n"); + return 0; +} + +/* + * parse_argv () -- + * parse the command line arguments, and fill in "pinfo" with any + * information from the command line. + */ +static void parse_argv (argc, argv, pinfo) + int argc; + char *argv[]; + struct sinfo *pinfo; +{ + int index, c; + + static struct option long_options[] = { + { "shell", required_argument, 0, 's' }, + { "list-shells", no_argument, 0, 'l' }, + { "help", no_argument, 0, 'u' }, + { "version", no_argument, 0, 'v' }, + { NULL, no_argument, 0, '0' }, + }; + + optind = c = 0; + while (c != EOF) { + c = getopt_long (argc, argv, "s:luv", long_options, &index); + switch (c) { + case EOF: + break; + case 'v': + printf ("%s\n", version_string); + exit (0); + case 'u': + usage (stdout); + exit (0); + case 'l': + get_shell_list (NULL); + exit (0); + case 's': + if (! optarg) { + usage (stderr); + exit (-1); + } + pinfo->shell = optarg; + break; + default: + usage (stderr); + exit (-1); + } + } + /* done parsing arguments. check for a username. */ + if (optind < argc) { + if (optind + 1 < argc) { + usage (stderr); + exit (-1); + } + pinfo->username = argv[optind]; + } +} + +/* + * usage () -- + * print out a usage message. + */ +static void usage (fp) + FILE *fp; +{ + fprintf (fp, "Usage: %s [ -s shell ] ", whoami); + fprintf (fp, "[ --list-shells ] [ --help ] [ --version ]\n"); + fprintf (fp, " [ username ]\n"); +} + +/* + * prompt () -- + * ask the user for a given field and return it. + */ +static char *prompt (question, def_val) + char *question; + char *def_val; +{ + int len; + char *ans, *cp; + + if (! def_val) def_val = ""; + printf("%s [%s]: ", question, def_val); + *buf = 0; + if (fgets (buf, sizeof (buf), stdin) == NULL) { + printf ("\nAborted.\n"); + exit (-1); + } + /* remove the newline at the end of buf. */ + ans = buf; + while (isspace (*ans)) ans++; + len = strlen (ans); + while (len > 0 && isspace (ans[len-1])) len--; + if (len <= 0) return NULL; + ans[len] = 0; + cp = (char *) xmalloc (len + 1); + strcpy (cp, buf); + return cp; +} + +/* + * check_shell () -- if the shell is completely invalid, print + * an error and return (-1). + * if the shell is a bad idea, print a warning. + */ +static int check_shell (shell) + char *shell; +{ + int i, c; + + if (*shell != '/') { + printf ("%s: shell must be a full path name.\n", whoami); + return (-1); + } + if (access (shell, F_OK) < 0) { + printf ("%s: \"%s\" does not exist.\n", whoami, shell); + return (-1); + } + if (access (shell, X_OK) < 0) { + printf ("%s: \"%s\" is not executable.\n", whoami, shell); + return (-1); + } + /* keep /etc/passwd clean. */ + for (i = 0; i < strlen (shell); i++) { + c = shell[i]; + if (c == ',' || c == ':' || c == '=' || c == '"' || c == '\n') { + printf ("%s: '%c' is not allowed.\n", whoami, c); + return (-1); + } + if (iscntrl (c)) { + printf ("%s: Control characters are not allowed.\n", whoami); + return (-1); + } + } + if (! get_shell_list (shell)) + printf ("warning: \"%s\" is not listed as a valid shell.\n", shell); + return 0; +} + +/* + * get_shell_list () -- if the given shell appears in /etc/shells, + * return true. if not, return false. + * if the given shell is NULL, /etc/shells is outputted to stdout. + */ +static boolean get_shell_list (shell_name) + char *shell_name; +{ + FILE *fp; + boolean found; + int len; + + found = false; + fp = fopen ("/etc/shells", "r"); + if (! fp) { + if (! shell_name) printf ("No known shells.\n"); + return true; + } + while (fgets (buf, sizeof (buf), fp) != NULL) { + /* ignore comments */ + if (*buf == '#') continue; + len = strlen (buf); + /* strip the ending newline */ + if (buf[len - 1] == '\n') buf[len - 1] = 0; + /* ignore lines that are too damn long */ + else continue; + /* check or output the shell */ + if (shell_name) { + if (! strcmp (shell_name, buf)) { + found = true; + break; + } + } + else printf ("%s\n", buf); + } + fclose (fp); + return found; +} + +/* + * xmalloc () -- malloc that never fails. + */ +static void *xmalloc (bytes) + int bytes; +{ + void *vp; + + vp = malloc (bytes); + if (! vp && bytes > 0) { + perror ("malloc failed"); + exit (-1); + } + return vp; +} diff --git a/login-utils/fastboot.8 b/login-utils/fastboot.8 new file mode 100644 index 00000000..386d9715 --- /dev/null +++ b/login-utils/fastboot.8 @@ -0,0 +1 @@ +.so man8/shutdown.8 diff --git a/login-utils/fasthalt.8 b/login-utils/fasthalt.8 new file mode 100644 index 00000000..386d9715 --- /dev/null +++ b/login-utils/fasthalt.8 @@ -0,0 +1 @@ +.so man8/shutdown.8 diff --git a/login-utils/halt.8 b/login-utils/halt.8 new file mode 100644 index 00000000..386d9715 --- /dev/null +++ b/login-utils/halt.8 @@ -0,0 +1 @@ +.so man8/shutdown.8 diff --git a/login-utils/islocal.c b/login-utils/islocal.c new file mode 100644 index 00000000..a4cfb16a --- /dev/null +++ b/login-utils/islocal.c @@ -0,0 +1,34 @@ +/* islocal.c - returns true if user is registered in the local + /etc/passwd file. Written by Alvaro Martinez Echevarria, + alvaro@enano.etsit.upm.es, to allow peaceful coexistence with yp. Nov 94. + Hacked a bit by poe@daimi.aau.dk + See also ftp://ftp.daimi.aau.dk/pub/linux/poe/admutil* +*/ + +#include +#include + +#define MAX_LENGTH 1024 + +int +is_local(char *user) +{ + FILE *fd; + char line[MAX_LENGTH]; + int local = 0; + + if(!(fd = fopen("/etc/passwd", "r"))) { + puts("Can't read /etc/passwd, exiting."); + exit(1); + } + + while(fgets(line, MAX_LENGTH, fd) > 0) { + if(!strncmp(line, user, strlen(user))) { + local = 1; + break; + } + } + fclose(fd); + return local; +} + diff --git a/login-utils/last.1 b/login-utils/last.1 new file mode 100644 index 00000000..44e6b827 --- /dev/null +++ b/login-utils/last.1 @@ -0,0 +1,59 @@ +.TH LAST 1 "20 March 1992" +.SH NAME +last \(em indicate last logins by user or terminal +.SH SYNOPSIS +.ad l +.B last +.RB [ \-\fP\fInumber\fP ] +.RB [ \-f +.IR filename ] +.RB [ \-t +.IR tty ] +.RB [ \-h +.IR hostname ] +.RB [ \-i +.IR address ] +.RB [ \-l ] +.RB [ \-y ] +.RI [ name ...] +.ad b +.SH DESCRIPTION +\fBLast\fP looks back in the \fBwtmp\fP file which records all logins +and logouts for information about a user, a teletype or any group of +users and teletypes. Arguments specify names of users or teletypes of +interest. If multiple arguments are given, the information which +applies to any of the arguments is printed. For example ``\fBlast root +console\fP'' would list all of root's sessions as well as all sessions +on the console terminal. \fBLast\fP displays the sessions of the +specified users and teletypes, most recent first, indicating the times +at which the session began, the duration of the session, and the +teletype which the session took place on. If the session is still +continuing or was cut short by a reboot, \fBlast\fP so indicates. +.LP +The pseudo-user \fBreboot\fP logs in at reboots of the system. +.LP +\fBLast\fP with no arguments displays a record of all logins and +logouts, in reverse order. +.LP +If \fBlast\fP is interrupted, it indicates how far the search has +progressed in \fBwtmp\fP. If interrupted with a quit signal \fBlast\fP +indicates how far the search has progressed so far, and the search +continues. +.SH OPTIONS +.IP \fB\-\fP\fInumber\fP +limit the number of entries displayed to that specified by \fInumber\fP. +.IP "\fB\-f\fP \fIfilename\fP" +Use \fIfilename\fP as the name of the accounting file instead of +.BR /etc/wtmp . +.IP "\fB\-h\fP \fIhostname\fP" +List only logins from \fIhostname\fP. +.IP "\fB\-i\fP \fIIP address\fP"" +List only logins from \fIIP address\fP. +.IP "\fB\-l\fP" +List IP addresses of remote hosts instead of truncated host names. +.IP "\fB\-t\fP \fItty\fP" +List only logins on \fItty\fP. +.IP "\fB\-y\fP" +Also report year of dates. +.SH FILES +/usr/adm/wtmp \(em login data base diff --git a/login-utils/last.c b/login-utils/last.c new file mode 100644 index 00000000..c00808c2 --- /dev/null +++ b/login-utils/last.c @@ -0,0 +1,438 @@ +/* + * Berkeley last for Linux. Currently maintained by poe@daimi.aau.dk at + * ftp://ftp.daimi.aau.dk/pub/linux/poe/admutil* + * + * Copyright (c) 1987 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 +char copyright[] = +"@(#) Copyright (c) 1987 Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)last.c 5.11 (Berkeley) 6/29/88"; +#endif /* not lint */ + +/* + * last + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pathnames.h" + +#define SECDAY (24*60*60) /* seconds in a day */ +#define NO 0 /* false/no */ +#define YES 1 /* true/yes */ + +static struct utmp buf[1024]; /* utmp read buffer */ + +#define HMAX (int)sizeof(buf[0].ut_host) /* size of utmp host field */ +#define LMAX (int)sizeof(buf[0].ut_line) /* size of utmp tty field */ +#define NMAX (int)sizeof(buf[0].ut_name) /* size of utmp name field */ + +typedef struct arg { + char *name; /* argument */ +#define HOST_TYPE -2 +#define TTY_TYPE -3 +#define USER_TYPE -4 +#define INET_TYPE -5 + int type; /* type of arg */ + struct arg *next; /* linked list pointer */ +} ARG; +ARG *arglist; /* head of linked list */ + +typedef struct ttytab { + long logout; /* log out time */ + char tty[LMAX + 1]; /* terminal name */ + struct ttytab *next; /* linked list pointer */ +} TTY; +TTY *ttylist; /* head of linked list */ + +static long currentout, /* current logout value */ + maxrec; /* records to display */ +static char *file = _PATH_WTMP; /* wtmp file */ + +static int doyear = 0; /* output year in dates */ +static int dolong = 0; /* print also ip-addr */ + +static void wtmp(), addarg(), hostconv(); +static int want(); +TTY *addtty(); +static char *ttyconv(); + +int +main(argc, argv) + int argc; + char **argv; +{ + extern int optind; + extern char *optarg; + int ch; + + while ((ch = getopt(argc, argv, "0123456789yli:f:h:t:")) != EOF) + switch((char)ch) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + /* + * kludge: last was originally designed to take + * a number after a dash. + */ + if (!maxrec) + maxrec = atol(argv[optind - 1] + 1); + break; + case 'f': + file = optarg; + break; + case 'h': + hostconv(optarg); + addarg(HOST_TYPE, optarg); + break; + case 't': + addarg(TTY_TYPE, ttyconv(optarg)); + break; + case 'y': + doyear = 1; + break; + case 'l': + dolong = 1; + break; + case 'i': + addarg(INET_TYPE, optarg); + break; + case '?': + default: + fputs("usage: last [-#] [-f file] [-t tty] [-h hostname] [user ...]\n", stderr); + exit(1); + } + for (argv += optind; *argv; ++argv) { +#define COMPATIBILITY +#ifdef COMPATIBILITY + /* code to allow "last p5" to work */ + addarg(TTY_TYPE, ttyconv(*argv)); +#endif + addarg(USER_TYPE, *argv); + } + wtmp(); + exit(0); +} + +/* + * print_partial_line -- + * print the first part of each output line according to specified format + */ +void +print_partial_line(bp) + struct utmp *bp; +{ + char *ct; + + ct = ctime(&bp->ut_time); + printf("%-*.*s %-*.*s ", NMAX, NMAX, bp->ut_name, + LMAX, LMAX, bp->ut_line); + + if (dolong) { + if (bp->ut_addr) { + struct in_addr foo; + foo.s_addr = bp->ut_addr; + printf("%-*.*s ", HMAX, HMAX, inet_ntoa(foo)); + } else { + printf("%-*.*s ", HMAX, HMAX, ""); + } + } else { + printf("%-*.*s ", HMAX, HMAX, bp->ut_host); + } + + if (doyear) { + printf("%10.10s %4.4s %5.5s ", ct, ct + 20, ct + 11); + } else { + printf("%10.10s %5.5s ", ct, ct + 11); + } +} + +/* + * wtmp -- + * read through the wtmp file + */ +static void +wtmp() +{ + register struct utmp *bp; /* current structure */ + register TTY *T; /* tty list entry */ + struct stat stb; /* stat of file for size */ + long bl, delta, /* time difference */ + lseek(), time(); + int bytes, wfd; + void onintr(); + char *ct, *crmsg; + + if ((wfd = open(file, O_RDONLY, 0)) < 0 || fstat(wfd, &stb) == -1) { + perror(file); + exit(1); + } + bl = (stb.st_size + sizeof(buf) - 1) / sizeof(buf); + + (void)time(&buf[0].ut_time); + (void)signal(SIGINT, onintr); + (void)signal(SIGQUIT, onintr); + + while (--bl >= 0) { + if (lseek(wfd, (long)(bl * sizeof(buf)), L_SET) == -1 || + (bytes = read(wfd, (char *)buf, sizeof(buf))) == -1) { + fprintf(stderr, "last: %s: ", file); + perror((char *)NULL); + exit(1); + } + for (bp = &buf[bytes / sizeof(buf[0]) - 1]; bp >= buf; --bp) { + /* + * if the terminal line is '~', the machine stopped. + * see utmp(5) for more info. + */ + if (!strncmp(bp->ut_line, "~", LMAX)) { + /* everybody just logged out */ + for (T = ttylist; T; T = T->next) + T->logout = -bp->ut_time; + currentout = -bp->ut_time; + crmsg = strncmp(bp->ut_name, "shutdown", NMAX) ? "crash" : "down "; + if (!bp->ut_name[0]) + (void)strcpy(bp->ut_name, "reboot"); + if (want(bp, NO)) { + ct = ctime(&bp->ut_time); + if(bp->ut_type != LOGIN_PROCESS) { + print_partial_line(bp); + putchar('\n'); + } + if (maxrec && !--maxrec) + return; + } + continue; + } + /* find associated tty */ + for (T = ttylist;; T = T->next) { + if (!T) { + /* add new one */ + T = addtty(bp->ut_line); + break; + } + if (!strncmp(T->tty, bp->ut_line, LMAX)) + break; + } + if (bp->ut_name[0] && bp->ut_type != LOGIN_PROCESS + && want(bp, YES)) { + + print_partial_line(bp); + + if (!T->logout) + puts(" still logged in"); + else { + if (T->logout < 0) { + T->logout = -T->logout; + printf("- %s", crmsg); + } + else + printf("- %5.5s", ctime(&T->logout)+11); + delta = T->logout - bp->ut_time; + if (delta < SECDAY) + printf(" (%5.5s)\n", asctime(gmtime(&delta))+11); + else + printf(" (%ld+%5.5s)\n", delta / SECDAY, asctime(gmtime(&delta))+11); + } + if (maxrec != -1 && !--maxrec) + return; + } + T->logout = bp->ut_time; + } + } + ct = ctime(&buf[0].ut_time); + printf("\nwtmp begins %10.10s %5.5s \n", ct, ct + 11); +} + +/* + * want -- + * see if want this entry + */ +static int +want(bp, check) + register struct utmp *bp; + int check; +{ + register ARG *step; + + if (check) + /* + * when uucp and ftp log in over a network, the entry in + * the utmp file is the name plus their process id. See + * etc/ftpd.c and usr.bin/uucp/uucpd.c for more information. + */ + if (!strncmp(bp->ut_line, "ftp", sizeof("ftp") - 1)) + bp->ut_line[3] = '\0'; + else if (!strncmp(bp->ut_line, "uucp", sizeof("uucp") - 1)) + bp->ut_line[4] = '\0'; + if (!arglist) + return(YES); + + for (step = arglist; step; step = step->next) + switch(step->type) { + case HOST_TYPE: + if (!strncmp(step->name, bp->ut_host, HMAX)) + return(YES); + break; + case TTY_TYPE: + if (!strncmp(step->name, bp->ut_line, LMAX)) + return(YES); + break; + case USER_TYPE: + if (!strncmp(step->name, bp->ut_name, NMAX)) + return(YES); + break; + case INET_TYPE: + if (bp->ut_addr == inet_addr(step->name)) + return(YES); + break; + } + return(NO); +} + +/* + * addarg -- + * add an entry to a linked list of arguments + */ +static void +addarg(type, arg) + int type; + char *arg; +{ + register ARG *cur; + + if (!(cur = (ARG *)malloc((unsigned int)sizeof(ARG)))) { + fputs("last: malloc failure.\n", stderr); + exit(1); + } + cur->next = arglist; + cur->type = type; + cur->name = arg; + arglist = cur; +} + +/* + * addtty -- + * add an entry to a linked list of ttys + */ +TTY * +addtty(ttyname) + char *ttyname; +{ + register TTY *cur; + + if (!(cur = (TTY *)malloc((unsigned int)sizeof(TTY)))) { + fputs("last: malloc failure.\n", stderr); + exit(1); + } + cur->next = ttylist; + cur->logout = currentout; + memcpy(cur->tty, ttyname, LMAX); + return(ttylist = cur); +} + +/* + * hostconv -- + * convert the hostname to search pattern; if the supplied host name + * has a domain attached that is the same as the current domain, rip + * off the domain suffix since that's what login(1) does. + */ +static void +hostconv(arg) + char *arg; +{ + static int first = 1; + static char *hostdot, + name[MAXHOSTNAMELEN]; + char *argdot; + + if (!(argdot = strchr(arg, '.'))) + return; + if (first) { + first = 0; + if (gethostname(name, sizeof(name))) { + perror("last: gethostname"); + exit(1); + } + hostdot = strchr(name, '.'); + } + if (hostdot && !strcmp(hostdot, argdot)) + *argdot = '\0'; +} + +/* + * ttyconv -- + * convert tty to correct name. + */ +static char * +ttyconv(arg) + char *arg; +{ + char *mval; + + /* + * kludge -- we assume that all tty's end with + * a two character suffix. + */ + if (strlen(arg) == 2) { + /* either 6 for "ttyxx" or 8 for "console" */ + if (!(mval = malloc((unsigned int)8))) { + fputs("last: malloc failure.\n", stderr); + exit(1); + } + if (!strcmp(arg, "co")) + (void)strcpy(mval, "console"); + else { + (void)strcpy(mval, "tty"); + (void)strcpy(mval + 3, arg); + } + return(mval); + } + if (!strncmp(arg, "/dev/", sizeof("/dev/") - 1)) + return(arg + 5); + return(arg); +} + +/* + * onintr -- + * on interrupt, we inform the user how far we've gotten + */ +void +onintr(signo) + int signo; +{ + char *ct; + + ct = ctime(&buf[0].ut_time); + printf("\ninterrupted %10.10s %5.5s \n", ct, ct + 11); + if (signo == SIGINT) + exit(1); + (void)fflush(stdout); /* fix required for rsh */ +} diff --git a/login-utils/login.1 b/login-utils/login.1 new file mode 100644 index 00000000..e6e30d82 --- /dev/null +++ b/login-utils/login.1 @@ -0,0 +1,131 @@ +.\" Copyright 1993 Rickard E. Faith (faith@cs.unc.edu) +.\" May be distributed under the GNU General Public License +.TH LOGIN 1 "1 February 1993" "Linux 0.99" "Linux Programmer's Manual" +.SH NAME +login \- sign on +.SH SYNOPSIS +.BR "login [ " name " ]" +.br +.B "login \-p" +.br +.BR "login \-h " hostname +.br +.BR "login \-f " name +.SH DESCRIPTION +.B login +is used when signing onto a system. It can also be used to switch from one +user to another at any time (most modern shells have support for this +feature built into them, however). + +If an argument is not given, +.B login +prompts for the username. + +If the user is +.I not +root, and if +.I /etc/nologin +exists, the contents of of this file are printed to the screen, and the +login is terminated. This is typically used to prevent logins when the +system is being taken down. + +If the user is root, then the login must be occuring on a tty listed in +.IR /etc/securetty . +Failures will be logged with the +.B syslog +facility. + +After these conditions are checked, the password will be requested and +checks (if a password is required for this username). Ten attempts are +allowed before +.B login +dies, but after the first three, the response starts to get very slow. +Login failures are reported via the +.B syslog +facility. This facility is also used to report any successful root logins. + +If the file +.I .hushlogin +exists, then a "quiet" login is performed (this disables the checking of +the checking of mail and the printing of the last login time and message of +the day). Otherwise, if +.I /var/adm/lastlog +exists, the last login time is printed (and the current login is recorded). + +Random administrative things, such as setting the UID and GID of the tty +are performed. The TERM environment variable is preserved, if it exists +(other environment variables are preserved if the +.B \-p +option is used). Then the HOME, PATH, SHELL, TERM, MAIL, and LOGNAME +environment variables are set. PATH defaults to +.I /usr/local/bin:/bin:/usr/bin:. +for normal users, and to +.I /sbin:/bin:/usr/sbin:/usr/bin +for root. Last, if this is not a "quiet" login, the message of the day is +printed and the file with the user's name in +.I /usr/spool/mail +will be checked, and a message printed if it has non-zero length. + +The user's shell is then started. If no shell is specified for the user in +.BR /etc/passwd , +then +.B /bin/sh +is used. If there is no directory specified in +.IR /etc/passwd , +then +.I / +is used (the home directory is checked for the +.I .hushlogin +file described above). +.SH OPTIONS +.TP +.B \-p +Used by +.BR getty (8) +to tell +.B login +not to destroy the environment +.TP +.B \-f +Used to skip a second login authentication. This specifically does +.B not +work for root, and does not appear to work well under Linux. +.TP +.B \-h +Used by other servers (i.e., +.BR telnetd (8)) +to pass the name of the remote host to +.B login +so that it may be placed in utmp and wtmp. Only the superuser may use this +option. +.SH FILES +.nf +.I /etc/utmp +.I /etc/wtmp +.I /usr/spool/mail/* +.I /etc/motd +.I /etc/passwd +.I /etc/nologin +.I .hushlogin +.fi +.SH "SEE ALSO" +.BR init (8), +.BR getty (8), +.BR mail (1), +.BR passwd (1), +.BR passwd (5), +.BR environ (7), +.BR shutdown (8) +.SH BUGS +Linux, unlike other draconian operating systems, does not check quotas. + +The undocumented BSD +.B \-r +option is not supported. This may be required by some +.BR rlogind (8) +programs. +.SH AUTHOR +Derived from BSD login 5.40 (5/9/89) by Michael Glad (glad@daimi.dk) for HP-UX +.br +Ported to Linux 0.12: Peter Orbaek (poe@daimi.aau.dk) + diff --git a/login-utils/login.c b/login-utils/login.c new file mode 100644 index 00000000..f0130f8e --- /dev/null +++ b/login-utils/login.c @@ -0,0 +1,1007 @@ +/* This program is derived from 4.3 BSD software and is + subject to the copyright notice below. + + The port to HP-UX has been motivated by the incapability + of 'rlogin'/'rlogind' as per HP-UX 6.5 (and 7.0) to transfer window sizes. + + Changes: + + - General HP-UX portation. Use of facilities not available + in HP-UX (e.g. setpriority) has been eliminated. + Utmp/wtmp handling has been ported. + + - The program uses BSD command line options to be used + in connection with e.g. 'rlogind' i.e. 'new login'. + + - HP features left out: logging of bad login attempts in /etc/btmp, + they are sent to syslog + + password expiry + + '*' as login shell, add it if you need it + + - BSD features left out: quota checks + password expiry + analysis of terminal type (tset feature) + + - BSD features thrown in: Security logging to syslogd. + This requires you to have a (ported) syslog + system -- 7.0 comes with syslog + + 'Lastlog' feature. + + - A lot of nitty gritty details has been adjusted in favour of + HP-UX, e.g. /etc/securetty, default paths and the environment + variables assigned by 'login'. + + - We do *nothing* to setup/alter tty state, under HP-UX this is + to be done by getty/rlogind/telnetd/some one else. + + Michael Glad (glad@daimi.dk) + Computer Science Department + Aarhus University + Denmark + + 1990-07-04 + + 1991-09-24 glad@daimi.aau.dk: HP-UX 8.0 port: + - now explictly sets non-blocking mode on descriptors + - strcasecmp is now part of HP-UX + 1992-02-05 poe@daimi.aau.dk: Ported the stuff to Linux 0.12 + From 1992 till now (1995) this code for Linux has been maintained at + ftp.daimi.aau.dk:/pub/linux/poe/ +*/ + +/* + * Copyright (c) 1980, 1987, 1988 The 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 +char copyright[] = +"@(#) Copyright (c) 1980, 1987, 1988 The Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)login.c 5.40 (Berkeley) 5/9/89"; +#endif /* not lint */ + +/* + * login [ name ] + * login -h hostname (for telnetd, etc.) + * login -f name (for pre-authenticated login: datakit, xterm, etc.) + */ + +/* #define TESTING */ + +#ifdef TESTING +#include "param.h" +#else +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define index strchr +#define rindex strrchr +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef TESTING +# include "utmp.h" +#else +# include +#endif + +#ifdef SHADOW_PWD +#include +#endif + +#ifndef linux +#include +#include +#else +struct lastlog + { long ll_time; + char ll_line[12]; + char ll_host[16]; + }; +#endif + +#include "pathnames.h" + +#define P_(s) () +void opentty P_((const char *tty)); +void getloginname P_((void)); +void timedout P_((void)); +int rootterm P_((char *ttyn)); +void motd P_((void)); +void sigint P_((void)); +void checknologin P_((void)); +void dolastlog P_((int quiet)); +void badlogin P_((char *name)); +char *stypeof P_((char *ttyid)); +void checktty P_((char *user, char *tty)); +void getstr P_((char *buf, int cnt, char *err)); +void sleepexit P_((int eval)); +#undef P_ + +#ifdef KERBEROS +#include +#include +char realm[REALM_SZ]; +int kerror = KSUCCESS, notickets = 1; +#endif + +#ifndef linux +#define TTYGRPNAME "tty" /* name of group to own ttys */ +#else +# define TTYGRPNAME "other" +# ifndef MAXPATHLEN +# define MAXPATHLEN 1024 +# endif +#endif + +/* + * This bounds the time given to login. Not a define so it can + * be patched on machines where it's too small. + */ +#ifndef linux +int timeout = 300; +#else +int timeout = 60; +#endif + +struct passwd *pwd; +int failures; +char term[64], *hostname, *username, *tty; + +char thishost[100]; + +#ifndef linux +struct sgttyb sgttyb; +struct tchars tc = { + CINTR, CQUIT, CSTART, CSTOP, CEOT, CBRK +}; +struct ltchars ltc = { + CSUSP, CDSUSP, CRPRNT, CFLUSH, CWERASE, CLNEXT +}; +#endif + +char *months[] = + { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", + "Sep", "Oct", "Nov", "Dec" }; + +/* provided by Linus Torvalds 16-Feb-93 */ +void +opentty(const char * tty) +{ + int i; + int fd = open(tty, O_RDWR); + + for (i = 0 ; i < fd ; i++) + close(i); + for (i = 0 ; i < 3 ; i++) + dup2(fd, i); + if (fd >= 3) + close(fd); +} + +int +main(argc, argv) + int argc; + char **argv; +{ + extern int errno, optind; + extern char *optarg, **environ; + struct timeval tp; + struct tm *ttp; + struct group *gr; + register int ch; + register char *p; + int ask, fflag, hflag, pflag, cnt; + int quietlog, passwd_req, ioctlval; + char *domain, *salt, *ttyn, *pp; + char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10]; + char *ctime(), *ttyname(), *stypeof(); + time_t time(); + void timedout(); + char *termenv; + +#ifdef linux + char tmp[100]; + /* Just as arbitrary as mountain time: */ + /* (void)setenv("TZ", "MET-1DST",0); */ +#endif + + (void)signal(SIGALRM, timedout); + (void)alarm((unsigned int)timeout); + (void)signal(SIGQUIT, SIG_IGN); + (void)signal(SIGINT, SIG_IGN); + + (void)setpriority(PRIO_PROCESS, 0, 0); +#ifdef HAVE_QUOTA + (void)quota(Q_SETUID, 0, 0, 0); +#endif + + /* + * -p is used by getty to tell login not to destroy the environment + * -f is used to skip a second login authentication + * -h is used by other servers to pass the name of the remote + * host to login so that it may be placed in utmp and wtmp + */ + (void)gethostname(tbuf, sizeof(tbuf)); + (void)strncpy(thishost, tbuf, sizeof(thishost)-1); + domain = index(tbuf, '.'); + + fflag = hflag = pflag = 0; + passwd_req = 1; + while ((ch = getopt(argc, argv, "fh:p")) != EOF) + switch (ch) { + case 'f': + fflag = 1; + break; + + case 'h': + if (getuid()) { + (void)fprintf(stderr, + "login: -h for super-user only.\n"); + exit(1); + } + hflag = 1; + if (domain && (p = index(optarg, '.')) && + strcasecmp(p, domain) == 0) + *p = 0; + hostname = optarg; + break; + + case 'p': + pflag = 1; + break; + case '?': + default: + (void)fprintf(stderr, + "usage: login [-fp] [username]\n"); + exit(1); + } + argc -= optind; + argv += optind; + if (*argv) { + username = *argv; + ask = 0; + } else + ask = 1; + +#ifndef linux + ioctlval = 0; + (void)ioctl(0, TIOCLSET, &ioctlval); + (void)ioctl(0, TIOCNXCL, 0); + (void)fcntl(0, F_SETFL, ioctlval); + (void)ioctl(0, TIOCGETP, &sgttyb); + sgttyb.sg_erase = CERASE; + sgttyb.sg_kill = CKILL; + (void)ioctl(0, TIOCSLTC, <c); + (void)ioctl(0, TIOCSETC, &tc); + (void)ioctl(0, TIOCSETP, &sgttyb); + + /* + * Be sure that we're in + * blocking mode!!! + * This is really for HPUX + */ + ioctlval = 0; + (void)ioctl(0, FIOSNBIO, &ioctlval); +#endif + + for (cnt = getdtablesize(); cnt > 2; cnt--) + close(cnt); + + ttyn = ttyname(0); + if (ttyn == NULL || *ttyn == '\0') { + (void)sprintf(tname, "%s??", _PATH_TTY); + ttyn = tname; + } + + setpgrp(); + + { + struct termios tt, ttt; + + tcgetattr(0, &tt); + ttt = tt; + ttt.c_cflag &= ~HUPCL; + + if((chown(ttyn, 0, 0) == 0) && (chmod(ttyn, 0622) == 0)) { + tcsetattr(0,TCSAFLUSH,&ttt); + signal(SIGHUP, SIG_IGN); /* so vhangup() wont kill us */ + vhangup(); + signal(SIGHUP, SIG_DFL); + } + + setsid(); + + /* re-open stdin,stdout,stderr after vhangup() closed them */ + /* if it did, after 0.99.5 it doesn't! */ + opentty(ttyn); + tcsetattr(0,TCSAFLUSH,&tt); + } + + if (tty = rindex(ttyn, '/')) + ++tty; + else + tty = ttyn; + + openlog("login", LOG_ODELAY, LOG_AUTH); + + for (cnt = 0;; ask = 1) { + ioctlval = 0; +#ifndef linux + (void)ioctl(0, TIOCSETD, &ioctlval); +#endif + + if (ask) { + fflag = 0; + getloginname(); + } + + checktty(username, tty); + + (void)strcpy(tbuf, username); + if (pwd = getpwnam(username)) + salt = pwd->pw_passwd; + else + salt = "xx"; + + /* if user not super-user, check for disabled logins */ + if (pwd == NULL || pwd->pw_uid) + checknologin(); + + /* + * Disallow automatic login to root; if not invoked by + * root, disallow if the uid's differ. + */ + if (fflag && pwd) { + int uid = getuid(); + + passwd_req = pwd->pw_uid == 0 || + (uid && uid != pwd->pw_uid); + } + + /* + * If trying to log in as root, but with insecure terminal, + * refuse the login attempt. + */ + if (pwd && pwd->pw_uid == 0 && !rootterm(tty)) { + (void)fprintf(stderr, + "%s login refused on this terminal.\n", + pwd->pw_name); + + if (hostname) + syslog(LOG_NOTICE, + "LOGIN %s REFUSED FROM %s ON TTY %s", + pwd->pw_name, hostname, tty); + else + syslog(LOG_NOTICE, + "LOGIN %s REFUSED ON TTY %s", + pwd->pw_name, tty); + continue; + } + + /* + * If no pre-authentication and a password exists + * for this user, prompt for one and verify it. + */ + if (!passwd_req || (pwd && !*pwd->pw_passwd)) + break; + + setpriority(PRIO_PROCESS, 0, -4); + pp = getpass("Password: "); + p = crypt(pp, salt); + setpriority(PRIO_PROCESS, 0, 0); + +#ifdef KERBEROS + + /* + * If not present in pw file, act as we normally would. + * If we aren't Kerberos-authenticated, try the normal + * pw file for a password. If that's ok, log the user + * in without issueing any tickets. + */ + + if (pwd && !krb_get_lrealm(realm,1)) { + /* + * get TGT for local realm; be careful about uid's + * here for ticket file ownership + */ + (void)setreuid(geteuid(),pwd->pw_uid); + kerror = krb_get_pw_in_tkt(pwd->pw_name, "", realm, + "krbtgt", realm, DEFAULT_TKT_LIFE, pp); + (void)setuid(0); + if (kerror == INTK_OK) { + memset(pp, 0, strlen(pp)); + notickets = 0; /* user got ticket */ + break; + } + } +#endif + (void) memset(pp, 0, strlen(pp)); + if (pwd && !strcmp(p, pwd->pw_passwd)) + break; + + (void)printf("Login incorrect\n"); + failures++; + badlogin(username); /* log ALL bad logins */ + + /* we allow 10 tries, but after 3 we start backing off */ + if (++cnt > 3) { + if (cnt >= 10) { + sleepexit(1); + } + sleep((unsigned int)((cnt - 3) * 5)); + } + } + + /* committed to login -- turn off timeout */ + (void)alarm((unsigned int)0); + +#ifdef HAVE_QUOTA + if (quota(Q_SETUID, pwd->pw_uid, 0, 0) < 0 && errno != EINVAL) { + switch(errno) { + case EUSERS: + (void)fprintf(stderr, + "Too many users logged on already.\nTry again later.\n"); + break; + case EPROCLIM: + (void)fprintf(stderr, + "You have too many processes running.\n"); + break; + default: + perror("quota (Q_SETUID)"); + } + sleepexit(0); + } +#endif + + /* paranoia... */ + endpwent(); + + /* This requires some explanation: As root we may not be able to + read the directory of the user if it is on an NFS mounted + filesystem. We temporarily set our effective uid to the user-uid + making sure that we keep root privs. in the real uid. + + A portable solution would require a fork(), but we rely on Linux + having the BSD setreuid() */ + + { + char tmpstr[MAXPATHLEN]; + uid_t ruid = getuid(); + gid_t egid = getegid(); + + strncpy(tmpstr, pwd->pw_dir, MAXPATHLEN-12); + strncat(tmpstr, ("/" _PATH_HUSHLOGIN), MAXPATHLEN); + + setregid(-1, pwd->pw_gid); + setreuid(0, pwd->pw_uid); + quietlog = (access(tmpstr, R_OK) == 0); + setuid(0); /* setreuid doesn't do it alone! */ + setreuid(ruid, 0); + setregid(-1, egid); + } + +#ifndef linux +#ifdef KERBEROS + if (notickets && !quietlog) + (void)printf("Warning: no Kerberos tickets issued\n"); +#endif + +#define TWOWEEKS (14*24*60*60) + if (pwd->pw_change || pwd->pw_expire) + (void)gettimeofday(&tp, (struct timezone *)NULL); + if (pwd->pw_change) + if (tp.tv_sec >= pwd->pw_change) { + (void)printf("Sorry -- your password has expired.\n"); + sleepexit(1); + } + else if (tp.tv_sec - pwd->pw_change < TWOWEEKS && !quietlog) { + ttp = localtime(&pwd->pw_change); + (void)printf("Warning: your password expires on %s %d, %d\n", + months[ttp->tm_mon], ttp->tm_mday, TM_YEAR_BASE + ttp->tm_year); + } + if (pwd->pw_expire) + if (tp.tv_sec >= pwd->pw_expire) { + (void)printf("Sorry -- your account has expired.\n"); + sleepexit(1); + } + else if (tp.tv_sec - pwd->pw_expire < TWOWEEKS && !quietlog) { + ttp = localtime(&pwd->pw_expire); + (void)printf("Warning: your account expires on %s %d, %d\n", + months[ttp->tm_mon], ttp->tm_mday, TM_YEAR_BASE + ttp->tm_year); + } + + /* nothing else left to fail -- really log in */ + { + struct utmp utmp; + + memset((char *)&utmp, 0, sizeof(utmp)); + (void)time(&utmp.ut_time); + strncpy(utmp.ut_name, username, sizeof(utmp.ut_name)); + if (hostname) + strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host)); + strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line)); + login(&utmp); + } +#else + /* for linux, write entries in utmp and wtmp */ + { + struct utmp ut; + char *ttyabbrev; + int wtmp; + + memset((char *)&ut, 0, sizeof(ut)); + ut.ut_type = USER_PROCESS; + ut.ut_pid = getpid(); + strncpy(ut.ut_line, ttyn + sizeof("/dev/")-1, sizeof(ut.ut_line)); + ttyabbrev = ttyn + sizeof("/dev/tty") - 1; + strncpy(ut.ut_id, ttyabbrev, sizeof(ut.ut_id)); + (void)time(&ut.ut_time); + strncpy(ut.ut_user, username, sizeof(ut.ut_user)); + + /* fill in host and ip-addr fields when we get networking */ + if (hostname) { + struct hostent *he; + + strncpy(ut.ut_host, hostname, sizeof(ut.ut_host)); + if ((he = gethostbyname(hostname))) + memcpy(&ut.ut_addr, he->h_addr_list[0], + sizeof(ut.ut_addr)); + } + + utmpname(_PATH_UTMP); + setutent(); + pututline(&ut); + endutent(); + + if((wtmp = open(_PATH_WTMP, O_APPEND|O_WRONLY)) >= 0) { + flock(wtmp, LOCK_EX); + write(wtmp, (char *)&ut, sizeof(ut)); + flock(wtmp, LOCK_UN); + close(wtmp); + } + } + /* fix_utmp_type_and_user(username, ttyn, LOGIN_PROCESS); */ +#endif + + dolastlog(quietlog); + +#ifndef linux + if (!hflag) { /* XXX */ + static struct winsize win = { 0, 0, 0, 0 }; + + (void)ioctl(0, TIOCSWINSZ, &win); + } +#endif + (void)chown(ttyn, pwd->pw_uid, + (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid); + + (void)chmod(ttyn, 0622); + (void)setgid(pwd->pw_gid); + + initgroups(username, pwd->pw_gid); + +#ifdef HAVE_QUOTA + quota(Q_DOWARN, pwd->pw_uid, (dev_t)-1, 0); +#endif + + if (*pwd->pw_shell == '\0') + pwd->pw_shell = _PATH_BSHELL; +#ifndef linux + /* turn on new line discipline for the csh */ + else if (!strcmp(pwd->pw_shell, _PATH_CSHELL)) { + ioctlval = NTTYDISC; + (void)ioctl(0, TIOCSETD, &ioctlval); + } +#endif + + /* preserve TERM even without -p flag */ + { + char *ep; + + if(!((ep = getenv("TERM")) && (termenv = strdup(ep)))) + termenv = "dumb"; + } + + /* destroy environment unless user has requested preservation */ + if (!pflag) + { + environ = (char**)malloc(sizeof(char*)); + memset(environ, 0, sizeof(char*)); + } + +#ifndef linux + (void)setenv("HOME", pwd->pw_dir, 1); + (void)setenv("SHELL", pwd->pw_shell, 1); + if (term[0] == '\0') + strncpy(term, stypeof(tty), sizeof(term)); + (void)setenv("TERM", term, 0); + (void)setenv("USER", pwd->pw_name, 1); + (void)setenv("PATH", _PATH_DEFPATH, 0); +#else + (void)setenv("HOME", pwd->pw_dir, 0); /* legal to override */ + if(pwd->pw_uid) + (void)setenv("PATH", _PATH_DEFPATH, 1); + else + (void)setenv("PATH", _PATH_DEFPATH_ROOT, 1); + (void)setenv("SHELL", pwd->pw_shell, 1); + (void)setenv("TERM", termenv, 1); + + /* mailx will give a funny error msg if you forget this one */ + (void)sprintf(tmp,"%s/%s",_PATH_MAILDIR,pwd->pw_name); + (void)setenv("MAIL",tmp,0); + + /* LOGNAME is not documented in login(1) but + HP-UX 6.5 does it. We'll not allow modifying it. + */ + (void)setenv("LOGNAME", pwd->pw_name, 1); +#endif + +#ifndef linux + if (tty[sizeof("tty")-1] == 'd') + syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name); +#endif + if (pwd->pw_uid == 0) + if (hostname) + syslog(LOG_NOTICE, "ROOT LOGIN ON %s FROM %s", + tty, hostname); + else + syslog(LOG_NOTICE, "ROOT LOGIN ON %s", tty); + + if (!quietlog) { + struct stat st; + + motd(); + (void)sprintf(tbuf, "%s/%s", _PATH_MAILDIR, pwd->pw_name); + if (stat(tbuf, &st) == 0 && st.st_size != 0) + (void)printf("You have %smail.\n", + (st.st_mtime > st.st_atime) ? "new " : ""); + } + + (void)signal(SIGALRM, SIG_DFL); + (void)signal(SIGQUIT, SIG_DFL); + (void)signal(SIGINT, SIG_DFL); + (void)signal(SIGTSTP, SIG_IGN); + (void)signal(SIGHUP, SIG_DFL); + + /* discard permissions last so can't get killed and drop core */ + if(setuid(pwd->pw_uid) < 0 && pwd->pw_uid) { + syslog(LOG_ALERT, "setuid() failed"); + exit(1); + } + + /* wait until here to change directory! */ + if (chdir(pwd->pw_dir) < 0) { + (void)printf("No directory %s!\n", pwd->pw_dir); + if (chdir("/")) + exit(0); + pwd->pw_dir = "/"; + (void)printf("Logging in with home = \"/\".\n"); + } + + /* if the shell field has a space: treat it like a shell script */ + if (strchr(pwd->pw_shell, ' ')) { + char *buff = malloc(strlen(pwd->pw_shell) + 6); + if (buff) { + strcpy(buff, "exec "); + strcat(buff, pwd->pw_shell); + execlp("/bin/sh", "-sh", "-c", buff, (char *)0); + fprintf(stderr, "login: couldn't exec shell script: %s.\n", + strerror(errno)); + exit(0); + } + fprintf(stderr, "login: no memory for shell script.\n"); + exit(0); + } + + tbuf[0] = '-'; + strcpy(tbuf + 1, ((p = rindex(pwd->pw_shell, '/')) ? + p + 1 : pwd->pw_shell)); + + execlp(pwd->pw_shell, tbuf, (char *)0); + (void)fprintf(stderr, "login: no shell: %s.\n", strerror(errno)); + exit(0); +} + +void +getloginname() +{ + register int ch; + register char *p; + static char nbuf[UT_NAMESIZE + 1]; + + for (;;) { + (void)printf("\n%s login: ", thishost); fflush(stdout); + for (p = nbuf; (ch = getchar()) != '\n'; ) { + if (ch == EOF) { + badlogin(username); + exit(0); + } + if (p < nbuf + UT_NAMESIZE) + *p++ = ch; + } + if (p > nbuf) + if (nbuf[0] == '-') + (void)fprintf(stderr, + "login names may not start with '-'.\n"); + else { + *p = '\0'; + username = nbuf; + break; + } + } +} + +void timedout() +{ + struct termio ti; + + (void)fprintf(stderr, "Login timed out after %d seconds\n", timeout); + + /* reset echo */ + (void) ioctl(0, TCGETA, &ti); + ti.c_lflag |= ECHO; + (void) ioctl(0, TCSETA, &ti); + exit(0); +} + +int +rootterm(ttyn) + char *ttyn; +#ifndef linux +{ + struct ttyent *t; + + return((t = getttynam(ttyn)) && t->ty_status&TTY_SECURE); +} +#else +{ + int fd; + char buf[100],*p; + int cnt, more; + + fd = open(SECURETTY, O_RDONLY); + if(fd < 0) return 1; + + /* read each line in /etc/securetty, if a line matches our ttyline + then root is allowed to login on this tty, and we should return + true. */ + for(;;) { + p = buf; cnt = 100; + while(--cnt >= 0 && (more = read(fd, p, 1)) == 1 && *p != '\n') p++; + if(more && *p == '\n') { + *p = '\0'; + if(!strcmp(buf, ttyn)) { + close(fd); + return 1; + } else + continue; + } else { + close(fd); + return 0; + } + } +} +#endif + +jmp_buf motdinterrupt; + +void +motd() +{ + register int fd, nchars; + void (*oldint)(), sigint(); + char tbuf[8192]; + + if ((fd = open(_PATH_MOTDFILE, O_RDONLY, 0)) < 0) + return; + oldint = signal(SIGINT, sigint); + if (setjmp(motdinterrupt) == 0) + while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0) + (void)write(fileno(stdout), tbuf, nchars); + (void)signal(SIGINT, oldint); + (void)close(fd); +} + +void sigint() +{ + longjmp(motdinterrupt, 1); +} + +void +checknologin() +{ + register int fd, nchars; + char tbuf[8192]; + + if ((fd = open(_PATH_NOLOGIN, O_RDONLY, 0)) >= 0) { + while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0) + (void)write(fileno(stdout), tbuf, nchars); + sleepexit(0); + } +} + +void +dolastlog(quiet) + int quiet; +{ + struct lastlog ll; + int fd; + + if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) { + (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET); + if (!quiet) { + if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) && + ll.ll_time != 0) { + (void)printf("Last login: %.*s ", + 24-5, (char *)ctime(&ll.ll_time)); + + if (*ll.ll_host != '\0') + printf("from %.*s\n", + (int)sizeof(ll.ll_host), ll.ll_host); + else + printf("on %.*s\n", + (int)sizeof(ll.ll_line), ll.ll_line); + } + (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET); + } + memset((char *)&ll, 0, sizeof(ll)); + (void)time(&ll.ll_time); + strncpy(ll.ll_line, tty, sizeof(ll.ll_line)); + if (hostname) + strncpy(ll.ll_host, hostname, sizeof(ll.ll_host)); + (void)write(fd, (char *)&ll, sizeof(ll)); + (void)close(fd); + } +} + +void +badlogin(name) + char *name; +{ + if (failures == 0) + return; + + if (hostname) + syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s, %s", + failures, failures > 1 ? "S" : "", hostname, name); + else + syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s, %s", + failures, failures > 1 ? "S" : "", tty, name); +} + +#undef UNKNOWN +#define UNKNOWN "su" + +#ifndef linux +char * +stypeof(ttyid) + char *ttyid; +{ + struct ttyent *t; + + return(ttyid && (t = getttynam(ttyid)) ? t->ty_type : UNKNOWN); +} +#endif + +void +checktty(user, tty) + char *user; + char *tty; +{ + FILE *f; + char buf[256]; + char *ptr; + char devname[50]; + struct stat stb; + + /* no /etc/usertty, default to allow access */ + if(!(f = fopen(_PATH_USERTTY, "r"))) return; + + while(fgets(buf, 255, f)) { + + /* strip comments */ + for(ptr = buf; ptr < buf + 256; ptr++) + if(*ptr == '#') *ptr = 0; + + strtok(buf, " \t"); + if(strncmp(user, buf, 8) == 0) { + while((ptr = strtok(NULL, "\t\n "))) { + if(strncmp(tty, ptr, 10) == 0) { + fclose(f); + return; + } + if(strcmp("PTY", ptr) == 0) { +#ifdef linux + sprintf(devname, "/dev/%s", ptr); + /* VERY linux dependent, recognize PTY as alias + for all pseudo tty's */ + if((stat(devname, &stb) >= 0) + && major(stb.st_rdev) == 4 + && minor(stb.st_rdev) >= 192) { + fclose(f); + return; + } +#endif + } + } + /* if we get here, /etc/usertty exists, there's a line + beginning with our username, but it doesn't contain the + name of the tty where the user is trying to log in. + So deny access! */ + fclose(f); + printf("Login on %s denied.\n", tty); + badlogin(user); + sleepexit(1); + } + } + fclose(f); + /* users not mentioned in /etc/usertty are by default allowed access + on all tty's */ +} + +void +getstr(buf, cnt, err) + char *buf, *err; + int cnt; +{ + char ch; + + do { + if (read(0, &ch, sizeof(ch)) != sizeof(ch)) + exit(1); + if (--cnt < 0) { + (void)fprintf(stderr, "%s too long\r\n", err); + sleepexit(1); + } + *buf++ = ch; + } while (ch); +} + +void +sleepexit(eval) + int eval; +{ + sleep((unsigned int)5); + exit(eval); +} + diff --git a/login-utils/mesg.1 b/login-utils/mesg.1 new file mode 100644 index 00000000..81932dfd --- /dev/null +++ b/login-utils/mesg.1 @@ -0,0 +1,24 @@ +.\" Original author: Miquel van Smoorenburg, miquels@drinkel.nl.mugnet.org +.\" Updated by faith@cs.unc.edu, Fri Oct 29 23:22:16 1993 +.TH MESG 1 "29 October 1993" "Linux 0.99" "Linux Programmer's Manual" +.SH NAME +mesg \- control write access to your terminal +.SH SYNOPSIS +.B mesg +.RB [y|n] +.br +.SH DESCRIPTION +.B Mesg +controls the access to your terminal by others. It's typically used +to allow/disallow others users to \fBwrite(1)\fP to your terminal. +.SH FLAGS +.IP y +Allow write access to your terminal. +.IP n +Disallow write access to your terminal. +.IP [none] +Prints out the current access state of your terminal. +.SH SEE ALSO +.BR write (1), wall (1) +.SH AUTHOR +Miquel van Smoorenburg, miquels@drinkel.nl.mugnet.org diff --git a/login-utils/mesg.c b/login-utils/mesg.c new file mode 100644 index 00000000..07c5fad1 --- /dev/null +++ b/login-utils/mesg.c @@ -0,0 +1,44 @@ +/* + * mesg.c The "mesg" utility. Gives / restrict access to + * your terminal by others. + * + * Usage: mesg [y|n]. + * Without arguments prints out the current settings. + */ +#include +#include +#include +#include +#include + +char *Version = "@(#) mesg 1.0 08-12-92 MvS"; + +int main(int argc, char **argv) +{ + struct stat st; + + if (!isatty(0)) { + /* Or should we look in /etc/utmp? */ + fprintf(stderr, "stdin: is not a tty"); + return(1); + } + + if (fstat(0, &st) < 0) { + perror("fstat"); + return(1); + } + if (argc < 2) { + printf("Is %s\n", ((st.st_mode & 022) == 022) ? "y" : "n"); + return(0); + } + if (argc > 2 || (argv[1][0] != 'y' && argv[1][0] != 'n')) { + fprintf(stderr, "Usage: mesg [y|n]\n"); + return(1); + } + if (argv[1][0] == 'y') + st.st_mode |= 022; + else + st.st_mode &= ~022; + fchmod(0, st.st_mode); + return(0); +} diff --git a/login-utils/newgrp.1 b/login-utils/newgrp.1 new file mode 100644 index 00000000..032a5d5a --- /dev/null +++ b/login-utils/newgrp.1 @@ -0,0 +1,24 @@ +.\" Original author unknown. This man page is in the public domain. +.\" Modified Sat Oct 9 17:46:48 1993 by faith@cs.unc.edu +.TH NEWGRP 1 "9 October 1993" "Linux 0.99" "Linux Programmer's Manual" +.SH NAME +newgrp \- log in to a new group +.SH SYNOPSIS +.BI "newgrp [ " group " ]" +.SH DESCRIPTION +.B Newgrp +changes the group identification of its caller, analogously to +.BR login (1). +The same person remains logged in, and the current directory +is unchanged, but calculations of access permissions to files are performed +with respect to the new group ID. +.LP +If no group is specified, the GID is changed to the login GID. +.LP +.SH FILES +.I /etc/group +.br +.I /etc/passwd + +.SH "SEE ALSO" +.BR login "(1), " group (5) diff --git a/login-utils/newgrp.c b/login-utils/newgrp.c new file mode 100644 index 00000000..ba13fdeb --- /dev/null +++ b/login-utils/newgrp.c @@ -0,0 +1,95 @@ +/* setgrp.c - by Michael Haardt. Set the gid if possible */ +/* Added a bit more error recovery/reporting - poe */ +/* Vesa Roukonen added code for asking password */ +/* Currently maintained at ftp://ftp.daimi.aau.dk/pub/linux/poe/ */ + +#include +#include +#include +#include +#include +#include +#include "pathnames.h" + +#ifndef TRUE +# define TRUE 1 +#endif + +#ifndef FALSE +# define FALSE 0 +#endif + +static int +allow_setgid(struct passwd *pe, struct group *ge) +{ + char **look; + int notfound = 1; + + if (getuid() == 0) return TRUE; /* root may do anything */ + + look = ge->gr_mem; + while (*look && (notfound = strcmp(*look++,pe->pw_name))); + + if(!notfound) return TRUE; /* member of group => OK */ + + /* Ask for password. Often there is no password in /etc/group, so + contrary to login et al. we let an empty password mean the same + as * in /etc/passwd */ + + if(ge->gr_passwd && ge->gr_passwd[0] != 0) { + if(strcmp(ge->gr_passwd, + crypt(getpass("Password: "), ge->gr_passwd)) == 0) { + return TRUE; /* password accepted */ + } + } + + return FALSE; /* default to denial */ +} + +int +main(int argc, char *argv[]) +{ + struct passwd *pw_entry; + struct group *gr_entry; + char *shell; + + if (!(pw_entry = getpwuid(getuid()))) { + perror("newgrp: Who are you?"); + exit(1); + } + + shell = (pw_entry->pw_shell[0] ? pw_entry->pw_shell : _PATH_BSHELL); + + if (argc < 2) { + if(setgid(pw_entry->pw_gid) < 0) { + perror("newgrp: setgid"); + exit(1); + } + } else { + if (!(gr_entry = getgrnam(argv[1]))) { + perror("newgrp: No such group."); + exit(1); + } else { + if(allow_setgid(pw_entry, gr_entry)) { + if(setgid(gr_entry->gr_gid) < 0) { + perror("newgrp: setgid"); + exit(1); + } + } else { + puts("newgrp: Permission denied"); + exit(1); + } + } + } + + if(setuid(getuid()) < 0) { + perror("newgrp: setuid"); + exit(1); + } + + fflush(stdout); fflush(stderr); + execl(shell,shell,(char*)0); + perror("No shell"); + fflush(stderr); + exit(1); +} diff --git a/login-utils/passwd.1 b/login-utils/passwd.1 new file mode 100644 index 00000000..d22c458f --- /dev/null +++ b/login-utils/passwd.1 @@ -0,0 +1,36 @@ +.\" Copyright 1992 Rickard E. Faith (faith@cs.unc.edu) +.\" May be distributed under the GNU General Public License +.TH PASSWD 1 "22 June 1994" "Linux 1.0" "Linux Programmer's Manual" +.SH NAME +passwd \- change password +.SH SYNOPSIS +.BR "passwd [ " name " ]" +.SH DESCRIPTION +.B passwd +will change the specified user's password. Only the superuser is allowed +to change other user's passwords. If the user is not root, then the old +password is prompted for and verified. + +A new password is prompted for twice, to avoid typing mistakes. Unless the +user is the superuser, the new password must have more than six characters, +and must have either both upper and lower case letters, or non-letters. +Some passwords which are similar to the user's name are not allowed. +.SH FILES +.I /etc/passwd +.br +.I /etc/shells +.SH "SEE ALSO" +.BR chsh (1), +.BR chfn (1) +.SH BUGS +A password consisting of all digits is allowed. +.br +No warnings are printed if the superuser chooses a poor password. +.br +The +.B \-f +and +.B \-s +options are not supported. +.SH AUTHOR +Peter Orbaek (poe@daimi.aau.dk) diff --git a/login-utils/passwd.c b/login-utils/passwd.c new file mode 100644 index 00000000..5bd6d3ab --- /dev/null +++ b/login-utils/passwd.c @@ -0,0 +1,193 @@ +/* passwd.c - change password on an account + * Initially written for Linux by Peter Orbaek + * Currently maintained at ftp://ftp.daimi.aau.dk/pub/linux/poe/ + */ + +/* Hacked by Alvaro Martinez Echevarria, alvaro@enano.etsit.upm.es, + to allow peaceful coexistence with yp. Nov 94. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern int is_local(char *); + +#define ascii_to_bin(c) ((c)>='a'?(c-59):(c)>='A'?((c)-53):(c)-'.') +#define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.') + +#define MAX_LENGTH 1024 + +int +main(argc, argv) + int argc; + char *argv[]; +{ + struct passwd *pe; + uid_t gotuid = getuid(); + char *pwdstr, *cryptstr; + char pwdstr1[10]; + int ucase, lcase, other; + char *p, *q, *user; + time_t tm; + char salt[2]; + FILE *fd_in, *fd_out; + char line[MAX_LENGTH]; + int error=0; + int r; + + umask(022); + + if(argc > 2) { + puts("Too many arguments"); + exit(1); + } else if(argc == 2) { + if(gotuid) { + puts("Only root can change the password for others"); + exit(1); + } + user = argv[1]; + } else { + if (!(user = getlogin())) { + if (!(pe = getpwuid( getuid() ))) { + puts("Cannot find login name"); + exit(1); + } else + user = pe->pw_name; + } + } + + if(!(pe = getpwnam(user))) { + puts("Can't find username anywhere. Are you really a user?"); + exit(1); + } + + if (!(is_local(user))) { + puts("Sorry, I can only change local passwords. Use yppasswd instead."); + exit(1); + } + + /* if somebody got into changing utmp... */ + if(gotuid && gotuid != pe->pw_uid) { + puts("UID and username does not match, imposter!"); + exit(1); + } + + printf( "Changing password for %s\n", user ); + + if(gotuid && pe->pw_passwd && pe->pw_passwd[0]) { + pwdstr = getpass("Enter old password: "); + if(strncmp(pe->pw_passwd, crypt(pwdstr, pe->pw_passwd), 13)) { + puts("Illegal password, imposter."); + exit(1); + } + } + +redo_it: + pwdstr = getpass("Enter new password: "); + strncpy(pwdstr1, pwdstr, 9); + pwdstr = getpass("Re-type new password: "); + + if(strncmp(pwdstr, pwdstr1, 8)) { + puts("You misspelled it. Password not changed."); + exit(0); + } + + if((strlen(pwdstr) < 6) && gotuid) { + puts("The password must have at least 6 characters, try again."); + goto redo_it; + } + + other = ucase = lcase = 0; + for(p = pwdstr; *p; p++) { + ucase = ucase || isupper(*p); + lcase = lcase || islower(*p); + other = other || !isalpha(*p); + } + + if((!ucase || !lcase) && !other && gotuid) { + puts("The password must have both upper- and lowercase"); + puts("letters, or non-letters; try again."); + goto redo_it; + } + + r = 0; + for(p = pwdstr, q = pe->pw_name; *q && *p; q++, p++) { + if(tolower(*p) != tolower(*q)) { + r = 1; + break; + } + } + + for(p = pwdstr + strlen(pwdstr)-1, q = pe->pw_name; + *q && p >= pwdstr; q++, p--) { + if(tolower(*p) != tolower(*q)) { + r += 2; + break; + } + } + + if(gotuid && r != 3) { + puts("Please don't use something like your username as password!"); + goto redo_it; + } + + /* do various other checks for stupid passwords here... */ + + time(&tm); + salt[0] = bin_to_ascii(tm & 0x3f); + salt[1] = bin_to_ascii((tm >> 5) & 0x3f); + cryptstr = crypt(pwdstr, salt); + + if(access("/etc/ptmp", F_OK) == 0) { + puts("/etc/ptmp exists, can't change password"); + exit(1); + } + + if(!(fd_out = fopen("/etc/ptmp", "w"))) { + puts("Can't open /etc/ptmp, can't update password"); + exit(1); + } + + if(!(fd_in = fopen("/etc/passwd", "r"))) { + puts("Can't read /etc/passwd, can't update password"); + exit(1); + } + while(fgets(line, sizeof(line), fd_in)) { + if(!strncmp(line,user,strlen(user))) { + pe->pw_passwd = cryptstr; + if(putpwent(pe, fd_out) < 0) { + error = 1; + } + } else { + if(fputs(line,fd_out) < 0) { + error = 1; + } + } + if(error) { + puts("Error while writing new password file, password not changed."); + fclose(fd_out); + endpwent(); + unlink("/etc/ptmp"); + exit(1); + } + } + fclose(fd_in); + fclose(fd_out); + + unlink("/etc/passwd.OLD"); + link("/etc/passwd", "/etc/passwd.OLD"); + unlink("/etc/passwd"); + link("/etc/ptmp", "/etc/passwd"); + unlink("/etc/ptmp"); + chmod("/etc/passwd", 0644); + chown("/etc/passwd", 0, 0); + + puts("Password changed."); + exit(0); +} diff --git a/login-utils/reboot.8 b/login-utils/reboot.8 new file mode 100644 index 00000000..386d9715 --- /dev/null +++ b/login-utils/reboot.8 @@ -0,0 +1 @@ +.so man8/shutdown.8 diff --git a/login-utils/setpwnam.c b/login-utils/setpwnam.c new file mode 100644 index 00000000..f7e6eb31 --- /dev/null +++ b/login-utils/setpwnam.c @@ -0,0 +1,209 @@ +/* + * setpwnam.c -- + * edit an entry in a password database. + * + * (c) 1994 Salvatore Valente + * This file is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This file is distributed with no warranty. + * + * Usage: + * 1) get a struct passwd * from getpwnam(). + * You should assume a struct passwd has an infinite number of fields, + * so you should not try to create one from scratch. + * 2) edit the fields you want to edit. + * 3) call setpwnam() with the edited struct passwd. + * + * You should never directly read from or write to /etc/passwd. + * All user database queries should be directed through + * getpwnam() and setpwnam(). + * + * Thanks to "two guys named Ian". + */ +/* faith + * 1.1.1.1 + * 1995/02/22 19:09:24 + */ + +#define DEBUG 0 + +/* because I use getpwent(), putpwent(), etc... */ +#define _SVID_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef BSD43 +#include +#endif + +extern int errno; + +typedef int boolean; +#define false 0 +#define true 1 + +#ifndef DEBUG +#define PASSWD_FILE "/etc/passwd" +#define PTMP_FILE "/etc/ptmp" +#else +#define PASSWD_FILE "/tmp/passwd" +#define PTMP_FILE "/tmp/ptmp" +#endif + +static int copy_pwd (struct passwd *src, struct passwd *dest); +static char *xstrdup (char *str); + +/* + * setpwnam () -- + * takes a struct passwd in which every field is filled in and valid. + * If the given username exists in the passwd file, his entry is + * replaced with the given entry. + */ +int setpwnam (struct passwd *pwd) +{ + char *passwd = PASSWD_FILE; + char *ptmp = PTMP_FILE; + FILE *fp; + int x, save_errno, fd; + struct passwd *entry; + boolean found; + char buf[50]; + struct passwd spwd; + + /* getpwent() returns a pointer to a static buffer. + * "pwd" might have some from getpwent(), so we have to copy it to + * some other buffer before calling getpwent(). + */ + if (copy_pwd (pwd, &spwd) < 0) + return (-1); + + /* sanity check */ + for (x = 0; x < 3; x++) { + if (x > 0) sleep (1); + fd = open (ptmp, O_WRONLY|O_CREAT|O_EXCL, 00644); + if (fd >= 0) break; + } + if (fd < 0) return (-1); + + /* ptmp should be owned by root.root or root.wheel */ + if (chown (ptmp, (uid_t) 0, (gid_t) 0) < 0) + perror ("chown"); + + /* open ptmp for writing and passwd for reading */ + fp = fdopen (fd, "w"); + if (! fp) goto fail; + + setpwent (); + + /* parse the passwd file */ + found = false; + while ((entry = getpwent ()) != NULL) { + if (! strcmp (spwd.pw_name, entry->pw_name)) { + entry = &spwd; + found = true; + } + if (putpwent (entry, fp) < 0) goto fail; + } + if (fclose (fp) < 0) goto fail; + close (fd); + endpwent (); + + if (! found) { + errno = ENOENT; /* give me something better */ + goto fail; + } + + strcpy (buf, passwd); + strcat (buf, "~"); + /* we don't care if we can't remove the backup file */ + remove (buf); + /* we don't care if we can't create the backup file */ + link (passwd, buf); + /* we DO care if we can't erase the passwd file */ + if (remove (passwd) < 0) { + /* if the file is still there, fail */ + if (access (passwd, F_OK) == 0) goto fail; + } + /* if we can't link ptmp to passwd, all is lost */ + if (link (ptmp, passwd) < 0) { + /* reinstall_system (); */ + return (-1); + } + /* if we can't erase the ptmp file, we simply lose */ + if (remove (ptmp) < 0) + return (-1); + /* finally: success */ + return 0; + + fail: + save_errno = errno; + if (fp) fclose (fp); + if (fd >= 0) close (fd); + endpwent (); + remove (ptmp); + errno = save_errno; + return (-1); +} + +#define memzero(ptr, size) memset((char *) ptr, 0, size) +static int failed; + +static int copy_pwd (struct passwd *src, struct passwd *dest) +{ + /* this routine destroys abstraction barriers. it's not portable + * across systems, or even across different versions of the C library + * on a given system. it's dangerous and evil and wrong and I dispise + * getpwent() for forcing me to write this. + */ + failed = 0; + memzero (dest, sizeof (struct passwd)); + dest->pw_name = xstrdup (src->pw_name); + dest->pw_passwd = xstrdup (src->pw_passwd); + dest->pw_uid = src->pw_uid; + dest->pw_gid = src->pw_gid; + dest->pw_gecos = xstrdup (src->pw_gecos); + dest->pw_dir = xstrdup (src->pw_dir); + dest->pw_shell = xstrdup (src->pw_shell); + return (failed); +} + +static char *xstrdup (char *str) +{ + char *dup; + + if (! str) + return NULL; + dup = (char *) malloc (strlen (str) + 1); + if (! dup) { + failed = -1; + return NULL; + } + strcpy (dup, str); + return dup; +} + +#ifdef NO_PUTPWENT + +int putpwent (const struct passwd *p, FILE *stream) +{ + if (p == NULL || stream == NULL) { + errno = EINVAL; + return (-1); + } + if (fprintf (stream, "%s:%s:%u:%u:%s:%s:%s\n", + p->pw_name, p->pw_passwd, p->pw_uid, p->pw_gid, + p->pw_gecos, p->pw_dir, p->pw_shell) < 0) + return (-1); + return(0); +} + +#endif diff --git a/login-utils/shutdown.8 b/login-utils/shutdown.8 new file mode 100644 index 00000000..78eb984b --- /dev/null +++ b/login-utils/shutdown.8 @@ -0,0 +1,112 @@ +.\" Copyright 1992 Rickard E. Faith (faith@cs.unc.edu) +.\" May be distributed under the GNU General Public License +.TH SHUTDOWN 8 "20 November 1993" "Linux 0.99" "Linux Programmer's Manual" +.SH NAME +shutdown \- close down the system +.SH SYNOPSIS +.nf +.BR "shutdown [ \-h | \-r ] [ \-fqs ] [ now | " hh:ss " | " +mins " ]" +.BR "reboot [ \-h | \-r ] [ \-fqs ] [ now | " hh:ss " | " +mins " ]" +.BR "fastboot [ \-h | \-r ] [ \-fqs ] [ now | " hh:ss " | " +mins " ]" +.BR "halt [ \-h | \-r ] [ \-fqs ] [ now | " hh:ss " | " +mins " ]" +.BR "fasthalt [ \-h | \-r ] [ \-fqs ] [ now | " hh:ss " | " +mins " ]" +.fi +.SH DESCRIPTION +.\" " for emacs hilit19 +In general, +.B shutdown +prepares the system for a power down or reboot. A absolute or delta time +can be given, and periodic messages will be sent to all users warning of +the shutdown. + +.B halt +is the same as +.B "shutdown -h -q now" + +.B fasthalt +is the same as +.B "shutdown -h -q -f now" + +.B reboot +is the same as +.B "shutdown -r -q now" + +.B fastboot +is the same as +.B "shutdown -r -q -f now" + +The default delta time, if none is specified, is 2 minutes. + +Five minutes before shutdown (or immediately, if shutdown is less than five +minutes away), the +.I /etc/nologin +file is created with a message stating that the system is going down and +that logins are no longer permitted. The +.B login (1) +program will not allow non-superusers to login during this period. A +message will be sent to all users at this time. + +When the shutdown time arrives, +.B shutdown +notifies all users, tells +.BR init (8) +not to spawn more +.BR getty (8)'s, +writes the shutdown time into the +.I /var/adm/wtmp +file, kills all other processes on the system, +.BR sync (2)'s, +unmounts all the disks, +.BR sync (2)'s +again, waits for a second, and then either terminates or reboots the +system. +.SH OPTIONS +.TP +.B \-h +Halt the system. Do not reboot. This option is used when powering down +the system. +.TP +.B \-r +Reboot the system. +.TP +.B \-f +Fast. When the system is rebooted, the file systems will not be checked. +This is arranged by creating +.IR /fastboot , +which +.I /etc/rc +must detect (and delete). +.TP +.B \-q +Quiet. This uses a default broadcast message, and does not prompt the user +for one. +.TP +.B \-s +Reboot in single user mode. This is arranged by creating +.IR /etc/singleboot , +which +.BR simpleinit (8) +detects (and deletes). +.SH FILES +.nf +.I /etc/rc +.I /fastboot +.I /etc/singleboot +.I /etc/nologin +.I /var/adm/wtmp +.fi +.SH "SEE ALSO" +.BR umount (8), +.BR login (1), +.BR reboot (2), +.BR simpleinit (8), +.BR init (8) +.SH BUGS +Unlike the BSD +.BR shutdown , +users are notified of shutdown only once or twice, instead of many times, +and at shorter and shorter intervals as "apocalypse approaches." +.SH AUTHOR +poe@daimi.aau.dk +.br +Modified by jrs@world.std.com diff --git a/login-utils/shutdown.c b/login-utils/shutdown.c new file mode 100644 index 00000000..0ca30398 --- /dev/null +++ b/login-utils/shutdown.c @@ -0,0 +1,435 @@ +/* shutdown.c - shutdown a Linux system + * Initially written by poe@daimi.aau.dk + * Currently maintained at ftp://ftp.daimi.aau.dk/pub/linux/poe/ + */ + +/* + * Modified by jrs@world.std.com to try to exec "umount -a" and if + * that doesn't work, then umount filesystems ourselves in reverse + * order. The old-way was in forward order. Also if the device + * field of the mtab does not start with a "/" then give umount + * the mount point instead. This is needed for the nfs and proc + * filesystems and yet is compatible with older systems. + * + * We also use the mntent library interface to read the mtab file + * instead of trying to parse it directly and no longer give a + * warning about not being able to umount the root. + * + * The reason "umount -a" should be tried first is because it may do + * special processing for some filesystems (such as informing an + * nfs server about nfs umounts) that we don't want to cope with here. + */ + +/* + * Various changes and additions to resemble SunOS 4 shutdown/reboot/halt(8) + * more closely by Scott Telford (s.telford@ed.ac.uk) 93/05/18. + * (I butchered Scotts patches somewhat. - poe) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pathnames.h" + +void usage(), int_handler(), write_user(struct utmp *); +void wall(), write_wtmp(), unmount_disks(), unmount_disks_ourselves(); + +char *prog; /* name of the program */ +int opt_reboot; /* true if -r option or reboot command */ +int timeout; /* number of seconds to shutdown */ +int opt_quiet; /* true if no message is wanted */ +int opt_fast; /* true if fast boot */ +char message[90]; /* reason for shutdown if any... */ +int opt_single = 0; /* true is we want to boot singleuser */ +char *whom; /* who is shutting the system down */ + +/* #define DEBUGGING */ + +#define WR(s) write(fd, s, strlen(s)) + +void +usage() +{ + fprintf(stderr, + "Usage: shutdown [-h|-r] [-fqs] [now|hh:ss|+mins]\n"); + exit(1); +} + +void +int_handler() +{ + unlink(_PATH_NOLOGIN); + signal(SIGINT, SIG_DFL); + puts("Shutdown process aborted\n"); + exit(1); +} + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int c,i; + int fd; + char *ptr; + +#ifndef DEBUGGING + if(geteuid()) { + fprintf(stderr, "Only root can shut a system down.\n"); + exit(1); + } +#endif + + if(*argv[0] == '-') argv[0]++; /* allow shutdown as login shell */ + prog = argv[0]; + if(ptr = strrchr(argv[0], '/')) prog = ++ptr; + + if(!strcmp("halt", prog)) { + opt_reboot = 0; + opt_quiet = 1; + opt_fast = 0; + timeout = 0; + } else if(!strcmp("fasthalt", prog)) { + opt_reboot = 0; + opt_quiet = 1; + opt_fast = 1; + timeout = 0; + } else if(!strcmp("reboot", prog)) { + opt_reboot = 1; + opt_quiet = 1; + opt_fast = 0; + timeout = 0; + if(argc > 1 && !strcmp(argv[1], "-s")) opt_single = 1; + } else if(!strcmp("fastboot", prog)) { + opt_reboot = 1; + opt_quiet = 1; + opt_fast = 1; + timeout = 0; + if(argc > 1 && !strcmp(argv[1], "-s")) opt_single = 1; + } else { + /* defaults */ + opt_reboot = 0; + opt_quiet = 0; + opt_fast = 0; + timeout = 2*60; + + c = 0; + while(++c < argc) { + if(argv[c][0] == '-') { + for(i = 1; argv[c][i]; i++) { + switch(argv[c][i]) { + case 'h': + opt_reboot = 0; + break; + case 'r': + opt_reboot = 1; + break; + case 'f': + opt_fast = 1; + break; + case 'q': + opt_quiet = 1; + break; + case 's': + opt_single = 1; + break; + + default: + usage(); + } + } + } else if(!strcmp("now", argv[c])) { + timeout = 0; + } else if(argv[c][0] == '+') { + timeout = 60 * atoi(&argv[c][1]); + } else { + char *colon; + int hour = 0; + int minute = 0; + time_t tics; + struct tm *tt; + int now, then; + + if(colon = strchr(argv[c], ':')) { + *colon = '\0'; + hour = atoi(argv[c]); + minute = atoi(++colon); + } else usage(); + + (void) time(&tics); + tt = localtime(&tics); + + now = 3600 * tt->tm_hour + 60 * tt->tm_min; + then = 3600 * hour + 60 * minute; + timeout = then - now; + if(timeout < 0) { + fprintf(stderr, "That must be tomorrow, can't you wait till then?\n"); + exit(1); + } + } + } + } + + if(!opt_quiet) { + /* now ask for message, gets() is insecure */ + int cnt = sizeof(message)-1; + char *ptr; + + printf("Why? "); fflush(stdout); + + ptr = message; + while(--cnt >= 0 && (*ptr = getchar()) && *ptr != '\n') { + ptr++; + } + *ptr = '\0'; + } else + strcpy(message, "for maintenance; bounce, bounce"); + +#ifdef DEBUGGING + printf("timeout = %d, quiet = %d, reboot = %d\n", + timeout, opt_quiet, opt_reboot); +#endif + + /* so much for option-processing, now begin termination... */ + if(!(whom = getlogin())) whom = "ghost"; + + setpriority(PRIO_PROCESS, 0, PRIO_MIN); + signal(SIGINT, int_handler); + signal(SIGHUP, int_handler); + signal(SIGQUIT, int_handler); + signal(SIGTERM, int_handler); + + chdir("/"); + + if(timeout > 5*60) { + sleep(timeout - 5*60); + timeout = 5*60; + } + + + if((fd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT)) >= 0) { + WR("\r\nThe system is being shut down within 5 minutes\r\n"); + write(fd, message, strlen(message)); + WR("\r\nLogin is therefore prohibited.\r\n"); + close(fd); + } + + signal(SIGPIPE, SIG_IGN); + + if(timeout > 0) { + wall(); + sleep(timeout); + } + + timeout = 0; + wall(); + sleep(3); + + /* now there's no turning back... */ + signal(SIGINT, SIG_IGN); + + /* do syslog message... */ + openlog(prog, LOG_CONS, LOG_AUTH); + syslog(LOG_NOTICE, "%s by %s: %s", + opt_reboot ? "rebooted" : "halted", whom, message); + closelog(); + + if(opt_fast) + if((fd = open("/fastboot", O_WRONLY|O_CREAT)) >= 0) + close(fd); + + kill(1, SIGTSTP); /* tell init not to spawn more getty's */ + write_wtmp(); + if(opt_single) + close(open(_PATH_SINGLE, O_CREAT|O_WRONLY)); + + sync(); + + signal(SIGTERM, SIG_IGN); + if(fork() > 0) sleep(1000); /* the parent will die soon... */ + setpgrp(); /* so the shell wont kill us in the fall */ + +#ifndef DEBUGGING + /* a gentle kill of all other processes except init */ + kill(-1, SIGTERM); + sleep(2); + + /* now use brute force... */ + kill(-1, SIGKILL); + + /* turn off accounting */ + acct(NULL); +#endif + sync(); + sleep(2); + + /* unmount disks... */ + unmount_disks(); + sync(); + sleep(1); + + if(opt_reboot) { + reboot(0xfee1dead, 672274793, 0x1234567); + } else { + printf("\nNow you can turn off the power...\n"); + /* allow C-A-D now, faith@cs.unc.edu */ + reboot(0xfee1dead, 672274793, 0x89abcdef); + } + /* NOTREACHED */ + exit(0); /* to quiet gcc */ +} + +/*** end of main() ***/ + +void +write_user(struct utmp *ut) +{ + int fd; + int minutes, hours; + char term[40] = {'/','d','e','v','/',0}; + char msg[100]; + + minutes = timeout / 60; + (void) strncat(term, ut->ut_line, sizeof(ut->ut_line)); + + /* try not to get stuck on a mangled ut_line entry... */ + if((fd = open(term, O_RDWR|O_NONBLOCK)) < 0) + return; + + sprintf(msg, "\007\r\nURGENT: broadcast message from %s:\r\n", whom); + WR(msg); + + if(minutes == 0) { + sprintf(msg, "System going down IMMEDIATELY!\r\n\n"); + } else if(minutes > 60) { + hours = minutes / 60; + sprintf(msg, "System going down in %d hour%s %d minutes\r\n", + hours, hours == 1 ? "" : "s", minutes - 60*hours); + } else { + sprintf(msg, "System going down in %d minute%s\r\n\n", + minutes, minutes == 1 ? "" : "s"); + } + WR(msg); + + sprintf(msg, "\t... %s ...\r\n\n", message); + WR(msg); + + close(fd); +} + +void +wall() +{ + /* write to all users, that the system is going down. */ + struct utmp *ut; + + utmpname(_PATH_UTMP); + setutent(); + + while(ut = getutent()) { + if(ut->ut_type == USER_PROCESS) + write_user(ut); + } + endutent(); +} + +void +write_wtmp() +{ + /* write in wtmp that we are dying */ + int fd; + struct utmp ut; + + memset((char *)&ut, 0, sizeof(ut)); + strcpy(ut.ut_line, "~"); + memcpy(ut.ut_name, "shutdown", sizeof(ut.ut_name)); + + time(&ut.ut_time); + ut.ut_type = BOOT_TIME; + + if((fd = open(_PATH_WTMP, O_WRONLY|O_APPEND)) > 0) { + write(fd, (char *)&ut, sizeof(ut)); + close(fd); + } +} + +void +unmount_disks() +{ + /* better to use umount directly because it may be smarter than us */ + + int pid; + int result; + int status; + + sync(); + if ((pid = fork()) < 0) { + printf("Cannot fork for umount, trying manually.\n"); + unmount_disks_ourselves(); + return; + } + if (!pid) { + execl(_PATH_UMOUNT, UMOUNT_ARGS, NULL); + printf("Cannot exec %s, trying umount.\n", _PATH_UMOUNT); + execlp("umount", UMOUNT_ARGS, NULL); + printf("Cannot exec umount, trying manually.\n"); + unmount_disks_ourselves(); + exit(0); + } + while ((result = wait(&status)) != -1 && result != pid) + ; + if (result == -1 || status) { + printf("Running umount failed, trying manually.\n"); + unmount_disks_ourselves(); + } +} + +void +unmount_disks_ourselves() +{ + /* unmount all disks */ + + FILE *mtab; + struct mntent *mnt; + char *mntlist[128]; + int i; + int n; + char *filesys; + + sync(); + if (!(mtab = setmntent(_PATH_MTAB, "r"))) { + printf("Cannot open %s.\n", _PATH_MTAB); + return; + } + n = 0; + while (n < 100 && (mnt = getmntent(mtab))) { + mntlist[n++] = strdup(mnt->mnt_fsname[0] == '/' ? + mnt->mnt_fsname : mnt->mnt_dir); + } + endmntent(mtab); + + /* we are careful to do this in reverse order of the mtab file */ + + for (i = n - 1; i >= 0; i--) { + filesys = mntlist[i]; +#ifdef DEBUGGING + printf("umount %s\n", filesys); +#else + if (umount(mntlist[i]) < 0) + printf("Couldn't umount %s\n", filesys); +#endif + } +} diff --git a/login-utils/simpleinit.8 b/login-utils/simpleinit.8 new file mode 100644 index 00000000..a506e1ae --- /dev/null +++ b/login-utils/simpleinit.8 @@ -0,0 +1,142 @@ +.\" Copyright 1992, 1993 Rickard E. Faith (faith@cs.unc.edu) +.\" May be distributed under the GNU General Public License +.\" " for emacs's hilit19 mode :-) +.TH SIMPLEINIT 8 "20 November 1993" "Linux 0.99" "Linux Programmer's Manual" +.SH NAME +simpleinit \- process control initialization +.SH SYNOPSIS +.B "init [ single ]" +.SH DESCRIPTION +.B init +is invoked as the last step in the Linux boot sequence. If the +.B single +option is used, or if the file +.I /etc/singleboot +exists, then single user mode will be entered, by starting +.IR /bin/sh . +If the file +.I /etc/securesingle +exists, then the root password will be required to start single user mode. +If the root password does not exist, or if +.I /etc/passwd +does not exist, the checking of the password will be skipped. + +If the file +.I /etc/TZ +exists, then the contents of that file will be read, and used to set the TZ +environment variable for each process started by +.BR simpleinit . +This "feature" is only available if it's configured at compile-time. It's +not normally needed. + +After single user mode is terminated, the +.I /etc/rc +file is executed, and the information in +.I /etc/inittab +will be used to start processes. + +While +.B init +is running, several signals are trapped, with special action taken. Since +.B init +has PID 1, sending signals to the +.B init +process is easy with the +.BR kill (1) +command. + +If +.B init +catches a SIGHUP (hangup) signal, the +.I /etc/inittab +will be read again. + +If +.B init +catches a SIGTSTP (terminal stop) signal, no more processes will be +spawned. This is a toggle, which is reset is +.B init +catches another SIGTSTP signal. + +If +.B init +catches a SIGINT (interrupt) signal, +.B init +will sync a few times, and try to start +.IR reboot . +Failing this, +.B init +will execute the system +.BR reboot (2) +call. Under Linux, it is possible to configure the Ctrl-Alt-Del sequence +to send a signal to +.B init +instead of rebooting the system. +.SH "THE INITTAB FILE" +Because of the number of init programs which are appearing in the Linux +community, the documentation for the +.I /etc/inittab +file, which is usually found with the +.BR inittab (5) +man page, is presented here: + +The format is + +.RS +.B "ttyline:termcap-entry:getty-command" +.RE + +An example is as follows: + +.nf +.RS +tty1:console:/sbin/getty 9600 tty1 +tty2:console:/sbin/getty 9600 tty2 +tty3:console:/sbin/getty 9600 tty3 +tty4:console:/sbin/getty 9600 tty4 +# tty5:console:/sbin/getty 9600 tty5 +# ttyS1:dumb:/sbin/getty 9600 ttyS1 +# ttyS2:dumb:/sbin/getty -m -t60 2400 ttyS2 +.RE +.fi + +Lines beginning with the +.B # +character are treated as comments. Please see documentation for the +.B getty (8) +command that you are using, since there are several of these in the Linux +community at this time. +.SH FILES +.I /etc/inittab +.br +.I /etc/singleboot +.br +.I /etc/securesingle +.br +.I /etc/TZ +.br +.I /etc/passwd +.br +.I /etc/rc +.SH "SEE ALSO" +.BR inittab (5), +.BR ctrlaltdel (8) +.BR reboot (8), +.BR termcap (5), +.BR getty (8), +.BR agetty (8), +.BR shutdown (8) +.SH BUGS +This program is called +.B simpleinit +to distinguish it from the System V compatible versions of init which are +starting to appear in the Linux community. +.B simpleinit +should be linked to, or made identical with, +.I init +for correct functionality. +.SH AUTHOR +Peter Orbaek (poe@daimi.aau.dk) +.br +Version 1.20, with patches for singleuser mode by Werner Almesberger + diff --git a/login-utils/simpleinit.c b/login-utils/simpleinit.c new file mode 100644 index 00000000..9b31c15e --- /dev/null +++ b/login-utils/simpleinit.c @@ -0,0 +1,445 @@ +/* simpleinit.c - poe@daimi.aau.dk */ +/* Version 1.21 */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef SHADOW_PWD +#include +#endif + +#include "pathnames.h" + +#define CMDSIZ 150 /* max size of a line in inittab */ +#define NUMCMD 30 /* max number of lines in inittab */ +#define NUMTOK 20 /* max number of tokens in inittab command */ + +#define RUN_RC +#define TZFILE "/etc/TZ" +char tzone[CMDSIZ]; +/* #define DEBUGGING */ + +/* Define this if you want init to ignore the termcap field in inittab for + console ttys. */ +/* #define SPECIAL_CONSOLE_TERM */ + +#define ever (;;) + +struct initline { + pid_t pid; + char tty[10]; + char termcap[30]; + char *toks[NUMTOK]; + char line[CMDSIZ]; +}; + +struct initline inittab[NUMCMD]; +int numcmd; +int stopped = 0; /* are we stopped */ + +int do_rc(); +void spawn(), hup_handler(), read_inittab(); +void tstp_handler(), int_handler(), set_tz(), write_wtmp(); +int boot_single(); + +void err(char *s) +{ + int fd; + + if((fd = open("/dev/console", O_WRONLY)) < 0) return; + + write(fd, "init: ", 6); + write(fd, s, strlen(s)); + close(fd); +} + +void +enter_single() +{ + pid_t pid; + int i; + + err("Booting to single user mode.\n"); + if((pid = fork()) == 0) { + /* the child */ + execl(_PATH_BSHELL, _PATH_BSHELL, NULL); + err("exec of single user shell failed\n"); + } else if(pid > 0) { + while(wait(&i) != pid) /* nothing */; + } else if(pid < 0) { + err("fork of single user shell failed\n"); + } + unlink(_PATH_SINGLE); +} + +int main(int argc, char *argv[]) +{ + int vec,i; + pid_t pid; + +#ifdef SET_TZ + set_tz(); +#endif + signal(SIGTSTP, tstp_handler); + signal(SIGINT, int_handler); + + /* + * start up in single user mode if /etc/singleboot exists or if + * argv[1] is "single". + */ + if(boot_single(0, argc, argv)) enter_single(); + +#ifdef RUN_RC + /*If we get a SIGTSTP before multi-user mode, do nothing*/ + while(stopped) + pause(); + if(do_rc() != 0 && boot_single(1, argc, argv) && !stopped) + enter_single(); + while(stopped) /*Also if /etc/rc fails & we get SIGTSTP*/ + pause(); +#endif + + write_wtmp(); /* write boottime record */ + + for(i = 0; i < NUMCMD; i++) + inittab[i].pid = -1; + + read_inittab(); + +#ifdef DEBUGGING + for(i = 0; i < numcmd; i++) { + char **p; + p = inittab[i].toks; + printf("toks= %s %s %s %s\n",p[0], p[1], p[2], p[3]); + printf("tty= %s\n", inittab[i].tty); + printf("termcap= %s\n", inittab[i].termcap); + } + exit(0); +#endif + signal(SIGHUP, hup_handler); + + for(i = 0; i < getdtablesize(); i++) close(i); + + for(i = 0; i < numcmd; i++) + spawn(i); + + for ever { + pid = wait(&vec); + + /* clear utmp entry, and append to wtmp if possible */ + { + struct utmp *ut; + int ut_fd; + + utmpname(_PATH_UTMP); + setutent(); + while(ut = getutent()) { + if(ut->ut_pid == pid) { + time(&ut->ut_time); + memset(&ut->ut_user, 0, UT_NAMESIZE); + memset(&ut->ut_host, 0, sizeof(ut->ut_host)); + ut->ut_type = DEAD_PROCESS; + ut->ut_pid = 0; + ut->ut_addr = 0; + endutent(); + pututline(ut); + if((ut_fd = open(_PATH_WTMP, O_APPEND|O_WRONLY)) >= 0) { + flock(ut_fd, LOCK_EX|LOCK_NB); + write(ut_fd, ut, sizeof(struct utmp)); + flock(ut_fd, LOCK_UN|LOCK_NB); + close(ut_fd); + } + break; + } + } + endutent(); + } + + for(i = 0; i < numcmd; i++) { + if(pid == inittab[i].pid || inittab[i].pid < 0) { + if(stopped) inittab[i].pid = -1; + else spawn(i); + break; + } + } + } +} + +#define MAXTRIES 3 /* number of tries allowed when giving the password */ + +/* + * return true if we should boot up in singleuser mode. If argv[i] is + * "single" or the file /etc/singleboot exists, then singleuser mode should + * be entered. If /etc/securesingle exists ask for root password first. + */ +int boot_single(int singlearg, int argc, char *argv[]) +{ + char *pass, *rootpass = NULL; + struct passwd *pwd; + int i; + + for(i = 1; i < argc; i++) { + if(argv[i] && !strcmp(argv[i], "single")) singlearg = 1; + } + + if(access(_PATH_SINGLE, 04) == 0 || singlearg) { + if(access(_PATH_SECURE, 04) == 0) { + if((pwd = getpwnam("root")) || (pwd = getpwuid(0))) + rootpass = pwd->pw_passwd; + else + return 1; /* a bad /etc/passwd should not lock out */ + + for(i = 0; i < MAXTRIES; i++) { + pass = getpass("Password: "); + if(pass == NULL) continue; + + if(!strcmp(crypt(pass, rootpass), rootpass)) { + return 1; + } + + puts("\nWrong password.\n"); + } + } else return 1; + } + return 0; +} + +/* + * run /etc/rc. The environment is passed to the script, so the RC environment + * variable can be used to decide what to do. RC may be set from LILO. + */ +int do_rc() +{ + pid_t pid; + int stat; + + if((pid = fork()) == 0) { + /* the child */ + char *argv[2]; + + argv[0] = _PATH_BSHELL; + argv[1] = (char *)0; + + close(0); + if(open(_PATH_RC, O_RDONLY, 0) == 0) { + execv(_PATH_BSHELL, argv); + err("exec rc failed\n"); + _exit(2); + } + err("open of rc file failed\n"); + _exit(1); + } else if(pid > 0) { + /* parent, wait till rc process dies before spawning */ + while(wait(&stat) != pid) /* nothing */; + } else if(pid < 0) { + err("fork of rc shell failed\n"); + } + return WEXITSTATUS(stat); +} + +void spawn(int i) +{ + pid_t pid; + int j; + + if((pid = fork()) < 0) { + inittab[i].pid = -1; + err("fork failed\n"); + return; + } + if(pid) { + /* this is the parent */ + inittab[i].pid = pid; + return; + } else { + /* this is the child */ + char term[40]; + char tz[CMDSIZ]; + char *env[3]; + + setsid(); + for(j = 0; j < getdtablesize(); j++) + (void) close(j); + + (void) sprintf(term, "TERM=%s", inittab[i].termcap); + env[0] = term; + env[1] = (char *)0; +#ifdef SET_TZ + (void) sprintf(tz, "TZ=%s", tzone); + env[1] = tz; +#endif + env[2] = (char *)0; + + execve(inittab[i].toks[0], inittab[i].toks, env); + err("exec failed\n"); + sleep(5); + _exit(1); + } +} + +void read_inittab() +{ + FILE *f; + char buf[CMDSIZ]; + int i,j,k; + char *ptr, *getty; + char tty[50]; + struct stat stb; + char *termenv, *getenv(); + + termenv = getenv("TERM"); /* set by kernel */ + /* termenv = "vt100"; */ + + if(!(f = fopen(_PATH_INITTAB, "r"))) { + err("cannot open inittab\n"); + _exit(1); + } + + i = 0; + while(!feof(f) && i < NUMCMD - 2) { + if(fgets(buf, CMDSIZ - 1, f) == 0) break; + buf[CMDSIZ-1] = 0; + + for(k = 0; k < CMDSIZ && buf[k]; k++) { + if(buf[k] == '#') { + buf[k] = 0; break; + } + } + + if(buf[0] == 0 || buf[0] == '\n') continue; + + (void) strcpy(inittab[i].line, buf); + + (void) strtok(inittab[i].line, ":"); + (void) strncpy(inittab[i].tty, inittab[i].line, 10); + inittab[i].tty[9] = 0; + (void) strncpy(inittab[i].termcap, + strtok((char *)0, ":"), 30); + inittab[i].termcap[29] = 0; + + getty = strtok((char *)0, ":"); + (void) strtok(getty, " \t\n"); + inittab[i].toks[0] = getty; + j = 1; + while(ptr = strtok((char *)0, " \t\n")) + inittab[i].toks[j++] = ptr; + inittab[i].toks[j] = (char *)0; + +#ifdef SPECIAL_CONSOLE_TERM + /* special-case termcap for the console ttys */ + (void) sprintf(tty, "/dev/%s", inittab[i].tty); + if(!termenv || stat(tty, &stb) < 0) { + err("no TERM or cannot stat tty\n"); + } else { + /* is it a console tty? */ + if(major(stb.st_rdev) == 4 && minor(stb.st_rdev) < 64) { + strncpy(inittab[i].termcap, termenv, 30); + inittab[i].termcap[29] = 0; + } + } +#endif + + i++; + } + fclose(f); + numcmd = i; +} + +void hup_handler() +{ + int i,j; + int oldnum; + struct initline savetab[NUMCMD]; + int had_already; + + (void) signal(SIGHUP, SIG_IGN); + + memcpy(savetab, inittab, NUMCMD * sizeof(struct initline)); + oldnum = numcmd; + read_inittab(); + + for(i = 0; i < numcmd; i++) { + had_already = 0; + for(j = 0; j < oldnum; j++) { + if(!strcmp(savetab[j].tty, inittab[i].tty)) { + had_already = 1; + if((inittab[i].pid = savetab[j].pid) < 0) + spawn(i); + } + } + if(!had_already) spawn(i); + } + + (void) signal(SIGHUP, hup_handler); +} + +void tstp_handler() +{ + stopped = ~stopped; + if(!stopped) hup_handler(); + + signal(SIGTSTP, tstp_handler); +} + +void int_handler() +{ + /* + * After Linux 0.96b PL1, we get a SIGINT when + * the user presses Ctrl-Alt-Del... + */ + + int pid; + + sync(); + sync(); + if((pid = fork()) == 0) { + /* reboot properly... */ + execl(_PATH_REBOOT, _PATH_REBOOT, (char *)0); + reboot(0xfee1dead, 672274793, 0x1234567); + } else if(pid < 0) + /* fork failed, try the hard way... */ + reboot(0xfee1dead, 672274793, 0x1234567); +} + +void set_tz() +{ + FILE *f; + int len; + + if((f=fopen(TZFILE, "r")) == (FILE *)NULL) return; + fgets(tzone, CMDSIZ-2, f); + fclose(f); + if((len=strlen(tzone)) < 2) return; + tzone[len-1] = 0; /* get rid of the '\n' */ + setenv("TZ", tzone, 0); +} + +void write_wtmp() +{ + int fd; + struct utmp ut; + + memset((char *)&ut, 0, sizeof(ut)); + strcpy(ut.ut_line, "~"); + memset(ut.ut_name, 0, sizeof(ut.ut_name)); + time(&ut.ut_time); + ut.ut_type = BOOT_TIME; + + if((fd = open(_PATH_WTMP, O_WRONLY|O_APPEND)) >= 0) { + flock(fd, LOCK_EX|LOCK_NB); /* make sure init won't hang */ + write(fd, (char *)&ut, sizeof(ut)); + flock(fd, LOCK_UN|LOCK_NB); + close(fd); + } +} diff --git a/login-utils/ttymsg.c b/login-utils/ttymsg.c new file mode 100644 index 00000000..40c178df --- /dev/null +++ b/login-utils/ttymsg.c @@ -0,0 +1,173 @@ +/* + * 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. + * + * Modified for Linux (which doesn\'t have snprintf()) by faith@cs.unc.edu + * on Sun Mar 7 16:14:18 1993. + * + */ + +#ifndef lint +static char sccsid[] = "@(#)ttymsg.c 5.8 (Berkeley) 7/1/91"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Display the contents of a uio structure on a terminal. Used by wall(1) + * and syslogd(8). Forks and finishes in child if write would block, waiting + * at most five minutes. Returns pointer to error string on unexpected error; + * string is not newline-terminated. Various "normal" errors are ignored + * (exclusive-use, lack of permission, etc.). + */ +char * +ttymsg(iov, iovcnt, line) + struct iovec *iov; + int iovcnt; + char *line; +{ + static char device[MAXNAMLEN] = _PATH_DEV; + static char errbuf[1024]; + register int cnt, fd, left, wret; + struct iovec localiov[6]; + int forked = 0; + + if (iovcnt > 6) + return ("too many iov's (change code in wall/ttymsg.c)"); + /* + * open will fail on slip lines or exclusive-use lines + * if not running as root; not an error. + */ + (void) strcpy(device + sizeof(_PATH_DEV) - 1, line); + if ((fd = open(device, O_WRONLY|O_NONBLOCK, 0)) < 0) { + if (errno == EBUSY || errno == EACCES) + return (NULL); +#ifdef __linux__ + (void) sprintf(errbuf, + "%s: %s", device, strerror(errno)); +#else + (void) snprintf(errbuf, sizeof(errbuf), + "%s: %s", device, strerror(errno)); +#endif + return (errbuf); + } + + for (cnt = left = 0; cnt < iovcnt; ++cnt) + left += iov[cnt].iov_len; + + for (;;) { + wret = writev(fd, iov, iovcnt); + if (wret >= left) + break; + if (wret >= 0) { + left -= wret; + if (iov != localiov) { + bcopy(iov, localiov, + iovcnt * sizeof(struct iovec)); + iov = localiov; + } + for (cnt = 0; wret >= iov->iov_len; ++cnt) { + wret -= iov->iov_len; + ++iov; + --iovcnt; + } + if (wret) { + iov->iov_base += wret; + iov->iov_len -= wret; + } + continue; + } + if (errno == EWOULDBLOCK) { + int cpid, off = 0; + + if (forked) { + (void) close(fd); + _exit(1); + } + cpid = fork(); + if (cpid < 0) { +#ifdef __linux__ + (void) sprintf(errbuf, + "fork: %s", strerror(errno)); +#else + (void) snprintf(errbuf, sizeof(errbuf), + "fork: %s", strerror(errno)); +#endif + (void) close(fd); + return (errbuf); + } + if (cpid) { /* parent */ + (void) close(fd); + return (NULL); + } + forked++; + /* wait at most 5 minutes */ + (void) signal(SIGALRM, SIG_DFL); + (void) signal(SIGTERM, SIG_DFL); /* XXX */ + (void) sigsetmask(0); + (void) alarm((u_int)(60 * 5)); + (void) fcntl(fd, O_NONBLOCK, &off); + continue; + } + /* + * We get ENODEV on a slip line if we're running as root, + * and EIO if the line just went away. + */ + if (errno == ENODEV || errno == EIO) + break; + (void) close(fd); + if (forked) + _exit(1); +#ifdef __linux__ + (void) sprintf(errbuf, + "%s: %s", device, strerror(errno)); +#else + (void) snprintf(errbuf, sizeof(errbuf), + "%s: %s", device, strerror(errno)); +#endif + return (errbuf); + } + + (void) close(fd); + if (forked) + _exit(0); + return (NULL); +} diff --git a/login-utils/vipw.8 b/login-utils/vipw.8 new file mode 100644 index 00000000..23d84ad6 --- /dev/null +++ b/login-utils/vipw.8 @@ -0,0 +1,72 @@ +.\" Copyright (c) 1983, 1991 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (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: @(#)vipw.8 6.7 (Berkeley) 3/16/91 +.\" vipw.8,v 1.1.1.1 1995/02/22 19:09:25 faith Exp +.\" +.Dd March 16, 1991 +.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 vipw +will ask you +to try again later. The default editor for +.Nm vipw +is +.Xr vi 1 . +.Sh ENVIRONMENT +If the following environment variable exists it will be utilized by +.Nm vipw : +.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 passwd 1 , +.Xr vi 1 , +.Xr passwd 5 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.0 . diff --git a/login-utils/vipw.c b/login-utils/vipw.c new file mode 100644 index 00000000..f36df5f2 --- /dev/null +++ b/login-utils/vipw.c @@ -0,0 +1,252 @@ +/* + * Copyright (c) 1987 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE 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) 1987 Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +/*static char sccsid[] = "from: @(#)vipw.c 5.16 (Berkeley) 3/3/91";*/ +static char rcsid[] = "vipw.c,v 1.1.1.1 1995/02/22 19:09:25 faith Exp"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pathnames.h" + + +char *progname = "vipw"; +void pw_error __P((char *, int, int)); + + +copyfile(from, to) + register int from, to; +{ + register 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(_PATH_PTMP, 1, 1); + if (nr < 0) + pw_error(_PATH_PASSWD, 1, 1); +} + + +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(SIGTSTP, SIG_IGN); + (void)signal(SIGTTOU, SIG_IGN); + + /* Create with exact permissions. */ + (void)umask(0); +} + +int +pw_lock() +{ + static char path[MAXPATHLEN] = _PATH_PTMP; + int lockfd, fd, ret; + char *p; + + /* + * If the 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_PASSWD, O_RDONLY, 0); + if (lockfd < 0) { + (void)fprintf(stderr, "%s: %s: %s\n", + progname, _PATH_PASSWD, strerror(errno)); + exit(1); + } + if (flock(lockfd, LOCK_EX|LOCK_NB)) { + (void)fprintf(stderr, + "%s: the password file is busy.\n", progname); + exit(1); + } + + if ((fd = open(_PATH_PTMPTMP, O_WRONLY|O_CREAT, 0644)) == -1) { + (void)fprintf(stderr, + "%s: %s: %s\n", progname, _PATH_PTMPTMP, strerror(errno)); + exit(1); + } + ret = link(_PATH_PTMPTMP, _PATH_PTMP); + (void)unlink(_PATH_PTMPTMP); + if (ret == -1) { + if (errno == EEXIST) + (void)fprintf(stderr, + "%s: the password file is busy\n", progname); + else + (void)fprintf(stderr, "%s: can't link %s: %s\n", progname, + _PATH_PTMP, strerror(errno)); + exit(1); + } + copyfile(lockfd, fd); + (void)close(lockfd); + (void)close(fd); + return(1); +} + +void +pw_unlock() +{ + (void)unlink(_PATH_PASSWD); + if (link(_PATH_PTMP, _PATH_PASSWD) == -1) { + (void)fprintf(stderr, + "%s: can't unlock %s: %s (your changes are still in %s)\n", + progname, _PATH_PASSWD, strerror(errno), _PATH_PTMP); + exit(1); + } + (void)unlink(_PATH_PTMP); +} + + +void +pw_edit(notsetuid) + int notsetuid; +{ + int pstat; + pid_t pid; + char *p, *editor; + + if (!(editor = getenv("EDITOR"))) + editor = _PATH_VI; + if ((p = strrchr(editor, '/')) != NULL) + ++p; + else + p = editor; + + if (!(pid = vfork())) { + if (notsetuid) { + (void)setgid(getgid()); + (void)setuid(getuid()); + } + execlp(editor, p, _PATH_PTMP, NULL); + _exit(1); + } + for (;;) { + pid = waitpid(pid, &pstat, WUNTRACED); + if (WIFSTOPPED(pstat)) { + /* the editor suspended, so suspend us as well */ + kill(getpid(), SIGSTOP); + kill(pid, SIGCONT); + } else { + break; + } + } + if (pid == -1 || !WIFEXITED(pstat) || WEXITSTATUS(pstat) != 0) + pw_error(editor, 1, 1); +} + +void +pw_error(name, err, eval) + char *name; + int err, eval; +{ + int sverrno; + + if (err) { + sverrno = errno; + (void)fprintf(stderr, "%s: ", progname); + if (name) + (void)fprintf(stderr, "%s: ", name); + (void)fprintf(stderr, "%s\n", strerror(sverrno)); + } + (void)fprintf(stderr, + "%s: %s unchanged\n", progname, _PATH_PASSWD); + (void)unlink(_PATH_PTMP); + exit(eval); +} + +main() +{ + register int pfd, tfd; + struct stat begin, end; + + pw_init(); + pw_lock(); + + if (stat(_PATH_PTMP, &begin)) + pw_error(_PATH_PTMP, 1, 1); + pw_edit(0); + if (stat(_PATH_PTMP, &end)) + pw_error(_PATH_PTMP, 1, 1); + if (begin.st_mtime == end.st_mtime) { + (void)fprintf(stderr, "vipw: no changes made\n"); + pw_error((char *)NULL, 0, 0); + } + pw_unlock(); + exit(0); +} diff --git a/login-utils/wall.1 b/login-utils/wall.1 new file mode 100644 index 00000000..788f5f2b --- /dev/null +++ b/login-utils/wall.1 @@ -0,0 +1,65 @@ +.\" Copyright (c) 1989, 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. +.\" +.\" @(#)wall.1 6.5 (Berkeley) 4/23/91 +.\" +.\" Modified for Linux, Mon Mar 8 18:07:38 1993, faith@cs.unc.edu +.\" +.Dd March 8, 1993 +.Dt WALL 1 +.Os "Linux 0.99" +.Sh NAME +.Nm wall +.Nd write a message to users +.Sh SYNOPSIS +.Nm wall +.Op Ar file +.Sh DESCRIPTION +.Nm Wall +displays the contents of +.Ar file +or, by default, its standard input, on the terminals of all +currently logged in users. +.Pp +Only the super-user can write on the +terminals of users who have chosen +to deny messages or are using a program which +automatically denies messages. +.Sh SEE ALSO +.Xr mesg 1 , +.Xr talk 1 , +.Xr write 1 , +.Xr shutdown 8 +.Sh HISTORY +A +.Nm +command appeared in +.At v7 . diff --git a/login-utils/wall.c b/login-utils/wall.c new file mode 100644 index 00000000..b10badba --- /dev/null +++ b/login-utils/wall.c @@ -0,0 +1,202 @@ +/* + * Copyright (c) 1988, 1990 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Modified for Linux, Mon Mar 8 18:08:30 1993, faith@cs.unc.edu + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1988 Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)wall.c 5.14 (Berkeley) 3/2/91"; +#endif /* not lint */ + +/* + * This program is not related to David Wall, whose Stanford Ph.D. thesis + * is entitled "Mechanisms for Broadcast and Selective Broadcast". + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IGNOREUSER "sleeper" + +int nobanner; +int mbufsize; +char *mbuf; + +/* ARGSUSED */ +main(argc, argv) + int argc; + char **argv; +{ + extern int optind; + int ch; + struct iovec iov; + struct utmp utmp; + FILE *fp; + char *p, *ttymsg(); + + while ((ch = getopt(argc, argv, "n")) != EOF) + switch (ch) { + case 'n': + /* undoc option for shutdown: suppress banner */ + if (geteuid() == 0) + nobanner = 1; + break; + case '?': + default: +usage: + (void)fprintf(stderr, "usage: wall [file]\n"); + exit(1); + } + argc -= optind; + argv += optind; + if (argc > 1) + goto usage; + +#ifdef __linux__ + if (argc != 1) + makemsg(""); + else +#endif + makemsg(*argv); + + if (!(fp = fopen(_PATH_UTMP, "r"))) { + (void)fprintf(stderr, "wall: cannot read %s.\n", _PATH_UTMP); + exit(1); + } + iov.iov_base = mbuf; + iov.iov_len = mbufsize; + /* NOSTRICT */ + while (fread((char *)&utmp, sizeof(utmp), 1, fp) == 1) { + if (!utmp.ut_name[0] || + !strncmp(utmp.ut_name, IGNOREUSER, sizeof(utmp.ut_name))) + continue; +#ifdef __linux__ + if (utmp.ut_type != USER_PROCESS) + continue; +#endif + if (p = ttymsg(&iov, 1, utmp.ut_line)) + (void)fprintf(stderr, "wall: %s\n", p); + } + exit(0); +} + +makemsg(fname) + char *fname; +{ + register int ch, cnt; + struct tm *lt; + struct passwd *pw; + struct stat sbuf; + time_t now, time(); + FILE *fp; + int fd; + char *p, *whom, hostname[MAXHOSTNAMELEN], lbuf[100], tmpname[15]; + char *getlogin(), *strcpy(), *ttyname(); + + (void)strcpy(tmpname, _PATH_TMP); + (void)strcat(tmpname, "/wall.XXXXXX"); + if (!(fd = mkstemp(tmpname)) || !(fp = fdopen(fd, "r+"))) { + (void)fprintf(stderr, "wall: can't open temporary file.\n"); + exit(1); + } + (void)unlink(tmpname); + + if (!nobanner) { + if (!(whom = getlogin())) + whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???"; + (void)gethostname(hostname, sizeof(hostname)); + (void)time(&now); + lt = localtime(&now); + + /* + * all this stuff is to blank out a square for the message; + * we wrap message lines at column 79, not 80, because some + * terminals wrap after 79, some do not, and we can't tell. + * Which means that we may leave a non-blank character + * in column 80, but that can't be helped. + */ + (void)fprintf(fp, "\r%79s\r\n", " "); + (void)sprintf(lbuf, "Broadcast Message from %s@%s", + whom, hostname); + (void)fprintf(fp, "%-79.79s\007\007\r\n", lbuf); + (void)sprintf(lbuf, " (%s) at %d:%02d ...", ttyname(2), + lt->tm_hour, lt->tm_min); + (void)fprintf(fp, "%-79.79s\r\n", lbuf); + } + (void)fprintf(fp, "%79s\r\n", " "); + + if (*fname && !(freopen(fname, "r", stdin))) { + (void)fprintf(stderr, "wall: can't read %s.\n", fname); + exit(1); + } + while (fgets(lbuf, sizeof(lbuf), stdin)) + for (cnt = 0, p = lbuf; ch = *p; ++p, ++cnt) { + if (cnt == 79 || ch == '\n') { + for (; cnt < 79; ++cnt) + putc(' ', fp); + putc('\r', fp); + putc('\n', fp); + cnt = 0; + } else + putc(ch, fp); + } + (void)fprintf(fp, "%79s\r\n", " "); + rewind(fp); + + if (fstat(fd, &sbuf)) { + (void)fprintf(stderr, "wall: can't stat temporary file.\n"); + exit(1); + } + mbufsize = sbuf.st_size; + if (!(mbuf = malloc((u_int)mbufsize))) { + (void)fprintf(stderr, "wall: out of memory.\n"); + exit(1); + } + if (fread(mbuf, sizeof(*mbuf), mbufsize, fp) != mbufsize) { + (void)fprintf(stderr, "wall: can't read temporary file.\n"); + exit(1); + } + (void)close(fd); +} diff --git a/makedev-1.4.1/LEGAL.NOTICE b/makedev-1.4.1/LEGAL.NOTICE new file mode 100644 index 00000000..51ff4101 --- /dev/null +++ b/makedev-1.4.1/LEGAL.NOTICE @@ -0,0 +1,22 @@ +(This license is based on one written by Ian F. Darwin.) + +This software is not subject to any export provision of the United +States Department of Commerce, and may be exported to any country, +planet, or star system. + +Permission is granted to any sentient being to use this software for +any purpose on any computer system, and to alter it and redistribute +it freely, subject to the following restrictions: + +1. The author is not responsible for the consequences of use of this + software, no matter how awful, even if they arise from flaws in it. + +2. The origin of this software must not be misrepresented, either by + explicit claim or by omission. Since few users ever read sources, + credits must appear in the documentation. + +3. Altered versions must be plainly marked as such, and must not be + misrepresented as being the original software. Since few users + ever read sources, credits must appear in the documentation. + +4. This notice may not be removed or altered. diff --git a/makedev-1.4.1/MAKEDEV-C.8 b/makedev-1.4.1/MAKEDEV-C.8 new file mode 100644 index 00000000..61442785 --- /dev/null +++ b/makedev-1.4.1/MAKEDEV-C.8 @@ -0,0 +1,80 @@ +.\" -*- nroff -*- +.TH MAKEDEV 8 "January 1995" "Version 1.4" +.SH NAME +MAKEDEV \- create and maintain filesystem device entries +.SH SYNOPSIS +.B MAKEDEV +[ +.I \-vcdnhV +] +device or device-group name(s) +.SH DESCRIPTION +.B MAKEDEV +is used to maintain the special filesystem entries found in /dev. It +creates, or optionally removes, one or more device entries. The names +and device numbers are defined in the DEVINFO file (q.v.); +site-specific configuration is found in the file MAKEDEV.cfg. +.B MAKEDEV +itself has no knowledge of device information. +.SH OPTIONS +.TP +.I -v +Verbose mode; print out exactly what's being done. +.TP +.I -c +Create; create the specified devices. [default] +.TP +.I -d +Delete; remove the specified devices instead of creating them. +.TP +.I -n +Do nothing; only print what would be done. Implies -v as well. +.TP +.I -h +Print a usage message. +.TP +.I -V +Print the version string. +.SS " " +The following targets are special: +.TP +.I update +Run MAKEDEV in update mode. This reads the list of devices currently +available from /proc/devices, and updates all entries in /dev to match +the device numbers found there. +.TP +.I local +Run MAKEDEV to create local devices. This option is obsolete and just +prints a warning message. Use DEVINFO.local and MAKEDEV.cfg to achieve +the same results. +.SH FILES +.TP 20 +.I /etc/devinfo +If ./DEVINFO is not found +.TP +.I /etc/devinfo.local +Alternate location for local info +.TP +.I /etc/makedev.cfg +If ./MAKEDEV.cfg is not found +.TP +.I MAKEDEV.cache +Cached data for `update' +.TP +.I /proc/devices +The kernel's list of current devices +.SH AUTHOR +David A. Holland (dholland@husc.harvard.edu) +.br +.br +Based on the older +.B MAKEDEV +shell script written by Nick Holloway. +Additional ideas were contributed by Rik Faith. +.SH NOTES +The LALR(1) parser generator used to build makedev.c from makedev.syn +is a commercial product. You won't be able to do a complete rebuild +unless you have it. +.SH SEE ALSO +.BR DEVINFO (5), +.BR MAKEDEV.cfg (5) diff --git a/makedev-1.4.1/Makefile b/makedev-1.4.1/Makefile new file mode 100644 index 00000000..29027865 --- /dev/null +++ b/makedev-1.4.1/Makefile @@ -0,0 +1,43 @@ +# Makefile -- Makefile for util-linux Linux utilities +# Created: Sat Feb 11 13:47:13 1995 +# Revised: Wed Feb 22 16:09:38 1995 by faith@cs.unc.edu +# Copyright 1992, 1993, 1994, 1995 Rickard E. Faith (faith@cs.unc.edu) +# + +include ../MCONFIG + +# Where to put man pages? + +MAN5= devinfo.5 makedev.cfg.5 +MAN8= MAKEDEV-C.8 + +# Where to put binaries? +# See the "install" rule for the links. . . + +DEV= MAKEDEV-C + +# Where to put data? + +FILES= devinfo makedev.cfg + +all: $(DEV) + +%.o: %.c + $(CC) -c $(CFLAGS) $< -o $@ + +# Rules for everything else + +MAKEDEV-C: makedev.o + $(CC) $(LDFLAGS) $< -o $@ + +install: all + $(INSTALLDIR) $(DEVDIR) $(ETCDIR) + $(INSTALLDAT) $(FILES) $(ETCDIR) + $(INSTALLBIN) $(DEV) $(DEVDIR) + $(INSTALLDIR) $(MAN5DIR) $(MAN8DIR) + $(INSTALLMAN) $(MAN5) $(MAN5DIR) + $(INSTALLMAN) $(MAN8) $(MAN8DIR) + +.PHONY: clean +clean: + -rm -f *.o *~ core $(DEV) diff --git a/makedev-1.4.1/README.MAKEDEV-C b/makedev-1.4.1/README.MAKEDEV-C new file mode 100644 index 00000000..9ecb72b0 --- /dev/null +++ b/makedev-1.4.1/README.MAKEDEV-C @@ -0,0 +1,36 @@ + +Readme file for MAKEDEV-C: + +MAKEDEV is a program to create the special file entries found in /dev. +MAKEDEV-C is a compiled binary that runs from a config file, called +DEVINFO. The config file is intended to be fairly human-readable, as +well as machine-readable. + +New in release 1.4.1: + +Substantial updates to DEVINFO. Most notably: + - hdc and hdd are now the drives on the 2nd IDE controller. + This is NOT how it used to be, nor is it how the shell MAKEDEV + does it, but it's what the kernel source indicates is right. + - SCSI tapes were totally messed up in the last release. + - New and more appropriate QIC device names. + - Assorted fixes. (See DEVINFO itself for more details.) + +Some changes to the sample MAKEDEV.cfg: + - added a class "system" (root.system, mode 660) + - do not omit hdc and hdd by default, as they now mean + something more useful than they used to. + + ---------------- + +New in release 1.4: + +First general public release. +Wrote man pages. + + ---------------- + +Release 1.3: + +First tentative limited-distribution release. + diff --git a/makedev-1.4.1/THIS_VERSION_IS_ALTERED b/makedev-1.4.1/THIS_VERSION_IS_ALTERED new file mode 100644 index 00000000..3c6d31dc --- /dev/null +++ b/makedev-1.4.1/THIS_VERSION_IS_ALTERED @@ -0,0 +1,6 @@ +This version of makedev-1.4 has been altered by Rik Faith +(faith@cs.unc.edu) to fit in with the util-linux suite of programs. + +The Makefile was re-written, a few programs were re-named, and the group +for printer was changed to daemon, to conform with the original MAKEDEV +script. diff --git a/makedev-1.4.1/devinfo b/makedev-1.4.1/devinfo new file mode 100644 index 00000000..a50458cf --- /dev/null +++ b/makedev-1.4.1/devinfo @@ -0,0 +1,343 @@ +/* + * DEVINFO: device information for MAKEDEV + * + * MANY THANKS to those who have sent in corrections - I don't have most + * of the hardware listed in here, so it won't get fixed if nobody tells me. + * Mail to: David A. Holland + * + * Version 1.4: 21-Feb-94 Corrected proc entry for ibcs2. + * 27-Feb-94 Make 12 VCs by default. + * Version 1.3: 14-Feb-94 Corrections from perusing the 1.1.91 source tree. + * hd1[a-b] becomes hd[c-d]; see comments below. + * Corrected idecd entry. Added entry for Aztech cdrom. + * Corrected sbpcd entries. + * Invented new [and better] names for the QIC tape entries. + * There appear to be up to 32 cyclades devices supported in 1.1.91. + * Version 1.2: 14-Feb-94 Revisions snarfed from shell MAKEDEV 2.1. + * Added cyclades, idecd, apm, dcf, /proc entry for joysticks, + * scanner becomes logiscan/m105scan/ac4096, some new audio devices, + * high-number floppy entries, scsi tapes+cds now go 0-7, + * corrected a comment erroneously indicating fd4 was possible, + * removed default major numbers for cdu31a, pcaudio, ibcs2. + * Version 1.1: 13-Feb-94 Corrected scsi tapes (which were totally wrong) + * Version 1.0: 11-Dec-94 Initial version + */ + +/* ignore when /proc/devices mentions these: */ +/* (this is how it was before; couldn't we use mem? */ +ignore { mem, tty, pcmcia } + +/* make a batch called generic, standard set of stuff */ +batch generic { + std, fd0, fd1, hda, hdb, xda, xdb, sda, sdb, + ptyp, ptyq, ptyr, ptys, console, vts, serial, + busmice, printers, fd +} + +// The "std" group - basic devices */ +char (std, 1) { + mem (kmem): 1 + kmem (kmem): 2 + null (public) : 3 + port (kmem) : 4 + zero (public) : 5 + core -> "/proc/kcore" + full (public) : 7 +} +block (std, 1) { + ram (disk) : 1 +} +char (std, 5) { + tty (public) : 0 +} + +/* the "console" group - system console */ +char (consoles,4) { + console (cons) : 0 # /dev/console + tty0 (cons) : 0 # tty0 == console +} + +/* VTs tty1-tty63 (tty0 is special) */ +/* group "vts" is tty1-8; "vts2" is the rest */ +char (vts, 4) tty[1-12] (tty) : 1 +char (vts2, 4) tty [13-63] (tty) : 9 + + +/* serial ports, ttyS0-ttyS63 and cua0-cua63 */ +/* group "serial" is just ttyS0-3 and cua0-3; "serial2" is the rest */ +char (serial, 4) ttyS[0-3] (tty) : 64 +char (serial2,4) ttyS[4-63] (tty) : 64+4 +char (serial, 5) cua[0-3] (dialout) : 64 +char (serial2,5) cua[4-63] (dialout) : 64 + 4 + +/* ptys: pty[pqrs][0-9a-f] and tty[pqrs][0-9][a-f] */ +/* grouped as ptyp, ptyq, ptyr, and ptys */ +char (ptyp, 4) { + ptyp[0x0-f] (pty) : 128+0*16 + ttyp[0x0-f] (tty) : 192+0*16 +} +char (ptyq, 4) { + ptyq[0x0-f] (pty) : 128+1*16 + ttyq[0x0-f] (tty) : 192+1*16 +} +char (ptyr, 4) { + ptyr[0x0-f] (pty) : 128+2*16 + ttyr[0x0-f] (tty) : 192+2*16 +} +char (ptys, 4) { + ptys[0x0-f] (pty) : 128+3*16 + ttys[0x0-f] (tty) : 192+3*16 +} + +/* cyclades serial multiplexer */ +char (cyclades=ttyC, 19) { + ttyC[0-31] (tty) : 32 +} +char (cyclades=cub, 20) { + cub[0-31] (dialout) : 32 +} + +/* parallel ports par0-3 and printers lp0-3 (which are merely aliases) */ +/* group is "printers" */ +char (printers=lp, 6) { + par[0-3] (printer) : 0 + lp[0-3] (printer) : 0 +} + +/* busmice: logibm, psaux, inportbm, atibm */ +char (busmice=mouse, 10) { + logibm (mouse) : 0 + psaux (mouse) : 1 + inportbm (mouse) : 2 + atibm (mouse) : 3 + # sejin (mouse) : 4 +} + +/* joysticks: js0, js1; group "js" */ +char (js=Joystick) js[0-1] (mouse) : 0 + +/* floppies: fd0-3, with lots of modes */ +block (floppies=fd, 2) { + fd[0-3] (floppy) : 0 + fd[0-3]d360 (floppy) : 4 + fd[0-3]h1200 (floppy) : 8 + fd[0-3]D360 (floppy) : 12 + fd[0-3]H360 (floppy) : 12 + fd[0-3]D720 (floppy) : 16 + fd[0-3]H720 (floppy) : 16 + fd[0-3]h360 (floppy) : 20 + fd[0-3]h720 (floppy) : 24 + fd[0-3]H1440 (floppy) : 28 + fd[0-3]H2880 (floppy) : 32 + fd[0-3]CompaQ (floppy) : 36 + fd[0-3]h1440 (floppy) : 40 + fd[0-3]H1680 (floppy) : 44 + fd[0-3]h410 (floppy) : 48 + fd[0-3]H820 (floppy) : 52 + fd[0-3]H1476 (floppy) : 56 + fd[0-3]H1722 (floppy) : 60 + fd[0-3]h420 (floppy) : 64 + fd[0-3]h830 (floppy) : 68 + fd[0-3]h1494 (floppy) : 72 + fd[0-3]h1743 (floppy) : 76 +} + +// There is a controversy regarding whether these should be either +// (1) hda-hdd are major number 3, hda1-hd1d are major number 22 +// (2) hda-hdb are major number 3, hdc-hdd are major number 22 +// +// Case (1) is commented out, as case (2) seems to be more popular. +// Case (2) also appears to make more sense in terms of the way +// the drivers are actually implemented. + +// /* AT hard disks hda-hdd (partitions 1-8 and main) */ +// block(hd=hd,3) hd[a-d] 8/64 +// /* AT hard disks on second controller */ +// block(hd1,22) hd1[a-d] 8/64 + +/* AT hard disks hda-b (partitions 1-8 and main) */ +block(hd=hd,3) hd[a-b] 8/64 +/* and 2nd controller */ +block(hd1,22) hd[c-d] 8/64 + +// Ordinarily, the use of the syntax above would automatically create +// this batch. Because of the way it's done (this is a bug, and hopefully +// will be fixed in a future release) the batch for the hd1 devices is +// also created as "hd". This cannot be accessed, but does no harm as +// long as the order of the above two declarations isn't reversed. sigh. +// Anyway, this line works around the problem. +batch hd1 { hdc hdd } + + +/* XT hard drives */ +block(xd=xd,13) xd[a-d] 8/64 + +/* scsi hard disks sda-sdh */ +block(sd=sd,8) sd[a-h] 8/16 + +/* loopback disk devices; group "loop" */ +block(loop=loop) loop[0-7] (disk) : 0 + +/* scsi tapes st0-7 */ +char(st=st, 9) { + st[0-7] (tape) : 0 + nst[0-7] (tape) : 128 +} + +/* qic tapes - group "qic" */ +// The following is what came with the shell MAKEDEV. +//char (qic=tpqic02, 12) { +// rmt8 (tape) : 6 +// rmt16 (tape) : 8 +// tape-d (tape) : 136 +// tape-reset (tape) : 255 +//} +// This, on the other hand, appears to be more correct. + +/* + * By the authority vested in me as the maintainer of this file, + * I have made up the device names. The "n" versions don't rewind, + * as with other tape devices. The number specifies the model/capacity. + * There's not really room left for unit numbers, but I suppose there + * probably aren't many people with multiple QIC drives either... + */ +char (qic=tpqic02, 12) { + nqt11 (tape) : 2 + qt11 (tape) : 3 + nqt24 (tape) : 4 + qt24 (tape) : 5 + nqt120 (tape) : 6 + qt120 (tape) : 7 + nqt150 (tape) : 8 + qt150 (tape) : 9 + qt-reset (tape) : 255 +} + +/* ftapes - group ftape */ +char (ftape=mt, 27) { + rft[0-3] (tape) : 0 + nrft[0-3] (tape) : 4 + ftape -> rft0 + nftape -> nrft0 +} + +/* scsi cd */ +block(sr=sr, 11) scd[0-7] (cdrom) : 0 + +/* sony cd */ +block(sonycd=cdu31a) sonycd (cdrom) : 0 + +/* mitsumi cd */ +block(mcd=mcd, 23) mcd (cdrom) : 0 + +/* Sony cdu535 */ +block(cdu535="cdu-535", 24) cdu535 (cdrom) : 0 + +/* LMS/Philips CD player (needs new major number) */ +block(lmscd, 24) lmscd (cdrom) : 0 + +/* Aztech CDROM */ +block(aztcd, 29) aztcd0 (cdrom) : 0 + +/* soundblaster CD, 1st controller */ +block(sbpcd=sbpcd, 25) { + sbpcd (cdrom) : 0 + sbpcd[0-3] (cdrom) : 0 +} +/* 2nd, 3rd, 4th */ +block(sbpcd=sbpcd2, 26) sbpcd[4-7] (cdrom) : 0 +block(sbpcd=sbpcd3, 27) sbpcd[8-11] (cdrom) : 0 +block(sbpcd=sbpcd4, 28) sbpcd[12-15] (cdrom) : 0 + +/* ide cd */ +block (idecd=idecd) { + idecd (cdrom) : 0 +} + +/* Logitech scanner */ +char (logiscan=logiscan) { + logiscan (scanner) : 0 +} + +char (m105scan=m105) { + m105scan (scanner) : 0 +} + +char (ac4096=ac4096) { + ac4096 (scanner) : 0 +} + +// this is apparently obsolete? +//char (scan=Scanner) { +// scan (scanner) : 0 +// scand (scanner) : 1 +//} + +/* audio */ +char (audio=sound, 14) { + mixer (audio) : 0 + sequencer (audio) : 1 + midi00 (audio) : 2 + midi -> midi00 + dsp (audio) : 3 + audio (audio) : 4 + sndstat (audio) : 6 +# sequencer2(audio) : 8 + mixer1 (audio) : 16 +# patmgr0 (audio) : 17 + midi01 (audio) : 18 + dsp1 (audio) : 19 + audio1 (audio) : 20 +# patmgr1 (audio) : 33 + midi02 (audio) : 34 + midi03 (audio) : 50 +} + +/* pcaudio */ +char (pcaudio=pcsp) { + pcmixer (audio) : 0 + pcsp (audio) : 3 + pcaudio (audio) : 4 +} + +/* sg: generic scsi devices */ +char (sg=sg, 21) { + sga (scsi) : 0 + sgb (scsi) : 1 + sgc (scsi) : 2 + sgd (scsi) : 3 + sge (scsi) : 4 + sgf (scsi) : 5 + sgg (scsi) : 6 + sgh (scsi) : 7 +} + +/* fd: file descriptors */ +char (fd, 0) { // the 0 is not used - there are only links in here! + fd -> "/proc/self/fd" + stdin -> "fd/0" + stdout -> "fd/1" + stderr -> "fd/2" +} + +/* ibcs2: coff emulation stuff */ +char (ibcs2=socksys, 30) { + socksys (ibcs2) : 0 + spx (ibcs2) : 2 + nfsd -> socksys + XOR -> null +} + +char (apm=apm_bios) { + apm (system) : 0 +} + +char (dcf=dcf) { + dcf (system) : 0 +} + +/* demo device for module stuff */ +char (hw=hw) { + helloworld (public) : 0 +} + diff --git a/makedev-1.4.1/devinfo.5 b/makedev-1.4.1/devinfo.5 new file mode 100644 index 00000000..4818631d --- /dev/null +++ b/makedev-1.4.1/devinfo.5 @@ -0,0 +1,122 @@ +.\" -*- nroff -*- +.TH DEVINFO 5 "January 1995" "Version 1.4" +.SH NAME +DEVINFO \- device entry database +.SH DESCRIPTION +.B DEVINFO +is a text file that describes all the possible devices for a system. +It is used by +.BR MAKEDEV (8) +to create special file entries in +.BR /dev . +It may be named either +.BR /dev/DEVINFO " or " /etc/devinfo . +Information about custom local devices, if any, should be placed in +.BR DEVINFO.local " or " /etc/devinfo.local , +which has the same syntax. +.LP +The file format is free-form. Both C, C++, and shell comments are +understood. There are basically four statements: +.TP +.RI "ignore { " proc-device... " }" +This causes the specified names to be ignored if found in +.BR /proc/devices . +.TP +.RI "batch { " device... " }" +This creates a "batch" \- a collection of devices which will all be +created when the batch is invoked. For example, in the standard +.BR DEVINFO , +"generic" is a batch. +.TP +.RI "block " device-spec +This defines one or more block devices. +.TP +.RI "char " device-spec +This defines one or more character devices. +.LP +Here is a sample +.IR device-spec : +.RS +.nf +(std, 1) { + mem (kmem) : 1 + null (public) : 3 + core -> "/proc/kcore" +} +.fi +.RE +.LP +This example defines a group of devices called "std", with major +number 1. Running +.B "\"MAKEDEV std\"" +will create all the devices in the group; running, for example, +.B "\"MAKEDEV null\"" +would make just the one device "null". +.LP +It is possible to specify, instead of just "std", something like +"std=foo". In this case, the stuff on the right-hand side of the +equals sign specifies a name from +.BR /proc/devices , +and the major number will be retrieved from there if present. If an +entry from +.BR /proc/devices +is specified, the explicit major number may be omitted. In this case, +if the number is not found in /proc/devices, attempts to create the +device will be rejected. +.LP +Inside the braces is a list of specific devices. The name in +parenthesis is the "class" - this is something specified in +.B MAKEDEV.cfg +(q.v.) that determines the ownership and permissions of the special +file created. In the above example, the device "mem" was set to have +the class "kmem", but "null" was set to be "public". Ordinarily you'd +define "public" to be mode 666, but "kmem" to be mode 660 and owned by +group kmem. The number after the colon is the minor number for this +particular device \- for instance, 3 for "null". +.LP +You may also specify a symbolic link with "->". For instance, above, +"core" was made a link to +.BR /proc/kcore . +Note that names may contain any characters, but names that contain +things other than alphanumerics, dash, and underscore should be put in +double quotes. +.LP +An entire range of devices can be created: you may specify a range of +numbers in brackets, like this: +.RS +.nf + +tty[1-8] (tty) : 1 + +.fi +.RE +This creates tty1\-tty8 with minor device numbers starting with 1. +If you specify the range in hex (prefixed by 0x) the device names will +be created numbered in hex, as is normal for ptys. The range may +appear inside the name string, but there may only be one range. +.LP +There is a special syntax for creating the entire banks of devices for +a hard drive: +.RS +.nf + + hd[a-d] 8/64 + +.fi +.RE +What this means is as follows: create hda, and 8 partitions on hda +(hda1 through hda8), starting with minor number 0. Then create hdb, +and 8 partitions, starting with minor number 64. Then hdc, etc., with +minor number 64*2 = 128. And so forth. These are automatically placed +in the class "disk". The necessary groups and batches are created so +you can ask +.B MAKEDEV +to create "hd" or "hda" or "hda1" and expect it to do the correct +thing. +.LP +Note that simple arithmetic is permitted for specifying the minor +device number, as this often makes things much clearer and less likely +to be accidentally broken. +.SH "SEE ALSO" +.BR MAKEDEV (8), +.BR MAKEDEV.cfg (5) diff --git a/makedev-1.4.1/makedev.c b/makedev-1.4.1/makedev.c new file mode 100644 index 00000000..59f16e0d --- /dev/null +++ b/makedev-1.4.1/makedev.c @@ -0,0 +1,2296 @@ +/* + * makedev.c: Generate /dev entries + * + * Based on the MAKEDEV shell script, version 2.0, distributed with + * util-linux 1.10 and written by Nick Holloway. + * + * A number of bugs were fixed, and some additional features added. + * Written 10-Dec-94 by David A. Holland, dholland@husc.harvard.edu + * + * Copyright 1994, 1995. All rights reserved. + * See the file LEGAL.NOTICE for conditions of redistribution. + * + * Bugs: + * None known right now. + * + * History: + * + * Version 1.4a: Sun Feb 26 18:08:45 1995, faith@cs.unc.edu + * Forced devinfo and makedev to be in /etc + * Version 1.4: 15-Jan-95 Wrote man pages. Now reads DEVINFO.local. + * Version 1.3: 31-Dec-94 Bug fixes. Added batches. Added omits. + * Version 1.2: 11-Dec-94 Add configuration file parsing. + * Version 1.1: 11-Dec-94 Distinguish block and character devices in the + * table of major device numbers. Changed the name and format of the + * update cache file to include the type. It appears that the old script + * was broken in this regard. + * Version 1.0: 10-Dec-94 Initial version. + */ + +static const char *version = "MAKEDEV-C version 1.4a"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define YES 1 +#define NO 0 + +static int isverbose=NO; /* flag: print out what we do? */ +static int deletion=NO; /* flag: delete instead of create */ +static int donothing=NO; /* flag: don't actually do anything */ + +/* + * Proto for main operative function. + */ +typedef enum { M_CREATE, M_OMIT } makeopts; +static void make(const char *batch_or_grp_or_devname, makeopts); + +/* + * Roll over and die. + */ +static void crash(const char *msg) { + fprintf(stderr, "MAKEDEV: %s\n", msg); + exit(1); +} + +/* + * Print a warning. + */ +static void warn(const char *format, ...) { + va_list ap; + va_start(ap, format); + fprintf(stderr, "MAKEDEV: "); + vfprintf(stderr, format, ap); + fprintf(stderr, "\n"); + va_end(ap); +} + +/* + * Translate string name to uid. + */ +static uid_t name2uid(const char *name) { + struct passwd *p = getpwnam(name); + if (!p) warn("undefined user: %s, using uid 0", name); + return p ? p->pw_uid : 0; /* make things owned by root by default */ +} + +/* + * Translate string name to gid. + */ +static gid_t name2gid(const char *name) { + struct group *g = getgrnam(name); + if (!g) warn("undefined group: %s, using gid 0", name); + return g ? g->gr_gid : 0; /* group 0 is a good default too */ +} + +/* + * Proto for parser. + */ +static void doparse(FILE *f, int filetype, const char *filename); + +/************************* device classes *************************/ + +/* + * A device class is a string attached to the device which tells us + * what set of permissions and ownership should be used. This is the + * table of classes. + */ + +typedef struct { + const char *classname; + const char *owner; + const char *group; + int mode; +} devclass; + +#define MAXCLASSES 32 +static devclass classes[MAXCLASSES]; +static int nclasses=0; + +static void addclass(const char *name, const char *o, const char *g, int m) { + if (nclasses>=MAXCLASSES) crash("out of space for device classes"); + classes[nclasses].classname = name; + classes[nclasses].owner = o; + classes[nclasses].group = g; + classes[nclasses].mode = m; + nclasses++; + name2uid(o); /* check for undefined users/groups */ + name2gid(g); +} + +static void loadclasses(void) { + FILE *f = fopen("/etc/makedev.cfg", "r"); + if (!f) crash("can't find makedev.cfg"); + doparse(f, 4, "makedev.cfg"); + fclose(f); +} + +/* + * Return the index into the above table for a particular class name. + */ +static int which_class(const char *name) { + int i; + for (i=0; i=0; i--, z<<=1) if (!(mode&z)) rv[i]='-'; + return rv; +} + +/* + * Create (or delete, or update) a block or character device. + */ +static void class_makedev(const char *name, const char *class, + int major, int minor, char type) { + int x = which_class(class), mode = classes[x].mode; + const char *owner = classes[x].owner, *group = classes[x].group; + if (isverbose) { + if (deletion) printf("rm -f %s\n", name); + else printf("%c%s 1 %-8s %-8s %3d, %3d for %s\n", type, + modestring(mode), owner, group, major, minor, name); + } + if (donothing) return; + if (unlink(name) && deletion) warn("Couldn't remove %s\n", name); + if (!deletion) { + dev_t q = (major<<8) | minor; + if (mknod(name, type=='c' ? S_IFCHR : S_IFBLK, q) || + chown(name, name2uid(owner), name2gid(group)) || + chmod(name, mode)) { + warn("couldn't create %s: %s", name, strerror(errno)); + } + } +} + +/************************* major number list *************************/ + +/* + * In Linux device major numbers can be allocated dynamically, so we go + * look in /proc/devices to see what they are. This keeps track of things. + */ + +typedef struct { + const char *procname; + int flag; +} majorentry; + +#define MAXMAJORS 256 +static majorentry cmajors[MAXMAJORS]; /* initialized to 0 */ +static majorentry bmajors[MAXMAJORS]; /* initialized to 0 */ +static int no_proc=0; /* true if we didn't find /proc/devices */ + +/* + * Store the name associated with a particular major device number. + */ +static void set_major(const char *procname, int ischar, int num) { + if (num<0 || num>255) { + warn("warning: got bogus major number %d for %s", num, procname); + return; + } + if (ischar) cmajors[num].procname=procname; + else bmajors[num].procname=procname; +} + +/* + * Look up a major device number by name; return the default value + * if provided. A default value of -1 implies the device is only + * dynamic, and so if there's no entry we shouldn't even note its + * existence. + */ +static int get_major(const char *procname, int ischar, int defaalt) { + int i; + if (!procname) return defaalt; + if (ischar) { + for (i=0; i=MAXALIASES) crash("out of space for aliases"); + aliases[naliases].procname = procname; + aliases[naliases].groupname = groupname; + naliases++; +} + +static void ignore_procname(const char *procname) { + addalias(procname, NULL); +} + +static const char *procnameof(const char *groupname) { + int i; + for (i=0; i=MAXBATCHES) crash("Out of space for batches"); + b = &batches[nbatches++]; + b->name = name; + b->busy = NO; + return b; +} + +/* + * Add something to a batch. + */ +static batch *add2batch(batch *b, const char *target) { + if (b->ntargets>=MAXTARGETS) { + warn("Too many targets for batch %s (max %d)", b->name, MAXTARGETS); + return b; + } + b->targets[b->ntargets++] = target; + return b; +} + +/* + * Run a batch. + */ +static void run_batch(const batch *b, makeopts m) { + int i; + for (i=0; intargets; i++) make(b->targets[i], m); +} + +/* + * Try to run a batch; returns YES if it found one. + */ +static int try_run_batch(const char *name, makeopts m) { + int i; + for (i=0; i owner and permissions) */ + int major, minor; /* device number */ + char type; /* 'c', 'b', or 'l' for symbolic link */ + int omit; /* don't make me if this is nonzero */ +} device; + +/* + * Create a device (link or actual "special file") - special files are + * passed on to class_makedev(). + */ +void makedev(device *d, makeopts m) { + if (m==M_OMIT) { + d->omit=1; + } + if (d->omit==1) return; + if (d->type=='l') { + if (isverbose) { + if (deletion) printf("rm -f %s\n", d->name); + else printf("lrwxrwxrwx %s -> %s\n", d->name, d->class); + } + if (donothing) return; + if (unlink(d->name) && deletion) warn("Couldn't remove %s\n", d->name); + if (!deletion) { + if (symlink(d->class, d->name)) /* class holds thing pointed to */ + warn("couldn't link %s -> %s: %s", d->name, d->class, strerror(errno)); + } + } + else class_makedev(d->name, d->class, d->major, d->minor, d->type); +} + +/* + * Array of devices. We allocate it once from main(); it doesn't grow. + * Should maybe make it growable sometime. This keeps track of all possible + * devices. We build this thing first, and then create devices from it as + * requested. + */ +static device *devices = NULL; +static int maxdevices, ndevices; + +/* + * Allocate space for the device array. + */ +static void allocate_devs(int nd) { + devices = malloc(nd * sizeof(device)); + if (!devices) crash("Out of memory"); + ndevices = 0; + maxdevices = nd; +} + +/* + * Check all the devices for having valid device classes. + */ +static void check_classes(void) { + int i; + const char *q=NULL; + for (i=0; i=maxdevices) crash("out of space for devices"); + devices[ndevices].name = name; + devices[ndevices].grp = grp; + devices[ndevices].class = class; + devices[ndevices].major = major; + devices[ndevices].minor = minor; + devices[ndevices].type = type; + devices[ndevices].omit = 0; + ndevices++; +} + +/* + * Create an entry for a symbolic link "device", such as /dev/fd + * (which is a symbolic link to /proc/self/fd) + */ +static void initlink(const char *name, const char *grp, const char *target) { + init(name, grp, target, 0, 0, 'l'); +} + +/* + * Init lots of devices. This creates a number of devices, numbered between + * lo and hi. The idea is that "base" contains a %d or %x (or something like + * that) in it which pulls in the number. The device group can also do this, + * though this will in most cases not be useful. "baseminor" is the minor + * number of the first device created. + */ +static void initlots(const char *base, int lo, int hi, const char *grp, + const char *class, + int maj, int baseminor, int type) { + char buf[32], gbuf[32]; + int i; + if (maj<0) return; + for (i=lo; i=high) return; + b = addbatch(base); + for (i=low; i<=high; i++) { + char *q; + sprintf(buf, "%s%c", base, i); + q = strdup(buf); + init(q, q, "disk", maj, (i-low)*minmult, 'b'); + strcpy(buf2, buf); + strcat(buf2, "%d"); + initlots(buf2, 1, nparts, buf, "disk", maj, (i-low)*minmult+1, 'b'); + add2batch(b, q); + } +} + +static void initdevs(void) { + FILE *f = fopen("/etc/devinfo", "r"); + if (!f) crash("Can't find devinfo"); + doparse(f,3, "devinfo"); + fclose(f); + f = fopen("/etc/devinfo.local", "r"); + if (!f) f = fopen("/usr/local/etc/devinfo.local", "r"); + if (f) { + doparse(f,3, "devinfo.local"); + fclose(f); + } +} + +/************************** update *************************/ + +/* + * Call make() with our names for something that appeared in /proc/devices. + */ + +static void transmake(const char *procname, makeopts m) { + const char *gname = groupnameof(procname); + if (gname) make(gname, m); +} + +/* + * Update a device that appeared in MAKEDEV.cache. Whenever we update, + * we save what we did into MAKEDEV.cache; this lets us avoid doing + * them over the next time. We only do something if the device has + * disappeared or the major number has changed. + * + * Note that this caching made the shell version go much faster (it took + * around 15 seconds with the cache, vs. over a minute if the cache was + * blown away.) For us, it still does, but it hardly matters: it shaves + * one second off a two-second execution. + * + * Also note the old script used DEVICES instead of MAKEDEV.cache. We + * changed because the old file didn't record whether something was + * a block or character device; since the sets of numbers are independent, + * this was bound to break. + */ +static void update2(const char *name, int ischar, int major) { + int now = get_major(name, ischar, -1); + if (now<0) { + deletion = 1; /* must have been zero if we're doing an update */ + transmake(name, M_CREATE); + deletion = 0; + } + else if (now!=major) { /* oops, it moved; remake it */ + transmake(name, M_CREATE); + if (ischar) cmajors[now].flag=1; + else bmajors[now].flag=1; + } + else { + if (ischar) cmajors[now].flag=1; /* unchanged; inhibit remaking it */ + else bmajors[now].flag=1; /* unchanged; inhibit remaking it */ + } +} + +static void updatefromcache(const char *name, int major, int type) { + update2(name, type=='c', major); +} + + +/* + * Update. Read the information stored in MAKEDEV.cache from the last + * update; fix anything that changed; then create any new devices that + * weren't listed the last time. (We use the "flag" field in the + * majors array to check this.) At that point, write out a new + * cache file. + */ +#define CACHEFILE "MAKEDEV.cache" + +static void update(void) { + FILE *f; + int i; + if (no_proc) { warn("Couldn't read anything from /proc/devices"); return; } + if (deletion) { warn("update and -d are incompatible"); return; } + f = fopen(CACHEFILE, "r"); + if (f) { + doparse(f, 2, CACHEFILE); + fclose(f); + } + for (i=0; i +#include +#include +#include + +#define RULE_CONTEXT (&((PCB).cs[(PCB).ssx])) +#define ERROR_CONTEXT ((PCB).cs[(PCB).error_frame_ssx]) +#define CONTEXT ((PCB).cs[(PCB).ssx]) + + + +parse_pcb_type parse_pcb; +#define PCB parse_pcb + +#line 698 "/usr/local/src/makedev/makedev.syn" +/************************* parsing support *************************/ + +/* + * Don't use the built-in error printing. + */ +#define SYNTAX_ERROR +#define PARSER_STACK_OVERFLOW +#define REDUCTION_TOKEN_ERROR + +static void doparse(FILE *f, int filetype, const char *filename) { + char *x; + int i=0, len; + if (filetype<1 || filetype>4) crash("tried to parse a bad file type"); + if (filetype!=1) { /* /proc/devices won't stat intelligently */ + struct stat buf; + if (fstat(fileno(f), &buf)) crash("fstat failed?!?"); + len = buf.st_size; + } + else len=1023; + x = malloc(len+1); + if (!x) crash("Out of memory"); + + len = fread(x, 1, len, f); /* it shouldn't return a short count... */ + if (len<0) crash("fread failed?!?"); + x[len]=0; + + init_parse(); + PCB.input_code = filetype+'0'; + parse(); + PCB.column--; /* correct for the filetype token */ + while (!PCB.exit_flag) { + PCB.input_code = x[i++]; + parse(); + } + if (PCB.exit_flag == AG_SYNTAX_ERROR_CODE) { + warn("syntax error: %s, line %d, column %d in file %s", + PCB.error_message, PCB.line, PCB.column, filename); + crash("Sorry, can't continue."); + } + else if (PCB.exit_flag != AG_SUCCESS_CODE) { + crash("parser stack overflow!"); + } +} + +#define STRINGSIZE 8192 +static char string_space[STRINGSIZE]; +static int stringptr=0; + +static const char *string_start(int c) { + if (stringptr>=STRINGSIZE) crash("out of string space"); + return string_space[stringptr]=c, string_space+stringptr++; +} + +static void string_push(int c) { + if (stringptr>=STRINGSIZE) crash("out of string space"); + string_space[stringptr++] = c; +} + +static void string_finish(void) { + string_push(0); +} + + +#line 790 "makedev.c" +#line 840 "/usr/local/src/makedev/makedev.syn" + static const char *cur_group=NULL, *cur_class=NULL; + static int cur_type; + static int cur_maj=0, cur_min=0, cur_bot=0, cur_top=0, ishex=0; + + static void dhsproc(const char *g, const char *p, int t, int m) { + cur_group = g; + cur_type = t; + cur_maj = get_major(p, (t=='c'), m); + cur_min = 0; + cur_bot = cur_top = ishex = 0; + if (p) addalias(p,g); + } + + static void newdev(const char *n) { + if (cur_maj<0) return; + init(n, cur_group, cur_class, cur_maj, cur_min, cur_type); + } + static void devrange(const char *n, const char *n1) { + char temp[32]; + if (cur_maj<0) return; + sprintf(temp, "%s%%d%s", n, n1 ? n1 : ""); + initlots(temp, cur_bot, cur_top, cur_group, cur_class, + cur_maj, cur_min, cur_type); + } + static void doinitlink(const char *src, const char *tg) { + if (cur_maj>=0) initlink(src, cur_group, tg); + } + +#line 820 "makedev.c" +#ifndef CONVERT_CASE +#define CONVERT_CASE(c) (c) +#endif +#ifndef TAB_SPACING +#define TAB_SPACING 8 +#endif + +#define ag_rp_1(n, s) (set_major(s,YES,n)) + +#define ag_rp_2(n, s) (set_major(s,NO,n)) + +#define ag_rp_3(n, maj, t) (updatefromcache(n,maj,t)) + +#define ag_rp_4() ('b') + +#define ag_rp_5() ('c') + +#define ag_rp_8(n, i) (add2batch(addbatch(n), i)) + +#define ag_rp_9(b, i) (add2batch(b,i)) + +#define ag_rp_10(n) (n) + +#define ag_rp_11(n) (ignore_procname(n)) + +#define ag_rp_12(t, g, p) (dhsproc(g,p,t,-1)) + +#define ag_rp_13(t, g, p, m) (dhsproc(g,p,t,m)) + +#define ag_rp_14(t, g, m) (dhsproc(g,NULL,t,m)) + +#define ag_rp_15(classname) (classname) + +#define ag_rp_16(c, min) ((cur_class=c, cur_min=min)) + +#define ag_rp_17(a, b) (cur_bot=a, cur_top=b, ishex=0) + +#define ag_rp_18(a, b) (cur_bot=a, cur_top=b, ishex=1) + +#define ag_rp_19(n) (newdev(n)) + +#define ag_rp_20(n, n1) (devrange(n,n1)) + +#define ag_rp_21(n) (devrange(n,NULL)) + +#define ag_rp_22(n, a, b, p, m) (initdisk(n, a, b, p, cur_maj, m)) + +#define ag_rp_23(n, tg) (doinitlink(n, tg)) + +#define ag_rp_24(n) (n) + +#define ag_rp_25(n) (n) + +#define ag_rp_26(n) (n) + +#define ag_rp_27(n, o, g, m) (addclass(n,o,g,m)) + +#define ag_rp_28(n) (make(n, M_OMIT)) + +#define ag_rp_29(n) (make(n, M_OMIT)) + +#define ag_rp_30(n) (n) + +#define ag_rp_31(s) (string_finish(), s) + +#define ag_rp_32(s) (s) + +#define ag_rp_33(c) (string_start(c)) + +#define ag_rp_34(s, c) (string_push(c), s) + +#define ag_rp_35(s) (string_finish(), s) + +#define ag_rp_36(c) (string_start(c)) + +#define ag_rp_37(s, c) (string_push(c), s) + +#define ag_rp_38(c) (c) + +#define ag_rp_39() ('\\') + +#define ag_rp_40() ('"') + +#define ag_rp_41(d) (d-'0') + +#define ag_rp_42(n, d) (n*10 + d-'0') + +#define ag_rp_43(d) (d) + +#define ag_rp_44(n, d) (16*n+d) + +#define ag_rp_45(d) (d) + +#define ag_rp_46(n, d) (16*n+d) + +#define ag_rp_47(d) (d-'0') + +#define ag_rp_48(d) (10 + (d&7)) + +#define ag_rp_49(d) (d-'0') + +#define ag_rp_50(n, d) (n*8+d-'0') + +#define ag_rp_51(x, t) (x+t) + +#define ag_rp_52(x, t) (x-t) + +#define ag_rp_53(t, f) (t*f) + +#define ag_rp_54(f) (-f) + +#define ag_rp_55(x) (x) + + +#define READ_COUNTS +#define WRITE_COUNTS +static parse_vs_type ag_null_value; +#define V(i,t) (*(t *) (&(PCB).vs[(PCB).ssx + i])) +#define VS(i) (PCB).vs[(PCB).ssx + i] + +#ifndef GET_CONTEXT +#define GET_CONTEXT CONTEXT = (PCB).input_context +#endif + +typedef enum { + ag_action_1, + ag_action_2, + ag_action_3, + ag_action_4, + ag_action_5, + ag_action_6, + ag_action_7, + ag_action_8, + ag_action_9, + ag_action_10, + ag_action_11, + ag_action_12 +} ag_parser_action; + +static int ag_ap; + + + +static const unsigned char ag_rpx[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, + 0, 0, 0, 3, 4, 5, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 6, 0, 0, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 0, 0, 0, 0, 0, 25, 26, 0, 0, 0, 27, 28, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 41, 42, 43, 44, + 45, 46, 47, 48, 0, 49, 50, 0, 51, 0, 0, 52, 53 +}; + +static unsigned char ag_key_itt[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0 +}; + +static unsigned short ag_key_pt[] = { + 1,121, 1,122, 1,125, 1,126, 1,138, 1,139,0 +}; + +static unsigned char ag_key_ch[] = { + 0, 47,255, 42,255, 42, 47,255, 88,120,255, 97,108,255,104,108,255, 45, + 47, 48, 66, 67, 98, 99,105,111,255, 42, 47,255, 47, 99,111,255, 42, 47, + 255, 97,108,255, 47, 98, 99,105,255, 42, 47,255, 47,255, 42, 47,255, 47, + 66, 67,255, 47, 99,111,255, 97,108,255, 47, 98, 99,105,255, 47,255, 47, + 66, 67,255, 42, 47,255, 97,108,255,104,108,255, 47, 66, 67, 98, 99,105, + 111,255, 97,108,255,104,108,255, 47, 66, 67, 98, 99,105,111,255, 99,111, + 255, 97,108,255, 98, 99,105,255, 66, 67,255, 42, 47,255, 45, 47,255, 88, + 120,255, 47, 48,255, 42, 47,255, 47, 98, 99,255, 98, 99,255, 42, 47,255, + 97,108,255,104,108,255, 47, 98, 99,105,111,255, 45,255, 88,120,255, 48, + 255 +}; + +static unsigned char ag_key_act[] = { + 0,3,4,3,4,0,0,4,0,0,4,7,7,4,7,7,4,3,2,2,3,3,2,2,7,7,4,0,0,4,2,7,7,4,0, + 0,4,7,7,4,2,2,7,7,4,0,0,4,2,4,0,0,4,2,3,3,4,3,7,7,4,7,7,4,3,2,7,7,4,3, + 4,3,3,3,4,0,0,4,7,7,4,7,7,4,2,3,3,2,2,7,7,4,7,7,4,7,7,4,3,3,3,2,2,7,7, + 4,7,7,4,7,7,4,2,7,7,4,3,3,4,0,0,4,3,2,4,0,0,4,3,2,4,0,0,4,2,7,7,4,7,7, + 4,0,0,4,7,7,4,7,7,4,2,2,2,7,7,4,3,4,0,0,4,2,4 +}; + +static unsigned char ag_key_parm[] = { + 0, 80, 0, 84, 0, 80, 86, 0,145,144, 0, 6, 0, 0, 2, 8, 0,137, + 0, 0,118,117, 0, 0, 4, 10, 0, 80, 86, 0, 0, 8, 10, 0, 80, 86, + 0, 6, 0, 0, 0, 0, 2, 4, 0, 80, 86, 0, 0, 0, 80, 86, 0, 0, + 118,117, 0, 86, 8, 10, 0, 6, 0, 0, 86, 0, 2, 4, 0, 86, 0, 86, + 118,117, 0, 80, 86, 0, 6, 0, 0, 2, 8, 0, 0,118,117, 0, 0, 4, + 10, 0, 6, 0, 0, 2, 8, 0, 86,118,117, 0, 0, 4, 10, 0, 8, 10, + 0, 6, 0, 0, 0, 2, 4, 0,118,117, 0, 80, 86, 0,137, 0, 0,145, + 144, 0, 80, 0, 0, 80, 86, 0, 0, 0, 2, 0, 0, 2, 0, 80, 86, 0, + 6, 0, 0, 2, 8, 0, 0, 0, 0, 4, 10, 0,137, 0,145,144, 0, 0, + 0 +}; + +static unsigned short ag_key_jmp[] = { + 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 38, 42, 0, 46, 49, 0, 4, + 5, 8, 6, 20, 11, 14, 53, 59, 0, 0, 0, 0, 27, 63, 68, 0, 0, 0, + 0, 72, 76, 0, 34, 37, 80, 84, 0, 0, 0, 0, 45, 0, 0, 0, 0, 50, + 90,104, 0,122,124,129, 0,135,139, 0,133, 61,143,147, 0,153, 0,155, + 157,171, 0, 0, 0, 0,221,225, 0,229,232, 0, 75,189,203, 78, 81,236, + 242, 0,280,284, 0,288,291, 0,246,248,262, 92, 95,295,301, 0,305,310, + 0,314,318, 0,109,322,326, 0,332,346, 0, 0, 0, 0,364,119, 0, 0, + 0, 0,366,125, 0, 0, 0, 0,131,368,373, 0,377,382, 0, 0, 0, 0, + 386,390, 0,394,397, 0,141,144,147,401,407, 0,411, 0, 0, 0, 0,158, + 0 +}; + +static unsigned short ag_key_index[] = { + 1, 3, 17, 0, 3, 3, 30, 40, 48, 53, 57, 64, 69, 71, 0, 0, 84, 98, + 106,112, 0,116, 0, 1, 1, 0, 0,106, 48, 48, 48, 48, 1, 1, 0, 0, + 0, 69,112, 0,122, 48, 0, 0, 48, 48, 69, 69,116, 48, 69, 69, 0,128, + 0, 0, 0, 69, 0, 69, 0, 0,134,138, 0, 0, 0,128, 0, 0, 69, 48, + 69, 0,150, 64, 0,156, 0, 69, 0,116, 0,116, 69, 0, 0, 0, 1, 0, + 0, 69, 69, 0, 1, 0,128,161, 0, 0, 0, 0, 0, 69, 69, 0, 57, 0, + 0, 0, 69, 0, 64, 69, 1, 0, 1, 1, 0, 0, 0, 0, 0,161, 64, 48, + 69, 69, 48, 0,128, 0, 48, 0, 0,161,161, 69, 69, 69, 69, 0, 0, 0, + 0, 0,128,161,161,128,161, 1, 0, 69, 69, 0, 1, 0, 69, 0 +}; + +static unsigned char ag_key_ends[] = { +42,0, 47,0, 62,0, 108,111,99,107,32,100,101,118,105,99,101,115,58,0, +104,97,114,97,99,116,101,114,32,100,101,118,105,99,101,115,58,0, +116,99,104,0, 111,99,107,0, 97,114,0, 97,115,115,0, +103,110,111,114,101,0, 109,105,116,0, 108,97,115,115,0, +109,105,116,0, 116,99,104,0, 111,99,107,0, 104,97,114,0, +103,110,111,114,101,0, 108,111,99,107,32,100,101,118,105,99,101,115,58,0, +104,97,114,97,99,116,101,114,32,100,101,118,105,99,101,115,58,0, +47,0, 108,97,115,115,0, 109,105,116,0, 47,0, 116,99,104,0, +111,99,107,0, 104,97,114,0, 103,110,111,114,101,0, 47,0, 47,0, +108,111,99,107,32,100,101,118,105,99,101,115,58,0, +104,97,114,97,99,116,101,114,32,100,101,118,105,99,101,115,58,0, +108,111,99,107,32,100,101,118,105,99,101,115,58,0, +104,97,114,97,99,116,101,114,32,100,101,118,105,99,101,115,58,0, +116,99,104,0, 111,99,107,0, 97,114,0, 97,115,115,0, +103,110,111,114,101,0, 109,105,116,0, 47,0, +108,111,99,107,32,100,101,118,105,99,101,115,58,0, +104,97,114,97,99,116,101,114,32,100,101,118,105,99,101,115,58,0, +116,99,104,0, 111,99,107,0, 97,114,0, 97,115,115,0, +103,110,111,114,101,0, 109,105,116,0, 108,97,115,115,0, +109,105,116,0, 116,99,104,0, 111,99,107,0, 104,97,114,0, +103,110,111,114,101,0, 108,111,99,107,32,100,101,118,105,99,101,115,58,0, +104,97,114,97,99,116,101,114,32,100,101,118,105,99,101,115,58,0, +62,0, 42,0, 108,111,99,107,0, 104,97,114,0, 108,111,99,107,0, +104,97,114,0, 116,99,104,0, 111,99,107,0, 97,114,0, 97,115,115,0, +103,110,111,114,101,0, 109,105,116,0, 62,0, +}; +#define AG_TCV(x) (((int)(x) >= -1 && (int)(x) <= 255) ? ag_tcv[(x) + 1] : 0) + +static const unsigned char ag_tcv[] = { + 18, 18,152,152,152,152,152,152,152,152,150, 93,152,152,150,152,152,152, + 152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,151,153, 95, + 87,153,153,153,153,130,128,149,148,127,133,153,135,154,113,114,115,116, + 154,154,154,155,155,131,153,153,129,153,153,153,156,156,156,156,156,156, + 157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157, + 157,157,134, 99,132,153,157,153,156,119,120,156,156,156,157,157,157,157, + 157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,124,153, + 123,153,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152, + 152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152, + 152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152, + 152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152, + 152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152, + 152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152, + 152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152, + 152,152,152,152,152 +}; + +#ifndef SYNTAX_ERROR +#define SYNTAX_ERROR fprintf(stderr,"%s, line %d, column %d\n", \ + (PCB).error_message, (PCB).line, (PCB).column) +#endif + +#ifndef FIRST_LINE +#define FIRST_LINE 1 +#endif + +#ifndef FIRST_COLUMN +#define FIRST_COLUMN 1 +#endif + + +#ifndef PARSER_STACK_OVERFLOW +#define PARSER_STACK_OVERFLOW {fprintf(stderr, \ + "\nParser stack overflow, line %d, column %d\n",\ + (PCB).line, (PCB).column);} +#endif + +#ifndef REDUCTION_TOKEN_ERROR +#define REDUCTION_TOKEN_ERROR {fprintf(stderr, \ + "\nReduction token error, line %d, column %d\n", \ + (PCB).line, (PCB).column);} +#endif + + +typedef enum + {ag_accept_key, ag_set_key, ag_jmp_key, ag_end_key, ag_no_match_key, + ag_cf_accept_key, ag_cf_set_key, ag_cf_end_key} key_words; + + +static void ag_track(void) { + int ag_k = 0; + while (ag_k < (PCB).rx) { + int ag_ch = (PCB).lab[ag_k++]; + switch (ag_ch) { + case '\n': + (PCB).column = 1, (PCB).line++; + case '\r': + case '\f': + break; + case '\t': + (PCB).column += (TAB_SPACING) - ((PCB).column - 1) % (TAB_SPACING); + break; + default: + (PCB).column++; + } + } + ag_k = 0; + while ((PCB).rx < (PCB).fx) (PCB).lab[ag_k++] = (PCB).lab[(PCB).rx++]; + (PCB).fx = ag_k; + (PCB).rx = 0; +} + + +static void ag_prot(void) { + int ag_k = 38 - ++(PCB).btsx; + if (ag_k <= (PCB).ssx) { + (PCB).exit_flag = AG_STACK_ERROR_CODE; + PARSER_STACK_OVERFLOW; + return; + } + (PCB).bts[(PCB).btsx] = (PCB).sn; + (PCB).bts[ag_k] = (PCB).ssx; + (PCB).vs[ag_k] = (PCB).vs[(PCB).ssx]; + (PCB).ss[ag_k] = (PCB).ss[(PCB).ssx]; +} + +static void ag_undo(void) { + if ((PCB).drt == -1) return; + while ((PCB).btsx) { + int ag_k = 38 - (PCB).btsx; + (PCB).sn = (PCB).bts[(PCB).btsx--]; + (PCB).ssx = (PCB).bts[ag_k]; + (PCB).vs[(PCB).ssx] = (PCB).vs[ag_k]; + (PCB).ss[(PCB).ssx] = (PCB).ss[ag_k]; + } + (PCB).token_number = (parse_token_type) (PCB).drt; + (PCB).ssx = (PCB).dssx; + (PCB).sn = (PCB).dsn; + (PCB).drt = -1; +} + + +static const unsigned char ag_tstt[] = { +151,150,80,0,2,111,112, +157,156,155,154,153,152,151,150,149,148,135,134,133,132,131,130,129,128,127, + 124,123,120,119,116,115,114,113,99,95,93,87,0,82,83, +151,150,80,0,2, +116,115,114,113,0,5,6,8,10,12, +157,156,155,154,153,152,151,150,149,148,135,134,133,132,131,130,129,128,127, + 124,123,120,119,116,115,114,113,99,95,93,87,0, +84,0, +151,150,80,0,2,111,112, +151,150,80,0,2,111,112, +151,150,80,0,2,111,112, +151,150,80,0,2,111,112, +139,138,93,87,86,0,3,13,14,15,85,88,92,140, +126,125,122,121,120,119,93,87,86,0,3,11,14,15,85,88,92,140, +157,156,133,120,119,95,93,87,86,0,3,9,14,15,85,88,92,140, +118,117,93,87,86,0,3,7,14,15,85,88,92,140, +157,156,155,154,153,152,151,150,149,148,135,134,133,132,131,130,129,128,127, + 124,123,120,119,116,115,114,113,99,95,87,18,0,90,91, +93,0, +151,150,80,0,2,111,112, +157,156,155,154,139,138,133,127,126,125,123,122,121,120,119,118,117,116,115, + 114,113,95,93,87,86,18,0,3,88,92,140, +139,138,0,69,70,71,72,73,75, +126,125,122,121,120,119,0,29,30,31,32,33,34,35,36,42,45, +157,156,133,120,119,95,0,1,4,26,27,28,141,142, +118,117,0,16,17,19,22, +157,156,155,154,153,152,151,150,149,148,135,134,133,132,131,130,129,128,127, + 124,123,120,119,116,115,114,113,99,95,87,18,0, +151,150,80,0,2,111,112, +151,150,80,0,2,111,112, +157,156,133,124,120,119,95,0,1,4,26,37,141,142, +157,156,133,120,119,95,0,1,4,26,141,142, +139,138,18,0,69,71,72,73,75, +151,150,80,0,2,111,112, +151,150,80,0,2,111,112, +151,150,80,0,2,111,112, +151,150,80,0,2,111,112, +151,150,80,0,2,111,112, +151,150,80,0,2,111,112, +130,0,50, +157,156,133,120,119,95,0,1,4,26,46,141,142, +124,0,37, +157,156,133,124,120,119,95,93,87,86,0,3,14,15,37,85,88,92,140, +126,125,122,121,120,119,18,0,29,30,31,32,33,34,36,42,45, +157,156,155,154,153,151,149,148,135,134,133,132,131,130,129,128,127,124,123, + 120,119,116,115,114,113,99,87,0,96,97, +151,150,80,0,2,111,112, +157,156,155,154,151,150,133,120,119,116,115,114,113,80,0,2,111,112, +155,154,116,115,114,113,0,25,100, +157,156,133,120,119,95,18,0,1,4,26,27,141,142, +151,150,80,0,2,111,112, +151,150,80,0,2,111,112, +87,86,0,3,14,85,88,92,140, +87,86,0,3,14,85,88,92,140, +118,117,18,0,16,19,22, +151,150,80,0,2,111,112, +157,156,133,120,119,95,93,87,86,0,3,14,15,85,88,92,140, +87,86,0,3,14,85,88,92,140, +131,0,57, +151,150,80,0,2,111,112, +157,156,133,120,119,95,0,1,4,26,51,141,142, +124,0,37, +127,123,0,41,48,49, +157,156,133,120,119,95,93,87,86,0,3,14,15,85,88,92,140, +157,156,133,120,119,95,0,1,4,26,38,65,141,142, +157,156,133,123,120,119,95,93,87,86,0,3,14,15,85,88,92,140, +99,95,0, +157,156,155,154,153,151,149,148,135,134,133,132,131,130,129,128,127,124,123, + 120,119,116,115,114,113,99,95,87,0,97, +151,150,80,0,2,111,112, +155,154,122,121,120,119,116,115,114,113,0,29,30,31,32,33,100, +155,154,116,115,114,113,0,23,24,25,100, +155,154,116,115,114,113,0,20,21,25,100, +157,156,133,120,119,95,0,1,4,26,76,77,141,142, +151,150,80,0,2,111,112, +157,156,133,120,119,95,0,1,4,26,141,142, +129,127,0,48,52, +157,156,133,120,119,95,93,87,86,0,3,14,15,85,88,92,140, +151,150,80,0,2,111,112, +157,156,133,123,120,119,95,93,87,86,0,3,14,15,85,88,92,140, +157,156,133,120,119,95,0,1,4,26,47,141,142, +151,150,80,0,2,111,112, +126,125,122,121,120,119,93,87,86,18,0,3,14,15,85,88,92,140, +157,156,133,120,119,95,0,1,4,26,43,44,141,142, +137,134,130,0,50,55,56,59,60,68, +157,156,133,120,119,95,0,1,4,26,38,39,40,65,141,142, +87,86,0,3,14,85,88,92,140, +157,156,155,154,133,120,119,116,115,114,113,95,0,1,4,26,100,141,142, +155,154,116,115,114,113,0,23,25,100, +157,156,155,154,133,120,119,116,115,114,113,95,0,1,4,26,100,141,142, +155,154,116,115,114,113,0,20,25,100, +157,156,133,127,123,120,119,95,93,87,86,0,3,14,15,85,88,92,140, +157,156,133,123,120,119,95,0,1,4,26,41,76,141,142, +157,156,133,120,119,95,0,1,4,26,141,142, +155,154,116,115,114,113,0,25,100, +151,150,80,0,2,111,112, +157,156,133,120,119,95,0,1,4,26,53,141,142, +157,156,133,120,119,95,0,1,4,26,47,141,142, +157,156,133,127,123,120,119,95,93,87,86,0,3,14,15,85,88,92,140, +157,156,133,127,123,120,119,95,93,87,86,0,3,14,15,85,88,92,140, +157,156,133,123,120,119,95,0,1,4,26,41,43,141,142, +151,150,80,0,2,111,112, +157,156,133,120,119,95,0,1,4,26,141,142, +151,150,80,0,2,111,112, +157,156,155,154,145,144,133,120,119,116,115,114,113,0,25,63,66,100,101,102, + 103, +157,156,133,130,120,119,95,0,1,4,26,50,55,56,65,141,142, +157,156,133,120,119,95,0,1,4,26,141,142, +131,0,57, +157,156,133,120,119,95,0,1,4,26,38,65,141,142, +123,0,41, +87,86,0,3,14,85,88,92,140, +87,86,0,3,14,85,88,92,140, +127,0,48,49, +139,138,93,87,86,18,0,3,14,15,85,88,92,140, +154,116,115,114,113,0,74,78,106, +155,154,128,116,115,114,113,0,54,100, +128,127,0,48,54, +157,156,133,127,123,120,119,95,93,87,86,0,3,14,15,85,88,92,140, +127,0,48,49, +126,125,122,121,120,119,93,87,86,18,0,3,14,15,85,88,92,140, +87,86,0,3,14,85,88,92,140, +151,150,80,0,2,111,112, +133,0,61, +151,150,80,0,2,111,112, +151,150,80,0,2,111,112, +156,155,154,120,119,116,115,114,113,0,100,104,105, +156,155,154,133,120,119,116,115,114,113,0,61,100,104,105, +155,154,133,116,115,114,113,0,61,100, +130,0,50,55,56, +128,0,54, +155,154,145,144,133,130,116,115,114,113,0,25,50,58,61,63,100,101,102,103, + 107,109, +126,125,122,121,120,119,93,87,86,18,0,3,14,15,85,88,92,140, +151,150,80,0,2,111,112, +154,116,115,114,113,0,106, +87,86,0,3,14,85,88,92,140, +151,150,80,0,2,111,112, +155,154,116,115,114,113,0,25,100, +151,150,80,0,2,111,112, +157,156,133,120,119,0,66, +151,150,80,0,2,111,112, +156,155,154,120,119,116,115,114,113,0,64,100,104,105, +155,154,116,115,114,113,0,25,100, +155,154,145,144,133,130,116,115,114,113,0,25,50,58,61,63,100,101,102,103, + 107,109, +155,154,145,144,133,130,116,115,114,113,0,25,50,61,63,100,101,102,103,109, +156,155,154,120,119,116,115,114,113,0,100,104,105, +155,154,116,115,114,113,0,100, +149,0,110, +148,133,87,86,0,3,14,61,85,88,92,108,140, +155,154,128,116,115,114,113,0,54,100, +132,0,62, +156,155,154,132,120,119,116,115,114,113,0,62,100,104,105, +155,154,132,116,115,114,113,0,62,100, +148,133,128,0,54,61,108, +151,150,80,0,2,111,112, +155,154,145,144,133,130,116,115,114,113,0,25,50,61,63,100,101,102,103,109, +155,154,145,144,133,130,116,115,114,113,0,25,50,61,63,100,101,102,103,107, + 109, +151,150,80,0,2,111,112, +155,154,145,144,133,130,116,115,114,113,0,25,50,61,63,100,101,102,103,107, + 109, +151,150,80,0,2,111,112, +155,154,116,115,114,113,0,25,100, +149,0,110, +149,0,110, +155,154,135,116,115,114,113,0,67,100, +151,150,80,0,2,111,112, +155,154,116,115,114,113,0,25,100, +155,154,116,115,114,113,87,86,0,3,14,85,88,92,100,140, + 0 +}; + + +static unsigned char ag_astt[1821] = { + 1,1,1,8,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,8,1,1,9,9,1,5,3,1,1,1,1,7,0,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,5,3,7,1,1,1,5,1,1,3,1,1,1,5,1,1,3,1,1,1,5,1,1,3, + 1,1,1,5,1,1,3,8,8,8,1,1,7,1,3,1,1,1,1,1,1,8,8,8,8,8,8,8,1,1,7,1,3,1,1,1,1, + 1,1,8,8,8,8,8,8,8,1,1,7,1,3,1,1,1,1,1,1,8,8,8,1,1,7,1,3,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,5,1,3,3,7,1,1,1,5, + 1,1,3,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,8,1,1,5,7,3,1,1,1,1,1,7, + 1,1,1,1,1,1,1,1,1,1,1,1,7,1,2,2,2,2,1,1,1,1,1,2,2,2,2,2,1,7,2,2,1,1,1,1,1, + 1,1,7,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,5,1,1,1,5,1,1,3,1,1,1,5,1,1,3,2,2,2,1,2,2,1,7,2,2,1,1,1,1,2,2,2,2,2,1,7, + 2,2,1,1,1,1,1,3,7,3,3,3,1,1,1,1,1,5,1,1,3,1,1,1,5,1,1,3,1,1,1,5,1,1,3,1,1, + 1,5,1,1,3,1,1,1,5,1,1,3,1,1,1,5,1,1,3,1,7,1,2,2,2,2,2,1,7,2,2,1,1,1,1,1,7, + 1,8,8,8,1,8,8,8,8,1,1,7,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,7,1,2,2,2,2,3,1,1,1, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,7,1,2,1,1,1,5,1,1,3, + 10,10,10,10,1,1,10,10,10,10,10,10,10,1,5,1,1,3,1,1,1,1,1,1,7,1,2,2,2,2,2,2, + 1,3,7,2,2,1,3,1,1,1,1,1,5,1,1,3,1,1,1,5,1,1,3,1,1,8,1,1,1,1,1,1,1,1,8,1,1, + 1,1,1,1,1,1,3,7,3,1,1,1,1,1,5,1,1,3,8,8,8,8,8,8,8,1,1,7,1,1,1,1,1,1,1,1,1, + 8,1,2,1,1,1,1,1,7,1,1,1,1,5,1,1,3,2,2,2,2,2,1,7,2,2,2,1,1,1,1,7,1,1,1,8,1, + 1,1,8,8,8,8,8,8,8,1,1,7,1,1,1,1,1,1,1,2,2,2,2,2,1,7,2,2,2,3,1,1,1,8,8,8,8, + 8,8,8,8,1,1,7,1,1,1,1,1,1,1,2,2,7,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,1,2,2,7,2,1,1,1,5,1,1,3,1,1,1,1,1,1,1,1,1,1,7,1,2,2,2,2,2,1,1,1, + 1,1,1,7,1,1,1,2,1,1,1,1,1,1,7,1,1,1,2,2,2,2,2,2,1,7,2,2,1,1,1,1,1,1,1,1,5, + 1,1,3,2,2,2,2,2,1,7,2,2,1,1,1,1,1,7,1,1,8,8,8,8,8,8,8,1,1,7,1,1,1,1,1,1,1, + 1,1,1,5,1,1,3,5,5,5,5,5,5,5,8,1,1,7,1,3,3,1,1,1,1,2,2,2,2,2,1,7,2,2,2,1,1, + 1,1,1,1,5,1,1,3,5,5,5,5,5,5,8,1,1,5,7,1,3,3,1,1,1,1,2,2,2,2,2,1,7,2,2,1,1, + 1,1,1,1,1,1,7,1,1,2,1,1,1,2,2,2,2,2,1,8,2,2,2,1,1,1,1,1,1,1,1,8,1,2,1,1,1, + 1,2,2,1,1,2,2,2,1,1,1,1,1,7,2,2,1,2,1,1,1,1,1,1,1,1,5,3,1,2,2,2,1,1,2,2,2, + 1,1,1,1,1,7,2,2,1,2,1,1,1,1,1,1,1,1,5,3,1,2,8,8,8,8,8,8,8,8,8,1,1,7,1,1,1, + 1,1,1,1,2,2,2,1,2,2,1,7,2,2,1,1,3,1,1,2,2,2,2,2,1,7,2,2,1,1,1,1,1,1,1,1,1, + 7,1,2,1,1,1,5,1,1,3,2,2,2,2,2,1,7,2,2,2,1,1,1,2,2,2,2,2,1,7,2,2,2,1,1,1,5, + 5,5,5,5,5,5,5,8,1,1,7,1,2,2,1,1,1,1,8,8,8,8,8,8,8,8,8,1,1,7,1,1,1,1,1,1,1, + 2,2,2,1,2,2,1,7,2,2,1,1,3,1,1,1,1,1,5,1,1,3,2,2,2,2,2,1,7,2,2,1,1,1,1,1,1, + 5,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,7,1,1,1,2,1,1,1,2,2,2,1,2,2,1,7,2,2,2,1, + 1,2,1,1,1,2,2,2,2,2,1,7,2,2,1,1,1,1,7,1,2,2,2,2,2,1,5,2,2,2,3,1,1,1,1,7,1, + 1,1,8,1,2,1,1,1,1,1,1,8,1,2,1,1,1,1,1,5,1,2,5,5,8,1,1,5,7,1,3,3,1,1,1,1,1, + 1,1,1,1,7,1,1,2,1,1,1,1,1,1,1,7,2,2,1,1,7,1,2,5,5,5,5,5,5,5,5,8,1,1,7,1,2, + 2,1,1,1,1,1,5,1,2,5,5,5,5,5,5,8,1,1,5,7,1,3,3,1,1,1,1,1,1,8,1,2,1,1,1,1,1, + 1,1,5,1,1,3,1,7,1,1,1,1,5,1,1,3,1,1,1,5,1,1,3,1,1,1,1,1,1,1,1,1,7,2,2,2,1, + 1,1,1,1,1,1,1,1,1,7,1,2,2,2,1,1,1,1,1,1,1,7,1,2,1,7,1,1,2,1,7,2,1,1,1,1,1, + 1,1,1,1,1,7,1,1,1,1,1,2,1,1,1,1,1,5,5,5,5,5,5,8,1,1,5,7,1,3,3,1,1,1,1,1,1, + 1,5,1,1,3,1,1,1,1,1,4,2,1,1,8,1,2,1,1,1,1,1,1,1,5,1,1,3,1,1,1,1,1,1,7,1,2, + 1,1,1,5,1,1,3,1,1,1,1,1,7,1,1,1,1,5,1,1,3,1,1,1,1,1,1,1,1,1,7,1,2,2,2,1,1, + 1,1,1,1,7,1,2,1,1,1,1,1,1,1,1,1,1,7,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,7,1,1,1,1,2,1,1,1,2,1,1,1,1,1,1,1,1,1,5,2,2,2,1,1,1,1,1,1,5,2,1,5,1,1, + 1,1,1,8,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,7,2,2,1,7,1,1,1,1,1,1,1,1,1,1,1,7,2, + 2,2,2,1,1,1,1,1,1,1,7,2,2,1,1,1,7,2,1,1,1,1,1,5,1,1,3,1,1,1,1,1,1,1,1,1,1, + 7,1,1,1,1,2,1,1,1,2,1,1,1,1,1,1,1,1,1,1,7,1,1,1,1,2,1,1,1,1,1,1,1,1,5,1,1, + 3,1,1,1,1,1,1,1,1,1,1,7,1,1,1,1,2,1,1,1,1,1,1,1,1,5,1,1,3,1,1,1,1,1,1,7,1, + 2,1,4,1,1,4,1,1,1,1,1,1,1,1,7,1,2,1,1,1,5,1,1,3,1,1,1,1,1,1,7,1,2,1,1,1,1, + 1,1,1,1,8,1,2,1,1,1,2,1,11 +}; + + +static unsigned char ag_pstt[] = { +2,2,1,3,2,2,3, +4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,4,5, +122,122,1,124,122, +6,7,8,9,3,0,13,12,11,10, +74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74, + 74,74,74,74,74,74,76, +77,5, +2,2,1,123,2,2,128, +2,2,1,123,2,2,127, +2,2,1,123,2,2,126, +2,2,1,123,2,2,125, +18,18,15,14,14,10,17,4,18,18,17,14,15,16, +19,19,19,19,19,19,15,14,14,11,17,3,19,19,17,14,15,16, +20,20,20,20,20,20,15,14,14,12,17,2,20,20,17,14,15,16, +21,21,15,14,14,13,17,1,21,21,17,14,15,16, +22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, + 22,22,22,22,22,22,85,22,88, +89,15, +2,2,1,123,2,2,152, +80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,15,14,14, + 80,17,79,14,15,16, +23,24,18,27,27,27,27,26,25, +32,33,28,29,30,31,19,34,22,23,24,25,38,38,37,36,35, +92,92,92,92,92,39,20,90,91,42,43,43,41,40, +44,45,21,48,48,47,46, +84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84, + 84,84,84,84,84,84,86, +2,2,1,123,2,2,151, +2,2,1,123,2,2,150, +92,92,92,49,92,92,39,25,90,91,51,50,41,40, +92,92,92,92,92,39,26,90,91,52,41,40, +23,24,62,27,61,61,61,26,25, +2,2,1,123,2,2,134, +2,2,1,123,2,2,133, +2,2,1,123,2,2,132, +2,2,1,123,2,2,131, +2,2,1,123,2,2,138, +2,2,1,123,2,2,137, +53,34,54, +92,92,92,92,92,39,35,90,91,55,56,41,40, +49,36,57, +58,58,58,49,58,58,58,15,14,14,37,17,58,58,59,17,14,15,16, +32,33,28,29,30,31,28,38,34,22,23,24,25,27,37,36,35, +97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97, + 60,97,39,61,95, +2,2,1,123,2,2,154, +93,93,93,93,2,2,93,93,93,93,93,93,93,1,123,2,2,153, +62,62,62,62,62,62,42,63,100, +92,92,92,92,92,39,20,43,90,91,42,19,41,40, +2,2,1,123,2,2,130, +2,2,1,123,2,2,129, +14,14,15,17,64,17,14,15,16, +14,14,15,17,65,17,14,15,16, +44,45,9,48,8,47,46, +2,2,1,123,2,2,136, +66,66,66,66,66,66,15,14,14,50,17,66,66,17,14,15,16, +14,14,15,17,66,17,14,15,16, +67,52,68, +2,2,1,123,2,2,142, +92,92,92,92,92,39,54,90,91,58,69,41,40, +49,55,70, +71,74,73,75,72,73, +76,76,76,76,76,76,15,14,14,57,17,76,76,17,14,15,16, +92,92,92,92,92,39,58,90,91,57,34,77,41,40, +78,78,78,78,78,78,78,15,14,14,59,17,78,78,17,14,15,16, +98,99,60, +97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97, + 60,94,97,61,96, +2,2,1,123,2,2,155, +62,62,28,29,30,31,62,62,62,62,63,79,22,23,24,25,101, +62,62,62,62,62,62,64,81,81,80,100, +62,62,62,62,62,62,65,83,83,82,100, +92,92,92,92,92,39,66,90,91,84,85,85,41,40, +2,2,1,123,2,2,143, +92,92,92,92,92,39,68,90,91,86,41,40, +88,71,69,87,89, +90,90,90,90,90,90,15,14,14,70,17,90,90,17,14,15,16, +2,2,1,123,2,2,139, +5,5,5,5,5,5,5,15,14,14,72,17,41,41,17,14,15,16, +92,92,92,92,92,39,73,90,91,43,91,41,40, +2,2,1,123,2,2,135, +5,5,5,5,5,5,15,14,14,5,75,17,38,38,17,14,15,16, +92,92,92,92,92,39,76,90,91,92,93,93,41,40, +94,96,53,77,99,100,52,98,97,95, +92,92,92,92,92,39,102,90,91,57,101,101,102,77,41,40, +14,14,15,17,21,17,14,15,16, +92,92,62,62,92,92,92,62,62,62,62,39,80,90,91,103,101,41,40, +62,62,62,62,62,62,15,14,80,100, +92,92,62,62,92,92,92,62,62,62,62,39,82,90,91,104,101,41,40, +62,62,62,62,62,62,12,11,82,100, +105,105,105,105,105,105,105,105,15,14,14,84,17,105,105,17,14,15,16, +92,92,92,74,92,92,39,85,90,91,84,106,68,41,40, +92,92,92,92,92,39,86,90,91,107,41,40, +62,62,62,62,62,62,87,108,100, +2,2,1,123,2,2,141, +92,92,92,92,92,39,89,90,91,59,109,41,40, +92,92,92,92,92,39,90,90,91,43,110,41,40, +5,5,5,5,5,5,5,5,15,14,14,91,17,42,42,17,14,15,16, +111,111,111,111,111,111,111,111,15,14,14,92,17,111,111,17,14,15,16, +92,92,92,74,92,92,39,93,90,91,92,112,36,41,40, +2,2,1,123,2,2,149, +92,92,92,92,92,39,95,90,91,113,41,40, +2,2,1,123,2,2,146, +114,114,62,62,116,117,114,114,114,62,62,62,62,97,120,119,115,100,118,118, + 118, +92,92,92,53,92,92,39,98,90,91,57,99,100,54,121,41,40, +92,92,92,92,92,39,99,90,91,122,41,40, +67,100,123, +92,92,92,92,92,39,32,90,91,57,30,77,41,40, +74,102,124, +14,14,15,17,17,17,14,15,16, +14,14,15,17,16,17,14,15,16, +71,40,72,70, +5,5,15,14,14,5,106,17,69,69,17,14,15,16, +125,125,125,125,125,107,127,126,110, +62,62,128,62,62,62,62,108,47,101, +128,71,109,129,45, +5,5,5,5,5,5,5,5,15,14,14,110,17,39,39,17,14,15,16, +71,40,72,44, +5,5,5,5,5,5,15,14,14,5,112,17,37,37,17,14,15,16, +14,14,15,17,56,17,14,15,16, +2,2,1,123,2,2,148, +130,115,131, +2,2,1,123,2,2,157, +2,2,1,123,2,2,156, +132,62,62,132,132,62,62,62,62,118,108,104,109, +132,62,62,130,132,132,62,62,62,62,119,133,108,105,109, +62,62,130,62,62,62,62,120,134,101, +53,121,99,100,53, +128,122,48, +62,62,116,117,130,53,62,62,62,62,123,138,135,140,136,137,100,118,118,118, + 139,139, +5,5,5,5,5,5,15,14,14,5,124,17,33,33,17,14,15,16, +2,2,1,123,2,2,159, +125,125,125,125,125,71,111, +14,14,15,17,65,17,14,15,16, +2,2,1,123,2,2,140, +62,62,62,62,62,62,129,141,100, +2,2,1,123,2,2,145, +114,114,114,114,114,131,142, +2,2,1,123,2,2,158, +132,62,62,132,132,62,62,62,62,133,143,108,106,109, +62,62,62,62,62,62,134,144,100, +62,62,116,117,130,53,62,62,62,62,135,138,135,145,136,137,100,118,118,118, + 139,139, +62,62,116,117,130,53,62,62,62,62,136,138,135,136,137,100,118,118,118,119, +132,62,62,132,132,62,62,62,62,118,108,105,109, +62,62,62,62,62,62,117,101, +146,112,147, +149,130,14,14,15,17,49,148,17,14,15,150,16, +62,62,128,62,62,62,62,141,46,101, +151,142,152, +132,62,62,151,132,132,62,62,62,62,143,51,108,107,109, +62,62,151,62,62,62,62,144,50,101, +149,130,128,145,120,148,150, +2,2,1,123,2,2,161, +62,62,116,117,130,53,62,62,62,62,147,138,135,136,137,100,118,118,118,116, +62,62,116,117,130,53,62,62,62,62,148,138,135,136,137,100,118,118,118,153, + 153, +2,2,1,123,2,2,160, +62,62,116,117,130,53,62,62,62,62,150,138,135,136,137,100,118,118,118,154, + 154, +2,2,1,123,2,2,144, +62,62,62,62,62,62,152,155,100, +146,114,147, +146,113,147, +62,62,156,62,62,62,62,155,157,101, +2,2,1,123,2,2,147, +62,62,62,62,62,62,157,158,100, +62,62,62,62,62,62,14,14,15,17,55,17,14,15,101,16, + 0 +}; + + +static const unsigned short ag_sbt[] = { + 0, 7, 41, 46, 56, 88, 90, 97, 104, 111, 118, 132, 150, 168, + 182, 216, 218, 225, 256, 265, 282, 296, 303, 335, 342, 349, 363, 375, + 384, 391, 398, 405, 412, 419, 426, 429, 442, 445, 464, 481, 511, 518, + 536, 545, 559, 566, 573, 582, 591, 598, 605, 622, 631, 634, 641, 654, + 657, 663, 680, 694, 712, 715, 745, 752, 769, 780, 791, 805, 812, 824, + 829, 846, 853, 871, 884, 891, 909, 923, 933, 949, 958, 977, 987,1006, + 1016,1035,1050,1062,1071,1078,1091,1104,1123,1142,1157,1164,1176,1183, + 1204,1221,1233,1236,1250,1253,1262,1271,1275,1289,1298,1308,1313,1332, + 1336,1354,1363,1370,1373,1380,1387,1400,1415,1425,1430,1433,1455,1473, + 1480,1487,1496,1503,1512,1519,1526,1533,1547,1556,1578,1598,1611,1619, + 1622,1635,1645,1648,1663,1673,1680,1687,1707,1728,1735,1756,1763,1772, + 1775,1778,1788,1795,1804,1820 +}; + + +static const unsigned short ag_sbe[] = { + 3, 38, 44, 50, 87, 89, 93, 100, 107, 114, 123, 141, 159, 173, + 213, 217, 221, 251, 258, 271, 288, 298, 334, 338, 345, 356, 369, 378, + 387, 394, 401, 408, 415, 422, 427, 435, 443, 455, 471, 508, 514, 532, + 542, 552, 562, 569, 575, 584, 594, 601, 614, 624, 632, 637, 647, 655, + 659, 672, 686, 704, 714, 743, 748, 762, 775, 786, 797, 808, 818, 826, + 838, 849, 863, 877, 887, 901, 915, 926, 939, 951, 970, 983, 999,1012, + 1027,1042,1056,1068,1074,1084,1097,1115,1134,1149,1160,1170,1179,1196, + 1211,1227,1234,1242,1251,1255,1264,1272,1281,1294,1305,1310,1324,1333, + 1346,1356,1366,1371,1376,1383,1396,1410,1422,1426,1431,1443,1465,1476, + 1485,1489,1499,1509,1515,1524,1529,1542,1553,1566,1588,1607,1617,1620, + 1626,1642,1646,1658,1670,1676,1683,1697,1717,1731,1745,1759,1769,1773, + 1776,1785,1791,1801,1812,1820 +}; + + +static const unsigned char ag_fl[] = { + 2,2,2,2,2,0,1,1,2,3,1,2,3,1,2,3,3,3,1,2,3,4,1,1,1,1,1,2,3,1,2,0,1,6,3, + 1,2,6,4,5,0,2,4,1,3,6,8,6,3,4,5,5,2,4,3,10,4,1,1,1,1,2,3,1,1,7,3,1,2,6, + 3,1,1,1,2,0,1,3,1,2,1,1,1,1,2,0,1,0,2,2,1,1,1,2,3,1,2,1,2,2,1,2,1,1,2, + 2,1,2,1,1,1,2,1,3,3,1,3,1,1,2,3,1,2,0,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 +}; + +static const unsigned char ag_ptt[] = { + 0, 5, 5, 5, 5, 15, 15, 17, 17, 7, 21, 21, 16, 24, 24, 16, 20, 23, + 28, 28, 9, 27, 29, 29, 29, 29, 35, 35, 11, 39, 39, 40, 40, 34, 34, 44, + 44, 34, 34, 46, 49, 49, 46, 47, 43, 36, 36, 36, 55, 56, 59, 59, 38, 38, + 38, 38, 38, 65, 51, 53, 70, 70, 13, 69, 69, 71, 72, 77, 77, 72, 76, 74, + 2, 82, 82, 83, 83, 2, 85, 85, 14, 88, 88, 90, 90, 91, 91, 92, 92,140, + 26, 26,141,141,142, 96, 96, 97, 97, 97, 25, 25,103,103, 63, 63, 64, 64, + 104,104, 78, 78, 58, 58, 58,107,107,109,109,109,109,111,111,112,112, 6, + 8, 10, 12, 19, 22, 30, 31, 32, 33, 41, 37, 42, 45, 48, 54, 52, 50, 57, + 62, 61, 60, 67, 66, 68, 73, 75, 3, 1, 4,100,101,102,105,106,108,110 +}; + + + + +static void ag_ra(void) +{ + switch(ag_rpx[ag_ap]) { + case 1: ag_rp_1(V(0,int), V(1,const char *)); break; + case 2: ag_rp_2(V(0,int), V(1,const char *)); break; + case 3: ag_rp_3(V(0,const char *), V(1,int), V(2,char)); break; + case 4: V(0,char) = ag_rp_4(); break; + case 5: V(0,char) = ag_rp_5(); break; + case 6: V(0,batch *) = ag_rp_8(V(0,const char *), V(3,const char *)); break; + case 7: V(0,batch *) = ag_rp_9(V(0,batch *), V(2,const char *)); break; + case 8: V(0,const char *) = ag_rp_10(V(0,const char *)); break; + case 9: ag_rp_11(V(0,const char *)); break; + case 10: ag_rp_12(V(0,char), V(2,const char *), V(4,const char *)); break; + case 11: ag_rp_13(V(0,char), V(2,const char *), V(4,const char *), V(6,int)); break; + case 12: ag_rp_14(V(0,char), V(2,const char *), V(4,int)); break; + case 13: V(0,const char *) = ag_rp_15(V(1,const char *)); break; + case 14: ag_rp_16(V(0,const char *), V(2,int)); break; + case 15: ag_rp_17(V(1,int), V(3,int)); break; + case 16: ag_rp_18(V(1,int), V(3,int)); break; + case 17: ag_rp_19(V(0,const char *)); break; + case 18: ag_rp_20(V(0,const char *), V(2,const char *)); break; + case 19: ag_rp_21(V(0,const char *)); break; + case 20: ag_rp_22(V(0,const char *), V(2,int), V(4,int), V(6,int), V(8,int)); break; + case 21: ag_rp_23(V(0,const char *), V(2,const char *)); break; + case 22: V(0,const char *) = ag_rp_24(V(0,const char *)); break; + case 23: V(0,const char *) = ag_rp_25(V(0,const char *)); break; + case 24: V(0,const char *) = ag_rp_26(V(0,const char *)); break; + case 25: ag_rp_27(V(1,const char *), V(3,const char *), V(4,const char *), V(5,int)); break; + case 26: ag_rp_28(V(1,const char *)); break; + case 27: ag_rp_29(V(0,const char *)); break; + case 28: V(0,int) = ag_rp_30(V(0,int)); break; + case 29: V(0,const char *) = ag_rp_31(V(0,const char *)); break; + case 30: V(0,const char *) = ag_rp_32(V(0,const char *)); break; + case 31: V(0,const char *) = ag_rp_33(V(0,int)); break; + case 32: V(0,const char *) = ag_rp_34(V(0,const char *), V(1,int)); break; + case 33: V(0,const char *) = ag_rp_35(V(1,const char *)); break; + case 34: V(0,const char *) = ag_rp_36(V(0,char)); break; + case 35: V(0,const char *) = ag_rp_37(V(0,const char *), V(1,char)); break; + case 36: V(0,char) = ag_rp_38(V(0,int)); break; + case 37: V(0,char) = ag_rp_39(); break; + case 38: V(0,char) = ag_rp_40(); break; + case 39: V(0,int) = ag_rp_41(V(0,int)); break; + case 40: V(0,int) = ag_rp_42(V(0,int), V(1,int)); break; + case 41: V(0,int) = ag_rp_43(V(1,int)); break; + case 42: V(0,int) = ag_rp_44(V(0,int), V(1,int)); break; + case 43: V(0,int) = ag_rp_45(V(0,int)); break; + case 44: V(0,int) = ag_rp_46(V(0,int), V(1,int)); break; + case 45: V(0,int) = ag_rp_47(V(0,int)); break; + case 46: V(0,int) = ag_rp_48(V(0,int)); break; + case 47: V(0,int) = ag_rp_49(V(0,int)); break; + case 48: V(0,int) = ag_rp_50(V(0,int), V(1,int)); break; + case 49: V(0,int) = ag_rp_51(V(0,int), V(2,int)); break; + case 50: V(0,int) = ag_rp_52(V(0,int), V(2,int)); break; + case 51: V(0,int) = ag_rp_53(V(0,int), V(2,int)); break; + case 52: V(0,int) = ag_rp_54(V(1,int)); break; + case 53: V(0,int) = ag_rp_55(V(1,int)); break; + } +} + +#define TOKEN_NAMES parse_token_names +const char *parse_token_names[158] = { + "file format", + "identifier", + "white space", + "simple eol", + "quoted string", + "file format", + "", + "devices", + "", + "cache", + "", + "devinfo", + "", + "config", + "eol", + "", + "device list", + "", + "eof", + "", + "character device", + "", + "", + "block device", + "", + "number", + "name", + "cachedevice", + "", + "devicetype", + "", + "", + "", + "", + "device block", + "", + "device header spec", + "", + "device decl", + "", + "", + "", + "", + "ignoramus", + "", + "", + "batch list", + "batch item", + "", + "", + "", + "groupname", + "", + "procname", + "", + "class", + "device tail", + "", + "expr", + "device range", + "", + "", + "", + "hex number", + "auto hex", + "devname", + "letter", + "", + "", + "config decl", + "", + "class decl", + "omit decl", + "", + "mode", + "", + "single omit", + "", + "octal number", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "qstring", + "qstring char", + "qchar", + "", + "digit", + "", + "", + "", + "hex digit", + "", + "octal digit", + "term", + "", + "factor", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "letter", + "", + "", + "", + "simple eol", + "identifier", + "quoted string", + "digit", + "", + "", + "", + "octal digit", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + +}; + +static char ag_msg[82]; +static char ag_mst[] = "Missing %s"; +static char ag_uet[] = "Unexpected %s"; +static char ag_ac[4] = "' '"; + +static void ag_diagnose(void) { + int ag_snd = (PCB).sn, ag_k; + const char *ag_p; + const char *ag_fmt = ag_uet; + + ag_k = ag_sbt[ag_snd]; + if (*TOKEN_NAMES[ag_tstt[ag_k]] && ag_astt[ag_k + 1] == ag_action_8) { + ag_p = TOKEN_NAMES[ag_tstt[ag_k]]; + ag_fmt = ag_mst; + } + else if ((PCB).token_number && *TOKEN_NAMES[(PCB).token_number]) { + ag_p = TOKEN_NAMES[(PCB).token_number]; + } + else if (isprint((*(PCB).lab)) && (*(PCB).lab) != '\\') { + ag_ac[1] = (*(PCB).lab); + ag_p = ag_ac; + } + else ag_p = "input"; + sprintf(ag_msg, ag_fmt, ag_p); + (PCB).error_message = ag_msg; + + +} +static int ag_action_1_r_proc(void); +static int ag_action_2_r_proc(void); +static int ag_action_3_r_proc(void); +static int ag_action_4_r_proc(void); +static int ag_action_1_s_proc(void); +static int ag_action_3_s_proc(void); +static int ag_action_1_proc(void); +static int ag_action_2_proc(void); +static int ag_action_3_proc(void); +static int ag_action_4_proc(void); +static int ag_action_5_proc(void); +static int ag_action_6_proc(void); +static int ag_action_7_proc(void); +static int ag_action_8_proc(void); +static int ag_action_9_proc(void); +static int ag_action_10_proc(void); +static int ag_action_11_proc(void); +static int ag_action_8_proc(void); + + +static int (*ag_r_procs_scan[])(void) = { + ag_action_1_r_proc, + ag_action_2_r_proc, + ag_action_3_r_proc, + ag_action_4_r_proc +}; + +static int (*ag_s_procs_scan[])(void) = { + ag_action_1_s_proc, + ag_action_2_r_proc, + ag_action_3_s_proc, + ag_action_4_r_proc +}; + +static int (*ag_gt_procs_scan[])(void) = { + ag_action_1_proc, + ag_action_2_proc, + ag_action_3_proc, + ag_action_4_proc, + ag_action_5_proc, + ag_action_6_proc, + ag_action_7_proc, + ag_action_8_proc, + ag_action_9_proc, + ag_action_10_proc, + ag_action_11_proc, + ag_action_8_proc +}; + + +static int ag_action_10_proc(void) { + (PCB).btsx = 0, (PCB).drt = -1; + ag_track(); + return 0; +} + +static int ag_action_11_proc(void) { + (PCB).btsx = 0, (PCB).drt = -1; + (*(int *) &(PCB).vs[(PCB).ssx]) = *(PCB).lab; + (PCB).ssx--; + ag_ra(); + (PCB).ssx++; + ag_track(); + return 0; +} + +static int ag_action_3_r_proc(void) { + int ag_sd = ag_fl[ag_ap] - 1; + if (ag_sd) (PCB).sn = (PCB).ss[(PCB).ssx -= ag_sd]; + (PCB).btsx = 0, (PCB).drt = -1; + (PCB).reduction_token = (parse_token_type) ag_ptt[ag_ap]; + ag_ra(); + return 1; +} + +static int ag_action_3_s_proc(void) { + int ag_sd = ag_fl[ag_ap] - 1; + if (ag_sd) (PCB).sn = (PCB).ss[(PCB).ssx -= ag_sd]; + (PCB).btsx = 0, (PCB).drt = -1; + (PCB).reduction_token = (parse_token_type) ag_ptt[ag_ap]; + ag_ra(); + return 1; +} + +static int ag_action_4_r_proc(void) { + int ag_sd = ag_fl[ag_ap] - 1; + if (ag_sd) (PCB).sn = (PCB).ss[(PCB).ssx -= ag_sd]; + (PCB).reduction_token = (parse_token_type) ag_ptt[ag_ap]; + return 1; +} + +static int ag_action_2_proc(void) { + (PCB).btsx = 0, (PCB).drt = -1; + if ((PCB).ssx >= 38) { + (PCB).exit_flag = AG_STACK_ERROR_CODE; + PARSER_STACK_OVERFLOW; + } + (*(int *) &(PCB).vs[(PCB).ssx]) = *(PCB).lab; + (PCB).ss[(PCB).ssx] = (PCB).sn; + (PCB).ssx++; + (PCB).sn = ag_ap; + ag_track(); + return 0; +} + +static int ag_action_9_proc(void) { + if((PCB).drt == -1) { + (PCB).drt=(PCB).token_number; + (PCB).dssx=(PCB).ssx; + (PCB).dsn=(PCB).sn; + } + ag_prot(); + (PCB).ss[(PCB).ssx] = (PCB).sn; + (PCB).ssx++; + (PCB).sn = ag_ap; + (PCB).rx = 0; + return (PCB).exit_flag == AG_RUNNING_CODE; +} + +static int ag_action_2_r_proc(void) { + (PCB).ssx++; + (PCB).sn = ag_ap; + return 0; +} + +static int ag_action_7_proc(void) { + --(PCB).ssx; + (PCB).exit_flag = AG_SUCCESS_CODE; + (PCB).rx = 0; + return 0; +} + +static int ag_action_1_proc(void) { + (PCB).exit_flag = AG_SUCCESS_CODE; + ag_track(); + return 0; +} + +static int ag_action_1_r_proc(void) { + (PCB).exit_flag = AG_SUCCESS_CODE; + return 0; +} + +static int ag_action_1_s_proc(void) { + (PCB).exit_flag = AG_SUCCESS_CODE; + return 0; +} + +static int ag_action_4_proc(void) { + int ag_sd = ag_fl[ag_ap] - 1; + (PCB).reduction_token = (parse_token_type) ag_ptt[ag_ap]; + (PCB).btsx = 0, (PCB).drt = -1; + (*(int *) &(PCB).vs[(PCB).ssx]) = *(PCB).lab; + if (ag_sd) (PCB).sn = (PCB).ss[(PCB).ssx -= ag_sd]; + else (PCB).ss[(PCB).ssx] = (PCB).sn; + ag_track(); + while ((PCB).exit_flag == AG_RUNNING_CODE) { + unsigned ag_t1 = ag_sbe[(PCB).sn] + 1; + unsigned ag_t2 = ag_sbt[(PCB).sn+1] - 1; + do { + unsigned ag_tx = (ag_t1 + ag_t2)/2; + if (ag_tstt[ag_tx] < (const unsigned char)(PCB).reduction_token) ag_t1 = ag_tx + 1; + else ag_t2 = ag_tx; + } while (ag_t1 < ag_t2); + ag_ap = ag_pstt[ag_t1]; + if ((ag_s_procs_scan[ag_astt[ag_t1]])() == 0) break; + } + return 0; +} + +static int ag_action_3_proc(void) { + int ag_sd = ag_fl[ag_ap] - 1; + (PCB).btsx = 0, (PCB).drt = -1; + (*(int *) &(PCB).vs[(PCB).ssx]) = *(PCB).lab; + if (ag_sd) (PCB).sn = (PCB).ss[(PCB).ssx -= ag_sd]; + else (PCB).ss[(PCB).ssx] = (PCB).sn; + ag_track(); + (PCB).reduction_token = (parse_token_type) ag_ptt[ag_ap]; + ag_ra(); + while ((PCB).exit_flag == AG_RUNNING_CODE) { + unsigned ag_t1 = ag_sbe[(PCB).sn] + 1; + unsigned ag_t2 = ag_sbt[(PCB).sn+1] - 1; + do { + unsigned ag_tx = (ag_t1 + ag_t2)/2; + if (ag_tstt[ag_tx] < (const unsigned char)(PCB).reduction_token) ag_t1 = ag_tx + 1; + else ag_t2 = ag_tx; + } while (ag_t1 < ag_t2); + ag_ap = ag_pstt[ag_t1]; + if ((ag_s_procs_scan[ag_astt[ag_t1]])() == 0) break; + } + return 0; +} + +static int ag_action_8_proc(void) { + ag_undo(); + (PCB).rx = 0; + (PCB).exit_flag = AG_SYNTAX_ERROR_CODE; + ag_diagnose(); + SYNTAX_ERROR; + {(PCB).rx = 1; ag_track();} + return (PCB).exit_flag == AG_RUNNING_CODE; +} + +static int ag_action_5_proc(void) { + int ag_sd = ag_fl[ag_ap]; + if((PCB).drt == -1) { + (PCB).drt=(PCB).token_number; + (PCB).dssx=(PCB).ssx; + (PCB).dsn=(PCB).sn; + } + if (ag_sd) (PCB).sn = (PCB).ss[(PCB).ssx -= ag_sd]; + else { + ag_prot(); + (PCB).ss[(PCB).ssx] = (PCB).sn; + } + (PCB).rx = 0; + (PCB).reduction_token = (parse_token_type) ag_ptt[ag_ap]; + ag_ra(); + while ((PCB).exit_flag == AG_RUNNING_CODE) { + unsigned ag_t1 = ag_sbe[(PCB).sn] + 1; + unsigned ag_t2 = ag_sbt[(PCB).sn+1] - 1; + do { + unsigned ag_tx = (ag_t1 + ag_t2)/2; + if (ag_tstt[ag_tx] < (const unsigned char)(PCB).reduction_token) ag_t1 = ag_tx + 1; + else ag_t2 = ag_tx; + } while (ag_t1 < ag_t2); + ag_ap = ag_pstt[ag_t1]; + if ((ag_r_procs_scan[ag_astt[ag_t1]])() == 0) break; + } + return (PCB).exit_flag == AG_RUNNING_CODE; +} + +static int ag_action_6_proc(void) { + int ag_sd = ag_fl[ag_ap]; + (PCB).reduction_token = (parse_token_type) ag_ptt[ag_ap]; + if((PCB).drt == -1) { + (PCB).drt=(PCB).token_number; + (PCB).dssx=(PCB).ssx; + (PCB).dsn=(PCB).sn; + } + if (ag_sd) { + (PCB).sn = (PCB).ss[(PCB).ssx -= ag_sd]; + } + else { + ag_prot(); + (PCB).vs[(PCB).ssx] = ag_null_value; + (PCB).ss[(PCB).ssx] = (PCB).sn; + } + (PCB).rx = 0; + while ((PCB).exit_flag == AG_RUNNING_CODE) { + unsigned ag_t1 = ag_sbe[(PCB).sn] + 1; + unsigned ag_t2 = ag_sbt[(PCB).sn+1] - 1; + do { + unsigned ag_tx = (ag_t1 + ag_t2)/2; + if (ag_tstt[ag_tx] < (const unsigned char)(PCB).reduction_token) ag_t1 = ag_tx + 1; + else ag_t2 = ag_tx; + } while (ag_t1 < ag_t2); + ag_ap = ag_pstt[ag_t1]; + if ((ag_r_procs_scan[ag_astt[ag_t1]])() == 0) break; + } + return (PCB).exit_flag == AG_RUNNING_CODE; +} + + +void init_parse(void) { + unsigned ag_t1 = 0; + (PCB).rx = (PCB).fx = 0; + (PCB).ss[0] = (PCB).sn = (PCB).ssx = 0; + (PCB).exit_flag = AG_RUNNING_CODE; + (PCB).key_sp = NULL; + (PCB).key_state = 0; + (PCB).line = FIRST_LINE; + (PCB).column = FIRST_COLUMN; + (PCB).btsx = 0, (PCB).drt = -1; + while (ag_tstt[ag_t1] == 0) { + ag_ap = ag_pstt[ag_t1]; + (ag_gt_procs_scan[ag_astt[ag_t1]])(); + ag_t1 = ag_sbt[(PCB).sn]; + } +} + +void parse(void) { + (PCB).lab[(PCB).fx++] = (PCB).input_code; + while ((PCB).exit_flag == AG_RUNNING_CODE) { + while (1) { + unsigned char *ag_p; + int ag_ch; + if ((PCB).rx >= (PCB).fx) return; + ag_ch = CONVERT_CASE((PCB).lab[(PCB).rx++]); + if ((PCB).key_sp) { + if (ag_ch != *(PCB).key_sp++) { + (PCB).rx = (PCB).save_index; + (PCB).key_sp = NULL; + (PCB).key_state = 0; + break; + } else if (*(PCB).key_sp) continue; + if (ag_key_act[(PCB).key_state] == ag_cf_end_key) { + int ag_k1; + int ag_k2; + if ((PCB).rx >= (PCB).fx) { + (PCB).rx--; + (PCB).key_sp--; + return; + } + (PCB).key_sp = NULL; + ag_k1 = ag_key_parm[(PCB).key_state]; + ag_k2 = ag_key_pt[ag_k1]; + if (ag_key_itt[ag_k2 + CONVERT_CASE((PCB).lab[(PCB).rx])]) + (PCB).rx = (PCB).save_index; + else { + (PCB).token_number = (parse_token_type) ag_key_pt[ag_k1+1]; + (PCB).key_state = 0; + } + break; + } + else { + (PCB).token_number = (parse_token_type) ag_key_parm[(PCB).key_state]; + (PCB).key_state = 0; + (PCB).key_sp = NULL; + } + break; + } + if ((PCB).key_state == 0) { + (PCB).token_number = (parse_token_type) AG_TCV(ag_ch); + if (((PCB).key_state = ag_key_index[(PCB).sn]) == 0) break; + (PCB).save_index = 1; + } + ag_p = &ag_key_ch[(PCB).key_state]; + while (*ag_p < ag_ch) ag_p++; + if (*ag_p == ag_ch) { + (PCB).key_state = (int)(ag_p - ag_key_ch); + switch (ag_key_act[(PCB).key_state]) { + case ag_cf_set_key: { + int ag_k1; + int ag_k2; + if ((PCB).rx >= (PCB).fx) { + (PCB).rx--; + return; + } + ag_k1 = ag_key_parm[(PCB).key_state]; + ag_k2 = ag_key_pt[ag_k1]; + (PCB).key_state = ag_key_jmp[(PCB).key_state]; + if (ag_key_itt[ag_k2 + CONVERT_CASE((PCB).lab[(PCB).rx])]) break; + (PCB).save_index = (PCB).rx; + (PCB).token_number = (parse_token_type) ag_key_pt[ag_k1+1]; + break; + } + case ag_set_key: + (PCB).save_index = (PCB).rx; + (PCB).token_number = (parse_token_type) ag_key_parm[(PCB).key_state]; + case ag_jmp_key: + (PCB).key_state = ag_key_jmp[(PCB).key_state]; + continue; + case ag_cf_end_key: + case ag_end_key: + (PCB).key_sp = ag_key_ends + ag_key_jmp[(PCB).key_state]; + continue; + case ag_accept_key: + (PCB).token_number = (parse_token_type) ag_key_parm[(PCB).key_state]; + (PCB).key_state = 0; + break; + case ag_cf_accept_key: { + int ag_k1; + int ag_k2; + if ((PCB).rx >= (PCB).fx) { + (PCB).rx--; + return; + } + ag_k1 = ag_key_parm[(PCB).key_state]; + ag_k2 = ag_key_pt[ag_k1]; + if (ag_key_itt[ag_k2 + CONVERT_CASE((PCB).lab[(PCB).rx])]) + (PCB).rx = (PCB).save_index; + else { + (PCB).rx--; + (PCB).token_number = (parse_token_type) ag_key_pt[ag_k1+1]; + (PCB).key_state = 0; + } + break; + } + } + break; + } else { + (PCB).rx = (PCB).save_index; + (PCB).key_state = 0; + break; + } + } + + { + unsigned ag_t1 = ag_sbt[(PCB).sn]; + unsigned ag_t2 = ag_sbe[(PCB).sn] - 1; + do { + unsigned ag_tx = (ag_t1 + ag_t2)/2; + if (ag_tstt[ag_tx] > (const unsigned char)(PCB).token_number) + ag_t1 = ag_tx + 1; + else ag_t2 = ag_tx; + } while (ag_t1 < ag_t2); + if (ag_tstt[ag_t1] != (PCB).token_number) ag_t1 = ag_sbe[(PCB).sn]; + ag_ap = ag_pstt[ag_t1]; + (ag_gt_procs_scan[ag_astt[ag_t1]])(); + } + } + +} + + diff --git a/makedev-1.4.1/makedev.cfg b/makedev-1.4.1/makedev.cfg new file mode 100644 index 00000000..3f4696e9 --- /dev/null +++ b/makedev-1.4.1/makedev.cfg @@ -0,0 +1,34 @@ +/* + * User and group ownerships, and permissions, for device classes. + */ + +class default: root system 0640 +class public: root system 0666 +class system: root system 0660 +class kmem: root kmem 0640 +class tty: root tty 0620 +class pty: root root 0666 +class cons: root tty 0622 // 622 for console? +class dialout: root uucp 0660 +class mouse: root system 0666 +class printer: root daemon 0660 +class floppy: root floppy 0660 +class disk: root disk 0640 +class scsi: root system 0600 +class cdrom: root disk 0660 +class tape: root disk 0660 +class audio: root system 0666 +class ibcs2: root system 0666 +class scanner: root system 0666 + +/* + * Things marked as omit will not be created. Ever. Be sure to check + * here if you try to make something and nothing happens. + */ + +omit { +// hdc hdd // hdc and hdd are now the 2nd controller. + // They're now made by "hd1", and *not* by "hd". + xdc xdd + sdc sdd sde sdf sdg sdh +} diff --git a/makedev-1.4.1/makedev.cfg.5 b/makedev-1.4.1/makedev.cfg.5 new file mode 100644 index 00000000..46a2642e --- /dev/null +++ b/makedev-1.4.1/makedev.cfg.5 @@ -0,0 +1,50 @@ +.\" -*- nroff -*- +.TH MAKEDEV.cfg 5 "January 1995" "Version 1.4" +.SH NAME +MAKEDEV.cfg \- configuration for MAKEDEV(8) +.SH DESCRIPTION +.B MAKEDEV.cfg +is a text file that tells +.BR MAKEDEV (8) +what to do (and, equally importantly, what not to do.) +Unlike +.BR DEVINFO (5), +which is meant to be centrally maintained, it contains all local +configuration for a particular site and all customization. +There are basically two kinds of declaration in this file: a "class" +declaration and an "omit" declaration. +.LP +A class declaration has the form +.RS + +.RI class " name " : " owner group-owner permissions" + +.RE +This says that any devices placed in the specified class by +.B DEVINFO +should be created with this ownership and these permissions. A sample +entry might be +.RS +.nf + +class public: root system 0666 + +.fi +.RE +This says that devices marked "public" should be owned by root.system +and have mode 666. +.LP +An omit declaration has the form +.RS + +.RI "omit { " device... " }" + +.RE +This causes the specified devices to never be created, +.B "EVEN IF EXPLICITLY SPECIFIED." +Use caution when setting this up. The intent is to be able to run +.B "\"MAKEDEV update\"" +and not have it create all sorts of useless devices you'd never use. +.SH "SEE ALSO" +.BR MAKEDEV (8), +.BR DEVINFO (5) diff --git a/makedev-1.4.1/makedev.h b/makedev-1.4.1/makedev.h new file mode 100644 index 00000000..be620b42 --- /dev/null +++ b/makedev-1.4.1/makedev.h @@ -0,0 +1,78 @@ +#ifndef MAKEDEV_H +#define MAKEDEV_H + +void init_parse(void); +void parse(void); +typedef union { + int alignment; + char ag_vt_2[sizeof(int)]; + char ag_vt_3[sizeof(char)]; + char ag_vt_4[sizeof(batch *)]; + char ag_vt_5[sizeof(const char *)]; +} parse_vs_type; + +typedef enum { + parse_white_space_token = 2, parse_file_format_token = 5, + parse_devices_token = 7, parse_cache_token = 9, parse_devinfo_token = 11, + parse_config_token = 13, parse_eol_token, parse_device_list_token = 16, + parse_eof_token = 18, parse_character_device_token = 20, + parse_block_device_token = 23, parse_number_token = 25, parse_name_token, + parse_cachedevice_token, parse_devicetype_token = 29, + parse_device_block_token = 34, parse_device_header_spec_token = 36, + parse_device_decl_token = 38, parse_ignoramus_token = 43, + parse_batch_list_token = 46, parse_batch_item_token, + parse_groupname_token = 51, parse_procname_token = 53, + parse_class_token = 55, parse_device_tail_token, parse_expr_token = 58, + parse_device_range_token, parse_hex_number_token = 63, + parse_auto_hex_token, parse_devname_token, parse_config_decl_token = 69, + parse_class_decl_token = 71, parse_omit_decl_token, parse_mode_token = 74, + parse_single_omit_token = 76, parse_octal_number_token = 78, + parse_qstring_token = 96, parse_qstring_char_token, parse_qchar_token, + parse_hex_digit_token = 104, parse_term_token = 107, + parse_factor_token = 109, parse_letter_token = 136, + parse_simple_eol_token = 140, parse_identifier_token, + parse_quoted_string_token, parse_digit_token, + parse_octal_digit_token = 147 +} parse_token_type; + +typedef struct { + parse_token_type token_number, reduction_token, error_frame_token; + int input_code; + int input_value; + int line, column; + int ssx, sn, error_frame_ssx; + int drt, dssx, dsn; + int ss[38]; + parse_vs_type vs[38]; + int bts[38], btsx; + unsigned char * pointer; + unsigned char * la_ptr; + int lab[19], rx, fx; + unsigned char *key_sp; + int save_index, key_state; + char *error_message; + char read_flag; + char exit_flag; +} parse_pcb_type; + + +#ifndef PRULE_CONTEXT +#define PRULE_CONTEXT(pcb) (&((pcb).cs[(pcb).ssx])) +#define PERROR_CONTEXT(pcb) ((pcb).cs[(pcb).error_frame_ssx]) +#define PCONTEXT(pcb) ((pcb).cs[(pcb).ssx]) +#endif + + +#ifndef AG_RUNNING_CODE_CODE +/* PCB.exit_flag values */ +#define AG_RUNNING_CODE 0 +#define AG_SUCCESS_CODE 1 +#define AG_SYNTAX_ERROR_CODE 2 +#define AG_REDUCTION_ERROR_CODE 3 +#define AG_STACK_ERROR_CODE 4 +#define AG_SEMANTIC_ERROR_CODE 5 +#endif + +extern parse_pcb_type parse_pcb; +#endif + diff --git a/makedev-1.4.1/makedev.syn b/makedev-1.4.1/makedev.syn new file mode 100644 index 00000000..26ccef31 --- /dev/null +++ b/makedev-1.4.1/makedev.syn @@ -0,0 +1,994 @@ +{ +/* + * makedev.c: Generate /dev entries + * + * Based on the MAKEDEV shell script, version 2.0, distributed with + * util-linux 1.10 and written by Nick Holloway. + * + * A number of bugs were fixed, and some additional features added. + * Written 10-Dec-94 by David A. Holland, dholland@husc.harvard.edu + * + * Copyright 1994, 1995. All rights reserved. + * See the file LEGAL.NOTICE for conditions of redistribution. + * + * Bugs: + * None known right now. + * + * History: + * + * Version 1.4: 15-Jan-95 Wrote man pages. Now reads DEVINFO.local. + * Version 1.3: 31-Dec-94 Bug fixes. Added batches. Added omits. + * Version 1.2: 11-Dec-94 Add configuration file parsing. + * Version 1.1: 11-Dec-94 Distinguish block and character devices in the + * table of major device numbers. Changed the name and format of the + * update cache file to include the type. It appears that the old script + * was broken in this regard. + * Version 1.0: 10-Dec-94 Initial version. + */ + +static const char *version = "MAKEDEV-C version 1.4"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define YES 1 +#define NO 0 + +static int isverbose=NO; /* flag: print out what we do? */ +static int deletion=NO; /* flag: delete instead of create */ +static int donothing=NO; /* flag: don't actually do anything */ + +/* + * Proto for main operative function. + */ +typedef enum { M_CREATE, M_OMIT } makeopts; +static void make(const char *batch_or_grp_or_devname, makeopts); + +/* + * Roll over and die. + */ +static void crash(const char *msg) { + fprintf(stderr, "MAKEDEV: %s\n", msg); + exit(1); +} + +/* + * Print a warning. + */ +static void warn(const char *format, ...) { + va_list ap; + va_start(ap, format); + fprintf(stderr, "MAKEDEV: "); + vfprintf(stderr, format, ap); + fprintf(stderr, "\n"); + va_end(ap); +} + +/* + * Translate string name to uid. + */ +static uid_t name2uid(const char *name) { + struct passwd *p = getpwnam(name); + if (!p) warn("undefined user: %s, using uid 0", name); + return p ? p->pw_uid : 0; /* make things owned by root by default */ +} + +/* + * Translate string name to gid. + */ +static gid_t name2gid(const char *name) { + struct group *g = getgrnam(name); + if (!g) warn("undefined group: %s, using gid 0", name); + return g ? g->gr_gid : 0; /* group 0 is a good default too */ +} + +/* + * Proto for parser. + */ +static void doparse(FILE *f, int filetype, const char *filename); + +/************************* device classes *************************/ + +/* + * A device class is a string attached to the device which tells us + * what set of permissions and ownership should be used. This is the + * table of classes. + */ + +typedef struct { + const char *classname; + const char *owner; + const char *group; + int mode; +} devclass; + +#define MAXCLASSES 32 +static devclass classes[MAXCLASSES]; +static int nclasses=0; + +static void addclass(const char *name, const char *o, const char *g, int m) { + if (nclasses>=MAXCLASSES) crash("out of space for device classes"); + classes[nclasses].classname = name; + classes[nclasses].owner = o; + classes[nclasses].group = g; + classes[nclasses].mode = m; + nclasses++; + name2uid(o); /* check for undefined users/groups */ + name2gid(g); +} + +static void loadclasses(void) { + FILE *f = fopen("MAKEDEV.cfg", "r"); + if (!f) f = fopen("../MAKEDEV.cfg", "r"); + if (!f) f = fopen("/etc/makedev.cfg", "r"); + if (!f) crash("can't find MAKEDEV.cfg"); + doparse(f, 4, "MAKEDEV.cfg"); + fclose(f); +} + +/* + * Return the index into the above table for a particular class name. + */ +static int which_class(const char *name) { + int i; + for (i=0; i=0; i--, z<<=1) if (!(mode&z)) rv[i]='-'; + return rv; +} + +/* + * Create (or delete, or update) a block or character device. + */ +static void class_makedev(const char *name, const char *class, + int major, int minor, char type) { + int x = which_class(class), mode = classes[x].mode; + const char *owner = classes[x].owner, *group = classes[x].group; + if (isverbose) { + if (deletion) printf("rm -f %s\n", name); + else printf("%c%s 1 %-8s %-8s %3d, %3d for %s\n", type, + modestring(mode), owner, group, major, minor, name); + } + if (donothing) return; + if (unlink(name) && deletion) warn("Couldn't remove %s\n", name); + if (!deletion) { + dev_t q = (major<<8) | minor; + if (mknod(name, type=='c' ? S_IFCHR : S_IFBLK, q) || + chown(name, name2uid(owner), name2gid(group)) || + chmod(name, mode)) { + warn("couldn't create %s: %s", name, strerror(errno)); + } + } +} + +/************************* major number list *************************/ + +/* + * In Linux device major numbers can be allocated dynamically, so we go + * look in /proc/devices to see what they are. This keeps track of things. + */ + +typedef struct { + const char *procname; + int flag; +} majorentry; + +#define MAXMAJORS 256 +static majorentry cmajors[MAXMAJORS]; /* initialized to 0 */ +static majorentry bmajors[MAXMAJORS]; /* initialized to 0 */ +static int no_proc=0; /* true if we didn't find /proc/devices */ + +/* + * Store the name associated with a particular major device number. + */ +static void set_major(const char *procname, int ischar, int num) { + if (num<0 || num>255) { + warn("warning: got bogus major number %d for %s", num, procname); + return; + } + if (ischar) cmajors[num].procname=procname; + else bmajors[num].procname=procname; +} + +/* + * Look up a major device number by name; return the default value + * if provided. A default value of -1 implies the device is only + * dynamic, and so if there's no entry we shouldn't even note its + * existence. + */ +static int get_major(const char *procname, int ischar, int defaalt) { + int i; + if (!procname) return defaalt; + if (ischar) { + for (i=0; i=MAXALIASES) crash("out of space for aliases"); + aliases[naliases].procname = procname; + aliases[naliases].groupname = groupname; + naliases++; +} + +static void ignore_procname(const char *procname) { + addalias(procname, NULL); +} + +static const char *procnameof(const char *groupname) { + int i; + for (i=0; i=MAXBATCHES) crash("Out of space for batches"); + b = &batches[nbatches++]; + b->name = name; + b->busy = NO; + return b; +} + +/* + * Add something to a batch. + */ +static batch *add2batch(batch *b, const char *target) { + if (b->ntargets>=MAXTARGETS) { + warn("Too many targets for batch %s (max %d)", b->name, MAXTARGETS); + return b; + } + b->targets[b->ntargets++] = target; + return b; +} + +/* + * Run a batch. + */ +static void run_batch(const batch *b, makeopts m) { + int i; + for (i=0; intargets; i++) make(b->targets[i], m); +} + +/* + * Try to run a batch; returns YES if it found one. + */ +static int try_run_batch(const char *name, makeopts m) { + int i; + for (i=0; i owner and permissions) */ + int major, minor; /* device number */ + char type; /* 'c', 'b', or 'l' for symbolic link */ + int omit; /* don't make me if this is nonzero */ +} device; + +/* + * Create a device (link or actual "special file") - special files are + * passed on to class_makedev(). + */ +void makedev(device *d, makeopts m) { + if (m==M_OMIT) { + d->omit=1; + } + if (d->omit==1) return; + if (d->type=='l') { + if (isverbose) { + if (deletion) printf("rm -f %s\n", d->name); + else printf("lrwxrwxrwx %s -> %s\n", d->name, d->class); + } + if (donothing) return; + if (unlink(d->name) && deletion) warn("Couldn't remove %s\n", d->name); + if (!deletion) { + if (symlink(d->class, d->name)) /* class holds thing pointed to */ + warn("couldn't link %s -> %s: %s", d->name, d->class, strerror(errno)); + } + } + else class_makedev(d->name, d->class, d->major, d->minor, d->type); +} + +/* + * Array of devices. We allocate it once from main(); it doesn't grow. + * Should maybe make it growable sometime. This keeps track of all possible + * devices. We build this thing first, and then create devices from it as + * requested. + */ +static device *devices = NULL; +static int maxdevices, ndevices; + +/* + * Allocate space for the device array. + */ +static void allocate_devs(int nd) { + devices = malloc(nd * sizeof(device)); + if (!devices) crash("Out of memory"); + ndevices = 0; + maxdevices = nd; +} + +/* + * Check all the devices for having valid device classes. + */ +static void check_classes(void) { + int i; + const char *q=NULL; + for (i=0; i=maxdevices) crash("out of space for devices"); + devices[ndevices].name = name; + devices[ndevices].grp = grp; + devices[ndevices].class = class; + devices[ndevices].major = major; + devices[ndevices].minor = minor; + devices[ndevices].type = type; + devices[ndevices].omit = 0; + ndevices++; +} + +/* + * Create an entry for a symbolic link "device", such as /dev/fd + * (which is a symbolic link to /proc/self/fd) + */ +static void initlink(const char *name, const char *grp, const char *target) { + init(name, grp, target, 0, 0, 'l'); +} + +/* + * Init lots of devices. This creates a number of devices, numbered between + * lo and hi. The idea is that "base" contains a %d or %x (or something like + * that) in it which pulls in the number. The device group can also do this, + * though this will in most cases not be useful. "baseminor" is the minor + * number of the first device created. + */ +static void initlots(const char *base, int lo, int hi, const char *grp, + const char *class, + int maj, int baseminor, int type) { + char buf[32], gbuf[32]; + int i; + if (maj<0) return; + for (i=lo; i=high) return; + b = addbatch(base); + for (i=low; i<=high; i++) { + char *q; + sprintf(buf, "%s%c", base, i); + q = strdup(buf); + init(q, q, "disk", maj, (i-low)*minmult, 'b'); + strcpy(buf2, buf); + strcat(buf2, "%d"); + initlots(buf2, 1, nparts, buf, "disk", maj, (i-low)*minmult+1, 'b'); + add2batch(b, q); + } +} + +static void initdevs(void) { + FILE *f = fopen("DEVINFO", "r"); + if (!f) f = fopen("../DEVINFO", "r"); + if (!f) f = fopen("/etc/devinfo", "r"); + if (!f) crash("Can't find DEVINFO"); + doparse(f,3, "DEVINFO"); + fclose(f); + f = fopen("DEVINFO.local", "r"); + if (!f) f = fopen("../DEVINFO.local", "r"); + if (!f) f = fopen("/etc/devinfo.local", "r"); + if (!f) f = fopen("/usr/local/etc/devinfo.local", "r"); + if (f) { + doparse(f,3, "DEVINFO.local"); + fclose(f); + } +} + +/************************** update *************************/ + +/* + * Call make() with our names for something that appeared in /proc/devices. + */ + +static void transmake(const char *procname, makeopts m) { + const char *gname = groupnameof(procname); + if (gname) make(gname, m); +} + +/* + * Update a device that appeared in MAKEDEV.cache. Whenever we update, + * we save what we did into MAKEDEV.cache; this lets us avoid doing + * them over the next time. We only do something if the device has + * disappeared or the major number has changed. + * + * Note that this caching made the shell version go much faster (it took + * around 15 seconds with the cache, vs. over a minute if the cache was + * blown away.) For us, it still does, but it hardly matters: it shaves + * one second off a two-second execution. + * + * Also note the old script used DEVICES instead of MAKEDEV.cache. We + * changed because the old file didn't record whether something was + * a block or character device; since the sets of numbers are independent, + * this was bound to break. + */ +static void update2(const char *name, int ischar, int major) { + int now = get_major(name, ischar, -1); + if (now<0) { + deletion = 1; /* must have been zero if we're doing an update */ + transmake(name, M_CREATE); + deletion = 0; + } + else if (now!=major) { /* oops, it moved; remake it */ + transmake(name, M_CREATE); + if (ischar) cmajors[now].flag=1; + else bmajors[now].flag=1; + } + else { + if (ischar) cmajors[now].flag=1; /* unchanged; inhibit remaking it */ + else bmajors[now].flag=1; /* unchanged; inhibit remaking it */ + } +} + +static void updatefromcache(const char *name, int major, int type) { + update2(name, type=='c', major); +} + + +/* + * Update. Read the information stored in MAKEDEV.cache from the last + * update; fix anything that changed; then create any new devices that + * weren't listed the last time. (We use the "flag" field in the + * majors array to check this.) At that point, write out a new + * cache file. + */ +#define CACHEFILE "MAKEDEV.cache" + +static void update(void) { + FILE *f; + int i; + if (no_proc) { warn("Couldn't read anything from /proc/devices"); return; } + if (deletion) { warn("update and -d are incompatible"); return; } + f = fopen(CACHEFILE, "r"); + if (f) { + doparse(f, 2, CACHEFILE); + fclose(f); + } + for (i=0; i4) crash("tried to parse a bad file type"); + if (filetype!=1) { /* /proc/devices won't stat intelligently */ + struct stat buf; + if (fstat(fileno(f), &buf)) crash("fstat failed?!?"); + len = buf.st_size; + } + else len=1023; + x = malloc(len+1); + if (!x) crash("Out of memory"); + + len = fread(x, 1, len, f); /* it shouldn't return a short count... */ + if (len<0) crash("fread failed?!?"); + x[len]=0; + + init_parse(); + PCB.input_code = filetype+'0'; + parse(); + PCB.column--; /* correct for the filetype token */ + while (!PCB.exit_flag) { + PCB.input_code = x[i++]; + parse(); + } + if (PCB.exit_flag == AG_SYNTAX_ERROR_CODE) { + warn("syntax error: %s, line %d, column %d in file %s", + PCB.error_message, PCB.line, PCB.column, filename); + crash("Sorry, can't continue."); + } + else if (PCB.exit_flag != AG_SUCCESS_CODE) { + crash("parser stack overflow!"); + } +} + +#define STRINGSIZE 8192 +static char string_space[STRINGSIZE]; +static int stringptr=0; + +static const char *string_start(int c) { + if (stringptr>=STRINGSIZE) crash("out of string space"); + return string_space[stringptr]=c, string_space+stringptr++; +} + +static void string_push(int c) { + if (stringptr>=STRINGSIZE) crash("out of string space"); + string_space[stringptr++] = c; +} + +static void string_finish(void) { + string_push(0); +} + +} +/************************* syntax begins here *************************/ + +/* + * We read four different file formats here: + * /proc/devices 1 + * MAKEDEV.cache 2 + * DEVINFO 3 + * MAKEDEV.config 4 + */ + + +[ + sticky {identifier} + disregard white space + lexeme {identifier, simple eol, quoted string} + distinguish keywords {'a-z' + 'A-Z'} + event driven + parser name = parse + line numbers +] + +(void) file format $ + -> '1', devices + -> '2', cache + -> '3', devinfo + -> '4', config + + +/************************* /proc/devices *************************/ + +(void) devices + -> eol?, device list..., eof + +(void) device list + -> "Character devices:", eol, character device... + -> "Block devices:", eol, block device... + +(void) character device + -> number:n, name:s, eol = set_major(s,YES,n); + +(void) block device + -> number:n, name:s, eol = set_major(s,NO,n); + +/************************* cache *************************/ + +(void) cache + -> eol?, cachedevice..., eof + +(void) cachedevice + -> name:n, number:maj, devicetype:t, eol = updatefromcache(n,maj,t); + +(char) devicetype + -> 'b' = 'b'; + -> 'c' = 'c'; + -> "block" = 'b'; + -> "char" = 'c'; + +/************************* devinfo *************************/ + +(void) devinfo + -> eol?, device block..., eof + +(void) device block + -> device header spec, '{', eol?, device decl?..., '}', eol? + -> device header spec, eol?, device decl + -> "ignore", '{', eol?, ignoramus..., '}', eol? + -> "batch", batch list, '}', eol? + +(batch *) batch list + -> name:n, '{', eol?, batch item:i, eol? = add2batch(addbatch(n), i); + -> batch list:b, [',', eol?], batch item:i, eol? = add2batch(b,i); + +(const char *) batch item + -> name:n = n; + +(void) ignoramus + -> name:n, eol?, [',', eol?] = ignore_procname(n); + +{ + static const char *cur_group=NULL, *cur_class=NULL; + static int cur_type; + static int cur_maj=0, cur_min=0, cur_bot=0, cur_top=0, ishex=0; + + static void dhsproc(const char *g, const char *p, int t, int m) { + cur_group = g; + cur_type = t; + cur_maj = get_major(p, (t=='c'), m); + cur_min = 0; + cur_bot = cur_top = ishex = 0; + if (p) addalias(p,g); + } + + static void newdev(const char *n) { + if (cur_maj<0) return; + init(n, cur_group, cur_class, cur_maj, cur_min, cur_type); + } + static void devrange(const char *n, const char *n1) { + char temp[32]; + if (cur_maj<0) return; + sprintf(temp, "%s%%d%s", n, n1 ? n1 : ""); + initlots(temp, cur_bot, cur_top, cur_group, cur_class, + cur_maj, cur_min, cur_type); + } + static void doinitlink(const char *src, const char *tg) { + if (cur_maj>=0) initlink(src, cur_group, tg); + } +} + +(void) device header spec + -> devicetype:t, '(', groupname:g, '=', procname:p, ')' = dhsproc(g,p,t,-1); + -> devicetype:t, '(', groupname:g, '=', procname:p, + ',', number:m, ')' = dhsproc(g,p,t,m); + -> devicetype:t, '(', groupname:g, ',', number:m, ')' = dhsproc(g,NULL,t,m); + +(const char *) class + -> '(', name:classname, ')' = classname; + +(void) device tail + -> class:c, ':', expr:min, eol = (cur_class=c, cur_min=min); + +(void) device range + -> '[', number:a, '-', number:b, ']' = cur_bot=a, cur_top=b, ishex=0; + -> '[', hex number:a, '-', auto hex:b, ']' = cur_bot=a, cur_top=b, ishex=1; + +(void) device decl + -> devname:n, device tail = newdev(n); + -> devname:n, device range, devname:n1, device tail = devrange(n,n1); + -> devname:n, device range, device tail = devrange(n,NULL); + -> devname:n, '[', letter:a, '-', letter:b,']', number:p,'/',number:m, eol = + initdisk(n, a, b, p, cur_maj, m); + -> devname:n, "->", name:tg, eol = doinitlink(n, tg); + +(const char *) devname -> name:n = n; +(const char *) groupname -> name:n = n; +(const char *) procname -> name:n = n; + + +/************************* config *************************/ + +(void) config + -> eol?, config decl..., eof + +(void) config decl + -> class decl + -> omit decl + +(void) class decl + -> "class", name:n, ':', name:o, name:g, mode:m, eol = addclass(n,o,g,m); + +(void) omit decl + -> "omit", name:n, eol = make(n, M_OMIT); + -> "omit", '{', eol?, single omit..., '}', eol? + +(void) single omit + -> name:n, eol?, [',', eol?] = make(n, M_OMIT); + +(int) mode + -> octal number:n = n; + +/************************* support *************************/ + +eof = -1 + 0 +digit = '0-9' +letter = 'a-z' + 'A-Z' + '-' + '_' +octal digit = '0-7' +qchar = 32..126 - '\\' - '"' + +(void) white space + -> ' ' + '\t' + '\r' + -> "/*", ~eof?..., "*/" + + +(void) eol + -> simple eol... + +(void) simple eol + -> [{"//" | '#'}, ~'\n'?...], '\n' + +(const char *) name + -> identifier:s = string_finish(), s; + -> quoted string:s = s; + +(const char *) identifier + -> letter:c = string_start(c); + -> identifier:s, letter+digit:c = string_push(c), s; + +(const char *) quoted string + -> '"', qstring:s, '"' = string_finish(), s; + +(const char *) qstring + -> qstring char:c = string_start(c); + -> qstring:s, qstring char:c = string_push(c), s; + +(char) qstring char + -> qchar:c = c; + -> '\\', '\\' = '\\'; + -> '\\', '"' = '"'; + +(int) number + -> digit:d = d-'0'; + -> number:n, digit:d = n*10 + d-'0'; + +(int) hex number + -> {"0x" | "0X"}, hex digit:d =d; + -> hex number:n, hex digit:d =16*n+d; + +(int) auto hex + -> hex digit:d =d; + -> auto hex:n, hex digit:d =16*n+d; + + +(int) hex digit + -> digit:d =d-'0'; + -> 'a-f' + 'A-F' :d =10 + (d&7); + +(int) octal number + -> octal digit:d = d-'0'; + -> octal number:n, octal digit:d = n*8+d-'0'; + +(int) expr + -> term + -> expr:x, '+', term:t =x+t; + -> expr:x, '-', term:t =x-t; + +(int) term + -> factor + -> term:t, '*', factor:f =t*f; +// -> term:t, '/', factor:f =t/f; + +(int) factor + -> number + -> hex number + -> '-', factor:f =-f; + -> '(', expr:x, ')' =x; diff --git a/misc-utils/Makefile b/misc-utils/Makefile new file mode 100644 index 00000000..e8e517fe --- /dev/null +++ b/misc-utils/Makefile @@ -0,0 +1,71 @@ +# Makefile -- Makefile for util-linux Linux utilities +# Created: Sat Dec 26 20:09:40 1992 +# Revised: Thu Feb 16 10:00:24 1995 by faith@cs.unc.edu +# Copyright 1992, 1993, 1994, 1995 Rickard E. Faith (faith@cs.unc.edu) +# + +include ../MCONFIG + +# Where to put man pages? + +MAN1= cal.1 clear.1 dnsdomainname.1 domainname.1 dsplit.1 \ + hostid.1 hostname.1 kill.1 logger.1 look.1 mcookie.1 \ + md5sum.1 namei.1 reset.1 script.1 setterm.1 tsort.1 \ + whereis.1 write.1 + +# Where to put binaries? +# See the "install" rule for the links. . . + +BIN= domainname hostname kill + +USRBIN= cal clear dsplit hostid logger look mcookie md5sum namei \ + reset script setterm tsort whereis write + +# Programs requiring special compilation + +NEEDS_TERMCAP= setterm +SCRIPTS= clear reset + +all: $(BIN) $(USRBIN) $(USRBIN.NONSHADOW) $(USRGAMES) getoptprog + +%.o: %.c + $(CC) -c $(CFLAGS) $< -o $@ + +$(NEEDS_TERMCAP): + $(CC) $(LDFLAGS) $^ -o $@ -ltermcap + +$(SCRIPTS): + cp $@.sh $@ + +# Rules for everything else + +cal: cal.o $(BSD)/getopt.o $(BSD)/err.o +clear: clear.sh +domainname: domainname.o +dsplit: dsplit.o +getoptprog: getoptprog.o $(BSD)/getopt.o +hostid: hostid.o +hostname: hostname.o +kill: kill.o procs.o +logger: logger.o $(BSD)/getopt.o +md5sum: md5.o +md5.o: md5.h +namei: namei.o +reset: reset.sh +script: script.o +setterm: setterm.o +tsort: tsort.o + +install: all + $(INSTALLDIR) $(BINDIR) $(USRBINDIR) + $(INSTALLBIN) $(BIN) $(BINDIR) + $(INSTALLBIN) $(USRBIN) $(USRBINDIR) + $(INSTALLBIN) getoptprog $(USRBINDIR)/getopt + (cd $(BINDIR); ln -sf hostname dnsdomainname) + $(INSTALLDIR) $(MAN1DIR) $(MAN8DIR) + $(INSTALLMAN) $(MAN1) $(MAN1DIR) + $(INSTALLMAN) getoptprog.1 $(MAN1DIR)/getopt.1 + +.PHONY: clean +clean: + -rm -f *.o *~ core $(BIN) $(USRBIN) getoptprog diff --git a/misc-utils/README.cal b/misc-utils/README.cal new file mode 100644 index 00000000..638ac9df --- /dev/null +++ b/misc-utils/README.cal @@ -0,0 +1,42 @@ +The cal(1) date routines were written from scratch, basically from first +principles. The algorithm for calculating the day of week from any +Gregorian date was "reverse engineered". This was necessary as most of +the documented algorithms have to do with date calculations for other +calendars (e.g. julian) and are only accurate when converted to gregorian +within a narrow range of dates. + +1 Jan 1 is a Saturday because that's what cal says and I couldn't change +that even if I was dumb enough to try. From this we can easily calculate +the day of week for any date. The algorithm for a zero based day of week: + + calculate the number of days in all prior years (year-1)*365 + add the number of leap years (days?) since year 1 + (not including this year as that is covered later) + add the day number within the year + this compensates for the non-inclusive leap year + calculation + if the day in question occurs before the gregorian reformation + (3 sep 1752 for our purposes), then simply return + (value so far - 1 + SATURDAY's value of 6) modulo 7. + if the day in question occurs during the reformation (3 sep 1752 + to 13 sep 1752 inclusive) return THURSDAY. This is my + idea of what happened then. It does not matter much as + this program never tries to find day of week for any day + that is not the first of a month. + otherwise, after the reformation, use the same formula as the + days before with the additional step of subtracting the + number of days (11) that were adjusted out of the calendar + just before taking the modulo. + +It must be noted that the number of leap years calculation is sensitive +to the date for which the leap year is being calculated. A year that occurs +before the reformation is determined to be a leap year if its modulo of +4 equals zero. But after the reformation, a year is only a leap year if +its modulo of 4 equals zero and its modulo of 100 does not. Of course, +there is an exception for these century years. If the modulo of 400 equals +zero, then the year is a leap year anyway. This is, in fact, what the +gregorian reformation was all about (a bit of error in the old algorithm +that caused the calendar to be inaccurate.) + +Once we have the day in year for the first of the month in question, the +rest is trivial. diff --git a/misc-utils/README.hostname b/misc-utils/README.hostname new file mode 100644 index 00000000..1e82b8cf --- /dev/null +++ b/misc-utils/README.hostname @@ -0,0 +1,29 @@ + +You may ask "Why another version of the hostname command?". The answer is +simple. A lot of people misuse the domainname command to get the DNS domain +name. Since the domainname command should ONLY be used to set/show the NIS +domain name (formerly known as Yellow Pages) there was no easy way to get +the FQDN (Fully Qualified Domain Name) or the DNS domainname from within a +shell script. + +This hostname command offers you some additional features: + +- show the FQDN (long host name) +- show the short host name +- show the DNS domain name +- read the host name from file + +For further informations simply type "hostname --help" or read the manual +page. + +If the program is called as dnsdomainname it will simply show the DNS domain +name. + +If you ONLY use the loopback mode you can only use the normal features +(set/show the host name) since you probably don't have a FQDN (Fully Qualified +Domain Name) in the /etc/hosts file. You can change this by either using +the dummy device or by changing the localhost line in /etc/hosts to +something like this (it will use localhost as an alias name): + +127.0.0.1 erdos.maths.groucho.edu localhost erdos + diff --git a/misc-utils/README.namei b/misc-utils/README.namei new file mode 100644 index 00000000..490939e0 --- /dev/null +++ b/misc-utils/README.namei @@ -0,0 +1,31 @@ +Tired of running into "Too many levels of symlinks" problems on +your 4.2 BSD derivitive machine? + +We sure did... our NFS'ed network of lots of Suns, Vaxen and so forth +made it impossible at times to trace down where a file REALLY lived. +I mean ls -l is nice, but wouldn't you like to follow things like +the namei routine in the kernel does? + +Well here it is.... the namei program. It follows things out until +a terminal state is found. + +This program compiles and runs under: + + SunOS 4.0.1 (sun3's) + SunOS 4.0.3 (sun4's) + SunOS 4.1.1 (sun4's) + Ultrix 3.1 + BSD 4.3 + +and probably a host of other 4.2 derived systems (but probably not +System V). + +Anyway, if anyone has any bugs (or enhancements), please send them to +me in E-mail form. + +And, by the way, if you make LOTS of money off of this program, please +don't tell me :-). + + -Roger (rogers@fangorn.wr.tek.com) + UUCP: ...!uunet!tektronix!fangorn.wr.tek.com!rogers + ARPA: diff --git a/misc-utils/README.script b/misc-utils/README.script new file mode 100644 index 00000000..83dfcc5d --- /dev/null +++ b/misc-utils/README.script @@ -0,0 +1,7 @@ +Here is a working version of the BSD script command which captures +the output of a terminal session in a file. + +If you have libc-4.2 you don't need cfmakeraw.c or paths.h + +Rick Sladkey +jrs@world.std.com diff --git a/misc-utils/README1.namei b/misc-utils/README1.namei new file mode 100644 index 00000000..fea56a3c --- /dev/null +++ b/misc-utils/README1.namei @@ -0,0 +1,14 @@ + +** NAMEI has local modifications, do not delete source when cleaning up ** + +"You're in a twisty maze of symbolic links, all different" + +namei - a utility to chase down a pathname and print details at each +level, especialy when following symbolic links. Very useful for figuring +out whats really going on in our large environment. Named after the routine +in the kernel that does essentialy the same thing whenever anyone tries to +find a file. + +Local modifications by Steve Tell include: changing the -m option to print +the file mode in a readable fashion, like "ls -l" does, instead of in octal. + diff --git a/misc-utils/cal.1 b/misc-utils/cal.1 new file mode 100644 index 00000000..80d95b27 --- /dev/null +++ b/misc-utils/cal.1 @@ -0,0 +1,81 @@ +.\" Copyright (c) 1989, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Kim Letkeman. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)cal.1 8.1 (Berkeley) 6/6/93 +.\" +.Dd June 6, 1993 +.Dt CAL 1 +.Os +.Sh NAME +.Nm cal +.Nd displays a calendar +.Sh SYNOPSIS +.Nm cal +.Op Fl jy +.Op Ar month Op Ar year +.Sh DESCRIPTION +.Nm Cal +displays a simple calendar. +If arguments are not specified, +the current month is displayed. +The options are as follows: +.Bl -tag -width Ds +.It Fl j +Display julian dates (days one-based, numbered from January 1). +.It Fl y +Display a calendar for the current year. +.El +.Pp +A single parameter specifies the year (1 - 9999) to be displayed; +note the year must be fully specified: +.Dq Li cal 89 +will +.Em not +display a calendar for 1989. +Two parameters denote the month (1 - 12) and year. +If no parameters are specified, the current month's calendar is +displayed. +.Pp +A year starts on Jan 1. +.Pp +The Gregorian Reformation is assumed to have occurred in 1752 on the 3rd +of September. +By this time, most countries had recognized the reformation (although a +few did not recognize it until the early 1900's.) +Ten days following that date were eliminated by the reformation, so the +calendar for that month is a bit unusual. +.Sh HISTORY +A +.Nm +command appeared in Version 6 AT&T UNIX. diff --git a/misc-utils/cal.c b/misc-utils/cal.c new file mode 100644 index 00000000..8004016d --- /dev/null +++ b/misc-utils/cal.c @@ -0,0 +1,450 @@ +/* + * 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 + * Kim Letkeman. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 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[] = "@(#)cal.c 8.4 (Berkeley) 4/2/94"; +#endif /* not lint */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define THURSDAY 4 /* for reformation */ +#define SATURDAY 6 /* 1 Jan 1 was a Saturday */ + +#define FIRST_MISSING_DAY 639787 /* 3 Sep 1752 */ +#define NUMBER_MISSING_DAYS 11 /* 11 day correction */ + +#define MAXDAYS 42 /* max slots in a month array */ +#define SPACE -1 /* used in day array */ + +static int days_in_month[2][13] = { + {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, + {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, +}; + +int sep1752[MAXDAYS] = { + SPACE, SPACE, 1, 2, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, + SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, + SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, + SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, +}, j_sep1752[MAXDAYS] = { + SPACE, SPACE, 245, 246, 258, 259, 260, + 261, 262, 263, 264, 265, 266, 267, + 268, 269, 270, 271, 272, 273, 274, + SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, + SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, + SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, +}, empty[MAXDAYS] = { + SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, + SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, + SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, + SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, + SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, + SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, +}; + +char *day_headings = " S M Tu W Th F S "; +char *j_day_headings = " S M Tu W Th F S "; + +/* leap year -- account for gregorian reformation in 1752 */ +#define leap_year(yr) \ + ((yr) <= 1752 ? !((yr) % 4) : \ + !((yr) % 4) && ((yr) % 100) || !((yr) % 400)) + +/* number of centuries since 1700, not inclusive */ +#define centuries_since_1700(yr) \ + ((yr) > 1700 ? (yr) / 100 - 17 : 0) + +/* number of centuries since 1700 whose modulo of 400 is 0 */ +#define quad_centuries_since_1700(yr) \ + ((yr) > 1600 ? ((yr) - 1600) / 400 : 0) + +/* number of leap years between year 1 and this year, not inclusive */ +#define leap_years_since_year_1(yr) \ + ((yr) / 4 - centuries_since_1700(yr) + quad_centuries_since_1700(yr)) + +int julian; + +void ascii_day __P((char *, int)); +void center __P((char *, int, int)); +void day_array __P((int, int, int *)); +int day_in_week __P((int, int, int)); +int day_in_year __P((int, int, int)); +void j_yearly __P((int)); +void monthly __P((int, int)); +void trim_trailing_spaces __P((char *)); +void usage __P((void)); +void yearly __P((int)); +void headers_init(void); + +int +main(argc, argv) + int argc; + char **argv; +{ + struct tm *local_time; + time_t now; + int ch, month, year, yflag; + +#ifdef __linux__ + extern char *__progname; + __progname = argv[0]; +#endif + + setlocale(LC_ALL,""); + headers_init(); + yflag = 0; + while ((ch = getopt(argc, argv, "jy")) != EOF) + switch(ch) { + case 'j': + julian = 1; + break; + case 'y': + yflag = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + month = 0; + switch(argc) { + case 2: + if ((month = atoi(*argv++)) < 1 || month > 12) + errx(1, "illegal month value: use 1-12"); + /* FALLTHROUGH */ + case 1: + if ((year = atoi(*argv)) < 1 || year > 9999) + errx(1, "illegal year value: use 1-9999"); + break; + case 0: + (void)time(&now); + local_time = localtime(&now); + year = local_time->tm_year + 1900; + if (!yflag) + month = local_time->tm_mon + 1; + break; + default: + usage(); + } + + if (month) + monthly(month, year); + else if (julian) + j_yearly(year); + else + yearly(year); + exit(0); +} + +#define DAY_LEN 3 /* 3 spaces per day */ +#define J_DAY_LEN 4 /* 4 spaces per day */ +#define WEEK_LEN 21 /* 7 days * 3 characters */ +#define J_WEEK_LEN 28 /* 7 days * 4 characters */ +#define HEAD_SEP 2 /* spaces between day headings */ +#define J_HEAD_SEP 2 + +void headers_init(void) +{ + int i; + + strcpy(day_headings,""); + for(i = 0 ; i < 7 ; i++ ) + { + strncat(day_headings,_time_info->abbrev_wkday[i],2); + strcat(day_headings," "); + } + strcpy(j_day_headings,""); + for(i = 0 ; i < 7 ; i++ ) + { + strcat(j_day_headings,_time_info->abbrev_wkday[i]); + strcat(j_day_headings," "); + } +} + +void +monthly(month, year) + int month, year; +{ + int col, row, len, days[MAXDAYS]; + char *p, lineout[30]; + + day_array(month, year, days); + len = sprintf(lineout, "%s %d", _time_info->full_month[month - 1], year); + (void)printf("%*s%s\n%s\n", + ((julian ? J_WEEK_LEN : WEEK_LEN) - len) / 2, "", + lineout, julian ? j_day_headings : day_headings); + for (row = 0; row < 6; row++) { + for (col = 0, p = lineout; col < 7; col++, + p += julian ? J_DAY_LEN : DAY_LEN) + ascii_day(p, days[row * 7 + col]); + *p = '\0'; + trim_trailing_spaces(lineout); + (void)printf("%s\n", lineout); + } +} + +void +j_yearly(year) + int year; +{ + int col, *dp, i, month, row, which_cal; + int days[12][MAXDAYS]; + char *p, lineout[80]; + + (void)sprintf(lineout, "%d", year); + center(lineout, J_WEEK_LEN * 2 + J_HEAD_SEP, 0); + (void)printf("\n\n"); + for (i = 0; i < 12; i++) + day_array(i + 1, year, days[i]); + (void)memset(lineout, ' ', sizeof(lineout) - 1); + lineout[sizeof(lineout) - 1] = '\0'; + for (month = 0; month < 12; month += 2) { + center(_time_info->full_month[month], J_WEEK_LEN, J_HEAD_SEP); + center(_time_info->full_month[month + 1], J_WEEK_LEN, 0); + (void)printf("\n%s%*s%s\n", j_day_headings, J_HEAD_SEP, "", + j_day_headings); + for (row = 0; row < 6; row++) { + for (which_cal = 0; which_cal < 2; which_cal++) { + p = lineout + which_cal * (J_WEEK_LEN + 2); + dp = &days[month + which_cal][row * 7]; + for (col = 0; col < 7; col++, p += J_DAY_LEN) + ascii_day(p, *dp++); + } + *p = '\0'; + trim_trailing_spaces(lineout); + (void)printf("%s\n", lineout); + } + } + (void)printf("\n"); +} + +void +yearly(year) + int year; +{ + int col, *dp, i, month, row, which_cal; + int days[12][MAXDAYS]; + char *p, lineout[80]; + + (void)sprintf(lineout, "%d", year); + center(lineout, WEEK_LEN * 3 + HEAD_SEP * 2, 0); + (void)printf("\n\n"); + for (i = 0; i < 12; i++) + day_array(i + 1, year, days[i]); + (void)memset(lineout, ' ', sizeof(lineout) - 1); + lineout[sizeof(lineout) - 1] = '\0'; + for (month = 0; month < 12; month += 3) { + center(_time_info->full_month[month], WEEK_LEN, HEAD_SEP); + center(_time_info->full_month[month + 1], WEEK_LEN, HEAD_SEP); + center(_time_info->full_month[month + 2], WEEK_LEN, 0); + (void)printf("\n%s%*s%s%*s%s\n", day_headings, HEAD_SEP, + "", day_headings, HEAD_SEP, "", day_headings); + for (row = 0; row < 6; row++) { + for (which_cal = 0; which_cal < 3; which_cal++) { + p = lineout + which_cal * (WEEK_LEN + 2); + dp = &days[month + which_cal][row * 7]; + for (col = 0; col < 7; col++, p += DAY_LEN) + ascii_day(p, *dp++); + } + *p = '\0'; + trim_trailing_spaces(lineout); + (void)printf("%s\n", lineout); + } + } + (void)printf("\n"); +} + +/* + * day_array -- + * Fill in an array of 42 integers with a calendar. Assume for a moment + * that you took the (maximum) 6 rows in a calendar and stretched them + * out end to end. You would have 42 numbers or spaces. This routine + * builds that array for any month from Jan. 1 through Dec. 9999. + */ +void +day_array(month, year, days) + int month, year; + int *days; +{ + int day, dw, dm; + + if (month == 9 && year == 1752) { + memmove(days, + julian ? j_sep1752 : sep1752, MAXDAYS * sizeof(int)); + return; + } + memmove(days, empty, MAXDAYS * sizeof(int)); + dm = days_in_month[leap_year(year)][month]; + dw = day_in_week(1, month, year); + day = julian ? day_in_year(1, month, year) : 1; + while (dm--) + days[dw++] = day++; +} + +/* + * day_in_year -- + * return the 1 based day number within the year + */ +int +day_in_year(day, month, year) + int day, month, year; +{ + int i, leap; + + leap = leap_year(year); + for (i = 1; i < month; i++) + day += days_in_month[leap][i]; + return (day); +} + +/* + * day_in_week + * return the 0 based day number for any date from 1 Jan. 1 to + * 31 Dec. 9999. Assumes the Gregorian reformation eliminates + * 3 Sep. 1752 through 13 Sep. 1752. Returns Thursday for all + * missing days. + */ +int +day_in_week(day, month, year) + int day, month, year; +{ + long temp; + + temp = (long)(year - 1) * 365 + leap_years_since_year_1(year - 1) + + day_in_year(day, month, year); + if (temp < FIRST_MISSING_DAY) + return ((temp - 1 + SATURDAY) % 7); + if (temp >= (FIRST_MISSING_DAY + NUMBER_MISSING_DAYS)) + return (((temp - 1 + SATURDAY) - NUMBER_MISSING_DAYS) % 7); + return (THURSDAY); +} + +void +ascii_day(p, day) + char *p; + int day; +{ + int display, val; + static char *aday[] = { + "", + " 1", " 2", " 3", " 4", " 5", " 6", " 7", + " 8", " 9", "10", "11", "12", "13", "14", + "15", "16", "17", "18", "19", "20", "21", + "22", "23", "24", "25", "26", "27", "28", + "29", "30", "31", + }; + + if (day == SPACE) { + memset(p, ' ', julian ? J_DAY_LEN : DAY_LEN); + return; + } + if (julian) { + if (val = day / 100) { + day %= 100; + *p++ = val + '0'; + display = 1; + } else { + *p++ = ' '; + display = 0; + } + val = day / 10; + if (val || display) + *p++ = val + '0'; + else + *p++ = ' '; + *p++ = day % 10 + '0'; + } else { + *p++ = aday[day][0]; + *p++ = aday[day][1]; + } + *p = ' '; +} + +void +trim_trailing_spaces(s) + char *s; +{ + char *p; + + for (p = s; *p; ++p) + continue; + while (p > s && isspace(*--p)) + continue; + if (p > s) + ++p; + *p = '\0'; +} + +void +center(str, len, separate) + char *str; + int len; + int separate; +{ + + len -= strlen(str); + (void)printf("%*s%s%*s", len / 2, "", str, len / 2 + len % 2, ""); + if (separate) + (void)printf("%*s", separate, ""); +} + +void +usage() +{ + + (void)fprintf(stderr, "usage: cal [-jy] [[month] year]\n"); + exit(1); +} diff --git a/misc-utils/clear.1 b/misc-utils/clear.1 new file mode 100644 index 00000000..1d4a5df7 --- /dev/null +++ b/misc-utils/clear.1 @@ -0,0 +1,25 @@ +.\" Copyright 1992 Rickard E. Faith (faith@cs.unc.edu) +.\" May be distributed under the GNU General Public License +.TH CLEAR 1 "10 October 1993" "Linux 0.99" "Linux Programmer's Manual" +.SH NAME +clear \- clear terminal screen +.SH SYNOPSIS +.BR clear +.SH DESCRIPTION +.B clear +calls +.BR tput (1) +with the +.I clear +argument. This causes +.B tput +to attempt to clear the screen checking the data in +.I /etc/termcap +and sending the appropriate sequence to the terminal. This command can be +redirected to clear the screen of some other terminal. +.SH "SEE ALSO" +.BR reset (1), +.BR stty (1), +.BR tput (1) +.SH AUTHOR +Rik Faith (faith@cs.unc.edu) diff --git a/misc-utils/clear.sh b/misc-utils/clear.sh new file mode 100644 index 00000000..73d1ebe1 --- /dev/null +++ b/misc-utils/clear.sh @@ -0,0 +1,2 @@ +#! /bin/sh +tput clear diff --git a/misc-utils/dnsdomainname.1 b/misc-utils/dnsdomainname.1 new file mode 100644 index 00000000..1f45128b --- /dev/null +++ b/misc-utils/dnsdomainname.1 @@ -0,0 +1 @@ +.so man1/hostname.1 diff --git a/misc-utils/domainname.1 b/misc-utils/domainname.1 new file mode 100644 index 00000000..447c7126 --- /dev/null +++ b/misc-utils/domainname.1 @@ -0,0 +1,43 @@ +.\" Copyright 1992, 1995 Rickard E. Faith (faith@cs.unc.edu) +.\" May be distributed under the GNU General Public License +.TH DOMAINNAME 1 "16 February 1995" "Linux 1.0" "Linux Programmer's Manual" +.SH NAME +domainname \- set or print NIS domain of current host +.SH SYNOPSIS +.BR "domainname [ " name " ]" +.SH DESCRIPTION +.B domainname +prints the NIS domainname of the current host, from the +.BR getdomainname (3) +library call. If an argument is present and the effective UID is 0, +.B domainname +changes the NIS domainname of the host, with the +.BR setdomainname (2) +system call. This is usually done at boot time in the +.I /etc/rc.local +script. +.PP +.B Note: +This command sets the NIS (Network Information Services) domain, +.I not +the DNS (Domain Name System) domain. Unless you are running NIS +(formerly known as Sun Yellow Pages (YP)), you should +.I not +use the +.B domainname +command to set your domain. You probably want to set the DNS domain, which +is used to map human-readable machine names into IP addresses on the +InterNet. See +.BR dnsdomainname (1) +for more information. +.SH FILES +.I /etc/rc.local +.SH "SEE ALSO" +.BR hostname (1), +.BR dnsdomainname (1), +.BR named (8), +.BR sendmail (8), +.bR ypinit (8) +.SH AUTHOR +Lars Wirzenius by substituting in hostname.c + diff --git a/misc-utils/domainname.c b/misc-utils/domainname.c new file mode 100644 index 00000000..107091e8 --- /dev/null +++ b/misc-utils/domainname.c @@ -0,0 +1,29 @@ +/* domainname.c - poe@daimi.aau.dk */ + +#include +#include +#include +#include + +#define MAXDNAME 64 + +int main(int argc, char *argv[]) +{ + char hn[MAXDNAME + 1]; + + if(argc >= 2) { + if(geteuid() || getuid()) { + puts("You must be root to change the domainname"); + exit(1); + } + if(strlen(argv[1]) > MAXDNAME) { + puts("That name is too long."); + exit(1); + } + setdomainname(argv[1], strlen(argv[1])); + } else { + getdomainname(hn, MAXDNAME); + puts(hn); + } + exit(0); +} diff --git a/misc-utils/dsplit.1 b/misc-utils/dsplit.1 new file mode 100644 index 00000000..f78f058f --- /dev/null +++ b/misc-utils/dsplit.1 @@ -0,0 +1,46 @@ +.\" Public Domain 1994 Rik Faith (faith@cs.unc.edu) +.TH DSPLIT 1 "5 July 1994" "Linux 1.1" "Linux Programmer's Manual" +.SH NAME +dsplit \- split a large file into pieces +.SH SYNOPSIS +.BI "dsplit [ \-size " nnn " ] [ " input_file " [ " output_base " ] ]" +.SH DESCRIPTION +.B dsplit +splits binary files into smaller chunks so that they may be placed on +floppy disks. +.SH OPTIONS +.TP +.BI \-size " nnn" +Specifies the size of each output file, in bytes. The default is 1457000, +which is enough to will a 1.44 MB floppy disk. +.TP +.I input_file +Specifies the name of the file to split up. A \- indicates standard input. +The default is standard input. +.TP +.I output_base +Specifies the name of the output files to be written. +.B dsplit +will append 000, 001, ..., to the +.IR output_base . +The default is "dsplit". +.SH "AUTHOR'S NOTES" +Submitted-by: arnstein@netcom.com (David Arnstein) +.br +Posting-number: Volume 40, Issue 51 +.br +Archive-name: dsplit/part01 +.br +Environment: MS-DOS, UNIX +.PP +Here is a portable binary file splitting program. It reads a binary file +and splits it into pieces. I use this program to put large binary files on +floppy disks. For this reason, the default size of the output files is +1,457,000 bytes, which just about fills up a 1.44 MB floppy disk. +.PP +Unlike other binary split programs I have seen, dsplit does not malloc a +huge block of memory. Dsplit is suitable for use under MSDOS and other +primitive operating systems. +.PP +(The program came from +gatekeeper.dec.com:/pub/usenet/comp.sources.misc/volume40/dsplit). diff --git a/misc-utils/dsplit.c b/misc-utils/dsplit.c new file mode 100644 index 00000000..14d8ffff --- /dev/null +++ b/misc-utils/dsplit.c @@ -0,0 +1,271 @@ +#ifdef lint + static char RCSid[] = "dsplit.c,v 1.1.1.1 1995/02/22 19:09:14"; +#endif +/* + Program dsplit: Splits a large file into pieces. + + Usage: + dsplit [-size nnn] [input_file [output_base]] + Size is size of each output file, in bytes. The default is 1457000, + enough to fill a "1.44MB" diskette, save 152 bytes. + input_file is the name of the file to split up. A dash (-) indicates + standard input. Defaults to standard input. + output_base is the name of the output files to be written, minus the + extension. Dsplit adds suffixes 000, 001, ... + The default base name is dsplit. +*/ +#include +#include +#include +#include +#if (defined (__MSDOS__) || defined (WIN32)) +#include +#include +#endif /* __MSDOS__ or WIN32 */ +#ifndef FILENAME_MAX +#define FILENAME_MAX 1024 +#endif + +#define DEFAULT_NAME "dsplit" +#define DEFAULT_SIZE 1457000L +#if (defined (__MSDOS__) || defined (WIN32)) +# define READ_OPTIONS "rb" +# define WRITE_OPTIONS "wb" +#else +# define READ_OPTIONS "r" +# define WRITE_OPTIONS "w" +#endif /* __MSDOS__ or WIN32 */ + +#ifndef MIN +#define MIN(a,b) (((a) <= (b)) ? (a) : (b)) +#endif + +static unsigned long output_size = DEFAULT_SIZE; +static char* base_name = DEFAULT_NAME; +static FILE* input_handle; +static char* input_name = "-"; + +#ifdef __STDC__ +static void init (int argc, char* argv[]); +static int write_one (int count); +static void ToLower (char* string); +static void usage_error (void); +#else /* not __STDC__ */ +static void init (/* int argc, char* argv[] */); +static int write_one (/* int count */); +static void ToLower (/* char* string */); +static void usage_error (/* void */); +#endif /* __STDC__ */ + + + +#ifdef __STDC__ +int main (int argc, char* argv[]) +#else +int main (argc, argv) +int argc; +char* argv[]; +#endif +{ + int count; + + /* Process command line arguments, open input file. */ + init (argc, argv); + + /* Write the output files */ + for (count = 0 ; write_one (count) ; ++count) + ; + + /* Close input file (a matter of style) */ + if (fclose (input_handle) != 0) + { + (void)fprintf (stderr, "Could not close file \"%s\" for input\n", + input_name); + return 1; + } + + /* Normal successful conclusion */ + return 0; +} + + + +#ifdef __STDC__ +static void init (int argc, char* argv[]) +#else +static void init (argc, argv) +int argc; +char* argv[]; +#endif +{ + int iarg; + int name_count; + + /* Initialize the input file handle to standard input. IBM's Toolset++ + won't let me do this statically, unfortunately. */ + input_handle = stdin; + + /* Initialize for following loop */ + name_count = 0; + + /* Loop over command line arguments */ + for (iarg = 1 ; iarg < argc ; ++iarg) + { + /* Point to argument,for convenience */ + char* arg = argv[iarg]; + + /* If this argument is an option */ + if (arg[0] == '-' && arg[1] != '\0') + { + /* Process option if recognized */ + ToLower (arg+1); + if (strcmp (arg+1, "size") != 0) + usage_error (); + ++iarg; + if (iarg >= argc) + usage_error (); + arg = argv[iarg]; + if (sscanf (arg, "%ld", &output_size) != 1) + { + (void)fprintf (stderr, "Illegal numeric expression \"%s\"\n", arg); + exit (1); + } + } + else /* argument is not an option */ + { + /* Argument is a name string. Determine which one. */ + switch (name_count) + { + case 0: + input_name = argv[iarg]; + break; + case 1: + base_name = argv[iarg]; + break; + default: + usage_error (); + break; + } + ++name_count; + + } /* End if this argument is an option */ + + } /* End loop over command line arguments */ + + /* Open the input file */ + if (strcmp (input_name, "-") == 0) + { +# if (defined (__MSDOS__) || defined (WIN32)) + if (setmode (0, O_BINARY) == -1) + { + perror ("dsplit: setmode"); + exit (1); + } +# endif + } + else + { + if ((input_handle = fopen (input_name, READ_OPTIONS)) == NULL) + { + (void)fprintf (stderr, "Could not open file \"%s\" for input\n", + input_name); + exit (1); + } + } +} + + + +#ifdef __STDC__ +static int write_one (int count) +#else +static int write_one (count) +int count; +#endif +{ + char output_name[FILENAME_MAX]; + int bytes_written; + unsigned long total_written; + FILE* output_handle; + + /* Read the first buffer full now, just to see if any data is left */ + static char buff[1024]; + int to_read = MIN (sizeof(buff), output_size); + int bytes_read = fread (buff, 1, to_read, input_handle); + if (bytes_read <= 0) + return 0; + + /* Open file for output */ + sprintf (output_name, "%s.%03d", base_name, count); + output_handle = fopen (output_name, WRITE_OPTIONS); + if (output_handle == NULL) + { + (void)fprintf (stderr, + "Could not open file \"%s\" for output\n", output_name); + exit (1); + } + + /* Write the first output buffer */ + bytes_written = fwrite (buff, 1, bytes_read, output_handle); + if (bytes_written != bytes_read) + { + (void)fprintf (stderr, "Error writing to file \"%s\"\n", output_name); + exit (1); + } + total_written = bytes_written; + + /* Write output file */ + while (total_written < output_size) + { + to_read = MIN (sizeof(buff), output_size-total_written); + bytes_read = fread (buff, 1, to_read, input_handle); + if (bytes_read <= 0) + break; + bytes_written = fwrite (buff, 1, bytes_read, output_handle); + if (bytes_written != bytes_read) + { + (void)fprintf (stderr, "Error writing to file \"%s\"\n", output_name); + exit (1); + } + total_written += bytes_written; + } + + /* Close the output file, it is complete */ + if (fclose (output_handle) != 0) + { + (void)fprintf (stderr, + "Could not close file \"%s\" for output\n", output_name); + exit (1); + } + + /* Indicate whether more data remains to be transferred */ + return (bytes_read == to_read); +} + + + +#ifdef __STDC__ +static void ToLower (char* string) +#else +static void ToLower (string) +char* string; +#endif +{ + + while (*string != '\0') + tolower (*string++); +} + + + +#ifdef __STDC__ +static void usage_error (void) +#else +static void usage_error () +#endif +{ + (void)fprintf (stderr, + "Usage: dsplit [-size nnn] [input_file [output_base]]\n"); + exit (1); +} + diff --git a/misc-utils/getoptprog.1 b/misc-utils/getoptprog.1 new file mode 100644 index 00000000..12853af7 --- /dev/null +++ b/misc-utils/getoptprog.1 @@ -0,0 +1,104 @@ +.Dd June 21, 1993 +.Dt GETOPT 1 +.Os +.Sh NAME +.Nm getopt +.Nd parse command options +.Sh SYNOPSIS +.Nm set \-\- \`getopt optstring $*\` +.Sh DESCRIPTION +.Nm Getopt +is used to break up options in command lines for easy parsing by +shell procedures, and to check for legal options. +.Op Optstring +is a string of recognized option letters (see +.Xr getopt 3 +); +if a letter is followed by a colon, the option +is expected to have an argument which may or may not be +separated from it by white space. +The special option +.B \-\- +is used to delimit the end of the options. +.Nm Getopt +will place +.B \-\- +in the arguments at the end of the options, +or recognize it if used explicitly. +The shell arguments +(\fB$1 $2\fR ...) are reset so that each option is +preceded by a +.B \- +and in its own shell argument; +each option argument is also in its own shell argument. +.Sh EXAMPLE +The following code fragment shows how one might process the arguments +for a command that can take the options +.Op a +and +.Op b , +and the option +.Op o , +which requires an argument. +.Pp +.Bd -literal -offset indent +set \-\- \`getopt abo: $*\` +if test $? != 0 +then + echo 'Usage: ...' + exit 2 +fi +for i +do + case "$i" + in + \-a|\-b) + flag=$i; shift;; + \-o) + oarg=$2; shift; shift;; + \-\-) + shift; break;; + esac +done +.Ed +.Pp +This code will accept any of the following as equivalent: +.Pp +.Bd -literal -offset indent +cmd \-aoarg file file +cmd \-a \-o arg file file +cmd \-oarg -a file file +cmd \-a \-oarg \-\- file file +.Ed +.Sh SEE ALSO +.Xr sh 1 , +.Xr getopt 3 +.Sh DIAGNOSTICS +.Nm Getopt +prints an error message on the standard error output when it +encounters an option letter not included in +.Op optstring . +.Sh HISTORY +Written by Henry Spencer, working from a Bell Labs manual page. +Behavior believed identical to the Bell version. +.Sh BUGS +Whatever +.Xr getopt 3 +has. +.Pp +Arguments containing white space or imbedded shell metacharacters +generally will not survive intact; this looks easy to fix but isn't. +.Pp +The error message for an invalid option is identified as coming +from +.Nm getopt +rather than from the shell procedure containing the invocation +of +.Nm getopt ; +this again is hard to fix. +.Pp +The precise best way to use the +.Nm set +command to set the arguments without disrupting the value(s) of +shell options varies from one shell version to another. +varies from one shell version to another. diff --git a/misc-utils/getoptprog.c b/misc-utils/getoptprog.c new file mode 100644 index 00000000..03b0987e --- /dev/null +++ b/misc-utils/getoptprog.c @@ -0,0 +1,30 @@ +#include + +main(argc, argv) +int argc; +char *argv[]; +{ + extern int optind; + extern char *optarg; + int c; + int status = 0; + + optind = 2; /* Past the program name and the option letters. */ + while ((c = getopt(argc, argv, argv[1])) != EOF) + switch (c) { + case '?': + status = 1; /* getopt routine gave message */ + break; + default: + if (optarg != NULL) + printf(" -%c %s", c, optarg); + else + printf(" -%c", c); + break; + } + printf(" --"); + for (; optind < argc; optind++) + printf(" %s", argv[optind]); + printf("\n"); + exit(status); +} diff --git a/misc-utils/hostid.1 b/misc-utils/hostid.1 new file mode 100644 index 00000000..9830a53d --- /dev/null +++ b/misc-utils/hostid.1 @@ -0,0 +1,23 @@ +.TH hostid 1 +.SH NAME +hostid \- set or print system's host id. +.SH SYNTAX +.B hostid +[\-v] [\|\fIdecimal-id\fR\|] +.SH DESCRIPTION +.\".NXR "hostid command" +The +.B hostid +command prints the current host id number in hexadecimal and both +decimal and hexadecimal in parenthesis if the \-v option is given. +This numeric value is expected to be unique across all hosts +and is normally set to resemble the host's Internet address. + +Only the super-user can set the hostid by giving an argument. This value is +stored in the file /etc/hostid and need only be performed once. + +.SH AUTHOR +Hostid is written by Mitch DSouza \- (m.dsouza@mrc-apu.cam.ac.uk) + +.SH SEE ALSO +gethostid(2), sethostid(2) diff --git a/misc-utils/hostid.c b/misc-utils/hostid.c new file mode 100644 index 00000000..829c5b67 --- /dev/null +++ b/misc-utils/hostid.c @@ -0,0 +1,36 @@ +/* Mitch DSouza - (m.dsouza@mrc-apu.cam.ac.uk) */ + +#include +#include +#include +#include +#include + +void main (int argc, char **argv) +{ + int verbose = 0; + + if(argc == 2 && strcmp(argv[1], "-v") == 0) { + verbose = 1; + argc--; + argv++; + } + + if (argc==2) { + if (sethostid(atoi(argv[1]))!=0) { + perror("sethostid"); + exit(1); + } + } else if (argc==1) { + unsigned long id = gethostid(); + + if(id && verbose) { + printf("Hostid is %lu (0x%lx)\n",id,id); + } else if(id) { + printf("0x%lx\n", id); + } else { + printf("Usage: %s hostid_number\n",*argv); + } + } + exit(0); +} diff --git a/misc-utils/hostname.1 b/misc-utils/hostname.1 new file mode 100644 index 00000000..9efc0758 --- /dev/null +++ b/misc-utils/hostname.1 @@ -0,0 +1,77 @@ +.TH HOSTNAME 1 "28 July 1994" "Linux" "Linux Programmer's Manual" +.SH NAME +hostname \- show or set the system's host name +.br +dnsdomainname \- show the system's domain name +.SH SYNOPSIS +.B hostname +.RB [ \-d ] +.RB [ \-\-domain ] +.RB [ \-F\ filename ] +.RB [ \-\-file\ filename ] +.RB [ \-f ] +.RB [ \-\-fqdn ] +.RB [ \-h ] +.RB [ \-\-help ] +.RB [ \-\-long ] +.RB [ \-s ] +.RB [ \-\-short ] +.RB [ \-v ] +.RB [ \-\-version ] +.RB [ name ] +.br +.B dnsdomainname +.SH DESCRIPTION +.B Hostname +is the program that is used to either set the host name or display +the current host or domain name of the system. This name is used +by many of the networking programs to identify the machine. +.LP +When called without any arguments, the program displays the current +name as set by the +.B hostname +command. You can change the output format to display always the short +or the long host name (FQDN). When called with arguments, the program will +set the value of the host name to the value specified. This usually is +done only once, at system startup time, by the +.I /etc/rc.d/rc.inet1 +configuration script. +.LP +Note, that only the super-user can change the host name. +.LP +If the program was called as +.B dnsdomainname +it will show the DNS domain name. You can't change the DNS domain name with +.B dnsdomainname +(see below). +.SH OPTIONS +.TP +.I "\-d, \-\-domain" +Display the name of the DNS domain. Don't use the command +.B domainname +to get the DNS domain name because it will show the NIS domain name and +not the DNS domain name. +.TP +.I "\-F, \-\-file filename" +Read the host name from the specified file. Comments (lines starting with +a `#') are ignored. +.TP +.I "\-f, \-\-fqdn, \-\-long" +Display the FQDN (Fully Qualified Domain Name). A FQDN consists of a +short host name and the DNS domain name. Unless you are using bind or NIS +for host lookups you can change the FQDN and the DNS domain name (which is +part of the FQDN) in the \fI/etc/hosts\fR file. +.TP +.I "\-h, \-\-help" +Print a usage message on standard output and exit successfully. +.TP +.I "\-s, \-\-short" +Display the short host name. +.TP +.I "\-v, \-\-version" +Print version information on standard output and exit successfully. +.SH FILES +.B /etc/hosts +.SH AUTHOR +Peter Tobias, + diff --git a/misc-utils/hostname.c b/misc-utils/hostname.c new file mode 100644 index 00000000..2ff915af --- /dev/null +++ b/misc-utils/hostname.c @@ -0,0 +1,184 @@ +/* hostname -- set the host name or show the host/domain name + + Copyright (C) 1994 Peter Tobias + + 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. + +*/ + + +#include +#include +#include +#include +#include +#include +#include + +#define NO_OPT -1 + +static char *program_name; +static const char *version_string = "hostname 1.6"; + +static void sethname(char *); +static void showhname(char *, int); +static void usage(void); + +static void sethname(char *hname) +{ + if(sethostname(hname, strlen(hname))) { + switch(errno) { + case EPERM: + fprintf(stderr,"%s: you must be root to change the host name\n", program_name); + break; + case EINVAL: + fprintf(stderr,"%s: name too long\n", program_name); + break; + default: + } + exit(1); + }; +} + +static void showhname(char *hname, int c) +{ + struct hostent *hp; + register char *p; + + if (!(hp = gethostbyname(hname))) { + herror(program_name); + exit(1); + } + + if (!(p = strchr(hp->h_name, '.')) && (c == 'd')) return; + + switch(c) { + case 'd': + printf("%s\n", ++p); + break; + case 'f': + printf("%s\n", hp->h_name); + break; + case 's': + if (p != NULL) *p = '\0'; + printf("%s\n", hp->h_name); + break; + default: + } +} + +static void usage(void) +{ + printf("Usage: %s [OPTION]... [hostname]\n\n\ + -d, --domain display the DNS domain name\n\ + -F, --file filename read the host name from file\n\ + -f, --fqdn, --long display the long host name (FQDN)\n\ + -s, --short display the short host name\n\ + -h, --help display this help and exit\n\ + -v, --version output version information and exit\n\ +\n\ + When the program is called without any arguments, it displays the\n\ + current host name as set by the hostname command. If an argument\n\ + is given, the program will set the value of the host name to the\n\ + value specified.\n\ + Unless you are using bind or NIS for host lookups you can change the\n\ + FQDN (Fully Qualified Domain Name) and the DNS domain name (which is\n\ + part of the FQDN) in the /etc/hosts file.\n", program_name); +} + +int main(int argc, char **argv) +{ + int c; + int option_index = 0; + + char myname[MAXHOSTNAMELEN+1]; + + static const struct option long_options[] = + { + {"domain", no_argument, 0, 'd'}, + {"file", required_argument, 0, 'F'}, + {"fqdn", no_argument, 0, 'f'}, + {"help", no_argument, 0, 'h'}, + {"long", no_argument, 0, 'f'}, + {"short", no_argument, 0, 's'}, + {"version", no_argument, 0, 'v'}, + {0, 0, 0, 0} + }; + + program_name = (rindex(argv[0], '/')) ? rindex(argv[0], '/') + 1 : argv[0]; + + if (strcmp(program_name, "dnsdomainname") == 0) { + if (argc > 1) { + fprintf(stderr,"%s: You can't change the DNS domainname with this command\n", program_name); + fprintf(stderr,"\nUnless you are using bind or NIS for host lookups you can change the DNS\n"); + fprintf(stderr,"domain name (which is part of the FQDN) in the /etc/hosts file.\n"); + exit(1); + } + c = 'd'; + } else + c = getopt_long(argc, argv, "dfF:hsv", long_options, &option_index); + + gethostname(myname, sizeof(myname)); + + switch(c) + { + case 'd': + case 'f': + case 's': + showhname(myname, c); + break; + case 'F': + { + register FILE *fd; + register char *p; + char fline[MAXHOSTNAMELEN]; + + if ((fd = fopen(optarg, "r")) != NULL) { + while (fgets(fline, sizeof(fline), fd) != NULL) + if ((p = index(fline, '\n')) != NULL) { + *p = '\0'; + if (fline[0] == '#') + continue; + sethname(fline); + } + (void) fclose(fd); + } else { + fprintf(stderr,"%s: can't open `%s'\n", + program_name, optarg); + exit(1); + } + } + break; + case 'h': + usage(); + break; + case 'v': + printf("%s\n", version_string); + break; + case '?': + fprintf(stderr,"Try `%s --help' for more information.\n", program_name); + exit(1); + break; + case NO_OPT: + if (optind < argc) { + sethname(argv[optind]); + exit(0); + } + default: + printf("%s\n", myname); + + }; + exit(0); +} diff --git a/misc-utils/kill.1 b/misc-utils/kill.1 new file mode 100644 index 00000000..aad5c23b --- /dev/null +++ b/misc-utils/kill.1 @@ -0,0 +1,49 @@ +.\" Copyright 1994 Salvatore Valente (svalente@mit.edu) +.\" Copyright 1992 Rickard E. Faith (faith@cs.unc.edu) +.\" May be distributed under the GNU General Public License +.TH KILL 1 "14 October 1994" "Linux Utilities" "Linux Programmer's Manual" +.SH NAME +kill \- terminate a process +.SH SYNOPSIS +.BR "kill" " [ \-s signal | \-p ] " " [ -a ] " "pid ..." +.br +.B "kill -l [ signal ]" +.SH DESCRIPTION +.B kill +sends the specified signal to the specified process. If no signal is +specified, the TERM signal is sent. The TERM signal will kill processes +which do not catch this signal. For other processes, if may be necessary +to use the KILL (9) signal, since this signal cannot be caught. + +Most modern shells have a builtin kill function. +.SH OPTIONS +.TP +.BR "pid ..." +Specify the list of processes that +.B kill +should signal. Each +.I pid +can be a process id, or a process name. +.TP +.BR \-s +Specify the signal to send. +The signal may be given as a signal name or number. +.TP +.BR \-p +Specify that +.B kill +should only print the process id +.I (pid) +of the named process, and should not send it a signal. +.TP +.BR \-l +Print a list of signal names. These are found in +.I /usr/include/linux/signal.h +.SH "SEE ALSO" +.BR bash (1), +.BR tcsh (1), +.BR kill (2), +.BR sigvec (2) +.SH AUTHOR +Taken from BSD 4.4. The ability to translate process names to process +ids was added by Salvatore Valente . diff --git a/misc-utils/kill.c b/misc-utils/kill.c new file mode 100644 index 00000000..f89ff67c --- /dev/null +++ b/misc-utils/kill.c @@ -0,0 +1,272 @@ +/* + * 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. + */ +/* + * oct 5 1994 -- almost entirely re-written to allow for process names. + * modifications (c) salvatore valente + * may be used / modified / distributed under the same terms as the original. + */ + +#include +#include +#include +#include +#include +#include + +#ifdef __linux__ +/* + * sys_signame -- an ordered list of signals. + * lifted from /usr/include/linux/signal.h + * this particular order is only correct for linux. + * this is _not_ portable. + */ +char *sys_signame[NSIG] = { + "zero", "HUP", "INT", "QUIT", "ILL", "TRAP", "IOT", "UNUSED", + "FPE", "KILL", "USR1", "SEGV", "USR2", "PIPE", "ALRM", "TERM", + "STKFLT","CHLD", "CONT", "STOP", "TSTP", "TTIN", "TTOU", "IO", + "XCPU", "XFSZ", "VTALRM","PROF", "WINCH", NULL +}; +#endif + +int main (int argc, char *argv[]); +char *mybasename(char *pathname); +int signame_to_signum (char *sig); +int arg_to_signum (char *arg); +void nosig (char *name); +void printsig (int sig); +void printsignals (FILE *fp); +int usage (int status); +int kill_verbose (char *procname, int pid, int sig); + +extern int *get_pids (char *, int); + +char version_string[] = "kill v2.0\n"; +char *whoami; + +int main (int argc, char *argv[]) +{ + int errors, numsig, pid; + char *ep, *arg; + int do_pid, do_kill, check_all; + int *pids, *ip; + + whoami = mybasename (*argv); + numsig = SIGTERM; + do_pid = (! strcmp (whoami, "pid")); + do_kill = 0; + check_all = 0; + + /* loop through the arguments. + actually, -a is the only option can be used with other options. + `kill' is basically a one-option-at-most program. */ + for (argc--, argv++; argc > 0; argc--, argv++) { + arg = *argv; + if (*arg != '-') { + break; + } + if (! strcmp (arg, "-u")) { + return usage (0); + } + if (! strcmp (arg, "-v")) { + fputs (version_string, stdout); + return 0; + } + if (! strcmp (arg, "-a")) { + check_all++; + continue; + } + if (! strcmp (arg, "-l")) { + if (argc < 2) { + printsignals (stdout); + return 0; + } + if (argc > 2) { + return usage (1); + } + /* argc == 2 */ + arg = argv[1]; + if ((numsig = arg_to_signum (arg)) < 0) { + fprintf (stderr, "%s: unknown signal %s\n", whoami, arg); + return 1; + } + printsig (numsig); + return 0; + } + if (! strcmp (arg, "-p")) { + do_pid++; + if (do_kill) + return usage (1); + continue; + } + if (! strcmp (arg, "-s")) { + if (argc < 2) { + return usage (1); + } + do_kill++; + if (do_pid) + return usage (1); + argc--, argv++; + arg = *argv; + if ((numsig = arg_to_signum (arg)) < 0) { + nosig (arg); + return 1; + } + continue; + } + /* `arg' begins with a dash but is not a known option. + so it's probably something like -HUP. + try to deal with it. */ + arg++; + if ((numsig = arg_to_signum (arg)) < 0) { + return usage (1); + } + do_kill++; + if (do_pid) + return usage (1); + continue; + } + + if (! *argv) { + return usage (1); + } + if (do_pid) { + numsig = -1; + } + + /* we're done with the options. + the rest of the arguments should be process ids and names. + kill them. */ + for (errors = 0; (arg = *argv) != NULL; argv++) { + pid = strtol (arg, &ep, 10); + if (! *ep) + errors += kill_verbose (arg, pid, numsig); + else { + pids = get_pids (arg, check_all); + if (! pids) { + errors++; + fprintf (stderr, "%s: can't find process \"%s\"\n", + whoami, arg); + continue; + } + for (ip = pids; *ip >= 0; ip++) + errors += kill_verbose (arg, *ip, numsig); + free (pids); + } + } + return (errors); +} + +char *mybasename (char *path) +{ + char *cp; + + cp = strrchr (path, '/'); + return (cp ? cp + 1 : path); +} + +int signame_to_signum (char *sig) +{ + int n; + + if (! strncasecmp (sig, "sig", 3)) + sig += 3; + for (n = 1; (n < NSIG) && (sys_signame[n] != NULL); n++) { + if (! strcasecmp (sys_signame[n], sig)) + return n; + } + return (-1); +} + +int arg_to_signum (char *arg) +{ + int numsig; + char *ep; + + if (isdigit (*arg)) { + numsig = strtol (arg, &ep, 10); + if (*ep != 0 || numsig < 0 || numsig >= NSIG) + return (-1); + return (numsig); + } + return (signame_to_signum (arg)); +} + +void nosig (char *name) +{ + fprintf (stderr, "%s: unknown signal %s; valid signals:\n", whoami, name); + printsignals (stderr); +} + +void printsig (int sig) +{ + printf ("%s\n", sys_signame[sig]); +} + +void printsignals (FILE *fp) +{ + int n; + + for (n = 1; (n < NSIG) && (sys_signame[n] != NULL); n++) { + fputs (sys_signame[n], fp); + if (n == (NSIG / 2) || n == (NSIG - 1)) + fputc ('\n', fp); + else + fputc (' ', fp); + } + if (n < (NSIG - 1)) + fputc ('\n', fp); +} + +int usage (int status) +{ + FILE *fp; + + fp = (status == 0 ? stdout : stderr); + fprintf (fp, "usage: %s [ -s signal | -p ] [ -a ] pid ...\n", whoami); + fprintf (fp, " %s -l [ signal ]\n", whoami); + return status; +} + +int kill_verbose (char *procname, int pid, int sig) +{ + if (sig < 0) { + printf ("%d\n", pid); + return 0; + } + if (kill (pid, sig) < 0) { + fprintf (stderr, "%s ", whoami); + perror (procname); + return 1; + } + return 0; +} diff --git a/misc-utils/logger.1 b/misc-utils/logger.1 new file mode 100644 index 00000000..0621dba1 --- /dev/null +++ b/misc-utils/logger.1 @@ -0,0 +1,100 @@ +.\" 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. +.\" +.\" @(#)logger.1 8.1 (Berkeley) 6/6/93 +.\" +.Dd June 6, 1993 +.Dt LOGGER 1 +.Os BSD 4.3 +.Sh NAME +.Nm logger +.Nd make entries in the system log +.Sh SYNOPSIS +.Nm logger +.Op Fl is +.Op Fl f Ar file +.Op Fl p Ar pri +.Op Fl t Ar tag +.Op Ar message ... +.Sh DESCRIPTION +.Nm Logger +provides a shell command interface to the +.Xr syslog 3 +system log module. +.Pp +Options: +.Pp +.Bl -tag -width "message" +.It Fl i +Log the process id of the logger process +with each line. +.It Fl s +Log the message to standard error, as well as the system log. +.It Fl f Ar file +Log the specified file. +.It Fl p Ar pri +Enter the message with the specified priority. +The priority may be specified numerically or as a ``facility.level'' +pair. +For example, ``\-p local3.info'' logs the message(s) as +.Ar info Ns rmational +level in the +.Ar local3 +facility. +The default is ``user.notice.'' +.It Fl t Ar tag +Mark every line in the log with the specified +.Ar tag . +.It Ar message +Write the message to log; if not specified, and the +.Fl f +flag is not +provided, standard input is logged. +.El +.Pp +The +.Nm logger +utility exits 0 on success, and >0 if an error occurs. +.Sh EXAMPLES +.Bd -literal -offset indent -compact +logger System rebooted + +logger \-p local0.notice \-t HOSTIDM \-f /dev/idmc +.Ed +.Sh SEE ALSO +.Xr syslog 3 , +.Xr syslogd 8 +.Sh STANDARDS +The +.Nm logger +command is expected to be +.St -p1003.2 +compatible. diff --git a/misc-utils/logger.c b/misc-utils/logger.c new file mode 100644 index 00000000..3fd3b6b2 --- /dev/null +++ b/misc-utils/logger.c @@ -0,0 +1,192 @@ +/* + * 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[] = "@(#)logger.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include + +#define SYSLOG_NAMES +#include + +int decode __P((char *, CODE *)); +int pencode __P((char *)); +void usage __P((void)); + +/* + * logger -- read and log utility + * + * Reads from an input and arranges to write the result on the system + * log. + */ +int +main(argc, argv) + int argc; + char *argv[]; +{ + int ch, logflags, pri; + char *tag, buf[1024]; + + tag = NULL; + pri = LOG_NOTICE; + logflags = 0; + while ((ch = getopt(argc, argv, "f:ip:st:")) != EOF) + switch((char)ch) { + case 'f': /* file to log */ + if (freopen(optarg, "r", stdin) == NULL) { + (void)fprintf(stderr, "logger: %s: %s.\n", + optarg, strerror(errno)); + exit(1); + } + break; + case 'i': /* log process id also */ + logflags |= LOG_PID; + break; + case 'p': /* priority */ + pri = pencode(optarg); + break; + case 's': /* log to standard error */ + logflags |= LOG_PERROR; + break; + case 't': /* tag */ + tag = optarg; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + /* setup for logging */ + openlog(tag ? tag : getlogin(), logflags, 0); + (void) fclose(stdout); + + /* log input line if appropriate */ + if (argc > 0) { + register char *p, *endp; + int len; + + for (p = buf, endp = buf + sizeof(buf) - 2; *argv;) { + len = strlen(*argv); + if (p + len > endp && p > buf) { + syslog(pri, "%s", buf); + p = buf; + } + if (len > sizeof(buf) - 1) + syslog(pri, "%s", *argv++); + else { + if (p != buf) + *p++ = ' '; + bcopy(*argv++, p, len); + *(p += len) = '\0'; + } + } + if (p != buf) + syslog(pri, "%s", buf); + } else + while (fgets(buf, sizeof(buf), stdin) != NULL) + syslog(pri, "%s", buf); + exit(0); +} + +/* + * Decode a symbolic name to a numeric value + */ +int +pencode(s) + register char *s; +{ + char *save; + int fac, lev; + + for (save = s; *s && *s != '.'; ++s); + if (*s) { + *s = '\0'; + fac = decode(save, facilitynames); + if (fac < 0) { + (void)fprintf(stderr, + "logger: unknown facility name: %s.\n", save); + exit(1); + } + *s++ = '.'; + } + else { + fac = 0; + s = save; + } + lev = decode(s, prioritynames); + if (lev < 0) { + (void)fprintf(stderr, + "logger: unknown priority name: %s.\n", save); + exit(1); + } + return ((lev & LOG_PRIMASK) | (fac & LOG_FACMASK)); +} + +int +decode(name, codetab) + char *name; + CODE *codetab; +{ + register CODE *c; + + if (isdigit(*name)) + return (atoi(name)); + + for (c = codetab; c->c_name; c++) + if (!strcasecmp(name, c->c_name)) + return (c->c_val); + + return (-1); +} + +void +usage() +{ + (void)fprintf(stderr, + "logger: [-is] [-f file] [-p pri] [-t tag] [ message ... ]\n"); + exit(1); +} diff --git a/misc-utils/look.1 b/misc-utils/look.1 new file mode 100644 index 00000000..872d4af7 --- /dev/null +++ b/misc-utils/look.1 @@ -0,0 +1,109 @@ +.\" 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. +.\" +.\" @(#)look.1 8.1 (Berkeley) 6/14/93 +.\" +.Dd June 14, 1993 +.Dt LOOK 1 +.Os +.Sh NAME +.Nm look +.Nd display lines beginning with a given string +.Sh SYNOPSIS +.Nm look +.Op Fl dfa +.Op Fl t Ar termchar +.Ar string +.Op Ar file +.Sh DESCRIPTION +The +.Nm look +utility displays any lines in +.Ar file +which contain +.Ar string +as a prefix. +As +.Nm look +performs a binary search, the lines in +.Ar file +must be sorted. +.Pp +If +.Ar file +is not specified, the file +.Pa /usr/dict/words +is used, only alphanumeric characters are compared and the case of +alphabetic characters is ignored. +.Pp +Options: +.Bl -tag -width Ds +.It Fl d +Dictionary character set and order, i.e. only alphanumeric characters +are compared. +.It Fl f +Ignore the case of alphabetic characters. +.It Fl a +Use the alternate dictionary +.Pa /usr/dict/web2 +.It Fl t +Specify a string termination character, i.e. only the characters +in +.Ar string +up to and including the first occurrence of +.Ar termchar +are compared. +.El +.Pp +The +.Nm look +utility exits 0 if one or more lines were found and displayed, +1 if no lines were found, and >1 if an error occurred. +.Sh FILES +.Bl -tag -width /usr/dict/words -compact +.It Pa /usr/dict/words +the dictionary +.It Pa /usr/dict/web2 +the alternate dictionary +.El +.Sh SEE ALSO +.Xr grep 1 , +.Xr sort 1 +.Sh COMPATIBILITY +The original manual page stated that tabs and blank characters participated +in comparisons when the +.Fl d +option was specified. +This was incorrect and the current man page matches the historic +implementation. +.Sh HISTORY +.Nm Look +appeared in Version 7 AT&T Unix. diff --git a/misc-utils/look.c b/misc-utils/look.c new file mode 100644 index 00000000..5a47970e --- /dev/null +++ b/misc-utils/look.c @@ -0,0 +1,365 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * David Hitz of Auspex Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 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) 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)look.c 8.1 (Berkeley) 6/14/93"; +#endif /* not lint */ + +/* + * look -- find lines in a sorted list. + * + * The man page said that TABs and SPACEs participate in -d comparisons. + * In fact, they were ignored. This implements historic practice, not + * the manual page. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "pathnames.h" + +/* + * FOLD and DICT convert characters to a normal form for comparison, + * according to the user specified flags. + * + * DICT expects integers because it uses a non-character value to + * indicate a character which should not participate in comparisons. + */ +#define EQUAL 0 +#define GREATER 1 +#define LESS (-1) +#define NO_COMPARE (-2) + +#define FOLD(c) (isascii(c) && isupper(c) ? tolower(c) : (c)) +#define DICT(c) (isascii(c) && isalnum(c) ? (c) : NO_COMPARE) + +int dflag, fflag; + +char *binary_search __P((char *, char *, char *)); +int compare __P((char *, char *, char *)); +void err __P((const char *fmt, ...)); +char *linear_search __P((char *, char *, char *)); +int look __P((char *, char *, char *)); +void print_from __P((char *, char *, char *)); + +static void usage __P((void)); + +main(argc, argv) + int argc; + char *argv[]; +{ + struct stat sb; + int ch, fd, termchar; + char *back, *file, *front, *string, *p; + + file = _PATH_WORDS; + termchar = '\0'; + while ((ch = getopt(argc, argv, "adft:")) != EOF) + switch(ch) { + case 'a': + file = _PATH_WORDS_ALT; + break; + case 'd': + dflag = 1; + break; + case 'f': + fflag = 1; + break; + case 't': + termchar = *optarg; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + switch (argc) { + case 2: /* Don't set -df for user. */ + string = *argv++; + file = *argv; + break; + case 1: /* But set -df by default. */ + dflag = fflag = 1; + string = *argv; + break; + default: + usage(); + } + + if (termchar != '\0' && (p = strchr(string, termchar)) != NULL) + *++p = '\0'; + + if ((fd = open(file, O_RDONLY, 0)) < 0 || fstat(fd, &sb)) + err("%s: %s", file, strerror(errno)); + if ((void *)(front = mmap(NULL, + (size_t)sb.st_size, + PROT_READ, + MAP_FILE|MAP_SHARED, + fd, + (off_t)0)) <= (void *)0) + err("%s: %s", file, strerror(errno)); + back = front + sb.st_size; + exit(look(string, front, back)); +} + +look(string, front, back) + char *string, *front, *back; +{ + register int ch; + register char *readp, *writep; + + /* Reformat string string to avoid doing it multiple times later. */ + for (readp = writep = string; ch = *readp++;) { + if (fflag) + ch = FOLD(ch); + if (dflag) + ch = DICT(ch); + if (ch != NO_COMPARE) + *(writep++) = ch; + } + *writep = '\0'; + + front = binary_search(string, front, back); + front = linear_search(string, front, back); + + if (front) + print_from(string, front, back); + return (front ? 0 : 1); +} + + +/* + * Binary search for "string" in memory between "front" and "back". + * + * This routine is expected to return a pointer to the start of a line at + * *or before* the first word matching "string". Relaxing the constraint + * this way simplifies the algorithm. + * + * Invariants: + * front points to the beginning of a line at or before the first + * matching string. + * + * back points to the beginning of a line at or after the first + * matching line. + * + * Base of the Invariants. + * front = NULL; + * back = EOF; + * + * Advancing the Invariants: + * + * p = first newline after halfway point from front to back. + * + * If the string at "p" is not greater than the string to match, + * p is the new front. Otherwise it is the new back. + * + * Termination: + * + * The definition of the routine allows it return at any point, + * since front is always at or before the line to print. + * + * In fact, it returns when the chosen "p" equals "back". This + * implies that there exists a string is least half as long as + * (back - front), which in turn implies that a linear search will + * be no more expensive than the cost of simply printing a string or two. + * + * Trying to continue with binary search at this point would be + * more trouble than it's worth. + */ +#define SKIP_PAST_NEWLINE(p, back) \ + while (p < back && *p++ != '\n'); + +char * +binary_search(string, front, back) + register char *string, *front, *back; +{ + register char *p; + + p = front + (back - front) / 2; + SKIP_PAST_NEWLINE(p, back); + + /* + * If the file changes underneath us, make sure we don't + * infinitely loop. + */ + while (p < back && back > front) { + if (compare(string, p, back) == GREATER) + front = p; + else + back = p; + p = front + (back - front) / 2; + SKIP_PAST_NEWLINE(p, back); + } + return (front); +} + +/* + * Find the first line that starts with string, linearly searching from front + * to back. + * + * Return NULL for no such line. + * + * This routine assumes: + * + * o front points at the first character in a line. + * o front is before or at the first line to be printed. + */ +char * +linear_search(string, front, back) + char *string, *front, *back; +{ + while (front < back) { + switch (compare(string, front, back)) { + case EQUAL: /* Found it. */ + return (front); + break; + case LESS: /* No such string. */ + return (NULL); + break; + case GREATER: /* Keep going. */ + break; + } + SKIP_PAST_NEWLINE(front, back); + } + return (NULL); +} + +/* + * Print as many lines as match string, starting at front. + */ +void +print_from(string, front, back) + register char *string, *front, *back; +{ + for (; front < back && compare(string, front, back) == EQUAL; ++front) { + for (; front < back && *front != '\n'; ++front) + if (putchar(*front) == EOF) + err("stdout: %s", strerror(errno)); + if (putchar('\n') == EOF) + err("stdout: %s", strerror(errno)); + } +} + +/* + * Return LESS, GREATER, or EQUAL depending on how the string1 compares with + * string2 (s1 ??? s2). + * + * o Matches up to len(s1) are EQUAL. + * o Matches up to len(s2) are GREATER. + * + * Compare understands about the -f and -d flags, and treats comparisons + * appropriately. + * + * The string "s1" is null terminated. The string s2 is '\n' terminated (or + * "back" terminated). + */ +int +compare(s1, s2, back) + register char *s1, *s2, *back; +{ + register int ch; + + for (; *s1 && s2 < back && *s2 != '\n';) { + ch = *s2; + if (fflag) + ch = FOLD(ch); + if (dflag) + ch = DICT(ch); + + if (ch == NO_COMPARE) { + ++s2; /* Ignore character in comparison. */ + continue; + } + if (*s1 != ch) + return (*s1 < ch ? LESS : GREATER); + ++s1; + ++s2; + } + return (*s1 ? GREATER : EQUAL); +} + +static void +usage() +{ + (void)fprintf(stderr, "usage: look [-dfa] [-t char] string [file]\n"); + exit(2); +} + +#if __STDC__ +#include +#else +#include +#endif + +void +#if __STDC__ +err(const char *fmt, ...) +#else +err(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)fprintf(stderr, "look: "); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fprintf(stderr, "\n"); + exit(2); + /* NOTREACHED */ +} diff --git a/misc-utils/mcookie.1 b/misc-utils/mcookie.1 new file mode 100644 index 00000000..39508254 --- /dev/null +++ b/misc-utils/mcookie.1 @@ -0,0 +1,17 @@ +.\" mcookie.1 -- +.\" Public Domain 1995 Rickard E. Faith (faith@cs.unc.edu) +.TH MCOOKIE 1 "12 Feb 1995" "" "Linux Programmer's Manual" +.SH NAME +mcookie \- generate magic cookies for xauth +.SH SYNOPSIS +.B mcookie +.SH DESCRIPTION +.B mcookie +generates a 128-bit random hexadecimal number for use with the X authority +system. Typical usage: +.RS +xauth add :0 . `mcookie` +.RE +.SH "SEE ALSO" +.BR X (1), +.BR xauth (1) diff --git a/misc-utils/mcookie.c b/misc-utils/mcookie.c new file mode 100644 index 00000000..d0730edd --- /dev/null +++ b/misc-utils/mcookie.c @@ -0,0 +1,44 @@ +/* mcookie.c -- Generates random numbers for xauth + * Created: Fri Feb 3 10:42:48 1995 by faith@cs.unc.edu + * Revised: Sun Feb 12 20:29:58 1995 by faith@cs.unc.edu + * Public Domain 1995 Rickard E. Faith (faith@cs.unc.edu) + * This program comes with ABSOLUTELY NO WARRANTY. + * + * mcookie.c,v 1.1.1.1 1995/02/22 19:09:16 faith Exp + */ + +#define SECURE 1 + +#include +#include +#if SECURE +#include +#include +#endif + +int main( void ) +{ + int i; +#if SECURE + struct timeval tv; + struct timezone tz; + + gettimeofday( &tv, &tz ); + srand( tv.tv_sec + tv.tv_usec ); +#else + long int t; + + time( &t ); + srand( t ); +#endif + + for (i = 0; i < 32; i++) { + int r = (rand() & 0x0f0) >> 4; + + if (r < 10) putchar( '0' + r ); + else putchar( 'a' + r - 10 ); + } + putchar ( '\n' ); + + return 0; +} diff --git a/misc-utils/md5.c b/misc-utils/md5.c new file mode 100644 index 00000000..005568b5 --- /dev/null +++ b/misc-utils/md5.c @@ -0,0 +1,253 @@ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ +#include /* for memcpy() */ +#include "md5.h" + +#ifndef HIGHFIRST +#define byteReverse(buf, len) /* Nothing */ +#else +void byteReverse(unsigned char *buf, unsigned longs); + +#ifndef ASM_MD5 +/* + * Note: this code is harmless on little-endian machines. + */ +void byteReverse(unsigned char *buf, unsigned longs) +{ + uint32 t; + do { + t = (uint32) ((unsigned) buf[3] << 8 | buf[2]) << 16 | + ((unsigned) buf[1] << 8 | buf[0]); + *(uint32 *) buf = t; + buf += 4; + } while (--longs); +} +#endif +#endif + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void MD5Init(struct MD5Context *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len) +{ + uint32 t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((uint32) len << 3)) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if (t) { + unsigned char *p = (unsigned char *) ctx->in + t; + + t = 64 - t; + if (len < t) { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32 *) ctx->in); + buf += t; + len -= t; + } + /* Process data in 64-byte chunks */ + + while (len >= 64) { + memcpy(ctx->in, buf, 64); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32 *) ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void MD5Final(unsigned char digest[16], struct MD5Context *ctx) +{ + unsigned count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32 *) ctx->in); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(p, 0, count - 8); + } + byteReverse(ctx->in, 14); + + /* Append length in bits and transform */ + ((uint32 *) ctx->in)[14] = ctx->bits[0]; + ((uint32 *) ctx->in)[15] = ctx->bits[1]; + + MD5Transform(ctx->buf, (uint32 *) ctx->in); + byteReverse((unsigned char *) ctx->buf, 4); + memcpy(digest, ctx->buf, 16); + memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ +} + +#ifndef ASM_MD5 + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +void MD5Transform(uint32 buf[4], uint32 const in[16]) +{ + register uint32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +#endif + diff --git a/misc-utils/md5.h b/misc-utils/md5.h new file mode 100644 index 00000000..e264f686 --- /dev/null +++ b/misc-utils/md5.h @@ -0,0 +1,27 @@ +#ifndef MD5_H +#define MD5_H + +#ifdef __alpha +typedef unsigned int uint32; +#else +typedef unsigned long uint32; +#endif + +struct MD5Context { + uint32 buf[4]; + uint32 bits[2]; + unsigned char in[64]; +}; + +void MD5Init(struct MD5Context *context); +void MD5Update(struct MD5Context *context, unsigned char const *buf, + unsigned len); +void MD5Final(unsigned char digest[16], struct MD5Context *context); +void MD5Transform(uint32 buf[4], uint32 const in[16]); + +/* + * This is needed to make RSAREF happy on some MS-DOS compilers. + */ +typedef struct MD5Context MD5_CTX; + +#endif /* !MD5_H */ diff --git a/misc-utils/md5sum.1 b/misc-utils/md5sum.1 new file mode 100644 index 00000000..86094b27 --- /dev/null +++ b/misc-utils/md5sum.1 @@ -0,0 +1,64 @@ +.\" md5sum.1 -- +.\" Public Domain 1995 Rik Faith (faith@cs.unc.edu) +.\" Revised: Sat Feb 11 12:16:48 1995 by faith@cs.unc.edu +.\" " +.TH MD5SUM 1 "11 February 1995" "Linux 1.0" "Linux Programmer's Manual" +.SH NAME +md5sum \- generate/check MD5 message digests +.SH SYNOPSIS +.BR "md5sum [" \-bv "] [" \-c +.BR "[ " file " ] ]" +.br +.BR "md5sum " file " ..." +.SH DESCRIPTION +.B md5sum +generates and checks MD5 message digests, as described in RFC-1321. The +"message digest" produced can be thought of as a 128-bit "signature" of the +input file. Typically, +.B md5sum +is used to verify the integrity of files made available for distribution +via anonymous ftp (for example, announcements for new versions of +.BR irc(1) +usually contain MD5 signatures). +.P +Message digests for a tree of files can be generated with a command similar +to the following: +.RS +.sp +find . -type f -print | xargs md5sum +.sp +.RE +The output of this command is suitable as input for the +.B \-c +option. +.SH OPTIONS +.TP +.BI "\-c [" file "]" +Check message digests. Input is taken from +.B stdin +or from the spcified +.IR file . +The input should be in the same format as the output generated by +.BR md5sum . +.TP +.B \-v +Verbose. Print file names when checking. +.TP +.B \-b +Read files in binary mode (otherwise, end-of-file conventions will be +ignored). +.SH HISTOY +The +.B md5sum +program was written by Branko Lankester and may be freely distributed. The +original source code is in the MIT PGP 2.6.2 distribution. Those concerned +about the integrity of this version should obtain the original sources and +compile their own version. +.PP +The underlying implementation of Ron Rivest's MD5 algorithm was written by +Colin Plumb and is in the Public Domain. (Equivalent code is also +available from RSA Data Security, Inc.) +.SH "SEE ALSO" +.BR sum (1), +.BR cksum (1), +.BR pgp (1) diff --git a/misc-utils/md5sum.c b/misc-utils/md5sum.c new file mode 100644 index 00000000..e0b1dc9c --- /dev/null +++ b/misc-utils/md5sum.c @@ -0,0 +1,243 @@ +/* + * md5sum.c - Generate/check MD5 Message Digests + * + * Compile and link with md5.c. If you don't have getopt() in your library + * also include getopt.c. For MSDOS you can also link with the wildcard + * initialization function (wildargs.obj for Turbo C and setargv.obj for MSC) + * so that you can use wildcards on the commandline. + * + * Written March 1993 by Branko Lankester + * Modified June 1993 by Colin Plumb for altered md5.c. + */ +#include +#include +#include "md5.h" + +#ifdef UNIX +#define FOPRTXT "r" +#define FOPRBIN "r" +#else +#ifdef VMS +#define FOPRTXT "r","ctx=stm" +#define FOPRBIN "rb","ctx=stm" +#else +#define FOPRTXT "r" +#define FOPRBIN "rb" +#endif +#endif + +extern char *optarg; +extern int optind; + +void usage(); +void print_digest(); +int mdfile(FILE *fp, unsigned char *digest); +int do_check(FILE *chkf); + +char *progname; +int verbose = 0; +int bin_mode = 0; + +void +main(int argc, char **argv) +{ + int opt, rc = 0; + int check = 0; + FILE *fp; + unsigned char digest[16]; + + progname = *argv; + while ((opt = getopt(argc, argv, "cbvp:h")) != EOF) { + switch (opt) { + case 'c': check = 1; break; + case 'v': verbose = 1; break; + case 'b': bin_mode = 1; break; + default: usage(); + } + } + argc -= optind; + argv += optind; + if (check) { + switch (argc) { + case 0: fp = stdin; break; + case 1: if ((fp = fopen(*argv, FOPRTXT)) == NULL) { + perror(*argv); + exit(2); + } + break; + default: usage(); + } + exit(do_check(fp)); + } + if (argc == 0) { + if (mdfile(stdin, digest)) { + fprintf(stderr, "%s: read error on stdin\n", progname); + exit(2); + } + print_digest(digest); + printf("\n"); + exit(0); + } + for ( ; argc > 0; --argc, ++argv) { + if (bin_mode) + fp = fopen(*argv, FOPRBIN); + else + fp = fopen(*argv, FOPRTXT); + if (fp == NULL) { + perror(*argv); + rc = 2; + continue; + } + if (mdfile(fp, digest)) { + fprintf(stderr, "%s: error reading %s\n", progname, *argv); + rc = 2; + } else { + print_digest(digest); + printf(" %c%s\n", bin_mode ? '*' : ' ', *argv); + } + fclose(fp); + } + exit(rc); +} + +void +usage() +{ + fprintf(stderr, "usage: md5sum [-bv] [-c [file]] | [file...]\n"); + fprintf(stderr, "Generates or checks MD5 Message Digests\n"); + fprintf(stderr, " -c check message digests (default is generate)\n"); + fprintf(stderr, " -v verbose, print file names when checking\n"); + fprintf(stderr, " -b read files in binary mode\n"); + fprintf(stderr, "The input for -c should be the list of message digests and file names\n"); + fprintf(stderr, "that is printed on stdout by this program when it generates digests.\n"); + exit(2); +} + +int +mdfile(FILE *fp, unsigned char *digest) +{ + unsigned char buf[1024]; + MD5_CTX ctx; + int n; + + MD5Init(&ctx); + while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) + MD5Update(&ctx, buf, n); + MD5Final(digest, &ctx); + if (ferror(fp)) + return -1; + return 0; +} + +void +print_digest(unsigned char *p) +{ + int i; + + for (i = 0; i < 16; ++i) + printf("%02x", *p++); +} + +int +hex_digit(int c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + return -1; +} + +int +get_md5_line(FILE *fp, unsigned char *digest, char *file) +{ + char buf[1024]; + int i, d1, d2, rc; + char *p = buf; + + if (fgets(buf, sizeof(buf), fp) == NULL) + return -1; + + for (i = 0; i < 16; ++i) { + if ((d1 = hex_digit(*p++)) == -1) + return 0; + if ((d2 = hex_digit(*p++)) == -1) + return 0; + *digest++ = d1*16 + d2; + } + if (*p++ != ' ') + return 0; + /* + * next char is an attribute char, space means text file + * if it's a '*' the file should be checked in binary mode. + */ + if (*p == ' ') + rc = 1; + else if (*p == '*') + rc = 2; + else { + fprintf(stderr, "%s: unrecognized line: %s", progname, buf); + return 0; + } + ++p; + i = strlen(p); + if (i < 2 || i > 255) + return 0; + p[i-1] = '\0'; + strcpy(file, p); + return rc; +} + +int +do_check(FILE *chkf) +{ + int rc, ex = 0, failed = 0, checked = 0; + unsigned char chk_digest[16], file_digest[16]; + char filename[256]; + FILE *fp; + int flen = 14; + + while ((rc = get_md5_line(chkf, chk_digest, filename)) >= 0) { + if (rc == 0) /* not an md5 line */ + continue; + if (verbose) { + if (strlen(filename) > flen) + flen = strlen(filename); + fprintf(stderr, "%-*s ", flen, filename); + } + if (bin_mode || rc == 2) + fp = fopen(filename, FOPRBIN); + else + fp = fopen(filename, FOPRTXT); + if (fp == NULL) { + fprintf(stderr, "%s: can't open %s\n", progname, filename); + ex = 2; + continue; + } + if (mdfile(fp, file_digest)) { + fprintf(stderr, "%s: error reading %s\n", progname, filename); + ex = 2; + fclose(fp); + continue; + } + fclose(fp); + if (memcmp(chk_digest, file_digest, 16) != 0) { + if (verbose) + fprintf(stderr, "FAILED\n"); + else + fprintf(stderr, "%s: MD5 check failed for '%s'\n", progname, filename); + ++failed; + } else if (verbose) + fprintf(stderr, "OK\n"); + ++checked; + } + if (verbose && failed) + fprintf(stderr, "%s: %d of %d file(s) failed MD5 check\n", progname, failed, checked); + if (!checked) { + fprintf(stderr, "%s: no files checked\n", progname); + return 3; + } + if (!ex && failed) + ex = 1; + return ex; +} diff --git a/misc-utils/namei.1 b/misc-utils/namei.1 new file mode 100644 index 00000000..348a3789 --- /dev/null +++ b/misc-utils/namei.1 @@ -0,0 +1,60 @@ +.\" +.\" Version 1.4 of namei +.\" +.TH NAMEI 1 "Local" +.SH NAME +namei - follow a pathname until a terminal point is found +.SH SYNOPSIS +.B namei +.I [-mx] +.I pathname +.I "[ pathname ... ]" +.SH DESCRIPTION +.I Namei +uses its arguments as pathnames to any type +of Unix file (symlinks, files, directories, and so forth). +.I Namei +then follows each pathname until a terminal +point is found (a file, directory, char device, etc). +If it finds a symbolic link, we show the link, and start +following it, indenting the output to show the context. +.PP +This program is useful for finding a "too many levels of +symbolic links" problems. +.PP +For each line output, +.I namei +outputs a the following characters to identify the file types found: +.LP +.nf + f: = the pathname we are currently trying to resolve + d = directory + l = symbolic link (both the link and it's contents are output) + s = socket + b = block device + c = character device + - = regular file + ? = an error of some kind +.fi +.PP +.I Namei +prints an informative message when +the maximum number of symbolic links this system can have has been exceeded. +.SH OPTIONS +.TP 8 +.B -x +Show mount point directories with a 'D', rather than a 'd'. +.TP 8 +.B -m +Show the mode bits of each file type in the style of ls(1), +for example 'rwxr-xr-x'. +.SH AUTHOR +Roger Southwick (rogers@amadeus.wr.tek.com) +.SH BUGS +To be discovered. +.SH CAVEATS +.I Namei +will follow an infinite loop of symbolic links forever. To escape, use +SIGINT (usually ^C). +.SH "SEE ALSO" +ls(1), stat(1) diff --git a/misc-utils/namei.c b/misc-utils/namei.c new file mode 100644 index 00000000..0424af0b --- /dev/null +++ b/misc-utils/namei.c @@ -0,0 +1,341 @@ +/*------------------------------------------------------------- + +The namei program + +By: Roger S. Southwick + +May 2, 1990 + + +Modifications by Steve Tell March 28, 1991 + +usage: namei pathname [pathname ... ] + +This program reads it's arguments as pathnames to any type +of Unix file (symlinks, files, directories, and so forth). +The program then follows each pathname until a terminal +point is found (a file, directory, char device, etc). +If it finds a symbolic link, we show the link, and start +following it, indenting the output to show the context. + +This program is useful for finding a "too many levels of +symbolic links" problems. + +For each line output, the program puts a file type first: + + f: = the pathname we are currently trying to resolve + d = directory + D = directory that is a mount point + l = symbolic link (both the link and it's contents are output) + s = socket + b = block device + c = character device + - = regular file + ? = an error of some kind + +The program prints an informative messages when we exceed +the maximum number of symbolic links this system can have. + +The program exits with a 1 status ONLY if it finds it cannot +chdir to /, or if it encounters an unknown file type. + +-------------------------------------------------------------*/ + +#ifndef lint +static char *RCSid = "namei.c,v 1.1.1.1 1995/02/22 19:09:16 faith Exp"; +#endif + +#include +#include +#include +#include + +extern char *sys_errlist[]; +extern int errno; +#define ERR sys_errlist[errno],errno + +int symcount; +int mflag = 0; +int xflag = 0; + +#ifndef MAXSYMLINKS +#define MAXSYMLINKS 256 +#endif + +static char *pperm(); + +main(argc, argv) +int argc; +char *argv[]; +{ + void namei(), usage(); + char *getwd(); + int getopt(); + extern int optind; + register int c; + char curdir[MAXPATHLEN]; + + if(argc < 2) + usage(); + + while((c = getopt(argc, argv, "mx")) != EOF){ + switch(c){ + case 'm': + mflag = !mflag; + break; + + case 'x': + xflag = !xflag; + break; + + case '?': + default: + usage(); + } + } + + if(getwd(curdir) == NULL){ + (void)fprintf(stderr, "namei: unable to get current directory - %s\n", curdir); + exit(1); + } + + + for(; optind < argc; optind++){ + (void)printf("f: %s\n", argv[optind]); + symcount = 1; + namei(argv[optind], 0); + + if(chdir(curdir) == -1){ + (void)fprintf(stderr, "namei: unable to chdir to %s - %s (%d)\n", curdir, ERR); + exit(1); + } + } + exit(0); +} + +void +usage() +{ + (void)fprintf(stderr,"usage: namei [-mx] pathname [pathname ...]\n"); + exit(1); +} + +#ifndef NODEV +#define NODEV (dev_t)(-1) +#endif + +void +namei(file, lev) + +register char *file; +register int lev; +{ + register char *cp; + char buf[BUFSIZ], sym[BUFSIZ]; + struct stat stb; + register int i; + dev_t lastdev = NODEV; + + /* + * See if the file has a leading /, and if so cd to root + */ + + if(*file == '/'){ + while(*file == '/') + file++; + + if(chdir("/") == -1){ + (void)fprintf(stderr,"namei: could not chdir to root!\n"); + exit(1); + } + for(i = 0; i < lev; i++) + (void)printf(" "); + + if(stat("/", &stb) == -1){ + (void)fprintf(stderr, "namei: could not stat root!\n"); + exit(1); + } + lastdev = stb.st_dev; + + if(mflag) + (void)printf(" d%s /\n", pperm(stb.st_mode)); + else + (void)printf(" d /\n"); + } + + for(;;){ + + /* + * Copy up to the next / (or nil) into buf + */ + + for(cp = buf; *file != '\0' && *file != '/'; cp++, file++) + *cp = *file; + + while(*file == '/') /* eat extra /'s */ + file++; + + *cp = '\0'; + + if(buf[0] == '\0'){ + + /* + * Buf is empty, so therefore we are done + * with this level of file + */ + + return; + } + + for(i = 0; i < lev; i++) + (void)printf(" "); + + /* + * See what type of critter this file is + */ + + if(lstat(buf, &stb) == -1){ + (void)printf(" ? %s - %s (%d)\n", buf, ERR); + return; + } + + switch(stb.st_mode & S_IFMT){ + case S_IFDIR: + + /* + * File is a directory, chdir to it + */ + + if(chdir(buf) == -1){ + (void)printf(" ? could not chdir into %s - %s (%d)\n", buf, ERR ); + return; + } + if(xflag && lastdev != stb.st_dev && lastdev != NODEV){ + /* Across mnt point */ + if(mflag) + (void)printf(" D%s %s\n", pperm(stb.st_mode), buf); + else + (void)printf(" D %s\n", buf); + } + else { + if(mflag) + (void)printf(" d%s %s\n", pperm(stb.st_mode), buf); + else + (void)printf(" d %s\n", buf); + } + lastdev = stb.st_dev; + + (void)fflush(stdout); + break; + + case S_IFLNK: + /* + * Sigh, another symlink. Read it's contents and + * call namei() + */ + + bzero(sym, BUFSIZ); + if(readlink(buf, sym, BUFSIZ) == -1){ + (void)printf(" ? problems reading symlink %s - %s (%d)\n", buf, ERR); + return; + } + + if(mflag) + (void)printf(" l%s %s -> %s", pperm(stb.st_mode), buf, sym); + else + (void)printf(" l %s -> %s", buf, sym); + + if(symcount > 0 && symcount++ > MAXSYMLINKS){ + (void)printf(" *** EXCEEDED UNIX LIMIT OF SYMLINKS ***"); + symcount = -1; + } + (void)printf("\n"); + namei(sym, lev + 1); + break; + + case S_IFCHR: + if(mflag) + (void)printf(" c%s %s\n", pperm(stb.st_mode), buf); + else + (void)printf(" c %s\n", buf); + break; + + case S_IFBLK: + if(mflag) + (void)printf(" b%s %s\n", pperm(stb.st_mode), buf); + else + (void)printf(" b %s\n", buf); + break; + + case S_IFSOCK: + if(mflag) + (void)printf(" s%s %s\n", pperm(stb.st_mode), buf); + else + (void)printf(" s %s\n", buf); + break; + + case S_IFREG: + if(mflag) + (void)printf(" -%s %s\n", pperm(stb.st_mode), buf); + else + (void)printf(" - %s\n", buf); + break; + + default: + (void)fprintf(stderr,"namei: unknown file type 0%06o on file %s\n", stb.st_mode, buf ); + exit(1); + + } + } +} + +/* Take a + * Mode word, as from a struct stat, and return + * a pointer to a static string containing a printable version like ls. + * For example 0755 produces "rwxr-xr-x" + */ +static char * +pperm(mode) +unsigned short mode; +{ + unsigned short m; + static char buf[16]; + char *bp; + char *lschars = "xwrxwrxwr"; /* the complete string backwards */ + char *cp; + int i; + + for(i = 0, cp = lschars, m = mode, bp = &buf[8]; + i < 9; + i++, cp++, m >>= 1, bp--) { + + if(m & 1) + *bp = *cp; + else + *bp = '-'; + } + buf[9] = '\0'; + + if(mode & S_ISUID) { + if(buf[2] == 'x') + buf[2] = 's'; + else + buf[2] = 'S'; + } + if(mode & S_ISGID) { + if(buf[5] == 'x') + buf[5] = 's'; + else + buf[5] = 'S'; + } + if(mode & S_ISVTX) { + if(buf[8] == 'x') + buf[8] = 't'; + else + buf[8] = 'T'; + } + + return &buf[0]; +} + + diff --git a/misc-utils/procs.c b/misc-utils/procs.c new file mode 100644 index 00000000..1d232415 --- /dev/null +++ b/misc-utils/procs.c @@ -0,0 +1,113 @@ +/* + * procs.c -- functions to parse the linux /proc filesystem. + * (c) 1994 salvatore valente + * + * this program is free software. you can redistribute it and + * modify it under the terms of the gnu general public license. + * there is no warranty. + * + * faith + * 1.2 + * 1995/02/23 01:20:40 + * + */ + +#define _POSIX_SOURCE 1 + +#include +#include +#include +#include +#include +#include +#include +#include + +extern char *mybasename (char *); +static char *parse_parens (char *buf); + +int *get_pids (char *process_name, int get_all) +{ + DIR *dir; + struct dirent *ent; + int status; + char *dname, fname[100], *cp, buf[256]; + struct stat st; + uid_t uid; + FILE *fp; + int pid, *pids, num_pids, pids_size; + + dir = opendir ("/proc"); + if (! dir) { + perror ("opendir /proc"); + return NULL; + } + uid = getuid (); + pids = NULL; + num_pids = pids_size = 0; + + while ((ent = readdir (dir)) != NULL) { + dname = ent->d_name; + if (! isdigit (*dname)) continue; + pid = atoi (dname); + sprintf (fname, "/proc/%d/cmdline", pid); + /* get the process owner */ + status = stat (fname, &st); + if (status != 0) continue; + if (! get_all && uid != st.st_uid) continue; + /* get the command line */ + fp = fopen (fname, "r"); + if (! fp) continue; + cp = fgets (buf, sizeof (buf), fp); + fclose (fp); + /* an empty command line means the process is swapped out */ + if (! cp || ! *cp) { + /* get the process name from the statfile */ + sprintf (fname, "/proc/%d/stat", pid); + fp = fopen (fname, "r"); + if (! fp) continue; + cp = fgets (buf, sizeof (buf), fp); + if (cp == NULL) continue; + fclose (fp); + cp = parse_parens (buf); + if (cp == NULL) continue; + } + /* ok, we got the process name. */ + if (strcmp (process_name, mybasename (cp))) continue; + while (pids_size < num_pids + 2) { + pids_size += 5; + pids = (int *) realloc (pids, sizeof (int) * pids_size); + } + pids[num_pids++] = pid; + pids[num_pids] = -1; + } + closedir (dir); + return (pids); +} + +/* + * parse_parens () -- return an index just past the first open paren in + * buf, and terminate the string at the matching close paren. + */ +static char *parse_parens (char *buf) +{ + char *cp, *ip; + int depth; + + cp = strchr (buf, '('); + if (cp == NULL) return NULL; + cp++; + depth = 1; + for (ip = cp; *ip; ip++) { + if (*ip == '(') + depth++; + if (*ip == ')') { + depth--; + if (depth == 0) { + *ip = 0; + break; + } + } + } + return cp; +} diff --git a/misc-utils/reset.1 b/misc-utils/reset.1 new file mode 100644 index 00000000..06ce4b2b --- /dev/null +++ b/misc-utils/reset.1 @@ -0,0 +1,29 @@ +.\" Copyright 1992 Rickard E. Faith (faith@cs.unc.edu) +.\" May be distributed under the GNU General Public License +.TH RESET 1 "10 October 1993" "Linux 0.99" "Linux Programmer's Manual" +.SH NAME +reset \- reset the terminal +.SH SYNOPSIS +.BR clear +.SH DESCRIPTION +.B reset +calls +.BR tput (1) +with the +.IR clear , rmacs , rmm , rmul , rs1 , rs2 ", and " rs3 +arguments. This causes +.B tput +to send appropriate reset strings to the terminal based on information in +.IR /etc/termcap . +This sequence seems to be sufficient to reset the Linux VC's when they +start printing "funny-looking" characters. For good measure, +.BR stty (1) +is called with the +.I sane +argument in an attempt to get cooked mode back. +.SH "SEE ALSO" +.BR reset (1), +.BR stty (1), +.BR tput (1) +.SH AUTHOR +Rik Faith (faith@cs.unc.edu) diff --git a/misc-utils/reset.sh b/misc-utils/reset.sh new file mode 100644 index 00000000..92d25390 --- /dev/null +++ b/misc-utils/reset.sh @@ -0,0 +1,13 @@ +#!/bin/sh +stty sane +tput clear +tput rmacs +tput rmm +tput rmso +tput rmul +tput rs1 +tput rs2 +tput rs3 +bot=$[ ${LINES:-`tput lines`} - 1 ] +if test "$bot" -le "0"; then bot=24; fi +tput csr 0 $bot diff --git a/misc-utils/script.1 b/misc-utils/script.1 new file mode 100644 index 00000000..ddc35223 --- /dev/null +++ b/misc-utils/script.1 @@ -0,0 +1,123 @@ +.\" Copyright (c) 1980, 1990 Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)script.1 6.5 (Berkeley) 7/27/91 +.\" +.Dd July 27, 1991 +.Dt SCRIPT 1 +.Os BSD 4 +.Sh NAME +.Nm script +.Nd make typescript of terminal session +.Sh SYNOPSIS +.Nm script +.Op Fl a +.Op Ar file +.Sh DESCRIPTION +.Nm Script +makes a typescript of everything printed on your terminal. +It is useful for students who need a hardcopy record of an interactive +session as proof of an assignment, as the typescript file +can be printed out later with +.Xr lpr 1 . +.Pp +If the argument +.Ar file +is given, +.Nm +saves all dialogue in +.Ar file . +If no file name is given, the typescript is saved in the file +.Pa typescript . +.Pp +Option: +.Bl -tag -width Ds +.It Fl a +Append the output to +.Ar file +or +.Pa typescript , +retaining the prior contents. +.El +.Pp +The script ends when the forked shell exits (a +.Em control-D +to exit +the Bourne shell +.Pf ( Xr sh 1 ) , +and +.Em exit , +.Em logout +or +.Em control-d +(if +.Em ignoreeof +is not set) for the +C-shell, +.Xr csh 1 ) . +.Pp +Certain interactive commands, such as +.Xr vi 1 , +create garbage in the typescript file. +.Nm Script +works best with commands that do not manipulate the +screen, the results are meant to emulate a hardcopy +terminal. +.Sh ENVIRONMENT +The following environment variable is utilized by +.Nm script : +.Bl -tag -width SHELL +.It Ev SHELL +If the variable +.Ev SHELL +exists, the shell forked by +.Nm script +will be that shell. If +.Ev SHELL +is not set, the Bourne shell +is assumed. (Most shells set this variable automatically). +.El +.Sh SEE ALSO +.Xr csh 1 +(for the +.Em history +mechanism). +.Sh HISTORY +The +.Nm script +command appeared in +.Bx 3.0 . +.Sh BUGS +.Nm Script +places +.Sy everything +in the log file, including linefeeds and backspaces. +This is not what the naive user expects. diff --git a/misc-utils/script.c b/misc-utils/script.c new file mode 100644 index 00000000..d1e8b1e9 --- /dev/null +++ b/misc-utils/script.c @@ -0,0 +1,294 @@ +/* + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE 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) 1980 Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)script.c 5.13 (Berkeley) 3/5/91"; +#endif /* not lint */ + +/* + * script + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef linux +#include +#include +#endif + +char *shell; +FILE *fscript; +int master; +int slave; +int child; +int subchild; +char *fname; + +struct termios tt; +struct winsize win; +int lb; +int l; +char line[] = "/dev/ptyXX"; +int aflg; + +main(argc, argv) + int argc; + char *argv[]; +{ + extern char *optarg; + extern int optind; + int ch; + void finish(); + char *getenv(); + + while ((ch = getopt(argc, argv, "a")) != EOF) + switch((char)ch) { + case 'a': + aflg++; + break; + case '?': + default: + fprintf(stderr, "usage: script [-a] [file]\n"); + exit(1); + } + argc -= optind; + argv += optind; + + if (argc > 0) + fname = argv[0]; + else + fname = "typescript"; + if ((fscript = fopen(fname, aflg ? "a" : "w")) == NULL) { + perror(fname); + fail(); + } + + shell = getenv("SHELL"); + if (shell == NULL) + shell = _PATH_BSHELL; + + getmaster(); + printf("Script started, file is %s\n", fname); + fixtty(); + + (void) signal(SIGCHLD, finish); + child = fork(); + if (child < 0) { + perror("fork"); + fail(); + } + if (child == 0) { + subchild = child = fork(); + if (child < 0) { + perror("fork"); + fail(); + } + if (child) + dooutput(); + else + doshell(); + } + doinput(); +} + +doinput() +{ + register int cc; + char ibuf[BUFSIZ]; + + (void) fclose(fscript); + while ((cc = read(0, ibuf, BUFSIZ)) > 0) + (void) write(master, ibuf, cc); + done(); +} + +#include + +void +finish() +{ + union wait status; + register int pid; + register int die = 0; + + while ((pid = wait3((int *)&status, WNOHANG, 0)) > 0) + if (pid == child) + die = 1; + + if (die) + done(); +} + +dooutput() +{ + register int cc; + time_t tvec, time(); + char obuf[BUFSIZ], *ctime(); + + (void) close(0); + tvec = time((time_t *)NULL); + fprintf(fscript, "Script started on %s", ctime(&tvec)); + for (;;) { + cc = read(master, obuf, sizeof (obuf)); + if (cc <= 0) + break; + (void) write(1, obuf, cc); + (void) fwrite(obuf, 1, cc, fscript); + } + done(); +} + +doshell() +{ + int t; + + /*** + t = open(_PATH_TTY, O_RDWR); + if (t >= 0) { + (void) ioctl(t, TIOCNOTTY, (char *)0); + (void) close(t); + } + ***/ + getslave(); + (void) close(master); + (void) fclose(fscript); + (void) dup2(slave, 0); + (void) dup2(slave, 1); + (void) dup2(slave, 2); + (void) close(slave); +#ifdef linux + execl(shell, strrchr(shell, '/') + 1, "-i", 0); +#else + execl(shell, "sh", "-i", 0); +#endif + perror(shell); + fail(); +} + +fixtty() +{ + struct termios rtt; + + rtt = tt; + cfmakeraw(&rtt); + rtt.c_lflag &= ~ECHO; + (void) tcsetattr(0, TCSAFLUSH, &rtt); +} + +fail() +{ + + (void) kill(0, SIGTERM); + done(); +} + +done() +{ + time_t tvec, time(); + char *ctime(); + + if (subchild) { + tvec = time((time_t *)NULL); + fprintf(fscript,"\nScript done on %s", ctime(&tvec)); + (void) fclose(fscript); + (void) close(master); + } else { + (void) tcsetattr(0, TCSAFLUSH, &tt); + printf("Script done, file is %s\n", fname); + } + exit(0); +} + +getmaster() +{ + char *pty, *bank, *cp; + struct stat stb; + + pty = &line[strlen("/dev/ptyp")]; + for (bank = "pqrs"; *bank; bank++) { + line[strlen("/dev/pty")] = *bank; + *pty = '0'; + if (stat(line, &stb) < 0) + break; + for (cp = "0123456789abcdef"; *cp; cp++) { + *pty = *cp; + master = open(line, O_RDWR); + if (master >= 0) { + char *tp = &line[strlen("/dev/")]; + int ok; + + /* verify slave side is usable */ + *tp = 't'; + ok = access(line, R_OK|W_OK) == 0; + *tp = 'p'; + if (ok) { + (void) tcgetattr(0, &tt); + (void) ioctl(0, TIOCGWINSZ, + (char *)&win); + return; + } + (void) close(master); + } + } + } + fprintf(stderr, "Out of pty's\n"); + fail(); +} + +getslave() +{ + + line[strlen("/dev/")] = 't'; + slave = open(line, O_RDWR); + if (slave < 0) { + perror(line); + fail(); + } + (void) tcsetattr(slave, TCSAFLUSH, &tt); + (void) ioctl(slave, TIOCSWINSZ, (char *)&win); + (void) setsid(); + (void) ioctl(slave, TIOCSCTTY, 0); +} diff --git a/misc-utils/setterm.1 b/misc-utils/setterm.1 new file mode 100644 index 00000000..794fea7d --- /dev/null +++ b/misc-utils/setterm.1 @@ -0,0 +1,88 @@ +.\" Copyright 1990 Gordon Irlam (gordoni@cs.ua.oz.au) +.\" Copyright 1992 Rickard E. Faith (faith@cs.unc.edu) +.\" Most of this was copied from the source code. Do not restrict distribution. +.\" May be distributed under the GNU General Public License +.TH SETTERM 1 "25 December 1992" "Linux 0.98" "Linux Programmer's Manual" +.SH NAME +setterm \- set terminal attributes +.SH SYNOPSIS +.nf +.BR "setterm [ \-term " terminal_name " ]" +.B "setterm [ \-reset ]" +.B "setterm [ \-initialize ]" +.B "setterm [ \-cursor [on|off] ]" +.B "setterm [ \-keyboard pc|olivetti|dutch|extended ]" +.B "setterm [ \-repeat [on|off] ]" +.B "setterm [ \-appcursorkeys [on|off] ]" +.B "setterm [ \-linewrap [on|off] ]" +.B "setterm [ \-snow [on|off] ]" +.B "setterm [ \-softscroll [on|off] ]" +.B "setterm [ \-defaults ]" +.B "setterm [ \-foreground black|red|green|yellow|blue|magenta|cyan|white|default ]" +.B "setterm [ \-background black|red|green|yellow|blue|magenta|cyan|white|default ]" +.B "setterm [ \-ulcolor black|grey|red|green|yellow|blue|magenta|cyan|white ]" +.B "setterm [ \-ulcolor bright red|green|yellow|blue|magenta|cyan|white ]" +.B "setterm [ \-hbcolor black|grey|red|green|yellow|blue|magenta|cyan|white ]" +.B "setterm [ \-hbcolor bright red|green|yellow|blue|magenta|cyan|white ]" +.B "setterm [ \-inversescreen [on|off] ]" +.B "setterm [ \-bold [on|off] ]" +.B "setterm [ \-half-bright [on|off] ]" +.B "setterm [ \-blink [on|off] ]" +.B "setterm [ \-reverse [on|off] ]" +.B "setterm [ \-underline [on|off] ]" +.B "setterm [ \-store ]" +.B "setterm [ \-clear [ all|rest ] ]" +.BR "setterm [ \-tabs [tab1 tab2 tab3 ... ] ]" " where (tabn = 1-160)" +.BR "setterm [ \-clrtabs [ tab1 tab2 tab3 ... ]" " where (tabn = 1-160)" +.BR "setterm [ \-regtabs [" " 1-160 " "] ]" +.BR "setterm [ \-blank [" " 0-60 " "] ]" +.BR "setterm [ \-dump [" " 1-NR_CONS " "] ]" +.BR "setterm [ \-append [" " 1-NR_CONS " "] ]" +.BR "setterm [ \-file" " dumpfilename " ] +.BR "setterm [ \-standout [" " attr " "] ]" +.fi +.SH DESCRIPTION +.B setterm +writes to standard output a character string that will invoke the specified +terminal capabilities. Where possibile +.I /etc/termcap +is consulted to find the string to use. Some options however do not +correspond to a +.BR termcap (5) +capability. In this case, if the terminal type is "minix-vc", or +"minix-vcam" the string that invokes the specified capabilities on the PC +Minix virtual console driver is output. Options that are not implemented +by the terminal are ignored. +.SH OPTIONS +Most options are self explanatory. The less obvious options are as +follows: +.TP +.B \-term +can be used to override the TERM environment variable. +.TP +.B \-reset +displays the terminal reset string, which typically resets the terminal to +its power on state. +.TP +.B \-initialize +displays the terminal initialization string, which typically sets the +terminal's rendering options, and other attributes to the default values. +.TP +.B \-default +sets the terminal's rendering options to the default values. +.TP +.B \-store +stores the terminal's current rendering options as the default values. +.SH "SEE ALSO" +.BR tput (1), +.BR stty (1), +.BR termcap (5), +.BR tty (4) +.SH BUGS +Differences between the Minux and Linux versions are not documented. +.SH AUTHORS +Gordon Irlam (gordoni@cs.ua.oz.au) +.br +Adaption to Linux by Peter MacDonald +.br +Enhancements by Mika Liljeberg (liljeber@cs.Helsinki.FI) diff --git a/misc-utils/setterm.c b/misc-utils/setterm.c new file mode 100644 index 00000000..e81fccc3 --- /dev/null +++ b/misc-utils/setterm.c @@ -0,0 +1,1137 @@ +/* setterm.c, set terminal attributes. + * + * Copyright (C) 1990 Gordon Irlam (gordoni@cs.ua.oz.au). Conditions of use, + * modification, and redistribution are contained in the file COPYRIGHT that + * forms part of this distribution. + * + * Adaption to Linux by Peter MacDonald. + * + * Enhancements by Mika Liljeberg (liljeber@cs.Helsinki.FI) + * + * + * Syntax: + * + * setterm + * [ -term terminal_name ] + * [ -reset ] + * [ -initialize ] + * [ -cursor [on|off] ] + * [ -keyboard pc|olivetti|dutch|extended ] + * [ -repeat [on|off] ] + * [ -appcursorkeys [on|off] ] + * [ -linewrap [on|off] ] + * [ -snow [on|off] ] + * [ -softscroll [on|off] ] + * [ -defaults ] + * [ -foreground black|red|green|yellow|blue|magenta|cyan|white|default ] + * [ -background black|red|green|yellow|blue|magenta|cyan|white|default ] + * [ -ulcolor black|grey|red|green|yellow|blue|magenta|cyan|white ] + * [ -ulcolor bright red|green|yellow|blue|magenta|cyan|white ] + * [ -hbcolor black|grey|red|green|yellow|blue|magenta|cyan|white ] + * [ -hbcolor bright red|green|yellow|blue|magenta|cyan|white ] + * [ -inversescreen [on|off] ] + * [ -bold [on|off] ] + * [ -half-bright [on|off] ] + * [ -blink [on|off] ] + * [ -reverse [on|off] ] + * [ -underline [on|off] ] + * [ -store ] + * [ -clear [ all|rest ] ] + * [ -tabs [tab1 tab2 tab3 ... ] ] (tabn = 1-160) + * [ -clrtabs [ tab1 tab2 tab3 ... ] (tabn = 1-160) + * [ -regtabs [1-160] ] + * [ -blank [0-60] ] + * [ -dump [1-NR_CONS ] ] + * [ -append [1-NR_CONS ] ] + * [ -file dumpfilename ] + * [ -standout [attr] ] + * [ -msg [on|off] ] + * [ -msglevel [0-8] ] + * [ -powersave [on|off] ] + * + * + * Semantics: + * + * Setterm writes to standard output a character string that will invoke the + * specified terminal capabilities. Where possibile termcap is consulted to + * find the string to use. Some options however do not correspond to a + * termcap capability. In this case if the terminal type is "con*", or + * "linux*" the string that invokes the specified capabilities on the PC + * Linux virtual console driver is output. Options that are not implemented + * by the terminal are ignored. + * + * The following options are non-obvious. + * + * -term can be used to override the TERM environment variable. + * + * -reset displays the terminal reset string, which typically resets the + * terminal to its power on state. + * + * -initialize displays the terminal initialization string, which typically + * sets the terminal's rendering options, and other attributes to the + * default values. + * + * -default sets the terminal's rendering options to the default values. + * + * -store stores the terminal's current rendering options as the default + * values. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* for syslog system call */ +#include +#include +_syscall3(int, syslog, int, type, char*, buf, int, len); + +/* Constants. */ + +/* Termcap constants. */ +#define TC_BUF_SIZE 1024 /* Size of termcap(3) buffer. */ +#define TC_ENT_SIZE 50 /* Size of termcap(3) entry buffer. */ + +/* General constants. */ +#define TRUE 1 +#define FALSE 0 + +/* Keyboard types. */ +#define PC 0 +#define OLIVETTI 1 +#define DUTCH 2 +#define EXTENDED 3 + +/* Colors. */ +#define BLACK 0 +#define RED 1 +#define GREEN 2 +#define YELLOW 3 +#define BLUE 4 +#define MAGENTA 5 +#define CYAN 6 +#define WHITE 7 +#define GREY 8 +#define DEFAULT 9 + +/* Control sequences. */ +#define ESC "\033" +#define DCS "\033P" +#define ST "\033\\" + +/* Static variables. */ + +char tc_buf[TC_BUF_SIZE]; /* Termcap buffer. */ + +/* Option flags. Set if the option is to be invoked. */ +int opt_term, opt_reset, opt_initialize, opt_cursor, opt_keyboard; +int opt_linewrap, opt_snow, opt_softscroll, opt_default, opt_foreground; +int opt_background, opt_bold, opt_blink, opt_reverse, opt_underline; +int opt_store, opt_clear, opt_blank, opt_snap, opt_snapfile, opt_standout; +int opt_append, opt_ulcolor, opt_hbcolor, opt_halfbright, opt_repeat; +int opt_tabs, opt_clrtabs, opt_regtabs, opt_appcursorkeys, opt_inversescreen; +int opt_msg, opt_msglevel, opt_powersave; + +/* Option controls. The variable names have been contracted to ensure + * uniqueness. + */ +char *opt_te_terminal_name; /* Terminal name. */ +int opt_cu_on, opt_li_on, opt_sn_on, opt_so_on, opt_bo_on, opt_hb_on, opt_bl_on; +int opt_re_on, opt_un_on, opt_rep_on, opt_appck_on, opt_invsc_on; +int opt_msg_on, opt_ps_on; /* Boolean switches. */ +int opt_ke_type; /* Keyboard type. */ +int opt_fo_color, opt_ba_color; /* Colors. */ +int opt_ul_color, opt_hb_color; +int opt_cl_all; /* Clear all or rest. */ +int opt_bl_min; /* Blank screen. */ +int opt_sn_num = 0; /* Snap screen. */ +int opt_st_attr; +int opt_rt_len; /* regular tab length */ +int opt_tb_array[161]; /* Array for tab list */ +int opt_msglevel_num; + +char opt_sn_name[200] = "screen.dump"; + +/* Command line parsing routines. + * + * Note that it is an error for a given option to be invoked more than once. + */ + +void parse_term(argc, argv, option, opt_term, bad_arg) +int argc; /* Number of arguments for this option. */ +char *argv[]; /* Arguments for this option. */ +int *option; /* Term flag to set. */ +char **opt_term; /* Terminal name to set. */ +int *bad_arg; /* Set to true if an error is detected. */ +{ +/* Parse a -term specification. */ + + if (argc != 1 || *option) *bad_arg = TRUE; + *option = TRUE; + if (argc == 1) { + *opt_term = argv[0]; + } +} + +void parse_none(argc, argv, option, bad_arg) +int argc; /* Number of arguments for this option. */ +char *argv[]; /* Arguments for this option. */ +int *option; /* Option flag to set. */ +int *bad_arg; /* Set to true if an error is detected. */ +{ +/* Parse a parameterless specification. */ + + if (argc != 0 || *option) *bad_arg = TRUE; + *option = TRUE; +} + +void parse_switch(argc, argv, option, opt_on, bad_arg) +int argc; /* Number of arguments for this option. */ +char *argv[]; /* Arguments for this option. */ +int *option; /* Option flag to set. */ +int *opt_on; /* Boolean option switch to set or reset. */ +int *bad_arg; /* Set to true if an error is detected. */ +{ +/* Parse a boolean (on/off) specification. */ + + if (argc > 1 || *option) *bad_arg = TRUE; + *option = TRUE; + if (argc == 1) { + if (strcmp(argv[0], "on") == 0) + *opt_on = TRUE; + else if (strcmp(argv[0], "off") == 0) + *opt_on = FALSE; + else + *bad_arg = TRUE; + } else { + *opt_on = TRUE; + } +} + +#if 0 +void parse_keyboard(argc, argv, option, opt_keyboard, bad_arg) +int argc; /* Number of arguments for this option. */ +char *argv[]; /* Arguments for this option. */ +int *option; /* Keyboard flag to set. */ +int *opt_keyboard; /* Keyboard type to set. */ +int *bad_arg; /* Set to true if an error is detected. */ +{ +/* Parse a -keyboard specification. */ + + if (argc != 1 || *option) *bad_arg = TRUE; + *option = TRUE; + if (argc == 1) { + if (strcmp(argv[0], "pc") == 0) + *opt_keyboard = PC; + else if (strcmp(argv[0], "olivetti") == 0) + *opt_keyboard = OLIVETTI; + else if (strcmp(argv[0], "dutch") == 0) + *opt_keyboard = DUTCH; + else if (strcmp(argv[0], "extended") == 0) + *opt_keyboard = EXTENDED; + else + *bad_arg = TRUE; + } +} +#endif + +void par_color(argc, argv, option, opt_color, bad_arg) +int argc; /* Number of arguments for this option. */ +char *argv[]; /* Arguments for this option. */ +int *option; /* Color flag to set. */ +int *opt_color; /* Color to set. */ +int *bad_arg; /* Set to true if an error is detected. */ +{ +/* Parse a -foreground or -background specification. */ + + if (argc != 1 || *option) *bad_arg = TRUE; + *option = TRUE; + if (argc == 1) { + if (strcmp(argv[0], "black") == 0) + *opt_color = BLACK; + else if (strcmp(argv[0], "red") == 0) + *opt_color = RED; + else if (strcmp(argv[0], "green") == 0) + *opt_color = GREEN; + else if (strcmp(argv[0], "yellow") == 0) + *opt_color = YELLOW; + else if (strcmp(argv[0], "blue") == 0) + *opt_color = BLUE; + else if (strcmp(argv[0], "magenta") == 0) + *opt_color = MAGENTA; + else if (strcmp(argv[0], "cyan") == 0) + *opt_color = CYAN; + else if (strcmp(argv[0], "white") == 0) + *opt_color = WHITE; + else if (strcmp(argv[0], "default") == 0) + *opt_color = DEFAULT; + else if (isdigit(argv[0][0])) + *opt_color = atoi(argv[0]); + else + *bad_arg = TRUE; + if(*opt_color < 0 || *opt_color > 15) + *bad_arg = TRUE; + } +} + +void par_color2(argc, argv, option, opt_color, bad_arg) +int argc; /* Number of arguments for this option. */ +char *argv[]; /* Arguments for this option. */ +int *option; /* Color flag to set. */ +int *opt_color; /* Color to set. */ +int *bad_arg; /* Set to true if an error is detected. */ +{ +/* Parse a -ulcolor or -hbcolor specification. */ + + if (!argc || argc > 2 || *option) *bad_arg = TRUE; + *option = TRUE; + *opt_color = 0; + if (argc == 2) { + if (strcmp(argv[0], "bright") == 0) + *opt_color = 8; + else { + *bad_arg = TRUE; + return; + } + } + if (argc) { + if (strcmp(argv[argc-1], "black") == 0) { + if(*opt_color) + *bad_arg = TRUE; + else + *opt_color = BLACK; + } else if (strcmp(argv[argc-1], "grey") == 0) { + if(*opt_color) + *bad_arg = TRUE; + else + *opt_color = GREY; + } else if (strcmp(argv[argc-1], "red") == 0) + *opt_color |= RED; + else if (strcmp(argv[argc-1], "green") == 0) + *opt_color |= GREEN; + else if (strcmp(argv[argc-1], "yellow") == 0) + *opt_color |= YELLOW; + else if (strcmp(argv[argc-1], "blue") == 0) + *opt_color |= BLUE; + else if (strcmp(argv[argc-1], "magenta") == 0) + *opt_color |= MAGENTA; + else if (strcmp(argv[argc-1], "cyan") == 0) + *opt_color |= CYAN; + else if (strcmp(argv[argc-1], "white") == 0) + *opt_color |= WHITE; + else if (isdigit(argv[argc-1][0])) + *opt_color = atoi(argv[argc-1]); + else + *bad_arg = TRUE; + if(*opt_color < 0 || *opt_color > 15) + *bad_arg = TRUE; + } +} + +void parse_clear(argc, argv, option, opt_all, bad_arg) +int argc; /* Number of arguments for this option. */ +char *argv[]; /* Arguments for this option. */ +int *option; /* Clear flag to set. */ +int *opt_all; /* Clear all switch to set or reset. */ +int *bad_arg; /* Set to true if an error is detected. */ +{ +/* Parse a -clear specification. */ + + if (argc > 1 || *option) *bad_arg = TRUE; + *option = TRUE; + if (argc == 1) { + if (strcmp(argv[0], "all") == 0) + *opt_all = TRUE; + else if (strcmp(argv[0], "rest") == 0) + *opt_all = FALSE; + else + *bad_arg = TRUE; + } else { + *opt_all = TRUE; + } +} + +void parse_blank(argc, argv, option, opt_all, bad_arg) +int argc; /* Number of arguments for this option. */ +char *argv[]; /* Arguments for this option. */ +int *option; /* Clear flag to set. */ +int *opt_all; /* Clear all switch to set or reset. */ +int *bad_arg; /* Set to true if an error is detected. */ +{ +/* Parse a -clear specification. */ + + if (argc > 1 || *option) *bad_arg = TRUE; + *option = TRUE; + if (argc == 1) { + *opt_all = atoi(argv[0]); + if ((*opt_all > 60) || (*opt_all < 0)) + *bad_arg = TRUE; + } else { + *opt_all = 0; + } +} + +#if 0 +void parse_standout(argc, argv, option, opt_all, bad_arg) +int argc; /* Number of arguments for this option. */ +char *argv[]; /* Arguments for this option. */ +int *option; /* Clear flag to set. */ +int *opt_all; /* Clear all switch to set or reset. */ +int *bad_arg; /* Set to true if an error is detected. */ +{ +/* Parse a -clear specification. */ + + if (argc > 1 || *option) *bad_arg = TRUE; + *option = TRUE; + if (argc == 1) { + *opt_all = atoi(argv[0]); + } else { + *opt_all = -1; + } +} +#endif + +void parse_msglevel(argc, argv, option, opt_all, bad_arg) +int argc; /* Number of arguments for this option. */ +char *argv[]; /* Arguments for this option. */ +int *option; /* Clear flag to set. */ +int *opt_all; /* Clear all switch to set or reset. */ +int *bad_arg; /* Set to true if an error is detected. */ +{ + + if (argc > 1 || *option) *bad_arg = TRUE; + *option = TRUE; + if (argc == 1) { + *opt_all = atoi(argv[0]); + if (*opt_all < 0 || *opt_all > 8) + *bad_arg = TRUE; + } else { + *opt_all = -1; + } +} + +void parse_snap(argc, argv, option, opt_all, bad_arg) +int argc; /* Number of arguments for this option. */ +char *argv[]; /* Arguments for this option. */ +int *option; /* Clear flag to set. */ +int *opt_all; /* Clear all switch to set or reset. */ +int *bad_arg; /* Set to true if an error is detected. */ +{ +/* Parse a -clear specification. */ + + if (argc > 1 || *option) *bad_arg = TRUE; + *option = TRUE; + if (argc == 1) { + *opt_all = atoi(argv[0]); + if ((*opt_all <= 0)) + *bad_arg = TRUE; + } else { + *opt_all = 0; + } +} + +void parse_snapfile(argc, argv, option, opt_all, bad_arg) +int argc; /* Number of arguments for this option. */ +char *argv[]; /* Arguments for this option. */ +int *option; /* Clear flag to set. */ +int *opt_all; /* Clear all switch to set or reset. */ +int *bad_arg; /* Set to true if an error is detected. */ +{ +/* Parse a -clear specification. */ + + if (argc != 1 || *option) *bad_arg = TRUE; + *option = TRUE; + if (argc == 1) { + strcpy((char *)opt_all, argv[0]); + } +} + +void parse_tabs(argc, argv, option, tab_array, bad_arg) +int argc; /* Number of arguments for this option. */ +char *argv[]; /* Arguments for this option. */ +int *option; /* Clear flag to set. */ +int *tab_array; /* Array of tabs */ +int *bad_arg; /* Set to true if an error is detected. */ +{ + if (*option || argc > 160) *bad_arg = TRUE; + *option = TRUE; + tab_array[argc] = -1; + while(argc--) { + tab_array[argc] = atoi(argv[argc]); + if(tab_array[argc] < 1 || tab_array[argc] > 160) { + *bad_arg = TRUE; + return; + } + } +} + +void parse_clrtabs(argc, argv, option, tab_array, bad_arg) +int argc; /* Number of arguments for this option. */ +char *argv[]; /* Arguments for this option. */ +int *option; /* Clear flag to set. */ +int *tab_array; /* Array of tabs */ +int *bad_arg; /* Set to true if an error is detected. */ +{ + if (*option || argc > 160) *bad_arg = TRUE; + *option = TRUE; + if(argc == 0) { + tab_array[0] = -1; + return; + } + tab_array[argc] = -1; + while(argc--) { + tab_array[argc] = atoi(argv[argc]); + if(tab_array[argc] < 1 || tab_array[argc] > 160) { + *bad_arg = TRUE; + return; + } + } +} + +void parse_regtabs(argc, argv, option, opt_len, bad_arg) +int argc; /* Number of arguments for this option. */ +char *argv[]; /* Arguments for this option. */ +int *option; /* Clear flag to set. */ +int *opt_len; /* Regular tab length. */ +int *bad_arg; /* Set to true if an error is detected. */ +{ + if (*option || argc > 1) *bad_arg = TRUE; + *option = TRUE; + if(argc == 0) { + *opt_len = 8; + return; + } + *opt_len = atoi(argv[0]); + if(*opt_len < 1 || *opt_len > 160) { + *bad_arg = TRUE; + return; + } +} + +void show_tabs() +{ + int i, co = tgetnum("co"); + + if(co > 0) { + printf("\r "); + for(i = 10; i < co-2; i+=10) + printf("%-10d", i); + putchar('\n'); + for(i = 1; i <= co; i++) + putchar(i%10+'0'); + putchar('\n'); + for(i = 1; i < co; i++) + printf("\tT\b"); + putchar('\n'); + } +} + + +#define STRCMP(str1,str2) strncmp(str1,str2,strlen(str1)) + +void parse_option(option, argc, argv, bad_arg) +char *option; /* Option with leading '-' removed. */ +int argc; /* Number of arguments for this option. */ +char *argv[]; /* Arguments for this option. */ +int *bad_arg; /* Set to true if an error is detected. */ +{ +/* Parse a single specification. */ + + if (STRCMP(option, "term") == 0) + parse_term(argc, argv, &opt_term, &opt_te_terminal_name, bad_arg); + else if (STRCMP(option, "reset") == 0) + parse_none(argc, argv, &opt_reset, bad_arg); + else if (STRCMP(option, "initialize") == 0) + parse_none(argc, argv, &opt_initialize, bad_arg); + else if (STRCMP(option, "cursor") == 0) + parse_switch(argc, argv, &opt_cursor, &opt_cu_on, bad_arg); +#if 0 + else if (STRCMP(option, "keyboard") == 0) + parse_keyboard(argc, argv, &opt_keyboard, &opt_ke_type, bad_arg); +#endif + else if (STRCMP(option, "repeat") == 0) + parse_switch(argc, argv, &opt_repeat, &opt_rep_on, bad_arg); + else if (STRCMP(option, "appcursorkeys") == 0) + parse_switch(argc, argv, &opt_appcursorkeys, &opt_appck_on, bad_arg); + else if (STRCMP(option, "linewrap") == 0) + parse_switch(argc, argv, &opt_linewrap, &opt_li_on, bad_arg); +#if 0 + else if (STRCMP(option, "snow") == 0) + parse_switch(argc, argv, &opt_snow, &opt_sn_on, bad_arg); + else if (STRCMP(option, "softscroll") == 0) + parse_switch(argc, argv, &opt_softscroll, &opt_so_on, bad_arg); +#endif + else if (STRCMP(option, "default") == 0) + parse_none(argc, argv, &opt_default, bad_arg); + else if (STRCMP(option, "foreground") == 0) + par_color(argc, argv, &opt_foreground, &opt_fo_color, bad_arg); + else if (STRCMP(option, "background") == 0) + par_color(argc, argv, &opt_background, &opt_ba_color, bad_arg); + else if (STRCMP(option, "ulcolor") == 0) + par_color2(argc, argv, &opt_ulcolor, &opt_ul_color, bad_arg); + else if (STRCMP(option, "hbcolor") == 0) + par_color2(argc, argv, &opt_hbcolor, &opt_hb_color, bad_arg); + else if (STRCMP(option, "inversescreen") == 0) + parse_switch(argc, argv, &opt_inversescreen, &opt_invsc_on, bad_arg); + else if (STRCMP(option, "bold") == 0) + parse_switch(argc, argv, &opt_bold, &opt_bo_on, bad_arg); + else if (STRCMP(option, "half-bright") == 0) + parse_switch(argc, argv, &opt_halfbright, &opt_hb_on, bad_arg); + else if (STRCMP(option, "blink") == 0) + parse_switch(argc, argv, &opt_blink, &opt_bl_on, bad_arg); + else if (STRCMP(option, "reverse") == 0) + parse_switch(argc, argv, &opt_reverse, &opt_re_on, bad_arg); + else if (STRCMP(option, "underline") == 0) + parse_switch(argc, argv, &opt_underline, &opt_un_on, bad_arg); + else if (STRCMP(option, "store") == 0) + parse_none(argc, argv, &opt_store, bad_arg); + else if (STRCMP(option, "clear") == 0) + parse_clear(argc, argv, &opt_clear, &opt_cl_all, bad_arg); + else if (STRCMP(option, "tabs") == 0) + parse_tabs(argc, argv, &opt_tabs, opt_tb_array, bad_arg); + else if (STRCMP(option, "clrtabs") == 0) + parse_clrtabs(argc, argv, &opt_clrtabs, opt_tb_array, bad_arg); + else if (STRCMP(option, "regtabs") == 0) + parse_regtabs(argc, argv, &opt_regtabs, &opt_rt_len, bad_arg); + else if (STRCMP(option, "blank") == 0) + parse_blank(argc, argv, &opt_blank, &opt_bl_min, bad_arg); + else if (STRCMP(option, "dump") == 0) + parse_snap(argc, argv, &opt_snap, &opt_sn_num, bad_arg); + else if (STRCMP(option, "append") == 0) + parse_snap(argc, argv, &opt_append, &opt_sn_num, bad_arg); + else if (STRCMP(option, "file") == 0) + parse_snapfile(argc, argv, &opt_snapfile, (int *)opt_sn_name, bad_arg); + else if (STRCMP(option, "msg") == 0) + parse_switch(argc, argv, &opt_msg, &opt_msg_on, bad_arg); + else if (STRCMP(option, "msglevel") == 0) + parse_msglevel(argc, argv, &opt_msglevel, &opt_msglevel_num, bad_arg); + else if (STRCMP(option, "powersave") == 0) + parse_switch(argc, argv, &opt_powersave, &opt_ps_on, bad_arg); +#if 0 + else if (STRCMP(option, "standout") == 0) + parse_standout(argc, argv, &opt_standout, &opt_st_attr, bad_arg); +#endif + else + *bad_arg = TRUE; +} + +/* End of command line parsing routines. */ + +void usage(prog_name) +char *prog_name; /* Name of this program. */ +{ +/* Print error message about arguments, and the command's syntax. */ + + fprintf(stderr, "%s: Argument error, usage\n", prog_name); + fprintf(stderr, "\n"); + fprintf(stderr, "%s\n", prog_name); + fprintf(stderr, " [ -term terminal_name ]\n"); + fprintf(stderr, " [ -reset ]\n"); + fprintf(stderr, " [ -initialize ]\n"); + fprintf(stderr, " [ -cursor [on|off] ]\n"); +#if 0 + fprintf(stderr, " [ -snow [on|off] ]\n"); + fprintf(stderr, " [ -softscroll [on|off] ]\n"); + fprintf(stderr, " [ -keyboard pc|olivetti|dutch|extended ]\n"); +#endif + fprintf(stderr, " [ -repeat [on|off] ]\n"); + fprintf(stderr, " [ -appcursorkeys [on|off] ]\n"); + fprintf(stderr, " [ -linewrap [on|off] ]\n"); + fprintf(stderr, " [ -default ]\n"); + fprintf(stderr, " [ -foreground black|blue|green|cyan"); + fprintf(stderr, "|red|magenta|yellow|white|default ]\n"); + fprintf(stderr, " [ -background black|blue|green|cyan"); + fprintf(stderr, "|red|magenta|yellow|white|default ]\n"); + fprintf(stderr, " [ -ulcolor black|grey|blue|green|cyan"); + fprintf(stderr, "|red|magenta|yellow|white ]\n"); + fprintf(stderr, " [ -ulcolor bright blue|green|cyan"); + fprintf(stderr, "|red|magenta|yellow|white ]\n"); + fprintf(stderr, " [ -hbcolor black|grey|blue|green|cyan"); + fprintf(stderr, "|red|magenta|yellow|white ]\n"); + fprintf(stderr, " [ -hbcolor bright blue|green|cyan"); + fprintf(stderr, "|red|magenta|yellow|white ]\n"); +#if 0 + fprintf(stderr, " [ -standout [ attr ] ]\n"); +#endif + fprintf(stderr, " [ -inversescreen [on|off] ]\n"); + fprintf(stderr, " [ -bold [on|off] ]\n"); + fprintf(stderr, " [ -half-bright [on|off] ]\n"); + fprintf(stderr, " [ -blink [on|off] ]\n"); + fprintf(stderr, " [ -reverse [on|off] ]\n"); + fprintf(stderr, " [ -underline [on|off] ]\n"); + fprintf(stderr, " [ -store ]\n"); + fprintf(stderr, " [ -clear [all|rest] ]\n"); + fprintf(stderr, " [ -tabs [ tab1 tab2 tab3 ... ] ] (tabn = 1-160)\n"); + fprintf(stderr, " [ -clrtabs [ tab1 tab2 tab3 ... ] ] (tabn = 1-160)\n"); + fprintf(stderr, " [ -regtabs [1-160] ]\n"); + fprintf(stderr, " [ -blank [0-60] ]\n"); + fprintf(stderr, " [ -dump [1-NR_CONSOLES] ]\n"); + fprintf(stderr, " [ -append [1-NR_CONSOLES] ]\n"); + fprintf(stderr, " [ -file dumpfilename ]\n"); + fprintf(stderr, " [ -msg [on|off] ]\n"); + fprintf(stderr, " [ -msglevel [0-8] ]\n"); + fprintf(stderr, " [ -powersave [on|off] ]\n"); +} + +char tc_ent_buf[TC_ENT_SIZE]; /* Buffer for storing a termcap entry. */ + +char *tc_entry(name) +char *name; /* Termcap capability string to lookup. */ +{ +/* Return the specified termcap string, or an empty string if no such termcap + * capability exists. + */ + + char *buf_ptr; + + buf_ptr = tc_ent_buf; + if (tgetstr(name, &buf_ptr) == NULL) tc_ent_buf[0] = '\0'; + return tc_ent_buf; +} + +void perform_sequence(vcterm) +int vcterm; /* Set if terminal is a virtual console. */ +{ + int result; +/* Perform the selected options. */ + + /* -reset. */ + if (opt_reset) { + printf("%s", tc_entry("rs")); + } + + /* -initialize. */ + if (opt_initialize) { + printf("%s", tc_entry("is")); + } + + /* -cursor [on|off]. */ + if (opt_cursor) { + if (opt_cu_on) + printf("%s", tc_entry("ve")); + else + printf("%s", tc_entry("vi")); + } + +#if 0 + /* -keyboard pc|olivetti|dutch|extended. Vc only. */ + if (opt_keyboard && vcterm) { + switch (opt_ke_type) { + case PC: + printf("%s%s%s", DCS, "keyboard.pc", ST); + break; + case OLIVETTI: + printf("%s%s%s", DCS, "keyboard.olivetti", ST); + break; + case DUTCH: + printf("%s%s%s", DCS, "keyboard.dutch", ST); + break; + case EXTENDED: + printf("%s%s%s", DCS, "keyboard.extended", ST); + break; + } + } +#endif + + /* -linewrap [on|off]. Vc only (vt102) */ + if (opt_linewrap && vcterm) { + if (opt_li_on) + printf("\033[?7h"); + else + printf("\033[?7l"); + } + + /* -repeat [on|off]. Vc only (vt102) */ + if (opt_repeat && vcterm) { + if (opt_rep_on) + printf("\033[?8h"); + else + printf("\033[?8l"); + } + + /* -appcursorkeys [on|off]. Vc only (vt102) */ + if (opt_appcursorkeys && vcterm) { + if (opt_appck_on) + printf("\033[?1h"); + else + printf("\033[?1l"); + } + +#if 0 + /* -snow [on|off]. Vc only. */ + if (opt_snow && vcterm) { + if (opt_sn_on) + printf("%s%s%s", DCS, "snow.on", ST); + else + printf("%s%s%s", DCS, "snow.off", ST); + } + + /* -softscroll [on|off]. Vc only. */ + if (opt_softscroll && vcterm) { + if (opt_so_on) + printf("%s%s%s", DCS, "softscroll.on", ST); + else + printf("%s%s%s", DCS, "softscroll.off", ST); + } +#endif + + /* -default. Vc sets default rendition, otherwise clears all + * attributes. + */ + if (opt_default) { + if (vcterm) + printf("\033[0m"); + else + printf("%s", tc_entry("me")); + } + + /* -foreground black|red|green|yellow|blue|magenta|cyan|white|default. + * Vc only (ANSI). + */ + if (opt_foreground && vcterm) { + printf("%s%s%c%s", ESC, "[3", '0' + opt_fo_color, "m"); + } + + /* -background black|red|green|yellow|blue|magenta|cyan|white|default. + * Vc only (ANSI). + */ + if (opt_background && vcterm) { + printf("%s%s%c%s", ESC, "[4", '0' + opt_ba_color, "m"); + } + + /* -ulcolor black|red|green|yellow|blue|magenta|cyan|white|default. + * Vc only. + */ + if (opt_ulcolor && vcterm) { + printf("\033[1;%d]", opt_ul_color); + } + + /* -hbcolor black|red|green|yellow|blue|magenta|cyan|white|default. + * Vc only. + */ + if (opt_hbcolor && vcterm) { + printf("\033[2;%d]", opt_hb_color); + } + + /* -inversescreen [on|off]. Vc only (vt102). + */ + if (opt_inversescreen) { + if (vcterm) + if (opt_invsc_on) + printf("\033[?5h"); + else + printf("\033[?5l"); + } + + /* -bold [on|off]. Vc behaves as expected, otherwise off turns off + * all attributes. + */ + if (opt_bold) { + if (opt_bo_on) + printf("%s", tc_entry("md")); + else { + if (vcterm) + printf("%s%s", ESC, "[22m"); + else + printf("%s", tc_entry("me")); + } + } + + /* -half-bright [on|off]. Vc behaves as expected, otherwise off turns off + * all attributes. + */ + if (opt_halfbright) { + if (opt_hb_on) + printf("%s", tc_entry("mh")); + else { + if (vcterm) + printf("%s%s", ESC, "[22m"); + else + printf("%s", tc_entry("me")); + } + } + + /* -blink [on|off]. Vc behaves as expected, otherwise off turns off + * all attributes. + */ + if (opt_blink) { + if (opt_bl_on) + printf("%s", tc_entry("mb")); + else { + if (vcterm) + printf("%s%s", ESC, "[25m"); + else + printf("%s", tc_entry("me")); + } + } + + /* -reverse [on|off]. Vc behaves as expected, otherwise off turns + * off all attributes. + */ + if (opt_reverse) { + if (opt_re_on) + printf("%s", tc_entry("mr")); + else { + if (vcterm) + printf("%s%s", ESC, "[27m"); + else + printf("%s", tc_entry("me")); + } + } + + /* -underline [on|off]. */ + if (opt_underline) { + if (opt_un_on) + printf("%s", tc_entry("us")); + else + printf("%s", tc_entry("ue")); + } + + /* -store. Vc only. */ + if (opt_store && vcterm) { + printf("\033[8]"); + } + + /* -clear [all|rest]. */ + if (opt_clear) { + if (opt_cl_all) + printf("%s", tc_entry("cl")); + else + printf("%s", tc_entry("cd")); + } + + /* -tabs Vc only. */ + if (opt_tabs && vcterm) { + int i; + + if (opt_tb_array[0] == -1) + show_tabs(); + else { + for(i=0; opt_tb_array[i] > 0; i++) + printf("\033[%dG\033H", opt_tb_array[i]); + putchar('\r'); + } + } + + /* -clrtabs Vc only. */ + if (opt_clrtabs && vcterm) { + int i; + + if (opt_tb_array[0] == -1) + printf("\033[3g"); + else + for(i=0; opt_tb_array[i]; i++) + printf("\033[%dG\033[g", opt_tb_array[i]); + putchar('\r'); + } + + /* -regtabs Vc only. */ + if (opt_regtabs && vcterm) { + int i; + + printf("\033[3g\r"); + for(i=opt_rt_len+1; i<=160; i+=opt_rt_len) + printf("\033[%dC\033H",opt_rt_len); + putchar('\r'); + } + + /* -blank [0-60]. */ + if (opt_blank) + printf("\033[9;%d]", opt_bl_min); + + /* -powersave [on|off] (console) */ + if (opt_powersave) { + char ioctlarg[2]; + ioctlarg[0] = 10; /* powersave */ + ioctlarg[1] = opt_ps_on; + if (ioctl(0,TIOCLINUX,ioctlarg)) + fprintf(stderr,"cannot (un)set powersave mode\n"); + } + +#if 0 + /* -standout [num]. */ + if (opt_standout) + /* nothing */; +#endif + + /* -snap [1-NR_CONS]. */ + if (opt_snap || opt_append) { + FILE *F; + + F = fopen(opt_sn_name, opt_snap ? "w" : "a"); + if (!F) { + perror(opt_sn_name); + fprintf(stderr,"setterm: can not open dump file %s for output\n", + opt_sn_name); + exit(-1); + } + screendump(opt_sn_num, F); + fclose(F); + } + + /* -msg [on|off]. */ + if (opt_msg && vcterm) { + if (opt_msg_on) + /* 7 -- Enable printk's to console */ + result = syslog(7, NULL, 0); + else + /* 6 -- Disable printk's to console */ + result = syslog(6, NULL, 0); + + if (result != 0) + printf("syslog error: %s\n", strerror(result)); + } + + /* -msglevel [0-8] */ + if (opt_msglevel && vcterm) { + /* 8 -- Set level of messages printed to console */ + result = syslog(8, NULL, opt_msglevel_num); + if (result != 0) + printf("syslog error: %s\n", strerror(result)); + } +} + +extern char *malloc(); + +screendump(int vcnum, FILE *F){ +#include + char infile[MAXPATHLEN]; + unsigned char header[4]; + unsigned int rows, cols; + int fd, i, j; + char *inbuf, *outbuf, *p, *q; + + sprintf(infile, "/dev/vcsa%d", vcnum); + fd = open(infile, 0); + if (fd < 0 || read(fd, header, 4) != 4) + goto try_ioctl; + rows = header[0]; + cols = header[1]; + if (rows * cols == 0) + goto try_ioctl; + inbuf = malloc(rows*cols*2); + outbuf = malloc(rows*(cols+1)); + if(!inbuf || !outbuf) { + fprintf(stderr, "Out of memory\n"); + exit(1); + } + if (read(fd, inbuf, rows*cols*2) != rows*cols*2) { + fprintf(stderr, "Error reading %s\n", infile); + exit(1); + } + p = inbuf; + q = outbuf; + for(i=0; i 0 && q[-1] == ' ') + q--; + *q++ = '\n'; + } + if (fwrite(outbuf, 1, q-outbuf, F) != q-outbuf) { + fprintf(stderr, "Error writing screendump\n"); + exit(1); + } + return; + +try_ioctl: + { +#define NUM_COLS 160 +#define NUM_ROWS 75 + char buf[NUM_COLS+1]; + unsigned char screenbuf[NUM_ROWS*NUM_COLS]; + screenbuf[0] = 0; + screenbuf[1] = (unsigned char) vcnum; + if (ioctl(0,TIOCLINUX,screenbuf) < 0) { + fprintf(stderr,"couldn't read %s, and cannot ioctl dump\n", + infile); + exit(1); + } + rows = screenbuf[0]; + cols = screenbuf[1]; + for (i=0; i +#include +#include +#include +#include + +/* + * Topological sort. Input is a list of pairs of strings seperated by + * white space (spaces, tabs, and/or newlines); strings are written to + * standard output in sorted order, one per line. + * + * usage: + * tsort [inputfile] + * If no input file is specified, standard input is read. + * + * Should be compatable with AT&T tsort HOWEVER the output is not identical + * (i.e. for most graphs there is more than one sorted order, and this tsort + * usually generates a different one then the AT&T tsort). Also, cycle + * reporting seems to be more accurate in this version (the AT&T tsort + * sometimes says a node is in a cycle when it isn't). + * + * Michael Rendell, michael@stretch.cs.mun.ca - Feb 26, '90 + */ +#define HASHSIZE 53 /* doesn't need to be big */ +#define NF_MARK 0x1 /* marker for cycle detection */ +#define NF_ACYCLIC 0x2 /* this node is cycle free */ + +typedef struct node_str NODE; + +struct node_str { + char *n_name; /* name of this node */ + NODE **n_prevp; /* pointer to previous node's n_next */ + NODE *n_next; /* next node in graph */ + NODE *n_hash; /* next node in hash table */ + int n_narcs; /* number of arcs in n_arcs[] */ + int n_arcsize; /* size of n_arcs[] array */ + NODE **n_arcs; /* array of arcs to other nodes */ + int n_refcnt; /* # of arcs pointing to this node */ + int n_flags; /* NF_* */ +}; + +typedef struct _buf { + char *b_buf; + int b_bsize; +} BUF; + +NODE *add_node(), *find_node(); +void add_arc(), no_memory(), remove_node(), tsort(); +char *grow_buf(), *malloc(); + +extern int errno; +NODE *graph; +NODE *hashtable[HASHSIZE]; +NODE **cycle_buf; +NODE **longest_cycle; + +main(argc, argv) + int argc; + char **argv; +{ + register BUF *b; + register int c, n; + FILE *fp; + int bsize, nused; + BUF bufs[2]; + + if (argc < 2) + fp = stdin; + /* == becomes > in next line per Volker Meyer_zu_Bexten + -- faith@cs.unc.edu, Sat Feb 4 21:25:09 1995 */ + else if (argc > 2) { + (void)fprintf(stderr, "usage: tsort [ inputfile ]\n"); + exit(1); + } else if (!(fp = fopen(argv[1], "r"))) { + (void)fprintf(stderr, "tsort: %s.\n", strerror(errno)); + exit(1); + } + + for (b = bufs, n = 2; --n >= 0; b++) + b->b_buf = grow_buf((char *)NULL, b->b_bsize = 1024); + + /* parse input and build the graph */ + for (n = 0, c = getc(fp);;) { + while (c != EOF && isspace(c)) + c = getc(fp); + if (c == EOF) + break; + + nused = 0; + b = &bufs[n]; + bsize = b->b_bsize; + do { + b->b_buf[nused++] = c; + if (nused == bsize) { + bsize *= 2; + b->b_buf = grow_buf(b->b_buf, bsize); + } + c = getc(fp); + } while (c != EOF && !isspace(c)); + + b->b_buf[nused] = '\0'; + b->b_bsize = bsize; + if (n) + add_arc(bufs[0].b_buf, bufs[1].b_buf); + n = !n; + } + (void)fclose(fp); + if (n) { + (void)fprintf(stderr, "tsort: odd data count.\n"); + exit(1); + } + + /* do the sort */ + tsort(); + exit(0); +} + +/* double the size of oldbuf and return a pointer to the new buffer. */ +char * +grow_buf(bp, size) + char *bp; + int size; +{ + char *realloc(); + + if (!(bp = realloc(bp, (u_int)size))) + no_memory(); + return(bp); +} + +/* + * add an arc from node s1 to node s2 in the graph. If s1 or s2 are not in + * the graph, then add them. + */ +void +add_arc(s1, s2) + char *s1, *s2; +{ + register NODE *n1; + NODE *n2; + int bsize; + + n1 = find_node(s1); + if (!n1) + n1 = add_node(s1); + + if (!strcmp(s1, s2)) + return; + + n2 = find_node(s2); + if (!n2) + n2 = add_node(s2); + + /* + * could check to see if this arc is here already, but it isn't + * worth the bother -- there usually isn't and it doesn't hurt if + * there is (I think :-). + */ + if (n1->n_narcs == n1->n_arcsize) { + if (!n1->n_arcsize) + n1->n_arcsize = 10; + bsize = n1->n_arcsize * sizeof(*n1->n_arcs) * 2; + n1->n_arcs = (NODE **)grow_buf((char *)n1->n_arcs, bsize); + n1->n_arcsize = bsize / sizeof(*n1->n_arcs); + } + n1->n_arcs[n1->n_narcs++] = n2; + ++n2->n_refcnt; +} + +hash_string(s) + char *s; +{ + register int hash, i; + + for (hash = 0, i = 1; *s; s++, i++) + hash += *s * i; + return(hash % HASHSIZE); +} + +/* + * find a node in the graph and return a pointer to it - returns null if not + * found. + */ +NODE * +find_node(name) + char *name; +{ + register NODE *n; + + for (n = hashtable[hash_string(name)]; n; n = n->n_hash) + if (!strcmp(n->n_name, name)) + return(n); + return((NODE *)NULL); +} + +/* Add a node to the graph and return a pointer to it. */ +NODE * +add_node(name) + char *name; +{ + register NODE *n; + int hash; + + if (!(n = (NODE *)malloc(sizeof(NODE))) || !(n->n_name = strdup(name))) + no_memory(); + + n->n_narcs = 0; + n->n_arcsize = 0; + n->n_arcs = (NODE **)NULL; + n->n_refcnt = 0; + n->n_flags = 0; + + /* add to linked list */ + if (n->n_next = graph) + graph->n_prevp = &n->n_next; + n->n_prevp = &graph; + graph = n; + + /* add to hash table */ + hash = hash_string(name); + n->n_hash = hashtable[hash]; + hashtable[hash] = n; + return(n); +} + +/* do topological sort on graph */ +void +tsort() +{ + register NODE *n, *next; + register int cnt; + + while (graph) { + /* + * keep getting rid of simple cases until there are none left, + * if there are any nodes still in the graph, then there is + * a cycle in it. + */ + do { + for (cnt = 0, n = graph; n; n = next) { + next = n->n_next; + if (n->n_refcnt == 0) { + remove_node(n); + ++cnt; + } + } + } while (graph && cnt); + + if (!graph) + break; + + if (!cycle_buf) { + /* + * allocate space for two cycle logs - one to be used + * as scratch space, the other to save the longest + * cycle. + */ + for (cnt = 0, n = graph; n; n = n->n_next) + ++cnt; + cycle_buf = + (NODE **)malloc((u_int)sizeof(NODE *) * cnt); + longest_cycle = + (NODE **)malloc((u_int)sizeof(NODE *) * cnt); + if (!cycle_buf || !longest_cycle) + no_memory(); + } + for (n = graph; n; n = n->n_next) + if (!(n->n_flags & NF_ACYCLIC)) { + if (cnt = find_cycle(n, n, 0, 0)) { + register int i; + + (void)fprintf(stderr, + "tsort: cycle in data.\n"); + for (i = 0; i < cnt; i++) + (void)fprintf(stderr, + "tsort: %s.\n", longest_cycle[i]->n_name); + remove_node(n); + break; + } else + /* to avoid further checks */ + n->n_flags = NF_ACYCLIC; + } + + if (!n) { + (void)fprintf(stderr, + "tsort: internal error -- could not find cycle.\n"); + exit(1); + } + } +} + +/* print node and remove from graph (does not actually free node) */ +void +remove_node(n) + register NODE *n; +{ + register NODE **np; + register int i; + + (void)printf("%s\n", n->n_name); + for (np = n->n_arcs, i = n->n_narcs; --i >= 0; np++) + --(*np)->n_refcnt; + n->n_narcs = 0; + *n->n_prevp = n->n_next; + if (n->n_next) + n->n_next->n_prevp = n->n_prevp; +} + +/* look for the longest cycle from node from to node to. */ +find_cycle(from, to, longest_len, depth) + NODE *from, *to; + int depth, longest_len; +{ + register NODE **np; + register int i, len; + + /* + * avoid infinite loops and ignore portions of the graph known + * to be acyclic + */ + if (from->n_flags & (NF_MARK|NF_ACYCLIC)) + return(0); + from->n_flags = NF_MARK; + + for (np = from->n_arcs, i = from->n_narcs; --i >= 0; np++) { + cycle_buf[depth] = *np; + if (*np == to) { + if (depth + 1 > longest_len) { + longest_len = depth + 1; + (void)memcpy((char *)longest_cycle, + (char *)cycle_buf, + longest_len * sizeof(NODE *)); + } + } else { + len = find_cycle(*np, to, longest_len, depth + 1); + if (len > longest_len) + longest_len = len; + } + } + from->n_flags &= ~NF_MARK; + return(longest_len); +} + +void +no_memory() +{ + (void)fprintf(stderr, "tsort: %s.\n", strerror(ENOMEM)); + exit(1); +} diff --git a/misc-utils/whereis.1 b/misc-utils/whereis.1 new file mode 100644 index 00000000..4d55ac6d --- /dev/null +++ b/misc-utils/whereis.1 @@ -0,0 +1,198 @@ +.\" Copyright (c) 1980, 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. +.\" +.\" @(#)whereis.1 from UCB 4.2 +.TH WHEREIS 1 "8 May 1994" +.SH NAME +whereis \- locate the binary, source, and manual page files for a command +.SH SYNOPSIS +.B whereis +[ +.B \-bmsu +] [ +.B \-BMS +.IR directory .\|.\|. +.B \-f +] +\fIfilename\fP\| +\&.\|.\|. +.IX "whereis command" "" "\fLwhereis\fP \(em find program" +.IX find "program \(em \fLwhereis\fP" +.IX "locate program" "" "locate program \(em \fLwhereis\fP" +.IX command locate "" "locate \(em \fLwhereis\fP" +.SH DESCRIPTION +.B whereis +locates source/binary and manuals sections for specified +files. +The supplied names are first stripped of leading pathname components +and any (single) trailing extension of the form +.BI . ext, +for example, +.BR .c . +Prefixes of +.B s. +resulting from use of source code control are also dealt with. +.B whereis +then attempts to locate the desired program in +a list of standard Linux places: +.IP +.nf +.ft B +/bin +/usr/bin +/etc +/usr/etc +/sbin +/usr/sbin +/usr/games +/usr/games/bin +/usr/emacs/etc +/usr/lib/emacs/19.22/etc +/usr/lib/emacs/19.23/etc +/usr/lib/emacs/19.24/etc +/usr/lib/emacs/19.25/etc +/usr/lib/emacs/19.26/etc +/usr/lib/emacs/19.27/etc +/usr/lib/emacs/19.28/etc +/usr/lib/emacs/19.29/etc +/usr/lib/emacs/19.30/etc +/usr/TeX/bin +/usr/tex/bin +/usr/interviews/bin/LINUX +/usr/bin/X11 +/usr/X11/bin +/usr/X11R5/bin +/usr/X11R6/bin +/usr/X386/bin +/usr/local/bin +/usr/local/etc +/usr/local/sbin +/usr/local/games +/usr/local/games/bin +/usr/local/emacs/etc +/usr/local/TeX/bin +/usr/local/tex/bin +/usr/local/bin/X11 + +/usr/contrib", +/usr/hosts", +/usr/include", + +/usr/g++-include", +.ft R +.fi +.SH OPTIONS +.TP +\fB\-b +Search only for binaries. +.TP +.B \-m +Search only for manual sections. +.TP +.B \-s +Search only for sources. +.TP +.B \-u +Search for unusual entries. A file is said to be unusual if it does +not have one entry of each requested type. +Thus +.RB ` "whereis\ \ \-m\ \ \-u\ \ *" ' +asks for those files in the current +directory which have no documentation. +.TP +.B \-B +Change or otherwise limit the places where +.B whereis +searches for binaries. +.TP +.B \-M +Change or otherwise limit the places where +.B whereis +searches for +manual sections. +.TP +.B \-S +Change or otherwise limit the places where +.B whereis +searches for sources. +.TP +.B \-f +Terminate the last directory list and signals the start of file names, +and +.I must +be used when any of the +.BR \-B , +.BR \-M , +or +.B \-S +options are used. +.SH EXAMPLE +Find all files in +.B /usr/bin +which are not documented +in +.B /usr/man/man1 +with source in +.BR /usr/src : +.IP +.nf +.ft B +example% cd /usr/bin +example% whereis \-u \-M /usr/man/man1 \-S /usr/src \-f * +.fi +.ft R +.SH FILES +.PD 0 +.TP 20 +.B /{bin,sbin,etc} +.TP +.B /usr/{lib,bin,old,new,local,games,include,etc,src,man,sbin, +.B X386,TeX,g++-include} +.TP +.B /usr/local/{X386,TeX,X11,include,lib,man,etc,bin,games, +.B emacs} +.TP +.B +.PD +.SH SEE ALSO +.BR chdir (2V) +.SH BUGS +Since +.B whereis +uses +.BR chdir (2V) +to run faster, pathnames given with the +.BR \-M , +.BR \-S , +or +.B \-B +must be full; that is, they must begin with a +.RB ` / '. diff --git a/misc-utils/whereis.c b/misc-utils/whereis.c new file mode 100644 index 00000000..f869040a --- /dev/null +++ b/misc-utils/whereis.c @@ -0,0 +1,458 @@ +/*- + * Copyright (c) 1980 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE 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) 1980 The Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)whereis.c 5.5 (Berkeley) 4/18/91"; +#endif /* not lint */ + +#include +#include +#include +#include + +static char *bindirs[] = { +#ifdef __linux__ + "/bin", + "/usr/bin", + "/etc", + "/usr/etc", + "/sbin", + "/usr/sbin", + "/usr/games", + "/usr/games/bin", + "/usr/emacs/etc", + "/usr/lib/emacs/19.22/etc", + "/usr/lib/emacs/19.23/etc", + "/usr/lib/emacs/19.24/etc", + "/usr/lib/emacs/19.25/etc", + "/usr/lib/emacs/19.26/etc", + "/usr/lib/emacs/19.27/etc", + "/usr/lib/emacs/19.28/etc", + "/usr/lib/emacs/19.29/etc", + "/usr/lib/emacs/19.30/etc", + "/usr/TeX/bin", + "/usr/tex/bin", + "/usr/interviews/bin/LINUX", + + "/usr/bin/X11", + "/usr/X11/bin", + "/usr/X11R5/bin", + "/usr/X11R6/bin", + "/usr/X386/bin", + + "/usr/local/bin", + "/usr/local/etc", + "/usr/local/sbin", + "/usr/local/games", + "/usr/local/games/bin", + "/usr/local/emacs/etc", + "/usr/local/TeX/bin", + "/usr/local/tex/bin", + "/usr/local/bin/X11", + + "/usr/contrib", + "/usr/hosts", + "/usr/include", + + "/usr/g++-include", +#else + "/bin", + "/sbin", + "/usr/ucb", + "/usr/bin", + "/usr/sbin", + "/usr/old", + "/usr/contrib", + "/usr/games", + "/usr/local", + "/usr/libexec", + "/usr/include", + "/usr/hosts", + "/usr/share", /*?*/ + "/etc", +#ifdef notdef + /* before reorg */ + "/etc", + "/bin", + "/usr/bin", + "/usr/games", + "/lib", + "/usr/ucb", + "/usr/lib", + "/usr/local", + "/usr/new", + "/usr/old", + "/usr/hosts", + "/usr/include", +#endif +#endif + 0 +}; +/* This needs to be redone - man pages live with sources */ +static char *mandirs[] = { + "/usr/man/man1", + "/usr/man/man2", + "/usr/man/man3", + "/usr/man/man4", + "/usr/man/man5", + "/usr/man/man6", + "/usr/man/man7", + "/usr/man/man8", +#ifdef __linux__ + "/usr/man/man9", +#endif + "/usr/man/manl", + "/usr/man/mann", + "/usr/man/mano", +#ifdef __linux__ + "/usr/X386/man/man1", + "/usr/X386/man/man2", + "/usr/X386/man/man3", + "/usr/X386/man/man4", + "/usr/X386/man/man5", + "/usr/X386/man/man6", + "/usr/X386/man/man7", + "/usr/X386/man/man8", + "/usr/X11/man/man1", + "/usr/X11/man/man2", + "/usr/X11/man/man3", + "/usr/X11/man/man4", + "/usr/X11/man/man5", + "/usr/X11/man/man6", + "/usr/X11/man/man7", + "/usr/X11/man/man8", + "/usr/TeX/man/man1", + "/usr/TeX/man/man2", + "/usr/TeX/man/man3", + "/usr/TeX/man/man4", + "/usr/TeX/man/man5", + "/usr/TeX/man/man6", + "/usr/TeX/man/man7", + "/usr/TeX/man/man8", + "/usr/interviews/man/mann", +#endif + 0 +}; +static char *srcdirs[] = { + "/usr/src/bin", + "/usr/src/sbin", + "/usr/src/etc", + "/usr/src/pgrm", + "/usr/src/usr.bin", + "/usr/src/usr.sbin", + "/usr/src/usr.ucb", + "/usr/src/usr.new", + "/usr/src/usr.lib", + "/usr/src/libexec", + "/usr/src/libdata", + "/usr/src/share", + "/usr/src/contrib", + "/usr/src/athena", + "/usr/src/devel", + "/usr/src/games", + "/usr/src/local", + "/usr/src/man", + "/usr/src/root", + "/usr/src/old", + "/usr/src/include", + /* still need libs */ +#ifdef notdef /* before reorg */ + "/usr/src/bin", + "/usr/src/usr.bin", + "/usr/src/etc", + "/usr/src/ucb", + "/usr/src/games", + "/usr/src/usr.lib", + "/usr/src/lib", + "/usr/src/local", + "/usr/src/new", + "/usr/src/old", + "/usr/src/include", + "/usr/src/lib/libc/gen", + "/usr/src/lib/libc/stdio", + "/usr/src/lib/libc/sys", + "/usr/src/lib/libc/net/common", + "/usr/src/lib/libc/net/inet", + "/usr/src/lib/libc/net/misc", + "/usr/src/ucb/pascal", + "/usr/src/ucb/pascal/utilities", + "/usr/src/undoc", +#endif + 0 +}; + +char sflag = 1; +char bflag = 1; +char mflag = 1; +char **Sflag; +int Scnt; +char **Bflag; +int Bcnt; +char **Mflag; +int Mcnt; +char uflag; +/* + * whereis name + * look for source, documentation and binaries + */ +main(argc, argv) + int argc; + char *argv[]; +{ + + argc--, argv++; + if (argc == 0) { +usage: + fprintf(stderr, "whereis [ -sbmu ] [ -SBM dir ... -f ] name...\n"); + exit(1); + } + do + if (argv[0][0] == '-') { + register char *cp = argv[0] + 1; + while (*cp) switch (*cp++) { + + case 'f': + break; + + case 'S': + getlist(&argc, &argv, &Sflag, &Scnt); + break; + + case 'B': + getlist(&argc, &argv, &Bflag, &Bcnt); + break; + + case 'M': + getlist(&argc, &argv, &Mflag, &Mcnt); + break; + + case 's': + zerof(); + sflag++; + continue; + + case 'u': + uflag++; + continue; + + case 'b': + zerof(); + bflag++; + continue; + + case 'm': + zerof(); + mflag++; + continue; + + default: + goto usage; + } + argv++; + } else + lookup(*argv++); + while (--argc > 0); + exit(0); +} + +getlist(argcp, argvp, flagp, cntp) + char ***argvp; + int *argcp; + char ***flagp; + int *cntp; +{ + + (*argvp)++; + *flagp = *argvp; + *cntp = 0; + for ((*argcp)--; *argcp > 0 && (*argvp)[0][0] != '-'; (*argcp)--) + (*cntp)++, (*argvp)++; + (*argcp)++; + (*argvp)--; +} + + +zerof() +{ + + if (sflag && bflag && mflag) + sflag = bflag = mflag = 0; +} +int count; +int print; + + +lookup(cp) + register char *cp; +{ + register char *dp; + + for (dp = cp; *dp; dp++) + continue; + for (; dp > cp; dp--) { + if (*dp == '.') { + *dp = 0; + break; + } + } + for (dp = cp; *dp; dp++) + if (*dp == '/') + cp = dp + 1; + if (uflag) { + print = 0; + count = 0; + } else + print = 1; +again: + if (print) + printf("%s:", cp); + if (sflag) { + looksrc(cp); + if (uflag && print == 0 && count != 1) { + print = 1; + goto again; + } + } + count = 0; + if (bflag) { + lookbin(cp); + if (uflag && print == 0 && count != 1) { + print = 1; + goto again; + } + } + count = 0; + if (mflag) { + lookman(cp); + if (uflag && print == 0 && count != 1) { + print = 1; + goto again; + } + } + if (print) + printf("\n"); +} + +looksrc(cp) + char *cp; +{ + if (Sflag == 0) { + find(srcdirs, cp); + } else + findv(Sflag, Scnt, cp); +} + +lookbin(cp) + char *cp; +{ + if (Bflag == 0) + find(bindirs, cp); + else + findv(Bflag, Bcnt, cp); +} + +lookman(cp) + char *cp; +{ + if (Mflag == 0) { + find(mandirs, cp); + } else + findv(Mflag, Mcnt, cp); +} + +findv(dirv, dirc, cp) + char **dirv; + int dirc; + char *cp; +{ + + while (dirc > 0) + findin(*dirv++, cp), dirc--; +} + +find(dirs, cp) + char **dirs; + char *cp; +{ + + while (*dirs) + findin(*dirs++, cp); +} + +findin(dir, cp) + char *dir, *cp; +{ + DIR *dirp; + struct direct *dp; + + dirp = opendir(dir); + if (dirp == NULL) + return; + while ((dp = readdir(dirp)) != NULL) { + if (itsit(cp, dp->d_name)) { + count++; + if (print) + printf(" %s/%s", dir, dp->d_name); + } + } + closedir(dirp); +} + +itsit(cp, dp) + register char *cp, *dp; +{ + register int i = strlen(dp); + + if (dp[0] == 's' && dp[1] == '.' && itsit(cp, dp+2)) + return (1); + while (*cp && *dp && *cp == *dp) + cp++, dp++, i--; + if (*cp == 0 && *dp == 0) + return (1); + while (isdigit(*dp)) + dp++; + if (*cp == 0 && *dp++ == '.') { + --i; + while (i > 0 && *dp) + if (--i, *dp++ == '.') + return (*dp++ == 'C' && *dp++ == 0); + return (1); + } + return (0); +} diff --git a/misc-utils/write.1 b/misc-utils/write.1 new file mode 100644 index 00000000..5125e156 --- /dev/null +++ b/misc-utils/write.1 @@ -0,0 +1,110 @@ +.\" Copyright (c) 1989 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Jef Poskanzer and Craig Leres of the Lawrence Berkeley Laboratory. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)write.1 6.5 (Berkeley) 4/24/91 +.\" +.\" Modified for Linux, Mon Mar 8 18:22:44 1993, faith@cs.unc.edu +.\" +.Dd March 8, 1993 +.Dt WRITE 1 +.Os "Linux 0.99" +.Sh NAME +.Nm write +.Nd send a message to another user +.Sh SYNOPSIS +.Nm write +.Ar user +.Op Ar ttyname +.Sh DESCRIPTION +.Nm Write +allows you to communicate with other users, by copying lines from +your terminal to theirs. +.Pp +When you run the +.Nm write +command, the user you are writing to gets a message of the form: +.Pp +.Dl Message from yourname@yourhost on yourtty at hh:mm ... +.Pp +Any further lines you enter will be copied to the specified user's +terminal. +If the other user wants to reply, they must run +.Nm write +as well. +.Pp +When you are done, type an end-of-file or interrupt character. +The other user will see the message +.Ql EOF +indicating that the +conversation is over. +.Pp +You can prevent people (other than the super-user) from writing to you +with the +.Xr mesg 1 +command. +Some commands, for example +.Xr nroff 1 +and +.Xr pr 1 , +disallow writing automatically, so that your output isn't overwritten. +.Pp +If the user you want to write to is logged in on more than one terminal, +you can specify which terminal to write to by specifying the terminal +name as the second operand to the +.Nm write +command. +Alternatively, you can let +.Nm write +select one of the terminals \- it will pick the one with the shortest +idle time. +This is so that if the user is logged in at work and also dialed up from +home, the message will go to the right place. +.Pp +The traditional protocol for writing to someone is that the string +.Ql \-o , +either at the end of a line or on a line by itself, means that it's the +other person's turn to talk. +The string +.Ql oo +means that the person believes the conversation to be +over. +.Sh SEE ALSO +.Xr mesg 1 , +.Xr talk 1 , +.Xr who 1 +.Sh HISTORY +A +.Nm +command appeared in +.At v6 . diff --git a/misc-utils/write.c b/misc-utils/write.c new file mode 100644 index 00000000..f2fe4a08 --- /dev/null +++ b/misc-utils/write.c @@ -0,0 +1,347 @@ +/* + * Copyright (c) 1989 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jef Poskanzer and Craig Leres of the Lawrence Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Modified for Linux, Mon Mar 8 18:16:24 1993, faith@cs.unc.edu + * Wed Jun 22 21:41:56 1994, faith@cs.unc.edu: + * Added fix from Mike Grupenhoff (kashmir@umiacs.umd.edu) + * + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1989 The Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)write.c 4.22 (Berkeley) 6/1/90"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __linux__ +#include +#endif + +extern int errno; + +main(argc, argv) + int argc; + char **argv; +{ + register char *cp; + time_t atime; + uid_t myuid; + int msgsok, myttyfd; + char tty[MAXPATHLEN], *mytty, *ttyname(); + void done(); + + /* check that sender has write enabled */ + if (isatty(fileno(stdin))) + myttyfd = fileno(stdin); + else if (isatty(fileno(stdout))) + myttyfd = fileno(stdout); + else if (isatty(fileno(stderr))) + myttyfd = fileno(stderr); + else { + (void)fprintf(stderr, "write: can't find your tty\n"); + exit(1); + } + if (!(mytty = ttyname(myttyfd))) { + (void)fprintf(stderr, "write: can't find your tty's name\n"); + exit(1); + } + if (cp = rindex(mytty, '/')) + mytty = cp + 1; + if (term_chk(mytty, &msgsok, &atime, 1)) + exit(1); + if (!msgsok) { + (void)fprintf(stderr, + "write: you have write permission turned off.\n"); + exit(1); + } + + myuid = getuid(); + + /* check args */ + switch (argc) { + case 2: + search_utmp(argv[1], tty, mytty, myuid); + do_write(tty, mytty, myuid); + break; + case 3: + if (!strncmp(argv[2], "/dev/", 5)) + argv[2] += 5; + if (utmp_chk(argv[1], argv[2])) { + (void)fprintf(stderr, + "write: %s is not logged in on %s.\n", + argv[1], argv[2]); + exit(1); + } + if (term_chk(argv[2], &msgsok, &atime, 1)) + exit(1); + if (myuid && !msgsok) { + (void)fprintf(stderr, + "write: %s has messages disabled on %s\n", + argv[1], argv[2]); + exit(1); + } + do_write(argv[2], mytty, myuid); + break; + default: + (void)fprintf(stderr, "usage: write user [tty]\n"); + exit(1); + } + done(); + /* NOTREACHED */ +} + +/* + * utmp_chk - checks that the given user is actually logged in on + * the given tty + */ +utmp_chk(user, tty) + char *user, *tty; +{ + struct utmp u; + int ufd; + + if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0) + return(0); /* ignore error, shouldn't happen anyway */ + + while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u)) + if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0 && + strncmp(tty, u.ut_line, sizeof(u.ut_line)) == 0) { + (void)close(ufd); + return(0); + } + + (void)close(ufd); + return(1); +} + +/* + * search_utmp - search utmp for the "best" terminal to write to + * + * Ignores terminals with messages disabled, and of the rest, returns + * the one with the most recent access time. Returns as value the number + * of the user's terminals with messages enabled, or -1 if the user is + * not logged in at all. + * + * Special case for writing to yourself - ignore the terminal you're + * writing from, unless that's the only terminal with messages enabled. + */ +search_utmp(user, tty, mytty, myuid) + char *user, *tty, *mytty; + uid_t myuid; +{ + struct utmp u; + time_t bestatime, atime; + int ufd, nloggedttys, nttys, msgsok, user_is_me; +#ifdef __linux__ + char atty[sizeof(u.ut_line) + 1]; +#else + char atty[UT_LINESIZE + 1]; +#endif + + if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0) { + perror("utmp"); + exit(1); + } + + nloggedttys = nttys = 0; + bestatime = 0; + user_is_me = 0; + while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u)) + if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0) { + ++nloggedttys; +#ifdef __linux__ + (void)strncpy(atty, u.ut_line, sizeof(u.ut_line)); + atty[sizeof(u.ut_line)] = '\0'; +#else + (void)strncpy(atty, u.ut_line, UT_LINESIZE); + atty[UT_LINESIZE] = '\0'; +#endif + if (term_chk(atty, &msgsok, &atime, 0)) + continue; /* bad term? skip */ + if (myuid && !msgsok) + continue; /* skip ttys with msgs off */ + if (strcmp(atty, mytty) == 0) { + user_is_me = 1; + continue; /* don't write to yourself */ + } +#ifdef __linux__ + if (u.ut_type != USER_PROCESS) + continue; /* it's not a valid entry */ +#endif + ++nttys; + if (atime > bestatime) { + bestatime = atime; + (void)strcpy(tty, atty); + } + } + + (void)close(ufd); + if (nloggedttys == 0) { + (void)fprintf(stderr, "write: %s is not logged in\n", user); + exit(1); + } + if (nttys == 0) { + if (user_is_me) { /* ok, so write to yourself! */ + (void)strcpy(tty, mytty); + return; + } + (void)fprintf(stderr, + "write: %s has messages disabled\n", user); + exit(1); + } else if (nttys > 1) { + (void)fprintf(stderr, + "write: %s is logged in more than once; writing to %s\n", + user, tty); + } +} + +/* + * term_chk - check that a terminal exists, and get the message bit + * and the access time + */ +term_chk(tty, msgsokP, atimeP, showerror) + char *tty; + int *msgsokP, showerror; + time_t *atimeP; +{ + struct stat s; + char path[MAXPATHLEN]; + + (void)sprintf(path, "/dev/%s", tty); + if (stat(path, &s) < 0) { + if (showerror) + (void)fprintf(stderr, + "write: %s: %s\n", path, strerror(errno)); + return(1); + } + *msgsokP = (s.st_mode & (S_IWRITE >> 3)) != 0; /* group write bit */ + *atimeP = s.st_atime; + return(0); +} + +/* + * do_write - actually make the connection + */ +do_write(tty, mytty, myuid) + char *tty, *mytty; + uid_t myuid; +{ + register char *login, *nows; + register struct passwd *pwd; + time_t now, time(); + char *getlogin(), path[MAXPATHLEN], host[MAXHOSTNAMELEN], line[512]; + void done(); + + /* Determine our login name before the we reopen() stdout */ + if ((login = getlogin()) == NULL) + if (pwd = getpwuid(myuid)) + login = pwd->pw_name; + else + login = "???"; + + (void)sprintf(path, "/dev/%s", tty); + if ((freopen(path, "w", stdout)) == NULL) { + (void)fprintf(stderr, "write: %s: %s\n", path, strerror(errno)); + exit(1); + } + + (void)signal(SIGINT, done); + (void)signal(SIGHUP, done); + + /* print greeting */ + if (gethostname(host, sizeof(host)) < 0) + (void)strcpy(host, "???"); + now = time((time_t *)NULL); + nows = ctime(&now); + nows[16] = '\0'; + (void)printf("\r\n\007\007\007Message from %s@%s on %s at %s ...\r\n", + login, host, mytty, nows + 11); + + while (fgets(line, sizeof(line), stdin) != NULL) + wr_fputs(line); +} + +/* + * done - cleanup and exit + */ +void +done() +{ + (void)printf("EOF\r\n"); + exit(0); +} + +/* + * wr_fputs - like fputs(), but makes control characters visible and + * turns \n into \r\n + */ +wr_fputs(s) + register char *s; +{ + register char c; + +#define PUTC(c) if (putchar(c) == EOF) goto err; + + for (; *s != '\0'; ++s) { + c = toascii(*s); + if (c == '\n') { + PUTC('\r'); + PUTC('\n'); + } else if (!isprint(c) && !isspace(c) && c != '\007') { + PUTC('^'); + PUTC(c^0x40); /* DEL to ?, others to alpha */ + } else + PUTC(c); + } + return; + +err: (void)fprintf(stderr, "write: %s\n", strerror(errno)); + exit(1); +#undef PUTC +} diff --git a/mount/Makefile b/mount/Makefile new file mode 100644 index 00000000..737c716a --- /dev/null +++ b/mount/Makefile @@ -0,0 +1,93 @@ +# To make "ext" the default file system type for mount +# (used when no other type is specified), replace \"minix\" by \"ext2\". +DEFAULT_FSTYPE=\"minix\" + +# you need rpcgen and libc-4.2 or rpclib to compile in the NFS support +DEFINES = -DHAVE_NFS -DFSTYPE_DEFAULT=$(DEFAULT_FSTYPE) + +include ../MCONFIG +#CC = gcc +#OPTFLAGS= -O2 -m486 -fomit-frame-pointer +##OPTFLAGS= -O2 -fomit-frame-pointer # or change on make's command line +#CFLAGS = -pipe $(OPTFLAGS) +WARNFLAGS = -Wall -Wstrict-prototypes -Wmissing-prototypes +#LDFLAGS = -s -N +LDLIBS = +RPCSVCDIR = rpcsvc +RPC_CFLAGS = -Wno-unused +RPCGEN = rpcgen +#INSTALL = install +#INSTALL_SUID = $(INSTALL) -m 4755 -o root +#INSTALL_PROG = $(INSTALL) -m 755 +#INSTALL_DATA = $(INSTALL) -m 644 +#prefix = /usr + +## for suid progs (mount, umount) +#BINDIR = /bin +## for nosuid progs (swapon) +#SBINDIR = /etc + +# End of configuration section. + +COMPILE = $(CC) -c $(WARNFLAGS) $(CFLAGS) $(DEFINES) +LINK = $(CC) $(LDFLAGS) + +SUID_PROGS = mount umount +NOSUID_PROGS = swapon +PROGS = $(SUID_PROGS) $(NOSUID_PROGS) +MAN5 = fstab.5 nfs.5 +MAN8 = mount.8 swapoff.8 swapon.8 umount.8 + +# comment these out if you are not compiling in NFS support +NFS_OBJS = nfsmount.o mount_xdr.o mount_clnt.o +# uncomment this if you don't have libc-4.2 but do have the rpclib +GEN_FILES = mount.x mount.h mount_xdr.c mount_clnt.c + +# comment these out if you are not compiling in loop support +LO_OBJS=lomount.o + +all: $(PROGS) + +install: $(PROGS) + $(INSTALLDIR) $(BINDIR) $(SBINDIR) + $(INSTALLSUID) -s $(SUID_PROGS) $(BINDIR) + $(INSTALLBIN) -s $(NOSUID_PROGS) $(SBINDIR) + (cd $(SBINDIR); ln -sf swapon swapoff) + $(INSTALLDIR) $(MAN5DIR) $(MAN8DIR) + $(INSTALLMAN) $(MAN5) $(MAN5DIR) + $(INSTALLMAN) $(MAN8) $(MAN8DIR) + +.c.o: + $(COMPILE) $< + +mount: mount.o fstab.o sundries.o version.o $(NFS_OBJS) $(LO_OBJS) + $(LINK) $^ $(LDLIBS) -o $@ + +umount: umount.o fstab.o sundries.o version.o $(LO_OBJS) + $(LINK) $^ $(LDLIBS) -o $@ + +swapon: swapon.o fstab.o version.o + $(LINK) $^ $(LDLIBS) -o $@ + +nfsmount.o mount_xdr.o mount_clnt.o: mount.h + +mount_clnt.o: mount_clnt.c + $(COMPILE) $(RPC_CFLAGS) mount_clnt.c + +mount_xdr.o: mount_xdr.c + $(COMPILE) $(RPC_CFLAGS) mount_xdr.c + +mount.h mount_xdr.c mount_clnt.c: mount.x + rm -f mount.h mount_xdr.c mount_clnt.c + $(RPCGEN) -h -o mount.h mount.x + $(RPCGEN) -c -o mount_xdr.c mount.x + $(RPCGEN) -l -o mount_clnt.c mount.x + +mount.x: + cp $(RPCSVCDIR)/mount.x . + +clean: + rm -f a.out core *~ *.o $(PROGS) + +clobber: clean + rm -f $(PROGS) $(GEN_FILES) diff --git a/mount/README.mount b/mount/README.mount new file mode 100644 index 00000000..9bea2dc2 --- /dev/null +++ b/mount/README.mount @@ -0,0 +1,147 @@ +mount/umount for Linux 0.99.14 +============================== + +Enhance nfsmount.c to allow the program number or port number +to be specified for both the mount daemon and the nfs daemon. +Also anticipate tcp and namelen support. + +Rewrite canonicalize in terms of realpath. Don't be obsessive about +the path pre-existing for nfs, ifs, none, etc. + +Fix memory overwriting bug in the new remount code. + +Fix mtab handling in the new remount code so entries appear +exactly once and in their proper mounting order. + +Fix defaults, remount and noauto so these options don't appear in the mtab. + +Repair extra options handling that got damaged with the remount code. + +Handle combining -o from the command line with options specified +in /etc/mtab or /etc/fstab. + +Fix completely broken file-locking. + +Beautify the options field so it contains no duplicates or redundancies. + +Added long-style options to all programs. + +Added version and help options to all programs. + +Brought the Makefile up to GNU standards regarding CFLAGS and LDLFLAGS. + +Added support for the `user' option where mount and umount run suid to root. + +Rick Sladkey + +mount/umount for Linux 0.99.10 +============================== + +[Stephen Tweedie ] + +A number of changes introduced to cater for new kernel facilities. +mount can now remount an already-mounted filesystem, and umount +attempts to unmount even root filesystems. Supercedes the [u]mount +previously available in the bootutils-0.1 collection. + +Fixed a minor bug in canonicalise(). + +mount/umount/swapon/swapoff(8) for Linux 0.99.6 +=============================================== + +Here is a minor update to the previous version that fixes +a longstanding "off by one" bug in parsing fs-specific +options. No other real changes. + +mount/umount/swapon/swapoff(8) for Linux 0.99.2 +=============================================== + +Here is a new version of Doug Quale's mount/umount package that +includes support for mounting and unmount NFS filesystems. It is +still possible to compile it without NFS support by modifying the +Makefile. Even if you don't have rpcgen, but do have libc-4.2 +you can "cp -p" the pre-generated files in the rpcsvc directory +into the mount source directory. + +The primary difference besides the actual NFS mounting code is that +mount understands hostname:/path syntax for the "device" as well as +the new keyword "none" which is useful for the proc filesystem. Also, +umount had to be trained to specify the mount-point instead of the +device when unmounting such filesystems. For compatibility, +filesystems with true devices are unmounted using their device name +which will still work with older kernels. However, all umounts could +just as well be done by specifying the mount point instead of the +device. + +Other changes since the beta NFS mount are: + +* incorportated H.J. Lu's changes for mtab permissions and errno handling +* corrected the error message for unhandled errors from mount and umount +* improved (a little :-) the reporting of handled mount and umount errors +* added the ability to NFS mount from a IP address as well as a hostname +* added a string error message instead of numeric for failed NFS mounts +* changed 32 to _NSIG when setting all signals (should be using sigismember) +* eliminated the obsolete HAVE_MOUNT5 and HAVE_SWAPOFF ifdefs +* added support for the sync and async mount options +* added the noauto option for fstab entries that shouldn't get mounted with -a +* changed mount -a to check the mtab for already mounted filesystems +* eliminated a few new warning messages from gcc 2.3.3 +* wrote an nfs man page + +Features still missing: + +* ability to background NFS mounts that have timed out +* notify the NFS server of umounts (but addr=ip-addr support is in there) +* add the possibility of interrupting an in-progress mount +* man pages for the other Linux filesystem types + +Rick Sladkey +jrs@world.std.com +=============================================== +mount/umount/swapon/swapoff(8) for Linux 0.98.5 +=============================================== + +This version fixed the umask of root. fchmod () is called +before close /etc/mtab. This version should work with +0.97.3 or above, although I only tested it under 0.98.5. +I also fixed the error report. + +H.J. Lu +hlu@eecs.wsu.edu +11/25/92 +=============================================== +mount/umount/swapon/swapoff(8) for Linux 0.97.3 +=============================================== + +The most significant improvement over the first release is the repair of +at least a half dozen really dumb bugs, mostly involving null pointers. +These bugs caused frequent core dumps and really made the code unusable. + +Some race conditions in the lock handling code have been removed. + +Swapoff is available for 0.97.3 and later kernels. + +Swapon supports multiple swap files. In particular, swapon -a will try +to enable swapping on all the swap entries in /etc/fstab. + +File system specific mount options are now supported. This is of particular +utility with Werner Almesberger's msdos fs. + +Umount -a now reads /etc/mtab instead of /etc/fstab (thanks to David +Engel for a valuable discussion on this and other points). In addition, +it umounts the entries in reverse order, ensuring that it tries to umount +/usr/spool before /usr, for instance. + +Mount will now print mtab for ordinary users as well as for the superuser. +Several people pointed out this deficiency, and it was a real no-brainer +that broke it in the first release. + +Thanks to Linus, for another great release. 0.97.3 compiled the first time +out and is working flawlessly. Thanks also to Ross Biro, for his work on +Linux TCP/IP which has made it much easier to get this little thing off my +machine. Special thanks to everyone who put up with my bugs. + +Brickbats etc. to + +Doug Quale +quale@saavik.cs.wisc.edu diff --git a/mount/fstab.5 b/mount/fstab.5 new file mode 100644 index 00000000..8f96ee3b --- /dev/null +++ b/mount/fstab.5 @@ -0,0 +1,168 @@ +.\" Copyright (c) 1980, 1989, 1991 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)fstab.5 6.5 (Berkeley) 5/10/91 +.\" +.\" Modified Sat Mar 6 20:45:03 1993, faith@cs.unc.edu, for Linux +.\" Sat Oct 9 10:07:10 1993: converted to man format by faith@cs.unc.edu +.\" Sat Nov 20 20:47:38 1993: hpfs documentation added +.\" Sat Nov 27 20:23:32 1993: Updated authorship information +.\" +.TH FSTAB 5 "27 November 1993" "Linux 0.99" "Linux Programmer's Manual" +.SH NAME +fstab \- static information about the filesystems +.SH SYNOPSIS +.B #include +.SH DESCRIPTION +The file +.B fstab +contains descriptive information about the various file systems. +.B fstab +is only read by programs, and not written; it is the duty of the system +administrator to properly create and maintain this file. Each filesystem +is described on a separate line; fields on each line are separated by tabs +or spaces. The order of records in +.B fstab +is important because +.BR fsck "(8), " mount "(8), and " umount "(8) +sequentially iterate through +.B fstab +doing their thing. + +The first field, +.RI ( fs_spec ), +describes the block special device or +remote filesystem to be mounted. + +The second field, +.RI ( fs_file ), +describes the mount point for the filesystem. For swap partitions, this +field should be specified as ``none''. + +The third field, +.RI ( fs_vfstype ), +describes the type of the filesystem. The system currently supports three +types of filesystems: +.TP +.I minix +a local filesystem, supporting filenames of length 14 or 30 characters. +.TP +.I ext +a local filesystem with longer filenames and larger inodes. This +filesystem has been replaced by the +.I ext2 +file system, and should no longer be used. +.TP +.I ext2 +a local filesystem with longer filenames, larger inodes, and lots of other +features. +.TP +.I xiafs +a local filesystem with longer filenames, larger inodes, and lots of other +features. +.TP +.I msdos +a local filesystem for MS-DOS partitions. +.TP +.I hpfs +a local filesystem for HPFS partitions. +.TP +.I iso9660 +a local filesystem used for CD-ROM drives. +.TP +.I nfs +a filesystem for mounting partitions from remote systems. +.TP +.I swap +a disk partition to be used for swapping. +.PP +If +.I vfs_fstype +is specified as ``ignore'' the entry is ignored. This is useful to show +disk partitions which are currently unused. + +The fourth field, +.RI ( fs_mntops ), +describes the mount options associated with the filesystem. + +It is formatted as a comma separated list of options. It contains at least +the type of mount plus any additional options appropriate to the filesystem +type. For documentation on all of the available options, see +.BR mount (8). + +The fifth field, +.RI ( fs_freq ), +is used for these filesystems by the +.BR dump (8) +command to determine which filesystems need to be dumped. If the fifth +field is not present, a value of zero is returned and +.B dump +will assume that the filesystem does not need to be dumped. + +The sixth field, +.RI ( fs_passno ), +is used by the +.BR fsck (8) +program to determine the order in which filesystem checks are done at +reboot time. The root filesystem should be specified with a +.I fs_passno +of 1, and other filesystems should have a +.I fs_passno +of 2. Filesystems within a drive will be checked sequentially, but +filesystems on different drives will be checked at the same time to utilize +parallelism available in the hardware. If the sixth field is not present +or zero, a value of zero is returned and +.B fsck +will assume that the filesystem does not need to be checked. + +The proper way to read records from +.B fstab +is to use the routines +.BR getmntent (3). +.SH FILES +.I /etc/fstab +The file +.B fstab +resides in +.IR /etc . +.SH BUGS +Linux does not, currently, support the special fields for +.BR dump " and " fsck . + +The documentation in +.BR mount (8) +is often more up-to-date. +.SH "SEE ALSO" +.BR getmntent "(3), " mount "(8), " swapon (8) +.SH HISTORY +The +.B fstab +file format appeared in 4.0BSD. diff --git a/mount/fstab.c b/mount/fstab.c new file mode 100644 index 00000000..95b0879e --- /dev/null +++ b/mount/fstab.c @@ -0,0 +1,92 @@ +/* /home/faith/cvs/util-linux/mount/fstab.c,v 1.1.1.1 1995/02/22 19:09:21 faith Exp */ + +#include "fstab.h" +#include + +#define streq(s, t) (strcmp ((s), (t)) == 0) + +/* These routines are superceded by mntent(3), but I use them for + convenience. Mntent(3) is used in the implementation, so be + very careful about the static buffers that are returned. */ + + +static FILE *F_fstab = NULL; + +/* Open fstab or rewind if already open. */ +int +setfsent (void) +{ + if (F_fstab) + return (fseek (F_fstab, 0L, SEEK_SET) == 0); + + F_fstab = setmntent (_PATH_FSTAB, "r"); + return (F_fstab != NULL); +} + +/* Close fstab. */ +void +endfsent (void) +{ + endmntent (F_fstab); +} + +/* Return next entry in fstab, skipping ignore entries. I also put + in some ugly hacks here to skip comments and blank lines. */ +struct mntent * +getfsent (void) +{ + struct mntent *fstab; + + if (!F_fstab && !setfsent()) + return 0; + + for (;;) + { + fstab = getmntent (F_fstab); + if (fstab == NULL) + { + if (!feof (F_fstab) && !ferror (F_fstab)) + continue; + else + break; + } + else if ((*fstab->mnt_fsname != '#') + && !streq (fstab->mnt_type, MNTTYPE_IGNORE)) + break; + } + return fstab; +} + +/* Find the dir FILE in fstab. */ +struct mntent * +getfsfile (const char *file) +{ + struct mntent *fstab; + + /* Open or rewind fstab. */ + if (!setfsent ()) + return 0; + + while ((fstab = getfsent ())) + if (streq (fstab->mnt_dir, file)) + break; + + return fstab; +} + +/* Find the device SPEC in fstab. */ +struct mntent * +getfsspec (const char *spec) +{ + struct mntent *fstab; + + /* Open or rewind fstab. */ + if (!setfsent()) + return 0; + + while ((fstab = getfsent ())) + if (streq (fstab->mnt_fsname, spec)) + break; + + return fstab; +} diff --git a/mount/fstab.h b/mount/fstab.h new file mode 100644 index 00000000..c3c48b9e --- /dev/null +++ b/mount/fstab.h @@ -0,0 +1,25 @@ +/* The fsent(3) routines are obsoleted by mntent(3). I use them for + convenience. Since the implementation uses mntent(3), be very + careful with the static buffers returned. + /home/faith/cvs/util-linux/mount/fstab.h,v 1.1.1.1 1995/02/22 19:09:21 faith Exp */ + +#ifndef _FSTAB_H +#include +#include + +#define _PATH_FSTAB "/etc/fstab" + +/* Translate fsent(3) stuff into mntent(3) stuff. + In general this won't work, but it's good enough here. */ +#define fstab mntent +#define fs_type mnt_type +#define fs_spec mnt_fsname +#define FSTAB_SW MNTTYPE_SWAP + +struct fstab *getfsent (void); +struct fstab *getfsspec (const char *spec); +struct fstab *getfsfile (const char *file); +int setfsent (void); +void endfsent (void); + +#endif /* _FSTAB_H */ diff --git a/mount/lomount.c b/mount/lomount.c new file mode 100644 index 00000000..5dd02d8b --- /dev/null +++ b/mount/lomount.c @@ -0,0 +1,223 @@ +/* Taken from Ted's losetup.c - Mitch */ + +/* + * losetup.c - setup and control loop devices + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "loop.h" + +char *crypt_name (int); +int crypt_type (char *); +void show_loop (char *); +int del_loop (const char *); +int set_loop (const char *, const char *, int offset, char *); +int lomount (const char *, const char *, const char *, char **, + int *, char **, char **); + +struct crypt_type_struct { + int id; + char *name; +} crypt_type_tbl[] = { + + { + LO_CRYPT_NONE, "no" + }, + { + LO_CRYPT_NONE, "none" + }, + { + LO_CRYPT_XOR, "xor" + }, + { + LO_CRYPT_DES, "DES" + }, + { + -1, NULL + } +}; + +char * +crypt_name (int id) +{ + int i; + + for (i = 0; crypt_type_tbl[i].id != -1; i++) + if (id == crypt_type_tbl[i].id) + return crypt_type_tbl[i].name; + return "undefined"; +} + +int +crypt_type (char *name) +{ + int i; + + for (i = 0; crypt_type_tbl[i].id != -1; i++) + if (!strcasecmp (name, crypt_type_tbl[i].name)) + return crypt_type_tbl[i].id; + return -1; +} + +void +show_loop (char *device) +{ + struct loop_info loopinfo; + int fd; + + if ((fd = open (device, O_RDWR)) < 0) { + fprintf(stderr, "loop: can't open device %s: %s\n", + device, strerror (errno)); + return; + } + if (ioctl (fd, LOOP_GET_STATUS, &loopinfo) < 0) { + fprintf(stderr, "loop: can't get info on device %s: %s\n", + device, strerror (errno)); + close (fd); + return; + } + printf ("%s: [%04x]:%ld (%s) offset %d, %s encryption\n", + device, loopinfo.lo_device, loopinfo.lo_inode, + loopinfo.lo_name, loopinfo.lo_offset, + crypt_name (loopinfo.lo_encrypt_type)); + close (fd); +} + +int +set_loop (const char *device, const char *file, int offset, char *encryption) +{ + struct loop_info loopinfo; + int fd, + ffd, + i; + char *pass; + + if ((fd = open (device, O_RDWR)) < 0) { + perror (device); + return 1; + } + if ((ffd = open (file, O_RDWR)) < 0) { + perror (file); + return 1; + } + memset (&loopinfo, 0, sizeof (loopinfo)); + strncpy (loopinfo.lo_name, file, LO_NAME_SIZE); + loopinfo.lo_name[LO_NAME_SIZE - 1] = 0; + if (encryption && (loopinfo.lo_encrypt_type = crypt_type (encryption)) + < 0) { + fprintf (stderr, "Unsupported encryption type %s", encryption); + return 1; + } + loopinfo.lo_offset = offset; + switch (loopinfo.lo_encrypt_type) { + case LO_CRYPT_NONE: + loopinfo.lo_encrypt_key_size = 0; + break; + case LO_CRYPT_XOR: + pass = getpass ("Password: "); + strncpy (loopinfo.lo_encrypt_key, pass, LO_KEY_SIZE); + loopinfo.lo_encrypt_key[LO_KEY_SIZE - 1] = 0; + loopinfo.lo_encrypt_key_size = strlen (loopinfo.lo_encrypt_key); + break; + case LO_CRYPT_DES: + pass = getpass ("Password: "); + strncpy (loopinfo.lo_encrypt_key, pass, 8); + loopinfo.lo_encrypt_key[8] = 0; + loopinfo.lo_encrypt_key_size = 8; + pass = getpass ("Init (up to 16 hex digits): "); + for (i = 0; i < 16 && pass[i]; i++) + if (isxdigit (pass[i])) + loopinfo.lo_init[i >> 3] |= (pass[i] > '9' ? + (islower (pass[i]) ? toupper (pass[i]) : + pass[i]) - 'A' + 10 : pass[i] - '0') << (i & 7) * 4; + else { + fprintf (stderr, "Non-hex digit '%c'.\n", pass[i]); + return 1; + } + break; + default: + fprintf (stderr, + "Don't know how to get key for encryption system %d\n", + loopinfo.lo_encrypt_type); + return 1; + } + if (ioctl (fd, LOOP_SET_FD, ffd) < 0) { + perror ("ioctl: LOOP_SET_FD"); + return 1; + } + if (ioctl (fd, LOOP_SET_STATUS, &loopinfo) < 0) { + (void) ioctl (fd, LOOP_CLR_FD, 0); + perror ("ioctl: LOOP_SET_STATUS"); + return 1; + } + close (fd); + close (ffd); + return 0; +} + +int +del_loop (const char *device) +{ + int fd; + + if ((fd = open (device, O_RDONLY)) < 0) { + fprintf(stderr, "loop: can't delete device %s: %s\n", + device, strerror (errno)); + return 1; + } + if (ioctl (fd, LOOP_CLR_FD, 0) < 0) { +#if 0 + perror ("ioctl: LOOP_CLR_FD"); +#endif + return 1; + } + return 0; +} + + +int +lomount (const char *spec, const char *node, const char *device, char **type, + int *flags, char **extra_opts, char **mount_opts) +{ + char *opt, + *opteq; + int val; + char *encryption = NULL, *vfs = NULL; + int offset = 0, err; + char new_opts[1024]; + + for (opt = strtok (*extra_opts, ","); opt; opt = strtok (NULL, ",")) { + if ((opteq = strchr (opt, '='))) { + val = atoi (opteq + 1); + *opteq = '\0'; + if (!strcmp (opt, "encryption")) + encryption = strdup(opteq + 1); + else if (!strcmp (opt, "vfs")) + vfs = strdup(opteq + 1); + else if (!strcmp (opt, "offset")) + offset = val; + else { + printf ("unknown loop mount parameter: " + "%s=%d (%s)\n", opt, val, opteq+1); + return 1; + } + } else { + printf ("unknown loop mount parameter: " + "%s\n", opt); + return 1; + } + } + err = set_loop (device, spec, offset, encryption); + sprintf(new_opts, "vfs=%s,offset=%d,encryption=%s", + *type = vfs ? vfs : FSTYPE_DEFAULT, offset, + encryption=crypt_type(encryption)<0?"none":encryption); + *extra_opts=strdup(new_opts); + return err; +} diff --git a/mount/loop.h b/mount/loop.h new file mode 100644 index 00000000..81ee7611 --- /dev/null +++ b/mount/loop.h @@ -0,0 +1,77 @@ +#ifndef _LINUX_LOOP_H +#define _LINUX_LOOP_H + +/* + * include/linux/loop.h + * + * Written by Theodore Ts'o, 3/29/93. + * + * Copyright 1993 by Theodore Ts'o. Redistribution of this file is + * permitted under the GNU Public License. + */ + +#define LO_NAME_SIZE 64 +#define LO_KEY_SIZE 32 + +struct loop_device { + int lo_number; + struct inode *lo_inode; + int lo_refcnt; + dev_t lo_device; + int lo_offset; + int lo_encrypt_type; + int lo_encrypt_key_size; + int lo_flags; + int (*transfer)(struct loop_device *, int cmd, + char *raw_buf, char *loop_buf, int size); + char lo_name[LO_NAME_SIZE]; + char lo_encrypt_key[LO_KEY_SIZE]; +#ifdef DES_AVAILABLE + des_key_schedule lo_des_key; + unsigned long lo_des_init[2]; +#endif +}; + +typedef int (* transfer_proc_t)(struct loop_device *, int cmd, + char *raw_buf, char *loop_buf, int size); + +/* + * Loop flags + */ +#define LO_FLAGS_DO_BMAP 0x00000001 + +struct loop_info { + int lo_number; /* ioctl r/o */ + dev_t lo_device; /* ioctl r/o */ + unsigned long lo_inode; /* ioctl r/o */ + dev_t lo_rdevice; /* ioctl r/o */ + int lo_offset; + int lo_encrypt_type; + int lo_encrypt_key_size; /* ioctl w/o */ + int lo_flags; /* ioctl r/o */ + char lo_name[LO_NAME_SIZE]; + unsigned char lo_encrypt_key[LO_KEY_SIZE]; /* ioctl w/o */ + unsigned long lo_init[2]; + char reserved[4]; +}; + +/* + * Loop encryption types --- LO_CRYPT_IDEA isn't supported yet + */ + +#define LO_CRYPT_NONE 0 +#define LO_CRYPT_XOR 1 +#define LO_CRYPT_DES 2 +#define LO_CRYPT_IDEA 4 +#define MAX_LO_CRYPT 3 + +/* + * IOCTL commands --- we will commandeer 0x4C ('L') + */ + +#define LOOP_SET_FD 0x4C00 +#define LOOP_CLR_FD 0x4C01 +#define LOOP_SET_STATUS 0x4C02 +#define LOOP_GET_STATUS 0x4C03 + +#endif diff --git a/mount/mount.8 b/mount/mount.8 new file mode 100644 index 00000000..a47091dd --- /dev/null +++ b/mount/mount.8 @@ -0,0 +1,589 @@ +.\" Copyright (c) 1980, 1989, 1991 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (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.8 6.17 (Berkeley) 8/5/91 +.\" +.\" When you change this file, please add an update notice to the ones below: +.\" +.\" Sun Dec 27 12:10:38 1992: Updated by faith@cs.unc.edu +.\" Thu Jan 14 21:15:06 1993: Updated by faith@cs.unc.edu +.\" Mon Feb 1 21:18:21 1993: Updated by faith@cs.unc.edu +.\" Sat Mar 6 20:46:29 1993: Updated by faith@cs.unc.edu +.\" Sat Oct 9 08:56:26 1993: Updated by faith@cs.unc.edu +.\" based on changes by Stephen Tweedie (sct@dcs.ed.ac.uk) +.\" Sat Oct 9 08:59:46 1993: Converted to man format by faith@cs.unc.edu +.\" Sat Nov 27 20:04:28 1993: File-system specific options documented by Rik +.\" Faith (faith@cs.unc.edu), using extensive additions taken from +.\" documentation written by Werner Almesberger +.\" (almesber@nessie.cs.id.ethz.ch), and email written by Eric +.\" Youngdale (eric@tantalus.nrl.navy.mil) and Remy Card +.\" (Remy.Card@masi.ibp.fr). +.\" Sun Apr 24 19:25:59 1994: Updated per information supplied by Remy Card. +.\" Thu Jul 14 07:44:36 1994: Updated absence of -t +.\" option. (faith@cs.unc.edu) +.\" Thu Jul 14 07:49:14 1994: Updated list of valid filesystems. +.\" Wed Feb 8 09:25:48 1995: Updated man pages for Mike Grupenhoff's changes. +.\" +.TH MOUNT 8 "8 February 1995" "Linux 1.1" "Linux Programmer's Manual" +.SH NAME +mount, umount \- mount and dismount file systems +.SH SYNOPSIS +.BI "mount [\-afrwuvn] [\-t " vfstype ] +.br +.BI "mount [\-frwuvn] [\-o " remount " [,...]] " "special " | " node" +.br +.BI "mount [\-frwun] [\-t " vfstype "] [\-o " options "] " "special node" +.br +.BI "umount [\-an] [\-t " vfstype ] +.br +.BI "umount " "special " | " node" +.\" " for hilit19 +.SH DESCRIPTION +The +.B mount +command calls the +.BR mount (2) +system call to prepare and graft a +.I special +device on to the file system tree at the point +.IR node . +If either +.IR special " or " node +are not provided, the appropriate information is taken from the +.BR fstab (5) +file. The special keyword +.I none +can be used instead of a path or +.I node +specification. This is useful when mounting the +.I proc +file system. + +The system maintains a list of currently mounted file systems. If no +arguments are given to +.BR mount , +this list is printed. + +Options available for the +.B mount +command: +.TP +.B \-f +Causes everything to be done except for the actual system call; if it's not +obvious, this ``fakes'' mounting the file system. This option is useful in +conjunction with the +.B \-v +flag to determine what the +.B mount +command is trying to do. +.TP +.B \-o +Options are specified with a +.B \-o +flag followed by a comma separated string of options. +.B N.B., +many of these options are only useful when they appear in the +.I /etc/fstab +file. The following options apply to any file system that is being +mounted: +.RS +.TP +.B async +All I/O to the file system should be done asynchronously. +.TP +.B auto +Can be mounted with the +.B \-a +option. +.TP +.B defaults +Use default options: +.BR rw ", " suid ", " dev ", " exec ", " auto ", " nouser ", and " async. +.TP +.B dev +Interpret character or block special devices on the file system. +.TP +.B exec +Permit execution of binaries. +.TP +.B noauto +Can only be mounted explicitly (i.e., the +.B \-a +option will not cause the file system to be mounted). +.TP +.B nodev +Do not interpret character or block special devices on the file +system. This options is useful for a server that has file systems +containing special devices for architectures other than its own. +.TP +.B noexec +Do not allow execution of any binaries on the mounted file system. +This options is useful for a server that has file systems containing +binaries for architectures other than its own. +.TP +.B nosuid +Do not allow set-user-identifier or set-group-identifier bits to take +effect. +.TP +.B nouser +Forbid an ordinary (i.e., non-root) user to mount the file system. +.TP +.B remount +Attempt to remount an already-mounted file system. This is commonly +used to change the mount flags for a file system, especially to make a +readonly file system writeable. +.TP +.B ro +Mount the file system read-only. +.TP +.B rw +Mount the file system read-write. +.TP +.B suid +Allow set-user-identifier or set-group-identifier bits to take +effect. +.TP +.B sync +All I/O to the file system should be done synchronously. +.TP +.B user +Allow an ordinary user to mount the file system. Ordinary users always +have the following options activated: +.BR noexec ", " nosuid ", and " nodev +(unless overridden by the superuser by using, for example, the following +option line: +.BR user,exec,dev,suid . +.PP +The following options apply only to certain file systems: +.TP +.BI case= value +For the +.I hpfs +file system, specify case as +.I lower +or +.IR asis . +.TP +.BI check= value +Tells the +.I ext2 +file sysem kernel code to do some more checks while the file system is +mounted. Currently (0.99.15), the following values can be specified with +this option: +.RS +.TP +.I none +no extra check is performed by the kernel code +.TP +.I normal +The inodes and blocks bitmaps are checked when the file system is mounted +(this is the default) +.TP +.I strict +In addition to the +.I normal +checks, block deallocation checks that the block to free is in the data +zone. +.RE +.TP +.BI check= value +For the +.I msdos +file system, three different levels of pickyness can be chosen: +.RS +.TP +.I relaxed +Upper and lower case are accepted and equivalent, long name parts are +truncated (e.g. verlongname.foobar becomes verylong.foo), leading and +embedded spaces are accepted in each name part (name and extension). +.TP +.I normal +Like "relaxed", but many special characters (*, ?, <, spaces, etc.) are +rejected. This is the default. +.TP +.I strict +Like "normal", but names may not contain long parts and special characters +that are sometimes used on Linux, but are not accepted by MS-DOS are +rejected. (+, =, spaces, etc.) +.RE +.TP +.BI conv= value +For the +.IR msdos , +.IR hpfs , +and +.I iso9660 +file systems, specify file conversion as +.IR binary ", " text ", or " auto . +The +.I iso9660 +file system also allows +.I value +to be +.IR mtext . + +The +.I msdos +file system can perform CRLF<-->NL (MS-DOS text format to UNIX text +format) conversion in the kernel. The following conversion modes are +available: +.RS +.TP +.I binary +no translation is performed. This is the default. +.TP +.I text +CRLF<-->NL translation is performed on all files. +.TP +.I auto +CRLF<-->NL translation is performed on all files that don't have a +"well-known binary" extension. The list of known extensions can be found at +the beginning of +.I fs/msdos/misc.c +(as of 09913r, the list is: exe, com, bin, app, sys, drv, ovl, ovr, obj, +lib, dll, pif, arc, zip, lha, lzh, zoo, tar, z, arj, tz, taz, tzp, tpz, +gif, bmp, tif, gl, jpg, pcx, tfm, vf, gf, pk, pxl, dvi). +.PP +Programs that do computed lseeks won't like in-kernel text conversion. + +For file systems mounted in +.B binary +mode, a conversion tool (fromdos/todos) is available. +.RE +.TP +.BI block= value +For the +.I iso9660 +file system, set the blocksize. +.TP +.B bsdgroups +See +.B grpid +.TP +.B cruft +For the +.I iso9660 +file system, set the +.I cruft +flag to 'y'. This option is available because there are buggy premastering +programs out there that leave junk in the top byte of the file size. This +option clears the top byte, but restricts files to 16Mb maximum in the +process. +.TP +.B debug +For the +.I msdos +file system, turn on the +.I debug +flag. A version string and a list of file system parameters will be +printed (these data are also printed if the parameters appear to be +inconsistent). +.TP +.B debug +For the +.I ext2fs +file system, causes the kernel code to display the file system parameters +when the file system is mounted. +.TP +.BI errors= value +For the +.I ext2fs +file system, specifies the error behavior: +.RS +.TP +.B continue +No special action is taken on errors (except marking the file system as +erroneous). This is the default. +.TP +.B remount +.TP +.B ro +The file system is remounted read only, and subsequent writes are refused. +.TP +.B panic +When an error is detected, the system panics. +.RE +.TP +.BI fat= value +For the +.I msdos +file system, specify either a 12 bit fat or a 16 bit fat. This overrides +the automatic FAT type detection routine. Use with caution! +.TP +.BI gid= value +For the +.I msdos +and +.I hpfs +file systems, give every file a gid equal to +.IR value . +.TP +B grpid +Causes the +.I ext2fs +to use the BSD behavior when creating files: file are created with the +group id of their parent directory. +.TP +.BI map= value +For the +.I iso9660 +file system, specify mapping as +.IR off " or " normal . +In general, non-Rock Ridge discs have all of the filenames in upper case, +and all of the filenames have a ";1" appended. The map option strips the +";1" and makes the name lower case. C.f. +.BR norock . +.TP +.B nocheck +For the +.IR ext2fs , +turns of checking (see +.BR check=none ). +.TP +.B nogrpid +Causes the +.I ext2fs +to use the System V behaviour when creating files: files are created with +the group id of the creating process, unless the setgid bit is set on the +parent directory. This is the default for all Linux file systems. +.TP +.B norock +Normal +.I iso9600 +filenames appear in a 8.3 format (i.e., DOS-like restrictions on filename +length), and in addition all characters are in upper case. Also there is +no field for file ownership, protection, number of links, provision for +block/character devices, etc. + +Rock Ridge is an extension to iso9660 that provides all of these unix like +features. Basically there are extensions to each directory record that +supply all of the additional information, and when Rock Ridge is in use, +the filesystem is indistinguishable from a normal UNIX file system (except +that it is read-only, of course). + +The +.B norock +switch disables the use of Rock Ridge extensions, even if available. C.f. +.BR map . +.TP +.B quiet +For the +.I msdos +file system, turn on the +.I quiet +flag. Attempts to chown or chmod files do not yield errors, although they +fail. Use with caution! +.TP +.BI sb= value +For the +.I ext2 +file system, use an alternate superblock located at block +.IR value . +.I value +is numbered in 1024 bytes blocks. An +.I ext2 +file system usually has backups of the super block at blocks 1, 8193, 16385 +and so on. +.TP +.BI sysvgroups +See +.B nogrpid +.TP +.BI uid= value +For the +.I msdos +and +.I hpfs +file systems, give every file a uid equal to +.IR value . +.TP +.BI umask= value +For the +.I msdos +and +.I hpfs +file systems, give every file a umask of +.IR value . +The radix defaults to octal. +.PP +The full set of options applied is determined by first extracting the +options for the file system from the +.B fstab +table, then applying any options specified by the +.B \-o +argument, and finally applying the +.BR \-r " or " \-w +option. + +If the +.I msdos +file system detects an inconsistency, it reports an error and sets the file +system read-only. The file system can be made writeable again by remounting +it. +.RE +.TP +.B \-r +The file system object is to be mounted read-only. +.TP +.BI \-t " vfstype" +The argument following the +.B \-t +is used to indicate the file system type. The file system types which are +currently supported are listed in +.IR linux/fs/filesystems.c : +.IR minux ", " ext ", " ext2 ", " xiafs ", " msdos ", " hpfs , +.IR proc ", " nfs ", " iso9660 ", " sysv ", " xenix ", " coherent . +Note that that last three are equivalent and that "xenix" and "coherent" +will be removed at some point in the future \(em use "sysv" instead. + +The type +.I minix +is the default. If no +.B \-t +option is given, the superblock is probed for the filesystem type (minix, +ext, ext2, xia are supported). If this probe fails and +.I /proc/filesystems +exists, then all of the filesystems listed will be tried, +.I except +for those that are labeled "nodev" (e.g., "proc" and "nfs"). + +For example, the +.B mount +command: +.RS + +.RS +mount -a -t nomsdos,ext +.RE + +mounts all file systems except those of type +.I msdos +and +.IR ext . +.RE +.TP +.B \-v +Verbose mode. +.TP +.B \-w +The file system object is to be read and write. +.TP +.B \-n +Mount without writing in +.IR /etc/mtab . +.PP +.B Umount +removes the +.I special +device grafted at point +.I node +from file system tree. + +Options for the +.B umount +command: +.TP +.B \-a +All of the file systems described in +.I /etc/mtab +are unmounted. +.TP +.BI \-t " vfstype" +Is used to indicate the actions should only be taken on file systems of the +specified type. More than one type may be specified in a comma separated +list. The list of file system types can be prefixed with ``no'' to specify +the file system types on which no action should be taken. (See example +above for the +.B mount +command.) + +.SH FILES +.I /etc/fstab +file system table +.br +.I /etc/mtab~ +lock file +.br +.I /etc/mtab.tmp +temporary file +.SH "SEE ALSO" +.BR mount "(2), " umount "(2), " fstab "(5), " swapon (8) +.SH BUGS +It is possible for a corrupted file system to cause a crash. +.PP +Some Linux file systems don't support +.BI \-o " synchronous" +(the ext2fs +.I does +support synchronous updates (a la BSD) when mounted with the +.B sync +option). +.PP +The +.BI \-o " remount" +may not be able to change mount parameters (all +.I ext2fs +parameters, except +.BR sb , +are changeable with a remount, for example, but you can't change +.B gid +or +.B umask +for the +.IR dosfs ). +.SH HISTORY +A +.B mount +command appeared in Version 6 AT&T UNIX. +.SH "AUTHORS AND CONTRIBUTORS" +.na +The Linux +.B mount +command has a long and continuing history. Major releases are noted below, +with the name of the primary modifier noted: +.sp +0.97.3: Doug Quale (quale@saavik.cs.wisc.edu). +.br +0.98.5: H. J. Lu (hlu@eecs.wsu.edu). +.br +0.99.2: Rick Sladkey (jrs@world.std.com). +.br +0.99.6: Rick Sladkey (jrs@world.std.com). +.br +0.99.10: Stephen Tweedie (sct@dcs.ed.ac.uk). +.br +0.99.14: Rick Sladkey (jrs@world.std.com). +.sp +(File-system specific information added to man page on 27 November 1993 by +Rik Faith with lots of information +.I and text +from the following file system authors: Werner Almesberger, Eric Youngdale, +and Remy Card.) diff --git a/mount/mount.c b/mount/mount.c new file mode 100644 index 00000000..b6ffbcd6 --- /dev/null +++ b/mount/mount.c @@ -0,0 +1,734 @@ +/* + * A mount(8) for Linux 0.99. + * mount.c,v 1.1.1.1 1993/11/18 08:40:51 jrs Exp + * + * Thu Jul 14 07:32:40 1994: faith@cs.unc.edu added changed from Adam + * J. Richter (adam@adam.yggdrasil.com) so that /proc/filesystems is used + * if no -t option is given. I modified his patches so that, if + * /proc/filesystems is not available, the behavior of mount is the same as + * it was previously. + * + * Wed Sep 14 22:43:00 1994: Mitchum DSouza + * (mitch@mrc-applied-psychology.cambridge.ac.uk) added support for mounting + * the "loop" device. + * + * Wed Sep 14 22:55:10 1994: Sander van Malssen (svm@kozmix.hacktic.nl) + * added support for remounting readonly file systems readonly. + * + * Wed Feb 8 09:23:18 1995: Mike Grupenhoff added + * a probe of the superblock for the type before /proc/filesystems is + * checked. + * + * Wed Feb 8 12:27:00 1995: Andries.Brouwer@cwi.nl fixed up error messages. + * + */ + +#include "sundries.h" + +#include +#include +#include +#include +#include +#include +#include + +int del_loop (const char *); + +/* True for fake mount (-f). */ +int fake = 0; + +/* Don't write a entry in /etc/mtab (-n). */ +int nomtab = 0; + +/* True for readonly (-r). */ +int readonly = 0; + +/* Nonzero for chatty (-v). */ +int verbose = 0; + +/* True for read/write (-w). */ +int readwrite = 0; + +/* True for all mount (-a). */ +int all = 0; + +/* True if ruid != euid. */ +int suid = 0; + +/* Map from -o and fstab option strings to the flag argument to mount(2). */ +struct opt_map +{ + const char *opt; /* option name */ + int inv; /* true if flag value should be inverted */ + int mask; /* flag mask value */ +}; + +/* Custom mount options for our own purposes. */ +#define MS_NOAUTO 0x80000000 +#define MS_USER 0x40000000 + +/* Options that we keep the mount system call from seeing. */ +#define MS_NOSYS (MS_NOAUTO|MS_USER) + +/* Options that we keep from appearing in the options field in the mtab. */ +#define MS_NOMTAB (MS_REMOUNT|MS_NOAUTO|MS_USER) + +/* OPTIONS that we make ordinary users have by default. */ +#define MS_SECURE (MS_NOEXEC|MS_NOSUID|MS_NODEV) + +const struct opt_map opt_map[] = +{ + { "defaults", 0, 0 }, /* default options */ + { "ro", 0, MS_RDONLY }, /* read-only */ + { "rw", 1, MS_RDONLY }, /* read-write */ + { "exec", 1, MS_NOEXEC }, /* permit execution of binaries */ + { "noexec", 0, MS_NOEXEC }, /* don't execute binaries */ + { "suid", 1, MS_NOSUID }, /* honor suid executables */ + { "nosuid", 0, MS_NOSUID }, /* don't honor suid executables */ + { "dev", 1, MS_NODEV }, /* interpret device files */ + { "nodev", 0, MS_NODEV }, /* don't interpret devices */ + { "sync", 0, MS_SYNCHRONOUS}, /* synchronous I/O */ + { "async", 1, MS_SYNCHRONOUS}, /* asynchronous I/O */ + { "remount", 0, MS_REMOUNT }, /* Alter flags of mounted FS */ + { "auto", 1, MS_NOAUTO }, /* Can be mounted using -a */ + { "noauto", 0, MS_NOAUTO }, /* Can only be mounted explicitly */ + { "user", 0, MS_USER }, /* Allow ordinary user to mount */ + { "nouser", 1, MS_USER }, /* Forbid ordinary user to mount */ + /* add new options here */ +#ifdef MS_NOSUB + { "sub", 1, MS_NOSUB }, /* allow submounts */ + { "nosub", 0, MS_NOSUB }, /* don't allow submounts */ +#endif + { NULL, 0, 0 } +}; + + +/* Report on a single mount. */ +static void +print_one (const struct mntent *mnt) +{ + printf ("%s on %s", mnt->mnt_fsname, mnt->mnt_dir); + if ((mnt->mnt_type != NULL) && *mnt->mnt_type != '\0') + printf (" type %s", mnt->mnt_type); + if (mnt->mnt_opts != NULL) + printf (" (%s)", mnt->mnt_opts); + printf ("\n"); +} + +/* Report on everything in mtab (of the specified types if any). */ +static int +print_all (string_list types) +{ + struct mntent *mnt; + + open_mtab ("r"); + + while ((mnt = getmntent (F_mtab)) != NULL) + if (matching_type (mnt->mnt_type, types)) + print_one (mnt); + + if (ferror (F_mtab)) + die (1, "mount: error reading %s: %s", MOUNTED, strerror (errno)); + + exit (0); +} + + +/* Look for OPT in opt_map table and return mask value. If OPT isn't found, + tack it onto extra_opts. */ +static inline void +parse_opt (const char *opt, int *mask, char *extra_opts) +{ + const struct opt_map *om; + + for (om = opt_map; om->opt != NULL; om++) + if (streq (opt, om->opt)) + { + if (om->inv) + *mask &= ~om->mask; + else + *mask |= om->mask; + if (om->mask == MS_USER) + *mask |= MS_SECURE; + return; + } + if (*extra_opts) + strcat(extra_opts, ","); + strcat(extra_opts, opt); +} + +/* Take -o options list and compute 4th and 5th args to mount(2). flags + gets the standard options and extra_opts anything we don't recognize. */ +static void +parse_opts (char *opts, int *flags, char **extra_opts) +{ + char *opt; + + *flags = 0; + *extra_opts = NULL; + + if (opts != NULL) + { + *extra_opts = xmalloc (strlen (opts) + 1); + **extra_opts = '\0'; + + for (opt = strtok (opts, ","); + opt != NULL; + opt = strtok (NULL, ",")) + parse_opt (opt, flags, *extra_opts); + } + + if (readonly) + *flags |= MS_RDONLY; + if (readwrite) + *flags &= ~MS_RDONLY; +} + +/* Try to build a canonical options string. */ +static char * +fix_opts_string (int flags, char *extra_opts) +{ + const struct opt_map *om; + char *new_opts; + char *tmp; + + new_opts = (flags & MS_RDONLY) ? "ro" : "rw"; + for (om = opt_map; om->opt != NULL; om++) + { + if (om->mask & MS_RDONLY) + continue; + if (om->inv || !om->mask || (flags & om->mask) != om->mask) + continue; + tmp = xmalloc(strlen(new_opts) + strlen(om->opt) + 2); + sprintf(tmp, "%s,%s", new_opts, om->opt); + new_opts = tmp; + flags &= ~om->mask; + } + if (extra_opts && *extra_opts) + { + tmp = xmalloc(strlen(new_opts) + strlen(extra_opts) + 2); + sprintf(tmp, "%s,%s", new_opts, extra_opts); + new_opts = tmp; + } + return new_opts; +} + + +/* + char *fstype(const char *device); + + probes the device and attempts to determine the type of filesystem + contained within. + + Original routine by ; made into a function + for mount(8) by Mike Grupenhoff . + + Currently supports: minix, ext, ext2, xia +*/ + +static char * +fstype(const char *device) +{ + int fd; + + /* MINIX */ + struct minix_super_block ms; + /* extended fs */ + struct ext_super_block es; + /* 2nd extended fs */ + struct ext2_super_block e2s; + /* xia fs */ + struct xiafs_super_block xfs; + + fd = open(device, O_RDONLY); + if (fd < 0) { + perror(device); + return 0; + } + lseek(fd, BLOCK_SIZE, SEEK_SET); + read(fd, (char *) &ms, sizeof(ms)); + if (ms.s_magic == MINIX_SUPER_MAGIC || ms.s_magic == MINIX_SUPER_MAGIC2) { + close(fd); + return("minix"); + } + + lseek(fd, BLOCK_SIZE, SEEK_SET); + read(fd, (char *) &es, sizeof(es)); + if (es.s_magic == EXT_SUPER_MAGIC) { + close(fd); + return("ext"); + } + + lseek(fd, BLOCK_SIZE, SEEK_SET); + read(fd, (char *) &e2s, sizeof(e2s)); + if (e2s.s_magic == EXT2_SUPER_MAGIC || e2s.s_magic == EXT2_PRE_02B_MAGIC) { + close(fd); + return("ext2"); + } + + lseek(fd, 0, SEEK_SET); + read(fd, (char *) &xfs, sizeof(xfs)); + if (xfs.s_magic == _XIAFS_SUPER_MAGIC) { + close(fd); + return("xiafs"); + } + + close(fd); + + return(0); + +} + + +/* Mount a single file system. Return status, + so don't exit on non-fatal errors. */ + +static int +try_mount5 (char *spec, char *node, char **type, int flags, char *mount_opts) { + FILE *procfs_file; + char line[100]; + char fsname[50]; + + if (*type) return mount5 (spec, node, *type, flags & ~MS_NOSYS, mount_opts); + if (( procfs_file = fopen("/proc/filesystems", "r")) == NULL) { + /* If /proc/filesystems is not available, + preserve the old behavior of mount. */ + return mount5 (spec, + node, + FSTYPE_DEFAULT, + flags & ~MS_NOSYS, mount_opts); + } + while (fgets(line, sizeof(line), procfs_file)) { + if (sscanf (line, "nodev %[^\n]\n", fsname) == 1) continue; + if (sscanf (line, " %[^ \n]\n", fsname) != 1) continue; + if (mount5 (spec, node, fsname, flags & ~MS_NOSYS, mount_opts) == 0) { + *type=xstrdup(fsname); + fclose(procfs_file); + return 0; + } + } + fclose(procfs_file); + return -1; +} + + +static int +mount_one (char *spec, char *node, char *type, char *opts, int freq, int pass) +{ + struct mntent mnt; + int mnt_err; + int flags; + char *extra_opts; + char *mount_opts; + int anti_recurse = 0; + int loop=0; + + if (type == NULL) + { + if (strchr (spec, ':') != NULL) + type = "nfs"; + } + + parse_opts (xstrdup (opts), &flags, &extra_opts); + + /* root may allow certain types of mounts by ordinary users */ + if (suid && !(flags & MS_USER)) + die (3, "mount: only root can mount %s on %s", spec, node); + + /* quietly succeed for fstab entries that don't get mounted automatically */ + if (all && (flags & MS_NOAUTO)) + return 0; + + mount_opts = extra_opts; + + if (!fake && type && strncmp("lo@", type, 3)==0) { + extern int lomount (char *, char *, char *, char **, + int *, char **, char **); + char *dev=type+3; + + loop=1; + if (lomount (spec, node, dev, &type, + &flags, &opts, &mount_opts) != 0) + return 1; + spec=dev; + mount_opts=NULL; + } + + if (!fake && type && streq (type, "nfs")) +#ifdef HAVE_NFS + if (nfsmount (spec, node, &flags, &extra_opts, &mount_opts) != 0) + return 1; +#else + die (1, "mount: this version doesn't support the type `nfs'"); +#endif + + if (!type && !(type = fstype(spec))) + return 1; + + block_signals (SIG_BLOCK); + + if (fake + || (try_mount5 (spec, node, &type, flags & ~MS_NOSYS, mount_opts)) == 0) + /* Mount succeeded, write mtab entry. */ + { + if (!nomtab) + { + mnt.mnt_fsname = canonicalize (spec); + mnt.mnt_dir = canonicalize (node); + mnt.mnt_type = loop?"loop":type; + mnt.mnt_opts = fix_opts_string (flags & ~MS_NOMTAB, + loop?opts:extra_opts); + mnt.mnt_freq = freq; + mnt.mnt_passno = pass; + + /* We get chatty now rather than after the update to mtab since the + mount succeeded, even if the write to /etc/mtab should fail. */ + if (verbose) + print_one (&mnt); + + if (flags & MS_REMOUNT) + { + close_mtab (); + update_mtab (mnt.mnt_dir, &mnt); + open_mtab ("a+"); + } + else + if ((addmntent (F_mtab, &mnt)) == 1) + die (1, "mount: error writing %s: %s", + MOUNTED, strerror (errno)); + } + + block_signals (SIG_UNBLOCK); + return 0; + } + + if (loop) + del_loop(spec); + + mnt_err = errno; /* work around for errno bug in sigprocmask */ + + block_signals (SIG_UNBLOCK); + + /* Mount failed, complain, but don't die. */ + switch (mnt_err) + { + case EPERM: + if (geteuid() == 0) + error ("mount: mount point %s is not a directory", node); + else + error ("mount: must be superuser to use mount"); + break; + case EBUSY: + error ("mount: %s already mounted or %s busy", spec, node); + break; + case ENOENT: + { struct stat statbuf; + if (stat (node, &statbuf)) + error ("mount: mount point %s does not exist", node); + else if (stat (spec, &statbuf)) + error ("mount: special device %s does not exist", spec); + else { + errno = mnt_err; + perror("mount"); + } + break; + } + case ENOTDIR: + error ("mount: mount point %s is not a directory", node); break; + case EINVAL: + error ("mount: wrong fs type or bad superblock on %s", spec); break; + case EMFILE: + error ("mount table full"); break; + case EIO: + error ("mount: %s: can't read superblock", spec); break; + case ENODEV: + error ("mount: fs type %s not supported by kernel", type); break; + case ENOTBLK: + error ("mount: %s is not a block device", spec); break; + case ENXIO: + error ("mount: %s is not a valid block device", spec); break; + case EACCES: /* pre-linux 1.1.38 */ + case EROFS: /* linux 1.1.38 and later */ + if (anti_recurse) + { + error ("mount: block device %s is not permitted on its filesystem", spec); + break; + } + else + { + anti_recurse++; + if (opts) + { + opts = realloc(xstrdup(opts), strlen(opts)+3); + strcat(opts, ",ro"); + } + else + opts = "ro"; + error ("mount: block device %s is write-protected, mounting read-only", spec); + return mount_one (spec, node, type, opts, freq, pass); + } + break; + default: + error ("mount: %s", strerror (mnt_err)); break; + } + return 1; +} + +/* Check if an fsname/dir pair was already in the old mtab. */ +static int +mounted (char *spec, char *node, string_list spec_list, string_list node_list) +{ + spec = canonicalize (spec); + node = canonicalize (node); + + while (spec_list != NULL) + { + if (streq (spec, car (spec_list)) && streq (node, car (node_list))) + return 1; + spec_list = cdr (spec_list); + node_list = cdr (node_list); + } + return 0; +} + +/* Mount all filesystems of the specified types except swap and root. */ +static int +mount_all (string_list types) +{ + struct mntent *fstab; + struct mntent *mnt; + string_list spec_list = NULL; + string_list node_list = NULL; + int status; + + rewind (F_mtab); + + while ((mnt = getmntent (F_mtab))) + if (matching_type (mnt->mnt_type, types) + && !streq (mnt->mnt_dir, "/") + && !streq (mnt->mnt_dir, "root")) + { + spec_list = cons (xstrdup (mnt->mnt_fsname), spec_list); + node_list = cons (xstrdup (mnt->mnt_dir), node_list); + } + + status = 0; + while ((fstab = getfsent ()) != NULL) + if (matching_type (fstab->mnt_type, types) + && !streq (fstab->mnt_dir, "/") + && !streq (fstab->mnt_dir, "root")) + if (mounted (fstab->mnt_fsname, fstab->mnt_dir, spec_list, node_list)) + { + if (verbose) + printf("mount: %s already mounted on %s\n", + fstab->mnt_fsname, fstab->mnt_dir); + } + else + status |= mount_one (fstab->mnt_fsname, fstab->mnt_dir, + fstab->mnt_type, fstab->mnt_opts, + fstab->mnt_freq, fstab->mnt_passno); + + return status; +} + +/* Create mtab with a root entry. */ +static void +create_mtab (void) +{ + struct mntent *fstab; + struct mntent mnt; + int flags; + char *extra_opts; + + if ((F_mtab = setmntent (MOUNTED, "a+")) == NULL) + die (1, "mount: can't open %s for writing: %s", MOUNTED, strerror (errno)); + + /* Find the root entry by looking it up in fstab, which might be wrong. + We could statfs "/" followed by a slew of stats on /dev/ but then + we'd have to unparse the mount options as well.... */ + if ((fstab = getfsfile ("/")) || (fstab = getfsfile ("root"))) + { + parse_opts (xstrdup (fstab->mnt_opts), &flags, &extra_opts); + mnt = *fstab; + mnt.mnt_fsname = canonicalize (fstab->mnt_fsname); + mnt.mnt_dir = "/"; + mnt.mnt_opts = fix_opts_string (flags, extra_opts); + + if (addmntent (F_mtab, &mnt) == 1) + die (1, "mount: error writing %s: %s", MOUNTED, strerror (errno)); + } + if (fchmod (fileno (F_mtab), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) + die (1, "mount: error changing mode of %s: %s", MOUNTED, strerror (errno)); + endmntent (F_mtab); +} + +extern char version[]; +static struct option longopts[] = +{ + { "all", 0, 0, 'a' }, + { "fake", 0, 0, 'f' }, + { "help", 0, 0, 'h' }, + { "no-mtab", 0, 0, 'n' }, + { "read-only", 0, 0, 'r' }, + { "ro", 0, 0, 'r' }, + { "verbose", 0, 0, 'v' }, + { "version", 0, 0, 'V' }, + { "read-write", 0, 0, 'w' }, + { "rw", 0, 0, 'w' }, + { "options", 1, 0, 'o' }, + { "types", 1, 0, 't' }, + { NULL, 0, 0, 0 } +}; + +const char *usage_string = "\ +usage: mount [-hV]\n\ + mount -a [-nfrvw] [-t vfstypes]\n\ + mount [-nfrvw] [-o options] special | node\n\ + mount [-nfrvw] [-t vfstype] [-o options] special node\n\ +"; + +static void +usage (FILE *fp, int n) +{ + fprintf (fp, "%s", usage_string); + exit (n); +} + +int +main (int argc, char *argv[]) +{ + int c; + char *options = NULL; + string_list types = NULL; + struct mntent *fs; + char *spec; + int result = 0; + struct stat statbuf; + + while ((c = getopt_long (argc, argv, "afhnrvVwt:o:", longopts, NULL)) != EOF) + switch (c) + { + case 'a': /* mount everything in fstab */ + ++all; + break; + case 'f': /* fake (don't actually do mount(2) call) */ + ++fake; + break; + case 'h': /* help */ + usage (stdout, 0); + break; + case 'n': /* mount without writing in /etc/mtab */ + ++nomtab; + break; + case 'r': /* mount readonly */ + ++readonly; + readwrite = 0; + break; + case 'v': /* be chatty */ + ++verbose; + break; + case 'V': /* version */ + printf ("%s\n", version); + exit (0); + case 'w': /* mount read/write */ + ++readwrite; + readonly = 0; + break; + case 't': /* specify file system types */ + types = parse_list (optarg); + break; + case 'o': /* specify mount options */ + options = optarg; + break; + case 0: + break; + case '?': + default: + usage (stderr, 1); + break; + } + + argc -= optind; + argv += optind; + + if (argc == 0) + { + if (options) + usage (stderr, 1); + if (!all) + return print_all (types); + } + + if (getuid () != geteuid ()) + { + suid = 1; + if (types || options || readwrite || nomtab || all || fake || argc != 1) + die (2, "mount: only root can do that"); + } + + if (!nomtab) + { + lock_mtab (); + if (stat(MOUNTED, &statbuf) < 0) + create_mtab (); + open_mtab ("a+"); + } + else if (stat(MOUNTED, &statbuf) >= 0) + open_mtab ("r"); + + + switch (argc) + { + case 0: + /* mount -a */ + result = mount_all (types); + break; + + case 1: + /* mount [-nfrvw] [-o options] special | node */ + if (types != NULL) + usage (stderr, 1); + /* Try to find the other pathname in fstab. */ + spec = canonicalize (*argv); + if (!(fs = getmntfile (spec)) + && !(fs = getfsspec (spec)) && !(fs = getfsfile (spec))) + die (2, "mount: can't find %s in %s or %s", + spec, MOUNTED, _PATH_FSTAB); + /* Merge the fstab and command line options. */ + if (options == NULL) + options = fs->mnt_opts; + else + { + char *tmp = xmalloc(strlen(fs->mnt_opts) + strlen(options) + 2); + + sprintf (tmp, "%s,%s", fs->mnt_opts, options); + options = tmp; + } + result = mount_one (xstrdup (fs->mnt_fsname), xstrdup (fs->mnt_dir), + xstrdup (fs->mnt_type), options, + fs->mnt_freq, fs->mnt_passno); + break; + + case 2: + /* mount [-nfrvw] [-t vfstype] [-o options] special node */ + if (types == NULL) + result = mount_one (argv[0], argv[1], NULL, options, 0, 0); + else if (cdr (types) == NULL) + result = mount_one (argv[0], argv[1], car (types), options, 0, 0); + else + usage (stderr, 2); + break; + + default: + usage (stderr, 2); + } + + if (!nomtab) + { + endmntent (F_mtab); + unlock_mtab (); + } + + exit (result); +} diff --git a/mount/mount.h b/mount/mount.h new file mode 100644 index 00000000..d70ccaf9 --- /dev/null +++ b/mount/mount.h @@ -0,0 +1,208 @@ +/* + * Please do not edit this file. + * It was generated using rpcgen. + */ + +#ifndef _MOUNT_H_RPCGEN +#define _MOUNT_H_RPCGEN + +#include + +#define MNTPATHLEN 1024 +#define MNTNAMLEN 255 +#define FHSIZE 32 + +typedef char fhandle[FHSIZE]; +#ifdef __cplusplus +extern "C" bool_t xdr_fhandle(XDR *, fhandle); +#elif __STDC__ +extern bool_t xdr_fhandle(XDR *, fhandle); +#else /* Old Style C */ +bool_t xdr_fhandle(); +#endif /* Old Style C */ + + +struct fhstatus { + u_int fhs_status; + union { + fhandle fhs_fhandle; + } fhstatus_u; +}; +typedef struct fhstatus fhstatus; +#ifdef __cplusplus +extern "C" bool_t xdr_fhstatus(XDR *, fhstatus*); +#elif __STDC__ +extern bool_t xdr_fhstatus(XDR *, fhstatus*); +#else /* Old Style C */ +bool_t xdr_fhstatus(); +#endif /* Old Style C */ + + +typedef char *dirpath; +#ifdef __cplusplus +extern "C" bool_t xdr_dirpath(XDR *, dirpath*); +#elif __STDC__ +extern bool_t xdr_dirpath(XDR *, dirpath*); +#else /* Old Style C */ +bool_t xdr_dirpath(); +#endif /* Old Style C */ + + +typedef char *name; +#ifdef __cplusplus +extern "C" bool_t xdr_name(XDR *, name*); +#elif __STDC__ +extern bool_t xdr_name(XDR *, name*); +#else /* Old Style C */ +bool_t xdr_name(); +#endif /* Old Style C */ + + +typedef struct mountbody *mountlist; +#ifdef __cplusplus +extern "C" bool_t xdr_mountlist(XDR *, mountlist*); +#elif __STDC__ +extern bool_t xdr_mountlist(XDR *, mountlist*); +#else /* Old Style C */ +bool_t xdr_mountlist(); +#endif /* Old Style C */ + + +struct mountbody { + name ml_hostname; + dirpath ml_directory; + mountlist ml_next; +}; +typedef struct mountbody mountbody; +#ifdef __cplusplus +extern "C" bool_t xdr_mountbody(XDR *, mountbody*); +#elif __STDC__ +extern bool_t xdr_mountbody(XDR *, mountbody*); +#else /* Old Style C */ +bool_t xdr_mountbody(); +#endif /* Old Style C */ + + +typedef struct groupnode *groups; +#ifdef __cplusplus +extern "C" bool_t xdr_groups(XDR *, groups*); +#elif __STDC__ +extern bool_t xdr_groups(XDR *, groups*); +#else /* Old Style C */ +bool_t xdr_groups(); +#endif /* Old Style C */ + + +struct groupnode { + name gr_name; + groups gr_next; +}; +typedef struct groupnode groupnode; +#ifdef __cplusplus +extern "C" bool_t xdr_groupnode(XDR *, groupnode*); +#elif __STDC__ +extern bool_t xdr_groupnode(XDR *, groupnode*); +#else /* Old Style C */ +bool_t xdr_groupnode(); +#endif /* Old Style C */ + + +typedef struct exportnode *exports; +#ifdef __cplusplus +extern "C" bool_t xdr_exports(XDR *, exports*); +#elif __STDC__ +extern bool_t xdr_exports(XDR *, exports*); +#else /* Old Style C */ +bool_t xdr_exports(); +#endif /* Old Style C */ + + +struct exportnode { + dirpath ex_dir; + groups ex_groups; + exports ex_next; +}; +typedef struct exportnode exportnode; +#ifdef __cplusplus +extern "C" bool_t xdr_exportnode(XDR *, exportnode*); +#elif __STDC__ +extern bool_t xdr_exportnode(XDR *, exportnode*); +#else /* Old Style C */ +bool_t xdr_exportnode(); +#endif /* Old Style C */ + + +#define MOUNTPROG ((u_long)100005) +#define MOUNTVERS ((u_long)1) + +#ifdef __cplusplus +#define MOUNTPROC_NULL ((u_long)0) +extern "C" void * mountproc_null_1(void *, CLIENT *); +extern "C" void * mountproc_null_1_svc(void *, struct svc_req *); +#define MOUNTPROC_MNT ((u_long)1) +extern "C" fhstatus * mountproc_mnt_1(dirpath *, CLIENT *); +extern "C" fhstatus * mountproc_mnt_1_svc(dirpath *, struct svc_req *); +#define MOUNTPROC_DUMP ((u_long)2) +extern "C" mountlist * mountproc_dump_1(void *, CLIENT *); +extern "C" mountlist * mountproc_dump_1_svc(void *, struct svc_req *); +#define MOUNTPROC_UMNT ((u_long)3) +extern "C" void * mountproc_umnt_1(dirpath *, CLIENT *); +extern "C" void * mountproc_umnt_1_svc(dirpath *, struct svc_req *); +#define MOUNTPROC_UMNTALL ((u_long)4) +extern "C" void * mountproc_umntall_1(void *, CLIENT *); +extern "C" void * mountproc_umntall_1_svc(void *, struct svc_req *); +#define MOUNTPROC_EXPORT ((u_long)5) +extern "C" exports * mountproc_export_1(void *, CLIENT *); +extern "C" exports * mountproc_export_1_svc(void *, struct svc_req *); +#define MOUNTPROC_EXPORTALL ((u_long)6) +extern "C" exports * mountproc_exportall_1(void *, CLIENT *); +extern "C" exports * mountproc_exportall_1_svc(void *, struct svc_req *); + +#elif __STDC__ +#define MOUNTPROC_NULL ((u_long)0) +extern void * mountproc_null_1(void *, CLIENT *); +extern void * mountproc_null_1_svc(void *, struct svc_req *); +#define MOUNTPROC_MNT ((u_long)1) +extern fhstatus * mountproc_mnt_1(dirpath *, CLIENT *); +extern fhstatus * mountproc_mnt_1_svc(dirpath *, struct svc_req *); +#define MOUNTPROC_DUMP ((u_long)2) +extern mountlist * mountproc_dump_1(void *, CLIENT *); +extern mountlist * mountproc_dump_1_svc(void *, struct svc_req *); +#define MOUNTPROC_UMNT ((u_long)3) +extern void * mountproc_umnt_1(dirpath *, CLIENT *); +extern void * mountproc_umnt_1_svc(dirpath *, struct svc_req *); +#define MOUNTPROC_UMNTALL ((u_long)4) +extern void * mountproc_umntall_1(void *, CLIENT *); +extern void * mountproc_umntall_1_svc(void *, struct svc_req *); +#define MOUNTPROC_EXPORT ((u_long)5) +extern exports * mountproc_export_1(void *, CLIENT *); +extern exports * mountproc_export_1_svc(void *, struct svc_req *); +#define MOUNTPROC_EXPORTALL ((u_long)6) +extern exports * mountproc_exportall_1(void *, CLIENT *); +extern exports * mountproc_exportall_1_svc(void *, struct svc_req *); + +#else /* Old Style C */ +#define MOUNTPROC_NULL ((u_long)0) +extern void * mountproc_null_1(); +extern void * mountproc_null_1_svc(); +#define MOUNTPROC_MNT ((u_long)1) +extern fhstatus * mountproc_mnt_1(); +extern fhstatus * mountproc_mnt_1_svc(); +#define MOUNTPROC_DUMP ((u_long)2) +extern mountlist * mountproc_dump_1(); +extern mountlist * mountproc_dump_1_svc(); +#define MOUNTPROC_UMNT ((u_long)3) +extern void * mountproc_umnt_1(); +extern void * mountproc_umnt_1_svc(); +#define MOUNTPROC_UMNTALL ((u_long)4) +extern void * mountproc_umntall_1(); +extern void * mountproc_umntall_1_svc(); +#define MOUNTPROC_EXPORT ((u_long)5) +extern exports * mountproc_export_1(); +extern exports * mountproc_export_1_svc(); +#define MOUNTPROC_EXPORTALL ((u_long)6) +extern exports * mountproc_exportall_1(); +extern exports * mountproc_exportall_1_svc(); +#endif /* Old Style C */ + +#endif /* !_MOUNT_H_RPCGEN */ diff --git a/mount/mount.x b/mount/mount.x new file mode 100644 index 00000000..7e0d7f3a --- /dev/null +++ b/mount/mount.x @@ -0,0 +1,161 @@ +/* @(#)mount.x 2.1 88/08/01 4.0 RPCSRC */ +/* @(#)mount.x 1.2 87/09/18 Copyr 1987 Sun Micro */ + +/* + * 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 + */ + +/* + * Protocol description for the mount program + */ + + +const MNTPATHLEN = 1024; /* maximum bytes in a pathname argument */ +const MNTNAMLEN = 255; /* maximum bytes in a name argument */ +const FHSIZE = 32; /* size in bytes of a file handle */ + +/* + * The fhandle is the file handle that the server passes to the client. + * All file operations are done using the file handles to refer to a file + * or a directory. The file handle can contain whatever information the + * server needs to distinguish an individual file. + */ +typedef opaque fhandle[FHSIZE]; + +/* + * If a status of zero is returned, the call completed successfully, and + * a file handle for the directory follows. A non-zero status indicates + * some sort of error. The status corresponds with UNIX error numbers. + */ +union fhstatus switch (unsigned fhs_status) { +case 0: + fhandle fhs_fhandle; +default: + void; +}; + +/* + * The type dirpath is the pathname of a directory + */ +typedef string dirpath; + +/* + * The type name is used for arbitrary names (hostnames, groupnames) + */ +typedef string name; + +/* + * A list of who has what mounted + */ +typedef struct mountbody *mountlist; +struct mountbody { + name ml_hostname; + dirpath ml_directory; + mountlist ml_next; +}; + +/* + * A list of netgroups + */ +typedef struct groupnode *groups; +struct groupnode { + name gr_name; + groups gr_next; +}; + +/* + * A list of what is exported and to whom + */ +typedef struct exportnode *exports; +struct exportnode { + dirpath ex_dir; + groups ex_groups; + exports ex_next; +}; + +program MOUNTPROG { + /* + * Version one of the mount protocol communicates with version two + * of the NFS protocol. The only connecting point is the fhandle + * structure, which is the same for both protocols. + */ + version MOUNTVERS { + /* + * Does no work. It is made available in all RPC services + * to allow server reponse testing and timing + */ + void + MOUNTPROC_NULL(void) = 0; + + /* + * If fhs_status is 0, then fhs_fhandle contains the + * file handle for the directory. This file handle may + * be used in the NFS protocol. This procedure also adds + * a new entry to the mount list for this client mounting + * the directory. + * Unix authentication required. + */ + fhstatus + MOUNTPROC_MNT(dirpath) = 1; + + /* + * Returns the list of remotely mounted filesystems. The + * mountlist contains one entry for each hostname and + * directory pair. + */ + mountlist + MOUNTPROC_DUMP(void) = 2; + + /* + * Removes the mount list entry for the directory + * Unix authentication required. + */ + void + MOUNTPROC_UMNT(dirpath) = 3; + + /* + * Removes all of the mount list entries for this client + * Unix authentication required. + */ + void + MOUNTPROC_UMNTALL(void) = 4; + + /* + * Returns a list of all the exported filesystems, and which + * machines are allowed to import it. + */ + exports + MOUNTPROC_EXPORT(void) = 5; + + /* + * Identical to MOUNTPROC_EXPORT above + */ + exports + MOUNTPROC_EXPORTALL(void) = 6; + } = 1; +} = 100005; diff --git a/mount/mount_clnt.c b/mount/mount_clnt.c new file mode 100644 index 00000000..bc6e5122 --- /dev/null +++ b/mount/mount_clnt.c @@ -0,0 +1,94 @@ +/* + * Please do not edit this file. + * It was generated using rpcgen. + */ + +#include /* for memset */ +#include "mount.h" + +/* Default timeout can be changed using clnt_control() */ +static struct timeval TIMEOUT = { 25, 0 }; + +void * +mountproc_null_1(void *argp, CLIENT *clnt) +{ + static char clnt_res; + + memset((char *)&clnt_res, 0, sizeof(clnt_res)); + if (clnt_call(clnt, MOUNTPROC_NULL, xdr_void, argp, xdr_void, &clnt_res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return ((void *)&clnt_res); +} + +fhstatus * +mountproc_mnt_1(dirpath *argp, CLIENT *clnt) +{ + static fhstatus clnt_res; + + memset((char *)&clnt_res, 0, sizeof(clnt_res)); + if (clnt_call(clnt, MOUNTPROC_MNT, xdr_dirpath, argp, xdr_fhstatus, &clnt_res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return (&clnt_res); +} + +mountlist * +mountproc_dump_1(void *argp, CLIENT *clnt) +{ + static mountlist clnt_res; + + memset((char *)&clnt_res, 0, sizeof(clnt_res)); + if (clnt_call(clnt, MOUNTPROC_DUMP, xdr_void, argp, xdr_mountlist, &clnt_res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return (&clnt_res); +} + +void * +mountproc_umnt_1(dirpath *argp, CLIENT *clnt) +{ + static char clnt_res; + + memset((char *)&clnt_res, 0, sizeof(clnt_res)); + if (clnt_call(clnt, MOUNTPROC_UMNT, xdr_dirpath, argp, xdr_void, &clnt_res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return ((void *)&clnt_res); +} + +void * +mountproc_umntall_1(void *argp, CLIENT *clnt) +{ + static char clnt_res; + + memset((char *)&clnt_res, 0, sizeof(clnt_res)); + if (clnt_call(clnt, MOUNTPROC_UMNTALL, xdr_void, argp, xdr_void, &clnt_res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return ((void *)&clnt_res); +} + +exports * +mountproc_export_1(void *argp, CLIENT *clnt) +{ + static exports clnt_res; + + memset((char *)&clnt_res, 0, sizeof(clnt_res)); + if (clnt_call(clnt, MOUNTPROC_EXPORT, xdr_void, argp, xdr_exports, &clnt_res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return (&clnt_res); +} + +exports * +mountproc_exportall_1(void *argp, CLIENT *clnt) +{ + static exports clnt_res; + + memset((char *)&clnt_res, 0, sizeof(clnt_res)); + if (clnt_call(clnt, MOUNTPROC_EXPORTALL, xdr_void, argp, xdr_exports, &clnt_res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return (&clnt_res); +} diff --git a/mount/mount_xdr.c b/mount/mount_xdr.c new file mode 100644 index 00000000..be5eb41f --- /dev/null +++ b/mount/mount_xdr.c @@ -0,0 +1,150 @@ +/* + * Please do not edit this file. + * It was generated using rpcgen. + */ + +#include "mount.h" + +bool_t +xdr_fhandle(XDR *xdrs, fhandle objp) +{ + + register long *buf; + + if (!xdr_opaque(xdrs, objp, FHSIZE)) { + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_fhstatus(XDR *xdrs, fhstatus *objp) +{ + + register long *buf; + + if (!xdr_u_int(xdrs, &objp->fhs_status)) { + return (FALSE); + } + switch (objp->fhs_status) { + case 0: + if (!xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle)) { + return (FALSE); + } + break; + default: + break; + } + return (TRUE); +} + +bool_t +xdr_dirpath(XDR *xdrs, dirpath *objp) +{ + + register long *buf; + + if (!xdr_string(xdrs, objp, MNTPATHLEN)) { + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_name(XDR *xdrs, name *objp) +{ + + register long *buf; + + if (!xdr_string(xdrs, objp, MNTNAMLEN)) { + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_mountlist(XDR *xdrs, mountlist *objp) +{ + + register long *buf; + + if (!xdr_pointer(xdrs, (char **)objp, sizeof(struct mountbody), (xdrproc_t)xdr_mountbody)) { + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_mountbody(XDR *xdrs, mountbody *objp) +{ + + register long *buf; + + if (!xdr_name(xdrs, &objp->ml_hostname)) { + return (FALSE); + } + if (!xdr_dirpath(xdrs, &objp->ml_directory)) { + return (FALSE); + } + if (!xdr_mountlist(xdrs, &objp->ml_next)) { + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_groups(XDR *xdrs, groups *objp) +{ + + register long *buf; + + if (!xdr_pointer(xdrs, (char **)objp, sizeof(struct groupnode), (xdrproc_t)xdr_groupnode)) { + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_groupnode(XDR *xdrs, groupnode *objp) +{ + + register long *buf; + + if (!xdr_name(xdrs, &objp->gr_name)) { + return (FALSE); + } + if (!xdr_groups(xdrs, &objp->gr_next)) { + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_exports(XDR *xdrs, exports *objp) +{ + + register long *buf; + + if (!xdr_pointer(xdrs, (char **)objp, sizeof(struct exportnode), (xdrproc_t)xdr_exportnode)) { + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_exportnode(XDR *xdrs, exportnode *objp) +{ + + register long *buf; + + if (!xdr_dirpath(xdrs, &objp->ex_dir)) { + return (FALSE); + } + if (!xdr_groups(xdrs, &objp->ex_groups)) { + return (FALSE); + } + if (!xdr_exports(xdrs, &objp->ex_next)) { + return (FALSE); + } + return (TRUE); +} diff --git a/mount/nfs.5 b/mount/nfs.5 new file mode 100644 index 00000000..ee5203f5 --- /dev/null +++ b/mount/nfs.5 @@ -0,0 +1,209 @@ +.\" nfs.5 "Rick Sladkey" +.\" Wed Feb 8 12:52:42 1995, faith@cs.unc.edu: updates for Ross Biro's +.\" patches. " +.TH NFS 5 "20 November 1993" "Linux 0.99" "Linux Programmer's Manual" +.SH NAME +nfs \- nfs fstab format and options +.SH SYNOPSIS +.B /etc/fstab +.SH DESCRIPTION +The +.I fstab +file contains information about which filesystems +to mount where and with what options. +For NFS mounts, it contains the server name and +exported server directory to mount from, +the local directory that is the mount point, +and the NFS specific options that control +the way the filesystem is mounted. +.P +Here is an example from an \fI/etc/fstab\fP file from an NFS mount. +.sp +.nf +.ta 2.5i +0.75i +0.75i +1.0i +server:/usr/local/pub /pub nfs timeo=14,intr +.fi +.DT +.SS Options +.TP 1.5i +.I rsize=n +The number of bytes NFS uses when reading files from an NFS server. +The default value is dependent on the kernel, currently 1024 bytes. +.TP 1.5i +.I wsize=n +The number of bytes NFS uses when writing files to an NFS server. +The default value is dependent on the kernel, currently 1024 bytes. +.TP 1.5i +.I timeo=n +The value in tenths of a second before sending the +first retransmission after an RPC timeout. +The default value is 7 tenths of a second. After the first timeout, +the timeout is doubled after each successive timeout until a maximum +timeout of 60 seconds is reached or the enough retransmissions +have occured to cause a major timeout. Then, if the filesystem +is hard mounted, each new timeout cascade restarts at twice the +initial value of the previous cascade, again doubling at each +retransmission. The maximum timeout is always 60 seconds. +Better overall performance may be achieved by increasing the +timeout when mounting on a busy network, to a slow server, or through +several routers or gateways. +.TP 1.5i +.I retrans=n +The number of minor timeouts and retransmissions that must occur before +a major timeout occurs. The default is 3 timeouts. When a major timeout +occurs, the file operation is either aborted or a "server not responding" +message is printed on the console. +.TP 1.5i +.I acregmin=n +The minimum time in seconds that attributes of a regular file should +be cached before requesting fresh information from a server. +The default is 3 seconds. +.TP 1.5i +.I acregmax=n +The maximum time in seconds that attributes of a regular file can +be cached before requesting fresh information from a server. +The default is 60 seconds. +.TP 1.5i +.I acdirmin=n +The minimum time in seconds that attributes of a directory should +be cached before requesting fresh information from a server. +The default is 30 seconds. +.TP 1.5i +.I acdirmax=n +The maximum time in seconds that attributes of a directory can +be cached before requesting fresh information from a server. +The default is 60 seconds. +.TP 1.5i +.I actimeo=n +Using actimeo sets all of +.I acregmin, +.I acregmax, +.I acdirmin, +and +.I acdirmax +to the same value. +There is no default value. +.TP 1.5i +.I retry=n +The number of times to retry a backgrounded NFS mount operation +before giving up. +The default value is 10000 times. +.TP 1.5i +.I namlen=n +When an NFS server does not support version two of the +RPC mount protocol, this option can be used to specify +the maximum length of a filename that is supported on +the remote filesystem. This is used to support the +POSIX pathconf functions. The default is 255 characters. +.TP 1.5i +.I port=n +The numeric value of the port to connect to the NFS server on. +If the port number is 0 (the default) then query the +remote host's portmapper for the port number to use. +If the remote host's NFS daemon is not registered with +its portmapper, the standard NFS port number 2049 is +used instead. +.TP 1.5i +.I mountport=n +The numeric value of the +.B mountd +port. +.TP 1.5i +.I mounthost=name +The name of the host running +.B mountd . +.TP 1.5i +.I mountprog=n +Use an alternate RPC program number to contact the +mount daemon on the remote host. This option is useful +for hosts that can run multiple NFS servers. +The default value is 100005 which is the standard RPC +mount daemon program number. +.TP 1.5i +.I mountvers=n +Use an alternate RPC version number to contact the +mount daemon on the remote host. This option is useful +for hosts that can run multiple NFS servers. +The default value is version 1. +.TP 1.5i +.I nfsprog=n +Use an alternate RPC program number to contact the +NFS daemon on the remote host. This option is useful +for hosts that can run multiple NFS servers. +The default value is 100003 which is the standard RPC +NFS daemon program number. +.TP 1.5i +.I nfsvers=n +Use an alternate RPC version number to contact the +NFS daemon on the remote host. This option is useful +for hosts that can run multiple NFS servers. +The default value is version 2. +.TP 1.5i +.I bg +If the first NFS mount attempt times out, continue trying the mount +in the background. +The default is to not to background the mount on timeout but fail. +.TP 1.5i +.I fg +If the first NFS mount attempt times out, fail immediately. +This is the default. +.TP 1.5i +.I soft +If an NFS file operation has a major timeout then report an I/O error to +the calling program. +The default is to continue retrying NFS file operations indefinitely. +.TP 1.5i +.I hard +If an NFS file operation has a major timeout then report +"server not responding" on the console and continue retrying indefinitely. +This is the default. +.TP 1.5i +.I intr +If an NFS file operation has a major timeout and it is hard mounted, +then allow signals to interupt the file operation and cause it to +return EINTR to the calling program. The default is to not +allow file operations to be interrupted. +.TP 1.5i +.I posix +Mount the NFS filesystem using POSIX semantics. This allows +an NFS filesystem to properly support the POSIX pathconf +command by querying the mount server for the maximum length +of a filename. To do this, the remote host must support version +two of the RPC mount protocol. Many NFS servers support only +version one. +.TP 1.5i +.I nocto +Supress the retrieval of new attributes when creating a file. +.TP 1.5i +.I noac +Disable all forms of attribute caching entirely. This extracts a +server performance penalty but it allows two different NFS clients +to get reasonable good results when both clients are actively +writing to common filesystem on the server. +.TP 1.5i +.I tcp +Mount the NFS filesystem using the TCP protocol instead of the +default UDP protocol. Many NFS severs only support UDP. +.TP 1.5i +.I udp +Mount the NFS filesystem using the UDP protocol. This +is the default. +.P +All of the non-value options have corresponding nooption forms. +For example, nointr means don't allow file operations to be +interrupted. +.SH FILES +.I /etc/fstab +.SH "SEE ALSO" +.BR fstab "(5), " mount "(8), " umount (8) +.SH AUTHOR +"Rick Sladkey" +.SH BUGS +The bg, fg, retry, posix, and nocto options are parsed by mount +but currently are silently ignored. +.P +The tcp and namlen options are implemented but are not currently +supported by the Linux kernel. +.P +The umount command should notify the server +when an NFS filesystem is unmounted. diff --git a/mount/nfsmount.c b/mount/nfsmount.c new file mode 100644 index 00000000..9be51be0 --- /dev/null +++ b/mount/nfsmount.c @@ -0,0 +1,475 @@ +/* + * nfsmount.c -- Linux NFS mount + * Copyright (C) 1993 Rick Sladkey + * + * 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. + * + * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port + * numbers to be specified on the command line. + */ + +/* + * nfsmount.c,v 1.1.1.1 1993/11/18 08:40:51 jrs Exp + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sundries.h" + +#include "mount.h" + +#include +#include +#include + +static char *strndup (char *str, int n) { + char *ret; + ret = malloc (n+1); + if (ret == NULL) { + perror ("malloc"); + return (NULL); + } + strncpy (ret, str, n); + return (ret); +} + +static char *nfs_strerror(int stat); + +int nfsmount(const char *spec, const char *node, int *flags, + char **extra_opts, char **mount_opts) +{ + char hostdir[1024]; + CLIENT *mclient; + char *hostname; + char *dirname; + char *old_opts; + char *mounthost=NULL; + char new_opts[1024]; + fhandle root_fhandle; + struct timeval total_timeout; + enum clnt_stat clnt_stat; + static struct nfs_mount_data data; + char *opt, *opteq; + int val; + struct hostent *hp; + struct sockaddr_in server_addr; + struct sockaddr_in mount_server_addr; + int msock, fsock; + struct timeval pertry_timeout; + struct fhstatus status; + char *s; + int port; + int mountport; + int bg; + int soft; + int intr; + int posix; + int nocto; + int noac; + int retry; + int tcp; + int mountprog; + int mountvers; + int nfsprog; + int nfsvers; + + msock = fsock = -1; + mclient = NULL; + strcpy(hostdir, spec); + if ((s = (strchr(hostdir, ':')))) { + hostname = hostdir; + dirname = s + 1; + *s = '\0'; + } + else { + fprintf(stderr, "mount: " + "directory to mount not in host:dir format\n"); + goto fail; + } + + if (hostname[0] >= '0' && hostname[0] <= '9') { + server_addr.sin_family = AF_INET; + server_addr.sin_addr.s_addr = inet_addr(hostname); + } + else if ((hp = gethostbyname(hostname)) == NULL) { + fprintf(stderr, "mount: can't get address for %s\n", hostname); + goto fail; + } + else { + server_addr.sin_family = AF_INET; + memcpy(&server_addr.sin_addr, hp->h_addr, hp->h_length); + } + + memcpy (&mount_server_addr, &server_addr, sizeof (mount_server_addr)); + + /* add IP address to mtab options for use when unmounting */ + + old_opts = *extra_opts; + if (!old_opts) + old_opts = ""; + sprintf(new_opts, "%s%saddr=%s", + old_opts, *old_opts ? "," : "", + inet_ntoa(server_addr.sin_addr)); + *extra_opts = strdup(new_opts); + + /* set default options */ + + data.rsize = 0; /* let kernel decide */ + data.wsize = 0; /* let kernel decide */ + data.timeo = 7; + data.retrans = 3; + data.acregmin = 3; + data.acregmax = 60; + data.acdirmin = 30; + data.acdirmax = 60; +#if NFS_MOUNT_VERSION >= 2 + data.namlen = NAME_MAX; +#endif + + bg = 0; + soft = 0; + intr = 0; + posix = 0; + nocto = 0; + noac = 0; + retry = 10000; + tcp = 0; + + mountprog = MOUNTPROG; + mountvers = MOUNTVERS; + port = 0; + mountport = 0; + nfsprog = NFS_PROGRAM; + nfsvers = NFS_VERSION; + + /* parse options */ + + for (opt = strtok(old_opts, ","); opt; opt = strtok(NULL, ",")) { + if ((opteq = strchr(opt, '='))) { + val = atoi(opteq + 1); + *opteq = '\0'; + if (!strcmp(opt, "rsize")) + data.rsize = val; + else if (!strcmp(opt, "wsize")) + data.wsize = val; + else if (!strcmp(opt, "timeo")) + data.timeo = val; + else if (!strcmp(opt, "retrans")) + data.retrans = val; + else if (!strcmp(opt, "acregmin")) + data.acregmin = val; + else if (!strcmp(opt, "acregmax")) + data.acregmax = val; + else if (!strcmp(opt, "acdirmin")) + data.acdirmin = val; + else if (!strcmp(opt, "acdirmax")) + data.acdirmax = val; + else if (!strcmp(opt, "actimeo")) { + data.acregmin = val; + data.acregmax = val; + data.acdirmin = val; + data.acdirmax = val; + } + else if (!strcmp(opt, "retry")) + retry = val; + else if (!strcmp(opt, "port")) + port = val; + else if (!strcmp(opt, "mountport")) + mountport = val; + else if (!strcmp(opt, "mounthost")) + mounthost=strndup(opteq+1, + strcspn(opteq+1," \t\n\r,")); + else if (!strcmp(opt, "mountprog")) + mountprog = val; + else if (!strcmp(opt, "mountvers")) + mountvers = val; + else if (!strcmp(opt, "nfsprog")) + nfsprog = val; + else if (!strcmp(opt, "nfsvers")) + nfsvers = val; + else if (!strcmp(opt, "namlen")) { +#if NFS_MOUNT_VERSION >= 2 + data.namlen = val; +#else + printf("Warning: Option namlen is not supported.\n"); +#endif + } + else if (!strcmp(opt, "addr")) + /* ignore */; + else { + printf("unknown nfs mount parameter: " + "%s=%d\n", opt, val); + goto fail; + } + } + else { + val = 1; + if (!strncmp(opt, "no", 2)) { + val = 0; + opt += 2; + } + if (!strcmp(opt, "bg")) + bg = val; + else if (!strcmp(opt, "fg")) + bg = !val; + else if (!strcmp(opt, "soft")) + soft = val; + else if (!strcmp(opt, "hard")) + soft = !val; + else if (!strcmp(opt, "intr")) + intr = val; + else if (!strcmp(opt, "posix")) + posix = val; + else if (!strcmp(opt, "cto")) + nocto = !val; + else if (!strcmp(opt, "ac")) + noac = !val; + else if (!strcmp(opt, "tcp")) + tcp = val; + else if (!strcmp(opt, "udp")) + tcp = !val; + else { + printf("unknown nfs mount option: " + "%s%s\n", val ? "" : "no", opt); + goto fail; + } + } + } + data.flags = (soft ? NFS_MOUNT_SOFT : 0) + | (intr ? NFS_MOUNT_INTR : 0) + | (posix ? NFS_MOUNT_POSIX : 0) + | (nocto ? NFS_MOUNT_NOCTO : 0) + | (noac ? NFS_MOUNT_NOAC : 0); +#if NFS_MOUNT_VERSION >= 2 + data.flags |= (tcp ? NFS_MOUNT_TCP : 0); +#endif + +#ifdef NFS_MOUNT_DEBUG + printf("rsize = %d, wsize = %d, timeo = %d, retrans = %d\n", + data.rsize, data.wsize, data.timeo, data.retrans); + printf("acreg (min, max) = (%d, %d), acdir (min, max) = (%d, %d)\n", + data.acregmin, data.acregmax, data.acdirmin, data.acdirmax); + printf("port = %d, bg = %d, retry = %d, flags = %.8x\n", + port, bg, retry, data.flags); + printf("mountprog = %d, mountvers = %d, nfsprog = %d, nfsvers = %d\n", + mountprog, mountvers, nfsprog, nfsvers); + printf("soft = %d, intr = %d, posix = %d, nocto = %d, noac = %d\n", + (data.flags & NFS_MOUNT_SOFT) != 0, + (data.flags & NFS_MOUNT_INTR) != 0, + (data.flags & NFS_MOUNT_POSIX) != 0, + (data.flags & NFS_MOUNT_NOCTO) != 0, + (data.flags & NFS_MOUNT_NOAC) != 0); +#if NFS_MOUNT_VERSION >= 2 + printf("tcp = %d\n", + (data.flags & NFS_MOUNT_TCP) != 0); +#endif +#if 0 + goto fail; +#endif +#endif + + data.version = NFS_MOUNT_VERSION; + *mount_opts = (char *) &data; + + if (*flags & MS_REMOUNT) + return 0; + + /* create mount deamon client */ + /* See if the nfs host = mount host. */ + if (mounthost) { + if (mounthost[0] >= '0' && mounthost[0] <= '9') { + mount_server_addr.sin_family = AF_INET; + mount_server_addr.sin_addr.s_addr = inet_addr(hostname); + } + else if ((hp = gethostbyname(mounthost)) == NULL) { + fprintf(stderr, "mount: can't get address for %s\n", hostname); + goto fail; + } + else { + mount_server_addr.sin_family = AF_INET; + memcpy(&mount_server_addr.sin_addr, hp->h_addr, hp->h_length); + } + } + + mount_server_addr.sin_port = htons(mountport); + msock = RPC_ANYSOCK; + if ((mclient = clnttcp_create(&mount_server_addr, + mountprog, mountvers, &msock, 0, 0)) == NULL) { + mount_server_addr.sin_port = htons(mountport); + msock = RPC_ANYSOCK; + pertry_timeout.tv_sec = 3; + pertry_timeout.tv_usec = 0; + if ((mclient = clntudp_create(&mount_server_addr, + mountprog, mountvers, pertry_timeout, &msock)) == NULL) { + clnt_pcreateerror("mount clntudp_create"); + goto fail; + } +#ifdef NFS_MOUNT_DEBUG + printf("using UDP for mount deamon\n"); +#endif + } +#ifdef NFS_MOUNT_DEBUG + else + printf("using TCP for mount deamon\n"); +#endif + mclient->cl_auth = authunix_create_default(); + total_timeout.tv_sec = 20; + total_timeout.tv_usec = 0; + + /* try to mount hostname:dirname */ + + clnt_stat = clnt_call(mclient, MOUNTPROC_MNT, + xdr_dirpath, &dirname, + xdr_fhstatus, &status, + total_timeout); + if (clnt_stat != RPC_SUCCESS) { + clnt_perror(mclient, "rpc mount"); + goto fail; + } + if (status.fhs_status != 0) { + fprintf(stderr, + "mount: %s:%s failed, reason given by server: %s\n", + hostname, dirname, nfs_strerror(status.fhs_status)); + goto fail; + } + memcpy((char *) &root_fhandle, (char *) status.fhstatus_u.fhs_fhandle, + sizeof (root_fhandle)); + + /* create nfs socket for kernel */ + + if (tcp) { +#if NFS_MOUNT_VERSION >= 2 + fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); +#else + printf("NFS over TCP is not supported.\n"); + goto fail; +#endif + } + else + fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (fsock < 0) { + perror("nfs socket"); + goto fail; + } + if (bindresvport(fsock, 0) < 0) { + perror("nfs bindresvport"); + goto fail; + } + if (port == 0) { + server_addr.sin_port = PMAPPORT; + port = pmap_getport(&server_addr, nfsprog, nfsvers, + tcp ? IPPROTO_TCP : IPPROTO_UDP); + if (port == 0) + port = NFS_PORT; +#ifdef NFS_MOUNT_DEBUG + else + printf("used portmapper to find NFS port\n"); +#endif + } +#ifdef NFS_MOUNT_DEBUG + printf("using port %d for nfs deamon\n", port); +#endif + server_addr.sin_port = htons(port); + if (connect(fsock, (struct sockaddr *) &server_addr, + sizeof (server_addr)) < 0) { + perror("nfs connect"); + goto fail; + } + + /* prepare data structure for kernel */ + + data.fd = fsock; + memcpy((char *) &data.root, (char *) &root_fhandle, + sizeof (root_fhandle)); + memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr)); + strncpy(data.hostname, hostname, sizeof(data.hostname)); + + /* clean up */ + + auth_destroy(mclient->cl_auth); + clnt_destroy(mclient); + close(msock); + return 0; + + /* abort */ + +fail: + if (msock != -1) { + auth_destroy(mclient->cl_auth); + clnt_destroy(mclient); + close(msock); + } + if (fsock != -1) + close(fsock); + return 1;} + + +/* + * We need to translate between nfs status return values and + * the local errno values which may not be the same. + */ + +#ifndef EDQUOT +#define EDQUOT ENOSPC +#endif + +static struct { + enum nfs_stat stat; + int errno; +} nfs_errtbl[] = { + { NFS_OK, 0 }, + { NFSERR_PERM, EPERM }, + { NFSERR_NOENT, ENOENT }, + { NFSERR_IO, EIO }, + { NFSERR_NXIO, ENXIO }, + { NFSERR_ACCES, EACCES }, + { NFSERR_EXIST, EEXIST }, + { NFSERR_NODEV, ENODEV }, + { NFSERR_NOTDIR, ENOTDIR }, + { NFSERR_ISDIR, EISDIR }, +#ifdef NFSERR_INVAL + { NFSERR_INVAL, EINVAL }, /* that Sun forgot */ +#endif + { NFSERR_FBIG, EFBIG }, + { NFSERR_NOSPC, ENOSPC }, + { NFSERR_ROFS, EROFS }, + { NFSERR_NAMETOOLONG, ENAMETOOLONG }, + { NFSERR_NOTEMPTY, ENOTEMPTY }, + { NFSERR_DQUOT, EDQUOT }, + { NFSERR_STALE, ESTALE }, +#ifdef EWFLUSH + { NFSERR_WFLUSH, EWFLUSH }, +#endif + { -1, EIO } +}; + +static char *nfs_strerror(int stat) +{ + int i; + static char buf[256]; + + for (i = 0; nfs_errtbl[i].stat != -1; i++) { + if (nfs_errtbl[i].stat == stat) + return strerror(nfs_errtbl[i].errno); + } + sprintf(buf, "unknown nfs status return value: %d", stat); + return buf; +} + diff --git a/mount/realpath.c b/mount/realpath.c new file mode 100644 index 00000000..4ea46a31 --- /dev/null +++ b/mount/realpath.c @@ -0,0 +1,178 @@ +/* + * realpath.c -- canonicalize pathname by removing symlinks + * Copyright (C) 1993 Rick Sladkey + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library 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 Library Public License for more details. + */ + +/* + * realpath.c,v 1.1.1.1 1993/11/18 08:40:51 jrs Exp + */ + +#ifdef __linux__ +extern char *realpath(const char *path, char *resolved_path); +#define HAVE_UNISTD_H +#define HAVE_STRING_H +#endif + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#if defined(HAVE_UNISTD_H) || defined(STDC_HEADERS) +#include +#endif +#include +#ifdef HAVE_STRING_H +#include +#else +#include +#endif +#ifdef _POSIX_VERSION +#include /* for PATH_MAX */ +#else +#include /* for MAXPATHLEN */ +#endif +#include +#ifndef STDC_HEADERS +extern int errno; +#endif + +#include /* for S_IFLNK */ + +#ifndef PATH_MAX +#ifdef _POSIX_VERSION +#define PATH_MAX _POSIX_PATH_MAX +#else +#ifdef MAXPATHLEN +#define PATH_MAX MAXPATHLEN +#else +#define PATH_MAX 1024 +#endif +#endif +#endif + +#define MAX_READLINKS 32 + +#ifdef __STDC__ +char *realpath(const char *path, char *resolved_path) +#else +char *realpath(path, resolved_path) +const char *path; +char *resolved_path; +#endif +{ + char copy_path[PATH_MAX]; + char link_path[PATH_MAX]; + char *new_path = resolved_path; + char *max_path; + int readlinks = 0; + int n; + + /* Make a copy of the source path since we may need to modify it. */ + strcpy(copy_path, path); + path = copy_path; + max_path = copy_path + PATH_MAX - 2; + /* If it's a relative pathname use getwd for starters. */ + if (*path != '/') { +#ifdef HAVE_GETCWD + getcwd(new_path, PATH_MAX - 1); +#else + getwd(new_path); +#endif + new_path += strlen(new_path); + if (new_path[-1] != '/') + *new_path++ = '/'; + } + else { + *new_path++ = '/'; + path++; + } + /* Expand each slash-separated pathname component. */ + while (*path != '\0') { + /* Ignore stray "/". */ + if (*path == '/') { + path++; + continue; + } + if (*path == '.') { + /* Ignore ".". */ + if (path[1] == '\0' || path[1] == '/') { + path++; + continue; + } + if (path[1] == '.') { + if (path[2] == '\0' || path[2] == '/') { + path += 2; + /* Ignore ".." at root. */ + if (new_path == resolved_path + 1) + continue; + /* Handle ".." by backing up. */ + while ((--new_path)[-1] != '/') + ; + continue; + } + } + } + /* Safely copy the next pathname component. */ + while (*path != '\0' && *path != '/') { + if (path > max_path) { + errno = ENAMETOOLONG; + return NULL; + } + *new_path++ = *path++; + } +#ifdef S_IFLNK + /* Protect against infinite loops. */ + if (readlinks++ > MAX_READLINKS) { + errno = ELOOP; + return NULL; + } + /* See if latest pathname component is a symlink. */ + *new_path = '\0'; + n = readlink(resolved_path, link_path, PATH_MAX - 1); + if (n < 0) { + /* EINVAL means the file exists but isn't a symlink. */ + if (errno != EINVAL) + return NULL; + } + else { + /* Note: readlink doesn't add the null byte. */ + link_path[n] = '\0'; + if (*link_path == '/') + /* Start over for an absolute symlink. */ + new_path = resolved_path; + else + /* Otherwise back up over this component. */ + while (*(--new_path) != '/') + ; + /* Safe sex check. */ + if (strlen(path) + n >= PATH_MAX) { + errno = ENAMETOOLONG; + return NULL; + } + /* Insert symlink contents into path. */ + strcat(link_path, path); + strcpy(copy_path, link_path); + path = copy_path; + } +#endif /* S_IFLNK */ + *new_path++ = '/'; + } + /* Delete trailing slash but don't whomp a lone slash. */ + if (new_path != resolved_path + 1 && new_path[-1] == '/') + new_path--; + /* Make sure it's null terminated. */ + *new_path = '\0'; + return resolved_path; +} + diff --git a/mount/rpcsvc/mount.h b/mount/rpcsvc/mount.h new file mode 100644 index 00000000..d70ccaf9 --- /dev/null +++ b/mount/rpcsvc/mount.h @@ -0,0 +1,208 @@ +/* + * Please do not edit this file. + * It was generated using rpcgen. + */ + +#ifndef _MOUNT_H_RPCGEN +#define _MOUNT_H_RPCGEN + +#include + +#define MNTPATHLEN 1024 +#define MNTNAMLEN 255 +#define FHSIZE 32 + +typedef char fhandle[FHSIZE]; +#ifdef __cplusplus +extern "C" bool_t xdr_fhandle(XDR *, fhandle); +#elif __STDC__ +extern bool_t xdr_fhandle(XDR *, fhandle); +#else /* Old Style C */ +bool_t xdr_fhandle(); +#endif /* Old Style C */ + + +struct fhstatus { + u_int fhs_status; + union { + fhandle fhs_fhandle; + } fhstatus_u; +}; +typedef struct fhstatus fhstatus; +#ifdef __cplusplus +extern "C" bool_t xdr_fhstatus(XDR *, fhstatus*); +#elif __STDC__ +extern bool_t xdr_fhstatus(XDR *, fhstatus*); +#else /* Old Style C */ +bool_t xdr_fhstatus(); +#endif /* Old Style C */ + + +typedef char *dirpath; +#ifdef __cplusplus +extern "C" bool_t xdr_dirpath(XDR *, dirpath*); +#elif __STDC__ +extern bool_t xdr_dirpath(XDR *, dirpath*); +#else /* Old Style C */ +bool_t xdr_dirpath(); +#endif /* Old Style C */ + + +typedef char *name; +#ifdef __cplusplus +extern "C" bool_t xdr_name(XDR *, name*); +#elif __STDC__ +extern bool_t xdr_name(XDR *, name*); +#else /* Old Style C */ +bool_t xdr_name(); +#endif /* Old Style C */ + + +typedef struct mountbody *mountlist; +#ifdef __cplusplus +extern "C" bool_t xdr_mountlist(XDR *, mountlist*); +#elif __STDC__ +extern bool_t xdr_mountlist(XDR *, mountlist*); +#else /* Old Style C */ +bool_t xdr_mountlist(); +#endif /* Old Style C */ + + +struct mountbody { + name ml_hostname; + dirpath ml_directory; + mountlist ml_next; +}; +typedef struct mountbody mountbody; +#ifdef __cplusplus +extern "C" bool_t xdr_mountbody(XDR *, mountbody*); +#elif __STDC__ +extern bool_t xdr_mountbody(XDR *, mountbody*); +#else /* Old Style C */ +bool_t xdr_mountbody(); +#endif /* Old Style C */ + + +typedef struct groupnode *groups; +#ifdef __cplusplus +extern "C" bool_t xdr_groups(XDR *, groups*); +#elif __STDC__ +extern bool_t xdr_groups(XDR *, groups*); +#else /* Old Style C */ +bool_t xdr_groups(); +#endif /* Old Style C */ + + +struct groupnode { + name gr_name; + groups gr_next; +}; +typedef struct groupnode groupnode; +#ifdef __cplusplus +extern "C" bool_t xdr_groupnode(XDR *, groupnode*); +#elif __STDC__ +extern bool_t xdr_groupnode(XDR *, groupnode*); +#else /* Old Style C */ +bool_t xdr_groupnode(); +#endif /* Old Style C */ + + +typedef struct exportnode *exports; +#ifdef __cplusplus +extern "C" bool_t xdr_exports(XDR *, exports*); +#elif __STDC__ +extern bool_t xdr_exports(XDR *, exports*); +#else /* Old Style C */ +bool_t xdr_exports(); +#endif /* Old Style C */ + + +struct exportnode { + dirpath ex_dir; + groups ex_groups; + exports ex_next; +}; +typedef struct exportnode exportnode; +#ifdef __cplusplus +extern "C" bool_t xdr_exportnode(XDR *, exportnode*); +#elif __STDC__ +extern bool_t xdr_exportnode(XDR *, exportnode*); +#else /* Old Style C */ +bool_t xdr_exportnode(); +#endif /* Old Style C */ + + +#define MOUNTPROG ((u_long)100005) +#define MOUNTVERS ((u_long)1) + +#ifdef __cplusplus +#define MOUNTPROC_NULL ((u_long)0) +extern "C" void * mountproc_null_1(void *, CLIENT *); +extern "C" void * mountproc_null_1_svc(void *, struct svc_req *); +#define MOUNTPROC_MNT ((u_long)1) +extern "C" fhstatus * mountproc_mnt_1(dirpath *, CLIENT *); +extern "C" fhstatus * mountproc_mnt_1_svc(dirpath *, struct svc_req *); +#define MOUNTPROC_DUMP ((u_long)2) +extern "C" mountlist * mountproc_dump_1(void *, CLIENT *); +extern "C" mountlist * mountproc_dump_1_svc(void *, struct svc_req *); +#define MOUNTPROC_UMNT ((u_long)3) +extern "C" void * mountproc_umnt_1(dirpath *, CLIENT *); +extern "C" void * mountproc_umnt_1_svc(dirpath *, struct svc_req *); +#define MOUNTPROC_UMNTALL ((u_long)4) +extern "C" void * mountproc_umntall_1(void *, CLIENT *); +extern "C" void * mountproc_umntall_1_svc(void *, struct svc_req *); +#define MOUNTPROC_EXPORT ((u_long)5) +extern "C" exports * mountproc_export_1(void *, CLIENT *); +extern "C" exports * mountproc_export_1_svc(void *, struct svc_req *); +#define MOUNTPROC_EXPORTALL ((u_long)6) +extern "C" exports * mountproc_exportall_1(void *, CLIENT *); +extern "C" exports * mountproc_exportall_1_svc(void *, struct svc_req *); + +#elif __STDC__ +#define MOUNTPROC_NULL ((u_long)0) +extern void * mountproc_null_1(void *, CLIENT *); +extern void * mountproc_null_1_svc(void *, struct svc_req *); +#define MOUNTPROC_MNT ((u_long)1) +extern fhstatus * mountproc_mnt_1(dirpath *, CLIENT *); +extern fhstatus * mountproc_mnt_1_svc(dirpath *, struct svc_req *); +#define MOUNTPROC_DUMP ((u_long)2) +extern mountlist * mountproc_dump_1(void *, CLIENT *); +extern mountlist * mountproc_dump_1_svc(void *, struct svc_req *); +#define MOUNTPROC_UMNT ((u_long)3) +extern void * mountproc_umnt_1(dirpath *, CLIENT *); +extern void * mountproc_umnt_1_svc(dirpath *, struct svc_req *); +#define MOUNTPROC_UMNTALL ((u_long)4) +extern void * mountproc_umntall_1(void *, CLIENT *); +extern void * mountproc_umntall_1_svc(void *, struct svc_req *); +#define MOUNTPROC_EXPORT ((u_long)5) +extern exports * mountproc_export_1(void *, CLIENT *); +extern exports * mountproc_export_1_svc(void *, struct svc_req *); +#define MOUNTPROC_EXPORTALL ((u_long)6) +extern exports * mountproc_exportall_1(void *, CLIENT *); +extern exports * mountproc_exportall_1_svc(void *, struct svc_req *); + +#else /* Old Style C */ +#define MOUNTPROC_NULL ((u_long)0) +extern void * mountproc_null_1(); +extern void * mountproc_null_1_svc(); +#define MOUNTPROC_MNT ((u_long)1) +extern fhstatus * mountproc_mnt_1(); +extern fhstatus * mountproc_mnt_1_svc(); +#define MOUNTPROC_DUMP ((u_long)2) +extern mountlist * mountproc_dump_1(); +extern mountlist * mountproc_dump_1_svc(); +#define MOUNTPROC_UMNT ((u_long)3) +extern void * mountproc_umnt_1(); +extern void * mountproc_umnt_1_svc(); +#define MOUNTPROC_UMNTALL ((u_long)4) +extern void * mountproc_umntall_1(); +extern void * mountproc_umntall_1_svc(); +#define MOUNTPROC_EXPORT ((u_long)5) +extern exports * mountproc_export_1(); +extern exports * mountproc_export_1_svc(); +#define MOUNTPROC_EXPORTALL ((u_long)6) +extern exports * mountproc_exportall_1(); +extern exports * mountproc_exportall_1_svc(); +#endif /* Old Style C */ + +#endif /* !_MOUNT_H_RPCGEN */ diff --git a/mount/rpcsvc/mount.x b/mount/rpcsvc/mount.x new file mode 100644 index 00000000..7e0d7f3a --- /dev/null +++ b/mount/rpcsvc/mount.x @@ -0,0 +1,161 @@ +/* @(#)mount.x 2.1 88/08/01 4.0 RPCSRC */ +/* @(#)mount.x 1.2 87/09/18 Copyr 1987 Sun Micro */ + +/* + * 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 + */ + +/* + * Protocol description for the mount program + */ + + +const MNTPATHLEN = 1024; /* maximum bytes in a pathname argument */ +const MNTNAMLEN = 255; /* maximum bytes in a name argument */ +const FHSIZE = 32; /* size in bytes of a file handle */ + +/* + * The fhandle is the file handle that the server passes to the client. + * All file operations are done using the file handles to refer to a file + * or a directory. The file handle can contain whatever information the + * server needs to distinguish an individual file. + */ +typedef opaque fhandle[FHSIZE]; + +/* + * If a status of zero is returned, the call completed successfully, and + * a file handle for the directory follows. A non-zero status indicates + * some sort of error. The status corresponds with UNIX error numbers. + */ +union fhstatus switch (unsigned fhs_status) { +case 0: + fhandle fhs_fhandle; +default: + void; +}; + +/* + * The type dirpath is the pathname of a directory + */ +typedef string dirpath; + +/* + * The type name is used for arbitrary names (hostnames, groupnames) + */ +typedef string name; + +/* + * A list of who has what mounted + */ +typedef struct mountbody *mountlist; +struct mountbody { + name ml_hostname; + dirpath ml_directory; + mountlist ml_next; +}; + +/* + * A list of netgroups + */ +typedef struct groupnode *groups; +struct groupnode { + name gr_name; + groups gr_next; +}; + +/* + * A list of what is exported and to whom + */ +typedef struct exportnode *exports; +struct exportnode { + dirpath ex_dir; + groups ex_groups; + exports ex_next; +}; + +program MOUNTPROG { + /* + * Version one of the mount protocol communicates with version two + * of the NFS protocol. The only connecting point is the fhandle + * structure, which is the same for both protocols. + */ + version MOUNTVERS { + /* + * Does no work. It is made available in all RPC services + * to allow server reponse testing and timing + */ + void + MOUNTPROC_NULL(void) = 0; + + /* + * If fhs_status is 0, then fhs_fhandle contains the + * file handle for the directory. This file handle may + * be used in the NFS protocol. This procedure also adds + * a new entry to the mount list for this client mounting + * the directory. + * Unix authentication required. + */ + fhstatus + MOUNTPROC_MNT(dirpath) = 1; + + /* + * Returns the list of remotely mounted filesystems. The + * mountlist contains one entry for each hostname and + * directory pair. + */ + mountlist + MOUNTPROC_DUMP(void) = 2; + + /* + * Removes the mount list entry for the directory + * Unix authentication required. + */ + void + MOUNTPROC_UMNT(dirpath) = 3; + + /* + * Removes all of the mount list entries for this client + * Unix authentication required. + */ + void + MOUNTPROC_UMNTALL(void) = 4; + + /* + * Returns a list of all the exported filesystems, and which + * machines are allowed to import it. + */ + exports + MOUNTPROC_EXPORT(void) = 5; + + /* + * Identical to MOUNTPROC_EXPORT above + */ + exports + MOUNTPROC_EXPORTALL(void) = 6; + } = 1; +} = 100005; diff --git a/mount/rpcsvc/mount_clnt.c b/mount/rpcsvc/mount_clnt.c new file mode 100644 index 00000000..bc6e5122 --- /dev/null +++ b/mount/rpcsvc/mount_clnt.c @@ -0,0 +1,94 @@ +/* + * Please do not edit this file. + * It was generated using rpcgen. + */ + +#include /* for memset */ +#include "mount.h" + +/* Default timeout can be changed using clnt_control() */ +static struct timeval TIMEOUT = { 25, 0 }; + +void * +mountproc_null_1(void *argp, CLIENT *clnt) +{ + static char clnt_res; + + memset((char *)&clnt_res, 0, sizeof(clnt_res)); + if (clnt_call(clnt, MOUNTPROC_NULL, xdr_void, argp, xdr_void, &clnt_res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return ((void *)&clnt_res); +} + +fhstatus * +mountproc_mnt_1(dirpath *argp, CLIENT *clnt) +{ + static fhstatus clnt_res; + + memset((char *)&clnt_res, 0, sizeof(clnt_res)); + if (clnt_call(clnt, MOUNTPROC_MNT, xdr_dirpath, argp, xdr_fhstatus, &clnt_res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return (&clnt_res); +} + +mountlist * +mountproc_dump_1(void *argp, CLIENT *clnt) +{ + static mountlist clnt_res; + + memset((char *)&clnt_res, 0, sizeof(clnt_res)); + if (clnt_call(clnt, MOUNTPROC_DUMP, xdr_void, argp, xdr_mountlist, &clnt_res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return (&clnt_res); +} + +void * +mountproc_umnt_1(dirpath *argp, CLIENT *clnt) +{ + static char clnt_res; + + memset((char *)&clnt_res, 0, sizeof(clnt_res)); + if (clnt_call(clnt, MOUNTPROC_UMNT, xdr_dirpath, argp, xdr_void, &clnt_res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return ((void *)&clnt_res); +} + +void * +mountproc_umntall_1(void *argp, CLIENT *clnt) +{ + static char clnt_res; + + memset((char *)&clnt_res, 0, sizeof(clnt_res)); + if (clnt_call(clnt, MOUNTPROC_UMNTALL, xdr_void, argp, xdr_void, &clnt_res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return ((void *)&clnt_res); +} + +exports * +mountproc_export_1(void *argp, CLIENT *clnt) +{ + static exports clnt_res; + + memset((char *)&clnt_res, 0, sizeof(clnt_res)); + if (clnt_call(clnt, MOUNTPROC_EXPORT, xdr_void, argp, xdr_exports, &clnt_res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return (&clnt_res); +} + +exports * +mountproc_exportall_1(void *argp, CLIENT *clnt) +{ + static exports clnt_res; + + memset((char *)&clnt_res, 0, sizeof(clnt_res)); + if (clnt_call(clnt, MOUNTPROC_EXPORTALL, xdr_void, argp, xdr_exports, &clnt_res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return (&clnt_res); +} diff --git a/mount/rpcsvc/mount_xdr.c b/mount/rpcsvc/mount_xdr.c new file mode 100644 index 00000000..be5eb41f --- /dev/null +++ b/mount/rpcsvc/mount_xdr.c @@ -0,0 +1,150 @@ +/* + * Please do not edit this file. + * It was generated using rpcgen. + */ + +#include "mount.h" + +bool_t +xdr_fhandle(XDR *xdrs, fhandle objp) +{ + + register long *buf; + + if (!xdr_opaque(xdrs, objp, FHSIZE)) { + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_fhstatus(XDR *xdrs, fhstatus *objp) +{ + + register long *buf; + + if (!xdr_u_int(xdrs, &objp->fhs_status)) { + return (FALSE); + } + switch (objp->fhs_status) { + case 0: + if (!xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle)) { + return (FALSE); + } + break; + default: + break; + } + return (TRUE); +} + +bool_t +xdr_dirpath(XDR *xdrs, dirpath *objp) +{ + + register long *buf; + + if (!xdr_string(xdrs, objp, MNTPATHLEN)) { + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_name(XDR *xdrs, name *objp) +{ + + register long *buf; + + if (!xdr_string(xdrs, objp, MNTNAMLEN)) { + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_mountlist(XDR *xdrs, mountlist *objp) +{ + + register long *buf; + + if (!xdr_pointer(xdrs, (char **)objp, sizeof(struct mountbody), (xdrproc_t)xdr_mountbody)) { + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_mountbody(XDR *xdrs, mountbody *objp) +{ + + register long *buf; + + if (!xdr_name(xdrs, &objp->ml_hostname)) { + return (FALSE); + } + if (!xdr_dirpath(xdrs, &objp->ml_directory)) { + return (FALSE); + } + if (!xdr_mountlist(xdrs, &objp->ml_next)) { + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_groups(XDR *xdrs, groups *objp) +{ + + register long *buf; + + if (!xdr_pointer(xdrs, (char **)objp, sizeof(struct groupnode), (xdrproc_t)xdr_groupnode)) { + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_groupnode(XDR *xdrs, groupnode *objp) +{ + + register long *buf; + + if (!xdr_name(xdrs, &objp->gr_name)) { + return (FALSE); + } + if (!xdr_groups(xdrs, &objp->gr_next)) { + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_exports(XDR *xdrs, exports *objp) +{ + + register long *buf; + + if (!xdr_pointer(xdrs, (char **)objp, sizeof(struct exportnode), (xdrproc_t)xdr_exportnode)) { + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_exportnode(XDR *xdrs, exportnode *objp) +{ + + register long *buf; + + if (!xdr_dirpath(xdrs, &objp->ex_dir)) { + return (FALSE); + } + if (!xdr_groups(xdrs, &objp->ex_groups)) { + return (FALSE); + } + if (!xdr_exports(xdrs, &objp->ex_next)) { + return (FALSE); + } + return (TRUE); +} diff --git a/mount/sundries.c b/mount/sundries.c new file mode 100644 index 00000000..45e0e14d --- /dev/null +++ b/mount/sundries.c @@ -0,0 +1,283 @@ +/* + * Support functions. Exported functions are prototyped in sundries.h. + * sundries.c,v 1.1.1.1 1993/11/18 08:40:51 jrs Exp + */ + +#include "sundries.h" + +/* File pointer for /etc/mtab. */ +FILE *F_mtab = NULL; + +/* File pointer for temp mtab. */ +FILE *F_temp = NULL; + +/* File descriptor for lock. Value tested in unlock_mtab() to remove race. */ +static int lock = -1; + +/* String list constructor. (car() and cdr() are defined in "sundries.h"). */ +string_list +cons (char *a, const string_list b) +{ + string_list p; + + p = xmalloc (sizeof *p); + + car (p) = a; + cdr (p) = b; + return p; +} + +void * +xmalloc (size_t size) +{ + void *t; + + if (size == 0) + return NULL; + + t = malloc (size); + if (t == NULL) + die (2, "not enough memory"); + + return t; +} + +char * +xstrdup (const char *s) +{ + char *t; + + if (s == NULL) + return NULL; + + t = strdup (s); + + if (t == NULL) + die (2, "not enough memory"); + + return t; +} + +/* Call this with SIG_BLOCK to block and SIG_UNBLOCK to unblock. */ +void +block_signals (int how) +{ + sigset_t sigs; + + sigfillset (&sigs); + sigprocmask (how, &sigs, (sigset_t *) 0); +} + + +/* Non-fatal error. Print message and return. */ +void +error (const char *fmt, ...) +{ + va_list args; + + va_start (args, fmt); + vfprintf (stderr, fmt, args); + fprintf (stderr, "\n"); + va_end (args); +} + +/* Fatal error. Print message and exit. */ +void +die (int err, const char *fmt, ...) +{ + va_list args; + + va_start (args, fmt); + vfprintf (stderr, fmt, args); + fprintf (stderr, "\n"); + va_end (args); + + unlock_mtab (); + exit (err); +} + +/* Ensure that the lock is released if we are interrupted. */ +static void +handler (int sig) +{ + die (2, "%s", sys_siglist[sig]); +} + +/* Create the lock file. The lock file will be removed if we catch a signal + or when we exit. The value of lock is tested to remove the race. */ +void +lock_mtab (void) +{ + int sig = 0; + struct sigaction sa; + + /* If this is the first time, ensure that the lock will be removed. */ + if (lock < 0) + { + sa.sa_handler = handler; + sa.sa_flags = 0; + sigfillset (&sa.sa_mask); + + while (sigismember (&sa.sa_mask, ++sig) != -1) + sigaction (sig, &sa, (struct sigaction *) 0); + + if ((lock = open (MOUNTED_LOCK, O_WRONLY|O_CREAT|O_EXCL, 0)) < 0) + die (2, "can't create lock file %s: %s", + MOUNTED_LOCK, strerror (errno)); + } +} + +/* Remove lock file. */ +void +unlock_mtab (void) +{ + if (lock != -1) + { + close( lock ); + unlink (MOUNTED_LOCK); + } +} + +/* Open mtab. */ +void +open_mtab (const char *mode) +{ + if ((F_mtab = setmntent (MOUNTED, mode)) == NULL) + die (2, "can't open %s: %s", MOUNTED, strerror (errno)); +} + +/* Close mtab. */ +void +close_mtab (void) +{ + if (fchmod (fileno (F_mtab), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) + die (1, "mount: error changing mode of %s: %s", MOUNTED, strerror (errno)); + endmntent (F_mtab); +} + +/* Update the mtab by removing any DIR entries and replace it with INSTEAD. */ +void +update_mtab (const char *dir, struct mntent *instead) +{ + struct mntent *mnt; + struct mntent *next; + int added = 0; + + open_mtab ("r"); + + if ((F_temp = setmntent (MOUNTED_TEMP, "w")) == NULL) + die (2, "can't open %s: %s", MOUNTED_TEMP, strerror (errno)); + + while ((mnt = getmntent (F_mtab))) + { + next = streq (mnt->mnt_dir, dir) ? (added++, instead) : mnt; + if (next && addmntent(F_temp, next) == 1) + die (1, "error writing %s: %s", MOUNTED_TEMP, strerror (errno)); + } + if (instead && !added) + addmntent(F_temp, instead); + + endmntent (F_mtab); + if (fchmod (fileno (F_temp), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) + die (1, "error changing mode of %s: %s", MOUNTED_TEMP, strerror (errno)); + endmntent (F_temp); + + if (rename (MOUNTED_TEMP, MOUNTED) < 0) + die (1, "can't rename %s to %s: %s", + MOUNTED_TEMP, MOUNTED, strerror(errno)); +} + +/* Given the name FILE, try to find it in mtab. */ +struct mntent * +getmntfile (const char *file) +{ + struct mntent *mnt; + + if (!F_mtab) + return NULL; + + rewind(F_mtab); + + while ((mnt = getmntent (F_mtab)) != NULL) + { + if (streq (mnt->mnt_dir, file)) + break; + if (streq (mnt->mnt_fsname, file)) + break; + } + + return mnt; +} + +/* Parse a list of strings like str[,str]... into a string list. */ +string_list +parse_list (char *strings) +{ + string_list list; + char *t; + + if (strings == NULL) + return NULL; + + list = cons (strtok (strings, ","), NULL); + + while ((t = strtok (NULL, ",")) != NULL) + list = cons (t, list); + + return list; +} + +/* True if fstypes match. Null *TYPES means match anything, + except that swap types always return false. This routine + has some ugliness to deal with ``no'' types. */ +int +matching_type (const char *type, string_list types) +{ + char *notype; + int no; /* true if a "no" type match, ie -t nominix */ + + if (streq (type, MNTTYPE_SWAP)) + return 0; + if (types == NULL) + return 1; + + if ((notype = alloca (strlen (type) + 3)) == NULL) + die (2, "mount: out of memory"); + sprintf (notype, "no%s", type); + no = (car (types)[0] == 'n') && (car (types)[1] == 'o'); + + /* If we get a match and the user specified a positive match type (e.g. + "minix") we return true. If we match and a negative match type (e.g. + "nominix") was specified we return false. */ + while (types != NULL) + if (streq (type, car (types))) + return !no; + else if (streq (notype, car (types))) + return 0; /* match with "nofoo" always returns false */ + else + types = cdr (types); + + /* No matches, so if the user specified a positive match type return false, + if a negative match type was specified, return true. */ + return no; +} + +/* Make a canonical pathname from PATH. Returns a freshly malloced string. + It is up the *caller* to ensure that the PATH is sensible. i.e. + canonicalize ("/dev/fd0/.") returns "/dev/fd0" even though ``/dev/fd0/.'' + is not a legal pathname for ``/dev/fd0.'' Anything we cannot parse + we return unmodified. */ +char * +canonicalize (const char *path) +{ + char *canonical = xmalloc (PATH_MAX + 1); + + if (path == NULL) + return NULL; + + if (realpath (path, canonical)) + return canonical; + + strcpy (canonical, path); + return canonical; +} diff --git a/mount/sundries.h b/mount/sundries.h new file mode 100644 index 00000000..ba878c9b --- /dev/null +++ b/mount/sundries.h @@ -0,0 +1,90 @@ +/* + * Support function prototypes. Functions are in sundries.c. + * sundries.h,v 1.1.1.1 1993/11/18 08:40:51 jrs Exp + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fstab.h" + + +#define streq(s, t) (strcmp ((s), (t)) == 0) + + +#define MOUNTED_LOCK "/etc/mtab~" +#define MOUNTED_TEMP "/etc/mtab.tmp" +#define _PATH_FSTAB "/etc/fstab" +#define LOCK_BUSY 3 + +/* File pointer for /etc/mtab. */ +extern FILE *F_mtab; + +/* File pointer for temp mtab. */ +extern FILE *F_temp; + +/* String list data structure. */ +typedef struct string_list +{ + char *hd; + struct string_list *tl; +} *string_list; + +#define car(p) ((p) -> hd) +#define cdr(p) ((p) -> tl) + +string_list cons (char *a, const string_list); + +/* Quiet compilation with -Wmissing-prototypes. */ +int main (int argc, char *argv[]); + +/* From mount_call.c. */ +int mount5 (const char *, const char *, const char *, int, void *); + +/* Functions in sundries.c that are used in mount.c and umount.c */ +void block_signals (int how); +char *canonicalize (const char *path); +char *realpath (const char *path, char *resolved_path); +void close_mtab (void); +void error (const char *fmt, ...); +void lock_mtab (void); +int matching_type (const char *type, string_list types); +void open_mtab (const char *mode); +string_list parse_list (char *strings); +void unlock_mtab (void); +void update_mtab (const char *special, struct mntent *with); +struct mntent *getmntfile (const char *file); +void *xmalloc (size_t size); +char *xstrdup (const char *s); + +/* Here is some serious cruft. */ +#ifdef __GNUC__ +#if defined(__GNUC_MINOR__) && __GNUC__ == 2 && __GNUC_MINOR__ >= 5 +void die (int errcode, const char *fmt, ...) __attribute__ ((noreturn)); +#else /* GNUC < 2.5 */ +void volatile die (int errcode, const char *fmt, ...); +#endif /* GNUC < 2.5 */ +#else /* !__GNUC__ */ +void die (int errcode, const char *fmt, ...); +#endif /* !__GNUC__ */ + +#ifdef HAVE_NFS +int nfsmount (const char *spec, const char *node, int *flags, + char **orig_opts, char **opt_args); +#endif + +#define mount5(special, dir, type, flags, data) \ + mount (special, dir, type, 0xC0ED0000 | (flags), data) + diff --git a/mount/swapoff.8 b/mount/swapoff.8 new file mode 100644 index 00000000..1a06b7e8 --- /dev/null +++ b/mount/swapoff.8 @@ -0,0 +1 @@ +.so man8/swapon.8 diff --git a/mount/swapon.8 b/mount/swapon.8 new file mode 100644 index 00000000..44c84168 --- /dev/null +++ b/mount/swapon.8 @@ -0,0 +1,94 @@ +.\" Copyright (c) 1980, 1991 Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)swapon.8 6.3 (Berkeley) 3/16/91 +.\" +.\" Sun Dec 27 12:31:30 1992: Modified by faith@cs.unc.edu +.\" Sat Mar 6 20:46:02 1993: Modified by faith@cs.unc.edu +.\" Sat Oct 9 09:35:30 1993: Converted to man format by faith@cs.unc.edu +.\" Sat Nov 27 20:22:42 1993: Updated authorship information, faith@cs.unc.edu +.\" +.TH SWAPON 8 "27 November 1993" "Linux 0.99" "Linux Programmer's Manual" +.SH NAME +swapon, swapoff \- enable/disable devices and files for paging and swapping +.SH SYNOPSIS +.B /etc/swapon \-a +.br +.BI /etc/swapon " specialfile " ... +.br +.B /etc/swapoff \-a +.br +.BI /etc/swapoff " specialfile " ... +.SH DESCRIPTION +.B Swapon +is used to specify devices on which paging and swapping are to take place. +Calls to +.B swapon +normally occur in the system multi-user initialization file +.I /etc/rc +making all swap devices available, so that the paging and swapping activity +is interleaved across several devices and files. + +Normally, the first form is used: +.TP +.B \-a +All devices marked as ``sw'' swap devices in +.I /etc/fstab +are made available. +.PP +.B Swapoff +disables swapping on the specified devices and files, or on all swap +entries in +.I /etc/fstab +when the +.B \-a +flag is given. +.SH SEE ALSO +.BR swapon "(2), " swapoff "(2), " fstab "(5), " init "(8), " mkswap (8), +.BR rc "(8), " mount (8) +.SH FILES +.I /dev/hd[ab]? +standard paging devices +.br +.I /dev/sd[ab]? +standard (SCSI) paging devices +.br +.I /etc/fstab +ascii filesystem description table +.SH HISTORY +The +.B swapon +command appeared in 4.0BSD. +.SH AUTHORS +See the Linux +.BR mount (8) +man page for a complete author list. Primary contributors include Doug +Quale, H. J. Lu, Rick Sladkey, and Stephen Tweedie. diff --git a/mount/swapon.c b/mount/swapon.c new file mode 100644 index 00000000..58403302 --- /dev/null +++ b/mount/swapon.c @@ -0,0 +1,109 @@ +/* + * A swapon(8)/swapoff(8) for Linux 0.99. + * swapon.c,v 1.1.1.1 1993/11/18 08:40:51 jrs Exp + */ + +#include "sundries.h" + +/* Nonzero for chatty (-v). This is a nonstandard flag (not in BSD). */ +int verbose = 0; + +extern char version[]; +static char *program_name; +static struct option longopts[] = +{ + { "all", 0, 0, 'a' }, + { "help", 0, 0, 'h' }, + { "verbose", 0, 0, 'v' }, + { "version", 0, 0, 'V' }, + { NULL, 0, 0, 0 } +}; + +const char *usage_string = "\ +usage: %s [-hV]\n\ + %s -a [-v]\n\ + %s [-v] special ...\n\ +"; + +static void +usage (FILE *fp, int n) +{ + fprintf (fp, usage_string, program_name, program_name, program_name); + exit (n); +} + +static int +swap (const char *special) +{ + int status; + + if (verbose) + printf("%s on device %s\n", program_name, special); + + if (streq (program_name, "swapon")) + status = swapon (special); + else + status = swapoff (special); + + if (status < 0) + fprintf (stderr, "%s: %s: %s\n", program_name, special, strerror (errno)); + + return status; +} + +int +main (int argc, char *argv[]) +{ + struct fstab *fstab; + int status; + int all = 0; + int c; + + if (strrchr (argv[0], '/') != NULL) + program_name = strrchr (argv[0], '/') + 1; + else + program_name = argv[0]; + + while ((c = getopt_long (argc, argv, "ahvV", longopts, NULL)) != EOF) + switch (c) + { + case 'a': /* all */ + ++all; + break; + case 'h': /* help */ + usage (stdout, 0); + break; + case 'v': /* be chatty */ + ++verbose; + break; + case 'V': /* version */ + printf ("%s\n", version); + exit (0); + case 0: + break; + case '?': + default: + usage (stderr, 1); + } + + argv += optind; + + status = 0; + + if (all) + { + while ((fstab = getfsent()) != NULL) + if (streq (fstab->fs_type, FSTAB_SW)) + status |= swap (fstab->fs_spec); + } + else if (*argv == NULL) + { + usage (stderr, 2); + } + else + { + while (*argv != NULL) + status |= swap (*argv++); + } + return status; +} diff --git a/mount/umount.8 b/mount/umount.8 new file mode 100644 index 00000000..85425b48 --- /dev/null +++ b/mount/umount.8 @@ -0,0 +1 @@ +.so man8/mount.8 diff --git a/mount/umount.c b/mount/umount.c new file mode 100644 index 00000000..c3cfee71 --- /dev/null +++ b/mount/umount.c @@ -0,0 +1,353 @@ +/* + * A umount(8) for Linux 0.99. + * umount.c,v 1.1.1.1 1993/11/18 08:40:51 jrs Exp + * + * Wed Sep 14 22:43:54 1994: Sebastian Lederer + * (lederer@next-pc.informatik.uni-bonn.de) added support for sending an + * unmount RPC call to the server when an NFS-filesystem is unmounted. + */ + +#include "sundries.h" + +#ifdef HAVE_NFS +#include +#include +#include +#include +#include +#include +#include "mount.h" +#include +#endif + + +#ifdef notyet +/* Nonzero for force umount (-f). This needs kernel support we don't have. */ +int force = 0; +#endif + +/* Nonzero for chatty (-v). This is a nonstandard flag (not in BSD). */ +int verbose = 0; + +/* True if ruid != euid. */ +int suid = 0; + +#ifdef HAVE_NFS +static int xdr_dir(XDR *xdrsp, char *dirp) +{ + return (xdr_string(xdrsp, &dirp, MNTPATHLEN)); +} +#endif + + + +/* Umount a single device. Return a status code, so don't exit + on a non-fatal error. We lock/unlock around each umount. */ +static int +umount_one (const char *spec, const char *node) +{ + int umnt_err; + int isroot; + struct mntent *mnt; + +#ifdef HAVE_NFS + char buffer[256]; + register CLIENT *clp; + struct sockaddr_in saddr; + struct timeval pertry, try; + enum clnt_stat clnt_stat; + int so = RPC_ANYSOCK; + char *p; + struct hostent *hostp; + char hostname[MAXHOSTNAMELEN]; + char dirname[1024]; +#endif /* HAVE_NFS */ + + + /* Special case for root. As of 0.99pl10 we can (almost) unmount root; + the kernel will remount it readonly so that we can carry on running + afterwards. The readonly remount is illegal if any files are opened + for writing at the time, so we can't update mtab for an unmount of + root. As it is only really a remount, this doesn't matter too + much. [sct May 29, 1993] */ + isroot = (streq (node, "/") || streq (node, "root")); + +#ifdef HAVE_NFS + strcpy(buffer,spec); + /* spec is constant so must use own buffer */ + if((p=strchr(buffer,':'))) + { + *p='\0'; + strcpy(hostname,buffer); + strcpy(dirname,p+1); +#ifdef DEBUG + printf("host: %s, directory: %s\n", hostname,dirname); +#endif + + + if (hostname[0] >= '0' && hostname[0] <= '9') + { + saddr.sin_addr.s_addr = inet_addr(hostname); + } + else + if ((hostp = gethostbyname(hostname)) == NULL) + { + fprintf(stderr, "mount: can't get address for %s\n", hostname); + return(1); + } + else + { + memcpy(&saddr.sin_addr, hostp->h_addr, hostp->h_length); + } + + saddr.sin_family = AF_INET; + saddr.sin_port = 0; + pertry.tv_sec = 3; + pertry.tv_usec = 0; + if ((clp = clntudp_create(&saddr, MOUNTPROG, MOUNTVERS, + pertry, &so)) == NULL) + { + clnt_pcreateerror("Cannot MOUNTPROG PRC"); + return (1); + } + clp->cl_auth = authunix_create_default(); + try.tv_sec = 20; + try.tv_usec = 0; + clnt_stat = clnt_call(clp, MOUNTPROC_UMNT, + xdr_dir, dirname, + xdr_void, (caddr_t)0, + try); + + if (clnt_stat != RPC_SUCCESS) + { + clnt_perror(clp, "Bad UMNT RPC"); + return (1); + } + auth_destroy(clp->cl_auth); + clnt_destroy(clp); + } +#endif /* HAVE_NFS */ + + if (!isroot) + lock_mtab (); + + if (umount (node) >= 0) + /* Umount succeeded, update mtab. */ + { + if (verbose) + printf ("%s umounted\n", spec); + + if (!isroot) + { + /* Special stuff for loop devices */ + open_mtab("r"); + if ((mnt = getmntfile (spec)) || + (mnt = getmntfile (node))) { + if (mnt && streq(mnt->mnt_type, "loop")) { + extern int del_loop(const char *); + + if (del_loop(spec)) + goto fail; + } + } + close_mtab(); + + /* Non-loop stuff */ + update_mtab (node, NULL); + unlock_mtab (); + } + return 0; + } + +fail: + /* Umount failed, complain, but don't die. */ + umnt_err = errno; + if (!isroot) + unlock_mtab (); + + switch (umnt_err) + { + case ENXIO: + error ("umount: %s: invalid block device", spec); break; + case EINVAL: + error ("umount: %s: not mounted", spec); break; + case EIO: + error ("umount: %s: can't write superblock", spec); break; + case EBUSY: + error ("umount: %s: device is busy", spec); break; + case ENOENT: + error ("umount: %s: not mounted", spec); break; + case EPERM: + error ("umount: %s: must be superuser to umount", spec); break; + case EACCES: + error ("umount: %s: block devices not permitted on fs", spec); break; + default: + error ("umount: %s: %s", spec, strerror (umnt_err)); break; + } + return 1; +} + +/* Unmount all filesystems of type VFSTYPES found in mtab. Since we are + concurrently updating mtab after every succesful umount, we have to + slurp in the entire file before we start. This isn't too bad, because + in any case it's important to umount mtab entries in reverse order + to umount, e.g. /usr/spool before /usr. */ +static int +umount_all (string_list types) +{ + string_list spec_list = NULL; + string_list node_list = NULL; + struct mntent *mnt; + int errors; + + open_mtab ("r"); + + while ((mnt = getmntent (F_mtab))) + if (matching_type (mnt->mnt_type, types)) + { + spec_list = cons (xstrdup (mnt->mnt_fsname), spec_list); + node_list = cons (xstrdup (mnt->mnt_dir), node_list); + } + + close_mtab (); + + errors = 0; + while (spec_list != NULL) + { + errors |= umount_one (car (spec_list), car (node_list)); + spec_list = cdr (spec_list); + node_list = cdr (node_list); + } + + sync (); + return errors; +} + +extern char version[]; +static struct option longopts[] = +{ + { "all", 0, 0, 'a' }, + { "force", 0, 0, 'f' }, + { "help", 0, 0, 'h' }, + { "verbose", 0, 0, 'v' }, + { "version", 0, 0, 'V' }, + { "types", 1, 0, 't' }, + { NULL, 0, 0, 0 } +}; + +char *usage_string = "\ +usage: umount [-hV]\n\ + umount -a [-v] [-t vfstypes]\n\ + umount [-v] special | node\n\ +"; + +static void +usage (FILE *fp, int n) +{ + fprintf (fp, "%s", usage_string); + exit (n); +} + +int +main (int argc, char *argv[]) +{ + int c; + int all = 0; + string_list types = NULL; + string_list options; + struct mntent *mnt; + struct mntent mntbuf; + struct mntent *fs; + char *file; + int result = 0; + + while ((c = getopt_long (argc, argv, "afhvVt:", longopts, NULL)) != EOF) + switch (c) + { + case 'a': /* umount everything */ + ++all; + break; + case 'f': /* force umount (needs kernel support) */ +#if 0 + ++force; +#else + die (2, "umount: forced umount not supported yet"); +#endif + break; + case 'h': /* help */ + usage (stdout, 0); + break; + case 'v': /* make noise */ + ++verbose; + break; + case 'V': /* version */ + printf ("%s\n", version); + exit (0); + case 't': /* specify file system type */ + types = parse_list (optarg); + break; + case 0: + break; + case '?': + default: + usage (stderr, 1); + } + + if (getuid () != geteuid ()) + { + suid = 1; + if (all || types) + die (2, "umount: only root can do that"); + } + + argc -= optind; + argv += optind; + + if (all) + result = umount_all (types); + else if (argc != 1) + usage (stderr, 2); + else + { + file = canonicalize (*argv); /* mtab paths are canonicalized */ + + open_mtab ("r"); + mnt = getmntfile (file); + if (mnt) + { + /* Copy the structure and strings becuase they're in static areas. */ + mntbuf = *mnt; + mnt = &mntbuf; + mnt->mnt_fsname = xstrdup (mnt->mnt_fsname); + mnt->mnt_dir = xstrdup (mnt->mnt_dir); + } + close_mtab (); + + if (suid) + { + if (!mnt) + die (2, "umount: %s is not mounted", file); + if (!(fs = getfsspec (file)) && !(fs = getfsfile (file))) + die (2, "umount: %s is not in the fstab", file); + if (!streq (mnt->mnt_fsname, fs->mnt_fsname) + || !streq (mnt->mnt_dir, fs->mnt_dir)) + die (2, "umount: %s mount disagrees with the fstab", file); + options = parse_list (fs->mnt_opts); + while (options) + { + if (streq (car (options), "user")) + break; + options = cdr (options); + } + if (!options) + die (2, "umount: only root can unmount %s from %s", + fs->mnt_fsname, fs->mnt_dir); + } + + if (mnt) + result = umount_one (xstrdup (mnt->mnt_fsname), xstrdup(mnt->mnt_dir)); + else + result = umount_one (*argv, *argv); + } + exit (result); +} diff --git a/mount/version.c b/mount/version.c new file mode 100644 index 00000000..ece944d2 --- /dev/null +++ b/mount/version.c @@ -0,0 +1 @@ +char version[] = "(u)mount: version from util-linux-2.2"; diff --git a/sys-utils/MAKEDEV b/sys-utils/MAKEDEV new file mode 100644 index 00000000..6326edd4 --- /dev/null +++ b/sys-utils/MAKEDEV @@ -0,0 +1,486 @@ +#! /bin/sh - + +RCSID='MAKEDEV,v 1.1.1.1 1995/02/22 19:09:11 faith Exp' + +#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---# +# Customisation: +# The devices fall into various classes. This section contains the mapping +# from a class name into a group name and permission. +# You will almost certainly need to edit the group name to match your +# system, and you may change the permissions to suit your preference. These +# lines _must_ be of the format "user group perm". + + public=" root system 666" + system=" root system 660" + kmem=" root kmem 660" + tty=" root tty 666" + cons=" root tty 622" # 622 for console? +dialout=" root uucp 660" + mouse=" root system 666" +printer=" root daemon 660" + floppy=" root floppy 660" + disk=" root disk 660" + scsi=" root system 600" + cdrom=" root disk 660" + tape=" root disk 660" + audio=" root system 666" + ibcs2=" root system 666" +scanner=" root system 666" + +#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---# + +procfs=/proc + +opt_v= +opt_d= +opt_n= + +while [ $# -ge 1 ] +do + case $1 in + --) shift; break ;; + -v) shift; opt_v=1 ;; + -d) shift; opt_d=1 ;; + -n) shift; opt_n=1; opt_v=1 ;; + -V) shift; opt_V=1 ;; + -*) echo "$0: unknown flag \"$1\"" >&2; exit 1 ;; + *) break ;; + esac +done + +if [ "$opt_V" ] +then + echo "$RCSID" + exit 0 +fi + +opts="${opt_n:+-n} ${opt_v:+-v} ${opt_d:+-d}" + +makedev () { # usage: makedev name [bcu] major minor owner group mode + if [ "$opt_v" ] + then if [ "$opt_d" ] + then echo "delete $1" + else echo "create $1 $2 $3 $4 $5:$6 $7" + fi + fi + if [ ! "$opt_n" ] + then if [ "$opt_d" ] + then + rm -f $1 + else + mknod $1- $2 $3 $4 && + chown $5:$6 $1- && + chmod $7 $1- && + mv $1- $1 + fi + fi +} +symlink () { # usage: symlink name target + if [ "$opt_v" ] + then if [ "$opt_d" ] + then echo "delete $1" + else echo "create $1 -> $2" + fi + fi + [ ! "$opt_n" ] && rm -f $1 && + [ ! "$opt_d" ] && ln -s $2 $1 +} + +devices= +if [ ! -f $procfs/devices ] +then + echo "$0: warning: can't read $procfs/devices" >&2 +else + exec 3<$procfs/devices + while read major device <&3 + do + case "$major" in + Character|Block|'') + ;; + *) + eval "major_$device=$major" + devices="$devices $device" + ;; + esac + done + exec 3<&- +fi + +Major () { + device=$2 + if [ "$opt_d" ] + then + echo -1 # don't care + else + eval echo \${major_$1:-\${device:?\"unknown major number for $1\"}} + fi +} + +cvt () { + while [ $# -ne 0 ] + do + case "$1" in + mem|tty|ttyp|cua|cub) ;; + hd) echo hda hdb ;; + xd) echo xda xdb ;; + fd) echo fd0 fd1 ;; + lp) echo lp0 lp1 lp2 ;; + mt) echo ftape ;; + loop) echo loop ;; + ibcs2) echo ibcs2 ;; + tpqic02) echo qic ;; + sound) echo audio ;; + logiscan) echo logiscan ;; + ac4096) echo ac4096 ;; + idecd) echo idecd ;; + hw) echo helloworld ;; + sbpcd | sbpcd[123]) echo $1 ;; + Joystick) echo js ;; + apm_bios) echo apm ;; + dcf) echo dcf ;; + pcmcia) ;; # taken care of by its own driver + ttyC) echo cyclades ;; + *) echo "$0: don't know what \"$1\" is" >&2 ;; + esac + shift + done +} + +for arg +do + case $arg in + generic) + $0 $opts std + $0 $opts fd0 fd1 + $0 $opts hda hdb + $0 $opts xda xdb + $0 $opts sda sdb + $0 $opts ptyp ptyq ptyr ptys + $0 $opts console tty1 tty2 tty3 tty4 tty5 tty6 tty7 tty8 + $0 $opts ttyS0 ttyS1 ttyS2 ttyS3 + $0 $opts busmice + $0 $opts lp0 lp1 lp2 + $0 $opts par0 par1 par2 + $0 $opts fd + ;; + local) + $0.local $opts + ;; + std) + makedev mem c 1 1 $kmem + makedev kmem c 1 2 $kmem + makedev null c 1 3 $public + makedev port c 1 4 $kmem + makedev zero c 1 5 $public + symlink core $procfs/kcore + makedev full c 1 7 $public + makedev ram b 1 1 $disk + makedev tty c 5 0 $tty + ;; + console|tty0) + makedev $arg c 4 0 $cons + ;; + tty[1-9]|tty[1-5][0-9]|tty[6][0-3]) + line=`expr $arg : "tty\(.*\)"` + makedev tty$line c 4 $line $tty + ;; + ttyS[0-9]|ttyS[1-5][0-9]|ttyS[6][0-3]) + line=`expr $arg : "ttyS\(.*\)"` + minor=`expr 64 + $line` + makedev ttyS$line c 4 $minor $tty + makedev cua$line c 5 $minor $dialout + ;; + pty[p-s]) + # Currently limited to 64 master/slave pairs. + bank=`expr $arg : "pty\(.\)"` + base=`expr \( pqrs : ".*$bank" - 1 \) \* 16` + for i in 0 1 2 3 4 5 6 7 8 9 a b c d e f + do + j=`expr 0123456789abcdef : ".*$i" - 1` + makedev pty$bank$i c 4 `expr 128 + $base + $j` $tty + makedev tty$bank$i c 4 `expr 192 + $base + $j` $tty + done + ;; + cyclades) + major1=`Major ttyC` || continue + major2=`Major cub` || continue + for i in 0 1 2 3 4 5 6 7 # 8 9 10 11 12 13 14 15 + do + makedev ttyC$i c $major1 `expr 32 + $i` $tty + makedev cub$i c $major2 `expr 32 + $i` $dialout + done + ;; + par[0-2]) + major=`Major lp 6` || continue + port=`expr $arg : "par\(.\)"` + makedev $arg c $major $port $printer + ;; + lp[0-2]) + major=`Major lp 6` || continue + port=`expr $arg : "lp\(.\)"` + makedev $arg c $major $port $printer + ;; + busmice) + major=`Major mouse 10` || continue + makedev logibm c $major 0 $mouse + makedev psaux c $major 1 $mouse + makedev inportbm c $major 2 $mouse + makedev atibm c $major 3 $mouse + # makedev sejin c $major 4 $mouse + ;; + js) + major=`Major Joystick` || continue + makedev js0 c $major 0 $mouse + makedev js1 c $major 1 $mouse + ;; + fd[0-4]) + major=`Major fd 2` || continue + unit=`expr $arg : "fd\(.\)"` + makedev fd${unit} b $major $unit $floppy + makedev fd${unit}d360 b $major `expr $unit + 4` $floppy + makedev fd${unit}h1200 b $major `expr $unit + 8` $floppy + makedev fd${unit}D360 b $major `expr $unit + 12` $floppy + symlink fd${unit}H360 fd${unit}D360 + makedev fd${unit}D720 b $major `expr $unit + 16` $floppy + symlink fd${unit}H720 fd${unit}D720 + makedev fd${unit}h360 b $major `expr $unit + 20` $floppy + makedev fd${unit}h720 b $major `expr $unit + 24` $floppy + makedev fd${unit}H1440 b $major `expr $unit + 28` $floppy + makedev fd${unit}E2880 b $major `expr $unit + 32` $floppy + makedev fd${unit}CompaQ b $major `expr $unit + 36` $floppy + + makedev fd${unit}h1440 b $major `expr $unit + 40` $floppy + makedev fd${unit}H1680 b $major `expr $unit + 44` $floppy + makedev fd${unit}h410 b $major `expr $unit + 48` $floppy + makedev fd${unit}H820 b $major `expr $unit + 52` $floppy + makedev fd${unit}h1476 b $major `expr $unit + 56` $floppy + makedev fd${unit}H1722 b $major `expr $unit + 60` $floppy + makedev fd${unit}h420 b $major `expr $unit + 64` $floppy + makedev fd${unit}H830 b $major `expr $unit + 68` $floppy + makedev fd${unit}h1494 b $major `expr $unit + 72` $floppy + makedev fd${unit}H1743 b $major `expr $unit + 76` $floppy + ;; + hd[a-d]) + major=`Major hd 3` || continue + unit=`expr $arg : "hd\(.\)"` + base=`expr \( abcd : ".*$unit" - 1 \) \* 64` + makedev hd$unit b $major $base $disk + for part in 1 2 3 4 5 6 7 8 # 9 10 11 12 13 14 15 16 + do + makedev hd$unit$part b $major `expr $base + $part` $disk + done + ;; + hd1[a-d]) + unit=`expr $arg : "hd1\(.\)"` + base=`expr \( abcd : ".*$unit" - 1 \) \* 64` + makedev hd1$unit b 22 $base $disk + for part in 1 2 3 4 5 6 7 8 # 9 10 11 12 13 14 15 16 + do + makedev hd1$unit$part b 22 `expr $base + $part` $disk + done + ;; + xd[a-d]) + major=`Major xd 13` || continue + unit=`expr $arg : "xd\(.\)"` + base=`expr \( abcd : ".*$unit" - 1 \) \* 64` + makedev xd$unit b $major $base $disk + for part in 1 2 3 4 5 6 7 8 # 9 10 11 12 13 14 15 16 + do + makedev xd$unit$part b $major `expr $base + $part` $disk + done + ;; + sd[a-h]) + major=`Major sd 8` || continue + unit=`expr $arg : "sd\(.\)"` + base=`expr \( abcdefgh : ".*$unit" - 1 \) \* 16` + makedev sd$unit b $major $base $disk + for part in 1 2 3 4 5 6 7 8 # 9 10 11 12 13 14 15 + do + minor=`expr $base + $part` + makedev sd$unit$part b $major $minor $disk + done + ;; + loop) + major=`Major loop` || continue + for part in 0 1 2 3 4 5 6 7 + do + makedev loop$part b $major $part $disk + done + ;; + st[0-7]) + major=`Major st 9` + unit=`expr $arg : "st\(.\)"` + makedev st$unit c $major $unit $tape + makedev nst$unit c $major `expr 128 + $unit` $tape + ;; + qic) + major=`Major tpqic02 12` + makedev rmt8 c $major 6 $tape + makedev rmt16 c $major 8 $tape + makedev tape-d c $major 136 $tape + makedev tape-reset c $major 255 $tape + ;; + ftape) + major=`Major mt 27` || continue + for unit in 0 1 2 3 + do + makedev rft$unit c $major $unit $tape + makedev nrft$unit c $major `expr $unit + 4` $tape + done + symlink ftape rft0 + symlink nftape nrft0 + ;; + scd[0-7]) + major=`Major sr 11` || continue + unit=`expr $arg : "scd\(.\)"` + makedev scd$unit b $major $unit $cdrom + ;; + sonycd) + major=`Major cdu31a` || continue + makedev $arg b $major 0 $cdrom + ;; + mcd) + major=`Major mcd 23` || continue + makedev $arg b $major 0 $cdrom + ;; + cdu535) + makedev $arg b 24 0 $cdrom + ;; + lmscd) + makedev $arg b 24 0 $cdrom + ;; + sbpcd|sbpcd[123]) + major=`Major $arg` || continue + base=`expr ${arg}0 : "sbpcd\(.\)"` + for minor in 0 1 2 3 + do + unit=`expr substr 0123456789abcdef \( $base \* 4 + $minor + 1 \) 1` + makedev spbcd$unit b $major $minor $cdrom + done + [ $arg = sbpcd ] && symlink $arg ${arg}0 + ;; + idecd) + major=`Major idecd` || continue + makedev $arg c $major 0 $cdrom + ;; + logiscan) + major=`Major logiscan` || continue + makedev $arg c $major 0 $scanner + ;; + m105scan) + major=`Major m105` || continue + makedev $arg c $major 0 $scanner + ;; + ac4096) + major=`Major ac4096` || continue + makedev $arg c $major 0 $scanner + ;; + audio) + major=`Major sound 14` + makedev mixer c $major 0 $audio + makedev sequencer c $major 1 $audio + makedev midi00 c $major 2 $audio + makedev dsp c $major 3 $audio + makedev audio c $major 4 $audio + makedev sndstat c $major 6 $audio +# makedev sequencer2 c $major 8 $audio + makedev mixer1 c $major 16 $audio +# makedev patmgr0 c $major 17 $audio + makedev midi01 c $major 18 $audio + makedev dsp1 c $major 19 $audio + makedev audio1 c $major 20 $audio +# makedev patmgr1 c $major 33 $audio + makedev midi02 c $major 34 $audio + makedev midi03 c $major 50 $audio + ;; + pcaudio) + major=`Major pcsp` || continue + makedev pcmixer c $major 0 $audio + makedev pcsp c $major 3 $audio + makedev pcaudio c $major 4 $audio + ;; + sg) + major=`Major sg 21` + for unit in a b c d e f g h + do + minor=`expr abcdefgh : ".*$unit" - 1` + makedev $arg$unit c $major $minor $scsi + done + ;; + fd) + # not really devices, we use the /proc filesystem + symlink fd $procfs/self/fd + symlink stdin fd/0 + symlink stdout fd/1 + symlink stderr fd/2 + ;; + ibcs2) + major=`Major ibcs2` || continue + makedev socksys c $major 0 $ibcs2 + symlink nfsd socksys + makedev spx c $major 1 $ibcs2 + symlink X0R null + ;; + apm) + major=`Major apm_bios` || continue + makedev $arg c $major 0 $system + ;; + dcf) + major=`Major dcf` || continue + makedev $arg c $major 0 $system + ;; + helloworld) + major=`Major hw` || continue + makedev helloworld c $major 0 $public + ;; + update) + if [ ! "$devices" ] + then + echo "$0: don't appear to have any devices" >&2 + continue + fi + if [ "$opt_d" ] + then + echo "$0: can't delete an update" >&2 + continue + fi + create= + delete= + devs="$devices" + if [ -f DEVICES ] + then + exec 3 DEVICES + ;; + *) + echo "$0: don't know how to make device \"$arg\"" >&2 + ;; + esac +done + +exit 0 diff --git a/sys-utils/MAKEDEV.8 b/sys-utils/MAKEDEV.8 new file mode 100644 index 00000000..fe0c3645 --- /dev/null +++ b/sys-utils/MAKEDEV.8 @@ -0,0 +1,147 @@ +.\" MAKEDEV.8,v 1.1.1.1 1995/02/22 19:09:12 faith Exp +.TH MAKEDEV 8 "14th August 1994" Linux "Linux Programmer's Manual" +.SH NAME +MAKEDEV \- create devices +.SH SYNOPSIS +.B "cd dev; ./MAKEDEV [ -n ] [ -v ] update" +.br +.BI "cd dev; ./MAKEDEV [ -n ] [ -v ]" "device" +.SH DESCRIPTION +.B MAKEDEV +is a script that will create the devices in \fC/dev\fP used to interface +with drivers in the kernel. +.PP +Note that programs giving the error \(*QENOENT: No such file or +directory\(*U normally means that the device file is missing, whereas +\(*QENODEV: No such device\(*U normally means the kernel does not have the +driver configured or loaded. +.SH OPTIONS +.TP +.B \-V +Print out version (actually RCS version information) and exit. +.TP +.B \-n +Do not actually update the devices, just print the actions that would be +performed. +.TP +.B \-d +Delete the devices. The main use for this flag is by +.I MAKEDEV +itself. +.TP +.B \-v +Be verbose. Print out the actions as they are performed. This is the +same output as produced by +.BR \-n . +.SH CUSTOMISATION +Since there is no standardisation in what names are used for system users +and groups, it is possible that you may need to modify +.B MAKEDEV +to reflect your site's settings. Near the top of the file is a mapping +from device type to user, group and permissions (e.g. all CD-ROM devices +are set from the \fC$cdrom\fP variable). If you wish to change the +defaults, this is the section to edit. +.SH DEVICES +.TP +.B General Options +.TP +.B update +This only works on kernels which have \fC/proc/interrupts\fP (introduced +during 1.1.x). This file is scanned to see what devices are currently +configured into the kernel, and this is compared with the previous +settings stored in the file called \fCDEVICES\fP. +Devices which are new since then or have a different major number are +created, and those which are no longer configured are deleted. +.TP +.B generic +Create a generic subset of devices. +.TP +.B +std +Standard devices. +.TP +.B local +.TP +.B Virtual Terminals +.TP +.B console +Also known as tty0. +.TP +.B tty{0..63} +Virtual consoles +.TP +.B Serial Devices +.TP +.I ttyS{0..63} +serial ports and corresponding dialout device +.TP +.I cyclades +Dial-in and dial-out devices for the cyclades intelligent I/O serial card. +.TP +.B Pseudo Terminals +.TP +.I pty[p-s] +banks of of master and slave pseudo terminals +.TP +.B Parallel Ports +.TP +.I par[0-3] lp[0-3] +parallel ports +.TP +.B Bus Mice +.TP +.I busmice +The various bus mice devices. +.TP +.B Joystick Devices +.TP +.I js +Joystick. Creates \fCjs0\fP and \fCjs1\fP. +.TP +.B Disks Devices +.TP +.I fd[0-4] +floppy disks +.TP +.I hd[a-d] +AT hard disks (1st controller) +.TP +.I hd1[a-d] +2nd AT controller hard disks +.TP +.I xd[a-d] +XT hard disks +.TP +.I sd[a-i] +SCSI hard disks +.TP +.I loop +Loopback disk devices +.TP +.B Tape Devices +.TP +.I st[0-7] +SCSI tapes +.TP +.I qic +QIC-80 tapes +.TP +.I ftape +floppy driver tapes (QIC-117) +.TP +.B CDROM Devices +.TP +.I scd[0-7] +SCSI CD players +.TP +.I sonycd +Sony CDU-31A CD player +.TP +.I mcd +Mitsumi CD player +.TP +.I cdu535 +Sony CDU-535 CD player +.TP +.I lmscd +LMS/Philips CD player (nee \ No newline at end of file diff --git a/sys-utils/Makefile b/sys-utils/Makefile new file mode 100644 index 00000000..a584f16d --- /dev/null +++ b/sys-utils/Makefile @@ -0,0 +1,88 @@ +# Makefile -- Makefile for util-linux Linux utilities +# Created: Sat Dec 26 20:09:40 1992 +# Revised: Sat Feb 11 17:52:09 1995 by faith@cs.unc.edu +# Copyright 1992, 1993, 1994, 1995 Rickard E. Faith (faith@cs.unc.edu) +# + +include ../MCONFIG + +# Where to put man pages? + +MAN1= arch.1 readprofile.1 + +MAN8= MAKEDEV.8 chroot.8 clock.8 ctrlaltdel.8 dmesg.8 ipcrm.8 \ + ipcs.8 kbdrate.8 lpcntl.8 ramsize.8 rdev.8 renice.8 \ + rootflags.8 setserial.8 setsid.8 swapdev.8 sync.8 tunelp.8 \ + update_state.8 vidmode.8 + +# Where to put binaries? +# See the "install" rule for the links. . . + +DEV= MAKEDEV + +SBIN= clock kbdrate sln + +BIN= arch dmesg setserial sync + +USRSBIN= chroot ctrlaltdel update_state + +USRBIN= ipcrm ipcs lpcntl rdev renice readprofile setsid tunelp + +# Where to put datebase files? + +USRINFO= ipc.info + +SCRIPTS= reset update_state + +all: $(SBIN) $(BIN) $(USRSBIN) $(USRBIN) + +sln: sln.c + $(CC) -static $(CFLAGS) $(LDFLAGS) $< -o $@ + +sync: sync.S + /lib/cpp sync.S > sync.s + as -o sync.o sync.s + ld -s -N -e _main sync.o -o sync + -rm sync.s + +%.o: %.c + $(CC) -c $(CFLAGS) $< -o $@ + +$(SCRIPTS): + cp $@.sh $@ + +# Rules for everything else + +arch: arch.o +chroot: chroot.o +clock: clock.o +ctrlaltdel: ctrlaltdel.o +ipcrm: ipcrm.o +ipcs: ipcs.o +kbdrate: kbdrate.o +lpcntl: lpcntl.o +rdev: rdev.o +renice: renice.o +readprofile: readprofile.o +setserial: setserial.o +setsid: setsid.o +update_state: update_state.sh + +install: all + $(INSTALLDIR) $(DEVDIR) $(SBINDIR) $(BINDIR) $(USRSBINDIR) $(USRBINDIR) + $(INSTALLBIN) $(DEV) $(DEVDIR) + $(INSTALLBIN) $(SBIN) $(SBINDIR) + $(INSTALLBIN) $(BIN) $(BINDIR) + $(INSTALLBIN) $(USRSBIN) $(USRSBINDIR) + $(INSTALLBIN) $(USRBIN) $(USRBINDIR) + (cd $(RDEVDIR); ln -sf rdev swapdev) + (cd $(RDEVDIR); ln -sf rdev ramsize) + (cd $(RDEVDIR); ln -sf rdev vidmode) + (cd $(RDEVDIR); ln -sf rdev rootflags) + $(INSTALLDIR) $(MAN1DIR) $(MAN8DIR) $(INFODIR) + $(INSTALLMAN) $(MAN1) $(MAN1DIR) + $(INSTALLMAN) $(MAN8) $(MAN8DIR) + $(INSTALLMAN) $(USRINFO) $(INFODIR) + +clean: + -rm -f *.o *~ core $(SBIN) $(BIN) $(USRSBIN) $(USRBIN) diff --git a/sys-utils/README.MAKEDEV b/sys-utils/README.MAKEDEV new file mode 100644 index 00000000..0cba8328 --- /dev/null +++ b/sys-utils/README.MAKEDEV @@ -0,0 +1,22 @@ +README.MAKEDEV,v 1.1.1.1 1995/02/22 19:09:12 faith Exp + +Here is the original comment taken from the MAKEDEV script. One day, +I'll do this properly. + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +This is my attempt at a MAKEDEV script. IMHO it cleans up many areas. +It can be used to determine the necessary info for a device without +actually creating it using the '-n' flag. + +It makes less individual devices and tends to make classes of devices +(eg "MAKEDEV hda" will create "hda" and the 8 partitions; "MAKEDEV ptyp" +will create the ptyp[0-f] master and ttyp[0-f] slave devices). + +If you are aware of any glaring omissions or errors, please let me know. +Also, if you are a developer who wants your devices supported by MAKEDEV, +let me know. + +Thanks to Ian Jackson for the original help and encouragement. + + Nick Holloway diff --git a/sys-utils/README.setserial b/sys-utils/README.setserial new file mode 100644 index 00000000..bf3a5847 --- /dev/null +++ b/sys-utils/README.setserial @@ -0,0 +1,73 @@ +setserial Version 2.10 + +Setserial is a program which allows you to look at and change various +attributes of a serial device, including its port, its IRQ, and other +serial port options. + +Starting with Linux 0.99 pl10, only the COM1-4 ports are configured, +using the default IRQ of 4 and 3. So, if you have any other serial +ports provided by other boards (such as an AST Fourport), or if COM3-4 +have been a non-standard IRQ so that you can use time simultaneously +with COM1-2, you *must* use this program in order to configure those +serial ports. + +The simplest way to configure the serial ports is to copy the provided +rc.serial file to /etc/rc.serial, and then add to /etc/rc the lines: + +if [ -f /etc/rc.serial ]; then +sh /etc/rc.serial +fi + +Take a look at /etc/rc.serial; it was written to be relatively easy to +modify, and you may need to modify it so that it works best in your +environment. + + +------------------------------------------------------- + +Here is setserial's command line syntax: + +usage: ./setserial [-abgqvVW] serial-device [cmd1 [arg]] [cmd2] ... + +Available options: + -a Display all possible information about the port + -b Display boot-time level of information + -q Quiet flag + -v Verbose flag + + -g Get and display the serial information of all + serial ports on the machine + -V Display the current Version and then exit + + -W Do wild interrupt initialization and then exit + +Available commands: (* = Takes an argument) + (^ = can be preceded by a '^' to turn off the option) + * port set the I/O port + * irq set the interrupt + * uart set UART type (none, 8250, 16450, 16550, 16550A + * baud_base set base baud rate (CLOCK_FREQ / 16) + * divisor set the custom divisor (see spd_custom) + ^ fourport configure the port as an AST Fourport + autoconfigure automatically configure the serial port + ^ auto_irq try to determine irq during autoconfiguration + ^ skip_test skip UART test during autoconfiguration + + ^ sak set the break key as the Secure Attention Key + ^ session_lockout Lock out callout port across different sessions + ^ pgrp_lockout Lock out callout port across different process groups + ^ split_termios Use separate termios for callout and dailin lines + ^ hup_notify Notify a process blocked on opening a dialin line + when a process has finished using a callout + line by returning EAGAIN to the open. + ^ callout_nohup Don't hangup the tty if carrier detect drops on a + callout line. + + spd_hi use 56kb instead of 38.4kb + spd_vhi use 115kb instead of 38.4kb + spd_cust use the custom divisor to set the speed at 38.4kb + (baud rate = baud_base / custom_divisor) + spd_normal use 38.4kb when a buad rate of 38.4kb is selected + +Use a leading '0x' for hex numbers. +CAUTION: Using an invalid port can lock up your machine! diff --git a/sys-utils/arch.1 b/sys-utils/arch.1 new file mode 100644 index 00000000..c465c55f --- /dev/null +++ b/sys-utils/arch.1 @@ -0,0 +1,18 @@ +.\" arch.1 -- +.\" Copyright 1993 Rickard E. Faith (faith@cs.unc.edu) +.\" Public domain: may be freely distributed. +.TH ARCH 1 "20 December 1993" "Linux 0.99" "Linux Programmer's Manual" +.SH NAME +arch \- print machine architecture +.SH SYNOPSIS +.B arch +.SH DESCRIPTION +.B arch +is equivalent to +.B uname -m + +On current Linux systems, +.B arch +prints "i386" or "i486". +.SH SEE ALSO +.BR uname (1) ", " uname (2) diff --git a/sys-utils/arch.c b/sys-utils/arch.c new file mode 100644 index 00000000..33dff304 --- /dev/null +++ b/sys-utils/arch.c @@ -0,0 +1,35 @@ +/* arch -- print machine architecture information + * Created: Mon Dec 20 12:27:15 1993 by faith@cs.unc.edu + * Revised: Mon Dec 20 12:29:23 1993 by faith@cs.unc.edu + * Copyright 1993 Rickard E. Faith (faith@cs.unc.edu) + + * 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. */ + +#include +#include + +int main (void) +{ + struct utsname utsbuf; + + if (uname( &utsbuf )) { + perror( "arch" ); + return 1; + } + + printf( "%s\n", utsbuf.machine ); + + return 0; +} diff --git a/sys-utils/chroot.8 b/sys-utils/chroot.8 new file mode 100644 index 00000000..4beadbfd --- /dev/null +++ b/sys-utils/chroot.8 @@ -0,0 +1,16 @@ +.\" Rick Sladkey +.\" In the public domain. +.\" Pathname modified by faith@cs.unc.edu +.TH CHROOT 8 "20 November 1993" "Linux 0.99" "Linux Programmer's Manual" +.SH NAME +chroot \- change root directory and execute a program there +.SH SYNOPSIS +.BI chroot " directory program" " [ " "arg ..." " ]" +.SH DESCRIPTION +.B chroot +changes the root directory for a process to a new directory +executes a program there. +.SH "SEE ALSO" +.BR chroot (2) +.SH AUTHOR +Rick Sladkey diff --git a/sys-utils/chroot.c b/sys-utils/chroot.c new file mode 100644 index 00000000..7ddbe791 --- /dev/null +++ b/sys-utils/chroot.c @@ -0,0 +1,25 @@ +/* + * chroot.c -- change root directory and execute a command there + * Rick Sladkey + * In the public domain. + */ + +#include +#include +#include + +int main(int argc, char *argv[]) +{ + if (argc < 3) { + fprintf(stderr, "usage: %s directory program [arg ...]\n", + argv[0]); + exit(1); + } + if (chroot(argv[1]) < 0) { + perror("chroot"); + exit(1); + } + execvp(argv[2], argv + 2); + perror("execvp"); + exit(1); +} diff --git a/sys-utils/clock.8 b/sys-utils/clock.8 new file mode 100644 index 00000000..f4dd83ad --- /dev/null +++ b/sys-utils/clock.8 @@ -0,0 +1,108 @@ +.\" Copyright 1992 Rickard E. Faith (faith@cs.unc.edu) +.\" May be distributed under the GNU General Public License +.TH CLOCK 8 "24 December 1992" "Linux 0.99" "Linux Programmer's Manual" +.SH NAME +clock \- manipulate the CMOS clock +.SH SYNOPSIS +.B "/etc/clock [ -u ] -r" +.br +.B "/etc/clock [ -u ] -w" +.br +.B "/etc/clock [ -u ] -s" +.br +.B "/etc/clock [ -u ] -a" +.SH DESCRIPTION +.B clock +manipulates the CMOS clock in variaous ways, allowing it to be read or +written, and allowing synchronization between the CMOS clock and the +kernel's version of the system time. +.SH OPTIONS +.TP +.B \-u +Indicates that the CMOS clock is set to Universal Time. +.TP +.B \-r +Read CMOS clock and print the result to stdout. +.TP +.B \-w +Write the system time into the CMOS clock. +.TP +.B \-s +Set the system time from the CMOS clock. +.TP +.B \-a +Set the system time from the CMOS clock, adjusting the time to correct for +systematic error, and writting it back into the CMOS clock. +.sp +This option uses the file +.I /etc/adjtime +to determine how the clock changes. It contains three numbers: +.RS +The first number is the correction in seconds per day (for example, if your +clock runs 5 seconds fast each day, the first number should read -5.0). +.LP +The second number tells when +.B clock +was last used, in seconds since 1/1/1970. +.LP +The third number is the remaining part of a second that was left over after +the last adjustment. +.LP +The following instructions are from the source code: +.TP +a) +create a file +.I /etc/adjtime +containing as the first and only line: '0.0 0 0.0' +.TP +b) +run +.I "clock -au" +or +.IR "clock -a" , +depending on whether your CMOS is in Universal or Local Time. This updates +the second number. +.TP +c) +set your system time using the +.I date +command. +.TP +d) +update your CMOS time using +.I "clock -wu" +or +.I clock -w +.TP +e) +replace the first number in +.I /etc/adjtime +by your correction. +.TP +f) +put the command +.I "clock -au" +or +.I "clock -a" +in your +.IR /etc/rc.local , +or let +.BR cron (8) +start it regularly. +.RE +.SH FILES +.I /etc/adjtime +.br +.I /etc/rc.local +.SH AUTHORS +.TP +.B V1.0 +Charles Hedrick, hedrick@cs.rutgers.edu, Apr 1992 +.TP +.B V1.1 +Modified for clock adjustments, Rob Hooft, hooft@chem.ruu.nl, Nov 1992 +.TP +.B V1.2 +Patches by Harald Koenig, koenig@nova.tat.physik.uni-tuebingen.de, +applied by Rob Hooft, hooft@EMBL-Heidelberg.DE, Oct 1993 +.sp diff --git a/sys-utils/clock.c b/sys-utils/clock.c new file mode 100644 index 00000000..15020c26 --- /dev/null +++ b/sys-utils/clock.c @@ -0,0 +1,490 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define USE_INLINE_ASM_IO + +#ifdef USE_INLINE_ASM_IO +#include +#endif + +/* V1.0 + * CMOS clock manipulation - Charles Hedrick, hedrick@cs.rutgers.edu, Apr 1992 + * + * clock [-u] -r - read cmos clock + * clock [-u] -w - write cmos clock from system time + * clock [-u] -s - set system time from cmos clock + * clock [-u] -a - set system time from cmos clock, adjust the time to + * correct for systematic error, and put it back to the cmos. + * -u indicates cmos clock is kept in universal time + * + * The program is designed to run setuid, since we need to be able to + * write the CMOS port. + * + * I don't know what the CMOS clock will do in 2000, so this program + * probably won't work past the century boundary. + * + ********************* + * V1.1 + * Modified for clock adjustments - Rob Hooft, hooft@chem.ruu.nl, Nov 1992 + * Also moved error messages to stderr. The program now uses getopt. + * Changed some exit codes. Made 'gcc 2.3 -Wall' happy. + * + * I think a small explanation of the adjustment routine should be given + * here. The problem with my machine is that its CMOS clock is 10 seconds + * per day slow. With this version of clock.c, and my '/etc/rc.local' + * reading '/etc/clock -au' instead of '/etc/clock -u -s', this error + * is automatically corrected at every boot. + * + * To do this job, the program reads and writes the file '/etc/adjtime' + * to determine the correction, and to save its data. In this file are + * three numbers: + * + * 1) the correction in seconds per day (So if your clock runs 5 + * seconds per day fast, the first number should read -5.0) + * 2) the number of seconds since 1/1/1970 the last time the program was + * used. + * 3) the remaining part of a second which was leftover after the last + * adjustment + * + * Installation and use of this program: + * + * a) create a file '/etc/adjtime' containing as the first and only line: + * '0.0 0 0.0' + * b) run 'clock -au' or 'clock -a', depending on whether your cmos is in + * universal or local time. This updates the second number. + * c) set your system time using the 'date' command. + * d) update your cmos time using 'clock -wu' or 'clock -w' + * e) replace the first number in /etc/adjtime by your correction. + * f) put the command 'clock -au' or 'clock -a' in your '/etc/rc.local' + * + * If the adjustment doesn't work for you, try contacting me by E-mail. + * + ****** + * V1.2 + * + * Applied patches by Harald Koenig (koenig@nova.tat.physik.uni-tuebingen.de) + * Patched and indented by Rob Hooft (hooft@EMBL-Heidelberg.DE) + * + * A free quote from a MAIL-message (with spelling corrections): + * + * "I found the explanation and solution for the CMOS reading 0xff problem + * in the 0.99pl13c (ALPHA) kernel: the RTC goes offline for a small amount + * of time for updating. Solution is included in the kernel source + * (linux/kernel/time.c)." + * + * "I modified clock.c to fix this problem and added an option (now default, + * look for USE_INLINE_ASM_IO) that I/O instructions are used as inline + * code and not via /dev/port (still possible via #undef ...)." + * + * With the new code, which is partially taken from the kernel sources, + * the CMOS clock handling looks much more "official". + * Thanks Harald (and Torsten for the kernel code)! + * + ****** + * V1.3 + * Canges from alan@spri.levels.unisa.edu.au (Alan Modra): + * a) Fix a few typos in comments and remove reference to making + * clock -u a cron job. The kernel adjusts cmos time every 11 + * minutes - see kernel/sched.c and kernel/time.c set_rtc_mmss(). + * This means we should really have a cron job updating + * /etc/adjtime every 11 mins (set last_time to the current time + * and not_adjusted to ???). + * b) Swapped arguments of outb() to agree with asm/io.h macro of the + * same name. Use outb() from asm/io.h as it's slightly better. + * c) Changed CMOS_READ and CMOS_WRITE to inline functions. Inserted + * cli()..sti() pairs in appropriate places to prevent possible + * errors, and changed ioperm() call to iopl() to allow cli. + * d) Moved some variables around to localise them a bit. + * e) Fixed bug with clock -ua or clock -us that cleared environment + * variable TZ. This fix also cured the annoying display of bogus + * day of week on a number of machines. (Use mktime(), ctime() + * rather than asctime() ) + * f) Use settimeofday() rather than stime(). This one is important + * as it sets the kernel's timezone offset, which is returned by + * gettimeofday(), and used for display of MSDOS and OS2 file + * times. + * g) faith@cs.unc.edu added -D flag for debugging + * + * V1.4: alan@SPRI.Levels.UniSA.Edu.Au (Alan Modra) + * Wed Feb 8 12:29:08 1995, fix for years > 2000. + * faith@cs.unc.edu added -v option to print version. + * + */ + +#define VERSION "1.4" + +/* Here the information for time adjustments is kept. */ +#define ADJPATH "/etc/adjtime" + + +/* used for debugging the code. */ +/*#define KEEP_OFF */ + +/* Globals */ +int readit = 0; +int adjustit = 0; +int writeit = 0; +int setit = 0; +int universal = 0; +int debug = 0; + +volatile void +usage () +{ + fprintf (stderr, + "clock [-u] -r|w|s|a|v\n" + " r: read and print CMOS clock\n" + " w: write CMOS clock from system time\n" + " s: set system time from CMOS clock\n" + " a: get system time and adjust CMOS clock\n" + " u: CMOS clock is in universal time\n" + " v: print version (" VERSION ") and exit\n" + ); + exit (1); +} + +#ifndef USE_INLINE_ASM_IO +int cmos_fd; +#endif + +static inline unsigned char cmos_read(unsigned char reg) +{ + register unsigned char ret; + __asm__ volatile ("cli"); + outb (reg | 0x80, 0x70); + ret = inb (0x71); + __asm__ volatile ("sti"); + return ret; +} + +static inline void cmos_write(unsigned char reg, unsigned char val) +{ + outb (reg | 0x80, 0x70); + outb (val, 0x71); +} + +#ifndef outb +static inline void +outb (char val, unsigned short port) +{ +#ifdef USE_INLINE_ASM_IO + __asm__ volatile ("out%B0 %0,%1"::"a" (val), "d" (port)); +#else + lseek (cmos_fd, port, 0); + write (cmos_fd, &val, 1); +#endif +} +#endif + +#ifndef inb +static inline unsigned char +inb (unsigned short port) +{ + unsigned char ret; +#ifdef USE_INLINE_ASM_IO + __asm__ volatile ("in%B0 %1,%0":"=a" (ret):"d" (port)); +#else + lseek (cmos_fd, port, 0); + read (cmos_fd, &ret, 1); +#endif + return ret; +} +#endif + +void +cmos_init () +{ +#ifdef USE_INLINE_ASM_IO + if (iopl (3)) + { + fprintf(stderr,"clock: unable to get I/O port access\n"); + exit (1); + } +#else + cmos_fd = open ("/dev/port", 2); + if (cmos_fd < 0) + { + perror ("unable to open /dev/port read/write : "); + exit (1); + } + if (lseek (cmos_fd, 0x70, 0) < 0 || lseek (cmos_fd, 0x71, 0) < 0) + { + perror ("unable to seek port 0x70 in /dev/port : "); + exit (1); + } +#endif +} + +static inline int +cmos_read_bcd (int addr) +{ + int b; + b = cmos_read (addr); + return (b & 15) + (b >> 4) * 10; +} + +static inline void +cmos_write_bcd (int addr, int value) +{ + cmos_write (addr, ((value / 10) << 4) + value % 10); +} + +int +main (int argc, char **argv, char **envp) +{ + struct tm tm; + time_t systime; + time_t last_time; + char arg; + double factor; + double not_adjusted; + int adjustment = 0; + unsigned char save_control, save_freq_select; + + while ((arg = getopt (argc, argv, "rwsuaDv")) != -1) + { + switch (arg) + { + case 'r': + readit = 1; + break; + case 'w': + writeit = 1; + break; + case 's': + setit = 1; + break; + case 'u': + universal = 1; + break; + case 'a': + adjustit = 1; + break; + case 'D': + debug = 1; + break; + case 'v': + fprintf( stderr, "clock " VERSION "\n" ); + exit(0); + default: + usage (); + } + } + + if (readit + writeit + setit + adjustit > 1) + usage (); /* only allow one of these */ + + if (!(readit | writeit | setit | adjustit)) /* default to read */ + readit = 1; + + cmos_init (); + + if (adjustit) + { /* Read adjustment parameters first */ + FILE *adj; + if ((adj = fopen (ADJPATH, "r")) == NULL) + { + perror (ADJPATH); + exit (2); + } + if (fscanf (adj, "%lf %d %lf", &factor, &last_time, ¬_adjusted) < 0) + { + perror (ADJPATH); + exit (2); + } + fclose (adj); + if (debug) printf ("Last adjustment done at %d seconds after 1/1/1970\n", last_time); + } + + if (readit || setit || adjustit) + { + int i; + +/* read RTC exactly on falling edge of update flag */ +/* Wait for rise.... (may take upto 1 second) */ + + for (i = 0; i < 10000000; i++) + if (cmos_read (10) & 0x80) + break; + +/* Wait for fall.... (must try at least 2.228 ms) */ + + for (i = 0; i < 1000000; i++) + if (!(cmos_read (10) & 0x80)) + break; + +/* The purpose of the "do" loop is called "low-risk programming" */ +/* In theory it should never run more than once */ + do + { + tm.tm_sec = cmos_read_bcd (0); + tm.tm_min = cmos_read_bcd (2); + tm.tm_hour = cmos_read_bcd (4); + tm.tm_wday = cmos_read_bcd (6); + tm.tm_mday = cmos_read_bcd (7); + tm.tm_mon = cmos_read_bcd (8); + tm.tm_year = cmos_read_bcd (9); + } + while (tm.tm_sec != cmos_read_bcd (0)); + if (tm.tm_year < 70) + tm.tm_year += 100; /* 70..99 => 1970..1999, 0..69 => 2000..2069 */ + tm.tm_mon--; /* DOS uses 1 base */ + tm.tm_wday -= 3; /* DOS uses 3 - 9 for week days */ + tm.tm_isdst = -1; /* don't know whether it's daylight */ + if (debug) printf ("Cmos time : %d:%d:%d\n", tm.tm_hour, tm.tm_min, tm.tm_sec); + } + + if (readit || setit || adjustit) + { +/* + * mktime() assumes we're giving it local time. If the CMOS clock + * is in GMT, we have to set up TZ so mktime knows it. tzset() gets + * called implicitly by the time code, but only the first time. When + * changing the environment variable, better call tzset() explicitly. + */ + if (universal) + { + char *zone; + zone = (char *) getenv ("TZ"); /* save original time zone */ + (void) putenv ("TZ="); + tzset (); + systime = mktime (&tm); + /* now put back the original zone */ + if (zone) + { + + char *zonebuf; + zonebuf = malloc (strlen (zone) + 4); + strcpy (zonebuf, "TZ="); + strcpy (zonebuf+3, zone); + putenv (zonebuf); + free (zonebuf); + } + else + { /* wasn't one, so clear it */ + putenv ("TZ"); + } + tzset (); + } + else + { + systime = mktime (&tm); + } + if (debug) printf ("Number of seconds since 1/1/1970 is %d\n", systime); + } + + if (readit) + { + printf ("%s", ctime (&systime )); + } + + if (setit || adjustit) + { + struct timeval tv; + struct timezone tz; + +/* program is designed to run setuid, be secure! */ + + if (getuid () != 0) + { + fprintf (stderr, "Sorry, must be root to set or adjust time\n"); + exit (2); + } + + if (adjustit) + { /* the actual adjustment */ + double exact_adjustment; + + exact_adjustment = ((double) (systime - last_time)) + * factor / (24 * 60 * 60) + + not_adjusted; + if (exact_adjustment > 0) + adjustment = (int) (exact_adjustment + 0.5); + else + adjustment = (int) (exact_adjustment - 0.5); + not_adjusted = exact_adjustment - (double) adjustment; + systime += adjustment; + if (debug) { + printf ("Time since last adjustment is %d seconds\n", + (int) (systime - last_time)); + printf ("Adjusting time by %d seconds\n", + adjustment); + printf ("remaining adjustment is %.3f seconds\n", + not_adjusted); + } + } +#ifndef KEEP_OFF + tv.tv_sec = systime; + tv.tv_usec = 0; + tz.tz_minuteswest = timezone / 60; + tz.tz_dsttime = daylight; + + if (settimeofday (&tv, &tz) != 0) + { + fprintf (stderr, + "Unable to set time -- probably you are not root\n"); + exit (1); + } + + if (debug) { + printf( "Called settimeofday:\n" ); + printf( "\ttv.tv_sec = %ld, tv.tv_usec = %ld\n", + tv.tv_sec, tv.tv_usec ); + printf( "\ttz.tz_minuteswest = %d, tz.tz_dsttime = %d\n", + tz.tz_minuteswest, tz.tz_dsttime ); + } +#endif + } + + if (writeit || (adjustit && adjustment != 0)) + { + struct tm *tmp; + systime = time (NULL); + if (universal) + tmp = gmtime (&systime); + else + tmp = localtime (&systime); + +#ifndef KEEP_OFF + __asm__ volatile ("cli"); + save_control = cmos_read (11); /* tell the clock it's being set */ + cmos_write (11, (save_control | 0x80)); + save_freq_select = cmos_read (10); /* stop and reset prescaler */ + cmos_write (10, (save_freq_select | 0x70)); + + cmos_write_bcd (0, tmp->tm_sec); + cmos_write_bcd (2, tmp->tm_min); + cmos_write_bcd (4, tmp->tm_hour); + cmos_write_bcd (6, tmp->tm_wday + 3); + cmos_write_bcd (7, tmp->tm_mday); + cmos_write_bcd (8, tmp->tm_mon + 1); + cmos_write_bcd (9, tmp->tm_year); + + cmos_write (10, save_freq_select); + cmos_write (11, save_control); + __asm__ volatile ("sti"); +#endif + if (debug) printf ("Set to : %d:%d:%d\n", tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + } + else + if (debug) printf ("CMOS clock unchanged.\n"); + /* Save data for next 'adjustit' call */ + if (adjustit) + { + FILE *adj; + if ((adj = fopen (ADJPATH, "w")) == NULL) + { + perror (ADJPATH); + exit (2); + } + fprintf (adj, "%f %d %f\n", factor, systime, not_adjusted); + fclose (adj); + } + exit (0); +} diff --git a/sys-utils/ctrlaltdel.8 b/sys-utils/ctrlaltdel.8 new file mode 100644 index 00000000..488f37c0 --- /dev/null +++ b/sys-utils/ctrlaltdel.8 @@ -0,0 +1,38 @@ +.\" Copyright 1992, 1993 Rickard E. Faith (faith@cs.unc.edu) +.\" May be distributed under the GNU General Public License +.TH CTRLALTDEL 8 "25 October 1993" "Linux 0.99" "Linux Programmer's Manual" +.SH NAME +ctrlaltdel \- set the function of the Ctrl-Alt-Del combination +.SH SYNOPSIS +.B "ctrlaltdel hard|soft" +.SH DESCRIPTION +Based on examination of the +.I linux/kernel/sys.c +code, it is clear that there are two supported functions that the +Ctrl-Alt-Del sequence can perform: a +.I hard +reset, which immediately reboots the computer without calling +.B sync (2) +and without any other preparation; and a +.I soft +reset, which sends the SIGINT (interrupt) signal to the +.B init +process (this is always the process with PID 1). If this option is used, +the +.B init (8) +program must support this feature. Since there are now several +.B init (8) +programs in the Linux community, please consult the documentation for the +version that you are currently using. + +.B ctrlaltdel +is usually used in the +.I /etc/rc.local +file. +.SH FILES +.I /etc/rc.local +.SH "SEE ALSO" +.BR simpleinit (8), +.BR init (8) +.SH AUTHOR +Peter Orbaek (poe@daimi.aau.dk) diff --git a/sys-utils/ctrlaltdel.c b/sys-utils/ctrlaltdel.c new file mode 100644 index 00000000..e5565d70 --- /dev/null +++ b/sys-utils/ctrlaltdel.c @@ -0,0 +1,38 @@ +/* + * ctrlaltdel.c - Set the function of the Ctrl-Alt-Del combination + * Created 4-Jul-92 by Peter Orbaek + * ftp://ftp.daimi.aau.dk/pub/linux/poe/ + */ + +#include +#include +#include + +int reboot(int magic, int magictoo, int flag); + +int +main(int argc, char *argv[]) { + + if(geteuid()) { + fprintf(stderr, "You must be root to set the Ctrl-Alt-Del behaviour.\n"); + exit(1); + } + + if(argc == 2 && !strcmp("hard", argv[1])) { + if(reboot(0xfee1dead, 672274793, 0x89abcdef) < 0) { + perror("ctrlaltdel: reboot"); + exit(1); + } + } else if(argc == 2 && !strcmp("soft", argv[1])) { + if(reboot(0xfee1dead, 672274793, 0) < 0) { + perror("ctrlaltdel: reboot"); + exit(1); + } + } else { + fprintf(stderr, "Usage: ctrlaltdel hard|soft\n"); + exit(1); + } + exit(0); +} + + diff --git a/sys-utils/dmesg.8 b/sys-utils/dmesg.8 new file mode 100644 index 00000000..d2e4ce1b --- /dev/null +++ b/sys-utils/dmesg.8 @@ -0,0 +1,49 @@ +.\" Copyright 1993 Rickard E. Faith (faith@cs.unc.edu) +.\" May be distributed under the GNU General Public License +.TH DMESG 8 "28 October 1993" "Linux 0.99" "Linux Programmer's Manual" +.SH NAME +dmesg \- print or control the kernel ring buffer +.SH SYNOPSIS +.BI "dmesg [ \-c ] [ \-n " level " ]" +.SH DESCRIPTION +.B dmesg +is used to examine or control the kernel ring buffer. + +The program helps users to print out their bootup messages. Instead of +copying the messages by hand, the user need only: +.RS +dmesg > boot.messages +.RE +and mail the +.I boot.messages +file to whoever can debug their problem. +.SH OPTIONS +.TP +.B \-c +clear the ring buffer contents after printing. +.TP +.BI \-n level +set the +.I level +at which logging of messages is done to the console. For example, +.B \-n 1 +prevents all messages, expect panic messages, from appearing on the +console. All levels of messages are still written to +.IR /proc/kmsg , +so +.BR syslogd (8) +can still be used to control exactly where kernel messages appear. When +the +.B \-n +option is used, +.B dmesg +will +.I not +print or clear the kernel ring buffer. + +When both options are used, only the last option on the command line will +have an effect. +.SH SEE ALSO +.BR syslogd (8) +.SH AUTHOR +Theodore Ts'o (tytso@athena.mit.edu) diff --git a/sys-utils/dmesg.c b/sys-utils/dmesg.c new file mode 100644 index 00000000..88052853 --- /dev/null +++ b/sys-utils/dmesg.c @@ -0,0 +1,88 @@ +/* dmesg.c -- Print out the contents of the kernel ring buffer + * Created: Sat Oct 9 16:19:47 1993 + * Revised: Thu Oct 28 21:52:17 1993 by faith@cs.unc.edu + * Copyright 1993 Theodore Ts'o (tytso@athena.mit.edu) + * This program comes with ABSOLUTELY NO WARRANTY. + * Modifications by Rick Sladkey (jrs@world.std.com) + */ + +#include +#include +#include + +#define __NR_klog __NR_syslog + +static inline _syscall3(int,klog,int,type,char *,b,int,len) + +static char *progname; + +usage() +{ + fprintf( stderr, "Usage: %s [-c] [-n level]\n", progname ); +} + +int main( int argc, char *argv[] ) +{ + char buf[4096]; + int i; + int n; + int c; + int level; + int lastc; + int cmd = 3; + + progname = argv[0]; + while ((c = getopt( argc, argv, "cn:" )) != EOF) { + switch (c) { + case 'c': + cmd = 4; + break; + case 'n': + cmd = 8; + level = atoi(optarg); + break; + case '?': + default: + usage(); + exit(1); + } + } + argc -= optind; + argv += optind; + + if (argc > 1) { + usage(); + exit(1); + } + + if (cmd == 8) { + n = klog( cmd, NULL, level ); + if (n < 0) { + perror( "klog" ); + exit( 1 ); + } + exit( 0 ); + } + + n = klog( cmd, buf, sizeof( buf ) ); + if (n < 0) { + perror( "klog" ); + exit( 1 ); + } + + lastc = '\n'; + for (i = 0; i < n; i++) { + if ((i == 0 || buf[i - 1] == '\n') && buf[i] == '<') { + i++; + while (buf[i] >= '0' && buf[i] <= '9') + i++; + if (buf[i] == '>') + i++; + } + lastc = buf[i]; + putchar( lastc ); + } + if (lastc != '\n') + putchar( '\n' ); + return 0; +} diff --git a/sys-utils/ipc.info b/sys-utils/ipc.info new file mode 100644 index 00000000..5d34e18d --- /dev/null +++ b/sys-utils/ipc.info @@ -0,0 +1,1106 @@ +This is Info file ipc.info, produced by Makeinfo-1.47 from the input +file ipc.texi. + + This file documents the System V style inter process communication +primitives available under linux. + + Copyright (C) 1992 krishna balasubramanian + + Permission is granted to use this material and the accompanying +programs within the terms of the GNU GPL. + + +File: ipc.info, Node: top, Next: Overview, Prev: Notes, Up: (dir) + +System V IPC. +************* + + These facilities are provided to maintain compatibility with +programs developed on system V unix systems and others that rely on +these system V mechanisms to accomplish inter process communication +(IPC). + + The specifics described here are applicable to the Linux +implementation. Other implementations may do things slightly +differently. + +* Menu: + +* Overview:: What is system V ipc? Overall mechanisms. +* Messages:: System calls for message passing. +* Semaphores:: System calls for semaphores. +* Shared Memory:: System calls for shared memory access. +* Notes:: Miscellaneous notes. + + +File: ipc.info, Node: Overview, Next: example, Prev: top, Up: top + +Overview +======== + +System V IPC consists of three mechanisms: + + * Messages : exchange messages with any process or server. + + * Semaphores : allow unrelated processes to synchronize execution. + + * Shared memory : allow unrelated processes to share memory. + +* Menu: + +* example:: Using shared memory. +* perms:: Description of access permissions. +* syscalls:: Overview of ipc system calls. + + Access to all resources is permitted on the basis of permissions set +up when the resource was created. + + A resource here consists of message queue, a semaphore set (array) +or a shared memory segment. + + A resource must first be allocated by a creator before it is used. +The creator can assign a different owner. After use the resource must +be explicitly destroyed by the creator or owner. + + A resource is identified by a numeric ID. Typically a creator +defines a KEY that may be used to access the resource. The user process +may then use this KEY in the "get" system call to obtain the ID for the +corresponding resource. This ID is then used for all further access. A +library call "ftok" is provided to translate pathnames or strings to +numeric keys. + + There are system and implementation defined limits on the number and +sizes of resources of any given type. Some of these are imposed by the +implementation and others by the system administrator when configuring +the kernel (*Note msglimits::, *Note semlimits::, *Note shmlimits::). + + There is an `msqid_ds', `semid_ds' or `shmid_ds' struct associated +with each message queue, semaphore array or shared segment. Each ipc +resource has an associated `ipc_perm' struct which defines the creator, +owner, access perms ..etc.., for the resource. These structures are +detailed in the following sections. + + +File: ipc.info, Node: example, Next: perms, Prev: Overview, Up: Overview + +example +======= + + Here is a code fragment with pointers on how to use shared memory. +The same methods are applicable to other resources. + + In a typical access sequence the creator allocates a new instance of +the resource with the `get' system call using the IPC_CREAT flag. + +creator process: + #include + int id; + key_t key; + char proc_id = 'C'; + int size = 0x5000; /* 20 K */ + int flags = 0664 | IPC_CREAT; /* read-only for others */ + + key = ftok ("~creator/ipckey", proc_id); + id = shmget (key, size, flags); + exit (0); /* quit leaving resource allocated */ + +Users then gain access to the resource using the same key. +Client process: + #include + char *shmaddr; + int id; + key_t key; + char proc_id = 'C'; + + key = ftok ("~creator/ipckey", proc_id); + + id = shmget (key, 0, 004); /* default size */ + if (id == -1) + perror ("shmget ..."); + + shmaddr = shmat (id, 0, SHM_RDONLY); /* attach segment for reading */ + if (shmaddr == (char *) -1) + perror ("shmat ..."); + + local_var = *(shmaddr + 3); /* read segment etc. */ + + shmdt (shmaddr); /* detach segment */ + +When the resource is no longer needed the creator should remove it. +Creator/owner process 2: + key = ftok ("~creator/ipckey", proc_id) + id = shmget (key, 0, 0); + shmctl (id, IPC_RMID, NULL); + + +File: ipc.info, Node: perms, Next: syscalls, Prev: example, Up: Overview + +Permissions +=========== + + Each resource has an associated `ipc_perm' struct which defines the +creator, owner and access perms for the resource. + + struct ipc_perm + key_t key; /* set by creator */ + ushort uid; /* owner euid and egid */ + ushort gid; + ushort cuid; /* creator euid and egid */ + ushort cgid; + ushort mode; /* access modes in lower 9 bits */ + ushort seq; /* sequence number */ + + The creating process is the default owner. The owner can be +reassigned by the creator and has creator perms. Only the owner, +creator or super-user can delete the resource. + + The lowest nine bits of the flags parameter supplied by the user to +the system call are compared with the values stored in `ipc_perms.mode' +to determine if the requested access is allowed. In the case that the +system call creates the resource, these bits are initialized from the +user supplied value. + + As for files, access permissions are specified as read, write and +exec for user, group or other (though the exec perms are unused). For +example 0624 grants read-write to owner, write-only to group and +read-only access to others. + + For shared memory, note that read-write access for segments is +determined by a separate flag which is not stored in the `mode' field. +Shared memory segments attached with write access can be read. + + The `cuid', `cgid', `key' and `seq' fields cannot be changed by the +user. + + +File: ipc.info, Node: syscalls, Next: Messages, Prev: perms, Up: Overview + +IPC system calls +================ + + This section provides an overview of the IPC system calls. See the +specific sections on each type of resource for details. + + Each type of mechanism provides a "get", "ctl" and one or more "op" +system calls that allow the user to create or procure the resource +(get), define its behaviour or destroy it (ctl) and manipulate the +resources (op). + +The "get" system calls +---------------------- + + The `get' call typically takes a KEY and returns a numeric ID that +is used for further access. The ID is an index into the resource table. +A sequence number is maintained and incremented when a resource is +destroyed so that acceses using an obselete ID is likely to fail. + + The user also specifies the permissions and other behaviour +charecteristics for the current access. The flags are or-ed with the +permissions when invoking system calls as in: + msgflg = IPC_CREAT | IPC_EXCL | 0666; + id = msgget (key, msgflg); + + * `key' : IPC_PRIVATE => new instance of resource is initialized. + + * `flags' : + IPC_CREAT : resource created for KEY if it does not exist. + + IPC_CREAT | IPC_EXCL : fail if resource exists for KEY. + + * returns : an identifier used for all further access to the + resource. + + Note that IPC_PRIVATE is not a flag but a special `key' that ensures +(when the call is successful) that a new resource is created. + + Use of IPC_PRIVATE does not make the resource inaccessible to other +users. For this you must set the access permissions appropriately. + + There is currently no way for a process to ensure exclusive access +to a resource. IPC_CREAT | IPC_EXCL only ensures (on success) that a new +resource was initialized. It does not imply exclusive access. + +See Also : *Note msgget::, *Note semget::, *Note shmget::. + +The "ctl" system calls +---------------------- + + Provides or alters the information stored in the structure that +describes the resource indexed by ID. + + #include + struct msqid_ds buf; + err = msgctl (id, IPC_STAT, &buf); + if (err) + !$#%* + else + printf ("creator uid = %d\n", buf.msg_perm.cuid); + .... + +Commands supported by all `ctl' calls: + * IPC_STAT : read info on resource specified by id into user + allocated buffer. The user must have read access to the resource. + + * IPC_SET : write info from buffer into resource data structure. The + user must be owner creator or super-user. + + * IPC_RMID : remove resource. The user must be the owner, creator or + super-user. + +The IPC_RMID command results in immediate removal of a message queue or +semaphore array. Shared memory segments however, are only destroyed +upon the last detach after IPC_RMID is executed. + + The `semctl' call provides a number of command options that allow +the user to determine or set the values of the semaphores in an array. + +See Also: *Note msgctl::, *Note semctl::, *Note shmctl::. + +The "op" system calls +--------------------- + + Used to send or receive messages, read or alter semaphore values, +attach or detach shared memory segments. The IPC_NOWAIT flag will cause +the operation to fail with error EAGAIN if the process has to wait on +the call. + +`flags' : IPC_NOWAIT => return with error if a wait is required. + +See Also: *Note msgsnd::,*Note msgrcv::,*Note semop::,*Note shmat::, +*Note shmdt::. + + +File: ipc.info, Node: Messages, Next: msgget, Prev: syscalls, Up: top + +Messages +======== + + A message resource is described by a struct `msqid_ds' which is +allocated and initialized when the resource is created. Some fields in +`msqid_ds' can then be altered (if desired) by invoking `msgctl'. The +memory used by the resource is released when it is destroyed by a +`msgctl' call. + + struct msqid_ds + struct ipc_perm msg_perm; + struct msg *msg_first; /* first message on queue (internal) */ + struct msg *msg_last; /* last message in queue (internal) */ + time_t msg_stime; /* last msgsnd time */ + time_t msg_rtime; /* last msgrcv time */ + time_t msg_ctime; /* last change time */ + struct wait_queue *wwait; /* writers waiting (internal) */ + struct wait_queue *rwait; /* readers waiting (internal) */ + ushort msg_cbytes; /* number of bytes used on queue */ + ushort msg_qnum; /* number of messages in queue */ + ushort msg_qbytes; /* max number of bytes on queue */ + ushort msg_lspid; /* pid of last msgsnd */ + ushort msg_lrpid; /* pid of last msgrcv */ + + To send or receive a message the user allocates a structure that +looks like a `msgbuf' but with an array `mtext' of the required size. +Messages have a type (positive integer) associated with them so that +(for example) a listener can choose to receive only messages of a given +type. + + struct msgbuf + long mtype; type of message (*Note msgrcv::). + char mtext[1]; message text .. why is this not a ptr? + + The user must have write permissions to send and read permissions to +receive messages on a queue. + + When `msgsnd' is invoked, the user's message is copied into an +internal struct `msg' and added to the queue. A `msgrcv' will then read +this message and free the associated struct `msg'. + +* Menu: + +* msgget:: +* msgsnd:: +* msgrcv:: +* msgctl:: +* msglimits:: Implementation defined limits. + + +File: ipc.info, Node: msgget, Next: msgsnd, Prev: Messages, Up: Messages + +msgget +------ + +A message queue is allocated by a msgget system call : + + msqid = msgget (key_t key, int msgflg); + + * `key': an integer usually got from `ftok()' or IPC_PRIVATE. + + * `msgflg': + IPC_CREAT : used to create a new resource if it does not + already exist. + + IPC_EXCL | IPC_CREAT : used to ensure failure of the call if + the resource already exists. + + rwxrwxrwx : access permissions. + + * returns: msqid (an integer used for all further access) on success. + -1 on failure. + + A message queue is allocated if there is no resource corresponding +to the given key. The access permissions specified are then copied into +the `msg_perm' struct and the fields in `msqid_ds' initialized. The +user must use the IPC_CREAT flag or key = IPC_PRIVATE, if a new +instance is to be allocated. If a resource corresponding to KEY already +exists, the access permissions are verified. + +Errors: +EACCES : (procure) Do not have permission for requested access. +EEXIST : (allocate) IPC_CREAT | IPC_EXCL specified and resource exists. +EIDRM : (procure) The resource was removed. +ENOSPC : All id's are taken (max of MSGMNI id's system-wide). +ENOENT : Resource does not exist and IPC_CREAT not specified. +ENOMEM : A new `msqid_ds' was to be created but ... nomem. + + +File: ipc.info, Node: msgsnd, Next: msgrcv, Prev: msgget, Up: Messages + +msgsnd +------ + + int msgsnd (int msqid, struct msgbuf *msgp, int msgsz, int msgflg); + + * `msqid' : id obtained by a call to msgget. + + * `msgsz' : size of msg text (`mtext') in bytes. + + * `msgp' : message to be sent. (msgp->mtype must be positive). + + * `msgflg' : IPC_NOWAIT. + + * returns : msgsz on success. -1 on error. + + The message text and type are stored in the internal `msg' +structure. `msg_cbytes', `msg_qnum', `msg_lspid', and `msg_stime' +fields are updated. Readers waiting on the queue are awakened. + +Errors: +EACCES : Do not have write permission on queue. +EAGAIN : IPC_NOWAIT specified and queue is full. +EFAULT : msgp not accessible. +EIDRM : The message queue was removed. +EINTR : Full queue ... would have slept but ... was interrupted. +EINVAL : mtype < 1, msgsz > MSGMAX, msgsz < 0, msqid < 0 or unused. +ENOMEM : Could not allocate space for header and text. + +File: ipc.info, Node: msgrcv, Next: msgctl, Prev: msgsnd, Up: Messages + +msgrcv +------ + + int msgrcv (int msqid, struct msgbuf *msgp, int msgsz, long msgtyp, + int msgflg); + + * msqid : id obtained by a call to msgget. + + * msgsz : maximum size of message to receive. + + * msgp : allocated by user to store the message in. + + * msgtyp : + 0 => get first message on queue. + + > 0 => get first message of matching type. + + < 0 => get message with least type which is <= abs(msgtyp). + + * msgflg : + IPC_NOWAIT : Return immediately if message not found. + + MSG_NOERROR : The message is truncated if it is larger than + msgsz. + + MSG_EXCEPT : Used with msgtyp > 0 to receive any msg except + of specified type. + + * returns : size of message if found. -1 on error. + + The first message that meets the `msgtyp' specification is +identified. For msgtyp < 0, the entire queue is searched for the +message with the smallest type. + + If its length is smaller than msgsz or if the user specified the +MSG_NOERROR flag, its text and type are copied to msgp->mtext and +msgp->mtype, and it is taken off the queue. + + The `msg_cbytes', `msg_qnum', `msg_lrpid', and `msg_rtime' fields +are updated. Writers waiting on the queue are awakened. + +Errors: +E2BIG : msg bigger than msgsz and MSG_NOERROR not specified. +EACCES : Do not have permission for reading the queue. +EFAULT : msgp not accessible. +EIDRM : msg queue was removed. +EINTR : msg not found ... would have slept but ... was interrupted. +EINVAL : msgsz > msgmax or msgsz < 0, msqid < 0 or unused. +ENOMSG : msg of requested type not found and IPC_NOWAIT specified. + + +File: ipc.info, Node: msgctl, Next: msglimits, Prev: msgrcv, Up: Messages + +msgctl +------ + + int msgctl (int msqid, int cmd, struct msqid_ds *buf); + + * msqid : id obtained by a call to msgget. + + * buf : allocated by user for reading/writing info. + + * cmd : IPC_STAT, IPC_SET, IPC_RMID (*Note syscalls::). + + IPC_STAT results in the copy of the queue data structure into the +user supplied buffer. + + In the case of IPC_SET, the queue size (`msg_qbytes') and the `uid', +`gid', `mode' (low 9 bits) fields of the `msg_perm' struct are set from +the user supplied values. `msg_ctime' is updated. + + Note that only the super user may increase the limit on the size of a +message queue beyond MSGMNB. + + When the queue is destroyed (IPC_RMID), the sequence number is +incremented and all waiting readers and writers are awakened. These +processes will then return with `errno' set to EIDRM. + +Errors: + +EPERM : Insufficient privilege to increase the size of the queue +(IPC_SET) or remove it (IPC_RMID). +EACCES : Do not have permission for reading the queue (IPC_STAT). +EFAULT : buf not accessible (IPC_STAT, IPC_SET). +EIDRM : msg queue was removed. +EINVAL : invalid cmd, msqid < 0 or unused. + + +File: ipc.info, Node: msglimits, Next: Semaphores, Prev: msgctl, Up: Messages + +Limis on Message Resources +-------------------------- + +Sizeof various structures: + msqid_ds 52 /* 1 per message queue .. dynamic */ + + msg 16 /* 1 for each message in system .. dynamic */ + + msgbuf 8 /* allocated by user */ + +Limits + * MSGMNI : number of message queue identifiers ... policy. + + * MSGMAX : max size of message. Header and message space allocated + on one page. MSGMAX = (PAGE_SIZE - sizeof(struct msg)). + Implementation maximum MSGMAX = 4080. + + * MSGMNB : default max size of a message queue ... policy. The + super-user can increase the size of a queue beyond MSGMNB by a + `msgctl' call. + +Unused or unimplemented: +MSGTQL max number of message headers system-wide. +MSGPOOL total size in bytes of msg pool. + + +File: ipc.info, Node: Semaphores, Next: semget, Prev: msglimits, Up: top + +Semaphores +========== + + Each semaphore has a value >= 0. An id provides access to an array +of `nsems' semaphores. Operations such as read, increment or decrement +semaphores in a set are performed by the `semop' call which processes +`nsops' operations at a time. Each operation is specified in a struct +`sembuf' described below. The operations are applied only if all of +them succeed. + + If you do not have a need for such arrays, you are probably better +off using the `test_bit', `set_bit' and `clear_bit' bit-operations +defined in . + + Semaphore operations may also be qualified by a SEM_UNDO flag which +results in the operation being undone when the process exits. + + If a decrement cannot go through, a process will be put to sleep on +a queue waiting for the `semval' to increase unless it specifies +IPC_NOWAIT. A read operation can similarly result in a sleep on a queue +waiting for `semval' to become 0. (Actually there are two queues per +semaphore array). + +A semaphore array is described by: + struct semid_ds + struct ipc_perm sem_perm; + time_t sem_otime; /* last semop time */ + time_t sem_ctime; /* last change time */ + struct wait_queue *eventn; /* wait for a semval to increase */ + struct wait_queue *eventz; /* wait for a semval to become 0 */ + struct sem_undo *undo; /* undo entries */ + ushort sem_nsems; /* no. of semaphores in array */ + +Each semaphore is described internally by : + struct sem + short sempid; /* pid of last semop() */ + ushort semval; /* current value */ + ushort semncnt; /* num procs awaiting increase in semval */ + ushort semzcnt; /* num procs awaiting semval = 0 */ + +* Menu: + +* semget:: +* semop:: +* semctl:: +* semlimits:: Limits imposed by this implementation. + + +File: ipc.info, Node: semget, Next: semop, Prev: Semaphores, Up: Semaphores + +semget +------ + +A semaphore array is allocated by a semget system call: + + semid = semget (key_t key, int nsems, int semflg); + + * `key' : an integer usually got from `ftok' or IPC_PRIVATE + + * `nsems' : + # of semaphores in array (0 <= nsems <= SEMMSL <= SEMMNS) + + 0 => dont care can be used when not creating the resource. If + successful you always get access to the entire array anyway. + + * semflg : + IPC_CREAT used to create a new resource + + IPC_EXCL used with IPC_CREAT to ensure failure if the + resource exists. + + rwxrwxrwx access permissions. + + * returns : semid on success. -1 on failure. + + An array of nsems semaphores is allocated if there is no resource +corresponding to the given key. The access permissions specified are +then copied into the `sem_perm' struct for the array along with the +user-id etc. The user must use the IPC_CREAT flag or key = IPC_PRIVATE +if a new resource is to be created. + +Errors: +EINVAL : nsems not in above range (allocate). +nsems greater than number in array (procure). +EEXIST : (allocate) IPC_CREAT | IPC_EXCL specified and resource exists. +EIDRM : (procure) The resource was removed. +ENOMEM : could not allocate space for semaphore array. +ENOSPC : No arrays available (SEMMNI), too few semaphores available +(SEMMNS). +ENOENT : Resource does not exist and IPC_CREAT not specified. +EACCES : (procure) do not have permission for specified access. + + +File: ipc.info, Node: semop, Next: semctl, Prev: semget, Up: Semaphores + +semop +----- + +Operations on semaphore arrays are performed by calling semop : + + int semop (int semid, struct sembuf *sops, unsigned nsops); + + * semid : id obtained by a call to semget. + + * sops : array of semaphore operations. + + * nsops : number of operations in array (0 < nsops < SEMOPM). + + * returns : semval for last operation. -1 on failure. + +Operations are described by a structure sembuf: + struct sembuf + ushort sem_num; /* semaphore index in array */ + short sem_op; /* semaphore operation */ + short sem_flg; /* operation flags */ + + The value `sem_op' is to be added (signed) to the current value +semval of the semaphore with index sem_num (0 .. nsems -1) in the set. +Flags recognized in sem_flg are IPC_NOWAIT and SEM_UNDO. + +Two kinds of operations can result in wait: + 1. If sem_op is 0 (read operation) and semval is non-zero, the process + sleeps on a queue waiting for semval to become zero or returns with + error EAGAIN if (IPC_NOWAIT | sem_flg) is true. + + 2. If (sem_op < 0) and (semval + sem_op < 0), the process either + sleeps on a queue waiting for semval to increase or returns with + error EAGAIN if (sem_flg & IPC_NOWAIT) is true. + + The array sops is first read in and preliminary checks performed on +the arguments. The operations are parsed to determine if any of them +needs write permissions or requests an undo operation. + + The operations are then tried and the process sleeps if any operation +that does not specify IPC_NOWAIT cannot go through. If a process sleeps +it repeats these checks on waking up. If any operation that requests +IPC_NOWAIT, cannot go through at any stage, the call returns with errno +set to EAGAIN. + + Finally, operations are committed when all go through without an +intervening sleep. Processes waiting on the zero_queue or +increment_queue are awakened if any of the semval's becomes zero or is +incremented respectively. + +Errors: +E2BIG : nsops > SEMOPM. +EACCES : Do not have permission for requested (read/alter) access. +EAGAIN : An operation with IPC_NOWAIT specified could not go through. +EFAULT : The array sops is not accessible. +EFBIG : An operation had semnum >= nsems. +EIDRM : The resource was removed. +EINTR : The process was interrupted on its way to a wait queue. +EINVAL : nsops is 0, semid < 0 or unused. +ENOMEM : SEM_UNDO requested. Could not allocate space for undo +structure. +ERANGE : sem_op + semval > SEMVMX for some operation. + + +File: ipc.info, Node: semctl, Next: semlimits, Prev: semop, Up: Semaphores + +semctl +------ + + int semctl (int semid, int semnum, int cmd, union semun arg); + + * semid : id obtained by a call to semget. + + * cmd : + GETPID return pid for the process that executed the last + semop. + + GETVAL return semval of semaphore with index semnum. + + GETNCNT return number of processes waiting for semval to + increase. + + GETZCNT return number of processes waiting for semval to + become 0 + + SETVAL set semval = arg.val. + + GETALL read all semval's into arg.array. + + SETALL set all semval's with values given in arg.array. + + * returns : 0 on success or as given above. -1 on failure. + + The first 4 operate on the semaphore with index semnum in the set. +The last two operate on all semaphores in the set. + + `arg' is a union : + union semun + int val; value for SETVAL. + struct semid_ds *buf; buffer for IPC_STAT and IPC_SET. + ushort *array; array for GETALL and SETALL + + * IPC_SET, SETVAL, SETALL : sem_ctime is updated. + + * SETVAL, SETALL : Undo entries are cleared for altered semaphores in + all processes. Processes sleeping on the wait queues are awakened + if a semval becomes 0 or increases. + + * IPC_SET : sem_perm.uid, sem_perm.gid, sem_perm.mode are updated + from user supplied values. + +Errors: + +EACCES : do not have permission for specified access. +EFAULT : arg is not accessible. +EIDRM : The resource was removed. +EINVAL : semid < 0 or semnum < 0 or semnum >= nsems. +EPERM : IPC_RMID, IPC_SET ... not creator, owner or super-user. +ERANGE : arg.array[i].semval > SEMVMX or < 0 for some i. + + +File: ipc.info, Node: semlimits, Next: Shared Memory, Prev: semctl, Up: Semaphores + +Limits on Semaphore Resources +----------------------------- + +Sizeof various structures: + semid_ds 44 /* 1 per semaphore array .. dynamic */ + sem 8 /* 1 for each semaphore in system .. dynamic */ + sembuf 6 /* allocated by user */ + sem_undo 20 /* 1 for each undo request .. dynamic */ + +Limits : + * SEMVMX 32767 semaphore maximum value (short). + + * SEMMNI number of semaphore identifiers (or arrays) system + wide...policy. + + * SEMMSL maximum number of semaphores per id. 1 semid_ds per + array, 1 struct sem per semaphore => SEMMSL = (PAGE_SIZE - + sizeof(semid_ds)) / sizeof(sem). Implementation maximum SEMMSL = + 500. + + * SEMMNS maximum number of semaphores system wide ... policy. + Setting SEMMNS >= SEMMSL*SEMMNI makes it irrelevent. + + * SEMOPM Maximum number of operations in one semop + call...policy. + +Unused or unimplemented: +SEMAEM adjust on exit max value. +SEMMNU number of undo structures system-wide. +SEMUME maximum number of undo entries per process. + + +File: ipc.info, Node: Shared Memory, Next: shmget, Prev: semlimits, Up: top + +Shared Memory +============= + + Shared memory is distinct from the sharing of read-only code pages or +the sharing of unaltered data pages that is available due to the +copy-on-write mechanism. The essential difference is that the shared +pages are dirty (in the case of Shared memory) and can be made to +appear at a convenient location in the process' address space. + +A shared segment is described by : + struct shmid_ds + struct ipc_perm shm_perm; + int shm_segsz; /* size of segment (bytes) */ + time_t shm_atime; /* last attach time */ + time_t shm_dtime; /* last detach time */ + time_t shm_ctime; /* last change time */ + ulong *shm_pages; /* internal page table */ + ushort shm_cpid; /* pid, creator */ + ushort shm_lpid; /* pid, last operation */ + short shm_nattch; /* no. of current attaches */ + + A shmget allocates a shmid_ds and an internal page table. A shmat +maps the segment into the process' address space with pointers into the +internal page table and the actual pages are faulted in as needed. The +memory associated with the segment must be explicitly destroyed by +calling shmctl with IPC_RMID. + +* Menu: + +* shmget:: +* shmat:: +* shmdt:: +* shmctl:: +* shmlimits:: Limits imposed by this implementation. + + +File: ipc.info, Node: shmget, Next: shmat, Prev: Shared Memory, Up: Shared Memory + +shmget +------ + +A shared memory segment is allocated by a shmget system call: + + int shmget(key_t key, int size, int shmflg); + + * key : an integer usually got from `ftok' or IPC_PRIVATE + + * size : size of the segment in bytes (SHMMIN <= size <= SHMMAX). + + * shmflg : + IPC_CREAT used to create a new resource + + IPC_EXCL used with IPC_CREAT to ensure failure if the + resource exists. + + rwxrwxrwx access permissions. + + * returns : shmid on success. -1 on failure. + + A descriptor for a shared memory segment is allocated if there isn't +one corresponding to the given key. The access permissions specified are +then copied into the `shm_perm' struct for the segment along with the +user-id etc. The user must use the IPC_CREAT flag or key = IPC_PRIVATE +to allocate a new segment. + + If the segment already exists, the access permissions are verified, +and a check is made to see that it is not marked for destruction. + + `size' is effectively rounded up to a multiple of PAGE_SIZE as shared +memory is allocated in pages. + +Errors: +EINVAL : (allocate) Size not in range specified above. +(procure) Size greater than size of segment. +EEXIST : (allocate) IPC_CREAT | IPC_EXCL specified and resource exists. +EIDRM : (procure) The resource is marked destroyed or was removed. +ENOSPC : (allocate) All id's are taken (max of SHMMNI id's system-wide). +Allocating a segment of the requested size would exceed the system wide +limit on total shared memory (SHMALL). +ENOENT : (procure) Resource does not exist and IPC_CREAT not specified. +EACCES : (procure) Do not have permission for specified access. +ENOMEM : (allocate) Could not allocate memory for shmid_ds or pg_table. + + +File: ipc.info, Node: shmat, Next: shmdt, Prev: shmget, Up: Shared Memory + +shmat +----- + +Maps a shared segment into the process' address space. + + char *virt_addr; + virt_addr = shmat (int shmid, char *shmaddr, int shmflg); + + * shmid : id got from call to shmget. + + * shmaddr : requested attach address. + If shmaddr is 0 the system finds an unmapped region. + If a non-zero value is indicated the value must be page + aligned or the user must specify the SHM_RND flag. + + * shmflg : + SHM_RDONLY : request read-only attach. + SHM_RND : attach address is rounded DOWN to a multiple of SHMLBA. + + * returns: virtual address of attached segment. -1 on failure. + + When shmaddr is 0, the attach address is determined by finding an +unmapped region in the address range 1G to 1.5G, starting at 1.5G and +coming down from there. The algorithm is very simple so you are +encouraged to avoid non-specific attaches. + +Algorithm: + Determine attach address as described above. + Check region (shmaddr, shmaddr + size) is not mapped and allocate + page tables (undocumented SHM_REMAP flag!). + Map the region by setting up pointers into the internal page table. + Add a descriptor for the attach to the task struct for the process. + `shm_nattch', `shm_lpid', `shm_atime' are updated. + +Notes: +The `brk' value is not altered. The segment is automatically detached +when the process exits. The same segment may be attached as read-only +or read-write and more than once in the process' address space. A +shmat can succeed on a segment marked for destruction. The request for +a particular type of attach is made using the SHM_RDONLY flag. There is +no notion of a write-only attach. The requested attach permissions +must fall within those allowed by `shm_perm.mode'. + +Errors: +EACCES : Do not have permission for requested access. +EINVAL : shmid < 0 or unused, shmaddr not aligned, attach at brk failed. +EIDRM : resource was removed. +ENOMEM : Could not allocate memory for descriptor or page tables. + + +File: ipc.info, Node: shmdt, Next: shmctl, Prev: shmat, Up: Shared Memory + +shmdt +----- + + int shmdt (char *shmaddr); + + * shmaddr : attach address of segment (returned by shmat). + + * returns : 0 on success. -1 on failure. + + An attached segment is detached and `shm_nattch' decremented. The +occupied region in user space is unmapped. The segment is destroyed if +it is marked for destruction and `shm_nattch' is 0. `shm_lpid' and +`shm_dtime' are updated. + +Errors: +EINVAL : No shared memory segment attached at shmaddr. + + +File: ipc.info, Node: shmctl, Next: shmlimits, Prev: shmdt, Up: Shared Memory + +shmctl +------ + +Destroys allocated segments. Reads/Writes the control structures. + + int shmctl (int shmid, int cmd, struct shmid_ds *buf); + + * shmid : id got from call to shmget. + + * cmd : IPC_STAT, IPC_SET, IPC_RMID (*Note syscalls::). + IPC_SET : Used to set the owner uid, gid, and shm_perms.mode + field. + + IPC_RMID : The segment is marked destroyed. It is only + destroyed on the last detach. + + IPC_STAT : The shmid_ds structure is copied into the user + allocated buffer. + + * buf : used to read (IPC_STAT) or write (IPC_SET) information. + + * returns : 0 on success, -1 on failure. + + The user must execute an IPC_RMID shmctl call to free the memory +allocated by the shared segment. Otherwise all the pages faulted in +will continue to live in memory or swap. + +Errors: +EACCES : Do not have permission for requested access. +EFAULT : buf is not accessible. +EINVAL : shmid < 0 or unused. +EIDRM : identifier destroyed. +EPERM : not creator, owner or super-user (IPC_SET, IPC_RMID). + + +File: ipc.info, Node: shmlimits, Next: Notes, Prev: shmctl, Up: Shared Memory + +Limits on Shared Memory Resources +--------------------------------- + +Limits: + * SHMMNI max num of shared segments system wide ... 4096. + + * SHMMAX max shared memory segment size (bytes) ... 4M + + * SHMMIN min shared memory segment size (bytes). 1 byte (though + PAGE_SIZE is the effective minimum size). + + * SHMALL max shared mem system wide (in pages) ... policy. + + * SHMLBA segment low boundary address multiple. Must be page + aligned. SHMLBA = PAGE_SIZE. + +Unused or unimplemented: +SHMSEG : maximum number of shared segments per process. + + +File: ipc.info, Node: Notes, Next: top, Prev: shmlimits, Up: top + +Miscellaneous Notes +=================== + + The system calls are mapped into one -- `sys_ipc'. This should be +transparent to the user. + +Semaphore `undo' requests +------------------------- + + There is one sem_undo structure associated with a process for each +semaphore which was altered (with an undo request) by the process. +`sem_undo' structures are freed only when the process exits. + + One major cause for unhappiness with the undo mechanism is that it +does not fit in with the notion of having an atomic set of operations +on an array. The undo requests for an array and each semaphore therein +may have been accumulated over many `semop' calls. Thus use the undo +mechanism with private semaphores only. + + Should the process sleep in `exit' or should all undo operations be +applied with the IPC_NOWAIT flag in effect? Currently those undo +operations which go through immediately are applied and those that +require a wait are ignored silently. + +Shared memory, `malloc' and the `brk'. +-------------------------------------- + + Note that since this section was written the implementation was +changed so that non-specific attaches are done in the region 1G - 1.5G. +However much of the following is still worth thinking about so I left +it in. + + On many systems, the shared memory is allocated in a special region +of the address space ... way up somewhere. As mentioned earlier, this +implementation attaches shared segments at the lowest possible address. +Thus if you plan to use `malloc', it is wise to malloc a large space +and then proceed to attach the shared segments. This way malloc sets +the brk sufficiently above the region it will use. + + Alternatively you can use `sbrk' to adjust the `brk' value as you +make shared memory attaches. The implementation is not very smart about +selecting attach addresses. Using the system default addresses will +result in fragmentation if detaches do not occur in the reverse +sequence as attaches. + + Taking control of the matter is probably best. The rule applied is +that attaches are allowed in unmapped regions other than in the text +space (see ). Also remember that attach addresses and segment +sizes are multiples of PAGE_SIZE. + + One more trap (I quote Bruno on this). If you use malloc() to get +space for your shared memory (ie. to fix the `brk'), you must ensure you +get an unmapped address range. This means you must mallocate more memory +than you had ever allocated before. Memory returned by malloc(), used, +then freed by free() and then again returned by malloc is no good. +Neither is calloced memory. + + Note that a shared memory region remains a shared memory region until +you unmap it. Attaching a segment at the `brk' and calling malloc after +that will result in an overlap of what malloc thinks is its space with +what is really a shared memory region. For example in the case of a +read-only attach, you will not be able to write to the overlapped +portion. + +Fork, exec and exit +------------------- + + On a fork, the child inherits attached shared memory segments but +not the semaphore undo information. + + In the case of an exec, the attached shared segments are detached. +The sem undo information however remains intact. + + Upon exit, all attached shared memory segments are detached. The +adjust values in the undo structures are added to the relevant semvals +if the operations are permitted. Disallowed operations are ignored. + +Other Features +-------------- + + These features of the current implementation are likely to be +modified in the future. + + The SHM_LOCK and SHM_UNLOCK flag are available (super-user) for use +with the `shmctl' call to prevent swapping of a shared segment. The user +must fault in any pages that are required to be present after locking +is enabled. + + The IPC_INFO, MSG_STAT, MSG_INFO, SHM_STAT, SHM_INFO, SEM_STAT, +SEMINFO `ctl' calls are used by the `ipcs' program to provide +information on allocated resources. These can be modified as needed or +moved to a proc file system interface. + + Thanks to Ove Ewerlid, Bruno Haible, Ulrich Pegelow and Linus +Torvalds for ideas, tutorials, bug reports and fixes, and merriment. +And more thanks to Bruno. + + + +Tag Table: +Node: top349 +Node: Overview1049 +Node: example2881 +Node: perms4383 +Node: syscalls5943 +Node: Messages9392 +Node: msgget11426 +Node: msgsnd12810 +Node: msgrcv13778 +Node: msgctl15477 +Node: msglimits16684 +Node: Semaphores17558 +Node: semget19517 +Node: semop21060 +Node: semctl23624 +Node: semlimits25383 +Node: Shared Memory26523 +Node: shmget28008 +Node: shmat29803 +Node: shmdt31851 +Node: shmctl32383 +Node: shmlimits33514 +Node: Notes34161 + +End Tag Table diff --git a/sys-utils/ipc.texi b/sys-utils/ipc.texi new file mode 100644 index 00000000..cd912716 --- /dev/null +++ b/sys-utils/ipc.texi @@ -0,0 +1,1310 @@ +\input texinfo @c -*-texinfo-*- +@comment %**start of header (This is for running Texinfo on a region.) +@setfilename ipc.info +@settitle Inter Process Communication. +@setchapternewpage odd +@comment %**end of header (This is for running Texinfo on a region.) + +@ifinfo +This file documents the System V style inter process communication +primitives available under linux. + +Copyright @copyright{} 1992 krishna balasubramanian + +Permission is granted to use this material and the accompanying +programs within the terms of the GNU GPL. +@end ifinfo + +@titlepage +@sp 10 +@center @titlefont{System V Inter Process Communication} +@sp 2 +@center krishna balasubramanian, + +@comment The following two commands start the copyright page. +@page +@vskip 0pt plus 1filll +Copyright @copyright{} 1992 krishna balasubramanian + +Permission is granted to use this material and the accompanying +programs within the terms of the GNU GPL. +@end titlepage + + + +@node top, Overview, Notes, (dir) +@chapter System V IPC. + +These facilities are provided to maintain compatibility with +programs developed on system V unix systems and others +that rely on these system V mechanisms to accomplish inter +process communication (IPC).@refill + +The specifics described here are applicable to the Linux implementation. +Other implementations may do things slightly differently. + +@menu +* Overview:: What is system V ipc? Overall mechanisms. +* Messages:: System calls for message passing. +* Semaphores:: System calls for semaphores. +* Shared Memory:: System calls for shared memory access. +* Notes:: Miscellaneous notes. +@end menu + +@node Overview, example, top, top +@section Overview + +@noindent System V IPC consists of three mechanisms: + +@itemize @bullet +@item +Messages : exchange messages with any process or server. +@item +Semaphores : allow unrelated processes to synchronize execution. +@item +Shared memory : allow unrelated processes to share memory. +@end itemize + +@menu +* example:: Using shared memory. +* perms:: Description of access permissions. +* syscalls:: Overview of ipc system calls. +@end menu + +Access to all resources is permitted on the basis of permissions +set up when the resource was created.@refill + +A resource here consists of message queue, a semaphore set (array) +or a shared memory segment.@refill + +A resource must first be allocated by a creator before it is used. +The creator can assign a different owner. After use the resource +must be explicitly destroyed by the creator or owner.@refill + +A resource is identified by a numeric @var{id}. Typically a creator +defines a @var{key} that may be used to access the resource. The user +process may then use this @var{key} in the @dfn{get} system call to obtain +the @var{id} for the corresponding resource. This @var{id} is then used for +all further access. A library call @dfn{ftok} is provided to translate +pathnames or strings to numeric keys.@refill + +There are system and implementation defined limits on the number and +sizes of resources of any given type. Some of these are imposed by the +implementation and others by the system administrator +when configuring the kernel (@xref{msglimits}, @xref{semlimits}, +@xref{shmlimits}).@refill + +There is an @code{msqid_ds}, @code{semid_ds} or @code{shmid_ds} struct +associated with each message queue, semaphore array or shared segment. +Each ipc resource has an associated @code{ipc_perm} struct which defines +the creator, owner, access perms ..etc.., for the resource. +These structures are detailed in the following sections.@refill + + + +@node example, perms, Overview, Overview +@section example + +Here is a code fragment with pointers on how to use shared memory. The +same methods are applicable to other resources.@refill + +In a typical access sequence the creator allocates a new instance +of the resource with the @code{get} system call using the IPC_CREAT +flag.@refill + +@noindent creator process:@* + +@example +#include +int id; +key_t key; +char proc_id = 'C'; +int size = 0x5000; /* 20 K */ +int flags = 0664 | IPC_CREAT; /* read-only for others */ + +key = ftok ("~creator/ipckey", proc_id); +id = shmget (key, size, flags); +exit (0); /* quit leaving resource allocated */ +@end example + +@noindent +Users then gain access to the resource using the same key.@* +@noindent +Client process: +@example +#include +char *shmaddr; +int id; +key_t key; +char proc_id = 'C'; + +key = ftok ("~creator/ipckey", proc_id); + +id = shmget (key, 0, 004); /* default size */ +if (id == -1) + perror ("shmget ..."); + +shmaddr = shmat (id, 0, SHM_RDONLY); /* attach segment for reading */ +if (shmaddr == (char *) -1) + perror ("shmat ..."); + +local_var = *(shmaddr + 3); /* read segment etc. */ + +shmdt (shmaddr); /* detach segment */ +@end example + +@noindent +When the resource is no longer needed the creator should remove it.@* +@noindent +Creator/owner process 2: +@example +key = ftok ("~creator/ipckey", proc_id) +id = shmget (key, 0, 0); +shmctl (id, IPC_RMID, NULL); +@end example + + +@node perms, syscalls, example, Overview +@section Permissions + +Each resource has an associated @code{ipc_perm} struct which defines the +creator, owner and access perms for the resource.@refill + +@example +struct ipc_perm + key_t key; /* set by creator */ + ushort uid; /* owner euid and egid */ + ushort gid; + ushort cuid; /* creator euid and egid */ + ushort cgid; + ushort mode; /* access modes in lower 9 bits */ + ushort seq; /* sequence number */ +@end example + +The creating process is the default owner. The owner can be reassigned +by the creator and has creator perms. Only the owner, creator or super-user +can delete the resource.@refill + +The lowest nine bits of the flags parameter supplied by the user to the +system call are compared with the values stored in @code{ipc_perms.mode} +to determine if the requested access is allowed. In the case +that the system call creates the resource, these bits are initialized +from the user supplied value.@refill + +As for files, access permissions are specified as read, write and exec +for user, group or other (though the exec perms are unused). For example +0624 grants read-write to owner, write-only to group and read-only +access to others.@refill + +For shared memory, note that read-write access for segments is determined +by a separate flag which is not stored in the @code{mode} field. +Shared memory segments attached with write access can be read.@refill + +The @code{cuid}, @code{cgid}, @code{key} and @code{seq} fields +cannot be changed by the user.@refill + + + +@node syscalls, Messages, perms, Overview +@section IPC system calls + +This section provides an overview of the IPC system calls. See the +specific sections on each type of resource for details.@refill + +Each type of mechanism provides a @dfn{get}, @dfn{ctl} and one or more +@dfn{op} system calls that allow the user to create or procure the +resource (get), define its behaviour or destroy it (ctl) and manipulate +the resources (op).@refill + + + +@subsection The @dfn{get} system calls + +The @code{get} call typically takes a @var{key} and returns a numeric +@var{id} that is used for further access. +The @var{id} is an index into the resource table. A sequence +number is maintained and incremented when a resource is +destroyed so that acceses using an obselete @var{id} is likely to fail.@refill + +The user also specifies the permissions and other behaviour +charecteristics for the current access. The flags are or-ed with the +permissions when invoking system calls as in:@refill +@example +msgflg = IPC_CREAT | IPC_EXCL | 0666; +id = msgget (key, msgflg); +@end example +@itemize @bullet +@item +@code{key} : IPC_PRIVATE => new instance of resource is initialized. +@item +@code{flags} : +@itemize @asis +@item +IPC_CREAT : resource created for @var{key} if it does not exist. +@item +IPC_CREAT | IPC_EXCL : fail if resource exists for @var{key}. +@end itemize +@item +returns : an identifier used for all further access to the resource. +@end itemize + +Note that IPC_PRIVATE is not a flag but a special @code{key} +that ensures (when the call is successful) that a new resource is +created.@refill + +Use of IPC_PRIVATE does not make the resource inaccessible to other +users. For this you must set the access permissions appropriately.@refill + +There is currently no way for a process to ensure exclusive access to a +resource. IPC_CREAT | IPC_EXCL only ensures (on success) that a new +resource was initialized. It does not imply exclusive access.@refill + +@noindent +See Also : @xref{msgget}, @xref{semget}, @xref{shmget}.@refill + + + +@subsection The @dfn{ctl} system calls + +Provides or alters the information stored in the structure that describes +the resource indexed by @var{id}.@refill + +@example +#include +struct msqid_ds buf; +err = msgctl (id, IPC_STAT, &buf); +if (err) + !$#%* +else + printf ("creator uid = %d\n", buf.msg_perm.cuid); + .... +@end example + +@noindent +Commands supported by all @code{ctl} calls:@* +@itemize @bullet +@item +IPC_STAT : read info on resource specified by id into user allocated +buffer. The user must have read access to the resource.@refill +@item +IPC_SET : write info from buffer into resource data structure. The +user must be owner creator or super-user.@refill +@item +IPC_RMID : remove resource. The user must be the owner, creator or +super-user.@refill +@end itemize + +The IPC_RMID command results in immediate removal of a message +queue or semaphore array. Shared memory segments however, are +only destroyed upon the last detach after IPC_RMID is executed.@refill + +The @code{semctl} call provides a number of command options that allow +the user to determine or set the values of the semaphores in an array.@refill + +@noindent +See Also: @xref{msgctl}, @xref{semctl}, @xref{shmctl}.@refill + + +@subsection The @dfn{op} system calls + +Used to send or receive messages, read or alter semaphore values, +attach or detach shared memory segments. +The IPC_NOWAIT flag will cause the operation to fail with error EAGAIN +if the process has to wait on the call.@refill + +@noindent +@code{flags} : IPC_NOWAIT => return with error if a wait is required. + +@noindent +See Also: @xref{msgsnd},@xref{msgrcv},@xref{semop},@xref{shmat}, +@xref{shmdt}.@refill + + + +@node Messages, msgget, syscalls, top +@section Messages + +A message resource is described by a struct @code{msqid_ds} which is +allocated and initialized when the resource is created. Some fields +in @code{msqid_ds} can then be altered (if desired) by invoking @code{msgctl}. +The memory used by the resource is released when it is destroyed by +a @code{msgctl} call.@refill + +@example +struct msqid_ds + struct ipc_perm msg_perm; + struct msg *msg_first; /* first message on queue (internal) */ + struct msg *msg_last; /* last message in queue (internal) */ + time_t msg_stime; /* last msgsnd time */ + time_t msg_rtime; /* last msgrcv time */ + time_t msg_ctime; /* last change time */ + struct wait_queue *wwait; /* writers waiting (internal) */ + struct wait_queue *rwait; /* readers waiting (internal) */ + ushort msg_cbytes; /* number of bytes used on queue */ + ushort msg_qnum; /* number of messages in queue */ + ushort msg_qbytes; /* max number of bytes on queue */ + ushort msg_lspid; /* pid of last msgsnd */ + ushort msg_lrpid; /* pid of last msgrcv */ +@end example + +To send or receive a message the user allocates a structure that looks +like a @code{msgbuf} but with an array @code{mtext} of the required size. +Messages have a type (positive integer) associated with them so that +(for example) a listener can choose to receive only messages of a +given type.@refill + +@example +struct msgbuf + long mtype; type of message (@xref{msgrcv}). + char mtext[1]; message text .. why is this not a ptr? +@end example + +The user must have write permissions to send and read permissions +to receive messages on a queue.@refill + +When @code{msgsnd} is invoked, the user's message is copied into +an internal struct @code{msg} and added to the queue. A @code{msgrcv} +will then read this message and free the associated struct @code{msg}.@refill + + +@menu +* msgget:: +* msgsnd:: +* msgrcv:: +* msgctl:: +* msglimits:: Implementation defined limits. +@end menu + + +@node msgget, msgsnd, Messages, Messages +@subsection msgget + +@noindent +A message queue is allocated by a msgget system call : + +@example +msqid = msgget (key_t key, int msgflg); +@end example + +@itemize @bullet +@item +@code{key}: an integer usually got from @code{ftok()} or IPC_PRIVATE.@refill +@item +@code{msgflg}: +@itemize @asis +@item +IPC_CREAT : used to create a new resource if it does not already exist. +@item +IPC_EXCL | IPC_CREAT : used to ensure failure of the call if the +resource already exists.@refill +@item +rwxrwxrwx : access permissions. +@end itemize +@item +returns: msqid (an integer used for all further access) on success. +-1 on failure.@refill +@end itemize + +A message queue is allocated if there is no resource corresponding +to the given key. The access permissions specified are then copied +into the @code{msg_perm} struct and the fields in @code{msqid_ds} +initialized. The user must use the IPC_CREAT flag or key = IPC_PRIVATE, +if a new instance is to be allocated. If a resource corresponding to +@var{key} already exists, the access permissions are verified.@refill + +@noindent +Errors:@* +@noindent +EACCES : (procure) Do not have permission for requested access.@* +@noindent +EEXIST : (allocate) IPC_CREAT | IPC_EXCL specified and resource exists.@* +@noindent +EIDRM : (procure) The resource was removed.@* +@noindent +ENOSPC : All id's are taken (max of MSGMNI id's system-wide).@* +@noindent +ENOENT : Resource does not exist and IPC_CREAT not specified.@* +@noindent +ENOMEM : A new @code{msqid_ds} was to be created but ... nomem. + + + + +@node msgsnd, msgrcv, msgget, Messages +@subsection msgsnd + +@example +int msgsnd (int msqid, struct msgbuf *msgp, int msgsz, int msgflg); +@end example + +@itemize @bullet +@item +@code{msqid} : id obtained by a call to msgget. +@item +@code{msgsz} : size of msg text (@code{mtext}) in bytes. +@item +@code{msgp} : message to be sent. (msgp->mtype must be positive). +@item +@code{msgflg} : IPC_NOWAIT. +@item +returns : msgsz on success. -1 on error. +@end itemize + +The message text and type are stored in the internal @code{msg} +structure. @code{msg_cbytes}, @code{msg_qnum}, @code{msg_lspid}, +and @code{msg_stime} fields are updated. Readers waiting on the +queue are awakened.@refill + +@noindent +Errors:@* +@noindent +EACCES : Do not have write permission on queue.@* +@noindent +EAGAIN : IPC_NOWAIT specified and queue is full.@* +@noindent +EFAULT : msgp not accessible.@* +@noindent +EIDRM : The message queue was removed.@* +@noindent +EINTR : Full queue ... would have slept but ... was interrupted.@* +@noindent +EINVAL : mtype < 1, msgsz > MSGMAX, msgsz < 0, msqid < 0 or unused.@* +@noindent +ENOMEM : Could not allocate space for header and text.@* + + + +@node msgrcv, msgctl, msgsnd, Messages +@subsection msgrcv + +@example +int msgrcv (int msqid, struct msgbuf *msgp, int msgsz, long msgtyp, + int msgflg); +@end example + +@itemize @bullet +@item +msqid : id obtained by a call to msgget. +@item +msgsz : maximum size of message to receive. +@item +msgp : allocated by user to store the message in. +@item +msgtyp : +@itemize @asis +@item +0 => get first message on queue. +@item +> 0 => get first message of matching type. +@item +< 0 => get message with least type which is <= abs(msgtyp). +@end itemize +@item +msgflg : +@itemize @asis +@item +IPC_NOWAIT : Return immediately if message not found. +@item +MSG_NOERROR : The message is truncated if it is larger than msgsz. +@item +MSG_EXCEPT : Used with msgtyp > 0 to receive any msg except of specified +type.@refill +@end itemize +@item +returns : size of message if found. -1 on error. +@end itemize + +The first message that meets the @code{msgtyp} specification is +identified. For msgtyp < 0, the entire queue is searched for the +message with the smallest type.@refill + +If its length is smaller than msgsz or if the user specified the +MSG_NOERROR flag, its text and type are copied to msgp->mtext and +msgp->mtype, and it is taken off the queue.@refill + +The @code{msg_cbytes}, @code{msg_qnum}, @code{msg_lrpid}, +and @code{msg_rtime} fields are updated. Writers waiting on the +queue are awakened.@refill + +@noindent +Errors:@* +@noindent +E2BIG : msg bigger than msgsz and MSG_NOERROR not specified.@* +@noindent +EACCES : Do not have permission for reading the queue.@* +@noindent +EFAULT : msgp not accessible.@* +@noindent +EIDRM : msg queue was removed.@* +@noindent +EINTR : msg not found ... would have slept but ... was interrupted.@* +@noindent +EINVAL : msgsz > msgmax or msgsz < 0, msqid < 0 or unused.@* +@noindent +ENOMSG : msg of requested type not found and IPC_NOWAIT specified. + + + +@node msgctl, msglimits, msgrcv, Messages +@subsection msgctl + +@example +int msgctl (int msqid, int cmd, struct msqid_ds *buf); +@end example + +@itemize @bullet +@item +msqid : id obtained by a call to msgget. +@item +buf : allocated by user for reading/writing info. +@item +cmd : IPC_STAT, IPC_SET, IPC_RMID (@xref{syscalls}). +@end itemize + +IPC_STAT results in the copy of the queue data structure +into the user supplied buffer.@refill + +In the case of IPC_SET, the queue size (@code{msg_qbytes}) +and the @code{uid}, @code{gid}, @code{mode} (low 9 bits) fields +of the @code{msg_perm} struct are set from the user supplied values. +@code{msg_ctime} is updated.@refill + +Note that only the super user may increase the limit on the size of a +message queue beyond MSGMNB.@refill + +When the queue is destroyed (IPC_RMID), the sequence number is +incremented and all waiting readers and writers are awakened. +These processes will then return with @code{errno} set to EIDRM.@refill + +@noindent +Errors: +@noindent +EPERM : Insufficient privilege to increase the size of the queue (IPC_SET) +or remove it (IPC_RMID).@* +@noindent +EACCES : Do not have permission for reading the queue (IPC_STAT).@* +@noindent +EFAULT : buf not accessible (IPC_STAT, IPC_SET).@* +@noindent +EIDRM : msg queue was removed.@* +@noindent +EINVAL : invalid cmd, msqid < 0 or unused. + + +@node msglimits, Semaphores, msgctl, Messages +@subsection Limis on Message Resources + +@noindent +Sizeof various structures: +@itemize @asis +@item +msqid_ds 52 /* 1 per message queue .. dynamic */ +@item +msg 16 /* 1 for each message in system .. dynamic */ +@item +msgbuf 8 /* allocated by user */ +@end itemize + +@noindent +Limits +@itemize @bullet +@item +MSGMNI : number of message queue identifiers ... policy. +@item +MSGMAX : max size of message. +Header and message space allocated on one page. +MSGMAX = (PAGE_SIZE - sizeof(struct msg)). +Implementation maximum MSGMAX = 4080.@refill +@item +MSGMNB : default max size of a message queue ... policy. +The super-user can increase the size of a +queue beyond MSGMNB by a @code{msgctl} call.@refill +@end itemize + +@noindent +Unused or unimplemented:@* +MSGTQL max number of message headers system-wide.@* +MSGPOOL total size in bytes of msg pool. + + + +@node Semaphores, semget, msglimits, top +@section Semaphores + +Each semaphore has a value >= 0. An id provides access to an array +of @code{nsems} semaphores. Operations such as read, increment or decrement +semaphores in a set are performed by the @code{semop} call which processes +@code{nsops} operations at a time. Each operation is specified in a struct +@code{sembuf} described below. The operations are applied only if all of +them succeed.@refill + +If you do not have a need for such arrays, you are probably better off using +the @code{test_bit}, @code{set_bit} and @code{clear_bit} bit-operations +defined in .@refill + +Semaphore operations may also be qualified by a SEM_UNDO flag which +results in the operation being undone when the process exits.@refill + +If a decrement cannot go through, a process will be put to sleep +on a queue waiting for the @code{semval} to increase unless it specifies +IPC_NOWAIT. A read operation can similarly result in a sleep on a +queue waiting for @code{semval} to become 0. (Actually there are +two queues per semaphore array).@refill + +@noindent +A semaphore array is described by: +@example +struct semid_ds + struct ipc_perm sem_perm; + time_t sem_otime; /* last semop time */ + time_t sem_ctime; /* last change time */ + struct wait_queue *eventn; /* wait for a semval to increase */ + struct wait_queue *eventz; /* wait for a semval to become 0 */ + struct sem_undo *undo; /* undo entries */ + ushort sem_nsems; /* no. of semaphores in array */ +@end example + +@noindent +Each semaphore is described internally by : +@example +struct sem + short sempid; /* pid of last semop() */ + ushort semval; /* current value */ + ushort semncnt; /* num procs awaiting increase in semval */ + ushort semzcnt; /* num procs awaiting semval = 0 */ +@end example + +@menu +* semget:: +* semop:: +* semctl:: +* semlimits:: Limits imposed by this implementation. +@end menu + +@node semget, semop, Semaphores, Semaphores +@subsection semget + +@noindent +A semaphore array is allocated by a semget system call: + +@example +semid = semget (key_t key, int nsems, int semflg); +@end example + +@itemize @bullet +@item +@code{key} : an integer usually got from @code{ftok} or IPC_PRIVATE +@item +@code{nsems} : +@itemize @asis +@item +# of semaphores in array (0 <= nsems <= SEMMSL <= SEMMNS) +@item +0 => dont care can be used when not creating the resource. +If successful you always get access to the entire array anyway.@refill +@end itemize +@item +semflg : +@itemize @asis +@item +IPC_CREAT used to create a new resource +@item +IPC_EXCL used with IPC_CREAT to ensure failure if the resource exists. +@item +rwxrwxrwx access permissions. +@end itemize +@item +returns : semid on success. -1 on failure. +@end itemize + +An array of nsems semaphores is allocated if there is no resource +corresponding to the given key. The access permissions specified are +then copied into the @code{sem_perm} struct for the array along with the +user-id etc. The user must use the IPC_CREAT flag or key = IPC_PRIVATE +if a new resource is to be created.@refill + +@noindent +Errors:@* +@noindent +EINVAL : nsems not in above range (allocate).@* + nsems greater than number in array (procure).@* +@noindent +EEXIST : (allocate) IPC_CREAT | IPC_EXCL specified and resource exists.@* +@noindent +EIDRM : (procure) The resource was removed.@* +@noindent +ENOMEM : could not allocate space for semaphore array.@* +@noindent +ENOSPC : No arrays available (SEMMNI), too few semaphores available (SEMMNS).@* +@noindent +ENOENT : Resource does not exist and IPC_CREAT not specified.@* +@noindent +EACCES : (procure) do not have permission for specified access. + + +@node semop, semctl, semget, Semaphores +@subsection semop + +@noindent +Operations on semaphore arrays are performed by calling semop : + +@example +int semop (int semid, struct sembuf *sops, unsigned nsops); +@end example +@itemize @bullet +@item +semid : id obtained by a call to semget. +@item +sops : array of semaphore operations. +@item +nsops : number of operations in array (0 < nsops < SEMOPM). +@item +returns : semval for last operation. -1 on failure. +@end itemize + +@noindent +Operations are described by a structure sembuf: +@example +struct sembuf + ushort sem_num; /* semaphore index in array */ + short sem_op; /* semaphore operation */ + short sem_flg; /* operation flags */ +@end example + +The value @code{sem_op} is to be added (signed) to the current value semval +of the semaphore with index sem_num (0 .. nsems -1) in the set. +Flags recognized in sem_flg are IPC_NOWAIT and SEM_UNDO.@refill + +@noindent +Two kinds of operations can result in wait: +@enumerate +@item +If sem_op is 0 (read operation) and semval is non-zero, the process +sleeps on a queue waiting for semval to become zero or returns with +error EAGAIN if (IPC_NOWAIT | sem_flg) is true.@refill +@item +If (sem_op < 0) and (semval + sem_op < 0), the process either sleeps +on a queue waiting for semval to increase or returns with error EAGAIN if +(sem_flg & IPC_NOWAIT) is true.@refill +@end enumerate + +The array sops is first read in and preliminary checks performed on +the arguments. The operations are parsed to determine if any of +them needs write permissions or requests an undo operation.@refill + +The operations are then tried and the process sleeps if any operation +that does not specify IPC_NOWAIT cannot go through. If a process sleeps +it repeats these checks on waking up. If any operation that requests +IPC_NOWAIT, cannot go through at any stage, the call returns with errno +set to EAGAIN.@refill + +Finally, operations are committed when all go through without an intervening +sleep. Processes waiting on the zero_queue or increment_queue are awakened +if any of the semval's becomes zero or is incremented respectively.@refill + +@noindent +Errors:@* +@noindent +E2BIG : nsops > SEMOPM.@* +@noindent +EACCES : Do not have permission for requested (read/alter) access.@* +@noindent +EAGAIN : An operation with IPC_NOWAIT specified could not go through.@* +@noindent +EFAULT : The array sops is not accessible.@* +@noindent +EFBIG : An operation had semnum >= nsems.@* +@noindent +EIDRM : The resource was removed.@* +@noindent +EINTR : The process was interrupted on its way to a wait queue.@* +@noindent +EINVAL : nsops is 0, semid < 0 or unused.@* +@noindent +ENOMEM : SEM_UNDO requested. Could not allocate space for undo structure.@* +@noindent +ERANGE : sem_op + semval > SEMVMX for some operation. + + +@node semctl, semlimits, semop, Semaphores +@subsection semctl + +@example +int semctl (int semid, int semnum, int cmd, union semun arg); +@end example + +@itemize @bullet +@item +semid : id obtained by a call to semget. +@item +cmd : +@itemize @asis +@item +GETPID return pid for the process that executed the last semop. +@item +GETVAL return semval of semaphore with index semnum. +@item +GETNCNT return number of processes waiting for semval to increase. +@item +GETZCNT return number of processes waiting for semval to become 0 +@item +SETVAL set semval = arg.val. +@item +GETALL read all semval's into arg.array. +@item +SETALL set all semval's with values given in arg.array. +@end itemize +@item +returns : 0 on success or as given above. -1 on failure. +@end itemize + +The first 4 operate on the semaphore with index semnum in the set. +The last two operate on all semaphores in the set.@refill + +@code{arg} is a union : +@example +union semun + int val; value for SETVAL. + struct semid_ds *buf; buffer for IPC_STAT and IPC_SET. + ushort *array; array for GETALL and SETALL +@end example + +@itemize @bullet +@item +IPC_SET, SETVAL, SETALL : sem_ctime is updated. +@item +SETVAL, SETALL : Undo entries are cleared for altered semaphores in +all processes. Processes sleeping on the wait queues are +awakened if a semval becomes 0 or increases.@refill +@item +IPC_SET : sem_perm.uid, sem_perm.gid, sem_perm.mode are updated from +user supplied values.@refill +@end itemize + +@noindent +Errors: +@noindent +EACCES : do not have permission for specified access.@* +@noindent +EFAULT : arg is not accessible.@* +@noindent +EIDRM : The resource was removed.@* +@noindent +EINVAL : semid < 0 or semnum < 0 or semnum >= nsems.@* +@noindent +EPERM : IPC_RMID, IPC_SET ... not creator, owner or super-user.@* +@noindent +ERANGE : arg.array[i].semval > SEMVMX or < 0 for some i. + + + + +@node semlimits, Shared Memory, semctl, Semaphores +@subsection Limits on Semaphore Resources + +@noindent +Sizeof various structures: +@example +semid_ds 44 /* 1 per semaphore array .. dynamic */ +sem 8 /* 1 for each semaphore in system .. dynamic */ +sembuf 6 /* allocated by user */ +sem_undo 20 /* 1 for each undo request .. dynamic */ +@end example + +@noindent +Limits :@* +@itemize @bullet +@item +SEMVMX 32767 semaphore maximum value (short). +@item +SEMMNI number of semaphore identifiers (or arrays) system wide...policy. +@item +SEMMSL maximum number of semaphores per id. +1 semid_ds per array, 1 struct sem per semaphore +=> SEMMSL = (PAGE_SIZE - sizeof(semid_ds)) / sizeof(sem). +Implementation maximum SEMMSL = 500.@refill +@item +SEMMNS maximum number of semaphores system wide ... policy. +Setting SEMMNS >= SEMMSL*SEMMNI makes it irrelevent.@refill +@item +SEMOPM Maximum number of operations in one semop call...policy. +@end itemize + +@noindent +Unused or unimplemented:@* +@noindent +SEMAEM adjust on exit max value.@* +@noindent +SEMMNU number of undo structures system-wide.@* +@noindent +SEMUME maximum number of undo entries per process. + + + +@node Shared Memory, shmget, semlimits, top +@section Shared Memory + +Shared memory is distinct from the sharing of read-only code pages or +the sharing of unaltered data pages that is available due to the +copy-on-write mechanism. The essential difference is that the +shared pages are dirty (in the case of Shared memory) and can be +made to appear at a convenient location in the process' address space.@refill + +@noindent +A shared segment is described by : +@example +struct shmid_ds + struct ipc_perm shm_perm; + int shm_segsz; /* size of segment (bytes) */ + time_t shm_atime; /* last attach time */ + time_t shm_dtime; /* last detach time */ + time_t shm_ctime; /* last change time */ + ulong *shm_pages; /* internal page table */ + ushort shm_cpid; /* pid, creator */ + ushort shm_lpid; /* pid, last operation */ + short shm_nattch; /* no. of current attaches */ +@end example + +A shmget allocates a shmid_ds and an internal page table. A shmat +maps the segment into the process' address space with pointers +into the internal page table and the actual pages are faulted in +as needed. The memory associated with the segment must be explicitly +destroyed by calling shmctl with IPC_RMID.@refill + +@menu +* shmget:: +* shmat:: +* shmdt:: +* shmctl:: +* shmlimits:: Limits imposed by this implementation. +@end menu + + +@node shmget, shmat, Shared Memory, Shared Memory +@subsection shmget + +@noindent +A shared memory segment is allocated by a shmget system call: + +@example +int shmget(key_t key, int size, int shmflg); +@end example + +@itemize @bullet +@item +key : an integer usually got from @code{ftok} or IPC_PRIVATE +@item +size : size of the segment in bytes (SHMMIN <= size <= SHMMAX). +@item +shmflg : +@itemize @asis +@item +IPC_CREAT used to create a new resource +@item +IPC_EXCL used with IPC_CREAT to ensure failure if the resource exists. +@item +rwxrwxrwx access permissions. +@end itemize +@item +returns : shmid on success. -1 on failure. +@end itemize + +A descriptor for a shared memory segment is allocated if there isn't one +corresponding to the given key. The access permissions specified are +then copied into the @code{shm_perm} struct for the segment along with the +user-id etc. The user must use the IPC_CREAT flag or key = IPC_PRIVATE +to allocate a new segment.@refill + +If the segment already exists, the access permissions are verified, +and a check is made to see that it is not marked for destruction.@refill + +@code{size} is effectively rounded up to a multiple of PAGE_SIZE as shared +memory is allocated in pages.@refill + +@noindent +Errors:@* +@noindent +EINVAL : (allocate) Size not in range specified above.@* + (procure) Size greater than size of segment.@* +@noindent +EEXIST : (allocate) IPC_CREAT | IPC_EXCL specified and resource exists.@* +@noindent +EIDRM : (procure) The resource is marked destroyed or was removed.@* +@noindent +ENOSPC : (allocate) All id's are taken (max of SHMMNI id's system-wide). +Allocating a segment of the requested size would exceed the +system wide limit on total shared memory (SHMALL).@refill +@* +@noindent +ENOENT : (procure) Resource does not exist and IPC_CREAT not specified.@* +@noindent +EACCES : (procure) Do not have permission for specified access.@* +@noindent +ENOMEM : (allocate) Could not allocate memory for shmid_ds or pg_table. + + + +@node shmat, shmdt, shmget, Shared Memory +@subsection shmat + +@noindent +Maps a shared segment into the process' address space. + +@example +char *virt_addr; +virt_addr = shmat (int shmid, char *shmaddr, int shmflg); +@end example + +@itemize @bullet +@item +shmid : id got from call to shmget. +@item +shmaddr : requested attach address.@* + If shmaddr is 0 the system finds an unmapped region.@* + If a non-zero value is indicated the value must be page + aligned or the user must specify the SHM_RND flag.@refill +@item +shmflg :@* + SHM_RDONLY : request read-only attach.@* + SHM_RND : attach address is rounded DOWN to a multiple of SHMLBA. +@item +returns: virtual address of attached segment. -1 on failure. +@end itemize + +When shmaddr is 0, the attach address is determined by finding an +unmapped region in the address range 1G to 1.5G, starting at 1.5G +and coming down from there. The algorithm is very simple so you +are encouraged to avoid non-specific attaches. + +@noindent +Algorithm: +@display +Determine attach address as described above. +Check region (shmaddr, shmaddr + size) is not mapped and allocate + page tables (undocumented SHM_REMAP flag!). +Map the region by setting up pointers into the internal page table. +Add a descriptor for the attach to the task struct for the process. +@code{shm_nattch}, @code{shm_lpid}, @code{shm_atime} are updated. +@end display + +@noindent +Notes:@* +The @code{brk} value is not altered. +The segment is automatically detached when the process exits. +The same segment may be attached as read-only or read-write and + more than once in the process' address space. +A shmat can succeed on a segment marked for destruction. +The request for a particular type of attach is made using the SHM_RDONLY flag. +There is no notion of a write-only attach. The requested attach + permissions must fall within those allowed by @code{shm_perm.mode}. + +@noindent +Errors:@* +@noindent +EACCES : Do not have permission for requested access.@* +@noindent +EINVAL : shmid < 0 or unused, shmaddr not aligned, attach at brk failed.@* +@noindent +EIDRM : resource was removed.@* +@noindent +ENOMEM : Could not allocate memory for descriptor or page tables. + + +@node shmdt, shmctl, shmat, Shared Memory +@subsection shmdt + +@example +int shmdt (char *shmaddr); +@end example + +@itemize @bullet +@item +shmaddr : attach address of segment (returned by shmat). +@item +returns : 0 on success. -1 on failure. +@end itemize + +An attached segment is detached and @code{shm_nattch} decremented. The +occupied region in user space is unmapped. The segment is destroyed +if it is marked for destruction and @code{shm_nattch} is 0. +@code{shm_lpid} and @code{shm_dtime} are updated.@refill + +@noindent +Errors:@* +@noindent +EINVAL : No shared memory segment attached at shmaddr. + + +@node shmctl, shmlimits, shmdt, Shared Memory +@subsection shmctl + +@noindent +Destroys allocated segments. Reads/Writes the control structures. + +@example +int shmctl (int shmid, int cmd, struct shmid_ds *buf); +@end example + +@itemize @bullet +@item +shmid : id got from call to shmget. +@item +cmd : IPC_STAT, IPC_SET, IPC_RMID (@xref{syscalls}). +@itemize @asis +@item +IPC_SET : Used to set the owner uid, gid, and shm_perms.mode field. +@item +IPC_RMID : The segment is marked destroyed. It is only destroyed +on the last detach.@refill +@item +IPC_STAT : The shmid_ds structure is copied into the user allocated buffer. +@end itemize +@item +buf : used to read (IPC_STAT) or write (IPC_SET) information. +@item +returns : 0 on success, -1 on failure. +@end itemize + +The user must execute an IPC_RMID shmctl call to free the memory +allocated by the shared segment. Otherwise all the pages faulted in +will continue to live in memory or swap.@refill + +@noindent +Errors:@* +@noindent +EACCES : Do not have permission for requested access.@* +@noindent +EFAULT : buf is not accessible.@* +@noindent +EINVAL : shmid < 0 or unused.@* +@noindent +EIDRM : identifier destroyed.@* +@noindent +EPERM : not creator, owner or super-user (IPC_SET, IPC_RMID). + + +@node shmlimits, Notes, shmctl, Shared Memory +@subsection Limits on Shared Memory Resources + +@noindent +Limits: +@itemize @bullet +@item +SHMMNI max num of shared segments system wide ... 4096. +@item +SHMMAX max shared memory segment size (bytes) ... 4M +@item +SHMMIN min shared memory segment size (bytes). +1 byte (though PAGE_SIZE is the effective minimum size).@refill +@item +SHMALL max shared mem system wide (in pages) ... policy. +@item +SHMLBA segment low boundary address multiple. +Must be page aligned. SHMLBA = PAGE_SIZE.@refill +@end itemize +@noindent +Unused or unimplemented:@* +SHMSEG : maximum number of shared segments per process. + + + +@node Notes, top, shmlimits, top +@section Miscellaneous Notes + +The system calls are mapped into one -- @code{sys_ipc}. This should be +transparent to the user.@refill + +@subsection Semaphore @code{undo} requests + +There is one sem_undo structure associated with a process for +each semaphore which was altered (with an undo request) by the process. +@code{sem_undo} structures are freed only when the process exits. + +One major cause for unhappiness with the undo mechanism is that +it does not fit in with the notion of having an atomic set of +operations on an array. The undo requests for an array and each +semaphore therein may have been accumulated over many @code{semop} +calls. Thus use the undo mechanism with private semaphores only.@refill + +Should the process sleep in @code{exit} or should all undo +operations be applied with the IPC_NOWAIT flag in effect? +Currently those undo operations which go through immediately are +applied and those that require a wait are ignored silently.@refill + +@subsection Shared memory, @code{malloc} and the @code{brk}. +Note that since this section was written the implementation was +changed so that non-specific attaches are done in the region +1G - 1.5G. However much of the following is still worth thinking +about so I left it in. + +On many systems, the shared memory is allocated in a special region +of the address space ... way up somewhere. As mentioned earlier, +this implementation attaches shared segments at the lowest possible +address. Thus if you plan to use @code{malloc}, it is wise to malloc a +large space and then proceed to attach the shared segments. This way +malloc sets the brk sufficiently above the region it will use.@refill + +Alternatively you can use @code{sbrk} to adjust the @code{brk} value +as you make shared memory attaches. The implementation is not very +smart about selecting attach addresses. Using the system default +addresses will result in fragmentation if detaches do not occur +in the reverse sequence as attaches.@refill + +Taking control of the matter is probably best. The rule applied +is that attaches are allowed in unmapped regions other than +in the text space (see ). Also remember that attach addresses +and segment sizes are multiples of PAGE_SIZE.@refill + +One more trap (I quote Bruno on this). If you use malloc() to get space +for your shared memory (ie. to fix the @code{brk}), you must ensure you +get an unmapped address range. This means you must mallocate more memory +than you had ever allocated before. Memory returned by malloc(), used, +then freed by free() and then again returned by malloc is no good. +Neither is calloced memory.@refill + +Note that a shared memory region remains a shared memory region until +you unmap it. Attaching a segment at the @code{brk} and calling malloc +after that will result in an overlap of what malloc thinks is its +space with what is really a shared memory region. For example in the case +of a read-only attach, you will not be able to write to the overlapped +portion.@refill + + +@subsection Fork, exec and exit + +On a fork, the child inherits attached shared memory segments but +not the semaphore undo information.@refill + +In the case of an exec, the attached shared segments are detached. +The sem undo information however remains intact.@refill + +Upon exit, all attached shared memory segments are detached. +The adjust values in the undo structures are added to the relevant semvals +if the operations are permitted. Disallowed operations are ignored.@refill + + +@subsection Other Features + +These features of the current implementation are +likely to be modified in the future. + +The SHM_LOCK and SHM_UNLOCK flag are available (super-user) for use with the +@code{shmctl} call to prevent swapping of a shared segment. The user +must fault in any pages that are required to be present after locking +is enabled. + +The IPC_INFO, MSG_STAT, MSG_INFO, SHM_STAT, SHM_INFO, SEM_STAT, SEMINFO +@code{ctl} calls are used by the @code{ipcs} program to provide information +on allocated resources. These can be modified as needed or moved to a proc +file system interface. + + +@sp 3 +Thanks to Ove Ewerlid, Bruno Haible, Ulrich Pegelow and Linus Torvalds +for ideas, tutorials, bug reports and fixes, and merriment. And more +thanks to Bruno. + + +@contents +@bye + diff --git a/sys-utils/ipcrm.8 b/sys-utils/ipcrm.8 new file mode 100644 index 00000000..ef2facb9 --- /dev/null +++ b/sys-utils/ipcrm.8 @@ -0,0 +1,15 @@ +.\" Copyright 1993 Rickard E. Faith (faith@cs.unc.edu) +.\" May be distributed under the GNU General Public License +.TH IPCRM 8 "9 October 1993" "Linux 0.99" "Linux Programmer's Manual" +.SH NAME +ipcs \- provide information on ipc facilities +.SH SYNOPSIS +.BI "ipcrm [ shm | msg | sem ] " id +.SH DESCRIPTION +.B ipcrm +will remove the resource speccified by +.IR id . +.SH SEE ALSO +.BR ipcs (8) +.SH AUTHOR +krishna balasubramanian (balasub@cis.ohio-state.edu) diff --git a/sys-utils/ipcrm.c b/sys-utils/ipcrm.c new file mode 100644 index 00000000..7273b341 --- /dev/null +++ b/sys-utils/ipcrm.c @@ -0,0 +1,50 @@ +/* + * krishna balasubramanian 1993 + */ + +#include +#include +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + int id; + union semun arg; + + arg.val = 0; + + if (argc != 3 || strlen(argv[1]) < 3) { + printf ("usage: %s [shm | msg | sem] id\n", argv[0]); + exit (1); + } + id = atoi (argv[2]); + switch (argv[1][1]) { + case 'h': + if (!shmctl (id, IPC_RMID, NULL)) + break; + perror ("shmctl "); + exit (1); + + case 'e': + if (!semctl (id, 0, IPC_RMID, arg)) + break; + perror ("semctl "); + exit (1); + + case 's': + if (!msgctl (id, IPC_RMID, NULL)) + break; + perror ("msgctl "); + exit (1); + + default: + printf ("usage: %s [-shm | -msg | -sem] id\n", argv[0]); + exit (1); + } + printf ("resource deleted\n"); + return 0; +} + diff --git a/sys-utils/ipcs.8 b/sys-utils/ipcs.8 new file mode 100644 index 00000000..97fddf51 --- /dev/null +++ b/sys-utils/ipcs.8 @@ -0,0 +1,58 @@ +.\" Copyright 1993 Rickard E. Faith (faith@cs.unc.edu) +.\" May be distributed under the GNU General Public License +.TH IPCS 8 "9 October 1993" "Linux 0.99" "Linux Programmer's Manual" +.SH NAME +ipcs \- provide information on ipc facilities +.SH SYNOPSIS +.B ipcs [ \-asmq ] [ \-tclup ] +.br +.BI "ipcs [ \-smq ] \-i " id +.br +.B ipcs \-h +.SH DESCRIPTION +.B ipcs +provides information on the ipc facilities for which the calling process +has read acccess. + +The +.B \-i +option allows a specific resource +.I id +to be specified. Only information on this +.I id +will be printed. + +Resources may be specified as follows: +.TP +.B \-m +shared memory segments +.TP +.B \-q +message queues +.TP +.B \-s +semaphore arrays +.TP +.B \-a +all (this is the default) +.PP +The output format may be specified as follows: +.TP +.B \-t +time +.TP +.B \-p +pid +.TP +.B \-c +creator +.TP +.B \-l +limits +.TP +.B \-u +summary +.SH SEE ALSO +.BR ipcrm (8) +.SH AUTHOR +krishna balasubramanian (balasub@cis.ohio-state.edu) diff --git a/sys-utils/ipcs.c b/sys-utils/ipcs.c new file mode 100644 index 00000000..8e7f9c60 --- /dev/null +++ b/sys-utils/ipcs.c @@ -0,0 +1,551 @@ +/* Original author unknown, but may be "krishna balasub@cis.ohio-state.edu" + Modified Sat Oct 9 10:55:28 1993 for 0.99.13 */ + +/* Patches from Mike Jagdis (jaggy@purplet.demon.co.uk) applied Wed Feb 8 +12:12:21 1995 by faith@cs.unc.edu to print numeric uids if no passwd file +entry. */ + +#include +#include +#include +#include +#include +#include +#include +#define __KERNEL__ +#include +#include +#include + + +#define LIMITS 1 +#define STATUS 2 +#define CREATOR 3 +#define TIME 4 +#define PID 5 + +void do_shm (char format); +void do_sem (char format); +void do_msg (char format); +void print_shm (int id); +void print_msg (int id); +void print_sem (int id); + +static char *progname; + +void usage(void) +{ + printf ("usage : %s -asmq -tclup \n", progname); + printf ("\t%s [-s -m -q] -i id\n", progname); + printf ("\t%s -h for help.\n", progname); + return; +} + +void help (void) +{ + printf ("%s provides information on ipc facilities for", progname); + printf (" which you have read access.\n"); + printf ("Resource Specification:\n\t-m : shared_mem\n\t-q : messages\n"); + printf ("\t-s : semaphores\n\t-a : all (default)\n"); + printf ("Output Format:\n\t-t : time\n\t-p : pid\n\t-c : creator\n"); + printf ("\t-l : limits\n\t-u : summary\n"); + printf ("-i id [-s -q -m] : details on resource identified by id\n"); + usage(); + return; +} + +int main (int argc, char **argv) +{ + int opt, msg = 0, sem = 0, shm = 0, id=0, print=0; + char format = 0; + char options[] = "atcluphsmqi:"; + + progname = argv[0]; + while ((opt = getopt (argc, argv, options)) != EOF) { + switch (opt) { + case 'i': + id = atoi (optarg); + print = 1; + break; + case 'a': + msg = shm = sem = 1; + break; + case 'q': + msg = 1; + break; + case 's': + sem = 1; + break; + case 'm': + shm = 1; + break; + case 't': + format = TIME; + break; + case 'c': + format = CREATOR; + break; + case 'p': + format = PID; + break; + case 'l': + format = LIMITS; + break; + case 'u': + format = STATUS; + break; + case 'h': + help(); + exit (0); + case '?': + usage(); + exit (0); + } + } + + if (print) { + if (shm) { + print_shm (id); + exit (0); + } + if (sem) { + print_sem (id); + exit (0); + } + if (msg) { + print_msg (id); + exit (0); + } + usage(); + } + + if ( !shm && !msg && !sem) + msg = sem = shm = 1; + printf ("\n"); + + if (shm) { + do_shm (format); + printf ("\n"); + } + if (sem) { + do_sem (format); + printf ("\n"); + } + if (msg) { + do_msg (format); + printf ("\n"); + } + return 0; +} + + +void print_perms (int id, struct ipc_perm *ipcp) +{ + struct passwd *pw; + struct group *gr; + + printf ("%-10d%-10o", id, ipcp->mode & 0777); + + if ((pw = getpwuid(ipcp->cuid))) + printf("%-10s", pw->pw_name); + else + printf("%-10d", ipcp->cuid); + if ((gr = getgrgid(ipcp->cgid))) + printf("%-10s", gr->gr_name); + else + printf("%-10d", ipcp->cgid); + + if ((pw = getpwuid(ipcp->uid))) + printf("%-10s", pw->pw_name); + else + printf("%-10d", ipcp->uid); + if ((gr = getgrgid(ipcp->gid))) + printf("%-10s\n", gr->gr_name); + else + printf("%-10d\n", ipcp->gid); +} + + +void do_shm (char format) +{ + int maxid, shmid, id; + struct shmid_ds shmseg; + struct shm_info shm_info; + struct shminfo shminfo; + struct ipc_perm *ipcp = &shmseg.shm_perm; + struct passwd *pw; + + maxid = shmctl (0, SHM_INFO, (struct shmid_ds *) &shm_info); + if (maxid < 0) { + printf ("kernel not configured for shared memory\n"); + return; + } + + switch (format) { + case LIMITS: + printf ("------ Shared Memory Limits --------\n"); + if ((shmctl (0, IPC_INFO, (struct shmid_ds *) &shminfo)) < 0 ) + return; + printf ("max number of segments = %d\n", shminfo.shmmni); + printf ("max seg size (kbytes) = %d\n", shminfo.shmmax >> 10); + printf ("max total shared memory (kbytes) = %d\n", shminfo.shmall << 2); + printf ("min seg size (bytes) = %d\n", shminfo.shmmin); + return; + + case STATUS: + printf ("------ Shared Memory Status --------\n"); + printf ("segments allocated %d\n", shm_info.used_ids); + printf ("pages allocated %d\n", shm_info.shm_tot); + printf ("pages resident %d\n", shm_info.shm_rss); + printf ("pages swapped %d\n", shm_info.shm_swp); + printf ("Swap performance: %d attempts\t %d successes\n", + shm_info.swap_attempts, shm_info.swap_successes); + return; + + case CREATOR: + printf ("------ Shared Memory Segment Creators/Owners --------\n"); + printf ("%-10s%-10s%-10s%-10s%-10s%-10s\n", + "shmid","perms","cuid","cgid","uid","gid"); + break; + + case TIME: + printf ("------ Shared Memory Attach/Detach/Change Times --------\n"); + printf ("%-10s%-10s %-20s%-20s%-20s\n", + "shmid","owner","attached","detached","changed"); + break; + + case PID: + printf ("------ Shared Memory Creator/Last-op --------\n"); + printf ("%-10s%-10s%-10s%-10s\n","shmid","owner","cpid","lpid"); + break; + + default: + printf ("------ Shared Memory Segments --------\n"); + printf ("%-10s%-10s%-10s%-10s%-10s%-12s\n", "shmid","owner", + "perms","bytes","nattch","status"); + break; + } + + for (id = 0; id <= maxid; id++) { + shmid = shmctl (id, SHM_STAT, &shmseg); + if (shmid < 0) + continue; + if (format == CREATOR) { + print_perms (shmid, ipcp); + continue; + } + pw = getpwuid(ipcp->uid); + switch (format) { + case TIME: + if (pw) + printf ("%-10d%-10.10s", shmid, pw->pw_name); + else + printf ("%-10d%-10d", shmid, ipcp->uid); + printf(" %-20.16s%-20.16s%-20.16s\n", + shmseg.shm_atime ? ctime(&shmseg.shm_atime) + 4 : "Not set", + shmseg.shm_dtime ? ctime(&shmseg.shm_dtime) + 4 : "Not set", + shmseg.shm_ctime ? ctime(&shmseg.shm_ctime) + 4 : "Not set"); + break; + case PID: + if (pw) + printf ("%-10d%-10.10s", shmid, pw->pw_name); + else + printf ("%-10d%-10d", shmid, ipcp->uid); + printf ("%-10d%-10d\n", + shmseg.shm_cpid, shmseg.shm_lpid); + break; + + default: + if (pw) + printf ("%-10d%-10.10s", shmid, pw->pw_name); + else + printf ("%-10d%-10d", shmid, ipcp->uid); + printf ("%-10o%-10d%-10d%-6s%-6s\n", + ipcp->mode & 0777, + shmseg.shm_segsz, shmseg.shm_nattch, + ipcp->mode & SHM_DEST ? "dest" : " ", + ipcp->mode & SHM_LOCKED ? "locked" : " "); + break; + } + } + return; +} + + +void do_sem (char format) +{ + int maxid, semid, id; + struct semid_ds semary; + struct seminfo seminfo; + struct ipc_perm *ipcp = &semary.sem_perm; + struct passwd *pw; + union semun arg; + + arg.array = (ushort *) &seminfo; + maxid = semctl (0, 0, SEM_INFO, arg); + if (maxid < 0) { + printf ("kernel not configured for semaphores\n"); + return; + } + + switch (format) { + case LIMITS: + printf ("------ Semaphore Limits --------\n"); + arg.array = (ushort *) &seminfo; /* damn union */ + if ((semctl (0, 0, IPC_INFO, arg)) < 0 ) + return; + printf ("max number of arrays = %d\n", seminfo.semmni); + printf ("max semaphores per array = %d\n", seminfo.semmsl); + printf ("max semaphores system wide = %d\n", seminfo.semmns); + printf ("max ops per semop call = %d\n", seminfo.semopm); + printf ("semaphore max value = %d\n", seminfo.semvmx); + return; + + case STATUS: + printf ("------ Semaphore Status --------\n"); + printf ("used arrays = %d\n", seminfo.semusz); + printf ("allocated semaphores = %d\n", seminfo.semaem); + return; + + case CREATOR: + printf ("------ Semaphore Arrays Creators/Owners --------\n"); + printf ("%-10s%-10s%-10s%-10s%-10s%-10s\n", + "semid","perms","cuid","cgid","uid","gid"); + break; + + case TIME: + printf ("------ Shared Memory Operation/Change Times --------\n"); + printf ("%-8s%-10s %-26.24s %-26.24s\n", + "shmid","owner","last-op","last-changed"); + break; + + case PID: + break; + + default: + printf ("------ Semaphore Arrays --------\n"); + printf ("%-10s%-10s%-10s%-10s%-12s\n", + "semid","owner","perms","nsems","status"); + break; + } + + for (id = 0; id <= maxid; id++) { + arg.buf = (struct semid_ds *) &semary; + semid = semctl (id, 0, SEM_STAT, arg); + if (semid < 0) + continue; + if (format == CREATOR) { + print_perms (semid, ipcp); + continue; + } + pw = getpwuid(ipcp->uid); + switch (format) { + case TIME: + if (pw) + printf ("%-8d%-10.10s", semid, pw->pw_name); + else + printf ("%-8d%-10d", semid, ipcp->uid); + printf (" %-26.24s %-26.24s\n", + semary.sem_otime ? ctime(&semary.sem_otime) : "Not set", + semary.sem_ctime ? ctime(&semary.sem_ctime) : "Not set"); + break; + case PID: + break; + + default: + if (pw) + printf ("%-10d%-10.9s", semid, pw->pw_name); + else + printf ("%-10d%-9d", semid, ipcp->uid); + printf ("%-10o%-10d\n", + ipcp->mode & 0777, + semary.sem_nsems); + break; + } + } + return; +} + + +void do_msg (char format) +{ + int maxid, msqid, id; + struct msqid_ds msgque; + struct msginfo msginfo; + struct ipc_perm *ipcp = &msgque.msg_perm; + struct passwd *pw; + + maxid = msgctl (0, MSG_INFO, (struct msqid_ds *) &msginfo); + if (maxid < 0) { + printf ("kernel not configured for shared memory\n"); + return; + } + + switch (format) { + case LIMITS: + if ((msgctl (0, IPC_INFO, (struct msqid_ds *) &msginfo)) < 0 ) + return; + printf ("------ Messages: Limits --------\n"); + printf ("max queues system wide = %d\n", msginfo.msgmni); + printf ("max size of message (bytes) = %d\n", msginfo.msgmax); + printf ("default max size of queue (bytes) = %d\n", msginfo.msgmnb); + return; + + case STATUS: + printf ("------ Messages: Status --------\n"); + printf ("allocated queues = %d\n", msginfo.msgpool); + printf ("used headers = %d\n", msginfo.msgmap); + printf ("used space = %d bytes\n", msginfo.msgtql); + return; + + case CREATOR: + printf ("------ Message Queues: Creators/Owners --------\n"); + printf ("%-10s%-10s%-10s%-10s%-10s%-10s\n", + "msqid","perms","cuid","cgid","uid","gid"); + break; + + case TIME: + printf ("------ Message Queues Send/Recv/Change Times --------\n"); + printf ("%-8s%-10s %-20s%-20s%-20s\n", + "msqid","owner","send","recv","change"); + break; + + case PID: + break; + + default: + printf ("------ Message Queues --------\n"); + printf ("%-10s%-10s%-10s%-12s%-12s\n", "msqid","owner", + "perms", "used-bytes", "messages"); + break; + } + + for (id = 0; id <= maxid; id++) { + msqid = msgctl (id, MSG_STAT, &msgque); + if (msqid < 0) + continue; + if (format == CREATOR) { + print_perms (msqid, ipcp); + continue; + } + pw = getpwuid(ipcp->uid); + switch (format) { + case TIME: + if (pw) + printf ("%-8d%-10.10s", msqid, pw->pw_name); + else + printf ("%-8d%-10d", msqid, ipcp->uid); + printf (" %-20.16s%-20.16s%-20.16s\n", + msgque.msg_stime ? ctime(&msgque.msg_stime) + 4 : "Not set", + msgque.msg_rtime ? ctime(&msgque.msg_rtime) + 4 : "Not set", + msgque.msg_ctime ? ctime(&msgque.msg_ctime) + 4 : "Not set"); + break; + case PID: + break; + + default: + if (pw) + printf ("%-10d%-10.10s", msqid, pw->pw_name); + else + printf ("%-10d%-10d", msqid, ipcp->uid); + printf ("%-10o%-12d%-12d\n", + ipcp->mode & 0777, msgque.msg_cbytes, + msgque.msg_qnum); + break; + } + } + return; +} + + +void print_shm (int shmid) +{ + struct shmid_ds shmds; + struct ipc_perm *ipcp = &shmds.shm_perm; + + if (shmctl (shmid, IPC_STAT, &shmds) == -1) { + perror ("shmctl "); + return; + } + + printf ("\nShared memory Segment shmid=%d\n", shmid); + printf ("uid=%d\tgid=%d\tcuid=%d\tcgid=%d\n", + ipcp->uid, ipcp->gid, ipcp->cuid, ipcp->cgid); + printf ("mode=%#o\taccess_perms=%#o\n", ipcp->mode, ipcp->mode & 0777); + printf ("bytes=%d\tlpid=%d\tcpid=%d\tnattch=%d\n", + shmds.shm_segsz, shmds.shm_lpid, shmds.shm_cpid, + shmds.shm_nattch); + printf ("att_time=%s", shmds.shm_atime ? ctime (&shmds.shm_atime) : + "Not set\n"); + printf ("det_time=%s", shmds.shm_dtime ? ctime (&shmds.shm_dtime) : + "Not set\n"); + printf ("change_time=%s", ctime (&shmds.shm_ctime)); + printf ("\n"); + return; +} + + + +void print_msg (int msqid) +{ + struct msqid_ds buf; + struct ipc_perm *ipcp = &buf.msg_perm; + + if (msgctl (msqid, IPC_STAT, &buf) == -1) { + perror ("msgctl "); + return; + } + printf ("\nMessage Queue msqid=%d\n", msqid); + printf ("uid=%d\tgid=%d\tcuid=%d\tcgid=%d\tmode=%#o\n", + ipcp->uid, ipcp->gid, ipcp->cuid, ipcp->cgid, ipcp->mode); + printf ("cbytes=%d\tqbytes=%d\tqnum=%d\tlspid=%d\tlrpid=%d\n", + buf.msg_cbytes, buf.msg_qbytes, buf.msg_qnum, buf.msg_lspid, + buf.msg_lrpid); + printf ("send_time=%srcv_time=%schange_time=%s", + buf.msg_rtime? ctime (&buf.msg_rtime) : "Not Set\n", + buf.msg_stime? ctime (&buf.msg_stime) : "Not Set\n", + buf.msg_ctime? ctime (&buf.msg_ctime) : "Not Set\n"); + printf ("\n"); + return; +} + +void print_sem (int semid) +{ + struct semid_ds semds; + struct ipc_perm *ipcp = &semds.sem_perm; + union semun arg; + int i; + + arg.buf = &semds; + if (semctl (semid, 0, IPC_STAT, arg) < 0) { + perror ("semctl "); + return; + } + printf ("\nSemaphore Array semid=%d\n", semid); + printf ("uid=%d\t gid=%d\t cuid=%d\t cgid=%d\n", + ipcp->uid, ipcp->gid, ipcp->cuid, ipcp->cgid); + printf ("mode=%#o, access_perms=%#o\n", ipcp->mode, ipcp->mode & 0777); + printf ("nsems = %d\n", semds.sem_nsems); + printf ("otime = %s", semds.sem_otime ? ctime (&semds.sem_otime) : + "Not set\n"); + printf ("ctime = %s", ctime (&semds.sem_ctime)); + + printf ("%-10s%-10s%-10s%-10s%-10s\n", "semnum","value","ncount", + "zcount","pid"); + arg.val = 0; + for (i=0; i< semds.sem_nsems; i++) { + int val, ncnt, zcnt, pid; + val = semctl (semid, i, GETVAL, arg); + ncnt = semctl (semid, i, GETNCNT, arg); + zcnt = semctl (semid, i, GETZCNT, arg); + pid = semctl (semid, i, GETPID, arg); + if (val < 0 || ncnt < 0 || zcnt < 0 || pid < 0) { + perror ("semctl "); + exit (1); + } + printf ("%-10d%-10d%-10d%-10d%-10d\n", i, val, ncnt, zcnt, pid); + } + printf ("\n"); + return; +} + diff --git a/sys-utils/kbdrate.8 b/sys-utils/kbdrate.8 new file mode 100644 index 00000000..3fef3395 --- /dev/null +++ b/sys-utils/kbdrate.8 @@ -0,0 +1,57 @@ +.\" Copyright 1992, 1994 Rickard E. Faith (faith@cs.unc.edu) +.\" May be distributed under the GNU General Public License +.\" Updated Wed Jun 22 21:09:43 1994, faith@cs.unc.edu +.TH KBDRATE 8 "22 June 1994" "Linux 1.1.19" "Linux Programmer's Manual" +.SH NAME +kbdrate \- reset the keyboard repeat rate and delay time +.SH SYNOPSIS +.B "kbdrate [ \-s ] [ \-r" +rate +.B "] [ \-d" +delay +.B ] +.SH DESCRIPTION +.B kbdrate +is used to change the IBM keyboard repeat rate and delay time. The delay +is the amount of time that a key must be depressed before it will start to +repeat. + +Using +.B kbdrate +without any options will reset the rate to 10.9 characters per second (cps) +and the delay to 250 milliseconds (mS). These are the IBM defaults. +.SH OPTIONS +.TP +.B \-s +Silent. No messages are printed. +.TP +.BI \-r " rate" +Change the keyboard repeat rate to +.I rate +cps. The allowable range is from 2.0 to 30.0 cps. Only certain, specific +values are possible, and the program will select the nearest possible value +to the one specified. The possible values are given, in characters per +second, as follows: 2.0, 2.1, 2.3, 2.5, 2.7, 3.0, 3.3, 3.7, 4.0, 4.3, 4.6, +5.0, 5.5, 6.0, 6.7, 7.5, 8.0, 8.6, 9.2, 10.0, 10.9, 12.0, 13.3, 15.0, 16.0, +17.1, 18.5, 20.0, 21.8, 24.0, 26.7, 30.0. +.TP +.BI \-d " delay" +Change the delay to +.I delay +milliseconds. The allowable range is from 250 to 1000 mS, but the only +possible values (based on hardware restrictions) are: 250mS, 500mS, 750mS, +and 1000mS. +.SH BUGS +Not all keyboards support all rates. +.PP +Not all keyboards have the rates mapped in the same way. +.PP +Setting the repeat rate on the Gateway AnyKey keyboard does not work. If +someone with a Gateway figures out how to program the keyboard, please send +mail to faith@cs.unc.edu. +.SH FILES +.I /etc/rc.local +.br +.I /dev/port +.SH AUTHOR +Rik Faith (faith@cs.unc.edu) diff --git a/sys-utils/kbdrate.c b/sys-utils/kbdrate.c new file mode 100644 index 00000000..d8632a20 --- /dev/null +++ b/sys-utils/kbdrate.c @@ -0,0 +1,130 @@ +/* +From: faith@cs.unc.edu (Rik Faith) +Subject: User mode keyboard rate changer +Date: 27 Apr 92 13:44:26 GMT + +I put together this program, called kbdrate.c, which will reset the keyboard +repeat rate and delay in user mode. The program must have read/write +access to /dev/port, so if /dev/port is only read/writeable by group port, +then kbdrate must run setgid to group port (for example). + +The "rate" is the rate in characters per second + +The "delay" is the amount of time the key must remain depressed before it +will start to repeat. + +Usage examples: + +kbdrate set rate to IBM default (10.9 cps, 250mS delay) +kbdrate -r 30.0 set rate to 30 cps and delay to 250mS +kbdrate -r 20.0 -s set rate to 20 cps (delay 250mS) -- don't print message +kbdrate -r 0 -d 0 set rate to 2.0 cps and delay to 250 mS + +I find it useful to put kbdrate in my /etc/rc file so that the keyboard +rate is set to something that I find comfortable at boot time. This sure +beats rebuilding the kernel! +*/ + +/********************** CUT HERE *****************************/ +/* kbdrate.c -- Set keyboard typematic rate (and delay) + * Created: Thu Apr 23 12:24:30 1992 + * Revised: Wed Jun 22 22:40:46 1994 by faith@cs.unc.edu + * Author: Rickard E. Faith, faith@cs.unc.edu + * Copyright 1992 Rickard E. Faith. Distributed under the GPL. + * This program comes with ABSOLUTELY NO WARRANTY. + * Usage: kbdrate [-r rate] [-d delay] [-s] + * Rate can range from 2.0 to 30.0 (units are characters per second) + * Delay can range from 250 to 1000 (units are milliseconds) + * -s suppressed message + * Compiles under gcc 2.1 for Linux (tested with the pre-0.96 kernel) + * + * Wed Jun 22 21:35:43 1994, faith@cs.unc.edu: + * Changed valid_rates per suggestion by Andries.Brouwer@cwi.nl. + * Wed Jun 22 22:18:29 1994, faith@cs.unc.edu: + * Added patch for AUSTIN notebooks from John Bowman + * (bowman@hagar.ph.utexas.edu) + */ + +#include +#include +#include + +static int valid_rates[] = { 300, 267, 240, 218, 200, 185, 171, 160, 150, + 133, 120, 109, 100, 92, 86, 80, 75, 67, + 60, 55, 50, 46, 43, 40, 37, 33, 30, 27, + 25, 23, 21, 20 }; +#define RATE_COUNT (sizeof( valid_rates ) / sizeof( int )) + +static int valid_delays[] = { 250, 500, 750, 1000 }; +#define DELAY_COUNT (sizeof( valid_delays ) / sizeof( int )) + +void main( int argc, char **argv ) +{ + double rate = 10.9; /* Default rate */ + int delay = 250; /* Default delay */ + int value = 0x7f; /* Maximum delay with slowest rate */ + /* DO NOT CHANGE this value */ + int silent = 0; + int fd; + char data; + int c; + int i; + extern char *optarg; + extern int optind; + + while ( (c = getopt( argc, argv, "r:d:s" )) != EOF ) + switch (c) { + case 'r': + rate = atof( optarg ); + break; + case 'd': + delay = atoi( optarg ); + break; + case 's': + silent = 1; + break; + } + + for (i = 0; i < RATE_COUNT; i++) + if (rate * 10 >= valid_rates[i]) { + value &= 0x60; + value |= i; + break; + } + + for (i = 0; i < DELAY_COUNT; i++) + if (delay <= valid_delays[i]) { + value &= 0x1f; + value |= i << 5; + break; + } + + if ( (fd = open( "/dev/port", O_RDWR )) < 0) { + perror( "Cannot open /dev/port" ); + exit( 1 ); + } + + do { + lseek( fd, 0x64, 0 ); + read( fd, &data, 1 ); + } while ((data & 2) == 2 ); /* wait */ + + lseek( fd, 0x60, 0 ); + data = 0xf3; /* set typematic rate */ + write( fd, &data, 1 ); + + do { + lseek( fd, 0x64, 0 ); + read( fd, &data, 1 ); + } while ((data & 2) == 2 ); /* wait */ + + lseek( fd, 0x60, 0 ); + sleep( 1 ); + write( fd, &value, 1 ); + + close( fd ); + + if (!silent) printf( "Typematic Rate set to %.1f cps (delay = %d mS)\n", + valid_rates[value & 0x1f] / 10.0, + valid_delays[ (value & 0x60) >> 5 ] ); +} diff --git a/sys-utils/lpcntl.8 b/sys-utils/lpcntl.8 new file mode 100644 index 00000000..87bcd039 --- /dev/null +++ b/sys-utils/lpcntl.8 @@ -0,0 +1,30 @@ +.\" Public Domain 1994 Rik Faith (faith@cs.unc.edu) +.\" " +.TH LPCNTL 8 "18 July 1994" "Linux 1.1" "Linux Programmer's Manual" +.SH NAME +lpcntl \- interface to line printer ioctl +.SH SYNOPSIS +.BI "lpcntl " device " [ " irq " ]" +.SH DESCRIPTION +.B lpcntl +is used to manipulate the line printer driver. +.PP +.I device +specifies a line printer device (e.g., +.IR /dev/lp1 ). +.PP +If +.I irq +is specified, then the line printer driver is set to use that IRQ. If the +.I irq +is zero, then the polling driver is selected. Only the super user may +change the IRQ. +.PP +If no +.I irq +is specified, then +.B lpcntl +will report the interrupt number currently in use, or will report that the +polling driver is currently being used. +.SH AUTHOR +Nigel Gamble (nigel@gate.net) diff --git a/sys-utils/lpcntl.c b/sys-utils/lpcntl.c new file mode 100644 index 00000000..bf164a63 --- /dev/null +++ b/sys-utils/lpcntl.c @@ -0,0 +1,54 @@ +/* + * Simple command interface to ioctl(fd, LPSETIRQ, irq). + * Nigel Gamble (nigel@gate.net) + * e.g. + * lpcntl /dev/lp1 7 + */ + +#include +#include +#include +#include + +int +main(int argc, char **argv) +{ + unsigned int irq; + int fd; + int ret; + + if (argc < 2) { + fprintf(stderr, "usage: %s []\n", argv[0]); + exit(1); + } + + fd = open(argv[1], O_RDONLY); + if (fd == -1) { + perror(argv[1]); + exit(1); + } + + if (argc == 2) { + irq = ioctl(fd, LPGETIRQ); + if (irq == -1) { + perror(argv[1]); + exit(1); + } + if (irq) + printf("%s using IRQ %d\n", argv[1], irq); + else + printf("%s using polling\n", argv[1]); + } else { + irq = atoi(argv[2]); + ret = ioctl(fd, LPSETIRQ, irq); + if (ret == -1) { + if (errno == EPERM) + fprintf(stderr, "%s: only super-user can change the IRQ\n", argv[0]); + else + perror(argv[1]); + exit(1); + } + } + + return 0; +} diff --git a/sys-utils/ramsize.8 b/sys-utils/ramsize.8 new file mode 100644 index 00000000..901bd759 --- /dev/null +++ b/sys-utils/ramsize.8 @@ -0,0 +1 @@ +.so man8/rdev.8 diff --git a/sys-utils/rdev.8 b/sys-utils/rdev.8 new file mode 100644 index 00000000..78a62355 --- /dev/null +++ b/sys-utils/rdev.8 @@ -0,0 +1,166 @@ +.\" Copyright 1992, 1993 Rickard E. Faith (faith@cs.unc.edu) +.\" May be distributed under the GNU General Public License +.\" Changes from sct@dcs.ed.ac.uk added Sat Oct 9 09:54:00 1993. +.TH RDEV 8 "20 November 1993" "Linux 0.99" "Linux Programmer's Manual" +.SH NAME +rdev \- query/set image root device, swap device, RAM disk size, or video mode +.SH SYNOPSIS +.nf +.BR "rdev [ \-rsvh ] [ \-o " offset " ] [ " image " [ " value " [ " offset " ] ] ]" +.BR "rdev [ \-o " offset " ] [ " image " [ " root_device " [ " offset " ] ] ]" +.BR "swapdev [ \-o " offset " ] [ " image " [ " swap_device " [ " offset " ] ] ]" +.BR "ramsize [ \-o " offset " ] [ " image " [ " size " [ " offset " ] ] ]" +.BR "vidmode [ \-o " offset " ] [ " image " [ " mode " [ " offset " ] ] ]" +.BR "rootflags [ \-o " offset " ] [ " image " [ " flags " [ " offset " ] ] ]" +.fi +.SH DESCRIPTION +.\" " for emacs hilit19 +With no arguments, +.B rdev +outputs an +.I /etc/mtab +line for the current root file system. +With no arguments, +.BR swapdev ", " ramsize ", " vidmode ", and " rootflags +print usage information. + +In a bootable image for the Linux kernel, there are several pairs of bytes +which specify the root device, the video mode, the size of the RAM disk, +and the swap device. These pairs of bytes, by default, begin at offset 504 +(decimal) in the kernel image: + +.nf +.RS + 498 Root flags +(500 and 502 Reserved) + 504 RAM Disk Size + 506 VGA Mode + 508 Root Device +(510 Boot Signature) +.RE +.fi + +.B rdev +will change these values. + +Typical values for the +.I image +parameter, which is a bootable Linux kernel image, are as follows: + +.nf +.RS +/vmlinux +/vmlinux.test +/vmunix +/vmunix.test +/dev/fd0 +/dev/fd1 +.RE +.fi + +When using the +.BR rdev ", or " swapdev +commands, the +.IR root_device " or " swap_device +parameter are as follows: + +.nf +.RS +/dev/hda[1-8] +/dev/hdb[1-8] +/dev/sda[1-8] +/dev/sdb[1-8] +.RE +.fi + +For the +.B ramsize +command, the +.B size +parameter specifies the size of the RAM disk in kilobytes. + +For the +.B rootflags +command, the +.B flags +parameter contains extra information used when mounting root. +Currently the only effect of these flags is to force the kernel to +mount the root filesystem in readonly mode if +.B flags +is non-zero. + +For the +.B vidmode +command, the +.B mode +parameter specifies the video mode: + +.nf +.RS +-3 = Prompt +-2 = Extended VGA +-1 = Normal VGA + 0 = as if "0" was pressed at the prompt + 1 = as if "1" was pressed at the prompt + 2 = as if "2" was pressed at the prompt + n = as if "n" was pressed at the prompt +.RE +.fi + +If the +.I value +is not specified, the +.I image +will be examined to determine the current settings. +.SH OPTIONS +.TP +.B \-s +Causes +.B rdev +to act like +.BR swapdev . +.TP +.B \-r +Causes +.B rdev +to act like +.BR ramsize . +.TP +.B \-R +Causes +.B rdev +to act like +.BR rootflags . +.TP +.B \-v +Causes +.B rdev +to act like +.BR vidmode . +.TP +.B \-h +Provides help. +.SH BUGS +For historical reasons, there are two methods for specifying alternative +values for the offset. +.sp +The user interface is cumbersome, non-intuitive, and should probably be +re-written from scratch, allowing multiple kernel image parameters to be +changed or examined with a single command. +.sp +If LILO is used, +.B rdev +is no longer needed for setting the root device and the VGA mode, since +these parameters that +.B rdev +modifies can be set from the LILO prompt during a boot. However, +.B rdev +is still needed at this time for setting the RAM disk size. Users are +encouraged to find the LILO documentation for more information, and to use +LILO when booting their systems. +.SH AUTHORS +.nf +Originally by Werner Almesberger (almesber@nessie.cs.id.ethz.ch) +Modified by Peter MacDonald (pmacdona@sanjuan.UVic.CA) +rootflags support added by Stephen Tweedie (sct@dcs.ed.ac.uk) +.fi diff --git a/sys-utils/rdev.c b/sys-utils/rdev.c new file mode 100644 index 00000000..cb3a730e --- /dev/null +++ b/sys-utils/rdev.c @@ -0,0 +1,244 @@ +/* + + rdev.c - query/set root device. + +------------------------------------------------------------------------- + +Date: Sun, 27 Dec 1992 15:55:31 +0000 +Subject: Re: rdev +From: almesber@nessie.cs.id.ethz.ch (Werner Almesberger) +To: Rik Faith + +There are quite a few versions of rdev: + + - the original rootdev that only printed the current root device, by + Linus. + - rdev that does what rootdev did and that also allows you to change + the root (and swap) device, by me. + - rdev got renamed to setroot and I think even to rootdev on various + distributions. + - Peter MacDonald added video mode and RAM disk setting and included + this version on SLS, called rdev again. I've attached his rdev.c to + this mail. + +------------------------------------------------------------------------- + +Date: 11 Mar 92 21:37:37 GMT +Subject: rdev - query/set root device +From: almesber@nessie.cs.id.ethz.ch (Werner Almesberger) +Organization: Swiss Federal Institute of Technology (ETH), Zurich, CH + +With all that socket, X11, disk driver and FS hacking going on, apparently +nobody has found time to address one of the minor nuisances of life: set- +ting the root FS device is still somewhat cumbersome. I've written a little +utility which can read and set the root device in boot images: + +rdev accepts an optional offset argument, just in case the address should +ever move from 508. If called without arguments, rdev outputs an mtab line +for the current root FS, just like /etc/rootdev does. + +ramsize sets the size of the ramdisk. If size is zero, no ramdisk is used. + +vidmode sets the default video mode at bootup time. -1 uses default video +mode, -2 uses menu. + +------------------------------------------------------------------------- + +Sun Dec 27 10:42:16 1992: Minor usage changes, faith@cs.unc.edu. +Tue Mar 30 09:31:52 1993: rdev -Rn to set root readonly flag, sct@dcs.ed.ac.uk +Wed Jun 22 21:12:29 1994: Applied patches from Dave + (gentzel@nova.enet.dec.com) to prevent dereferencing + the NULL pointer, faith@cs.unc.edu + +------------------------------------------------------------------------- + +*/ + +/* rdev.c - query/set root device. */ + +usage() +{ + + puts("usage: rdev [ -rsv ] [ -o OFFSET ] [ IMAGE [ VALUE [ OFFSET ] ] ]"); + puts(" rdev /dev/fd0 (or rdev /linux, etc.) displays the current ROOT device"); + puts(" rdev /dev/fd0 /dev/hda2 sets ROOT to /dev/hda2"); + puts(" rdev -R /dev/fd0 1 set the ROOTFLAGS (readonly status)"); + puts(" rdev -s /dev/fd0 /dev/hda2 set the SWAP device"); + puts(" rdev -r /dev/fd0 627 set the RAMDISK size"); + puts(" rdev -v /dev/fd0 1 set the bootup VIDEOMODE"); + puts(" rdev -o N ... use the byte offset N"); + puts(" rootflags ... same as rdev -R"); + puts(" swapdev ... same as rdev -s"); + puts(" ramsize ... same as rdev -r"); + puts(" vidmode ... same as rdev -v"); + puts("Note: video modes are: -3=Ask, -2=Extended, -1=NormalVga, 1=key1, 2=key2,..."); + puts(" use -R 1 to mount root readonly, -R 0 for read/write."); + exit(-1); +} + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define DEFAULT_OFFSET 508 + + +static void die(char *msg) +{ + perror(msg); + exit(1); +} + + +static char *find_dev(int number) +{ + DIR *dp; + struct dirent *dir; + static char name[PATH_MAX+1]; + struct stat s; + + if (!number) return "Boot device"; + if ((dp = opendir("/dev")) == NULL) die("opendir /dev"); + strcpy(name,"/dev/"); + while (dir = readdir(dp)) { + strcpy(name+5,dir->d_name); + if (stat(name,&s) < 0) die(name); + if ((s.st_mode & S_IFMT) == S_IFBLK && s.st_rdev == number) return name; + } + sprintf(name,"0x%04x",number); + return name; +} + +/* enum { RDEV, SDEV, RAMSIZE, VIDMODE }; */ +enum { RDEV, VIDMODE, RAMSIZE, SDEV, __syssize__, ROOTFLAGS }; +char *cmdnames[6] = { "rdev", "vidmode", "ramsize", "swapdev", + "", "rootflags"}; +char *desc[6] = { "Root device", "Video mode", "Ramsize", "Swap device", + "", "Root flags"}; +#define shift(n) argv+=n,argc-=n + +int main(int argc,char **argv) +{ + int image,offset,dev_nr, i, newoffset=-1; + char *device, cmd = 0, *ptr, tmp[40]; + struct stat s; + + device = NULL; + if (ptr = strrchr(argv[0],'/')) + ptr++; + else + ptr = argv[0]; + for (i=0; i<=5; i++) + if (!strcmp(ptr,cmdnames[i])) + break; + cmd = i; + if (cmd>5) + cmd=RDEV; + offset = DEFAULT_OFFSET-cmd*2; + + while (argc > 1) + { + if (argv[1][0] != '-') + break; + else + switch (argv[1][1]) + { + case 'R': + cmd=ROOTFLAGS; + offset = DEFAULT_OFFSET-cmd*2; + shift(1); + break; + case 'r': + cmd=RAMSIZE; + offset = DEFAULT_OFFSET-cmd*2; + shift(1); + break; + case 'v': + cmd=VIDMODE; + offset = DEFAULT_OFFSET-cmd*2; + shift(1); + break; + case 's': + cmd=SDEV; + offset = DEFAULT_OFFSET-cmd*2; + shift(1); + break; + case 'o': + if (argv[1][2]) + { + newoffset=atoi(argv[1]+2); + shift(1); + break; + } else if (argc > 2) { + newoffset=atoi(argv[2]); + shift(2); + break; + } + /* Fall through. . . */ + default: + usage(); + } + } + if (newoffset >= 0) + offset = newoffset; + + if ((cmd==RDEV) && (argc == 1 || argc > 4)) { + if (stat("/",&s) < 0) die("/"); + printf("%s /\n",find_dev(s.st_dev)); + exit(0); + } else if ((cmd != RDEV) && (argc == 1 || argc > 4)) usage(); + + if ((cmd==RDEV) || (cmd==SDEV)) + { + if (argc == 4) { + device = argv[2]; + offset = atoi(argv[3]); + } + else { + if (argc == 3) { + if (isdigit(*argv[2])) offset = atoi(argv[2]); + else device = argv[2]; + } + } + } + else + { + if (argc>=3) + device = argv[2]; + if (argc==4) + offset = atoi(argv[3]); + } + if (device) { + if ((cmd==SDEV) || (cmd==RDEV)) + { if (stat(device,&s) < 0) die(device); + } else + s.st_rdev=atoi(device); + if ((image = open(argv[1],O_WRONLY)) < 0) die(argv[1]); + if (lseek(image,offset,0) < 0) die("lseek"); + if (write(image,(char *)&s.st_rdev,2) != 2) die(argv[1]); + if (close(image) < 0) die("close"); + } + else { + if ((image = open(argv[1],O_RDONLY)) < 0) die(argv[1]); + if (lseek(image,offset,0) < 0) die("lseek"); + dev_nr = 0; + if (read(image,(char *)&dev_nr,2) != 2) die(argv[1]); + if (close(image) < 0) die("close"); + printf(desc[cmd]); + if ((cmd==SDEV) || (cmd==RDEV)) + printf(" %s\n", find_dev(dev_nr)); + else + printf(" %d\n", dev_nr); + } + return 0; +} + + diff --git a/sys-utils/readprofile.1 b/sys-utils/readprofile.1 new file mode 100644 index 00000000..fd5d7196 --- /dev/null +++ b/sys-utils/readprofile.1 @@ -0,0 +1,159 @@ +.TH READPROFILE 1 "January 1995" +.UC 4 +.SH NAME +readprofile - a tool to read kernel profiling information +.SH SYNOPSIS +.B readprofile +[ +.I options +] + +.SH VERSION +This manpage documents version 1.1 of the program. + +.SH DESCRIPTION + +.LP +The +.B readprofile +command uses the +.B /proc/profile +information to print ascii data on standard output. +The output is +organized in three columns: the first is the number of clock ticks, +the second is the name of the C function in the kernel where those many +ticks occurred, and the third is the normalized `load' of the procedure, +calculated as a ratio between the number of thicks and the lenght of +the procedure. The output is filled with blanks to ease readability. + +.LP +Available command line options are the following: + +.TP +.RB -m " mapfile" +Specify a mapfile, which by default is +.B /usr/src/linux/System.map. +To ease use of +.B readprofile +with kernels in the 1.1.7x series, if the default file can't be opened, +the alternate file +.B /usr/src/linux/zSystem.map +is tried. +You should specify the map file on cmdline if your current kernel isn't the +last one you compiled. If the name of the map file ends with `.gz' it +is decompressed on the fly. + +.TP +.RB -p " pro-file" +Specify a different profiling buffer, which by default is +.B /proc/profile. +Using a different pro-file is useful if you want to `freeze' the +kernel profiling at some time and read it later. The +.B /proc/profile +file can be copied using `cat' or `cp'. If the name of the pro-file +ends by `.gz' it is decompressed on the fly. The pro-file is such that +.B gzip +shrinks it by 50-100 times. + +.TP +.B -i +Info. This makes +.B readprofile +only print the profiling step used by the kernel. +The profiling step is the resolution of the profiling buffer, and +is chosen during kernel configuration (through `make config'). +If the +.B -t +(terse) switch is used together with +.B -i +only the decimal number is printed. + +.TP +.B -a +Print all symbols in the mapfile. By default the procedures with 0 reported +ticks are not printed. + +.TP +.B -r +Reset the profiling buffer. This can only be invoked by root, because +.B /proc/profile +is readable by everybody but writable only by the superuser. + +.TP +.B -t +Terse. This causes the output to be unfilled. It is the format used in the +first release of +.B readprofile. + +.TP +.B -v +Verbose. The output is organized in four columns and filled with blanks. +The first column is the RAM address of a kernel function, the second is +the name of the function, the third is the number of clock ticks and the +last is the normalized load. + +.TP +.B -V +Version. This makes +.B readprofile +print its version number and exit. + +.SH EXAMPLES +Browse the profiling buffer ordering by clock ticks: +.nf + readprofile | sort -nr | less + +.fi +Print the 20 most loaded procedures: +.nf + readprofile | sort -nr +2 | head -20 + +.fi +Print only filesystem profile: +.nf + readprofile | grep _ext2 + +.fi +Look at all the kernel information, with ram addresses" +.nf + readprofile -av | less + +.fi +Browse a gzipped `freezed' profile buffer for a non current kernel: +.nf + readprofile -p ~/profile.freeze.gz -m /zImage.map + +.fi + +.SH BUGS + +.LP +.B readprofile +needs a kernel version 1.1.73 or newer, because +.B /proc/profile +is absent +in older versions. + +.LP +To enable profiling, the kernel must be reconfigured, recompiled, and +rebooted. No profiling module is available, and it wouldn't be easy to +build. So this can be construed as a feature. + +.LP +Profiling is disabled when interrupts are inhibited. This means that many +profiling ticks happen when interrupts are re-enabled. Watch out for +misleading information. + +.SH AUTHOR + +Readprofile and /proc/profile are by Alessandro Rubini (rubini@ipvvis.unipv.it) + +.SH FILES +.nf +/proc/profile A binary snapshot of the profiling buffer. +/usr/src/linux/System.map The symbol table for the kernel. +/usr/src/linux/zSystem.map Old name for the symbol table. + +/usr/src/linux/* The program being profiled :-) +.fi + diff --git a/sys-utils/readprofile.c b/sys-utils/readprofile.c new file mode 100644 index 00000000..58234f6a --- /dev/null +++ b/sys-utils/readprofile.c @@ -0,0 +1,223 @@ +/* + * readprofile.c - used to read /proc/profile + * + * Copyright (C) 1994 Alessandro Rubini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include /* getopt() */ +#include + +#define RELEASE "1.1, Jan 1995" + +#define S_LEN 128 + +static char *prgname; + +/* These are the defaults and they cna be changed */ +static char defaultmap1[]="/usr/src/linux/System.map"; +static char defaultmap2[]="/usr/src/linux/zSystem.map"; +static char defaultpro[]="/proc/profile"; +static char optstring[]="m:p:itvarV"; + +void usage() +{ + fprintf(stderr, + "%s: Usage: \"%s [options]\n" + "\t -m (default = \"%s\")\n" + "\t -p (default = \"%s\")\n" + "\t -i print only info about the sampling step\n" + "\t -t print terse data\n" + "\t -v print verbose data\n" + "\t -a print all symbols, even if count is 0\n" + "\t -r reset all the counters (root only)\n" + "\t -V print version and exit\n" + ,prgname,prgname,defaultmap1,defaultpro); + exit(1); +} + +FILE *myopen(char *name, char *mode, int *flag) +{ +static char cmdline[S_LEN]; + + if (!strcmp(name+strlen(name)-3,".gz")) + { + *flag=1; + sprintf(cmdline,"zcat %s", name); + return popen(cmdline,mode); + } + *flag=0; + return fopen(name,mode); +} + +int main (int argc, char **argv) +{ +FILE *pro; +FILE *map; +unsigned long l; +char *proFile; +char *mapFile; +int add, step; +int fn_add[2]; /* current and next address */ +char fn_name[2][S_LEN]; /* current and next name */ +char mode[8]; +int i,c,current=0; +int optAll=0, optInfo=0, optReset=0, optTerse=0, optVerbose=0; +char mapline[S_LEN]; +int maplineno=1; +int popenMap, popenPro; /* flags to tell if popen() is used */ + +#define next (current^1) + + prgname=argv[0]; + proFile=defaultpro; + mapFile=defaultmap1; + + while ((c=getopt(argc,argv,optstring))!=-1) + { + switch(c) + { + case 'm': mapFile=optarg; break; + case 'p': proFile=optarg; break; + case 'a': optAll++; break; + case 'i': optInfo++; break; + case 't': optTerse++; break; + case 'r': optReset++; break; + case 'v': optVerbose++; break; + case 'V': printf("%s Version %s\n",prgname,RELEASE); exit(0); + default: usage(); + } + } + + if (optReset) + { + pro=fopen(defaultpro,"w"); + if (!pro) + {perror(proFile); exit(1);} + fprintf(pro,"anything\n"); + fclose(pro); + exit(0); + } + + if (!(pro=myopen(proFile,"r",&popenPro))) + {fprintf(stderr,"%s: ",prgname);perror(proFile);exit(1);} + + /* + * In opening the map file, try both the default names, but exit + * at first fail if the filename was specified on cmdline + */ + for (map=NULL; map==NULL; ) + { + if (!(map=myopen(mapFile,"r",&popenMap))) + { + fprintf(stderr,"%s: ",prgname);perror(mapFile); + if (mapFile!=defaultmap1) exit(1); + mapFile=defaultmap2; + } + } + +#define NEXT_WORD(where) \ + (fread((void *)where, 1,sizeof(unsigned long),pro), feof(pro) ? 0 : 1) + + /* + * Init the 'next' field + */ + if (!fgets(mapline,S_LEN,map)) + { + fprintf(stderr,"%s: %s(%i): premature EOF\n",prgname,mapFile,maplineno); + exit(1); + } + if (sscanf(mapline,"%x %s %s",&(fn_add[next]),mode,fn_name[next])!=3) + { + fprintf(stderr,"%s: %s(%i): wrong map line\n",prgname,mapFile, maplineno); + exit(1); + } + + add=0; + + if (!NEXT_WORD(&step)) + { + fprintf(stderr,"%s: %s: premature EOF\n",prgname,proFile); + exit(1); + } + + if (optInfo) + { + printf(optTerse ? "%i\n" : "The sampling step in the kernel is %i bytes\n", + step); + exit(0); + } + + /* + * The main loop is build around the mapfile + */ + + while(current^=1, maplineno++, fgets(mapline,S_LEN,map)) + { + int fn_len; + int count=0; + + + if (sscanf(mapline,"%x %s %s",&(fn_add[next]),mode,fn_name[next])!=3) + { + fprintf(stderr,"%s: %s(%i): wrong map line\n", + prgname,mapFile, maplineno); + exit(1); + } + + if (!(fn_len=fn_add[next]-fn_add[current])) + continue; + + if (*mode=='d' || *mode=='D') break; /* only text is profiled */ + + while (add +#include +#include + +#include +#include + +/* + * Change the priority (nice) of processes + * or groups of processes which are already + * running. + */ +main(argc, argv) + char **argv; +{ + int which = PRIO_PROCESS; + int who = 0, prio, errs = 0; + + argc--, argv++; + if (argc < 2) { + fprintf(stderr, "usage: renice priority [ [ -p ] pids ] "); + fprintf(stderr, "[ [ -g ] pgrps ] [ [ -u ] users ]\n"); + exit(1); + } + prio = atoi(*argv); + argc--, argv++; + if (prio > PRIO_MAX) + prio = PRIO_MAX; + if (prio < PRIO_MIN) + prio = PRIO_MIN; + for (; argc > 0; argc--, argv++) { + if (strcmp(*argv, "-g") == 0) { + which = PRIO_PGRP; + continue; + } + if (strcmp(*argv, "-u") == 0) { + which = PRIO_USER; + continue; + } + if (strcmp(*argv, "-p") == 0) { + which = PRIO_PROCESS; + continue; + } + if (which == PRIO_USER) { + register struct passwd *pwd = getpwnam(*argv); + + if (pwd == NULL) { + fprintf(stderr, "renice: %s: unknown user\n", + *argv); + continue; + } + who = pwd->pw_uid; + } else { + who = atoi(*argv); + if (who < 0) { + fprintf(stderr, "renice: %s: bad value\n", + *argv); + continue; + } + } + errs += donice(which, who, prio); + } + exit(errs != 0); +} + +donice(which, who, prio) + int which, who, prio; +{ + int oldprio; + extern int errno; + + errno = 0, oldprio = getpriority(which, who); + if (oldprio == -1 && errno) { + fprintf(stderr, "renice: %d: ", who); + perror("getpriority"); + return (1); + } + if (setpriority(which, who, prio) < 0) { + fprintf(stderr, "renice: %d: ", who); + perror("setpriority"); + return (1); + } + printf("%d: old priority %d, new priority %d\n", who, oldprio, prio); + return (0); +} diff --git a/sys-utils/rootflags.8 b/sys-utils/rootflags.8 new file mode 100644 index 00000000..901bd759 --- /dev/null +++ b/sys-utils/rootflags.8 @@ -0,0 +1 @@ +.so man8/rdev.8 diff --git a/sys-utils/setserial.8 b/sys-utils/setserial.8 new file mode 100644 index 00000000..539db21a --- /dev/null +++ b/sys-utils/setserial.8 @@ -0,0 +1,392 @@ +.\" Copyright 1992, 1993 Rickard E. Faith (faith@cs.unc.edu) +.\" May be distributed under the GNU General Public License +.\" Portions of this text are from the README in setserial-2.01.tar.z, +.\" but I can't figure out who wrote that document. If anyone knows, +.\" please tell me +.\" +.\" [tytso:19940519.2239EDT] I did... - Ted Ts'o (tytso@mit.edu) +.\" Sat Aug 27 17:08:38 1994 Changes from Kai Petzke +.\" (wpp@marie.physik.tu-berlin.de) were applied by Rik Faith +.\" (faith@cs.unc.edu) +.\" " +.TH SETSERIAL 8 "27 August 1994" "Setserial 2.10" "Linux Programmer's Manual" +.SH NAME +setserial \- get/set Linux serial port information +.SH SYNOPSIS +.B setserial +.B "[ \-abqvVW ]" +device +.BR "[ " parameter1 " [ " arg " ] ] ..." + +.B "setserial -g" +.B "[ \-abv ]" +device1 ... +.SH DESCRIPTION +.B setserial +is a program designed to set and/or report the configuration information +associated with a serial port. This information includes what I/O +port and IRQ a particular serial port is using, and whether or not the +break key should be interpreted as the Secure Attention Key, and so +on. + +During the normal bootup process, only COM ports 1-4 are initialized, +using the default I/O ports and IRQ values, as listed below. In order +to initialize any additional serial ports, or to change the COM 1-4 +ports to a nonstadard configuration, the +.B setserial +program should be used. Typically it is called from an +.I rc.serial +script, which is usually run out of +.IR /etc/rc.local . + +The +.I device +argument or arguments specifies the serial device which should be configured or +interrogated. It will usually have the following form: +.BR /dev/cua[0-3] . + +If no parameters are specified, +.B setserial +will print out the port type (i.e., 8250, 16450, 16550, 16550A), the +hardware I/O port, the hardware IRQ line, its "baud base," and some of +its operational flags. + +If the +.B \-g +option is given, the arguments to setserial are interpreted as a list +of devices for which the characteristics of those devices should be +printed. + +Without the +.B \-g +option, the first argument to setserial is interpreted as the device +to be modified or characteristics to be printed, and any additional +arguments are interpreted as parameters which should be assigned +to that serial device. + +For the most part, superuser privilege is required to set the +configuration parameters of a serial port. A few serial port parameters +can be set by normal users, however, and these will be noted as +exceptions in this manual page. + +.SH OPTIONS +.B Setserial +accepts the following options: + +.TP +.B \-a +When reporting the configuration of a serial device, print all +available information. +.TP +.B \-b +When reporting the configuration of a serial device, print a summary +of the device's configuration, which might be suitable for printing +during the bootup process, during the /etc/rc script. +.TP +.B \-q +Be quiet. +.B Setserial +will print fewer lines of output. +.TP +.B \-v +Be verbose. +.B Setserial +will print additional status output. +.TP +.B \-V +Display version and exit. +.TP +.B \-W +Do wild interrupt initialization and exit. + +.SH PARAMETERS +The following parameters can be assigned to a serial port. + +All argument values are assumed to be in decimal unless preceeded by "0x". + +.TP +.BR port " port_number" +The +.B port +option sets the I/O port, as described above. +.TP +.BR irq " irq_number" +The +.B irq +option sets the hardware IRQ, as described above. +.TP +.BR uart " uart_type" +This option is used to set the UART type. The permitted types are +.BR none , +8250, 16450, 16550, and 16550A. Since the 8250 and 16450 UARTS do not have +FIFO's, and since the original 16550 have bugs which make the FIFO's unusable, +the FIFO will only be used on chips identifiied as 16550A UARTs. +Setting the UART type to 8250, 16450, or 16550 will enable the serial +port without trying to use the FIFO. Using UART type +.B none +will disable the port. + +Some internal modems are billed as having a "16550A UART with a 1k +buffer". This is a lie. They do not have really have a 16550A +compatible UART; instead what they have is a 16450 compatible UART +with a 1k receive buffer to prevent receiver overruns. This is +important, because they do not have a transmit FIFO. Hence, they are +not compatible with a 16550A UART, and the autoconfiguration process +will correctly identify them as 16450's. If you attempt to override +this using the +.B uart +parameter, you will see dropped characters during file transmissions. +These UART's usually have other problems: the +.B skip_test +parameter also often must be specified. +.TP +.B autoconfig +When this parameter is given, +.B setserial +will ask the kernel to attempt to automatically configure the serial +port. The I/O port must be correctly set; the kernel will attempt to +determine the UART type, and if the +.B auto_irq +parameter is set, Linux will attempt to automatically determine the +IRQ. The +.B autoconfigure +parameter should be given after the +.BR port , auto_irq ", and" skip_test +parameters have been specified. +.TP +.B auto_irq +During autoconfiguration, try to determine the IRQ. This feature is +not guaranteed to always produce the correct result; some hardware +configurations will fool the Linux kernel. It is generally safer not +to use the +.B auto_irq +feature, but rather to specify the IRQ to be used explicitly, using +the +.B irq +parameter. +.TP +.B ^auto_irq +During autoconfiguration, do +.I not +try to determine the IRQ. +.TP +.B skip_test +During autoconfiguration, skip the UART test. Some internal modems do +not have National Semiconductor compatible UART's, but have cheap +imitations instead. Some of these cheasy imitations UART's do not +fully support the loopback detection mode, which is used by the kernel +to make sure there really is a UART at a particular address before +attempting to configure it. So for certain internal modems you will +need to specify this parameter so Linux can initialize the UART +correctly. +.TP +.B ^skip_test +During autoconfiguration, do +.I not +skip the UART test. +.TP +.BR baud_base " baud_base" +This option sets the base baud rate, which is the clock frequency divided +by 16. Normally this value is 115200, which is also the fastest baud +rate which the UART can support. +.TP +.B +spd_hi +Use 57.6kb when the application requests 38.4kb. +This parameter may be specified by a non-privileged user. +.TP +.B spd_vhi +Use 115kb when the application requests 38.4kb. +This parameter may be specified by a non-privileged user. +.TP +.B spd_cust +Use the custom divisor to set the speed when the application requests +38.4kb. In this case, the baud rate is the +.B baud_base +divided by the +.BR divisor . +This parameter may be specified by a non-privileged user. +.TP +.B spd_normal +Use 38.4kb when the application requests 38.4kb. +This parameter may be specified by a non-privileged user. +.TP +.BR divisor " divisor" +This option sets the custom divison. This divisor will be used then the +.B spd_cust +option is selected and the serial port is set to 38.4kb by the +application. +This parameter may be specified by a non-privileged user. +.TP +.B sak +Set the break key at the Secure Attention Key. +.TP +.B ^sak +disable the Secure Attention Key. +.TP +.B fourport +Configure the port as an AST Fourport card. +.TP +.B ^fourport +Disable AST Fourport configuration. +.TP +.BR close_delay " delay" +Specify the amount of time, in hundredths of a second, that DTR should +remain low on a serial line after the callout device is closed, before +the blocked dialin device raises DTR again. The default value of this +option is 50, or a half-second delay. +.TP +.B session_lockout +Lock out callout port (/dev/cuaXX) accesses across different sessions. +That is, once a process has opened a port, do not allow a process with +a different session ID to open that port until the first process has +closed it. +.TP +.B ^session_lockout +Do not lock out callout port accesses across different sessions. +.TP +.B pgrp_lockout +Lock out callout port (/dev/cuaXX) accesses across different process groups. +That is, once a process has opened a port, do not allow a process in a +different process group to open that port until the first process has +closed it. +.TP +.B ^pgrp_lockout +Do not lock out callout port accesses across different process groups. +.TP +.B hup_notify +Notify a process blocked on opening a dial in line when a process has +finished using a callout line (either by closing it or by the serial +line being hung up) by returning EAGAIN to the open. + +The application of this parameter is for getty's which are blocked on +a serial port's dial in line. This allows the getty to reset the +modem (which may have had its configuration modified by the +application using the callout device) before blocking on the open again. +.TP +.B ^hup_notify +Do not notify a process blocked on opening a dial in line when the +callout device is hung up. +.TP +.B split_termios +Treat the termios settings used by the callout device and the termios +settings used by the dialin devices as separate. +.TP +.B ^split_termios +Use the same termios structure to store both the dialin and callout +ports. This is the default option. +.TP +.B callout_nohup +If this particular serial port is opened as a callout device, do not +hangup the tty when carrier detect is dropped. +.TP +.B ^callout_nohup +Do not skip hanging up the tty when a serial port is opened as a +callout device. Of course, the HUPCL termios flag must be enabled if +the hangup is to occur. +.SH CONSIDERATIONS OF CONFIGURING SERIAL PORTS +It is important to note that setserial merely tells the Linux kernel +where it should expect to find the I/O port and IRQ lines of a +particular serial port. It does *not* configure the hardware, the +actual serial board, to use a particular I/O port. In order to do +that, you will need to physically program the serial board, usually by +setting some jumpers or by switching some DIP switches. + +This section will provide some pointers in helping you decide how you +would like to configure your serial ports. + +The "standard MS-DOS" port associations are given below: + +.nf +.RS +/dev/ttyS0 (COM1), port 0x3f8, irq 4 +/dev/ttyS1 (COM2), port 0x2f8, irq 3 +/dev/ttyS2 (COM3), port 0x3e8, irq 4 +/dev/ttyS3 (COM4), port 0x2e8, irq 3 +.RE +.fi + +Due to the limitations in the design of the AT/ISA bus architecture, +normally an IRQ line may not be shared between two or more serial +ports. If you attempt to do this, one or both serial ports will +become unreliable if you try to use both simultaneously. This +limitation can be overcome by special multi-port serial port boards, +which are designed to share multiple serial ports over a single IRQ +line. Multi-port serial cards supported by Linux include the AST +FourPort, the Accent Async board, the Usenet Serial II board, the +Bocaboard BB-1004, BB-1008, and BB-2016 boards, and the HUB-6 serial +board. + +The selection of an alternative IRQ line +is difficult, since most of them are already used. The following table +lists the "standard MS-DOS" assignments of available IRQ lines: + +.nf +.RS +IRQ 3: COM2 +IRQ 4: COM1 +IRQ 5: LPT2 +IRQ 7: LPT1 +.RE +.fi + +Most people find that IRQ 5 is a good choice, assuming that there is +only one parallel port active in the computer. Another good choice is +IRQ 2 (aka IRQ 9); although this IRQ is sometimes used by network +cards, and very rarely VGA cards will be configured to use IRQ 2 as a +vertical retrace interrupt. If your VGA card is configured this way; +try to disable it so you can reclaim that IRQ line for some other +card. It's not necessary for Linux and most other Operating systems. + +The only other available IRQ lines are 3, 4, and 7, and these are +probably used by the other serial and parallel ports. (If your serial +card has a 16bit card edge connector, and supports higher interrupt +numbers, then IRQ 10, 11, 12, and 15 are also available.) + +On AT class machines, IRQ 2 is seen as IRQ 9, and Linux will interpret it +in this manner. + +IRQ's other than 2 (9), 3, 4, 5, 7, 10, 11, 12, and 15, should +.I not +be used, since they are assigned to other hardware and cannot, in general, +be changed. Here are the "standard" assignments: + +.nf +.RS +IRQ 0 Timer channel 0 +IRQ 1 Keyboard +IRQ 2 Cascade for controller 2 +IRQ 3 Serial port 2 +IRQ 4 Serial port 1 +IRQ 5 Parallel port 2 (Reserved in PS/2) +IRQ 6 Floppy diskette +IRQ 7 Parallel port 1 +IRQ 8 Real-time clock +IRQ 9 Redirected to IRQ2 +IRQ 10 Reserved +IRQ 11 Reserved +IRQ 12 Reserved (Auxillary device in PS/2) +IRQ 13 Math coprocessor +IRQ 14 Hard disk controller +IRQ 15 Reserved +.RE +.fi + + +.SH CAUTION +CAUTION: Using an invalid port can lock up your machine. +.SH FILES +.BR /etc/rc.local +.BR /etc/rc.serial +.SH "SEE ALSO" +.BR tty (4), +.BR ttys (4), +kernel/chr_drv/serial.c +.SH AUTHOR +The original version of setserial was written by Rick Sladkey +(jrs@world.std.com), and was modified by Michael K. Johnson +(johnsonm@stolaf.edu). + +This version has since been rewritten from scratch by Theodore Ts'o +(tytso@mit.edu) on 1/1/93. Any bugs or problems are solely his +responsibility. diff --git a/sys-utils/setserial.c b/sys-utils/setserial.c new file mode 100644 index 00000000..71179baa --- /dev/null +++ b/sys-utils/setserial.c @@ -0,0 +1,436 @@ +/* setserial.c - get/set Linux serial port info - rick sladkey */ +/* modified to do work again and added setting fast serial speeds, + Michael K. Johnson, johnsonm@stolaf.edu */ +/* + * Very heavily modified --- almost rewritten from scratch --- to have + * a more flexible command structure. Now able to set any of the + * serial-specific options using the TIOCSSERIAL ioctl(). + * Theodore Ts'o, tytso@mit.edu, 1/1/93 + * + * Last modified: [tytso:19940520.0036EDT] + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#define VERSION_STR "2.10" + +char *progname; + +int verbosity = 1; /* 1 = normal, 0=boot-time, 2=everything */ +int verbose_flag = 0; /* print results after setting a port */ +int quiet_flag = 0; + +struct serial_type_struct { + int id; + char *name; +} serial_type_tbl[] = { + PORT_UNKNOWN, "unknown", + PORT_8250, "8250", + PORT_16450, "16450", + PORT_16550, "16550", + PORT_16550A, "16550A", + PORT_UNKNOWN, "none", + -1, NULL +}; + +#define CMD_FLAG 1 +#define CMD_PORT 2 +#define CMD_IRQ 3 +#define CMD_DIVISOR 4 +#define CMD_TYPE 5 +#define CMD_BASE 6 +#define CMD_DELAY 7 +#define CMD_CONFIG 8 + +#define FLAG_CAN_INVERT 0x0001 +#define FLAG_NEED_ARG 0x0002 + +struct flag_type_table { + int cmd; + char *name; + int bits; + int mask; + int level; + int flags; +} flag_type_tbl[] = { + CMD_FLAG, "spd_normal", 0, ASYNC_SPD_MASK, 2, 0, + CMD_FLAG, "spd_hi", ASYNC_SPD_HI, ASYNC_SPD_MASK, 0, 0, + CMD_FLAG, "spd_vhi", ASYNC_SPD_VHI, ASYNC_SPD_MASK, 0, 0, + CMD_FLAG, "spd_cust", ASYNC_SPD_CUST, ASYNC_SPD_MASK, 0, 0, + + CMD_FLAG, "SAK", ASYNC_SAK, ASYNC_SAK, 0, FLAG_CAN_INVERT, + CMD_FLAG, "Fourport", ASYNC_FOURPORT, ASYNC_FOURPORT, 0, FLAG_CAN_INVERT, + CMD_FLAG, "hup_notify", ASYNC_HUP_NOTIFY, ASYNC_HUP_NOTIFY, 0, FLAG_CAN_INVERT, + CMD_FLAG, "skip_test", ASYNC_SKIP_TEST,ASYNC_SKIP_TEST,2, FLAG_CAN_INVERT, + CMD_FLAG, "auto_irq", ASYNC_AUTO_IRQ, ASYNC_AUTO_IRQ, 2, FLAG_CAN_INVERT, +#ifdef ASYNC_SPLIT_TERMIOS + CMD_FLAG, "split_termios", ASYNC_SPLIT_TERMIOS, ASYNC_SPLIT_TERMIOS, 2, FLAG_CAN_INVERT, +#endif + CMD_FLAG, "session_lockout", ASYNC_SESSION_LOCKOUT, ASYNC_SESSION_LOCKOUT, 2, FLAG_CAN_INVERT, + CMD_FLAG, "pgrp_lockout", ASYNC_PGRP_LOCKOUT, ASYNC_PGRP_LOCKOUT, 2, FLAG_CAN_INVERT, +#ifdef ASYNC_CALLOUT_NOHUP + CMD_FLAG, "callout_nohup", ASYNC_CALLOUT_NOHUP, ASYNC_CALLOUT_NOHUP, 2, FLAG_CAN_INVERT, +#endif + + CMD_PORT, "port", 0, 0, 0, FLAG_NEED_ARG, + CMD_IRQ, "irq", 0, 0, 0, FLAG_NEED_ARG, + CMD_DIVISOR, "divisor", 0, 0, 0, FLAG_NEED_ARG, + CMD_TYPE, "uart", 0, 0, 0, FLAG_NEED_ARG, + CMD_BASE, "base", 0, 0, 0, FLAG_NEED_ARG, + CMD_BASE, "baud_base", 0, 0, 0, FLAG_NEED_ARG, + CMD_DELAY, "close_delay", 0, 0, 0, FLAG_NEED_ARG, + CMD_CONFIG, "autoconfig", 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, +}; + +char *serial_type(int id) +{ + int i; + + for (i = 0; serial_type_tbl[i].id != -1; i++) + if (id == serial_type_tbl[i].id) + return serial_type_tbl[i].name; + return "undefined"; +} + +int uart_type(char *name) +{ + int i; + + for (i = 0; serial_type_tbl[i].id != -1; i++) + if (!strcasecmp(name, serial_type_tbl[i].name)) + return serial_type_tbl[i].id; + return -1; +} + + +int atonum(char *s) +{ + int n; + + while (*s == ' ') + s++; + if (strncmp(s, "0x", 2) == 0 || strncmp(s, "0X", 2) == 0) + sscanf(s + 2, "%x", &n); + else if (s[0] == '0' && s[1]) + sscanf(s + 1, "%o", &n); + else + sscanf(s, "%d", &n); + return n; +} + +void print_flags(struct serial_struct *serinfo, + char *prefix, char *postfix) +{ + struct flag_type_table *p; + int flags; + int first = 1; + + flags = serinfo->flags; + + for (p = flag_type_tbl; p->name; p++) { + if (p->cmd != CMD_FLAG) + continue; + if (verbosity < p->level) + continue; + if ((flags & p->mask) == p->bits) { + if (first) { + printf("%s", prefix); + first = 0; + } else + printf(" "); + printf("%s", p->name); + } + } + + if (!first) + printf("%s", postfix); +} + +void get_serial(char *device) +{ + struct serial_struct serinfo; + int fd; + + if ((fd = open(device, O_RDWR|O_NONBLOCK)) < 0) { + perror(device); + return; + } + if (ioctl(fd, TIOCGSERIAL, &serinfo) < 0) { + perror("Cannot get serial info"); + close(fd); + return; + } + if (serinfo.irq == 9) + serinfo.irq = 2; /* People understand 2 better than 9 */ + if (verbosity==2) { + printf("%s, Line %d, UART: %s, Port: 0x%.4x, IRQ: %d\n", + device, serinfo.line, serial_type(serinfo.type), + serinfo.port, serinfo.irq); + printf("\tBaud_base: %d, close_delay: %d, divisor: %d\n", + serinfo.baud_base, serinfo.close_delay, + serinfo.custom_divisor); + print_flags(&serinfo, "\tFlags: ", ""); + printf("\n\n"); + } else if (verbosity==0) { + if (serinfo.type) { + printf("%s at 0x%.4x (irq = %d) is a %s", + device, serinfo.port, serinfo.irq, + serial_type(serinfo.type)); + print_flags(&serinfo, " (", ")"); + printf("\n"); + } + } else { + printf("%s, UART: %s, Port: 0x%.4x, IRQ: %d", + device, serial_type(serinfo.type), + serinfo.port, serinfo.irq); + print_flags(&serinfo, ", Flags: ", ""); + printf("\n"); + } + close(fd); +} + +void set_serial(char *device, char ** arg) +{ + struct serial_struct old_serinfo, new_serinfo; + struct flag_type_table *p; + int fd; + int do_invert = 0; + char *word; + + + if ((fd = open(device, O_RDWR|O_NONBLOCK)) < 0) { + if (verbosity==0 && errno==ENOENT) + exit(201); + perror(device); + exit(201); + } + if (ioctl(fd, TIOCGSERIAL, &old_serinfo) < 0) { + perror("Cannot get serial info"); + exit(1); + } + new_serinfo = old_serinfo; + while (*arg) { + do_invert = 0; + word = *arg++; + if (*word == '^') { + do_invert++; + word++; + } + for (p = flag_type_tbl; p->name; p++) { + if (!strcasecmp(p->name, word)) + break; + } + if (!p->name) { + fprintf(stderr, "Invalid flag: %s\n", word); + exit(1); + } + if (do_invert && !(p->flags & FLAG_CAN_INVERT)) { + fprintf(stderr, "This flag can not be inverted: %s\n", word); + exit(1); + } + if ((p->flags & FLAG_NEED_ARG) && !*arg) { + fprintf(stderr, "Missing argument for %s\n", word); + exit(1); + } + switch (p->cmd) { + case CMD_FLAG: + new_serinfo.flags &= ~p->mask; + if (!do_invert) + new_serinfo.flags |= p->bits; + break; + case CMD_PORT: + new_serinfo.port = atonum(*arg++); + break; + case CMD_IRQ: + new_serinfo.irq = atonum(*arg++); + break; + case CMD_DIVISOR: + new_serinfo.custom_divisor = atonum(*arg++); + break; + case CMD_TYPE: + new_serinfo.type = uart_type(*arg++); + if (new_serinfo.type < 0) { + fprintf(stderr, "Illegal UART type: %s", *--arg); + exit(1); + } + break; + case CMD_BASE: + new_serinfo.baud_base = atonum(*arg++); + break; + case CMD_DELAY: + new_serinfo.close_delay = atonum(*arg++); + break; + case CMD_CONFIG: + if (ioctl(fd, TIOCSSERIAL, &new_serinfo) < 0) { + perror("Cannot set serial info"); + exit(1); + } + if (ioctl(fd, TIOCSERCONFIG) < 0) { + perror("Cannot autoconfigure port"); + exit(1); + } + if (ioctl(fd, TIOCGSERIAL, &new_serinfo) < 0) { + perror("Cannot get serial info"); + exit(1); + } + break; + default: + fprintf(stderr, "Internal error: unhandled cmd #%d\n", p->cmd); + exit(1); + } + } + if (ioctl(fd, TIOCSSERIAL, &new_serinfo) < 0) { + perror("Cannot set serial info"); + exit(1); + } + close(fd); + if (verbose_flag) + get_serial(device); +} + +void do_wild_intr(char *device) +{ + int fd; + int i, mask; + int wild_mask = -1; + + if ((fd = open(device, O_RDWR|O_NONBLOCK)) < 0) { + perror(device); + exit(1); + } + if (ioctl(fd, TIOCSERSWILD, &wild_mask) < 0) { + perror("Cannot scan for wild interrupts"); + exit(1); + } + if (ioctl(fd, TIOCSERGWILD, &wild_mask) < 0) { + perror("Cannot get wild interrupt mask"); + exit(1); + } + close(fd); + if (quiet_flag) + return; + if (wild_mask) { + printf("Wild interrupts found: "); + for (i=0, mask=1; mask <= wild_mask; i++, mask <<= 1) + if (mask & wild_mask) + printf(" %d", i); + printf("\n"); + } else if (verbose_flag) + printf("No wild interrupts found.\n"); + return; +} + + + + +void usage() +{ + fprintf(stderr, "setserial Version %s\n\n", VERSION_STR); + fprintf(stderr, + "usage: %s serial-device [cmd1 [arg]] ... \n\n", progname); + fprintf(stderr, "Available commands: (* = Takes an argument)\n"); + fprintf(stderr, "\t\t(^ = can be preceded by a '^' to turn off the option)\n"); +fprintf(stderr, "\t* port\t\tset the I/O port\n"); + fprintf(stderr, "\t* irq\t\tset the interrupt\n"); + fprintf(stderr, "\t* uart\t\tset UART type (none, 8250, 16450, 16550, 16550A\n"); + fprintf(stderr, "\t* baud_base\tset base baud rate (CLOCK_FREQ / 16)\n"); + fprintf(stderr, "\t* divisor\tset the custom divisor (see spd_custom)\n"); + fprintf(stderr, "\t* close_delay\tset the amount of time (in 1/100 of a\n"); + fprintf(stderr, "\t\t\t\tsecond) that DTR should be kept low\n"); + fprintf(stderr, "\t\t\t\twhile being closed\n"); + + fprintf(stderr, "\t^ fourport\tconfigure the port as an AST Fourport\n"); + fprintf(stderr, "\t autoconfigure\tautomatically configure the serial port\n"); + fprintf(stderr, "\t^ auto_irq\ttry to determine irq during autoconfiguration\n"); + fprintf(stderr, "\t^ skip_test\tskip UART test during autoconfiguration\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "\t^ sak\t\tset the break key as the Secure Attention Key\n"); + fprintf(stderr, "\t^ session_lockout Lock out callout port across different sessions\n"); + fprintf(stderr, "\t^ pgrp_lockout\tLock out callout port across different process groups\n"); +#ifdef ASYNC_CALLOUT_NOHUP + fprintf(stderr, "\t^ callout_nohup\tDon't hangup the tty when carrier detect drops\n"); +#endif + fprintf(stderr, "\t\t\t\t on the callout device\n"); +#ifdef ASYNC_SPLIT_TERMIOS + fprintf(stderr, "\t^ split_termios Use separate termios for callout and dailin lines\n"); +#endif + fprintf(stderr, "\t^ hup_notify\tNotify a process blocked on opening a dial in line\n"); + fprintf(stderr, "\t\t\t\twhen a process has finished using a callout\n"); + fprintf(stderr, "\t\t\t\tline by returning EAGAIN to the open.\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "\t spd_hi\tuse 56kb instead of 38.4kb\n"); + fprintf(stderr, "\t spd_vhi\tuse 115kb instead of 38.4kb\n"); + fprintf(stderr, "\t spd_cust\tuse the custom divisor to set the speed at 38.4kb\n"); + fprintf(stderr, "\t\t\t\t(baud rate = baud_base / custom_divisor)\n"); + fprintf(stderr, "\t spd_normal\tuse 38.4kb when a buad rate of 38.4kb is selected\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Use a leading '0x' for hex numbers.\n"); + fprintf(stderr, "CAUTION: Using an invalid port can lock up your machine!\n"); + exit(1); +} + +main(int argc, char **argv) +{ + int get_flag = 0, wild_intr_flag = 0; + int c; + extern int optind; + extern char *optarg; + + progname = argv[0]; + if (argc == 1) + usage(); + while ((c = getopt(argc, argv, "abgqvVW")) != EOF) { + switch (c) { + case 'a': + verbosity = 2; + break; + case 'b': + verbosity = 0; + break; + case 'q': + quiet_flag++; + break; + case 'v': + verbose_flag++; + break; + case 'g': + get_flag++; + break; + case 'V': + fprintf(stderr, "setserial version %s\n", VERSION_STR); + exit(0); + case 'W': + wild_intr_flag++; + break; + default: + usage(); + } + } + if (get_flag) { + argv += optind; + while (*argv) + get_serial(*argv++); + exit(0); + } + if (argc == optind) + usage(); + if (wild_intr_flag) { + do_wild_intr(argv[optind]); + exit(0); + } + if (argc-optind == 1) + get_serial(argv[optind]); + else + set_serial(argv[optind], argv+optind+1); + exit(0); +} + diff --git a/sys-utils/setsid.8 b/sys-utils/setsid.8 new file mode 100644 index 00000000..59ee0a20 --- /dev/null +++ b/sys-utils/setsid.8 @@ -0,0 +1,15 @@ +.\" Rick Sladkey +.\" In the public domain. +.\" Path modifications by faith@cs.unc.edu +.TH SETSID 8 "20 November 1993" "Linux 0.99" "Linux Programmer's Manual" +.SH NAME +setsid \- run a program in a new session +.SH SYNOPSIS +.BI setsid " program" " [ " "arg ..." " ]" +.SH DESCRIPTION +.B setsid +runs a program in a new session. +.SH "SEE ALSO" +.BR setsid (2) +.SH AUTHOR +Rick Sladkey diff --git a/sys-utils/setsid.c b/sys-utils/setsid.c new file mode 100644 index 00000000..10f1501d --- /dev/null +++ b/sys-utils/setsid.c @@ -0,0 +1,25 @@ +/* + * setsid.c -- execute a command in a new session + * Rick Sladkey + * In the public domain. + */ + +#include +#include +#include + +int main(int argc, char *argv[]) +{ + if (argc < 2) { + fprintf(stderr, "usage: %s program [arg ...]\n", + argv[0]); + exit(1); + } + if (setsid() < 0) { + perror("setsid"); + exit(1); + } + execvp(argv[1], argv + 1); + perror("execvp"); + exit(1); +} diff --git a/sys-utils/sln.c b/sys-utils/sln.c new file mode 100644 index 00000000..257b4342 --- /dev/null +++ b/sys-utils/sln.c @@ -0,0 +1,72 @@ +/* `sln' program to create links between files. + Copyright (C) 1986, 1989, 1990, 1991, 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. */ + +/* Written by Mike Parker and David MacKenzie. */ + +#include +#include +#include +#include + +#if !defined(S_ISDIR) && defined(S_IFDIR) +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif + +int +main (argc, argv) + int argc; + char **argv; +{ + struct stat stats; + + if (argc != 3) return 1; + + /* Destination must not be a directory. */ +#if 0 + if (stat (argv [2], &stats) == 0 && S_ISDIR (stats.st_mode)) + { + return 1; + } +#endif + + if (lstat (argv [2], &stats) == 0) + { + if (S_ISDIR (stats.st_mode)) + { + return 1; + } + else if (unlink (argv [2]) && errno != ENOENT) + { + return 1; + } + } + else if (errno != ENOENT) + { + return 1; + } + +#ifdef S_ISLNK + if (symlink (argv [1], argv [2]) == 0) +#else + if (link (argv [1], argv [2]) == 0) +#endif + { + return 0; + } + + return 1; +} diff --git a/sys-utils/swapdev.8 b/sys-utils/swapdev.8 new file mode 100644 index 00000000..901bd759 --- /dev/null +++ b/sys-utils/swapdev.8 @@ -0,0 +1 @@ +.so man8/rdev.8 diff --git a/sys-utils/sync.8 b/sys-utils/sync.8 new file mode 100644 index 00000000..f8bb704f --- /dev/null +++ b/sys-utils/sync.8 @@ -0,0 +1,38 @@ +.\" Copyright 1992, 1993 Rickard E. Faith (faith@cs.unc.edu) +.\" May be distributed under the GNU General Public License +.TH SYNC 8 "20 November 1993" "Linux 0.99" "Linux Programmer's Manual" +.SH NAME +sync \- flush Linux filesystem buffers +.SH SYNOPSIS +.B sync +.SH DESCRIPTION +.B sync +executes +.BR sync (2), +which flushes the filesystem buffers to disk. +.B sync +should be called before the processor is halted in an unusual manner (i.e., +before causing a kernel panic when debugging new kernel code). In general, +the processor should be halted using the +.BR reboot "(8), or " halt (8) +commands, which will attempt to put the system in a quiescent state before +calling +.BR sync (2). + +From Linus: "Note that +.B sync +is only guaranteed to schedule the dirty blocks for writing: it can +actually take a short time before all the blocks are finally written. If +you are doing the +.B sync +with the expectation of killing the machine soon after, please take this +into account and sleep for a few seconds. [The +.BR reboot (8) +command takes these precautions.] +.SH "SEE ALSO" +.BR sync (2), +.BR update (8), +.BR reboot (8), +.BR halt (8) +.SH AUTHOR +Linus Torvalds (torvalds@cs.helsinki.fi) diff --git a/sys-utils/sync.S b/sys-utils/sync.S new file mode 100644 index 00000000..2fa8f566 --- /dev/null +++ b/sys-utils/sync.S @@ -0,0 +1,21 @@ +/* File: + * sync.S + * Compile: + * /lib/cpp sync.S > sync.s + * as -o sync.o sync.s + * ld -s -N -e _main sync.o -o sync + * Author: + * Nick Holloway, with thanks to James Bonfield + * Last Changed: + * September 1993. + */ +# include + + .text + .globl _main +_main: + movl $(SYS_sync),%eax /* sync () */ + int $0x80 + movl $(SYS_exit),%eax /* exit ( 0 ) */ + movl $0,%ebx + int $0x80 diff --git a/sys-utils/tunelp.8 b/sys-utils/tunelp.8 new file mode 100644 index 00000000..55911845 --- /dev/null +++ b/sys-utils/tunelp.8 @@ -0,0 +1,84 @@ +.\" This file Copyright 1992 Michael K. Johnson (johnsonm@nigel.vnet.net) +.\" It may be distributed under the GNU Public License, version 2, or +.\" any higher version. See section COPYING of the GNU Public license +.\" for conditions under which this file may be redistributed. +.\" tunelp.8,v 1.1.1.1 1995/02/22 19:09:12 faith Exp +.TH tunelp 8 "26 August 1992" "Cohesive Systems" "Linux Programmer's Manual" +.SH NAME +tunelp \- set various parameters for the lp device +.SH SYNOPSIS +\fBtunelp\fP \fI\fP [-i \fI\fP | -t \fI