New command --check-programs for gpgconf.

This commit is contained in:
Werner Koch 2007-08-29 09:51:37 +00:00
parent f268889b8f
commit b13587ef16
17 changed files with 267 additions and 25 deletions

4
NEWS
View File

@ -11,6 +11,10 @@ Noteworthy changes in version 2.0.7
enforce-passphrase-constraints and max-passphrase-days to enforce-passphrase-constraints and max-passphrase-days to
gpg-agent. gpg-agent.
* Add command --check-components to gpgconf. Gpgconf now uses the
installed versions of the programs and does not anymore search via
PATH for them.
Noteworthy changes in version 2.0.6 (2007-08-16) Noteworthy changes in version 2.0.6 (2007-08-16)
------------------------------------------------ ------------------------------------------------

View File

@ -126,7 +126,7 @@ check_passphrase_pattern (ctrl_t ctrl, const char *pw)
if (gnupg_spawn_process_fd (pgmname, argv, fileno (infp), -1, -1, &pid)) if (gnupg_spawn_process_fd (pgmname, argv, fileno (infp), -1, -1, &pid))
result = 1; /* Execute error - assume password should no be used. */ result = 1; /* Execute error - assume password should no be used. */
else if (gnupg_wait_process (pgmname, pid)) else if (gnupg_wait_process (pgmname, pid, NULL))
result = 1; /* Helper returned an error - probably a match. */ result = 1; /* Helper returned an error - probably a match. */
else else
result = 0; /* Success; i.e. no match. */ result = 0; /* Success; i.e. no match. */

View File

@ -1,3 +1,11 @@
2007-08-29 Werner Koch <wk@g10code.com>
* exechelp.c (gnupg_wait_process): Add arg EXITCODE. Changed all
callers.
* util.h (GNUPG_MODULE_NAME_GPGSM, GNUPG_MODULE_NAME_GPG): New.
* homedir.c (gnupg_module_name): Add them
2007-08-28 Werner Koch <wk@g10code.com> 2007-08-28 Werner Koch <wk@g10code.com>
* gettime.c (check_isotime, add_isotime): New. Orginally written * gettime.c (check_isotime, add_isotime): New. Orginally written

View File

@ -570,11 +570,13 @@ gnupg_spawn_process_fd (const char *pgmname, const char *argv[],
/* Wait for the process identified by PID to terminate. PGMNAME should /* Wait for the process identified by PID to terminate. PGMNAME should
be the same as suplieed to the spawn fucntion and is only used for be the same as supplied to the spawn function and is only used for
diagnostics. Returns 0 if the process succeded, GPG_ERR_GENERAL for diagnostics. Returns 0 if the process succeeded, GPG_ERR_GENERAL
any failures of the spawned program or other error codes.*/ for any failures of the spawned program or other error codes. If
EXITCODE is not NULL the exit code of the process is stored at this
address or -1 if it could not be retrieved. */
gpg_error_t gpg_error_t
gnupg_wait_process (const char *pgmname, pid_t pid) gnupg_wait_process (const char *pgmname, pid_t pid, int *exitcode)
{ {
gpg_err_code_t ec; gpg_err_code_t ec;
@ -583,6 +585,9 @@ gnupg_wait_process (const char *pgmname, pid_t pid)
int code; int code;
DWORD exc; DWORD exc;
if (exitcode)
*exitcode = -1;
if (pid == (pid_t)(-1)) if (pid == (pid_t)(-1))
return gpg_error (GPG_ERR_INV_VALUE); return gpg_error (GPG_ERR_INV_VALUE);
@ -609,10 +614,16 @@ gnupg_wait_process (const char *pgmname, pid_t pid)
{ {
log_error (_("error running `%s': exit status %d\n"), log_error (_("error running `%s': exit status %d\n"),
pgmname, (int)exc ); pgmname, (int)exc );
if (exitcode)
*exitcode = (int)exc;
ec = GPG_ERR_GENERAL; ec = GPG_ERR_GENERAL;
} }
else else
ec = 0; {
if (exitcode)
*exitcode = 0;
ec = 0;
}
CloseHandle (proc); CloseHandle (proc);
break; break;
@ -626,6 +637,9 @@ gnupg_wait_process (const char *pgmname, pid_t pid)
#else /* !HAVE_W32_SYSTEM */ #else /* !HAVE_W32_SYSTEM */
int i, status; int i, status;
if (exitcode)
*exitcode = -1;
if (pid == (pid_t)(-1)) if (pid == (pid_t)(-1))
return gpg_error (GPG_ERR_INV_VALUE); return gpg_error (GPG_ERR_INV_VALUE);
@ -650,6 +664,8 @@ gnupg_wait_process (const char *pgmname, pid_t pid)
{ {
log_error (_("error running `%s': exit status %d\n"), pgmname, log_error (_("error running `%s': exit status %d\n"), pgmname,
WEXITSTATUS (status)); WEXITSTATUS (status));
if (exitcode)
*exitcode = WEXITSTATUS (status);
ec = GPG_ERR_GENERAL; ec = GPG_ERR_GENERAL;
} }
else if (!WIFEXITED (status)) else if (!WIFEXITED (status))
@ -658,11 +674,14 @@ gnupg_wait_process (const char *pgmname, pid_t pid)
ec = GPG_ERR_GENERAL; ec = GPG_ERR_GENERAL;
} }
else else
ec = 0; {
if (exitcode)
*exitcode = 0;
ec = 0;
}
#endif /* !HAVE_W32_SYSTEM */ #endif /* !HAVE_W32_SYSTEM */
return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec); return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec);
} }

