2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 #include <sys/param.h>
32 #include <sys/socket.h>
54 #include "throughput.h"
60 #include "descriptor.h"
75 struct device dev; /* What struct physical knows about */
76 int fd_out; /* output descriptor */
79 #define device2exec(d) ((d)->type == EXEC_DEVICE ? (struct execdevice *)d : NULL)
84 return sizeof(struct execdevice);
88 exec_Free(struct physical *p)
90 struct execdevice *dev = device2exec(p->handler);
92 if (dev->fd_out != -1)
98 exec_device2iov(struct device *d, struct iovec *iov, int *niov,
99 int maxiov __unused, int *auxfd, int *nauxfd)
101 struct execdevice *dev;
102 int sz = physical_MaxDeviceSize();
104 iov[*niov].iov_base = d = realloc(d, sz);
106 log_Printf(LogALERT, "Failed to allocate memory: %d\n", sz);
107 AbortProgram(EX_OSERR);
109 iov[*niov].iov_len = sz;
112 dev = device2exec(d);
113 if (dev->fd_out >= 0) {
114 *auxfd = dev->fd_out;
120 exec_RemoveFromSet(struct physical *p, fd_set *r, fd_set *w, fd_set *e)
122 struct execdevice *dev = device2exec(p->handler);
125 p->handler->removefromset = NULL;
126 sets = physical_RemoveFromSet(p, r, w, e);
127 p->handler->removefromset = exec_RemoveFromSet;
129 if (dev->fd_out >= 0) {
130 if (w && FD_ISSET(dev->fd_out, w)) {
131 FD_CLR(dev->fd_out, w);
132 log_Printf(LogTIMER, "%s: fdunset(w) %d\n", p->link.name, dev->fd_out);
135 if (e && FD_ISSET(dev->fd_out, e)) {
136 FD_CLR(dev->fd_out, e);
137 log_Printf(LogTIMER, "%s: fdunset(e) %d\n", p->link.name, dev->fd_out);
146 exec_Write(struct physical *p, const void *v, size_t n)
148 struct execdevice *dev = device2exec(p->handler);
149 int fd = dev->fd_out == -1 ? p->fd : dev->fd_out;
151 return write(fd, v, n);
154 static struct device baseexecdevice = {
158 { CD_NOTREQUIRED, 0 },
176 exec_iov2device(int type, struct physical *p, struct iovec *iov,
177 int *niov, int maxiov __unused, int *auxfd, int *nauxfd)
179 if (type == EXEC_DEVICE) {
180 struct execdevice *dev = (struct execdevice *)iov[(*niov)++].iov_base;
182 dev = realloc(dev, sizeof *dev); /* Reduce to the correct size */
184 log_Printf(LogALERT, "Failed to allocate memory: %d\n",
186 AbortProgram(EX_OSERR);
190 dev->fd_out = *auxfd;
195 /* Refresh function pointers etc */
196 memcpy(&dev->dev, &baseexecdevice, sizeof dev->dev);
198 physical_SetupStack(p, dev->dev.name, PHYSICAL_NOFORCE);
206 exec_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
208 struct physical *p = descriptor2physical(d);
209 struct execdevice *dev = device2exec(p->handler);
212 if (w && dev->fd_out >= 0) {
213 FD_SET(dev->fd_out, w);
214 log_Printf(LogTIMER, "%s: fdset(w) %d\n", p->link.name, dev->fd_out);
219 if (e && dev->fd_out >= 0) {
220 FD_SET(dev->fd_out, e);
221 log_Printf(LogTIMER, "%s: fdset(e) %d\n", p->link.name, dev->fd_out);
225 if (result && *n <= dev->fd_out)
226 *n = dev->fd_out + 1;
228 return result + physical_doUpdateSet(d, r, w, e, n, 0);
232 exec_IsSet(struct fdescriptor *d, const fd_set *fdset)
234 struct physical *p = descriptor2physical(d);
235 struct execdevice *dev = device2exec(p->handler);
236 int result = dev->fd_out >= 0 && FD_ISSET(dev->fd_out, fdset);
237 result += physical_IsSet(d, fdset);
243 exec_Create(struct physical *p)
245 struct execdevice *dev;
249 if (*p->name.full == '!') {
252 if ((dev = malloc(sizeof *dev)) == NULL) {
253 log_Printf(LogWARN, "%s: Cannot allocate an exec device: %s\n",
254 p->link.name, strerror(errno));
259 p->fd--; /* We own the device but maybe can't use it - change fd */
260 type = physical_IsSync(p) ? SOCK_DGRAM : SOCK_STREAM;
262 if (socketpair(AF_UNIX, type, PF_UNSPEC, fids) < 0) {
263 log_Printf(LogPHASE, "Unable to create pipe for line exec: %s\n",
268 static int child_status; /* This variable is abused ! */
269 int stat, argc, i, ret, wret, pidpipe[2];
273 stat = fcntl(fids[0], F_GETFL, 0);
276 fcntl(fids[0], F_SETFL, stat);
279 if (pipe(pidpipe) == -1) {
280 log_Printf(LogPHASE, "Unable to pipe for line exec: %s\n",
286 } else switch ((pid = fork())) {
288 log_Printf(LogPHASE, "Unable to fork for line exec: %s\n",
301 setuid(ID0realuid());
305 switch ((pid = vfork())) {
312 log_Printf(LogPHASE, "Unable to vfork to drop parent: %s\n",
318 write(pidpipe[1], &pid, sizeof pid);
320 _exit(child_status); /* The error from exec() ! */
323 log_Printf(LogDEBUG, "Exec'ing ``%s''\n", p->name.base);
325 if ((argc = MakeArgs(p->name.base, argv, VECSIZE(argv),
326 PARSE_REDUCE|PARSE_NOHASH)) < 0) {
327 log_Printf(LogWARN, "Syntax error in exec command\n");
331 command_Expand(argv, argc, (char const *const *)argv,
332 p->dl->bundle, 0, realpid);
334 dup2(fids[1], STDIN_FILENO);
335 dup2(fids[1], STDOUT_FILENO);
336 dup2(fids[1], STDERR_FILENO);
337 for (i = getdtablesize(); i > STDERR_FILENO; i--)
338 fcntl(i, F_SETFD, 1);
341 child_status = errno; /* Only works for vfork() */
342 printf("execvp failed: %s: %s\r\n", *argv, strerror(child_status));
349 if (read(pidpipe[0], &p->session_owner, sizeof p->session_owner) !=
350 sizeof p->session_owner)
351 p->session_owner = (pid_t)-1;
353 while ((wret = waitpid(pid, &stat, 0)) == -1 && errno == EINTR)
356 log_Printf(LogWARN, "Waiting for child process: %s\n",
359 p->session_owner = (pid_t)-1;
361 } else if (WIFSIGNALED(stat)) {
362 log_Printf(LogWARN, "Child process received sig %d !\n",
365 p->session_owner = (pid_t)-1;
367 } else if (WIFSTOPPED(stat)) {
368 log_Printf(LogWARN, "Child process received stop sig %d !\n",
370 /* I guess that's ok.... */
371 } else if ((ret = WEXITSTATUS(stat))) {
372 log_Printf(LogWARN, "Cannot exec \"%s\": %s\n", p->name.base,
375 p->session_owner = (pid_t)-1;
379 log_Printf(LogDEBUG, "Using descriptor %d for child\n", p->fd);
386 if (fstat(p->fd, &st) != -1 && (st.st_mode & S_IFIFO)) {
387 if ((dev = malloc(sizeof *dev)) == NULL)
388 log_Printf(LogWARN, "%s: Cannot allocate an exec device: %s\n",
389 p->link.name, strerror(errno));
390 else if (p->fd == STDIN_FILENO) {
391 log_Printf(LogPHASE, "%s: Using stdin/stdout to communicate with "
392 "parent (pipe mode)\n", p->link.name);
393 dev->fd_out = dup(STDOUT_FILENO);
395 /* Hook things up so that we monitor dev->fd_out */
396 p->desc.UpdateSet = exec_UpdateSet;
397 p->desc.IsSet = exec_IsSet;
404 memcpy(&dev->dev, &baseexecdevice, sizeof dev->dev);
405 physical_SetupStack(p, dev->dev.name, PHYSICAL_NOFORCE);
406 if (p->cfg.cd.necessity != CD_DEFAULT)
407 log_Printf(LogWARN, "Carrier settings ignored\n");