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>
19257398Ssbruno#include <sys/wait.h>
2044743Smarkm#include <signal.h>
2144743Smarkm#include <stdio.h>
2244743Smarkm#include <syslog.h>
2344743Smarkm#include <string.h>
24257398Ssbruno#include <unistd.h>
25257398Ssbruno#include <fcntl.h>
2644743Smarkm
2744743Smarkmextern void exit();
2844743Smarkm
2944743Smarkm/* Local stuff. */
3044743Smarkm
3144743Smarkm#include "tcpd.h"
3244743Smarkm
3344743Smarkm/* Forward declarations. */
3444743Smarkm
35350345Sbrooksstatic void do_child(char *command);
3644743Smarkm
3744743Smarkm/* shell_cmd - execute shell command */
3844743Smarkm
3944743Smarkmvoid    shell_cmd(command)
4044743Smarkmchar   *command;
4144743Smarkm{
4244743Smarkm    int     child_pid;
4344743Smarkm    int     wait_pid;
4444743Smarkm
4544743Smarkm    /*
4644743Smarkm     * Most of the work is done within the child process, to minimize the
4744743Smarkm     * risk of damage to the parent.
4844743Smarkm     */
4944743Smarkm
5044743Smarkm    switch (child_pid = fork()) {
5144743Smarkm    case -1:					/* error */
5244743Smarkm	tcpd_warn("cannot fork: %m");
5344743Smarkm	break;
5444743Smarkm    case 00:					/* child */
5544743Smarkm	do_child(command);
5644743Smarkm	/* NOTREACHED */
5744743Smarkm    default:					/* parent */
5844743Smarkm	while ((wait_pid = wait((int *) 0)) != -1 && wait_pid != child_pid)
5944743Smarkm	     /* void */ ;
6044743Smarkm    }
6144743Smarkm}
6244743Smarkm
6344743Smarkm/* do_child - exec command with { stdin, stdout, stderr } to /dev/null */
6444743Smarkm
65350345Sbrooksstatic void do_child(char *command)
6644743Smarkm{
6744743Smarkm    char   *error;
6844743Smarkm    int     tmp_fd;
6944743Smarkm
7044743Smarkm    /*
7144743Smarkm     * Systems with POSIX sessions may send a SIGHUP to grandchildren if the
7244743Smarkm     * child exits first. This is sick, sessions were invented for terminals.
7344743Smarkm     */
7444743Smarkm
7544743Smarkm    signal(SIGHUP, SIG_IGN);
7644743Smarkm
7744743Smarkm    /* Set up new stdin, stdout, stderr, and exec the shell command. */
7844743Smarkm
7944743Smarkm    for (tmp_fd = 0; tmp_fd < 3; tmp_fd++)
8044743Smarkm	(void) close(tmp_fd);
8144743Smarkm    if (open("/dev/null", 2) != 0) {
8244743Smarkm	error = "open /dev/null: %m";
8344743Smarkm    } else if (dup(0) != 1 || dup(0) != 2) {
8444743Smarkm	error = "dup: %m";
8544743Smarkm    } else {
8644743Smarkm	(void) execl("/bin/sh", "sh", "-c", command, (char *) 0);
8744743Smarkm	error = "execl /bin/sh: %m";
8844743Smarkm    }
8944743Smarkm
9044743Smarkm    /* Something went wrong. We MUST terminate the child process. */
9144743Smarkm
9244743Smarkm    tcpd_warn(error);
9344743Smarkm    _exit(0);
9444743Smarkm}
95