[comp.os.minix] Improved init

housel@en.ecn.purdue.edu (Peter S. Housel) (02/07/89)

	This version of init has two improvements. First, it is capable
of recieving a signal (number 1, SIGHUP) and responding by re-reading
/etc/ttys (provided mm has been changed to allow signals to init).

	The reasons for doing this are simple. If one occasionally,
but not always, logs on to some tty (tty1 or tty2, say), then the
memory taken up by the login process may hurt if there isn't much
memory to go around. Also, you may use a tty for both dialin and dialout
(logins and term sessions) and want to be able to shut off the login
and turn it back on. With the current init, one has to edit /etc/ttys,
switch root filesystems, and reboot; this is a major pain.

	/etc/ttys should contain one line for each tty device which may
potentially recieve a login. The format is explained in the comments in
init.c. If you want to turn a line on, edit /etc/ttys to change the '0'
to a '1', and "kill -1 1". Init should start a login process on that line.
To shut off a line, change the '1' to '0' and send the same signal.
If there is currently a login running, init will not kill it (you may have
to find the pid using the F1 key and kill it manually; signal 1 will do).

	The second added feature is minor - the third field may contain
the entire line name (e.g. "tty1") instead of just "1". This is the standard
Unix v7 format. This init supports both forms.

	The third feature is that this init (along with patched login)
maintain the /etc/utmp file. This makes programs such as who(1) and
users(1) much easier to write, and makes getlogin(3) (as it is
normally defined) possible. (Reboot records also now contain "reboot")
as suggested by Terrence Holm.

	Effort was made to minimize the code added to implement these
features. Init and login are almost always in the system and it does not
pay for them to be wasteful of space. Another way to save space is to split
out the execn() routine from lib/exec.c into a separate ".s" file so
that it is not loaded with execve() and friends.

This code should work on the ST with possible minor mods.

-Peter S. Housel-	housel@en.ecn.purdue.edu	...!pur-ee!housel

