538 lines
13 KiB
C
538 lines
13 KiB
C
#if !defined( lint ) && !defined( SABER )
|
|
static const char sccsid[] = "@(#)logout.c 4.02 97/04/01 xlockmore";
|
|
|
|
#endif
|
|
|
|
/*-
|
|
* logout.c: handle compile-time optional logout
|
|
*
|
|
* See xlock.c for copying information.
|
|
*
|
|
* xclosedown code
|
|
* Copyright 1990 by Janet Carson
|
|
* Permission to use, copy, modify, and distribute this software and its
|
|
* documentation for any purpose is hereby granted without fee, provided that
|
|
* the above copyright notice appear in all copies and that both that
|
|
* copyright notice and this permission notice appear in supporting
|
|
* documentation. The author makes no representations about the
|
|
* suitability of this software for any purpose. It is provided "as is"
|
|
* without express or implied warranty.
|
|
*
|
|
* Revision History:
|
|
* 27-Jul-95: put back in logout.c (oops).
|
|
* Window shutdown program by Janet L. Carson,
|
|
* Baylor College of Medicine.
|
|
* Main procedure modified for use w/ xlock by
|
|
* Anthony Thyssen <anthony@cit.gu.edu.au>.
|
|
* 24-Feb-95: fullLock rewritten to handle non-default group names from
|
|
* Dale A. Harris <rodmur@ecst.csuchico.edu>
|
|
* 13-Feb-95: Heath A. Kehoe <hakehoe@icaen.uiowa.edu>.
|
|
* Mostly taken from bomb.c
|
|
* 1994: bomb.c written. Copyright (c) 1994 Dave Shield
|
|
* Liverpool Computer Science
|
|
*/
|
|
|
|
#include "xlock.h"
|
|
#include "iostuff.h"
|
|
|
|
#if defined( USE_AUTO_LOGOUT ) || defined( USE_BUTTON_LOGOUT ) || defined( USE_BOMB )
|
|
|
|
#if ( HAVE_SYSLOG_H && defined( USE_SYSLOG ))
|
|
#include <syslog.h>
|
|
#endif
|
|
#if defined(__CYGWIN__) || defined(SOLARIS2)
|
|
#include <signal.h>
|
|
#endif
|
|
#include <sys/signal.h>
|
|
|
|
extern Bool inroot, inwindow, nolock, debug;
|
|
extern char *logoutCmd;
|
|
|
|
/*-
|
|
* This file contains a function called logoutUser() that, when called,
|
|
* will (try) to log out the user.
|
|
*
|
|
* A portable way to do this is to simply kill all of the user's processes,
|
|
* but this is a really ugly way to do it (it kills background jobs that
|
|
* users may want to remain running after they log out).
|
|
*
|
|
* If your system provides for a cleaner/easier way to log out a user,
|
|
* you may implement it here.
|
|
*
|
|
* For example, on some systems, one may define for the users an environment
|
|
* variable named XSESSION that contains the pid of the X session leader
|
|
* process. So, to log the user out, we just need to kill that process,
|
|
* and the X session will end. Of course, a user can defeat that by
|
|
* changing the value of XSESSION; so we can fall back on the ugly_logout()
|
|
* method.
|
|
*
|
|
* If you can't log the user out (and you don't want to use the brute
|
|
* force method) simply return from logoutUser(), and xlock will continue
|
|
* on it's merry way (only applies if USE_AUTO_LOGOUT or USE_BUTTON_LOGOUT
|
|
* is defined.)
|
|
*/
|
|
|
|
#define NAP_TIME 5 /* Sleep between shutdown attempts */
|
|
|
|
|
|
#ifdef CLOSEDOWN_LOGOUT
|
|
|
|
/* Logout the user by contacting the display and closeing all windows */
|
|
|
|
|
|
/*-
|
|
* Window shutdown program by Janet L. Carson, Baylor College of Medicine.
|
|
* Version 1.0, placed in /contrib on 2/12/90.
|
|
*
|
|
* Please send comments or fixes to jcarson@bcm.tmc.edu
|
|
*/
|
|
|
|
/*-
|
|
* I'm probably going to get some BadWindow errors as I kill clients
|
|
* which have multiple windows open and then try to kill them again
|
|
* on another of their windows. I'm just going to plow right through!
|
|
* The flag is set back to false in recurse_tree and kill_tree...
|
|
*/
|
|
|
|
static int err_occurred = False;
|
|
|
|
static int
|
|
err_handler(Display * display, XErrorEvent * err)
|
|
{
|
|
err_occurred = True;
|
|
return 0;
|
|
}
|
|
|
|
/*-
|
|
* Looking for properties...
|
|
*/
|
|
|
|
static int
|
|
has_property(Display * display, Window window, Atom prop)
|
|
{
|
|
int nprops, j, retval = 0;
|
|
Atom *list = XListProperties(display, window, &nprops);
|
|
|
|
if (err_occurred)
|
|
return 0;
|
|
|
|
for (j = 0; j < nprops; j++) {
|
|
if (list[j] == prop) {
|
|
retval = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (nprops)
|
|
XFree((caddr_t) list);
|
|
|
|
return retval;
|
|
}
|
|
|
|
/*-
|
|
* Send a WM_PROTOCOLS WM_DELETE_WINDOW message to a window
|
|
*/
|
|
|
|
static void
|
|
send_delete_message(Display * display, Window window,
|
|
Atom protocols_atom, Atom delete_window_atom)
|
|
{
|
|
XClientMessageEvent xclient;
|
|
|
|
xclient.type = ClientMessage;
|
|
xclient.send_event = True;
|
|
xclient.display = display;
|
|
xclient.window = window;
|
|
xclient.message_type = protocols_atom;
|
|
xclient.format = 32;
|
|
xclient.data.l[0] = delete_window_atom;
|
|
|
|
XSendEvent(display, window, False, 0, (XEvent *) & xclient);
|
|
}
|
|
|
|
/*-
|
|
* To shutdown a top level window: if the window participates
|
|
* in WM_DELETE_WINDOW, let the client shut itself off. Otherwise,
|
|
* do an XKillClient on it.
|
|
*/
|
|
|
|
static void
|
|
handle_top_level(Display * display, Window window,
|
|
Atom protocols_atom, Atom delete_window_atom)
|
|
{
|
|
Atom *prots;
|
|
int nprots, j;
|
|
|
|
if (has_property(display, window, protocols_atom)) {
|
|
XGetWMProtocols(display, window, &prots, &nprots);
|
|
|
|
if (err_occurred)
|
|
return;
|
|
|
|
for (j = 0; j < nprots; j++)
|
|
if (prots[j] == delete_window_atom) {
|
|
send_delete_message(display, window,
|
|
protocols_atom, delete_window_atom);
|
|
break;
|
|
}
|
|
if (j == nprots) /* delete window not found */
|
|
XKillClient(display, window);
|
|
|
|
XFree((caddr_t) prots);
|
|
} else
|
|
XKillClient(display, window);
|
|
}
|
|
|
|
/*-
|
|
* recurse_tree: look for top level windows to kill all the way down
|
|
* the window tree. This pass is "nice"--I'll use delete_window protocol
|
|
* if the window supports it. If I get an error in the middle, I'll start
|
|
* over again at the same level, because reparenting window managers throw
|
|
* windows back up to the root...
|
|
*/
|
|
|
|
static void
|
|
recurse_tree(Display * display, Window window,
|
|
Atom state_atom, Atom protocols_atom, Atom delete_window_atom)
|
|
{
|
|
Window root, parent, *kids;
|
|
unsigned int nkids;
|
|
int j;
|
|
int swm_state;
|
|
|
|
for (;;) {
|
|
XQueryTree(display, window, &root, &parent, &kids, &nkids);
|
|
if (err_occurred) {
|
|
err_occurred = False;
|
|
return;
|
|
}
|
|
for (j = 0; j < nkids; j++) {
|
|
swm_state = has_property(display, kids[j], state_atom);
|
|
|
|
if (err_occurred)
|
|
break;
|
|
|
|
if (swm_state) {
|
|
handle_top_level(display, kids[j], protocols_atom, delete_window_atom);
|
|
if (err_occurred)
|
|
break;
|
|
} else
|
|
recurse_tree(display, kids[j],
|
|
state_atom, protocols_atom, delete_window_atom);
|
|
}
|
|
|
|
XFree((caddr_t) kids);
|
|
|
|
/* when I get all the way through a level without an error, I'm done */
|
|
|
|
if (err_occurred)
|
|
err_occurred = False;
|
|
else
|
|
return;
|
|
|
|
}
|
|
}
|
|
|
|
/*-
|
|
* This is the second pass--anything left gets an XKillClient!
|
|
*/
|
|
|
|
static void
|
|
kill_tree(Display * display, Window window)
|
|
{
|
|
Window root, parent, *kids;
|
|
unsigned int nkids;
|
|
int j;
|
|
|
|
for (;;) {
|
|
XQueryTree(display, window, &root, &parent, &kids, &nkids);
|
|
if (err_occurred) {
|
|
err_occurred = False;
|
|
return;
|
|
}
|
|
for (j = 0; j < nkids; j++) {
|
|
XKillClient(display, kids[j]);
|
|
if (err_occurred)
|
|
break;
|
|
}
|
|
|
|
XFree((caddr_t) kids);
|
|
|
|
/* when I get all the way through a level without an error, I'm done */
|
|
|
|
if (err_occurred)
|
|
err_occurred = False;
|
|
else
|
|
return;
|
|
}
|
|
}
|
|
|
|
/*-
|
|
* Main program
|
|
*/
|
|
|
|
static void
|
|
closedownLogout(Display * display, int screens)
|
|
{
|
|
Atom __SWM_DELETE_WINDOW = None;
|
|
Atom __SWM_PROTOCOLS = None;
|
|
Atom __SWM_STATE = None;
|
|
int j;
|
|
|
|
#if 0
|
|
/* synchronize -- so I'm aware of errors immediately */
|
|
XSynchronize(display, True);
|
|
|
|
/* use my error handler from here on out */
|
|
(void) XSetErrorHandler(err_handler);
|
|
#endif
|
|
|
|
/* init atoms */
|
|
__SWM_STATE = XInternAtom(display, "__SWM_STATE", False);
|
|
__SWM_PROTOCOLS = XInternAtom(display, "__SWM_PROTOCOLS", False);
|
|
__SWM_DELETE_WINDOW = XInternAtom(display, "__SWM_DELETE_WINDOW", False);
|
|
|
|
/* start looking for windows to kill -- be nice on pass 1 */
|
|
for (j = 0; j < screens; j++)
|
|
recurse_tree(display, RootWindow(display, j),
|
|
__SWM_STATE, __SWM_PROTOCOLS, __SWM_DELETE_WINDOW);
|
|
|
|
/* wait for things to clean themselves up */
|
|
(void) sleep(NAP_TIME);
|
|
|
|
/* this will forcibly kill anything that's still around --
|
|
this second pass may or may not be needed... */
|
|
for (j = 0; j < screens; j++)
|
|
kill_tree(display, RootWindow(display, j));
|
|
(void) sleep(NAP_TIME);
|
|
}
|
|
|
|
#endif /* CLOSEDOWN_LOGOUT */
|
|
|
|
#ifdef SESSION_LOGOUT
|
|
static void
|
|
sessionLogout(void)
|
|
{
|
|
char *pidstr;
|
|
|
|
pidstr = getenv("XSESSION");
|
|
if (pidstr) {
|
|
kill(atoi(pidstr), SIGTERM);
|
|
(void) sleep(NAP_TIME);
|
|
}
|
|
}
|
|
|
|
#endif /* SESSION_LOGOUT */
|
|
|
|
#ifdef SunCplusplus
|
|
/* #include <signal.h> */
|
|
extern void (*signal(int, void (*)(int))) (int);
|
|
extern int kill(pid_t, int);
|
|
#else
|
|
#if 0
|
|
extern int signal(int, void *);
|
|
extern int kill(int, int);
|
|
#endif
|
|
#endif
|
|
|
|
static void
|
|
uglyLogout(void)
|
|
{
|
|
#ifndef VMS
|
|
#ifndef KILL_ALL_OTHERS
|
|
#define KILL_ALL_OTHERS -1
|
|
(void) signal(SIGHUP, SIG_IGN);
|
|
(void) signal(SIGTERM, SIG_IGN);
|
|
#endif
|
|
|
|
(void) kill(KILL_ALL_OTHERS, SIGHUP);
|
|
(void) sleep(NAP_TIME);
|
|
(void) kill(KILL_ALL_OTHERS, SIGTERM);
|
|
(void) sleep(NAP_TIME);
|
|
|
|
#if ( HAVE_SYSLOG_H && defined( USE_SYSLOG ))
|
|
syslog(SYSLOG_NOTICE, "%s: failed to exit - sending kill (uid %d)\n",
|
|
ProgramName, getuid());
|
|
#endif
|
|
|
|
(void) kill(KILL_ALL_OTHERS, SIGKILL);
|
|
(void) sleep(NAP_TIME);
|
|
|
|
#if ( HAVE_SYSLOG_H && defined( USE_SYSLOG ))
|
|
syslog(SYSLOG_WARNING, "%s: still won't exit - suicide (uid %d)\n",
|
|
ProgramName, getuid());
|
|
#endif
|
|
|
|
(void) kill((int) getpid(), SIGKILL);
|
|
#endif /* !VMS */
|
|
exit(-1);
|
|
}
|
|
|
|
#if ( HAVE_SYSLOG_H && defined( USE_SYSLOG ))
|
|
extern void syslogStop(char *);
|
|
#endif
|
|
|
|
void
|
|
logoutUser(Display * display
|
|
#ifdef CLOSEDOWN_LOGOUT
|
|
, int screens
|
|
#endif
|
|
)
|
|
{
|
|
|
|
#if ( HAVE_SYSLOG_H && defined( USE_SYSLOG ))
|
|
syslog(SYSLOG_INFO, "%s: expired. closing down (uid %d) on %s\n",
|
|
ProgramName, getuid(), getenv("DISPLAY"));
|
|
syslogStop(XDisplayString(display));
|
|
#endif
|
|
if (logoutCmd && *logoutCmd) {
|
|
int cmd_pid;
|
|
|
|
if ((cmd_pid = (int) FORK()) == -1) {
|
|
(void) fprintf(stderr, "Failed to launch \"%s\"\n", logoutCmd);
|
|
perror(ProgramName);
|
|
cmd_pid = 0;
|
|
} else if (!cmd_pid) {
|
|
(void) system(logoutCmd);
|
|
exit(0);
|
|
}
|
|
}
|
|
#ifdef CLOSEDOWN_LOGOUT
|
|
(void) finish(display, False);
|
|
#else
|
|
(void) finish(display, True);
|
|
#endif
|
|
#ifdef VMS
|
|
(void) system("mcr decw$endsession -noprompt");
|
|
#else
|
|
#ifdef __sgi
|
|
(void) system("/usr/bin/X11/tellwm end_session >/dev/null 2>&1");
|
|
(void) sleep(10); /* Give the above a chance to run */
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef CLOSEDOWN_LOGOUT
|
|
/* Do not want to kill other user's processes e.g. telnet session */
|
|
closedownLogout(display, screens);
|
|
return;
|
|
#endif
|
|
#ifdef SESSION_LOGOUT
|
|
sessionLogout();
|
|
#endif
|
|
uglyLogout();
|
|
exit(-1);
|
|
}
|
|
|
|
|
|
#if defined( USE_AUTO_LOGOUT ) || defined( USE_BUTTON_LOGOUT )
|
|
/*
|
|
* Determine whether to "fully" lock the terminal, or
|
|
* whether the time-limited version should be imposed.
|
|
*
|
|
* Policy:
|
|
* Members of staff can fully lock
|
|
* (hard-wired user/group names + file read at run time)
|
|
* Students (i.e. everyone else)
|
|
* are forced to use the time-limit version.
|
|
*
|
|
* An alternative policy could be based on display location
|
|
*/
|
|
#define FULL_LOCK 1
|
|
#define TEMP_LOCK 0
|
|
|
|
/* assuming only staff file is needed */
|
|
#ifndef STAFF_FILE
|
|
#define STAFF_FILE "/usr/remote/etc/xlock.staff"
|
|
#endif
|
|
|
|
#undef passwd
|
|
#undef pw_name
|
|
#undef getpwnam
|
|
#undef getpwuid
|
|
#include <pwd.h>
|
|
#include <grp.h>
|
|
#include <errno.h>
|
|
#ifndef NGROUPS
|
|
#if HAVE_LIMITS_H
|
|
#include <limits.h>
|
|
#endif
|
|
#ifdef NGROUPS_MAX
|
|
#define NGROUPS NGROUPS_MAX
|
|
#else
|
|
#define NGROUPS NGROUPS_MAX_DEFAULT
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef NO_NEGATIVE_LOGOUT
|
|
extern int logoutButton;
|
|
#endif
|
|
|
|
int
|
|
fullLock(void)
|
|
{
|
|
uid_t uid;
|
|
int ngroups = NGROUPS;
|
|
|
|
#ifdef SUNOS4
|
|
gid_t mygidset[NGROUPS * sizeof (gid_t)];
|
|
|
|
#else
|
|
gid_t mygidset[NGROUPS];
|
|
|
|
#endif
|
|
int ngrps, i;
|
|
struct passwd *pwp;
|
|
struct group *gp;
|
|
FILE *fp;
|
|
char buf[BUFSIZ];
|
|
#ifndef NO_NEGATIVE_LOGOUT
|
|
|
|
if (logoutButton < 0)
|
|
return (FULL_LOCK);
|
|
#endif
|
|
/* The debug portion may depend on what you are debugging. :) */
|
|
if (inwindow || inroot || nolock /*|| debug */ )
|
|
return (FULL_LOCK); /* (mostly) harmless user */
|
|
uid = getuid();
|
|
/* Do not try to logout root! */
|
|
if (!uid)
|
|
return (FULL_LOCK); /* root */
|
|
|
|
pwp = getpwuid(uid);
|
|
if ((ngrps = getgroups(ngroups, mygidset)) == -1)
|
|
perror(ProgramName);
|
|
|
|
#ifdef STAFF_NETGROUP
|
|
if (innetgr(STAFF_NETGROUP, NULL, pwp->pw_name, NULL))
|
|
return (FULL_LOCK);
|
|
#endif
|
|
|
|
if ((fp = my_fopen(STAFF_FILE, "r")) == NULL)
|
|
return (TEMP_LOCK);
|
|
|
|
while ((fgets(buf, BUFSIZ, fp)) != NULL) {
|
|
char *cp;
|
|
|
|
if ((cp = (char *) strchr(buf, '\n')) != NULL)
|
|
*cp = '\0';
|
|
if (!strcmp(buf, pwp->pw_name))
|
|
return (FULL_LOCK);
|
|
if ((gp = getgrnam(buf)) != NULL) {
|
|
/* check all of user's groups */
|
|
#ifdef SUNOS4
|
|
for (i = 1; i < ngrps * sizeof (gid_t); i += 2)
|
|
#else
|
|
for (i = 0; i < ngrps; ++i)
|
|
#endif
|
|
if (gp->gr_gid == mygidset[i])
|
|
return (FULL_LOCK);
|
|
}
|
|
}
|
|
(void) fclose(fp);
|
|
|
|
return (TEMP_LOCK);
|
|
}
|
|
#endif /* USE_AUTO_LOGOUT || USE_BUTTON_LOGOUT */
|
|
|
|
#endif /* USE_AUTO_LOGOUT || USE_BUTTON_LOGOUT || USE_BOMB */
|