/* This file is part of GNUnet. Copyright (C) 2013-2019 GNUnet e.V. GNUnet is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. GNUnet 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . SPDX-License-Identifier: AGPL3.0-or-later */ /** * @file util/gnunet-qr.c * @author Hartmut Goebel (original implementation) * @author Martin Schanzenbach (integrate gnunet-uri) * @author Christian Grothoff (error handling) */ #include #include #include #include "platform.h" #include "gnunet_util_lib.h" #define LOG(fmt, ...) \ if (verbose) \ printf (fmt, ## __VA_ARGS__) /** * Video device to capture from. Sane default for GNU/Linux systems. */ static char *device = "/dev/video0"; /** * --verbose option */ static unsigned int verbose; /** * --silent option */ static int silent = false; /** * Handler exit code */ static long unsigned int exit_code = 1; /** * Helper process we started. */ static struct GNUNET_OS_Process *p; /** * Child signal handler. */ static struct GNUNET_SIGNAL_Context *shc_chld; /** * Pipe used to communicate child death via signal. */ static struct GNUNET_DISK_PipeHandle *sigpipe; /** * Process ID of this process at the time we installed the various * signal handlers. */ static pid_t my_pid; /** * Task triggered whenever we receive a SIGCHLD (child * process died) or when user presses CTRL-C. * * @param cls closure, NULL */ static void maint_child_death (void *cls) { enum GNUNET_OS_ProcessStatusType type; if ((GNUNET_OK != GNUNET_OS_process_status (p, &type, &exit_code)) || (type != GNUNET_OS_PROCESS_EXITED)) GNUNET_break (0 == GNUNET_OS_process_kill (p, GNUNET_TERM_SIG)); GNUNET_SIGNAL_handler_uninstall (shc_chld); shc_chld = NULL; if (NULL != sigpipe) { GNUNET_DISK_pipe_close (sigpipe); sigpipe = NULL; } GNUNET_OS_process_destroy (p); } /** * Signal handler called for signals that causes us to wait for the child process. */ static void sighandler_chld () { static char c; int old_errno = errno; /* backup errno */ if (getpid () != my_pid) _exit (1); /* we have fork'ed since the signal handler was created, * ignore the signal, see https://gnunet.org/vfork discussion */ GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_WRITE), &c, sizeof(c)); errno = old_errno; } /** * Dispatch URIs to the appropriate GNUnet helper process * * @param cls closure * @param uri uri to dispatch * @param cfgfile name of the configuration file used (for saving, can be NULL!) * @param cfg configuration */ static void gnunet_uri (void *cls, const char *uri, const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *cfg) { const char *orig_uri; const char *slash; char *subsystem; char *program; struct GNUNET_SCHEDULER_Task *rt; orig_uri = uri; if (0 != strncasecmp ("gnunet://", uri, strlen ("gnunet://"))) { fprintf (stderr, _ ("Invalid URI: does not start with `%s'\n"), "gnunet://"); return; } uri += strlen ("gnunet://"); if (NULL == (slash = strchr (uri, '/'))) { fprintf (stderr, _ ("Invalid URI: fails to specify subsystem\n")); return; } subsystem = GNUNET_strndup (uri, slash - uri); if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, "uri", subsystem, &program)) { fprintf (stderr, _ ("No handler known for subsystem `%s'\n"), subsystem); GNUNET_free (subsystem); return; } GNUNET_free (subsystem); sigpipe = GNUNET_DISK_pipe (GNUNET_DISK_PF_NONE); GNUNET_assert (NULL != sigpipe); rt = GNUNET_SCHEDULER_add_read_file ( GNUNET_TIME_UNIT_FOREVER_REL, GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ), &maint_child_death, NULL); my_pid = getpid (); shc_chld = GNUNET_SIGNAL_handler_install (SIGCHLD, &sighandler_chld); { char **argv = NULL; unsigned int argc = 0; char *u = GNUNET_strdup (program); for (const char *tok = strtok (u, " "); NULL != tok; tok = strtok (NULL, " ")) GNUNET_array_append (argv, argc, GNUNET_strdup (tok)); GNUNET_array_append (argv, argc, GNUNET_strdup (orig_uri)); GNUNET_array_append (argv, argc, NULL); p = GNUNET_OS_start_process_vap (GNUNET_OS_INHERIT_STD_ALL, NULL, NULL, NULL, argv[0], argv); for (unsigned int i = 0; i