#include <getopt.h>
#include <ctype.h>
#include <stdarg.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <wordexp.h>
+#include <unistd.h>
#ifdef HAVE_LOCALE_H
#include <locale.h>
#endif
/* 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);
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");
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");
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 }
};
switch (args.cmd)
{
- case CMD_DEFAULT:
case CMD_SERVER:
- gpgme_server (>);
+ gpgme_server (>, 1);
+ break;
+ case CMD_DEFAULT:
+ gpgme_server (>, 0);
break;
}