mirror of https://github.com/nirenjan/libx52.git
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
parent
293ba0a99d
commit
18c0c72c74
|
@ -13,7 +13,7 @@ libx52test*
|
||||||
libx52util/util_char_map.c
|
libx52util/util_char_map.c
|
||||||
udev/*.rules
|
udev/*.rules
|
||||||
x52d*
|
x52d*
|
||||||
!daemon/x52d_*.*
|
!daemon/x52d*.*
|
||||||
test-*
|
test-*
|
||||||
libx52-*.tar.gz
|
libx52-*.tar.gz
|
||||||
|
|
||||||
|
|
|
@ -861,6 +861,7 @@ FILE_PATTERNS = \
|
||||||
libx52io.h \
|
libx52io.h \
|
||||||
libx52util.h \
|
libx52util.h \
|
||||||
x52_cli.c \
|
x52_cli.c \
|
||||||
|
x52dcomm.h \
|
||||||
*.dox
|
*.dox
|
||||||
|
|
||||||
# The RECURSIVE tag can be used to specify whether or not subdirectories should
|
# The RECURSIVE tag can be used to specify whether or not subdirectories should
|
||||||
|
|
|
@ -46,6 +46,20 @@ x52d_LDFLAGS += @EVDEV_LIBS@
|
||||||
x52d_LDADD += libx52io.la
|
x52d_LDADD += libx52io.la
|
||||||
endif
|
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
|
x52dconfdir = @sysconfdir@/x52d
|
||||||
x52dconf_DATA = daemon/x52d.conf
|
x52dconf_DATA = daemon/x52d.conf
|
||||||
|
|
||||||
|
@ -63,6 +77,7 @@ EXTRA_DIST += \
|
||||||
daemon/x52d_device.h \
|
daemon/x52d_device.h \
|
||||||
daemon/x52d_io.h \
|
daemon/x52d_io.h \
|
||||||
daemon/x52d_mouse.h \
|
daemon/x52d_mouse.h \
|
||||||
|
daemon/x52dcomm.h \
|
||||||
daemon/x52d.conf
|
daemon/x52d.conf
|
||||||
|
|
||||||
if HAVE_SYSTEMD
|
if HAVE_SYSTEMD
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -17,6 +17,8 @@
|
||||||
|
|
||||||
#define X52D_PID_FILE RUNDIR "/" X52D_APP_NAME ".pid"
|
#define X52D_PID_FILE RUNDIR "/" X52D_APP_NAME ".pid"
|
||||||
|
|
||||||
|
#define X52D_SOCK_COMMAND RUNDIR "/" X52D_APP_NAME ".cmd"
|
||||||
|
|
||||||
#include "gettext.h"
|
#include "gettext.h"
|
||||||
#define N_(x) gettext_noop(x)
|
#define N_(x) gettext_noop(x)
|
||||||
#define _(x) gettext(x)
|
#define _(x) gettext(x)
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue