summaryrefslogtreecommitdiffstats
path: root/login-utils
diff options
context:
space:
mode:
authorKarel Zak2006-12-07 00:25:32 +0100
committerKarel Zak2006-12-07 00:25:32 +0100
commit6dbe3af945a63f025561abb83275cee9ff06c57b (patch)
tree19e59eac8ac465b5bc409b5adf815b582c92f633 /login-utils
downloadkernel-qcow2-util-linux-6dbe3af945a63f025561abb83275cee9ff06c57b.tar.gz
kernel-qcow2-util-linux-6dbe3af945a63f025561abb83275cee9ff06c57b.tar.xz
kernel-qcow2-util-linux-6dbe3af945a63f025561abb83275cee9ff06c57b.zip
Imported from util-linux-2.2 tarball.
Diffstat (limited to 'login-utils')
-rw-r--r--login-utils/Makefile112
-rw-r--r--login-utils/README.admutil162
-rw-r--r--login-utils/README.getty26
-rw-r--r--login-utils/README.poeigl440
-rw-r--r--login-utils/agetty.8241
-rw-r--r--login-utils/agetty.c1099
-rw-r--r--login-utils/chfn.166
-rw-r--r--login-utils/chfn.c414
-rw-r--r--login-utils/chsh.151
-rw-r--r--login-utils/chsh.c313
-rw-r--r--login-utils/fastboot.81
-rw-r--r--login-utils/fasthalt.81
-rw-r--r--login-utils/halt.81
-rw-r--r--login-utils/islocal.c34
-rw-r--r--login-utils/last.159
-rw-r--r--login-utils/last.c438
-rw-r--r--login-utils/login.1131
-rw-r--r--login-utils/login.c1007
-rw-r--r--login-utils/mesg.124
-rw-r--r--login-utils/mesg.c44
-rw-r--r--login-utils/newgrp.124
-rw-r--r--login-utils/newgrp.c95
-rw-r--r--login-utils/passwd.136
-rw-r--r--login-utils/passwd.c193
-rw-r--r--login-utils/reboot.81
-rw-r--r--login-utils/setpwnam.c209
-rw-r--r--login-utils/shutdown.8112
-rw-r--r--login-utils/shutdown.c435
-rw-r--r--login-utils/simpleinit.8142
-rw-r--r--login-utils/simpleinit.c445
-rw-r--r--login-utils/ttymsg.c173
-rw-r--r--login-utils/vipw.872
-rw-r--r--login-utils/vipw.c252
-rw-r--r--login-utils/wall.165
-rw-r--r--login-utils/wall.c202
35 files changed, 7120 insertions, 0 deletions
diff --git a/login-utils/Makefile b/login-utils/Makefile
new file mode 100644
index 000000000..88e0b8220
--- /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 <bjdouma@xs4all.nl> 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 000000000..789252d10
--- /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
+ <alvaro@enano.etsit.upm.es>, 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 <gentzel@nova.enet.dec.com>
+ 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 <dholland@husc.harvard.edu> 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 <ruokonen@taivas.lut.fi> 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 <ruokonen@taivas.lut.fi> 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.
+
+ <flebbe@cygnus.tat.physik.uni-tuebingen.de> 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 <jrs@world.std.com> provided patches for better
+ umounting code, needed in connection with NFS.
+
+ Remy Card <card@masi.ibp.fr> 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 000000000..4e32faa03
--- /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,
+<jh@moon.nbn.com>), 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 000000000..f6f8933c4
--- /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 <quinlan@yggdrasil.com> and Ross Biro
+ <biro@yggdrasil.com> 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 <st@epcc.ed.ac.uk> 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 <jeremy@suite.sw.oz.au>
+ 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 <janl@math.uio.no>
+
+ 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 <HYVATTI@cc.helsinki.fi>.
+
+ 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 <bill@goshawk.lanl.gov> 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 <roques@juliet.ka.sub.org> provided a patch
+ that cleans up the handling of the -L option on agetty.
+ Rik Faith <faith@cs.unc.edu> 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 <dthumim@mit.edu> 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 <st@epcc.ed.ac.uk>.
+
+ 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 <almesber@bernina.ethz.ch>
+ 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 <arumble@extro.ucc.su.OZ.AU> 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 <ssd@nevets.oau.org> 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 <sander@kozmix.hacktic.nl> 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 <miquels@maestro.htsa.aha.nl>
+ 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 <hostname> 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 000000000..3f3cf6ada
--- /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: "<junk><speed><junk>".
+\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 "<n> users" where <n> 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 <wietse@wzv.win.tue.nl>
+Eindhoven University of Technology
+Department of Mathematics and Computer Science
+Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands
+
+Peter Orbaek <poe@daimi.aau.dk>
+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 000000000..a8cd45db8
--- /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 <poe@daimi.aau.dk>
+ 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 <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <termio.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <varargs.h>
+#include <ctype.h>
+#include <utmp.h>
+#include <getopt.h>
+#include <memory.h>
+#include <sys/file.h>
+
+#ifdef linux
+#include "pathnames.h"
+#include <sys/param.h>
+#define USE_SYSLOG
+#endif
+
+ /* If USE_SYSLOG is undefined all diagnostics go directly to /dev/console. */
+
+#ifdef USE_SYSLOG
+#include <syslog.h>
+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 <sys/utsname.h>
+#include <time.h>
+#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:
+ *
+ * <junk><number><junk>
+ *
+ * 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 000000000..9be9fff0a
--- /dev/null
+++ b/login-utils/chfn.1
@@ -0,0 +1,66 @@
+.\"
+.\" chfn.1 -- change your finger information
+.\" (c) 1994 by salvatore valente <svalente@athena.mit.edu>
+.\"
+.\" 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 <svalente@mit.edu>
diff --git a/login-utils/chfn.c b/login-utils/chfn.c
new file mode 100644
index 000000000..2effa85d2
--- /dev/null
+++ b/login-utils/chfn.c
@@ -0,0 +1,414 @@
+/*
+ * chfn.c -- change your finger information
+ * (c) 1994 by salvatore valente <svalente@athena.mit.edu>
+ *
+ * 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 <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <errno.h>
+#include <ctype.h>
+#include <getopt.h>
+
+#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 000000000..ec278fb4f
--- /dev/null
+++ b/login-utils/chsh.1
@@ -0,0 +1,51 @@
+.\"
+.\" chsh.1 -- change your login shell
+.\" (c) 1994 by salvatore valente <svalente@athena.mit.edu>
+.\"
+.\" 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 <svalente@mit.edu>
diff --git a/login-utils/chsh.c b/login-utils/chsh.c
new file mode 100644
index 000000000..9a9a52e85
--- /dev/null
+++ b/login-utils/chsh.c
@@ -0,0 +1,313 @@
+/*
+ * chsh.c -- change your login shell
+ * (c) 1994 by salvatore valente <svalente@athena.mit.edu>
+ *
+ * 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 <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <errno.h>
+#include <ctype.h>
+#include <getopt.h>
+
+#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 000000000..386d9715a
--- /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 000000000..386d9715a
--- /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 000000000..386d9715a
--- /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 000000000..a4cfb16ab
--- /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 <stdio.h>
+#include <string.h>
+
+#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 000000000..44e6b8270
--- /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 000000000..c00808c2e
--- /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 <sys/param.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <signal.h>
+#include <string.h>
+#include <time.h>
+#include <utmp.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#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 000000000..e6e30d82a
--- /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 000000000..f0130f8ed
--- /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 <sys/param.h>
+#endif
+
+#include <ctype.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/file.h>
+#include <termios.h>
+#include <string.h>
+#define index strchr
+#define rindex strrchr
+#include <sys/ioctl.h>
+#include <signal.h>
+#include <errno.h>
+#include <grp.h>
+#include <pwd.h>
+#include <setjmp.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/syslog.h>
+#include <sys/sysmacros.h>
+#include <netdb.h>
+
+#ifdef TESTING
+# include "utmp.h"
+#else
+# include <utmp.h>
+#endif
+
+#ifdef SHADOW_PWD
+#include <shadow.h>
+#endif
+
+#ifndef linux
+#include <tzfile.h>
+#include <lastlog.h>
+#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 <kerberos/krb.h>
+#include <sys/termios.h>
+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, &ltc);
+ (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 000000000..81932dfd9
--- /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 000000000..07c5fad1f
--- /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 <stdio.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+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 000000000..032a5d5aa
--- /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 000000000..ba13fdeb1
--- /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 <unistd.h>
+#include <pwd.h>
+#include <grp.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#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 000000000..d22c458fa
--- /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 000000000..5bd6d3abd
--- /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 <poe@daimi.aau.dk>
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <termios.h>
+#include <pwd.h>
+#include <ctype.h>
+#include <time.h>
+#include <string.h>
+
+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 000000000..386d9715a
--- /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 000000000..f7e6eb312
--- /dev/null
+++ b/login-utils/setpwnam.c
@@ -0,0 +1,209 @@
+/*
+ * setpwnam.c --
+ * edit an entry in a password database.
+ *
+ * (c) 1994 Salvatore Valente <svalente@mit.edu>
+ * 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 <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <errno.h>
+#ifdef BSD43
+#include <sys/file.h>
+#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 000000000..78eb984b0
--- /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 000000000..0ca303988
--- /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 <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <utmp.h>
+#include <time.h>
+#include <string.h>
+#include <ctype.h>
+#include <signal.h>
+#include <sys/param.h>
+#include <termios.h>
+#include <mntent.h>
+#include <sys/mount.h>
+#include <sys/wait.h>
+#include <syslog.h>
+#include <sys/resource.h>
+#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 000000000..a506e1ae9
--- /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 000000000..9b31c15e4
--- /dev/null
+++ b/login-utils/simpleinit.c
@@ -0,0 +1,445 @@
+/* simpleinit.c - poe@daimi.aau.dk */
+/* Version 1.21 */
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <limits.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <signal.h>
+#include <pwd.h>
+#include <sys/file.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <utmp.h>
+#ifdef SHADOW_PWD
+#include <shadow.h>
+#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 000000000..40c178df2
--- /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 <sys/types.h>
+#include <sys/uio.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <errno.h>
+#include <paths.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+/*
+ * 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 000000000..23d84ad69
--- /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 000000000..f36df5f2e
--- /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 <sys/types.h>
+#include <sys/stat.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <sys/resource.h>
+#include <sys/file.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <paths.h>
+#include <unistd.h>
+
+#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 000000000..788f5f2b7
--- /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 000000000..b10badbac
--- /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 <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <utmp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <paths.h>
+
+#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);
+}