New command --check-programs for gpgconf.
authorWerner Koch <wk@gnupg.org>
Wed, 29 Aug 2007 09:51:37 +0000 (09:51 +0000)
committerWerner Koch <wk@gnupg.org>
Wed, 29 Aug 2007 09:51:37 +0000 (09:51 +0000)
17 files changed:
NEWS
agent/genkey.c
common/ChangeLog
common/exechelp.c
common/exechelp.h
common/homedir.c
common/util.h
doc/ChangeLog
doc/examples/gpgconf.conf
doc/tools.texi
sm/export.c
sm/import.c
tools/ChangeLog
tools/gpgconf-comp.c
tools/gpgconf.c
tools/gpgconf.h
tools/no-libgcrypt.c

diff --git a/NEWS b/NEWS
index 4ea308e..99380d8 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -11,6 +11,10 @@ Noteworthy changes in version 2.0.7
    enforce-passphrase-constraints and max-passphrase-days to
    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)
 ------------------------------------------------
index 11b093d..48ba39d 100644 (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))
     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.  */
   else
     result = 0; /* Success; i.e. no match.  */
index 7f61743..e577363 100644 (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>
 
        * gettime.c (check_isotime, add_isotime): New.  Orginally written
index 2a65970..4ec481f 100644 (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
-   be the same as suplieed to the spawn fucntion and is only used for
-   diagnostics. Returns 0 if the process succeded, GPG_ERR_GENERAL for
-   any failures of the spawned program or other error codes.*/
+   be the same as supplied to the spawn function and is only used for
+   diagnostics. Returns 0 if the process succeeded, GPG_ERR_GENERAL
+   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
-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;
 
@@ -583,6 +585,9 @@ gnupg_wait_process (const char *pgmname, pid_t pid)
   int code;
   DWORD exc;
 
+  if (exitcode)
+    *exitcode = -1;
+
   if (pid == (pid_t)(-1))
     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"),
                        pgmname, (int)exc );
+            if (exitcode)
+              *exitcode = (int)exc;
             ec = GPG_ERR_GENERAL;
           }
         else
-          ec = 0;
+          {
+            if (exitcode)
+              *exitcode = 0;
+            ec = 0;
+          }
         CloseHandle (proc);
         break;
 
@@ -626,6 +637,9 @@ gnupg_wait_process (const char *pgmname, pid_t pid)
 #else /* !HAVE_W32_SYSTEM */
   int i, status;
 
+  if (exitcode)
+    *exitcode = -1;
+
   if (pid == (pid_t)(-1))
     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,
                  WEXITSTATUS (status));
+      if (exitcode)
+        *exitcode = WEXITSTATUS (status);
       ec = GPG_ERR_GENERAL;
     }
   else if (!WIFEXITED (status))
@@ -658,11 +674,14 @@ gnupg_wait_process (const char *pgmname, pid_t pid)
       ec = GPG_ERR_GENERAL;
     }
   else 
-    ec = 0;
+    {
+      if (exitcode)
+        *exitcode = 0;
+      ec = 0;
+    }
 #endif /* !HAVE_W32_SYSTEM */
 
   return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec);
-
 }
 
 
