/* gpgconf-comp.c - Configuration utility for GnuPG.
- * Copyright (C) 2004, 2007, 2008, 2009 Free Software Foundation, Inc.
+ * Copyright (C) 2004, 2007-2011 Free Software Foundation, Inc.
+ * Copyright (C) 2016 Werner Koch
*
* This file is part of GnuPG.
*
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with GnuPG; if not, see <http://www.gnu.org/licenses/>.
+ * along with GnuPG; if not, see <https://www.gnu.org/licenses/>.
*/
#if HAVE_CONFIG_H
#include <errno.h>
#include <time.h>
#include <stdarg.h>
-#include <signal.h>
+#ifdef HAVE_SIGNAL_H
+# include <signal.h>
+#endif
#include <ctype.h>
#ifdef HAVE_W32_SYSTEM
# define WIN32_LEAN_AND_MEAN 1
#endif
/* For log_logv(), asctimestamp(), gnupg_get_time (). */
-#define JNLIB_NEED_LOG_LOGV
-#include "util.h"
-#include "i18n.h"
-#include "exechelp.h"
+#include "../common/util.h"
+#include "../common/i18n.h"
+#include "../common/exechelp.h"
+#include "../common/sysutils.h"
+#include "../common/status.h"
-#include "gc-opt-flags.h"
+#include "../common/gc-opt-flags.h"
#include "gpgconf.h"
-
/* There is a problem with gpg 1.4 under Windows: --gpgconf-list
returns a plain filename without escaping. As long as we have not
- fixed that we need to use gpg2 - it might actually be better to use
- gpg2 in any case. */
-#ifdef HAVE_W32_SYSTEM
+ fixed that we need to use gpg2. */
+#if defined(HAVE_W32_SYSTEM) && !defined(HAVE_W32CE_SYSTEM)
#define GPGNAME "gpg2"
#else
-#define GPGNAME "gpg"
+#define GPGNAME GPG_NAME
#endif
\f
va_list arg_ptr;
va_start (arg_ptr, fmt);
- log_logv (JNLIB_LOG_ERROR, fmt, arg_ptr);
+ log_logv (GPGRT_LOG_ERROR, fmt, arg_ptr);
va_end (arg_ptr);
if (errnum)
{
log_printf (NULL);
log_printf ("fatal error (exit status %i)\n", status);
- exit (status);
+ gpgconf_failure (gpg_error_from_errno (errnum));
}
}
\f
/* Forward declaration. */
-static void gpg_agent_runtime_change (void);
-static void scdaemon_runtime_change (void);
+static void gpg_agent_runtime_change (int killflag);
+static void scdaemon_runtime_change (int killflag);
+static void dirmngr_runtime_change (int killflag);
/* Backend configuration. Backends are used to decide how the default
and current value of an option can be determined, and how the
/* The GnuPG SCDaemon. */
GC_BACKEND_SCDAEMON,
- /* The Aegypten directory manager. */
+ /* The GnuPG directory manager. */
GC_BACKEND_DIRMNGR,
- /* The LDAP server list file for the Aegypten director manager. */
+ /* The LDAP server list file for the director manager. */
GC_BACKEND_DIRMNGR_LDAP_SERVER_LIST,
+ /* The Pinentry (not a part of GnuPG, proper). */
+ GC_BACKEND_PINENTRY,
+
/* The number of the above entries. */
GC_BACKEND_NR
} gc_backend_t;
/* To be able to implement generic algorithms for the various
backends, we collect all information about them in this struct. */
-static struct
+static const struct
{
/* The name of the backend. */
const char *name;
/* 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
+ path of the program. 0 is used if no backend program is
available. */
char module_name;
- /* The runtime change callback. */
- void (*runtime_change) (void);
+ /* The runtime change callback. If KILLFLAG is true the component
+ is killed and not just reloaded. */
+ void (*runtime_change) (int killflag);
/* The option name for the configuration filename of this backend.
This must be an absolute filename. It can be an option from a
} gc_backend[GC_BACKEND_NR] =
{
{ NULL }, /* GC_BACKEND_ANY dummy entry. */
- { "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,
- scdaemon_runtime_change, "gpgconf-scdaemon.conf" },
- { "DirMngr", "dirmngr", GNUPG_MODULE_NAME_DIRMNGR,
- NULL, "gpgconf-dirmngr.conf" },
- { "DirMngr LDAP Server List", NULL, 0,
+ { GPG_DISP_NAME, GPGNAME, GNUPG_MODULE_NAME_GPG,
+ NULL, GPGCONF_NAME "-" GPG_NAME ".conf" },
+ { GPGSM_DISP_NAME, GPGSM_NAME, GNUPG_MODULE_NAME_GPGSM,
+ NULL, GPGCONF_NAME "-" GPGSM_NAME ".conf" },
+ { GPG_AGENT_DISP_NAME, GPG_AGENT_NAME, GNUPG_MODULE_NAME_AGENT,
+ gpg_agent_runtime_change, GPGCONF_NAME"-" GPG_AGENT_NAME ".conf" },
+ { SCDAEMON_DISP_NAME, SCDAEMON_NAME, GNUPG_MODULE_NAME_SCDAEMON,
+ scdaemon_runtime_change, GPGCONF_NAME"-" SCDAEMON_NAME ".conf" },
+ { DIRMNGR_DISP_NAME, DIRMNGR_NAME, GNUPG_MODULE_NAME_DIRMNGR,
+ dirmngr_runtime_change, GPGCONF_NAME "-" DIRMNGR_NAME ".conf" },
+ { DIRMNGR_DISP_NAME " LDAP Server List", NULL, 0,
NULL, "ldapserverlist-file", "LDAP Server" },
+ { "Pinentry", "pinentry", GNUPG_MODULE_NAME_PINENTRY,
+ NULL, GPGCONF_NAME "-pinentry.conf" },
};
\f
/* For every argument, we record some information about it in the
following struct. */
-static struct
+static const struct
{
/* For every argument type exists a basic argument type that can be
used as a fallback for input and validation purposes. */
} gc_expert_level_t;
/* A description for each expert level. */
-static struct
+static const struct
{
const char *name;
} gc_level[] =
several times. A comma separated list of arguments is used as the
argument value. */
#define GC_OPT_FLAG_LIST (1UL << 2)
-/* The NO_CHANGE flag for an option indicates that the user should not
- be allowed to change this option using the standard gpgconf method.
- Frontends using gpgconf should grey out such options, so that only
- the current value is displayed. */
-#define GC_OPT_FLAG_NO_CHANGE (1UL <<7)
/* A human-readable description for each flag. */
-static struct
+static const struct
{
const char *name;
} gc_flag[] =
#define GC_OPTION_NULL { NULL }
\f
+#ifndef BUILD_WITH_AGENT
+#define gc_options_gpg_agent NULL
+#else
/* The options of the GC_COMPONENT_GPG_AGENT component. */
static gc_option_t gc_options_gpg_agent[] =
{
/* The configuration file to which we write the changes. */
- { "gpgconf-gpg-agent.conf", GC_OPT_FLAG_NONE, GC_LEVEL_INTERNAL,
+ { GPGCONF_NAME"-" GPG_AGENT_NAME ".conf",
+ GC_OPT_FLAG_NONE, GC_LEVEL_INTERNAL,
NULL, NULL, GC_ARG_TYPE_FILENAME, GC_BACKEND_GPG_AGENT },
{ "Monitor",
{ "enable-ssh-support", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC,
"gnupg", "enable ssh support",
GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT },
+ { "ssh-fingerprint-digest",
+ GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME, GC_LEVEL_EXPERT,
+ "gnupg", "|ALGO|use ALGO to show ssh fingerprints",
+ GC_ARG_TYPE_STRING, GC_BACKEND_GPG_AGENT },
{ "enable-putty-support", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC,
"gnupg", "enable putty support",
GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT },
+ { "enable-extended-key-format", GC_OPT_FLAG_RUNTIME, GC_LEVEL_INVISIBLE,
+ NULL, NULL,
+ GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT },
{ "Debug",
GC_OPT_FLAG_GROUP, GC_LEVEL_ADVANCED,
{ "ignore-cache-for-signing", GC_OPT_FLAG_RUNTIME,
GC_LEVEL_BASIC, "gnupg", "do not use the PIN cache when signing",
GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT },
+ { "allow-emacs-pinentry", GC_OPT_FLAG_RUNTIME,
+ GC_LEVEL_ADVANCED,
+ "gnupg", "allow passphrase to be prompted through Emacs",
+ GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT },
+ { "grab", GC_OPT_FLAG_RUNTIME, GC_LEVEL_EXPERT,
+ "gnupg", NULL,
+ GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT },
+ { "no-allow-external-cache", GC_OPT_FLAG_RUNTIME,
+ GC_LEVEL_BASIC, "gnupg", "disallow the use of an external password cache",
+ GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT },
{ "no-allow-mark-trusted", GC_OPT_FLAG_RUNTIME,
GC_LEVEL_ADVANCED, "gnupg", "disallow clients to mark keys as \"trusted\"",
GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT },
- { "no-grab", GC_OPT_FLAG_RUNTIME, GC_LEVEL_EXPERT,
- "gnupg", "do not grab keyboard and mouse",
+ { "no-allow-loopback-pinentry", GC_OPT_FLAG_RUNTIME,
+ GC_LEVEL_EXPERT, "gnupg", "disallow caller to override the pinentry",
GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT },
{ "Passphrase policy",
"gnupg", N_("Options enforcing a passphrase policy") },
{ "enforce-passphrase-constraints", GC_OPT_FLAG_RUNTIME,
GC_LEVEL_EXPERT, "gnupg",
- N_("do not allow to bypass the passphrase policy"),
+ N_("do not allow bypassing the passphrase policy"),
GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT },
{ "min-passphrase-len", GC_OPT_FLAG_RUNTIME,
GC_LEVEL_ADVANCED, "gnupg",
GC_LEVEL_EXPERT, "gnupg",
N_("do not allow the reuse of old passphrases"),
GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT },
+ { "pinentry-timeout", GC_OPT_FLAG_RUNTIME,
+ GC_LEVEL_ADVANCED, "gnupg",
+ N_("|N|set the Pinentry timeout to N seconds"),
+ GC_ARG_TYPE_UINT32, GC_BACKEND_GPG_AGENT },
GC_OPTION_NULL
};
+#endif /*BUILD_WITH_AGENT*/
+#ifndef BUILD_WITH_SCDAEMON
+#define gc_options_scdaemon NULL
+#else
/* The options of the GC_COMPONENT_SCDAEMON component. */
static gc_option_t gc_options_scdaemon[] =
{
/* The configuration file to which we write the changes. */
- { "gpgconf-scdaemon.conf", GC_OPT_FLAG_NONE, GC_LEVEL_INTERNAL,
+ { GPGCONF_NAME"-"SCDAEMON_NAME".conf",
+ GC_OPT_FLAG_NONE, GC_LEVEL_INTERNAL,
NULL, NULL, GC_ARG_TYPE_FILENAME, GC_BACKEND_SCDAEMON },
{ "Monitor",
GC_OPTION_NULL
};
+#endif /*BUILD_WITH_SCDAEMON*/
-
+#ifndef BUILD_WITH_GPG
+#define gc_options_gpg NULL
+#else
/* The options of the GC_COMPONENT_GPG component. */
static gc_option_t gc_options_gpg[] =
{
/* The configuration file to which we write the changes. */
- { "gpgconf-gpg.conf", GC_OPT_FLAG_NONE, GC_LEVEL_INTERNAL,
+ { GPGCONF_NAME"-"GPG_NAME".conf",
+ GC_OPT_FLAG_NONE, GC_LEVEL_INTERNAL,
NULL, NULL, GC_ARG_TYPE_FILENAME, GC_BACKEND_GPG },
{ "Monitor",
{ "group", GC_OPT_FLAG_LIST, GC_LEVEL_ADVANCED,
"gnupg", N_("|SPEC|set up email aliases"),
GC_ARG_TYPE_ALIAS_LIST, GC_BACKEND_GPG },
- { "options", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT,
- "gnupg", "|FILE|read options from FILE",
+ { "options", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE,
+ NULL, NULL,
GC_ARG_TYPE_FILENAME, GC_BACKEND_GPG },
+ { "compliance", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT,
+ NULL, NULL,
+ GC_ARG_TYPE_STRING, GC_BACKEND_GPG },
+ { "default-new-key-algo", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE,
+ NULL, NULL,
+ GC_ARG_TYPE_STRING, GC_BACKEND_GPG },
+ { "default_pubkey_algo",
+ (GC_OPT_FLAG_ARG_OPT|GC_OPT_FLAG_NO_CHANGE), GC_LEVEL_INVISIBLE,
+ NULL, NULL,
+ GC_ARG_TYPE_STRING, GC_BACKEND_GPG },
+ { "trust-model",
+ GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE,
+ NULL, NULL,
+ GC_ARG_TYPE_STRING, GC_BACKEND_GPG },
+
{ "Debug",
GC_OPT_FLAG_GROUP, GC_LEVEL_ADVANCED,
{ "Keyserver",
GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC,
"gnupg", N_("Configuration for Keyservers") },
- { "keyserver", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC,
- "gnupg", N_("|URL|use keyserver at URL"),
+ { "keyserver", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT,
+ "gnupg", N_("|URL|use keyserver at URL"), /* Deprecated - use dirmngr */
GC_ARG_TYPE_STRING, GC_BACKEND_GPG },
{ "allow-pka-lookup", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC,
"gnupg", N_("allow PKA lookups (DNS requests)"),
{ "auto-key-locate", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED,
"gnupg", N_("|MECHANISMS|use MECHANISMS to locate keys by mail address"),
GC_ARG_TYPE_STRING, GC_BACKEND_GPG },
+ { "auto-key-retrieve", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT,
+ NULL, NULL, GC_ARG_TYPE_NONE, GC_BACKEND_GPG },
+ { "no-auto-key-retrieve", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE,
+ NULL, NULL, GC_ARG_TYPE_NONE, GC_BACKEND_GPG },
+ { "disable-dirmngr", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT,
+ "gnupg", N_("disable all access to the dirmngr"),
+ GC_ARG_TYPE_NONE, GC_BACKEND_GPG },
+ { "max-cert-depth",
+ GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE,
+ NULL, NULL,
+ GC_ARG_TYPE_UINT32, GC_BACKEND_GPG },
+ { "completes-needed",
+ GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE,
+ NULL, NULL,
+ GC_ARG_TYPE_UINT32, GC_BACKEND_GPG },
+ { "marginals-needed",
+ GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE,
+ NULL, NULL,
+ GC_ARG_TYPE_UINT32, GC_BACKEND_GPG },
GC_OPTION_NULL
};
+#endif /*BUILD_WITH_GPG*/
-
+#ifndef BUILD_WITH_GPGSM
+#define gc_options_gpgsm NULL
+#else
/* The options of the GC_COMPONENT_GPGSM component. */
static gc_option_t gc_options_gpgsm[] =
{
/* The configuration file to which we write the changes. */
- { "gpgconf-gpgsm.conf", GC_OPT_FLAG_NONE, GC_LEVEL_INTERNAL,
+ { GPGCONF_NAME"-"GPGSM_NAME".conf",
+ GC_OPT_FLAG_NONE, GC_LEVEL_INTERNAL,
NULL, NULL, GC_ARG_TYPE_FILENAME, GC_BACKEND_GPGSM },
{ "Monitor",
{ "keyserver", GC_OPT_FLAG_LIST, GC_LEVEL_BASIC,
"gnupg", N_("|SPEC|use this keyserver to lookup keys"),
GC_ARG_TYPE_LDAP_SERVER, GC_BACKEND_GPGSM },
+ { "default_pubkey_algo",
+ (GC_OPT_FLAG_ARG_OPT|GC_OPT_FLAG_NO_CHANGE), GC_LEVEL_INVISIBLE,
+ NULL, NULL,
+ GC_ARG_TYPE_STRING, GC_BACKEND_GPGSM },
+ { "compliance", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT,
+ NULL, NULL,
+ GC_ARG_TYPE_STRING, GC_BACKEND_GPGSM },
{ "Debug",
GC_OPT_FLAG_GROUP, GC_LEVEL_ADVANCED,
{ "disable-crl-checks", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC,
"gnupg", "never consult a CRL",
GC_ARG_TYPE_NONE, GC_BACKEND_GPGSM },
+ { "enable-crl-checks", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE,
+ NULL, NULL,
+ GC_ARG_TYPE_NONE, GC_BACKEND_GPGSM },
{ "disable-trusted-cert-crl-check", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT,
"gnupg", N_("do not check CRLs for root certificates"),
GC_ARG_TYPE_NONE, GC_BACKEND_GPGSM },
GC_OPTION_NULL
};
+#endif /*BUILD_WITH_GPGSM*/
+#ifndef BUILD_WITH_DIRMNGR
+#define gc_options_dirmngr NULL
+#else
/* The options of the GC_COMPONENT_DIRMNGR component. */
static gc_option_t gc_options_dirmngr[] =
{
/* The configuration file to which we write the changes. */
- { "gpgconf-dirmngr.conf", GC_OPT_FLAG_NONE, GC_LEVEL_INTERNAL,
+ { GPGCONF_NAME"-"DIRMNGR_NAME".conf",
+ GC_OPT_FLAG_NONE, GC_LEVEL_INTERNAL,
NULL, NULL, GC_ARG_TYPE_FILENAME, GC_BACKEND_DIRMNGR },
{ "Monitor",
{ "options", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT,
"dirmngr", "|FILE|read options from FILE",
GC_ARG_TYPE_FILENAME, GC_BACKEND_DIRMNGR },
+ { "resolver-timeout", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE,
+ NULL, NULL,
+ GC_ARG_TYPE_INT32, GC_BACKEND_DIRMNGR },
+ { "nameserver", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE,
+ NULL, NULL,
+ GC_ARG_TYPE_STRING, GC_BACKEND_DIRMNGR },
{ "Debug",
GC_OPT_FLAG_GROUP, GC_LEVEL_ADVANCED,
{ "force", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC,
"dirmngr", "force loading of outdated CRLs",
GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR },
+ { "allow-version-check", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC,
+ "dirmngr", "allow online software version check",
+ GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR },
+
+ { "Tor",
+ GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC,
+ "gnupg", N_("Options controlling the use of Tor") },
+ { "use-tor", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC,
+ "dirmngr", "route all network traffic via TOR",
+ GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR },
+
+ { "Keyserver",
+ GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC,
+ "gnupg", N_("Configuration for Keyservers") },
+ { "keyserver", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC,
+ "gnupg", N_("|URL|use keyserver at URL"),
+ GC_ARG_TYPE_STRING, GC_BACKEND_DIRMNGR },
{ "HTTP",
GC_OPT_FLAG_GROUP, GC_LEVEL_ADVANCED,
GC_OPTION_NULL
};
+#endif /*BUILD_WITH_DIRMNGR*/
+
+
+/* The options of the GC_COMPONENT_PINENTRY component. */
+static gc_option_t gc_options_pinentry[] =
+ {
+ /* A dummy option to allow gc_component_list_components to find the
+ pinentry backend. Needs to be a conf file. */
+ { GPGCONF_NAME"-pinentry.conf",
+ GC_OPT_FLAG_NONE, GC_LEVEL_INTERNAL,
+ NULL, NULL, GC_ARG_TYPE_FILENAME, GC_BACKEND_PINENTRY },
+
+ GC_OPTION_NULL
+ };
+
\f
/* Component system. Each component is a set of options that can be
/* The LDAP Directory Manager for CRLs. */
GC_COMPONENT_DIRMNGR,
+ /* The external Pinentry. */
+ GC_COMPONENT_PINENTRY,
+
/* The number of components. */
GC_COMPONENT_NR
} gc_component_t;
/* The information associated with each component. */
-static struct
+static const struct
{
/* The name of this component. Must not contain a colon (':')
character. */
gc_option_t *options;
} gc_component[] =
{
- { "gpg", NULL, "GPG for OpenPGP", gc_options_gpg },
- { "gpg-agent", NULL, "GPG Agent", gc_options_gpg_agent },
- { "scdaemon", NULL, "Smartcard Daemon", gc_options_scdaemon },
- { "gpgsm", NULL, "GPG for S/MIME", gc_options_gpgsm },
- { "dirmngr", NULL, "Directory Manager", gc_options_dirmngr }
+ { "gpg", "gnupg", N_("OpenPGP"), gc_options_gpg },
+ { "gpg-agent","gnupg", N_("Private Keys"), gc_options_gpg_agent },
+ { "scdaemon", "gnupg", N_("Smartcards"), gc_options_scdaemon },
+ { "gpgsm", "gnupg", N_("S/MIME"), gc_options_gpgsm },
+ { "dirmngr", "gnupg", N_("Network"), gc_options_dirmngr },
+ { "pinentry", "gnupg", N_("Passphrase Entry"), gc_options_pinentry }
};
\f
-/* Engine specific support. */
+
+/* Initialization and finalization. */
+
static void
-gpg_agent_runtime_change (void)
+gc_option_free (gc_option_t *o)
{
-#ifndef HAVE_W32_SYSTEM
- char *agent = getenv ("GPG_AGENT_INFO");
- char *pid_str;
- unsigned long pid_long;
- char *tail;
- pid_t pid;
-
- if (!agent)
+ if (o == NULL || o->name == NULL)
return;
- pid_str = strchr (agent, ':');
- if (!pid_str)
- return;
+ xfree (o->value);
+ gc_option_free (o + 1);
+}
- pid_str++;
- errno = 0;
- pid_long = strtoul (pid_str, &tail, 0);
- if (errno || (*tail != ':' && *tail != '\0'))
- return;
+static void
+gc_components_free (void)
+{
+ int i;
+ for (i = 0; i < DIM (gc_component); i++)
+ gc_option_free (gc_component[i].options);
+}
- pid = (pid_t) pid_long;
+void
+gc_components_init (void)
+{
+ atexit (gc_components_free);
+}
- /* Check for overflow. */
- if (pid_long != (unsigned long) pid)
- return;
+\f
- /* Ignore any errors here. */
- kill (pid, SIGHUP);
-#else
- gpg_error_t err;
+/* Engine specific support. */
+static void
+gpg_agent_runtime_change (int killflag)
+{
+ gpg_error_t err = 0;
const char *pgmname;
- const char *argv[2];
- pid_t pid;
+ const char *argv[5];
+ pid_t pid = (pid_t)(-1);
+ char *abs_homedir = NULL;
+ int i = 0;
pgmname = gnupg_module_name (GNUPG_MODULE_NAME_CONNECT_AGENT);
- argv[0] = "reloadagent";
- argv[1] = NULL;
+ if (!gnupg_default_homedir_p ())
+ {
+ abs_homedir = make_absfilename_try (gnupg_homedir (), NULL);
+ if (!abs_homedir)
+ err = gpg_error_from_syserror ();
- err = gnupg_spawn_process_fd (pgmname, argv, -1, -1, -1, &pid);
+ argv[i++] = "--homedir";
+ argv[i++] = abs_homedir;
+ }
+ argv[i++] = "--no-autostart";
+ argv[i++] = killflag? "KILLAGENT" : "RELOADAGENT";
+ argv[i++] = NULL;
+
+ if (!err)
+ err = gnupg_spawn_process_fd (pgmname, argv, -1, -1, -1, &pid);
if (!err)
- err = gnupg_wait_process (pgmname, pid, NULL);
+ err = gnupg_wait_process (pgmname, pid, 1, NULL);
if (err)
- gc_error (0, 0, "error running `%s%s': %s",
- pgmname, " reloadagent", gpg_strerror (err));
-#endif /*!HAVE_W32_SYSTEM*/
+ gc_error (0, 0, "error running '%s %s': %s",
+ pgmname, argv[1], gpg_strerror (err));
+ gnupg_release_process (pid);
+ xfree (abs_homedir);
}
static void
-scdaemon_runtime_change (void)
+scdaemon_runtime_change (int killflag)
{
- gpg_error_t err;
+ gpg_error_t err = 0;
const char *pgmname;
- const char *argv[6];
- pid_t pid;
+ const char *argv[9];
+ pid_t pid = (pid_t)(-1);
+ char *abs_homedir = NULL;
+ int i = 0;
+
+ (void)killflag; /* For scdaemon kill and reload are synonyms. */
/* We use "GETINFO app_running" to see whether the agent is already
running and kill it only in this case. This avoids an explicit
obviously a race condition but that should not harm too much. */
pgmname = gnupg_module_name (GNUPG_MODULE_NAME_CONNECT_AGENT);
- argv[0] = "-s";
- argv[1] = "GETINFO scd_running";
- argv[2] = "/if ${! $?}";
- argv[3] = "scd killscd";
- argv[4] = "/end";
- argv[5] = NULL;
+ if (!gnupg_default_homedir_p ())
+ {
+ abs_homedir = make_absfilename_try (gnupg_homedir (), NULL);
+ if (!abs_homedir)
+ err = gpg_error_from_syserror ();
+
+ argv[i++] = "--homedir";
+ argv[i++] = abs_homedir;
+ }
+ argv[i++] = "-s";
+ argv[i++] = "--no-autostart";
+ argv[i++] = "GETINFO scd_running";
+ argv[i++] = "/if ${! $?}";
+ argv[i++] = "scd killscd";
+ argv[i++] = "/end";
+ argv[i++] = NULL;
+
+ if (!err)
+ err = gnupg_spawn_process_fd (pgmname, argv, -1, -1, -1, &pid);
+ if (!err)
+ err = gnupg_wait_process (pgmname, pid, 1, NULL);
+ if (err)
+ gc_error (0, 0, "error running '%s %s': %s",
+ pgmname, argv[4], gpg_strerror (err));
+ gnupg_release_process (pid);
+ xfree (abs_homedir);
+}
+
+
+static void
+dirmngr_runtime_change (int killflag)
+{
+ gpg_error_t err = 0;
+ const char *pgmname;
+ const char *argv[6];
+ pid_t pid = (pid_t)(-1);
+ char *abs_homedir = NULL;
+
+ pgmname = gnupg_module_name (GNUPG_MODULE_NAME_CONNECT_AGENT);
+ argv[0] = "--no-autostart";
+ argv[1] = "--dirmngr";
+ argv[2] = killflag? "KILLDIRMNGR" : "RELOADDIRMNGR";
+ if (gnupg_default_homedir_p ())
+ argv[3] = NULL;
+ else
+ {
+ abs_homedir = make_absfilename_try (gnupg_homedir (), NULL);
+ if (!abs_homedir)
+ err = gpg_error_from_syserror ();
+
+ argv[3] = "--homedir";
+ argv[4] = abs_homedir;
+ argv[5] = NULL;
+ }
+
+ if (!err)
+ err = gnupg_spawn_process_fd (pgmname, argv, -1, -1, -1, &pid);
+ if (!err)
+ err = gnupg_wait_process (pgmname, pid, 1, NULL);
+ if (err)
+ gc_error (0, 0, "error running '%s %s': %s",
+ pgmname, argv[2], gpg_strerror (err));
+ gnupg_release_process (pid);
+ xfree (abs_homedir);
+}
+
+
+/* Launch the gpg-agent or the dirmngr if not already running. */
+gpg_error_t
+gc_component_launch (int component)
+{
+ gpg_error_t err;
+ const char *pgmname;
+ const char *argv[3];
+ int i;
+ pid_t pid;
+
+ if (component < 0)
+ {
+ err = gc_component_launch (GC_COMPONENT_GPG_AGENT);
+ if (!err)
+ err = gc_component_launch (GC_COMPONENT_DIRMNGR);
+ return err;
+ }
+
+ if (!(component == GC_COMPONENT_GPG_AGENT
+ || component == GC_COMPONENT_DIRMNGR))
+ {
+ es_fputs (_("Component not suitable for launching"), es_stderr);
+ es_putc ('\n', es_stderr);
+ gpgconf_failure (0);
+ }
+
+ pgmname = gnupg_module_name (GNUPG_MODULE_NAME_CONNECT_AGENT);
+ i = 0;
+ if (component == GC_COMPONENT_DIRMNGR)
+ argv[i++] = "--dirmngr";
+ argv[i++] = "NOP";
+ argv[i] = NULL;
err = gnupg_spawn_process_fd (pgmname, argv, -1, -1, -1, &pid);
if (!err)
- err = gnupg_wait_process (pgmname, pid, NULL);
+ err = gnupg_wait_process (pgmname, pid, 1, NULL);
if (err)
- gc_error (0, 0, "error running `%s%s': %s",
- pgmname, " scd killscd", gpg_strerror (err));
+ gc_error (0, 0, "error running '%s%s%s': %s",
+ pgmname,
+ component == GC_COMPONENT_DIRMNGR? " --dirmngr":"",
+ " NOP",
+ gpg_strerror (err));
+ gnupg_release_process (pid);
+ return err;
+}
+
+
+/* Unconditionally restart COMPONENT. */
+void
+gc_component_kill (int component)
+{
+ int runtime[GC_BACKEND_NR];
+ gc_option_t *option;
+ gc_backend_t backend;
+
+ /* Set a flag for the backends to be reloaded. */
+ for (backend = 0; backend < GC_BACKEND_NR; backend++)
+ runtime[backend] = 0;
+
+ if (component < 0)
+ {
+ for (component = 0; component < GC_COMPONENT_NR; component++)
+ {
+ option = gc_component[component].options;
+ for (; option && option->name; option++)
+ runtime[option->backend] = 1;
+ }
+ }
+ else
+ {
+ assert (component < GC_COMPONENT_NR);
+ option = gc_component[component].options;
+ for (; option && option->name; option++)
+ runtime[option->backend] = 1;
+ }
+
+ /* Do the restart for the selected backends. */
+ for (backend = 0; backend < GC_BACKEND_NR; backend++)
+ {
+ if (runtime[backend] && gc_backend[backend].runtime_change)
+ (*gc_backend[backend].runtime_change) (1);
+ }
}
for (backend = 0; backend < GC_BACKEND_NR; backend++)
runtime[backend] = 0;
- if (component == -1)
+ if (component < 0)
{
for (component = 0; component < GC_COMPONENT_NR; component++)
{
for (backend = 0; backend < GC_BACKEND_NR; backend++)
{
if (runtime[backend] && gc_backend[backend].runtime_change)
- (*gc_backend[backend].runtime_change) ();
+ (*gc_backend[backend].runtime_change) (0);
}
}
\f
/* More or less Robust version of dgettext. It has the side effect of
switching the codeset to utf-8 because this is what we want to
- output. In theory it is posible to keep the orginal code set and
+ output. In theory it is possible to keep the original code set and
switch back for regular disgnostic output (redefine "_(" for that)
but given the natur of this tool, being something invoked from
other pograms, it does not make much sense. */
if (!switched_codeset)
{
switched_codeset = 1;
- gettext_select_utf8 (1);
+ gettext_use_utf8 (1);
}
if (!strcmp (domain, "gnupg"))
text = (char*)gettext (msgid);
return text ? text : msgid;
}
+ else
+ return msgid;
#elif defined(ENABLE_NLS)
if (domain)
{
switched_codeset = 1;
bind_textdomain_codeset (PACKAGE_GT, "utf-8");
- bindtextdomain ("dirmngr", LOCALEDIR);
- bind_textdomain_codeset ("dirmngr", "utf-8");
+ bindtextdomain (DIRMNGR_NAME, LOCALEDIR);
+ bind_textdomain_codeset (DIRMNGR_NAME, "utf-8");
}
return text ? text : msgid;
}
else
-#endif
return msgid;
+#else
+ (void)domain;
+ return msgid;
+#endif
}
*(dst++) = '2';
*(dst++) = 'c';
}
+ else if (*src == '\n')
+ {
+ /* The newline is problematic in a line-based format. */
+ *(dst++) = '%';
+ *(dst++) = '0';
+ *(dst++) = 'a';
+ }
else
*(dst++) = *(src);
src++;
\f
/* List all components that are available. */
void
-gc_component_list_components (FILE *out)
+gc_component_list_components (estream_t out)
{
gc_component_t component;
gc_option_t *option;
desc = gc_component[component].desc;
desc = my_dgettext (gc_component[component].desc_domain, desc);
- fprintf (out, "%s:%s:",
- gc_component[component].name, gc_percent_escape (desc));
- fprintf (out, "%s\n", gc_percent_escape (pgmname));
+ es_fprintf (out, "%s:%s:",
+ gc_component[component].name, gc_percent_escape (desc));
+ es_fprintf (out, "%s\n", gc_percent_escape (pgmname));
}
}
}
}
-/* Collect all error lines from file descriptor FD. Only lines
- prefixed with TAG are considered. Close that file descriptor
- then. Returns a list of error line items (which may be empty).
- There is no error return. */
+/* Collect all error lines from stream FP. Only lines prefixed with
+ TAG are considered. Returns a list of error line items (which may
+ be empty). There is no error return. */
static error_line_t
-collect_error_output (int fd, const char *tag)
+collect_error_output (estream_t fp, const char *tag)
{
- FILE *fp;
char buffer[1024];
char *p, *p2, *p3;
int c, cont_line;
error_line_t eitem, errlines, *errlines_tail;
size_t taglen = strlen (tag);
- fp = fdopen (fd, "r");
- if (!fp)
- gc_error (1, errno, "can't fdopen pipe for reading");
-
errlines = NULL;
errlines_tail = &errlines;
pos = 0;
cont_line = 0;
- while ((c=getc (fp)) != EOF)
+ while ((c=es_getc (fp)) != EOF)
{
buffer[pos++] = c;
if (pos >= sizeof buffer - 5 || c == '\n')
p = buffer + taglen + 1;
while (*p == ' ' || *p == '\t')
p++;
+ trim_trailing_spaces (p); /* Get rid of extra CRs. */
if (!*p)
; /* Empty lines are ignored. */
else if ( (p2 = strchr (p, ':')) && (p3 = strchr (p2+1, ':'))
}
/* We ignore error lines not terminated by a LF. */
-
- fclose (fp);
return errlines;
}
/* 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)
+gc_component_check_options (int component, estream_t out, const char *conf_file)
{
gpg_error_t err;
unsigned int result;
int i;
pid_t pid;
int exitcode;
- int filedes[2];
+ estream_t errfp;
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 (backend = 0; backend < GC_BACKEND_NR; backend++)
backend_seen[backend] = 0;
argv[i++] = "--options";
argv[i++] = conf_file;
}
- argv[i++] = "--gpgconf-test";
+ if (component == GC_COMPONENT_PINENTRY)
+ argv[i++] = "--version";
+ else
+ 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. */
- }
+ err = gnupg_spawn_process (pgmname, argv, NULL, NULL, 0,
+ NULL, NULL, &errfp, &pid);
+ if (err)
+ result |= 1; /* Program could not be run. */
else
{
- close (filedes[1]);
- errlines = collect_error_output (filedes[0],
+ errlines = collect_error_output (errfp,
gc_component[component].name);
- if (gnupg_wait_process (pgmname, pid, &exitcode))
+ if (gnupg_wait_process (pgmname, pid, 1, &exitcode))
{
if (exitcode == -1)
result |= 1; /* Program could not be run or it
terminated abnormally. */
result |= 2; /* Program returned an error. */
}
+ gnupg_release_process (pid);
+ es_fclose (errfp);
}
/* If the program could not be run, we can't tell whether
desc = gc_component[component].desc;
desc = my_dgettext (gc_component[component].desc_domain, desc);
- fprintf (out, "%s:%s:",
- gc_component[component].name, gc_percent_escape (desc));
- fputs (gc_percent_escape (pgmname), out);
- fprintf (out, ":%d:%d:", !(result & 1), !(result & 2));
+ es_fprintf (out, "%s:%s:",
+ gc_component[component].name, gc_percent_escape (desc));
+ es_fputs (gc_percent_escape (pgmname), out);
+ es_fprintf (out, ":%d:%d:", !(result & 1), !(result & 2));
for (errptr = errlines; errptr; errptr = errptr->next)
{
if (errptr != errlines)
- fputs ("\n:::::", out); /* Continuation line. */
+ es_fputs ("\n:::::", out); /* Continuation line. */
if (errptr->fname)
- fputs (gc_percent_escape (errptr->fname), out);
- putc (':', out);
+ es_fputs (gc_percent_escape (errptr->fname), out);
+ es_putc (':', out);
if (errptr->fname)
- fprintf (out, "%u", errptr->lineno);
- putc (':', out);
- fputs (gc_percent_escape (errptr->errtext), out);
- putc (':', out);
+ es_fprintf (out, "%u", errptr->lineno);
+ es_putc (':', out);
+ es_fputs (gc_percent_escape (errptr->errtext), out);
+ es_putc (':', out);
}
- putc ('\n', out);
+ es_putc ('\n', out);
}
while (errlines)
}
+
/* Check all components that are available. */
void
-gc_check_programs (FILE *out)
+gc_check_programs (estream_t out)
{
gc_component_t component;
\f
/* List the option OPTION. */
static void
-list_one_option (const gc_option_t *option, FILE *out)
+list_one_option (const gc_option_t *option, estream_t out)
{
const char *desc = NULL;
char *arg_name = NULL;
FIELDS. */
/* The name field. */
- fprintf (out, "%s", option->name);
+ es_fprintf (out, "%s", option->name);
/* The flags field. */
- fprintf (out, ":%lu", option->flags);
+ es_fprintf (out, ":%lu", option->flags);
if (opt.verbose)
{
- putc (' ', out);
+ es_putc (' ', out);
if (!option->flags)
- fprintf (out, "none");
+ es_fprintf (out, "none");
else
{
unsigned long flags = option->flags;
if (first)
first = 0;
else
- putc (',', out);
- fprintf (out, "%s", gc_flag[flag].name);
+ es_putc (',', out);
+ es_fprintf (out, "%s", gc_flag[flag].name);
}
flags >>= 1;
flag++;
}
/* The level field. */
- fprintf (out, ":%u", option->level);
+ es_fprintf (out, ":%u", option->level);
if (opt.verbose)
- fprintf (out, " %s", gc_level[option->level].name);
+ es_fprintf (out, " %s", gc_level[option->level].name);
/* The description field. */
- fprintf (out, ":%s", desc ? gc_percent_escape (desc) : "");
+ es_fprintf (out, ":%s", desc ? gc_percent_escape (desc) : "");
/* The type field. */
- fprintf (out, ":%u", option->arg_type);
+ es_fprintf (out, ":%u", option->arg_type);
if (opt.verbose)
- fprintf (out, " %s", gc_arg_type[option->arg_type].name);
+ es_fprintf (out, " %s", gc_arg_type[option->arg_type].name);
/* The alternate type field. */
- fprintf (out, ":%u", gc_arg_type[option->arg_type].fallback);
+ es_fprintf (out, ":%u", gc_arg_type[option->arg_type].fallback);
if (opt.verbose)
- fprintf (out, " %s",
- gc_arg_type[gc_arg_type[option->arg_type].fallback].name);
+ es_fprintf (out, " %s",
+ gc_arg_type[gc_arg_type[option->arg_type].fallback].name);
/* The argument name field. */
- fprintf (out, ":%s", arg_name ? gc_percent_escape (arg_name) : "");
- if (arg_name)
- xfree (arg_name);
+ es_fprintf (out, ":%s", arg_name ? gc_percent_escape (arg_name) : "");
+ xfree (arg_name);
/* The default value field. */
- fprintf (out, ":%s", option->default_value ? option->default_value : "");
+ es_fprintf (out, ":%s", option->default_value ? option->default_value : "");
/* The default argument field. */
- fprintf (out, ":%s", option->default_arg ? option->default_arg : "");
+ es_fprintf (out, ":%s", option->default_arg ? option->default_arg : "");
/* The value field. */
if (gc_arg_type[option->arg_type].fallback == GC_ARG_TYPE_NONE
&& option->value)
/* The special format "1,1,1,1,...,1" is converted to a number
here. */
- fprintf (out, ":%u", (unsigned int)((strlen (option->value) + 1) / 2));
+ es_fprintf (out, ":%u", (unsigned int)((strlen (option->value) + 1) / 2));
else
- fprintf (out, ":%s", option->value ? option->value : "");
+ es_fprintf (out, ":%s", option->value ? option->value : "");
/* ADD NEW FIELDS HERE. */
- putc ('\n', out);
+ es_putc ('\n', out);
}
/* List all options of the component COMPONENT. */
void
-gc_component_list_options (int component, FILE *out)
+gc_component_list_options (int component, estream_t out)
{
const gc_option_t *option = gc_component[component].options;
else
filename = "";
-#ifdef HAVE_DOSISH_SYSTEM
+#if HAVE_W32CE_SYSTEM
+ if (!(filename[0] == '/' || filename[0] == '\\'))
+#elif defined(HAVE_DOSISH_SYSTEM)
if (!(filename[0]
&& filename[1] == ':'
- && (filename[2] == '/' || filename[2] == '\\')))
+ && (filename[2] == '/' || filename[2] == '\\')) /* x:\ or x:/ */
+ && !((filename[0] == '\\' && filename[1] == '\\')
+ || (filename[0] == '/' && filename[1] == '/'))) /* \\server */
#else
if (filename[0] != '/')
#endif
\f
/* Retrieve the options for the component COMPONENT from backend
- BACKEND, which we already know is a program-type backend. */
+ * BACKEND, which we already know is a program-type backend. With
+ * ONLY_INSTALLED set components which are not installed are silently
+ * ignored. */
static void
-retrieve_options_from_program (gc_component_t component, gc_backend_t backend)
+retrieve_options_from_program (gc_component_t component, gc_backend_t backend,
+ int only_installed)
{
gpg_error_t err;
- int filedes[2];
const char *pgmname;
const char *argv[2];
+ estream_t outfp;
int exitcode;
pid_t pid;
char *line = NULL;
size_t line_len = 0;
ssize_t length;
- FILE *config;
+ estream_t config;
char *config_filename;
- err = gnupg_create_inbound_pipe (filedes);
- if (err)
- gc_error (1, 0, _("error creating a pipe: %s\n"), gpg_strerror (err));
-
pgmname = (gc_backend[backend].module_name
? gnupg_module_name (gc_backend[backend].module_name)
: gc_backend[backend].program );
argv[0] = "--gpgconf-list";
argv[1] = NULL;
- err = gnupg_spawn_process_fd (pgmname, argv, -1, filedes[1], -1, &pid);
+ if (only_installed && access (pgmname, X_OK))
+ {
+ return; /* The component is not installed. */
+ }
+
+ err = gnupg_spawn_process (pgmname, argv, NULL, NULL, 0,
+ NULL, &outfp, NULL, &pid);
if (err)
{
- close (filedes[0]);
- close (filedes[1]);
- gc_error (1, 0, "could not gather active options from `%s': %s",
+ gc_error (1, 0, "could not gather active options from '%s': %s",
pgmname, gpg_strerror (err));
}
- close (filedes[1]);
- config = fdopen (filedes[0], "r");
- if (!config)
- gc_error (1, errno, "can't fdopen pipe for reading");
- while ((length = read_line (config, &line, &line_len, NULL)) > 0)
+ while ((length = es_read_line (outfp, &line, &line_len, NULL)) > 0)
{
gc_option_t *option;
char *linep;
if (end)
*(end++) = '\0';
- errno = 0;
+ gpg_err_set_errno (0);
flags = strtoul (linep, &tail, 0);
if (errno)
gc_error (1, errno, "malformed flags in option %s from %s",
option->default_value = xstrdup (default_value);
}
}
- if (length < 0 || ferror (config))
- gc_error (1, errno, "error reading from %s",pgmname);
- if (fclose (config))
+ if (length < 0 || es_ferror (outfp))
+ gc_error (1, errno, "error reading from %s", pgmname);
+ if (es_fclose (outfp))
gc_error (1, errno, "error closing %s", pgmname);
- err = gnupg_wait_process (pgmname, pid, &exitcode);
+ err = gnupg_wait_process (pgmname, pid, 1, &exitcode);
if (err)
gc_error (1, 0, "running %s failed (exitcode=%d): %s",
pgmname, exitcode, gpg_strerror (err));
+ gnupg_release_process (pid);
/* At this point, we can parse the configuration file. */
config_filename = get_config_filename (component, backend);
- config = fopen (config_filename, "r");
+ config = es_fopen (config_filename, "r");
if (!config)
- gc_error (0, errno, "warning: can not open config file %s",
- config_filename);
+ {
+ if (errno != ENOENT)
+ gc_error (0, errno, "warning: can not open config file %s",
+ config_filename);
+ }
else
{
- while ((length = read_line (config, &line, &line_len, NULL)) > 0)
+ while ((length = es_read_line (config, &line, &line_len, NULL)) > 0)
{
char *name;
char *value;
if (!(option->flags & GC_OPT_FLAG_LIST))
{
if (option->value)
- free (option->value);
+ xfree (option->value);
option->value = opt_value;
}
else
option->value = opt_value;
else
{
- char *opt_val = opt_value;
-
- option->value = xasprintf ("%s,%s", option->value,
- opt_val);
+ char *old = option->value;
+ option->value = xasprintf ("%s,%s", old, opt_value);
+ xfree (old);
xfree (opt_value);
}
}
}
}
- if (length < 0 || ferror (config))
+ if (length < 0 || es_ferror (config))
gc_error (1, errno, "error reading from %s", config_filename);
- if (fclose (config))
+ if (es_fclose (config))
gc_error (1, errno, "error closing %s", config_filename);
}
gc_option_t *list_option;
gc_option_t *config_option;
char *list_filename;
- FILE *list_file;
+ gpgrt_stream_t list_file;
char *line = NULL;
size_t line_len = 0;
ssize_t length;
assert (!list_option->active);
list_filename = get_config_filename (component, backend);
- list_file = fopen (list_filename, "r");
+ list_file = gpgrt_fopen (list_filename, "r");
if (!list_file)
gc_error (0, errno, "warning: can not open list file %s", list_filename);
else
{
- while ((length = read_line (list_file, &line, &line_len, NULL)) > 0)
+ while ((length = gpgrt_read_line (list_file, &line, &line_len, NULL)) > 0)
{
char *start;
char *end;
else
list = xasprintf ("\"%s", gc_percent_escape (start));
}
- if (length < 0 || ferror (list_file))
+ if (length < 0 || gpgrt_ferror (list_file))
gc_error (1, errno, "can not read list file %s", list_filename);
}
if (config_option->flags & GC_OPT_FLAG_NO_CHANGE)
list_option->flags |= GC_OPT_FLAG_NO_CHANGE;
- if (list_file && fclose (list_file))
+ if (list_file && gpgrt_fclose (list_file))
gc_error (1, errno, "error closing %s", list_filename);
xfree (line);
}
/* Retrieve the currently active options and their defaults from all
involved backends for this component. Using -1 for component will
- retrieve all options from all components. */
+ retrieve all options from all installed components. */
void
gc_component_retrieve_options (int component)
{
do
{
+ if (component == GC_COMPONENT_PINENTRY)
+ continue; /* Skip this dummy component. */
+
option = gc_component[component].options;
while (option && option->name)
assert (backend != GC_BACKEND_ANY);
if (gc_backend[backend].program)
- retrieve_options_from_program (component, backend);
+ retrieve_options_from_program (component, backend,
+ process_all);
else
retrieve_options_from_file (component, backend);
}
\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
- type GC_ARG_TYPE_NONE. */
+ * NEW_VALUE_NR the value of the number in NEW_VALUE if OPTION is of
+ * type GC_ARG_TYPE_NONE. If VERBATIM is set the profile parsing mode
+ * is used. */
static void
option_check_validity (gc_option_t *option, unsigned long flags,
- char *new_value, unsigned long *new_value_nr)
+ char *new_value, unsigned long *new_value_nr,
+ int verbatim)
{
char *arg;
{
char *tail;
- errno = 0;
+ gpg_err_set_errno (0);
*new_value_nr = strtoul (new_value, &tail, 0);
if (errno)
arg = new_value;
do
{
- if (*arg == '\0' || *arg == ',')
+ if (*arg == '\0' || (*arg == ',' && !verbatim))
{
if (!(option->flags & GC_OPT_FLAG_ARG_OPT))
gc_error (1, 0, "argument required for option %s", option->name);
- if (*arg == ',' && !(option->flags & GC_OPT_FLAG_LIST))
+ if (*arg == ',' && !verbatim && !(option->flags & GC_OPT_FLAG_LIST))
gc_error (1, 0, "list found for non-list option %s", option->name);
}
else if (gc_arg_type[option->arg_type].fallback == GC_ARG_TYPE_STRING)
{
- if (*arg != '"')
+ if (*arg != '"' && !verbatim)
gc_error (1, 0, "string argument for option %s must begin "
"with a quote (\") character", option->name);
we do not quote arguments in configuration files, and
thus no argument is indistinguishable from the empty
string. */
- if (arg[1] == '\0' || arg[1] == ',')
+ if (arg[1] == '\0' || (arg[1] == ',' && !verbatim))
gc_error (1, 0, "empty string argument for option %s is "
"currently not allowed. Please report this!",
option->name);
}
else if (gc_arg_type[option->arg_type].fallback == GC_ARG_TYPE_INT32)
{
- errno = 0;
- (void) strtol (arg, &arg, 0);
+ long res;
+
+ gpg_err_set_errno (0);
+ res = strtol (arg, &arg, 0);
+ (void) res;
if (errno)
gc_error (1, errno, "invalid argument for option %s",
option->name);
- if (*arg != '\0' && *arg != ',')
+ if (*arg != '\0' && (*arg != ',' || verbatim))
gc_error (1, 0, "garbage after argument for option %s",
option->name);
}
- else if (gc_arg_type[option->arg_type].fallback == GC_ARG_TYPE_INT32)
+ else if (gc_arg_type[option->arg_type].fallback == GC_ARG_TYPE_UINT32)
{
- errno = 0;
- (void) strtoul (arg, &arg, 0);
+ unsigned long res;
+
+ gpg_err_set_errno (0);
+ res = strtoul (arg, &arg, 0);
+ (void) res;
if (errno)
gc_error (1, errno, "invalid argument for option %s",
option->name);
- if (*arg != '\0' && *arg != ',')
+ if (*arg != '\0' && (*arg != ',' || verbatim))
gc_error (1, 0, "garbage after argument for option %s",
option->name);
}
- arg = strchr (arg, ',');
+ arg = verbatim? strchr (arg, ',') : NULL;
if (arg)
arg++;
}
#define BUF_LEN 4096
char buffer[BUF_LEN];
int len;
- FILE *src;
- FILE *dst;
+ gpgrt_stream_t src;
+ gpgrt_stream_t dst;
- src = fopen (src_name, "r");
+ src = gpgrt_fopen (src_name, "r");
if (src == NULL)
return -1;
- dst = fopen (dst_name, "w");
+ dst = gpgrt_fopen (dst_name, "w");
if (dst == NULL)
{
int saved_err = errno;
- fclose (src);
- errno = saved_err;
+ gpgrt_fclose (src);
+ gpg_err_set_errno (saved_err);
return -1;
}
{
int written;
- len = fread (buffer, 1, BUF_LEN, src);
+ len = gpgrt_fread (buffer, 1, BUF_LEN, src);
if (len == 0)
break;
- written = fwrite (buffer, 1, len, dst);
+ written = gpgrt_fwrite (buffer, 1, len, dst);
if (written != len)
break;
}
- while (!feof (src) && !ferror (src) && !ferror (dst));
+ while (! gpgrt_feof (src) && ! gpgrt_ferror (src) && ! gpgrt_ferror (dst));
- if (ferror (src) || ferror (dst) || !feof (src))
+ if (gpgrt_ferror (src) || gpgrt_ferror (dst) || ! gpgrt_feof (src))
{
int saved_errno = errno;
- fclose (src);
- fclose (dst);
+ gpgrt_fclose (src);
+ gpgrt_fclose (dst);
unlink (dst_name);
- errno = saved_errno;
+ gpg_err_set_errno (saved_errno);
return -1;
}
- if (fclose (dst))
+ if (gpgrt_fclose (dst))
gc_error (1, errno, "error closing %s", dst_name);
- if (fclose (src))
+ if (gpgrt_fclose (src))
gc_error (1, errno, "error closing %s", src_name);
return 0;
/* Create and verify the new configuration file for the specified
- backend and component. Returns 0 on success and -1 on error. */
+ * backend and component. Returns 0 on success and -1 on error. This
+ * function may store pointers to malloced strings in SRC_FILENAMEP,
+ * DEST_FILENAMEP, and ORIG_FILENAMEP. Those must be freed by the
+ * caller. The strings refer to three versions of the configuration
+ * file:
+ *
+ * SRC_FILENAME: The updated configuration is written to this file.
+ * DEST_FILENAME: Name of the configuration file read by the
+ * component.
+ * ORIG_FILENAME: A backup of the previous configuration file.
+ *
+ * To apply the configuration change, rename SRC_FILENAME to
+ * DEST_FILENAME. To revert to the previous configuration, rename
+ * ORIG_FILENAME to DEST_FILENAME. */
static int
change_options_file (gc_component_t component, gc_backend_t backend,
char **src_filenamep, char **dest_filenamep,
char **orig_filenamep)
{
- static const char marker[] = "###+++--- GPGConf ---+++###";
+ static const char marker[] = "###+++--- " GPGCONF_DISP_NAME " ---+++###";
/* True if we are within the marker in the config file. */
int in_marker = 0;
gc_option_t *option;
ssize_t length;
int res;
int fd;
- FILE *src_file = NULL;
- FILE *dest_file = NULL;
+ gpgrt_stream_t src_file = NULL;
+ gpgrt_stream_t dest_file = NULL;
char *src_filename;
char *dest_filename;
char *orig_filename;
/* Note that get_config_filename() calls percent_deescape(), so we
call this before processing the arguments. */
dest_filename = xstrdup (get_config_filename (component, backend));
- src_filename = xasprintf ("%s.gpgconf.%i.new", dest_filename, getpid ());
- orig_filename = xasprintf ("%s.gpgconf.%i.bak", dest_filename, getpid ());
+ src_filename = xasprintf ("%s.%s.%i.new",
+ dest_filename, GPGCONF_NAME, (int)getpid ());
+ orig_filename = xasprintf ("%s.%s.%i.bak",
+ dest_filename, GPGCONF_NAME, (int)getpid ());
arg = option->new_value;
if (arg && arg[0] == '\0')
res = link (dest_filename, orig_filename);
#endif
if (res < 0 && errno != ENOENT)
- return -1;
+ {
+ xfree (dest_filename);
+ xfree (src_filename);
+ xfree (orig_filename);
+ return -1;
+ }
if (res < 0)
{
xfree (orig_filename);
fd = open (src_filename, O_CREAT | O_EXCL | O_WRONLY, 0644);
if (fd < 0)
return -1;
- src_file = fdopen (fd, "w");
+ src_file = gpgrt_fdopen (fd, "w");
res = errno;
if (!src_file)
{
- errno = res;
+ gpg_err_set_errno (res);
return -1;
}
process. */
if (orig_filename)
{
- dest_file = fopen (dest_filename, "r");
+ dest_file = gpgrt_fopen (dest_filename, "r");
if (!dest_file)
goto change_file_one_err;
- while ((length = read_line (dest_file, &line, &line_len, NULL)) > 0)
+ while ((length = gpgrt_read_line (dest_file, &line, &line_len, NULL)) > 0)
{
int disable = 0;
char *start;
{
if (!in_marker)
{
- fprintf (src_file,
- "# GPGConf disabled this option here at %s\n",
- asctimestamp (gnupg_get_time ()));
- if (ferror (src_file))
+ gpgrt_fprintf (src_file,
+ "# %s disabled this option here at %s\n",
+ GPGCONF_DISP_NAME, asctimestamp (gnupg_get_time ()));
+ if (gpgrt_ferror (src_file))
goto change_file_one_err;
- fprintf (src_file, "# %s", line);
- if (ferror (src_file))
+ gpgrt_fprintf (src_file, "# %s", line);
+ if (gpgrt_ferror (src_file))
goto change_file_one_err;
}
}
else
{
- fprintf (src_file, "%s", line);
- if (ferror (src_file))
+ gpgrt_fprintf (src_file, "%s", line);
+ if (gpgrt_ferror (src_file))
goto change_file_one_err;
}
}
- if (length < 0 || ferror (dest_file))
+ if (length < 0 || gpgrt_ferror (dest_file))
goto change_file_one_err;
}
proceed. Note that we first write a newline, this guards us
against files which lack the newline at the end of the last
line, while it doesn't hurt us in all other cases. */
- fprintf (src_file, "\n%s\n", marker);
- if (ferror (src_file))
+ gpgrt_fprintf (src_file, "\n%s\n", marker);
+ if (gpgrt_ferror (src_file))
goto change_file_one_err;
}
followed by the rest of the original file. */
while (cur_arg)
{
- fprintf (src_file, "%s\n", cur_arg);
+ gpgrt_fprintf (src_file, "%s\n", cur_arg);
/* Find next argument. */
if (arg)
cur_arg = NULL;
}
- fprintf (src_file, "%s %s\n", marker, asctimestamp (gnupg_get_time ()));
- if (ferror (src_file))
+ gpgrt_fprintf (src_file, "%s %s\n", marker, asctimestamp (gnupg_get_time ()));
+ if (gpgrt_ferror (src_file))
goto change_file_one_err;
if (!in_marker)
{
- fprintf (src_file, "# GPGConf edited this configuration file.\n");
- if (ferror (src_file))
+ gpgrt_fprintf (src_file, "# %s edited this configuration file.\n",
+ GPGCONF_DISP_NAME);
+ if (gpgrt_ferror (src_file))
goto change_file_one_err;
- fprintf (src_file, "# It will disable options before this marked "
+ gpgrt_fprintf (src_file, "# It will disable options before this marked "
"block, but it will\n");
- if (ferror (src_file))
+ if (gpgrt_ferror (src_file))
goto change_file_one_err;
- fprintf (src_file, "# never change anything below these lines.\n");
- if (ferror (src_file))
+ gpgrt_fprintf (src_file, "# never change anything below these lines.\n");
+ if (gpgrt_ferror (src_file))
goto change_file_one_err;
}
if (dest_file)
{
- while ((length = read_line (dest_file, &line, &line_len, NULL)) > 0)
+ while ((length = gpgrt_read_line (dest_file, &line, &line_len, NULL)) > 0)
{
- fprintf (src_file, "%s", line);
- if (ferror (src_file))
+ gpgrt_fprintf (src_file, "%s", line);
+ if (gpgrt_ferror (src_file))
goto change_file_one_err;
}
- if (length < 0 || ferror (dest_file))
+ if (length < 0 || gpgrt_ferror (dest_file))
goto change_file_one_err;
}
xfree (line);
line = NULL;
- res = fclose (src_file);
+ res = gpgrt_fclose (src_file);
if (res)
{
res = errno;
close (fd);
if (dest_file)
- fclose (dest_file);
- errno = res;
+ gpgrt_fclose (dest_file);
+ gpg_err_set_errno (res);
return -1;
}
close (fd);
if (dest_file)
{
- res = fclose (dest_file);
+ res = gpgrt_fclose (dest_file);
if (res)
return -1;
}
res = errno;
if (src_file)
{
- fclose (src_file);
+ gpgrt_fclose (src_file);
close (fd);
}
if (dest_file)
- fclose (dest_file);
- errno = res;
+ gpgrt_fclose (dest_file);
+ gpg_err_set_errno (res);
return -1;
}
/* Create and verify the new configuration file for the specified
- backend and component. Returns 0 on success and -1 on error. */
+ * backend and component. Returns 0 on success and -1 on error. If
+ * VERBATIM is set the profile mode is used. This function may store
+ * pointers to malloced strings in SRC_FILENAMEP, DEST_FILENAMEP, and
+ * ORIG_FILENAMEP. Those must be freed by the caller. The strings
+ * refer to three versions of the configuration file:
+ *
+ * SRC_FILENAME: The updated configuration is written to this file.
+ * DEST_FILENAME: Name of the configuration file read by the
+ * component.
+ * ORIG_FILENAME: A backup of the previous configuration file.
+ *
+ * To apply the configuration change, rename SRC_FILENAME to
+ * DEST_FILENAME. To revert to the previous configuration, rename
+ * ORIG_FILENAME to DEST_FILENAME. */
static int
change_options_program (gc_component_t component, gc_backend_t backend,
char **src_filenamep, char **dest_filenamep,
- char **orig_filenamep)
+ char **orig_filenamep,
+ int verbatim)
{
- static const char marker[] = "###+++--- GPGConf ---+++###";
+ static const char marker[] = "###+++--- " GPGCONF_DISP_NAME " ---+++###";
/* True if we are within the marker in the config file. */
int in_marker = 0;
gc_option_t *option;
ssize_t length;
int res;
int fd;
- FILE *src_file = NULL;
- FILE *dest_file = NULL;
+ gpgrt_stream_t src_file = NULL;
+ gpgrt_stream_t dest_file = NULL;
char *src_filename;
char *dest_filename;
char *orig_filename;
/* FIXME. Throughout the function, do better error reporting. */
dest_filename = xstrdup (get_config_filename (component, backend));
- src_filename = xasprintf ("%s.gpgconf.%i.new", dest_filename, getpid ());
- orig_filename = xasprintf ("%s.gpgconf.%i.bak", dest_filename, getpid ());
+ src_filename = xasprintf ("%s.%s.%i.new",
+ dest_filename, GPGCONF_NAME, (int)getpid ());
+ orig_filename = xasprintf ("%s.%s.%i.bak",
+ dest_filename, GPGCONF_NAME, (int)getpid ());
#ifdef HAVE_W32_SYSTEM
res = copy_file (dest_filename, orig_filename);
res = link (dest_filename, orig_filename);
#endif
if (res < 0 && errno != ENOENT)
- return -1;
+ {
+ xfree (dest_filename);
+ xfree (src_filename);
+ xfree (orig_filename);
+ return -1;
+ }
if (res < 0)
{
xfree (orig_filename);
fd = open (src_filename, O_CREAT | O_EXCL | O_WRONLY, 0644);
if (fd < 0)
return -1;
- src_file = fdopen (fd, "w");
+ src_file = gpgrt_fdopen (fd, "w");
res = errno;
if (!src_file)
{
- errno = res;
+ gpg_err_set_errno (res);
return -1;
}
process. */
if (orig_filename)
{
- dest_file = fopen (dest_filename, "r");
+ dest_file = gpgrt_fopen (dest_filename, "r");
if (!dest_file)
goto change_one_err;
- while ((length = read_line (dest_file, &line, &line_len, NULL)) > 0)
+ while ((length = gpgrt_read_line (dest_file, &line, &line_len, NULL)) > 0)
{
int disable = 0;
char *start;
{
if (!in_marker)
{
- fprintf (src_file,
- "# GPGConf disabled this option here at %s\n",
- asctimestamp (gnupg_get_time ()));
- if (ferror (src_file))
+ gpgrt_fprintf (src_file,
+ "# %s disabled this option here at %s\n",
+ GPGCONF_DISP_NAME, asctimestamp (gnupg_get_time ()));
+ if (gpgrt_ferror (src_file))
goto change_one_err;
- fprintf (src_file, "# %s", line);
- if (ferror (src_file))
+ gpgrt_fprintf (src_file, "# %s", line);
+ if (gpgrt_ferror (src_file))
goto change_one_err;
}
}
else
{
- fprintf (src_file, "%s", line);
- if (ferror (src_file))
+ gpgrt_fprintf (src_file, "%s", line);
+ if (gpgrt_ferror (src_file))
goto change_one_err;
}
}
- if (length < 0 || ferror (dest_file))
+ if (length < 0 || gpgrt_ferror (dest_file))
goto change_one_err;
}
proceed. Note that we first write a newline, this guards us
against files which lack the newline at the end of the last
line, while it doesn't hurt us in all other cases. */
- fprintf (src_file, "\n%s\n", marker);
- if (ferror (src_file))
+ gpgrt_fprintf (src_file, "\n%s\n", marker);
+ if (gpgrt_ferror (src_file))
goto change_one_err;
}
/* At this point, we have copied everything up to the end marker
/* We have to turn on UTF8 strings for GnuPG. */
if (backend == GC_BACKEND_GPG && ! utf8strings_seen)
- fprintf (src_file, "utf8-strings\n");
+ gpgrt_fprintf (src_file, "utf8-strings\n");
option = gc_component[component].options;
while (option->name)
{
if (*arg == '\0' || *arg == ',')
{
- fprintf (src_file, "%s\n", option->name);
- if (ferror (src_file))
+ gpgrt_fprintf (src_file, "%s\n", option->name);
+ if (gpgrt_ferror (src_file))
goto change_one_err;
}
else if (gc_arg_type[option->arg_type].fallback
== GC_ARG_TYPE_NONE)
{
assert (*arg == '1');
- fprintf (src_file, "%s\n", option->name);
- if (ferror (src_file))
+ gpgrt_fprintf (src_file, "%s\n", option->name);
+ if (gpgrt_ferror (src_file))
goto change_one_err;
arg++;
{
char *end;
- assert (*arg == '"');
- arg++;
-
- end = strchr (arg, ',');
- if (end)
- *end = '\0';
-
- fprintf (src_file, "%s %s\n", option->name,
- percent_deescape (arg));
- if (ferror (src_file))
+ if (!verbatim)
+ {
+ log_assert (*arg == '"');
+ arg++;
+
+ end = strchr (arg, ',');
+ if (end)
+ *end = '\0';
+ }
+ else
+ end = NULL;
+
+ gpgrt_fprintf (src_file, "%s %s\n", option->name,
+ verbatim? arg : percent_deescape (arg));
+ if (gpgrt_ferror (src_file))
goto change_one_err;
if (end)
if (end)
*end = '\0';
- fprintf (src_file, "%s %s\n", option->name, arg);
- if (ferror (src_file))
+ gpgrt_fprintf (src_file, "%s %s\n", option->name, arg);
+ if (gpgrt_ferror (src_file))
goto change_one_err;
if (end)
option++;
}
- fprintf (src_file, "%s %s\n", marker, asctimestamp (gnupg_get_time ()));
- if (ferror (src_file))
+ gpgrt_fprintf (src_file, "%s %s\n", marker, asctimestamp (gnupg_get_time ()));
+ if (gpgrt_ferror (src_file))
goto change_one_err;
if (!in_marker)
{
- fprintf (src_file, "# GPGConf edited this configuration file.\n");
- if (ferror (src_file))
+ gpgrt_fprintf (src_file, "# %s edited this configuration file.\n",
+ GPGCONF_DISP_NAME);
+ if (gpgrt_ferror (src_file))
goto change_one_err;
- fprintf (src_file, "# It will disable options before this marked "
+ gpgrt_fprintf (src_file, "# It will disable options before this marked "
"block, but it will\n");
- if (ferror (src_file))
+ if (gpgrt_ferror (src_file))
goto change_one_err;
- fprintf (src_file, "# never change anything below these lines.\n");
- if (ferror (src_file))
+ gpgrt_fprintf (src_file, "# never change anything below these lines.\n");
+ if (gpgrt_ferror (src_file))
goto change_one_err;
}
if (dest_file)
{
- while ((length = read_line (dest_file, &line, &line_len, NULL)) > 0)
+ while ((length = gpgrt_read_line (dest_file, &line, &line_len, NULL)) > 0)
{
- fprintf (src_file, "%s", line);
- if (ferror (src_file))
+ gpgrt_fprintf (src_file, "%s", line);
+ if (gpgrt_ferror (src_file))
goto change_one_err;
}
- if (length < 0 || ferror (dest_file))
+ if (length < 0 || gpgrt_ferror (dest_file))
goto change_one_err;
}
xfree (line);
line = NULL;
- res = fclose (src_file);
+ res = gpgrt_fclose (src_file);
if (res)
{
res = errno;
close (fd);
if (dest_file)
- fclose (dest_file);
- errno = res;
+ gpgrt_fclose (dest_file);
+ gpg_err_set_errno (res);
return -1;
}
close (fd);
if (dest_file)
{
- res = fclose (dest_file);
+ res = gpgrt_fclose (dest_file);
if (res)
return -1;
}
res = errno;
if (src_file)
{
- fclose (src_file);
+ gpgrt_fclose (src_file);
close (fd);
}
if (dest_file)
- fclose (dest_file);
- errno = res;
+ gpgrt_fclose (dest_file);
+ gpg_err_set_errno (res);
return -1;
}
/* Common code for gc_component_change_options and
- gc_process_gpgconf_conf. */
+ * gc_process_gpgconf_conf. If VERBATIM is set the profile parsing
+ * mode is used. */
static void
change_one_value (gc_option_t *option, int *runtime,
- unsigned long flags, char *new_value)
+ unsigned long flags, char *new_value, int verbatim)
{
unsigned long new_value_nr = 0;
- option_check_validity (option, flags, new_value, &new_value_nr);
+ option_check_validity (option, flags, new_value, &new_value_nr, verbatim);
if (option->flags & GC_OPT_FLAG_RUNTIME)
runtime[option->backend] = 1;
/* Read the modifications from IN and apply them. If IN is NULL the
modifications are expected to already have been set to the global
- table. */
+ table. If VERBATIM is set the profile mode is used. */
void
-gc_component_change_options (int component, FILE *in, FILE *out)
+gc_component_change_options (int component, estream_t in, estream_t out,
+ int verbatim)
{
int err = 0;
+ int block = 0;
int runtime[GC_BACKEND_NR];
char *src_filename[GC_BACKEND_NR];
char *dest_filename[GC_BACKEND_NR];
size_t line_len = 0;
ssize_t length;
+ if (component == GC_COMPONENT_PINENTRY)
+ return; /* Dummy component for now. */
+
for (backend = 0; backend < GC_BACKEND_NR; backend++)
{
runtime[backend] = 0;
if (in)
{
/* Read options from the file IN. */
- while ((length = read_line (in, &line, &line_len, NULL)) > 0)
+ while ((length = es_read_line (in, &line, &line_len, NULL)) > 0)
{
char *linep;
unsigned long flags = 0;
if (end)
*(end++) = '\0';
- errno = 0;
+ gpg_err_set_errno (0);
flags = strtoul (linep, &tail, 0);
if (errno)
gc_error (1, errno, "malformed flags in option %s", line);
continue;
}
- change_one_value (option, runtime, flags, new_value);
+ change_one_value (option, runtime, flags, new_value, 0);
}
+ if (length < 0 || gpgrt_ferror (in))
+ gc_error (1, errno, "error reading stream 'in'");
}
/* Now that we have collected and locally verified the changes,
err = change_options_program (component, option->backend,
&src_filename[option->backend],
&dest_filename[option->backend],
- &orig_filename[option->backend]);
+ &orig_filename[option->backend],
+ verbatim);
if (! err)
{
/* External verification. */
gc_error (0, 0,
_("External verification of component %s failed"),
gc_component[component].name);
- errno = EINVAL;
+ gpg_err_set_errno (EINVAL);
}
}
option++;
}
+ /* We are trying to atomically commit all changes. Unfortunately,
+ we cannot rely on gnupg_rename_file to manage the signals for us,
+ doing so would require us to pass NULL as BLOCK to any subsequent
+ call to it. Instead, we just manage the signal handling
+ manually. */
+ block = 1;
+ gnupg_block_all_signals ();
+
if (! err && ! opt.dry_run)
{
int i;
assert (dest_filename[i]);
if (orig_filename[i])
- {
-#ifdef HAVE_W32_SYSTEM
- /* There is no atomic update on W32. */
- err = unlink (dest_filename[i]);
-#endif /* HAVE_W32_SYSTEM */
- if (!err)
- err = rename (src_filename[i], dest_filename[i]);
- }
+ err = gnupg_rename_file (src_filename[i], dest_filename[i], NULL);
else
{
#ifdef HAVE_W32_SYSTEM
/* We skip the unlink if we expect the file not to
be there. */
- err = rename (src_filename[i], dest_filename[i]);
+ err = gnupg_rename_file (src_filename[i], dest_filename[i], NULL);
#else /* HAVE_W32_SYSTEM */
/* This is a bit safer than rename() because we
expect DEST_FILENAME not to be there. If it
}
if (err)
break;
+ xfree (src_filename[i]);
src_filename[i] = NULL;
}
}
int i;
int saved_errno = errno;
- /* An error occured or a dry-run is requested. */
+ /* An error occurred or a dry-run is requested. */
for (i = 0; i < GC_BACKEND_NR; i++)
{
if (src_filename[i])
a version of the file that is even newer than the one
we just installed. */
if (orig_filename[i])
- {
-#ifdef HAVE_W32_SYSTEM
- /* There is no atomic update on W32. */
- unlink (dest_filename[i]);
-#endif /* HAVE_W32_SYSTEM */
- rename (orig_filename[i], dest_filename[i]);
- }
+ gnupg_rename_file (orig_filename[i], dest_filename[i], NULL);
else
unlink (dest_filename[i]);
}
for (backend = 0; backend < GC_BACKEND_NR; backend++)
{
if (runtime[backend] && gc_backend[backend].runtime_change)
- (*gc_backend[backend].runtime_change) ();
+ (*gc_backend[backend].runtime_change) (0);
}
/* Move the per-process backup file into its place. */
assert (dest_filename[backend]);
- backup_filename = xasprintf ("%s.gpgconf.bak", dest_filename[backend]);
-
-#ifdef HAVE_W32_SYSTEM
- /* There is no atomic update on W32. */
- unlink (backup_filename);
-#endif /* HAVE_W32_SYSTEM */
- rename (orig_filename[backend], backup_filename);
+ backup_filename = xasprintf ("%s.%s.bak",
+ dest_filename[backend], GPGCONF_NAME);
+ gnupg_rename_file (orig_filename[backend], backup_filename, NULL);
+ xfree (backup_filename);
}
leave:
+ if (block)
+ gnupg_unblock_all_signals ();
xfree (line);
+ for (backend = 0; backend < GC_BACKEND_NR; backend++)
+ {
+ xfree (src_filename[backend]);
+ xfree (dest_filename[backend]);
+ xfree (orig_filename[backend]);
+ }
}
/* Under Windows we don't support groups. */
if (group && *group)
gc_error (0, 0, _("Note that group specifications are ignored\n"));
+#ifndef HAVE_W32CE_SYSTEM
if (*user)
{
static char *my_name;
if (!strcmp (user, my_name))
return 1; /* Found. */
}
+#endif /*HAVE_W32CE_SYSTEM*/
#else /*!HAVE_W32_SYSTEM*/
/* First check whether the user matches. */
if (*user)
returned on error. */
int
gc_process_gpgconf_conf (const char *fname_arg, int update, int defaults,
- FILE *listfp)
+ estream_t listfp)
{
int result = 0;
char *line = NULL;
size_t line_len = 0;
ssize_t length;
- FILE *config;
+ gpgrt_stream_t config;
int lineno = 0;
int in_rule = 0;
int got_match = 0;
if (fname_arg)
fname = xstrdup (fname_arg);
else
- fname = make_filename (gnupg_sysconfdir (), "gpgconf.conf", NULL);
+ fname = make_filename (gnupg_sysconfdir (), GPGCONF_NAME EXTSEP_S "conf",
+ NULL);
for (backend_id = 0; backend_id < GC_BACKEND_NR; backend_id++)
runtime[backend_id] = 0;
- config = fopen (fname, "r");
+ config = gpgrt_fopen (fname, "r");
if (!config)
{
/* Do not print an error if the file is not available, except
when running in syntax check mode. */
if (errno != ENOENT || !update)
{
- gc_error (0, errno, "can not open global config file `%s'", fname);
+ gc_error (0, errno, "can not open global config file '%s'", fname);
result = -1;
}
xfree (fname);
return result;
}
- while ((length = read_line (config, &line, &line_len, NULL)) > 0)
+ while ((length = gpgrt_read_line (config, &line, &line_len, NULL)) > 0)
{
char *key, *component, *option, *flags, *value;
char *empty;
;
if (!*p)
{
- gc_error (0, 0, "missing rule at `%s', line %d", fname, lineno);
+ gc_error (0, 0, "missing rule at '%s', line %d", fname, lineno);
result = -1;
+ gpgconf_write_status (STATUS_WARNING,
+ "gpgconf.conf %d file '%s' line %d "
+ "missing rule",
+ GPG_ERR_SYNTAX, fname, lineno);
continue;
}
*p++ = 0;
}
else if (!in_rule)
{
- gc_error (0, 0, "continuation but no rule at `%s', line %d",
+ gc_error (0, 0, "continuation but no rule at '%s', line %d",
fname, lineno);
result = -1;
continue;
;
if (p == component)
{
- gc_error (0, 0, "missing component at `%s', line %d",
+ gc_error (0, 0, "missing component at '%s', line %d",
fname, lineno);
+ gpgconf_write_status (STATUS_WARNING,
+ "gpgconf.conf %d file '%s' line %d "
+ " missing component",
+ GPG_ERR_NO_NAME, fname, lineno);
result = -1;
continue;
}
component_id = gc_component_find (component);
if (component_id < 0)
{
- gc_error (0, 0, "unknown component at `%s', line %d",
+ gc_error (0, 0, "unknown component at '%s', line %d",
fname, lineno);
+ gpgconf_write_status (STATUS_WARNING,
+ "gpgconf.conf %d file '%s' line %d "
+ "unknown component",
+ GPG_ERR_UNKNOWN_NAME, fname, lineno);
result = -1;
}
;
if (p == option)
{
- gc_error (0, 0, "missing option at `%s', line %d",
+ gc_error (0, 0, "missing option at '%s', line %d",
fname, lineno);
+ gpgconf_write_status (STATUS_WARNING,
+ "gpgconf.conf %d file '%s' line %d "
+ "missing option",
+ GPG_ERR_INV_NAME, fname, lineno);
result = -1;
continue;
}
option_info = find_option (component_id, option, GC_BACKEND_ANY);
if (!option_info)
{
- gc_error (0, 0, "unknown option at `%s', line %d",
+ gc_error (0, 0, "unknown option at '%s', line %d",
fname, lineno);
+ gpgconf_write_status (STATUS_WARNING,
+ "gpgconf.conf %d file '%s' line %d "
+ "unknown option",
+ GPG_ERR_UNKNOWN_OPTION, fname, lineno);
result = -1;
}
}
p = strchr (flags, ']');
if (!p)
{
- gc_error (0, 0, "syntax error in rule at `%s', line %d",
+ gc_error (0, 0, "syntax error in rule at '%s', line %d",
fname, lineno);
+ gpgconf_write_status (STATUS_WARNING,
+ "gpgconf.conf %d file '%s' line %d "
+ "syntax error in rule",
+ GPG_ERR_SYNTAX, fname, lineno);
result = -1;
continue;
}
if (*value)
{
gc_error (0, 0, "flag \"default\" may not be combined "
- "with a value at `%s', line %d",
+ "with a value at '%s', line %d",
fname, lineno);
result = -1;
}
;
else
{
- gc_error (0, 0, "unknown flag at `%s', line %d",
+ gc_error (0, 0, "unknown flag at '%s', line %d",
fname, lineno);
result = -1;
}
*p = 0; /* We better strip any extra stuff. */
}
- fprintf (listfp, "k:%s:", gc_percent_escape (key));
- fprintf (listfp, "%s\n", group? gc_percent_escape (group):"");
+ es_fprintf (listfp, "k:%s:", gc_percent_escape (key));
+ es_fprintf (listfp, "%s\n", group? gc_percent_escape (group):"");
}
/* All other lines are rule records. */
- fprintf (listfp, "r:::%s:%s:%s:",
- gc_component[component_id].name,
- option_info->name? option_info->name : "",
- flags? flags : "");
+ es_fprintf (listfp, "r:::%s:%s:%s:",
+ gc_component[component_id].name,
+ option_info->name? option_info->name : "",
+ flags? flags : "");
if (value != empty)
- fprintf (listfp, "\"%s", gc_percent_escape (value));
+ es_fprintf (listfp, "\"%s", gc_percent_escape (value));
- putc ('\n', listfp);
+ es_putc ('\n', listfp);
}
/* Check whether the key matches but do this only if we are not
if (defaults)
{
- assert (component_id >= 0 && component_id < GC_COMPONENT_NR);
-
- /* Here we explicitly allow to update the value again. */
+ /* Here we explicitly allow updating the value again. */
if (newflags)
{
option_info->new_flags = 0;
xfree (option_info->new_value);
option_info->new_value = NULL;
}
- change_one_value (option_info, runtime, newflags, value);
+ change_one_value (option_info, runtime, newflags, value, 0);
}
}
}
- if (length < 0 || ferror (config))
+ if (length < 0 || gpgrt_ferror (config))
{
- gc_error (0, errno, "error reading from `%s'", fname);
+ gc_error (0, errno, "error reading from '%s'", fname);
result = -1;
}
- if (fclose (config) && ferror (config))
- gc_error (0, errno, "error closing `%s'", fname);
+ if (gpgrt_fclose (config))
+ gc_error (0, errno, "error closing '%s'", fname);
xfree (line);
for (component_id = 0; component_id < GC_COMPONENT_NR; component_id++)
{
- gc_component_change_options (component_id, NULL, NULL);
+ gc_component_change_options (component_id, NULL, NULL, 0);
}
opt.runtime = save_opt_runtime;
{
for (backend_id = 0; backend_id < GC_BACKEND_NR; backend_id++)
if (runtime[backend_id] && gc_backend[backend_id].runtime_change)
- (*gc_backend[backend_id].runtime_change) ();
+ (*gc_backend[backend_id].runtime_change) (0);
}
}
xfree (fname);
return result;
}
+
+
+/*
+ * Apply the profile FNAME to all known configure files.
+ */
+gpg_error_t
+gc_apply_profile (const char *fname)
+{
+ gpg_error_t err;
+ char *fname_buffer = NULL;
+ char *line = NULL;
+ size_t line_len = 0;
+ ssize_t length;
+ estream_t fp;
+ int lineno = 0;
+ int runtime[GC_BACKEND_NR];
+ int backend_id;
+ int component_id = -1;
+ int skip_section = 0;
+ int error_count = 0;
+ int newflags;
+
+ if (!fname)
+ fname = "-";
+
+ for (backend_id = 0; backend_id < GC_BACKEND_NR; backend_id++)
+ runtime[backend_id] = 0;
+
+
+ if (!(!strcmp (fname, "-")
+ || strchr (fname, '/')
+#ifdef HAVE_W32_SYSTEM
+ || strchr (fname, '\\')
+#endif
+ || strchr (fname, '.')))
+ {
+ /* FNAME looks like a standard profile name. Check whether one
+ * is installed and use that instead of the given file name. */
+ fname_buffer = xstrconcat (gnupg_datadir (), DIRSEP_S,
+ fname, ".prf", NULL);
+ if (!access (fname_buffer, F_OK))
+ fname = fname_buffer;
+ }
+
+ fp = !strcmp (fname, "-")? es_stdin : es_fopen (fname, "r");
+ if (!fp)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("can't open '%s': %s\n", fname, gpg_strerror (err));
+ return err;
+ }
+
+ if (opt.verbose)
+ log_info ("applying profile '%s'\n", fname);
+
+ err = 0;
+ while ((length = es_read_line (fp, &line, &line_len, NULL)) > 0)
+ {
+ char *name, *flags, *value;
+ gc_option_t *option_info = NULL;
+ char *p;
+
+ lineno++;
+ name = line;
+ while (*name == ' ' || *name == '\t')
+ name++;
+ if (!*name || *name == '#' || *name == '\r' || *name == '\n')
+ continue;
+ trim_trailing_spaces (name);
+
+ /* Check whether this is a new section. */
+ if (*name == '[')
+ {
+ name++;
+ skip_section = 0;
+ /* New section: Get the name of the component. */
+ p = strchr (name, ']');
+ if (!p)
+ {
+ error_count++;
+ log_info ("%s:%d:%d: error: syntax error in section tag\n",
+ fname, lineno, (int)(name - line));
+ skip_section = 1;
+ continue;
+ }
+ *p++ = 0;
+ if (*p)
+ log_info ("%s:%d:%d: warning: garbage after section tag\n",
+ fname, lineno, (int)(p - line));
+
+ trim_spaces (name);
+ component_id = gc_component_find (name);
+ if (component_id < 0)
+ {
+ log_info ("%s:%d:%d: warning: skipping unknown section '%s'\n",
+ fname, lineno, (int)(name - line), name );
+ skip_section = 1;
+ }
+ continue;
+ }
+
+ if (skip_section)
+ continue;
+ if (component_id < 0)
+ {
+ error_count++;
+ log_info ("%s:%d:%d: error: not in a valid section\n",
+ fname, lineno, (int)(name - line));
+ skip_section = 1;
+ continue;
+ }
+
+ /* Parse the option name. */
+ for (p = name; *p && !spacep (p); p++)
+ ;
+ *p++ = 0;
+ value = p;
+
+ option_info = find_option (component_id, name, GC_BACKEND_ANY);
+ if (!option_info)
+ {
+ error_count++;
+ log_info ("%s:%d:%d: error: unknown option '%s' in section '%s'\n",
+ fname, lineno, (int)(name - line),
+ name, gc_component[component_id].name);
+ continue;
+ }
+
+ /* Parse the optional flags. */
+ trim_spaces (value);
+ flags = value;
+ if (*flags == '[')
+ {
+ flags++;
+ p = strchr (flags, ']');
+ if (!p)
+ {
+ log_info ("%s:%d:%d: warning: invalid flag specification\n",
+ fname, lineno, (int)(p - line));
+ continue;
+ }
+ *p++ = 0;
+ value = p;
+ trim_spaces (value);
+ }
+ else /* No flags given. */
+ flags = NULL;
+
+ /* Set required defaults. */
+ if (gc_arg_type[option_info->arg_type].fallback == GC_ARG_TYPE_NONE
+ && !*value)
+ value = "1";
+
+ /* Check and save this option. */
+ newflags = 0;
+ if (flags && !strcmp (flags, "default"))
+ newflags |= GC_OPT_FLAG_DEFAULT;
+
+ if (newflags)
+ option_info->new_flags = 0;
+ if (*value)
+ {
+ xfree (option_info->new_value);
+ option_info->new_value = NULL;
+ }
+ change_one_value (option_info, runtime, newflags, value, 1);
+ }
+
+ if (length < 0 || es_ferror (fp))
+ {
+ err = gpg_error_from_syserror ();
+ error_count++;
+ log_error (_("%s:%u: read error: %s\n"),
+ fname, lineno, gpg_strerror (err));
+ }
+ if (es_fclose (fp))
+ log_error (_("error closing '%s'\n"), fname);
+ if (error_count)
+ log_error (_("error parsing '%s'\n"), fname);
+
+ xfree (line);
+
+ /* If it all worked, process the options. */
+ if (!err)
+ {
+ /* We need to switch off the runtime update, so that we can do
+ it later all at once. */
+ int save_opt_runtime = opt.runtime;
+ opt.runtime = 0;
+
+ for (component_id = 0; component_id < GC_COMPONENT_NR; component_id++)
+ {
+ gc_component_change_options (component_id, NULL, NULL, 1);
+ }
+ opt.runtime = save_opt_runtime;
+
+ if (opt.runtime)
+ {
+ for (backend_id = 0; backend_id < GC_BACKEND_NR; backend_id++)
+ if (runtime[backend_id] && gc_backend[backend_id].runtime_change)
+ (*gc_backend[backend_id].runtime_change) (0);
+ }
+ }
+
+ xfree (fname_buffer);
+ return err;
+}