1 /* asschk.c - Assuan Server Checker
2 * Copyright (C) 2002 Free Software Foundation, Inc.
4 * This file is part of GnuPG.
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.
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.
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/>.
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
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.
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.
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
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 '!'.
46 The general syntax of a command is:
48 [<name> =] <statement> [<args>]
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:
61 Open file FILENAME for read access and return the file descriptor.
64 Create file FILENAME, open for write access and return the file
68 Connect to the Assuan server PROGRAM.
71 Send LINE to the server.
74 Expect an OK response from the server. Status and data out put
78 Expect an ERR response from the server. Status and data out put
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.
86 Terminate the process.
89 Terminate the process if CONDITION evaluates to true.
92 Terminate the process with an exit code of 1 if CONDITION
95 cmpfiles <first> <second>
96 Returns true when the content of the files FIRST and SECOND match.
99 Return the value of the environment variable NAME.
112 #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 )
113 # define ATTR_PRINTF(f,a) __attribute__ ((format (printf,f,a)))
115 # define ATTR_PRINTF(f,a)
118 #if __STDC_VERSION__ < 199901L
120 # define __func__ __FUNCTION__
122 /* Let's try our luck here. Some systems may provide __func__ without
123 providing __STDC_VERSION__ 199901L. */
125 # define __func__ "<unknown>"
130 #define spacep(p) (*(p) == ' ' || *(p) == '\t')
132 #define MAX_LINELEN 2048
150 struct variable_s *next;
156 typedef struct variable_s *VARIABLE;
159 static void die (const char *format, ...) ATTR_PRINTF(1,2);
162 /* Name of this program to be printed in error messages. */
163 static const char *invocation_name;
165 /* Talk a bit about what is going on. */
166 static int opt_verbose;
168 /* Option to ignore the echo command. */
169 static int opt_no_echo;
171 /* File descriptors used to communicate with the current server. */
172 static int server_send_fd = -1;
173 static int server_recv_fd = -1;
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
178 static char recv_line[MAX_LINELEN];
179 /* Tell the status of the current line. */
180 static LINETYPE recv_type;
182 /* This is our variable storage. */
183 static VARIABLE variable_list;
187 die (const char *format, ...)
192 fprintf (stderr, "%s: ", invocation_name);
194 va_start (arg_ptr, format);
195 vfprintf (stderr, format, arg_ptr);
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))
208 err (const char *format, ...)
213 fprintf (stderr, "%s: ", invocation_name);
215 va_start (arg_ptr, format);
216 vfprintf (stderr, format, arg_ptr);
224 void *p = malloc (n);
231 xcalloc (size_t n, size_t m)
233 void *p = calloc (n, m);
240 xstrdup (const char *s)
242 char *p = xmalloc (strlen (s)+1);
248 /* Write LENGTH bytes from BUFFER to FD. */
250 writen (int fd, const char *buffer, size_t length)
254 int nwritten = write (fd, buffer, length);
260 return -1; /* write error */
271 /* Assuan specific stuff. */
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. */
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 size_t nleft = sizeof recv_line;
286 char *buf = recv_line;
295 if (pending_len >= nleft)
296 die_0 ("received line too large");
297 memcpy (buf, pending, pending_len);
305 n = read (fd, buf, nleft);
307 while (n < 0 && errno == EINTR);
310 if (opt_verbose && n >= 0 )
314 printf ("%s: read \"", __func__);
315 for (i = 0; i < n; i ++)
316 putc (buf[i], stdout);
321 die_2 ("reading fd %d failed: %s", fd, strerror (errno));
323 die_1 ("received incomplete line on fd %d", fd);
328 for (; n && *p != '\n'; n--, p++)
335 memcpy (pending, p + 1, n);
343 die_0 ("received line too large");
346 if (p[0] == 'O' && p[1] == 'K' && (p[2] == ' ' || !p[2]))
351 else if (p[0] == 'E' && p[1] == 'R' && p[2] == 'R'
352 && (p[3] == ' ' || !p[3]))
354 recv_type = LINE_ERR;
357 else if (p[0] == 'S' && (p[1] == ' ' || !p[1]))
359 recv_type = LINE_STAT;
362 else if (p[0] == 'D' && p[1] == ' ')
364 recv_type = LINE_DATA;
367 else if (p[0] == 'E' && p[1] == 'N' && p[2] == 'D' && !p[3])
369 recv_type = LINE_END;
373 die_1 ("invalid line type (%.5s)", p);
378 /* Write LINE to the server using FD. It is expected that the line
379 contains the terminating linefeed as last character. */
381 write_assuan (int fd, const char *line)
384 size_t n = strlen (line);
387 die_0 ("line too long for Assuan protocol");
388 strcpy (buffer, line);
389 if (!n || buffer[n-1] != '\n')
392 if (writen (fd, buffer, n))
393 die_3 ("sending line (\"%s\") to %d failed: %s", buffer, fd,
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.*/
403 start_server (const char *pgmname)
410 die_1 ("pipe creation failed: %s", strerror (errno));
412 die_1 ("pipe creation failed: %s", strerror (errno));
418 die_0 ("fork failed");
424 arg0 = strrchr (pgmname, '/');
430 if (wp[0] != STDIN_FILENO)
432 if (dup2 (wp[0], STDIN_FILENO) == -1)
433 die_1 ("dup2 failed in child: %s", strerror (errno));
436 if (rp[1] != STDOUT_FILENO)
438 if (dup2 (rp[1], STDOUT_FILENO) == -1)
439 die_1 ("dup2 failed in child: %s", strerror (errno));
444 int fd = open ("/dev/null", O_WRONLY);
446 die_1 ("can't open '/dev/null': %s", strerror (errno));
447 if (dup2 (fd, STDERR_FILENO) == -1)
448 die_1 ("dup2 failed in child: %s", strerror (errno));
454 execl (pgmname, arg0, "--server", NULL);
455 die_2 ("exec failed for '%s': %s", pgmname, strerror (errno));
459 server_send_fd = wp[1];
460 server_recv_fd = rp[0];
462 read_assuan (server_recv_fd);
463 if (recv_type != LINE_OK)
464 die_0 ("no greating message");
471 /* Script intepreter. */
474 unset_var (const char *name)
478 for (var=variable_list; var && strcmp (var->name, name); var = var->next)
482 /* fprintf (stderr, "unsetting '%s'\n", name); */
484 if (var->type == VARTYPE_FD && var->value)
488 fd = atoi (var->value);
489 if (fd != -1 && fd != 0 && fd != 1 && fd != 2)
501 set_type_var (const char *name, const char *value, VARTYPE type)
507 for (var=variable_list; var && strcmp (var->name, name); var = var->next)
511 var = xcalloc (1, sizeof *var + strlen (name));
512 strcpy (var->name, name);
513 var->next = variable_list;
522 if (var->type == VARTYPE_FD && var->value)
526 fd = atoi (var->value);
527 if (fd != -1 && fd != 0 && fd != 1 && fd != 2)
533 if (var->type == VARTYPE_COUNTER)
535 /* We need some extra sapce as scratch area for get_var. */
536 var->value = xmalloc (strlen (value) + 1 + 20);
537 strcpy (var->value, value);
540 var->value = xstrdup (value);
544 set_var (const char *name, const char *value)
546 set_type_var (name, value, 0);
551 get_var (const char *name)
555 for (var=variable_list; var && strcmp (var->name, name); var = var->next)
559 if (var->type == VARTYPE_COUNTER && var->value)
560 { /* Use the scratch space allocated by set_var. */
561 char *p = var->value + strlen(var->value)+1;
562 sprintf (p, "%u", var->count);
570 /* Incremente all counter type variables with NAME in their VALUE. */
572 inc_counter (const char *name)
578 for (var=variable_list; var; var = var->next)
580 if (var->type == VARTYPE_COUNTER
581 && var->value && !strcmp (var->value, name))
587 /* Expand variables in LINE and return a new allocated buffer if
588 required. The function might modify LINE if the expanded version
591 expand_line (char *buffer)
601 p = strchr (line, '$');
603 return result; /* nothing more to expand */
605 if (p[1] == '$') /* quoted */
607 memmove (p, p+1, strlen (p+1)+1);
611 for (pend=p+1; *pend && !spacep (pend)
612 && *pend != '$' && *pend != '/'; pend++)
618 value = get_var (p+1);
622 value = get_var (p+1);
625 valuelen = strlen (value);
626 if (valuelen <= pend - p)
628 memcpy (p, value, valuelen);
632 memmove (p, p+n, strlen (p+n)+1);
637 char *src = result? result : buffer;
640 dst = xmalloc (strlen (src) + valuelen + 1);
642 memcpy (dst, src, n);
643 memcpy (dst + n, value, valuelen);
645 strcpy (dst + n, pend);
655 /* Evaluate COND and return the result. */
657 eval_boolean (const char *cond)
661 for ( ; *cond == '!'; cond++)
663 if (!*cond || (*cond == '0' && !cond[1]))
673 cmd_let (const char *assign_to, char *arg)
675 set_var (assign_to, arg);
680 cmd_echo (const char *assign_to, char *arg)
684 printf ("%s\n", arg);
688 cmd_send (const char *assign_to, char *arg)
692 fprintf (stderr, "sending '%s'\n", arg);
693 write_assuan (server_send_fd, arg);
697 handle_status_line (char *arg)
701 for (p=arg; *p && !spacep (p); p++)
715 cmd_expect_ok (const char *assign_to, char *arg)
721 fprintf (stderr, "expecting OK\n");
724 char *p = read_assuan (server_recv_fd);
726 fprintf (stderr, "got line '%s'\n", recv_line);
727 if (recv_type == LINE_STAT)
728 handle_status_line (p);
730 while (recv_type != LINE_OK && recv_type != LINE_ERR);
731 if (recv_type != LINE_OK)
732 die_1 ("expected OK but got '%s'", recv_line);
736 cmd_expect_err (const char *assign_to, char *arg)
742 fprintf (stderr, "expecting ERR\n");
745 char *p = read_assuan (server_recv_fd);
747 fprintf (stderr, "got line '%s'\n", recv_line);
748 if (recv_type == LINE_STAT)
749 handle_status_line (p);
751 while (recv_type != LINE_OK && recv_type != LINE_ERR);
752 if (recv_type != LINE_ERR)
753 die_1 ("expected ERR but got '%s'", recv_line);
757 cmd_count_status (const char *assign_to, char *arg)
761 if (!*assign_to || !*arg)
762 die_0 ("syntax error: count-status requires an argument and a variable");
764 for (p=arg; *p && !spacep (p); p++)
768 for (*p++ = 0; spacep (p); p++)
771 die_0 ("cmpfiles: syntax error");
773 set_type_var (assign_to, arg, VARTYPE_COUNTER);
777 cmd_openfile (const char *assign_to, char *arg)
783 fd = open (arg, O_RDONLY);
784 while (fd == -1 && errno == EINTR);
786 die_2 ("error opening '%s': %s", arg, strerror (errno));
788 sprintf (numbuf, "%d", fd);
789 set_type_var (assign_to, numbuf, VARTYPE_FD);
793 cmd_createfile (const char *assign_to, char *arg)
799 fd = open (arg, O_WRONLY|O_CREAT|O_TRUNC, 0666);
800 while (fd == -1 && errno == EINTR);
802 die_2 ("error creating '%s': %s", arg, strerror (errno));
804 sprintf (numbuf, "%d", fd);
805 set_type_var (assign_to, numbuf, VARTYPE_FD);
810 cmd_pipeserver (const char *assign_to, char *arg)
815 die_0 ("syntax error: servername missing");
822 cmd_quit_if(const char *assign_to, char *arg)
826 if (eval_boolean (arg))
831 cmd_fail_if(const char *assign_to, char *arg)
835 if (eval_boolean (arg))
841 cmd_cmpfiles (const char *assign_to, char *arg)
846 char buffer1[2048]; /* note: both must be of equal size. */
848 size_t nread1, nread2;
851 set_var (assign_to, "0");
852 for (p=arg; *p && !spacep (p); p++)
855 die_0 ("cmpfiles: syntax error");
856 for (*p++ = 0; spacep (p); p++)
859 for (; *p && !spacep (p); p++)
863 for (*p++ = 0; spacep (p); p++)
866 die_0 ("cmpfiles: syntax error");
869 fp1 = fopen (arg, "rb");
872 err ("can't open '%s': %s", arg, strerror (errno));
875 fp2 = fopen (second, "rb");
878 err ("can't open '%s': %s", second, strerror (errno));
882 while ( (nread1 = fread (buffer1, 1, sizeof buffer1, fp1)))
886 nread2 = fread (buffer2, 1, sizeof buffer2, fp2);
889 if (nread1 != nread2 || memcmp (buffer1, buffer2, nread1))
895 if (feof (fp1) && feof (fp2) && !rc)
899 set_var (assign_to, "1");
902 err ("cmpfiles: read error: %s", strerror (errno));
904 err ("cmpfiles: mismatch");
910 cmd_getenv (const char *assign_to, char *arg)
913 s = *arg? getenv (arg):"";
914 set_var (assign_to, s? s:"");
920 /* Process the current script line LINE. */
922 interpreter (char *line)
926 void (*fnc)(const char*, char*);
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 },
938 { "quit-if" , cmd_quit_if },
939 { "fail-if" , cmd_fail_if },
940 { "cmpfiles" , cmd_cmpfiles },
941 { "getenv" , cmd_getenv },
947 char *assign_to = NULL;
948 char *must_free = NULL;
950 for ( ;spacep (line); line++)
952 if (!*line || *line == '#')
953 return 0; /* empty or comment */
954 p = expand_line (line);
959 for ( ;spacep (line); line++)
961 if (!*line || *line == '#')
964 return 0; /* empty or comment */
967 for (p=line; *p && !spacep (p) && *p != '='; p++)
976 for (*p++ = 0; spacep (p); p++)
982 die_0 ("syntax error");
987 { /* this is an assignment */
988 for (p++; spacep (p); p++)
992 unset_var (assign_to);
997 for (; *p && !spacep (p); p++)
1003 for (*p++ = 0; spacep (p); p++)
1007 for (i=0; cmdtbl[i].name && strcmp (stmt, cmdtbl[i].name); i++)
1009 if (!cmdtbl[i].name)
1012 die_1 ("invalid statement '%s'\n", stmt);
1015 set_var (assign_to, stmt);
1021 cmdtbl[i].fnc (assign_to, p);
1023 return cmdtbl[i].fnc? 0:1;
1029 main (int argc, char **argv)
1035 invocation_name = "asschk";
1038 invocation_name = *argv++;
1040 p = strrchr (invocation_name, '/');
1042 invocation_name = p+1;
1046 set_var ("?","1"); /* defaults to true */
1048 for (; argc; argc--, argv++)
1053 if (!strcmp (p, "--verbose"))
1055 else if (!strcmp (p, "--no-echo"))
1057 else if (*p == '-' && p[1] == 'D')
1060 pend = strchr (p, '=');
1065 set_var (p, pend+1);
1071 else if (*p == '-' && p[1] == '-' && !p[2])
1080 die ("usage: asschk [--verbose] {-D<name>[=<value>]}");
1083 while (fgets (buffer, sizeof buffer, stdin))
1085 p = strchr (buffer,'\n');
1087 die_0 ("incomplete script line");
1089 if (interpreter (buffer))