View File

@ -52,8 +52,10 @@ gpg_error_t gnupg_spawn_process_fd (const char *pgmname,
/* Wait for the process identified by PID to terminate. PGMNAME should /* Wait for the process identified by PID to terminate. PGMNAME should
be the same as supplied to the spawn fucntion and is only used for be the same as supplied to the spawn fucntion and is only used for
diagnostics. Returns 0 if the process succeded, GPG_ERR_GENERAL diagnostics. Returns 0 if the process succeded, GPG_ERR_GENERAL
for any failures of the spawned program or other error codes.*/ for any failures of the spawned program or other error codes. If
gpg_error_t gnupg_wait_process (const char *pgmname, pid_t pid); EXITCODE is not NULL the exit code of the process is stored at this
address or -1 if it could not be retrieved. */
gpg_error_t gnupg_wait_process (const char *pgmname, pid_t pid, int *exitcode);
/* Spawn a new process and immediatley detach from it. The name of /* Spawn a new process and immediatley detach from it. The name of

View File

@ -372,6 +372,12 @@ gnupg_module_name (int which)
case GNUPG_MODULE_NAME_CHECK_PATTERN: case GNUPG_MODULE_NAME_CHECK_PATTERN:
X(libexecdir, "gpg-check-pattern"); X(libexecdir, "gpg-check-pattern");
case GNUPG_MODULE_NAME_GPGSM:
X(bindir, "gpgsm");
case GNUPG_MODULE_NAME_GPG:
X(bindir, "gpg2");
default: default:
BUG (); BUG ();
} }

View File

@ -182,12 +182,16 @@ const char *gnupg_libdir (void);
const char *gnupg_datadir (void); const char *gnupg_datadir (void);
const char *dirmngr_socket_name (void); const char *dirmngr_socket_name (void);
/* All module names. We also include gpg and gpgsm for the sake for
gpgconf. */
#define GNUPG_MODULE_NAME_AGENT 1 #define GNUPG_MODULE_NAME_AGENT 1
#define GNUPG_MODULE_NAME_PINENTRY 2 #define GNUPG_MODULE_NAME_PINENTRY 2
#define GNUPG_MODULE_NAME_SCDAEMON 3 #define GNUPG_MODULE_NAME_SCDAEMON 3
#define GNUPG_MODULE_NAME_DIRMNGR 4 #define GNUPG_MODULE_NAME_DIRMNGR 4
#define GNUPG_MODULE_NAME_PROTECT_TOOL 5 #define GNUPG_MODULE_NAME_PROTECT_TOOL 5
#define GNUPG_MODULE_NAME_CHECK_PATTERN 6 #define GNUPG_MODULE_NAME_CHECK_PATTERN 6
#define GNUPG_MODULE_NAME_GPGSM 7
#define GNUPG_MODULE_NAME_GPG 8
const char *gnupg_module_name (int which); const char *gnupg_module_name (int which);

View File

@ -1,3 +1,7 @@
2007-08-29 Werner Koch <wk@g10code.com>
* tools.texi (Checking programs): New.
2007-08-27 Werner Koch <wk@g10code.com> 2007-08-27 Werner Koch <wk@g10code.com>
* examples/pwpattern.list: New. * examples/pwpattern.list: New.

View File

@ -34,7 +34,14 @@
# :staff gpg-agent allow-mark-trusted [change] # :staff gpg-agent allow-mark-trusted [change]
# gpg-agent min-passphrase-len 6 # gpg-agent min-passphrase-len 6
# #
# * gpg-agent min-passphrase-len [no-change] 12 # * gpg-agent min-passphrase-len [no-change] 8
# gpg-agent min-passphrase-nonalpha [no-change] 1
# gpg-agent max-passphrase-days [no-change] 700
# gpg-agent enable-passphrase-history [no-change]
# gpg-agent enforce-passphrase-policy [default]
# gpg-agent enforce-passphrase-policy [no-change]
# gpg-agent max-cache-ttl [no-change] 10800
# gpg-agent max-cache-ttl-ssh [no-change] 10800
# gpg-agent allow-mark-trusted [default] # gpg-agent allow-mark-trusted [default]
# gpg-agent allow-mark-trusted [no-change] # gpg-agent allow-mark-trusted [no-change]
# gpgsm enable-ocsp # gpgsm enable-ocsp
@ -46,7 +53,7 @@
# to 6. All other users are not allowed to change # to 6. All other users are not allowed to change
# "min-passphrase-len" and "allow-mark-trusted". When "gpgconf # "min-passphrase-len" and "allow-mark-trusted". When "gpgconf
# --apply-defaults" is used for them, "min-passphrase-len" is set to # --apply-defaults" is used for them, "min-passphrase-len" is set to
# 12, "allow-mark-trusted" deleted from the config file and # 8, "allow-mark-trusted" deleted from the config file and
# "enable-ocsp" is put into the config file of gpgsm. The latter may # "enable-ocsp" is put into the config file of gpgsm. The latter may
# be changed by any user. # be changed by any user.
#------------------------------------------------------------------- #-------------------------------------------------------------------

View File

@ -200,6 +200,7 @@ throughout this section.
* Invoking gpgconf:: List of all commands and options. * Invoking gpgconf:: List of all commands and options.
* Format conventions:: Formatting conventions relevant for all commands. * Format conventions:: Formatting conventions relevant for all commands.
* Listing components:: List all gpgconf components. * Listing components:: List all gpgconf components.
* Checking programs:: Check all programs know to gpgconf.
* Listing options:: List all options of a component. * Listing options:: List all options of a component.
* Changing options:: Changing options of a component. * Changing options:: Changing options of a component.
* Files used by gpgconf:: What files are used by gpgconf. * Files used by gpgconf:: What files are used by gpgconf.
@ -218,6 +219,9 @@ One of the following commands must be given:
List all components. This is the default command used if none is List all components. This is the default command used if none is
specified. specified.
@item --check-programs
List all available backend programs and test whether they are runnable.
@item --list-options @var{component} @item --list-options @var{component}
List all options of the component @var{component}. List all options of the component @var{component}.
@ -335,6 +339,14 @@ by a space, followed by a human readable description of that value (if
the verbose option is used). You should ignore everything in the the verbose option is used). You should ignore everything in the
field that follows the number. field that follows the number.
@item @w{boolean value}
Some fields contain a @emph{boolean value}. This is a number with
either the value 0 or 1. The number may be followed by a space,
followed by a human readable description of that value (if the verbose
option is used). You should ignore everything in the field that follows
the number; checking just the first character is sufficient in this
case.
@item option @item option
Some fields contain an @emph{option} argument. The format of an Some fields contain an @emph{option} argument. The format of an
option argument depends on the type of the option and on some flags: option argument depends on the type of the option and on some flags:
@ -436,6 +448,62 @@ dirmngr:Directory Manager
@end example @end example
@node Checking programs
@subsection Checking programs
The command @code{--check-programs} is similar to
@code{--list-components} but works on backend programs and not on
components. It runs each program to test wether it is installed and
runnable. This also includes a syntax check of all config file options
of the program.
The command argument @code{--check-programs} lists all available
programs, one per line. The format of each line is:
@code{@var{name}:@var{description}:@var{program name}:@var{available}:@var{config okay}:}
@table @var
@item name
This field contains a name tag of the program which is identical to the
name of the component. The name tag is to be used @emph{verbatim}. It
is thus not in any escaped format.
@item description
The @emph{string} in this field contains a human-readable description
of the component. It can be displayed to the user of the GUI for
informational purposes. It is @emph{percent-escaped} and
@emph{localized}.
@item program name
The @emph{string} in this field contains the absolute name of the
program's file. It can be used to unambiguously invoke that program.
It is @emph{percent-escaped}.
@item available
The @emph{boolean value} in this field indicates whether the program is
installed and runnable.
@item config okay
The @emph{boolean value} in this field indicates whether the program's
config file is syntactically okay.
@end table
@noindent
In the following example the @command{dirmngr} is not runnable and the
configuration file of @command{scdaemon} is not okay.
@example
$ gpgconf --check-programs
gpg:GPG for OpenPGP:/usr/local/bin/gpg2:1:1:
gpg-agent:GPG Agent:/usr/local/bin/gpg-agent:1:1:
scdaemon:Smartcard Daemon:/usr/local/bin/scdaemon:1:0:
gpgsm:GPG for S/MIME:/usr/local/bin/gpgsm:1:1:
dirmngr:Directory Manager:/usr/local/bin/dirmngr:0:0:
@end example
@node Listing options @node Listing options
@subsection Listing options @subsection Listing options

