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
)
678 printf ("%s\n", arg
);
682 cmd_send (const char *assign_to
, char *arg
)
686 fprintf (stderr
, "sending `%s'\n", arg
);
687 write_assuan (server_send_fd
, arg
);
691 handle_status_line (char *arg
)
695 for (p
=arg
; *p
&& !spacep (p
); p
++)
709 cmd_expect_ok (const char *assign_to
, char *arg
)
715 fprintf (stderr
, "expecting OK\n");
718 char *p
= read_assuan (server_recv_fd
);
720 fprintf (stderr
, "got line `%s'\n", recv_line
);
721 if (recv_type
== LINE_STAT
)
722 handle_status_line (p
);
724 while (recv_type
!= LINE_OK
&& recv_type
!= LINE_ERR
);
725 if (recv_type
!= LINE_OK
)
726 die ("expected OK but got `%s'", recv_line
);
730 cmd_expect_err (const char *assign_to
, char *arg
)
736 fprintf (stderr
, "expecting ERR\n");
739 char *p
= read_assuan (server_recv_fd
);
741 fprintf (stderr
, "got line `%s'\n", recv_line
);
742 if (recv_type
== LINE_STAT
)
743 handle_status_line (p
);
745 while (recv_type
!= LINE_OK
&& recv_type
!= LINE_ERR
);
746 if (recv_type
!= LINE_ERR
)
747 die ("expected ERR but got `%s'", recv_line
);
751 cmd_count_status (const char *assign_to
, char *arg
)
755 if (!*assign_to
|| !*arg
)
756 die ("syntax error: count-status requires an argument and a variable");
758 for (p
=arg
; *p
&& !spacep (p
); p
++)
762 for (*p
++ = 0; spacep (p
); p
++)
765 die ("cmpfiles: syntax error");
767 set_type_var (assign_to
, arg
, VARTYPE_COUNTER
);
771 cmd_openfile (const char *assign_to
, char *arg
)
777 fd
= open (arg
, O_RDONLY
);
778 while (fd
== -1 && errno
== EINTR
);
780 die ("error opening `%s': %s", arg
, strerror (errno
));
782 sprintf (numbuf
, "%d", fd
);
783 set_type_var (assign_to
, numbuf
, VARTYPE_FD
);
787 cmd_createfile (const char *assign_to
, char *arg
)
793 fd
= open (arg
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0666);
794 while (fd
== -1 && errno
== EINTR
);
796 die ("error creating `%s': %s", arg
, strerror (errno
));
798 sprintf (numbuf
, "%d", fd
);
799 set_type_var (assign_to
, numbuf
, VARTYPE_FD
);
804 cmd_pipeserver (const char *assign_to
, char *arg
)
809 die ("syntax error: servername missing");
816 cmd_quit_if(const char *assign_to
, char *arg
)
820 if (eval_boolean (arg
))
825 cmd_fail_if(const char *assign_to
, char *arg
)
829 if (eval_boolean (arg
))
835 cmd_cmpfiles (const char *assign_to
, char *arg
)
840 char buffer1
[2048]; /* note: both must be of equal size. */
842 size_t nread1
, nread2
;
845 set_var (assign_to
, "0");
846 for (p
=arg
; *p
&& !spacep (p
); p
++)
849 die ("cmpfiles: syntax error");
850 for (*p
++ = 0; spacep (p
); p
++)
853 for (; *p
&& !spacep (p
); p
++)
857 for (*p
++ = 0; spacep (p
); p
++)
860 die ("cmpfiles: syntax error");
863 fp1
= fopen (arg
, "rb");
866 err ("can't open `%s': %s", arg
, strerror (errno
));
869 fp2
= fopen (second
, "rb");
872 err ("can't open `%s': %s", second
, strerror (errno
));
876 while ( (nread1
= fread (buffer1
, 1, sizeof buffer1
, fp1
)))
880 nread2
= fread (buffer2
, 1, sizeof buffer2
, fp2
);
883 if (nread1
!= nread2
|| memcmp (buffer1
, buffer2
, nread1
))
889 if (feof (fp1
) && feof (fp2
) && !rc
)
893 set_var (assign_to
, "1");
896 err ("cmpfiles: read error: %s", strerror (errno
));
898 err ("cmpfiles: mismatch");
904 cmd_getenv (const char *assign_to
, char *arg
)
907 s
= *arg
? getenv (arg
):"";
908 set_var (assign_to
, s
? s
:"");
914 /* Process the current script line LINE. */
916 interpreter (char *line
)
920 void (*fnc
)(const char*, char*);
923 { "echo" , cmd_echo
},
924 { "send" , cmd_send
},
925 { "expect-ok" , cmd_expect_ok
},
926 { "expect-err", cmd_expect_err
},
927 { "count-status", cmd_count_status
},
928 { "openfile" , cmd_openfile
},
929 { "createfile", cmd_createfile
},
930 { "pipeserver", cmd_pipeserver
},
932 { "quit-if" , cmd_quit_if
},
933 { "fail-if" , cmd_fail_if
},
934 { "cmpfiles" , cmd_cmpfiles
},
935 { "getenv" , cmd_getenv
},
941 char *assign_to
= NULL
;
942 char *must_free
= NULL
;
944 for ( ;spacep (line
); line
++)
946 if (!*line
|| *line
== '#')
947 return 0; /* empty or comment */
948 p
= expand_line (line
);
953 for ( ;spacep (line
); line
++)
955 if (!*line
|| *line
== '#')
958 return 0; /* empty or comment */
961 for (p
=line
; *p
&& !spacep (p
) && *p
!= '='; p
++)
970 for (*p
++ = 0; spacep (p
); p
++)
976 die ("syntax error");
981 { /* this is an assignment */
982 for (p
++; spacep (p
); p
++)
986 unset_var (assign_to
);
991 for (; *p
&& !spacep (p
); p
++)
997 for (*p
++ = 0; spacep (p
); p
++)
1001 for (i
=0; cmdtbl
[i
].name
&& strcmp (stmt
, cmdtbl
[i
].name
); i
++)
1003 if (!cmdtbl
[i
].name
)
1006 die ("invalid statement `%s'\n", stmt
);
1009 set_var (assign_to
, stmt
);
1015 cmdtbl
[i
].fnc (assign_to
, p
);
1017 return cmdtbl
[i
].fnc
? 0:1;
1023 main (int argc
, char **argv
)
1029 invocation_name
= "asschk";
1032 invocation_name
= *argv
++;
1034 p
= strrchr (invocation_name
, '/');
1036 invocation_name
= p
+1;
1040 set_var ("?","1"); /* defaults to true */
1042 for (; argc
; argc
--, argv
++)
1047 if (!strcmp (p
, "--verbose"))
1049 else if (!strcmp (p
, "--no-echo"))
1051 else if (*p
== '-' && p
[1] == 'D')
1054 pend
= strchr (p
, '=');
1059 set_var (p
, pend
+1);
1065 else if (*p
== '-' && p
[1] == '-' && !p
[2])
1074 die ("usage: asschk [--verbose] {-D<name>[=<value>]}");
1077 while (fgets (buffer
, sizeof buffer
, stdin
))
1079 p
= strchr (buffer
,'\n');
1081 die ("incomplete script line");
1083 if (interpreter (buffer
))