From d6736509bfbe288092cd2c5ce0431b5a40da76f1 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Wed, 10 Oct 2012 11:13:33 -0400 Subject: [PATCH] gpgme-tool: add a simple socket server (with -s/--server). src/gpgme-tool.c (socket_path, socket_fd, cleanup_handler, expand_path): New. (gpgme_server): Add socket_server argument. Signed-off-by: W. Trevor King --- src/gpgme-tool.c | 155 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 138 insertions(+), 17 deletions(-) diff --git a/src/gpgme-tool.c b/src/gpgme-tool.c index eb1fbb8..556471a 100644 --- a/src/gpgme-tool.c +++ b/src/gpgme-tool.c @@ -29,6 +29,12 @@ #include #include #include +#include +#include +#include +#include +#include +#include #ifdef HAVE_LOCALE_H #include #endif @@ -620,9 +626,27 @@ peek_membuf (membuf_t *mb, size_t *len) /* SUPPORT. */ FILE *log_stream; char *program_name = "gpgme-tool"; +char *socket_path = NULL; +static int socket_fd = -1; #define spacep(p) (*(p) == ' ' || *(p) == '\t') +void +cleanup_handler (int signum) +{ + if (socket_fd >= 0) + { + close(socket_fd); + socket_fd = -1; + } + if (socket_path) + { + unlink(socket_path); + free(socket_path); + socket_path = NULL; + } + exit(EXIT_SUCCESS); +} void log_error (int status, gpg_error_t errnum, const char *fmt, ...) GT_GCC_A_PRINTF(3,4); @@ -3409,12 +3433,44 @@ register_commands (assuan_context_t ctx) return 0; } +char * +expand_path(char *raw_path) +{ + wordexp_t words; + char *path; + + if (wordexp (raw_path, &words, WRDE_NOCMD | WRDE_SHOWERR)) + { + /* TODO: check for WRDE_BADCHAR, etc. */ + fprintf (log_stream, "could not expand socket path\n"); + exit(EXIT_FAILURE); + } + if (words.we_wordc != 1) + { + fprintf (log_stream, "expanded socket path into %d fields\n", + words.we_wordc); + exit(EXIT_FAILURE); + } + path = malloc (sizeof(char) * (strlen(words.we_wordv[0]) + 1)); + if (! path) + { + perror("can't allocate path"); + exit(EXIT_FAILURE); + } + strcpy (path, words.we_wordv[0]); + + wordfree (&words); + + return path; +} /* TODO: password callback can do INQUIRE. */ void -gpgme_server (gpgme_tool_t gt) +gpgme_server (gpgme_tool_t gt, int socket_server) { gpg_error_t err; + pid_t pid; + int sock = -1; assuan_fd_t filedes[2]; struct server server; static const char hello[] = ("GPGME-Tool " VERSION " ready"); @@ -3433,25 +3489,88 @@ gpgme_server (gpgme_tool_t gt) gt->write_data = server_write_data; gt->write_data_hook = &server; - /* We use a pipe based server so that we can work from scripts. - assuan_init_pipe_server will automagically detect when we are - called with a socketpair and ignore FIELDES in this case. */ -#ifdef HAVE_W32CE_SYSTEM - filedes[0] = ASSUAN_STDIN; - filedes[1] = ASSUAN_STDOUT; -#else - filedes[0] = assuan_fdopen (0); - filedes[1] = assuan_fdopen (1); -#endif err = assuan_new (&server.assuan_ctx); if (err) log_error (1, err, "can't create assuan context"); assuan_set_pointer (server.assuan_ctx, &server); - err = assuan_init_pipe_server (server.assuan_ctx, filedes); - if (err) - log_error (1, err, "can't initialize assuan server"); + if (socket_server) + { + struct sockaddr_un addr; + + socket_fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (socket_fd == -1) + { + perror("can't create stream socket"); + exit(EXIT_FAILURE); + } + + addr.sun_family = AF_UNIX; + socket_path = expand_path("~/.gnupg/S.gpgme-tool"); + strcpy (addr.sun_path, socket_path); + + if (bind (socket_fd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un))) + { + perror("can't bind to stream socket"); + exit(EXIT_FAILURE); + } + + signal(SIGINT, &cleanup_handler); + signal(SIGTERM, &cleanup_handler); + + listen(socket_fd, 5); + + for (;;) + { + sock = accept(socket_fd, NULL, 0); + if (sock == -1) + { + perror("accept"); + continue; + } + if ((pid = fork ()) == -1) + { + perror("fork"); + close(sock); + sock = -1; + continue; + } + else if (pid == 0) + { + break; /* child; continue setting up server */ + } + /* parent; continue listening for other connections */ + } + + /* the child doesn't need cleanup handlers */ + signal(SIGINT, SIG_DFL); + signal(SIGTERM, SIG_DFL); + + err = assuan_init_socket_server ( + server.assuan_ctx, sock, + ASSUAN_SOCKET_SERVER_FDPASSING | ASSUAN_SOCKET_SERVER_ACCEPTED); + if (err) + log_error (1, err, "can't initialize assuan server"); + } + else + { + /* We use a pipe based server so that we can work from scripts. + assuan_init_pipe_server will automagically detect when we are + called with a socketpair and ignore FIELDES in this case. */ +#ifdef HAVE_W32CE_SYSTEM + filedes[0] = ASSUAN_STDIN; + filedes[1] = ASSUAN_STDOUT; +#else + filedes[0] = assuan_fdopen (0); + filedes[1] = assuan_fdopen (1); +#endif + + err = assuan_init_pipe_server (server.assuan_ctx, filedes); + if (err) + log_error (1, err, "can't initialize assuan server"); + } + err = register_commands (server.assuan_ctx); if (err) log_error (1, err, "can't register assuan commands"); @@ -3494,7 +3613,7 @@ static char doc[] = "GPGME Tool -- Assuan server exposing GPGME operations"; static char args_doc[] = "COMMAND [OPTIONS...]"; static struct argp_option options[] = { - { "server", 's', 0, 0, "Server mode" }, + { "server", 's', 0, 0, "Socket server mode (the default is a pipe server)" }, { 0 } }; @@ -3569,9 +3688,11 @@ main (int argc, char *argv[]) switch (args.cmd) { - case CMD_DEFAULT: case CMD_SERVER: - gpgme_server (>); + gpgme_server (>, 1); + break; + case CMD_DEFAULT: + gpgme_server (>, 0); break; } -- 2.26.2