From 5a7849214067f9296b1ca66e9d1519a8066666f1 Mon Sep 17 00:00:00 2001 From: nirenjan Date: Thu, 4 Nov 2021 13:48:18 -0700 Subject: [PATCH] Make X52 daemon listen on a Unix socket This change makes X52 daemon listen on a Unix socket. This is in preparation for changes that will read from the socket and allow clients to communicate with and control the daemon. --- daemon/Makefile.am | 7 +++- daemon/x52d_comm_client.c | 24 +++-------- daemon/x52d_comm_internal.c | 51 ++++++++++++++++++++++ daemon/x52d_const.h | 2 + daemon/x52d_main.c | 84 ++++++++++++++++++++++++++++++++++++- daemon/x52dcomm-internal.h | 19 +++++++++ daemon/x52dcomm.h | 1 + po/libx52.pot | 70 +++++++++++++++++++++++-------- po/xx_PL.po | 73 ++++++++++++++++++++++++-------- 9 files changed, 274 insertions(+), 57 deletions(-) create mode 100644 daemon/x52d_comm_internal.c create mode 100644 daemon/x52dcomm-internal.h diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 5f85f1c..5e6552b 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -14,7 +14,8 @@ x52d_SOURCES = \ daemon/x52d_device.c \ daemon/x52d_clock.c \ daemon/x52d_mouse.c \ - daemon/x52d_led.c + daemon/x52d_led.c \ + daemon/x52d_comm_internal.c x52d_CFLAGS = \ -I $(top_srcdir) \ @@ -50,7 +51,8 @@ lib_LTLIBRARIES += libx52dcomm.la # Client library to communicate with X52 daemon libx52dcomm_la_SOURCES = \ - daemon/x52d_comm_client.c + daemon/x52d_comm_client.c \ + daemon/x52d_comm_internal.c libx52dcomm_la_CFLAGS = \ -I $(top_srcdir) \ -DSYSCONFDIR=\"$(sysconfdir)\" \ @@ -87,6 +89,7 @@ EXTRA_DIST += \ daemon/x52d_io.h \ daemon/x52d_mouse.h \ daemon/x52dcomm.h \ + daemon/x52dcomm-internal.h \ daemon/x52d.conf if HAVE_SYSTEMD diff --git a/daemon/x52d_comm_client.c b/daemon/x52d_comm_client.c index 2dd6d32..61905f6 100644 --- a/daemon/x52d_comm_client.c +++ b/daemon/x52d_comm_client.c @@ -16,23 +16,18 @@ #include #include "x52dcomm.h" -#include "x52d_const.h" +#include "x52dcomm-internal.h" int x52d_dial_command(const char *sock_path) { int sock; - socklen_t len; + int len; struct sockaddr_un remote; int saved_errno; - if (sock_path == NULL) { - sock_path = X52D_SOCK_COMMAND; - } - - len = strlen(sock_path); - if (len >= sizeof(remote.sun_path)) { - /* Socket path will not fit inside sun_path */ - errno = E2BIG; + len = x52d_setup_command_sock(sock_path, &remote); + if (len < 0) { + /* Error when setting up sockaddr */ return -1; } @@ -43,15 +38,8 @@ int x52d_dial_command(const char *sock_path) return -1; } - /* Setup the sockaddr structure */ - memset(&remote, 0, sizeof(remote)); - remote.sun_family = AF_UNIX; - /* We've already verified that sock_path will fit, so we don't need strncpy */ - strcpy(remote.sun_path, sock_path); - len += sizeof(remote.sun_family); - /* Connect to the socket */ - if (connect(sock, (struct sockaddr *)&remote, len) == -1) { + if (connect(sock, (struct sockaddr *)&remote, (socklen_t)len) == -1) { /* Failure connecting to the socket. Cleanup */ saved_errno = errno; /* close may modify errno, so we save it prior to the call */ diff --git a/daemon/x52d_comm_internal.c b/daemon/x52d_comm_internal.c new file mode 100644 index 0000000..15ecbc2 --- /dev/null +++ b/daemon/x52d_comm_internal.c @@ -0,0 +1,51 @@ +/* + * Saitek X52 Pro MFD & LED driver - Client communication library + * + * 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 "x52dcomm-internal.h" +#include "x52d_const.h" + +const char * x52d_command_sock_path(const char *sock_path) +{ + if (sock_path == NULL) { + sock_path = X52D_SOCK_COMMAND; + } + + return sock_path; +} + +int x52d_setup_command_sock(const char *sock_path, struct sockaddr_un *remote) +{ + int len; + if (remote == NULL) { + errno = EINVAL; + return -1; + } + + sock_path = x52d_command_sock_path(sock_path); + + len = strlen(sock_path); + if ((size_t)len >= sizeof(remote->sun_path)) { + /* Socket path will not fit inside sun_path */ + errno = E2BIG; + return -1; + } + + /* Setup the sockaddr structure */ + memset(remote, 0, sizeof(*remote)); + remote->sun_family = AF_UNIX; + /* We've already verified that sock_path will fit, so we don't need strncpy */ + strcpy(remote->sun_path, sock_path); + len += sizeof(remote->sun_family); + + return len; +} diff --git a/daemon/x52d_const.h b/daemon/x52d_const.h index 9f2d48c..80c2891 100644 --- a/daemon/x52d_const.h +++ b/daemon/x52d_const.h @@ -23,4 +23,6 @@ #define N_(x) gettext_noop(x) #define _(x) gettext(x) +#define X52D_MAX_CLIENTS 63 + #endif // !defined X52D_CONST_H diff --git a/daemon/x52d_main.c b/daemon/x52d_main.c index eb07579..ff8d87c 100644 --- a/daemon/x52d_main.c +++ b/daemon/x52d_main.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include "x52d_clock.h" @@ -22,6 +23,8 @@ #include "x52d_device.h" #include "x52d_io.h" #include "x52d_mouse.h" +#include "x52dcomm-internal.h" +#include "x52dcomm.h" #include "pinelog.h" static volatile int flag_quit; @@ -86,7 +89,8 @@ static void usage(int exit_code) fprintf(stderr, _("Usage: %s [-f] [-v] [-q]\n" "\t[-l log-file] [-o override]\n" - "\t[-c config-file] [-p pid-file]\n"), + "\t[-c config-file] [-p pid-file]\n" + "\t[-s command-socket-path]\n"), X52D_APP_NAME); exit(exit_code); } @@ -194,6 +198,59 @@ static void start_daemon(bool foreground, const char *pid_file) } } +/* Bind and listen to the command socket */ +static int listen_command(const char *command_sock) +{ + int sock_fd; + int len; + struct sockaddr_un local; + int flags; + + len = x52d_setup_command_sock(command_sock, &local); + if (len < 0) { + return -1; + } + + sock_fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock_fd < 0) { + /* Failure creating the socket. Abort early */ + PINELOG_ERROR(_("Error creating command socket: %s"), strerror(errno)); + return -1; + } + + /* Mark the socket as non-blocking */ + flags = fcntl(sock_fd, F_GETFL); + if (flags < 0) { + PINELOG_ERROR(_("Error getting command socket flags: %s"), strerror(errno)); + goto sock_failure; + } + if (fcntl(sock_fd, F_SETFL, flags | O_NONBLOCK) < 0) { + PINELOG_ERROR(_("Error setting command socket flags: %s"), strerror(errno)); + goto sock_failure; + } + + /* Cleanup any existing socket */ + unlink(local.sun_path); + if (bind(sock_fd, (struct sockaddr *)&local, (socklen_t)len) < 0) { + /* Failure binding socket */ + PINELOG_ERROR(_("Error binding to command socket: %s"), strerror(errno)); + goto listen_failure; + } + + if (listen(sock_fd, X52D_MAX_CLIENTS) < 0) { + PINELOG_ERROR(_("Error listening on command socket: %s"), strerror(errno)); + goto listen_failure; + } + + return sock_fd; + +listen_failure: + unlink(local.sun_path); +sock_failure: + close(sock_fd); + return -1; +} + int main(int argc, char **argv) { int verbosity = 0; @@ -202,7 +259,9 @@ int main(int argc, char **argv) char *log_file = NULL; char *conf_file = NULL; const char *pid_file = NULL; + const char *command_sock = NULL; int opt; + int command_sock_fd; /* Initialize gettext */ #if ENABLE_NLS @@ -224,8 +283,9 @@ int main(int argc, char **argv) * -q silent behavior * -l path to log file * -p path to PID file (only used if running in background) + * -s path to command socket */ - while ((opt = getopt(argc, argv, "fvql:o:c:p:h")) != -1) { + while ((opt = getopt(argc, argv, "fvql:o:c:p:s:h")) != -1) { switch (opt) { case 'f': foreground = true; @@ -266,6 +326,10 @@ int main(int argc, char **argv) pid_file = optarg; break; + case 's': + command_sock = optarg; + break; + case 'h': usage(EXIT_SUCCESS); break; @@ -281,6 +345,8 @@ int main(int argc, char **argv) PINELOG_DEBUG(_("Verbosity = %d"), verbosity); PINELOG_DEBUG(_("Log file = %s"), log_file); PINELOG_DEBUG(_("Config file = %s"), conf_file); + PINELOG_DEBUG(_("PID file = %s"), pid_file); + PINELOG_DEBUG(_("Command socket = %s"), command_sock); start_daemon(foreground, pid_file); @@ -295,6 +361,11 @@ int main(int argc, char **argv) x52d_mouse_evdev_init(); #endif + command_sock_fd = listen_command(command_sock); + if (command_sock_fd < 0) { + goto cleanup; + } + // Apply configuration x52d_config_apply(); @@ -321,6 +392,7 @@ int main(int argc, char **argv) PINELOG_INFO(_("Received termination signal %s"), strsignal(flag_quit)); +cleanup: // Stop device threads x52d_clock_exit(); x52d_dev_exit(); @@ -329,6 +401,14 @@ int main(int argc, char **argv) x52d_io_exit(); #endif + // Close the socket and remove the socket file + if (command_sock_fd >= 0) { + command_sock = x52d_command_sock_path(command_sock); + PINELOG_TRACE("Closing command socket %s", command_sock); + close(command_sock_fd); + unlink(command_sock); + } + // Remove the PID file PINELOG_TRACE("Removing PID file %s", pid_file); unlink(pid_file); diff --git a/daemon/x52dcomm-internal.h b/daemon/x52dcomm-internal.h new file mode 100644 index 0000000..cd5c061 --- /dev/null +++ b/daemon/x52dcomm-internal.h @@ -0,0 +1,19 @@ +/* + * Saitek X52 Pro MFD & LED driver - communication library interal functions + * + * Copyright (C) 2021 Nirenjan Krishnan (nirenjan@nirenjan.org) + * + * SPDX-License-Identifier: GPL-2.0-only WITH Classpath-exception-2.0 + */ + +#ifndef X52DCOMM_INTERNAL_H +#define X52DCOMM_INTERNAL_H + +#include +#include +#include + +const char *x52d_command_sock_path(const char *sock_path); +int x52d_setup_command_sock(const char *sock_path, struct sockaddr_un *remote); + +#endif // !defined X52DCOMM_INTERNAL_H diff --git a/daemon/x52dcomm.h b/daemon/x52dcomm.h index bf40ce4..902facc 100644 --- a/daemon/x52dcomm.h +++ b/daemon/x52dcomm.h @@ -88,3 +88,4 @@ int x52d_send_command(int sock_fd, char *buffer, size_t buflen); #endif #endif // !defined X52DCOMM_H + diff --git a/po/libx52.pot b/po/libx52.pot index 4e8f4dd..6cde6a2 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 10:50-0700\n" +"POT-Creation-Date: 2021-11-04 13:37-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -491,81 +491,117 @@ msgstr "" msgid "OK" msgstr "" -#: daemon/x52d_main.c:60 +#: daemon/x52d_main.c:63 #, c-format msgid "Error %d setting log file: %s\n" msgstr "" -#: daemon/x52d_main.c:76 +#: daemon/x52d_main.c:79 #, c-format msgid "Error %d installing handler for signal %d: %s" msgstr "" -#: daemon/x52d_main.c:87 +#: daemon/x52d_main.c:90 #, c-format msgid "" "Usage: %s [-f] [-v] [-q]\n" "\t[-l log-file] [-o override]\n" "\t[-c config-file] [-p pid-file]\n" +"\t[-s command-socket-path]\n" msgstr "" -#: daemon/x52d_main.c:117 +#: daemon/x52d_main.c:121 #, c-format msgid "Daemon is already running as PID %u" msgstr "" -#: daemon/x52d_main.c:255 +#: daemon/x52d_main.c:217 +#, c-format +msgid "Error creating command socket: %s" +msgstr "" + +#: daemon/x52d_main.c:224 +#, c-format +msgid "Error getting command socket flags: %s" +msgstr "" + +#: daemon/x52d_main.c:228 +#, c-format +msgid "Error setting command socket flags: %s" +msgstr "" + +#: daemon/x52d_main.c:236 +#, c-format +msgid "Error binding to command socket: %s" +msgstr "" + +#: daemon/x52d_main.c:241 +#, c-format +msgid "Error listening on command socket: %s" +msgstr "" + +#: daemon/x52d_main.c:315 #, c-format msgid "Unable to parse configuration override '%s'\n" msgstr "" -#: daemon/x52d_main.c:279 +#: daemon/x52d_main.c:343 #, c-format msgid "Foreground = %s" msgstr "" -#: daemon/x52d_main.c:279 daemon/x52d_main.c:280 +#: daemon/x52d_main.c:343 daemon/x52d_main.c:344 msgid "true" msgstr "" -#: daemon/x52d_main.c:279 daemon/x52d_main.c:280 +#: daemon/x52d_main.c:343 daemon/x52d_main.c:344 msgid "false" msgstr "" -#: daemon/x52d_main.c:280 +#: daemon/x52d_main.c:344 #, c-format msgid "Quiet = %s" msgstr "" -#: daemon/x52d_main.c:281 +#: daemon/x52d_main.c:345 #, c-format msgid "Verbosity = %d" msgstr "" -#: daemon/x52d_main.c:282 +#: daemon/x52d_main.c:346 #, c-format msgid "Log file = %s" msgstr "" -#: daemon/x52d_main.c:283 +#: daemon/x52d_main.c:347 #, c-format msgid "Config file = %s" msgstr "" -#: daemon/x52d_main.c:309 +#: daemon/x52d_main.c:348 +#, c-format +msgid "PID file = %s" +msgstr "" + +#: daemon/x52d_main.c:349 +#, c-format +msgid "Command socket = %s" +msgstr "" + +#: daemon/x52d_main.c:380 msgid "Reloading X52 configuration" msgstr "" -#: daemon/x52d_main.c:316 +#: daemon/x52d_main.c:387 msgid "Saving X52 configuration to disk" msgstr "" -#: daemon/x52d_main.c:322 +#: daemon/x52d_main.c:393 #, c-format msgid "Received termination signal %s" msgstr "" -#: daemon/x52d_main.c:336 +#: daemon/x52d_main.c:416 msgid "Shutting down X52 daemon" msgstr "" diff --git a/po/xx_PL.po b/po/xx_PL.po index 7cf0848..cb5c026 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 10:50-0700\n" -"PO-Revision-Date: 2021-11-04 10:52-0700\n" +"POT-Creation-Date: 2021-11-04 13:37-0700\n" +"PO-Revision-Date: 2021-11-04 13:45-0700\n" "Last-Translator: Nirenjan Krishnan \n" "Language-Team: Dummy Language for testing i18n\n" "Language: xx_PL\n" @@ -534,84 +534,121 @@ msgstr "Estingtay aracterchay 0x%02x..." msgid "OK" msgstr "OKay" -#: daemon/x52d_main.c:60 +#: daemon/x52d_main.c:63 #, c-format msgid "Error %d setting log file: %s\n" msgstr "Erroray %d ettingsay oglay ilefay: %s\n" -#: daemon/x52d_main.c:76 +#: daemon/x52d_main.c:79 #, c-format msgid "Error %d installing handler for signal %d: %s" msgstr "Erroray %d installingay andlerhay orfay ignalsay %d: %s" -#: daemon/x52d_main.c:87 +#: daemon/x52d_main.c:90 #, c-format msgid "" "Usage: %s [-f] [-v] [-q]\n" "\t[-l log-file] [-o override]\n" "\t[-c config-file] [-p pid-file]\n" +"\t[-s command-socket-path]\n" msgstr "" "Usageay: %s [-f] [-v] [-q]\n" "\t[-l oglay-ilefay] [-o overrideay]\n" "\t[-c onfigcay-ilefay] [-p idpay-ilefay]\n" +"\t[-s ommandcay-ocketsay-athpay]\n" -#: daemon/x52d_main.c:117 +#: daemon/x52d_main.c:121 #, c-format msgid "Daemon is already running as PID %u" msgstr "Aemonday isay alreadyay unningray asay IDPay %u" -#: daemon/x52d_main.c:255 +#: daemon/x52d_main.c:217 +#, c-format +msgid "Error creating command socket: %s" +msgstr "Erroray eatingcray ommandcay ocketsay: %s" + +#: daemon/x52d_main.c:224 +#, c-format +msgid "Error getting command socket flags: %s" +msgstr "Erroray ettinggay ommandcay ocketsay agsflay: %s" + +#: daemon/x52d_main.c:228 +#, c-format +msgid "Error setting command socket flags: %s" +msgstr "Erroray ettingsay ommandcay ocketsay agsflay: %s" + +#: daemon/x52d_main.c:236 +#, c-format +msgid "Error binding to command socket: %s" +msgstr "Erroray indingbay otay ommandcay ocketsay: %s" + +#: daemon/x52d_main.c:241 +#, c-format +msgid "Error listening on command socket: %s" +msgstr "Erroray isteninglay onay ommandcay ocketsay: %s" + +#: daemon/x52d_main.c:315 #, c-format msgid "Unable to parse configuration override '%s'\n" msgstr "Unableay otay arsepay onfigurationcay overrideay '%s'\n" -#: daemon/x52d_main.c:279 +#: daemon/x52d_main.c:343 #, c-format msgid "Foreground = %s" msgstr "Oregroundfay = %s" -#: daemon/x52d_main.c:279 daemon/x52d_main.c:280 +#: daemon/x52d_main.c:343 daemon/x52d_main.c:344 msgid "true" msgstr "uetray" -#: daemon/x52d_main.c:279 daemon/x52d_main.c:280 +#: daemon/x52d_main.c:343 daemon/x52d_main.c:344 msgid "false" msgstr "alsefay" -#: daemon/x52d_main.c:280 +#: daemon/x52d_main.c:344 #, c-format msgid "Quiet = %s" msgstr "Uietqay = %s" -#: daemon/x52d_main.c:281 +#: daemon/x52d_main.c:345 #, c-format msgid "Verbosity = %d" msgstr "Erbosityvay = %d" -#: daemon/x52d_main.c:282 +#: daemon/x52d_main.c:346 #, c-format msgid "Log file = %s" msgstr "Oglay ilefay = %s" -#: daemon/x52d_main.c:283 +#: daemon/x52d_main.c:347 #, c-format msgid "Config file = %s" msgstr "Onfigcay ilefay = %s" -#: daemon/x52d_main.c:309 +#: daemon/x52d_main.c:348 +#, c-format +msgid "PID file = %s" +msgstr "IDPay ilefay = %s" + +#: daemon/x52d_main.c:349 +#, c-format +msgid "Command socket = %s" +msgstr "Ommandcay ocketsay = %s" + +#: daemon/x52d_main.c:380 msgid "Reloading X52 configuration" msgstr "Eloadingray X52 onfigurationcay" -#: daemon/x52d_main.c:316 +#: daemon/x52d_main.c:387 msgid "Saving X52 configuration to disk" msgstr "Avingsay X52 onfigurationcay otay iskday" -#: daemon/x52d_main.c:322 +#: daemon/x52d_main.c:393 #, c-format msgid "Received termination signal %s" msgstr "Eceivedray erminationtay ignalsay %s" -#: daemon/x52d_main.c:336 +#: daemon/x52d_main.c:416 msgid "Shutting down X52 daemon" msgstr "Uttingshay ownday X52 aemonday"