[pkg-fso-commits] [SCM] Automatic Display Manager branch, pamhelper, updated. debian/0.1-38-g8ab57e9

Enrico Zini enrico at enricozini.org
Fri Feb 20 17:48:48 UTC 2009


The following commit has been merged in the pamhelper branch:
commit 8ab57e98e3b2763708af1ac3a96f1cb5ee5d57d8
Author: Enrico Zini <enrico at enricozini.org>
Date:   Fri Feb 20 17:48:11 2009 +0000

    Complete redesign
    
     - Back to NODM_XINIT, NODM_XSESSION, NODM_X_OPTIONS.
     - Virtually split nodm in two: one that starts, monitors and restarts
       xinit, one that runs the X session and does the pam / login work
    
    This allows to start X as root (otherwise it will refuse to start) and
    then run the proper session inside X.

diff --git a/Makefile b/Makefile
index e25eb4c..f85232b 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,10 @@
+all: nodm nodm-session
+
 nodm: nodm.c
 	gcc -o $@ $< -lpam -lpam_misc -Wall
 
+nodm-session: nodm
+	ln -fs nodm nodm-session
+
 clean:
 	rm -f nodm
diff --git a/debian/nodm.config b/debian/nodm.config
index 999560a..cb746a8 100644
--- a/debian/nodm.config
+++ b/debian/nodm.config
@@ -13,12 +13,18 @@ if [ -s /etc/default/nodm ] ; then
 	if [ -n "$NODM_USER" ] ; then
 		db_set nodm/user "$NODM_USER"
 	fi
-	if [ -n "$NODM_COMMAND" ] ; then
-		db_set nodm/command "$NODM_COMMAND"
+	if [ -n "$NODM_X_OPTIONS" ] ; then
+		db_set nodm/x_options "$NODM_X_OPTIONS"
 	fi
 	if [ -n "$NODM_MIN_SESSION_TIME" ]; then
 		db_set nodm/min_session_time "$NODM_MIN_SESSION_TIME"
 	fi
+	if [ -n "$NODM_XINIT" ]; then
+		db_set nodm/xinit "$NODM_XINIT"
+	fi
+	if [ -n "$NODM_XSESSION" ]; then
+		db_set nodm/xsession "$NODM_XSESSION"
+	fi
 fi
 
 db_input medium nodm/enabled || true
@@ -31,10 +37,16 @@ if [ "x$START" = "xtrue" ] ; then
 	db_input medium nodm/user || true
 	db_go
 
-        db_input low nodm/command || true
+        db_input low nodm/x_options || true
         db_go
 
 	db_input low nodm/min_session_time || true
 	db_go
+
+	db_input low nodm/xinit || true
+	db_go
+
+	db_input low nodm/xsession || true
+	db_go
 fi
 
diff --git a/debian/nodm.init b/debian/nodm.init
index 2d53d21..d1f928c 100644
--- a/debian/nodm.init
+++ b/debian/nodm.init
@@ -19,8 +19,10 @@ PIDDIR=/var/run/
 PIDFILE=${PIDDIR}/${NAME}.pid
 
 NODM_ENABLED=no
+NODM_XINIT=/usr/bin/xinit
+NODM_XSESSION=/etc/X11/Xsession
+NODM_X_OPTIONS="vt7 -nolisten tcp"
 NODM_USER=root
-NODM_COMMAND='/usr/bin/xinit /etc/X11/Xsession -- vt7 -nolisten tcp'
 NODM_MIN_SESSION_TIME=60
 
 if [ -f /etc/default/$NAME ]
@@ -28,13 +30,14 @@ then
 	. /etc/default/$NAME
 fi
 
-export NODM_USER NODM_COMMAND NODM_MIN_SESSION_TIME
+export NODM_XINIT NODM_XSESSION NODM_X_OPTIONS NODM_USER NODM_MIN_SESSION_TIME
 
 # If you change the user to a non-root user, make sure you
 # set allowed_users=anybody in /etc/X11/Xwrapper.config
 
 # Gracefully exit if the package or its dependencies have been removed (but not purged).
-[ -x /usr/sbin/nodm ] || exit 0
+[ -x /usr/sbin/nodm -a -x "$NODM_XSESSION" -a -x "$NODM_XINIT" ] || exit 0
+
 
 # Load the VERBOSE setting and other rcS variables
 . /lib/init/vars.sh
