Home | History | Annotate | Download | only in src
      1 /*-
      2  * Copyright (c) 1999 Brian Somers <brian (at) Awfulhak.org>
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  * 1. Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  * 2. Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in the
     12  *    documentation and/or other materials provided with the distribution.
     13  *
     14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     24  * SUCH DAMAGE.
     25  *
     26  * $FreeBSD: src/usr.sbin/ppp/exec.c,v 1.29.10.1.4.1 2010/12/21 17:10:29 kensmith Exp $
     27  */
     28 
     29 #include <sys/param.h>
     30 #include <sys/socket.h>
     31 #include <sys/un.h>
     32 
     33 #include <errno.h>
     34 #include <fcntl.h>
     35 #include <stdio.h>
     36 #include <stdlib.h>
     37 #include <string.h>
     38 #include <sysexits.h>
     39 #include <sys/wait.h>
     40 #include <sys/stat.h>
     41 #include <sys/uio.h>
     42 #include <termios.h>
     43 #include <unistd.h>
     44 
     45 #include "layer.h"
     46 #include "defs.h"
     47 #include "mbuf.h"
     48 #include "log.h"
     49 #include "timer.h"
     50 #include "lqr.h"
     51 #include "hdlc.h"
     52 #include "throughput.h"
     53 #include "fsm.h"
     54 #include "lcp.h"
     55 #include "ccp.h"
     56 #include "link.h"
     57 #include "async.h"
     58 #include "descriptor.h"
     59 #include "physical.h"
     60 #include "mp.h"
     61 #include "chat.h"
     62 #include "command.h"
     63 #include "auth.h"
     64 #include "chap.h"
     65 #include "cbcp.h"
     66 #include "datalink.h"
     67 #include "id.h"
     68 #include "main.h"
     69 #include "exec.h"
     70 
     71 
     72 struct execdevice {
     73   struct device dev;		/* What struct physical knows about */
     74   int fd_out;			/* output descriptor */
     75 };
     76 
     77 #define device2exec(d) ((d)->type == EXEC_DEVICE ? (struct execdevice *)d : NULL)
     78 
     79 unsigned
     80 exec_DeviceSize(void)
     81 {
     82   return sizeof(struct execdevice);
     83 }
     84 
     85 static void
     86 exec_Free(struct physical *p)
     87 {
     88   struct execdevice *dev = device2exec(p->handler);
     89 
     90   if (dev->fd_out != -1)
     91     close(dev->fd_out);
     92   free(dev);
     93 }
     94 
     95 static void
     96 exec_device2iov(struct device *d, struct iovec *iov, int *niov,
     97                int maxiov __unused, int *auxfd, int *nauxfd)
     98 {
     99   struct execdevice *dev;
    100   int sz = physical_MaxDeviceSize();
    101 
    102   iov[*niov].iov_base = d = realloc(d, sz);
    103   if (d == NULL) {
    104     log_Printf(LogALERT, "Failed to allocate memory: %d\n", sz);
    105     AbortProgram(EX_OSERR);
    106   }
    107   iov[*niov].iov_len = sz;
    108   (*niov)++;
    109 
    110   dev = device2exec(d);
    111   if (dev->fd_out >= 0) {
    112     *auxfd = dev->fd_out;
    113     (*nauxfd)++;
    114   }
    115 }
    116 
    117 static int
    118 exec_RemoveFromSet(struct physical *p, fd_set *r, fd_set *w, fd_set *e)
    119 {
    120   struct execdevice *dev = device2exec(p->handler);
    121   int sets;
    122 
    123   p->handler->removefromset = NULL;
    124   sets = physical_RemoveFromSet(p, r, w, e);
    125   p->handler->removefromset = exec_RemoveFromSet;
    126 
    127   if (dev->fd_out >= 0) {
    128     if (w && FD_ISSET(dev->fd_out, w)) {
    129       FD_CLR(dev->fd_out, w);
    130       log_Printf(LogTIMER, "%s: fdunset(w) %d\n", p->link.name, dev->fd_out);
    131       sets++;
    132     }
    133     if (e && FD_ISSET(dev->fd_out, e)) {
    134       FD_CLR(dev->fd_out, e);
    135       log_Printf(LogTIMER, "%s: fdunset(e) %d\n", p->link.name, dev->fd_out);
    136       sets++;
    137     }
    138   }
    139 
    140   return sets;
    141 }
    142 
    143 static ssize_t
    144 exec_Write(struct physical *p, const void *v, size_t n)
    145 {
    146   struct execdevice *dev = device2exec(p->handler);
    147   int fd = dev->fd_out == -1 ? p->fd : dev->fd_out;
    148 
    149   return write(fd, v, n);
    150 }
    151 
    152 static struct device baseexecdevice = {
    153   EXEC_DEVICE,
    154   "exec",
    155   0,
    156   { CD_NOTREQUIRED, 0 },
    157   NULL,
    158   exec_RemoveFromSet,
    159   NULL,
    160   NULL,
    161   NULL,
    162   NULL,
    163   NULL,
    164   exec_Free,
    165   NULL,
    166   exec_Write,
    167   exec_device2iov,
    168   NULL,
    169   NULL,
    170   NULL
    171 };
    172 
    173 struct device *
    174 exec_iov2device(int type, struct physical *p, struct iovec *iov,
    175                 int *niov, int maxiov __unused, int *auxfd, int *nauxfd)
    176 {
    177   if (type == EXEC_DEVICE) {
    178     struct execdevice *dev = (struct execdevice *)iov[(*niov)++].iov_base;
    179 
    180     dev = realloc(dev, sizeof *dev);	/* Reduce to the correct size */
    181     if (dev == NULL) {
    182       log_Printf(LogALERT, "Failed to allocate memory: %d\n",
    183                  (int)(sizeof *dev));
    184       AbortProgram(EX_OSERR);
    185     }
    186 
    187     if (*nauxfd) {
    188       dev->fd_out = *auxfd;
    189       (*nauxfd)--;
    190     } else
    191       dev->fd_out = -1;
    192 
    193     /* Refresh function pointers etc */
    194     memcpy(&dev->dev, &baseexecdevice, sizeof dev->dev);
    195 
    196     physical_SetupStack(p, dev->dev.name, PHYSICAL_NOFORCE);
    197     return &dev->dev;
    198   }
    199 
    200   return NULL;
    201 }
    202 
    203 static int
    204 exec_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
    205 {
    206   struct physical *p = descriptor2physical(d);
    207   struct execdevice *dev = device2exec(p->handler);
    208   int result = 0;
    209 
    210   if (w && dev->fd_out >= 0) {
    211     FD_SET(dev->fd_out, w);
    212     log_Printf(LogTIMER, "%s: fdset(w) %d\n", p->link.name, dev->fd_out);
    213     result++;
    214     w = NULL;
    215   }
    216 
    217   if (e && dev->fd_out >= 0) {
    218     FD_SET(dev->fd_out, e);
    219     log_Printf(LogTIMER, "%s: fdset(e) %d\n", p->link.name, dev->fd_out);
    220     result++;
    221   }
    222 
    223   if (result && *n <= dev->fd_out)
    224     *n = dev->fd_out + 1;
    225 
    226   return result + physical_doUpdateSet(d, r, w, e, n, 0);
    227 }
    228 
    229 static int
    230 exec_IsSet(struct fdescriptor *d, const fd_set *fdset)
    231 {
    232   struct physical *p = descriptor2physical(d);
    233   struct execdevice *dev = device2exec(p->handler);
    234   int result = dev->fd_out >= 0 && FD_ISSET(dev->fd_out, fdset);
    235   result += physical_IsSet(d, fdset);
    236 
    237   return result;
    238 }
    239 
    240 struct device *
    241 exec_Create(struct physical *p)
    242 {
    243   struct execdevice *dev;
    244 
    245   dev = NULL;
    246   if (p->fd < 0) {
    247     if (*p->name.full == '!') {
    248       int fids[2], type;
    249 
    250       if ((dev = malloc(sizeof *dev)) == NULL) {
    251         log_Printf(LogWARN, "%s: Cannot allocate an exec device: %s\n",
    252                    p->link.name, strerror(errno));
    253         return NULL;
    254       }
    255       dev->fd_out = -1;
    256 
    257       p->fd--;	/* We own the device but maybe can't use it - change fd */
    258       type = physical_IsSync(p) ? SOCK_DGRAM : SOCK_STREAM;
    259 
    260       if (socketpair(AF_UNIX, type, PF_UNSPEC, fids) < 0) {
    261         log_Printf(LogPHASE, "Unable to create pipe for line exec: %s\n",
    262                    strerror(errno));
    263         free(dev);
    264         dev = NULL;
    265       } else {
    266         static int child_status;		/* This variable is abused ! */
    267         int stat, argc, i, ret, wret, pidpipe[2];
    268         pid_t pid, realpid;
    269         char *argv[MAXARGS];
    270 
    271         stat = fcntl(fids[0], F_GETFL, 0);
    272         if (stat > 0) {
    273           stat |= O_NONBLOCK;
    274           fcntl(fids[0], F_SETFL, stat);
    275         }
    276         realpid = getpid();
    277         if (pipe(pidpipe) == -1) {
    278           log_Printf(LogPHASE, "Unable to pipe for line exec: %s\n",
    279                      strerror(errno));
    280           close(fids[1]);
    281           close(fids[0]);
    282           free(dev);
    283           dev = NULL;
    284         } else switch ((pid = fork())) {
    285           case -1:
    286             log_Printf(LogPHASE, "Unable to fork for line exec: %s\n",
    287                        strerror(errno));
    288             close(pidpipe[0]);
    289             close(pidpipe[1]);
    290             close(fids[1]);
    291             close(fids[0]);
    292             break;
    293 
    294           case 0:
    295             close(pidpipe[0]);
    296             close(fids[0]);
    297             timer_TermService();
    298   #ifndef NOSUID
    299             setuid(ID0realuid());
    300   #endif
    301 
    302             child_status = 0;
    303             switch ((pid = vfork())) {
    304               case 0:
    305                 close(pidpipe[1]);
    306                 break;
    307 
    308               case -1:
    309                 ret = errno;
    310                 log_Printf(LogPHASE, "Unable to vfork to drop parent: %s\n",
    311                            strerror(errno));
    312                 close(pidpipe[1]);
    313                 _exit(ret);
    314 
    315               default:
    316                 write(pidpipe[1], &pid, sizeof pid);
    317                 close(pidpipe[1]);
    318                 _exit(child_status);	/* The error from exec() ! */
    319             }
    320 
    321             log_Printf(LogDEBUG, "Exec'ing ``%s''\n", p->name.base);
    322 
    323             if ((argc = MakeArgs(p->name.base, argv, VECSIZE(argv),
    324                                  PARSE_REDUCE|PARSE_NOHASH)) < 0) {
    325               log_Printf(LogWARN, "Syntax error in exec command\n");
    326               _exit(ESRCH);
    327             }
    328 
    329             command_Expand(argv, argc, (char const *const *)argv,
    330                            p->dl->bundle, 0, realpid);
    331 
    332             dup2(fids[1], STDIN_FILENO);
    333             dup2(fids[1], STDOUT_FILENO);
    334             dup2(fids[1], STDERR_FILENO);
    335             for (i = getdtablesize(); i > STDERR_FILENO; i--)
    336               fcntl(i, F_SETFD, 1);
    337 
    338             execvp(*argv, argv);
    339             child_status = errno;		/* Only works for vfork() */
    340             printf("execvp failed: %s: %s\r\n", *argv, strerror(child_status));
    341             _exit(child_status);
    342             break;
    343 
    344           default:
    345             close(pidpipe[1]);
    346             close(fids[1]);
    347             if (read(pidpipe[0], &p->session_owner, sizeof p->session_owner) !=
    348                 sizeof p->session_owner)
    349               p->session_owner = (pid_t)-1;
    350             close(pidpipe[0]);
    351             while ((wret = waitpid(pid, &stat, 0)) == -1 && errno == EINTR)
    352               ;
    353             if (wret == -1) {
    354               log_Printf(LogWARN, "Waiting for child process: %s\n",
    355                          strerror(errno));
    356               close(fids[0]);
    357               p->session_owner = (pid_t)-1;
    358               break;
    359             } else if (WIFSIGNALED(stat)) {
    360               log_Printf(LogWARN, "Child process received sig %d !\n",
    361                          WTERMSIG(stat));
    362               close(fids[0]);
    363               p->session_owner = (pid_t)-1;
    364               break;
    365             } else if (WIFSTOPPED(stat)) {
    366               log_Printf(LogWARN, "Child process received stop sig %d !\n",
    367                          WSTOPSIG(stat));
    368               /* I guess that's ok.... */
    369             } else if ((ret = WEXITSTATUS(stat))) {
    370               log_Printf(LogWARN, "Cannot exec \"%s\": %s\n", p->name.base,
    371                          strerror(ret));
    372               close(fids[0]);
    373               p->session_owner = (pid_t)-1;
    374               break;
    375             }
    376             p->fd = fids[0];
    377             log_Printf(LogDEBUG, "Using descriptor %d for child\n", p->fd);
    378         }
    379       }
    380     }
    381   } else {
    382     struct stat st;
    383 
    384     if (fstat(p->fd, &st) != -1 && (st.st_mode & S_IFIFO)) {
    385       if ((dev = malloc(sizeof *dev)) == NULL)
    386         log_Printf(LogWARN, "%s: Cannot allocate an exec device: %s\n",
    387                    p->link.name, strerror(errno));
    388       else if (p->fd == STDIN_FILENO) {
    389         log_Printf(LogPHASE, "%s: Using stdin/stdout to communicate with "
    390                    "parent (pipe mode)\n", p->link.name);
    391         dev->fd_out = dup(STDOUT_FILENO);
    392 
    393         /* Hook things up so that we monitor dev->fd_out */
    394         p->desc.UpdateSet = exec_UpdateSet;
    395         p->desc.IsSet = exec_IsSet;
    396       } else
    397         dev->fd_out = -1;
    398     }
    399   }
    400 
    401   if (dev) {
    402     memcpy(&dev->dev, &baseexecdevice, sizeof dev->dev);
    403     physical_SetupStack(p, dev->dev.name, PHYSICAL_NOFORCE);
    404     if (p->cfg.cd.necessity != CD_DEFAULT)
    405       log_Printf(LogWARN, "Carrier settings ignored\n");
    406     return &dev->dev;
    407   }
    408 
    409   return NULL;
    410 }
    411