Add keydctl for talking to keyd backend
[onak.git] / keydctl.c
diff --git a/keydctl.c b/keydctl.c
new file mode 100644 (file)
index 0000000..b786fea
--- /dev/null
+++ b/keydctl.c
@@ -0,0 +1,202 @@
+/*
+ * keydctl.c - A simple program to control a running keyd instance
+ *
+ * Copyright 2011 Jonathan McDowell <noodles@earth.li>
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "config.h"
+#include "keyd.h"
+#include "onak-conf.h"
+
+/* HACK: We need to stop onak-conf.o requiring this. */
+void *DBFUNCS = NULL;
+
+static int keyd_fd = -1;
+static int verbose = 0;
+
+static int keyd_do_command(enum keyd_ops cmd, void *buf, size_t len)
+{
+       uint32_t tmp;
+
+       if (keyd_fd < 0) {
+               return -1;
+       }
+
+       tmp = cmd;
+       if (write(keyd_fd, &tmp, sizeof(tmp)) != sizeof(tmp)) {
+               if (verbose >= 0) {
+                       fprintf(stderr,
+                               "Couldn't write keyd command %d: %s (%d)\n",
+                               cmd, strerror(errno), errno);
+               }
+               exit(EXIT_FAILURE);
+       } else if (read(keyd_fd, &tmp, sizeof(tmp)) != sizeof(tmp)) {
+               if (verbose >= 0) {
+                       fprintf(stderr,
+                               "Couldn't read keyd command %d reply: "
+                               "%s (%d)\n",
+                               cmd, strerror(errno), errno);
+                       }
+               exit(EXIT_FAILURE);
+       } else if (tmp != KEYD_REPLY_OK) {
+               return -1;
+       } else if (buf == NULL) {
+               return 0;
+       } else if (read(keyd_fd, &tmp, sizeof(tmp)) != sizeof(tmp)) {
+               if (verbose >= 0) {
+                       fprintf(stderr,
+                               "Couldn't read keyd command %d reply length: "
+                               "%s (%d)\n",
+                               cmd, strerror(errno), errno);
+               }
+               exit(EXIT_FAILURE);
+       } else if (tmp > len) {
+               /* TODO: Read what we can into buf and skip the rest */
+               return -1;
+       } else {
+               return read(keyd_fd, buf, tmp);
+       }
+}
+
+static void keyd_connect(void)
+{
+       struct sockaddr_un sock;
+       uint32_t           reply = KEYD_REPLY_UNKNOWN_CMD;
+
+       keyd_fd = socket(PF_UNIX, SOCK_STREAM, 0);
+       if (keyd_fd < 0) {
+               if (verbose >= 0) {
+                       fprintf(stderr,
+                               "Couldn't open socket: %s (%d)\n",
+                               strerror(errno),
+                               errno);
+               }
+               exit(EXIT_FAILURE);
+       }
+
+       sock.sun_family = AF_UNIX;
+       snprintf(sock.sun_path, sizeof(sock.sun_path) - 1, "%s/%s",
+                       config.db_dir,
+                       KEYD_SOCKET);
+       if (connect(keyd_fd, (struct sockaddr *) &sock, sizeof(sock)) < 0) {
+               if (verbose >= 0) {
+                       fprintf(stderr,
+                               "Couldn't connect to socket %s: %s (%d)\n",
+                               sock.sun_path,
+                               strerror(errno),
+                               errno);
+               }
+               exit(EXIT_FAILURE);
+       }
+
+       keyd_do_command(KEYD_CMD_VERSION, &reply, sizeof(reply));
+       if (reply != keyd_version) {
+               if (verbose >= 0) {
+                       fprintf(stderr, "Error! keyd protocol version "
+                               "mismatch. (us = %d, it = %d)\n",
+                               keyd_version, reply);
+               }
+               exit(EXIT_FAILURE);
+       }
+
+       return;
+}
+
+static void keyd_close(void)
+{
+       uint32_t cmd = KEYD_CMD_CLOSE;
+
+       if (write(keyd_fd, &cmd, sizeof(cmd)) != sizeof(cmd) && verbose >= 0) {
+               fprintf(stderr, "Couldn't send close cmd: %s (%d)\n",
+                               strerror(errno),
+                               errno);
+       }
+
+       if (shutdown(keyd_fd, SHUT_RDWR) < 0 && verbose >= 0) {
+               fprintf(stderr, "Error shutting down socket: %d\n",
+                               errno);
+       }
+       if (close(keyd_fd) < 0 && verbose >= 0) {
+               fprintf(stderr, "Error closing down socket: %d\n",
+                               errno);
+       }
+       keyd_fd = -1;
+
+       return;
+
+}
+
+static void keyd_status(void)
+{
+       uint32_t reply;
+
+       keyd_do_command(KEYD_CMD_VERSION, &reply, sizeof(reply));
+       printf("Using keyd protocol version %d.\n", reply);
+
+       return;
+}
+
+static void usage(void)
+{
+       puts("keydctl " PACKAGE_VERSION " - control an onak keyd instance.\n");
+       puts("Usage:\n");
+       puts("\tonak [options] <command> <parameters>\n");
+       puts("\tCommands:\n");
+       puts("\tcheck    - check if keyd is running");
+       puts("\tquit     - request that keyd cleanly shuts down");
+       puts("\tstatus   - display running keyd status");
+       exit(EXIT_FAILURE);
+}
+
+int main(int argc, char *argv[])
+{
+       int      optchar;
+       char    *configfile = NULL;
+
+       while ((optchar = getopt(argc, argv, "c:h")) != -1 ) {
+               switch (optchar) {
+               case 'c':
+                       configfile = strdup(optarg);
+                       break;
+               case 'h':
+               default:
+                       usage();
+                       break;
+               }
+       }
+
+       readconfig(configfile);
+       free(configfile);
+       configfile = NULL;
+
+       if ((argc - optind) < 1) {
+               usage();
+       } else if (!strcmp("check", argv[optind])) {
+               /* Just do the connect and close quietly */
+               verbose = -1;
+               keyd_connect();
+               keyd_close();
+       } else if (!strcmp("status", argv[optind])) {
+               keyd_connect();
+               keyd_status();
+               keyd_close();
+       } else if (!strcmp("quit", argv[optind])) {
+               keyd_connect();
+               keyd_do_command(KEYD_CMD_QUIT, NULL, 0);
+               keyd_close();
+       } else {
+               usage();
+       }
+
+
+       exit(EXIT_SUCCESS);
+}