Blame daemon/spawn.c

Packit 8480eb
/* ----------------------------------------------------------------------- *
Packit 8480eb
 * 
Packit 8480eb
 *  spawn.c - run programs synchronously with output redirected to syslog
Packit 8480eb
 *   
Packit 8480eb
 *   Copyright 1997 Transmeta Corporation - All Rights Reserved
Packit 8480eb
 *   Copyright 2005 Ian Kent <raven@themaw.net>
Packit 8480eb
 *
Packit 8480eb
 *   This program is free software; you can redistribute it and/or modify
Packit 8480eb
 *   it under the terms of the GNU General Public License as published by
Packit 8480eb
 *   the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
Packit 8480eb
 *   USA; either version 2 of the License, or (at your option) any later
Packit 8480eb
 *   version; incorporated herein by reference.
Packit 8480eb
 *
Packit 8480eb
 * ----------------------------------------------------------------------- */
Packit 8480eb
Packit 8480eb
#include <signal.h>
Packit 8480eb
#include <stdarg.h>
Packit 8480eb
#include <stdio.h>
Packit 8480eb
#include <stdlib.h>
Packit 8480eb
#include <string.h>
Packit 8480eb
#include <sys/types.h>
Packit 8480eb
#include <dirent.h>
Packit 8480eb
#include <grp.h>
Packit 8480eb
#include <time.h>
Packit 8480eb
#include <poll.h>
Packit 8480eb
#include <sys/wait.h>
Packit 8480eb
#include <sys/stat.h>
Packit 8480eb
#include <sys/mount.h>
Packit 8480eb
Packit 8480eb
#include "automount.h"
Packit 8480eb
Packit 8480eb
static pthread_mutex_t spawn_mutex = PTHREAD_MUTEX_INITIALIZER;
Packit 8480eb
static pthread_mutex_t open_mutex = PTHREAD_MUTEX_INITIALIZER;
Packit 8480eb
Packit 8480eb
#define SPAWN_OPT_NONE		0x0000
Packit 8480eb
#define SPAWN_OPT_LOCK		0x0001
Packit 8480eb
#define SPAWN_OPT_OPEN		0x0002
Packit 8480eb
Packit 8480eb
#define MTAB_LOCK_RETRIES	3
Packit 8480eb
Packit 8480eb
void dump_core(void)
Packit 8480eb
{
Packit 8480eb
	sigset_t segv;
Packit 8480eb
Packit 8480eb
	sigemptyset(&segv);
Packit 8480eb
	sigaddset(&segv, SIGSEGV);
Packit 8480eb
	pthread_sigmask(SIG_UNBLOCK, &segv, NULL);
Packit 8480eb
	sigprocmask(SIG_UNBLOCK, &segv, NULL);
Packit 8480eb
Packit 8480eb
	raise(SIGSEGV);
Packit 8480eb
}
Packit 8480eb
Packit 8480eb
void open_mutex_lock(void)
Packit 8480eb
{
Packit 8480eb
	int _o_lock = pthread_mutex_lock(&open_mutex);
Packit 8480eb
	if (_o_lock)
Packit 8480eb
		fatal(_o_lock);
Packit 8480eb
}
Packit 8480eb
Packit 8480eb
void open_mutex_unlock(void)
Packit 8480eb
{
Packit 8480eb
	int _o_unlock = pthread_mutex_unlock(&open_mutex);
Packit 8480eb
	if (_o_unlock)
Packit 8480eb
		fatal(_o_unlock);
Packit 8480eb
}
Packit 8480eb
Packit 8480eb
/*
Packit 8480eb
 * Use CLOEXEC flag for open(), pipe(), fopen() (read-only case) and
Packit 8480eb
 * socket() if possible.
Packit 8480eb
 */
