diff --git a/doc/ChangeLog b/doc/ChangeLog index d083a9b2f..cd2f616cc 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,3 +1,8 @@ +2008-05-20 Marcus Brinkmann + + * tools.texi (Invoking gpgconf): Add --dry-run and --check-options. + (Checking programs): Document --check-options. + 2008-05-15 Marcus Brinkmann * gpg.texi (Operational GPG Commands): Mention the way to change diff --git a/doc/tools.texi b/doc/tools.texi index 79a909d2f..888012e0c 100644 --- a/doc/tools.texi +++ b/doc/tools.texi @@ -230,6 +230,9 @@ List all options of the component @var{component}. @item --change-options @var{component} Change the options of the component @var{component}. +@item --check-options @var{component} +Check the options for the component @var{component}. + @item --apply-defaults Update all configuration files with values taken from the global configuration file (usually @file{/etc/gnupg/gpgconf.conf}). @@ -260,11 +263,10 @@ The following options may be used: Outputs additional information while running. Specifically, this extends numerical field values by human-readable descriptions. -@c FIXME: Not yet supported. -@c @item -n -@c @itemx --dry-run -@c Do not actually change anything. Useful together with -@c @code{--change-options} for testing purposes. +@item -n +@itemx --dry-run +Do not actually change anything. This is currently only implemented +for @code{--change-options} and can be used for testing purposes. @item -r @itemx --runtime @@ -531,6 +533,11 @@ gpgsm:GPG for S/MIME:/usr/local/bin/gpgsm:1:1: dirmngr:Directory Manager:/usr/local/bin/dirmngr:0:0: @end example +@noindent +The command @w{@code{--check-options @var{component}}} will verify the +configuration file in the same manner as @code{--check-programs}, but +only for the component @var{component}. + @node Listing options @subsection Listing options @@ -769,6 +776,10 @@ argument is used (only allowed if the argument is optional for this option). Otherwise, the option will be set to the specified value. @end table +@noindent +The output of the command is the same as that of +@code{--check-options} for the modified configuration file. + Examples: To set the force option, which is of basic type @code{none (0)}: diff --git a/tools/ChangeLog b/tools/ChangeLog index 6638be57a..a28b774da 100644 --- a/tools/ChangeLog +++ b/tools/ChangeLog @@ -1,3 +1,18 @@ +2008-05-20 Marcus Brinkmann + + * gpgconf.h (gc_component_check_programs): Rename to ... + (gc_check_programs): ... this. + (gc_component_change_options): Add argument OUT. + (gc_component_check_options): New function. + * gpgconf.c (enum cmd_and_opt_values): New option aCheckOptions. + (opts): Add new option aCheckOptions (aka --check-options). + (main): Handle new option aCheckOptions. + * gpgconf-comp.c (gc_component_check_programs): Rename to ... + (gc_check_programs): ... this. Refactor core of it to ... + (gc_component_check_options): ... this new function. + (gc_component_change_options): Add new argument OUT. Externally + verify all changes. Implement option --dry-run. + 2008-05-09 Werner Koch * gpgconf-comp.c (my_dgettext) [USE_SIMPLE_GETTEXT]: Hack to diff --git a/tools/gpgconf-comp.c b/tools/gpgconf-comp.c index 664589b9c..5dc32d6eb 100644 --- a/tools/gpgconf-comp.c +++ b/tools/gpgconf-comp.c @@ -1317,118 +1317,140 @@ collect_error_output (int fd, const char *tag) } - -/* Check all components that are available. */ -void -gc_component_check_programs (FILE *out) +/* Check the options of a single component. Returns 0 if everything + is OK. */ +int +gc_component_check_options (int component, FILE *out, const char *conf_file) { gpg_error_t err; - gc_component_t component; unsigned int result; int backend_seen[GC_BACKEND_NR]; gc_backend_t backend; gc_option_t *option; - const char *desc; const char *pgmname; - const char *argv[2]; + const char *argv[4]; + int i; pid_t pid; int exitcode; int filedes[2]; - error_line_t errlines, errptr; + error_line_t errlines; /* We use a temporary file to collect the error output. It would be better to use a pipe here but as of now we have no suitable fucntion to create a portable pipe outside of exechelp. Thus it is easier to use the tempfile approach. */ - for (component = 0; component < GC_COMPONENT_NR; component++) + + for (backend = 0; backend < GC_BACKEND_NR; backend++) + backend_seen[backend] = 0; + + option = gc_component[component].options; + for (; option && option->name; option++) { - if (!gc_component[component].options) - continue; + if ((option->flags & GC_OPT_FLAG_GROUP)) + continue; + backend = option->backend; + if (backend_seen[backend]) + continue; + backend_seen[backend] = 1; + assert (backend != GC_BACKEND_ANY); + if (!gc_backend[backend].program) + continue; + if (!gc_backend[backend].module_name) + continue; - for (backend = 0; backend < GC_BACKEND_NR; backend++) - backend_seen[backend] = 0; + break; + } + if (! option || ! option->name) + return 0; - option = gc_component[component].options; - for (; option && option->name; option++) - { - if ((option->flags & GC_OPT_FLAG_GROUP)) - continue; - backend = option->backend; - if (backend_seen[backend]) - continue; - backend_seen[backend] = 1; - assert (backend != GC_BACKEND_ANY); - if (!gc_backend[backend].program) - continue; - if (!gc_backend[backend].module_name) - continue; + pgmname = gnupg_module_name (gc_backend[backend].module_name); + i = 0; + if (conf_file) + { + argv[i++] = "--options"; + argv[i++] = conf_file; + } + argv[i++] = "--gpgconf-test"; + argv[i++] = NULL; + + err = gnupg_create_inbound_pipe (filedes); + if (err) + gc_error (1, 0, _("error creating a pipe: %s\n"), + gpg_strerror (err)); + + result = 0; + errlines = NULL; + if (gnupg_spawn_process_fd (pgmname, argv, -1, -1, filedes[1], &pid)) + { + close (filedes[0]); + close (filedes[1]); + result |= 1; /* Program could not be run. */ + } + else + { + close (filedes[1]); + errlines = collect_error_output (filedes[0], + gc_component[component].name); + if (gnupg_wait_process (pgmname, pid, &exitcode)) + { + if (exitcode == -1) + result |= 1; /* Program could not be run or it + terminated abnormally. */ + result |= 2; /* Program returned an error. */ + } + } + + /* If the program could not be run, we can't tell whether + the config file is good. */ + if (result & 1) + result |= 2; + + if (out) + { + const char *desc; + error_line_t errptr; - pgmname = gnupg_module_name (gc_backend[backend].module_name); - argv[0] = "--gpgconf-test"; - argv[1] = NULL; + desc = gc_component[component].desc; + desc = my_dgettext (gc_component[component].desc_domain, desc); + fprintf (out, "%s:%s:", + gc_component[component].name, my_percent_escape (desc)); + fputs (my_percent_escape (pgmname), out); + fprintf (out, ":%d:%d:", !(result & 1), !(result & 2)); + for (errptr = errlines; errptr; errptr = errptr->next) + { + if (errptr != errlines) + fputs ("\n:::::", out); /* Continuation line. */ + if (errptr->fname) + fputs (my_percent_escape (errptr->fname), out); + putc (':', out); + if (errptr->fname) + fprintf (out, "%u", errptr->lineno); + putc (':', out); + fputs (my_percent_escape (errptr->errtext), out); + putc (':', out); + } + putc ('\n', out); + } - err = gnupg_create_inbound_pipe (filedes); - if (err) - gc_error (1, 0, _("error creating a pipe: %s\n"), - gpg_strerror (err)); + while (errlines) + { + error_line_t tmp = errlines->next; + xfree (errlines); + errlines = tmp; + } - result = 0; - errlines = NULL; - if (gnupg_spawn_process_fd (pgmname, argv, -1, -1, filedes[1], &pid)) - { - close (filedes[0]); - close (filedes[1]); - result |= 1; /* Program could not be run. */ - } - else - { - close (filedes[1]); - errlines = collect_error_output (filedes[0], - gc_component[component].name); - if (gnupg_wait_process (pgmname, pid, &exitcode)) - { - if (exitcode == -1) - result |= 1; /* Program could not be run or it - terminated abnormally. */ - result |= 2; /* Program returned an error. */ - } - } - - /* If the program could not be run, we can't tell whether - the config file is good. */ - if ((result&1)) - result |= 2; - - desc = gc_component[component].desc; - desc = my_dgettext (gc_component[component].desc_domain, desc); - fprintf (out, "%s:%s:", - gc_component[component].name, my_percent_escape (desc)); - fputs (my_percent_escape (pgmname), out); - fprintf (out, ":%d:%d:", !(result & 1), !(result & 2)); - for (errptr = errlines; errptr; errptr = errptr->next) - { - if (errptr != errlines) - fputs ("\n:::::", out); /* Continuation line. */ - if (errptr->fname) - fputs (my_percent_escape (errptr->fname), out); - putc (':', out); - if (errptr->fname) - fprintf (out, "%u", errptr->lineno); - putc (':', out); - fputs (my_percent_escape (errptr->errtext), out); - putc (':', out); - } - putc ('\n', out); - - while (errlines) - { - error_line_t tmp = errlines->next; - xfree (errlines); - errlines = tmp; - } - break; /* Loop over options of this component */ - } - } + return result; +} + + +/* Check all components that are available. */ +void +gc_check_programs (FILE *out) +{ + gc_component_t component; + + for (component = 0; component < GC_COMPONENT_NR; component++) + gc_component_check_options (component, out, NULL); } @@ -2831,7 +2853,7 @@ change_one_value (gc_option_t *option, int *runtime, modifications are expected to already have been set to the global table. */ void -gc_component_change_options (int component, FILE *in) +gc_component_change_options (int component, FILE *in, FILE *out) { int err = 0; int runtime[GC_BACKEND_NR]; @@ -2935,10 +2957,26 @@ gc_component_change_options (int component, FILE *in) } if (gc_backend[option->backend].program) - err = change_options_program (component, option->backend, - &src_pathname[option->backend], - &dest_pathname[option->backend], - &orig_pathname[option->backend]); + { + err = change_options_program (component, option->backend, + &src_pathname[option->backend], + &dest_pathname[option->backend], + &orig_pathname[option->backend]); + if (! err) + { + /* External verification. */ + err = gc_component_check_options (component, out, + src_pathname[option->backend]); + if (err) + { + gc_error (0, 0, + _("External verification of component %s failed"), + gc_component[component].name); + errno = EINVAL; + } + } + + } else err = change_options_file (component, option->backend, &src_pathname[option->backend], @@ -2951,7 +2989,7 @@ gc_component_change_options (int component, FILE *in) option++; } - if (!err) + if (! err && ! opt.dry_run) { int i; @@ -2994,12 +3032,12 @@ gc_component_change_options (int component, FILE *in) } } - if (err) + if (err || opt.dry_run) { int i; int saved_errno = errno; - /* An error occured. */ + /* An error occured or a dry-run is requested. */ for (i = 0; i < GC_BACKEND_NR; i++) { if (src_pathname[i]) @@ -3027,7 +3065,11 @@ gc_component_change_options (int component, FILE *in) unlink (dest_pathname[i]); } } - gc_error (1, saved_errno, "could not commit changes"); + if (err) + gc_error (1, saved_errno, "could not commit changes"); + + /* Fall-through for dry run. */ + goto leave; } /* If it all worked, notify the daemons of the changes. */ @@ -3055,6 +3097,7 @@ gc_component_change_options (int component, FILE *in) rename (orig_pathname[backend], backup_pathname); } + leave: xfree (line); } @@ -3463,7 +3506,7 @@ gc_process_gpgconf_conf (const char *fname_arg, int update, int defaults, for (component_id = 0; component_id < GC_COMPONENT_NR; component_id++) { - gc_component_change_options (component_id, NULL); + gc_component_change_options (component_id, NULL, NULL); } opt.runtime = save_opt_runtime; diff --git a/tools/gpgconf.c b/tools/gpgconf.c index 9c1c77a2d..8813d545b 100644 --- a/tools/gpgconf.c +++ b/tools/gpgconf.c @@ -43,6 +43,7 @@ enum cmd_and_opt_values aCheckPrograms, aListOptions, aChangeOptions, + aCheckOptions, aApplyDefaults, aListConfig, aCheckConfig @@ -59,6 +60,7 @@ static ARGPARSE_OPTS opts[] = { aCheckPrograms, "check-programs", 256, N_("check all programs") }, { aListOptions, "list-options", 256, N_("|COMPONENT|list options") }, { aChangeOptions, "change-options", 256, N_("|COMPONENT|change options") }, + { aCheckOptions, "check-options", 256, N_("|COMPONENT|check options") }, { aApplyDefaults, "apply-defaults", 256, N_("apply global default values") }, { aListConfig, "list-config", 256, @@ -167,6 +169,7 @@ main (int argc, char **argv) case aCheckPrograms: case aListOptions: case aChangeOptions: + case aCheckOptions: case aApplyDefaults: case aListConfig: case aCheckConfig: @@ -192,11 +195,12 @@ main (int argc, char **argv) case aCheckPrograms: /* Check all programs. */ - gc_component_check_programs (get_outfp (&outfp)); + gc_check_programs (get_outfp (&outfp)); break; case aListOptions: case aChangeOptions: + case aCheckOptions: if (!fname) { fputs (_("usage: gpgconf [options] "), stderr); @@ -219,8 +223,10 @@ main (int argc, char **argv) exit (1); if (cmd == aListOptions) gc_component_list_options (idx, get_outfp (&outfp)); + else if (cmd == aChangeOptions) + gc_component_change_options (idx, stdin, get_outfp (&outfp)); else - gc_component_change_options (idx, stdin); + gc_component_check_options (idx, get_outfp (&outfp), NULL); } break; diff --git a/tools/gpgconf.h b/tools/gpgconf.h index 2f4bd5216..0a27dd9c3 100644 --- a/tools/gpgconf.h +++ b/tools/gpgconf.h @@ -43,7 +43,7 @@ void gc_error (int status, int errnum, const char *fmt, ...); void gc_component_list_components (FILE *out); /* List all programs along with their status. */ -void gc_component_check_programs (FILE *out); +void gc_check_programs (FILE *out); /* Find the component with the name NAME. Returns -1 if not found. */ @@ -57,7 +57,12 @@ void gc_component_retrieve_options (int component); void gc_component_list_options (int component, FILE *out); /* Read the modifications from IN and apply them. */ -void gc_component_change_options (int component, FILE *in); +void gc_component_change_options (int component, FILE *in, FILE *out); + +/* Check the options of a single component. Returns 0 if everything + is OK. */ +int gc_component_check_options (int component, FILE *out, + const char *conf_file); /* Process global configuration file. */ int gc_process_gpgconf_conf (const char *fname, int update, int defaults,