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
141
142
143
144
145
146
147
|
/*
* Copyright (C) 2003-2009 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/** \file exec.c Helper functions for spawning new processes. */
#include <dirent.h>
#include "para.h"
#include "close_on_fork.h"
#include "error.h"
#include "string.h"
#include "fd.h"
/**
* Spawn a new process and redirect fd 0, 1, and 2.
*
* \param pid Will hold the pid of the created process upon return.
* \param file Path of the executable to execute.
* \param args The argument array for the command.
* \param fds a Pointer to a value-result array.
*
* \return Standard.
*
* \sa null(4), pipe(2), dup2(2), fork(2), exec(3).
*/
static int para_exec(pid_t *pid, const char *file, char *const *const args, int *fds)
{
int ret, in[2] = {-1, -1}, out[2] = {-1, -1}, err[2] = {-1, -1},
null = -1; /* ;) */
ret = -E_DUP_PIPE;
if (fds[0] > 0 && pipe(in) < 0)
goto err_out;
if (fds[1] > 0 && pipe(out) < 0)
goto err_out;
if (fds[2] > 0 && pipe(err) < 0)
goto err_out;
if (!fds[0] || !fds[1] || !fds[2]) {
ret = para_open("/dev/null", O_RDWR, 42);
if (ret < 0)
goto err_out;
null = ret;
}
ret = fork();
if (ret < 0) {
ret = -ERRNO_TO_PARA_ERROR(errno);
goto err_out;
}
*pid = ret;
if (!(*pid)) { /* child */
if (fds[0] >= 0) {
if (fds[0]) {
close(in[1]);
if (in[0] != STDIN_FILENO)
dup2(in[0], STDIN_FILENO);
} else
dup2(null, STDIN_FILENO);
}
if (fds[1] >= 0) {
if (fds[1]) {
close(out[0]);
if (out[1] != STDOUT_FILENO)
dup2(out[1], STDOUT_FILENO);
} else
dup2(null, STDOUT_FILENO);
}
if (fds[2] >= 0) {
if (fds[2]) {
close(err[0]);
if (err[1] != STDERR_FILENO)
dup2(err[1], STDERR_FILENO);
} else
dup2(null, STDERR_FILENO);
}
if (null >= 0)
close(null);
execvp(file, args);
_exit(EXIT_FAILURE);
}
if (fds[0] > 0) {
close(in[0]);
*fds = in[1];
}
if (fds[1] > 0) {
close(out[1]);
*(fds + 1) = out[0];
}
if (fds[2] > 0) {
close(err[1]);
*(fds + 2) = err[0];
}
if (null >= 0)
close(null);
return 1;
err_out:
if (err[0] >= 0)
close(err[0]);
if (err[1] >= 0)
close(err[1]);
if (out[0] >= 0)
close(out[0]);
if (out[1] >= 0)
close(out[1]);
if (in[0] >= 0)
close(in[0]);
if (in[1] >= 0)
close(in[1]);
if (null >= 0)
close(null);
PARA_ERROR_LOG("%s\n", para_strerror(-ret));
return ret;
}
/**
* Exec the given command.
*
* \param pid Will hold the pid of the created process upon return.
* \param cmdline Holds the command and its arguments, seperated by spaces.
* \param fds A pointer to a value-result array.
*
* This function uses fork/exec to create a new process. \a fds must be a
* pointer to three integers, corresponding to stdin, stdout and stderr
* respectively. It specifies how to deal with fd 0, 1, 2 in the child. The
* contents of \a fds are interpreted as follows:
*
* - fd[i] < 0: leave fd \a i alone.
* - fd[i] = 0: dup fd \a i to \p /dev/null.
* - fd[i] > 0: create a pipe and dup i to one end of that pipe.
* Upon return, fd[i] contains the file descriptor of the pipe.
*
* In any case, all unneeded filedescriptors are closed.
*
* \return Standard.
*/
int para_exec_cmdline_pid(pid_t *pid, const char *cmdline, int *fds)
{
int ret;
char **argv;
char *tmp = para_strdup(cmdline);
split_args(tmp, &argv, " \t");
ret = para_exec(pid, argv[0], argv, fds);
free(argv);
free(tmp);
return ret;
}
|