Blob Blame History Raw
autofs-5.0.3 - don't use proc for is running check

From: Ian Kent <raven@themaw.net>

Using /proc/<pid>/cmdline to check if the daemon is running allows
any user to create a trivial program called "automount" and prevent
the system automounter from running simply by executing it and
leaving it running. This patch makes autofs use a flag file for this
check instead.
---

 CHANGELOG          |    1 
 Makefile.conf.in   |    3 +
 aclocal.m4         |   16 ++++
 configure          |   35 +++++++++
 configure.in       |   17 +++++
 daemon/Makefile    |    5 +
 daemon/automount.c |   95 ++++++++------------------
 daemon/flag.c      |  192 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 8 files changed, 294 insertions(+), 70 deletions(-)
 create mode 100644 daemon/flag.c


diff --git a/CHANGELOG b/CHANGELOG
index f40a941..3921552 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -24,6 +24,7 @@
 - fix incorrect if check in get user info.
 - fix couple of memory leaks.
 - add command line option to override check for daemon already running.
+- don't use proc file system when checking if the daemon is running.
  
 14/01/2008 autofs-5.0.3
 -----------------------
diff --git a/Makefile.conf.in b/Makefile.conf.in
index 09c3129..d88f5ee 100644
--- a/Makefile.conf.in
+++ b/Makefile.conf.in
@@ -74,6 +74,9 @@ autofsmapdir = @mapdir@
 # Location for autofs fifos
 autofsfifodir = @fifodir@
 
+# Location for autofs flag file
+autofsflagdir = @flagdir@
+
 # Where to install the automount program
 sbindir = @sbindir@
 
diff --git a/aclocal.m4 b/aclocal.m4
index a1105ae..9ef4050 100644
--- a/aclocal.m4
+++ b/aclocal.m4
@@ -136,6 +136,22 @@ AC_DEFUN(AF_FIFO_D,
   done
 fi])
 
+dnl --------------------------------------------------------------------------
+dnl AF_FLAG_D
+dnl
+dnl Check the location of the autofs flag file directory
+dnl --------------------------------------------------------------------------
+AC_DEFUN(AF_FLAG_D,
+[if test -z "$flagdir"; then
+  for flag_d in /var/run /tmp; do
+    if test -z "$flagdir"; then
+      if test -d "$flag_d"; then
+        flagdir="$flag_d"
+      fi
+    fi
+  done
+fi])
+
 dnl ----------------------------------- ##                   -*- Autoconf -*-
 dnl Check if --with-dmalloc was given.  ##
 dnl From Franc,ois Pinard               ##
diff --git a/configure b/configure
index 0d3268c..9278196 100755
--- a/configure
+++ b/configure
@@ -655,6 +655,7 @@ initdir
 confdir
 mapdir
 fifodir
+flagdir
 DMALLOCLIB
 MOUNT
 HAVE_MOUNT
@@ -1295,6 +1296,7 @@ Optional Packages:
   --with-confdir=DIR	  use DIR for autofs configuration files
   --with-mapdir=PATH	  look in PATH for mount maps used by the automounter
   --with-fifodir=PATH	   use PATH as the directory for fifos used by the automounter
+  --with-flagdir=PATH	   use PATH as the directory for the flag file used by the automounter
   --with-dmalloc          use dmalloc, as in
 			  http://www.dmalloc.com/dmalloc.tar.gz
   --with-hesiod=DIR	  enable Hesiod support (libs and includes in DIR)
@@ -1876,6 +1878,36 @@ echo "${ECHO_T}$fifodir" >&6; }
 
 
 #