diff --git a/debian/nodm.install b/debian/nodm.install
index 2cdcc73..cd8d39b 100644
--- a/debian/nodm.install
+++ b/debian/nodm.install
@@ -1 +1,2 @@
 nodm		/usr/sbin/
+nodm-session	/usr/sbin/
diff --git a/debian/nodm.postinst b/debian/nodm.postinst
index cee468b..88e4c30 100644
--- a/debian/nodm.postinst
+++ b/debian/nodm.postinst
@@ -11,16 +11,22 @@ if [ "$1" = "configure" ] ; then
     NODM_ENABLED="$RET"
     db_get nodm/user
     NODM_USER="$RET"
-    db_get nodm/command
-    NODM_COMMAND="$RET"
+    db_get nodm/x_options
+    NODM_X_OPTIONS="$RET"
     db_get nodm/min_session_time
     NODM_MIN_SESSION_TIME="$RET"
+    db_get nodm/xinit
+    NODM_XINIT="$RET"
+    db_get nodm/xsession
+    NODM_XSESSION="$RET"
 
     if [ -s /etc/default/nodm ] ; then
 	    sed -i -r -e "s,^NODM_ENABLED=.*,NODM_ENABLED=$NODM_ENABLED," \
 		      -e "s,^NODM_USER=.*,NODM_USER=$NODM_USER," \
-		      -e "s,^NODM_COMMAND=.*,NODM_COMMAND='$NODM_COMMAND'," \
+		      -e "s,^NODM_X_OPTIONS=.*,NODM_X_OPTIONS='$NODM_X_OPTIONS'," \
 		      -e "s,^NODM_MIN_SESSION_TIME=.*,NODM_MIN_SESSION_TIME=$NODM_MIN_SESSION_TIME," \
+		      -e "s,^NODM_XINIT=.*,NODM_XINIT=$NODM_XINIT," \
+		      -e "s,^NODM_XSESSION=.*,NODM_XSESSION=$NODM_XSESSION," \
 		      /etc/default/nodm
     else
 	    cat <<EOF > /etc/default/nodm
@@ -33,7 +39,13 @@ NODM_ENABLED=$NODM_ENABLED
 NODM_USER=$NODM_USER
 
 # xinit program
-NODM_COMMAND='$NODM_COMMAND'
+NODM_XINIT=$NODM_XINIT
+
+# X session
+NODM_XSESSION=$NODM_XSESSION
+
+# Options for the X server
+NODM_X_OPTIONS='$NODM_X_OPTIONS'
 
 # If an X session will run for less than this time in seconds, nodm will wait an
 # increasing bit of time before restarting the session.
diff --git a/debian/nodm.templates b/debian/nodm.templates
index cb94d58..68a0f10 100644
--- a/debian/nodm.templates
+++ b/debian/nodm.templates
@@ -15,14 +15,11 @@ _Description: User to start a session for:
  nodm starts an X session for a user without asking for authentication.  What
  user should be used for the session?
 
-Template: nodm/command
+Template: nodm/x_options
 Type: string
-Default: /usr/bin/xinit /etc/X11/Xsession -- vt7 -nolisten tcp
-_Description: Command to use to run the X session
- This command is passed to the shell to start the X session.
- .
- Please do not use single quotes, as they are used as delimiters in
- /etc/default/nodm
+Default: vt7 -nolisten tcp
+_Description: Options for the X server:
+ Options to pass to the X server when starting the session.
 
 Template: nodm/min_session_time
 Type: string
@@ -31,3 +28,22 @@ _Description: Minimum time (in seconds) for a session to be considered ok
  If an X session will run for less than this time in seconds, nodm will wait an
  amount of time before restarting the session.  The waiting time will grow
  until a session lasts longer than this amount.
+
+Template: nodm/xinit
+Type: string
+Default: /usr/bin/xinit
+_Description: xinit program to use
+ You can change this to use a different xinit program.
+ .
+ nodm will start the X session with the command:
+ $NODM_XINIT $NODM_XSESSION -- $NODM_X_OPTIONS
+
+Template: nodm/xsession
+Type: string
+Default: /etc/X11/Xsession
+_Description: X session to use
+ You can change this to use a different X session script.
+ .
+ nodm will start the X session with the command:
+ $NODM_XINIT $NODM_XSESSION -- $NODM_X_OPTIONS
+
diff --git a/nodm.c b/nodm.c
index 81c85f0..acc388a 100644
--- a/nodm.c
+++ b/nodm.c
@@ -47,6 +47,9 @@
 
 
 #define NAME "nodm"
