source: trunk/athena/bin/session/session_gate.c @ 13610

Revision 13610, 9.8 KB checked in by danw, 25 years ago (diff)
autoconfiscate and warnsify
Line 
1/*
2 *  session_gate - Keeps session alive by continuing to run
3 *
4 *      $Id: session_gate.c,v 1.18 1999-09-21 20:09:49 danw Exp $
5 */
6
7#include <fcntl.h>
8#include <signal.h>
9#include <string.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <sys/types.h>
13#include <sys/file.h>
14#include <sys/stat.h>
15#include <sys/wait.h>
16#include <unistd.h>
17
18#ifdef SGISession
19#include <errno.h>
20#include <X11/Xlib.h>
21
22Display *dpy;
23Window root;
24Atom sessionAtom, wmAtom;
25int logoutsignal = 0;
26int SGISession_debug = 0;
27
28#define SGISession_TIMEOUT 1
29#define SGISession_ATHENALOGOUT 2
30#define SGISession_SGILOGOUT 3
31
32#define SGISession_NOBODY 1
33#define SGISession_WMONLY 2
34#define SGISession_EVERYONE 3
35
36int SGISession_Initialize(void);
37void flaglogout(void);
38int SGISession_Wait(void);
39int SGISession_WhoCares(void);
40void SGISession_EndSession(void);
41void SGISession_Debug(char *message);
42#endif
43
44#define MINUTE 60
45#define PID_FILE_TEMPLATE "/tmp/session_gate_pid."
46
47static char filename[80];
48
49int itoa(int x, char *buf);
50void cleanup(void);
51void logout(void);
52void clean_child(void);
53time_t check_pid_file(char *filename, pid_t pid, time_t mtime);
54time_t create_pid_file(char *filename, pid_t pid);
55time_t update_pid_file(char* filename, pid_t pid);
56void write_pid_to_file(pid_t pid, int fd);
57
58int main(int argc, char **argv)
59{
60    pid_t pid, parentpid;
61    char buf[10];
62    time_t mtime;
63    int dologout = 0;
64    struct sigaction sa;
65#ifdef SGISession
66    int logoutStarted = 0;
67
68
69    while (*argv)
70      {
71        if (!strcmp(*argv, "-logout"))
72          dologout = 1;
73
74        if (!strcmp(*argv, "-debug"))
75          SGISession_debug = 1;
76
77        argv++;
78      }
79#else
80    if (argc == 2 && !strcmp(argv[1], "-logout"))
81      dologout = 1;
82#endif
83
84    pid = getpid();
85    parentpid = getppid();
86   
87    /*  Set up signal handlers for a clean exit  */
88    sa.sa_flags = 0;
89    sigemptyset(&sa.sa_mask);
90
91#ifdef SGISession
92    sa.sa_handler = flaglogout;
93#else
94    if (dologout)
95      sa.sa_handler = logout;
96    else
97      sa.sa_handler = cleanup;
98#endif
99    sigaction(SIGHUP, &sa, NULL);
100    sigaction(SIGINT, &sa, NULL);
101    sigaction(SIGQUIT, &sa, NULL);
102    sigaction(SIGTERM, &sa, NULL);
103
104    /* Clean up zobmied children */
105    sa.sa_handler = clean_child;
106    sigaction(SIGCHLD, &sa, NULL);
107
108    /*  Figure out the filename  */
109
110    strcpy(filename, PID_FILE_TEMPLATE);
111    itoa(getuid(), buf);
112    strcat(filename, buf);
113
114    /*  Put pid in file for the first time  */
115
116    mtime = check_pid_file(filename, pid, (time_t)0);
117
118    /*
119     * Now sit and wait.  If a signal occurs, catch it, cleanup, and exit.
120     * Every minute, wake up and perform the following checks:
121     *   - If parent process does not exist, cleanup and exit.
122     *   - If pid file has been modified or is missing, refresh it.
123     */
124
125#ifdef SGISession
126#define goodbye() if (dologout) logout(); else cleanup()
127
128    if (0 == SGISession_Initialize())
129      {
130        SGISession_Debug("Gating an SGI session\n");
131        while (1)
132          {
133            switch(SGISession_Wait())
134              {
135              case SGISession_TIMEOUT: /* MINUTE passed */
136                SGISession_Debug("chime\n");
137                clean_child();  /* In case there are any zombied children */
138                if (parentpid != getppid())
139                  cleanup();
140                mtime = check_pid_file(filename, pid, mtime);
141                break;
142
143              case SGISession_ATHENALOGOUT: /* HUP signal */
144                SGISession_Debug("Received hangup\n");
145                logoutsignal = 0;
146                if (logoutStarted)
147                  {
148                    /* This is our second signal. Something bad must
149                       have happened the first time. Just log the user
150                       out. */
151                    SGISession_Debug("Second hangup; doing Athena logout\n");
152                    goodbye();
153                  }
154
155                logoutStarted = 1;
156                switch(SGISession_WhoCares())
157                  {
158                  case SGISession_NOBODY:
159                    SGISession_Debug("Nobody cares; doing Athena logout\n");
160                    goodbye();
161                    break;
162                  case SGISession_WMONLY:
163                    SGISession_Debug(
164"Window manager about cares session but reaper was not run;\n\
165calling endsession and doing Athena logout\n");
166                    SGISession_EndSession();
167                    goodbye();
168                    break;
169                  case SGISession_EVERYONE:
170                    SGISession_Debug(
171                     "starting endsession and waiting for window property\n");
172                    SGISession_EndSession();
173                    break;
174                  }
175                break;
176
177              case SGISession_SGILOGOUT: /* property deleted */
178                SGISession_Debug(
179                         "Window property deleted; doing Athena logout\n");
180                goodbye();
181                break;
182              }
183          }
184      }
185    else
186 SGISession_Debug("Gating a normal session; SGISession_Initialize failed\n");
187#endif
188   
189    while (1)
190      {
191          sleep(MINUTE);
192          clean_child();        /* In case there are any zombied children */
193          if (parentpid != getppid())
194            cleanup();
195          mtime = check_pid_file(filename, pid, mtime);
196      }
197}
198
199#ifdef SGISession
200int SGISession_Initialize(void)
201{
202  int screen;
203
204  dpy = XOpenDisplay(NULL);
205
206    if (dpy == NULL)
207      return -1;
208
209  screen = DefaultScreen(dpy);
210  root = RootWindow(dpy, screen);
211
212  sessionAtom = XInternAtom(dpy, "_SGI_SESSION_PROPERTY", False);
213  wmAtom = XInternAtom(dpy, "_SGI_TELL_WM", False);
214
215  XSelectInput(dpy, root, PropertyChangeMask);
216  return 0;
217}
218
219void flaglogout(void)
220{
221  logoutsignal++;
222}
223
224int SGISession_Wait(void)
225{
226  static int initialized = 0;
227  static fd_set read;
228  static struct timeval timeout;
229  int selret;
230  XEvent event;
231
232  if (!initialized)
233    {
234      FD_ZERO(&read);
235      timeout.tv_sec = MINUTE;
236      timeout.tv_usec = 0;
237      initialized++;
238    }
239
240  while (1)
241    {
242      while (XPending(dpy) > 0)
243        {
244          if (logoutsignal)
245            return SGISession_ATHENALOGOUT;
246
247          XNextEvent(dpy, &event);
248          if (event.type == PropertyNotify &&
249              event.xproperty.atom == sessionAtom &&
250              event.xproperty.state == PropertyDelete)
251            return SGISession_SGILOGOUT;
252        }
253
254      FD_SET(ConnectionNumber(dpy), &read);
255
256      if (logoutsignal)
257        return SGISession_ATHENALOGOUT;
258
259      selret = select(ConnectionNumber(dpy) + 1,
260                      &read, NULL, NULL, &timeout);
261      switch(selret)
262        {
263        case -1:
264          if (errno == EINTR && logoutsignal)
265            return SGISession_ATHENALOGOUT;
266          if (errno != EINTR) /* !SIGCHLD, basically */
267            fprintf(stderr,
268                    "session_gate: Unexpected error %d from select\n",
269                    errno);
270          break;
271        case 0:
272          return SGISession_TIMEOUT;
273        default:
274          if (!FD_ISSET(ConnectionNumber(dpy), &read))
275            {
276              /* This can't happen. */
277              fprintf(stderr,
278                      "session_gate: select returned data for unknown fd\n");
279              exit(0);
280            }
281          break;
282        }
283    }
284}
285
286int SGISession_WhoCares(void)
287{
288  Atom *properties;
289  int i, numprops;
290  int wmcares = 0, wecare = 0;
291
292  properties = XListProperties(dpy, root, &numprops);
293  if (properties)
294    {
295      for (i = 0; i < numprops; i++)
296        if (properties[i] == sessionAtom)
297          wecare = 1;
298        else
299          if (properties[i] == wmAtom)
300            wmcares = 1;
301    }
302  else
303    return SGISession_NOBODY;
304
305  XFree(properties);
306
307  if (wmcares && wecare)
308    return SGISession_EVERYONE;
309
310  if (wmcares)
311    return SGISession_WMONLY;
312
313  return SGISession_NOBODY;
314}
315
316void SGISession_EndSession(void)
317{
318  system("/usr/bin/X11/endsession -f");
319}
320
321void SGISession_Debug(char *message)
322{
323  if (SGISession_debug)
324    fprintf(stderr, "session_gate debug: %s", message);
325}
326#endif
327
328
329static int powers[] = {100000,10000,1000,100,10,1,0};
330
331int itoa(int x, char *buf)
332{
333    int i;
334    int pos=0;
335    int digit;
336
337    for (i = 0; powers[i]; i++)
338      {
339          digit = (x/powers[i]) % 10;
340          if ((pos > 0) || (digit != 0) || (powers[i+1] == 0))
341            buf[pos++] = '0' + (char) digit;
342      }
343    buf[pos] = '\0';
344    return pos;
345}
346
347
348void cleanup(void)
349{
350    unlink(filename);
351    exit(0);
352}
353
354void logout(void)
355{
356    int f;
357    struct sigaction sa;
358
359    /* Ignore Alarm in child since HUP probably arrived during
360     * sleep() in main loop.
361     */
362    sa.sa_flags = 0;
363    sigemptyset(&sa.sa_mask);
364    sa.sa_handler = SIG_IGN;
365    sigaction(SIGALRM, &sa, NULL);
366    unlink(filename);
367    f = open(".logout", O_RDONLY, 0);
368    if (f >= 0) {
369        char buf[2];
370
371        if (read(f, buf, 2) == -1)
372            exit(0);
373        lseek(f, 0, SEEK_SET);
374        if (!memcmp(buf, "#!", 2))
375            execl(".logout", ".logout", 0);
376        /* if .logout wasn't executable, silently fall through to
377           old behavior */
378        dup2(f, 0);
379        execl("/bin/athena/tcsh", "logout", 0);
380    }
381    exit(0);
382}
383
384/*
385 * clean_child() --
386 *      Clean up any zombied children that are awaiting this process's
387 *      acknowledgement of the child's death.
388 */
389void clean_child(void)
390{
391    waitpid((pid_t)-1, 0, WNOHANG);
392}
393
394time_t check_pid_file(char *filename, pid_t pid, time_t mtime)
395{
396    struct stat st_buf;
397
398    if (stat(filename, &st_buf) == -1)
399      return create_pid_file(filename, pid);    /*  File gone:  create  */
400    else if (st_buf.st_mtime > mtime)
401      return update_pid_file(filename, pid);    /*  File changed:  update  */
402    else
403      return mtime;                             /*  File unchanged  */
404}
405
406
407time_t create_pid_file(char *filename, pid_t pid)
408{
409    int fd;
410    struct stat st_buf;
411
412    if ((fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0)) >= 0)
413      {
414          write_pid_to_file(pid, fd);
415          fchmod(fd, S_IREAD | S_IWRITE);   /* set file mode 600 */
416          close(fd);
417      }
418
419    stat(filename, &st_buf);
420    return st_buf.st_mtime;
421}
422
423
424time_t update_pid_file(char* filename, pid_t pid)
425{
426    int fd;
427    char buf[BUFSIZ];
428    int count;
429    char* start;
430    char* end;
431    struct stat st_buf;
432
433    /*  Determine if the file already contains the pid  */
434
435    if ((fd = open(filename, O_RDONLY, 0)) >= 0)
436      {
437          if ((count = read(fd, buf, BUFSIZ-1)) > 0)
438            {
439                buf[count-1] = '\0';
440                start = buf;
441                while ((end = strchr(start, '\n')) != 0)
442                  {
443                      *end = '\0';
444                      if (atoi(start) == pid)
445                        {
446                            stat(filename, &st_buf);
447                            return st_buf.st_mtime;
448                        }
449                      start = end + 1;
450                  }
451            }
452          close(fd);
453      }
454
455    /*  Append the pid to the file  */
456
457    if ((fd = open(filename, O_WRONLY | O_APPEND, 0)) >= 0)
458      {
459          write_pid_to_file(pid, fd);
460          close(fd);
461      }
462         
463    stat(filename, &st_buf);
464    return st_buf.st_mtime;
465}
466
467
468void write_pid_to_file(pid_t pid, int fd)
469{
470    char buf[10];
471
472    itoa(pid, buf);
473    strcat(buf, "\n");
474    write(fd, buf, strlen(buf));
475}
Note: See TracBrowser for help on using the repository browser.