Example #1
0
static void edit_file(const struct passwd *pas, const char *file)
{
	const char *ptr;
	pid_t pid;

	pid = xvfork();
	if (pid) { /* parent */
		wait4pid(pid);
		return;
	}

	/* CHILD - change user and run editor */
	/* initgroups, setgid, setuid */
	change_identity(pas);
	setup_environment(pas->pw_shell,
			SETUP_ENV_CHANGEENV | SETUP_ENV_TO_TMP,
			pas);
	ptr = getenv("VISUAL");
	if (!ptr) {
		ptr = getenv("EDITOR");
		if (!ptr)
			ptr = "vi";
	}

	BB_EXECLP(ptr, ptr, file, NULL);
	bb_perror_msg_and_die("can't execute '%s'", ptr);
}
Example #2
0
void FAST_FUNC launch_helper(const char **argv)
{
	// setup vanilla unidirectional pipes interchange
	int i;
	int pipes[4];

	xpipe(pipes);
	xpipe(pipes + 2);

	// NB: handler must be installed before vfork
	bb_signals(0
		+ (1 << SIGCHLD)
		+ (1 << SIGALRM)
		, signal_handler);

	G.helper_pid = xvfork();

	i = (!G.helper_pid) * 2; // for parent:0, for child:2
	close(pipes[i + 1]); // 1 or 3 - closing one write end
	close(pipes[2 - i]); // 2 or 0 - closing one read end
	xmove_fd(pipes[i], STDIN_FILENO); // 0 or 2 - using other read end
	xmove_fd(pipes[3 - i], STDOUT_FILENO); // 3 or 1 - other write end

	if (!G.helper_pid) {
		// child: try to execute connection helper
		// NB: SIGCHLD & SIGALRM revert to SIG_DFL on exec
		BB_EXECVP_or_die((char**)argv);
	}

	// parent
	// check whether child is alive
	//redundant:signal_handler(SIGCHLD);
	// child seems OK -> parent goes on
	atexit(kill_helper);
}
pid_t FAST_FUNC fork_or_rexec(char **argv)
{
	pid_t pid;
	/* Maybe we are already re-execed and come here again? */
	if (re_execed)
		return 0;
	pid = xvfork();
	if (pid) /* parent */
		return pid;
	/* child - re-exec ourself */
	re_exec(argv);
}
Example #4
0
void FAST_FUNC open_transformer(int fd, const char *transform_prog)
#endif
{
	struct fd_pair fd_pipe;
	int pid;

	xpiped_pair(fd_pipe);
	pid = BB_MMU ? xfork() : xvfork();
	if (pid == 0) {
		/* Child */
		close(fd_pipe.rd); /* we don't want to read from the parent */
		// FIXME: error check?
#if BB_MMU
		{
			transformer_aux_data_t aux;
			init_transformer_aux_data(&aux);
			aux.check_signature = check_signature;
			transformer(&aux, fd, fd_pipe.wr);
			if (ENABLE_FEATURE_CLEAN_UP) {
				close(fd_pipe.wr); /* send EOF */
				close(fd);
			}
			/* must be _exit! bug was actually seen here */
			_exit(EXIT_SUCCESS);
		}
#else
		{
			char *argv[4];
			xmove_fd(fd, 0);
			xmove_fd(fd_pipe.wr, 1);
			argv[0] = (char*)transform_prog;
			argv[1] = (char*)"-cf";
			argv[2] = (char*)"-";
			argv[3] = NULL;
			BB_EXECVP(transform_prog, argv);
			bb_perror_msg_and_die("can't execute '%s'", transform_prog);
		}
#endif
		/* notreached */
	}

	/* parent process */
	close(fd_pipe.wr); /* don't want to write to the child */
	xmove_fd(fd_pipe.rd, fd);
}
Example #5
0
static int open_as_user(const struct passwd *pas, const char *file)
{
	pid_t pid;
	char c;

	pid = xvfork();
	if (pid) { /* PARENT */
		if (wait4pid(pid) == 0) {
			/* exitcode 0: child says it can read */
			return open(file, O_RDONLY);
		}
		return -1;
	}

	/* CHILD */
	/* initgroups, setgid, setuid */
	change_identity(pas);
	/* We just try to read one byte. If it works, file is readable
	 * under this user. We signal that by exiting with 0. */
	_exit(safe_read(xopen(file, O_RDONLY), &c, 1) < 0);
}
Example #6
0
void FAST_FUNC launch_helper(const char **argv)
{
	// setup vanilla unidirectional pipes interchange
	int i;
	int pipes[4];

	xpipe(pipes);
	xpipe(pipes + 2);

	// NB: handler must be installed before vfork
	bb_signals(0
		+ (1 << SIGCHLD)
		+ (1 << SIGALRM)
		, signal_handler);

	G.helper_pid = xvfork();

	i = (!G.helper_pid) * 2; // for parent:0, for child:2
	close(pipes[i + 1]);     // 1 or 3 - closing one write end
	close(pipes[2 - i]);     // 2 or 0 - closing one read end
	xmove_fd(pipes[i], STDIN_FILENO);      // 0 or 2 - using other read end
	xmove_fd(pipes[3 - i], STDOUT_FILENO); // 3 or 1 - using other write end
	// End result:
	// parent stdout [3] -> child stdin [2]
	// child stdout [1] -> parent stdin [0]

	if (!G.helper_pid) {
		// child
		// if parent dies, get SIGTERM
		prctl(PR_SET_PDEATHSIG, SIGTERM, 0, 0, 0);
		// try to execute connection helper
		// NB: SIGCHLD & SIGALRM revert to SIG_DFL on exec
		BB_EXECVP_or_die((char**)argv);
	}

	// parent goes on
}
Example #7
0
/* Run command CMD and return statistics on it.
   Put the statistics in *RESP.  */
