PROGS_LDFLAGS_EXTRA =
ifeq (x@KEYD@, xyes)
-PROGS += keyd
+PROGS += keyd keydctl
KEYDB_OBJ = keydb_keyd.o
-SRCS += keyd.c keydb_keyd.c
+SRCS += keyd.c keydb_keyd.c keydctl.c
else
KEYDB_OBJ = keydb_$(DBTYPE).o
endif
ifeq (x@DBTYPE@, xdynamic)
LIBS += -ldl
BACKENDS = $(foreach be,@BACKENDS@,libkeydb_$(be).so)
-PROGS += keyd
+PROGS += keyd keydctl
PROGS_LDFLAGS_EXTRA = -rdynamic
-SRCS += keyd.c
+SRCS += keyd.c keydctl.c
endif
OBJS = stats.o cleankey.o $(CORE_OBJS) $(KEYDB_OBJ)
install onak-mail.pl $(DESTDIR)/@libdir@/onak
install onak splitkeys $(DESTDIR)/@bindir@
install onak.1 splitkeys.1 $(DESTDIR)/@mandir@/man1
- install keyd.8 onak-mail.pl.8 $(DESTDIR)/@mandir@/man8
+ install keyd.8 keydctl.8 onak-mail.pl.8 $(DESTDIR)/@mandir@/man8
ifeq (x@DBTYPE@, xdynamic)
install $(BACKENDS) $(DESTDIR)/@libdir@/onak/backends
install -d $(DESTDIR)/@sbindir@
install keyd $(DESTDIR)/@sbindir@
+ install keydctl $(DESTDIR)/@bindir@
endif
keyd: keyd.o $(CORE_OBJS) keydb_$(DBTYPE).o
$(CC) $(LDFLAGS) $(PROGS_LDFLAGS_EXTRA) \
-o keyd keyd.o $(CORE_OBJS) keydb_$(DBTYPE).o $(LIBS)
+keydctl: keydctl.o onak-conf.o ll.o log.o
+ $(CC) $(LDFLAGS) $(PROGS_LDFLAGS_EXTRA) \
+ -o keydctl keydctl.o onak-conf.o ll.o log.o $(LIBS)
+
libkeydb_db4.so: keydb_db4.o
$(CC) -shared $(DB4LIBS) -o libkeydb_db4.so keydb_db4.o $(CORE_OBJS)
$(CC) $(CFLAGS) -DCONFIGFILE=\"@sysconfdir@/onak.conf\" \
-DDBFUNCS=keydb_@DBTYPE@_funcs -c onak-conf.c
+# HACK: onak-conf.o needs to be able to see keydb_@DBTYPE@_funcs, but
+# keydctl doesn't want to link against the DB stuff. To be fixed more cleanly.
+keydctl.o: keydctl.c keyd.h
+ $(CC) $(CFLAGS) -DDBFUNCS=keydb_@DBTYPE@_funcs -c keydctl.c
+
onak-mail.pl: onak-mail.pl.in
sed 's:@CONFIG@:@sysconfdir@/onak.conf:g' < onak-mail.pl.in > onak-mail.pl
chmod +x onak-mail.pl
--- /dev/null
+.TH KEYD 8
+.SH NAME
+keydctl \- control an onak keyd instance
+.SH SYNOPSIS
+.PP
+.B keydctl
+[
+.B options
+]
+.B command
+.SH DESCRIPTION
+.PP
+keydctl is a command line client for interacting with a backend keyd
+daemon. It's intended to perform functions that are specifically related
+to keyd rather than being generic keyserver functions. See
+.BR onak(1)
+for details about how to perform key related operations.
+.SS "Options"
+.TP
+\fB\-c \fIFILE\fR\fR
+Use \fIFILE\fR as the config file instead of the default.
+.TP
+\fB\-h\fR
+Display help text.
+.SS "Commands"
+.TP
+.B check
+Query if keyd is running and accepting commands. Returns 0 if it is, 1
+otherwise. Outputs nothing to stdout/stderr.
+.TP
+.B quit
+Request that keyd exits cleanly
+.TP
+.B status
+Display the status of a running keyd. Currently just the protocol version
+in use.
+.SH FILES
+.br
+.nf
+.\" set tabstop to longest possible filename, plus a wee bit
+.ta \w'/usr/lib/perl/getopts.pl 'u
+\fI/etc/onak.conf\fR default configuration file
+.SH NOTES
+This man page could probably do with some more details.
+.SH "SEE ALSO"
+.BR onak (1)
+.BR keyd (8)
+.SH AUTHOR
+onak was written by Jonathan McDowell <noodles@earth.li>. It can be found at
+http://www.earth.li/projectpurple/progs/onak.html
--- /dev/null
+/*
+ * 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);
+}