1/*
2 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
3 * Use is subject to license terms.
4 */
5
6#pragma ident	"%Z%%M%	%I%	%E% SMI"
7
8 /*
9  * shell_cmd() takes a shell command after %<character> substitutions. The
10  * command is executed by a /bin/sh child process, with standard input,
11  * standard output and standard error connected to /dev/null.
12  *
13  * Diagnostics are reported through syslog(3).
14  *
15  * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
16  */
17
18#ifndef lint
19static char sccsid[] = "@(#) shell_cmd.c 1.5 94/12/28 17:42:44";
20#endif
21
22/* System libraries. */
23
24#include <sys/types.h>
25#include <sys/param.h>
26#include <signal.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <unistd.h>
30#include <wait.h>
31#include <fcntl.h>
32#include <syslog.h>
33#include <string.h>
34
35extern void exit();
36
37/* Local stuff. */
38
39#include "tcpd.h"
40
41/* Forward declarations. */
42
43static void do_child();
44
45/* shell_cmd - execute shell command */
46
47void    shell_cmd(command)
48char   *command;
49{
50    int     child_pid;
51    int     wait_pid;
52
53    /*
54     * Most of the work is done within the child process, to minimize the
55     * risk of damage to the parent.
56     */
57
58    switch (child_pid = fork()) {
59    case -1:					/* error */
60	tcpd_warn("cannot fork: %m");
61	break;
62    case 00:					/* child */
63	do_child(command);
64	/* NOTREACHED */
65    default:					/* parent */
66	while ((wait_pid = wait((int *) 0)) != -1 && wait_pid != child_pid)
67	     /* void */ ;
68    }
69}
70
71/* do_child - exec command with { stdin, stdout, stderr } to /dev/null */
72
73static void do_child(command)
74char   *command;
75{
76    char   *error;
77    int     tmp_fd;
78
79    /*
80     * Systems with POSIX sessions may send a SIGHUP to grandchildren if the
81     * child exits first. This is sick, sessions were invented for terminals.
82     */
83
84    signal(SIGHUP, SIG_IGN);
85
86    /* Set up new stdin, stdout, stderr, and exec the shell command. */
87
88    for (tmp_fd = 0; tmp_fd < 3; tmp_fd++)
89	(void) close(tmp_fd);
90    if (open("/dev/null", 2) != 0) {
91	error = "open /dev/null: %m";
92    } else if (dup(0) != 1 || dup(0) != 2) {
93	error = "dup: %m";
94    } else {
95	(void) execl("/bin/sh", "sh", "-c", command, (char *) 0);
96	error = "execl /bin/sh: %m";
97    }
98
99    /* Something went wrong. We MUST terminate the child process. */
100
101    tcpd_warn(error);
102    _exit(0);
103}
104