static void run_command(char *const *cmd, resource_t *resp)
{
	pid_t pid;
	void (*interrupt_signal)(int);
	void (*quit_signal)(int);

	resp->elapsed_ms = monotonic_ms();
	pid = xvfork();
	if (pid == 0) {
		/* Child */
		BB_EXECVP_or_die((char**)cmd);
	}

	/* Have signals kill the child but not self (if possible).  */
//TODO: just block all sigs? and reenable them in the very end in main?
	interrupt_signal = signal(SIGINT, SIG_IGN);
	quit_signal = signal(SIGQUIT, SIG_IGN);

	resuse_end(pid, resp);

	/* Re-enable signals.  */
	signal(SIGINT, interrupt_signal);
	signal(SIGQUIT, quit_signal);
}
Example #8
0
int start_stop_daemon_main(int argc UNUSED_PARAM, char **argv)
{
	unsigned opt;
	char *signame;
	char *startas;
	char *chuid;
#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY
//	char *retry_arg = NULL;
//	int retries = -1;
	char *opt_N;
#endif

	INIT_G();

	opt = GETOPT32(argv, "^"
		"KSbqtma:n:s:u:c:x:p:"
		IF_FEATURE_START_STOP_DAEMON_FANCY("ovN:R:")
			/* -K or -S is required; they are mutually exclusive */
			/* -p is required if -m is given */
			/* -xpun (at least one) is required if -K is given */
			/* -xa (at least one) is required if -S is given */
			/* -q turns off -v */
			"\0"
			"K:S:K--S:S--K:m?p:K?xpun:S?xa"
			IF_FEATURE_START_STOP_DAEMON_FANCY("q-v"),
		LONGOPTS
		&startas, &cmdname, &signame, &userspec, &chuid, &execname, &pidfile
		IF_FEATURE_START_STOP_DAEMON_FANCY(,&opt_N)
		/* We accept and ignore -R <param> / --retry <param> */
		IF_FEATURE_START_STOP_DAEMON_FANCY(,NULL)
	);

	if (opt & OPT_s) {
		signal_nr = get_signum(signame);
		if (signal_nr < 0) bb_show_usage();
	}

	if (!(opt & OPT_a))
		startas = execname;
	if (!execname) /* in case -a is given and -x is not */
		execname = startas;
	if (execname) {
		G.execname_sizeof = strlen(execname) + 1;
		G.execname_cmpbuf = xmalloc(G.execname_sizeof + 1);
	}

//	IF_FEATURE_START_STOP_DAEMON_FANCY(
//		if (retry_arg)
//			retries = xatoi_positive(retry_arg);
//	)
	//argc -= optind;
	argv += optind;

	if (userspec) {
		user_id = bb_strtou(userspec, NULL, 10);
		if (errno)
			user_id = xuname2uid(userspec);
	}
	/* Both start and stop need to know current processes */
	do_procinit();

	if (opt & CTX_STOP) {
		int i = do_stop();
		return (opt & OPT_OKNODO) ? 0 : (i <= 0);
	}

	if (G.found_procs) {
		if (!QUIET)
			printf("%s is already running\n%u\n", execname, (unsigned)G.found_procs->pid);
		return !(opt & OPT_OKNODO);
	}

#ifdef OLDER_VERSION_OF_X
	if (execname)
		xstat(execname, &G.execstat);
#endif

	*--argv = startas;
	if (opt & OPT_BACKGROUND) {
#if BB_MMU
		bb_daemonize(DAEMON_DEVNULL_STDIO + DAEMON_CLOSE_EXTRA_FDS + DAEMON_DOUBLE_FORK);
		/* DAEMON_DEVNULL_STDIO is superfluous -
		 * it's always done by bb_daemonize() */
#else
		/* Daemons usually call bb_daemonize_or_rexec(), but SSD can do
		 * without: SSD is not itself a daemon, it _execs_ a daemon.
		 * The usual NOMMU problem of "child can't run indefinitely,
		 * it must exec" does not bite us: we exec anyway.
		 */
		pid_t pid = xvfork();
		if (pid != 0) {
			/* parent */
			/* why _exit? the child may have changed the stack,
			 * so "return 0" may do bad things */
			_exit(EXIT_SUCCESS);
		}
		/* Child */
		setsid(); /* detach from controlling tty */
		/* Redirect stdio to /dev/null, close extra FDs */
		bb_daemon_helper(DAEMON_DEVNULL_STDIO + DAEMON_CLOSE_EXTRA_FDS);
#endif
	}
	if (opt & OPT_MAKEPID) {
		/* User wants _us_ to make the pidfile */
		write_pidfile(pidfile);
	}
	if (opt & OPT_c) {
		struct bb_uidgid_t ugid;
		parse_chown_usergroup_or_die(&ugid, chuid);
		if (ugid.uid != (uid_t) -1L) {
			struct passwd *pw = xgetpwuid(ugid.uid);
			if (ugid.gid != (gid_t) -1L)
				pw->pw_gid = ugid.gid;
			/* initgroups, setgid, setuid: */
			change_identity(pw);
		} else if (ugid.gid != (gid_t) -1L) {
			xsetgid(ugid.gid);
			setgroups(1, &ugid.gid);
		}
	}
#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY
	if (opt & OPT_NICELEVEL) {
		/* Set process priority */
		int prio = getpriority(PRIO_PROCESS, 0) + xatoi_range(opt_N, INT_MIN/2, INT_MAX/2);
		if (setpriority(PRIO_PROCESS, 0, prio) < 0) {
			bb_perror_msg_and_die("setpriority(%d)", prio);
		}
	}
#endif
	execvp(startas, argv);
	bb_perror_msg_and_die("can't execute '%s'", startas);
}
void FAST_FUNC data_extract_to_command(archive_handle_t *archive_handle)
{
	file_header_t *file_header = archive_handle->file_header;

#if 0 /* do we need this? ENABLE_FEATURE_TAR_SELINUX */
	char *sctx = archive_handle->tar__sctx[PAX_NEXT_FILE];
	if (!sctx)
		sctx = archive_handle->tar__sctx[PAX_GLOBAL];
	if (sctx) { /* setfscreatecon is 4 syscalls, avoid if possible */
		setfscreatecon(sctx);
		free(archive_handle->tar__sctx[PAX_NEXT_FILE]);
		archive_handle->tar__sctx[PAX_NEXT_FILE] = NULL;
	}
#endif

	if ((file_header->mode & S_IFMT) == S_IFREG) {
		pid_t pid;
		int p[2], status;
		char *tar_env[TAR_MAX];

		memset(tar_env, 0, sizeof(tar_env));

		xpipe(p);
		pid = BB_MMU ? xfork() : xvfork();
		if (pid == 0) {
			/* Child */
			/* str2env(tar_env, TAR_FILETYPE, "f"); - parent should do it once */
			oct2env(tar_env, TAR_MODE, file_header->mode);
			str2env(tar_env, TAR_FILENAME, file_header->name);
			str2env(tar_env, TAR_REALNAME, file_header->name);
#if ENABLE_FEATURE_TAR_UNAME_GNAME
			str2env(tar_env, TAR_UNAME, file_header->tar__uname);
			str2env(tar_env, TAR_GNAME, file_header->tar__gname);
#endif
			dec2env(tar_env, TAR_SIZE, file_header->size);
			dec2env(tar_env, TAR_UID, file_header->uid);
			dec2env(tar_env, TAR_GID, file_header->gid);
			close(p[1]);
			xdup2(p[0], STDIN_FILENO);
			signal(SIGPIPE, SIG_DFL);
			execl(archive_handle->tar__to_command_shell,
				archive_handle->tar__to_command_shell,
				"-c",
				archive_handle->tar__to_command,
				NULL);
			bb_perror_msg_and_die("can't execute '%s'", archive_handle->tar__to_command_shell);
		}
		close(p[0]);
		/* Our caller is expected to do signal(SIGPIPE, SIG_IGN)
		 * so that we don't die if child don't read all the input: */
		bb_copyfd_exact_size(archive_handle->src_fd, p[1], -file_header->size);
		close(p[1]);

		if (safe_waitpid(pid, &status, 0) == -1)
			bb_perror_msg_and_die("waitpid");
		if (WIFEXITED(status) && WEXITSTATUS(status))
			bb_error_msg_and_die("'%s' returned status %d",
				archive_handle->tar__to_command, WEXITSTATUS(status));
		if (WIFSIGNALED(status))
			bb_error_msg_and_die("'%s' terminated on signal %d",
				archive_handle->tar__to_command, WTERMSIG(status));

		if (!BB_MMU) {
			int i;
			for (i = 0; i < TAR_MAX; i++) {
				if (tar_env[i])
					bb_unsetenv_and_free(tar_env[i]);
			}
		}
	}

#if 0 /* ENABLE_FEATURE_TAR_SELINUX */
	if (sctx)
		/* reset the context after creating an entry */
		setfscreatecon(NULL);
#endif
}
Example #10
0
#include "unarchive.h"