+# The user can specify --with-flagdir=PATH to specify where autofs flag file goes
+#
+if test -z "$flagdir"; then
+  for flag_d in /var/run /tmp; do
+    if test -z "$flagdir"; then
+      if test -d "$flag_d"; then
+        flagdir="$flag_d"
+      fi
+    fi
+  done
+fi
+
+# Check whether --with-flagdir was given.
+if test "${with_flagdir+set}" = set; then
+  withval=$with_flagdir; if test -z "$withval" -o "$withval" = "yes" -o "$withval" = "no"
+	then
+		:
+	else
+		filagdir="${withval}"
+	fi
+
+fi
+
+{ echo "$as_me:$LINENO: checking for autofs flag file directory" >&5
+echo $ECHO_N "checking for autofs flag file directory... $ECHO_C" >&6; }
+{ echo "$as_me:$LINENO: result: $flagdir" >&5
+echo "${ECHO_T}$flagdir" >&6; }
+
+
+#
 # Optional include dmalloc
 #
 { echo "$as_me:$LINENO: checking if malloc debugging is wanted" >&5
@@ -6247,6 +6279,7 @@ initdir!$initdir$ac_delim
 confdir!$confdir$ac_delim
 mapdir!$mapdir$ac_delim
 fifodir!$fifodir$ac_delim
+flagdir!$flagdir$ac_delim
 DMALLOCLIB!$DMALLOCLIB$ac_delim
 MOUNT!$MOUNT$ac_delim
 HAVE_MOUNT!$HAVE_MOUNT$ac_delim
@@ -6297,7 +6330,7 @@ LIBOBJS!$LIBOBJS$ac_delim
 LTLIBOBJS!$LTLIBOBJS$ac_delim
 _ACEOF
 
-  if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 89; then
+  if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 90; then
     break
   elif $ac_last_try; then
     { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5
diff --git a/configure.in b/configure.in
index 27b9bec..5ba3a49 100644
--- a/configure.in
+++ b/configure.in
@@ -96,6 +96,23 @@ AC_MSG_RESULT([$fifodir])
 AC_SUBST(fifodir)
 
 #
+# The user can specify --with-flagdir=PATH to specify where autofs flag file goes
+#
+AF_FLAG_D()
+AC_ARG_WITH(flagdir,
+[  --with-flagdir=PATH	   use PATH as the directory for the flag file used by the automounter],
+	if test -z "$withval" -o "$withval" = "yes" -o "$withval" = "no"
+	then
+		:
+	else
+		filagdir="${withval}"
+	fi
+)
+AC_MSG_CHECKING([for autofs flag file directory])
+AC_MSG_RESULT([$flagdir])
+AC_SUBST(flagdir)
+
+#
 # Optional include dmalloc
 #
 AM_WITH_DMALLOC()
diff --git a/daemon/Makefile b/daemon/Makefile
index 528a684..9c2d858 100644
--- a/daemon/Makefile
+++ b/daemon/Makefile
@@ -6,9 +6,9 @@
 include ../Makefile.rules
 
 SRCS = automount.c indirect.c direct.c spawn.c module.c mount.c \
-	lookup.c state.c
+	lookup.c state.c flag.c
 OBJS = automount.o indirect.o direct.o spawn.o module.o mount.o \
-	lookup.o state.o
+	lookup.o state.o flag.o
 
 version := $(shell cat ../.version)
 
@@ -17,6 +17,7 @@ CFLAGS += -DAUTOFS_LIB_DIR=\"$(autofslibdir)\"
 CFLAGS += -DAUTOFS_MAP_DIR=\"$(autofsmapdir)\"
 CFLAGS += -DAUTOFS_CONF_DIR=\"$(autofsconfdir)\"
 CFLAGS += -DAUTOFS_FIFO_DIR=\"$(autofsfifodir)\"
+CFLAGS += -DAUTOFS_FLAG_DIR=\"$(autofsflagdir)\"
 CFLAGS += -DVERSION_STRING=\"$(version)\"
 LDFLAGS += -rdynamic
 LIBS = -ldl
diff --git a/daemon/automount.c b/daemon/automount.c
index 48ac30a..dbf267c 100644
--- a/daemon/automount.c
+++ b/daemon/automount.c
@@ -81,6 +81,8 @@ pthread_key_t key_thread_stdenv_vars;
 
 #define MAX_OPEN_FILES		10240
 
+int aquire_flag_file(void);
+void release_flag_file(void);
 static int umount_all(struct autofs_point *ap, int force);
 
 extern pthread_mutex_t master_mutex;
@@ -1098,7 +1100,7 @@ static int handle_packet(struct autofs_point *ap)
 	return -1;
 }
 
-static void become_daemon(unsigned foreground)
+static void become_daemon(unsigned foreground, unsigned daemon_check)
 {
 	FILE *pidfp;
 	char buf[MAX_ERR_BUF];
@@ -1118,9 +1120,14 @@ static void become_daemon(unsigned foreground)
 	}
 
 	/* Detach from foreground process */
-	if (foreground)
+	if (foreground) {
+		if (daemon_check && !aquire_flag_file()) {
+			fprintf(stderr, "%s: program is already running.\n",
+				program);
+			exit(1);
+		}
 		log_to_stderr();
-	else {
+	} else {
 		pid = fork();
 		if (pid > 0) {
 			int r;
@@ -1136,6 +1143,13 @@ static void become_daemon(unsigned foreground)
 		}
 		close(start_pipefd[0]);
 
+		if (daemon_check && !aquire_flag_file()) {
+			fprintf(stderr, "%s: program is already running.\n",
+				program);
+			close(start_pipefd[1]);
+			exit(1);
+		}
+
 		/*
 		 * Make our own process group for "magic" reason: processes that share
 		 * our pgrp see the raw filesystem behind the magic.
@@ -1143,6 +1157,7 @@ static void become_daemon(unsigned foreground)
 		if (setsid() == -1) {
 			char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
 			fprintf(stderr, "setsid: %s", estr);
+			close(start_pipefd[1]);
 			exit(1);
 		}
 		log_to_syslog();
@@ -1617,64 +1632,6 @@ static void key_thread_stdenv_vars_destroy(void *arg)
 	return;
 }
 
-static int is_automount_running(void)
-{
-	FILE *fp;
-	DIR *dir;
-	struct dirent entry;
-	struct dirent *result;
-	char path[PATH_MAX + 1], buf[PATH_MAX];
-
-	if ((dir = opendir("/proc")) == NULL) {
-		fprintf(stderr, "cannot opendir(/proc)\n");
-		exit(1);
-	}
-
-	while (readdir_r(dir, &entry, &result) == 0) {
-		int path_len, pid = 0;
-
-		if (!result)
-			break;
-
-		if (*entry.d_name == '.')
-			continue;
-
-		if (!strcmp(entry.d_name, "self"))
-			continue;
-
-		if (!isdigit(*entry.d_name))
-			continue;
-
-		pid = atoi(entry.d_name);
-		if (pid == getpid())
-			continue;
-
-		path_len = sprintf(path, "/proc/%s/cmdline", entry.d_name);
-		if (path_len >= PATH_MAX) {
-			fprintf(stderr,
-				"buffer to small for /proc path\n");
-			return -1;
-		}
-		path[path_len] = '\0';
-
-		fp = fopen(path, "r");
-		if (fp) {
-			int c, len = 0;
-
-			while (len < 127 && (c = fgetc(fp)) != EOF && c)
-				buf[len++] = c;
-			buf[len] = '\0';
-
-			if (strstr(buf, "automount"))
-				return pid;
-			fclose(fp);
-		}
-	}
-	closedir(dir);
-
-	return 0;
-}
-
 static void usage(void)
 {
 	fprintf(stderr,
@@ -1973,11 +1930,6 @@ int main(int argc, char *argv[])
 		exit(exit_code);
 	}
 
-	if (daemon_check && is_automount_running() > 0) {
-		fprintf(stderr, "%s: program is already running.\n",
-			program);
-		exit(1);
-	}
 #if 0
 	if (!load_autofs4_module()) {
 		fprintf(stderr, "%s: can't load %s filesystem module.\n",
@@ -2009,7 +1961,7 @@ int main(int argc, char *argv[])
 		     "can't increase core file limit - continuing");
 #endif
 
-	become_daemon(foreground);
+	become_daemon(foreground, daemon_check);
 
 	if (argc == 0)
 		master_list = master_new(NULL, timeout, ghost);
@@ -2020,6 +1972,7 @@ int main(int argc, char *argv[])
 		logerr("%s: can't create master map %s",
 			program, argv[0]);
 		close(start_pipefd[1]);
+		release_flag_file();
 		exit(1);
 	}
 
@@ -2027,6 +1980,7 @@ int main(int argc, char *argv[])
 		logerr("%s: failed to init thread attribute struct!",
 		     program);
 		close(start_pipefd[1]);
+		release_flag_file();
 		exit(1);
 	}
 
@@ -2035,6 +1989,7 @@ int main(int argc, char *argv[])
 		logerr("%s: failed to set detached thread attribute!",
 		     program);
 		close(start_pipefd[1]);
+		release_flag_file();
 		exit(1);
 	}
 
@@ -2044,6 +1999,7 @@ int main(int argc, char *argv[])
 		logerr("%s: failed to set stack size thread attribute!",
 		       program);
 		close(start_pipefd[1]);
+		release_flag_file();
 		exit(1);
 	}
 #endif
@@ -2060,6 +2016,7 @@ int main(int argc, char *argv[])
 		       program);
 		master_kill(master_list);
 		close(start_pipefd[1]);
+		release_flag_file();
 		exit(1);
 	}
 
@@ -2067,6 +2024,7 @@ int main(int argc, char *argv[])
 		logerr("%s: failed to create alarm handler thread!", program);
 		master_kill(master_list);
 		close(start_pipefd[1]);
+		release_flag_file();
 		exit(1);
 	}
 
@@ -2074,6 +2032,7 @@ int main(int argc, char *argv[])
 		logerr("%s: failed to create FSM handler thread!", program);
 		master_kill(master_list);
 		close(start_pipefd[1]);
+		release_flag_file();
 		exit(1);
 	}
 
@@ -2086,6 +2045,7 @@ int main(int argc, char *argv[])
 		*pst_stat = 3;
 		res = write(start_pipefd[1], pst_stat, sizeof(*pst_stat));
 		close(start_pipefd[1]);
+		release_flag_file();
 		exit(3);
 	}
 
@@ -2102,6 +2062,7 @@ int main(int argc, char *argv[])
 		pid_file = NULL;
 	}
 	closelog();
+	release_flag_file();
 
 #ifdef LIBXML2_WORKAROUND
 	if (dh)
diff --git a/daemon/flag.c b/daemon/flag.c
new file mode 100644
index 0000000..d8ca61b
--- /dev/null
+++ b/daemon/flag.c
@@ -0,0 +1,192 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * flag.c - autofs flag file management
+ *
+ * Copyright 2008 Red Hat, Inc. All rights reserved.
+ * Copyright 2008 Ian Kent <raven@themaw.net>
+ *
+ * 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, Inc., 675 Mass Ave, Cambridge MA 02139,
+ * USA; 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.
+ *
+ * ----------------------------------------------------------------------- */
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <time.h>
+#include <unistd.h>
+#include <string.h>
+#include <alloca.h>
+#include <stdio.h>
+#include <signal.h>
+#include <errno.h>
+
+#define MAX_PIDSIZE	20
+#define FLAG_FILE	AUTOFS_FLAG_DIR "/autofs-running"
+
+/* Flag for already existing flag file. */
+static int we_created_flagfile = 0;
+
+/* file descriptor of flag file */
+static int fd = -1;
+
+static int flag_is_owned(int fd)
+{
+	int pid = 0, tries = 3;
+
+	while (tries--) {
+		char pidbuf[MAX_PIDSIZE + 1];
+		int got;
+
+		lseek(fd, 0, SEEK_SET);
+		got = read(fd, pidbuf, MAX_PIDSIZE);
+		/*
+		 * We add a terminator to the pid to verify write complete.
+		 * If the write isn't finished in 300 milliseconds then it's
+		 * probably a stale lock file.
+		 */
+		if (got > 0 && pidbuf[got - 1] == '\n') {
+			sscanf(pidbuf, "%d", &pid);
+			break;
+		} else {
+			struct timespec t = { 0, 100000000 };
+			struct timespec r;
+
+			while (nanosleep(&t, &r) == -1 && errno == EINTR)
+				memcpy(&t, &r, sizeof(struct timespec));
+
+			continue;
+		}
+
+		/* Stale flagfile */
+		if (!tries)
+			return 0;
+	}
+
+
+	if (pid) {
+		int ret;
+
+		ret = kill(pid, 0);
+		/*
+		 * If lock file exists but is not owned by a process
+		 * we return unowned status so we can get rid of it
+		 * and continue.
+		 */
+		if (ret == -1 && errno == ESRCH)
+			return 0;
+	} else {
+		/*
+		 * Odd, no pid in file - so what should we do?
+		 * Assume something bad happened to owner and
+		 * return unowned status.
+		 */
+		return 0;
+	}
+
+	return 1;
+}
+
+/* Remove flag file. */
+void release_flag_file(void)
+{
+	if (fd > 0) {
+		close(fd);
+		fd = -1;
+	}
+
+	if (we_created_flagfile) {
+		unlink(FLAG_FILE);
+		we_created_flagfile = 0;
+	}
+}
+
+/* * Try to create flag file */
+int aquire_flag_file(void)
+{
+	char *linkf;
+	int len;
+
+	len = strlen(FLAG_FILE) + MAX_PIDSIZE;
+	linkf = alloca(len + 1);
+	snprintf(linkf, len, "%s.%d", FLAG_FILE, getpid());
+
+	/*
+	 * Repeat until it was us who made the link or we find the
+	 * flag file already exists. If an unexpected error occurs
+	 * we return 0 claiming the flag file exists which may not
+	 * really be the case.
+	 */
+	while (!we_created_flagfile) {
+		int errsv, i, j;
+
+		i = open(linkf, O_WRONLY|O_CREAT, 0);
+		if (i < 0) {
+			release_flag_file();
+			return 0;
+		}
+		close(i);
+
+		j = link(linkf, FLAG_FILE);
+		errsv = errno;
+
+		(void) unlink(linkf);
+
+		if (j < 0 && errsv != EEXIST) {
+			release_flag_file();
+			return 0;
+		}
+
+		fd = open(FLAG_FILE, O_RDWR);
+		if (fd < 0) {
+			/* Maybe the file was just deleted? */
+			if (errno == ENOENT)
+				continue;
+			release_flag_file();
+			return 0;
+		}
+
+		if (j == 0) {
+			char pidbuf[MAX_PIDSIZE + 1];
+			int pidlen;
+
+			pidlen = sprintf(pidbuf, "%d\n", getpid());
+			if (write(fd, pidbuf, pidlen) != pidlen) {
+				release_flag_file();
+				return 0;
+			}
+
+			we_created_flagfile = 1;
+		} else {
+			/*
+			 * Someone else made the link.
+			 * If the flag file is not owned by anyone clean
+			 * it up and try again, otherwise return fail.
+			 */
+			if (!flag_is_owned(fd)) {
+				close(fd);
+				fd = -1;
+				unlink(FLAG_FILE);
+				continue;
+			}
+
+			release_flag_file();
+			return 0;
+		}
+
+		close(fd);
+		fd = -1;
+	}
+
+	return 1;
+}
+