From: Karel Zak Date: Wed, 6 Dec 2006 23:25:32 +0000 (+0100) Subject: Imported from util-linux-2.2 tarball. X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6dbe3af945a63f025561abb83275cee9ff06c57b;p=util-linux Imported from util-linux-2.2 tarball. --- 6dbe3af945a63f025561abb83275cee9ff06c57b 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