commit 27909800761a0019372616370f509848b663d62c from: Omar Polo date: Mon Jul 26 13:55:58 2021 UTC add kamirepl, a REPL to test kamid commit - 12c6d6991404ee5238bf52eef88e8623b4f8df03 commit + 27909800761a0019372616370f509848b663d62c blob - 3e6292ab62fb7a49a707cf7e5bcf9b3773e9ed81 blob + 4b0c14a89e4c08b52e8cc46e84e630c7131c8e6e --- .gitignore +++ .gitignore @@ -19,6 +19,7 @@ compile_flags.txt kamid kamictl +kamirepl *.o kamid.conf parse.c blob - a62fa713572c3f6848ec2c947af2e18c77deb27f blob + 80549f9ad50bcf16423cd4fed6df6a012d7579a2 --- Makefile.am +++ Makefile.am @@ -1,4 +1,4 @@ -bin_PROGRAMS = kamictl kamid kamirelp +bin_PROGRAMS = kamictl kamid kamirepl EXTRA_kamid_SOURCES = compat/ohash.h compat/imsg.h compat/queue.h compat/tree.h @@ -32,6 +32,13 @@ kamid_SOURCES = client.c \ utils.c \ utils.h +kamirepl_SOURCES = kamid.h \ + kamirepl.c \ + log.c \ + log.h \ + utils.c \ + utils.h + LDADD = $(LIBOBJS) BUILT_SOURCES = compile_flags.txt blob - /dev/null blob + 6040d3bad633ffc70266f8930e7479fbfa8c66e1 (mode 644) --- /dev/null +++ kamirepl.c @@ -0,0 +1,456 @@ +/* + * Copyright (c) 2021 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "compat.h" + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kamid.h" +#include "log.h" +#include "utils.h" + +#define PROMPT "> " + +/* flags */ +int verbose; +int tls; +const char *keypath; +const char *crtpath; +const char *host; +const char *port; + +/* state */ +struct tls_config *tlsconf; +struct tls *ctx; + +static void ATTR_DEAD usage(int); + +static void sig_handler(int, short, void *); + +static int openconn(void); + +static void tls_readcb(int, short, void *); +static void tls_writecb(int, short, void *); + +static void client_read(struct bufferevent *, void *); +static void client_write(struct bufferevent *, void *); +static void client_error(struct bufferevent *, short, void *); + +static void readcmd(int, short, void *); + +static void handle_9p(const void *, size_t); +static void prompt(void); + +static void ATTR_DEAD +usage(int ret) +{ + fprintf(stderr, + "usage: %s [-chv] [-C crt] [-K key] [-H host] [-P port]\n", + getprogname()); + fprintf(stderr, PACKAGE_NAME " suite version " PACKAGE_VERSION "\n"); + exit(ret); +} + +static void +sig_handler(int sig, short event, void *d) +{ + /* + * Normal signal handler rules don't apply because libevent + * decouples for us. + */ + + switch (sig) { + case SIGINT: + case SIGTERM: + log_warnx("\rShutting down..."); + event_loopbreak(); + return; + default: + fatalx("unexpected signal %d", sig); + } +} + +static int +openconn(void) +{ + struct addrinfo hints, *res, *res0; + int error; + int save_errno; + int s; + const char *cause = NULL; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + if ((error = getaddrinfo(host, port, &hints, &res0))) { + warnx("%s", gai_strerror(error)); + return -1; + } + + s = -1; + for (res = res0; res; res = res->ai_next) { + s = socket(res->ai_family, res->ai_socktype, + res->ai_protocol); + if (s == -1) { + cause = "socket"; + continue; + } + + if (connect(s, res->ai_addr, res->ai_addrlen) == -1) { + cause = "connect"; + save_errno = errno; + close(s); + errno = save_errno; + s = -1; + continue; + } + + break; + } + + freeaddrinfo(res0); + + if (s == -1) + warn("%s", cause); + + return s; +} + +static void +tls_readcb(int fd, short event, void *d) +{ + struct bufferevent *bufev = d; + char buf[IBUF_READ_SIZE]; + int what = EVBUFFER_READ; + int howmuch = IBUF_READ_SIZE; + ssize_t ret; + size_t len; + + if (event == EV_TIMEOUT) { + what |= EVBUFFER_TIMEOUT; + goto err; + } + + if (bufev->wm_read.high != 0) + howmuch = MIN(sizeof(buf), bufev->wm_read.high); + + switch (ret = tls_read(ctx, buf, howmuch)) { + case TLS_WANT_POLLIN: + case TLS_WANT_POLLOUT: + goto retry; + case -1: + what |= EVBUFFER_ERROR; + goto err; + } + len = ret; + + if (len == 0) { + what |= EVBUFFER_EOF; + goto err; + } + + if (evbuffer_add(bufev->input, buf, len) == -1) { + what |= EVBUFFER_ERROR; + goto err; + } + + event_add(&bufev->ev_read, NULL); + + len = EVBUFFER_LENGTH(bufev->input); + if (bufev->wm_read.low != 0 && len < bufev->wm_read.low) + return; + if (bufev->readcb != NULL) + (*bufev->readcb)(bufev, bufev->cbarg); + return; + +retry: + event_add(&bufev->ev_read, NULL); + return; + +err: + (*bufev->errorcb)(bufev, what, bufev->cbarg); +} + +static void +tls_writecb(int fd, short event, void *d) +{ + struct bufferevent *bufev = d; + ssize_t ret; + short what = EVBUFFER_WRITE; + + if (event == EV_TIMEOUT) { + what |= EVBUFFER_TIMEOUT; + goto err; + } + + if (EVBUFFER_LENGTH(bufev->output) != 0) { + ret = tls_write(ctx, + EVBUFFER_DATA(bufev->output), + EVBUFFER_LENGTH(bufev->output)); + switch (ret) { + case TLS_WANT_POLLIN: + case TLS_WANT_POLLOUT: + goto retry; + case -1: + what |= EVBUFFER_ERROR; + goto err; + } + evbuffer_drain(bufev->output, ret); + } + + if (EVBUFFER_LENGTH(bufev->output) != 0) + event_add(&bufev->ev_write, NULL); + + if (bufev->writecb != NULL && + EVBUFFER_LENGTH(bufev->output) <= bufev->wm_write.low) + (*bufev->writecb)(bufev, bufev->cbarg); + return; + +retry: + event_add(&bufev->ev_write, NULL); + return; +err: + (*bufev->errorcb)(bufev, what, bufev->cbarg); +} + +static void +client_read(struct bufferevent *bev, void *data) +{ + struct evbuffer *src = EVBUFFER_INPUT(bev); + uint32_t len; + + for (;;) { + if (EVBUFFER_LENGTH(src) < 4) + return; + + memcpy(&len, EVBUFFER_DATA(src), sizeof(len)); + len = le32toh(len); + + if (len > EVBUFFER_LENGTH(src)) + return; + + handle_9p(EVBUFFER_DATA(src), len); + evbuffer_drain(src, len); + } +} + +static void +client_write(struct bufferevent *bev, void *data) +{ + return; /* nothing to do */ +} + +static void +client_error(struct bufferevent *bev, short err, void *data) +{ + if (err & EVBUFFER_ERROR) + fatal("buffer event error"); + + if (err & EVBUFFER_EOF) { + printf("\r"); + log_info("EOF"); + event_loopbreak(); + return; + } + + printf("\r"); + log_warnx("unknown event error"); + event_loopbreak(); +} + +static void +readcmd(int fd, short event, void *data) +{ + char *line = NULL; + size_t linesize = 0; + ssize_t linelen; + + if ((linelen = getline(&line, &linesize, stdin)) != -1) { + line[linelen-1] = '\0'; + + printf("\r"); + log_info("TODO: parse `%s'", line); + prompt(); + } + + free(line); + if (ferror(stdin)) + fatal("getline"); +} + +static void +handle_9p(const void *data, size_t len) +{ + struct np_msg_header hdr; + + assert(len >= sizeof(hdr)); + memcpy(&hdr, data, sizeof(hdr)); + + hdr.len = le32toh(hdr.len); + /* type is one byte long, no endianness issues */ + hdr.tag = le16toh(hdr.tag); + + printf("\r"); + log_info("[%d] type=%s len=%"PRIu32, hdr.tag, pp_msg_type(hdr.type), + hdr.len); + prompt(); +} + +static void +prompt(void) +{ + printf("%s", PROMPT); + fflush(stdout); +} + +int +main(int argc, char **argv) +{ + int ch, sock, handshake; + struct bufferevent *bev; + struct event inev, ev_sigint, ev_sigterm; + + signal(SIGPIPE, SIG_IGN); + + while ((ch = getopt(argc, argv, "C:cH:hK:P:v")) != -1) { + switch (ch) { + case 'C': + crtpath = optarg; + break; + case 'c': + tls = 1; + break; + case 'H': + host = optarg; + break; + case 'h': + usage(0); + break; + case 'K': + keypath = optarg; + break; + case 'P': + port = optarg; + break; + case 'v': + verbose = 1; + break; + default: + usage(1); + } + } + + if (host == NULL) + host = "localhost"; + if (port == NULL) + port = "1337"; + + argc -= optind; + argv += optind; + + if (argc != 0) + usage(1); + /* if (!tls || (crtpath != NULL || keypath != NULL)) */ + /* usage(1); */ + if (!tls) + errx(1, "must enable tls (for now)"); + + log_init(1, LOG_DAEMON); + log_setverbose(verbose); + log_procinit(getprogname()); + + if ((tlsconf = tls_config_new()) == NULL) + fatalx("tls_config_new"); + tls_config_insecure_noverifycert(tlsconf); + tls_config_insecure_noverifyname(tlsconf); + if (tls_config_set_keypair_file(tlsconf, crtpath, keypath) == -1) + fatalx("can't load certs (%s, %s)", crtpath, keypath); + + if ((ctx = tls_client()) == NULL) + fatal("tls_client"); + if (tls_configure(ctx, tlsconf) == -1) + fatalx("tls_configure: %s", tls_error(ctx)); + + log_info("connecting to %s:%s...", host, port); + + if ((sock = openconn()) == -1) + fatalx("can't connect to %s:%s", host, port); + + if (tls_connect_socket(ctx, sock, host) == -1) + fatalx("tls_connect_socket: %s", tls_error(ctx)); + + for (handshake = 0; !handshake;) { + switch (tls_handshake(ctx)) { + case -1: + fatalx("tls_handshake: %s", tls_error(ctx)); + case 0: + handshake = 1; + break; + } + } + + log_info("connected!"); + + event_init(); + + signal_set(&ev_sigint, SIGINT, sig_handler, NULL); + signal_set(&ev_sigterm, SIGINT, sig_handler, NULL); + + signal_add(&ev_sigint, NULL); + signal_add(&ev_sigterm, NULL); + + bev = bufferevent_new(sock, client_read, client_write, client_error, + NULL); + if (bev == NULL) + fatal("bufferevent_new"); + + /* setup tls/io */ + event_set(&bev->ev_read, sock, EV_READ, tls_readcb, bev); + event_set(&bev->ev_write, sock, EV_WRITE, tls_writecb, bev); + + bufferevent_setwatermark(bev, EV_READ|EV_WRITE, + sizeof(struct np_msg_header), 0); + bufferevent_enable(bev, EV_READ|EV_WRITE); + + event_set(&inev, 0, EV_READ, readcmd, NULL); + event_add(&inev, NULL); + + prompt(); + event_dispatch(); + + bufferevent_free(bev); + tls_free(ctx); + tls_config_free(tlsconf); + close(sock); + + return 0; +}