View File

@ -713,7 +713,7 @@ export_p12 (ctrl_t ctrl, const unsigned char *certimg, size_t certimglen,
fclose (fp); fclose (fp);
if (pid != -1) if (pid != -1)
{ {
if (!gnupg_wait_process (pgmname, pid)) if (!gnupg_wait_process (pgmname, pid, NULL))
child_err = 0; child_err = 0;
} }
if (!err) if (!err)

View File

@ -635,7 +635,7 @@ parse_p12 (ctrl_t ctrl, ksba_reader_t reader,
fclose (fp); fclose (fp);
if (pid != -1) if (pid != -1)
{ {
if (!gnupg_wait_process (pgmname, pid)) if (!gnupg_wait_process (pgmname, pid, NULL))
child_err = 0; child_err = 0;
} }
if (!err) if (!err)

View File

@ -1,3 +1,12 @@
2007-08-29 Werner Koch <wk@g10code.com>
* gpgconf.c: New comamnd --check-programs.
* gpgconf-comp.c (gc_component_check_programs): New.
(gc_backend): Add member MODULE_NAME and add these module names.
(retrieve_options_from_program): Use module name so that we use an
absolute file name and don't rely on $PATH.
* no-libgcrypt.c (gcry_control): New.
2007-08-28 Werner Koch <wk@g10code.com> 2007-08-28 Werner Koch <wk@g10code.com>
* gpgconf-comp.c <gpg-agent>: Add options --max-passphrase-days * gpgconf-comp.c <gpg-agent>: Add options --max-passphrase-days
@ -713,7 +722,7 @@
* watchgnupg.c: New. * watchgnupg.c: New.
Copyright 2003, 2004, 2005 Free Software Foundation, Inc. Copyright 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
This file is free software; as a special exception the author gives This file is free software; as a special exception the author gives
unlimited permission to copy and/or distribute it, with or without unlimited permission to copy and/or distribute it, with or without

View File

@ -43,6 +43,7 @@
#define JNLIB_NEED_LOG_LOGV #define JNLIB_NEED_LOG_LOGV
#include "util.h" #include "util.h"
#include "i18n.h" #include "i18n.h"
#include "exechelp.h"
#include "gc-opt-flags.h" #include "gc-opt-flags.h"
#include "gpgconf.h" #include "gpgconf.h"
@ -153,6 +154,12 @@ static struct
GPGConf. In this case, PROGRAM is NULL. */ GPGConf. In this case, PROGRAM is NULL. */
char *program; char *program;
/* The module name (GNUPG_MODULE_NAME_foo) as defined by
../common/util.h. This value is used to get the actual installed
path of the program. 0 is used if no backedn program is
available. */
char module_name;
/* The runtime change callback. */ /* The runtime change callback. */
void (*runtime_change) (void); void (*runtime_change) (void);
@ -168,14 +175,18 @@ static struct
} gc_backend[GC_BACKEND_NR] = } gc_backend[GC_BACKEND_NR] =
{ {
{ NULL }, /* GC_BACKEND_ANY dummy entry. */ { NULL }, /* GC_BACKEND_ANY dummy entry. */
{ "GnuPG", GPGNAME, NULL, "gpgconf-gpg.conf" }, { "GnuPG", GPGNAME, GNUPG_MODULE_NAME_GPG,
{ "GPGSM", "gpgsm", NULL, "gpgconf-gpgsm.conf" }, NULL, "gpgconf-gpg.conf" },
{ "GPG Agent", "gpg-agent", gpg_agent_runtime_change, { "GPGSM", "gpgsm", GNUPG_MODULE_NAME_GPGSM,
"gpgconf-gpg-agent.conf" }, NULL, "gpgconf-gpgsm.conf" },
{ "SCDaemon", "scdaemon", NULL, "gpgconf-scdaemon.conf" }, { "GPG Agent", "gpg-agent", GNUPG_MODULE_NAME_AGENT,
{ "DirMngr", "dirmngr", NULL, "gpgconf-dirmngr.conf" }, gpg_agent_runtime_change, "gpgconf-gpg-agent.conf" },
{ "DirMngr LDAP Server List", NULL, NULL, "ldapserverlist-file", { "SCDaemon", "scdaemon", GNUPG_MODULE_NAME_SCDAEMON,
"LDAP Server" }, NULL, "gpgconf-scdaemon.conf" },
{ "DirMngr", "dirmngr", GNUPG_MODULE_NAME_DIRMNGR,
NULL, "gpgconf-dirmngr.conf" },
{ "DirMngr LDAP Server List", NULL, 0,
NULL, "ldapserverlist-file", "LDAP Server" },
}; };
@ -1129,6 +1140,81 @@ gc_component_list_components (FILE *out)
} }
} }
/* Check all components that are available. */
void
gc_component_check_programs (FILE *out)
{
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];
pid_t pid;
int exitcode;
for (component = 0; component < GC_COMPONENT_NR; component++)
{
if (!gc_component[component].options)
continue;
for (backend = 0; backend < GC_BACKEND_NR; backend++)
backend_seen[backend] = 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);
argv[0] = "--gpgconf-test";
argv[1] = NULL;
/* Note that under Windows the spawn fucntion returns an
error if the progrom could not be executed whereas under
Unix the wait function returns an error. */
result = 0;
if (gnupg_spawn_process_fd (pgmname, argv, -1, -1, -1, &pid))
result |= 1; /* Program could not be run. */
else 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:\n", !(result & 1), !(result & 2));
break; /* Loop over options of this component */
}
}
}
/* Find the component with the name NAME. Returns -1 if not /* Find the component with the name NAME. Returns -1 if not
found. */ found. */
@ -1362,7 +1448,10 @@ retrieve_options_from_program (gc_component_t component, gc_backend_t backend)
FILE *config; FILE *config;
char *config_pathname; char *config_pathname;
cmd_line = xasprintf ("%s --gpgconf-list", gc_backend[backend].program); cmd_line = xasprintf ("%s --gpgconf-list",
gc_backend[backend].module_name ?
gnupg_module_name (gc_backend[backend].module_name) :
gc_backend[backend].program );
config = popen (cmd_line, "r"); config = popen (cmd_line, "r");
if (!config) if (!config)
@ -1663,6 +1752,8 @@ gc_component_retrieve_options (int component)
while (process_all && ++component < GC_COMPONENT_NR); while (process_all && ++component < GC_COMPONENT_NR);
} }
/* Perform a simple validity check based on the type. Return in /* Perform a simple validity check based on the type. Return in
NEW_VALUE_NR the value of the number in NEW_VALUE if OPTION is of NEW_VALUE_NR the value of the number in NEW_VALUE if OPTION is of

View File

@ -1,5 +1,5 @@
/* gpgconf.c - Configuration utility for GnuPG /* gpgconf.c - Configuration utility for GnuPG
* Copyright (C) 2003 Free Software Foundation, Inc. * Copyright (C) 2003, 2007 Free Software Foundation, Inc.
* *
* This file is part of GnuPG. * This file is part of GnuPG.
* *
@ -40,6 +40,7 @@ enum cmd_and_opt_values
oHomedir, oHomedir,
aListComponents, aListComponents,
aCheckPrograms,
aListOptions, aListOptions,
aChangeOptions, aChangeOptions,
aApplyDefaults, aApplyDefaults,
@ -54,6 +55,7 @@ static ARGPARSE_OPTS opts[] =
{ 300, NULL, 0, N_("@Commands:\n ") }, { 300, NULL, 0, N_("@Commands:\n ") },
{ aListComponents, "list-components", 256, N_("list all components") }, { aListComponents, "list-components", 256, N_("list all components") },
{ aCheckPrograms, "check-programs", 256, N_("check all programs") },
{ aListOptions, "list-options", 256, N_("|COMPONENT|list options") }, { aListOptions, "list-options", 256, N_("|COMPONENT|list options") },
{ aChangeOptions, "change-options", 256, N_("|COMPONENT|change options") }, { aChangeOptions, "change-options", 256, N_("|COMPONENT|change options") },
{ aApplyDefaults, "apply-defaults", 256, { aApplyDefaults, "apply-defaults", 256,
@ -137,6 +139,7 @@ main (int argc, char **argv)
case oNoVerbose: opt.verbose = 0; break; case oNoVerbose: opt.verbose = 0; break;
case aListComponents: case aListComponents:
case aCheckPrograms:
case aListOptions: case aListOptions:
case aChangeOptions: case aChangeOptions:
case aApplyDefaults: case aApplyDefaults:
@ -161,6 +164,11 @@ main (int argc, char **argv)
gc_component_list_components (stdout); gc_component_list_components (stdout);
break; break;
case aCheckPrograms:
/* Check all programs. */
gc_component_check_programs (stdout);
break;
case aListOptions: case aListOptions:
case aChangeOptions: case aChangeOptions:
if (!fname) if (!fname)

View File

@ -40,6 +40,9 @@ struct
/* List all components that are available. */ /* List all components that are available. */
void gc_component_list_components (FILE *out); void gc_component_list_components (FILE *out);
/* List all programs along with their status. */
void gc_component_check_programs (FILE *out);
/* Find the component with the name NAME. Returns -1 if not /* Find the component with the name NAME. Returns -1 if not
found. */ found. */
int gc_component_find (const char *name); int gc_component_find (const char *name);

View File

@ -102,3 +102,12 @@ gcry_free (void *a)
if (a) if (a)
free (a); free (a);
} }
/* We need this dummy because exechelp.c uses gcry_control to
terminate the secure memeory. */
gcry_error_t
gcry_control (enum gcry_ctl_cmds CMD, ...)
{
return 0;
}