Packit 8480eb
static int cloexec_works = 0;
Packit 8480eb
Packit 8480eb
static void check_cloexec(int fd)
Packit 8480eb
{
Packit 8480eb
	if (cloexec_works == 0) {
Packit 8480eb
		int fl = fcntl(fd, F_GETFD);
Packit 8480eb
		if (fl != -1)
Packit 8480eb
			cloexec_works = (fl & FD_CLOEXEC) ? 1 : -1;
Packit 8480eb
	}
Packit 8480eb
	if (cloexec_works > 0)
Packit 8480eb
		return;
Packit 8480eb
	fcntl(fd, F_SETFD, FD_CLOEXEC);
Packit 8480eb
	return;
Packit 8480eb
}
Packit 8480eb
Packit 8480eb
int open_fd(const char *path, int flags)
Packit 8480eb
{
Packit 8480eb
	int fd;
Packit 8480eb
Packit 8480eb
	open_mutex_lock();
Packit 8480eb
#if defined(O_CLOEXEC) && defined(SOCK_CLOEXEC)
Packit 8480eb
	if (cloexec_works != -1)
Packit 8480eb
		flags |= O_CLOEXEC;
Packit 8480eb
#endif
Packit 8480eb
	fd = open(path, flags);
Packit 8480eb
	if (fd == -1) {
Packit 8480eb
		open_mutex_unlock();
Packit 8480eb
		return -1;
Packit 8480eb
	}
Packit 8480eb
	check_cloexec(fd);
Packit 8480eb
	open_mutex_unlock();
Packit 8480eb
	return fd;
Packit 8480eb
}
Packit 8480eb
Packit 8480eb
int open_fd_mode(const char *path, int flags, int mode)
Packit 8480eb
{
Packit 8480eb
	int fd;
Packit 8480eb
Packit 8480eb
	open_mutex_lock();
Packit 8480eb
#if defined(O_CLOEXEC) && defined(SOCK_CLOEXEC)
Packit 8480eb
	if (cloexec_works != -1)
Packit 8480eb
		flags |= O_CLOEXEC;
Packit 8480eb
#endif
Packit 8480eb
	fd = open(path, flags, mode);
Packit 8480eb
	if (fd == -1) {
Packit 8480eb
		open_mutex_unlock();
Packit 8480eb
		return -1;
Packit 8480eb
	}
Packit 8480eb
	check_cloexec(fd);
Packit 8480eb
	open_mutex_unlock();
Packit 8480eb
	return fd;
Packit 8480eb
}
Packit 8480eb
Packit 8480eb
int open_pipe(int pipefd[2])
Packit 8480eb
{
Packit 8480eb
	int ret;
Packit 8480eb
Packit 8480eb
	open_mutex_lock();
Packit 8480eb
#if defined(O_CLOEXEC) && defined(SOCK_CLOEXEC) && defined(HAVE_PIPE2)
Packit 8480eb
	if (cloexec_works != -1) {
Packit 8480eb
		ret = pipe2(pipefd, O_CLOEXEC);
Packit 8480eb
		if (ret != -1)
Packit 8480eb
			goto done;
Packit 8480eb
		if (errno != EINVAL)
Packit 8480eb
			goto err;
Packit 8480eb
	}
Packit 8480eb
#endif
Packit 8480eb
	ret = pipe(pipefd);
Packit 8480eb
	if (ret == -1)
Packit 8480eb
		goto err;
Packit 8480eb
	check_cloexec(pipefd[0]);
Packit 8480eb
	check_cloexec(pipefd[1]);
Packit 8480eb
done:
Packit 8480eb
	open_mutex_unlock();
Packit 8480eb
	return 0;
Packit 8480eb
err:
Packit 8480eb
	open_mutex_unlock();
Packit 8480eb
	return -1;
Packit 8480eb
}
Packit 8480eb
Packit 8480eb
int open_sock(int domain, int type, int protocol)
Packit 8480eb
{
Packit 8480eb
	int fd;
Packit 8480eb
Packit 8480eb
	open_mutex_lock();
Packit 8480eb
#ifdef SOCK_CLOEXEC
Packit 8480eb
	if (cloexec_works != -1)
Packit 8480eb
		type |= SOCK_CLOEXEC;
Packit 8480eb
#endif
Packit 8480eb
	fd = socket(domain, type, protocol);
Packit 8480eb
	if (fd == -1) {
Packit 8480eb
		open_mutex_unlock();
Packit 8480eb
		return -1;
Packit 8480eb
	}
Packit 8480eb
	check_cloexec(fd);
Packit 8480eb
	open_mutex_unlock();
Packit 8480eb
	return fd;
Packit 8480eb
}
Packit 8480eb
Packit 8480eb
FILE *open_fopen_r(const char *path)
Packit 8480eb
{
Packit 8480eb
	FILE *f;
Packit 8480eb
Packit 8480eb
	open_mutex_lock();
Packit 8480eb
#if defined(O_CLOEXEC) && defined(SOCK_CLOEXEC)
Packit 8480eb
	if (cloexec_works != -1) {
Packit 8480eb
		f = fopen(path, "re");
Packit 8480eb
		if (f != NULL) {
Packit 8480eb
			check_cloexec(fileno(f));
Packit 8480eb
			open_mutex_unlock();
Packit 8480eb
			return f;
Packit 8480eb
		}
Packit 8480eb
	}
Packit 8480eb
#endif
Packit 8480eb
	f = fopen(path, "r");
Packit 8480eb
	if (f == NULL) {
Packit 8480eb
		open_mutex_unlock();
Packit 8480eb
		return NULL;
Packit 8480eb
	}
Packit 8480eb
	check_cloexec(fileno(f));
Packit 8480eb
	open_mutex_unlock();
Packit 8480eb
	return f;
Packit 8480eb
}
Packit 8480eb
Packit 8480eb
FILE *open_setmntent_r(const char *table)
Packit 8480eb
{
Packit 8480eb
	FILE *tab;
Packit 8480eb
Packit 8480eb
	open_mutex_lock();
Packit 8480eb
#if defined(O_CLOEXEC) && defined(SOCK_CLOEXEC)
Packit 8480eb
	if (cloexec_works != -1) {
Packit 8480eb
		tab = setmntent(table, "re");
Packit 8480eb
		if (tab != NULL) {
Packit 8480eb
			check_cloexec(fileno(tab));
Packit 8480eb
			open_mutex_unlock();
Packit 8480eb
			return tab;
Packit 8480eb
		}
Packit 8480eb
	}
Packit 8480eb
#endif
Packit 8480eb
	tab = fopen(table, "r");
Packit 8480eb
	if (tab == NULL) {
Packit 8480eb
		open_mutex_unlock();
Packit 8480eb
		return NULL;
Packit 8480eb
	}
Packit 8480eb
	check_cloexec(fileno(tab));
Packit 8480eb
	open_mutex_unlock();
Packit 8480eb
	return tab;
Packit 8480eb
}
Packit 8480eb
Packit 8480eb
/*
Packit 8480eb
 * Used by subprocesses which exec to avoid carrying over the main
Packit 8480eb
 * daemon's signalling environment
Packit 8480eb
 */
