source: trunk/athena/etc/xdm/xlogin/xlogin.c @ 12350

Revision 12350, 52.0 KB checked in by ghudson, 26 years ago (diff)
Some RCS ID cleanup: delete $Log$ and replace other RCS keywords with $Id$.
Line 
1/* $Id: xlogin.c,v 1.79 1999-01-22 23:16:27 ghudson Exp $ */
2 
3#include <unistd.h>
4#include <string.h>
5#include <stdio.h>
6#include <stdlib.h>
7#include <signal.h>
8#include <sys/types.h>
9#include <sys/wait.h>
10#include <sys/time.h>
11#include <sys/stat.h>
12#include <sys/file.h>
13#include <utmp.h>
14#include <fcntl.h>
15#include <X11/Intrinsic.h>
16#include <ctype.h>
17#include <errno.h>
18#include <pwd.h>
19#include <X11/Wc/WcCreate.h>
20#include <X11/StringDefs.h>
21#include <X11/Xaw/Label.h>
22#include <X11/Xaw/Text.h>
23#include <X11/Xaw/Form.h>
24#ifdef SOLARIS
25#include <X11/Xaw/SmeBSB.h>
26#endif
27#include <X11/Xlib.h>
28#include <X11/Xatom.h>
29#include <X11/Shell.h>
30#include <X11/Xmu/Drawing.h>
31#include <X11/Xmu/Converters.h>
32#include <larv.h>
33#include "Clock.h"
34#include "owl.h"
35#include "environment.h"
36
37#ifdef SYSV
38#define random  lrand48
39#define srandom srand48
40#endif
41
42#ifndef MIN
43#define MIN(a,b) ((a) < (b) ? (a) : (b))
44#endif
45
46#ifndef MOTD_FILENAME
47#define MOTD_FILENAME "/afs/athena.mit.edu/system/config/motd/login.77"
48#endif
49
50#ifdef sgi
51char athconsole[64];
52FILE *xdmstream;
53int xdmfd;
54#endif
55
56#define OWL_AWAKE 0
57#define OWL_SLEEPY 1
58
59#define OWL_STATIC 0
60#define OWL_BLINKINGCLOSED 1
61#define OWL_BLINKINGOPEN 2
62#define OWL_SLEEPING 3
63#define OWL_WAKING 4
64
65#define ACTIVATED 1
66#define REACTIVATING 2
67
68gid_t def_grplist[] = { 101 };          /* default group list */
69
70
71/* Function declarations. */
72
73extern void AriRegisterAthena ();
74static void move_instructions(), screensave(), unsave(), start_reactivate();
75static void blinkOwl(), blinkIs(), initOwl(), adjustOwl();
76static void catch_child(), setFontPath();
77static Boolean auxConditions();
78extern pid_t fork_and_store(pid_t *var);
79void focusACT(), unfocusACT(), runACT(), runCB(), focusCB(), resetCB();
80void idleReset(), loginACT(), localErrorHandler(), setcorrectfocus();
81void sigconsACT(), sigconsCB(), callbackACT(), attachandrunCB();
82void windowShutdownACT(), windowShutdownCB();
83extern void add_converter ();
84
85
86/* Definition of the Application resources structure. */
87
88typedef struct _XLoginResources {
89  int save_timeout;
90  int move_timeout;
91  int blink_timeout;
92  int reactivate_timeout;
93  int activate_timeout;
94  int restart_timeout;
95  int randomize;
96  int detach_interval;
97  String activate_prog;
98  String reactivate_prog;
99  String tty;
100  String session;
101  String fontpath;
102  String srvdcheck;
103  String loginName;
104  Boolean blankAll;
105  Boolean showMotd;
106  String motdFile;
107  String motd2File;
108} XLoginResources;
109
110/* Command line options table.  Only resources are entered here...there is a
111 * pass over the remaining options after XtParseCommand is let loose.
112 */
113
114static XrmOptionDescRec options[] = {
115  {"-save",     "*saveTimeout",         XrmoptionSepArg,        NULL},
116  {"-move",     "*moveTimeout",         XrmoptionSepArg,        NULL},
117  {"-blink",    "*blinkTimeout",        XrmoptionSepArg,        NULL},
118  {"-reactivate","*reactivateProg",     XrmoptionSepArg,        NULL},
119  {"-randomize","*randomize",           XrmoptionSepArg,        NULL},
120  {"-detach",   "*detachInterval",      XrmoptionSepArg,        NULL},
121  {"-idle",     "*reactivateTimeout",   XrmoptionSepArg,        NULL},
122  {"-wait",     "*activateTimeout",     XrmoptionSepArg,        NULL},
123  {"-restart",  "*restartTimeout",      XrmoptionSepArg,        NULL},
124  {"-tty",      "*loginTty",            XrmoptionSepArg,        NULL},
125  {"-session",  "*sessionScript",       XrmoptionSepArg,        NULL},
126  {"-srvdcheck","*srvdCheck",           XrmoptionSepArg,        NULL},
127  {"-fp",       "*fontPath",            XrmoptionSepArg,        NULL},
128  {"-blankall", "*blankAll",            XrmoptionNoArg,   (caddr_t) "on"},
129  {"-noblankall","*blankAll",           XrmoptionNoArg,   (caddr_t) "off"},
130  {"-motdfile", "*motdFile",            XrmoptionSepArg,        NULL},
131  {"-motd2file","*motd2File",           XrmoptionSepArg,        NULL},
132};
133
134/* The structure containing the resource information for the
135 * Xlogin application resources.
136 */
137
138#define Offset(field) (XtOffset(XLoginResources *, field))
139
140static XtResource my_resources[] = {
141  {"saveTimeout", XtCInterval, XtRInt, sizeof(int),
142     Offset(save_timeout), XtRImmediate, (caddr_t) 120},
143  {"moveTimeout", XtCInterval, XtRInt, sizeof(int),
144     Offset(move_timeout), XtRImmediate, (caddr_t) 20},
145  {"blinkTimeout", XtCInterval, XtRInt, sizeof(int),
146     Offset(blink_timeout), XtRImmediate, (caddr_t) 40},
147  {"reactivateProg", XtCFile, XtRString, sizeof(String),
148     Offset(reactivate_prog), XtRImmediate, "/etc/athena/reactivate"},
149  {"randomize", XtCInterval, XtRInt, sizeof(int),
150     Offset(randomize), XtRImmediate, (caddr_t) 60},
151  {"detachInterval", XtCInterval, XtRInt, sizeof(int),
152     Offset(detach_interval), XtRImmediate, (caddr_t) 12},
153  {"activateTimeout", XtCInterval, XtRInt, sizeof(int),
154     Offset(activate_timeout), XtRImmediate, (caddr_t) 30},
155  {"restartTimeout", XtCInterval, XtRInt, sizeof(int),
156     Offset(restart_timeout), XtRImmediate, (caddr_t) (60 * 60 * 12)},
157  {"reactivateTimeout", XtCInterval, XtRInt, sizeof(int),
158     Offset(reactivate_timeout), XtRImmediate, (caddr_t) 300},
159  {"loginTty", XtCFile, XtRString, sizeof(String),
160     Offset(tty), XtRImmediate, (caddr_t) "ttyv0"},
161  {"sessionScript", XtCFile, XtRString, sizeof(String),
162     Offset(session), XtRImmediate, (caddr_t) "/etc/athena/login/Xsession"},
163  {"srvdcheck", XtCFile, XtRString, sizeof(String),
164     Offset(srvdcheck), XtRImmediate, (caddr_t) "/srvd/.rvdinfo"},
165#ifdef SOLARIS
166  {"fontPath", XtCString, XtRString, sizeof(String),
167     Offset(fontpath), XtRImmediate, (caddr_t) "/usr/openwin/lib/fonts/" },
168#else
169  {"fontPath", XtCString, XtRString, sizeof(String),
170     Offset(fontpath), XtRImmediate, (caddr_t) "/usr/athena/lib/X11/fonts/misc/,/usr/athena/lib/X11/fonts/75dpi/,/usr/athena/lib/X11/fonts/100dpi/" },
171#endif
172  {"loginName", XtCString, XtRString, sizeof(String),
173     Offset(loginName), XtRImmediate, (caddr_t) "" },
174  {"blankAllScreens", XtCBoolean, XtRBoolean, sizeof(Boolean),
175     Offset(blankAll), XtRImmediate, (caddr_t) True},
176  {"showMotd", XtCBoolean, XtRBoolean, sizeof(Boolean),
177     Offset(showMotd), XtRImmediate, (caddr_t) True},
178  {"motdFile", XtCString, XtRString, sizeof(String),
179     Offset(motdFile), XtRImmediate, (caddr_t) MOTD_FILENAME },
180  {"motd2File", XtCString, XtRString, sizeof(String),
181     Offset(motd2File), XtRImmediate, (caddr_t) "" },
182};
183
184#undef Offset
185
186XtActionsRec actions[] = {
187    { "setfocus", focusACT },
188    { "unsetfocus", unfocusACT },
189    { "run", runACT },
190    { "idleReset", idleReset },
191    { "login", loginACT },
192    { "reset", resetCB },
193    { "setCorrectFocus", setcorrectfocus },
194    { "signalConsoleACT", sigconsACT },
195    { "callbackACT", callbackACT },
196    { "windowShutdownACT", windowShutdownACT },
197};
198
199
200
201
202#ifndef CONSOLEPID
203#define CONSOLEPID "/var/athena/console.pid"
204#endif
205
206#ifndef UTMP_FILE
207#ifdef _PATH_UTMP
208#define UTMP_FILE _PATH_UTMP
209#else /* _PATH_UTMP */
210#define UTMP_FILE "/var/adm/utmp"
211#endif /* _PATH_UTMP */
212#endif /* UTMP_FILE */
213
214/* Globals. */
215
216XtIntervalId curr_timerid = 0, blink_timerid = 0, is_timerid = 0,
217  react_timerid = 0;
218Widget appShell;
219Widget saver, ins;
220Widget savershell[10];
221int num_screens;
222XLoginResources resources;
223GC owlGC, isGC;
224Display *dpy;
225Window owlWindow, isWindow;
226int owlNumBitmaps, isNumBitmaps;
227/* unsigned */ int owlWidth, owlHeight, isWidth, isHeight;
228int owlState, owlDelta, isDelta, owlTimeout, isTimeout;
229Pixmap owlBitmaps[20], isBitmaps[20];
230struct timeval starttime;
231pid_t activation_pid, attach_pid, attachhelp_pid, quota_pid;
232int activation_state, activate_count = 0, attach_state, attachhelp_state;
233int exiting = FALSE;
234extern char *defaultpath;
235char login[128], passwd[128];
236sigset_t sig_zero;
237
238#ifdef SOLARIS_MAE
239int netspy = FALSE;
240#endif
241
242/* Local Globals */
243
244static struct sigaction sigact, osigact;
245
246void main(argc, argv)
247     int argc;
248     char* argv[];
249{
250  XtAppContext app;
251  XEvent e;
252  Widget hitanykey, namew;
253  Display *dpy1;
254  char hname[1024], *c;
255  Arg args[1];
256  int i;
257  unsigned acc = 0;
258  int pid;
259#ifdef sgi
260  int fdflags;
261#endif
262
263  sigemptyset(&sig_zero);
264
265#ifdef sgi
266  /* Get stderr and stdout for our own uses - we don't want them going
267   * through various paths of xdm. Under Irix, xdm does a lot of the
268   * actually logging-in; it calls xlogin with stdout a pipe it listens
269   * to to determine whom to log in. We need this communication, but we
270   * also want stdout to work correctly (out to console). So we make
271   * a copy of the stdout stream, and then reopen stdout to whatever
272   * tty we belong to (or /dev/console, if that doesn't work).
273   */
274  if (nanny_getTty(athconsole, sizeof(athconsole)))
275    strcpy(athconsole, "/dev/console");
276
277  xdmfd = dup(fileno(stdout));
278  if (xdmfd != -1)
279    {
280      xdmstream = fdopen(xdmfd, "w");
281      if (xdmstream == NULL)
282        {
283          close(xdmfd);
284          xdmstream = stdout;
285        }
286      else if (NULL == freopen(athconsole, "w", stdout))
287        (void)freopen("/dev/console", "w", stdout);
288    }
289  else
290    xdmstream = stdout; /* Some stuff will break, but better than losing. */
291  /* Actually, losing gracefully might be wise... */
292
293  fdflags = fcntl(fileno(xdmstream), F_GETFD);
294  if (fdflags != -1)
295    fcntl(fileno(xdmstream), F_SETFD, fdflags | FD_CLOEXEC);
296
297  if (NULL == freopen(athconsole, "w", stderr))
298    (void)freopen("/dev/console", "w", stderr);
299  /* if (stderr == NULL)
300     tough luck; */
301#endif
302
303  /* Have to find this argument before initializing the toolkit.
304   * We set both XUSERFILESEARCHPATH and XENVIRONMENT.  The effect is
305   * that the -config argument names a directory that will have the
306   * file Xlogin which contains the resources, and may optionally have
307   * a Xlogin.local file containing additional resources which will
308   * override those in the regular file.
309   */
310  for (i = 1; i < argc; i++)
311    if (!strcmp(argv[i], "-config") && (i + 1 < argc))
312      {
313        c = getenv("XUSERFILESEARCHPATH");
314        if (c)
315          sprintf(hname, "%s:%s/%%N", c, argv[i + 1]);
316        else
317          sprintf(hname, "%s/%%N", argv[i + 1]);
318        psetenv("XUSERFILESEARCHPATH", hname, 1);
319        sprintf(hname, "%s/Xlogin.local", argv[i + 1]);
320        psetenv("XENVIRONMENT", hname, 1);
321        break;
322      }
323
324  /* Initialize Toolkit creating the application shell, and get
325   * application resources.
326   */
327  appShell = XtInitialize("xlogin", "Xlogin",
328                          options, XtNumber(options),
329                          &argc, argv);
330  add_converter();
331  app = XtWidgetToApplicationContext(appShell);
332  XtAppSetErrorHandler(app, localErrorHandler);
333  dpy = XtDisplay(appShell);
334  XtAppAddActions(app, actions, XtNumber(actions));
335
336  XtGetApplicationResources(appShell, (caddr_t)&resources,
337                            my_resources, XtNumber(my_resources),
338                            NULL, (Cardinal)0);
339
340#ifndef sgi
341  /* Tell the display manager we're ready, just like the X server
342   * handshake. This code used to be right before XtMainLoop. However,
343   * under Ultrix dm is required to open /dev/xcons and manually pipe
344   * it to the console window. It won't start this process until
345   * it gets its SIGUSR1 from us. So, if we do output to the console
346   * (where our stderr and stdout are directed) before sending the SIGUSR1,
347   * it may show up as "black bar" messages. This is suboptimal. Since
348   * I have no idea why this handshake is helpful in the first place,
349   * beyond knowing the exec of XLogin succeeded, I don't see any reason
350   * not to just get it over with and get the console flowing when we
351   * need it. We need it now. --- cfields
352   */
353  sigaction(SIGUSR1, NULL, &osigact);
354  if (osigact.sa_handler == SIG_IGN)
355    kill(getppid(), SIGUSR1);
356#endif /* not sgi */
357
358#ifdef SOLARIS_MAE
359  /* Make sure the network device has the proper owner and protections.
360   * But don't muck with it unless the file NETSPY exists. */
361
362  netspy = file_exists(NETSPY);
363  if (netspy)
364    {
365      chown(NETDEV, ROOT, SYS);
366      chmod(NETDEV, 0600);
367    }
368#endif
369
370  /* Call reactivate with the -prelogin option. This restores /etc/passwd,
371   * blows away stray processes, runs access_off, and a couple of other
372   * low overhead things (if PUBLIC=true). This is low overhead because
373   * we want login to start up ASAP, but we pay the price for what we do
374   * to make sure the workstation is as clean as it ought to be with respect
375   * to performance and security. This code has to come after the resources
376   * are loaded, so we know where the reactivate script is.
377   */
378  pid = fork();
379  switch(pid)
380    {
381    case 0:
382      execl(resources.reactivate_prog, resources.reactivate_prog,
383            "-prelogin", 0);
384      fprintf(stderr, "XLogin: unable to exec reactivate program \"%s\"\n",
385              resources.reactivate_prog);
386      _exit(1);
387      break;
388    case -1:
389      fprintf(stderr, "XLogin: unable to fork for reactivatation\n");
390      break;
391    default:
392      waitpid(pid, NULL, 0);
393      break;
394    }
395
396  /* We set up the signal handler later than we used to because we don't
397   * need or want it to be running to handle the prelogin script.
398   */
399  sigemptyset(&sigact.sa_mask);
400  sigact.sa_flags = 0;
401  sigact.sa_handler = catch_child;
402  sigaction(SIGCHLD, &sigact, NULL);
403
404  WcRegisterCallback(app, "UnsetFocus", unfocusACT, NULL);
405  WcRegisterCallback(app, "runCB", runCB, NULL);
406  WcRegisterCallback(app, "setfocusCB", focusCB, NULL);
407  WcRegisterCallback(app, "resetCB", resetCB, NULL);
408  WcRegisterCallback(app, "signalConsoleCB", sigconsCB, NULL);
409  WcRegisterCallback(app, "idleResetCB", idleReset, NULL);
410  WcRegisterCallback(app, "attachAndRunCB", attachandrunCB, NULL);
411  WcRegisterCallback(app, "windowShutdownCB", windowShutdownCB, NULL);
412
413  /* Register all Athena widget classes. */
414  AriRegisterAthena (app);
415  WcRegisterClassPtr(app, "ClockWidget", clockWidgetClass);
416  WcRegisterClassPtr(app, "clockWidgetClass", clockWidgetClass);
417
418  /* Clear console window. */
419  sigconsCB(NULL, "clear", NULL);
420
421  /* Create widget tree below toplevel shell using Xrm database. */
422  WcWidgetCreation(appShell);
423
424  /* Realize the widget tree, finish up initializing, and enter the
425   * main application loop.
426   */
427  XtRealizeWidget(appShell);
428
429  initOwl(appShell);            /* widget tree MUST be realized... */
430  adjustOwl(appShell);
431
432  /* Put the first component of the hostname in the label of the host
433   * widget. */
434  gethostname(hname, sizeof(hname));
435  c = strchr(hname, '.');
436  if (c)
437      *c = 0;
438  XtSetArg(args[0], XtNlabel, hname);
439  namew = WcFullNameToWidget(appShell, "*login*host");
440  XtSetValues(namew, args, 1);
441  namew = WcFullNameToWidget(appShell, "*instructions*host");
442  XtSetValues(namew, args, 1);
443  XtSetArg(args[0], XtNstring, login);
444  XtSetValues(WcFullNameToWidget(appShell, "*name_input"), args, 1);
445  XtSetArg(args[0], XtNstring, passwd);
446  XtSetValues(WcFullNameToWidget(appShell, "*pword_input"), args, 1);
447
448  /* Seed the random number generator with our hostname. */
449  c = hname;
450  while (*c)
451    acc = (acc << 1) ^ *c++;
452  srandom(acc);
453  resources.reactivate_timeout += random() % resources.randomize;
454  saver = WcFullNameToWidget(appShell, "*savershell");
455  ins = WcFullNameToWidget(appShell, "*instructions");
456  hitanykey = WcFullNameToWidget(appShell, "*hitanykey");
457
458#define MASK    KeyReleaseMask | ButtonReleaseMask
459  XtAddEventHandler(saver, MASK, FALSE, unsave, (XtPointer)TRUE);
460  XtAddEventHandler(hitanykey, MASK, FALSE, unsave, (XtPointer)TRUE);
461
462  curr_timerid = XtAddTimeOut(resources.save_timeout * 1000, screensave, NULL);
463  blink_timerid = XtAddTimeOut(1000, blinkOwl, NULL);
464  is_timerid = XtAddTimeOut(1000, blinkIs, NULL);
465  gettimeofday(&starttime, NULL);
466  resetCB(namew, NULL, NULL);
467  if (access(resources.srvdcheck, F_OK) != 0)
468    start_reactivate(NULL, NULL);
469  else
470    activation_state = ACTIVATED;
471
472  psetenv("PATH", defaultpath, 1);
473#ifdef HOSTTYPE
474  psetenv("hosttype", HOSTTYPE, 1); /* environment.h */
475#endif
476
477  /* Create shells to blank out all other screens, if any... */
478
479  /* Cover ourselves by setting number of screens to one. */
480  num_screens = 1;
481  savershell[0] = saver;        /* Fill in the saver shell for now... */
482
483  if (resources.blankAll && ScreenCount(dpy) > 1)
484    {
485      int this_screen = XScreenNumberOfScreen(XtScreen(saver));
486      char *orig_dpy, *ptr;
487      int zero = 0;
488      num_screens = MIN(ScreenCount(dpy), 10);
489      orig_dpy = XtNewString(DisplayString(dpy));
490
491      /* If the alloc worked, continue. We are counting on
492       * displaystring to always contain a period. The displaystring
493       * is always canonicalized for us...  isn't that nice of them?
494       */
495      if (orig_dpy != NULL)     
496        {
497          if ((ptr = strrchr(orig_dpy, '.')) != NULL)
498            {
499              ptr++;
500              /* Only does screens 0 thru 9. If you have more screens
501               * than that, you lose.
502               */
503              for (i = 0; i < ScreenCount(dpy) && i < 10; i++)
504                {
505                  if (i == this_screen)
506                    savershell[i] = saver;
507                  else
508                    {
509                      Widget root;
510
511                      *ptr = (char) i + '0';
512                      dpy1 = XtOpenDisplay(app, orig_dpy, "xlogin", "Xlogin",
513                                           NULL, 0, &zero, NULL);
514                      root = XtAppCreateShell("xlogin", "Xlogin",
515                                              applicationShellWidgetClass,
516                                              dpy1, NULL, 0);
517                      savershell[i] = XtCreatePopupShell("savershell",
518                                                 transientShellWidgetClass,
519                                                 root, NULL, 0);
520                      XtAddEventHandler(savershell[i], MASK,
521                                        FALSE, unsave, (XtPointer)TRUE);
522                    }
523                }
524            }
525        }
526      XtFree(orig_dpy);
527    }
528
529  larv_set_busy(0);
530
531  /* This is just XtMainLoop() with a send_event filter. */
532  while (1)
533    {
534      XtAppNextEvent(app, &e);
535      if (e.xany.send_event == False)
536        XtDispatchEvent(&e);
537    }
538}
539
540static Dimension x_max = 0, y_max = 0;
541
542static void move_instructions(data, timerid)
543     XtPointer data;
544     XtIntervalId *timerid;
545{
546  Position x, y;
547  Window wins[2];
548
549  if (!x_max)           /* Get sizes if we haven't done so already. */
550    {
551      Arg args[2];
552
553      XtSetArg(args[0], XtNwidth, &x_max);
554      XtSetArg(args[1], XtNheight, &y_max);
555      XtGetValues(ins, args, 2);
556
557      if (WidthOfScreen(XtScreen(ins)) < x_max + 1)
558        x_max = 1;
559      else
560        x_max = WidthOfScreen(XtScreen(ins)) - x_max;
561
562      if (HeightOfScreen(XtScreen(ins)) < y_max + 1)
563        y_max = 1;
564      else
565        y_max = HeightOfScreen(XtScreen(ins)) - y_max;
566    }
567
568  x = random() % x_max;
569  y = random() % y_max;
570  XtMoveWidget(ins, x, y);
571
572  if (activation_state != REACTIVATING)
573    {
574      XRaiseWindow(XtDisplay(ins), XtWindow(ins));
575      wins[0] = XtWindow(ins);
576      wins[1] = XtWindow(saver);
577      XRestackWindows(XtDisplay(ins), wins, 2);
578    }
579
580  curr_timerid = XtAddTimeOut(resources.move_timeout * 1000,
581                              move_instructions, NULL);
582}
583
584static void start_reactivate(data, timerid)
585     XtPointer  data;
586     XtIntervalId  *timerid;
587{
588  int in_use = 0;
589  int file;
590  struct utmp utmp;
591  struct timeval now;
592
593#ifndef sgi /* Not our problem on the SGI. */
594  gettimeofday(&now, NULL);
595  if (now.tv_sec - starttime.tv_sec > resources.restart_timeout)
596    {
597      fprintf(stderr, "Restarting X Server\n");
598      exit(0);
599    }
600#endif /* sgi */
601
602  do_motd();
603
604  file = open(UTMP_FILE, O_RDONLY, 0);
605  if (file >= 0)
606    {
607      while (read(file, (char *) &utmp, sizeof(utmp)) > 0)
608        {
609          if (utmp.ut_name[0] != 0
610#ifdef USER_PROCESS
611              && utmp.ut_type == USER_PROCESS
612#endif
613              )
614            {
615              in_use = 1;
616              break;
617            }
618        }
619      close(file);
620    }
621
622  if (in_use || activation_state == REACTIVATING)
623    {
624      react_timerid = XtAddTimeOut(resources.reactivate_timeout * 1000,
625                                   start_reactivate, NULL);
626      return;
627    }
628
629  /* Clear the console window. */
630  sigconsCB(NULL, "clear", NULL);
631
632  activation_state = REACTIVATING;
633  switch(fork_and_store(&activation_pid))
634    {
635    case 0:
636      if (activate_count % resources.detach_interval == 0)
637        execl(resources.reactivate_prog, resources.reactivate_prog,
638              "-detach", 0);
639      else
640        execl(resources.reactivate_prog, resources.reactivate_prog, 0);
641      fprintf(stderr, "XLogin: unable to exec reactivate program \"%s\"\n",
642              resources.reactivate_prog);
643      _exit(1);
644    case -1:
645      fprintf(stderr, "XLogin: unable to fork for reactivatation\n");
646      activation_state = ACTIVATED;
647      break;
648    default:
649      break;
650    }
651  activate_count++;
652  react_timerid = XtAddTimeOut(resources.reactivate_timeout * 1000,
653                               start_reactivate, NULL);
654}
655
656void idleReset()
657{
658  if (curr_timerid)
659    XtRemoveTimeOut(curr_timerid);
660  curr_timerid = XtAddTimeOut(resources.save_timeout * 1000,
661                              screensave, NULL);
662}
663
664static void stop_activate(data, timerid)
665     XtPointer data;
666     XtIntervalId *timerid;
667{
668  if (activation_state == ACTIVATED)
669    return;
670
671  kill(activation_pid, SIGKILL);
672  fprintf(stderr, "Workstation activation failed to finish normally.\n");
673  activation_state = ACTIVATED;
674}
675
676static void screensave(data, timerid)
677     XtPointer  data;
678     XtIntervalId  *timerid;
679{
680  static int first_time = TRUE;
681  Pixmap pixmap;
682  Cursor cursor;
683  XColor c;
684  int i;
685
686  XtPopdown(WcFullNameToWidget(appShell, "*getSessionShell"));
687  XtPopdown(WcFullNameToWidget(appShell, "*warningShell"));
688  XtPopdown(WcFullNameToWidget(appShell, "*queryShell"));
689
690  for (i = 0; i < num_screens; i++)
691    XtPopup(savershell[i], XtGrabNone);
692
693  do_motd();
694  XtPopup(ins, XtGrabNone);
695  XRaiseWindow(XtDisplay(ins), XtWindow(ins));
696  unfocusACT(appShell, NULL, NULL, NULL);
697  if (first_time)
698    {
699      /* Contortions to "get rid of" cursor on screensaver windows. */
700      c.pixel = BlackPixel(dpy, DefaultScreen(dpy));
701      XQueryColor(dpy, DefaultColormap(dpy, DefaultScreen(dpy)), &c);
702
703      pixmap = XCreateBitmapFromData(dpy, XtWindow(appShell), "", 1, 1);
704
705      cursor = XCreatePixmapCursor(dpy, pixmap, pixmap, &c, &c,
706                                   (unsigned int)0, (unsigned int)0);
707      XFreePixmap(dpy, pixmap);
708      for (i = 0; i < num_screens; i++)
709        XDefineCursor(dpy, XtWindow(savershell[i]), cursor);
710      XDefineCursor(dpy, XtWindow(ins), cursor);
711      XFreeCursor(dpy, cursor);
712
713      first_time = FALSE;
714    }
715
716  if (blink_timerid != 0)       /* Don't do animation while screensaved. */
717    XtRemoveTimeOut(blink_timerid);
718  blink_timerid = 0;
719  if (is_timerid != 0)
720    XtRemoveTimeOut(is_timerid);
721  is_timerid = 0;
722
723  /* Don't let the X server's screensaver kick in. */
724  XSetScreenSaver(dpy, 0, -1, DefaultBlanking, DefaultExposures);
725  curr_timerid = XtAddTimeOut(resources.move_timeout * 1000,
726                              move_instructions, NULL);
727  react_timerid = XtAddTimeOut(resources.reactivate_timeout * 1000,
728                               start_reactivate, NULL);
729}
730
731/* Check the motd file and update the contents of the widget if necessary. */
732do_motd()
733{
734  static Widget motdtext = NULL;
735  static time_t modtime = 0, modtime2 = 0;
736  struct stat stbuf, stbuf2;
737  Arg args[1];
738  char buf[10000], *temp, *s, *d;
739  int fid, len, do_g_motd, do_l_motd;
740
741  if (!motdtext)
742    {
743      motdtext = WcFullNameToWidget(appShell, "*motd");
744
745      /* Initialize motdtext to NULL in case it never gets set.
746       * This happens in the case of a bad stat on the motd
747       * file, or if showMotd false.
748       */
749      buf[0] = '\0';
750      XtSetArg(args[0], XtNlabel, buf);
751      XtSetValues(motdtext, args, 1);
752    }
753
754  if (resources.showMotd)
755    {
756      do_g_motd = (resources.motdFile != NULL && *resources.motdFile &&
757                   !stat(resources.motdFile, &stbuf) &&
758                   stbuf.st_mtime != modtime);
759      if (do_g_motd)
760        modtime = stbuf.st_mtime;
761
762      do_l_motd = (resources.motd2File != NULL && *resources.motd2File &&
763                   !stat(resources.motd2File, &stbuf2) &&
764                   stbuf2.st_mtime != modtime2);
765      if (do_l_motd)
766        modtime2 = stbuf2.st_mtime;
767
768      if (do_g_motd || do_l_motd)
769        {
770          /* Read the new motd. */
771          len = 0;
772          if (resources.motdFile != NULL && *resources.motdFile &&
773              (fid = open(resources.motdFile, O_RDONLY)) >= 0)
774            {
775              len = read(fid, buf, sizeof(buf));
776              close(fid);
777            }
778          if (resources.motd2File != NULL && *resources.motd2File &&
779              (fid = open(resources.motd2File, O_RDONLY)) >= 0)
780            {
781              len += read(fid, &(buf[len]), sizeof(buf) - len);
782              close(fid);
783            }
784          buf[len] = 0;
785
786          /* De-tabbify the motd (label widgets don't do tabs). */
787          for (s = buf; *s; s++)
788            if (*s == '\t') len += 7;
789          d = temp = malloc(len + 1);
790          len = 0;
791          for (s = buf; *s; s++)
792            {
793              switch(*s)
794                {
795                case '\t':
796                  *d++ = ' ';
797                  len++;
798                  while (len++ % 8 != 0)
799                    *d++ = ' ';
800                  len--;
801                  break;
802                case '\n':
803                  len = 0;
804                  *d++ = *s;
805                  break;
806                default:
807                  *d++ = *s;
808                  len++;
809                }
810            }
811          *d = 0;
812
813          /* Now set the text. */
814          XtSetArg(args[0], XtNlabel, temp);
815          XtSetValues(motdtext, args, 1);
816          free(temp);
817
818          /* Force move_instructions() to recompute size. */
819          x_max = 0;
820        }
821    }
822}
823
824static void unsave(w, popdown, event, bool)
825     Widget w;
826     int popdown;
827     XEvent *event;
828     Boolean *bool;
829{
830  /* Hide the console window. */
831  sigconsCB(NULL, "hide", NULL);
832
833  if (popdown)
834    {
835      int i;
836
837      XtPopdown(ins);
838      for (i = 0; i < num_screens; i++)
839        XtPopdown(savershell[i]);
840    }
841
842  /* Enable the X server's screensaver. */
843  XSetScreenSaver(dpy, -1, -1, DefaultBlanking, DefaultExposures);
844  resetCB(w, NULL, NULL);
845
846  if (curr_timerid != 0)
847    XtRemoveTimeOut(curr_timerid);
848  curr_timerid = XtAddTimeOut(resources.save_timeout * 1000,
849                              screensave, NULL);
850  blink_timerid = XtAddTimeOut(random() % (10 * 1000),
851                               blinkOwl, NULL);
852  is_timerid = XtAddTimeOut(random() % (10 * 1000),
853                            blinkIs, NULL);
854  if (react_timerid != 0)
855    XtRemoveTimeOut(react_timerid);
856  if (activation_state == REACTIVATING)
857    react_timerid = XtAddTimeOut(resources.activate_timeout * 1000,
858                                 stop_activate, NULL);
859}
860
861void loginACT(w, event, p, n)
862     Widget w;
863     XEvent *event;
864     String *p;
865     Cardinal *n;
866{
867  Arg args[2];
868  char *script;
869  int mode = 1;
870  Pixmap bm1, bm2, bm3, bm4, bm5;
871  XawTextBlock tb;
872  extern char *dologin();
873  XEvent e;
874
875  if (curr_timerid)
876    XtRemoveTimeOut(curr_timerid);
877
878  XtSetArg(args[0], XtNleftBitmap, &bm1);
879  XtGetValues(WcFullNameToWidget(appShell, "*lmenuEntry1"), args, 1);
880  XtSetArg(args[0], XtNleftBitmap, &bm2);
881  XtGetValues(WcFullNameToWidget(appShell, "*lmenuEntry2"), args, 1);
882  XtSetArg(args[0], XtNleftBitmap, &bm3);
883  XtGetValues(WcFullNameToWidget(appShell, "*lmenuEntry3"), args, 1);
884  XtSetArg(args[0], XtNleftBitmap, &bm4);
885  XtGetValues(WcFullNameToWidget(appShell, "*lmenuEntry4"), args, 1);
886  XtSetArg(args[0], XtNleftBitmap, &bm5);
887  XtGetValues(WcFullNameToWidget(appShell, "*lmenuEntry5"), args, 1);
888
889  /* Determine which option was selected by seeing which 4 of the 5 match. */
890  if (bm1 == bm2 && bm1 == bm3 && bm1 == bm4)
891    mode = 5;
892  if (bm1 == bm2 && bm1 == bm3 && bm1 == bm5)
893    mode = 4;
894  if (bm1 == bm2 && bm1 == bm4 && bm1 == bm5)
895    mode = 3;
896  if (bm1 == bm3 && bm1 == bm4 && bm1 == bm5)
897    mode = 2;
898  if (bm2 == bm3 && bm2 == bm4 && bm2 == bm5)
899    mode = 1;
900
901  XtSetArg(args[0], XtNstring, &script);
902  XtGetValues(WcFullNameToWidget(appShell, "*getsession*value"), args, 1);
903  unfocusACT(appShell, NULL, NULL, NULL);
904  XtUnmapWidget(appShell);
905  /* To clear the cut buffer in case someone types ^U while typing
906   * their password.
907   */
908  XDeleteProperty(dpy, DefaultRootWindow(dpy), XA_CUT_BUFFER0);
909  XDeleteProperty(dpy, DefaultRootWindow(dpy), XA_CUT_BUFFER1);
910  XFlush(dpy);
911
912  /* Wait for activation to finish.  We play games with signals here
913   * because we are not waiting within the XtMainloop for it to handle
914   * the timers.
915   */
916  if (activation_state != ACTIVATED)
917    {
918      void (*oldsig)();
919
920      fprintf(stderr, "Waiting for workstation to finish activating...");
921      fflush(stderr);
922      sigemptyset(&sigact.sa_mask);
923      sigact.sa_flags = 0;
924      sigact.sa_handler = stop_activate;
925      sigaction(SIGALRM, &sigact, &osigact);
926      alarm(resources.activate_timeout);
927      while (activation_state != ACTIVATED)
928        sigsuspend(&sig_zero);
929      alarm(0);
930      sigaction(SIGALRM, &osigact, NULL);
931      fprintf(stderr, "done.\n");
932    }
933
934  if (access(resources.srvdcheck, F_OK) != 0)
935    tb.ptr = "Workstation failed to activate successfully.  "
936      "Please notify the Athena Hotline, x3-1410, hotline@mit.edu.";
937  else
938    {
939      setFontPath();
940#ifdef sgi
941      /* We obtained the tty earlier from nanny. */
942      resources.tty = athconsole + 5;
943#endif
944
945      XWarpPointer(dpy, None, RootWindow(dpy, DefaultScreen(dpy)),
946                   0, 0, 0, 0, 300, 300);
947      XFlush(dpy);
948      larv_set_busy(1);
949      tb.ptr = dologin(login, passwd, mode, script, resources.tty,
950                       resources.session, DisplayString(dpy));
951      larv_set_busy(0);
952      XWarpPointer(dpy, None, RootWindow(dpy, DefaultScreen(dpy)),
953                   0, 0, 0, 0, WidthOfScreen(DefaultScreenOfDisplay(dpy))/2,
954                   HeightOfScreen(DefaultScreenOfDisplay(dpy))/2);
955    }
956
957  XtMapWidget(appShell);
958  XtPopup(WcFullNameToWidget(appShell, "*warningShell"), XtGrabExclusive);
959  tb.firstPos = 0;
960  tb.length = strlen(tb.ptr);
961  tb.format = FMT8BIT;
962  XawTextReplace(WcFullNameToWidget(appShell, "*warning*value"),
963                 0, 65536, &tb);
964  XtCallActionProc(WcFullNameToWidget(appShell, "*warning*value"),
965                   "form-paragraph", &e, NULL, 0);
966  focusCB(appShell, "*warning*value", NULL);
967  curr_timerid = XtAddTimeOut(resources.save_timeout * 1000,
968                              screensave, NULL);
969}
970
971/* Login failed: Set the exit flag, then return the message the usual way. */
972char *lose(msg)
973     char *msg;
974{
975  exiting = TRUE;
976  return msg;
977}
978
979void focusACT(w, event, p, n)
980     Widget w;
981     XEvent *event;
982     String *p;
983     Cardinal *n;
984{
985  Widget target;
986
987#ifdef sgi
988  static int done_once = 0;
989
990  /* This crock works around the an invalid argument error on the
991   * XSetInputFocus() call below the very first time it is called,
992   * only when running on the RIOS.  We still don't know just what
993   * causes it.
994   * I sure wish I'd modified this comment when I added sgi to
995   * the ifdef the first time. I should try taking it back out to
996   * find out.
997   */
998  if (done_once == 0)
999    {
1000      done_once++;
1001      XSync(dpy, FALSE);
1002      sleep(1);
1003      XSync(dpy, FALSE);
1004    }
1005#endif
1006
1007  target = WcFullNameToWidget(appShell, p[0]);
1008  XSetInputFocus(dpy, XtWindow(target), RevertToPointerRoot, CurrentTime);
1009
1010  if (owlState == OWL_SLEEPY)
1011    if (blink_timerid != 0)
1012      {
1013        XtRemoveTimeOut(blink_timerid);
1014        blink_timerid = 0;
1015        owlDelta = OWL_WAKING;
1016        blinkOwl(NULL, NULL);
1017      }
1018}
1019
1020void focusCB(w, s, unused)
1021     Widget w;
1022     char *s;
1023     caddr_t unused;
1024{
1025  Cardinal one = 1;
1026
1027  focusACT(w, NULL, &s, &one);
1028}
1029
1030void unfocusACT(w, event, p, n)
1031     Widget w;
1032     XEvent *event;
1033     String *p;
1034     Cardinal *n;
1035{
1036  int rvt;
1037  Window win;
1038
1039  XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
1040  XGetInputFocus(dpy, &win, &rvt);
1041}
1042
1043void setcorrectfocus(w, event, p, n)
1044     Widget w;
1045     XEvent *event;
1046     String *p;
1047     Cardinal *n;
1048{
1049  Arg args[2];
1050  char *win;
1051  Cardinal i;
1052  Boolean bool;
1053
1054  XtSetArg(args[0], XtNdisplayCaret, &bool);
1055  XtGetValues(WcFullNameToWidget(appShell, "*name_input"), args, 1);
1056  if (bool)
1057    win = "*name_input";
1058  else
1059    win = "*pword_input";
1060  i = 1;
1061  focusACT(w, event, &win, &i);
1062}
1063
1064void runACT(w, event, p, n)
1065     Widget w;
1066     XEvent *event;
1067     char **p;
1068     Cardinal *n;
1069{
1070  char **argv;
1071  int i;
1072#ifdef sgi
1073  extern char **environ;
1074#endif
1075  struct passwd *pw;
1076
1077  unfocusACT(w, event, p, n);
1078  argv = (char **)malloc(sizeof(char *) * (*n + 3));
1079  argv[0] = "sh";
1080  argv[1] = "-c";
1081  for (i = 0; i < *n; i++)
1082    argv[i+2] = p[i];
1083  argv[i+2] = NULL;
1084
1085  /* Wait for activation to finish. */
1086  if (activation_state != ACTIVATED)
1087    fprintf(stderr, "Waiting for workstation to finish activating...\n");
1088  while (activation_state != ACTIVATED)
1089    sigsuspend(&sig_zero);
1090  if (access(resources.srvdcheck, F_OK) != 0)
1091    {
1092      fprintf(stderr, "Workstation failed to activate successfully.\n"
1093              "Please notify the Athena Hotline, x3-1410, hotline@mit.edu.\n");
1094      return;
1095    }
1096  sigconsCB(NULL, "hide", NULL);
1097  setFontPath();
1098  XFlush(dpy);
1099  XtCloseDisplay(dpy);
1100
1101  /* Set up the pre-login environment.
1102   *
1103   * By default, all of xlogin's environment is passed to the
1104   * pre-login applications. Some of xlogin's environment is
1105   * not appropriately passed on; if such a new element is
1106   * introduced into xlogin, it should be unsetenved here.
1107   *
1108   * Note that the environment for user logins is set
1109   * up in verify.c: it is NOT RELATED to this environment
1110   * setup. If you add a new environment variable here,
1111   * consider whether or not it also needs to be added there.
1112   * Note that variables that need to be unsetenved here do not
1113   * need similar treatment in the user login area, since there
1114   * no variables are passed by default.
1115   *
1116   * Note also that below are not the only environment variables
1117   * mucked with. Others are done earlier for other functions
1118   * of xlogin.
1119   */
1120  punsetenv("XUSERFILESEARCHPATH");
1121  punsetenv("XENVIRONMENT");
1122
1123  psetenv("PATH", defaultpath, 1);
1124  psetenv("USER", "nobody", 1);
1125  psetenv("SHELL", "/bin/sh", 1);
1126  psetenv("DISPLAY", ":0", 1);
1127
1128#ifdef sgi
1129  psetenv("PRELOGIN", "true", 1);
1130  if (nanny_setupUser("nobody", environ, argv))
1131    {
1132      fprintf(stderr, "Unable to set up for 'nobody' app\n");
1133      return;
1134    }
1135
1136  fprintf(xdmstream, "%s", "nobody");
1137  fputc(0, xdmstream);
1138
1139  larv_set_busy(1);
1140  exit(0);
1141#else
1142  pw = getpwnam("nobody");
1143  if (!pw)
1144    {
1145      fprintf(stderr, "Unable to find 'nobody' account\n");
1146      return;
1147    }
1148  setgroups(sizeof(def_grplist)/sizeof(gid_t), def_grplist);
1149
1150  setgid(def_grplist[0]);
1151  if (setuid(pw->pw_uid))
1152    {
1153      fprintf(stderr, "Unable to set user id.\n");
1154      return;
1155    }
1156  larv_set_busy(1);
1157  execv("/bin/sh", argv);
1158  fprintf(stderr, "XLogin: unable to exec /bin/sh\n");
1159  _exit(3);
1160#endif
1161}
1162
1163void runCB(w, s, unused)
1164     Widget w;
1165     char *s;
1166     caddr_t unused;
1167{
1168  Cardinal i = 1;
1169
1170  runACT(w, NULL, &s, &i);
1171}
1172
1173void attachandrunCB(w, s, unused)
1174     Widget w;
1175     char *s;
1176     caddr_t unused;
1177{
1178  char *cmd, locker[256];
1179  Cardinal i = 1;
1180
1181  cmd = strchr(s, ',');
1182  if (cmd == NULL)
1183    {
1184      fprintf(stderr,
1185              "Xlogin warning: need two arguments in AttachAndRun(%s)\n",
1186              s);
1187      return;
1188    }
1189  strncpy(locker, s, cmd - s);
1190  locker[cmd - s] = 0;
1191  cmd++;
1192
1193  attach_state = -1;
1194  switch(fork_and_store(&attach_pid))
1195    {
1196    case 0:
1197      execlp("attach", "attach", "-n", "-h", "-q", locker, NULL);
1198      fprintf(stderr, "Xlogin warning: unable to attach locker %s\n", locker);
1199      _exit(1);
1200    case -1:
1201      fprintf(stderr, "Xlogin: unable to fork to attach locker\n");
1202      break;
1203    default:
1204      while (attach_state == -1)
1205        sigsuspend(&sig_zero);
1206      if (attach_state != 0)
1207        {
1208          fprintf(stderr, "Unable to attach locker %s, aborting...\n",
1209                  locker);
1210          return;
1211        }
1212    }
1213
1214  runACT(w, NULL, &cmd, &i);
1215}
1216
1217void windowShutdownCB(w, s, unused)
1218     Widget w;
1219     char *s;
1220     caddr_t unused;
1221{
1222  larv_set_busy(1);
1223#ifdef sgi
1224  /* If this returns 0, the X server has been killed and it's time
1225   * to go. If not, we should probably pop up a dialog box.
1226   */
1227  if (!nanny_setConsoleMode())
1228    exit(0);
1229#else
1230  exit(3);
1231#endif
1232}
1233
1234void windowShutdownACT(w, event, p, n)
1235     Widget w;
1236     XEvent *event;
1237     char **p;
1238     Cardinal *n;
1239{
1240  larv_set_busy(1);
1241#ifdef sgi
1242  if (!nanny_setConsoleMode())
1243    exit(0);
1244#else
1245  exit(3);
1246#endif
1247}
1248
1249void sigconsACT(w, event, p, n)
1250     Widget w;
1251     XEvent *event;
1252     char **p;
1253     Cardinal *n;
1254{
1255  int sig, pid;
1256  FILE *f;
1257  char buf[BUFSIZ];
1258
1259  if (!strcmp(p[0], "clear"))
1260    sig = SIGFPE;
1261  else if (!strcmp(p[0], "hide"))
1262    sig = SIGUSR2;
1263  else if (!strcmp(p[0], "show"))
1264    sig = SIGUSR1;
1265  else if (!strcmp(p[0], "config"))
1266    sig = SIGHUP;
1267  else
1268    sig = atoi(p[0]);
1269
1270  f = fopen(CONSOLEPID, "r");
1271  if (f)
1272    {
1273      fgets(buf, sizeof(buf), f);
1274      pid = atoi(buf);
1275      if (pid)
1276        kill(pid, sig);
1277      fclose(f);
1278    }
1279}
1280
1281void sigconsCB(w, s, unused)
1282     Widget w;
1283     char *s;
1284     caddr_t unused;
1285{
1286  Cardinal i = 1;
1287
1288  sigconsACT(w, NULL, &s, &i);
1289}
1290
1291void callbackACT(w, event, p, n)
1292     Widget w;
1293     XEvent *event;
1294     char **p;
1295     Cardinal *n;
1296{
1297  w = WcFullNameToWidget(appShell, p[0]);
1298  XtCallCallbacks(w, "callback", p[1]);
1299}
1300
1301void resetCB(w, s, unused)
1302     Widget w;
1303     char *s;
1304     caddr_t unused;
1305{
1306  XawTextBlock tb;
1307  Widget name_input;
1308
1309  if (exiting == TRUE)
1310    exit(0);
1311  focusCB(appShell, "*name_input", NULL);
1312  WcSetValueCB(appShell, "*lmenuEntry1.leftBitmap: check", NULL);
1313  WcSetValueCB(appShell, "*lmenuEntry2.leftBitmap: white", NULL);
1314  WcSetValueCB(appShell, "*lmenuEntry3.leftBitmap: white", NULL);
1315  WcSetValueCB(appShell, "*lmenuEntry4.leftBitmap: white", NULL);
1316  WcSetValueCB(appShell, "*lmenuEntry5.leftBitmap: white", NULL);
1317  WcSetValueCB(appShell, "*selection.label:  ", NULL);
1318  WcSetValueCB(appShell, "*name_input.displayCaret: TRUE", NULL);
1319
1320  tb.firstPos = tb.length = 0;
1321  tb.ptr = "";
1322  tb.format = FMT8BIT;
1323  XawTextReplace(WcFullNameToWidget(appShell, "*pword_input"), 0, 65536, &tb);
1324  XawTextReplace(WcFullNameToWidget(appShell, "*getsession*value"),
1325                 0, 65536, &tb);
1326
1327  if (resources.loginName != NULL  &&  strlen(resources.loginName) != 0)
1328    {
1329      if (strlen(resources.loginName) > 8)
1330        resources.loginName[8] = '\0';
1331      tb.ptr = resources.loginName;
1332      tb.length = strlen(tb.ptr);
1333    }
1334  name_input = WcFullNameToWidget(appShell, "*name_input");
1335  XawTextReplace(name_input, 0, 65536, &tb);
1336  XawTextSetInsertionPoint(name_input, (XawTextPosition) tb.length);
1337
1338  if (curr_timerid)
1339    XtRemoveTimeOut(curr_timerid);
1340  curr_timerid = XtAddTimeOut(resources.save_timeout * 1000,
1341                              screensave, NULL);
1342}
1343
1344setvalue(w, done, unused)
1345     Widget w;
1346     int *done;
1347{
1348  *done = 1;
1349}
1350
1351typedef struct _prompt_callback_info {
1352  void (*abort_proc)();
1353  void *abort_arg;
1354} prompt_callback_info;
1355
1356void click_abort(widget, client_data, call_data)
1357     Widget widget;
1358     XtPointer client_data, call_data;
1359{
1360  prompt_callback_info *cdata = (prompt_callback_info *)client_data;
1361
1362  cdata->abort_proc(cdata->abort_arg);
1363}
1364
1365void timeout_abort(client_data, timer_id)
1366     XtPointer client_data;
1367     XtIntervalId *timer_id;
1368{
1369  prompt_callback_info *cdata = (prompt_callback_info *)client_data;
1370
1371  cdata->abort_proc(cdata->abort_arg);
1372}
1373
1374prompt_user(msg, abort_proc, abort_arg)
1375     char *msg;
1376     void (*abort_proc)();
1377     void *abort_arg;
1378{
1379  XawTextBlock tb;
1380  XEvent e;
1381  prompt_callback_info cb;
1382  static void (*oldcallback)() = NULL;
1383  static int done;
1384
1385  cb.abort_proc = abort_proc;
1386  cb.abort_arg = abort_arg;
1387
1388  XtPopup(WcFullNameToWidget(appShell, "*queryShell"), XtGrabExclusive);
1389  tb.firstPos = 0;
1390  tb.ptr = msg;
1391  tb.length = strlen(msg);
1392  tb.format = FMT8BIT;
1393  XawTextReplace(WcFullNameToWidget(appShell, "*query*value"),
1394                 0, 65536, &tb);
1395  XtCallActionProc(WcFullNameToWidget(appShell, "*query*value"),
1396                   "form-paragraph", &e, NULL, 0);
1397  focusCB(appShell, "*query*value", NULL);
1398  if (oldcallback)
1399    XtRemoveCallback(WcFullNameToWidget(appShell, "*query*giveup"),
1400                     XtNcallback, oldcallback, NULL);
1401  else
1402    XtAddCallback(WcFullNameToWidget(appShell, "*query*cont"),
1403                  XtNcallback, (XtCallbackProc)setvalue, &done);
1404  XtAddCallback(WcFullNameToWidget(appShell, "*query*giveup"),
1405                XtNcallback, click_abort, &cb);
1406  oldcallback = abort_proc;
1407  curr_timerid = XtAddTimeOut(resources.save_timeout * 1000,
1408                              timeout_abort, &cb);
1409
1410  /* Repeat main_loop here so we can check status & return. */
1411  done = 0;
1412  while (!done)
1413    {
1414      XtAppNextEvent((XtAppContext)_XtDefaultAppContext(), &e);
1415      if (e.xany.send_event == False)
1416        XtDispatchEvent(&e);
1417    }
1418
1419  XtRemoveTimeOut(curr_timerid);
1420  curr_timerid = 0;
1421  XtPopdown(WcFullNameToWidget(appShell, "*queryShell"));
1422  XFlush(dpy);
1423}
1424
1425#define updateOwl()     XCopyPlane(dpy, owlBitmaps[owlCurBitmap], \
1426                                   owlWindow, owlGC, 0, 0, \
1427                                   owlWidth, owlHeight, 0, 0, 1)
1428#define updateIs()      XCopyPlane(dpy, isBitmaps[isCurBitmap], \
1429                                   isWindow, isGC, 0, 0, \
1430                                   isWidth, isHeight, 0, 0, 1)
1431
1432static void blinkOwl(data, intervalid)
1433     XtPointer data;
1434     XtIntervalId *intervalid;
1435{
1436  static int owlCurBitmap;
1437  owlTimeout = 0;
1438
1439  if (owlNumBitmaps == 0)
1440    return;
1441
1442  switch(owlDelta)
1443    {
1444    case OWL_BLINKINGCLOSED:    /* your eyelids are getting heavy... */
1445      owlCurBitmap++;
1446      updateOwl();
1447      if (owlCurBitmap == owlNumBitmaps - 1)
1448        owlDelta = OWL_BLINKINGOPEN;
1449      break;
1450
1451    case OWL_BLINKINGOPEN:      /* you will awake, feeling refreshed... */
1452      owlCurBitmap--;
1453      updateOwl();
1454      if (owlCurBitmap == ((owlState == OWL_SLEEPY) * (owlNumBitmaps) / 2))
1455        {
1456          owlTimeout = random() % (10 * 1000);
1457          owlDelta = OWL_BLINKINGCLOSED;
1458        }
1459      break;
1460
1461    case OWL_SLEEPING:          /* transition to sleeping state */
1462      owlCurBitmap++;
1463      updateOwl();
1464      if (owlCurBitmap == ((owlState == OWL_SLEEPY) * (owlNumBitmaps) / 2))
1465        {
1466          owlDelta = OWL_BLINKINGCLOSED;
1467          owlTimeout = random() % (10 * 1000);
1468        }
1469      break;
1470
1471    case OWL_WAKING:            /* transition to waking state */
1472      if (owlCurBitmap)
1473        owlCurBitmap--;
1474      updateOwl();
1475      if (owlCurBitmap == 0)
1476        {
1477          owlDelta = OWL_BLINKINGCLOSED;
1478          owlTimeout = random() % (10 * 1000);
1479        }
1480      break;
1481
1482    case OWL_STATIC:
1483      break;
1484    }
1485
1486  blink_timerid = XtAddTimeOut((owlTimeout
1487                                ? owlTimeout : resources.blink_timeout +
1488                                3 * resources.blink_timeout *
1489                                ((owlState == OWL_SLEEPY) &&
1490                                 (owlDelta != OWL_WAKING))),
1491                                blinkOwl, NULL);
1492}
1493
1494static void blinkIs(data, intervalid)
1495     XtPointer data;
1496     XtIntervalId *intervalid;
1497{
1498  static int isCurBitmap;
1499  isTimeout = 0;
1500
1501  if (isNumBitmaps == 0)
1502    return;
1503
1504  switch(isDelta)
1505    {
1506    case OWL_BLINKINGCLOSED:    /* your eyelids are getting heavy... */
1507      isCurBitmap++;
1508      updateIs();
1509      if (isCurBitmap == isNumBitmaps - 1)
1510        isDelta = OWL_BLINKINGOPEN;
1511      break;
1512
1513    case OWL_BLINKINGOPEN:      /* you will awake, feeling refreshed... */
1514      isCurBitmap--;
1515      updateIs();
1516      if (isCurBitmap == 0)
1517        {
1518          isTimeout = random() % (10 * 1000);
1519          isDelta = OWL_BLINKINGCLOSED;
1520        }
1521      break;
1522
1523    case OWL_STATIC:
1524      break;
1525    }
1526
1527  is_timerid = XtAddTimeOut((isTimeout
1528                             ? isTimeout : resources.blink_timeout),
1529                            blinkIs, NULL);
1530}
1531
1532static void initOwl(search)
1533     Widget search;
1534{
1535  Widget owl, is;
1536  Arg args[3];
1537  int n, done;
1538  char *filenames, *ptr;
1539  XGCValues values;
1540  XtGCMask valuemask;
1541
1542  owl = WcFullNameToWidget(search, "*eyes");
1543
1544  if (owl != NULL)
1545    {
1546      owlWindow = XtWindow(owl);
1547      if (owlWindow != None)
1548        {
1549          n = 0;
1550          done = 0;
1551          XtSetArg(args[n], XtNlabel, &filenames);
1552          n++;
1553          XtSetArg(args[n], XtNforeground, &values.foreground);
1554          n++;
1555          XtSetArg(args[n], XtNbackground, &values.background);
1556          n++;
1557          XtGetValues(owl, args, n);
1558
1559          values.function = GXcopy;
1560          valuemask = GCForeground | GCBackground | GCFunction;
1561
1562          owlNumBitmaps = 0;
1563          ptr = filenames;
1564          while (ptr != NULL && !done)
1565            {
1566              while (*ptr != '\0' && !isspace(*ptr))
1567                ptr++;
1568
1569              if (*ptr == '\0')
1570                done = 1;
1571              else
1572                *ptr = '\0';
1573
1574              owlBitmaps[owlNumBitmaps] = XmuLocateBitmapFile(XtScreen(owl),
1575                                                              filenames,
1576                                                              NULL, 0,
1577                                                              &owlWidth,
1578                                                              &owlHeight,
1579                                                              NULL, NULL);
1580              if (owlBitmaps[owlNumBitmaps] == None)
1581                return; /* abort */
1582              owlNumBitmaps++;
1583              if (!done)
1584                {
1585                  *ptr = ' ';
1586                  while (isspace(*ptr))
1587                    ptr++;
1588                }
1589              filenames = ptr;
1590            }
1591
1592          owlGC = XtGetGC(owl, valuemask, &values);
1593          if (auxConditions())
1594            {
1595              owlState = OWL_SLEEPY;
1596              owlDelta = OWL_SLEEPING;
1597            }
1598          else
1599            {
1600              owlState = OWL_AWAKE;
1601              owlDelta = OWL_BLINKINGCLOSED;
1602            }
1603          isDelta = OWL_BLINKINGCLOSED;
1604        }
1605    }
1606
1607  is = WcFullNameToWidget(search, "*logo2");
1608
1609  if (is != NULL)
1610    {
1611      isWindow = XtWindow(is);
1612      if (isWindow != None)
1613        {
1614          n = 0;
1615          done = 0;
1616          XtSetArg(args[n], XtNlabel, &filenames);
1617          n++;
1618          XtSetArg(args[n], XtNforeground, &values.foreground);
1619          n++;
1620          XtSetArg(args[n], XtNbackground, &values.background);
1621          n++;
1622          XtGetValues(is, args, n);
1623
1624          values.function = GXcopy;
1625          valuemask = GCForeground | GCBackground | GCFunction;
1626
1627          isNumBitmaps = 0;
1628          ptr = filenames;
1629          while (ptr != NULL && !done)
1630            {
1631              while (*ptr != '\0' && !isspace(*ptr))
1632                ptr++;
1633
1634              if (*ptr == '\0')
1635                done = 1;
1636              else
1637                *ptr = '\0';
1638
1639              isBitmaps[isNumBitmaps] = XmuLocateBitmapFile(XtScreen(is),
1640                                                            filenames,
1641                                                            NULL, 0,
1642                                                            &isWidth,
1643                                                            &isHeight,
1644                                                            NULL, NULL);
1645              if (isBitmaps[isNumBitmaps] == None)
1646                return; /* abort */
1647              isNumBitmaps++;
1648              if (!done)
1649                {
1650                  *ptr = ' ';
1651                  while (isspace(*ptr))
1652                    ptr++;
1653                }
1654              filenames = ptr;
1655            }
1656
1657          isGC = XtGetGC(is, valuemask, &values);
1658        }
1659    }
1660}
1661
1662static short conditions[] =
1663{
1664   3116,  3147,  3180,  3211,  3243,  3273,  3305,  3335,  3366,  3397,
1665   3427,  3459,  3617,  3647,  3682,  3711,  3742,  3774,  3804,  3836,
1666   3866,  3897,  3928,  3959,  3990,  4148,  4179,  4211,  4242,  4274,
1667   4304,  4336,  4366,  4397,  4429,  4459,  4491,  4649,  4680,  4713,
1668   4743,  4775,  4805,  4837,  4868,  4898,  4930,  4961,  4990,  5022,
1669   5180,  5211,  5244,  5274,  5306,  5336,  5368,  5398,  5429,  5461,
1670   5491,  5523,  5682,  5712,  5746,  5776,  5807,  5838,  5869,  5899,
1671   5930,  5962,  5992,  6024,  6183,  6214,  6246,  6277,  6308,  6338,
1672   6370,  6399,  6429,  6460,  6491,  6522,  6554,  6713,  6743,  6777,
1673   6808,  6839,  6869,  6901,  6931,  6961,  6993,  7023,  7055,  7214,
1674   7244,  7278,  7309,  7341,  7371,  7402,  7433,  7463,  7494,  7525,
1675   7556,  7715,  7746,  7779,  7810,  7842,  7871,  7902,  7933,  7964,
1676   7994,  8025,  8056,  8087,  8246,  8276,  8309,  8340,  8371,  8402,
1677   8434,  8464,  8495,  8526,  8557,  8588,  8746,  8777,  8810,  8841,
1678   8872,  8903,  8935,  8965,  8996,  9028,  9058,  9090,  9119,  9278,
1679   9308,  9341,  9372,  9403,  9434,  9465,  9496,  9527,  9558,  9589,
1680   9621,  9779,  9810,  9843,  9873,  9905,  9935,  9967,  9997, 10028,
1681  10059, 10090, 10122, 10281, 10311, 10344, 10374, 10405, 10436, 10467,
1682  10497, 10527, 10557, 10589, 10620, 10652, 10810, 10841, 10875, 10905,
1683  10936, 10967, 10998, 11028, 11059, 11090, 11121, 11153, 11311, 11342,
1684  11376, 11407, 11438, 11468, 11500, 11530, 11560, 11592, 11622, 11654,
1685  11813, 11843, 11877, 11908, 11939, 11970, 12001, 12031, 12061, 12091,
1686  12123, 12153, 12185, 12343, 12374, 12407, 12437, 12469, 12500, 12531,
1687  12562, 12592, 12623, 12654, 12685, 12844, 12874, 12908, 12939, 12970,
1688  13001, 13032, 13063, 13094, 13125, 13156, 13187, 13345, 13375, 13409,
1689  13439, 13469, 13501, 13532, 13563, 13594, 13624, 13656, 13687, 13718,
1690  13877, 13907, 13940, 13971, 14002, 14033, 14064, 14095, 14125, 14157,
1691  14188, 14220, 14378, 14409, 14441, 14471, 14503, 14533, 14564, 14595,
1692  14626, 14657, 14687, 14718, 14749, 14908, 14939, 14972, 15002, 15034,
1693  15064, 15095, 15126, 15156, 15188, 15219, 15250, 15409, 15440, 15474,
1694  15504, 15535, 15566, 15597, 15627, 15658, 15689, 15720, 15751, 15910,
1695  15941, 15975, 16005, 16037, 16067, 16099, 16129, 16158, 16189, 16220,
1696  16251, 16282, 16441, 16472, 16505, 16535, 16567, 16597, 16629, 16659,
1697  16689, 16721, 16751, 16783, 16941, 16972, 17006, 17036, 17068, 17099,
1698  17130, 17161, 17191, 17222, 17253, 17284, 17443, 17473, 17507, 17537,
1699  17569, 17599, 17629, 17661, 17691, 17722, 17753, 17784, 17815, 17974,
1700  18004, 18038, 18068, 18100, 18130, 18162, 18193, 18223, 18255, 18285,
1701  18317, 18475, 18506, 18538, 18569, 18600, 18631, 18662, 18693, 18723,
1702  18755, 18786, 18817, 18847, 19006, 19036, 19069, 19100, 19131, 19161,
1703  19193, 19223, 19254, 19286, 19316, 19348, 19507, 19538, 19571, 19601,
1704  19633, 19663, 19694, 19725, 19755, 19787, 19817, 19849, 20008, 20039,
1705  20072, 20103, 20134, 20165, 20196, 20226, 20257, 20286, 20318, 20348,
1706  20380, 20539, 20570, 20602, 20633, 20664, 20695, 20726, 20756, 20787,
1707  20818, 20849, 20880, 21039, 21070, 21103, 21134, 21166, 21196, 21228,
1708  21258, 21288, 21320, 21350, 21382, 21540, 21571, 21604, 21635, 21667,
1709  21697, 21729, 21759, 21789, 21819, 21851, 21881, 21913, 22071, 22102,
1710  22135, 22166, 22197, 22228, 22260, 22290, 22321, 22352, 22383, 22414,
1711  22573, 22603, 22636, 22666, 22698, 22728, 22760, 22790, 22821, 22853,
1712  22883, 22915, 23073, 23103, 23137, 23167, 23197, 23228, 23259, 23290,
1713  23321, 23352, 23383, 23414, 23446
1714};
1715
1716static int conditions_len = sizeof(conditions) / sizeof(short);
1717
1718static Boolean conditionsMet()
1719{
1720  time_t t;
1721  struct tm *now;
1722  short test;
1723  int i;
1724
1725  t = time(0);
1726  now = localtime(&t);
1727  test = (now->tm_year - 92) * 512 + (now->tm_mon + 1) * 32 + now->tm_mday;
1728
1729  i = 0;
1730  while ((i < conditions_len) && (test > conditions[i]))
1731    i++;
1732
1733  if (i == conditions_len)
1734    return False;
1735
1736  if ((test == conditions[i]) && (now->tm_hour >= 18))
1737    return True;
1738
1739  return False;
1740}
1741
1742static short auxconditions[] =
1743{
1744  1424, 1427, 1428, 1429, 1430, 1718, 1719, 1720, 1721, 1722,
1745  2448, 2449, 2450, 2451, 2452, 2739, 2740, 2741, 2742, 2743,
1746  3470, 3471, 3472, 3473, 3474, 3761, 3762, 3763, 3764, 3765
1747};
1748
1749static int auxconditions_len = sizeof(auxconditions) / sizeof(short);
1750
1751static Boolean auxConditions()
1752{
1753  time_t t;
1754  struct tm *now;
1755  short test;
1756  int i;
1757
1758  t = time(0);
1759  now = localtime(&t);
1760  test = (now->tm_year - 92) * 512 + (now->tm_mon + 1) * 32 + now->tm_mday;
1761
1762  i = 0;
1763  while ((i < auxconditions_len) && (test > auxconditions[i]))
1764    i++;
1765
1766  if (i == auxconditions_len)
1767    return False;
1768
1769  if ((test == auxconditions[i]) &&
1770      (now->tm_hour >= 6) && (now->tm_hour <= 18))
1771    return True;
1772
1773  return False;
1774}
1775
1776static void adjustOwl(search)
1777     Widget search;
1778{
1779  Widget version, logo, eyes, Slogo, Sversion;
1780  XtWidgetGeometry logoGeom, eyesGeom, versionGeom;
1781  Pixmap owlPix;
1782  Arg args[2];
1783  int newx;
1784
1785  if (conditionsMet())
1786    {
1787      /* Look up important widgets. */
1788      logo = WcFullNameToWidget(search, "*login*logo");
1789      eyes = WcFullNameToWidget(search, "*eyes");
1790      version = WcFullNameToWidget(search, "*login*version");
1791
1792      Slogo = WcFullNameToWidget(search, "*hitanykey*logo");
1793      Sversion = WcFullNameToWidget(search, "*hitanykey*version");
1794
1795      /* Plug in the owl bitmap. */
1796      owlPix = XCreatePixmapFromBitmapData(dpy, DefaultRootWindow(dpy),
1797                                           owl_bits, owl_width, owl_height,
1798                                           1, 0, 1);
1799      XtSetArg(args[0], XtNbitmap, owlPix);
1800      XtSetValues(logo, args, 1);
1801
1802      XtSetValues(Slogo, args, 1);
1803
1804      /* Adjust position of eyes and version. */
1805      XtQueryGeometry(logo, NULL, &logoGeom);
1806      XtQueryGeometry(eyes, NULL, &eyesGeom);
1807      XtQueryGeometry(version, NULL, &versionGeom);
1808
1809      XtMoveWidget(eyes, (logoGeom.width -
1810                          ((eyesGeom.x - logoGeom.x) + eyesGeom.width)) +
1811                         logoGeom.x,
1812                   eyesGeom.y);
1813
1814      newx = (logoGeom.width - ((versionGeom.x - logoGeom.x) +
1815                                versionGeom.width)) + logoGeom.x;
1816      XtMoveWidget(version, newx, versionGeom.y);
1817
1818      /* This depends on the fact that both logos have the same geometry. */
1819      XtSetArg(args[0], XtNhorizDistance, newx);
1820      XtSetValues(Sversion, args, 1);
1821    }
1822}
1823
1824/* Called from within the toolkit. */
1825void localErrorHandler(s)
1826     String s;
1827{
1828  fprintf(stderr, "XLogin X error: %s\n", s);
1829  cleanup(NULL);
1830  exit(1);
1831}
1832
1833static void catch_child()
1834{
1835  int pid;
1836  int status;
1837  char *number();
1838
1839  while (1)
1840    {
1841      pid = waitpid(-1, &status, WNOHANG);
1842      if (pid == -1 && errno == ECHILD)
1843        break;
1844      if (pid == activation_pid)
1845        {
1846          switch(activation_state)
1847            {
1848            case REACTIVATING:
1849              if (pid == activation_pid)
1850                activation_state = ACTIVATED;
1851              break;
1852            case ACTIVATED:
1853            default:
1854              fprintf(stderr, "XLogin: child %d exited\n", pid);
1855            }
1856        }
1857      else if (pid == attach_pid)
1858        {
1859          attach_state = WEXITSTATUS(status);
1860        }
1861      else if (pid == attachhelp_pid)
1862        {
1863          attachhelp_state =  WEXITSTATUS(status);
1864        }
1865      else if (pid == quota_pid)
1866        {
1867          quota_pid = 0;
1868        }
1869      else
1870        {
1871          fprintf(stderr, "XLogin: child %d exited with status %d\n",
1872                  pid, WEXITSTATUS(status));
1873        }
1874    }
1875}
1876
1877static void setFontPath()
1878{
1879  static int ndirs = 0;
1880  static char **dirlist;
1881  char *cp, **oldlist;
1882  int i, j, nold;
1883  char *dirs;
1884  struct stat statbuf;
1885
1886  if (!ndirs)
1887    {
1888      /* Make a copy of the fontpath which we can step on. */
1889      dirs = strdup(resources.fontpath);
1890      if (dirs == NULL)
1891        localErrorHandler("Out of memory");
1892
1893      /* Get the old font path so we can add to it. */
1894      oldlist = XGetFontPath(dpy, &nold);
1895
1896      /* Count the number of directories we will have total. */
1897      ndirs = nold + 1;
1898      cp = dirs;
1899      while (cp = strchr(cp, ','))
1900        {
1901          ndirs++;
1902          cp++;
1903        }
1904
1905      /* Allocate space for the directory list. */
1906      dirlist = (char **) malloc(ndirs * sizeof(char *));
1907      if (dirlist == NULL)
1908        localErrorHandler("Out of memory");
1909
1910      /* Copy the old directory list. */
1911      for (i = 0; i < nold; i++)
1912        dirlist[i] = strdup(oldlist[i]);
1913      XFreeFontPath(oldlist);
1914
1915      /* Copy the entries in resources.fontpath. */
1916      cp = dirs;
1917      dirlist[i++] = cp;
1918      while (cp = strchr(cp, ','))
1919        {
1920          *cp++ = '\0';
1921          dirlist[i++] = cp;
1922        }
1923
1924      /* Discard directories which aren't present. */
1925      j = 0;
1926      for (i = 0; i < ndirs; i++)
1927        {
1928          if (stat(dirlist[i], &statbuf) == 0 && S_ISDIR(statbuf.st_mode))
1929            dirlist[j++] = dirlist[i];
1930        }
1931      ndirs = j;
1932    }
1933
1934  XSetFontPath(dpy, dirlist, ndirs);
1935}
Note: See TracBrowser for help on using the repository browser.