146686Sbrian/*- 246686Sbrian * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org> 346686Sbrian * All rights reserved. 446686Sbrian * 546686Sbrian * Redistribution and use in source and binary forms, with or without 646686Sbrian * modification, are permitted provided that the following conditions 746686Sbrian * are met: 846686Sbrian * 1. Redistributions of source code must retain the above copyright 946686Sbrian * notice, this list of conditions and the following disclaimer. 1046686Sbrian * 2. Redistributions in binary form must reproduce the above copyright 1146686Sbrian * notice, this list of conditions and the following disclaimer in the 1246686Sbrian * documentation and/or other materials provided with the distribution. 1346686Sbrian * 1446686Sbrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1546686Sbrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1646686Sbrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1746686Sbrian * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1846686Sbrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1946686Sbrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2046686Sbrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2146686Sbrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2246686Sbrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2346686Sbrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2446686Sbrian * SUCH DAMAGE. 2546686Sbrian * 2650479Speter * $FreeBSD$ 2746686Sbrian */ 2846686Sbrian 2946686Sbrian#include <sys/param.h> 3046686Sbrian#include <sys/socket.h> 3146686Sbrian#include <sys/un.h> 3246686Sbrian 3346686Sbrian#include <errno.h> 3446686Sbrian#include <fcntl.h> 3546686Sbrian#include <stdio.h> 3646686Sbrian#include <stdlib.h> 3746686Sbrian#include <string.h> 38196514Sbrian#include <sysexits.h> 3946686Sbrian#include <sys/wait.h> 40196514Sbrian#include <sys/stat.h> 4147769Sbrian#include <sys/uio.h> 4246686Sbrian#include <termios.h> 4346686Sbrian#include <unistd.h> 4446686Sbrian 4546686Sbrian#include "layer.h" 4646686Sbrian#include "defs.h" 4746686Sbrian#include "mbuf.h" 4846686Sbrian#include "log.h" 4946686Sbrian#include "timer.h" 5046686Sbrian#include "lqr.h" 5146686Sbrian#include "hdlc.h" 5246686Sbrian#include "throughput.h" 5346686Sbrian#include "fsm.h" 5446686Sbrian#include "lcp.h" 5546686Sbrian#include "ccp.h" 5646686Sbrian#include "link.h" 5746686Sbrian#include "async.h" 5846686Sbrian#include "descriptor.h" 5946686Sbrian#include "physical.h" 6046686Sbrian#include "mp.h" 6146686Sbrian#include "chat.h" 6246686Sbrian#include "command.h" 6346686Sbrian#include "auth.h" 6446686Sbrian#include "chap.h" 6546686Sbrian#include "cbcp.h" 6646686Sbrian#include "datalink.h" 6755253Sbrian#include "id.h" 68196514Sbrian#include "main.h" 6946686Sbrian#include "exec.h" 7046686Sbrian 71196514Sbrian 72196514Sbrianstruct execdevice { 73196514Sbrian struct device dev; /* What struct physical knows about */ 74196514Sbrian int fd_out; /* output descriptor */ 75196514Sbrian}; 76196514Sbrian 77196514Sbrian#define device2exec(d) ((d)->type == EXEC_DEVICE ? (struct execdevice *)d : NULL) 78196514Sbrian 79196514Sbrianunsigned 80196514Sbrianexec_DeviceSize(void) 81196514Sbrian{ 82196514Sbrian return sizeof(struct execdevice); 83196514Sbrian} 84196514Sbrian 85196514Sbrianstatic void 86196514Sbrianexec_Free(struct physical *p) 87196514Sbrian{ 88196514Sbrian struct execdevice *dev = device2exec(p->handler); 89196514Sbrian 90196514Sbrian if (dev->fd_out != -1) 91196514Sbrian close(dev->fd_out); 92196514Sbrian free(dev); 93196514Sbrian} 94196514Sbrian 95196514Sbrianstatic void 96196514Sbrianexec_device2iov(struct device *d, struct iovec *iov, int *niov, 97196514Sbrian int maxiov __unused, int *auxfd, int *nauxfd) 98196514Sbrian{ 99196514Sbrian struct execdevice *dev; 100196514Sbrian int sz = physical_MaxDeviceSize(); 101196514Sbrian 102196514Sbrian iov[*niov].iov_base = d = realloc(d, sz); 103196514Sbrian if (d == NULL) { 104196514Sbrian log_Printf(LogALERT, "Failed to allocate memory: %d\n", sz); 105196514Sbrian AbortProgram(EX_OSERR); 106196514Sbrian } 107196514Sbrian iov[*niov].iov_len = sz; 108196514Sbrian (*niov)++; 109196514Sbrian 110196514Sbrian dev = device2exec(d); 111196514Sbrian if (dev->fd_out >= 0) { 112196514Sbrian *auxfd = dev->fd_out; 113196514Sbrian (*nauxfd)++; 114196514Sbrian } 115196514Sbrian} 116196514Sbrian 117196514Sbrianstatic int 118196514Sbrianexec_RemoveFromSet(struct physical *p, fd_set *r, fd_set *w, fd_set *e) 119196514Sbrian{ 120196514Sbrian struct execdevice *dev = device2exec(p->handler); 121196514Sbrian int sets; 122196514Sbrian 123196514Sbrian p->handler->removefromset = NULL; 124196514Sbrian sets = physical_RemoveFromSet(p, r, w, e); 125196514Sbrian p->handler->removefromset = exec_RemoveFromSet; 126196514Sbrian 127196514Sbrian if (dev->fd_out >= 0) { 128196514Sbrian if (w && FD_ISSET(dev->fd_out, w)) { 129196514Sbrian FD_CLR(dev->fd_out, w); 130196514Sbrian log_Printf(LogTIMER, "%s: fdunset(w) %d\n", p->link.name, dev->fd_out); 131196514Sbrian sets++; 132196514Sbrian } 133196514Sbrian if (e && FD_ISSET(dev->fd_out, e)) { 134196514Sbrian FD_CLR(dev->fd_out, e); 135196514Sbrian log_Printf(LogTIMER, "%s: fdunset(e) %d\n", p->link.name, dev->fd_out); 136196514Sbrian sets++; 137196514Sbrian } 138196514Sbrian } 139196514Sbrian 140196514Sbrian return sets; 141196514Sbrian} 142196514Sbrian 143196514Sbrianstatic ssize_t 144196514Sbrianexec_Write(struct physical *p, const void *v, size_t n) 145196514Sbrian{ 146196514Sbrian struct execdevice *dev = device2exec(p->handler); 147196514Sbrian int fd = dev->fd_out == -1 ? p->fd : dev->fd_out; 148196514Sbrian 149196514Sbrian return write(fd, v, n); 150196514Sbrian} 151196514Sbrian 152196514Sbrianstatic struct device baseexecdevice = { 15347061Sbrian EXEC_DEVICE, 15447061Sbrian "exec", 15578410Sbrian 0, 15653733Sbrian { CD_NOTREQUIRED, 0 }, 15747061Sbrian NULL, 158196514Sbrian exec_RemoveFromSet, 15947061Sbrian NULL, 16047061Sbrian NULL, 16147061Sbrian NULL, 16247061Sbrian NULL, 16347061Sbrian NULL, 164196514Sbrian exec_Free, 16547061Sbrian NULL, 166196514Sbrian exec_Write, 167196514Sbrian exec_device2iov, 16847061Sbrian NULL, 16947061Sbrian NULL, 17047061Sbrian NULL 17147061Sbrian}; 17247061Sbrian 17347061Sbrianstruct device * 17447061Sbrianexec_iov2device(int type, struct physical *p, struct iovec *iov, 175196514Sbrian int *niov, int maxiov __unused, int *auxfd, int *nauxfd) 17646686Sbrian{ 17747461Sbrian if (type == EXEC_DEVICE) { 178196514Sbrian struct execdevice *dev = (struct execdevice *)iov[(*niov)++].iov_base; 179196514Sbrian 180196514Sbrian dev = realloc(dev, sizeof *dev); /* Reduce to the correct size */ 181196514Sbrian if (dev == NULL) { 182196514Sbrian log_Printf(LogALERT, "Failed to allocate memory: %d\n", 183196514Sbrian (int)(sizeof *dev)); 184196514Sbrian AbortProgram(EX_OSERR); 185196514Sbrian } 186196514Sbrian 187196514Sbrian if (*nauxfd) { 188196514Sbrian dev->fd_out = *auxfd; 189196514Sbrian (*nauxfd)--; 190196514Sbrian } else 191196514Sbrian dev->fd_out = -1; 192196514Sbrian 193196514Sbrian /* Refresh function pointers etc */ 194196514Sbrian memcpy(&dev->dev, &baseexecdevice, sizeof dev->dev); 195196514Sbrian 196196514Sbrian physical_SetupStack(p, dev->dev.name, PHYSICAL_NOFORCE); 197196514Sbrian return &dev->dev; 19847461Sbrian } 19947061Sbrian 20047061Sbrian return NULL; 20147061Sbrian} 20247061Sbrian 203196514Sbrianstatic int 204196514Sbrianexec_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n) 20547061Sbrian{ 206196514Sbrian struct physical *p = descriptor2physical(d); 207196514Sbrian struct execdevice *dev = device2exec(p->handler); 208196514Sbrian int result = 0; 20946686Sbrian 210196514Sbrian if (w && dev->fd_out >= 0) { 211196514Sbrian FD_SET(dev->fd_out, w); 212196514Sbrian log_Printf(LogTIMER, "%s: fdset(w) %d\n", p->link.name, dev->fd_out); 213196514Sbrian result++; 214196514Sbrian w = NULL; 215196514Sbrian } 21652942Sbrian 217196514Sbrian if (e && dev->fd_out >= 0) { 218196514Sbrian FD_SET(dev->fd_out, e); 219196514Sbrian log_Printf(LogTIMER, "%s: fdset(e) %d\n", p->link.name, dev->fd_out); 220196514Sbrian result++; 221196514Sbrian } 22246686Sbrian 223196514Sbrian if (result && *n <= dev->fd_out) 224196514Sbrian *n = dev->fd_out + 1; 22546686Sbrian 226196514Sbrian return result + physical_doUpdateSet(d, r, w, e, n, 0); 227196514Sbrian} 22846686Sbrian 229196514Sbrianstatic int 230196514Sbrianexec_IsSet(struct fdescriptor *d, const fd_set *fdset) 231196514Sbrian{ 232196514Sbrian struct physical *p = descriptor2physical(d); 233196514Sbrian struct execdevice *dev = device2exec(p->handler); 234196514Sbrian int result = dev->fd_out >= 0 && FD_ISSET(dev->fd_out, fdset); 235196514Sbrian result += physical_IsSet(d, fdset); 23646686Sbrian 237196514Sbrian return result; 238196514Sbrian} 23958454Sbrian 240196514Sbrianstruct device * 241196514Sbrianexec_Create(struct physical *p) 242196514Sbrian{ 243196514Sbrian struct execdevice *dev; 24446686Sbrian 245196514Sbrian dev = NULL; 246196514Sbrian if (p->fd < 0) { 247196514Sbrian if (*p->name.full == '!') { 248196514Sbrian int fids[2], type; 249196514Sbrian 250196514Sbrian if ((dev = malloc(sizeof *dev)) == NULL) { 251196514Sbrian log_Printf(LogWARN, "%s: Cannot allocate an exec device: %s\n", 252196514Sbrian p->link.name, strerror(errno)); 253196514Sbrian return NULL; 254196514Sbrian } 255196514Sbrian dev->fd_out = -1; 256196514Sbrian 257196514Sbrian p->fd--; /* We own the device but maybe can't use it - change fd */ 258196514Sbrian type = physical_IsSync(p) ? SOCK_DGRAM : SOCK_STREAM; 259196514Sbrian 260196514Sbrian if (socketpair(AF_UNIX, type, PF_UNSPEC, fids) < 0) { 261196514Sbrian log_Printf(LogPHASE, "Unable to create pipe for line exec: %s\n", 262196514Sbrian strerror(errno)); 263196514Sbrian free(dev); 264196514Sbrian dev = NULL; 265196514Sbrian } else { 266196514Sbrian static int child_status; /* This variable is abused ! */ 267196514Sbrian int stat, argc, i, ret, wret, pidpipe[2]; 268196514Sbrian pid_t pid, realpid; 269196514Sbrian char *argv[MAXARGS]; 270196514Sbrian 271196514Sbrian stat = fcntl(fids[0], F_GETFL, 0); 272196514Sbrian if (stat > 0) { 273196514Sbrian stat |= O_NONBLOCK; 274196514Sbrian fcntl(fids[0], F_SETFL, stat); 275196514Sbrian } 276196514Sbrian realpid = getpid(); 277196514Sbrian if (pipe(pidpipe) == -1) { 278196514Sbrian log_Printf(LogPHASE, "Unable to pipe for line exec: %s\n", 279196514Sbrian strerror(errno)); 28046686Sbrian close(fids[1]); 281196514Sbrian close(fids[0]); 282196514Sbrian free(dev); 283196514Sbrian dev = NULL; 284196514Sbrian } else switch ((pid = fork())) { 285196514Sbrian case -1: 286196514Sbrian log_Printf(LogPHASE, "Unable to fork for line exec: %s\n", 28758454Sbrian strerror(errno)); 288196514Sbrian close(pidpipe[0]); 289196514Sbrian close(pidpipe[1]); 290196514Sbrian close(fids[1]); 29158454Sbrian close(fids[0]); 29258454Sbrian break; 293196514Sbrian 294196514Sbrian case 0: 295196514Sbrian close(pidpipe[0]); 29658454Sbrian close(fids[0]); 297196514Sbrian timer_TermService(); 298196514Sbrian #ifndef NOSUID 299196514Sbrian setuid(ID0realuid()); 300196514Sbrian #endif 301196514Sbrian 302196514Sbrian child_status = 0; 303196514Sbrian switch ((pid = vfork())) { 304196514Sbrian case 0: 305196514Sbrian close(pidpipe[1]); 306196514Sbrian break; 307196514Sbrian 308196514Sbrian case -1: 309196514Sbrian ret = errno; 310196514Sbrian log_Printf(LogPHASE, "Unable to vfork to drop parent: %s\n", 311196514Sbrian strerror(errno)); 312196514Sbrian close(pidpipe[1]); 313196514Sbrian _exit(ret); 314196514Sbrian 315196514Sbrian default: 316196514Sbrian write(pidpipe[1], &pid, sizeof pid); 317196514Sbrian close(pidpipe[1]); 318196514Sbrian _exit(child_status); /* The error from exec() ! */ 319196514Sbrian } 320196514Sbrian 321196514Sbrian log_Printf(LogDEBUG, "Exec'ing ``%s''\n", p->name.base); 322196514Sbrian 323196514Sbrian if ((argc = MakeArgs(p->name.base, argv, VECSIZE(argv), 324196514Sbrian PARSE_REDUCE|PARSE_NOHASH)) < 0) { 325196514Sbrian log_Printf(LogWARN, "Syntax error in exec command\n"); 326196514Sbrian _exit(ESRCH); 327196514Sbrian } 328196514Sbrian 329196514Sbrian command_Expand(argv, argc, (char const *const *)argv, 330196514Sbrian p->dl->bundle, 0, realpid); 331196514Sbrian 332196514Sbrian dup2(fids[1], STDIN_FILENO); 333196514Sbrian dup2(fids[1], STDOUT_FILENO); 334196514Sbrian dup2(fids[1], STDERR_FILENO); 335196514Sbrian for (i = getdtablesize(); i > STDERR_FILENO; i--) 336196514Sbrian fcntl(i, F_SETFD, 1); 337196514Sbrian 338196514Sbrian execvp(*argv, argv); 339196514Sbrian child_status = errno; /* Only works for vfork() */ 340196514Sbrian printf("execvp failed: %s: %s\r\n", *argv, strerror(child_status)); 341196514Sbrian _exit(child_status); 34258454Sbrian break; 343196514Sbrian 344196514Sbrian default: 345196514Sbrian close(pidpipe[1]); 346196514Sbrian close(fids[1]); 347196514Sbrian if (read(pidpipe[0], &p->session_owner, sizeof p->session_owner) != 348196514Sbrian sizeof p->session_owner) 349196514Sbrian p->session_owner = (pid_t)-1; 350196514Sbrian close(pidpipe[0]); 351196514Sbrian while ((wret = waitpid(pid, &stat, 0)) == -1 && errno == EINTR) 352196514Sbrian ; 353196514Sbrian if (wret == -1) { 354196514Sbrian log_Printf(LogWARN, "Waiting for child process: %s\n", 355196514Sbrian strerror(errno)); 356196514Sbrian close(fids[0]); 357196514Sbrian p->session_owner = (pid_t)-1; 358196514Sbrian break; 359196514Sbrian } else if (WIFSIGNALED(stat)) { 360196514Sbrian log_Printf(LogWARN, "Child process received sig %d !\n", 361196514Sbrian WTERMSIG(stat)); 362196514Sbrian close(fids[0]); 363196514Sbrian p->session_owner = (pid_t)-1; 364196514Sbrian break; 365196514Sbrian } else if (WIFSTOPPED(stat)) { 366196514Sbrian log_Printf(LogWARN, "Child process received stop sig %d !\n", 367196514Sbrian WSTOPSIG(stat)); 368196514Sbrian /* I guess that's ok.... */ 369196514Sbrian } else if ((ret = WEXITSTATUS(stat))) { 370196514Sbrian log_Printf(LogWARN, "Cannot exec \"%s\": %s\n", p->name.base, 371196514Sbrian strerror(ret)); 372196514Sbrian close(fids[0]); 373196514Sbrian p->session_owner = (pid_t)-1; 374196514Sbrian break; 375196514Sbrian } 376196514Sbrian p->fd = fids[0]; 377196514Sbrian log_Printf(LogDEBUG, "Using descriptor %d for child\n", p->fd); 378196514Sbrian } 37946686Sbrian } 38046686Sbrian } 381196514Sbrian } else { 382196514Sbrian struct stat st; 383196514Sbrian 384196514Sbrian if (fstat(p->fd, &st) != -1 && (st.st_mode & S_IFIFO)) { 385196514Sbrian if ((dev = malloc(sizeof *dev)) == NULL) 386196514Sbrian log_Printf(LogWARN, "%s: Cannot allocate an exec device: %s\n", 387196514Sbrian p->link.name, strerror(errno)); 388196514Sbrian else if (p->fd == STDIN_FILENO) { 389196514Sbrian log_Printf(LogPHASE, "%s: Using stdin/stdout to communicate with " 390196514Sbrian "parent (pipe mode)\n", p->link.name); 391196514Sbrian dev->fd_out = dup(STDOUT_FILENO); 392196514Sbrian 393196514Sbrian /* Hook things up so that we monitor dev->fd_out */ 394196514Sbrian p->desc.UpdateSet = exec_UpdateSet; 395196514Sbrian p->desc.IsSet = exec_IsSet; 396196514Sbrian } else 397196514Sbrian dev->fd_out = -1; 398196514Sbrian } 39946686Sbrian } 40046686Sbrian 401196514Sbrian if (dev) { 402196514Sbrian memcpy(&dev->dev, &baseexecdevice, sizeof dev->dev); 403196514Sbrian physical_SetupStack(p, dev->dev.name, PHYSICAL_NOFORCE); 404196514Sbrian if (p->cfg.cd.necessity != CD_DEFAULT) 405196514Sbrian log_Printf(LogWARN, "Carrier settings ignored\n"); 406196514Sbrian return &dev->dev; 407196514Sbrian } 408196514Sbrian 40947061Sbrian return NULL; 41046686Sbrian} 411