diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 5e6552b..1184c19 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -15,6 +15,7 @@ x52d_SOURCES = \ daemon/x52d_clock.c \ daemon/x52d_mouse.c \ daemon/x52d_led.c \ + daemon/x52d_command.c \ daemon/x52d_comm_internal.c x52d_CFLAGS = \ @@ -88,6 +89,7 @@ EXTRA_DIST += \ daemon/x52d_device.h \ daemon/x52d_io.h \ daemon/x52d_mouse.h \ + daemon/x52d_command.h \ daemon/x52dcomm.h \ daemon/x52dcomm-internal.h \ daemon/x52d.conf diff --git a/daemon/x52d_command.c b/daemon/x52d_command.c new file mode 100644 index 0000000..10a41d9 --- /dev/null +++ b/daemon/x52d_command.c @@ -0,0 +1,166 @@ +/* + * Saitek X52 Pro MFD & LED driver - Command processor + * + * Copyright (C) 2021 Nirenjan Krishnan (nirenjan@nirenjan.org) + * + * SPDX-License-Identifier: GPL-2.0-only WITH Classpath-exception-2.0 + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include + +#include "pinelog.h" +#include "x52d_const.h" +#include "x52d_command.h" + +#define MAX_CONN (X52D_MAX_CLIENTS + 1) + +#define INVALID_CLIENT -1 + +static int client_fd[X52D_MAX_CLIENTS]; +static int active_clients; + +void x52d_command_init(void) +{ + for (int i = 0; i < X52D_MAX_CLIENTS; i++) { + client_fd[i] = INVALID_CLIENT; + } + + active_clients = 0; +} + +static void register_client(int sock_fd) +{ + int fd; + + if (active_clients >= X52D_MAX_CLIENTS) { + /* Ignore the incoming connection */ + return; + } + + fd = accept(sock_fd, NULL, NULL); + if (fd < 0) { + PINELOG_ERROR(_("Error accepting client connection on command socket: %s"), + strerror(errno)); + return; + } + + PINELOG_TRACE("Accepted client %d on command socket", fd); + + for (int i = 0; i < X52D_MAX_CLIENTS; i++) { + if (client_fd[i] == INVALID_CLIENT) { + client_fd[i] = fd; + active_clients++; + break; + } + } +} + +static void deregister_client(int fd) +{ + for (int i = 0; i < X52D_MAX_CLIENTS; i++) { + if (client_fd[i] == fd) { + client_fd[i] = INVALID_CLIENT; + active_clients--; + close(fd); + + PINELOG_TRACE("Disconnected client %d from command socket", fd); + break; + } + } + +} + +static void client_error(int fd) +{ + int error; + socklen_t errlen = sizeof(error); + + getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&error, &errlen); + PINELOG_ERROR(_("Error when polling command socket: FD %d, error %d, len %lu"), + fd, error, (unsigned long int)errlen); + deregister_client(fd); +} + +static int poll_clients(int sock_fd, struct pollfd *pfd) +{ + int pfd_count; + int rc; + + memset(pfd, 0, sizeof(*pfd) * MAX_CONN); + + pfd_count = 1; + pfd[0].fd = sock_fd; + pfd[0].events = POLLIN | POLLERR; + for (int i = 0; i < X52D_MAX_CLIENTS; i++) { + if (client_fd[i] != INVALID_CLIENT) { + pfd[pfd_count].fd = client_fd[i]; + pfd[pfd_count].events = POLLIN | POLLERR | POLLHUP; + pfd_count++; + } + } + + PINELOG_TRACE("Polling %d file descriptors", pfd_count); + rc = poll(pfd, pfd_count, -1); + if (rc < 0) { + if (errno != EINTR) { + PINELOG_ERROR(_("Error when polling for command: %s"), strerror(errno)); + return -1; + } + } else if (rc == 0) { + PINELOG_INFO(_("Timed out when polling for command")); + } + + return rc; +} + +int x52d_command_loop(int sock_fd) +{ + struct pollfd pfd[MAX_CONN]; + int rc; + int i; + + rc = poll_clients(sock_fd, pfd); + if (rc <= 0) { + return -1; + } + + for (i = 0; i < MAX_CONN; i++) { + if (pfd[i].revents & POLLHUP) { + /* Remote hungup */ + deregister_client(pfd[i].fd); + } else if (pfd[i].revents & POLLERR) { + /* Error reading from the socket */ + client_error(pfd[i].fd); + } else if (pfd[i].revents & POLLIN) { + if (pfd[i].fd == sock_fd) { + register_client(sock_fd); + } else { + char buffer[1024]; + int sent; + + rc = recv(pfd[i].fd, buffer, sizeof(buffer), 0); + if (rc < 0) { + PINELOG_ERROR(_("Error reading from client %d: %s"), + pfd[i].fd, strerror(errno)); + continue; + } + + // TODO: Parse and handle command. + // Echo it back to the client for now. + sent = send(pfd[i].fd, buffer, rc, 0); + if (sent != rc) { + PINELOG_ERROR(_("Short write to client %d; expected %d bytes, wrote %d bytes"), + pfd[i].fd, rc, sent); + } + } + } + } + + return 0; +} diff --git a/daemon/x52d_command.h b/daemon/x52d_command.h new file mode 100644 index 0000000..96513a2 --- /dev/null +++ b/daemon/x52d_command.h @@ -0,0 +1,15 @@ +/* + * Saitek X52 Pro MFD & LED driver - Command processor + * + * Copyright (C) 2021 Nirenjan Krishnan (nirenjan@nirenjan.org) + * + * SPDX-License-Identifier: GPL-2.0-only WITH Classpath-exception-2.0 + */ + +#ifndef X52D_COMMAND_H +#define X52D_COMMAND_H + +void x52d_command_init(void); +int x52d_command_loop(int sock_fd); + +#endif // !defined X52D_COMMAND_H diff --git a/daemon/x52d_main.c b/daemon/x52d_main.c index ff8d87c..fff3c51 100644 --- a/daemon/x52d_main.c +++ b/daemon/x52d_main.c @@ -23,6 +23,7 @@ #include "x52d_device.h" #include "x52d_io.h" #include "x52d_mouse.h" +#include "x52d_command.h" #include "x52dcomm-internal.h" #include "x52dcomm.h" #include "pinelog.h" @@ -356,6 +357,7 @@ int main(int argc, char **argv) // Start device threads x52d_dev_init(); x52d_clock_init(); + x52d_command_init(); #if defined(HAVE_EVDEV) x52d_io_init(); x52d_mouse_evdev_init(); @@ -371,9 +373,7 @@ int main(int argc, char **argv) flag_quit = 0; while(!flag_quit) { - // TODO: Replace with main event loop - // Let all threads run in background forever - sleep(600); + x52d_command_loop(command_sock_fd); /* Check if we need to reload configuration */ if (flag_reload) { diff --git a/po/POTFILES.in b/po/POTFILES.in index 3b7426c..1435f68 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -14,6 +14,7 @@ joytest/x52_test_mfd.c daemon/x52d_main.c daemon/x52d_clock.c +daemon/x52d_command.c daemon/x52d_config.c daemon/x52d_config_dump.c daemon/x52d_config_parser.c diff --git a/po/libx52.pot b/po/libx52.pot index 6cde6a2..92b03c2 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 13:37-0700\n" +"POT-Creation-Date: 2021-11-04 15:33-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -491,17 +491,17 @@ msgstr "" msgid "OK" msgstr "" -#: daemon/x52d_main.c:63 +#: daemon/x52d_main.c:64 #, c-format msgid "Error %d setting log file: %s\n" msgstr "" -#: daemon/x52d_main.c:79 +#: daemon/x52d_main.c:80 #, c-format msgid "Error %d installing handler for signal %d: %s" msgstr "" -#: daemon/x52d_main.c:90 +#: daemon/x52d_main.c:91 #, c-format msgid "" "Usage: %s [-f] [-v] [-q]\n" @@ -510,81 +510,81 @@ msgid "" "\t[-s command-socket-path]\n" msgstr "" -#: daemon/x52d_main.c:121 +#: daemon/x52d_main.c:122 #, c-format msgid "Daemon is already running as PID %u" msgstr "" -#: daemon/x52d_main.c:217 +#: daemon/x52d_main.c:218 #, c-format msgid "Error creating command socket: %s" msgstr "" -#: daemon/x52d_main.c:224 +#: daemon/x52d_main.c:225 #, c-format msgid "Error getting command socket flags: %s" msgstr "" -#: daemon/x52d_main.c:228 +#: daemon/x52d_main.c:229 #, c-format msgid "Error setting command socket flags: %s" msgstr "" -#: daemon/x52d_main.c:236 +#: daemon/x52d_main.c:237 #, c-format msgid "Error binding to command socket: %s" msgstr "" -#: daemon/x52d_main.c:241 +#: daemon/x52d_main.c:242 #, c-format msgid "Error listening on command socket: %s" msgstr "" -#: daemon/x52d_main.c:315 +#: daemon/x52d_main.c:316 #, c-format msgid "Unable to parse configuration override '%s'\n" msgstr "" -#: daemon/x52d_main.c:343 +#: daemon/x52d_main.c:344 #, c-format msgid "Foreground = %s" msgstr "" -#: daemon/x52d_main.c:343 daemon/x52d_main.c:344 +#: daemon/x52d_main.c:344 daemon/x52d_main.c:345 msgid "true" msgstr "" -#: daemon/x52d_main.c:343 daemon/x52d_main.c:344 +#: daemon/x52d_main.c:344 daemon/x52d_main.c:345 msgid "false" msgstr "" -#: daemon/x52d_main.c:344 -#, c-format -msgid "Quiet = %s" -msgstr "" - #: daemon/x52d_main.c:345 #, c-format -msgid "Verbosity = %d" +msgid "Quiet = %s" msgstr "" #: daemon/x52d_main.c:346 #, c-format -msgid "Log file = %s" +msgid "Verbosity = %d" msgstr "" #: daemon/x52d_main.c:347 #, c-format -msgid "Config file = %s" +msgid "Log file = %s" msgstr "" #: daemon/x52d_main.c:348 #, c-format -msgid "PID file = %s" +msgid "Config file = %s" msgstr "" #: daemon/x52d_main.c:349 #, c-format +msgid "PID file = %s" +msgstr "" + +#: daemon/x52d_main.c:350 +#, c-format msgid "Command socket = %s" msgstr "" @@ -659,6 +659,35 @@ msgstr "" msgid "Shutting down X52 clock manager thread" msgstr "" +#: daemon/x52d_command.c:48 +#, c-format +msgid "Error accepting client connection on command socket: %s" +msgstr "" + +#: daemon/x52d_command.c:85 +#, c-format +msgid "Error when polling command socket: FD %d, error %d, len %lu" +msgstr "" + +#: daemon/x52d_command.c:112 +#, c-format +msgid "Error when polling for command: %s" +msgstr "" + +#: daemon/x52d_command.c:116 +msgid "Timed out when polling for command" +msgstr "" + +#: daemon/x52d_command.c:149 +#, c-format +msgid "Error reading from client %d: %s" +msgstr "" + +#: daemon/x52d_command.c:158 +#, c-format +msgid "Short write to client %d; expected %d bytes, wrote %d bytes" +msgstr "" + #: daemon/x52d_config.c:26 #, c-format msgid "Error %d setting configuration defaults: %s" diff --git a/po/xx_PL.po b/po/xx_PL.po index cb5c026..57b8fa9 100644 --- a/po/xx_PL.po +++ b/po/xx_PL.po @@ -7,8 +7,8 @@ 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 13:37-0700\n" -"PO-Revision-Date: 2021-11-04 13:45-0700\n" +"POT-Creation-Date: 2021-11-04 15:33-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" "Language: xx_PL\n" @@ -534,17 +534,17 @@ msgstr "Estingtay aracterchay 0x%02x..." msgid "OK" msgstr "OKay" -#: daemon/x52d_main.c:63 +#: daemon/x52d_main.c:64 #, c-format msgid "Error %d setting log file: %s\n" msgstr "Erroray %d ettingsay oglay ilefay: %s\n" -#: daemon/x52d_main.c:79 +#: daemon/x52d_main.c:80 #, c-format msgid "Error %d installing handler for signal %d: %s" msgstr "Erroray %d installingay andlerhay orfay ignalsay %d: %s" -#: daemon/x52d_main.c:90 +#: daemon/x52d_main.c:91 #, c-format msgid "" "Usage: %s [-f] [-v] [-q]\n" @@ -557,80 +557,80 @@ msgstr "" "\t[-c onfigcay-ilefay] [-p idpay-ilefay]\n" "\t[-s ommandcay-ocketsay-athpay]\n" -#: daemon/x52d_main.c:121 +#: daemon/x52d_main.c:122 #, c-format msgid "Daemon is already running as PID %u" msgstr "Aemonday isay alreadyay unningray asay IDPay %u" -#: daemon/x52d_main.c:217 +#: daemon/x52d_main.c:218 #, c-format msgid "Error creating command socket: %s" msgstr "Erroray eatingcray ommandcay ocketsay: %s" -#: daemon/x52d_main.c:224 +#: daemon/x52d_main.c:225 #, c-format msgid "Error getting command socket flags: %s" msgstr "Erroray ettinggay ommandcay ocketsay agsflay: %s" -#: daemon/x52d_main.c:228 +#: daemon/x52d_main.c:229 #, c-format msgid "Error setting command socket flags: %s" msgstr "Erroray ettingsay ommandcay ocketsay agsflay: %s" -#: daemon/x52d_main.c:236 +#: daemon/x52d_main.c:237 #, c-format msgid "Error binding to command socket: %s" msgstr "Erroray indingbay otay ommandcay ocketsay: %s" -#: daemon/x52d_main.c:241 +#: daemon/x52d_main.c:242 #, c-format msgid "Error listening on command socket: %s" msgstr "Erroray isteninglay onay ommandcay ocketsay: %s" -#: daemon/x52d_main.c:315 +#: daemon/x52d_main.c:316 #, c-format msgid "Unable to parse configuration override '%s'\n" msgstr "Unableay otay arsepay onfigurationcay overrideay '%s'\n" -#: daemon/x52d_main.c:343 +#: daemon/x52d_main.c:344 #, c-format msgid "Foreground = %s" msgstr "Oregroundfay = %s" -#: daemon/x52d_main.c:343 daemon/x52d_main.c:344 +#: daemon/x52d_main.c:344 daemon/x52d_main.c:345 msgid "true" msgstr "uetray" -#: daemon/x52d_main.c:343 daemon/x52d_main.c:344 +#: daemon/x52d_main.c:344 daemon/x52d_main.c:345 msgid "false" msgstr "alsefay" -#: daemon/x52d_main.c:344 +#: daemon/x52d_main.c:345 #, c-format msgid "Quiet = %s" msgstr "Uietqay = %s" -#: daemon/x52d_main.c:345 +#: daemon/x52d_main.c:346 #, c-format msgid "Verbosity = %d" msgstr "Erbosityvay = %d" -#: daemon/x52d_main.c:346 +#: daemon/x52d_main.c:347 #, c-format msgid "Log file = %s" msgstr "Oglay ilefay = %s" -#: daemon/x52d_main.c:347 +#: daemon/x52d_main.c:348 #, c-format msgid "Config file = %s" msgstr "Onfigcay ilefay = %s" -#: daemon/x52d_main.c:348 +#: daemon/x52d_main.c:349 #, c-format msgid "PID file = %s" msgstr "IDPay ilefay = %s" -#: daemon/x52d_main.c:349 +#: daemon/x52d_main.c:350 #, c-format msgid "Command socket = %s" msgstr "Ommandcay ocketsay = %s" @@ -709,6 +709,37 @@ 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 +#, 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 +#, 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 +#, c-format +msgid "Error when polling for command: %s" +msgstr "Erroray enwhay ollingpay orfay ommandcay: %s" + +#: daemon/x52d_command.c:116 +msgid "Timed out when polling for command" +msgstr "Imedtay outay enwhay ollingpay orfay ommandcay" + +#: daemon/x52d_command.c:149 +#, c-format +msgid "Error reading from client %d: %s" +msgstr "Erroray eadingray omfray ientclay %d: %s" + +#: daemon/x52d_command.c:158 +#, c-format +msgid "Short write to client %d; expected %d bytes, wrote %d bytes" +msgstr "" +"Ortshay itewray otay ientclay %d; expecteday %d ytesbay, otewray %d ytesbay" + #: daemon/x52d_config.c:26 #, c-format msgid "Error %d setting configuration defaults: %s"