s3: oplock: Fix race condition when closing an oplocked file.
[samba.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         {"timeout", 't', POPT_ARG_INT,  &opt_timeout, 't'},
384         {"verbose", 'v', POPT_ARG_NONE, &opt_verbose, 'v'},
385         POPT_TABLEEND
386 };
387
388 int main(int argc, const char **argv)
389 {
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;
397
398         pc = poptGetContext("texpect",
399                             argc,
400                             argv,
401                             long_options,
402                             POPT_CONTEXT_POSIXMEHARDER);
403
404         if (argc == 1) {
405                 poptPrintHelp(pc, stderr, 0);
406                 return 1;
407         }
408
409         while ((optidx = poptGetNextOpt(pc)) != -1) {
410                 ;;
411         }
412
413         instruction_file = poptGetArg(pc);
414         args = poptGetArgs(pc);
415         program_args = (char * const *)discard_const_p(char *, args);
416         program = program_args[0];
417
418         if (opt_verbose) {
419                 int i;
420
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]);
425                 }
426                 printf("\n");
427         }
428
429         parse_configuration(instruction_file);
430
431         open_pty();
432
433         pid = fork();
434         switch (pid) {
435                 case -1:
436                         err(1, "Failed to fork");
437                 case 0:
438
439                         if(setsid()<0)
440                                 err(1, "setsid");
441
442                         dup2(slave, STDIN_FILENO);
443                         dup2(slave, STDOUT_FILENO);
444                         dup2(slave, STDERR_FILENO);
445
446                         sys_closefrom(STDERR_FILENO + 1);
447
448                         /* texpect <expect_instructions> <progname> [<args>] */
449                         execvp(program, program_args);
450                         err(1, "Failed to exec: %s", program);
451                 default:
452                         close(slave);
453                         {
454                                 struct sigaction sa;
455
456                                 sa.sa_handler = caught_signal;
457                                 sa.sa_flags = 0;
458                                 sigemptyset (&sa.sa_mask);
459
460                                 sigaction(SIGALRM, &sa, NULL);
461                         }
462
463                         return eval_parent(pid);
464         }
465 }