selftest: initial version of new repl_move test
[Samba.git] / lib / texpect / texpect.c
blobb553de8ca5ca543a1f4ffc6b61ec85c2cad9c3fb
1 /*
2 * Copyright (c) 2008 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
34 #include "replace.h"
35 #include "system/filesys.h"
36 #include "system/wait.h"
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
50 #ifdef STREAMSPTY
51 #include <stropts.h>
52 #endif /* STREAMPTY */
54 #include <popt.h>
56 #ifdef HAVE_ERR_H
57 #include <err.h>
58 #else
59 const char progname[] = "unknown program";
61 static void err(int eval, const char *fmt, ...)
63 int err_errno = errno;
64 va_list ap;
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);
74 static void errx(int eval, const char *fmt, ...)
76 va_list ap;
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);
86 #endif
88 struct command {
89 enum { CMD_EXPECT = 0, CMD_SEND, CMD_PASSWORD } type;
90 unsigned int lineno;
91 char *str;
92 struct command *next;
99 static struct command *commands, **next = &commands;
101 static sig_atomic_t alarmset = 0;
103 static int opt_timeout = 10;
104 static int opt_verbose;
106 static int master;
107 static int slave;
108 static char line[256] = { 0 };
110 static void caught_signal(int signo)
112 alarmset = signo;
116 static void open_pty(void)
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
128 char *clone[] = {
129 "/dev/ptc",
130 "/dev/ptmx",
131 "/dev/ptm",
132 "/dev/ptym/clone",
133 NULL
135 char **q;
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");
153 return;
157 #endif /* STREAMSPTY */
159 /* more cases, like open /dev/ptmx, etc */
161 exit(77);
168 static char *iscmd(const char *buf, const char *s)
170 size_t len = strlen(s);
172 if (strncmp(buf, s, len) != 0) {
173 return NULL;
176 return strdup(buf + len);
179 /*******************************************************************
180 A write wrapper that will deal with EINTR.
181 ********************************************************************/
183 static ssize_t sys_write(int fd, const void *buf, size_t count)
185 ssize_t ret;
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;
197 static void parse_configuration(const char *fn)
199 struct command *c;
200 char s[1024];
201 char *str;
202 unsigned int lineno = 0;
203 FILE *cmd;
205 cmd = fopen(fn, "r");
206 if (cmd == NULL)
207 err(1, "open: %s", fn);
209 while (fgets(s, sizeof(s), cmd) != NULL) {
211 s[strcspn(s, "#\n")] = '\0';
212 lineno++;
214 c = calloc(1, sizeof(*c));
215 if (c == NULL)
216 errx(1, "malloc");
218 c->lineno = lineno;
219 (*next) = c;
220 next = &(c->next);
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);
235 fclose(cmd);
238 /* A wrapper to close als file descriptors above the given fd */
239 static int sys_closefrom(int fd)
241 int num = getdtablesize();
243 if (num < 0) {
244 num = 1024;
247 for (; fd <= num; fd++) {
248 close(fd);
251 return 0;
259 static int eval_parent(pid_t pid)
261 struct command *c;
262 char in;
263 size_t len = 0;
264 ssize_t sret;
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);
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;
281 len++;
282 if (c->str[len] == '\0') {
283 break;
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);
295 if (sret <= 0) {
296 errx(1, "end command while waiting for %s (line %u)",
297 c->str, c->lineno);
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;
305 if (opt_verbose) {
306 printf("[send %s]\n", msg);
309 len = strlen(c->str);
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);
331 if (sys_write(master, &ctrl, 1) != 1) {
332 errx(1, "command refused input (line %u)", c->lineno);
334 } else {
335 if (sys_write(master, &c->str[i], 1) != 1) {
336 errx(1, "command refused input (line %u)", c->lineno);
339 i++;
341 break;
343 default:
344 abort();
348 while(read(master, &in, sizeof(in)) > 0) {
349 printf("%c", in);
352 if (opt_verbose) {
353 printf("[end of program]\n");
357 * Fetch status from child
360 int ret, status;
362 ret = waitpid(pid, &status, 0);
363 if (ret == -1) {
364 err(1, "waitpid");
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;
375 return 0;
381 struct poptOption long_options[] = {
382 POPT_AUTOHELP
383 {"timeout", 't', POPT_ARG_INT, &opt_timeout, 't'},
384 {"verbose", 'v', POPT_ARG_NONE, &opt_verbose, 'v'},
385 POPT_TABLEEND
388 int main(int argc, const char **argv)
390 int optidx = 0;
391 pid_t pid;
392 poptContext pc;
393 const char *instruction_file;
394 const char **args;
395 const char *program;
396 char * const *program_args;
398 pc = poptGetContext("texpect",
399 argc,
400 argv,
401 long_options,
402 POPT_CONTEXT_POSIXMEHARDER);
404 if (argc == 1) {
405 poptPrintHelp(pc, stderr, 0);
406 return 1;
409 while ((optidx = poptGetNextOpt(pc)) != -1) {
413 instruction_file = poptGetArg(pc);
414 args = poptGetArgs(pc);
415 program_args = (char * const *)discard_const_p(char *, args);
416 program = program_args[0];
418 if (opt_verbose) {
419 int i;
421 printf("Using instruction_file: %s\n", instruction_file);
422 printf("Executing '%s' ", program);
423 for (i = 0; program_args && program_args[i] != NULL; i++) {
424 printf("'%s' ", program_args[i]);
426 printf("\n");
429 parse_configuration(instruction_file);
431 open_pty();
433 pid = fork();
434 switch (pid) {
435 case -1:
436 err(1, "Failed to fork");
437 case 0:
439 if(setsid()<0)
440 err(1, "setsid");
442 dup2(slave, STDIN_FILENO);
443 dup2(slave, STDOUT_FILENO);
444 dup2(slave, STDERR_FILENO);
446 sys_closefrom(STDERR_FILENO + 1);
448 /* texpect <expect_instructions> <progname> [<args>] */
449 execvp(program, program_args);
450 err(1, "Failed to exec: %s", program);
451 default:
452 close(slave);
454 struct sigaction sa;
456 sa.sa_handler = caught_signal;
457 sa.sa_flags = 0;
458 sigemptyset (&sa.sa_mask);
460 sigaction(SIGALRM, &sa, NULL);
463 return eval_parent(pid);