Small fix to documentation.
[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 <string.h>
25 #include <fcntl.h>
26 #include <unistd.h>
27 #include <poll.h>
28 #include <errno.h>
29 #include <termios.h>
30 #include <time.h>
31 #include <assert.h>
32 #include <sys/types.h>
33 #include <sys/wait.h>
34 #include <sys/time.h>
35
36 #include <pcre.h>
37
38 /* RHEL 6 pcre did not define PCRE_PARTIAL_SOFT.  However PCRE_PARTIAL
39  * is a synonym so use that.
40  */
41 #ifndef PCRE_PARTIAL_SOFT
42 #define PCRE_PARTIAL_SOFT PCRE_PARTIAL
43 #endif
44
45 #include "miniexpect.h"
46
47 #define DEBUG 0
48
49 static mexp_h *
50 create_handle (void)
51 {
52   mexp_h *h = malloc (sizeof *h);
53   if (h == NULL)
54     return NULL;
55
56   /* Initialize the fields to default values. */
57   h->fd = -1;
58   h->pid = 0;
59   h->timeout = 60000;
60   h->read_size = 1024;
61   h->pcre_error = 0;
62   h->buffer = NULL;
63   h->len = h->alloc = 0;
64   h->next_match = -1;
65   h->user1 = h->user2 = h->user3 = NULL;
66
67   return h;
68 }
69
70 static void
71 clear_buffer (mexp_h *h)
72 {
73   free (h->buffer);
74   h->buffer = NULL;
75   h->alloc = h->len = 0;
76   h->next_match = -1;
77 }
78
79 int
80 mexp_close (mexp_h *h)
81 {
82   int status = 0;
83
84   free (h->buffer);
85
86   if (h->fd >= 0)
87     close (h->fd);
88   if (h->pid > 0) {
89     if (waitpid (h->pid, &status, 0) == -1)
90       return -1;
91   }
92
93   free (h);
94
95   return status;
96 }
97
98 mexp_h *
99 mexp_spawnl (const char *file, const char *arg, ...)
100 {
101   char **argv, **new_argv;
102   size_t i;
103   va_list args;
104   mexp_h *h;
105
106   argv = malloc (sizeof (char *));
107   if (argv == NULL)
108     return NULL;
109   argv[0] = (char *) arg;
110
111   va_start (args, arg);
112   for (i = 1; arg != NULL; ++i) {
113     arg = va_arg (args, const char *);
114     new_argv = realloc (argv, sizeof (char *) * (i+1));
115     if (new_argv == NULL) {
116       free (argv);
117       va_end (args);
118       return NULL;
119     }
120     argv = new_argv;
121     argv[i] = (char *) arg;
122   }
123
124   h = mexp_spawnv (file, argv);
125   free (argv);
126   va_end (args);
127   return h;
128 }
129
130 mexp_h *
131 mexp_spawnv (const char *file, char **argv)
132 {
133   mexp_h *h = NULL;
134   int fd = -1;
135   int err;
136   char slave[1024];
137   pid_t pid = 0;
138
139   fd = posix_openpt (O_RDWR|O_NOCTTY);
140   if (fd == -1)
141     goto error;
142
143   if (grantpt (fd) == -1)
144     goto error;
145
146   if (unlockpt (fd) == -1)
147     goto error;
148
149   /* Get the slave pty name now, but don't open it in the parent. */
150   if (ptsname_r (fd, slave, sizeof slave) != 0)
151     goto error;
152
153   /* Create the handle last before we fork. */
154   h = create_handle ();
155   if (h == NULL)
156     goto error;
157
158   pid = fork ();
159   if (pid == -1)
160     goto error;
161
162   if (pid == 0) {               /* Child. */
163     struct termios terminal_settings;
164     int slave_fd;
165
166     setsid ();
167
168     /* Open the slave side of the pty.  We must do this in the child
169      * after setsid so it becomes our controlling tty.
170      */
171     slave_fd = open (slave, O_RDWR);
172     if (slave_fd == -1)
173       goto error;
174
175     /* Set raw mode. */
176     tcgetattr (slave_fd, &terminal_settings);
177     cfmakeraw (&terminal_settings);
178     tcsetattr (slave_fd, TCSANOW, &terminal_settings);
179
180     /* Set up stdin, stdout, stderr to point to the pty. */
181     dup2 (slave_fd, 0);
182     dup2 (slave_fd, 1);
183     dup2 (slave_fd, 2);
184     close (slave_fd);
185
186     /* Close the master side of the pty - do this late to avoid a
187      * kernel bug, see sshpass source code.
188      */
189     close (fd);
190
191     /* Run the subprocess. */
192     execvp (file, argv);
193     perror (file);
194     _exit (EXIT_FAILURE);
195   }
196
197   /* Parent. */
198
199   h->fd = fd;
200   h->pid = pid;
201   return h;
202
203  error:
204   err = errno;
205   if (fd >= 0)
206     close (fd);
207   if (pid > 0)
208     waitpid (pid, NULL, 0);
209   if (h != NULL)
210     mexp_close (h);
211   errno = err;
212   return NULL;
213 }
214
215 enum mexp_status
216 mexp_expect (mexp_h *h, const mexp_regexp *regexps, int *ovector, int ovecsize)
217 {
218   time_t start_t, now_t;
219   int timeout;
220   struct pollfd pfds[1];
221   int r;
222   ssize_t rs;
223
224   time (&start_t);
225
226   if (h->next_match == -1) {
227     /* Fully clear the buffer, then read. */
228     clear_buffer (h);
229   } else {
230     /* See the comment in the manual about h->next_match.  We have
231      * some data remaining in the buffer, so begin by matching that.
232      */
233     memmove (&h->buffer[0], &h->buffer[h->next_match], h->len - h->next_match);
234     h->len -= h->next_match;
235     h->buffer[h->len] = '\0';
236     h->next_match = -1;
237     goto try_match;
238   }
239
240   for (;;) {
241     /* If we've got a timeout then work out how many seconds are left.
242      * Timeout == 0 is not particularly well-defined, but it probably
243      * means "return immediately if there's no data to be read".
244      */
245     if (h->timeout >= 0) {
246       time (&now_t);
247       timeout = h->timeout - ((now_t - start_t) * 1000);
248       if (timeout < 0)
249         timeout = 0;
250     }
251     else
252       timeout = 0;
253
254     pfds[0].fd = h->fd;
255     pfds[0].events = POLLIN;
256     pfds[0].revents = 0;
257     r = poll (pfds, 1, timeout);
258 #if DEBUG
259     fprintf (stderr, "DEBUG: poll returned %d\n", r);
260 #endif
261     if (r == -1)
262       return MEXP_ERROR;
263
264     if (r == 0)
265       return MEXP_TIMEOUT;
266
267     /* Otherwise we expect there is something to read from the file
268      * descriptor.
269      */
270     if (h->alloc - h->len <= h->read_size) {
271       char *new_buffer;
272       /* +1 here allows us to store \0 after the data read */
273       new_buffer = realloc (h->buffer, h->alloc + h->read_size + 1);
274       if (new_buffer == NULL)
275         return MEXP_ERROR;
276       h->buffer = new_buffer;
277       h->alloc += h->read_size;
278     }
279     rs = read (h->fd, h->buffer + h->len, h->read_size);
280 #if DEBUG
281     fprintf (stderr, "DEBUG: read returned %zd\n", rs);
282 #endif
283     if (rs == -1) {
284       /* Annoyingly on Linux (I'm fairly sure this is a bug) if the
285        * writer closes the connection, the entire pty is destroyed,
286        * and read returns -1 / EIO.  Handle that special case here.
287        */
288       if (errno == EIO)
289         return MEXP_EOF;
290       return MEXP_ERROR;
291     }
292     if (rs == 0)
293       return MEXP_EOF;
294
295     /* We read something. */
296     h->len += rs;
297     h->buffer[h->len] = '\0';
298 #if DEBUG
299     fprintf (stderr, "DEBUG: read %zd bytes from pty\n", rs);
300     fprintf (stderr, "DEBUG: buffer content: %s\n", h->buffer);
301 #endif
302
303   try_match:
304     /* See if there is a full or partial match against any regexp. */
305     if (regexps) {
306       size_t i;
307       int can_clear_buffer = 1;
308
309       assert (h->buffer != NULL);
310
311       for (i = 0; regexps[i].r > 0; ++i) {
312         int options = regexps[i].options | PCRE_PARTIAL_SOFT;
313
314         r = pcre_exec (regexps[i].re, regexps[i].extra,
315                        h->buffer, (int)h->len, 0,
316                        options,
317                        ovector, ovecsize);
318         h->pcre_error = r;
319
320         if (r >= 0) {
321           /* A full match. */
322           if (ovector != NULL && ovecsize >= 1 && ovector[1] >= 0)
323             h->next_match = ovector[1];
324           else
325             h->next_match = -1;
326           return regexps[i].r;
327         }
328
329         else if (r == PCRE_ERROR_NOMATCH) {
330           /* No match at all. */
331           /* (nothing here) */
332         }
333
334         else if (r == PCRE_ERROR_PARTIAL) {
335           /* Partial match.  Keep the buffer and keep reading. */
336           can_clear_buffer = 0;
337         }
338
339         else {
340           /* An actual PCRE error. */
341           return MEXP_PCRE_ERROR;
342         }
343       }
344
345       /* If none of the regular expressions matched (not partially)
346        * then we can clear the buffer.  This is an optimization.
347        */
348       if (can_clear_buffer)
349         clear_buffer (h);
350
351     } /* if (regexps) */
352   }
353 }
354
355 int
356 mexp_printf (mexp_h *h, const char *fs, ...)
357 {
358   va_list args;
359   char *msg;
360   int len;
361   size_t n;
362   ssize_t r;
363   char *p;
364
365   va_start (args, fs);
366   len = vasprintf (&msg, fs, args);
367   va_end (args);
368
369   if (len < 0)
370     return -1;
371
372 #if DEBUG
373   fprintf (stderr, "DEBUG: writing: %s\n", msg);
374 #endif
375
376   n = len;
377   p = msg;
378   while (n > 0) {
379     r = write (h->fd, p, n);
380     if (r == -1) {
381       free (msg);
382       return -1;
383     }
384     n -= r;
385     p += r;
386   }
387
388   free (msg);
389   return len;
390 }