File: shell_cmd.c

package info (click to toggle)
tcp-wrappers 7.6.q-31
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 1,828 kB
  • sloc: ansic: 17,852; makefile: 2,308; sh: 39
file content (140 lines) | stat: -rw-r--r-- 3,561 bytes parent folder | download | duplicates (7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
 /*
  * shell_cmd() takes a shell command after %<character> substitutions. The
  * command is executed by a /bin/sh child process, with standard input,
  * standard output and standard error connected to /dev/null.
  * 
  * Diagnostics are reported through syslog(3).
  * 
  * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
  */

#ifndef lint
static char sccsid[] = "@(#) shell_cmd.c 1.5 94/12/28 17:42:44";
#endif

/* System libraries. */

#include <sys/types.h>
#include <sys/param.h>
#include <signal.h>
#include <stdio.h>
#include <syslog.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>

extern void exit();

/* Local stuff. */

#include "tcpd.h"

/* Forward declarations. */

static void do_child();

/*
 * The sigchld handler. If there is a SIGCHLD caused by a child other than
 * ours, we set a flag and raise the signal later.
 */
volatile static int foreign_sigchld;
volatile static int our_child_pid;
static void sigchld(int sig, siginfo_t *si, void *unused)
{
    if (si && si->si_pid != our_child_pid)
	foreign_sigchld = 1;
}

/* shell_cmd - execute shell command */

void    shell_cmd(command)
char   *command;
{
    int     child_pid;

    struct sigaction new_action, old_action;
    sigset_t new_mask, old_mask, empty_mask;

    new_action.sa_sigaction = &sigchld;
    new_action.sa_flags = SA_SIGINFO;
    sigemptyset(&new_action.sa_mask);
    sigemptyset(&new_mask);
    sigemptyset(&empty_mask);
    sigaddset(&new_mask, SIGCHLD);

    /*
     * Set the variables for handler, set the handler and block the signal
     * until we have the pid.
     */
    foreign_sigchld = 0; our_child_pid = 0;
    sigprocmask(SIG_BLOCK, &new_mask, &old_mask);
    sigaction(SIGCHLD, &new_action, &old_action);

    /*
     * Most of the work is done within the child process, to minimize the
     * risk of damage to the parent.
     */

    switch (child_pid = fork()) {
    case -1:					/* error */
	tcpd_warn("cannot fork: %m");
	break;
    case 00:					/* child */
	/* Clear the blocked mask for the child not to be surprised. */
	sigprocmask(SIG_SETMASK, &empty_mask, 0);
	do_child(command);
	/* NOTREACHED */
    default:					/* parent */
	our_child_pid = child_pid;
	sigprocmask(SIG_UNBLOCK, &new_mask, 0);
	while (waitpid(child_pid, (int *) 0, 0) == -1 && errno == EINTR);
    }

    /*
     * Revert the signal mask and the SIGCHLD handler.
     */
    sigprocmask(SIG_SETMASK, &old_mask, 0);
    sigaction(SIGCHLD, &old_action, 0);

    /* If there was a foreign SIGCHLD, raise it after we have restored the old
     * mask and handler. */
    if (foreign_sigchld)
	raise(SIGCHLD);
}

/* do_child - exec command with { stdin, stdout, stderr } to /dev/null */

static void do_child(command)
char   *command;
{
    char   *error;
    int     tmp_fd;

    /*
     * Systems with POSIX sessions may send a SIGHUP to grandchildren if the
     * child exits first. This is sick, sessions were invented for terminals.
     */

    signal(SIGHUP, SIG_IGN);

    /* Set up new stdin, stdout, stderr, and exec the shell command. */

    for (tmp_fd = 0; tmp_fd < 3; tmp_fd++)
	(void) close(tmp_fd);
    if (open("/dev/null", 2) != 0) {
	error = "open /dev/null: %m";
    } else if (dup(0) != 1 || dup(0) != 2) {
	error = "dup: %m";
    } else {
	(void) execl("/bin/sh", "sh", "-c", command, (char *) 0);
	error = "execl /bin/sh: %m";
    }

    /* Something went wrong. We MUST terminate the child process. */

    tcpd_warn(error);
    _exit(0);
}