echo 'x - init.c'
sed 's/^X//' <<'**-init.c-EOF-**' >init.c
X/* This process is the father (mother) of all Minix user processes. When
X * Minix comes up, this is process number 2, and has a pid of 1. It
X * executes the /etc/rc shell file, and then reads the /etc/ttys file to
X * determine which terminals need a login process. The ttys file consists
X * of three-field lines as follows:
X *	abc
X * where
X *	a = 0 (line disabled = no shell), 1 (enabled = shell started)
X *	b = a-r defines UART paramers (baud, bits, parity), 0 for console
X *	c = line number or line name
X *
X * The letters a-r correspond to the 18 entries of the uart table below.
X * For example, 'a' is 110 baud, 8 bits, no parity; 'b' is 300 baud, 8 bits,
X * no parity; 'j' is 2400 baud, 7 bits, even parity; etc. If the third field
X * is a digit, then the terminal device will be /dev/tty{c}, otherwise it
X * will be /dev/{c}. Note that since login cheats in determining the
X * slot number, entries in /etc/ttys should always be in minor device number
X * order.
X *
X * If the file /usr/adm/wtmp and /etc/utmp exist and are writable, init (with
X * help from login) maintains login accounting.
X *
X * Sending a signal 1 (SIGHUP) to init will cause it to reread /etc/ttys
X * and start up new shell processes if necessary. It will not, however, kill
X * off login processes for lines that have been turned off.
X */
X
X#include <signal.h>
X#include <sgtty.h>
X#include <utmp.h>
X
X#ifndef WTMP
X#define WTMP		"/usr/adm/wtmp"	/* wtmp login accounting file */
X#endif
X#ifndef UTMP
X#define UTMP		"/etc/utmp"	/* utmp login file */
X#endif
X
X#define CONSOLE		"/dev/tty0"	/* system console device */
X
X#define PIDSLOTS	4		/* maximum number of ttys entries */
X#define TTYSBUF		8 * PIDSLOTS	/* buffer for reading /etc/ttys */
X#define STACKSIZE	256		/* init's stack */
X
Xstruct uart
X       {
X	int baud;
X	int flags;
X       } uart[] =
X       {
X	B110,   BITS8,		/* 'a':  110 baud, 8 bits, no parity */
X	B300,   BITS8,		/* 'b':  300 baud, 8 bits, no parity */
X	B1200,  BITS8,		/* 'c': 1200 baud, 8 bits, no parity */
X	B2400,  BITS8,		/* 'd': 2400 baud, 8 bits, no parity */
X	B4800,  BITS8,		/* 'e': 4800 baud, 8 bits, no parity */
X	B9600,  BITS8,		/* 'f': 9600 baud, 8 bits, no parity */
X
X	B110,   BITS7 | EVENP,	/* 'g':  110 baud, 7 bits, even parity */
X	B300,   BITS7 | EVENP,	/* 'h':  300 baud, 7 bits, even parity */
X	B1200,  BITS7 | EVENP,	/* 'i': 1200 baud, 7 bits, even parity */
X	B2400,  BITS7 | EVENP,	/* 'j': 2400 baud, 7 bits, even parity */
X	B4800,  BITS7 | EVENP,	/* 'k': 4800 baud, 7 bits, even parity */
X	B9600,  BITS7 | EVENP,	/* 'l': 9600 baud, 7 bits, even parity */
X
X	B110,   BITS7 | ODDP,	/* 'm':  110 baud, 7 bits, odd parity */
X	B300,   BITS7 | ODDP,	/* 'n':  300 baud, 7 bits, odd parity */
X	B1200,  BITS7 | ODDP,	/* 'o': 1200 baud, 7 bits, odd parity */
X	B2400,  BITS7 | ODDP,	/* 'p': 2400 baud, 7 bits, odd parity */
X	B4800,  BITS7 | ODDP,	/* 'q': 4800 baud, 7 bits, odd parity */
X	B9600,  BITS7 | ODDP	/* 'r': 9600 baud, 7 bits, odd parity */
X       };
X
X#define NPARAMSETS (sizeof uart / sizeof(struct uart))
X
Xstruct slotent
X       {
X	int onflag;		/* should this ttyslot be on? */
X	int pid;		/* pid of login process for this tty line */
X	char name[8];		/* name of this tty */
X	int flags;		/* sg_flags field for this tty */
X	int speed;		/* sg_ispeed for this tty */
X       };
X
Xstruct slotent slots[PIDSLOTS];	/* init table of ttys and pids */
X
Xchar stack[STACKSIZE];		/* init's stack */
Xchar *stackpt = &stack[STACKSIZE];
Xchar **environ;			/* declaration required by library routines */
Xextern int errno;
X
Xstruct sgttyb args;		/* buffer for TIOCGETP */
Xint gothup = 0;			/* flag, showing signal 1 was recieved */
Xint pidct = 0;			/* count of running children */
X
Xmain()
X{
X  int pid;			/* pid of child process */
X  int i, k;			/* loop variables */
X  int status;			/* return status from child process */
X  struct slotent *slotp;	/* slots[] pointer */
X  int onhup();			/* SIGHUP interrupt catch routine */
X
X  sync();			/* force buffers out onto disk */
X
X  /* Execute the /etc/rc file. */
X  if(fork())
X    {
X     /* Parent just waits. */
X     wait(&status);
X    }
X  else
X    {
X     /* Child exec's the shell to do the work. */
X     if(open("/etc/rc", 0) < 0)
X        exit(-1);
X     dup(open(CONSOLE, 1));	/* std output, error */
X     execn("/bin/sh");
X     exit(-2);			/* impossible */
X    }
X
X  wtmp("~", "reboot", -1);	/* log system reboot */
X
X  /* Read the /etc/ttys file */
X  readttys();
X  
X  /* Main loop. If login processes have already been started up, wait for one
X   * to terminate, or for a HUP signal to arrive. Start up new login processes
X   * for all ttys which don't have them. Note that wait() also returns when
X   * somebody's orphan dies, in which case ignore it.
X   * First set up the signals.
X   */
X
X  for (i = 1; i <= NR_SIGS; i++) signal(i, SIG_IGN);
X  signal(SIGHUP, onhup);
X
X  while(1)
X       {
X	sync();
X
X	if( pidct && (pid = wait(&status)) > 0)
X	  {
X	   /* Search to see which line terminated. */
X	   for(slotp = slots; slotp < &slots[PIDSLOTS]; ++slotp)
X	      {
X	       if(slotp->pid == pid)
X		 {
X	   	  --pidct;
X		  slotp->pid = 0;		/* now no login process */
X		  wtmp(slotp->name, "", slotp - slots);	/* note the logout */
X		  break;
X		}
X	      }
X	  }
X
X	/* If a signal 1 (SIGHUP) is recieved, reread /etc/ttys */
X	if(gothup)
X	  {
X	   readttys();
X	   gothup = 0;
X	  }
X
X	/* See which lines need a login process started up */
X	for(slotp = slots; slotp < &slots[PIDSLOTS]; ++slotp)
X	   {
X	    if(slotp->onflag && slotp->pid <= 0)
X	       startup(slotp - slots);
X	   }
X       }
X}
X
Xonhup()
X{
X gothup = 1;
X signal(SIGHUP, onhup);
X}
X
X/* (Re)read /etc/ttys. */
Xreadttys()
X{
X  char ttys[TTYSBUF];			/* buffer for reading /etc/ttys */
X  register char *p;			/* current pos. within ttys */
X  char *endp;				/* pointer to end of ttys buffer */
X  int fd;				/* file descriptor for /etc/ttys */
X  struct slotent *slotp = slots;	/* entry in slots[] */
X  char *q;				/* pointer for copying ttyname */
X
X  if( (fd = open("/etc/ttys", 0)) < 0 )
X    {
X     write(open(CONSOLE, 1), "Init can't open /etc/ttys\n", 26);
X     while (1) ;		/* just hang -- system cannot be started */
X    }
X
X  /* Read /etc/ttys file. */
X  endp = (p = ttys) + read(fd, ttys, TTYSBUF);
X  *endp = '\n';
X
X  while(p < endp)
X       {
X	slotp->onflag = ('1' == *p++);			/* 'line on' flag */
X	slotp->flags = CRMOD | XTABS | ECHO;		/* sg_flags setting */
X	if('a' <= *p && *p <= 'a' + NPARAMSETS)		/* a serial line? */
X          {
X	   slotp->flags |= uart[*p - 'a'].flags;
X	   slotp->speed = uart[*p - 'a'].baud;
X          }
X        ++p;
X
X	if('0' <= *p && *p <= '9')			/* ttyname = digit? */
X	  {
X	   strncpy(slotp->name, "tty?", sizeof (slotp->name));
X	   slotp->name[3] = *p;				/* fill in '?' */
X	  }
X	else						/* full name - copy */
X	  {
X	   for(q = slotp->name; *p != '\n';)
X	       *q++ = *p++;
X
X	   *q = '\0';
X	  }
X
X	while(*p++ != '\n')
X	      ;
X
X	++slotp;
X       }
X
X  close(fd);
X}
X
Xstartup(linenr)
Xint linenr;
X{
X/* Fork off a process for the indicated line. */
X
X  register struct slotent *slotp;	/* pointer to ttyslot */
X  int pid;				/* new pid */
X  char line[30];			/* tty device name */
X
X  slotp = &slots[linenr];
X
X  if( (pid = fork()) != 0 )
X    {
X     /* Parent */
X     slotp->pid = pid;
X     if( pid > 0 ) ++pidct;
X    }
X  else
X    {
X     /* Child */
X     close(0);					/* just in case */
X     strcpy(line, "/dev/");			/* part of device name */
X     strncat(line, slotp->name, sizeof (slotp->name));	/* rest of name */
X
X     if( open(line, 2) != 0 ) exit(-3);		/* standard input */
X     if(        dup(0) != 1 ) exit(-3);		/* standard output */
X     if( 	dup(1) != 2 ) exit(-3);		/* standard error */
X
X     /* Set line parameters. */
X
X     ioctl(0, TIOCGETP, &args);	/* get parameters */
X     args.sg_ispeed = args.sg_ospeed = slotp->speed;
X     args.sg_flags = slotp->flags;
X     ioctl(0, TIOCSETP, &args);
X
X     /* Try to exec login, or in an emergency, exec the shell. */
X     execn("/usr/bin/login");
X     execn("/bin/login");
X     execn("/bin/sh");
X     execn("/usr/bin/sh");
X     return;			/* impossible, we hope */
X  }
X}
X
Xwtmp(line, user, lineno)
Xchar *line, *user; int lineno;
X{
X  struct utmp entry;
X  register int fd;
X  extern long time();
X
X  (void) strncpy( entry.ut_line, line, sizeof(entry.ut_line) );
X  (void) strncpy( entry.ut_name, user, sizeof(entry.ut_name) );
X
X  entry.ut_time = time( 0L );
X
X  if( (fd = open( WTMP, 1 )) < 0 )
X     return;
X  if( lseek( fd, 0L, 2 ) >= 0L )
X    {
X     write( fd, (char *) &entry, sizeof(struct utmp) );
X    }
X
X  close( fd );
X
X  if( lineno >= 0 )	/* remove entry from utmp */
X    {
X     if( (fd = open( UTMP, 1 )) < 0 )
X        return;
X     if( lseek( fd, (long) (lineno * sizeof(struct utmp)), 0 ) >= 0 )
X       {
X        write( fd, (char *) &entry, sizeof(struct utmp) );
X       }
X
X     close( fd );
X    }
X}
**-init.c-EOF-**
echo 'x - login.c.cdif'
sed 's/^X//' <<'**-login.c.cdif-EOF-**' >login.c.cdif
X*** login.c	Mon Feb  6 19:54:07 1989
X--- /usr/src/commands/login.c	Wed Dec 28 10:05:02 1988
X***************
X*** 11,25 ****
X   *  - Don't allow a login if "/etc/nologin" exists.
X   *  - Cause a failure on bad "pw_shell" fields.
X   *  - Record the login in "/usr/adm/wtmp".
X!  */
X! 
X  #include <signal.h>
X  #include <sgtty.h>
X  #include <pwd.h>
X  #include <sys/stat.h>
X  
X  #define  NULL   (char *) 0
X- #define WTMPSIZE           8
X  #define DIGIT 3
X  
X  extern char *crypt();
X--- 11,28 ----
X   *  - Don't allow a login if "/etc/nologin" exists.
X   *  - Cause a failure on bad "pw_shell" fields.
X   *  - Record the login in "/usr/adm/wtmp".
X!  *
X!  * Peter S. Housel   Dec. 1988
X!  *  - Record the login in "/etc/utmp" also.
X!  */
X! 
X! #include <utmp.h>
X  #include <signal.h>
X  #include <sgtty.h>
X  #include <pwd.h>
X  #include <sys/stat.h>
X  
X  #define  NULL   (char *) 0
X  #define DIGIT 3
X  
X  extern char *crypt();
X***************
X*** 44,49 ****
X--- 47,53 ----
X  	      NULL
X  };
X  char wtmpfile[] = {"/usr/adm/wtmp"};
X+ char utmpfile[] = {"/etc/utmp"};
X  char ttyname[] = {"tty?"};
X  
X  
X***************
X*** 131,137 ****
X  		ttyname[DIGIT] = '0' + ttynr;
X  
X  		/*  Write login record to /usr/adm/wtmp  */
X! 		wtmp(ttyname, name);
X  
X  		setgid( pwd->pw_gid );
X  		setuid( pwd->pw_uid );
X--- 135,141 ----
X  		ttyname[DIGIT] = '0' + ttynr;
X  
X  		/*  Write login record to /usr/adm/wtmp  */
X! 		wtmp(ttyname, name, ttynr);
X  
X  		setgid( pwd->pw_gid );
X  		setuid( pwd->pw_uid );
X***************
X*** 168,194 ****
X    time_out = 1;
X  }
X  
X! wtmp(tty, name)
X! {
X! /* Make an entry in /usr/adm/wtmp. */
X! 
X!   int i, fd;
X!   long t, time();
X!   char ttybuff[WTMPSIZE], namebuff[WTMPSIZE];
X! 
X!   fd = open(wtmpfile, 2);
X!   if (fd < 0) return;		/* if wtmp does not exist, no accounting */
X!   lseek(fd, 0L, 2);		/* append to file */
X! 
X!   for (i = 0; i < WTMPSIZE; i++) {
X! 	ttybuff[i] = 0;
X! 	namebuff[i] = 0;
X!   }
X!   strncpy(ttybuff, tty, 8);
X!   strncpy(namebuff, name, 8);
X!   time(&t);
X!   write(fd, ttybuff, WTMPSIZE);
X!   write(fd, namebuff, WTMPSIZE);
X!   write(fd, &t, sizeof(t));
X!   close(fd);
X! }
X--- 172,209 ----
X    time_out = 1;
X  }
X  
X! wtmp(line, user, lineno)
X! char *line;			/* tty device name */
X! char *user;			/* user name */
X! int lineno;			/* 'slot' in /etc/ttys and /etc/utmp */
X! {
X!   /* Make entries in /usr/adm/wtmp and /etc/utmp. */
X!   struct utmp entry;
X!   register int fd;
X!   extern long time();
X! 
X!   (void) strncpy(entry.ut_line, line, sizeof(entry.ut_line));
X!   (void) strncpy(entry.ut_name, user, sizeof(entry.ut_name));
X! 
X!   entry.ut_time = time(0L);
X! 
X!   if ((fd = open(wtmpfile, 1)) < 0) return;
X! 
X!   if(lseek( fd, 0L, 2 ) >= 0L) {
X! 	/* append the entry to the wtmp file */
X! 	write( fd, (char *) &entry, sizeof(struct utmp) );
X!   }
X! 
X!   close( fd );
X! 
X!   if( lineno >= 0 ) {
X! 	/* replace the utmp entry */
X! 	if( (fd = open( utmpfile, 1 )) < 0 )
X!         	return;
X! 	if( lseek( fd, (long) (lineno * sizeof(struct utmp)), 0 ) >= 0 ) {
X! 		write( fd, (char *) &entry, sizeof(struct utmp) );
X! 	}
X! 
X! 	close( fd );
X!   }
X! }
**-login.c.cdif-EOF-**
exit 0