Add client communication library

This change adds a library to connect to the X52 daemon and send
commands and receive responses. The library is a thin wrapper around the
POSIX sockets API. While a client could implement the functions
themselves, the library makes it a little bit easier, as well as
allowing for third-party clients to connect to and communicate with the
daemon.
reverse-scroll
nirenjan 2021-11-03 21:13:51 -07:00
parent 293ba0a99d
commit 18c0c72c74
6 changed files with 217 additions and 1 deletions

2
.gitignore vendored
View File

@ -13,7 +13,7 @@ libx52test*
libx52util/util_char_map.c
udev/*.rules
x52d*
!daemon/x52d_*.*
!daemon/x52d*.*
test-*
libx52-*.tar.gz

View File

@ -861,6 +861,7 @@ FILE_PATTERNS = \
libx52io.h \
libx52util.h \
x52_cli.c \
x52dcomm.h \
*.dox
# The RECURSIVE tag can be used to specify whether or not subdirectories should

View File

@ -46,6 +46,20 @@ x52d_LDFLAGS += @EVDEV_LIBS@
x52d_LDADD += libx52io.la
endif
lib_LTLIBRARIES += libx52dcomm.la
# Client library to communicate with X52 daemon
libx52dcomm_la_SOURCES = \
daemon/x52d_comm_client.c
libx52dcomm_la_CFLAGS = \
-I $(top_srcdir) \
-DSYSCONFDIR=\"$(sysconfdir)\" \
-DLOCALEDIR=\"$(localedir)\" \
-DLOGDIR=\"$(localstatedir)/log\" \
-DRUNDIR=\"$(localstatedir)/run\" \
$(WARN_CFLAGS)
libx52dcomm_la_LDFLAGS = $(WARN_LDFLAGS)
x52dconfdir = @sysconfdir@/x52d
x52dconf_DATA = daemon/x52d.conf
@ -63,6 +77,7 @@ EXTRA_DIST += \
daemon/x52d_device.h \
daemon/x52d_io.h \
daemon/x52d_mouse.h \
daemon/x52dcomm.h \
daemon/x52d.conf
if HAVE_SYSTEMD

View File

@ -0,0 +1,108 @@
/*
* 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 <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include "x52dcomm.h"
#include "x52d_const.h"
int x52d_dial_command(const char *sock_path)
{
int sock;
socklen_t 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;
return -1;
}
/* Create a socket */
sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock == -1) {
/* Failure creating the socket, abort early */
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) {
/* Failure connecting to the socket. Cleanup */
saved_errno = errno;
/* close may modify errno, so we save it prior to the call */
close(sock);
sock = -1;
errno = saved_errno;
}
return sock;
}
int x52d_send_command(int sock_fd, char *buffer, size_t buflen)
{
int rc;
for (;;) {
/*
* Unix sockets should have sufficient capacity to send the full
* datagram in a single message. Assume that is the case.
*/
rc = send(sock_fd, buffer, buflen, 0);
if (rc < 0) {
// Error
if (errno == EINTR) {
// System call interrupted due to signal. Try again
continue;
} else {
// Failed. Return early
return -1;
}
}
break;
}
/* Wait till we get a response */
for (;;) {
rc = recv(sock_fd, buffer, buflen, 0);
if (rc < 0) {
// Error
if (errno == EINTR) {
// System call interrupted due to signal. Try again
continue;
} else {
// Failed. Return early
return -1;
}
}
break;
}
return rc;
}

View File

@ -17,6 +17,8 @@
#define X52D_PID_FILE RUNDIR "/" X52D_APP_NAME ".pid"
#define X52D_SOCK_COMMAND RUNDIR "/" X52D_APP_NAME ".cmd"
#include "gettext.h"
#define N_(x) gettext_noop(x)
#define _(x) gettext(x)

90
daemon/x52dcomm.h 100644
View File

@ -0,0 +1,90 @@
/*
* Saitek X52 Pro MFD & LED driver
*
* Copyright (C) 2021 Nirenjan Krishnan (nirenjan@nirenjan.org)
*
* SPDX-License-Identifier: GPL-2.0-only WITH Classpath-exception-2.0
*/
/**
* @file x52dcomm.h
* @brief Functions, structures and enumerations for the Saitek X52 MFD & LED
* daemon communication library.
*
* This file contains the type, enum and function prototypes for the Saitek X52
* daemon communication library. These functions allow a client application to
* communicate with a running X52 daemon, execute commands and retrieve data.
*
* @author Nirenjan Krishnan (nirenjan@nirenjan.org)
*/
#ifndef X52DCOMM_H
#define X52DCOMM_H
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup x52dcomm Daemon communication
*
* These functions are used to communicate with the X52 daemon.
*
* @{
*/
/**
* @brief Open a connection to the daemon.
*
* This method opens a socket connection to the daemon command socket. This
* socket allows the client to issue commands and retrieve data. The \p sock_path
* parameter may be NULL, in which case, it will use the default socket path.
*
* The client will need to use the returned descriptor to communicate with the
* daemon using \ref x52d_send_command. Once finished, the client may use the
* \c close(2) method to close the file descriptor.
*
* @param[in] sock_path Path to the daemon command socket.
*
* @returns Non-negative socket file descriptor on success.
* @returns -1 on failure, and set \c errno accordingly.
*
* @exception E2BIG returned if the passed socket path is too big
*/
int x52d_dial_command(const char *sock_path);
/**
* @brief Send a command to the daemon and retrieve the response.
*
* The client sends the command and parameters as a single NULL terminated
* string, and retrieves the response in the same manner. Depending on the
* result, the return status is either a positive integer or -1, and \c errno
* is set accordingly.
*
* \p buffer should contain sufficient space to accomodate the returned
* response string.
*
* This is a blocking function and will not return until either a response is
* received from the server, or an exception condition occurs.
*
* @param[in] sock_fd Socket descriptor returned from
* \ref x52d_dial_command
*
* @param[inout] buffer Pointer to the string containing the command and
* parameters. This is also used to save the returned
* response.
*
* @param[in] buflen Length of the buffer to hold the returned response.
*
* @returns number of bytes returned from the server
* @returns -1 on an error condition, and \c errno is set accordingly.
*/
int x52d_send_command(int sock_fd, char *buffer, size_t buflen);
/** @} */
#ifdef __cplusplus
}
#endif
#endif // !defined X52DCOMM_H