Packit 8480eb
void reset_signals(void)
Packit 8480eb
{
Packit 8480eb
	struct sigaction sa;
Packit 8480eb
	sigset_t allsignals;
Packit 8480eb
	int i;
Packit 8480eb
Packit 8480eb
	sigfillset(&allsignals);
Packit 8480eb
	sigprocmask(SIG_BLOCK, &allsignals, NULL);
Packit 8480eb
Packit 8480eb
	/* Discard all pending signals */
Packit 8480eb
	sa.sa_handler = SIG_IGN;
Packit 8480eb
	sigemptyset(&sa.sa_mask);
Packit 8480eb
	sa.sa_flags = 0;
Packit 8480eb
Packit 8480eb
	for (i = 1; i < NSIG; i++)
Packit 8480eb
		if (i != SIGKILL && i != SIGSTOP)
Packit 8480eb
			sigaction(i, &sa, NULL);
Packit 8480eb
Packit 8480eb
	sa.sa_handler = SIG_DFL;
Packit 8480eb
Packit 8480eb
	for (i = 1; i < NSIG; i++)
Packit 8480eb
		if (i != SIGKILL && i != SIGSTOP)
Packit 8480eb
			sigaction(i, &sa, NULL);
Packit 8480eb
Packit 8480eb
	/* Ignore the user signals that may be sent so that we
Packit 8480eb
	 *  don't terminate execed program by mistake */
Packit 8480eb
	sa.sa_handler = SIG_IGN;
Packit 8480eb
	sa.sa_flags = SA_RESTART;
Packit 8480eb
	sigaction(SIGUSR1, &sa, NULL);
Packit 8480eb
	sigaction(SIGUSR2, &sa, NULL);
Packit 8480eb
Packit 8480eb
	sigprocmask(SIG_UNBLOCK, &allsignals, NULL);
Packit 8480eb
}
Packit 8480eb
Packit 8480eb
#define ERRBUFSIZ 2047		/* Max length of error string excl \0 */
Packit 8480eb
Packit 8480eb
static int timed_read(int pipe, char *buf, size_t len, int time)
Packit 8480eb
{
Packit 8480eb
	struct pollfd pfd[1];
Packit 8480eb
	int timeout = time;
Packit 8480eb
	int ret;
Packit 8480eb
Packit 8480eb
	pfd[0].fd = pipe;
Packit 8480eb
	pfd[0].events = POLLIN;
Packit 8480eb
Packit 8480eb
	if (time != -1) {
Packit 8480eb
		if (time >= (INT_MAX - 1)/1000)
Packit 8480eb
			timeout = INT_MAX - 1;
Packit 8480eb
		else
Packit 8480eb
			timeout = time * 1000;
Packit 8480eb
	}
Packit 8480eb
Packit 8480eb
	ret = poll(pfd, 1, timeout);
Packit 8480eb
	if (ret <= 0) {
Packit 8480eb
		if (ret == 0)
Packit 8480eb
			ret = -ETIMEDOUT;
Packit 8480eb
		return ret;
Packit 8480eb
	}
Packit 8480eb
Packit 8480eb
	if (pfd[0].fd == -1)
Packit 8480eb
		return 0;
Packit 8480eb
Packit 8480eb
	if ((pfd[0].revents & (POLLIN|POLLHUP)) == POLLHUP)
Packit 8480eb
		return 0;
Packit 8480eb
Packit 8480eb
	while ((ret = read(pipe, buf, len)) == -1 && errno == EINTR);
Packit 8480eb
Packit 8480eb
	return ret;
Packit 8480eb
}
Packit 8480eb
Packit 8480eb
static int do_spawn(unsigned logopt, unsigned int wait,
Packit 8480eb
		    unsigned int options, const char *prog,
Packit 8480eb
		    const char *const *argv)
Packit 8480eb
{
Packit 8480eb
	pid_t f;
Packit 8480eb
	int ret, status, pipefd[2];
Packit 8480eb
	char errbuf[ERRBUFSIZ + 1], *p, *sp;
Packit 8480eb
	int errp, errn;
Packit 8480eb
	int cancel_state;
Packit 8480eb
	unsigned int use_lock = options & SPAWN_OPT_LOCK;
Packit 8480eb
	unsigned int use_open = options & SPAWN_OPT_OPEN;
Packit 8480eb
	sigset_t allsigs, tmpsig, oldsig;
Packit 8480eb
	struct thread_stdenv_vars *tsv;
Packit 8480eb
	pid_t euid = 0;
Packit 8480eb
	gid_t egid = 0;
Packit 8480eb
Packit 8480eb
	if (open_pipe(pipefd))
Packit 8480eb
		return -1;
Packit 8480eb
Packit 8480eb
	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state);
Packit 8480eb
Packit 8480eb
	sigfillset(&allsigs);
Packit 8480eb
	pthread_sigmask(SIG_BLOCK, &allsigs, &oldsig);
Packit 8480eb
Packit 8480eb
	if (use_lock) {
Packit 8480eb
		status = pthread_mutex_lock(&spawn_mutex);
Packit 8480eb
		if (status)
Packit 8480eb
			fatal(status);
Packit 8480eb
	}
Packit 8480eb
Packit 8480eb
	tsv = pthread_getspecific(key_thread_stdenv_vars);
Packit 8480eb
	if (tsv) {
Packit 8480eb
		euid = tsv->uid;
Packit 8480eb
		egid = tsv->gid;
Packit 8480eb
	}
Packit 8480eb
Packit 8480eb
	open_mutex_lock();
Packit 8480eb
	f = fork();