index ff7e59d..7abc037 100644 (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
    be the same as supplied to the spawn fucntion and is only used for
    diagnostics.  Returns 0 if the process succeded, GPG_ERR_GENERAL
-   for any failures of the spawned program or other error codes.*/
-gpg_error_t gnupg_wait_process (const char *pgmname, pid_t pid);
+   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 gnupg_wait_process (const char *pgmname, pid_t pid, int *exitcode);
 
 
 /* Spawn a new process and immediatley detach from it.  The name of
index 85506b8..3105aec 100644 (file)
@@ -372,6 +372,12 @@ gnupg_module_name (int which)
     case GNUPG_MODULE_NAME_CHECK_PATTERN:
       X(libexecdir, "gpg-check-pattern");
 
+    case GNUPG_MODULE_NAME_GPGSM:
+      X(bindir, "gpgsm");
+
+    case GNUPG_MODULE_NAME_GPG:
+      X(bindir, "gpg2");
+
     default: 
       BUG ();
     }
index 75f6ed0..f358509 100644 (file)
@@ -182,12 +182,16 @@ const char *gnupg_libdir (void);
 const char *gnupg_datadir (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_PINENTRY     2
 #define GNUPG_MODULE_NAME_SCDAEMON     3 
 #define GNUPG_MODULE_NAME_DIRMNGR      4
 #define GNUPG_MODULE_NAME_PROTECT_TOOL 5
 #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);
 
 
index 10c9f1b..d083158 100644 (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>
 
        * examples/pwpattern.list: New.
index f66b6b1..0f4a021 100644 (file)
 # :staff  gpg-agent allow-mark-trusted [change]
 #         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 [no-change]
 #         gpgsm     enable-ocsp           
@@ -46,7 +53,7 @@
 # to 6.  All other users are not allowed to change
 # "min-passphrase-len" and "allow-mark-trusted".  When "gpgconf
 # --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
 # be changed by any user.
 #-------------------------------------------------------------------
index cce773d..4726331 100644 (file)
@@ -200,6 +200,7 @@ throughout this section.
 * Invoking gpgconf::      List of all commands and options.
 * Format conventions::    Formatting conventions relevant for all commands.
 * Listing components::    List all gpgconf components.
+* Checking programs::     Check all programs know to gpgconf.
 * Listing options::       List all options of a component.
 * Changing options::      Changing options of a component.
 * 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
 specified.
 
+@item --check-programs
+List all available backend programs and test whether they are runnable.
+
 @item --list-options @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
 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
 Some fields contain an @emph{option} argument.  The format of an
 option argument depends on the type of the option and on some flags:
@@ -436,6 +448,62 @@ dirmngr:Directory Manager
 @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
 @subsection Listing options
 
index e6c29ef..6854c2a 100644 (file)
@@ -713,7 +713,7 @@ export_p12 (ctrl_t ctrl, const unsigned char *certimg, size_t certimglen,
     fclose (fp);
   if (pid != -1)
     {
-      if (!gnupg_wait_process (pgmname, pid))
+      if (!gnupg_wait_process (pgmname, pid, NULL))
         child_err = 0;
     }
   if (!err)
index 4cbea84..069408f 100644 (file)
@@ -635,7 +635,7 @@ parse_p12 (ctrl_t ctrl, ksba_reader_t reader,
     fclose (fp);
   if (pid != -1)
     {
-      if (!gnupg_wait_process (pgmname, pid))
+      if (!gnupg_wait_process (pgmname, pid, NULL))
         child_err = 0;
     }
   if (!err)
index dd4f12d..7e7784d 100644 (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>
 
        * gpgconf-comp.c <gpg-agent>: Add options --max-passphrase-days
        * 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
  unlimited permission to copy and/or distribute it, with or without
index e2c03d4..748db7e 100644 (file)
@@ -43,6 +43,7 @@
 #define JNLIB_NEED_LOG_LOGV
 #include "util.h"
 #include "i18n.h"
+#include "exechelp.h"
 
 #include "gc-opt-flags.h"
 #include "gpgconf.h"
@@ -153,6 +154,12 @@ static struct
      GPGConf.  In this case, PROGRAM is NULL.  */
   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.  */
   void (*runtime_change) (void);
 
@@ -168,14 +175,18 @@ static struct
 } gc_backend[GC_BACKEND_NR] =
   {
     { NULL },          /* GC_BACKEND_ANY dummy entry.  */
-    { "GnuPG", GPGNAME, NULL, "gpgconf-gpg.conf" },
-    { "GPGSM", "gpgsm", NULL, "gpgconf-gpgsm.conf" },
-    { "GPG Agent", "gpg-agent", gpg_agent_runtime_change,
-      "gpgconf-gpg-agent.conf" },
-    { "SCDaemon", "scdaemon", NULL, "gpgconf-scdaemon.conf" },
-    { "DirMngr", "dirmngr", NULL, "gpgconf-dirmngr.conf" },
-    { "DirMngr LDAP Server List", NULL, NULL, "ldapserverlist-file",
-      "LDAP Server" },
+    { "GnuPG", GPGNAME, GNUPG_MODULE_NAME_GPG,
+      NULL, "gpgconf-gpg.conf" },
+    { "GPGSM", "gpgsm", GNUPG_MODULE_NAME_GPGSM,
+      NULL, "gpgconf-gpgsm.conf" },
+    { "GPG Agent", "gpg-agent", GNUPG_MODULE_NAME_AGENT, 
+      gpg_agent_runtime_change, "gpgconf-gpg-agent.conf" },
+    { "SCDaemon", "scdaemon", GNUPG_MODULE_NAME_SCDAEMON,
+      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" },
   };
 
 \f
@@ -1129,6 +1140,81 @@ gc_component_list_components (FILE *out)
     }
 }
 
+
+\f
+/* 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  */
+        }
+    } 
+}
+
+
 \f
 /* Find the component with the name NAME.  Returns -1 if not
    found.  */
@@ -1362,7 +1448,10 @@ retrieve_options_from_program (gc_component_t component, gc_backend_t backend)
   FILE *config;
   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");
   if (!config)
@@ -1663,6 +1752,8 @@ gc_component_retrieve_options (int component)
   while (process_all && ++component < GC_COMPONENT_NR);
 
 }
+
+
 \f
 /* 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
index e71a24e..3d81f01 100644 (file)
@@ -1,5 +1,5 @@
 /* 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.
  *
@@ -40,6 +40,7 @@ enum cmd_and_opt_values
     oHomedir,
 
     aListComponents,
+    aCheckPrograms,
     aListOptions,
     aChangeOptions,
     aApplyDefaults,
@@ -54,6 +55,7 @@ static ARGPARSE_OPTS opts[] =
     { 300, NULL, 0, N_("@Commands:\n ") },
     
     { aListComponents, "list-components", 256, N_("list all components") },
+    { aCheckPrograms, "check-programs", 256, N_("check all programs") },
     { aListOptions, "list-options", 256, N_("|COMPONENT|list options") },
     { aChangeOptions, "change-options", 256, N_("|COMPONENT|change options") },
     { aApplyDefaults, "apply-defaults", 256,
@@ -137,6 +139,7 @@ main (int argc, char **argv)
         case oNoVerbose: opt.verbose = 0; break;
 
         case aListComponents:
+        case aCheckPrograms:
         case aListOptions:
         case aChangeOptions:
         case aApplyDefaults:
@@ -161,6 +164,11 @@ main (int argc, char **argv)
       gc_component_list_components (stdout);
       break;
 
+    case aCheckPrograms:
+      /* Check all programs. */
+      gc_component_check_programs (stdout);
+      break;
+
     case aListOptions:
     case aChangeOptions:
       if (!fname)
index 59900b1..f0d3c59 100644 (file)
@@ -40,6 +40,9 @@ struct
 /* List all components that are available.  */
 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
    found.  */
 int gc_component_find (const char *name);
index 966ff16..c9122e7 100644 (file)
@@ -102,3 +102,12 @@ gcry_free (void *a)
   if (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;
+}