diff --git a/daemon/x52d_command.c b/daemon/x52d_command.c index 10a41d9..43d3911 100644 --- a/daemon/x52d_command.c +++ b/daemon/x52d_command.c @@ -7,16 +7,21 @@ */ #include "config.h" +#include #include +#include #include #include +#include #include #include +#include #include #include "pinelog.h" #include "x52d_const.h" #include "x52d_command.h" +#include "x52d_config.h" #define MAX_CONN (X52D_MAX_CLIENTS + 1) @@ -119,6 +124,182 @@ static int poll_clients(int sock_fd, struct pollfd *pfd) return rc; } +#if defined __has_attribute +# if __has_attribute(format) + __attribute((format(printf, 4, 5))) +# endif +#endif +static void response_formatted(char *buffer, int *buflen, const char *type, + const char *fmt, ...) +{ + va_list ap; + char response[1024]; + int resplen; + int typelen; + + typelen = strlen(type) + 1; + strcpy(response + 2, type); + resplen = typelen + 2; + + if (*fmt) { + va_start(ap, fmt); + resplen += vsnprintf(response + typelen, sizeof(response) - typelen, fmt, ap); + va_end(ap); + } + + typelen = htons(resplen); + memcpy(response, &typelen, 2); + memcpy(buffer, response, resplen); + *buflen = resplen; +} + +static void response_strings(char *buffer, int *buflen, const char *type, int count, ...) +{ + va_list ap; + char response[1024]; + int resplen; + int arglen; + int i; + char *arg; + + arglen = strlen(type) + 1; + strcpy(response + 2, type); + resplen = arglen + 2; + + va_start(ap, count); + for (i = 0; i < count; i++) { + arg = va_arg(ap, char *); + arglen = strlen(arg) + 1; + if ((size_t)(arglen + resplen) >= sizeof(response)) { + PINELOG_ERROR("Too many arguments for response_strings %s", type); + break; + } + + strcpy(response + resplen, arg); + resplen += arglen; + } + va_end(ap); + + arglen = htons(resplen); + memcpy(response, &arglen, 2); + memcpy(buffer, response, resplen); + *buflen = resplen; +} + +#define NUMARGS(...) (sizeof((const char *[]){__VA_ARGS__}) / sizeof(const char *)) +#define ERR(...) response_strings(buffer, buflen, "ERR", NUMARGS(__VA_ARGS__), ##__VA_ARGS__) +#define ERR_fmt(fmt, ...) response_formatted(buffer, buflen, "ERR", fmt, ##__VA_ARGS__) + +#define OK(...) response_strings(buffer, buflen, "OK", NUMARGS(__VA_ARGS__), ##__VA_ARGS__) +#define OK_fmt(fmt, ...) response_formatted(buffer, buflen, "OK", fmt, ##__VA_ARGS__) + +#define MATCH(idx, cmd) if (strcasecmp(argv[idx], cmd) == 0) + +static bool check_file(const char *file_path, int mode) +{ + if (*file_path == '\0') { + return false; + } + + if (mode && access(file_path, mode)) { + return false; + } + + return true; +} + +static void cmd_config(char *buffer, int *buflen, int argc, char **argv) +{ + if (argc < 2) { + ERR("Insufficient arguments for 'config' command"); + return; + } + + MATCH(1, "load") { + if (argc == 3) { + if (!check_file(argv[2], R_OK)) { + ERR_fmt("Invalid file '%s' for 'config load' command", argv[2]); + return; + } + + x52d_config_load(argv[2]); + x52d_config_apply(); + OK("load", argv[2]); + } else { + // Invalid number of args + ERR_fmt("Unexpected arguments for 'config load' command; got %d, expected 3", argc); + } + return; + } + + MATCH(1, "reload") { + if (argc == 2) { + raise(SIGHUP); + OK("reload"); + } else { + ERR_fmt("Unexpected arguments for 'config reload' command; got %d, expected 2", argc); + } + return; + } + + MATCH(1, "dump") { + if (argc == 3) { + if (!check_file(argv[2], 0)) { + ERR_fmt("Invalid file '%s' for 'config dump' command", argv[2]); + return; + } + + x52d_config_save(argv[2]); + OK("dump", argv[2]); + } else { + ERR_fmt("Unexpected arguments for 'config dump' command; got %d, expected 3", argc); + } + + return; + } + + MATCH(1, "save") { + if (argc == 2) { + raise(SIGUSR1); + OK("save"); + } else { + ERR_fmt("Unexpected arguments for 'config save' command; got %d, expected 2", argc); + } + return; + } + + ERR_fmt("Unknown subcommand '%s' for 'config' command", argv[1]); +} + +static void command_parser(char *buffer, int *buflen) +{ + int argc = 0; + char *argv[512] = { 0 }; + int i = 0; + + while (i < *buflen) { + if (buffer[i]) { + argv[argc] = buffer + i; + argc++; + for (; i < *buflen && buffer[i]; i++); + // At this point, buffer[i] = '\0' + // Skip to the next character. + i++; + } else { + // We should never reach here, unless we have two NULs in a row + argv[argc] = buffer + i; + argc++; + i++; + } + } + + MATCH(0, "config") { + cmd_config(buffer, buflen, argc, argv); + } else { + ERR("Unknown command '%s'", argv[0]); + } +} + int x52d_command_loop(int sock_fd) { struct pollfd pfd[MAX_CONN]; @@ -141,7 +322,7 @@ int x52d_command_loop(int sock_fd) if (pfd[i].fd == sock_fd) { register_client(sock_fd); } else { - char buffer[1024]; + char buffer[1024] = { 0 }; int sent; rc = recv(pfd[i].fd, buffer, sizeof(buffer), 0); @@ -151,8 +332,10 @@ int x52d_command_loop(int sock_fd) continue; } - // TODO: Parse and handle command. - // Echo it back to the client for now. + // Parse and handle command. + command_parser(buffer, &rc); + + PINELOG_TRACE("Sending %d bytes in response '%s'", rc, buffer); sent = send(pfd[i].fd, buffer, rc, 0); if (sent != rc) { PINELOG_ERROR(_("Short write to client %d; expected %d bytes, wrote %d bytes"), diff --git a/po/libx52.pot b/po/libx52.pot index 5f0090b..ebf1b89 100644 --- a/po/libx52.pot +++ b/po/libx52.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: libx52 0.2.3\n" "Report-Msgid-Bugs-To: https://github.com/nirenjan/libx52/issues\n" -"POT-Creation-Date: 2021-11-04 22:04-0700\n" +"POT-Creation-Date: 2021-11-05 15:09-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -659,31 +659,31 @@ msgstr "" msgid "Shutting down X52 clock manager thread" msgstr "" -#: daemon/x52d_command.c:48 +#: daemon/x52d_command.c:53 #, c-format msgid "Error accepting client connection on command socket: %s" msgstr "" -#: daemon/x52d_command.c:85 +#: daemon/x52d_command.c:90 #, c-format msgid "Error when polling command socket: FD %d, error %d, len %lu" msgstr "" -#: daemon/x52d_command.c:112 +#: daemon/x52d_command.c:117 #, c-format msgid "Error when polling for command: %s" msgstr "" -#: daemon/x52d_command.c:116 +#: daemon/x52d_command.c:121 msgid "Timed out when polling for command" msgstr "" -#: daemon/x52d_command.c:149 +#: daemon/x52d_command.c:330 #, c-format msgid "Error reading from client %d: %s" msgstr "" -#: daemon/x52d_command.c:158 +#: daemon/x52d_command.c:341 #, c-format msgid "Short write to client %d; expected %d bytes, wrote %d bytes" msgstr "" diff --git a/po/xx_PL.po b/po/xx_PL.po index e70e1f0..3091c92 100644 --- a/po/xx_PL.po +++ b/po/xx_PL.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: libx52 0.2.3\n" "Report-Msgid-Bugs-To: https://github.com/nirenjan/libx52/issues\n" -"POT-Creation-Date: 2021-11-04 22:04-0700\n" +"POT-Creation-Date: 2021-11-05 15:09-0700\n" "PO-Revision-Date: 2021-11-04 15:35-0700\n" "Last-Translator: Nirenjan Krishnan \n" "Language-Team: Dummy Language for testing i18n\n" @@ -709,32 +709,32 @@ msgstr "Erroray %d initializingay ockclay eadthray: %s" msgid "Shutting down X52 clock manager thread" msgstr "Uttingshay ownday X52 ockclay anagermay eadthray" -#: daemon/x52d_command.c:48 +#: daemon/x52d_command.c:53 #, c-format msgid "Error accepting client connection on command socket: %s" msgstr "Erroray acceptingay ientclay onnectioncay onay ommandcay ocketsay: %s" -#: daemon/x52d_command.c:85 +#: daemon/x52d_command.c:90 #, c-format msgid "Error when polling command socket: FD %d, error %d, len %lu" msgstr "" "Erroray enwhay ollingpay ommandcay ocketsay: FDay %d, erroray %d, enlay %lu" -#: daemon/x52d_command.c:112 +#: daemon/x52d_command.c:117 #, c-format msgid "Error when polling for command: %s" msgstr "Erroray enwhay ollingpay orfay ommandcay: %s" -#: daemon/x52d_command.c:116 +#: daemon/x52d_command.c:121 msgid "Timed out when polling for command" msgstr "Imedtay outay enwhay ollingpay orfay ommandcay" -#: daemon/x52d_command.c:149 +#: daemon/x52d_command.c:330 #, c-format msgid "Error reading from client %d: %s" msgstr "Erroray eadingray omfray ientclay %d: %s" -#: daemon/x52d_command.c:158 +#: daemon/x52d_command.c:341 #, c-format msgid "Short write to client %d; expected %d bytes, wrote %d bytes" msgstr ""