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.
pull/6/head
Bill Somerville 2018-03-12 16:24:12 +00:00
rodzic 09e6ab6ef1
commit 489564a1af
5 zmienionych plików z 219 dodań i 73 usunięć

Wyświetl plik

@ -153,6 +153,7 @@ dnl Checks for typedefs, structures, and compiler characteristics.
AC_TYPE_SIZE_T AC_TYPE_SIZE_T
AC_HEADER_TIME AC_HEADER_TIME
AC_CHECK_TYPES([siginfo_t],[],[],[[#include <signal.h>]]) AC_CHECK_TYPES([siginfo_t],[],[],[[#include <signal.h>]])
AC_CHECK_TYPES([sig_atomic_t],[],[],[[#include <signal.h>]])
dnl Checks for libraries. dnl Checks for libraries.

Wyświetl plik

@ -567,7 +567,7 @@ int main(int argc, char *argv[])
do do
{ {
retcode = rigctl_parse(my_rig, stdin, stdout, argv, argc); retcode = rigctl_parse(my_rig, stdin, stdout, argv, argc, NULL);
if (retcode == 2) if (retcode == 2)
{ {

Wyświetl plik

@ -90,12 +90,6 @@ extern int read_history();
/* Hash table implementation See: http://uthash.sourceforge.net/ */ /* Hash table implementation See: http://uthash.sourceforge.net/ */
#include "uthash.h" #include "uthash.h"
#ifdef HAVE_PTHREAD
# include <pthread.h>
static pthread_mutex_t rig_mutex = PTHREAD_MUTEX_INITIALIZER;
#endif
#define STR1(S) #S #define STR1(S) #S
#define STR(S) STR1(S) #define STR(S) STR1(S)
@ -601,7 +595,7 @@ int ext_resp = 0;
unsigned char resp_sep = '\n'; /* Default response separator */ 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 */ int retcode; /* generic return code from functions */
unsigned char cmd; unsigned char cmd;
@ -1512,14 +1506,7 @@ int rigctl_parse(RIG *my_rig, FILE *fin, FILE *fout, char *argv[], int argc)
#endif /* HAVE_LIBREADLINE */ #endif /* HAVE_LIBREADLINE */
if (sync_cb) sync_cb (1); /* lock if necessary */
/*
* mutex locking needed because rigctld is multithreaded
* and hamlib is not MT-safe
*/
#ifdef HAVE_PTHREAD
pthread_mutex_lock(&rig_mutex);
#endif
if (!prompt) if (!prompt)
{ {
@ -1572,10 +1559,7 @@ int rigctl_parse(RIG *my_rig, FILE *fin, FILE *fout, char *argv[], int argc)
p2 ? p2 : "", p2 ? p2 : "",
p3 ? p3 : ""); p3 ? p3 : "");
#ifdef HAVE_PTHREAD if (sync_cb) sync_cb (0); /* unlock if necessary */
pthread_mutex_unlock(&rig_mutex);
#endif
if (retcode != RIG_OK) if (retcode != RIG_OK)
{ {

Wyświetl plik

@ -45,6 +45,7 @@ int dump_chan(FILE *, RIG *, channel_t *);
int print_conf_list(const struct confparams *cfp, rig_ptr_t data); int print_conf_list(const struct confparams *cfp, rig_ptr_t data);
int set_conf(RIG *my_rig, char *conf_parms); 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 */ #endif /* RIGCTL_PARSE_H */

Wyświetl plik

@ -27,6 +27,11 @@
# include "config.h" # include "config.h"
#endif #endif
#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -118,6 +123,19 @@ void *handle_socket(void *arg);
void usage(void); 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 interactive = 1; /* no cmd because of daemon */
int prompt = 0; /* Daemon mode for rigparse return string */ int prompt = 0; /* Daemon mode for rigparse return string */
int vfo_mode = 0; /* vfo_mode=0 means target VFO is current VFO */ 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 #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) 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[]) int main(int argc, char *argv[])
{ {
RIG *my_rig; /* handle to rig (instance) */
rig_model_t my_model = RIG_MODEL_DUMMY; rig_model_t my_model = RIG_MODEL_DUMMY;
int retcode; /* generic return code from functions */ int retcode; /* generic return code from functions */
int verbose = 0;
int show_conf = 0; int show_conf = 0;
int dump_caps_opt = 0; int dump_caps_opt = 0;
const char *rig_file = NULL, *ptt_file = NULL, *dcd_file = NULL; const char *rig_file = NULL, *ptt_file = NULL, *dcd_file = NULL;
@ -500,6 +561,7 @@ int main(int argc, char *argv[])
exit(0); exit(0);
} }
/* open and close rig connection to check early for issues */
retcode = rig_open(my_rig); retcode = rig_open(my_rig);
if (retcode != RIG_OK) 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", rig_debug(RIG_DEBUG_VERBOSE, "Backend version: %s, Status: %s\n",
my_rig->caps->version, rig_strstatus(my_rig->caps->status)); 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__ #ifdef __MINGW32__
# ifndef SO_OPENTYPE # ifndef SO_OPENTYPE
# define SO_OPENTYPE 0x7008 # define SO_OPENTYPE 0x7008
@ -639,28 +709,47 @@ int main(int argc, char *argv[])
exit(1); exit(1);
} }
#if HAVE_SIGACTION
struct sigaction act;
#ifdef SIGPIPE #ifdef SIGPIPE
/* Ignore SIGPIPE as we will handle it at the write()/send() calls /* Ignore SIGPIPE as we will handle it at the write()/send() calls
that will consequently fail with EPIPE. All child threads will that will consequently fail with EPIPE. All child threads will
inherit this disposition which is what we want. */ inherit this disposition which is what we want. */
#if HAVE_SIGACTION
struct sigaction act;
memset(&act, 0, sizeof act); memset(&act, 0, sizeof act);
act.sa_handler = SIG_IGN; act.sa_handler = SIG_IGN;
act.sa_flags = SA_RESTART; act.sa_flags = SA_RESTART;
if (sigaction(SIGPIPE, &act, NULL)) if (sigaction(SIGPIPE, &act, NULL))
{ {
handle_error(RIG_DEBUG_ERR, "sigaction"); handle_error(RIG_DEBUG_ERR, "sigaction SIGPIPE");
} }
#endif
#elif HAVE_SIGNAL #ifdef SIGINT
memset(&act, 0, sizeof act);
if (SIG_ERR == signal(SIGPIPE, SIG_IGN)) 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
#endif #endif
@ -677,57 +766,84 @@ int main(int argc, char *argv[])
exit(1); exit(1);
} }
arg->rig = my_rig; /* use select to allow for periodic checks for CTRL+C */
arg->clilen = sizeof(arg->cli_addr); fd_set set;
arg->sock = accept(sock_listen, struct timeval timeout;
(struct sockaddr *)&arg->cli_addr, FD_ZERO (&set);
&arg->clilen); FD_SET (sock_listen, &set);
timeout.tv_sec = 5;
if (arg->sock < 0) timeout.tv_usec = 0;
{ retcode = select (sock_listen + 1, &set, NULL, NULL, &timeout);
handle_error(RIG_DEBUG_ERR, "accept"); if (-1 == retcode) {
rig_debug (RIG_DEBUG_ERR, "select\n");
}
else if (!retcode) {
if (ctrl_c) {
break; 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, if (arg->sock < 0)
arg->clilen, {
host, handle_error(RIG_DEBUG_ERR, "accept");
sizeof(host), break;
serv, }
sizeof(serv),
NI_NOFQDN))
< 0)
{
rig_debug(RIG_DEBUG_WARN, if ((retcode = getnameinfo((struct sockaddr const *)&arg->cli_addr,
"Peer lookup error: %s", arg->clilen,
gai_strerror(retcode)); 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, rig_debug(RIG_DEBUG_VERBOSE,
"Connection opened from %s:%s\n", "Connection opened from %s:%s\n",
host, host,
serv); serv);
#ifdef HAVE_PTHREAD #ifdef HAVE_PTHREAD
pthread_attr_init(&attr); pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 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) if (retcode != 0)
{ {
rig_debug(RIG_DEBUG_ERR, "pthread_create: %s\n", strerror(retcode)); rig_debug(RIG_DEBUG_ERR, "pthread_create: %s\n", strerror(retcode));
break; break;
} }
#else #else
handle_socket(arg); handle_socket(arg);
#endif #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 */ rig_close(my_rig); /* close port */
#endif
rig_cleanup(my_rig); /* if you care about memory */ rig_cleanup(my_rig); /* if you care about memory */
#ifdef __MINGW32__ #ifdef __MINGW32__
@ -746,7 +862,7 @@ void * handle_socket(void *arg)
struct handle_data *handle_data_arg = (struct handle_data *)arg; struct handle_data *handle_data_arg = (struct handle_data *)arg;
FILE *fsockin; FILE *fsockin;
FILE *fsockout; FILE *fsockout;
int retcode; int retcode = RIG_OK;
char host[NI_MAXHOST]; char host[NI_MAXHOST];
char serv[NI_MAXSERV]; char serv[NI_MAXSERV];
@ -784,17 +900,61 @@ void * handle_socket(void *arg)
goto handle_exit; 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 do
{ {
retcode = rigctl_parse(handle_data_arg->rig, fsockin, fsockout, NULL, 0); retcode = rigctl_parse(handle_data_arg->rig, fsockin, fsockout, NULL, 0, sync_callback);
if (ferror(fsockin) || ferror(fsockout))
if (ferror(fsockin) || ferror(fsockout))
{ {
retcode = 1; retcode = 1;
} }
} }
while (retcode == 0 || retcode == 2 || retcode == -RIG_ENAVAIL); 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, if ((retcode = getnameinfo((struct sockaddr const *)&handle_data_arg->cli_addr,
handle_data_arg->clilen, handle_data_arg->clilen,
host, host,