summaryrefslogtreecommitdiff
path: root/clients/bokfctl.c
diff options
context:
space:
mode:
Diffstat (limited to 'clients/bokfctl.c')
-rw-r--r--clients/bokfctl.c236
1 files changed, 32 insertions, 204 deletions
diff --git a/clients/bokfctl.c b/clients/bokfctl.c
index c541fec..1f1372c 100644
--- a/clients/bokfctl.c
+++ b/clients/bokfctl.c
@@ -1,168 +1,19 @@
-#include <errno.h>
-#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <unistd.h>
+#include "client.h"
#include "util.h"
#include "version.h"
#include "yyjson.h"
-static ssize_t write_all(int fd, const char *buf, size_t len)
-{
- size_t off = 0;
- while (off < len) {
- ssize_t w = write(fd, buf + off, len - off);
- if (w < 0) {
- if (errno == EINTR)
- continue;
- return -1;
- }
- off += (size_t)w;
- }
- return (ssize_t)off;
-}
-
-static char *read_line_fd(int fd)
-{
- struct buf b;
- buf_init(&b);
- char chunk[4096];
- for (;;) {
- ssize_t r = read(fd, chunk, sizeof chunk);
- if (r < 0) {
- if (errno == EINTR)
- continue;
- buf_free(&b);
- return NULL;
- }
- if (r == 0)
- break;
- unsigned char *nl = memchr(chunk, '\n', (size_t)r);
- if (nl) {
- buf_append(&b, chunk, (size_t)(nl - (unsigned char *)chunk));
- break;
- }
- buf_append(&b, chunk, (size_t)r);
- }
- char *out = xmalloc(b.len + 1);
- memcpy(out, b.p ? (char *)b.p : "", b.len);
- out[b.len] = '\0';
- buf_free(&b);
- return out;
-}
-
-static int tcp_connect_addr(const char *addrport)
-{
- char host[256] = "127.0.0.1";
- char port[16] = "8787";
- const char *colon = strrchr(addrport, ':');
- if (colon) {
- size_t hl = (size_t)(colon - addrport);
- if (hl < sizeof host) {
- memcpy(host, addrport, hl);
- host[hl] = '\0';
- }
- snprintf(port, sizeof port, "%s", colon + 1);
- } else {
- snprintf(port, sizeof port, "%s", addrport);
- }
- struct addrinfo hints, *res = NULL;
- memset(&hints, 0, sizeof hints);
- hints.ai_family = AF_UNSPEC;
- hints.ai_socktype = SOCK_STREAM;
- if (getaddrinfo(host, port, &hints, &res) != 0)
- return -1;
- int fd = -1;
- for (struct addrinfo *ai = res; ai; ai = ai->ai_next) {
- fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
- if (fd < 0)
- continue;
- if (connect(fd, ai->ai_addr, ai->ai_addrlen) == 0)
- break;
- close(fd);
- fd = -1;
- }
- freeaddrinfo(res);
- return fd;
-}
-
-static int connect_target(const char *target)
-{
- if (strncmp(target, "tcp:", 4) == 0)
- return tcp_connect_addr(target + 4);
- struct sockaddr_un sa;
- memset(&sa, 0, sizeof sa);
- sa.sun_family = AF_UNIX;
- if (strlen(target) >= sizeof sa.sun_path) {
- errno = ENAMETOOLONG;
- return -1;
- }
- snprintf(sa.sun_path, sizeof sa.sun_path, "%s", target);
- int fd = socket(AF_UNIX, SOCK_STREAM, 0);
- if (fd < 0)
- return -1;
- if (connect(fd, (struct sockaddr *)&sa, sizeof sa) != 0) {
- close(fd);
- return -1;
- }
- return fd;
-}
-
-static char *make_request(const char *cmd, const char *session, int64_t org,
- const char *args_json, const char *id)
-{
- yyjson_doc *adoc = NULL;
- if (args_json) {
- adoc = yyjson_read(args_json, strlen(args_json), 0);
- if (!adoc || !yyjson_is_obj(yyjson_doc_get_root(adoc))) {
- yyjson_doc_free(adoc);
- return NULL;
- }
- }
- yyjson_mut_doc *d = yyjson_mut_doc_new(NULL);
- yyjson_mut_val *o = yyjson_mut_obj(d);
- yyjson_mut_doc_set_root(d, o);
- yyjson_mut_obj_add_int(d, o, "v", 1);
- yyjson_mut_obj_add_str(d, o, "id", id ? id : "cli");
- yyjson_mut_obj_add_str(d, o, "cmd", cmd);
- if (session)
- yyjson_mut_obj_add_str(d, o, "session", session);
- if (org > 0)
- yyjson_mut_obj_add_int(d, o, "org", org);
- if (adoc) {
- yyjson_mut_val *args = yyjson_val_mut_copy(d, yyjson_doc_get_root(adoc));
- yyjson_mut_obj_add_val(d, o, "args", args);
- yyjson_doc_free(adoc);
- }
- char *s = yyjson_mut_write(d, 0, NULL);
- yyjson_mut_doc_free(d);
- return s;
-}
-
-static char *make_login_args(const char *user, const char *password)
-{
- yyjson_mut_doc *d = yyjson_mut_doc_new(NULL);
- yyjson_mut_val *o = yyjson_mut_obj(d);
- yyjson_mut_doc_set_root(d, o);
- yyjson_mut_obj_add_str(d, o, "method", "password");
- yyjson_mut_obj_add_str(d, o, "username", user);
- yyjson_mut_obj_add_str(d, o, "password", password);
- char *s = yyjson_mut_write(d, 0, NULL);
- yyjson_mut_doc_free(d);
- return s;
-}
-
static void usage(void)
{
fprintf(stderr,
"usage: bokfctl [options] <cmd> [args-json]\n"
"\n"
"options:\n"
- " --socket TARGET unix socket path or tcp:host:port\n"
+ " --socket TARGET unix path, tcp:host:port or tls:host:port\n"
" (env BOKFD_SOCKET, default /run/bokfd/bokfd.sock)\n"
" --user NAME login user (env BOKFD_USER)\n"
" --password PW login password (env BOKFD_PASSWORD)\n"
@@ -175,6 +26,12 @@ static void usage(void)
" bokfctl describe\n");
}
+static int is_local_cmd(const char *cmd)
+{
+ return strcmp(cmd, "health") == 0 || strcmp(cmd, "meta") == 0 ||
+ strcmp(cmd, "session.open") == 0;
+}
+
int main(int argc, char **argv)
{
const char *target = getenv("BOKFD_SOCKET");
@@ -245,90 +102,61 @@ int main(int argc, char **argv)
return 2;
}
- int fd = connect_target(target);
- if (fd < 0) {
+ struct client_conn conn;
+ if (client_connect(target, &conn) != 0) {
fprintf(stderr, "bokfctl: cannot connect to %s: %s\n", target,
- strerror(errno));
+ client_last_error());
return 2;
}
- char session[128] = "";
- if (strcmp(cmd, "health") != 0 && strcmp(cmd, "meta") != 0 &&
- strcmp(cmd, "session.open") != 0) {
+ char *session = NULL;
+ if (!is_local_cmd(cmd)) {
if (!user || !password) {
fprintf(stderr,
"bokfctl: set BOKFD_USER and BOKFD_PASSWORD (or --user/--password) to log in\n");
- close(fd);
+ client_close(&conn);
return 2;
}
- char *largs = make_login_args(user, password);
- char *lreq = make_request("session.open", NULL, 0, largs, "login");
- free(largs);
- if (!lreq || write_all(fd, lreq, strlen(lreq)) < 0 ||
- write_all(fd, "\n", 1) < 0) {
- fprintf(stderr, "bokfctl: send failed\n");
- free(lreq);
- close(fd);
- return 2;
- }
- free(lreq);
- char *lresp = read_line_fd(fd);
- if (!lresp) {
- fprintf(stderr, "bokfctl: no response\n");
- close(fd);
- return 2;
- }
- yyjson_doc *ld = yyjson_read(lresp, strlen(lresp), 0);
- int ok = ld && yyjson_is_obj(yyjson_doc_get_root(ld)) &&
- yyjson_get_bool(yyjson_obj_get(yyjson_doc_get_root(ld), "ok"));
- const char *sid = NULL;
- if (ok) {
- yyjson_val *r = yyjson_obj_get(yyjson_doc_get_root(ld), "result");
- yyjson_val *s = r ? yyjson_obj_get(r, "session") : NULL;
- if (s && yyjson_is_str(s))
- sid = yyjson_get_str(s);
- }
- if (!ok || !sid) {
- fprintf(stderr, "%s\n", lresp);
- yyjson_doc_free(ld);
- free(lresp);
- close(fd);
- return 1;
+ char *lerr = NULL;
+ if (client_login(&conn, user, password, &session, &lerr) != 0) {
+ fprintf(stderr, "%s\n", lerr ? lerr : "login failed");
+ int rc = lerr && lerr[0] == '{' ? 1 : 2;
+ free(lerr);
+ client_close(&conn);
+ return rc;
}
- snprintf(session, sizeof session, "%s", sid);
- yyjson_doc_free(ld);
- free(lresp);
}
char *req = NULL;
if (strcmp(cmd, "raw") == 0) {
if (!args_json) {
fprintf(stderr, "bokfctl: raw requires a full request JSON\n");
- close(fd);
+ client_close(&conn);
return 2;
}
req = xstrdup(args_json);
} else {
- req = make_request(cmd, session[0] ? session : NULL, org, args_json,
- "cli");
+ req = client_make_request(cmd, session, org, args_json, "cli");
if (!req) {
fprintf(stderr, "bokfctl: args must be a JSON object\n");
- close(fd);
+ free(session);
+ client_close(&conn);
return 2;
}
}
- if (write_all(fd, req, strlen(req)) < 0 || write_all(fd, "\n", 1) < 0) {
- fprintf(stderr, "bokfctl: send failed\n");
+ free(session);
+ if (client_send_line(&conn, req) != 0) {
+ fprintf(stderr, "bokfctl: send failed: %s\n", client_last_error());
free(req);
- close(fd);
+ client_close(&conn);
return 2;
}
free(req);
- char *resp = read_line_fd(fd);
- close(fd);
+ char *resp = client_read_line(&conn);
+ client_close(&conn);
if (!resp) {
- fprintf(stderr, "bokfctl: no response\n");
+ fprintf(stderr, "bokfctl: no response: %s\n", client_last_error());
return 2;
}
yyjson_doc *rd = yyjson_read(resp, strlen(resp), 0);