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

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