144743Smarkm /*
244743Smarkm  * shell_cmd() takes a shell command after %<character> substitutions. The
344743Smarkm  * command is executed by a /bin/sh child process, with standard input,
444743Smarkm  * standard output and standard error connected to /dev/null.
544743Smarkm  *
644743Smarkm  * Diagnostics are reported through syslog(3).
744743Smarkm  *
844743Smarkm  * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
944743Smarkm  */
1044743Smarkm
1144743Smarkm#ifndef lint
1244743Smarkmstatic char sccsid[] = "@(#) shell_cmd.c 1.5 94/12/28 17:42:44";
1344743Smarkm#endif
1444743Smarkm
1544743Smarkm/* System libraries. */
1644743Smarkm
1744743Smarkm#include <sys/types.h>
1844743Smarkm#include <sys/param.h>
1944743Smarkm#include <signal.h>
2044743Smarkm#include <stdio.h>
2144743Smarkm#include <syslog.h>
2244743Smarkm#include <string.h>
2344743Smarkm
2444743Smarkmextern void exit();
2544743Smarkm
2644743Smarkm/* Local stuff. */
2744743Smarkm
2844743Smarkm#include "tcpd.h"
2944743Smarkm
3044743Smarkm/* Forward declarations. */
3144743Smarkm
3244743Smarkmstatic void do_child();
3344743Smarkm
3444743Smarkm/* shell_cmd - execute shell command */
3544743Smarkm
3644743Smarkmvoid    shell_cmd(command)
3744743Smarkmchar   *command;
3844743Smarkm{
3944743Smarkm    int     child_pid;
4044743Smarkm    int     wait_pid;
4144743Smarkm
4244743Smarkm    /*
4344743Smarkm     * Most of the work is done within the child process, to minimize the
4444743Smarkm     * risk of damage to the parent.
4544743Smarkm     */
4644743Smarkm
4744743Smarkm    switch (child_pid = fork()) {
4844743Smarkm    case -1:					/* error */
4944743Smarkm	tcpd_warn("cannot fork: %m");
5044743Smarkm	break;
5144743Smarkm    case 00:					/* child */
5244743Smarkm	do_child(command);
5344743Smarkm	/* NOTREACHED */
5444743Smarkm    default:					/* parent */
5544743Smarkm	while ((wait_pid = wait((int *) 0)) != -1 && wait_pid != child_pid)
5644743Smarkm	     /* void */ ;
5744743Smarkm    }
5844743Smarkm}
5944743Smarkm
6044743Smarkm/* do_child - exec command with { stdin, stdout, stderr } to /dev/null */
6144743Smarkm
6244743Smarkmstatic void do_child(command)
6344743Smarkmchar   *command;
6444743Smarkm{
6544743Smarkm    char   *error;
6644743Smarkm    int     tmp_fd;
6744743Smarkm
6844743Smarkm    /*
6944743Smarkm     * Systems with POSIX sessions may send a SIGHUP to grandchildren if the
7044743Smarkm     * child exits first. This is sick, sessions were invented for terminals.
7144743Smarkm     */
7244743Smarkm
7344743Smarkm    signal(SIGHUP, SIG_IGN);
7444743Smarkm
7544743Smarkm    /* Set up new stdin, stdout, stderr, and exec the shell command. */
7644743Smarkm
7744743Smarkm    for (tmp_fd = 0; tmp_fd < 3; tmp_fd++)
7844743Smarkm	(void) close(tmp_fd);
7944743Smarkm    if (open("/dev/null", 2) != 0) {
8044743Smarkm	error = "open /dev/null: %m";
8144743Smarkm    } else if (dup(0) != 1 || dup(0) != 2) {
8244743Smarkm	error = "dup: %m";
8344743Smarkm    } else {
8444743Smarkm	(void) execl("/bin/sh", "sh", "-c", command, (char *) 0);
8544743Smarkm	error = "execl /bin/sh: %m";
8644743Smarkm    }
8744743Smarkm
8844743Smarkm    /* Something went wrong. We MUST terminate the child process. */
8944743Smarkm
9044743Smarkm    tcpd_warn(error);
9144743Smarkm    _exit(0);
9244743Smarkm}
93