LCOV - code coverage report
Current view: top level - lib/texpect - texpect.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 119 176 67.6 %
Date: 2024-04-21 15:09:00 Functions: 5 8 62.5 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2008 Kungliga Tekniska Högskolan
       3             :  * (Royal Institute of Technology, Stockholm, Sweden).
       4             :  * All rights reserved.
       5             :  *
       6             :  * Redistribution and use in source and binary forms, with or without
       7             :  * modification, are permitted provided that the following conditions
       8             :  * are met:
       9             :  *
      10             :  * 1. Redistributions of source code must retain the above copyright
      11             :  *    notice, this list of conditions and the following disclaimer.
      12             :  *
      13             :  * 2. Redistributions in binary form must reproduce the above copyright
      14             :  *    notice, this list of conditions and the following disclaimer in the
      15             :  *    documentation and/or other materials provided with the distribution.
      16             :  *
      17             :  * 3. Neither the name of the Institute nor the names of its contributors
      18             :  *    may be used to endorse or promote products derived from this software
      19             :  *    without specific prior written permission.
      20             :  *
      21             :  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
      22             :  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
      23             :  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
      24             :  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
      25             :  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
      26             :  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
      27             :  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
      28             :  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
      29             :  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
      30             :  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
      31             :  * SUCH DAMAGE.
      32             :  */
      33             : 
      34             : #include "replace.h"
      35             : #include "system/filesys.h"
      36             : #include "system/wait.h"
      37             : #include "lib/util/sys_rw.h"
      38             : 
      39             : #ifdef HAVE_PTY_H
      40             : #include <pty.h>
      41             : #endif
      42             : #ifdef HAVE_UTIL_H
      43             : #include <util.h>
      44             : #endif
      45             : #ifdef HAVE_BSD_LIBUTIL_H
      46             : #include <bsd/libutil.h>
      47             : #elif defined HAVE_LIBUTIL_H
      48             : #include <libutil.h>
      49             : #endif
      50             : 
      51             : #ifdef  STREAMSPTY
      52             : #include <stropts.h>
      53             : #endif /* STREAMPTY */
      54             : 
      55             : #include <popt.h>
      56             : 
      57             : #ifdef HAVE_ERR_H
      58             : #include <err.h>
      59             : #else
      60             : const char progname[] = "unknown program";
      61             : 
      62             : static void err(int eval, const char *fmt, ...) PRINTF_ATTRIBUTE(2, 0);
      63             : static void errx(int eval, const char *fmt, ...) PRINTF_ATTRIBUTE(2, 0);
      64             : 
      65           0 : static void err(int eval, const char *fmt, ...)
      66             : {
      67           0 :         int err_errno = errno;
      68             :         va_list ap;
      69             : 
      70           0 :         fprintf(stderr, "%s: ", progname);
      71           0 :         va_start(ap, fmt);
      72           0 :         vfprintf(stderr, fmt, ap);
      73           0 :         va_end(ap);
      74           0 :         fprintf(stderr, ": %s\n", strerror(err_errno));
      75           0 :         exit(eval);
      76             : }
      77             : 
      78           0 : static void errx(int eval, const char *fmt, ...)
      79             : {
      80             :         va_list ap;
      81             : 
      82           0 :         fprintf(stderr, "%s: ", progname);
      83           0 :         va_start(ap, fmt);
      84           0 :         vfprintf(stderr, fmt, ap);
      85           0 :         va_end(ap);
      86           0 :         fprintf(stderr, "\n");
      87           0 :         exit(eval);
      88             : }
      89             : 
      90             : #endif
      91             : 
      92             : struct command {
      93             :         enum { CMD_EXPECT = 0, CMD_SEND, CMD_PASSWORD } type;
      94             :         unsigned int lineno;
      95             :         char *str;
      96             :         struct command *next;
      97             : };
      98             : 
      99             : /*
     100             :  *
     101             :  */
     102             : 
     103             : static struct command *commands, **next = &commands;
     104             : 
     105             : static sig_atomic_t alarmset = 0;
     106             : 
     107             : static int opt_timeout = 10;
     108             : static int opt_verbose;
     109             : 
     110             : static int master;
     111             : static int slave;
     112             : static char line[256] = { 0 };
     113             : 
     114           0 : static void caught_signal(int signo)
     115             : {
     116           0 :         alarmset = signo;
     117           0 : }
     118             : 
     119             : 
     120          44 : static void open_pty(void)
     121             : {
     122             : #ifdef _AIX
     123             :         printf("implement open_pty\n");
     124             :         exit(77);
     125             : #endif
     126             : #if defined(HAVE_OPENPTY) || defined(__linux) || defined(__osf__) /* XXX */
     127          44 :         if(openpty(&master, &slave, line, 0, 0) == 0)
     128          44 :                 return;
     129             : #endif /* HAVE_OPENPTY .... */
     130             : #ifdef STREAMSPTY
     131             :         {
     132             :                 char *clone[] = {
     133             :                         "/dev/ptc",
     134             :                         "/dev/ptmx",
     135             :                         "/dev/ptm",
     136             :                         "/dev/ptym/clone",
     137             :                         NULL
     138             :                 };
     139             :                 char **q;
     140             : 
     141             :                 for(q = clone; *q; q++){
     142             :                         master = open(*q, O_RDWR);
     143             :                         if(master >= 0){
     144             : #ifdef HAVE_GRANTPT
     145             :                                 grantpt(master);
     146             : #endif
     147             : #ifdef HAVE_UNLOCKPT
     148             :                                 unlockpt(master);
     149             : #endif
     150             :                                 strlcpy(line, ptsname(master), sizeof(line));
     151             :                                 slave = open(line, O_RDWR);
     152             :                                 if (slave < 0)
     153             :                                         errx(1, "failed to open slave when using %s", *q);
     154             :                                 ioctl(slave, I_PUSH, "ptem");
     155             :                                 ioctl(slave, I_PUSH, "ldterm");
     156             : 
     157             :                                 return;
     158             :                         }
     159             :                 }
     160             :         }
     161             : #endif /* STREAMSPTY */
     162             : 
     163             :         /* more cases, like open /dev/ptmx, etc */
     164             : 
     165           0 :         exit(77);
     166             : }
     167             : 
     168             : /*
     169             :  *
     170             :  */
     171             : 
     172         377 : static char *iscmd(const char *buf, const char *s)
     173             : {
     174         377 :         size_t len = strlen(s);
     175             : 
     176         377 :         if (strncmp(buf, s, len) != 0) {
     177         130 :                 return NULL;
     178             :         }
     179             : 
     180         247 :         return strdup(buf + len);
     181             : }
     182             : 
     183          44 : static void parse_configuration(const char *fn)
     184             : {
     185             :         struct command *c;
     186             :         char s[1024];
     187             :         char *str;
     188          44 :         unsigned int lineno = 0;
     189             :         FILE *cmd;
     190             : 
     191          44 :         cmd = fopen(fn, "r");
     192          44 :         if (cmd == NULL)
     193           0 :                 err(1, "open: %s", fn);
     194             : 
     195         291 :         while (fgets(s, sizeof(s),  cmd) != NULL) {
     196             : 
     197         247 :                 s[strcspn(s, "#\n")] = '\0';
     198         247 :                 lineno++;
     199             : 
     200         247 :                 c = calloc(1, sizeof(*c));
     201         247 :                 if (c == NULL)
     202           0 :                         errx(1, "malloc");
     203             : 
     204         247 :                 c->lineno = lineno;
     205         247 :                 (*next) = c;
     206         247 :                 next = &(c->next);
     207             : 
     208         247 :                 if ((str = iscmd(s, "expect ")) != NULL) {
     209         135 :                         c->type = CMD_EXPECT;
     210         135 :                         c->str = str;
     211         112 :                 } else if ((str = iscmd(s, "send ")) != NULL) {
     212          94 :                         c->type = CMD_SEND;
     213          94 :                         c->str = str;
     214          18 :                 } else if ((str = iscmd(s, "password ")) != NULL) {
     215          18 :                         c->type = CMD_PASSWORD;
     216          18 :                         c->str = str;
     217             :                 } else
     218           0 :                         errx(1, "Invalid command on line %d: %s", lineno, s);
     219             :         }
     220             : 
     221          44 :         fclose(cmd);
     222          44 : }
     223             : 
     224             : /*
     225             :  *
     226             :  */
     227             : 
     228          44 : static int eval_parent(pid_t pid)
     229             : {
     230             :         struct command *c;
     231             :         char in;
     232          44 :         size_t len = 0;
     233             :         ssize_t sret;
     234             : 
     235         291 :         for (c = commands; c != NULL; c = c->next) {
     236         247 :                 switch(c->type) {
     237         135 :                 case CMD_EXPECT:
     238         135 :                         if (opt_verbose) {
     239           2 :                                 printf("[expecting %s]\n", c->str);
     240             :                         }
     241         135 :                         len = 0;
     242         135 :                         alarm(opt_timeout);
     243        4851 :                         while((sret = read(master, &in, sizeof(in))) > 0) {
     244        4851 :                                 alarm(opt_timeout);
     245        4851 :                                 printf("%c", in);
     246        4851 :                                 if (c->str[len] != in) {
     247        2060 :                                         len = 0;
     248        2060 :                                         continue;
     249             :                                 }
     250        2791 :                                 len++;
     251        2791 :                                 if (c->str[len] == '\0') {
     252         135 :                                         break;
     253             :                                 }
     254             :                         }
     255         135 :                         alarm(0);
     256         135 :                         if (alarmset == SIGALRM) {
     257           0 :                                 errx(1, "timeout waiting for %s (line %u)",
     258             :                                                 c->str, c->lineno);
     259         135 :                         } else if (alarmset) {
     260           0 :                                 errx(1, "got a signal %d waiting for %s (line %u)",
     261             :                                                 (int)alarmset, c->str, c->lineno);
     262             :                         }
     263             : 
     264         135 :                         if (sret <= 0) {
     265           0 :                                 errx(1, "end command while waiting for %s (line %u)",
     266             :                                                 c->str, c->lineno);
     267             :                         }
     268         135 :                         break;
     269         112 :                 case CMD_SEND:
     270             :                 case CMD_PASSWORD: {
     271         112 :                         size_t i = 0;
     272         112 :                         const char *msg = (c->type == CMD_PASSWORD) ? "****" : c->str;
     273             : 
     274         112 :                         if (opt_verbose) {
     275           2 :                                 printf("[send %s]\n", msg);
     276             :                         }
     277             : 
     278         112 :                         len = strlen(c->str);
     279             : 
     280        1474 :                         while (i < len) {
     281        1474 :                                 if (c->str[i] == '\\' && i < len - 1) {
     282             :                                         char ctrl;
     283         112 :                                         i++;
     284         112 :                                         switch(c->str[i]) {
     285         112 :                                         case 'n':
     286         112 :                                                 ctrl = '\n';
     287         112 :                                                 break;
     288           0 :                                         case 'r':
     289           0 :                                                 ctrl = '\r';
     290           0 :                                                 break;
     291           0 :                                         case 't':
     292           0 :                                                 ctrl = '\t';
     293           0 :                                                 break;
     294           0 :                                         default:
     295           0 :                                                 errx(1,
     296             :                                                      "unknown control char %c (line %u)",
     297           0 :                                                      c->str[i],
     298             :                                                      c->lineno);
     299             :                                         }
     300         112 :                                         if (sys_write(master, &ctrl, 1) != 1) {
     301           0 :                                                 errx(1, "command refused input (line %u)", c->lineno);
     302             :                                         }
     303             :                                 } else {
     304        1250 :                                         if (sys_write(master, &c->str[i], 1) != 1) {
     305           0 :                                                 errx(1, "command refused input (line %u)", c->lineno);
     306             :                                         }
     307             :                                 }
     308        1362 :                                 i++;
     309             :                         }
     310         112 :                         break;
     311             :                 }
     312           0 :                 default:
     313           0 :                         abort();
     314             :                 }
     315             :         }
     316             : 
     317        4083 :         while(read(master, &in, sizeof(in)) > 0) {
     318        4039 :                 printf("%c", in);
     319             :         }
     320             : 
     321          44 :         if (opt_verbose) {
     322           2 :                 printf("[end of program]\n");
     323             :         }
     324             : 
     325             :         /*
     326             :          * Fetch status from child
     327             :          */
     328             :         {
     329             :                 int ret, status;
     330             : 
     331          44 :                 ret = waitpid(pid, &status, 0);
     332          44 :                 if (ret == -1) {
     333           0 :                         err(1, "waitpid");
     334             :                 }
     335             : 
     336          44 :                 if (WIFEXITED(status) && WEXITSTATUS(status)) {
     337           3 :                         return WEXITSTATUS(status);
     338          41 :                 } else if (WIFSIGNALED(status)) {
     339           0 :                         printf("killed by signal: %d\n", WTERMSIG(status));
     340           0 :                         return 1;
     341             :                 }
     342             :         }
     343             : 
     344          41 :         return 0;
     345             : }
     346             : 
     347             : /*
     348             :  *
     349             :  */
     350             : struct poptOption long_options[] = {
     351             :         POPT_AUTOHELP
     352             :         {
     353             :                 .longName  = "timeout",
     354             :                 .shortName = 't',
     355             :                 .argInfo   = POPT_ARG_INT,
     356             :                 .arg       = &opt_timeout,
     357             :                 .val       = 't',
     358             :         },
     359             :         {
     360             :                 .longName  = "verbose",
     361             :                 .shortName = 'v',
     362             :                 .argInfo   = POPT_ARG_NONE,
     363             :                 .arg       = &opt_verbose,
     364             :                 .val       = 'v',
     365             :         },
     366             :         POPT_TABLEEND
     367             : };
     368             : 
     369          44 : int main(int argc, const char **argv)
     370             : {
     371          44 :         int optidx = 0;
     372             :         pid_t pid;
     373          44 :         poptContext pc = NULL;
     374             :         const char *instruction_file;
     375             :         const char **args;
     376             :         const char *program;
     377             :         char * const *program_args;
     378             : 
     379          44 :         pc = poptGetContext("texpect",
     380             :                             argc,
     381             :                             argv,
     382             :                             long_options,
     383             :                             POPT_CONTEXT_POSIXMEHARDER);
     384             : 
     385          44 :         if (argc == 1) {
     386           0 :                 poptPrintHelp(pc, stderr, 0);
     387           0 :                 goto out;
     388             :         }
     389             : 
     390          46 :         while ((optidx = poptGetNextOpt(pc)) != -1) {
     391           2 :                 switch (optidx) {
     392           0 :                 case POPT_ERROR_BADOPT:
     393           0 :                         fprintf(stderr, "\nInvalid option %s: %s\n\n",
     394             :                                 poptBadOption(pc, 0), poptStrerror(optidx));
     395           0 :                         poptPrintUsage(pc, stderr, 0);
     396           0 :                         exit(1);
     397             :                 }
     398             :         }
     399             : 
     400          44 :         instruction_file = poptGetArg(pc);
     401          44 :         args = poptGetArgs(pc);
     402          44 :         if (args == NULL) {
     403           0 :                 poptPrintHelp(pc, stderr, 0);
     404           0 :                 goto out;
     405             :         }
     406             : 
     407          44 :         program_args = (char * const *)discard_const_p(char *, args);
     408          44 :         program = program_args[0];
     409             : 
     410          44 :         if (opt_verbose) {
     411             :                 int i;
     412             : 
     413           2 :                 printf("Using instruction_file: %s\n", instruction_file);
     414           2 :                 printf("Executing '%s' ", program);
     415          28 :                 for (i = 0; program_args[i] != NULL; i++) {
     416          26 :                         printf("'%s' ", program_args[i]);
     417             :                 }
     418           2 :                 printf("\n");
     419             :         }
     420             : 
     421          44 :         parse_configuration(instruction_file);
     422             : 
     423          44 :         open_pty();
     424             : 
     425          44 :         pid = fork();
     426          88 :         switch (pid) {
     427           0 :                 case -1:
     428           0 :                         err(1, "Failed to fork");
     429             : 
     430             :                         /* Never reached */
     431           0 :                         goto out;
     432          44 :                 case 0:
     433             : 
     434          44 :                         if(setsid()<0)
     435           0 :                                 err(1, "setsid");
     436             : 
     437          44 :                         dup2(slave, STDIN_FILENO);
     438          44 :                         dup2(slave, STDOUT_FILENO);
     439          44 :                         dup2(slave, STDERR_FILENO);
     440             : 
     441          44 :                         closefrom(STDERR_FILENO + 1);
     442             : 
     443             :                         /* texpect <expect_instructions> <progname> [<args>] */
     444          44 :                         execvp(program, program_args);
     445          44 :                         err(1, "Failed to exec: %s", program);
     446             : 
     447             :                         /* Never reached */
     448           0 :                         goto out;
     449          44 :                 default:
     450          44 :                         close(slave);
     451             :                         {
     452             :                                 struct sigaction sa;
     453             : 
     454          44 :                                 sa.sa_handler = caught_signal;
     455          44 :                                 sa.sa_flags = 0;
     456          44 :                                 sigemptyset (&sa.sa_mask);
     457             : 
     458          44 :                                 sigaction(SIGALRM, &sa, NULL);
     459             :                         }
     460             : 
     461          44 :                         poptFreeContext(pc);
     462          44 :                         return eval_parent(pid);
     463             :         }
     464             : 
     465             :         /* Never reached */
     466             : 
     467           0 : out:
     468           0 :         poptFreeContext(pc);
     469           0 :         return 1;
     470             : }

Generated by: LCOV version 1.14