Packit 8480eb
	if (f == 0) {
Packit 8480eb
		char **pargv = (char **) argv;
Packit 8480eb
		int loc = 0;
Packit 8480eb
Packit 8480eb
		reset_signals();
Packit 8480eb
		close(pipefd[0]);
Packit 8480eb
		dup2(pipefd[1], STDOUT_FILENO);
Packit 8480eb
		dup2(pipefd[1], STDERR_FILENO);
Packit 8480eb
		close(pipefd[1]);
Packit 4e4825
		open_mutex_unlock();
Packit 8480eb
Packit 8480eb
		/* what to mount must always be second last */
Packit 8480eb
		while (*pargv++)
Packit 8480eb
			loc++;
Packit 8480eb
		if (loc <= 3)
Packit 8480eb
			goto done;
Packit 8480eb
		loc -= 2;
Packit 8480eb
Packit 8480eb
		/*
Packit 8480eb
		 * If the mount location starts with a "/" then it is
Packit 8480eb
		 * a local path. In this case it is a bind mount, a
Packit 8480eb
		 * loopback mount or a file system that uses a local
Packit 8480eb
		 * path so we need to check for dependent mounts.
Packit 8480eb
		 *
Packit 8480eb
		 * I hope host names are never allowed "/" as first char
Packit 8480eb
		 */
Packit 8480eb
		if (use_open && *(argv[loc]) == '/') {
Packit 8480eb
			char **p;
Packit 8480eb
			int is_bind, fd;
Packit 8480eb
Packit 8480eb
			pid_t pgrp = getpgrp();
Packit 8480eb
Packit 8480eb
			/*
Packit 8480eb
			 * Pretend to be requesting user and set non-autofs
Packit 8480eb
			 * program group to trigger mount
Packit 8480eb
			 */
Packit 8480eb
			if (euid) {
Packit 8480eb
				if (!tsv->user)
Packit 8480eb
					fprintf(stderr,
Packit 8480eb
						"warning: can't init groups\n");
Packit 8480eb
				else if (initgroups(tsv->user, egid) == -1)
Packit 8480eb
					fprintf(stderr,
Packit 8480eb
						"warning: initgroups: %s\n",
Packit 8480eb
						strerror(errno));
Packit 8480eb
Packit 8480eb
				if (setegid(egid) == -1)
Packit 8480eb
					fprintf(stderr,
Packit 8480eb
						"warning: setegid: %s\n",
Packit 8480eb
						strerror(errno));
Packit 8480eb
				if (seteuid(euid) == -1)
Packit 8480eb
					fprintf(stderr,
Packit 8480eb
						"warning: seteuid: %s\n",
Packit 8480eb
						strerror(errno));
Packit 8480eb
			}
Packit 8480eb
			setpgrp();
Packit 8480eb
Packit 8480eb
			/*
Packit 8480eb
			 * Trigger the recursive mount.
Packit 8480eb
			 *
Packit 8480eb
			 * Ignore the open(2) return code as there may be
Packit 8480eb
			 * multiple waiters for this mount and we need to
Packit 8480eb
			 * let the VFS handle returns to each individual
Packit 8480eb
			 * waiter.
Packit 8480eb
			 */
Packit 8480eb
			fd = open(argv[loc], O_DIRECTORY);
Packit 8480eb
			if (fd != -1)
Packit 8480eb
				close(fd);
Packit 8480eb
Packit 8480eb
			if (seteuid(0) == -1)
Packit 8480eb
				fprintf(stderr,
Packit 8480eb
					"warning: seteuid: %s\n",
Packit 8480eb
					strerror(errno));
Packit 8480eb
			if (setegid(0) == -1)
Packit 8480eb
				fprintf(stderr,
Packit 8480eb
					"warning: setegid: %s\n",
Packit 8480eb
					strerror(errno));
Packit 8480eb
			if (pgrp >= 0)
Packit 8480eb
				setpgid(0, pgrp);
Packit 8480eb
Packit 8480eb
			/*
Packit 8480eb
			 * The kernel leaves mount type autofs alone because
Packit 8480eb
			 * they are supposed to be autofs sub-mounts and they
Packit 8480eb
			 * look after their own expiration. So mounts bound
Packit 8480eb
			 * to an autofs submount won't ever be expired.
Packit 8480eb
			 */
Packit 8480eb
			is_bind = 0;
Packit 8480eb
			p = (char **) argv;
Packit 8480eb
			while (*p) {
Packit 8480eb
				if (strcmp(*p, "--bind")) {
Packit 8480eb
					p++;
Packit 8480eb
					continue;
Packit 8480eb
				}
Packit 8480eb
				is_bind = 1;
Packit 8480eb
				break;
Packit 8480eb
			}
Packit 8480eb
			if (!is_bind)
Packit 8480eb
				goto done;
Packit 8480eb
Packit 8480eb
			if (is_mounted(_PROC_MOUNTS, argv[loc], MNTS_AUTOFS)) {
Packit 8480eb
				fprintf(stderr,
Packit 8480eb
				     "error: can't bind to an autofs mount\n");
Packit 8480eb
				close(STDOUT_FILENO);
Packit 8480eb
				close(STDERR_FILENO);
Packit 8480eb
				 _exit(EINVAL);
Packit 8480eb
			}
Packit 8480eb
		}
Packit 8480eb
done:
Packit 8480eb
		execv(prog, (char *const *) argv);
Packit 8480eb
		_exit(255);	/* execv() failed */
Packit 8480eb
	} else {
Packit 8480eb
		tmpsig = oldsig;
Packit 8480eb
Packit 8480eb
		sigaddset(&tmpsig, SIGCHLD);
Packit 8480eb
		pthread_sigmask(SIG_SETMASK, &tmpsig, NULL);
Packit 8480eb
		open_mutex_unlock();
Packit 8480eb
Packit 8480eb
		close(pipefd[1]);
Packit 8480eb
Packit 8480eb
		if (f < 0) {
Packit 8480eb
			close(pipefd[0]);
Packit 8480eb
			if (use_lock) {
Packit 8480eb
				status = pthread_mutex_unlock(&spawn_mutex);
Packit 8480eb
				if (status)
Packit 8480eb
					fatal(status);
Packit 8480eb
			}
Packit 8480eb
			pthread_sigmask(SIG_SETMASK, &oldsig, NULL);
Packit 8480eb
			pthread_setcancelstate(cancel_state, NULL);
Packit 8480eb
			return -1;
Packit 8480eb
		}
Packit 8480eb
Packit 8480eb
		errp = 0;
Packit 8480eb
		do {
Packit 8480eb
			errn = timed_read(pipefd[0],
Packit 8480eb
					  errbuf + errp, ERRBUFSIZ - errp, wait);
Packit 8480eb
			if (errn > 0) {
Packit 8480eb
				errp += errn;
Packit 8480eb
Packit 8480eb
				sp = errbuf;
Packit 8480eb
				while (errp && (p = memchr(sp, '\n', errp))) {
Packit 8480eb
					*p++ = '\0';
Packit 8480eb
					if (sp[0])	/* Don't output empty lines */
Packit 8480eb
						warn(logopt, ">> %s", sp);
Packit 8480eb
					errp -= (p - sp);
Packit 8480eb
					sp = p;
Packit 8480eb
				}
Packit 8480eb
Packit 8480eb
				if (errp && sp != errbuf)
Packit 8480eb
					memmove(errbuf, sp, errp);
Packit 8480eb
Packit 8480eb
				if (errp >= ERRBUFSIZ) {
Packit 8480eb
					/* Line too long, split */
Packit 8480eb
					errbuf[errp] = '\0';
Packit 8480eb
					warn(logopt, ">> %s", errbuf);
Packit 8480eb
					errp = 0;
Packit 8480eb
				}
Packit 8480eb
			}
Packit 8480eb
		} while (errn > 0);
Packit 8480eb
Packit 8480eb
		if (errn == -ETIMEDOUT)
Packit 8480eb
			kill(f, SIGTERM);
Packit 8480eb
Packit 8480eb
		close(pipefd[0]);
Packit 8480eb
Packit 8480eb
		if (errp > 0) {
Packit 8480eb
			/* End of file without \n */
Packit 8480eb
			errbuf[errp] = '\0';
Packit 8480eb
			warn(logopt, ">> %s", errbuf);
Packit 8480eb
		}
Packit 8480eb
Packit 8480eb
		if (waitpid(f, &ret, 0) != f)
Packit 8480eb
			ret = -1;	/* waitpid() failed */
Packit 8480eb
Packit 8480eb
		if (use_lock) {
Packit 8480eb
			status = pthread_mutex_unlock(&spawn_mutex);
Packit 8480eb
			if (status)
Packit 8480eb
				fatal(status);
Packit 8480eb
		}
Packit 8480eb
		pthread_sigmask(SIG_SETMASK, &oldsig, NULL);
Packit 8480eb
		pthread_setcancelstate(cancel_state, NULL);
Packit 8480eb
Packit 8480eb
		return ret;
Packit 8480eb
	}
