#include #include #include #include "client.h" #include "util.h" #include "version.h" #include "yyjson.h" static void usage(void) { fprintf(stderr, "usage: bokfctl [options] [args-json]\n" "\n" "options:\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" " --org ID active org for this request\n" " --version\n" "\n" "examples:\n" " bokfctl health\n" " bokfctl org.create '{\"name\":\"AB Ett\"}'\n" " 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"); if (!target) target = "/run/bokfd/bokfd.sock"; const char *user = getenv("BOKFD_USER"); const char *password = getenv("BOKFD_PASSWORD"); int64_t org = 0; const char *cmd = NULL; const char *args_json = NULL; for (int i = 1; i < argc; i++) { const char *a = argv[i]; const char *val = NULL; if (!strncmp(a, "--socket", 8) && (a[8] == '\0' || a[8] == '=')) { val = a[8] == '=' ? a + 9 : (i + 1 < argc ? argv[++i] : NULL); if (!val) { fprintf(stderr, "bokfctl: --socket requires a value\n"); return 2; } target = val; } else if (!strncmp(a, "--user", 6) && (a[6] == '\0' || a[6] == '=')) { val = a[6] == '=' ? a + 7 : (i + 1 < argc ? argv[++i] : NULL); if (!val) { fprintf(stderr, "bokfctl: --user requires a value\n"); return 2; } user = val; } else if (!strncmp(a, "--password", 10) && (a[10] == '\0' || a[10] == '=')) { val = a[10] == '=' ? a + 11 : (i + 1 < argc ? argv[++i] : NULL); if (!val) { fprintf(stderr, "bokfctl: --password requires a value\n"); return 2; } password = val; } else if (!strncmp(a, "--org", 5) && (a[5] == '\0' || a[5] == '=')) { val = a[5] == '=' ? a + 6 : (i + 1 < argc ? argv[++i] : NULL); if (!val) { fprintf(stderr, "bokfctl: --org requires a value\n"); return 2; } org = strtoll(val, NULL, 10); } else if (strcmp(a, "--version") == 0) { printf("bokfctl %s\n", BOKF_VERSION); return 0; } else if (strcmp(a, "--help") == 0 || strcmp(a, "-h") == 0) { usage(); return 0; } else if (a[0] == '-' && a[1] != '\0') { fprintf(stderr, "bokfctl: unknown option %s\n", a); usage(); return 2; } else if (!cmd) { cmd = a; } else if (!args_json) { args_json = a; } else { fprintf(stderr, "bokfctl: unexpected argument %s\n", a); return 2; } } if (!cmd) { usage(); return 2; } struct client_conn conn; if (client_connect(target, &conn) != 0) { fprintf(stderr, "bokfctl: cannot connect to %s: %s\n", target, client_last_error()); return 2; } 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"); client_close(&conn); return 2; } 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; } } char *req = NULL; if (strcmp(cmd, "raw") == 0) { if (!args_json) { fprintf(stderr, "bokfctl: raw requires a full request JSON\n"); client_close(&conn); return 2; } req = xstrdup(args_json); } else { req = client_make_request(cmd, session, org, args_json, "cli"); if (!req) { fprintf(stderr, "bokfctl: args must be a JSON object\n"); free(session); client_close(&conn); return 2; } } free(session); if (client_send_line(&conn, req) != 0) { fprintf(stderr, "bokfctl: send failed: %s\n", client_last_error()); free(req); client_close(&conn); return 2; } free(req); char *resp = client_read_line(&conn); client_close(&conn); if (!resp) { fprintf(stderr, "bokfctl: no response: %s\n", client_last_error()); return 2; } yyjson_doc *rd = yyjson_read(resp, strlen(resp), 0); char *pretty = rd ? yyjson_write(rd, YYJSON_WRITE_PRETTY, NULL) : NULL; printf("%s\n", pretty ? pretty : resp); int ok = rd && yyjson_is_obj(yyjson_doc_get_root(rd)) && yyjson_get_bool(yyjson_obj_get(yyjson_doc_get_root(rd), "ok")); if (rd) yyjson_doc_free(rd); free(pretty); free(resp); return ok ? 0 : 1; }