Commit a277ba17 authored by Jiri (George) Lebl's avatar Jiri (George) Lebl Committed by George Lebl

The fifo is not only for chooser now, it is a general protocol to control

Wed Jul 04 20:58:31 2001  George Lebl <jirka@5z.com>

	* gdm-safe-restart.in, Makefile.am, configure.in, daemon/choose.[ch],
	  daemon/gdm.[ch], daemon/slave.c, daemon/xdmcp.c, gui/gdmconfig.c:
	  The fifo is not only for chooser now, it is a general protocol to
	  control some gdm things, more to come.  It's writing direct binary
	  structs, but then again this is only a fifo and you should only be
	  able to write to it from gdm and root account on the same machine.
	  The daemon now knows when a user is logged in on a display and
	  there is a safe restart option with SIGUSR1 to the daemon.  Also
	  the daemon knows the x pids now and can whack the X server when
	  the slave crashes, so that we don't get busy hanging server.

	* daemon/misc.c: fails are LOG_CRIT not LOG_ERR (I hope this is
	  correct)
parent 0fb36737
Wed Jul 04 20:58:31 2001 George Lebl <jirka@5z.com>
* gdm-safe-restart.in, Makefile.am, configure.in, daemon/choose.[ch],
daemon/gdm.[ch], daemon/slave.c, daemon/xdmcp.c, gui/gdmconfig.c:
The fifo is not only for chooser now, it is a general protocol to
control some gdm things, more to come. It's writing direct binary
structs, but then again this is only a fifo and you should only be
able to write to it from gdm and root account on the same machine.
The daemon now knows when a user is logged in on a display and
there is a safe restart option with SIGUSR1 to the daemon. Also
the daemon knows the x pids now and can whack the X server when
the slave crashes, so that we don't get busy hanging server.
* daemon/misc.c: fails are LOG_CRIT not LOG_ERR (I hope this is
correct)
Tue Jul 03 13:18:18 2001 George Lebl <jirka@5z.com>
* Makefile.am, config/Makefile.am: prepend $DESTDIR to $PAM_PREFIX
......
......@@ -10,9 +10,10 @@ EXTRA_DIST = \
xml-i18n-extract.in \
xml-i18n-merge.in \
xml-i18n-update.in \
gdm-restart.in
gdm-restart.in \
gdm-safe-restart.in
sbin_SCRIPTS = gdm-restart
sbin_SCRIPTS = gdm-restart gdm-safe-restart
install-data-local: gdmconfig-security
if CONSOLE_HELPER
......
......@@ -16,3 +16,8 @@ xsri like logo functionality
the photosetup proggie should be put into some sort of crapplet and there
should be other settings in the crapplet as well.
Version /var/run/gdm.version, and check this on gdm-safe-restart, and
do safe restart in spec file
Make sure that greeter whacks all on SIGPIPE (X connection fail)
......@@ -392,6 +392,7 @@ config/gdm.conf
config/Gnome
config/gnomerc
gdm-restart
gdm-safe-restart
gdmconfig-security
gdm.spec
])
......
......@@ -29,7 +29,6 @@
#include <errno.h>
#include <X11/Xlib.h>
#include <X11/Xmd.h>
#include <X11/Xdmcp.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
......@@ -81,39 +80,23 @@ remove_oldest_pending (void)
}
gboolean
gdm_choose_socket_handler (GIOChannel *source, GIOCondition cond, gpointer data)
gdm_choose_data (void *uncast_data, size_t len)
{
gchar buf[PIPE_SIZE];
gint len;
int id;
struct in_addr addr;
GdmChooseData data;
GSList *li;
if (cond != G_IO_IN)
return TRUE;
if (g_io_channel_read (source, buf, PIPE_SIZE, &len) != G_IO_ERROR_NONE)
return TRUE;
gdm_debug ("gdm_choose_socket_handler: Read %d bytes", len);
if (buf[0] != STX)
return TRUE;
if (len != 1/*stx*/ + sizeof (int) /* id */ + sizeof (struct in_addr)) {
gdm_debug ("gdm_choose_socket_handler: Data not correct size");
return TRUE;
if (len != sizeof (data)) {
return FALSE;
}
id = *(int *) (&buf[1]);
memcpy (&addr, &buf[1+sizeof(int)], sizeof (struct in_addr));
memcpy (&data, uncast_data, len);
gdm_debug ("gdm_choose_socket_handler: got indirect id: %d address: %s",
id, inet_ntoa (addr));
gdm_debug ("gdm_choose_data: got indirect id: %d address: %s",
data.id, inet_ntoa (data.addr));
for (li = indirect; li != NULL; li = li->next) {
GdmIndirectDisplay *idisp = li->data;
if (idisp->id == id) {
if (idisp->id == data.id) {
/* whack the oldest if more then allowed */
while (ipending >= GdmMaxIndirect &&
remove_oldest_pending ())
......@@ -122,7 +105,7 @@ gdm_choose_socket_handler (GIOChannel *source, GIOCondition cond, gpointer data)
idisp->acctime = time (NULL);
g_free (idisp->chosen_host);
idisp->chosen_host = g_new (struct in_addr, 1);
memcpy (idisp->chosen_host, &addr,
memcpy (idisp->chosen_host, &(data.addr),
sizeof (struct in_addr));
/* Now this display is pending */
......@@ -132,7 +115,7 @@ gdm_choose_socket_handler (GIOChannel *source, GIOCondition cond, gpointer data)
}
}
return TRUE;
return FALSE;
}
......
......@@ -21,6 +21,9 @@
#include "gdm.h"
#include <netinet/in.h>
#include <arpa/inet.h>
GdmIndirectDisplay * gdm_choose_indirect_alloc (struct sockaddr_in *clnt_sa);
GdmIndirectDisplay * gdm_choose_indirect_lookup (struct sockaddr_in *clnt_sa);
void gdm_choose_indirect_dispose (GdmIndirectDisplay *id);
......@@ -28,11 +31,14 @@ void gdm_choose_indirect_dispose (GdmIndirectDisplay *id);
/* dispose of indirect display of id, if no host is set */
void gdm_choose_indirect_dispose_empty_id (guint id);
gboolean gdm_choose_socket_handler (GIOChannel *source,
GIOCondition cond,
gpointer data);
typedef struct _GdmChooseData GdmChooseData;
struct _GdmChooseData {
int id;
struct in_addr addr;
};
gboolean gdm_choose_data (void *uncast_data,
size_t len);
#endif /* CHOOSE_H */
......
......@@ -21,6 +21,10 @@
#include <signal.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <ctype.h>
#include <pwd.h>
#include <grp.h>
......@@ -35,6 +39,7 @@
#include "xdmcp.h"
#include "verify.h"
#include "display.h"
#include "choose.h"
#include "errorgui.h"
static const gchar RCSid[]="$Id$";
......@@ -44,6 +49,11 @@ static const gchar RCSid[]="$Id$";
static void gdm_config_parse (void);
static void gdm_local_servers_start (GdmDisplay *d);
static void gdm_daemonify (void);
static gboolean gdm_slave_socket_handler (GIOChannel *source,
GIOCondition cond,
gpointer data);
static void gdm_safe_restart (void);
static void gdm_restart_now (void);
/* Global vars */
GSList *displays; /* List of displays managed */
......@@ -52,6 +62,8 @@ sigset_t sysmask; /* Inherited system signal mask */
gchar *argdelim = " "; /* argv argument delimiter */
uid_t GdmUserId; /* Userid under which gdm should run */
gid_t GdmGroupId; /* Groupid under which gdm should run */
int fifofd = -1; /* fifo fd for the slave communication fifo */
guint fifo_source = 0; /* source for the above */
/* True if the server that was run was in actuallity not specified in the
* config file. That is if xdmcp was disabled and no local servers were
......@@ -128,6 +140,8 @@ char **stored_argv = NULL;
int stored_argc = 0;
char *stored_path = NULL;
static gboolean gdm_restart_mode = FALSE;
/**
* gdm_config_parse:
......@@ -485,6 +499,7 @@ final_cleanup (void)
g_slist_free (list);
gdm_xdmcp_close ();
gdm_fifo_close ();
closelog();
unlink (GdmPidFile);
......@@ -677,14 +692,18 @@ gdm_cleanup_children (void)
gint exitstatus = 0, status;
GdmDisplay *d = NULL;
gchar **argv;
gboolean crashed;
/* Pid and exit status of slave that died */
pid = waitpid (-1, &exitstatus, WNOHANG);
if (WIFEXITED (exitstatus))
if (WIFEXITED (exitstatus)) {
status = WEXITSTATUS (exitstatus);
else
crashed = FALSE;
} else {
status = DISPLAY_SUCCESS;
crashed = TRUE;
}
gdm_debug ("gdm_cleanup_children: child %d returned %d", pid, status);
......@@ -697,6 +716,23 @@ gdm_cleanup_children (void)
if (!d)
return;
if (crashed) {
if (d->servpid > 0) {
if (kill (d->servpid, SIGTERM) == 0)
waitpid (d->servpid, NULL, 0);
/* just for paranoia's sake, yes sleeping in the
* daemon is bad but this is an exceptional
* situation and not normal occurance. */
sleep (3);
d->servpid = 0;
}
}
/* definately not logged in now */
d->logged_in = FALSE;
/* Declare the display dead */
d->slavepid = 0;
d->dispstat = DISPLAY_DEAD;
......@@ -748,6 +784,9 @@ gdm_cleanup_children (void)
case DISPLAY_ABORT: /* Bury this display for good */
gdm_info (_("gdm_child_action: Aborting display %s"), d->name);
if (gdm_restart_mode)
gdm_safe_restart ();
gdm_display_unmanage (d);
break;
......@@ -783,18 +822,15 @@ gdm_cleanup_children (void)
gdm_error (_("gdm_child_action: Suspend failed: %s"), strerror (errno));
break;
case DISPLAY_RESTARTGDM:
final_cleanup ();
if (stored_path != NULL)
putenv (stored_path);
execvp (stored_argv[0], stored_argv);
gdm_error (_("Failed to restart self"));
_exit (1);
gdm_restart_now ();
break;
case DISPLAY_XFAILED: /* X sucks */
if (gdm_restart_mode)
gdm_safe_restart ();
/* in remote case just drop to _REMANAGE */
if (d->type == TYPE_LOCAL) {
time_t now = time (NULL);
......@@ -828,6 +864,9 @@ gdm_cleanup_children (void)
case DISPLAY_REMANAGE: /* Remanage display */
default:
gdm_debug ("gdm_child_action: Slave process returned %d", status);
if (gdm_restart_mode)
gdm_safe_restart ();
/* This is a local server so we start a new slave */
if (d->type == TYPE_LOCAL) {
......@@ -841,18 +880,38 @@ gdm_cleanup_children (void)
break;
}
if (gdm_restart_mode)
gdm_safe_restart ();
gdm_quit ();
}
static void
term_cleanup (void)
gdm_restart_now (void)
{
gdm_debug ("term_cleanup: Got TERM/INT. Going down!");
gdm_info (_("Gdm restarting ..."));
final_cleanup ();
if (stored_path != NULL)
putenv (stored_path);
execvp (stored_argv[0], stored_argv);
gdm_error (_("Failed to restart self"));
_exit (1);
}
static void
gdm_safe_restart (void)
{
GSList *li;
for (li = displays; li != NULL; li = li->next) {
GdmDisplay *d = li->data;
if (d->logged_in)
return;
}
gdm_restart_now ();
}
static gboolean
mainloop_sig_callback (gint8 sig, gpointer data)
......@@ -866,17 +925,18 @@ mainloop_sig_callback (gint8 sig, gpointer data)
case SIGINT:
case SIGTERM:
term_cleanup ();
gdm_debug ("mainloop_sig_callback: Got TERM/INT. Going down!");
final_cleanup ();
exit (EXIT_SUCCESS);
break;
case SIGHUP:
term_cleanup ();
if (stored_path != NULL)
putenv (stored_path);
execvp (stored_argv[0], stored_argv);
gdm_error (_("Failed to restart self"));
_exit (1);
gdm_restart_now ();
break;
case SIGUSR1:
gdm_restart_mode = TRUE;
gdm_safe_restart ();
break;
default:
......@@ -912,6 +972,42 @@ store_argv (int argc, char *argv[])
stored_argc = argc;
}
static void
create_fifo (void)
{
gchar *fifopath;
GIOChannel *fifochan;
fifopath = g_strconcat (GdmServAuthDir, "/.gdmfifo", NULL);
unlink (fifopath);
if (mkfifo (fifopath, 0660) < 0) {
gdm_fail (_("%s: Could not make FIFO"),
"create_fifo");
return;
}
fifofd = open (fifopath, O_RDWR); /* Open with write to avoid EOF */
if (fifofd < 0) {
gdm_fail (_("%s: Could not open FIFO"),
"create_fifo");
return;
}
chmod (fifopath, 0660);
g_free (fifopath);
fifochan = g_io_channel_unix_new (fifofd);
fifo_source = g_io_add_watch_full
(fifochan, G_PRIORITY_DEFAULT,
G_IO_IN|G_IO_PRI|G_IO_ERR|G_IO_HUP|G_IO_NVAL,
gdm_slave_socket_handler, NULL, NULL);
g_io_channel_unref (fifochan);
}
int
main (int argc, char *argv[])
{
......@@ -989,19 +1085,27 @@ main (int argc, char *argv[])
g_signal_add (SIGTERM, mainloop_sig_callback, NULL);
g_signal_add (SIGINT, mainloop_sig_callback, NULL);
g_signal_add (SIGHUP, mainloop_sig_callback, NULL);
g_signal_add (SIGUSR1, mainloop_sig_callback, NULL);
term.sa_handler = signal_notify;
term.sa_flags = SA_RESTART;
sigemptyset (&term.sa_mask);
if (sigaction (SIGTERM, &term, NULL) < 0)
gdm_fail (_("gdm_main: Error setting up TERM signal handler"));
gdm_fail (_("%s: Error setting up TERM signal handler"),
"gdm_main");
if (sigaction (SIGINT, &term, NULL) < 0)
gdm_fail (_("gdm_main: Error setting up INT signal handler"));
gdm_fail (_("%s: Error setting up INT signal handler"),
"gdm_main");
if (sigaction (SIGHUP, &term, NULL) < 0)
gdm_fail (_("gdm_main: Error setting up HUP signal handler"));
gdm_fail (_("%s: Error setting up HUP signal handler"),
"gdm_main");
if (sigaction (SIGUSR1, &term, NULL) < 0)
gdm_fail (_("%s: Error setting up USR1 signal handler"),
"gdm_main");
child.sa_handler = signal_notify;
child.sa_flags = SA_RESTART|SA_NOCLDSTOP;
......@@ -1009,13 +1113,14 @@ main (int argc, char *argv[])
sigaddset (&child.sa_mask, SIGCHLD);
if (sigaction (SIGCHLD, &child, NULL) < 0)
gdm_fail (_("gdm_main: Error setting up CHLD signal handler"));
gdm_fail (_("%s: Error setting up CHLD signal handler"), "gdm_main");
sigemptyset (&mask);
sigaddset (&mask, SIGINT);
sigaddset (&mask, SIGTERM);
sigaddset (&mask, SIGCHLD);
sigaddset (&mask, SIGHUP);
sigaddset (&mask, SIGUSR1);
sigprocmask (SIG_UNBLOCK, &mask, &sysmask); /* Save system sigmask */
gdm_debug ("gdm_main: Here we go...");
......@@ -1024,6 +1129,8 @@ main (int argc, char *argv[])
if (GdmXdmcp)
gdm_xdmcp_init();
create_fifo ();
/* Start local X servers */
g_slist_foreach (displays, (GFunc) gdm_local_servers_start, NULL);
......@@ -1161,4 +1268,104 @@ gdm_quit (void)
g_main_quit (main_loop);
}
static gboolean
gdm_slave_socket_handler (GIOChannel *source,
GIOCondition cond,
gpointer data)
{
GdmSlaveHeader header;
gchar buf[PIPE_SIZE];
gint len;
if (cond != G_IO_IN)
return TRUE;
if (g_io_channel_read (source, (char *)&header, sizeof (header), &len)
!= G_IO_ERROR_NONE)
return TRUE;
if (len < sizeof (header))
return TRUE;
gdm_debug ("gdm_slave_socket_handler: opcode %d len %d",
(int) header.opcode,
(int) header.len);
if (header.len > 0) {
if (g_io_channel_read (source, buf, header.len, &len)
!= G_IO_ERROR_NONE)
return TRUE;
if (len != header.len)
return TRUE;
} else {
len = 0;
}
gdm_debug ("gdm_slave_socket_handler: Read %d bytes", len);
if (header.opcode <= GDM_SOP_INVALID ||
header.opcode >= GDM_SOP_LAST) {
gdm_error (_("%s: Invalid opcode"),
"gdm_slave_socket_handler");
return TRUE;
}
if (header.opcode == GDM_SOP_CHOOSER) {
gdm_choose_data (buf, header.len);
} else if (header.opcode == GDM_SOP_XPID) {
GdmXPidData data;
if (header.len == sizeof (data)) {
GdmDisplay *d;
memcpy (&data, buf, sizeof (data));
/* Find out who this slave belongs to */
d = gdm_display_lookup (data.slave_pid);
if (d != NULL) {
d->servpid = data.xpid;
}
}
} else if (header.opcode == GDM_SOP_LOGGED_IN) {
GdmLoggedInData data;
if (header.len == sizeof (data)) {
GdmDisplay *d;
memcpy (&data, buf, sizeof (data));
/* Find out who this slave belongs to */
d = gdm_display_lookup (data.slave_pid);
if (d != NULL) {
d->logged_in = data.logged_in;
/* if the user just logged out,
* let's see if it's safe to restart */
if (gdm_restart_mode &&
! data.logged_in)
gdm_safe_restart ();
}
}
}
return TRUE;
}
void
gdm_fifo_close (void)
{
gchar *fifopath;
if (fifo_source > 0) {
g_source_remove (fifo_source);
fifo_source = 0;
}
if (fifofd > 0) {
close (fifofd);
fifofd = -1;
}
fifopath = g_strconcat (GdmServAuthDir, "/.gdmfifo", NULL);
unlink (fifopath);
g_free (fifopath);
}
/* EOF */
......@@ -248,6 +248,8 @@ struct _GdmDisplay {
gboolean disabled;
gboolean logged_in; /* TRUE if someone is logged in */
gboolean timed_login_ok;
int screenx;
......@@ -282,6 +284,44 @@ void g_signal_notify (gint8 signal);
void gdm_run (void);
void gdm_quit (void);
/* primitive protocol for controlling the daemon from the chooser
* or gdmconfig or whatnot */
enum {
GDM_SOP_INVALID = 0,
GDM_SOP_CHOOSER, /* Chosen host information */
GDM_SOP_XPID, /* Pid of X server so that in case of
slave crash, daemon can kill it */
GDM_SOP_LOGGED_IN, /* status of user, if logged in or not,
useful for safe restarts */
GDM_SOP_LAST
};
typedef struct _GdmXPidData GdmXPidData;
struct _GdmXPidData {
pid_t slave_pid;
pid_t xpid;
};
typedef struct _GdmLoggedInData GdmLoggedInData;
struct _GdmLoggedInData {
pid_t slave_pid;
gboolean logged_in;
};
/* Protocol is not for use outside of gdm so endianess be damned,
* it's not really a "wire" protocol, just for the programs of the
* gdm suite. Which are all compiled on the same machine etc... etc...
* blah blah blah*/
typedef struct _GdmSlaveHeader GdmSlaveHeader;
struct _GdmSlaveHeader {
int opcode;
size_t len; /* length of data (not including header) */
};
void gdm_fifo_close (void);
#define gdm_string_empty(x) ((x)==NULL||(x)[0]=='\0')
#define gdm_sure_string(x) ((x)!=NULL?(x):"")
......
......@@ -53,7 +53,7 @@ gdm_fail (const gchar *format, ...)
va_end (args);
/* Log to both syslog and stderr */
syslog (LOG_ERR, s);
syslog (LOG_CRIT, s);
fprintf (stderr, "%s\n", s);
fflush (stderr);
......
......@@ -51,6 +51,7 @@
#include "filecheck.h"
#include "auth.h"
#include "server.h"
#include "choose.h"
#include "errorgui.h"
......@@ -131,6 +132,8 @@ static void gdm_child_exit (gint status, const gchar *format, ...);
static gint gdm_slave_exec_script (GdmDisplay *d, const gchar *dir,
const char *login, struct passwd *pwent);
static gchar * gdm_parse_enriched_login (const gchar *s, GdmDisplay *display);
static void gdm_send_logged_in (gboolean logged_in);
static void gdm_send_xpid (pid_t xpid);
/* Yay thread unsafety */
......@@ -238,6 +241,7 @@ gdm_slave_start (GdmDisplay *display)
/* Whack the server if we want to restart it next time
* we run gdm_slave_run */
gdm_server_stop (display);
gdm_send_xpid (0);
} else {
/* OK about to start again so redo our cookies and reinit
* the server */
......@@ -347,8 +351,10 @@ gdm_slave_run (GdmDisplay *display)
/* if this is local display start a server if one doesn't
* exist */
if (d->type == TYPE_LOCAL &&
d->servpid <= 0)
d->servpid <= 0) {
gdm_server_start (d);
gdm_send_xpid (d->servpid);
}
gdm_setenv ("XAUTHORITY", d->authfile);
gdm_setenv ("DISPLAY", d->name);
......@@ -417,22 +423,31 @@ gdm_slave_run (GdmDisplay *display)
strcmp (ParsedAutomaticLogin, "root") != 0) {
gdm_first_login = FALSE;
gdm_send_logged_in (TRUE);
setup_automatic_session (d, ParsedAutomaticLogin);
gdm_slave_session_start();
gdm_send_logged_in (FALSE);
gdm_debug ("gdm_slave_run: Automatic login done");
} else {
if (gdm_first_login)
gdm_first_login = FALSE;
gdm_slave_greeter (); /* Start the greeter */
gdm_slave_wait_for_login (); /* wait for a password */
gdm_send_logged_in (TRUE);
if (do_timed_login) {
/* timed out into a timed login */
do_timed_login = FALSE;
setup_automatic_session (d, ParsedTimedLogin);
}
gdm_slave_session_start ();
gdm_send_logged_in (FALSE);
}
}
......@@ -697,6 +712,8 @@ gdm_slave_wait_for_login (void)
continue;
}
gdm_send_logged_in (TRUE);
/* disable the login screen, we don't want people to
* log in in the meantime */
gdm_slave_greeter_ctl_no_ret (GDM_DISABLE, "");
......@@ -708,6 +725,8 @@ gdm_slave_wait_for_login (void)
gdm_verify_cleanup ();
gdm_send_logged_in (FALSE);
gdm_slave_greeter_ctl_no_ret (GDM_ENABLE, "");
gdm_slave_greeter_ctl_no_ret (GDM_RESETOK, "");
continue;
......@@ -1113,13 +1132,81 @@ gdm_slave_greeter (void)
}
}
static void
gdm_send_xpid (pid_t xpid)
{
GdmSlaveHeader header;
GdmXPidData data;
int fd;
char *fifopath;
gdm_debug ("Sending X pid == %ld for slave %ld",
(long)xpid,
(long)getpid ());
fifopath = g_strconcat (GdmServAuthDir, "/.gdmfifo", NULL);
fd = open (fifopath, O_WRONLY);
/* eek */
if (fd < 0) {
gdm_error (_("%s: Can't open fifo!"), "gdm_send_logged_in");
return;
}
header.opcode = GDM_SOP_XPID;
header.len = sizeof (data);
data.slave_pid = getpid ();
data.xpid = xpid;
write (fd, &header, sizeof (header));
write (fd, &data, sizeof (data));
close (fd);
}
static void
gdm_send_logged_in (gboolean logged_in)
{
GdmSlaveHeader header;
GdmLoggedInData data;
int fd;
char *fifopath;
gdm_debug ("Sending logged_in == %s for slave %ld",
logged_in ? "TRUE" : "FALSE",
(long)getpid ());
fifopath = g_strconcat (GdmServAuthDir, "/.gdmfifo", NULL);
fd = open (fifopath, O_WRONLY);
/* eek */
if (fd < 0) {