ldap_client: Make ldap_parse_basic_url() IPv6-address aware
[vlendec/samba-autobuild/.git] / lib / texpect / texpect.c
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
38 #ifdef HAVE_PTY_H
39 #include <pty.h>
40 #endif
41 #ifdef HAVE_UTIL_H
42 #include <util.h>
43 #endif
44 #ifdef HAVE_BSD_LIBUTIL_H
45 #include <bsd/libutil.h>
46 #elif defined HAVE_LIBUTIL_H
47 #include <libutil.h>
48 #endif
49
50 #ifdef  STREAMSPTY
51 #include <stropts.h>
52 #endif /* STREAMPTY */
53
54 #include <popt.h>
55
56 #ifdef HAVE_ERR_H
57 #include <err.h>
58 #else
59 const char progname[] = "unknown program";
60
61 static void err(int eval, const char *fmt, ...)
62 {
63         int err_errno = errno;
64         va_list ap;
65
66         fprintf(stderr, "%s: ", progname);
67         va_start(ap, fmt);
68         vfprintf(stderr, fmt, ap);
69         va_end(ap);
70         fprintf(stderr, ": %s\n", strerror(err_errno));
71         exit(eval);
72 }
73
74 static void errx(int eval, const char *fmt, ...)
75 {
76         va_list ap;
77
78         fprintf(stderr, "%s: ", progname);
79         va_start(ap, fmt);
80         vfprintf(stderr, fmt, ap);
81         va_end(ap);
82         fprintf(stderr, "\n");
83         exit(eval);
84 }
85
86 #endif
87
88 struct command {
89         enum { CMD_EXPECT = 0, CMD_SEND, CMD_PASSWORD } type;
90         unsigned int lineno;
91         char *str;
92         struct command *next;
93 };
94
95 /*
96  *
97  */
98
99 static struct command *commands, **next = &commands;
100
101 static sig_atomic_t alarmset = 0;
102
103 static int opt_timeout = 10;
104 static int opt_verbose;
105
106 static int master;
107 static int slave;
108 static char line[256] = { 0 };
109
110 static void caught_signal(int signo)
111 {
112         alarmset = signo;
113 }
114
115
116 static void open_pty(void)
117 {
118 #ifdef _AIX
119         printf("implement open_pty\n");
120         exit(77);
121 #endif
122 #if defined(HAVE_OPENPTY) || defined(__linux) || defined(__osf__) /* XXX */
123         if(openpty(&master, &slave, line, 0, 0) == 0)
124                 return;
125 #endif /* HAVE_OPENPTY .... */
126 #ifdef STREAMSPTY
127         {
128                 char *clone[] = {
129                         "/dev/ptc",
130                         "/dev/ptmx",
131                         "/dev/ptm",
132                         "/dev/ptym/clone",
133                         NULL
134                 };
135                 char **q;
136
137                 for(q = clone; *q; q++){
138                         master = open(*q, O_RDWR);
139                         if(master >= 0){
140 #ifdef HAVE_GRANTPT
141                                 grantpt(master);
142 #endif
143 #ifdef HAVE_UNLOCKPT
144                                 unlockpt(master);
145 #endif
146                                 strlcpy(line, ptsname(master), sizeof(line));
147                                 slave = open(line, O_RDWR);
148                                 if (slave < 0)
149                                         errx(1, "failed to open slave when using %s", *q);
150                                 ioctl(slave, I_PUSH, "ptem");
151                                 ioctl(slave, I_PUSH, "ldterm");
152
153                                 return;
154                         }
155                 }
156         }
157 #endif /* STREAMSPTY */
158
159         /* more cases, like open /dev/ptmx, etc */
160
161         exit(77);
162 }
163
164 /*
165  *
166  */
167
168 static char *iscmd(const char *buf, const char *s)
169 {
170         size_t len = strlen(s);
171
172         if (strncmp(buf, s, len) != 0) {
173                 return NULL;
174         }
175
176         return strdup(buf + len);
177 }
178
179 /*******************************************************************
180 A write wrapper that will deal with EINTR.
181 ********************************************************************/
182
183 static ssize_t sys_write(int fd, const void *buf, size_t count)
184 {
185         ssize_t ret;
186
187         do {
188                 ret = write(fd, buf, count);
189 #if defined(EWOULDBLOCK)
190         } while (ret == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK));
191 #else
192         } while (ret == -1 && (errno == EINTR || errno == EAGAIN));
193 #endif
194         return ret;
195 }
196
197 static void parse_configuration(const char *fn)
198 {
199         struct command *c;
200         char s[1024];
201         char *str;
202         unsigned int lineno = 0;
203         FILE *cmd;
204
205         cmd = fopen(fn, "r");
206         if (cmd == NULL)
207                 err(1, "open: %s", fn);
208
209         while (fgets(s, sizeof(s),  cmd) != NULL) {
210
211                 s[strcspn(s, "#\n")] = '\0';
212                 lineno++;
213
214                 c = calloc(1, sizeof(*c));
215                 if (c == NULL)
216                         errx(1, "malloc");
217
218                 c->lineno = lineno;
219                 (*next) = c;
220                 next = &(c->next);
221
222                 if ((str = iscmd(s, "expect ")) != NULL) {
223                         c->type = CMD_EXPECT;
224                         c->str = str;
225                 } else if ((str = iscmd(s, "send ")) != NULL) {
226                         c->type = CMD_SEND;
227                         c->str = str;
228                 } else if ((str = iscmd(s, "password ")) != NULL) {
229                         c->type = CMD_PASSWORD;
230                         c->str = str;
231                 } else
232                         errx(1, "Invalid command on line %d: %s", lineno, s);
233         }
234
235         fclose(cmd);
236 }
237
238 /* A wrapper to close als file descriptors above the given fd */
239 static int sys_closefrom(int fd)
240 {
241         int num = getdtablesize();
242
243         if (num < 0) {
244                 num = 1024;
245         }
246
247         for (; fd <= num; fd++) {
248                 close(fd);
249         }
250
251         return 0;
252 }
253
254
255 /*
256  *
257  */
258
259 static int eval_parent(pid_t pid)
260 {
261         struct command *c;
262         char in;
263         size_t len = 0;
264         ssize_t sret;
265
266         for (c = commands; c != NULL; c = c->next) {
267                 switch(c->type) {
268                 case CMD_EXPECT:
269                         if (opt_verbose) {
270                                 printf("[expecting %s]\n", c->str);
271                         }
272                         len = 0;
273                         alarm(opt_timeout);
274                         while((sret = read(master, &in, sizeof(in))) > 0) {
275                                 alarm(opt_timeout);
276                                 printf("%c", in);
277                                 if (c->str[len] != in) {
278                                         len = 0;
279                                         continue;
280                                 }
281                                 len++;
282                                 if (c->str[len] == '\0') {
283                                         break;
284                                 }
285                         }
286                         alarm(0);
287                         if (alarmset == SIGALRM) {
288                                 errx(1, "timeout waiting for %s (line %u)",
289                                                 c->str, c->lineno);
290                         } else if (alarmset) {
291                                 errx(1, "got a signal %d waiting for %s (line %u)",
292                                                 (int)alarmset, c->str, c->lineno);
293                         }
294
295                         if (sret <= 0) {
296                                 errx(1, "end command while waiting for %s (line %u)",
297                                                 c->str, c->lineno);
298                         }
299                         break;
300                 case CMD_SEND:
301                 case CMD_PASSWORD: {
302                         size_t i = 0;
303                         const char *msg = (c->type == CMD_PASSWORD) ? "****" : c->str;
304
305                         if (opt_verbose) {
306                                 printf("[send %s]\n", msg);
307                         }
308
309                         len = strlen(c->str);
310
311                         while (i < len) {
312                                 if (c->str[i] == '\\' && i < len - 1) {
313                                         char ctrl;
314                                         i++;
315                                         switch(c->str[i]) {
316                                         case 'n':
317                                                 ctrl = '\n';
318                                                 break;
319                                         case 'r':
320                                                 ctrl = '\r';
321                                                 break;
322                                         case 't':
323                                                 ctrl = '\t';
324                                                 break;
325                                         default:
326                                                 errx(1,
327                                                      "unknown control char %c (line %u)",
328                                                      c->str[i],
329                                                      c->lineno);
330                                         }
331                                         if (sys_write(master, &ctrl, 1) != 1) {
332                                                 errx(1, "command refused input (line %u)", c->lineno);
333                                         }
334                                 } else {
335                                         if (sys_write(master, &c->str[i], 1) != 1) {
336                                                 errx(1, "command refused input (line %u)", c->lineno);
337                                         }
338                                 }
339                                 i++;
340                         }
341                         break;
342                 }
343                 default:
344                         abort();
345                 }
346         }
347
348         while(read(master, &in, sizeof(in)) > 0) {
349                 printf("%c", in);
350         }
351
352         if (opt_verbose) {
353                 printf("[end of program]\n");
354         }
355
356         /*
357          * Fetch status from child
358          */
359         {
360                 int ret, status;
361
362                 ret = waitpid(pid, &status, 0);
363                 if (ret == -1) {
364                         err(1, "waitpid");
365                 }
366
367                 if (WIFEXITED(status) && WEXITSTATUS(status)) {
368                         return WEXITSTATUS(status);
369                 } else if (WIFSIGNALED(status)) {
370                         printf("killed by signal: %d\n", WTERMSIG(status));
371                         return 1;
372                 }
373         }
374
375         return 0;
376 }
377
378 /*
379  *
380  */
381 struct poptOption long_options[] = {
382         POPT_AUTOHELP
383         {
384                 .longName  = "timeout",
385                 .shortName = 't',
386                 .argInfo   = POPT_ARG_INT,
387                 .arg       = &opt_timeout,
388                 .val       = 't',
389         },
390         {
391                 .longName  = "verbose",
392                 .shortName = 'v',
393                 .argInfo   = POPT_ARG_NONE,
394                 .arg       = &opt_verbose,
395                 .val       = 'v',
396         },
397         POPT_TABLEEND
398 };
399
400 int main(int argc, const char **argv)
401 {
402         int optidx = 0;
403         pid_t pid;
404         poptContext pc;
405         const char *instruction_file;
406         const char **args;
407         const char *program;
408         char * const *program_args;
409
410         pc = poptGetContext("texpect",
411                             argc,
412                             argv,
413                             long_options,
414                             POPT_CONTEXT_POSIXMEHARDER);
415
416         if (argc == 1) {
417                 poptPrintHelp(pc, stderr, 0);
418                 return 1;
419         }
420
421         while ((optidx = poptGetNextOpt(pc)) != -1) {
422                 ;;
423         }
424
425         instruction_file = poptGetArg(pc);
426         args = poptGetArgs(pc);
427         program_args = (char * const *)discard_const_p(char *, args);
428         program = program_args[0];
429
430         if (opt_verbose) {
431                 int i;
432
433                 printf("Using instruction_file: %s\n", instruction_file);
434                 printf("Executing '%s' ", program);
435                 for (i = 0; program_args && program_args[i] != NULL; i++) {
436                         printf("'%s' ", program_args[i]);
437                 }
438                 printf("\n");
439         }
440
441         parse_configuration(instruction_file);
442
443         open_pty();
444
445         pid = fork();
446         switch (pid) {
447                 case -1:
448                         err(1, "Failed to fork");
449
450                         /* Never reached */
451                         return 1;
452                 case 0:
453
454                         if(setsid()<0)
455                                 err(1, "setsid");
456
457                         dup2(slave, STDIN_FILENO);
458                         dup2(slave, STDOUT_FILENO);
459                         dup2(slave, STDERR_FILENO);
460
461                         sys_closefrom(STDERR_FILENO + 1);
462
463                         /* texpect <expect_instructions> <progname> [<args>] */
464                         execvp(program, program_args);
465                         err(1, "Failed to exec: %s", program);
466
467                         /* Never reached */
468                         return 1;
469                 default:
470                         close(slave);
471                         {
472                                 struct sigaction sa;
473
474                                 sa.sa_handler = caught_signal;
475                                 sa.sa_flags = 0;
476                                 sigemptyset (&sa.sa_mask);
477
478                                 sigaction(SIGALRM, &sa, NULL);
479                         }
480
481                         return eval_parent(pid);
482         }
483
484         /* Never reached */
485         return 1;
486 }