Line data Source code
1 : /* asschk.c - Assuan Server Checker
2 : * Copyright (C) 2002 Free Software Foundation, Inc.
3 : *
4 : * This file is part of GnuPG.
5 : *
6 : * GnuPG is free software; you can redistribute it and/or modify
7 : * it under the terms of the GNU General Public License as published by
8 : * the Free Software Foundation; either version 3 of the License, or
9 : * (at your option) any later version.
10 : *
11 : * GnuPG is distributed in the hope that it will be useful,
12 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : * GNU General Public License for more details.
15 : *
16 : * You should have received a copy of the GNU General Public License
17 : * along with this program; if not, see <https://www.gnu.org/licenses/>.
18 : */
19 :
20 : /* This is a simple stand-alone Assuan server test program. We don't
21 : want to use the assuan library because we don't want to hide errors
22 : in that library.
23 :
24 : The script language is line based. Empty lines or lines containing
25 : only white spaces are ignored, line with a hash sign as first non
26 : white space character are treated as comments.
27 :
28 : A simple macro mechanism is implemnted. Macros are expanded before
29 : a line is processed but after comment processing. Macros are only
30 : expanded once and non existing macros expand to the empty string.
31 : A macro is dereferenced by prefixing its name with a dollar sign;
32 : the end of the name is currently indicated by a white space, a
33 : dollar sign or a slash. To use a dollor sign verbatim, double it.
34 :
35 : A macro is assigned by prefixing a statement with the macro name
36 : and an equal sign. The value is assigned verbatim if it does not
37 : resemble a command, otherwise the return value of the command will
38 : get assigned. The command "let" may be used to assign values
39 : unambigiously and it should be used if the value starts with a
40 : letter.
41 :
42 : Conditions are not yes implemented except for a simple evaluation
43 : which yields false for an empty string or the string "0". The
44 : result may be negated by prefixing with a '!'.
45 :
46 : The general syntax of a command is:
47 :
48 : [<name> =] <statement> [<args>]
49 :
50 : If NAME is not specifed but the statement returns a value it is
51 : assigned to the name "?" so that it can be referenced using "$?".
52 : The following commands are implemented:
53 :
54 : let <value>
55 : Return VALUE.
56 :
57 : echo <value>
58 : Print VALUE.
59 :
60 : openfile <filename>
61 : Open file FILENAME for read access and return the file descriptor.
62 :
63 : createfile <filename>
64 : Create file FILENAME, open for write access and return the file
65 : descriptor.
66 :
67 : pipeserver <program>
68 : Connect to the Assuan server PROGRAM.
69 :
70 : send <line>
71 : Send LINE to the server.
72 :
73 : expect-ok
74 : Expect an OK response from the server. Status and data out put
75 : is ignored.
76 :
77 : expect-err
78 : Expect an ERR response from the server. Status and data out put
79 : is ignored.
80 :
81 : count-status <code>
82 : Initialize the assigned variable to 0 and assign it as an counter for
83 : status code CODE. This command must be called with an assignment.
84 :
85 : quit
86 : Terminate the process.
87 :
88 : quit-if <condition>
89 : Terminate the process if CONDITION evaluates to true.
90 :
91 : fail-if <condition>
92 : Terminate the process with an exit code of 1 if CONDITION
93 : evaluates to true.
94 :
95 : cmpfiles <first> <second>
96 : Returns true when the content of the files FIRST and SECOND match.
97 :
98 : getenv <name>
99 : Return the value of the environment variable NAME.
100 :
101 : */
102 :
103 : #include <stdio.h>
104 : #include <stdlib.h>
105 : #include <string.h>
106 : #include <errno.h>
107 : #include <stdarg.h>
108 : #include <assert.h>
109 : #include <unistd.h>
110 : #include <fcntl.h>
111 :
112 : #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 )
113 : # define ATTR_PRINTF(f,a) __attribute__ ((format (printf,f,a)))
114 : #else
115 : # define ATTR_PRINTF(f,a)
116 : #endif
117 :
118 : #if __STDC_VERSION__ < 199901L
119 : # if __GNUC__ >= 2
120 : # define __func__ __FUNCTION__
121 : # else
122 : /* Let's try our luck here. Some systems may provide __func__ without
123 : providing __STDC_VERSION__ 199901L. */
124 : # if 0
125 : # define __func__ "<unknown>"
126 : # endif
127 : # endif
128 : #endif
129 :
130 : #define spacep(p) (*(p) == ' ' || *(p) == '\t')
131 :
132 : #define MAX_LINELEN 2048
133 :
134 : typedef enum {
135 : LINE_OK = 0,
136 : LINE_ERR,
137 : LINE_STAT,
138 : LINE_DATA,
139 : LINE_END,
140 : } LINETYPE;
141 :
142 : typedef enum {
143 : VARTYPE_SIMPLE = 0,
144 : VARTYPE_FD,
145 : VARTYPE_COUNTER
146 : } VARTYPE;
147 :
148 :
149 : struct variable_s {
150 : struct variable_s *next;
151 : VARTYPE type;
152 : unsigned int count;
153 : char *value;
154 : char name[1];
155 : };
156 : typedef struct variable_s *VARIABLE;
157 :
158 :
159 : static void die (const char *format, ...) ATTR_PRINTF(1,2);
160 :
161 :
162 : /* Name of this program to be printed in error messages. */
163 : static const char *invocation_name;
164 :
165 : /* Talk a bit about what is going on. */
166 : static int opt_verbose;
167 :
168 : /* Option to ignore the echo command. */
169 : static int opt_no_echo;
170 :
171 : /* File descriptors used to communicate with the current server. */
172 : static int server_send_fd = -1;
173 : static int server_recv_fd = -1;
174 :
175 : /* The Assuan protocol limits the line length to 1024, so we can
176 : safely use a (larger) buffer. The buffer is filled using the
177 : read_assuan(). */
178 : static char recv_line[MAX_LINELEN];
179 : /* Tell the status of the current line. */
180 : static LINETYPE recv_type;
181 :
182 : /* This is our variable storage. */
183 : static VARIABLE variable_list;
184 :
185 :
186 : static void
187 0 : die (const char *format, ...)
188 : {
189 : va_list arg_ptr;
190 :
191 0 : fflush (stdout);
192 0 : fprintf (stderr, "%s: ", invocation_name);
193 :
194 0 : va_start (arg_ptr, format);
195 0 : vfprintf (stderr, format, arg_ptr);
196 0 : va_end (arg_ptr);
197 0 : putc ('\n', stderr);
198 :
199 0 : exit (1);
200 : }
201 :
202 : #define die_0(format) (die) ("%s: " format, __func__)
203 : #define die_1(format, a) (die) ("%s: " format, __func__, (a))
204 : #define die_2(format, a, b) (die) ("%s: " format, __func__, (a),(b))
205 : #define die_3(format, a, b, c) (die) ("%s: " format, __func__, (a),(b),(c))
206 :
207 : static void
208 0 : err (const char *format, ...)
209 : {
210 : va_list arg_ptr;
211 :
212 0 : fflush (stdout);
213 0 : fprintf (stderr, "%s: ", invocation_name);
214 :
215 0 : va_start (arg_ptr, format);
216 0 : vfprintf (stderr, format, arg_ptr);
217 0 : va_end (arg_ptr);
218 0 : putc ('\n', stderr);
219 0 : }
220 :
221 : static void *
222 0 : xmalloc (size_t n)
223 : {
224 0 : void *p = malloc (n);
225 0 : if (!p)
226 0 : die ("out of core");
227 0 : return p;
228 : }
229 :
230 : static void *
231 0 : xcalloc (size_t n, size_t m)
232 : {
233 0 : void *p = calloc (n, m);
234 0 : if (!p)
235 0 : die ("out of core");
236 0 : return p;
237 : }
238 :
239 : static char *
240 0 : xstrdup (const char *s)
241 : {
242 0 : char *p = xmalloc (strlen (s)+1);
243 0 : strcpy (p, s);
244 0 : return p;
245 : }
246 :
247 :
248 : /* Write LENGTH bytes from BUFFER to FD. */
249 : static int
250 0 : writen (int fd, const char *buffer, size_t length)
251 : {
252 0 : while (length)
253 : {
254 0 : int nwritten = write (fd, buffer, length);
255 :
256 0 : if (nwritten < 0)
257 : {
258 0 : if (errno == EINTR)
259 0 : continue;
260 0 : return -1; /* write error */
261 : }
262 0 : length -= nwritten;
263 0 : buffer += nwritten;
264 : }
265 0 : return 0; /* okay */
266 : }
267 :
268 :
269 :
270 :
271 : /* Assuan specific stuff. */
272 :
273 : /* Read a line from FD, store it in the global recv_line, analyze the
274 : type and store that in recv_type. The function terminates on a
275 : communication error. Returns a pointer into the inputline to the
276 : first byte of the arguments. The parsing is very strict to match
277 : exaclty what we want to send. */
278 : static char *
279 0 : read_assuan (int fd)
280 : {
281 : /* FIXME: For general robustness, the pending stuff needs to be
282 : associated with FD. */
283 : static char pending[MAX_LINELEN];
284 : static size_t pending_len;
285 0 : size_t nleft = sizeof recv_line;
286 0 : char *buf = recv_line;
287 : char *p;
288 :
289 0 : while (nleft > 0)
290 : {
291 : int n;
292 :
293 0 : if (pending_len)
294 : {
295 0 : if (pending_len >= nleft)
296 0 : die_0 ("received line too large");
297 0 : memcpy (buf, pending, pending_len);
298 0 : n = pending_len;
299 0 : pending_len = 0;
300 : }
301 : else
302 : {
303 : do
304 : {
305 0 : n = read (fd, buf, nleft);
306 : }
307 0 : while (n < 0 && errno == EINTR);
308 : }
309 :
310 0 : if (opt_verbose && n >= 0 )
311 : {
312 : int i;
313 :
314 0 : printf ("%s: read \"", __func__);
315 0 : for (i = 0; i < n; i ++)
316 0 : putc (buf[i], stdout);
317 0 : printf ("\"\n");
318 : }
319 :
320 0 : if (n < 0)
321 0 : die_2 ("reading fd %d failed: %s", fd, strerror (errno));
322 0 : else if (!n)
323 0 : die_1 ("received incomplete line on fd %d", fd);
324 0 : p = buf;
325 0 : nleft -= n;
326 0 : buf += n;
327 :
328 0 : for (; n && *p != '\n'; n--, p++)
329 : ;
330 0 : if (n)
331 : {
332 0 : if (n>1)
333 : {
334 0 : n--;
335 0 : memcpy (pending, p + 1, n);
336 0 : pending_len = n;
337 : }
338 0 : *p = '\0';
339 0 : break;
340 : }
341 : }
342 0 : if (!nleft)
343 0 : die_0 ("received line too large");
344 :
345 0 : p = recv_line;
346 0 : if (p[0] == 'O' && p[1] == 'K' && (p[2] == ' ' || !p[2]))
347 : {
348 0 : recv_type = LINE_OK;
349 0 : p += 3;
350 : }
351 0 : else if (p[0] == 'E' && p[1] == 'R' && p[2] == 'R'
352 0 : && (p[3] == ' ' || !p[3]))
353 : {
354 0 : recv_type = LINE_ERR;
355 0 : p += 4;
356 : }
357 0 : else if (p[0] == 'S' && (p[1] == ' ' || !p[1]))
358 : {
359 0 : recv_type = LINE_STAT;
360 0 : p += 2;
361 : }
362 0 : else if (p[0] == 'D' && p[1] == ' ')
363 : {
364 0 : recv_type = LINE_DATA;
365 0 : p += 2;
366 : }
367 0 : else if (p[0] == 'E' && p[1] == 'N' && p[2] == 'D' && !p[3])
368 : {
369 0 : recv_type = LINE_END;
370 0 : p += 3;
371 : }
372 : else
373 0 : die_1 ("invalid line type (%.5s)", p);
374 :
375 0 : return p;
376 : }
377 :
378 : /* Write LINE to the server using FD. It is expected that the line
379 : contains the terminating linefeed as last character. */
380 : static void
381 0 : write_assuan (int fd, const char *line)
382 : {
383 : char buffer[1026];
384 0 : size_t n = strlen (line);
385 :
386 0 : if (n > 1024)
387 0 : die_0 ("line too long for Assuan protocol");
388 0 : strcpy (buffer, line);
389 0 : if (!n || buffer[n-1] != '\n')
390 0 : buffer[n++] = '\n';
391 :
392 0 : if (writen (fd, buffer, n))
393 0 : die_3 ("sending line (\"%s\") to %d failed: %s", buffer, fd,
394 : strerror (errno));
395 0 : }
396 :
397 :
398 : /* Start the server with path PGMNAME and connect its stdout and
399 : strerr to a newly created pipes; the file descriptors are then
400 : store in the gloabl variables SERVER_SEND_FD and
401 : SERVER_RECV_FD. The initial handcheck is performed.*/
402 : static void
403 0 : start_server (const char *pgmname)
404 : {
405 : int rp[2];
406 : int wp[2];
407 : pid_t pid;
408 :
409 0 : if (pipe (rp) < 0)
410 0 : die_1 ("pipe creation failed: %s", strerror (errno));
411 0 : if (pipe (wp) < 0)
412 0 : die_1 ("pipe creation failed: %s", strerror (errno));
413 :
414 0 : fflush (stdout);
415 0 : fflush (stderr);
416 0 : pid = fork ();
417 0 : if (pid < 0)
418 0 : die_0 ("fork failed");
419 :
420 0 : if (!pid)
421 : {
422 : const char *arg0;
423 :
424 0 : arg0 = strrchr (pgmname, '/');
425 0 : if (arg0)
426 0 : arg0++;
427 : else
428 0 : arg0 = pgmname;
429 :
430 0 : if (wp[0] != STDIN_FILENO)
431 : {
432 0 : if (dup2 (wp[0], STDIN_FILENO) == -1)
433 0 : die_1 ("dup2 failed in child: %s", strerror (errno));
434 0 : close (wp[0]);
435 : }
436 0 : if (rp[1] != STDOUT_FILENO)
437 : {
438 0 : if (dup2 (rp[1], STDOUT_FILENO) == -1)
439 0 : die_1 ("dup2 failed in child: %s", strerror (errno));
440 0 : close (rp[1]);
441 : }
442 0 : if (!opt_verbose)
443 : {
444 0 : int fd = open ("/dev/null", O_WRONLY);
445 0 : if (fd == -1)
446 0 : die_1 ("can't open '/dev/null': %s", strerror (errno));
447 0 : if (dup2 (fd, STDERR_FILENO) == -1)
448 0 : die_1 ("dup2 failed in child: %s", strerror (errno));
449 0 : close (fd);
450 : }
451 :
452 0 : close (wp[1]);
453 0 : close (rp[0]);
454 0 : execl (pgmname, arg0, "--server", NULL);
455 0 : die_2 ("exec failed for '%s': %s", pgmname, strerror (errno));
456 : }
457 0 : close (wp[0]);
458 0 : close (rp[1]);
459 0 : server_send_fd = wp[1];
460 0 : server_recv_fd = rp[0];
461 :
462 0 : read_assuan (server_recv_fd);
463 0 : if (recv_type != LINE_OK)
464 0 : die_0 ("no greating message");
465 0 : }
466 :
467 :
468 :
469 :
470 :
471 : /* Script intepreter. */
472 :
473 : static void
474 0 : unset_var (const char *name)
475 : {
476 : VARIABLE var;
477 :
478 0 : for (var=variable_list; var && strcmp (var->name, name); var = var->next)
479 : ;
480 0 : if (!var)
481 0 : return;
482 : /* fprintf (stderr, "unsetting '%s'\n", name); */
483 :
484 0 : if (var->type == VARTYPE_FD && var->value)
485 : {
486 : int fd;
487 :
488 0 : fd = atoi (var->value);
489 0 : if (fd != -1 && fd != 0 && fd != 1 && fd != 2)
490 0 : close (fd);
491 : }
492 :
493 0 : free (var->value);
494 0 : var->value = NULL;
495 0 : var->type = 0;
496 0 : var->count = 0;
497 : }
498 :
499 :
500 : static void
501 0 : set_type_var (const char *name, const char *value, VARTYPE type)
502 : {
503 : VARIABLE var;
504 :
505 0 : if (!name)
506 0 : name = "?";
507 0 : for (var=variable_list; var && strcmp (var->name, name); var = var->next)
508 : ;
509 0 : if (!var)
510 : {
511 0 : var = xcalloc (1, sizeof *var + strlen (name));
512 0 : strcpy (var->name, name);
513 0 : var->next = variable_list;
514 0 : variable_list = var;
515 : }
516 : else
517 : {
518 0 : free (var->value);
519 0 : var->value = NULL;
520 : }
521 :
522 0 : if (var->type == VARTYPE_FD && var->value)
523 : {
524 : int fd;
525 :
526 0 : fd = atoi (var->value);
527 0 : if (fd != -1 && fd != 0 && fd != 1 && fd != 2)
528 0 : close (fd);
529 : }
530 :
531 0 : var->type = type;
532 0 : var->count = 0;
533 0 : if (var->type == VARTYPE_COUNTER)
534 : {
535 : /* We need some extra sapce as scratch area for get_var. */
536 0 : var->value = xmalloc (strlen (value) + 1 + 20);
537 0 : strcpy (var->value, value);
538 : }
539 : else
540 0 : var->value = xstrdup (value);
541 0 : }
542 :
543 : static void
544 0 : set_var (const char *name, const char *value)
545 : {
546 0 : set_type_var (name, value, 0);
547 0 : }
548 :
549 :
550 : static const char *
551 0 : get_var (const char *name)
552 : {
553 : VARIABLE var;
554 :
555 0 : for (var=variable_list; var && strcmp (var->name, name); var = var->next)
556 : ;
557 0 : if (!var)
558 0 : return NULL;
559 0 : if (var->type == VARTYPE_COUNTER && var->value)
560 : { /* Use the scratch space allocated by set_var. */
561 0 : char *p = var->value + strlen(var->value)+1;
562 0 : sprintf (p, "%u", var->count);
563 0 : return p;
564 : }
565 : else
566 0 : return var->value;
567 : }
568 :
569 :
570 : /* Incremente all counter type variables with NAME in their VALUE. */
571 : static void
572 0 : inc_counter (const char *name)
573 : {
574 : VARIABLE var;
575 :
576 0 : if (!*name)
577 0 : return;
578 0 : for (var=variable_list; var; var = var->next)
579 : {
580 0 : if (var->type == VARTYPE_COUNTER
581 0 : && var->value && !strcmp (var->value, name))
582 0 : var->count++;
583 : }
584 : }
585 :
586 :
587 : /* Expand variables in LINE and return a new allocated buffer if
588 : required. The function might modify LINE if the expanded version
589 : fits into it. */
590 : static char *
591 0 : expand_line (char *buffer)
592 : {
593 0 : char *line = buffer;
594 : char *p, *pend;
595 : const char *value;
596 : size_t valuelen, n;
597 0 : char *result = NULL;
598 :
599 0 : while (*line)
600 : {
601 0 : p = strchr (line, '$');
602 0 : if (!p)
603 0 : return result; /* nothing more to expand */
604 :
605 0 : if (p[1] == '$') /* quoted */
606 : {
607 0 : memmove (p, p+1, strlen (p+1)+1);
608 0 : line = p + 1;
609 0 : continue;
610 : }
611 0 : for (pend=p+1; *pend && !spacep (pend)
612 0 : && *pend != '$' && *pend != '/'; pend++)
613 : ;
614 0 : if (*pend)
615 : {
616 0 : int save = *pend;
617 0 : *pend = 0;
618 0 : value = get_var (p+1);
619 0 : *pend = save;
620 : }
621 : else
622 0 : value = get_var (p+1);
623 0 : if (!value)
624 0 : value = "";
625 0 : valuelen = strlen (value);
626 0 : if (valuelen <= pend - p)
627 : {
628 0 : memcpy (p, value, valuelen);
629 0 : p += valuelen;
630 0 : n = pend - p;
631 0 : if (n)
632 0 : memmove (p, p+n, strlen (p+n)+1);
633 0 : line = p;
634 : }
635 : else
636 : {
637 0 : char *src = result? result : buffer;
638 : char *dst;
639 :
640 0 : dst = xmalloc (strlen (src) + valuelen + 1);
641 0 : n = p - src;
642 0 : memcpy (dst, src, n);
643 0 : memcpy (dst + n, value, valuelen);
644 0 : n += valuelen;
645 0 : strcpy (dst + n, pend);
646 0 : line = dst + n;
647 0 : free (result);
648 0 : result = dst;
649 : }
650 : }
651 0 : return result;
652 : }
653 :
654 :
655 : /* Evaluate COND and return the result. */
656 : static int
657 0 : eval_boolean (const char *cond)
658 : {
659 0 : int true = 1;
660 :
661 0 : for ( ; *cond == '!'; cond++)
662 0 : true = !true;
663 0 : if (!*cond || (*cond == '0' && !cond[1]))
664 0 : return !true;
665 0 : return true;
666 : }
667 :
668 :
669 :
670 :
671 :
672 : static void
673 0 : cmd_let (const char *assign_to, char *arg)
674 : {
675 0 : set_var (assign_to, arg);
676 0 : }
677 :
678 :
679 : static void
680 0 : cmd_echo (const char *assign_to, char *arg)
681 : {
682 : (void)assign_to;
683 0 : if (!opt_no_echo)
684 0 : printf ("%s\n", arg);
685 0 : }
686 :
687 : static void
688 0 : cmd_send (const char *assign_to, char *arg)
689 : {
690 : (void)assign_to;
691 0 : if (opt_verbose)
692 0 : fprintf (stderr, "sending '%s'\n", arg);
693 0 : write_assuan (server_send_fd, arg);
694 0 : }
695 :
696 : static void
697 0 : handle_status_line (char *arg)
698 : {
699 : char *p;
700 :
701 0 : for (p=arg; *p && !spacep (p); p++)
702 : ;
703 0 : if (*p)
704 : {
705 0 : int save = *p;
706 0 : *p = 0;
707 0 : inc_counter (arg);
708 0 : *p = save;
709 : }
710 : else
711 0 : inc_counter (arg);
712 0 : }
713 :
714 : static void
715 0 : cmd_expect_ok (const char *assign_to, char *arg)
716 : {
717 : (void)assign_to;
718 : (void)arg;
719 :
720 0 : if (opt_verbose)
721 0 : fprintf (stderr, "expecting OK\n");
722 : do
723 : {
724 0 : char *p = read_assuan (server_recv_fd);
725 0 : if (opt_verbose > 1)
726 0 : fprintf (stderr, "got line '%s'\n", recv_line);
727 0 : if (recv_type == LINE_STAT)
728 0 : handle_status_line (p);
729 : }
730 0 : while (recv_type != LINE_OK && recv_type != LINE_ERR);
731 0 : if (recv_type != LINE_OK)
732 0 : die_1 ("expected OK but got '%s'", recv_line);
733 0 : }
734 :
735 : static void
736 0 : cmd_expect_err (const char *assign_to, char *arg)
737 : {
738 : (void)assign_to;
739 : (void)arg;
740 :
741 0 : if (opt_verbose)
742 0 : fprintf (stderr, "expecting ERR\n");
743 : do
744 : {
745 0 : char *p = read_assuan (server_recv_fd);
746 0 : if (opt_verbose > 1)
747 0 : fprintf (stderr, "got line '%s'\n", recv_line);
748 0 : if (recv_type == LINE_STAT)
749 0 : handle_status_line (p);
750 : }
751 0 : while (recv_type != LINE_OK && recv_type != LINE_ERR);
752 0 : if (recv_type != LINE_ERR)
753 0 : die_1 ("expected ERR but got '%s'", recv_line);
754 0 : }
755 :
756 : static void
757 0 : cmd_count_status (const char *assign_to, char *arg)
758 : {
759 : char *p;
760 :
761 0 : if (!*assign_to || !*arg)
762 0 : die_0 ("syntax error: count-status requires an argument and a variable");
763 :
764 0 : for (p=arg; *p && !spacep (p); p++)
765 : ;
766 0 : if (*p)
767 : {
768 0 : for (*p++ = 0; spacep (p); p++)
769 : ;
770 0 : if (*p)
771 0 : die_0 ("cmpfiles: syntax error");
772 : }
773 0 : set_type_var (assign_to, arg, VARTYPE_COUNTER);
774 0 : }
775 :
776 : static void
777 0 : cmd_openfile (const char *assign_to, char *arg)
778 : {
779 : int fd;
780 : char numbuf[20];
781 :
782 : do
783 0 : fd = open (arg, O_RDONLY);
784 0 : while (fd == -1 && errno == EINTR);
785 0 : if (fd == -1)
786 0 : die_2 ("error opening '%s': %s", arg, strerror (errno));
787 :
788 0 : sprintf (numbuf, "%d", fd);
789 0 : set_type_var (assign_to, numbuf, VARTYPE_FD);
790 0 : }
791 :
792 : static void
793 0 : cmd_createfile (const char *assign_to, char *arg)
794 : {
795 : int fd;
796 : char numbuf[20];
797 :
798 : do
799 0 : fd = open (arg, O_WRONLY|O_CREAT|O_TRUNC, 0666);
800 0 : while (fd == -1 && errno == EINTR);
801 0 : if (fd == -1)
802 0 : die_2 ("error creating '%s': %s", arg, strerror (errno));
803 :
804 0 : sprintf (numbuf, "%d", fd);
805 0 : set_type_var (assign_to, numbuf, VARTYPE_FD);
806 0 : }
807 :
808 :
809 : static void
810 0 : cmd_pipeserver (const char *assign_to, char *arg)
811 : {
812 : (void)assign_to;
813 :
814 0 : if (!*arg)
815 0 : die_0 ("syntax error: servername missing");
816 :
817 0 : start_server (arg);
818 0 : }
819 :
820 :
821 : static void
822 0 : cmd_quit_if(const char *assign_to, char *arg)
823 : {
824 : (void)assign_to;
825 :
826 0 : if (eval_boolean (arg))
827 0 : exit (0);
828 0 : }
829 :
830 : static void
831 0 : cmd_fail_if(const char *assign_to, char *arg)
832 : {
833 : (void)assign_to;
834 :
835 0 : if (eval_boolean (arg))
836 0 : exit (1);
837 0 : }
838 :
839 :
840 : static void
841 0 : cmd_cmpfiles (const char *assign_to, char *arg)
842 : {
843 0 : char *p = arg;
844 : char *second;
845 : FILE *fp1, *fp2;
846 : char buffer1[2048]; /* note: both must be of equal size. */
847 : char buffer2[2048];
848 : size_t nread1, nread2;
849 0 : int rc = 0;
850 :
851 0 : set_var (assign_to, "0");
852 0 : for (p=arg; *p && !spacep (p); p++)
853 : ;
854 0 : if (!*p)
855 0 : die_0 ("cmpfiles: syntax error");
856 0 : for (*p++ = 0; spacep (p); p++)
857 : ;
858 0 : second = p;
859 0 : for (; *p && !spacep (p); p++)
860 : ;
861 0 : if (*p)
862 : {
863 0 : for (*p++ = 0; spacep (p); p++)
864 : ;
865 0 : if (*p)
866 0 : die_0 ("cmpfiles: syntax error");
867 : }
868 :
869 0 : fp1 = fopen (arg, "rb");
870 0 : if (!fp1)
871 : {
872 0 : err ("can't open '%s': %s", arg, strerror (errno));
873 0 : return;
874 : }
875 0 : fp2 = fopen (second, "rb");
876 0 : if (!fp2)
877 : {
878 0 : err ("can't open '%s': %s", second, strerror (errno));
879 0 : fclose (fp1);
880 0 : return;
881 : }
882 0 : while ( (nread1 = fread (buffer1, 1, sizeof buffer1, fp1)))
883 : {
884 0 : if (ferror (fp1))
885 0 : break;
886 0 : nread2 = fread (buffer2, 1, sizeof buffer2, fp2);
887 0 : if (ferror (fp2))
888 0 : break;
889 0 : if (nread1 != nread2 || memcmp (buffer1, buffer2, nread1))
890 : {
891 0 : rc = 1;
892 0 : break;
893 : }
894 : }
895 0 : if (feof (fp1) && feof (fp2) && !rc)
896 : {
897 0 : if (opt_verbose)
898 0 : err ("files match");
899 0 : set_var (assign_to, "1");
900 : }
901 0 : else if (!rc)
902 0 : err ("cmpfiles: read error: %s", strerror (errno));
903 : else
904 0 : err ("cmpfiles: mismatch");
905 0 : fclose (fp1);
906 0 : fclose (fp2);
907 : }
908 :
909 : static void
910 0 : cmd_getenv (const char *assign_to, char *arg)
911 : {
912 : const char *s;
913 0 : s = *arg? getenv (arg):"";
914 0 : set_var (assign_to, s? s:"");
915 0 : }
916 :
917 :
918 :
919 :
920 : /* Process the current script line LINE. */
921 : static int
922 0 : interpreter (char *line)
923 : {
924 : static struct {
925 : const char *name;
926 : void (*fnc)(const char*, char*);
927 : } cmdtbl[] = {
928 : { "let" , cmd_let },
929 : { "echo" , cmd_echo },
930 : { "send" , cmd_send },
931 : { "expect-ok" , cmd_expect_ok },
932 : { "expect-err", cmd_expect_err },
933 : { "count-status", cmd_count_status },
934 : { "openfile" , cmd_openfile },
935 : { "createfile", cmd_createfile },
936 : { "pipeserver", cmd_pipeserver },
937 : { "quit" , NULL },
938 : { "quit-if" , cmd_quit_if },
939 : { "fail-if" , cmd_fail_if },
940 : { "cmpfiles" , cmd_cmpfiles },
941 : { "getenv" , cmd_getenv },
942 : { NULL }
943 : };
944 : char *p, *save_p;
945 : int i, save_c;
946 0 : char *stmt = NULL;
947 0 : char *assign_to = NULL;
948 0 : char *must_free = NULL;
949 :
950 0 : for ( ;spacep (line); line++)
951 : ;
952 0 : if (!*line || *line == '#')
953 0 : return 0; /* empty or comment */
954 0 : p = expand_line (line);
955 0 : if (p)
956 : {
957 0 : must_free = p;
958 0 : line = p;
959 0 : for ( ;spacep (line); line++)
960 : ;
961 0 : if (!*line || *line == '#')
962 : {
963 0 : free (must_free);
964 0 : return 0; /* empty or comment */
965 : }
966 : }
967 0 : for (p=line; *p && !spacep (p) && *p != '='; p++)
968 : ;
969 0 : if (*p == '=')
970 : {
971 0 : *p = 0;
972 0 : assign_to = line;
973 : }
974 0 : else if (*p)
975 : {
976 0 : for (*p++ = 0; spacep (p); p++)
977 : ;
978 0 : if (*p == '=')
979 0 : assign_to = line;
980 : }
981 0 : if (!*line)
982 0 : die_0 ("syntax error");
983 0 : stmt = line;
984 0 : save_c = 0;
985 0 : save_p = NULL;
986 0 : if (assign_to)
987 : { /* this is an assignment */
988 0 : for (p++; spacep (p); p++)
989 : ;
990 0 : if (!*p)
991 : {
992 0 : unset_var (assign_to);
993 0 : free (must_free);
994 0 : return 0;
995 : }
996 0 : stmt = p;
997 0 : for (; *p && !spacep (p); p++)
998 : ;
999 0 : if (*p)
1000 : {
1001 0 : save_p = p;
1002 0 : save_c = *p;
1003 0 : for (*p++ = 0; spacep (p); p++)
1004 : ;
1005 : }
1006 : }
1007 0 : for (i=0; cmdtbl[i].name && strcmp (stmt, cmdtbl[i].name); i++)
1008 : ;
1009 0 : if (!cmdtbl[i].name)
1010 : {
1011 0 : if (!assign_to)
1012 0 : die_1 ("invalid statement '%s'\n", stmt);
1013 0 : if (save_p)
1014 0 : *save_p = save_c;
1015 0 : set_var (assign_to, stmt);
1016 0 : free (must_free);
1017 0 : return 0;
1018 : }
1019 :
1020 0 : if (cmdtbl[i].fnc)
1021 0 : cmdtbl[i].fnc (assign_to, p);
1022 0 : free (must_free);
1023 0 : return cmdtbl[i].fnc? 0:1;
1024 : }
1025 :
1026 :
1027 :
1028 : int
1029 0 : main (int argc, char **argv)
1030 : {
1031 : char buffer[2048];
1032 : char *p, *pend;
1033 :
1034 0 : if (!argc)
1035 0 : invocation_name = "asschk";
1036 : else
1037 : {
1038 0 : invocation_name = *argv++;
1039 0 : argc--;
1040 0 : p = strrchr (invocation_name, '/');
1041 0 : if (p)
1042 0 : invocation_name = p+1;
1043 : }
1044 :
1045 :
1046 0 : set_var ("?","1"); /* defaults to true */
1047 :
1048 0 : for (; argc; argc--, argv++)
1049 : {
1050 0 : p = *argv;
1051 0 : if (*p != '-')
1052 0 : break;
1053 0 : if (!strcmp (p, "--verbose"))
1054 0 : opt_verbose++;
1055 0 : else if (!strcmp (p, "--no-echo"))
1056 0 : opt_no_echo++;
1057 0 : else if (*p == '-' && p[1] == 'D')
1058 : {
1059 0 : p += 2;
1060 0 : pend = strchr (p, '=');
1061 0 : if (pend)
1062 : {
1063 0 : int tmp = *pend;
1064 0 : *pend = 0;
1065 0 : set_var (p, pend+1);
1066 0 : *pend = tmp;
1067 : }
1068 : else
1069 0 : set_var (p, "1");
1070 : }
1071 0 : else if (*p == '-' && p[1] == '-' && !p[2])
1072 : {
1073 0 : argc--; argv++;
1074 0 : break;
1075 : }
1076 : else
1077 : break;
1078 : }
1079 0 : if (argc)
1080 0 : die ("usage: asschk [--verbose] {-D<name>[=<value>]}");
1081 :
1082 :
1083 0 : while (fgets (buffer, sizeof buffer, stdin))
1084 : {
1085 0 : p = strchr (buffer,'\n');
1086 0 : if (!p)
1087 0 : die_0 ("incomplete script line");
1088 0 : *p = 0;
1089 0 : if (interpreter (buffer))
1090 0 : break;
1091 0 : fflush (stdout);
1092 : }
1093 0 : return 0;
1094 : }
|