diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 09:59:15 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 09:59:15 +0000 |
commit | 8de1ee1b2b676b0d07586f0752750dd6b0fb7511 (patch) | |
tree | dd46fd7dc3863045696cd0e48032d8a36fa0daf5 /g13/g13.c | |
parent | Initial commit. (diff) | |
download | gnupg2-8de1ee1b2b676b0d07586f0752750dd6b0fb7511.tar.xz gnupg2-8de1ee1b2b676b0d07586f0752750dd6b0fb7511.zip |
Adding upstream version 2.2.27.upstream/2.2.27upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'g13/g13.c')
-rw-r--r-- | g13/g13.c | 1050 |
1 files changed, 1050 insertions, 0 deletions
diff --git a/g13/g13.c b/g13/g13.c new file mode 100644 index 0000000..6500916 --- /dev/null +++ b/g13/g13.c @@ -0,0 +1,1050 @@ +/* g13.c - Disk Key management with GnuPG + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <unistd.h> +#include <fcntl.h> +#include <npth.h> + +#define INCLUDED_BY_MAIN_MODULE 1 +#include "g13.h" + +#include <gcrypt.h> +#include <assuan.h> + +#include "../common/i18n.h" +#include "../common/sysutils.h" +#include "../common/gc-opt-flags.h" +#include "../common/asshelp.h" +#include "../common/init.h" +#include "keyblob.h" +#include "server.h" +#include "runner.h" +#include "create.h" +#include "mount.h" +#include "suspend.h" +#include "mountinfo.h" +#include "backend.h" +#include "call-syshelp.h" + + +enum cmd_and_opt_values { + aNull = 0, + oQuiet = 'q', + oVerbose = 'v', + oRecipient = 'r', + + aGPGConfList = 500, + aGPGConfTest, + aCreate, + aMount, + aUmount, + aSuspend, + aResume, + aServer, + aFindDevice, + + oOptions, + oDebug, + oDebugLevel, + oDebugAll, + oDebugNone, + oDebugWait, + oDebugAllowCoreDump, + oLogFile, + oNoLogFile, + oAuditLog, + + oOutput, + + oAgentProgram, + oGpgProgram, + oType, + + oDisplay, + oTTYname, + oTTYtype, + oLCctype, + oLCmessages, + oXauthority, + + oStatusFD, + oLoggerFD, + + oNoVerbose, + oNoSecmemWarn, + oNoGreeting, + oNoTTY, + oNoOptions, + oHomedir, + oWithColons, + oDryRun, + oNoDetach, + + oNoRandomSeedFile, + oFakedSystemTime + }; + + +static ARGPARSE_OPTS opts[] = { + + ARGPARSE_group (300, N_("@Commands:\n ")), + + ARGPARSE_c (aCreate, "create", N_("Create a new file system container")), + ARGPARSE_c (aMount, "mount", N_("Mount a file system container") ), + ARGPARSE_c (aUmount, "umount", N_("Unmount a file system container") ), + ARGPARSE_c (aSuspend, "suspend", N_("Suspend a file system container") ), + ARGPARSE_c (aResume, "resume", N_("Resume a file system container") ), + ARGPARSE_c (aServer, "server", N_("Run in server mode")), + ARGPARSE_c (aFindDevice, "find-device", "@"), + + ARGPARSE_c (aGPGConfList, "gpgconf-list", "@"), + ARGPARSE_c (aGPGConfTest, "gpgconf-test", "@"), + + ARGPARSE_group (301, N_("@\nOptions:\n ")), + + ARGPARSE_s_s (oRecipient, "recipient", N_("|USER-ID|encrypt for USER-ID")), + ARGPARSE_s_s (oType, "type", N_("|NAME|use container format NAME")), + + ARGPARSE_s_s (oOutput, "output", N_("|FILE|write output to FILE")), + ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")), + ARGPARSE_s_n (oQuiet, "quiet", N_("be somewhat more quiet")), + ARGPARSE_s_n (oNoTTY, "no-tty", N_("don't use the terminal at all")), + ARGPARSE_s_n (oNoDetach, "no-detach", N_("do not detach from the console")), + ARGPARSE_s_s (oLogFile, "log-file", N_("|FILE|write log output to FILE")), + ARGPARSE_s_n (oNoLogFile, "no-log-file", "@"), + ARGPARSE_s_i (oLoggerFD, "logger-fd", "@"), + + ARGPARSE_s_n (oDryRun, "dry-run", N_("do not make any changes")), + + ARGPARSE_conffile (oOptions, "options", N_("|FILE|read options from FILE")), + + ARGPARSE_s_s (oDebug, "debug", "@"), + ARGPARSE_s_s (oDebugLevel, "debug-level", + N_("|LEVEL|set the debugging level to LEVEL")), + ARGPARSE_s_n (oDebugAll, "debug-all", "@"), + ARGPARSE_s_n (oDebugNone, "debug-none", "@"), + ARGPARSE_s_i (oDebugWait, "debug-wait", "@"), + ARGPARSE_s_n (oDebugAllowCoreDump, "debug-allow-core-dump", "@"), + + ARGPARSE_s_i (oStatusFD, "status-fd", + N_("|FD|write status info to this FD")), + + ARGPARSE_group (302, N_( + "@\n(See the man page for a complete listing of all commands and options)\n" + )), + + ARGPARSE_group (303, N_("@\nExamples:\n\n" + " blurb\n" + " blurb\n")), + + /* Hidden options. */ + ARGPARSE_s_n (oNoVerbose, "no-verbose", "@"), + ARGPARSE_s_n (oNoSecmemWarn, "no-secmem-warning", "@"), + ARGPARSE_s_n (oNoGreeting, "no-greeting", "@"), + ARGPARSE_noconffile (oNoOptions, "no-options", "@"), + ARGPARSE_s_s (oHomedir, "homedir", "@"), + ARGPARSE_s_s (oAgentProgram, "agent-program", "@"), + ARGPARSE_s_s (oGpgProgram, "gpg-program", "@"), + ARGPARSE_s_s (oDisplay, "display", "@"), + ARGPARSE_s_s (oTTYname, "ttyname", "@"), + ARGPARSE_s_s (oTTYtype, "ttytype", "@"), + ARGPARSE_s_s (oLCctype, "lc-ctype", "@"), + ARGPARSE_s_s (oLCmessages, "lc-messages", "@"), + ARGPARSE_s_s (oXauthority, "xauthority", "@"), + ARGPARSE_s_s (oFakedSystemTime, "faked-system-time", "@"), + ARGPARSE_s_n (oWithColons, "with-colons", "@"), + ARGPARSE_s_n (oNoRandomSeedFile, "no-random-seed-file", "@"), + + /* Command aliases. */ + + ARGPARSE_end () +}; + + +/* The list of supported debug flags. */ +static struct debug_flags_s debug_flags [] = + { + { DBG_MOUNT_VALUE , "mount" }, + { DBG_CRYPTO_VALUE , "crypto" }, + { DBG_MEMORY_VALUE , "memory" }, + { DBG_MEMSTAT_VALUE, "memstat" }, + { DBG_IPC_VALUE , "ipc" }, + { 0, NULL } + }; + + +/* The timer tick interval used by the idle task. */ +#define TIMERTICK_INTERVAL_SEC (1) + +/* It is possible that we are currently running under setuid permissions. */ +static int maybe_setuid = 1; + +/* Helper to implement --debug-level and --debug. */ +static const char *debug_level; +static unsigned int debug_value; + +/* Flag to indicate that a shutdown was requested. */ +static int shutdown_pending; + +/* The thread id of the idle task. */ +static npth_t idle_task_thread; + + +/* The container type as specified on the command line. */ +static int cmdline_conttype; + + + +static void set_cmd (enum cmd_and_opt_values *ret_cmd, + enum cmd_and_opt_values new_cmd ); + +static void start_idle_task (void); +static void join_idle_task (void); + + +/* Begin NPth wrapper functions. */ +ASSUAN_SYSTEM_NPTH_IMPL; + + +static const char * +my_strusage( int level ) +{ + const char *p; + + switch (level) + { + case 9: p = "GPL-3.0-or-later"; break; + case 11: p = "@G13@ (@GNUPG@)"; + break; + case 13: p = VERSION; break; + case 14: p = GNUPG_DEF_COPYRIGHT_LINE; break; + case 17: p = PRINTABLE_OS_NAME; break; + case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n"); + break; + case 1: + case 40: p = _("Usage: @G13@ [options] [files] (-h for help)"); + break; + case 41: + p = _("Syntax: @G13@ [options] [files]\n" + "Create, mount or unmount an encrypted file system container\n"); + break; + + case 31: p = "\nHome: "; break; + case 32: p = gnupg_homedir (); break; + + default: p = NULL; break; + } + return p; +} + + +static void +wrong_args (const char *text) +{ + fprintf (stderr, _("usage: %s [options] "), G13_NAME); + fputs (text, stderr); + putc ('\n', stderr); + g13_exit (2); +} + + +/* Setup the debugging. With a DEBUG_LEVEL of NULL only the active + debug flags are propagated to the subsystems. With DEBUG_LEVEL + set, a specific set of debug flags is set; and individual debugging + flags will be added on top. */ +static void +set_debug (void) +{ + int numok = (debug_level && digitp (debug_level)); + int numlvl = numok? atoi (debug_level) : 0; + + if (!debug_level) + ; + else if (!strcmp (debug_level, "none") || (numok && numlvl < 1)) + opt.debug = 0; + else if (!strcmp (debug_level, "basic") || (numok && numlvl <= 2)) + opt.debug = DBG_IPC_VALUE|DBG_MOUNT_VALUE; + else if (!strcmp (debug_level, "advanced") || (numok && numlvl <= 5)) + opt.debug = DBG_IPC_VALUE|DBG_MOUNT_VALUE; + else if (!strcmp (debug_level, "expert") || (numok && numlvl <= 8)) + opt.debug = (DBG_IPC_VALUE|DBG_MOUNT_VALUE|DBG_CRYPTO_VALUE); + else if (!strcmp (debug_level, "guru") || numok) + { + opt.debug = ~0; + /* if (numok) */ + /* opt.debug &= ~(DBG_HASHING_VALUE); */ + } + else + { + log_error (_("invalid debug-level '%s' given\n"), debug_level); + g13_exit(2); + } + + opt.debug |= debug_value; + + if (opt.debug && !opt.verbose) + opt.verbose = 1; + if (opt.debug) + opt.quiet = 0; + + if (opt.debug & DBG_CRYPTO_VALUE ) + gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1); + gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose); + + if (opt.debug) + parse_debug_flag (NULL, &opt.debug, debug_flags); +} + + + +static void +set_cmd (enum cmd_and_opt_values *ret_cmd, enum cmd_and_opt_values new_cmd) +{ + enum cmd_and_opt_values cmd = *ret_cmd; + + if (!cmd || cmd == new_cmd) + cmd = new_cmd; + else + { + log_error (_("conflicting commands\n")); + g13_exit (2); + } + + *ret_cmd = cmd; +} + + +int +main ( int argc, char **argv) +{ + ARGPARSE_ARGS pargs; + int orig_argc; + char **orig_argv; + gpg_error_t err = 0; + /* const char *fname; */ + int may_coredump; + char *last_configname = NULL; + const char *configname = NULL; + int debug_argparser = 0; + int no_more_options = 0; + char *logfile = NULL; + int greeting = 0; + int nogreeting = 0; + /* int debug_wait = 0; */ + int use_random_seed = 1; + /* int nodetach = 0; */ + /* int nokeysetup = 0; */ + enum cmd_and_opt_values cmd = 0; + struct server_control_s ctrl; + strlist_t recipients = NULL; + + early_system_init (); + gnupg_reopen_std (G13_NAME); + set_strusage (my_strusage); + gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN); + + log_set_prefix (G13_NAME, GPGRT_LOG_WITH_PREFIX); + + /* Make sure that our subsystems are ready. */ + i18n_init (); + init_common_subsystems (&argc, &argv); + + npth_init (); + + /* Take extra care of the random pool. */ + gcry_control (GCRYCTL_USE_SECURE_RNDPOOL); + + may_coredump = disable_core_dumps (); + + g13_init_signals (); + + dotlock_create (NULL, 0); /* Register locking cleanup. */ + + opt.session_env = session_env_new (); + if (!opt.session_env) + log_fatal ("error allocating session environment block: %s\n", + strerror (errno)); + + /* First check whether we have a config file on the commandline. */ + orig_argc = argc; + orig_argv = argv; + pargs.argc = &argc; + pargs.argv = &argv; + pargs.flags= 1|(1<<6); /* Do not remove the args, ignore version. */ + pargs.flags= (ARGPARSE_FLAG_KEEP | ARGPARSE_FLAG_NOVERSION); + while (gnupg_argparse (NULL, &pargs, opts)) + { + switch (pargs.r_opt) + { + case oDebug: + case oDebugAll: + debug_argparser++; + break; + + case oHomedir: + gnupg_set_homedir (pargs.r.ret_str); + break; + } + } + /* Reset the flags. */ + pargs.flags &= ~(ARGPARSE_FLAG_KEEP | ARGPARSE_FLAG_NOVERSION); + + /* Initialize the secure memory. */ + gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0); + maybe_setuid = 0; + + /* + * Now we are now working under our real uid + */ + + /* Setup malloc hooks. */ + { + struct assuan_malloc_hooks malloc_hooks; + + malloc_hooks.malloc = gcry_malloc; + malloc_hooks.realloc = gcry_realloc; + malloc_hooks.free = gcry_free; + assuan_set_malloc_hooks (&malloc_hooks); + } + + /* Prepare libassuan. */ + assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT); + assuan_set_system_hooks (ASSUAN_SYSTEM_NPTH); + setup_libassuan_logging (&opt.debug, NULL); + + /* Setup a default control structure for command line mode. */ + memset (&ctrl, 0, sizeof ctrl); + g13_init_default_ctrl (&ctrl); + ctrl.no_server = 1; + ctrl.status_fd = -1; /* No status output. */ + + /* The configuraton directories for use by gpgrt_argparser. */ + gnupg_set_confdir (GNUPG_CONFDIR_SYS, gnupg_sysconfdir ()); + gnupg_set_confdir (GNUPG_CONFDIR_USER, gnupg_homedir ()); + + argc = orig_argc; + argv = orig_argv; + pargs.argc = &argc; + pargs.argv = &argv; + pargs.flags |= (ARGPARSE_FLAG_RESET + | ARGPARSE_FLAG_KEEP + | ARGPARSE_FLAG_SYS + | ARGPARSE_FLAG_USER); + while (!no_more_options + && gnupg_argparser (&pargs, opts, G13_NAME EXTSEP_S "conf")) + { + switch (pargs.r_opt) + { + case ARGPARSE_CONFFILE: + { + if (debug_argparser) + log_info (_("reading options from '%s'\n"), + pargs.r_type? pargs.r.ret_str: "[cmdline]"); + if (pargs.r_type) + { + xfree (last_configname); + last_configname = xstrdup (pargs.r.ret_str); + configname = last_configname; + } + else + configname = NULL; + } + break; + + case aGPGConfList: + case aGPGConfTest: + set_cmd (&cmd, pargs.r_opt); + nogreeting = 1; + /* nokeysetup = 1; */ + break; + + case aServer: + case aMount: + case aUmount: + case aSuspend: + case aResume: + case aCreate: + case aFindDevice: + set_cmd (&cmd, pargs.r_opt); + break; + + case oOutput: opt.outfile = pargs.r.ret_str; break; + + case oQuiet: opt.quiet = 1; break; + case oNoGreeting: nogreeting = 1; break; + case oNoTTY: break; + + case oDryRun: opt.dry_run = 1; break; + + case oVerbose: + opt.verbose++; + gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose); + break; + case oNoVerbose: + opt.verbose = 0; + gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose); + break; + + case oLogFile: logfile = pargs.r.ret_str; break; + case oNoLogFile: logfile = NULL; break; + + case oNoDetach: /*nodetach = 1; */break; + + case oDebug: + if (parse_debug_flag (pargs.r.ret_str, &opt.debug, debug_flags)) + { + pargs.r_opt = ARGPARSE_INVALID_ARG; + pargs.err = ARGPARSE_PRINT_ERROR; + } + break; + case oDebugAll: debug_value = ~0; break; + case oDebugNone: debug_value = 0; break; + case oDebugLevel: debug_level = pargs.r.ret_str; break; + case oDebugWait: /*debug_wait = pargs.r.ret_int; */break; + case oDebugAllowCoreDump: + may_coredump = enable_core_dumps (); + break; + + case oStatusFD: ctrl.status_fd = pargs.r.ret_int; break; + case oLoggerFD: log_set_fd (pargs.r.ret_int ); break; + + case oHomedir: gnupg_set_homedir (pargs.r.ret_str); break; + + case oAgentProgram: opt.agent_program = pargs.r.ret_str; break; + case oGpgProgram: opt.gpg_program = pargs.r.ret_str; break; + case oDisplay: opt.display = xstrdup (pargs.r.ret_str); break; + case oTTYname: opt.ttyname = xstrdup (pargs.r.ret_str); break; + case oTTYtype: opt.ttytype = xstrdup (pargs.r.ret_str); break; + case oLCctype: opt.lc_ctype = xstrdup (pargs.r.ret_str); break; + case oLCmessages: opt.lc_messages = xstrdup (pargs.r.ret_str); break; + case oXauthority: opt.xauthority = xstrdup (pargs.r.ret_str); break; + + case oFakedSystemTime: + { + time_t faked_time = isotime2epoch (pargs.r.ret_str); + if (faked_time == (time_t)(-1)) + faked_time = (time_t)strtoul (pargs.r.ret_str, NULL, 10); + gnupg_set_time (faked_time, 0); + } + break; + + case oNoSecmemWarn: gcry_control (GCRYCTL_DISABLE_SECMEM_WARN); break; + + case oNoRandomSeedFile: use_random_seed = 0; break; + + case oRecipient: /* Store the encryption key. */ + add_to_strlist (&recipients, pargs.r.ret_str); + break; + + case oType: + if (!strcmp (pargs.r.ret_str, "help")) + { + be_parse_conttype_name (NULL); + g13_exit (0); + } + cmdline_conttype = be_parse_conttype_name (pargs.r.ret_str); + if (!cmdline_conttype) + { + pargs.r_opt = ARGPARSE_INVALID_ARG; + pargs.err = ARGPARSE_PRINT_ERROR; + } + break; + + default: + if (configname) + pargs.err = ARGPARSE_PRINT_WARNING; + else + pargs.err = ARGPARSE_PRINT_ERROR; + break; + } + } + gnupg_argparse (NULL, &pargs, NULL); /* Release internal state. */ + + /* Construct GPG arguments. */ + { + strlist_t last; + last = append_to_strlist (&opt.gpg_arguments, "-z"); + last = append_to_strlist (&last, "0"); + last = append_to_strlist (&last, "--trust-model"); + last = append_to_strlist (&last, "always"); + (void) last; + } + + if (!last_configname) + opt.config_filename = make_filename (gnupg_homedir (), + G13_NAME EXTSEP_S "conf", + NULL); + else + { + opt.config_filename = last_configname; + last_configname = NULL; + } + + if (log_get_errorcount(0)) + g13_exit(2); + + /* Now that we have the options parsed we need to update the default + control structure. */ + g13_init_default_ctrl (&ctrl); + ctrl.recipients = recipients; + recipients = NULL; + + if (nogreeting) + greeting = 0; + + if (greeting) + { + fprintf (stderr, "%s %s; %s\n", + strusage(11), strusage(13), strusage(14) ); + fprintf (stderr, "%s\n", strusage(15) ); + } + + if (may_coredump && !opt.quiet) + log_info (_("WARNING: program may create a core file!\n")); + + /* Print a warning if an argument looks like an option. */ + if (!opt.quiet && !(pargs.flags & ARGPARSE_FLAG_STOP_SEEN)) + { + int i; + + for (i=0; i < argc; i++) + if (argv[i][0] == '-' && argv[i][1] == '-') + log_info (_("Note: '%s' is not considered an option\n"), argv[i]); + } + + + if (logfile) + { + log_set_file (logfile); + log_set_prefix (NULL, (GPGRT_LOG_WITH_PREFIX + | GPGRT_LOG_WITH_TIME + | GPGRT_LOG_WITH_PID )); + } + + if (gnupg_faked_time_p ()) + { + gnupg_isotime_t tbuf; + + log_info (_("WARNING: running with faked system time: ")); + gnupg_get_isotime (tbuf); + dump_isotime (tbuf); + log_printf ("\n"); + } + + /* Print any pending secure memory warnings. */ + gcry_control (GCRYCTL_RESUME_SECMEM_WARN); + + /* Setup the debug flags for all subsystems. */ + set_debug (); + + /* Install emergency cleanup handler. */ + g13_install_emergency_cleanup (); + + /* Terminate if we found any error until now. */ + if (log_get_errorcount(0)) + g13_exit (2); + + /* Set the standard GnuPG random seed file. */ + if (use_random_seed) + { + char *p = make_filename (gnupg_homedir (), "random_seed", NULL); + gcry_control (GCRYCTL_SET_RANDOM_SEED_FILE, p); + xfree(p); + } + + /* Store given filename into FNAME. */ + /* fname = argc? *argv : NULL; */ + + /* Parse all given encryption keys. This does a lookup of the keys + and stops if any of the given keys was not found. */ +#if 0 /* Currently not implemented. */ + if (!nokeysetup) + { + strlist_t sl; + int failed = 0; + + for (sl = ctrl->recipients; sl; sl = sl->next) + if (check_encryption_key ()) + failed = 1; + if (failed) + g13_exit (1); + } +#endif /*0*/ + + /* Dispatch command. */ + err = 0; + switch (cmd) + { + case aGPGConfList: + { /* List options and default values in the GPG Conf format. */ + char *config_filename_esc = percent_escape (opt.config_filename, NULL); + + printf ("gpgconf-g13.conf:%lu:\"%s\n", + GC_OPT_FLAG_DEFAULT, config_filename_esc); + xfree (config_filename_esc); + + printf ("verbose:%lu:\n", GC_OPT_FLAG_NONE); + printf ("quiet:%lu:\n", GC_OPT_FLAG_NONE); + printf ("debug-level:%lu:\"none:\n", GC_OPT_FLAG_DEFAULT); + printf ("log-file:%lu:\n", GC_OPT_FLAG_NONE); + } + break; + case aGPGConfTest: + /* This is merely a dummy command to test whether the + configuration file is valid. */ + break; + + case aServer: + { + start_idle_task (); + ctrl.no_server = 0; + err = g13_server (&ctrl); + if (err) + log_error ("server exited with error: %s <%s>\n", + gpg_strerror (err), gpg_strsource (err)); + else + g13_request_shutdown (); + } + break; + + case aFindDevice: + { + char *blockdev; + + if (argc != 1) + wrong_args ("--find-device name"); + + err = call_syshelp_find_device (&ctrl, argv[0], &blockdev); + if (err) + log_error ("error finding device '%s': %s <%s>\n", + argv[0], gpg_strerror (err), gpg_strsource (err)); + else + puts (blockdev); + } + break; + + case aCreate: /* Create a new container. */ + { + if (argc != 1) + wrong_args ("--create filename"); + start_idle_task (); + err = g13_create_container (&ctrl, argv[0]); + if (err) + log_error ("error creating a new container: %s <%s>\n", + gpg_strerror (err), gpg_strsource (err)); + else + g13_request_shutdown (); + } + break; + + case aMount: /* Mount a container. */ + { + if (argc != 1 && argc != 2 ) + wrong_args ("--mount filename [mountpoint]"); + start_idle_task (); + err = g13_mount_container (&ctrl, argv[0], argc == 2?argv[1]:NULL); + if (err) + log_error ("error mounting container '%s': %s <%s>\n", + *argv, gpg_strerror (err), gpg_strsource (err)); + } + break; + + case aUmount: /* Unmount a mounted container. */ + { + if (argc != 1) + wrong_args ("--umount filename"); + err = g13_umount_container (&ctrl, argv[0], NULL); + if (err) + log_error ("error unmounting container '%s': %s <%s>\n", + *argv, gpg_strerror (err), gpg_strsource (err)); + } + break; + + case aSuspend: /* Suspend a container. */ + { + /* Fixme: Should we add a suspend all container option? */ + if (argc != 1) + wrong_args ("--suspend filename"); + err = g13_suspend_container (&ctrl, argv[0]); + if (err) + log_error ("error suspending container '%s': %s <%s>\n", + *argv, gpg_strerror (err), gpg_strsource (err)); + } + break; + + case aResume: /* Resume a suspended container. */ + { + /* Fixme: Should we add a resume all container option? */ + if (argc != 1) + wrong_args ("--resume filename"); + err = g13_resume_container (&ctrl, argv[0]); + if (err) + log_error ("error resuming container '%s': %s <%s>\n", + *argv, gpg_strerror (err), gpg_strsource (err)); + } + break; + + default: + log_error (_("invalid command (there is no implicit command)\n")); + break; + } + + g13_deinit_default_ctrl (&ctrl); + + if (!err) + join_idle_task (); + + /* Cleanup. */ + g13_exit (0); + return 8; /*NOTREACHED*/ +} + + +/* Store defaults into the per-connection CTRL object. */ +void +g13_init_default_ctrl (ctrl_t ctrl) +{ + ctrl->conttype = cmdline_conttype? cmdline_conttype : CONTTYPE_ENCFS; +} + + +/* Release remaining resources allocated in the CTRL object. */ +void +g13_deinit_default_ctrl (ctrl_t ctrl) +{ + call_syshelp_release (ctrl); + FREE_STRLIST (ctrl->recipients); +} + + +/* Request a shutdown. This can be used when the process should + * finish instead of running the idle task. */ +void +g13_request_shutdown (void) +{ + shutdown_pending++; +} + + +/* This function is called for each signal we catch. It is run in the + main context or the one of a NPth thread and thus it is not + restricted in what it may do. */ +static void +handle_signal (int signo) +{ + switch (signo) + { +#ifndef HAVE_W32_SYSTEM + case SIGHUP: + log_info ("SIGHUP received - re-reading configuration\n"); + /* Fixme: Not yet implemented. */ + break; + + case SIGUSR1: + log_info ("SIGUSR1 received - printing internal information:\n"); + /* Fixme: We need to see how to integrate pth dumping into our + logging system. */ + /* pth_ctrl (PTH_CTRL_DUMPSTATE, log_get_stream ()); */ + mountinfo_dump_all (); + break; + + case SIGUSR2: + log_info ("SIGUSR2 received - no action defined\n"); + break; + + case SIGTERM: + if (!shutdown_pending) + log_info ("SIGTERM received - shutting down ...\n"); + else + log_info ("SIGTERM received - still %u runners active\n", + runner_get_threads ()); + shutdown_pending++; + if (shutdown_pending > 2) + { + log_info ("shutdown forced\n"); + log_info ("%s %s stopped\n", strusage(11), strusage(13) ); + g13_exit (0); + } + break; + + case SIGINT: + log_info ("SIGINT received - immediate shutdown\n"); + log_info( "%s %s stopped\n", strusage(11), strusage(13)); + g13_exit (0); + break; +#endif /*!HAVE_W32_SYSTEM*/ + + default: + log_info ("signal %d received - no action defined\n", signo); + } +} + + +/* This ticker function is called about every TIMERTICK_INTERVAL_SEC + seconds. */ +static void +handle_tick (void) +{ + /* log_debug ("TICK\n"); */ +} + + +/* The idle task. We use a separate thread to do idle stuff and to + catch signals. */ +static void * +idle_task (void *dummy_arg) +{ + int signo; /* The number of a raised signal is stored here. */ + int saved_errno; + struct timespec abstime; + struct timespec curtime; + struct timespec timeout; + int ret; + + (void)dummy_arg; + + /* Create the event to catch the signals. */ +#ifndef HAVE_W32_SYSTEM + npth_sigev_init (); + npth_sigev_add (SIGHUP); + npth_sigev_add (SIGUSR1); + npth_sigev_add (SIGUSR2); + npth_sigev_add (SIGINT); + npth_sigev_add (SIGTERM); + npth_sigev_fini (); +#endif + + npth_clock_gettime (&abstime); + abstime.tv_sec += TIMERTICK_INTERVAL_SEC; + + for (;;) + { + /* The shutdown flag allows us to terminate the idle task. */ + if (shutdown_pending) + { + runner_cancel_all (); + + if (!runner_get_threads ()) + break; /* ready */ + } + + npth_clock_gettime (&curtime); + if (!(npth_timercmp (&curtime, &abstime, <))) + { + /* Timeout. */ + handle_tick (); + npth_clock_gettime (&abstime); + abstime.tv_sec += TIMERTICK_INTERVAL_SEC; + } + npth_timersub (&abstime, &curtime, &timeout); + +#ifndef HAVE_W32_SYSTEM + ret = npth_pselect (0, NULL, NULL, NULL, &timeout, npth_sigev_sigmask()); + saved_errno = errno; + + while (npth_sigev_get_pending(&signo)) + handle_signal (signo); +#else + ret = npth_eselect (0, NULL, NULL, NULL, &timeout, NULL, NULL); + saved_errno = errno; +#endif + + if (ret == -1 && saved_errno != EINTR) + { + log_error (_("npth_pselect failed: %s - waiting 1s\n"), + strerror (saved_errno)); + npth_sleep (1); + continue; + } + + if (ret <= 0) + { + /* Interrupt or timeout. Will be handled when calculating the + next timeout. */ + continue; + } + + /* Here one would add processing of file descriptors. */ + } + + log_info (_("%s %s stopped\n"), strusage(11), strusage(13)); + return NULL; +} + + +/* Start the idle task. */ +static void +start_idle_task (void) +{ + npth_attr_t tattr; + npth_t thread; + sigset_t sigs; /* The set of signals we want to catch. */ + int err; + +#ifndef HAVE_W32_SYSTEM + /* These signals should always go to the idle task, so they need to + be blocked everywhere else. We assume start_idle_task is called + from the main thread before any other threads are created. */ + sigemptyset (&sigs); + sigaddset (&sigs, SIGHUP); + sigaddset (&sigs, SIGUSR1); + sigaddset (&sigs, SIGUSR2); + sigaddset (&sigs, SIGINT); + sigaddset (&sigs, SIGTERM); + npth_sigmask (SIG_BLOCK, &sigs, NULL); +#endif + + npth_attr_init (&tattr); + npth_attr_setdetachstate (&tattr, NPTH_CREATE_JOINABLE); + + err = npth_create (&thread, &tattr, idle_task, NULL); + if (err) + { + log_fatal ("error starting idle task: %s\n", strerror (err)); + return; /*NOTREACHED*/ + } + npth_setname_np (thread, "idle-task"); + idle_task_thread = thread; + npth_attr_destroy (&tattr); +} + + +/* Wait for the idle task to finish. */ +static void +join_idle_task (void) +{ + int err; + + /* FIXME: This assumes that a valid pthread_t is non-null. That is + not guaranteed. */ + if (idle_task_thread) + { + err = npth_join (idle_task_thread, NULL); + if (err) + log_error ("waiting for idle task thread failed: %s\n", + strerror (err)); + } +} |