From 489564a1af0611af74fe9f3d7ab660bc6f717107 Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Mon, 12 Mar 2018 16:24:12 +0000 Subject: [PATCH] Make rig_open() call in rigctld lazy and add graceful termination Because some rigs lock their front panel when opened for CAT it is helpful to call rig_close() in rigctld when no clients are connected. This change does that. A CTRL+C handler is also added to allow rig_close() to be called during exit. --- configure.ac | 1 + tests/rigctl.c | 2 +- tests/rigctl_parse.c | 22 +--- tests/rigctl_parse.h | 3 +- tests/rigctld.c | 264 ++++++++++++++++++++++++++++++++++--------- 5 files changed, 219 insertions(+), 73 deletions(-) diff --git a/configure.ac b/configure.ac index bf8b26e34..706561cb9 100644 --- a/configure.ac +++ b/configure.ac @@ -153,6 +153,7 @@ dnl Checks for typedefs, structures, and compiler characteristics. AC_TYPE_SIZE_T AC_HEADER_TIME AC_CHECK_TYPES([siginfo_t],[],[],[[#include ]]) +AC_CHECK_TYPES([sig_atomic_t],[],[],[[#include ]]) dnl Checks for libraries. diff --git a/tests/rigctl.c b/tests/rigctl.c index 43413d7c8..82458eabf 100644 --- a/tests/rigctl.c +++ b/tests/rigctl.c @@ -567,7 +567,7 @@ int main(int argc, char *argv[]) do { - retcode = rigctl_parse(my_rig, stdin, stdout, argv, argc); + retcode = rigctl_parse(my_rig, stdin, stdout, argv, argc, NULL); if (retcode == 2) { diff --git a/tests/rigctl_parse.c b/tests/rigctl_parse.c index 538c20ef4..0deeb5a12 100644 --- a/tests/rigctl_parse.c +++ b/tests/rigctl_parse.c @@ -90,12 +90,6 @@ extern int read_history(); /* Hash table implementation See: http://uthash.sourceforge.net/ */ #include "uthash.h" -#ifdef HAVE_PTHREAD -# include - -static pthread_mutex_t rig_mutex = PTHREAD_MUTEX_INITIALIZER; -#endif - #define STR1(S) #S #define STR(S) STR1(S) @@ -601,7 +595,7 @@ int ext_resp = 0; unsigned char resp_sep = '\n'; /* Default response separator */ -int rigctl_parse(RIG *my_rig, FILE *fin, FILE *fout, char *argv[], int argc) +int rigctl_parse(RIG *my_rig, FILE *fin, FILE *fout, char *argv[], int argc, sync_cb_t sync_cb) { int retcode; /* generic return code from functions */ unsigned char cmd; @@ -1512,14 +1506,7 @@ int rigctl_parse(RIG *my_rig, FILE *fin, FILE *fout, char *argv[], int argc) #endif /* HAVE_LIBREADLINE */ - - /* - * mutex locking needed because rigctld is multithreaded - * and hamlib is not MT-safe - */ -#ifdef HAVE_PTHREAD - pthread_mutex_lock(&rig_mutex); -#endif + if (sync_cb) sync_cb (1); /* lock if necessary */ if (!prompt) { @@ -1572,10 +1559,7 @@ int rigctl_parse(RIG *my_rig, FILE *fin, FILE *fout, char *argv[], int argc) p2 ? p2 : "", p3 ? p3 : ""); -#ifdef HAVE_PTHREAD - pthread_mutex_unlock(&rig_mutex); -#endif - + if (sync_cb) sync_cb (0); /* unlock if necessary */ if (retcode != RIG_OK) { diff --git a/tests/rigctl_parse.h b/tests/rigctl_parse.h index 48a0d14d8..dfe2a0669 100644 --- a/tests/rigctl_parse.h +++ b/tests/rigctl_parse.h @@ -45,6 +45,7 @@ int dump_chan(FILE *, RIG *, channel_t *); int print_conf_list(const struct confparams *cfp, rig_ptr_t data); int set_conf(RIG *my_rig, char *conf_parms); -int rigctl_parse(RIG *my_rig, FILE *fin, FILE *fout, char *argv[], int argc); +typedef void (*sync_cb_t)(int); +int rigctl_parse(RIG *my_rig, FILE *fin, FILE *fout, char *argv[], int argc, sync_cb_t sync_cb); #endif /* RIGCTL_PARSE_H */ diff --git a/tests/rigctld.c b/tests/rigctld.c index 18f22b610..dbade4a28 100644 --- a/tests/rigctld.c +++ b/tests/rigctld.c @@ -27,6 +27,11 @@ # include "config.h" #endif +#ifdef WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif + #include #include #include @@ -118,6 +123,19 @@ void *handle_socket(void *arg); void usage(void); +#ifdef HAVE_PTHREAD +static unsigned client_count; +#endif + +static RIG * my_rig; /* handle to rig (instance) */ +static int verbose; + +#ifdef HAVE_SIG_ATOMIC_T +static sig_atomic_t volatile ctrl_c; +#else +static int volatile ctrl_c; +#endif + int interactive = 1; /* no cmd because of daemon */ int prompt = 0; /* Daemon mode for rigparse return string */ int vfo_mode = 0; /* vfo_mode=0 means target VFO is current VFO */ @@ -129,6 +147,51 @@ const char *src_addr = NULL; /* INADDR_ANY */ #define MAXCONFLEN 128 +static void sync_callback (int lock) +{ +#ifdef HAVE_PTHREAD + static pthread_mutex_t client_lock = PTHREAD_MUTEX_INITIALIZER; + if (lock) { + pthread_mutex_lock (&client_lock); + rig_debug (RIG_DEBUG_VERBOSE, "client lock engaged\n"); + } + else { + rig_debug (RIG_DEBUG_VERBOSE, "client lock disengaged\n"); + pthread_mutex_unlock (&client_lock); + } +#endif +} + +#ifdef WIN32 +static BOOL WINAPI CtrlHandler (DWORD fdwCtrlType) +{ + rig_debug (RIG_DEBUG_VERBOSE, "CtrlHandler called\n"); + switch (fdwCtrlType) + { + case CTRL_C_EVENT: + case CTRL_CLOSE_EVENT: + ctrl_c = 1; + return TRUE; + + default: + return FALSE; + } +} +#else +static void signal_handler (int sig) +{ + switch (sig) + { + case SIGINT: + ctrl_c = 1; + break; + + default: + /* do nothing */ + break; + } +} +#endif static void handle_error(enum rig_debug_level_e lvl, const char *msg) { @@ -165,12 +228,10 @@ static void handle_error(enum rig_debug_level_e lvl, const char *msg) int main(int argc, char *argv[]) { - RIG *my_rig; /* handle to rig (instance) */ rig_model_t my_model = RIG_MODEL_DUMMY; int retcode; /* generic return code from functions */ - int verbose = 0; int show_conf = 0; int dump_caps_opt = 0; const char *rig_file = NULL, *ptt_file = NULL, *dcd_file = NULL; @@ -500,6 +561,7 @@ int main(int argc, char *argv[]) exit(0); } + /* open and close rig connection to check early for issues */ retcode = rig_open(my_rig); if (retcode != RIG_OK) @@ -518,6 +580,14 @@ int main(int argc, char *argv[]) rig_debug(RIG_DEBUG_VERBOSE, "Backend version: %s, Status: %s\n", my_rig->caps->version, rig_strstatus(my_rig->caps->status)); + rig_close(my_rig); /* we will reopen for clients */ + if (verbose > 0) + { + printf("Closed rig model %d, '%s - will reopen for clients'\n", + my_rig->caps->rig_model, + my_rig->caps->model_name); + } + #ifdef __MINGW32__ # ifndef SO_OPENTYPE # define SO_OPENTYPE 0x7008 @@ -639,28 +709,47 @@ int main(int argc, char *argv[]) exit(1); } +#if HAVE_SIGACTION + struct sigaction act; + #ifdef SIGPIPE /* Ignore SIGPIPE as we will handle it at the write()/send() calls that will consequently fail with EPIPE. All child threads will inherit this disposition which is what we want. */ -#if HAVE_SIGACTION - struct sigaction act; memset(&act, 0, sizeof act); act.sa_handler = SIG_IGN; act.sa_flags = SA_RESTART; - if (sigaction(SIGPIPE, &act, NULL)) { - handle_error(RIG_DEBUG_ERR, "sigaction"); + handle_error(RIG_DEBUG_ERR, "sigaction SIGPIPE"); } +#endif -#elif HAVE_SIGNAL - - if (SIG_ERR == signal(SIGPIPE, SIG_IGN)) +#ifdef SIGINT + memset(&act, 0, sizeof act); + act.sa_handler = signal_handler; + if (sigaction(SIGINT, &act, NULL)) { - handle_error(RIG_DEBUG_ERR, "signal"); + handle_error(RIG_DEBUG_ERR, "sigaction SIGINT"); } - +#endif +#elif defined (WIN32) + if (!SetConsoleCtrlHandler (CtrlHandler, TRUE)) + { + handle_error(RIG_DEBUG_ERR, "SetConsoleCtrlHandler"); + } +#elif HAVE_SIGNAL +#ifdef SIGPIPE + if (SIG_ERR == signal(SIGPIPE, SIG_IGN)) + { + handle_error(RIG_DEBUG_ERR, "signal SIGPIPE"); + } +#endif +#ifdef SIGINT + if (SIG_ERR == signal(SIGINT, signal_handler)) + { + handle_error(RIG_DEBUG_ERR, "signal SIGINT"); + } #endif #endif @@ -677,57 +766,84 @@ int main(int argc, char *argv[]) exit(1); } - arg->rig = my_rig; - arg->clilen = sizeof(arg->cli_addr); - arg->sock = accept(sock_listen, - (struct sockaddr *)&arg->cli_addr, - &arg->clilen); - - if (arg->sock < 0) - { - handle_error(RIG_DEBUG_ERR, "accept"); + /* use select to allow for periodic checks for CTRL+C */ + fd_set set; + struct timeval timeout; + FD_ZERO (&set); + FD_SET (sock_listen, &set); + timeout.tv_sec = 5; + timeout.tv_usec = 0; + retcode = select (sock_listen + 1, &set, NULL, NULL, &timeout); + if (-1 == retcode) { + rig_debug (RIG_DEBUG_ERR, "select\n"); + } + else if (!retcode) { + if (ctrl_c) { break; + } } + else { + arg->rig = my_rig; + arg->clilen = sizeof(arg->cli_addr); + arg->sock = accept(sock_listen, + (struct sockaddr *)&arg->cli_addr, + &arg->clilen); - if ((retcode = getnameinfo((struct sockaddr const *)&arg->cli_addr, - arg->clilen, - host, - sizeof(host), - serv, - sizeof(serv), - NI_NOFQDN)) - < 0) - { + if (arg->sock < 0) + { + handle_error(RIG_DEBUG_ERR, "accept"); + break; + } - rig_debug(RIG_DEBUG_WARN, - "Peer lookup error: %s", - gai_strerror(retcode)); - } + if ((retcode = getnameinfo((struct sockaddr const *)&arg->cli_addr, + arg->clilen, + host, + sizeof(host), + serv, + sizeof(serv), + NI_NOFQDN)) + < 0) + { + rig_debug(RIG_DEBUG_WARN, + "Peer lookup error: %s", + gai_strerror(retcode)); + } - rig_debug(RIG_DEBUG_VERBOSE, - "Connection opened from %s:%s\n", - host, - serv); + rig_debug(RIG_DEBUG_VERBOSE, + "Connection opened from %s:%s\n", + host, + serv); #ifdef HAVE_PTHREAD - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - retcode = pthread_create(&thread, &attr, handle_socket, arg); + retcode = pthread_create(&thread, &attr, handle_socket, arg); - if (retcode != 0) - { - rig_debug(RIG_DEBUG_ERR, "pthread_create: %s\n", strerror(retcode)); - break; - } + if (retcode != 0) + { + rig_debug(RIG_DEBUG_ERR, "pthread_create: %s\n", strerror(retcode)); + break; + } #else - handle_socket(arg); + handle_socket(arg); #endif + } } - while (retcode == 0); + while (retcode == 0 && !ctrl_c); +#ifdef HAVE_PTHREAD + /* allow threads to finish current action */ + sync_callback (1); + if (client_count) { + rig_debug (RIG_DEBUG_WARN, "%d outstanding client(s)\n", client_count); + } + rig_close (my_rig); + sync_callback (0); +#else rig_close(my_rig); /* close port */ +#endif rig_cleanup(my_rig); /* if you care about memory */ #ifdef __MINGW32__ @@ -746,7 +862,7 @@ void * handle_socket(void *arg) struct handle_data *handle_data_arg = (struct handle_data *)arg; FILE *fsockin; FILE *fsockout; - int retcode; + int retcode = RIG_OK; char host[NI_MAXHOST]; char serv[NI_MAXSERV]; @@ -784,17 +900,61 @@ void * handle_socket(void *arg) goto handle_exit; } +#ifdef HAVE_PTHREAD + sync_callback (1); + if (!client_count++) { + retcode = rig_open (my_rig); + if (RIG_OK == retcode && verbose > 0) + { + printf("Opened rig model %d, '%s'\n", + my_rig->caps->rig_model, + my_rig->caps->model_name); + } + } + sync_callback (0); +#else + retcode = rig_open (my_rig); + if (RIG_OK == retcode && verbose > 0) + { + printf("Opened rig model %d, '%s'\n", + my_rig->caps->rig_model, + my_rig->caps->model_name); + } +#endif + do { - retcode = rigctl_parse(handle_data_arg->rig, fsockin, fsockout, NULL, 0); - - if (ferror(fsockin) || ferror(fsockout)) + retcode = rigctl_parse(handle_data_arg->rig, fsockin, fsockout, NULL, 0, sync_callback); + if (ferror(fsockin) || ferror(fsockout)) { - retcode = 1; + retcode = 1; } } while (retcode == 0 || retcode == 2 || retcode == -RIG_ENAVAIL); +#ifdef HAVE_PTHREAD + sync_callback (1); + /* Release rig if there are no clients */ + if (!--client_count) { + rig_close (my_rig); + if (verbose > 0) + { + printf("Closed rig model %d, '%s - no clients, will reopen for new clients'\n", + my_rig->caps->rig_model, + my_rig->caps->model_name); + } + } + sync_callback (0); +#else + rig_close (my_rig); + if (verbose > 0) + { + printf("Closed rig model %d, '%s - will reopen for new clients'\n", + my_rig->caps->rig_model, + my_rig->caps->model_name); + } +#endif + if ((retcode = getnameinfo((struct sockaddr const *)&handle_data_arg->cli_addr, handle_data_arg->clilen, host,