Packit 8480eb
}
Packit 8480eb
Packit 8480eb
int spawnv(unsigned logopt, const char *prog, const char *const *argv)
Packit 8480eb
{
Packit 8480eb
	return do_spawn(logopt, -1, SPAWN_OPT_NONE, prog, argv);
Packit 8480eb
}
Packit 8480eb
Packit 8480eb
int spawnl(unsigned logopt, const char *prog, ...)
Packit 8480eb
{
Packit 8480eb
	va_list arg;
Packit 8480eb
	int argc;
Packit 8480eb
	char **argv, **p;
Packit 8480eb
Packit 8480eb
	va_start(arg, prog);
Packit 8480eb
	for (argc = 1; va_arg(arg, char *); argc++);
Packit 8480eb
	va_end(arg);
Packit 8480eb
Packit 8480eb
	if (!(argv = alloca(sizeof(char *) * argc)))
Packit 8480eb
		return -1;
Packit 8480eb
Packit 8480eb
	va_start(arg, prog);
Packit 8480eb
	p = argv;
Packit 8480eb
	while ((*p++ = va_arg(arg, char *)));
Packit 8480eb
	va_end(arg);
Packit 8480eb
Packit 8480eb
	return do_spawn(logopt, -1, SPAWN_OPT_NONE, prog, (const char **) argv);
Packit 8480eb
}
Packit 8480eb
Packit 8480eb
int spawn_mount(unsigned logopt, ...)
Packit 8480eb
{
Packit 8480eb
	va_list arg;
Packit 8480eb
	int argc;
Packit 8480eb
	char **argv, **p;
Packit 8480eb
	char prog[] = PATH_MOUNT;
Packit 8480eb
	char arg0[] = PATH_MOUNT;
Packit 8480eb
	char argn[] = "-n";
Packit 8480eb
	/* In case we need to use the fake option to mount */
Packit 8480eb
	char arg_fake[] = "-f";
Packit 8480eb
	unsigned int options;
Packit 8480eb
	unsigned int retries = MTAB_LOCK_RETRIES;
Packit 8480eb
	int update_mtab = 1, ret, printed = 0;
Packit 8480eb
	unsigned int wait = defaults_get_mount_wait();
Packit 8480eb
	char buf[PATH_MAX + 1];
Packit 8480eb
Packit 8480eb
	/* If we use mount locking we can't validate the location */
Packit 8480eb
#ifdef ENABLE_MOUNT_LOCKING
Packit 8480eb
	options = SPAWN_OPT_LOCK;
Packit 8480eb
#else
Packit 8480eb
	options = SPAWN_OPT_OPEN;
Packit 8480eb
#endif
Packit 8480eb
Packit 8480eb
	va_start(arg, logopt);
Packit 8480eb
	for (argc = 1; va_arg(arg, char *); argc++);
Packit 8480eb
	va_end(arg);
Packit 8480eb
Packit 8480eb
	ret = readlink(_PATH_MOUNTED, buf, PATH_MAX);
Packit 8480eb
	if (ret != -1) {
Packit 8480eb
		buf[ret] = '\0';
Packit 8480eb
		if (!strcmp(buf, _PROC_MOUNTS) ||
Packit 8480eb
		    !strcmp(buf, _PROC_SELF_MOUNTS)) {
Packit 8480eb
			debug(logopt,
Packit 8480eb
			      "mtab link detected, passing -n to mount");
Packit 8480eb
			argc++;
Packit 8480eb
			update_mtab = 0;
Packit 8480eb
		}
Packit 8480eb
	}
Packit 8480eb
Packit 8480eb
	/* Alloc 1 extra slot in case we need to use the "-f" option */
Packit 8480eb
	if (!(argv = alloca(sizeof(char *) * (argc + 2))))
Packit 8480eb
		return -1;
Packit 8480eb
Packit 8480eb
	argv[0] = arg0;
Packit 8480eb
Packit 8480eb
	va_start(arg, logopt);
Packit 8480eb
	if (update_mtab)
Packit 8480eb
		p = argv + 1;
Packit 8480eb
	else {
Packit 8480eb
		argv[1] = argn;
Packit 8480eb
		p = argv + 2;
Packit 8480eb
	}
Packit 8480eb
	while ((*p = va_arg(arg, char *))) {
Packit 8480eb
		if (options == SPAWN_OPT_OPEN && !strcmp(*p, "-t")) {
Packit 8480eb
			*(++p) = va_arg(arg, char *);
Packit 8480eb
			if (!*p)
Packit 8480eb
				break;
Packit 8480eb
			/*
Packit 8480eb
			 * A cifs mount location begins with a "/" but
Packit 8480eb
			 * is not a local path, so don't try to resolve
Packit 8480eb
			 * it. Mmmm ... does anyone use smbfs these days?
Packit 8480eb
			 */
Packit 8480eb
			if (strstr(*p, "cifs"))
Packit 8480eb
				options = SPAWN_OPT_NONE;
Packit 8480eb
		}
Packit 8480eb
		p++;
Packit 8480eb
	}
Packit 8480eb
	va_end(arg);
Packit 8480eb
Packit 8480eb
	while (retries--) {
Packit 8480eb
		ret = do_spawn(logopt, wait, options, prog, (const char **) argv);
Packit 8480eb
		if (ret == MTAB_NOTUPDATED) {
Packit 8480eb
			struct timespec tm = {3, 0};
Packit 8480eb
Packit 8480eb
			/*
Packit 8480eb
			 * If the mount succeeded but the mtab was not
Packit 8480eb
			 * updated, then retry the mount with the -f (fake)
Packit 8480eb
			 * option to just update the mtab.
Packit 8480eb
			 */
Packit 8480eb
			if (!printed) {
Packit 8480eb
				debug(logopt, "mount failed with error code 16"
Packit 8480eb
				      ", retrying with the -f option");
Packit 8480eb
				printed = 1;
Packit 8480eb
			}
Packit 8480eb
Packit 8480eb
			/*
Packit 8480eb
			 * Move the last two args so do_spawn() can find the
Packit 8480eb
			 * mount target.
Packit 8480eb
			 */
Packit 8480eb
			if (!argv[argc]) {
Packit 8480eb
				argv[argc + 1] = NULL;
Packit 8480eb
				argv[argc] = argv[argc - 1];
Packit 8480eb
				argv[argc - 1] = argv[argc - 2];
Packit 8480eb
				argv[argc - 2] = arg_fake;
Packit 8480eb
			}
Packit 8480eb
Packit 8480eb
			nanosleep(&tm, NULL);
Packit 8480eb
Packit 8480eb
			continue;
Packit 8480eb
		}
Packit 8480eb
		break;
Packit 8480eb
	}
Packit 8480eb
Packit 8480eb
	/* This is not a fatal error */
Packit 8480eb
	if (ret == MTAB_NOTUPDATED) {
Packit 8480eb
		/*
Packit 8480eb
		 * Version 5 requires that /etc/mtab be in sync with
Packit 8480eb
		 * /proc/mounts. If we're unable to update matb after
Packit 8480eb
		 * retrying then we have no choice but umount the mount
Packit 8480eb
		 * and return a fail.
Packit 8480eb
		 */
Packit 8480eb
		warn(logopt,
Packit 8480eb
		     "Unable to update the mtab file, forcing mount fail!");
Packit 8480eb
		umount(argv[argc]);
Packit 8480eb
		ret = MNT_FORCE_FAIL;
Packit 8480eb
	}
Packit 8480eb
Packit 8480eb
	return ret;
Packit 8480eb
}
Packit 8480eb
Packit 8480eb
/*
Packit 8480eb
 * For bind mounts that depend on the target being mounted (possibly
Packit 8480eb
 * itself an automount) we attempt to mount the target using an open(2)
Packit 8480eb
 * call. For this to work the location must be the second last arg.
Packit 8480eb
 *
Packit 8480eb
 * NOTE: If mount locking is enabled this type of recursive mount cannot
Packit 8480eb
 *	 work.
Packit 8480eb
 */
