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 2 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, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
21 /* This is a simple stand-alone Assuan server test program. We don't
22 want to use the assuan library because we don't want to hide errors
25 The script language is line based. Empty lines or lines containing
26 only white spaces are ignored, line with a hash sign as first non
27 white space character are treated as comments.
29 A simple macro mechanism is implemnted. Macros are expanded before
30 a line is processed but after comment processing. Macros are only
31 expanded once and non existing macros expand to the empty string.
32 A macro is dereferenced by prefixing its name with a dollar sign;
33 the end of the name is currently indicated by a white space, a
34 dollar sign or a slash. To use a dollor sign verbatim, double it.
36 A macro is assigned by prefixing a statement with the macro name
37 and an equal sign. The value is assigned verbatim if it does not
38 resemble a command, otherwise the return value of the command will
39 get assigned. The command "let" may be used to assign values
40 unambigiously and it should be used if the value starts with a
43 Conditions are not yes implemented except for a simple evaluation
44 which yields false for an empty string or the string "0". The
45 result may be negated by prefixing with a '!'.
47 The general syntax of a command is:
49 [<name> =] <statement> [<args>]
51 If NAME is not specifed but the statement returns a value it is
52 assigned to the name "?" so that it can be referenced using "$?".
53 The following commands are implemented:
62 Open file FILENAME for read access and retrun the file descriptor.
65 Create file FILENAME, open for write access and retrun the file
69 Connect to the Assuan server PROGRAM.
72 Send LINE to the server.
75 Expect an OK response from the server. Status and data out put
79 Expect an ERR response from the server. Status and data out put
83 Initialize the assigned variable to 0 and assign it as an counter for
84 status code CODE. This command must be called with an assignment.
87 Terminate the process.
90 Terminate the process if CONDITION evaluates to true.
93 Terminate the process with an exit code of 1 if CONDITION
96 cmpfiles <first> <second>
97 Returns true when the content of the files FIRST and SECOND match.
100 Return the value of the environment variable NAME.
113 #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 )
114 # define ATTR_PRINTF(f,a) __attribute__ ((format (printf,f,a)))
116 # define ATTR_PRINTF(f,a)
119 #define spacep(p) (*(p) == ' ' || *(p) == '\t')
121 #define MAX_LINELEN 2048
139 struct variable_s
*next
;
145 typedef struct variable_s
*VARIABLE
;
148 static void die (const char *format
, ...) ATTR_PRINTF(1,2);
151 /* Name of this program to be printed in error messages. */
152 static const char *invocation_name
;
154 /* Talk a bit about what is going on. */
155 static int opt_verbose
;
157 /* Option to ignore the echo command. */
158 static int opt_no_echo
;
160 /* File descriptors used to communicate with the current server. */
161 static int server_send_fd
= -1;
162 static int server_recv_fd
= -1;
164 /* The Assuan protocol limits the line length to 1024, so we can
165 safely use a (larger) buffer. The buffer is filled using the
167 static char recv_line
[MAX_LINELEN
];
168 /* Tell the status of the current line. */
169 static LINETYPE recv_type
;
171 /* This is our variable storage. */
172 static VARIABLE variable_list
;
176 die (const char *format
, ...)
181 fprintf (stderr
, "%s: ", invocation_name
);
183 va_start (arg_ptr
, format
);
184 vfprintf (stderr
, format
, arg_ptr
);
191 #define die(format, args...) (die) ("%s: " format, __FUNCTION__ , ##args)
194 err (const char *format
, ...)
199 fprintf (stderr
, "%s: ", invocation_name
);
201 va_start (arg_ptr
, format
);
202 vfprintf (stderr
, format
, arg_ptr
);
210 void *p
= malloc (n
);
217 xcalloc (size_t n
, size_t m
)
219 void *p
= calloc (n
, m
);
226 xstrdup (const char *s
)
228 char *p
= xmalloc (strlen (s
)+1);
234 /* Write LENGTH bytes from BUFFER to FD. */
236 writen (int fd
, const char *buffer
, size_t length
)
240 int nwritten
= write (fd
, buffer
, length
);
246 return -1; /* write error */
257 /* Assuan specific stuff. */
259 /* Read a line from FD, store it in the global recv_line, analyze the
260 type and store that in recv_type. The function terminates on a
261 communication error. Returns a pointer into the inputline to the
262 first byte of the arguments. The parsing is very strict to match
263 excalty what we want to send. */
267 static char pending
[MAX_LINELEN
];
268 static size_t pending_len
;
269 size_t nleft
= sizeof recv_line
;
270 char *buf
= recv_line
;
279 if (pending_len
>= nleft
)
280 die ("received line too large");
281 memcpy (buf
, pending
, pending_len
);
286 n
= read (fd
, buf
, nleft
);
291 printf ("%s: read \"", __FUNCTION__
);
292 for (i
= 0; i
< n
; i
++)
293 putc (buf
[i
], stdout
);
301 die ("reading fd %d failed: %s", fd
, strerror (errno
));
304 die ("received incomplete line on fd %d", fd
);
309 for (; n
&& *p
!= '\n'; n
--, p
++)
316 memcpy (pending
, p
+ 1, n
);
324 die ("received line too large");
327 if (p
[0] == 'O' && p
[1] == 'K' && (p
[2] == ' ' || !p
[2]))
332 else if (p
[0] == 'E' && p
[1] == 'R' && p
[2] == 'R'
333 && (p
[3] == ' ' || !p
[3]))
335 recv_type
= LINE_ERR
;
338 else if (p
[0] == 'S' && (p
[1] == ' ' || !p
[1]))
340 recv_type
= LINE_STAT
;
343 else if (p
[0] == 'D' && p
[1] == ' ')
345 recv_type
= LINE_DATA
;
348 else if (p
[0] == 'E' && p
[1] == 'N' && p
[2] == 'D' && !p
[3])
350 recv_type
= LINE_END
;
354 die ("invalid line type (%.5s)", p
);
359 /* Write LINE to the server using FD. It is expected that the line
360 contains the terminating linefeed as last character. */
362 write_assuan (int fd
, const char *line
)
365 size_t n
= strlen (line
);
368 die ("line too long for Assuan protocol");
369 strcpy (buffer
, line
);
370 if (!n
|| buffer
[n
-1] != '\n')
373 if (writen (fd
, buffer
, n
))
374 die ("sending line (\"%s\") to %d failed: %s", buffer
, fd
,
379 /* Start the server with path PGMNAME and connect its stdout and
380 strerr to a newly created pipes; the file descriptors are then
381 store in the gloabl variables SERVER_SEND_FD and
382 SERVER_RECV_FD. The initial handcheck is performed.*/
384 start_server (const char *pgmname
)
391 die ("pipe creation failed: %s", strerror (errno
));
393 die ("pipe creation failed: %s", strerror (errno
));
405 arg0
= strrchr (pgmname
, '/');
411 if (wp
[0] != STDIN_FILENO
)
413 if (dup2 (wp
[0], STDIN_FILENO
) == -1)
414 die ("dup2 failed in child: %s", strerror (errno
));
417 if (rp
[1] != STDOUT_FILENO
)
419 if (dup2 (rp
[1], STDOUT_FILENO
) == -1)
420 die ("dup2 failed in child: %s", strerror (errno
));
425 int fd
= open ("/dev/null", O_WRONLY
);
427 die ("can't open `/dev/null': %s", strerror (errno
));
428 if (dup2 (fd
, STDERR_FILENO
) == -1)
429 die ("dup2 failed in child: %s", strerror (errno
));
435 execl (pgmname
, arg0
, "--server", NULL
);
436 die ("exec failed for `%s': %s", pgmname
, strerror (errno
));
440 server_send_fd
= wp
[1];
441 server_recv_fd
= rp
[0];
443 read_assuan (server_recv_fd
);
444 if (recv_type
!= LINE_OK
)
445 die ("no greating message");
452 /* Script intepreter. */
455 unset_var (const char *name
)
459 for (var
=variable_list
; var
&& strcmp (var
->name
, name
); var
= var
->next
)
463 /* fprintf (stderr, "unsetting `%s'\n", name); */
465 if (var
->type
== VARTYPE_FD
&& var
->value
)
469 fd
= atoi (var
->value
);
470 if (fd
!= -1 && fd
!= 0 && fd
!= 1 && fd
!= 2)
482 set_type_var (const char *name
, const char *value
, VARTYPE type
)
488 for (var
=variable_list
; var
&& strcmp (var
->name
, name
); var
= var
->next
)
492 var
= xcalloc (1, sizeof *var
+ strlen (name
));
493 strcpy (var
->name
, name
);
494 var
->next
= variable_list
;
500 if (var
->type
== VARTYPE_FD
&& var
->value
)
504 fd
= atoi (var
->value
);
505 if (fd
!= -1 && fd
!= 0 && fd
!= 1 && fd
!= 2)
511 if (var
->type
== VARTYPE_COUNTER
)
513 /* We need some extra sapce as scratch area for get_var. */
514 var
->value
= xmalloc (strlen (value
) + 1 + 20);
515 strcpy (var
->value
, value
);
518 var
->value
= xstrdup (value
);
522 set_var (const char *name
, const char *value
)
524 set_type_var (name
, value
, 0);
529 get_var (const char *name
)
533 for (var
=variable_list
; var
&& strcmp (var
->name
, name
); var
= var
->next
)
537 if (var
->type
== VARTYPE_COUNTER
&& var
->value
)
538 { /* Use the scratch space allocated by set_var. */
539 char *p
= var
->value
+ strlen(var
->value
)+1;
540 sprintf (p
, "%u", var
->count
);
548 /* Incremente all counter type variables with NAME in their VALUE. */
550 inc_counter (const char *name
)
556 for (var
=variable_list
; var
; var
= var
->next
)
558 if (var
->type
== VARTYPE_COUNTER
559 && var
->value
&& !strcmp (var
->value
, name
))
565 /* Expand variables in LINE and return a new allocated buffer if
566 required. The function might modify LINE if the expanded version
569 expand_line (char *buffer
)
579 p
= strchr (line
, '$');
581 return result
; /* nothing more to expand */
583 if (p
[1] == '$') /* quoted */
585 memmove (p
, p
+1, strlen (p
+1)+1);
589 for (pend
=p
+1; *pend
&& !spacep (pend
)
590 && *pend
!= '$' && *pend
!= '/'; pend
++)
596 value
= get_var (p
+1);
600 value
= get_var (p
+1);
603 valuelen
= strlen (value
);
604 if (valuelen
<= pend
- p
)
606 memcpy (p
, value
, valuelen
);
610 memmove (p
, p
+n
, strlen (p
+n
)+1);
615 char *src
= result
? result
: buffer
;
618 dst
= xmalloc (strlen (src
) + valuelen
+ 1);
620 memcpy (dst
, src
, n
);
621 memcpy (dst
+ n
, value
, valuelen
);
623 strcpy (dst
+ n
, pend
);
633 /* Evaluate COND and return the result. */
635 eval_boolean (const char *cond
)
639 for ( ; *cond
== '!'; cond
++)
641 if (!*cond
|| (*cond
== '0' && !cond
[1]))
651 cmd_let (const char *assign_to
, char *arg
)
653 set_var (assign_to
, arg
);
658 cmd_echo (const char *assign_to
, char *arg
)
661 printf ("%s\n", arg
);
665 cmd_send (const char *assign_to
, char *arg
)
668 fprintf (stderr
, "sending `%s'\n", arg
);
669 write_assuan (server_send_fd
, arg
);
673 handle_status_line (char *arg
)
677 for (p
=arg
; *p
&& !spacep (p
); p
++)
691 cmd_expect_ok (const char *assign_to
, char *arg
)
694 fprintf (stderr
, "expecting OK\n");
697 char *p
= read_assuan (server_recv_fd
);
699 fprintf (stderr
, "got line `%s'\n", recv_line
);
700 if (recv_type
== LINE_STAT
)
701 handle_status_line (p
);
703 while (recv_type
!= LINE_OK
&& recv_type
!= LINE_ERR
);
704 if (recv_type
!= LINE_OK
)
705 die ("expected OK but got `%s'", recv_line
);
709 cmd_expect_err (const char *assign_to
, char *arg
)
712 fprintf (stderr
, "expecting ERR\n");
715 char *p
= read_assuan (server_recv_fd
);
717 fprintf (stderr
, "got line `%s'\n", recv_line
);
718 if (recv_type
== LINE_STAT
)
719 handle_status_line (p
);
721 while (recv_type
!= LINE_OK
&& recv_type
!= LINE_ERR
);
722 if (recv_type
!= LINE_ERR
)
723 die ("expected ERR but got `%s'", recv_line
);
727 cmd_count_status (const char *assign_to
, char *arg
)
731 if (!*assign_to
|| !*arg
)
732 die ("syntax error: count-status requires an argument and a variable");
734 for (p
=arg
; *p
&& !spacep (p
); p
++)
738 for (*p
++ = 0; spacep (p
); p
++)
741 die ("cmpfiles: syntax error");
743 set_type_var (assign_to
, arg
, VARTYPE_COUNTER
);
747 cmd_openfile (const char *assign_to
, char *arg
)
753 fd
= open (arg
, O_RDONLY
);
754 while (fd
== -1 && errno
== EINTR
);
756 die ("error opening `%s': %s", arg
, strerror (errno
));
758 sprintf (numbuf
, "%d", fd
);
759 set_type_var (assign_to
, numbuf
, VARTYPE_FD
);
763 cmd_createfile (const char *assign_to
, char *arg
)
769 fd
= open (arg
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0666);
770 while (fd
== -1 && errno
== EINTR
);
772 die ("error creating `%s': %s", arg
, strerror (errno
));
774 sprintf (numbuf
, "%d", fd
);
775 set_type_var (assign_to
, numbuf
, VARTYPE_FD
);
780 cmd_pipeserver (const char *assign_to
, char *arg
)
783 die ("syntax error: servername missing");
790 cmd_quit_if(const char *assign_to
, char *arg
)
792 if (eval_boolean (arg
))
797 cmd_fail_if(const char *assign_to
, char *arg
)
799 if (eval_boolean (arg
))
805 cmd_cmpfiles (const char *assign_to
, char *arg
)
810 char buffer1
[2048]; /* note: both must be of equal size. */
812 size_t nread1
, nread2
;
815 set_var (assign_to
, "0");
816 for (p
=arg
; *p
&& !spacep (p
); p
++)
819 die ("cmpfiles: syntax error");
820 for (*p
++ = 0; spacep (p
); p
++)
823 for (; *p
&& !spacep (p
); p
++)
827 for (*p
++ = 0; spacep (p
); p
++)
830 die ("cmpfiles: syntax error");
833 fp1
= fopen (arg
, "rb");
836 err ("can't open `%s': %s", arg
, strerror (errno
));
839 fp2
= fopen (second
, "rb");
842 err ("can't open `%s': %s", second
, strerror (errno
));
846 while ( (nread1
= fread (buffer1
, 1, sizeof buffer1
, fp1
)))
850 nread2
= fread (buffer2
, 1, sizeof buffer2
, fp2
);
853 if (nread1
!= nread2
|| memcmp (buffer1
, buffer2
, nread1
))
859 if (feof (fp1
) && feof (fp2
) && !rc
)
863 set_var (assign_to
, "1");
866 err ("cmpfiles: read error: %s", strerror (errno
));
868 err ("cmpfiles: mismatch");
874 cmd_getenv (const char *assign_to
, char *arg
)
877 s
= *arg
? getenv (arg
):"";
878 set_var (assign_to
, s
? s
:"");
884 /* Process the current script line LINE. */
886 interpreter (char *line
)
890 void (*fnc
)(const char*, char*);
893 { "echo" , cmd_echo
},
894 { "send" , cmd_send
},
895 { "expect-ok" , cmd_expect_ok
},
896 { "expect-err", cmd_expect_err
},
897 { "count-status", cmd_count_status
},
898 { "openfile" , cmd_openfile
},
899 { "createfile", cmd_createfile
},
900 { "pipeserver", cmd_pipeserver
},
902 { "quit-if" , cmd_quit_if
},
903 { "fail-if" , cmd_fail_if
},
904 { "cmpfiles" , cmd_cmpfiles
},
905 { "getenv" , cmd_getenv
},
911 char *assign_to
= NULL
;
912 char *must_free
= NULL
;
914 for ( ;spacep (line
); line
++)
916 if (!*line
|| *line
== '#')
917 return 0; /* empty or comment */
918 p
= expand_line (line
);
923 for ( ;spacep (line
); line
++)
925 if (!*line
|| *line
== '#')
928 return 0; /* empty or comment */
931 for (p
=line
; *p
&& !spacep (p
) && *p
!= '='; p
++)
940 for (*p
++ = 0; spacep (p
); p
++)
946 die ("syntax error");
951 { /* this is an assignment */
952 for (p
++; spacep (p
); p
++)
956 unset_var (assign_to
);
961 for (; *p
&& !spacep (p
); p
++)
967 for (*p
++ = 0; spacep (p
); p
++)
971 for (i
=0; cmdtbl
[i
].name
&& strcmp (stmt
, cmdtbl
[i
].name
); i
++)
976 die ("invalid statement `%s'\n", stmt
);
979 set_var (assign_to
, stmt
);
985 cmdtbl
[i
].fnc (assign_to
, p
);
987 return cmdtbl
[i
].fnc
? 0:1;
993 main (int argc
, char **argv
)
999 invocation_name
= "asschk";
1002 invocation_name
= *argv
++;
1004 p
= strrchr (invocation_name
, '/');
1006 invocation_name
= p
+1;
1010 set_var ("?","1"); /* defaults to true */
1012 for (; argc
; argc
--, argv
++)
1017 if (!strcmp (p
, "--verbose"))
1019 else if (!strcmp (p
, "--no-echo"))
1021 else if (*p
== '-' && p
[1] == 'D')
1024 pend
= strchr (p
, '=');
1029 set_var (p
, pend
+1);
1035 else if (*p
== '-' && p
[1] == '-' && !p
[2])
1044 die ("usage: asschk [--verbose] {-D<name>[=<value>]}");
1047 while (fgets (buffer
, sizeof buffer
, stdin
))
1049 p
= strchr (buffer
,'\n');
1051 die ("incomplete script line");
1053 if (interpreter (buffer
))