autofs-5.0.3 - don't use proc for is running check From: Ian Kent Using /proc//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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} +