/* transformer(), more than meets the eye */
/*
 * On MMU machine, the transform_prog is removed by macro magic
 * in include/unarchive.h. On NOMMU, transformer is removed.
 */
void FAST_FUNC open_transformer(int fd,
	IF_DESKTOP(long long) int FAST_FUNC (*transformer)(int src_fd, int dst_fd),
	const char *transform_prog)
{
	struct fd_pair fd_pipe;
	int pid;

	xpiped_pair(fd_pipe);
	pid = BB_MMU ? xfork() : xvfork();
	if (pid == 0) {
		/* Child */
		close(fd_pipe.rd); /* we don't want to read from the parent */
		// FIXME: error check?
#if BB_MMU
		transformer(fd, fd_pipe.wr);
		if (ENABLE_FEATURE_CLEAN_UP) {
			close(fd_pipe.wr); /* send EOF */
			close(fd);
		}
		/* must be _exit! bug was actually seen here */
		_exit(EXIT_SUCCESS);
#else
		{
			char *argv[4];
Example #11
0
int script_main(int argc UNUSED_PARAM, char **argv)
{
	int opt;
	int mode;
	int child_pid;
	int attr_ok; /* NB: 0: ok */
	int winsz_ok;
	int pty;
	char pty_line[GETPTY_BUFSIZE];
	struct termios tt, rtt;
	struct winsize win;
	const char *fname = "typescript";
	const char *shell;
	char shell_opt[] = "-i";
	char *shell_arg = NULL;
	enum {
		OPT_a = (1 << 0),
		OPT_c = (1 << 1),
		OPT_f = (1 << 2),
		OPT_q = (1 << 3),
		OPT_t = (1 << 4),
	};

#if ENABLE_LONG_OPTS
	static const char getopt_longopts[] ALIGN1 =
		"append\0"  No_argument       "a"
		"command\0" Required_argument "c"
		"flush\0"   No_argument       "f"
		"quiet\0"   No_argument       "q"
		IF_SCRIPTREPLAY("timing\0" No_argument "t")
		;

	applet_long_options = getopt_longopts;
#endif

	opt_complementary = "?1"; /* max one arg */
	opt = getopt32(argv, "ac:fq" IF_SCRIPTREPLAY("t") , &shell_arg);
	//argc -= optind;
	argv += optind;
	if (argv[0]) {
		fname = argv[0];
	}
	mode = O_CREAT|O_TRUNC|O_WRONLY;
	if (opt & OPT_a) {
		mode = O_CREAT|O_APPEND|O_WRONLY;
	}
	if (opt & OPT_c) {
		shell_opt[1] = 'c';
	}
	if (!(opt & OPT_q)) {
		printf("Script started, file is %s\n", fname);
	}

	shell = get_shell_name();

	/* Some people run "script ... 0>&-".
	 * Our code assumes that STDIN_FILENO != pty.
	 * Ensure STDIN_FILENO is not closed:
	 */
	bb_sanitize_stdio();

	pty = xgetpty(pty_line);

	/* get current stdin's tty params */
	attr_ok = tcgetattr(0, &tt);
	winsz_ok = ioctl(0, TIOCGWINSZ, (char *)&win);

	rtt = tt;
	cfmakeraw(&rtt);
	rtt.c_lflag &= ~ECHO;
	tcsetattr(0, TCSAFLUSH, &rtt);

	/* "script" from util-linux exits when child exits,
	 * we wouldn't wait for EOF from slave pty
	 * (output may be produced by grandchildren of child) */
	signal(SIGCHLD, record_signo);

	/* TODO: SIGWINCH? pass window size changes down to slave? */

	child_pid = xvfork();

	if (child_pid) {
		/* parent */
#define buf bb_common_bufsiz1
		struct pollfd pfd[2];
		int outfd, count, loop;
		double oldtime = ENABLE_SCRIPTREPLAY ? time(NULL) : 0;
		smallint fd_count = 2;

		outfd = xopen(fname, mode);
		pfd[0].fd = pty;
		pfd[0].events = POLLIN;
		pfd[1].fd = STDIN_FILENO;
		pfd[1].events = POLLIN;
		ndelay_on(pty); /* this descriptor is not shared, can do this */
		/* ndelay_on(STDIN_FILENO); - NO, stdin can be shared! Pity :( */

		/* copy stdin to pty master input,
		 * copy pty master output to stdout and file */
		/* TODO: don't use full_write's, use proper write buffering */
		while (fd_count && !bb_got_signal) {
			/* not safe_poll! we want SIGCHLD to EINTR poll */
			if (poll(pfd, fd_count, -1) < 0 && errno != EINTR) {
				/* If child exits too quickly, we may get EIO:
				 * for example, try "script -c true" */
				break;
			}
			if (pfd[0].revents) {
				errno = 0;
				count = safe_read(pty, buf, sizeof(buf));
				if (count <= 0 && errno != EAGAIN) {
					/* err/eof from pty: exit */
					goto restore;
				}
				if (count > 0) {
					if (ENABLE_SCRIPTREPLAY && (opt & OPT_t)) {
						struct timeval tv;
						double newtime;

						gettimeofday(&tv, NULL);
						newtime = tv.tv_sec + (double) tv.tv_usec / 1000000;
						fprintf(stderr, "%f %u\n", newtime - oldtime, count);
						oldtime = newtime;
					}
					full_write(STDOUT_FILENO, buf, count);
					full_write(outfd, buf, count);
					if (opt & OPT_f) {
						fsync(outfd);
					}
				}
			}
			if (pfd[1].revents) {
				count = safe_read(STDIN_FILENO, buf, sizeof(buf));
				if (count <= 0) {
					/* err/eof from stdin: don't read stdin anymore */
					pfd[1].revents = 0;
					fd_count--;
				} else {
					full_write(pty, buf, count);
				}
			}
		}
		/* If loop was exited because SIGCHLD handler set bb_got_signal,
		 * there still can be some buffered output. But dont loop forever:
		 * we won't pump orphaned grandchildren's output indefinitely.
		 * Testcase: running this in script:
		 *      exec dd if=/dev/zero bs=1M count=1
		 * must have "1+0 records in, 1+0 records out" captured too.
		 * (util-linux's script doesn't do this. buggy :) */
		loop = 999;
		/* pty is in O_NONBLOCK mode, we exit as soon as buffer is empty */
		while (--loop && (count = safe_read(pty, buf, sizeof(buf))) > 0) {
			full_write(STDOUT_FILENO, buf, count);
			full_write(outfd, buf, count);
		}
 restore:
		if (attr_ok == 0)
			tcsetattr(0, TCSAFLUSH, &tt);
		if (!(opt & OPT_q))
			printf("Script done, file is %s\n", fname);
		return EXIT_SUCCESS;
	}

	/* child: make pty slave to be input, output, error; run shell */
	close(pty); /* close pty master */
	/* open pty slave to fd 0,1,2 */
	close(0);
	xopen(pty_line, O_RDWR); /* uses fd 0 */
	xdup2(0, 1);
	xdup2(0, 2);
	/* copy our original stdin tty's parameters to pty */
	if (attr_ok == 0)
		tcsetattr(0, TCSAFLUSH, &tt);
	if (winsz_ok == 0)
		ioctl(0, TIOCSWINSZ, (char *)&win);
	/* set pty as a controlling tty */
	setsid();
	ioctl(0, TIOCSCTTY, 0 /* 0: don't forcibly steal */);

	/* Non-ignored signals revert to SIG_DFL on exec anyway */
	/*signal(SIGCHLD, SIG_DFL);*/
	execl(shell, shell, shell_opt, shell_arg, (char *) NULL);
	bb_simple_perror_msg_and_die(shell);
}
Example #12
0
int timeout_main(int argc UNUSED_PARAM, char **argv)
{
	int signo;
	int status;
	int parent = 0;
	int timeout = 10;
	pid_t pid;
#if !BB_MMU
	char *sv1, *sv2;
#endif
	const char *opt_s = "TERM";

	/* -p option is not documented, it is needed to support NOMMU. */

	/* -t SECONDS; -p PARENT_PID */
	opt_complementary = "t+" USE_FOR_NOMMU(":p+");
	/* '+': stop at first non-option */
	getopt32(argv, "+s:t:" USE_FOR_NOMMU("p:"), &opt_s, &timeout, &parent);
	/*argv += optind; - no, wait for bb_daemonize_or_rexec! */
	signo = get_signum(opt_s);
	if (signo < 0)
		bb_error_msg_and_die("unknown signal '%s'", opt_s);

	/* We want to create a grandchild which will watch
	 * and kill the grandparent. Other methods:
	 * making parent watch child disrupts parent<->child link
	 * (example: "tcpsvd 0.0.0.0 1234 timeout service_prog" -
	 * it's better if service_prog is a child of tcpsvd!),
	 * making child watch parent results in programs having
	 * unexpected children. */

	if (parent) /* we were re-execed, already grandchild */
		goto grandchild;
	if (!argv[optind]) /* no PROG? */
		bb_show_usage();

#if !BB_MMU
	sv1 = argv[optind];
	sv2 = argv[optind + 1];
#endif
	pid = xvfork();
	if (pid == 0) {
		/* Child: spawn grandchild and exit */
		parent = getppid();
#if !BB_MMU
		argv[optind] = xasprintf("-p%u", parent);
		argv[optind + 1] = NULL;
#endif
		/* NB: exits with nonzero on error: */
		bb_daemonize_or_rexec(0, argv);
		/* Here we are grandchild. Sleep, then kill grandparent */
 grandchild:
		/* Just sleep(HUGE_NUM); kill(parent) may kill wrong process! */
		while (1) {
			sleep(1);
			if (--timeout <= 0)
				break;
			if (kill(parent, 0)) {
				/* process is gone */
				return EXIT_SUCCESS;
			}
		}
		kill(parent, signo);
		return EXIT_SUCCESS;
	}

	/* Parent */
	wait(&status); /* wait for child to die */
	/* Did intermediate [v]fork or exec fail? */
	if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
		return EXIT_FAILURE;
	/* Ok, exec a program as requested */
	argv += optind;
#if !BB_MMU
	argv[0] = sv1;
	argv[1] = sv2;
#endif
	BB_EXECVP_or_die(argv);
}