diff options
author | Karel Zak | 2006-12-07 00:25:32 +0100 |
---|---|---|
committer | Karel Zak | 2006-12-07 00:25:32 +0100 |
commit | 6dbe3af945a63f025561abb83275cee9ff06c57b (patch) | |
tree | 19e59eac8ac465b5bc409b5adf815b582c92f633 /login-utils | |
download | kernel-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')
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, <c); + (void)ioctl(0, TIOCSETC, &tc); + (void)ioctl(0, TIOCSETP, &sgttyb); + + /* + * Be sure that we're in + * blocking mode!!! + * This is really for HPUX + */ + ioctlval = 0; + (void)ioctl(0, FIOSNBIO, &ioctlval); +#endif + + for (cnt = getdtablesize(); cnt > 2; cnt--) + close(cnt); + + ttyn = ttyname(0); + if (ttyn == NULL || *ttyn == '\0') { + (void)sprintf(tname, "%s??", _PATH_TTY); + ttyn = tname; + } + + setpgrp(); + + { + struct termios tt, ttt; + + tcgetattr(0, &tt); + ttt = tt; + ttt.c_cflag &= ~HUPCL; + + if((chown(ttyn, 0, 0) == 0) && (chmod(ttyn, 0622) == 0)) { + tcsetattr(0,TCSAFLUSH,&ttt); + signal(SIGHUP, SIG_IGN); /* so vhangup() wont kill us */ + vhangup(); + signal(SIGHUP, SIG_DFL); + } + + setsid(); + + /* re-open stdin,stdout,stderr after vhangup() closed them */ + /* if it did, after 0.99.5 it doesn't! */ + opentty(ttyn); + tcsetattr(0,TCSAFLUSH,&tt); + } + + if (tty = rindex(ttyn, '/')) + ++tty; + else + tty = ttyn; + + openlog("login", LOG_ODELAY, LOG_AUTH); + + for (cnt = 0;; ask = 1) { + ioctlval = 0; +#ifndef linux + (void)ioctl(0, TIOCSETD, &ioctlval); +#endif + + if (ask) { + fflag = 0; + getloginname(); + } + + checktty(username, tty); + + (void)strcpy(tbuf, username); + if (pwd = getpwnam(username)) + salt = pwd->pw_passwd; + else + salt = "xx"; + + /* if user not super-user, check for disabled logins */ + if (pwd == NULL || pwd->pw_uid) + checknologin(); + + /* + * Disallow automatic login to root; if not invoked by + * root, disallow if the uid's differ. + */ + if (fflag && pwd) { + int uid = getuid(); + + passwd_req = pwd->pw_uid == 0 || + (uid && uid != pwd->pw_uid); + } + + /* + * If trying to log in as root, but with insecure terminal, + * refuse the login attempt. + */ + if (pwd && pwd->pw_uid == 0 && !rootterm(tty)) { + (void)fprintf(stderr, + "%s login refused on this terminal.\n", + pwd->pw_name); + + if (hostname) + syslog(LOG_NOTICE, + "LOGIN %s REFUSED FROM %s ON TTY %s", + pwd->pw_name, hostname, tty); + else + syslog(LOG_NOTICE, + "LOGIN %s REFUSED ON TTY %s", + pwd->pw_name, tty); + continue; + } + + /* + * If no pre-authentication and a password exists + * for this user, prompt for one and verify it. + */ + if (!passwd_req || (pwd && !*pwd->pw_passwd)) + break; + + setpriority(PRIO_PROCESS, 0, -4); + pp = getpass("Password: "); + p = crypt(pp, salt); + setpriority(PRIO_PROCESS, 0, 0); + +#ifdef KERBEROS + + /* + * If not present in pw file, act as we normally would. + * If we aren't Kerberos-authenticated, try the normal + * pw file for a password. If that's ok, log the user + * in without issueing any tickets. + */ + + if (pwd && !krb_get_lrealm(realm,1)) { + /* + * get TGT for local realm; be careful about uid's + * here for ticket file ownership + */ + (void)setreuid(geteuid(),pwd->pw_uid); + kerror = krb_get_pw_in_tkt(pwd->pw_name, "", realm, + "krbtgt", realm, DEFAULT_TKT_LIFE, pp); + (void)setuid(0); + if (kerror == INTK_OK) { + memset(pp, 0, strlen(pp)); + notickets = 0; /* user got ticket */ + break; + } + } +#endif + (void) memset(pp, 0, strlen(pp)); + if (pwd && !strcmp(p, pwd->pw_passwd)) + break; + + (void)printf("Login incorrect\n"); + failures++; + badlogin(username); /* log ALL bad logins */ + + /* we allow 10 tries, but after 3 we start backing off */ + if (++cnt > 3) { + if (cnt >= 10) { + sleepexit(1); + } + sleep((unsigned int)((cnt - 3) * 5)); + } + } + + /* committed to login -- turn off timeout */ + (void)alarm((unsigned int)0); + +#ifdef HAVE_QUOTA + if (quota(Q_SETUID, pwd->pw_uid, 0, 0) < 0 && errno != EINVAL) { + switch(errno) { + case EUSERS: + (void)fprintf(stderr, + "Too many users logged on already.\nTry again later.\n"); + break; + case EPROCLIM: + (void)fprintf(stderr, + "You have too many processes running.\n"); + break; + default: + perror("quota (Q_SETUID)"); + } + sleepexit(0); + } +#endif + + /* paranoia... */ + endpwent(); + + /* This requires some explanation: As root we may not be able to + read the directory of the user if it is on an NFS mounted + filesystem. We temporarily set our effective uid to the user-uid + making sure that we keep root privs. in the real uid. + + A portable solution would require a fork(), but we rely on Linux + having the BSD setreuid() */ + + { + char tmpstr[MAXPATHLEN]; + uid_t ruid = getuid(); + gid_t egid = getegid(); + + strncpy(tmpstr, pwd->pw_dir, MAXPATHLEN-12); + strncat(tmpstr, ("/" _PATH_HUSHLOGIN), MAXPATHLEN); + + setregid(-1, pwd->pw_gid); + setreuid(0, pwd->pw_uid); + quietlog = (access(tmpstr, R_OK) == 0); + setuid(0); /* setreuid doesn't do it alone! */ + setreuid(ruid, 0); + setregid(-1, egid); + } + +#ifndef linux +#ifdef KERBEROS + if (notickets && !quietlog) + (void)printf("Warning: no Kerberos tickets issued\n"); +#endif + +#define TWOWEEKS (14*24*60*60) + if (pwd->pw_change || pwd->pw_expire) + (void)gettimeofday(&tp, (struct timezone *)NULL); + if (pwd->pw_change) + if (tp.tv_sec >= pwd->pw_change) { + (void)printf("Sorry -- your password has expired.\n"); + sleepexit(1); + } + else if (tp.tv_sec - pwd->pw_change < TWOWEEKS && !quietlog) { + ttp = localtime(&pwd->pw_change); + (void)printf("Warning: your password expires on %s %d, %d\n", + months[ttp->tm_mon], ttp->tm_mday, TM_YEAR_BASE + ttp->tm_year); + } + if (pwd->pw_expire) + if (tp.tv_sec >= pwd->pw_expire) { + (void)printf("Sorry -- your account has expired.\n"); + sleepexit(1); + } + else if (tp.tv_sec - pwd->pw_expire < TWOWEEKS && !quietlog) { + ttp = localtime(&pwd->pw_expire); + (void)printf("Warning: your account expires on %s %d, %d\n", + months[ttp->tm_mon], ttp->tm_mday, TM_YEAR_BASE + ttp->tm_year); + } + + /* nothing else left to fail -- really log in */ + { + struct utmp utmp; + + memset((char *)&utmp, 0, sizeof(utmp)); + (void)time(&utmp.ut_time); + strncpy(utmp.ut_name, username, sizeof(utmp.ut_name)); + if (hostname) + strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host)); + strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line)); + login(&utmp); + } +#else + /* for linux, write entries in utmp and wtmp */ + { + struct utmp ut; + char *ttyabbrev; + int wtmp; + + memset((char *)&ut, 0, sizeof(ut)); + ut.ut_type = USER_PROCESS; + ut.ut_pid = getpid(); + strncpy(ut.ut_line, ttyn + sizeof("/dev/")-1, sizeof(ut.ut_line)); + ttyabbrev = ttyn + sizeof("/dev/tty") - 1; + strncpy(ut.ut_id, ttyabbrev, sizeof(ut.ut_id)); + (void)time(&ut.ut_time); + strncpy(ut.ut_user, username, sizeof(ut.ut_user)); + + /* fill in host and ip-addr fields when we get networking */ + if (hostname) { + struct hostent *he; + + strncpy(ut.ut_host, hostname, sizeof(ut.ut_host)); + if ((he = gethostbyname(hostname))) + memcpy(&ut.ut_addr, he->h_addr_list[0], + sizeof(ut.ut_addr)); + } + + utmpname(_PATH_UTMP); + setutent(); + pututline(&ut); + endutent(); + + if((wtmp = open(_PATH_WTMP, O_APPEND|O_WRONLY)) >= 0) { + flock(wtmp, LOCK_EX); + write(wtmp, (char *)&ut, sizeof(ut)); + flock(wtmp, LOCK_UN); + close(wtmp); + } + } + /* fix_utmp_type_and_user(username, ttyn, LOGIN_PROCESS); */ +#endif + + dolastlog(quietlog); + +#ifndef linux + if (!hflag) { /* XXX */ + static struct winsize win = { 0, 0, 0, 0 }; + + (void)ioctl(0, TIOCSWINSZ, &win); + } +#endif + (void)chown(ttyn, pwd->pw_uid, + (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid); + + (void)chmod(ttyn, 0622); + (void)setgid(pwd->pw_gid); + + initgroups(username, pwd->pw_gid); + +#ifdef HAVE_QUOTA + quota(Q_DOWARN, pwd->pw_uid, (dev_t)-1, 0); +#endif + + if (*pwd->pw_shell == '\0') + pwd->pw_shell = _PATH_BSHELL; +#ifndef linux + /* turn on new line discipline for the csh */ + else if (!strcmp(pwd->pw_shell, _PATH_CSHELL)) { + ioctlval = NTTYDISC; + (void)ioctl(0, TIOCSETD, &ioctlval); + } +#endif + + /* preserve TERM even without -p flag */ + { + char *ep; + + if(!((ep = getenv("TERM")) && (termenv = strdup(ep)))) + termenv = "dumb"; + } + + /* destroy environment unless user has requested preservation */ + if (!pflag) + { + environ = (char**)malloc(sizeof(char*)); + memset(environ, 0, sizeof(char*)); + } + +#ifndef linux + (void)setenv("HOME", pwd->pw_dir, 1); + (void)setenv("SHELL", pwd->pw_shell, 1); + if (term[0] == '\0') + strncpy(term, stypeof(tty), sizeof(term)); + (void)setenv("TERM", term, 0); + (void)setenv("USER", pwd->pw_name, 1); + (void)setenv("PATH", _PATH_DEFPATH, 0); +#else + (void)setenv("HOME", pwd->pw_dir, 0); /* legal to override */ + if(pwd->pw_uid) + (void)setenv("PATH", _PATH_DEFPATH, 1); + else + (void)setenv("PATH", _PATH_DEFPATH_ROOT, 1); + (void)setenv("SHELL", pwd->pw_shell, 1); + (void)setenv("TERM", termenv, 1); + + /* mailx will give a funny error msg if you forget this one */ + (void)sprintf(tmp,"%s/%s",_PATH_MAILDIR,pwd->pw_name); + (void)setenv("MAIL",tmp,0); + + /* LOGNAME is not documented in login(1) but + HP-UX 6.5 does it. We'll not allow modifying it. + */ + (void)setenv("LOGNAME", pwd->pw_name, 1); +#endif + +#ifndef linux + if (tty[sizeof("tty")-1] == 'd') + syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name); +#endif + if (pwd->pw_uid == 0) + if (hostname) + syslog(LOG_NOTICE, "ROOT LOGIN ON %s FROM %s", + tty, hostname); + else + syslog(LOG_NOTICE, "ROOT LOGIN ON %s", tty); + + if (!quietlog) { + struct stat st; + + motd(); + (void)sprintf(tbuf, "%s/%s", _PATH_MAILDIR, pwd->pw_name); + if (stat(tbuf, &st) == 0 && st.st_size != 0) + (void)printf("You have %smail.\n", + (st.st_mtime > st.st_atime) ? "new " : ""); + } + + (void)signal(SIGALRM, SIG_DFL); + (void)signal(SIGQUIT, SIG_DFL); + (void)signal(SIGINT, SIG_DFL); + (void)signal(SIGTSTP, SIG_IGN); + (void)signal(SIGHUP, SIG_DFL); + + /* discard permissions last so can't get killed and drop core */ + if(setuid(pwd->pw_uid) < 0 && pwd->pw_uid) { + syslog(LOG_ALERT, "setuid() failed"); + exit(1); + } + + /* wait until here to change directory! */ + if (chdir(pwd->pw_dir) < 0) { + (void)printf("No directory %s!\n", pwd->pw_dir); + if (chdir("/")) + exit(0); + pwd->pw_dir = "/"; + (void)printf("Logging in with home = \"/\".\n"); + } + + /* if the shell field has a space: treat it like a shell script */ + if (strchr(pwd->pw_shell, ' ')) { + char *buff = malloc(strlen(pwd->pw_shell) + 6); + if (buff) { + strcpy(buff, "exec "); + strcat(buff, pwd->pw_shell); + execlp("/bin/sh", "-sh", "-c", buff, (char *)0); + fprintf(stderr, "login: couldn't exec shell script: %s.\n", + strerror(errno)); + exit(0); + } + fprintf(stderr, "login: no memory for shell script.\n"); + exit(0); + } + + tbuf[0] = '-'; + strcpy(tbuf + 1, ((p = rindex(pwd->pw_shell, '/')) ? + p + 1 : pwd->pw_shell)); + + execlp(pwd->pw_shell, tbuf, (char *)0); + (void)fprintf(stderr, "login: no shell: %s.\n", strerror(errno)); + exit(0); +} + +void +getloginname() +{ + register int ch; + register char *p; + static char nbuf[UT_NAMESIZE + 1]; + + for (;;) { + (void)printf("\n%s login: ", thishost); fflush(stdout); + for (p = nbuf; (ch = getchar()) != '\n'; ) { + if (ch == EOF) { + badlogin(username); + exit(0); + } + if (p < nbuf + UT_NAMESIZE) + *p++ = ch; + } + if (p > nbuf) + if (nbuf[0] == '-') + (void)fprintf(stderr, + "login names may not start with '-'.\n"); + else { + *p = '\0'; + username = nbuf; + break; + } + } +} + +void timedout() +{ + struct termio ti; + + (void)fprintf(stderr, "Login timed out after %d seconds\n", timeout); + + /* reset echo */ + (void) ioctl(0, TCGETA, &ti); + ti.c_lflag |= ECHO; + (void) ioctl(0, TCSETA, &ti); + exit(0); +} + +int +rootterm(ttyn) + char *ttyn; +#ifndef linux +{ + struct ttyent *t; + + return((t = getttynam(ttyn)) && t->ty_status&TTY_SECURE); +} +#else +{ + int fd; + char buf[100],*p; + int cnt, more; + + fd = open(SECURETTY, O_RDONLY); + if(fd < 0) return 1; + + /* read each line in /etc/securetty, if a line matches our ttyline + then root is allowed to login on this tty, and we should return + true. */ + for(;;) { + p = buf; cnt = 100; + while(--cnt >= 0 && (more = read(fd, p, 1)) == 1 && *p != '\n') p++; + if(more && *p == '\n') { + *p = '\0'; + if(!strcmp(buf, ttyn)) { + close(fd); + return 1; + } else + continue; + } else { + close(fd); + return 0; + } + } +} +#endif + +jmp_buf motdinterrupt; + +void +motd() +{ + register int fd, nchars; + void (*oldint)(), sigint(); + char tbuf[8192]; + + if ((fd = open(_PATH_MOTDFILE, O_RDONLY, 0)) < 0) + return; + oldint = signal(SIGINT, sigint); + if (setjmp(motdinterrupt) == 0) + while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0) + (void)write(fileno(stdout), tbuf, nchars); + (void)signal(SIGINT, oldint); + (void)close(fd); +} + +void sigint() +{ + longjmp(motdinterrupt, 1); +} + +void +checknologin() +{ + register int fd, nchars; + char tbuf[8192]; + + if ((fd = open(_PATH_NOLOGIN, O_RDONLY, 0)) >= 0) { + while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0) + (void)write(fileno(stdout), tbuf, nchars); + sleepexit(0); + } +} + +void +dolastlog(quiet) + int quiet; +{ + struct lastlog ll; + int fd; + + if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) { + (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET); + if (!quiet) { + if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) && + ll.ll_time != 0) { + (void)printf("Last login: %.*s ", + 24-5, (char *)ctime(&ll.ll_time)); + + if (*ll.ll_host != '\0') + printf("from %.*s\n", + (int)sizeof(ll.ll_host), ll.ll_host); + else + printf("on %.*s\n", + (int)sizeof(ll.ll_line), ll.ll_line); + } + (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET); + } + memset((char *)&ll, 0, sizeof(ll)); + (void)time(&ll.ll_time); + strncpy(ll.ll_line, tty, sizeof(ll.ll_line)); + if (hostname) + strncpy(ll.ll_host, hostname, sizeof(ll.ll_host)); + (void)write(fd, (char *)&ll, sizeof(ll)); + (void)close(fd); + } +} + +void +badlogin(name) + char *name; +{ + if (failures == 0) + return; + + if (hostname) + syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s, %s", + failures, failures > 1 ? "S" : "", hostname, name); + else + syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s, %s", + failures, failures > 1 ? "S" : "", tty, name); +} + +#undef UNKNOWN +#define UNKNOWN "su" + +#ifndef linux +char * +stypeof(ttyid) + char *ttyid; +{ + struct ttyent *t; + + return(ttyid && (t = getttynam(ttyid)) ? t->ty_type : UNKNOWN); +} +#endif + +void +checktty(user, tty) + char *user; + char *tty; +{ + FILE *f; + char buf[256]; + char *ptr; + char devname[50]; + struct stat stb; + + /* no /etc/usertty, default to allow access */ + if(!(f = fopen(_PATH_USERTTY, "r"))) return; + + while(fgets(buf, 255, f)) { + + /* strip comments */ + for(ptr = buf; ptr < buf + 256; ptr++) + if(*ptr == '#') *ptr = 0; + + strtok(buf, " \t"); + if(strncmp(user, buf, 8) == 0) { + while((ptr = strtok(NULL, "\t\n "))) { + if(strncmp(tty, ptr, 10) == 0) { + fclose(f); + return; + } + if(strcmp("PTY", ptr) == 0) { +#ifdef linux + sprintf(devname, "/dev/%s", ptr); + /* VERY linux dependent, recognize PTY as alias + for all pseudo tty's */ + if((stat(devname, &stb) >= 0) + && major(stb.st_rdev) == 4 + && minor(stb.st_rdev) >= 192) { + fclose(f); + return; + } +#endif + } + } + /* if we get here, /etc/usertty exists, there's a line + beginning with our username, but it doesn't contain the + name of the tty where the user is trying to log in. + So deny access! */ + fclose(f); + printf("Login on %s denied.\n", tty); + badlogin(user); + sleepexit(1); + } + } + fclose(f); + /* users not mentioned in /etc/usertty are by default allowed access + on all tty's */ +} + +void +getstr(buf, cnt, err) + char *buf, *err; + int cnt; +{ + char ch; + + do { + if (read(0, &ch, sizeof(ch)) != sizeof(ch)) + exit(1); + if (--cnt < 0) { + (void)fprintf(stderr, "%s too long\r\n", err); + sleepexit(1); + } + *buf++ = ch; + } while (ch); +} + +void +sleepexit(eval) + int eval; +{ + sleep((unsigned int)5); + exit(eval); +} + diff --git a/login-utils/mesg.1 b/login-utils/mesg.1 new file mode 100644 index 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); +} |