summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Zeuthen <davidz@redhat.com>2007-10-04 20:39:09 -0400
committerWilliam Jon McCann <mccann@jhu.edu>2007-10-04 20:39:09 -0400
commita90ecd3544f03c16f6c5a97aafb1c0b33b58101c (patch)
tree3c52a6ac890ce96ab9af100655577614002b8e28
parentad9dbd345dbec43ffc24481a0d21c261e49347fb (diff)
add a way to synchronously run programs on session add/remove/activity_change
This patch adds support for running programs when 1. A session is added 2. A session is removed 3. The activity of a session changes Executables with the suffix .ck in the directories $sysconfdir/ConsoleKit/run-session.d (typically /etc/ConsoleKit/run-session.d) and $libdir/ConsoleKit/run-session.d (typically /usr/lib/ConsoleKit/run-session.d) will be run on each event. The former directory is meant to be used for the system administrator and the latter is meant to be used by programs. Only when all programs in these directories have run, ConsoleKit will resume event processing and e.g. broadcast the event on the system message bus. Hence, this new mechanism can be used to safely (e.g. without race conditions) perform operations on certain resources before programs in the desktop session are told they may use them. The obvious example here is managing ACL's on /dev such that certain device nodes are only available to users in local and active sessions. The environment of the program launched is the environment that the ConsoleKit daemon was launched with and also the following variables (variables tagged with [*] may not be set). CK_SESSION_ID CK_SESSION_TYPE CK_SESSION_SEAT_ID CK_SESSION_USER_UID CK_SESSION_DISPLAY_DEVICE [*] CK_SESSION_X11_DISPLAY_DEVICE [*] CK_SESSION_X11_DISPLAY [*] CK_SESSION_REMOTE_HOST_NAME [*] CK_SESSION_IS_ACTIVE CK_SESSION_IS_LOCAL corresponding to the properties of a Session object in question. Each program is passed exactly one parameter that can assume one of the following values: - session_active_changed: is_active changed - session_added: the session was added - session_removed: the session was removed As a safety hatch, there is a timeout of 15 seconds for each program; if it hasn't exited within 15 seconds, the daemon will send it a SIGTERM signal, and move on to the next program. The daemon is still responsive when the program is running - this is to ensure that the program itself can call into the org.freedesktop.ConsoleKit service.
-rw-r--r--src/Makefile.am6
-rw-r--r--src/ck-run-programs.c230
-rw-r--r--src/ck-run-programs.h38
-rw-r--r--src/ck-seat.c5
-rw-r--r--src/ck-session.c39
5 files changed, 318 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 3f142a3..ec4e04f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -98,6 +98,8 @@ console_kit_daemon_SOURCES = \
ck-session.c \
ck-log.h \
ck-log.c \
+ ck-run-programs.c \
+ ck-run-programs.h \
$(BUILT_SOURCES) \
$(NULL)
@@ -163,3 +165,7 @@ CLEANFILES = $(BUILT_SOURCES)
MAINTAINERCLEANFILES = \
*~ \
Makefile.in
+
+install-data-local:
+ -mkdir -p $(DESTDIR)$(sysconfdir)/ConsoleKit/run-session.d
+ -mkdir -p $(DESTDIR)$(libdir)/ConsoleKit/run-session.d
diff --git a/src/ck-run-programs.c b/src/ck-run-programs.c
new file mode 100644
index 0000000..f9d62c7
--- /dev/null
+++ b/src/ck-run-programs.c
@@ -0,0 +1,230 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 David Zeuthen <david@fubar.dk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <signal.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+
+#include "ck-run-programs.h"
+
+/* The number of wall-clock seconds a program is allowed to run before we kill it */
+#define TIMEOUT_SECONDS 15
+
+/* Guaranteed by POSIX; see 'man environ' for details */
+extern char **environ;
+
+typedef struct {
+ int refcount;
+
+ char *path;
+
+ gboolean child_is_running;
+ guint watch_id;
+ guint timeout_id;
+ GPid pid;
+} ChildData;
+
+static ChildData *
+_child_data_new (void)
+{
+ ChildData *cd;
+
+ cd = g_new0 (ChildData, 1);
+ cd->refcount = 1;
+ g_debug ("Allocated ChildData %p", cd);
+
+ return cd;
+}
+
+static ChildData *
+_child_data_ref (ChildData *cd)
+{
+ cd->refcount++;
+ return cd;
+}
+
+
+static void
+_child_data_unref (ChildData *cd)
+{
+ cd->refcount--;
+ if (cd->refcount == 0) {
+ g_free (cd->path);
+ g_free (cd);
+ g_debug ("Freeing ChildData %p", cd);
+ }
+}
+
+
+static void
+_child_watch (GPid pid,
+ int status,
+ ChildData *cd)
+{
+ g_debug ("In _child_watch for pid %d", pid);
+
+ g_spawn_close_pid (pid);
+ g_source_remove (cd->timeout_id);
+
+ cd->child_is_running = FALSE;
+
+ _child_data_unref (cd);
+}
+
+static gboolean
+_child_timeout (ChildData *cd)
+{
+ /* The program we ran timed out; this is a bug in the program */
+ g_warning ("The program %s didn't exit within %d seconds; killing it", cd->path, TIMEOUT_SECONDS);
+
+ kill (cd->pid, SIGTERM);
+
+ cd->child_is_running = FALSE;
+ return FALSE;
+}
+
+/**
+ * ck_run_programs:
+ * @dirpath: Path to a directory containing programs to run
+ * @action: Argument to pass to each program
+ * @extra_env: Extra environment to pass
+ *
+ * Synchronously run all scripts with suffix .ck in the given
+ * directory.
+ */
+void
+ck_run_programs (const char *dirpath,
+ const char *action,
+ char **extra_env)
+{
+ GDir *dir;
+ GError *error;
+ const char *name;
+ char **env_for_child;
+ int environ_len;
+ int extra_env_len;
+ int n;
+ int m;
+
+ g_return_if_fail (dirpath != NULL);
+ g_return_if_fail (action != NULL);
+
+ g_debug ("Running programs in %s for action %s", dirpath, action);
+
+ /* Construct an environment consisting of the existing and the given environment */
+ environ_len = environ != NULL ? g_strv_length (environ) : 0;
+ extra_env_len = extra_env != NULL ? g_strv_length (extra_env) : 0;
+ env_for_child = g_new0 (char *, environ_len + extra_env_len + 2);
+ m = 0;
+ for (n = 0; n < environ_len; n++) {
+ env_for_child [m++] = g_strdup (environ[n]);
+ }
+ for (n = 0; n < extra_env_len; n++) {
+ env_for_child [m++] = g_strdup (extra_env[n]);
+ }
+ env_for_child[m] = NULL;
+
+ error = NULL;
+
+ dir = g_dir_open (dirpath, 0, &error);
+ if (dir == NULL) {
+ /* This is unexpected; it means ConsoleKit isn't properly installed */
+ g_warning ("Unable to open directory %s: %s", dirpath, error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ while ((name = g_dir_read_name (dir)) != NULL) {
+ char *child_argv[3];
+ ChildData *cd;
+ gboolean res;
+
+ if (!g_str_has_suffix (name, ".ck"))
+ continue;
+
+ child_argv[0] = g_strdup_printf ("%s/%s", dirpath, name);
+ child_argv[1] = (char *) action;
+ child_argv[2] = NULL;
+
+ error = NULL;
+ cd = _child_data_new ();
+ cd->path = g_strdup (child_argv[0]);
+
+ /* The ChildData instance is also unreffed in _child_watch; we only ref
+ * it here to prevent cd from being destroyed while checking it in
+ * the mainloop
+ */
+ _child_data_ref (cd);
+
+ res = g_spawn_async (NULL,
+ child_argv,
+ env_for_child,
+ G_SPAWN_DO_NOT_REAP_CHILD,
+ NULL,
+ NULL,
+ &cd->pid,
+ &error);
+ if (! res) {
+ /* This is unexpected; it means the program to run isn't installed correctly */
+ g_warning ("Unable to spawn %s: %s", child_argv[0], error->message);
+ g_error_free (error);
+ _child_data_unref (cd);
+ _child_data_unref (cd);
+ goto out_loop;
+ }
+ cd->child_is_running = TRUE;
+
+ g_debug ("Waiting for child with pid %d", cd->pid);
+
+ cd->watch_id = g_child_watch_add (cd->pid,
+ (GChildWatchFunc)_child_watch,
+ cd);
+ cd->timeout_id = g_timeout_add (TIMEOUT_SECONDS * 1000,
+ (GSourceFunc)_child_timeout,
+ cd);
+
+ /* run the mainloop; this allows the main daemon to
+ * continue serving clients (including the program we
+ * just launched) */
+ while (cd->child_is_running) {
+ g_main_context_iteration (NULL, TRUE);
+ }
+
+ g_debug ("Done waiting for child with pid %d", cd->pid);
+ _child_data_unref (cd);
+
+ out_loop:
+ g_free (child_argv[0]);
+ }
+out:
+ g_strfreev (env_for_child);
+}
diff --git a/src/ck-run-programs.h b/src/ck-run-programs.h
new file mode 100644
index 0000000..77cdf48
--- /dev/null
+++ b/src/ck-run-programs.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 David Zeuthen <david@fubar.dk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifndef __CK_RUN_PROGRAMS_H
+#define __CK_RUN_PROGRAMS_H
+
+#include <glib-object.h>
+#include <dbus/dbus-glib.h>
+#include "ck-session.h"
+
+G_BEGIN_DECLS
+
+void ck_run_programs (const char *dirpath, const char *action, char **extra_env);
+
+void ck_session_run_programs (CkSession *session, const char *action);
+
+
+G_END_DECLS
+
+#endif /* __CK_RUN_PROGRAMS_H */
diff --git a/src/ck-seat.c b/src/ck-seat.c
index e04b569..63d7b6c 100644
--- a/src/ck-seat.c
+++ b/src/ck-seat.c
@@ -42,6 +42,7 @@
#include "ck-session.h"
#include "ck-vt-monitor.h"
+#include "ck-run-programs.h"
#define CK_SEAT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CK_TYPE_SEAT, CkSeatPrivate))
@@ -547,6 +548,8 @@ ck_seat_remove_session (CkSeat *seat,
g_signal_handlers_disconnect_by_func (session, session_activate, seat);
+ ck_session_run_programs (session, "session_removed");
+
g_debug ("Emitting removed signal: %s", ssid);
g_signal_emit (seat, signals [SESSION_REMOVED], 0, ssid);
@@ -581,6 +584,8 @@ ck_seat_add_session (CkSeat *seat,
g_signal_connect_object (session, "activate", G_CALLBACK (session_activate), seat, 0);
/* FIXME: attach to property notify signals? */
+ ck_session_run_programs (session, "session_added");
+
g_debug ("Emitting added signal: %s", ssid);
g_signal_emit (seat, signals [SESSION_ADDED], 0, ssid);
diff --git a/src/ck-session.c b/src/ck-session.c
index f136333..6f16720 100644
--- a/src/ck-session.c
+++ b/src/ck-session.c
@@ -40,6 +40,7 @@
#include "ck-session.h"
#include "ck-session-glue.h"
#include "ck-marshal.h"
+#include "ck-run-programs.h"
#define CK_SESSION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CK_TYPE_SESSION, CkSessionPrivate))
@@ -394,6 +395,7 @@ ck_session_set_active (CkSession *session,
if (session->priv->active != active) {
session->priv->active = active;
+ ck_session_run_programs (session, "session_active_changed");
g_signal_emit (session, signals [ACTIVE_CHANGED], 0, active);
}
@@ -1206,3 +1208,40 @@ ck_session_new_with_parameters (const char *ssid,
return CK_SESSION (object);
}
+
+void
+ck_session_run_programs (CkSession *session,
+ const char *action)
+{
+ int n;
+ char *extra_env[11]; /* be sure to adjust this as needed */
+
+ n = 0;
+
+ extra_env[n++] = g_strdup_printf ("CK_SESSION_ID=%s", session->priv->id);
+ if (session->priv->session_type != NULL) {
+ extra_env[n++] = g_strdup_printf ("CK_SESSION_TYPE=%s", session->priv->session_type);
+ }
+ extra_env[n++] = g_strdup_printf ("CK_SESSION_SEAT_ID=%s", session->priv->seat_id);
+ extra_env[n++] = g_strdup_printf ("CK_SESSION_USER_UID=%d", session->priv->uid);
+ if (session->priv->display_device != NULL && strlen (session->priv->display_device) > 0) {
+ extra_env[n++] = g_strdup_printf ("CK_SESSION_DISPLAY_DEVICE=%s", session->priv->display_device);
+ }
+ if (session->priv->x11_display_device != NULL && strlen (session->priv->x11_display_device) > 0) {
+ extra_env[n++] = g_strdup_printf ("CK_SESSION_X11_DISPLAY_DEVICE=%s", session->priv->x11_display_device);
+ }
+ extra_env[n++] = g_strdup_printf ("CK_SESSION_X11_DISPLAY=%s", session->priv->x11_display);
+ if (session->priv->remote_host_name != NULL && strlen (session->priv->remote_host_name) > 0) {
+ extra_env[n++] = g_strdup_printf ("CK_SESSION_REMOTE_HOST_NAME=%s", session->priv->remote_host_name);
+ }
+ extra_env[n++] = g_strdup_printf ("CK_SESSION_IS_ACTIVE=%s", session->priv->active ? "true" : "false");
+ extra_env[n++] = g_strdup_printf ("CK_SESSION_IS_LOCAL=%s", session->priv->is_local ? "true" : "false");
+ extra_env[n++] = NULL;
+
+ ck_run_programs (SYSCONFDIR "/ConsoleKit/run-session.d", action, extra_env);
+ ck_run_programs (LIBDIR "/ConsoleKit/run-session.d", action, extra_env);
+
+ for (n = 0; extra_env[n] != NULL; n++) {
+ g_free (extra_env[n]);
+ }
+}