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_HEADER_TIME
AC_CHECK_TYPES([siginfo_t],[],[],[[#include <signal.h>]])
AC_CHECK_TYPES([sig_atomic_t],[],[],[[#include <signal.h>]])
dnl Checks for libraries.

Wyświetl plik

@ -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)
{

Wyświetl plik

@ -90,12 +90,6 @@ extern int read_history();
/* Hash table implementation See: http://uthash.sourceforge.net/ */
#include "uthash.h"
#ifdef HAVE_PTHREAD
# include <pthread.h>
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)
{

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 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 */

Wyświetl plik

@ -27,6 +27,11 @@
# include "config.h"
#endif
#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -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,