+#define SESSION_CMD "/usr/sbin/nodm-session"
+/* #define DEBUG_NODM */
+
 
 #include <getopt.h>
 #include <grp.h>
@@ -101,11 +104,9 @@ static struct pam_conv conv = {
 #define E_CMD_NOEXEC            126     /* can't run command/shell */
 #define E_CMD_NOTFOUND          127     /* can't find command/shell to run */
 
-/* #define DEBUG_NODM */
-
 #ifdef DEBUG_NODM
 /* Turn syslog into fprintf, for debugging */
-#define syslog(prio, str, ...) do { fprintf(stderr, str, __VA_ARGS__); fprintf(stderr, "\n"); } while (0)
+#define syslog(prio, ...) do { fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); } while (0)
 #endif
 
 /*
@@ -117,9 +118,6 @@ static struct pam_conv conv = {
 /* User we are changing to */
 static char name[BUFSIZ];
 
-/* Command that we are running */
-static char command[BUFSIZ];
-
 static pam_handle_t *pamh = NULL;
 static int caught = 0;
 
@@ -202,20 +200,11 @@ static void catch_signals (int sig)
  * have been applied.  Some work was needed to get it integrated into
  * su.c from shadow.
  */
-static int run_shell (const char* command, int* status)
+static int run_shell (const char** args, int* status)
 {
 	int child;
 	sigset_t ourset;
 	struct sigaction action;
-	const char* args[5];
-
-	args[0] = "/bin/sh";
-	args[1] = "-l";
-	args[2] = "-c";
-	args[3] = command;
-	args[4] = NULL;
-
-	syslog (LOG_INFO, "Running %s %s %s '%s'", args[0], args[1], args[2], args[3]);
 
 	child = fork ();
 	if (child == 0) {	/* child shell */
@@ -307,20 +296,45 @@ killed:
 	return -1;
 }
 
-void run_session(const char* command)
+/*
+ * Run the X session
+ *
+ * @param xinit
+ *   The path to xinit
+ * @param xsession
+ *   The path to the X session
+ * @param xoptions
+ *   The path to the X options (can be NULL if no options are to be passed)
+ * @param mst
+ *   The minimum time (in seconds) that a session should last to be considered
+ *   successful
+ */
+void run_and_restart(const char* xinit, const char* xsession, const char* xoptions, int mst)
 {
 	static int retry_times[] = { 0, 0, 30, 30, 60, 60, -1 };
 	int restart_count = 0;
-	char* s_mst = getenv("NODM_MIN_SESSION_TIME");
-	int mst = s_mst ? atoi(s_mst) : 60;
+	char command[BUFSIZ];
+	const char* args[4];
+
+	if (xoptions != NULL)
+		snprintf(command, BUFSIZ, "%s %s -- %s", xinit, xsession, xoptions);
+	else
+		snprintf(command, BUFSIZ, "%s %s", xinit, xsession);
+	command[BUFSIZ-1] = 0;
+
+	args[0] = "/bin/sh";
+	args[1] = "-c";
+	args[2] = command;
+	args[3] = 0;
 
 	while (1)
 	{
-		/* Run the shell */
+		/* Run the X server */
 		time_t begin = time(NULL);
 		time_t end;
 		int status;
-		if (run_shell(command, &status))
+		syslog (LOG_INFO, "Running %s %s '%s'", args[0], args[1], args[2]);
+		if (run_shell(args, &status))
 			return;
 		end = time(NULL);
 
@@ -341,45 +355,102 @@ void run_session(const char* command)
 }
 
 /*
- * su - switch user id
+ * Copy from the environment the value of $name into dest.
  *
- *	su changes the user's ids to the values for the specified user.  if
- *	no new user name is specified, "root" is used by default.
+ * If $name is not in the environment, use def.
  *
- *	Any additional arguments are passed to the user's shell. In
- *	particular, the argument "-c" will cause the next argument to be
- *	interpreted as a command by the common shell programs.
+ * @param destination buffer, should be at least BUFSIZ long
+ * @param name name of the environment variable to look up
+ * @param def default value to use if $name is not found
  */
-int main (int argc, char **argv)
+static void string_from_env(char* dest, const char* name, const char* def)
 {
-	char *cp;
-	const char *tty = 0;	/* Name of tty SU is run from        */
-	uid_t my_uid;
-	struct passwd *pw = 0;
-	char **envcp;
-	int ret;
+	char* cp = getenv(name);
+	if (cp != NULL)
+		strncpy(dest, cp, BUFSIZ-1);
+	else
+		strncpy(dest, def, BUFSIZ-1);
+	dest[BUFSIZ-1] = 0;
+}
 
-	/*
-	 * Get the program name. The program name is used as a prefix to
-	 * most error messages.
-	 */
-	Prog = Basename (argv[0]);
+static void monitor_cmdline_help(int argc, char** argv, FILE* out)
+{
+	fprintf(out, "Usage: %s [options]\n\n", argv[0]);
+	fprintf(out, "Options:\n");
+	fprintf(out, " --help         print this help message\n");
+	fprintf(out, " --session=cmd  run cmd instead of %s\n", SESSION_CMD);
+	fprintf(out, "                (use for testing)\n");
+}
 
-	openlog(NAME, LOG_PID, LOG_AUTHPRIV);
+/*
+ * Start the monitor, that will continue to rerun xinit with appropriate delays
+ */
+static int nodm_monitor(int argc, char **argv)
+{
+	static int opt_help = 0;
+	static struct option options[] =
+	{
+		/* These options set a flag. */
+		{"help",    no_argument,       &opt_help, 1},
+		{"session", required_argument, 0, 's'},
+		{0, 0, 0, 0}
+	};
+	const char* opt_session = SESSION_CMD;
+	char xinit[BUFSIZ];
+	char xoptions[BUFSIZ];
+	char* cp;
+	int mst;
+
+	/* Parse command line options */
+	while (1)
+	{
+		int option_index = 0;
+		int c = getopt_long(argc, argv, "s:", options, &option_index);
+		if (c == -1) break;
+		switch (c)
+		{
+			case 0: break;
+			case 's': opt_session = optarg; break;
+			default:
+				  fprintf(stderr, "Invalid command line option\n");
+				  monitor_cmdline_help(argc, argv, stderr);
+				  return 1;
+		}
+	}
+	if (opt_help)
+	{
+		monitor_cmdline_help(argc, argv, stdout);
+		return 0;
+	}
 
-	/*
-	 * Process the command line arguments. 
-	 */
+	syslog(LOG_INFO, "Starting nodm monitor");
 
-	// TODO command line processing
+	/* Read the configuration from the environment */
+	cp = getenv("NODM_MIN_SESSION_TIME");
+	mst = cp ? atoi(cp) : 60;
+	string_from_env(xinit, "NODM_XINIT", "/usr/bin/xinit");
+	string_from_env(xoptions, "NODM_X_OPTIONS", "");
 
-	/* We only run if we are root */
-	my_uid = getuid ();
-	if (my_uid != 0)
-	{
-		fprintf (stderr, _("%s: can only be run by root\n"), Prog);
-		return E_NOPERM;
-	}
+	run_and_restart(xinit, opt_session, xoptions[0] == 0 ? NULL : xoptions, mst);
+
+	return 0;
+}
+
+/*
+ * Start the session, with proper autologin and pam handling
+ */
+static int nodm_session(int argc, char **argv)
+{
+	int ret;
+	char *cp;
+	char **envcp;
+	const char *tty = 0;	/* Name of tty SU is run from        */
+	struct passwd *pw = 0;
+	int status;
+	char xsession[BUFSIZ];
+	const char* args[5];
+
+	string_from_env(xsession, "NODM_XSESSION", "/etc/X11/Xsession");
 
 	/*
 	 * Get the tty name. Entries will be logged indicating that the user
@@ -400,12 +471,6 @@ int main (int argc, char **argv)
 	else
 		STRFCPY(name, getenv("NODM_USER"));
 
-	/* Get the command that we should run */
-	if (getenv("NODM_COMMAND") == NULL)
-		strcpy(command, "/usr/bin/xinit /etc/X11/Xsession -- vt7 -nolisten tcp");
-	else
-		STRFCPY(command, getenv("NODM_COMMAND"));
-
 	ret = pam_start (NAME, name, &conv, &pamh);
 	if (ret != PAM_SUCCESS) {
 		syslog (LOG_ERR, "pam_start: error %d", ret);
@@ -508,9 +573,23 @@ int main (int argc, char **argv)
 	setenv ("HOME", pwent.pw_dir, 1);
 	setenv ("USER", pwent.pw_name, 1);
 	setenv ("LOGNAME", pwent.pw_name, 1);
+
+	/* Clear the NODM_* environment variables */
+	unsetenv("NODM_USER");
+	unsetenv("NODM_XINIT");
+	unsetenv("NODM_XSESSION");
+	unsetenv("NODM_X_OPTIONS");
+	unsetenv("NODM_MIN_SESSION_TIME");
+
 	chdir (pwent.pw_dir);
 
-	run_session(command);
+	args[0] = "/bin/sh";
+	args[1] = "-l";
+	args[2] = "-c";
+	args[3] = xsession;
+	args[4] = NULL;
+
+	run_shell(args, &status);
 
 	ret = pam_close_session (pamh, 0);
 	if (ret != PAM_SUCCESS) {
@@ -523,6 +602,60 @@ int main (int argc, char **argv)
 
 	ret = pam_end (pamh, PAM_SUCCESS);
 
+	return status;
+}
+
+/* Return true if string ends with substring */
+static int ends_with(const char* string, const char* substring)
+{
+	int len_string = strlen(string);
+	int len_substring = strlen(substring);
+	if (len_string < len_substring)
+		return 0;
+	return strcmp(string + (len_string - len_substring), substring) == 0;
+}
+
+/*
+ * nodm - start X with autologin to a given user
+ *
+ * First, X is started as root, and nodm itself is used as the session.
+ *
+ * When run as the session, nodm performs a proper login to a given user and
+ * starts the X session.
+ */
+int main (int argc, char **argv)
+{
+	int ret;
+
+	/*
+	 * Get the program name. The program name is used as a prefix to
+	 * most error messages.
+	 */
+	Prog = Basename (argv[0]);
+
+	openlog(NAME, LOG_PID, LOG_AUTHPRIV);
+
+	/*
+	 * Process the command line arguments. 
+	 */
+
+	// TODO command line processing
+
+	/* We only run if we are root */
+	if (getuid() != 0)
+	{
+		fprintf (stderr, _("%s: can only be run by root\n"), Prog);
+		return E_NOPERM;
+	}
+
+	if (ends_with(argv[0], "-session"))
+	{
+		syslog(LOG_INFO, "Starting nodm X session");
+		ret = nodm_session(argc, argv);
+	} else {
+		ret = nodm_monitor(argc, argv);
+	}
+
 	closelog ();
-	return 0;
+	return ret;
 }
diff --git a/test_nodm b/test_nodm
index 633b9dc..1c1d86f 100755
--- a/test_nodm
+++ b/test_nodm
@@ -1,26 +1,27 @@
 #!/bin/sh
 
-if [ -z "$1" ]
-then
-	echo "Use: $0 username" >&2
-	exit 1
-fi
-
-if [ "$1" != runtest ]
-then
-	export NODM_USER=$1
-	export NODM_COMMAND="`pwd`/$0 runtest -- a b c"
-	export NODM_MIN_SESSION_TIME=60
-	exec ./nodm 
-else
-	test "$2" != "--" && echo "\$2 is '$2' instead of '--'"
-	test "$3" != "a" && echo "\$3 is '$3' instead of 'a'"
-	test "$4" != "b" && echo "\$4 is '$4' instead of 'b'"
-	test "$5" != "c" && echo "\$5 is '$5' instead of 'c'"
-	echo "PATH is: $PATH"
-	echo "USER is: $USER"
-	echo "HOME is: $HOME"
-	echo "pwd: `pwd`"
-	echo "id: `id`"
-	exit 0
-fi
+case "$1" in
+	'')
+#		test "$2" != "a" && echo "\$3 is '$2' instead of 'a'"
+#		test "$3" != "b" && echo "\$4 is '$3' instead of 'b'"
+#		test "$4" != "c" && echo "\$5 is '$4' instead of 'c'"
+		echo "PATH is: $PATH"
+		echo "USER is: $USER"
+		echo "HOME is: $HOME"
+		echo "pwd: `pwd`"
+		echo "id: `id`"
+		exit 0
+		;;
+	*)
+		export NODM_USER=$1
+		export NODM_XINIT=exec
+		export NODM_XSESSION="`pwd`/$0"
+#		export NODM_X_OPTIONS="runtest a b c"
+		export NODM_MIN_SESSION_TIME=60
+		exec ./nodm --session `pwd`/nodm-session
+		;;
+#	'')
+#		echo "Use: $0 username" >&2
+#		exit 1
+#		;;
+esac

-- 
Automatic Display Manager



More information about the pkg-fso-commits mailing list