Add x52ctl daemon communication program

reverse-scroll
nirenjan 2021-11-04 10:50:10 -07:00
parent 18c0c72c74
commit 931f945133
3 changed files with 181 additions and 1 deletions

View File

@ -861,6 +861,7 @@ FILE_PATTERNS = \
libx52io.h \
libx52util.h \
x52_cli.c \
x52ctl.c \
x52dcomm.h \
*.dox

View File

@ -3,7 +3,7 @@
# Copyright (C) 2021 Nirenjan Krishnan (nirenjan@nirenjan.org)
#
# SPDX-License-Identifier: GPL-2.0-only WITH Classpath-exception-2.0
bin_PROGRAMS += x52d
bin_PROGRAMS += x52d x52ctl
# Service daemon that manages the X52 device
x52d_SOURCES = \
@ -60,6 +60,15 @@ libx52dcomm_la_CFLAGS = \
$(WARN_CFLAGS)
libx52dcomm_la_LDFLAGS = $(WARN_LDFLAGS)
x52include_HEADERS += daemon/x52dcomm.h
x52ctl_SOURCES = daemon/x52ctl.c
x52ctl_CFLAGS = \
-I $(top_srcdir) \
$(WARN_CFLAGS)
x52ctl_LDFLAGS = $(WARN_LDFLAGS)
x52ctl_LDADD = libx52dcomm.la
x52dconfdir = @sysconfdir@/x52d
x52dconf_DATA = daemon/x52d.conf

170
daemon/x52ctl.c 100644
View File

@ -0,0 +1,170 @@
/*
* Saitek X52 Pro MFD & LED driver - Daemon controller
*
* Copyright (C) 2021 Nirenjan Krishnan (nirenjan@nirenjan.org)
*
* SPDX-License-Identifier: GPL-2.0-only WITH Classpath-exception-2.0
*/
/**
@page x52ctl Command Line controller to X52 daemon
\htmlonly
<b>x52ctl</b> - Command line controller to X52 daemon
\endhtmlonly
# SYNOPSIS
<tt>\b x52ctl [\a -i] [\a -s socket-path] [command] </tt>
# DESCRIPTION
x52ctl is a program that can be used to communicate with the X52 daemon. It can
be used either as a one-shot program that can be run from another program or
script, or it can be run interactively.
Commands are sent to the running daemon, and responses are written to standard
output.
If not running interactively, then you must specify a command, or the program
will exit with a failure exit code. If running interactively, the program will
request input and send that to the daemon, until the user either enters the
string "quit", or terminates input by using Ctrl+D.
*/
#include "config.h"
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <unistd.h>
#include "x52d_const.h"
#include "x52dcomm.h"
#define APP_NAME "x52ctl"
#if HAVE_FUNC_ATTRIBUTE_NORETURN
__attribute__((noreturn))
#endif
static void usage(int exit_code)
{
fprintf(stderr, _("Usage: %s [-i] [-s socket-path] [command]\n"), APP_NAME);
exit(exit_code);
}
static int send_command(int sock_fd, char *buffer, size_t buflen)
{
int rc;
rc = x52d_send_command(sock_fd, buffer, buflen);
if (rc >= 0) {
if (write(STDOUT_FILENO, buffer, rc) < 0) {
perror("write");
return -1;
}
} else {
perror("x52d_send_command");
return -1;
}
fputc('\n', stdout);
return 0;
}
int main(int argc, char **argv)
{
bool interactive = false;
char *socket_path = NULL;
int opt;
int i;
int sock_fd;
int rc = EXIT_SUCCESS;
char buffer[1024];
size_t buflen;
/*
* Parse command line arguments
*
* -i Interactive
* -s Socket path
*/
while ((opt = getopt(argc, argv, "is:h")) != -1) {
switch (opt) {
case 'i':
interactive = true;
break;
case 's':
socket_path = optarg;
break;
case 'h':
usage(EXIT_SUCCESS);
break;
default:
usage(EXIT_FAILURE);
break;
}
}
if (!interactive && optind >= argc) {
usage(EXIT_FAILURE);
}
/* Connect to the socket */
sock_fd = x52d_dial_command(socket_path);
if (sock_fd < 0) {
perror("x52d_dial_command");
return EXIT_FAILURE;
}
if (interactive) {
if (optind < argc) {
fprintf(stderr,
_("Running in interactive mode, ignoring extra arguments\n"));
}
fputs("> ", stdout);
while (fgets(buffer, sizeof(buffer), stdin) != NULL) {
if (strcasecmp(buffer, "quit\n") == 0) {
break;
}
if (send_command(sock_fd, buffer, strlen(buffer))) {
rc = EXIT_FAILURE;
goto cleanup;
}
fputs("> ", stdout);
}
} else {
memset(buffer, 0, sizeof(buffer));
buflen = 0;
for (i = optind; i < argc; i++) {
buflen += snprintf(&buffer[buflen], sizeof(buffer) - buflen,
"%s ", argv[i]);
if (buflen >= sizeof(buffer)) {
fprintf(stderr, _("Argument length too long\n"));
rc = EXIT_FAILURE;
goto cleanup;
}
}
if (buflen > sizeof(buffer)) {
buflen = sizeof(buffer);
}
buflen--;
buffer[buflen] = '\0';
if (send_command(sock_fd, buffer, buflen)) {
rc = EXIT_FAILURE;
goto cleanup;
}
}
cleanup:
close(sock_fd);
return rc;
}