f525b68630a7f15fa6b9d97abb7e9b2d634ff4af
[miniexpect.git] / miniexpect.c
1 /* miniexpect
2  * Copyright (C) 2014 Red Hat Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18
19 #include <config.h>
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <stdarg.h>
24 #include <fcntl.h>
25 #include <unistd.h>
26 #include <poll.h>
27 #include <errno.h>
28 #include <termios.h>
29 #include <time.h>
30 #include <assert.h>
31 #include <sys/types.h>
32 #include <sys/wait.h>
33 #include <sys/time.h>
34
35 #include <pcre.h>
36
37 #include "miniexpect.h"
38
39 #define DEBUG 0
40
41 static mexp_h *
42 create_handle (void)
43 {
44   mexp_h *h = malloc (sizeof *h);
45   if (h == NULL)
46     return NULL;
47
48   /* Initialize the fields to default values. */
49   h->fd = -1;
50   h->pid = 0;
51   h->timeout = 60000;
52   h->read_size = 1024;
53   h->buffer = NULL;
54   h->len = h->alloc = 0;
55   h->user1 = h->user2 = h->user3 = NULL;
56
57   return h;
58 }
59
60 static void
61 clear_buffer (mexp_h *h)
62 {
63   free (h->buffer);
64   h->buffer = NULL;
65   h->alloc = h->len = 0;
66 }
67
68 int
69 mexp_close (mexp_h *h)
70 {
71   int status = 0;
72
73   free (h->buffer);
74
75   if (h->fd >= 0)
76     close (h->fd);
77   if (h->pid > 0) {
78     if (waitpid (h->pid, &status, 0) == -1)
79       return -1;
80   }
81
82   return status;
83 }
84
85 mexp_h *
86 mexp_spawnl (const char *file, const char *arg, ...)
87 {
88   char **argv, **new_argv;
89   size_t i;
90   va_list args;
91   mexp_h *h;
92
93   argv = malloc (sizeof (char *));
94   if (argv == NULL)
95     return NULL;
96   argv[0] = (char *) arg;
97
98   va_start (args, arg);
99   for (i = 1; arg != NULL; ++i) {
100     arg = va_arg (args, const char *);
101     new_argv = realloc (argv, sizeof (char *) * (i+1));
102     if (new_argv == NULL) {
103       free (argv);
104       return NULL;
105     }
106     argv = new_argv;
107     argv[i] = (char *) arg;
108   }
109
110   h = mexp_spawnv (file, argv);
111   free (argv);
112   return h;
113 }
114
115 mexp_h *
116 mexp_spawnv (const char *file, char **argv)
117 {
118   mexp_h *h;
119   int fd = -1;
120   int err;
121   char slave[1024];
122   pid_t pid = 0;
123
124   fd = posix_openpt (O_RDWR|O_NOCTTY);
125   if (fd == -1)
126     goto error;
127
128   if (grantpt (fd) == -1)
129     goto error;
130
131   if (unlockpt (fd) == -1)
132     goto error;
133
134   /* Get the slave pty name now, but don't open it in the parent. */
135   if (ptsname_r (fd, slave, sizeof slave) != 0)
136     goto error;
137
138   /* Create the handle last before we fork. */
139   h = create_handle ();
140   if (h == NULL)
141     goto error;
142
143   pid = fork ();
144   if (pid == -1)
145     goto error;
146
147   if (pid == 0) {               /* Child. */
148     struct termios terminal_settings;
149     int slave_fd;
150
151     setsid ();
152
153     /* Open the slave side of the pty.  We must do this in the child
154      * after setsid so it becomes our controlling tty.
155      */
156     slave_fd = open (slave, O_RDWR);
157     if (slave_fd == -1)
158       goto error;
159
160     /* Set raw mode. */
161     tcgetattr (slave_fd, &terminal_settings);
162     cfmakeraw (&terminal_settings);
163     tcsetattr (slave_fd, TCSANOW, &terminal_settings);
164
165     /* Set up stdin, stdout, stderr to point to the pty. */
166     dup2 (slave_fd, 0);
167     dup2 (slave_fd, 1);
168     dup2 (slave_fd, 2);
169     close (slave_fd);
170
171     /* Close the master side of the pty - do this late to avoid a
172      * kernel bug, see sshpass source code.
173      */
174     close (fd);
175
176     /* Run the subprocess. */
177     execvp (file, argv);
178     perror (file);
179     _exit (EXIT_FAILURE);
180   }
181
182   /* Parent. */
183
184   h->fd = fd;
185   h->pid = pid;
186   return h;
187
188  error:
189   err = errno;
190   if (fd >= 0)
191     close (fd);
192   if (pid > 0)
193     waitpid (pid, NULL, 0);
194   errno = err;
195   return NULL;
196 }
197
198 enum mexp_status
199 mexp_expect (mexp_h *h, const pcre *code,
200              const pcre_extra *extra,
201              int options, int *ovector, int ovecsize,
202              int *pcre_ret)
203 {
204   time_t start_t, now_t;
205   int timeout;
206   struct pollfd pfds[1];
207   int r;
208   ssize_t rs;
209
210   time (&start_t);
211
212   options |= PCRE_PARTIAL_SOFT;
213
214   /* Clear the read buffer. */
215   clear_buffer (h);
216
217   for (;;) {
218     /* If we've got a timeout then work out how many seconds are left.
219      * Timeout == 0 is not particularly well-defined, but it probably
220      * means "return immediately if there's no data to be read".
221      */
222     if (h->timeout >= 0) {
223       time (&now_t);
224       timeout = h->timeout - ((now_t - start_t) * 1000);
225       if (timeout < 0)
226         timeout = 0;
227     }
228     else
229       timeout = 0;
230
231     pfds[0].fd = h->fd;
232     pfds[0].events = POLLIN;
233     pfds[0].revents = 0;
234     r = poll (pfds, 1, timeout);
235 #if DEBUG
236     fprintf (stderr, "DEBUG: poll returned %d\n", r);
237 #endif
238     if (r == -1)
239       return MEXP_ERROR;
240
241     if (r == 0)
242       return MEXP_TIMEOUT;
243
244     /* Otherwise we expect there is something to read from the file
245      * descriptor.
246      */
247     if (h->alloc - h->len <= h->read_size) {
248       char *new_buffer;
249       /* +1 here allows us to store \0 after the data read */
250       new_buffer = realloc (h->buffer, h->alloc + h->read_size + 1);
251       if (new_buffer == NULL)
252         return MEXP_ERROR;
253       h->buffer = new_buffer;
254       h->alloc += h->read_size;
255     }
256     rs = read (h->fd, h->buffer + h->len, h->read_size);
257 #if DEBUG
258     fprintf (stderr, "DEBUG: read returned %zd\n", rs);
259 #endif
260     if (rs == -1) {
261       /* Annoyingly on Linux (I'm fairly sure this is a bug) if the
262        * writer closes the connection, the entire pty is destroyed,
263        * and read returns -1 / EIO.  Handle that special case here.
264        */
265       if (errno == EIO)
266         return MEXP_EOF;
267       return MEXP_ERROR;
268     }
269     if (rs == 0)
270       return MEXP_EOF;
271
272     /* We read something. */
273     h->len += rs;
274     h->buffer[h->len] = '\0';
275 #if DEBUG
276     fprintf (stderr, "DEBUG: read %zd bytes from pty\n", rs);
277     fprintf (stderr, "DEBUG: buffer content: %s\n", h->buffer);
278 #endif
279
280     /* See if there is a full or partial match against the regular expression. */
281     if (code) {
282       assert (h->buffer != NULL);
283       r = pcre_exec (code, extra, h->buffer, (int)h->len, 0,
284                      options, ovector, ovecsize);
285       if (pcre_ret)
286         *pcre_ret = r;
287
288       if (r >= 0) {
289         /* A full match. */
290         return MEXP_MATCHED;
291       }
292
293       else if (r == PCRE_ERROR_NOMATCH) {
294         /* No match at all, so we can dump the input buffer. */
295         clear_buffer (h);
296       }
297
298       else if (r == PCRE_ERROR_PARTIAL) {
299         /* Partial match.  Keep the buffer and keep reading. */
300         /* (nothing here) */
301       }
302
303       else {
304         /* An actual PCRE error. */
305         return MEXP_PCRE_ERROR;
306       }
307     } /* if (code) */
308   }
309 }
310
311 int
312 mexp_printf (mexp_h *h, const char *fs, ...)
313 {
314   va_list args;
315   char *msg;
316   int len;
317   size_t n;
318   ssize_t r;
319   char *p;
320
321   va_start (args, fs);
322   len = vasprintf (&msg, fs, args);
323   va_end (args);
324
325   if (len < 0)
326     return -1;
327
328 #if DEBUG
329   fprintf (stderr, "DEBUG: writing: %s\n", msg);
330 #endif
331
332   n = len;
333   p = msg;
334   while (n > 0) {
335     r = write (h->fd, p, n);
336     if (r == -1) {
337       free (msg);
338       return -1;
339     }
340     n -= r;
341     p += r;
342   }
343
344   free (msg);
345   return len;
346 }