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 <http://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(format, args...) (die) ("%s: " format, __func__ , ##args)
205 err (const char *format
, ...)
210 fprintf (stderr
, "%s: ", invocation_name
);
212 va_start (arg_ptr
, format
);
213 vfprintf (stderr
, format
, arg_ptr
);
221 void *p
= malloc (n
);
228 xcalloc (size_t n
, size_t m
)
230 void *p
= calloc (n
, m
);
237 xstrdup (const char *s
)
239 char *p
= xmalloc (strlen (s
)+1);
245 /* Write LENGTH bytes from BUFFER to FD. */
247 writen (int fd
, const char *buffer
, size_t length
)
251 int nwritten
= write (fd
, buffer
, length
);
257 return -1; /* write error */
268 /* Assuan specific stuff. */
270 /* Read a line from FD, store it in the global recv_line, analyze the
271 type and store that in recv_type. The function terminates on a
272 communication error. Returns a pointer into the inputline to the
273 first byte of the arguments. The parsing is very strict to match
274 exaclty what we want to send. */
278 /* FIXME: For general robustness, the pending stuff needs to be
279 associated with FD. */
280 static char pending
[MAX_LINELEN
];
281 static size_t pending_len
;
282 size_t nleft
= sizeof recv_line
;
283 char *buf
= recv_line
;
292 if (pending_len
>= nleft
)
293 die ("received line too large");
294 memcpy (buf
, pending
, pending_len
);
302 n
= read (fd
, buf
, nleft
);
304 while (n
< 0 && errno
== EINTR
);
307 if (opt_verbose
&& n
>= 0 )
311 printf ("%s: read \"", __func__
);
312 for (i
= 0; i
< n
; i
++)
313 putc (buf
[i
], stdout
);
318 die ("reading fd %d failed: %s", fd
, strerror (errno
));
320 die ("received incomplete line on fd %d", fd
);
325 for (; n
&& *p
!= '\n'; n
--, p
++)
332 memcpy (pending
, p
+ 1, n
);
340 die ("received line too large");
343 if (p
[0] == 'O' && p
[1] == 'K' && (p
[2] == ' ' || !p
[2]))
348 else if (p
[0] == 'E' && p
[1] == 'R' && p
[2] == 'R'
349 && (p
[3] == ' ' || !p
[3]))
351 recv_type
= LINE_ERR
;
354 else if (p
[0] == 'S' && (p
[1] == ' ' || !p
[1]))
356 recv_type
= LINE_STAT
;
359 else if (p
[0] == 'D' && p
[1] == ' ')
361 recv_type
= LINE_DATA
;
364 else if (p
[0] == 'E' && p
[1] == 'N' && p
[2] == 'D' && !p
[3])
366 recv_type
= LINE_END
;
370 die ("invalid line type (%.5s)", p
);
375 /* Write LINE to the server using FD. It is expected that the line
376 contains the terminating linefeed as last character. */
378 write_assuan (int fd
, const char *line
)
381 size_t n
= strlen (line
);
384 die ("line too long for Assuan protocol");
385 strcpy (buffer
, line
);
386 if (!n
|| buffer
[n
-1] != '\n')
389 if (writen (fd
, buffer
, n
))
390 die ("sending line (\"%s\") to %d failed: %s", buffer
, fd
,
395 /* Start the server with path PGMNAME and connect its stdout and
396 strerr to a newly created pipes; the file descriptors are then
397 store in the gloabl variables SERVER_SEND_FD and
398 SERVER_RECV_FD. The initial handcheck is performed.*/
400 start_server (const char *pgmname
)
407 die ("pipe creation failed: %s", strerror (errno
));
409 die ("pipe creation failed: %s", strerror (errno
));
421 arg0
= strrchr (pgmname
, '/');
427 if (wp
[0] != STDIN_FILENO
)
429 if (dup2 (wp
[0], STDIN_FILENO
) == -1)
430 die ("dup2 failed in child: %s", strerror (errno
));
433 if (rp
[1] != STDOUT_FILENO
)
435 if (dup2 (rp
[1], STDOUT_FILENO
) == -1)
436 die ("dup2 failed in child: %s", strerror (errno
));
441 int fd
= open ("/dev/null", O_WRONLY
);
443 die ("can't open `/dev/null': %s", strerror (errno
));
444 if (dup2 (fd
, STDERR_FILENO
) == -1)
445 die ("dup2 failed in child: %s", strerror (errno
));
451 execl (pgmname
, arg0
, "--server", NULL
);
452 die ("exec failed for `%s': %s", pgmname
, strerror (errno
));
456 server_send_fd
= wp
[1];
457 server_recv_fd
= rp
[0];
459 read_assuan (server_recv_fd
);
460 if (recv_type
!= LINE_OK
)
461 die ("no greating message");
468 /* Script intepreter. */
471 unset_var (const char *name
)
475 for (var
=variable_list
; var
&& strcmp (var
->name
, name
); var
= var
->next
)
479 /* fprintf (stderr, "unsetting `%s'\n", name); */
481 if (var
->type
== VARTYPE_FD
&& var
->value
)
485 fd
= atoi (var
->value
);
486 if (fd
!= -1 && fd
!= 0 && fd
!= 1 && fd
!= 2)
498 set_type_var (const char *name
, const char *value
, VARTYPE type
)
504 for (var
=variable_list
; var
&& strcmp (var
->name
, name
); var
= var
->next
)
508 var
= xcalloc (1, sizeof *var
+ strlen (name
));
509 strcpy (var
->name
, name
);
510 var
->next
= variable_list
;
516 if (var
->type
== VARTYPE_FD
&& var
->value
)
520 fd
= atoi (var
->value
);
521 if (fd
!= -1 && fd
!= 0 && fd
!= 1 && fd
!= 2)
527 if (var
->type
== VARTYPE_COUNTER
)
529 /* We need some extra sapce as scratch area for get_var. */
530 var
->value
= xmalloc (strlen (value
) + 1 + 20);
531 strcpy (var
->value
, value
);
534 var
->value
= xstrdup (value
);
538 set_var (const char *name
, const char *value
)
540 set_type_var (name
, value
, 0);
545 get_var (const char *name
)
549 for (var
=variable_list
; var
&& strcmp (var
->name
, name
); var
= var
->next
)
553 if (var
->type
== VARTYPE_COUNTER
&& var
->value
)
554 { /* Use the scratch space allocated by set_var. */
555 char *p
= var
->value
+ strlen(var
->value
)+1;
556 sprintf (p
, "%u", var
->count
);
564 /* Incremente all counter type variables with NAME in their VALUE. */
566 inc_counter (const char *name
)
572 for (var
=variable_list
; var
; var
= var
->next
)
574 if (var
->type
== VARTYPE_COUNTER
575 && var
->value
&& !strcmp (var
->value
, name
))
581 /* Expand variables in LINE and return a new allocated buffer if
582 required. The function might modify LINE if the expanded version
585 expand_line (char *buffer
)
595 p
= strchr (line
, '$');
597 return result
; /* nothing more to expand */
599 if (p
[1] == '$') /* quoted */
601 memmove (p
, p
+1, strlen (p
+1)+1);
605 for (pend
=p
+1; *pend
&& !spacep (pend
)
606 && *pend
!= '$' && *pend
!= '/'; pend
++)
612 value
= get_var (p
+1);
616 value
= get_var (p
+1);
619 valuelen
= strlen (value
);
620 if (valuelen
<= pend
- p
)
622 memcpy (p
, value
, valuelen
);
626 memmove (p
, p
+n
, strlen (p
+n
)+1);
631 char *src
= result
? result
: buffer
;
634 dst
= xmalloc (strlen (src
) + valuelen
+ 1);
636 memcpy (dst
, src
, n
);
637 memcpy (dst
+ n
, value
, valuelen
);
639 strcpy (dst
+ n
, pend
);
649 /* Evaluate COND and return the result. */
651 eval_boolean (const char *cond
)
655 for ( ; *cond
== '!'; cond
++)
657 if (!*cond
|| (*cond
== '0' && !cond
[1]))
667 cmd_let (const char *assign_to
, char *arg
)
669 set_var (assign_to
, arg
);
674 cmd_echo (const char *assign_to
, char *arg
)
677 printf ("%s\n", arg
);
681 cmd_send (const char *assign_to
, char *arg
)
684 fprintf (stderr
, "sending `%s'\n", arg
);
685 write_assuan (server_send_fd
, arg
);
689 handle_status_line (char *arg
)
693 for (p
=arg
; *p
&& !spacep (p
); p
++)
707 cmd_expect_ok (const char *assign_to
, char *arg
)
710 fprintf (stderr
, "expecting OK\n");
713 char *p
= read_assuan (server_recv_fd
);
715 fprintf (stderr
, "got line `%s'\n", recv_line
);
716 if (recv_type
== LINE_STAT
)
717 handle_status_line (p
);
719 while (recv_type
!= LINE_OK
&& recv_type
!= LINE_ERR
);
720 if (recv_type
!= LINE_OK
)
721 die ("expected OK but got `%s'", recv_line
);
725 cmd_expect_err (const char *assign_to
, char *arg
)
728 fprintf (stderr
, "expecting ERR\n");
731 char *p
= read_assuan (server_recv_fd
);
733 fprintf (stderr
, "got line `%s'\n", recv_line
);
734 if (recv_type
== LINE_STAT
)
735 handle_status_line (p
);
737 while (recv_type
!= LINE_OK
&& recv_type
!= LINE_ERR
);
738 if (recv_type
!= LINE_ERR
)
739 die ("expected ERR but got `%s'", recv_line
);
743 cmd_count_status (const char *assign_to
, char *arg
)
747 if (!*assign_to
|| !*arg
)
748 die ("syntax error: count-status requires an argument and a variable");
750 for (p
=arg
; *p
&& !spacep (p
); p
++)
754 for (*p
++ = 0; spacep (p
); p
++)
757 die ("cmpfiles: syntax error");
759 set_type_var (assign_to
, arg
, VARTYPE_COUNTER
);
763 cmd_openfile (const char *assign_to
, char *arg
)
769 fd
= open (arg
, O_RDONLY
);
770 while (fd
== -1 && errno
== EINTR
);
772 die ("error opening `%s': %s", arg
, strerror (errno
));
774 sprintf (numbuf
, "%d", fd
);
775 set_type_var (assign_to
, numbuf
, VARTYPE_FD
);
779 cmd_createfile (const char *assign_to
, char *arg
)
785 fd
= open (arg
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0666);
786 while (fd
== -1 && errno
== EINTR
);
788 die ("error creating `%s': %s", arg
, strerror (errno
));
790 sprintf (numbuf
, "%d", fd
);
791 set_type_var (assign_to
, numbuf
, VARTYPE_FD
);
796 cmd_pipeserver (const char *assign_to
, char *arg
)
799 die ("syntax error: servername missing");
806 cmd_quit_if(const char *assign_to
, char *arg
)
808 if (eval_boolean (arg
))
813 cmd_fail_if(const char *assign_to
, char *arg
)
815 if (eval_boolean (arg
))
821 cmd_cmpfiles (const char *assign_to
, char *arg
)
826 char buffer1
[2048]; /* note: both must be of equal size. */
828 size_t nread1
, nread2
;
831 set_var (assign_to
, "0");
832 for (p
=arg
; *p
&& !spacep (p
); p
++)
835 die ("cmpfiles: syntax error");
836 for (*p
++ = 0; spacep (p
); p
++)
839 for (; *p
&& !spacep (p
); p
++)
843 for (*p
++ = 0; spacep (p
); p
++)
846 die ("cmpfiles: syntax error");
849 fp1
= fopen (arg
, "rb");
852 err ("can't open `%s': %s", arg
, strerror (errno
));
855 fp2
= fopen (second
, "rb");
858 err ("can't open `%s': %s", second
, strerror (errno
));
862 while ( (nread1
= fread (buffer1
, 1, sizeof buffer1
, fp1
)))
866 nread2
= fread (buffer2
, 1, sizeof buffer2
, fp2
);
869 if (nread1
!= nread2
|| memcmp (buffer1
, buffer2
, nread1
))
875 if (feof (fp1
) && feof (fp2
) && !rc
)
879 set_var (assign_to
, "1");
882 err ("cmpfiles: read error: %s", strerror (errno
));
884 err ("cmpfiles: mismatch");
890 cmd_getenv (const char *assign_to
, char *arg
)
893 s
= *arg
? getenv (arg
):"";
894 set_var (assign_to
, s
? s
:"");
900 /* Process the current script line LINE. */
902 interpreter (char *line
)
906 void (*fnc
)(const char*, char*);
909 { "echo" , cmd_echo
},
910 { "send" , cmd_send
},
911 { "expect-ok" , cmd_expect_ok
},
912 { "expect-err", cmd_expect_err
},
913 { "count-status", cmd_count_status
},
914 { "openfile" , cmd_openfile
},
915 { "createfile", cmd_createfile
},
916 { "pipeserver", cmd_pipeserver
},
918 { "quit-if" , cmd_quit_if
},
919 { "fail-if" , cmd_fail_if
},
920 { "cmpfiles" , cmd_cmpfiles
},
921 { "getenv" , cmd_getenv
},
927 char *assign_to
= NULL
;
928 char *must_free
= NULL
;
930 for ( ;spacep (line
); line
++)
932 if (!*line
|| *line
== '#')
933 return 0; /* empty or comment */
934 p
= expand_line (line
);
939 for ( ;spacep (line
); line
++)
941 if (!*line
|| *line
== '#')
944 return 0; /* empty or comment */
947 for (p
=line
; *p
&& !spacep (p
) && *p
!= '='; p
++)
956 for (*p
++ = 0; spacep (p
); p
++)
962 die ("syntax error");
967 { /* this is an assignment */
968 for (p
++; spacep (p
); p
++)
972 unset_var (assign_to
);
977 for (; *p
&& !spacep (p
); p
++)
983 for (*p
++ = 0; spacep (p
); p
++)
987 for (i
=0; cmdtbl
[i
].name
&& strcmp (stmt
, cmdtbl
[i
].name
); i
++)
992 die ("invalid statement `%s'\n", stmt
);
995 set_var (assign_to
, stmt
);
1001 cmdtbl
[i
].fnc (assign_to
, p
);
1003 return cmdtbl
[i
].fnc
? 0:1;
1009 main (int argc
, char **argv
)
1015 invocation_name
= "asschk";
1018 invocation_name
= *argv
++;
1020 p
= strrchr (invocation_name
, '/');
1022 invocation_name
= p
+1;
1026 set_var ("?","1"); /* defaults to true */
1028 for (; argc
; argc
--, argv
++)
1033 if (!strcmp (p
, "--verbose"))
1035 else if (!strcmp (p
, "--no-echo"))
1037 else if (*p
== '-' && p
[1] == 'D')
1040 pend
= strchr (p
, '=');
1045 set_var (p
, pend
+1);
1051 else if (*p
== '-' && p
[1] == '-' && !p
[2])
1060 die ("usage: asschk [--verbose] {-D<name>[=<value>]}");
1063 while (fgets (buffer
, sizeof buffer
, stdin
))
1065 p
= strchr (buffer
,'\n');
1067 die ("incomplete script line");
1069 if (interpreter (buffer
))