Packit 8480eb
int spawn_bind_mount(unsigned logopt, ...)
Packit 8480eb
{
Packit 8480eb
	va_list arg;
Packit 8480eb
	int argc;
Packit 8480eb
	char **argv, **p;
Packit 8480eb
	char prog[] = PATH_MOUNT;
Packit 8480eb
	char arg0[] = PATH_MOUNT;
Packit 8480eb
	char bind[] = "--bind";
Packit 8480eb
	char argn[] = "-n";
Packit 8480eb
	/* In case we need to use the fake option to mount */
Packit 8480eb
	char arg_fake[] = "-f";
Packit 8480eb
	unsigned int options;
Packit 8480eb
	unsigned int retries = MTAB_LOCK_RETRIES;
Packit 8480eb
	int update_mtab = 1, ret, printed = 0;
Packit 8480eb
	unsigned int wait = defaults_get_mount_wait();
Packit 8480eb
	char buf[PATH_MAX + 1];
Packit 8480eb
Packit 8480eb
	/* If we use mount locking we can't validate the location */
Packit 8480eb
#ifdef ENABLE_MOUNT_LOCKING
Packit 8480eb
	options = SPAWN_OPT_LOCK;
Packit 8480eb
#else
Packit 8480eb
	options = SPAWN_OPT_OPEN;
Packit 8480eb
#endif
Packit 8480eb
Packit 8480eb
	/*
Packit 8480eb
	 * Alloc 2 extra slots, one for the bind option and one in case
Packit 8480eb
	 * we need to use the "-f" option
Packit 8480eb
	 */
Packit 8480eb
	va_start(arg, logopt);
Packit 8480eb
	for (argc = 2; va_arg(arg, char *); argc++);
Packit 8480eb
	va_end(arg);
Packit 8480eb
Packit 8480eb
	ret = readlink(_PATH_MOUNTED, buf, PATH_MAX);
Packit 8480eb
	if (ret != -1) {
Packit 8480eb
		buf[ret] = '\0';
Packit 8480eb
		if (!strcmp(buf, _PROC_MOUNTS) ||
Packit 8480eb
		    !strcmp(buf, _PROC_SELF_MOUNTS)) {
Packit 8480eb
			debug(logopt,
Packit 8480eb
			      "mtab link detected, passing -n to mount");
Packit 8480eb
			argc++;
Packit 8480eb
			update_mtab = 0;
Packit 8480eb
		}
Packit 8480eb
	}
Packit 8480eb
Packit 8480eb
	if (!(argv = alloca(sizeof(char *) * (argc + 2))))
Packit 8480eb
		return -1;
Packit 8480eb
Packit 8480eb
	argv[0] = arg0;
Packit 8480eb
	argv[1] = bind;
Packit 8480eb
Packit 8480eb
	va_start(arg, logopt);
Packit 8480eb
	if (update_mtab)
Packit 8480eb
		p = argv + 2;
Packit 8480eb
	else {
Packit 8480eb
		argv[2] = argn;
Packit 8480eb
		p = argv + 3;
Packit 8480eb
	}
Packit 8480eb
	while ((*p++ = va_arg(arg, char *)));
Packit 8480eb
	va_end(arg);
Packit 8480eb
Packit 8480eb
	while (retries--) {
Packit 8480eb
		ret = do_spawn(logopt, wait, options, prog, (const char **) argv);
Packit 8480eb
		if (ret == MTAB_NOTUPDATED) {
Packit 8480eb
			struct timespec tm = {3, 0};
Packit 8480eb
Packit 8480eb
			/*
Packit 8480eb
			 * If the mount succeeded but the mtab was not
Packit 8480eb
			 * updated, then retry the mount with the -f (fake)
Packit 8480eb
			 * option to just update the mtab.
Packit 8480eb
			 */
Packit 8480eb
			if (!printed) {
Packit 8480eb
				debug(logopt, "mount failed with error code 16"
Packit 8480eb
				      ", retrying with the -f option");
Packit 8480eb
				printed = 1;
Packit 8480eb
			}
Packit 8480eb
Packit 8480eb
			/*
Packit 8480eb
			 * Move the last two args so do_spawn() can find the
Packit 8480eb
			 * mount target.
Packit 8480eb
			 */
Packit 8480eb
			if (!argv[argc]) {
Packit 8480eb
				argv[argc + 1] = NULL;
Packit 8480eb
				argv[argc] = argv[argc - 1];
Packit 8480eb
				argv[argc - 1] = argv[argc - 2];
Packit 8480eb
				argv[argc - 2] = arg_fake;
Packit 8480eb
			}
Packit 8480eb
Packit 8480eb
			nanosleep(&tm, NULL);
Packit 8480eb
Packit 8480eb
			continue;
Packit 8480eb
		}
Packit 8480eb
		break;
Packit 8480eb
	}
Packit 8480eb
Packit 8480eb
	/* This is not a fatal error */
Packit 8480eb
	if (ret == MTAB_NOTUPDATED) {
Packit 8480eb
		/*
Packit 8480eb
		 * Version 5 requires that /etc/mtab be in sync with
Packit 8480eb
		 * /proc/mounts. If we're unable to update matb after
Packit 8480eb
		 * retrying then we have no choice but umount the mount
Packit 8480eb
		 * and return a fail.
Packit 8480eb
		 */
Packit 8480eb
		warn(logopt,
Packit 8480eb
		     "Unable to update the mtab file, forcing mount fail!");
Packit 8480eb
		umount(argv[argc]);
Packit 8480eb
		ret = MNT_FORCE_FAIL;
Packit 8480eb
	}
Packit 8480eb
Packit 8480eb
	return ret;
Packit 8480eb
}
Packit 8480eb
Packit 8480eb
int spawn_umount(unsigned logopt, ...)
Packit 8480eb
{
Packit 8480eb
	va_list arg;
Packit 8480eb
	int argc;
Packit 8480eb
	char **argv, **p;
Packit 8480eb
	char prog[] = PATH_UMOUNT;
Packit 8480eb
	char arg0[] = PATH_UMOUNT;
Packit 8480eb
#ifdef HAVE_NO_CANON_UMOUNT
Packit 8480eb
	char * const arg_c = "-c";
Packit 8480eb
#else
Packit 8480eb
	char * const arg_c = NULL;
Packit 8480eb
#endif
Packit 8480eb
	char argn[] = "-n";
Packit 8480eb
	unsigned int options;
Packit 8480eb
	unsigned int retries = MTAB_LOCK_RETRIES;
Packit 8480eb
	int update_mtab = 1, ret, printed = 0;
Packit 8480eb
	unsigned int wait = defaults_get_umount_wait();
Packit 8480eb
	char buf[PATH_MAX + 1];
Packit 8480eb
Packit 8480eb
#ifdef ENABLE_MOUNT_LOCKING
Packit 8480eb
	options = SPAWN_OPT_LOCK;
Packit 8480eb
#else
Packit 8480eb
	options = SPAWN_OPT_NONE;
Packit 8480eb
#endif
Packit 8480eb
Packit 8480eb
	va_start(arg, logopt);
Packit 8480eb
	for (argc = 1; va_arg(arg, char *); argc++);
Packit 8480eb
	va_end(arg);
Packit 8480eb
Packit 8480eb
	ret = readlink(_PATH_MOUNTED, buf, PATH_MAX);
Packit 8480eb
	if (ret != -1) {
Packit 8480eb
		buf[ret] = '\0';
Packit 8480eb
		if (!strcmp(buf, _PROC_MOUNTS) ||
Packit 8480eb
		    !strcmp(buf, _PROC_SELF_MOUNTS)) {
Packit 8480eb
			debug(logopt,
Packit 8480eb
			      "mtab link detected, passing -n to mount");
Packit 8480eb
			argc++;
Packit 8480eb
			update_mtab = 0;
Packit 8480eb
		}
Packit 8480eb
	}
Packit 8480eb
	if (arg_c)
Packit 8480eb
		argc++;;
Packit 8480eb
Packit 8480eb
	if (!(argv = alloca(sizeof(char *) * (argc + 1))))
Packit 8480eb
		return -1;
Packit 8480eb
Packit 8480eb
	p = argv;
Packit 8480eb
	*p++ = arg0;
Packit 8480eb
	if (arg_c)
Packit 8480eb
		*p++ = arg_c;
Packit 8480eb
Packit 8480eb
	if (!update_mtab)
Packit 8480eb
		*p++ = argn;
Packit 8480eb
Packit 8480eb
	va_start(arg, logopt);
Packit 8480eb
	while ((*p++ = va_arg(arg, char *)));
Packit 8480eb
	va_end(arg);
Packit 8480eb
Packit 8480eb
	while (retries--) {
Packit 8480eb
		ret = do_spawn(logopt, wait, options, prog, (const char **) argv);
Packit 8480eb
		if (ret == MTAB_NOTUPDATED) {
Packit 8480eb
			/*
Packit 8480eb
			 * If the mount succeeded but the mtab was not
Packit 8480eb
			 * updated, then retry the umount just to update
Packit 8480eb
			 * the mtab.
Packit 8480eb
			 */
Packit 8480eb
			if (!printed) {
Packit 8480eb
				debug(logopt, "umount failed with error code 16"
Packit 8480eb
				      ", retrying with the -f option");
Packit 8480eb
				printed = 1;
Packit 8480eb
			}
Packit 8480eb
		} else {
Packit 8480eb
			/*
Packit 8480eb
			 * umount does not support the "fake" option.  Thus,
Packit 8480eb
			 * if we got a return value of MTAB_NOTUPDATED the
Packit 8480eb
			 * first time, that means the umount actually
Packit 8480eb
			 * succeeded.  Then, a following umount will fail
Packit 8480eb
			 * due to the fact that nothing was mounted on the
Packit 8480eb
			 * mount point. So, report this as success.
Packit 8480eb
			 */
Packit 8480eb
			if (retries < MTAB_LOCK_RETRIES - 1)
Packit 8480eb
				ret = 0;
Packit 8480eb
			break;
Packit 8480eb
		}
Packit 8480eb
	}
Packit 8480eb
Packit 8480eb
	/* This is not a fatal error */
Packit 8480eb
	if (ret == MTAB_NOTUPDATED) {
Packit 8480eb
		warn(logopt, "Unable to update the mtab file, /proc/mounts "
Packit 8480eb
		     "and /etc/mtab will differ");
Packit 8480eb
		ret = 0;
Packit 8480eb
	}
Packit 8480eb
Packit 8480eb
	return ret;
Packit 8480eb
}
Packit 8480eb