source: trunk/athena/etc/dm/dm.c @ 15261

Revision 15261, 30.0 KB checked in by ghudson, 24 years ago (diff)
Don't write output if we got an error from read, even if the error was EINTR.
Line 
1/* $Id: dm.c,v 1.18 2000-11-08 23:17:02 ghudson Exp $
2 *
3 * Copyright (c) 1990, 1991 by the Massachusetts Institute of Technology
4 * For copying and distribution information, please see the file
5 * <mit-copyright.h>.
6 *
7 * This is the top-level of the display manager and console control
8 * for Athena's xlogin.
9 */
10
11#include <mit-copyright.h>
12
13#include <sys/types.h>
14#include <sys/ioctl.h>
15#include <sys/wait.h>
16#include <sys/file.h>
17#include <sys/stat.h>
18#include <sys/time.h>
19#ifdef HAVE_SYS_STRREDIR_H
20#include <sys/strredir.h>
21#endif
22#ifdef HAVE_SYS_STROPTS_H
23#include <sys/stropts.h>
24#endif
25#include <stdio.h>
26#include <stdlib.h>
27#include <unistd.h>
28#include <signal.h>
29#include <fcntl.h>
30#include <utmp.h>
31#include <ctype.h>
32#include <string.h>
33#include <errno.h>
34#ifdef HAVE_UTMPX_H
35#include <utmpx.h>
36#endif
37#include <termios.h>
38#include <syslog.h>
39#ifdef HAVE_UTIL_H
40#include <util.h>
41#endif
42#ifdef HAVE_PTY_H
43#include <pty.h>
44#endif
45
46#include <X11/Xlib.h>
47#include <al.h>
48
49#ifndef lint
50static const char rcsid[] = "$Id: dm.c,v 1.18 2000-11-08 23:17:02 ghudson Exp $";
51#endif
52
53/* Process states */
54#define NONEXISTENT     0
55#define RUNNING         1
56#define STARTUP         2
57#define CONSOLELOGIN    3
58#define FAILED          4
59
60#ifndef FALSE
61#define FALSE           0
62#define TRUE            (!FALSE)
63#endif
64
65static sigset_t sig_zero;
66static sigset_t sig_cur;
67
68/* flags used by signal handlers */
69pid_t xpid, consolepid, loginpid;
70volatile int alarm_running = NONEXISTENT;
71volatile int x_running = NONEXISTENT;
72volatile int console_running = NONEXISTENT;
73volatile int console_failed = FALSE;
74volatile int login_running = NONEXISTENT;
75char *logintty;
76int console_tty = 0;
77
78#if defined(UTMP_FILE)
79char *utmpf = UTMP_FILE;
80char *wtmpf = WTMP_FILE;
81#elif defined(_PATH_UTMP)
82char *utmpf = _PATH_UTMP;
83char *wtmpf = _PATH_WTMP;
84#else
85char *utmpf = "/var/adm/utmp";
86char *wtmpf = "/var/adm/wtmp";
87#endif
88char *xpids = "/var/athena/X%d.pid";
89char *xhosts = "/etc/X%d.hosts";
90char *consolepidf = "/var/athena/console.pid";
91char *dmpidf = "/var/athena/dm.pid";
92char *consolelog = "/var/athena/console.log";
93
94static void die(int signo);
95static void child(int signo);
96static void catchalarm(int signo);
97static void xready(int signo);
98static void shutdown(int signo);
99static void loginready(int signo);
100static char *getconf(char *file, char *name);
101static char **parseargs(char *line, char *extra, char *extra1, char *extra2);
102static void console_login(char *conf, char *msg);
103static void start_console(int fd, char **argv, int redir);
104static void cleanup(char *tty);
105static pid_t fork_and_store(pid_t *var);
106static void x_stop_wait(void);
107static void writepid(char *file, pid_t pid);
108
109#ifndef HAVE_LOGOUT
110static void logout(const char *line);
111#endif
112#ifndef HAVE_LOGIN_TTY
113static int login_tty(int fd);
114#endif
115#ifndef HAVE_OPENPTY
116static int openpty(int *amaster, int *aslave, char *name,
117                   struct termios *termp, struct winsize *winp);
118static int termsetup(int fd, struct termios *termp, struct winsize *winp);
119#endif
120
121
122/* the console process will run as daemon */
123#define DAEMON 1
124
125#define X_START_WAIT    30      /* wait up to 30 seconds for X to be ready */
126#define LOGIN_START_WAIT 60     /* wait up to 1 minute for Xlogin */
127#ifndef BUFSIZ
128#define BUFSIZ          1024
129#endif
130
131static int max_fd;
132
133/* Setup signals, start X, start console, start login, wait */
134
135int main(int argc, char **argv)
136{
137  char *consoletty, *conf, *p;
138  char **dmargv, **xargv, **consoleargv = NULL, **loginargv;
139  char xpidf[256], line[16], buf[256];
140  fd_set readfds;
141  int pgrp, file, tries, count, redir = TRUE;
142  char dpyname[10], dpyacl[40];
143  Display *dpy;
144  XHostAddress *hosts;
145  int nhosts, dpynum = 0;
146  struct stat hostsinfo;
147  Bool state;
148  time_t now, last_console_failure = 0;
149  struct sigaction sigact;
150  sigset_t mask;
151#if defined(SRIOCSREDIR) || defined(TIOCCONS)
152  int on;
153#endif
154  int fd;
155  char loginttyname[256];
156  int ttyfd;
157
158  logintty = &loginttyname[5]; /* skip over the /dev/ */
159
160  sigemptyset(&sigact.sa_mask);
161  sigact.sa_flags = 0;
162  (void) sigemptyset(&sig_zero);
163
164/*
165 * Note about setting environment variables in dm:
166 *
167 *   All environment variables passed to dm and set in dm are
168 *   subsequently passed to any children of dm. This is usually
169 *   true of processes that exec in children, so that's not a
170 *   big surprise.
171 *
172 *   However, xlogin is one of the children dm forks, and it goes
173 *   to lengths to ensure that the environments of users logging in
174 *   are ISOLATED from xlogin's own environment. Therefore, do not
175 *   expect that setting an environment variable here will reach the
176 *   user unless you have gone to lengths to make sure that xlogin
177 *   passes it on. Put another way, if you set a new environment
178 *   variable here, consider whether or not it should be seen by the
179 *   user. If it should, go modify verify.c as well. Consider also
180 *   whether the variable should be seen _only_ by the user. If so,
181 *   make the change only in xlogin, and not here.
182 *
183 *   As an added complication, xlogin _does_ pass environment variables
184 *   on to the pre-login options. Therefore, if you set an environment
185 *   variable that should _not_ be seen, you must filter it in xlogin.c.
186 *
187 *   Confused? Too bad. I'm in a nasty, if verbose, mood this year.
188 *
189 * General summary:
190 *
191 *   If you add an environment variable here there are three likely
192 *   possibilities:
193 *
194 *     1. It's for the user only, not needed by any of dm's children.
195 *        --> Don't set it here. Set it in verify.c for users and in
196 *        --> xlogin.c for the pre-login options, if appropriate.
197 *
198 *     2. It's for dm and its children only, and _should not_ be seen
199 *        by the user or pre-login options.
200 *        --> You must filter the option from the pre-login options
201 *        --> in xlogin.c. No changes to verify.c are required.
202 *
203 *     3. It's for dm and the user and the pre-login options.
204 *        --> You must pass the option explicitly to the user in
205 *        --> verify.c. No changes to xlogin.c are required.
206 *
207 *                                                   --- cfields
208 */
209#ifdef notdef
210  putenv("LD_LIBRARY_PATH=/usr/openwin/lib");
211  putenv("OPENWINHOME=/usr/openwin");
212#endif
213
214  if (argc < 2)
215    {
216      fprintf(stderr, "dm: first argument must be configuration file\n");
217      sleep(60);
218      exit(1);
219    }
220
221  conf = argv[1];
222
223  if (argc != 4 && (argc != 5 || strcmp(argv[3], "-noconsole")))
224    {
225      fprintf(stderr,
226              "usage: %s configfile logintty [-noconsole] consoletty\n",
227              argv[0]);
228      console_login(conf, NULL);
229    }
230  if (argc == 5)
231    redir = FALSE;
232
233  /* parse argument lists */
234  /* ignore argv[2] */
235  consoletty = argv[argc - 1];
236
237  openlog("dm", 0, LOG_USER);
238
239  /* We use options from the config file rather than taking
240   * them from the command line because the current command
241   * line form is gross (why???), and I don't see a good way
242   * to extend it without making things grosser or breaking
243   * backwards compatibility. So, we take a line from the
244   * config file and use real parsing.
245   */
246  p = getconf(conf, "dm");
247  if (p != NULL)
248    {
249      dmargv = parseargs(p, NULL, NULL, NULL);
250      while (*dmargv)
251        {
252          if (!strcmp(*dmargv, "-display"))
253            {
254              dmargv++;
255              if (*dmargv)
256                {
257                  dpynum = atoi(*(dmargv) + 1);
258                  dmargv++;
259                }
260            }
261          else
262            dmargv++;
263        }
264    }
265
266  p = getconf(conf, "X");
267  if (p == NULL)
268    console_login(conf, "\ndm: Can't find X command line\n");
269  xargv = parseargs(p, NULL, NULL, NULL);
270
271  p = getconf(conf, "console");
272  if (p == NULL)
273    console_login(conf, "\ndm: Can't find console command line\n");
274
275  consoleargv = parseargs(p, NULL, NULL, NULL);
276
277  p = getconf(conf, "login");
278  if (p == NULL)
279    console_login(conf, "\ndm: Can't find login command line\n");
280  loginargv = parseargs(p, logintty, "-tty", logintty);
281
282  /* Signal Setup */
283  sigact.sa_handler = SIG_IGN;
284  sigaction(SIGTSTP, &sigact, NULL);
285  sigaction(SIGTTIN, &sigact, NULL);
286  sigaction(SIGTTOU, &sigact, NULL);
287  /* so that X pipe errors don't nuke us */
288  sigaction(SIGPIPE, &sigact, NULL);
289  sigact.sa_handler = shutdown;
290  sigaction(SIGFPE, &sigact, NULL);
291  sigact.sa_handler = die;
292  sigaction(SIGHUP, &sigact, NULL);
293  sigaction(SIGINT, &sigact, NULL);
294  sigaction(SIGTERM, &sigact, NULL);
295  sigact.sa_handler = child;
296  sigaction(SIGCHLD, &sigact, NULL);
297  sigact.sa_handler = catchalarm;
298  sigaction(SIGALRM, &sigact, NULL);
299
300  strcpy(line, "/dev/");
301  strcat(line, consoletty);
302
303  fd = open(line, O_RDWR);
304  if (fd == -1)
305    {
306      syslog(LOG_ERR, "Cannot open %s: %m", line);
307      /* This probably won't work, but it seems to be the appropriate
308         punt location. */
309      console_login(conf, "Cannot open tty.\n");
310    }
311
312  login_tty(fd);
313
314  /* Set the console characteristics so we don't lose later */
315  setpgid(0, pgrp = getpid());  /* Reset the tty pgrp  */
316  tcsetpgrp(0, pgrp);
317
318  /* save our pid file */
319  writepid(dmpidf, getpid());
320
321  /* Fire up X */
322  xpid = 0;
323  for (tries = 0; tries < 3; tries++)
324    {
325      syslog(LOG_DEBUG, "Starting X, try #%d", tries + 1);
326      x_running = STARTUP;
327      sigact.sa_handler = xready;
328      sigaction(SIGUSR1, &sigact, NULL);
329      switch (fork_and_store(&xpid))
330        {
331        case 0:
332          if (fcntl(2, F_SETFD, 1) == -1)
333            close(2);
334          (void) sigprocmask(SIG_SETMASK, &sig_zero, (sigset_t *) 0);
335
336          /* ignoring SIGUSR1 will cause the server to send us a SIGUSR1
337           * when it is ready to accept connections
338           */
339          sigact.sa_handler = SIG_IGN;
340          sigaction(SIGUSR1, &sigact, NULL);
341          p = *xargv;
342          *xargv = "X";
343          execv(p, xargv);
344          fprintf(stderr, "dm: X server failed exec: %s\n", strerror(errno));
345          _exit(1);
346        case -1:
347          fprintf(stderr, "dm: Unable to fork to start X server: %s\n",
348                  strerror(errno));
349          break;
350        default:
351          sprintf(xpidf, xpids, dpynum);
352          writepid(xpidf, xpid);
353
354          if (x_running == STARTUP)
355            {
356              alarm(X_START_WAIT);
357              alarm_running = RUNNING;
358              sigsuspend(&sig_zero);
359            }
360          if (x_running != RUNNING)
361            {
362              syslog(LOG_DEBUG, "X failed to start; alarm_running=%d",
363                     alarm_running);
364              if (alarm_running == NONEXISTENT)
365                fprintf(stderr, "dm: Unable to start X\n");
366              else
367                fprintf(stderr, "dm: X failed to become ready\n");
368
369              /* If X wouldn't run, it could be that an existing X
370               * process hasn't shut down.  Wait X_STOP_WAIT seconds
371               * for that to happen.
372               */
373              x_stop_wait();
374            }
375          sigact.sa_handler = SIG_IGN;
376          sigaction(SIGUSR1, &sigact, NULL);
377        }
378      if (x_running == RUNNING)
379        break;
380    }
381  alarm(0);
382  if (x_running != RUNNING)
383    {
384      syslog(LOG_DEBUG, "Giving up on starting X.");
385      console_login(conf, "\nUnable to start X, doing console login "
386                    "instead.\n");
387    }
388
389  /* Tighten up security a little bit. Remove all hosts from X's
390   * access control list, assuming /etc/X0.hosts does not exist or
391   * has zero length. If it does exist with nonzero length, this
392   * behavior is not wanted. The desired effect of removing all hosts
393   * is that only connections from the Unix domain socket will be
394   * allowed.       
395
396   * More secure code using Xau also exists, but there wasn't
397   * time to completely flesh it out and resolve a couple of
398   * issues. This code is probably good enough, but we'll see.
399   * Maybe next time.
400
401   * This code has the added benefit of leaving an X display
402   * connection open, owned by dm. This provides a less-hacky
403   * solution to the config_console problem, where if config_console
404   * is the first program run on user login, it causes the only
405   * X app running at the time, console, to exit, thus resetting
406   * the X server. Thus this code also allows the removal of the
407   * hack in xlogin that attempts to solve the same problem, but
408   * fails on the RS/6000 for reasons unexplored.
409
410   * P.S. Don't run this code under Solaris 2.2- (2.3 is safe).
411   * Removing all hosts from the acl on that server results in
412   * no connections, not even from the Unix domain socket, being
413   * allowed. --- cfields
414   */
415
416  sprintf(dpyacl, xhosts, dpynum);
417  sprintf(dpyname, ":%d", dpynum);
418  dpy = XOpenDisplay(dpyname);
419  if (dpy != NULL && (stat(dpyacl, &hostsinfo) || hostsinfo.st_size == 0))
420    {
421      hosts = XListHosts(dpy, &nhosts, &state);
422      if (hosts != NULL)
423        {
424          XRemoveHosts(dpy, hosts, nhosts);
425          XFlush(dpy);
426          XFree(hosts);
427        }
428    }
429  /* else if (dpy == NULL)
430   *   Could've sworn the X server was running now.
431   *   Follow the original code path. No need introducing new bugs
432   *   to this hairy code, just preserve the old behavior as though
433   *   this code had never been added.
434   */
435
436  /* set up the console pty */
437  if (openpty(&console_tty, &ttyfd, loginttyname, NULL, NULL)==-1)
438    console_login(conf, "Cannot allocate pseudo-terminal\n");
439
440  /* start up console */
441  start_console(console_tty, consoleargv, redir);
442
443  /* Fire up the X login */
444  for (tries = 0; tries < 3; tries++)
445    {
446      syslog(LOG_DEBUG, "Starting xlogin, try #%d", tries + 1);
447      login_running = STARTUP;
448      sigact.sa_handler = loginready;
449      sigaction(SIGUSR1, &sigact, NULL);
450      switch (fork_and_store(&loginpid))
451        {
452        case 0:
453          max_fd = sysconf(_SC_OPEN_MAX);
454          for (file = 0; file < max_fd; file++)
455            {
456              if (file != ttyfd)
457                close(file);
458            }
459
460          login_tty(ttyfd);
461         
462          file = open("/dev/null", O_RDONLY);
463          if (file >= 0)
464            {
465              dup2(file, 0);
466              if (file != 0)
467                close(file);
468            }
469         
470          if (redir)
471            {
472              /* really ought to check the return status of these */
473#ifdef SRIOCSREDIR
474              on = open("/dev/console", O_RDONLY);
475              if (on >= 0)
476                {
477                  ioctl(on, SRIOCSREDIR, 1);
478                  close(on);
479                }
480#else
481#ifdef TIOCCONS
482              on = 1;
483              ioctl(1, TIOCCONS, &on);
484#endif
485#endif
486            }
487          (void) sigprocmask(SIG_SETMASK, &sig_zero, (sigset_t *) 0);
488          /* ignoring SIGUSR1 will cause xlogin to send us a SIGUSR1
489           * when it is ready
490           */
491          sigact.sa_handler = SIG_IGN;
492          sigaction(SIGUSR1, &sigact, NULL);
493          /* dm ignores sigpipe; because of this, all of the children (ie, */
494          /* the entire session) inherit this unless we fix it now */
495          sigact.sa_handler = SIG_DFL;
496          sigaction(SIGPIPE, &sigact, NULL);
497          execv(loginargv[0], loginargv);
498          fprintf(stderr, "dm: X login failed exec: %s\n", strerror(errno));
499          _exit(1);
500        case -1:
501          fprintf(stderr, "dm: Unable to fork to start X login: %s\n",
502                  strerror(errno));
503          break;
504        default:
505          alarm(LOGIN_START_WAIT);
506          alarm_running = RUNNING;
507          while (login_running == STARTUP && alarm_running == RUNNING)
508            sigsuspend(&sig_zero);
509          if (login_running != RUNNING)
510            {
511              syslog(LOG_DEBUG, "xlogin failed to start; alarm_running=%d",
512                     alarm_running);
513              kill(loginpid, SIGKILL);
514              if (alarm_running != NONEXISTENT)
515                fprintf(stderr, "dm: Unable to start Xlogin\n");
516              else
517                fprintf(stderr, "dm: Xlogin failed to become ready\n");
518            }
519        }
520      if (login_running == RUNNING)
521        break;
522    }
523  sigact.sa_handler = SIG_IGN;
524  sigaction(SIGUSR1, &sigact, NULL);
525  alarm(0);
526  if (login_running != RUNNING)
527    {
528      syslog(LOG_DEBUG, "Giving up on starting xlogin.");
529      console_login(conf, "\nUnable to start xlogin, doing console login "
530                    "instead.\n");
531    }
532
533  /* main loop.  Wait for SIGCHLD, waking up every minute anyway. */
534  (void) sigemptyset(&sig_cur);
535  (void) sigaddset(&sig_cur, SIGCHLD);
536  (void) sigprocmask(SIG_BLOCK, &sig_cur, NULL);
537  while (1)
538    {
539      /* Wait for something to hapen */
540      if (console_failed)
541        {
542          /* if no console is running, we must copy bits from the console
543           * (master side of pty) to the real console to appear as black
544           * bar messages.
545           */
546          FD_ZERO(&readfds);
547          FD_SET(console_tty, &readfds);
548          (void) sigprocmask(SIG_SETMASK, &sig_zero, &mask);
549          count = select(console_tty + 1, &readfds, NULL, NULL, NULL);
550          (void) sigprocmask(SIG_BLOCK, &mask, NULL);
551          if (count > 0 && FD_ISSET(console_tty, &readfds))
552            {
553              file = read(console_tty, buf, sizeof(buf));
554              if (file != -1)
555                write(1, buf, file);
556            }
557        }
558      else
559        {
560          alarm(60);
561          sigsuspend(&sig_zero);
562        }
563
564      if (login_running == STARTUP)
565        {
566          (void) sigprocmask(SIG_SETMASK, &sig_zero, NULL);
567          console_login(conf, "\nConsole login requested.\n");
568        }
569      if (console_running == FAILED)
570        {
571          console_running = NONEXISTENT;
572          time(&now);
573          if (now - last_console_failure <= 3)
574            {
575              /* Give up on console.  Set the console characteristics so
576               * we don't lose later. */
577              syslog(LOG_ERR, "Giving up on the console");
578              setpgid(0, pgrp = getpid());      /* Reset the tty pgrp */
579              tcsetpgrp(0, pgrp);
580              console_failed = TRUE;
581            }
582          else
583            last_console_failure = now;
584        }
585      if (console_running == NONEXISTENT && !console_failed)
586        start_console(console_tty, consoleargv, redir);
587      if (login_running == NONEXISTENT || x_running == NONEXISTENT)
588        {
589          syslog(LOG_DEBUG, "login_running=%d, x_running=%d, quitting",
590                 login_running, x_running);
591          (void) sigprocmask(SIG_SETMASK, &sig_zero, NULL);
592          cleanup(logintty);
593          x_stop_wait();
594          _exit(0);
595        }
596    }
597}
598
599
600/* Start a login on the raw console */
601
602static void console_login(char *conf, char *msg)
603{
604  int i, cfirst = TRUE, pgrp;
605  char *nl = "\r\n";
606  struct termios ttybuf;
607  char *p, **cargv;
608  int fd;
609
610  syslog(LOG_DEBUG, "Performing console login: %s", msg);
611  sigemptyset(&sig_zero);
612  if (login_running != NONEXISTENT && login_running != STARTUP)
613    kill(loginpid, SIGKILL);
614  if (console_running != NONEXISTENT)
615    {
616      if (cfirst)
617        kill(consolepid, SIGHUP);
618      else
619        kill(consolepid, SIGKILL);
620      cfirst = FALSE;
621    }
622  if (x_running != NONEXISTENT)
623    kill(xpid, SIGTERM);
624
625  x_stop_wait();
626
627  p = getconf(conf, "ttylogin");
628  if (p == NULL)
629    {
630      fprintf(stderr, "dm: Can't find login command line\n");
631      exit(1);
632    }
633  cargv = parseargs(p, NULL, NULL, NULL);
634
635  setpgid(0, pgrp = 0);         /* We have to reset the tty pgrp */
636  tcsetpgrp(0, pgrp);
637  tcflush(0, TCIOFLUSH);
638
639  (void) tcgetattr(0, &ttybuf);
640  ttybuf.c_lflag |= (ICANON | ISIG | ECHO);
641  (void) tcsetattr(0, TCSADRAIN, &ttybuf);
642  (void) sigprocmask(SIG_SETMASK, &sig_zero, NULL);
643  max_fd = sysconf(_SC_OPEN_MAX);
644
645  for (i = 3; i < max_fd; i++)
646    close(i);
647
648  if (msg)
649    fprintf(stderr, "%s", msg);
650  else
651    fprintf(stderr, "%s", nl);
652
653  fd = open("/dev/console", O_RDWR);
654  if (fd >= 0)
655    login_tty(fd);
656
657  execv(p, cargv);
658
659  fprintf(stderr, "dm: Unable to start console login: %s\n", strerror(errno));
660  _exit(1);
661}
662
663
664/* Start the console program.  It will have stdin set to the controling
665 * side of the console pty, and stdout set to /dev/console inherited
666 * from the display manager.
667 */
668
669static void start_console(int fd, char **argv, int redir)
670{
671  int file;
672
673  syslog(LOG_DEBUG, "Starting console");
674
675  /* Create console log file owned by daemon */
676  if (access(consolelog, F_OK) != 0)
677    {
678      file = open(consolelog, O_CREAT, 0644);
679      close(file);
680    }
681  chown(consolelog, DAEMON, 0);
682
683  console_running = RUNNING;
684  switch (fork_and_store(&consolepid))
685    {
686    case 0:
687      /* Close all file descriptors except stdout/stderr */
688      close(0);
689      max_fd = sysconf(_SC_OPEN_MAX);
690      for (file = 3; file < max_fd; file++)
691        if (file != fd)
692          close(file);
693      setsid();
694      dup2(fd, 0);
695      close(fd);
696
697      setgid(DAEMON);
698      setuid(DAEMON);
699      (void) sigprocmask(SIG_SETMASK, &sig_zero, (sigset_t *) 0);
700      execv(argv[0], argv);
701      fprintf(stderr, "dm: Failed to exec console: %s\n", strerror(errno));
702      _exit(1);
703    case -1:
704      fprintf(stderr, "dm: Unable to fork to start console: %s\n",
705              strerror(errno));
706      _exit(1);
707    default:
708      writepid(consolepidf, consolepid);
709    }
710}
711
712/* Kill children and hang around forever */
713
714static void shutdown(int signo)
715{
716  int pgrp, i;
717  struct termios tc;
718  char buf[BUFSIZ];
719  int notused;
720
721  syslog(LOG_DEBUG, "Received SIGFPE, performing shutdown");
722  if (login_running == RUNNING)
723    kill(loginpid, SIGHUP);
724  if (console_running == RUNNING)
725    kill(consolepid, SIGHUP);
726  if (x_running == RUNNING)
727    kill(xpid, SIGTERM);
728
729  setpgid(0, pgrp = 0);         /* We have to reset the tty pgrp */
730  tcsetpgrp(0, pgrp);
731  tcflush(0, TCIOFLUSH);
732
733  (void) tcgetattr(0, &tc);
734  tc.c_lflag |= (ICANON | ISIG | ECHO);
735  (void) tcsetattr(0, TCSADRAIN, &tc);
736
737  (void) sigprocmask(SIG_SETMASK, &sig_zero, (sigset_t *) 0);
738
739  /* Make sure the slave side is open before we read from the master;
740   *  some systems will return EIO if there are no current writers.
741   */
742  sprintf(buf, "/dev/%s", logintty);
743  notused = open(buf, O_RDWR, 0);
744  while (1)
745    {
746      i = read(console_tty, buf, sizeof(buf));
747      if (i == -1 && errno != EINTR)
748        perror("console pty");
749      else if (i > 0)
750        write(1, buf, i);
751    }
752}
753
754
755/* Kill children, remove password entry */
756
757static void cleanup(char *tty)
758{
759  int file, ret;
760  struct utmp utmp;
761  char login[sizeof(utmp.ut_name) + 1];
762  char *errr;
763
764  if (login_running == RUNNING)
765    kill(loginpid, SIGHUP);
766  if (console_running == RUNNING)
767    kill(consolepid, SIGHUP);
768  if (x_running == RUNNING)
769    kill(xpid, SIGTERM);
770
771  /* Find out what the login name was, so we can feed it to libal. */
772  login[0] = '\0';
773  if ((file = open(utmpf, O_RDWR, 0)) >= 0)
774    {
775      while (read(file, (char *) &utmp, sizeof(utmp)) > 0)
776        {
777          if (!strncmp(utmp.ut_line, tty, sizeof(utmp.ut_line)))
778            {
779              strncpy(login, utmp.ut_name, sizeof(utmp.ut_name));
780              login[sizeof(utmp.ut_name)] = '\0';
781            }
782        }
783      close(file);
784    }
785
786  /* Update the utmp & wtmp. */
787
788  logout(tty);
789 
790  if (login[0])
791    {
792      ret = al_acct_revert(login, loginpid);
793      if (ret != AL_SUCCESS)
794        {
795          syslog(LOG_ERR, "al_acct_revert(%s, %d): %s", login, loginpid,
796                 al_strerror(ret, &errr));
797          al_free_errmem(errr);
798        }
799    }
800
801  tcflush(0, TCIOFLUSH);
802}
803
804
805/* When we get sigchild, figure out which child it was and set
806 * appropriate flags
807 */
808
809static void child(int signo)
810{
811  int pid;
812  int status;
813  struct sigaction sigact;
814
815  sigemptyset(&sigact.sa_mask);
816  sigact.sa_flags = 0;
817  sigact.sa_handler = child;
818  sigaction(SIGCHLD, &sigact, NULL);
819
820  while (1)
821    {
822      pid = waitpid(-1, &status, WNOHANG);
823      if (pid == 0 || pid == -1)
824        return;
825
826      if (pid == xpid)
827        {
828          syslog(LOG_DEBUG, "Received SIGCHLD for xpid (%d), status %d", pid,
829                 status);
830          x_running = NONEXISTENT;
831        }
832      else if (pid == consolepid)
833        {
834          syslog(LOG_DEBUG,
835                 "Received SIGCHLD for consolepid (%d), status %d", pid,
836                 status);
837          if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
838            console_running = FAILED;
839          else
840            console_running = NONEXISTENT;
841        }
842      else if (pid == loginpid)
843        {
844          syslog(LOG_DEBUG, "Received SIGCHLD for loginpid (%d), status %d",
845                 pid, status);
846          if (WEXITSTATUS(status) == CONSOLELOGIN)
847            login_running = STARTUP;
848          else
849            login_running = NONEXISTENT;
850        }
851      else
852        {
853          syslog(LOG_DEBUG, "Received SIGCHLD for unknown pid %d", pid);
854          fprintf(stderr, "dm: Unexpected SIGCHLD from pid %d\n", pid);
855        }
856    }
857}
858
859
860static void xready(int signo)
861{
862  syslog(LOG_DEBUG, "Received SIGUSR1; setting x_running.");
863  x_running = RUNNING;
864}
865
866static void loginready(int signo)
867{
868  syslog(LOG_DEBUG, "Received SIGUSR1; setting login_running.");
869  login_running = RUNNING;
870}
871
872/* When an alarm happens, just note it and return */
873
874static void catchalarm(int signo)
875{
876  syslog(LOG_DEBUG, "Received SIGALRM.");
877  alarm_running = NONEXISTENT;
878}
879
880
881/* kill children and go away */
882
883static void die(int signo)
884{
885  syslog(LOG_DEBUG, "Dying on signal %d", signo);
886  cleanup(logintty);
887  _exit(0);
888}
889
890/* Takes a command line, returns an argv-style  NULL terminated array
891 * of strings.  The array is in malloc'ed memory.
892 */
893
894static char **parseargs(char *line, char *extra, char *extra1, char *extra2)
895{
896  int i = 0;
897  char *p = line;
898  char **ret;
899
900  while (*p)
901    {
902      while (*p && isspace((unsigned char)*p))
903        p++;
904      while (*p && !isspace((unsigned char)*p))
905        p++;
906      i++;
907    }
908  ret = (char **) malloc(sizeof(char *) * (i + 4));
909
910  p = line;
911  i = 0;
912  while (*p)
913    {
914      while (*p && isspace((unsigned char)*p))
915        p++;
916      if (*p == 0)
917        break;
918      ret[i++] = p;
919      while (*p && !isspace((unsigned char)*p))
920        p++;
921      if (*p == 0)
922        break;
923      *p++ = 0;
924    }
925  if (extra)
926    ret[i++] = extra;
927  if (extra1)
928    ret[i++] = extra1;
929  if (extra2)
930    ret[i++] = extra2;
931
932  ret[i] = NULL;
933  return (ret);
934}
935
936/* Find a named field in the config file.  Config file contains
937 * comment lines starting with #, and lines with a field name,
938 * whitespace, then field value.  This routine returns the field
939 * value, or NULL on error.
940 */
941
942static char *getconf(char *file, char *name)
943{
944  static char buf[8192];
945  static int inited = 0;
946  char *p, *ret;
947  int i;
948
949  if (!inited)
950    {
951      int fd;
952
953      fd = open(file, O_RDONLY, 0644);
954      if (fd < 0)
955        return (NULL);
956      i = read(fd, buf, sizeof(buf));
957      if (i >= sizeof(buf) - 1)
958        fprintf(stderr, "dm: warning - config file is to long to parse\n");
959      buf[i] = 0;
960      close(fd);
961      inited = 1;
962    }
963
964  for (p = &buf[0]; p && *p; p = strchr(p, '\n'))
965    {
966      if (*p == '\n')
967        p++;
968      if (p == NULL || *p == 0)
969        return (NULL);
970      if (*p == '#')
971        continue;
972      if (strncmp(p, name, strlen(name)))
973        continue;
974      p += strlen(name);
975      if (*p && !isspace((unsigned char)*p))
976        continue;
977      while (*p && isspace((unsigned char)*p))
978        p++;
979      if (*p == 0)
980        return (NULL);
981      ret = strchr(p, '\n');
982      if (ret)
983        i = ret - p;
984      else
985        i = strlen(p);
986      ret = malloc(i + 1);
987      memcpy(ret, p, i + 1);
988      ret[i] = 0;
989      return (ret);
990    }
991  return (NULL);
992}
993
994/* Fork, storing the pid in a variable var and returning the pid.
995 * Make sure that the pid is stored before any SIGCHLD can be
996 * delivered.
997 */
998static pid_t fork_and_store(pid_t * var)
999{
1000  sigset_t mask, omask;
1001  pid_t pid;
1002
1003  sigemptyset(&mask);
1004  sigaddset(&mask, SIGCHLD);
1005  sigprocmask(SIG_BLOCK, &mask, &omask);
1006  pid = fork();
1007  *var = pid;
1008  sigprocmask(SIG_SETMASK, &omask, NULL);
1009  return pid;
1010}
1011
1012static void x_stop_wait(void)
1013{
1014  sigset_t mask, omask;
1015
1016  sigfillset(&mask);
1017  sigdelset(&mask, SIGALRM);
1018  sigprocmask(SIG_BLOCK, &mask, &omask);
1019  alarm(0);
1020  while (waitpid(xpid, NULL, 0) == -1 && errno == EINTR)
1021    ;
1022  x_running = NONEXISTENT;
1023  sigprocmask(SIG_SETMASK, &omask, NULL);
1024}
1025
1026/* Write a pid to a file */
1027static void writepid(char *file, pid_t pid)
1028{
1029  FILE *fp;
1030
1031  fp = fopen(file, "w");
1032
1033  if (fp == NULL)
1034    return;
1035
1036  fprintf(fp, "%lu\n", (unsigned long)pid);
1037  fclose(fp);
1038}
1039
1040#ifndef HAVE_LOGOUT
1041#ifdef HAVE_PUTUTLINE
1042static void logout(const char *line)
1043{
1044  struct utmp utmp;
1045  struct utmp *putmp;
1046  pid_t pid;
1047#ifdef HAVE_PUTUTXLINE
1048  struct utmpx utmpx;
1049#endif
1050#ifndef HAVE_UPDWTMP
1051  int file;
1052#endif
1053
1054  strcpy(utmp.ut_line, line);
1055  setutent();
1056  putmp = getutline(&utmp);
1057  if (putmp != NULL)
1058    {
1059      time(&utmp.ut_time);
1060      strncpy(utmp.ut_line, putmp->ut_line, sizeof(utmp.ut_line));
1061      strncpy(utmp.ut_user, putmp->ut_name, sizeof(utmp.ut_name));
1062      pid = getpid();
1063      utmp.ut_pid = pid;
1064      strncpy(utmp.ut_id, putmp->ut_id, sizeof(utmp.ut_id));
1065      utmp.ut_type = DEAD_PROCESS;
1066      pututline(&utmp);
1067#ifdef HAVE_PUTUTXLINE
1068      getutmpx(&utmp, &utmpx);
1069      utmpx.ut_pid = pid;
1070      setutxent();
1071      pututxline(&utmpx);
1072      endutxent();
1073#endif /* HAVE_PUTUTXLINE */
1074
1075      updwtmp(wtmpf, &utmp);
1076    }
1077  endutent();
1078}
1079#endif
1080#endif
1081
1082#ifndef HAVE_LOGIN_TTY
1083static int login_tty(int fd)
1084{
1085#ifndef TIOCSCTTY
1086  char *name;
1087#endif
1088  int ttyfd;
1089
1090  setsid();
1091
1092#ifdef TIOCSCTTY
1093  if (ioctl(fd, TIOCSCTTY, NULL) == -1)
1094    return(-1);
1095  ttyfd = fd;
1096#else
1097  name = ttyname(fd);
1098  if (ttyname == NULL)
1099    return(-1);
1100  close(fd);
1101  ttyfd = open(name, O_RDWR);
1102#endif
1103
1104  if (ttyfd == -1)
1105    return(-1);
1106
1107  dup2(ttyfd, STDIN_FILENO);
1108  dup2(ttyfd, STDOUT_FILENO);
1109  dup2(ttyfd, STDERR_FILENO);
1110
1111  if (ttyfd > STDERR_FILENO)
1112    close(ttyfd);
1113
1114  return(0);
1115}
1116#endif
1117
1118#ifndef HAVE_OPENPTY
1119#if HAVE__GETPTY
1120/* Oooh, we're on an sgi. */
1121static int openpty(int *amaster, int *aslave, char *name,
1122                   struct termios *termp, struct winsize *winp)
1123{
1124  char *p;
1125
1126  p = _getpty(amaster, O_RDWR, 0600, 0);
1127
1128  if (p == NULL)
1129    return(-1);
1130
1131  if (name != NULL)
1132    strcpy(name, p);
1133
1134  *aslave = open(p, O_RDWR);
1135  if (*aslave < 0)
1136    {
1137      close(*amaster);
1138      return(-1);
1139    }
1140
1141  if (termsetup(*aslave, termp, winp) < 0)
1142    {
1143      close(*amaster);
1144      close(*aslave);
1145      return(-1);
1146    }
1147
1148  return(0);
1149}
1150#elif HAVE_GRANTPT
1151static int openpty(int *amaster, int *aslave, char *name,
1152                   struct termios *termp, struct winsize *winp)
1153{
1154  char *p;
1155 
1156  *amaster = open("/dev/ptmx", O_RDWR);
1157  if (*amaster < 0)
1158    return(-1);
1159
1160  if (grantpt(*amaster) < 0)
1161    {
1162      close(*amaster);
1163      return(-1);
1164    }
1165
1166  if (unlockpt(*amaster) < 0)
1167    {
1168      close(*amaster);
1169      return(-1);
1170    }
1171
1172  p = ptsname(*amaster);
1173
1174  if (name != NULL)
1175    strcpy(name, p);
1176
1177  *aslave = open(p, O_RDWR);
1178  if(*aslave < 0)
1179    {
1180      close(*amaster);
1181      return(-1);
1182    }
1183
1184  if (ioctl(*aslave, I_PUSH, "ptem") < 0)
1185
1186    {
1187      close(*amaster);
1188      close(*aslave);
1189      return(-1);
1190    }
1191 
1192  if (ioctl(*aslave, I_PUSH, "ldterm") < 0)
1193    {
1194      close(*amaster);
1195      close(*aslave);
1196      return(-1);
1197    }
1198 
1199  if (ioctl(*aslave, I_PUSH, "ttcompat") < 0)
1200    {
1201      close(*amaster);
1202      close(*aslave);
1203      return(-1);
1204    }
1205
1206  if (termsetup(*aslave, termp, winp) < 0)
1207    {
1208      close(*amaster);
1209      close(*aslave);
1210      return(-1);
1211    }
1212
1213  return(0);
1214}
1215#else
1216/* traditional bsd way */
1217static int openpty(int *amaster, int *aslave, char *name,
1218                   struct termios *termp, struct winsize *winp);
1219{
1220  char s[20];
1221  char *p, *q;
1222
1223  *amaster == -1;
1224
1225  strcpy(s, "/dev/ptyxx");
1226  for (p = "pqrstuvwxyzPQRST"; *p; p++)
1227    {
1228      s[8] = *p;
1229      for(q = "0123456789abcdef"; *q; q++)
1230        {
1231          s[9] = *q;
1232
1233          *amaster = open(s, O_RDWR);
1234          if (*amaster == ENOENT)
1235            return(-1);
1236          else
1237            continue;
1238
1239          s[5]='t';
1240
1241          break;
1242        }
1243     
1244      if (*amaster != -1)
1245          break;
1246    }
1247
1248  if (name != NULL)
1249    strcpy(s, name);
1250
1251  *aslave = open(s, O_RDWR);
1252
1253  if (*aslave < 0)
1254    {
1255      close(*amaster);
1256      return(-1);
1257    }
1258
1259
1260  if (termsetup(*aslave, termp, winp) < 0)
1261    {
1262      close(*amaster);
1263      close(*aslave);
1264      return(-1);
1265    }
1266
1267  return(0);
1268}
1269#endif
1270
1271static int termsetup(int fd, struct termios *termp, struct winsize *winp)
1272{
1273  if (termp != NULL)
1274      if (tcsetattr(fd, TCSANOW, termp) < 0)
1275          return(-1);
1276
1277  if (winp != NULL)
1278    if (ioctl(fd, TIOCSWINSZ, winp) < 0)
1279      return(-1);
1280
1281  return(0);
1282}
1283#endif
Note: See TracBrowser for help on using the repository browser.