Skip to content

Commit

Permalink
Systemd units for SvxLink and RemoteTrx reset h/w on stop
Browse files Browse the repository at this point in the history
The commandline option --reset was added to SvxLink and RemoteTrx. That
is used by the systemd units for those applications to reset the
hardware to initial state after main application exit.

The --quit commandline option was also added to suppress info printouts
during the hardware reset run.
  • Loading branch information
sm0svx committed Feb 8, 2025
1 parent f9bd8c6 commit d797049
Show file tree
Hide file tree
Showing 11 changed files with 236 additions and 163 deletions.
16 changes: 13 additions & 3 deletions src/doc/man/remotetrx.1
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
.TH REMOTETRX 1 "AUGUST 2021" Linux "User Manuals"
.TH REMOTETRX 1 "FEBRUARY 2025" Linux "User Manuals"
.
.SH NAME
.
remotetrx \- The SvxLink remote transceiver application
.
.SH SYNOPSIS
.
.BI "remotetrx [--help] [--daemon] [--logfile=" "log file" "] [--config=" "configuration file" "] [--pidfile=" "pid file" "] [--runasuser=" "user name" ]
.BI "remotetrx [--help] [--daemon] [--quiet] [--reset] [--version] [--logfile=" "log file" "] [--config=" "configuration file" "] [--pidfile=" "pid file" "] [--runasuser=" "user name" ]
.
.SH DESCRIPTION
.
Expand Down Expand Up @@ -44,7 +44,7 @@ Print a help message and exit.
.B --daemon
Start the SvxLink remote receiver server as a daemon.
.TP
.B --runasuser
.BI "--runasuser" "username"
Start RemoteTrx as the specified user. The switch to the new user
will happen after the log and pid files has been opened.
.TP
Expand All @@ -57,6 +57,16 @@ Specify a pid file to write the process ID into.
.TP
.BI "--config=" "configuration file"
Specify which configuration file to use.
.TP
.B --reset
Do a normal application startup, initializing everything, but exit immediately
when the initialization is done.
.TP
.B --quiet
Don't output any info messages, just warnings and errors.
.TP
.B --version
Print the application version then exit.
.
.SH FILES
.
Expand Down
16 changes: 13 additions & 3 deletions src/doc/man/svxlink.1
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
.TH SVXLINK 1 "AUGUST 2021" Linux "User Manuals"
.TH SVXLINK 1 "FEBRUARY 2025" Linux "User Manuals"
.
.SH NAME
.
svxlink \- A general purpose voice services system for ham radio use
.
.SH SYNOPSIS
.
.BI "svxlink [--help] [--daemon] [--logfile=" "log file" "] [--config=" "configuration file" "] [--pidfile=" "pid file" "] [--runasuser=" "user name" ]
.BI "svxlink [--help] [--daemon] [--quiet] [--reset] [--version] [--logfile=" "log file" "] [--config=" "configuration file" "] [--pidfile=" "pid file" "] [--runasuser=" "user name" ]
.
.SH DESCRIPTION
.
Expand All @@ -24,7 +24,7 @@ Print a help message and exit.
.B --daemon
Start the SvxLink server as a daemon.
.TP
.B --runasuser
.BI "--runasuser=" "username"
Start the SvxLink server as the specified user. The switch to the new user
will happen after the log and pid files has been opened.
.TP
Expand All @@ -37,6 +37,16 @@ Specify a pid file to write the process ID into.
.TP
.BI "--config=" "configuration file"
Specify which configuration file to use.
.TP
.B --reset
Do a normal application startup, initializing everything, but exit immediately
when the initialization is done.
.TP
.B --quiet
Don't output any info messages, just warnings and errors.
.TP
.B --version
Print the application version then exit.
.
.SH FILES
.
Expand Down
8 changes: 8 additions & 0 deletions src/svxlink/ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@
* APRS beacon and status packets changed destination to the SvxLink specific
one, "APSVX1".

* Add commandline option --reset to svxlink and remotetrx. It was added to get
an easy way to reset all SvxLink hardware to initial state. The default
systemd unit will run the application with that option set after the main
application has exited.

* Add command line option --quiet to svxlink and remotetrx. Suppress output of
info messages. Warning and error messages will still be output.



1.8.0 -- 25 Feb 2024
Expand Down
140 changes: 80 additions & 60 deletions src/svxlink/remotetrx/remotetrx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,8 @@ static char *logfile_name = NULL;
static char *runasuser = NULL;
static char *config = NULL;
static int daemonize = 0;
static int reset = 0;
static int quiet = 0;
static int logfd = -1;
static FdWatch *stdin_watch = 0;
static FdWatch *stdout_watch = 0;
Expand Down Expand Up @@ -238,80 +240,89 @@ int main(int argc, char **argv)
perror("sigaction");
exit(1);
}

int pipefd[2] = {-1, -1};
int noclose = 0;
if (logfile_name != 0)
if (quiet || (logfile_name != 0))
{
/* Open the logfile */
if (!logfile_open())
int devnull = open("/dev/null", O_RDWR);
if (devnull == -1)
{
perror("open(/dev/null)");
exit(1);
}

/* Create a pipe to route stdout through */
if (pipe(pipefd) == -1)
if (quiet)
{
perror("pipe");
exit(1);
/* Redirect stdout to /dev/null */
dup2(devnull, STDOUT_FILENO);
}
int flags = fcntl(pipefd[0], F_GETFL);
if (flags == -1)
{
perror("fcntl(..., F_GETFL)");
exit(1);
}
flags |= O_NONBLOCK;
if (fcntl(pipefd[0], F_SETFL, flags) == -1)
{
perror("fcntl(..., F_SETFL)");
exit(1);
}
stdout_watch = new FdWatch(pipefd[0], FdWatch::FD_WATCH_RD);
stdout_watch->activity.connect(sigc::ptr_fun(&stdout_handler));

/* Redirect stdout to the logpipe */
if (dup2(pipefd[1], STDOUT_FILENO) == -1)
if (logfile_name != 0)
{
perror("dup2(stdout)");
exit(1);
}
/* Open the logfile */
if (!logfile_open())
{
exit(1);
}
atexit(logfile_flush);

/* Redirect stderr to the logpipe */
if (dup2(pipefd[1], STDERR_FILENO) == -1)
{
perror("dup2(stderr)");
exit(1);
}
/* Create a pipe to route stdout and stderr through */
if (pipe(pipefd) == -1)
{
perror("pipe");
exit(1);
}
int flags = fcntl(pipefd[0], F_GETFL);
if (flags == -1)
{
perror("fcntl(..., F_GETFL)");
exit(1);
}
flags |= O_NONBLOCK;
if (fcntl(pipefd[0], F_SETFL, flags) == -1)
{
perror("fcntl(..., F_SETFL)");
exit(1);
}
stdout_watch = new FdWatch(pipefd[0], FdWatch::FD_WATCH_RD);
stdout_watch->activity.connect(sigc::ptr_fun(&stdout_handler));

// We also need to close stdin but that is not a good idea since we need
// the stdin filedescriptor to keep being allocated so that it is not
// assigned to some other random filedescriptor allocation. That would
// be very bad.
int devnull = open("/dev/null", O_RDONLY);
if (devnull == -1)
{
perror("open(/dev/null)");
exit(1);
}
if (dup2(devnull, STDIN_FILENO) == -1)
{
perror("dup2(stdin)");
exit(1);
if (!quiet)
{
/* Redirect stdout to the logpipe */
if (dup2(pipefd[1], STDOUT_FILENO) == -1)
{
perror("dup2(stdout)");
exit(1);
}

/* Force stdout to line buffered mode */
if (setvbuf(stdout, NULL, _IOLBF, 0) != 0)
{
perror("setlinebuf");
exit(1);
}
}

/* Redirect stderr to the logpipe */
if (dup2(pipefd[1], STDERR_FILENO) == -1)
{
perror("dup2(stderr)");
exit(1);
}

/* Redirect stdin to /dev/null */
if (dup2(devnull, STDIN_FILENO) == -1)
{
perror("dup2(stdin)");
exit(1);
}

/* Tell the daemon function call not to close the file descriptors */
noclose = 1;
}
close(devnull);

/* Force stdout to line buffered mode */
if (setvbuf(stdout, NULL, _IOLBF, 0) != 0)
{
perror("setlinebuf");
exit(1);
}

atexit(logfile_flush);

/* Tell the daemon function call not to close the file descriptors */
noclose = 1;
}

if (daemonize)
Expand Down Expand Up @@ -556,9 +567,14 @@ int main(int argc, char **argv)
trx_handlers.push_back(trx_handler);
cout << endl;
}

if (!trx_handlers.empty())
{
if (reset)
{
std::cout << "Initialization done. Exiting." << std::endl;
Async::Application::app().quit();
}
app.exec();
}
else
Expand Down Expand Up @@ -642,6 +658,10 @@ static void parse_arguments(int argc, const char **argv)
*/
{"daemon", 0, POPT_ARG_NONE, &daemonize, 0,
"Start " PROGRAM_NAME " as a daemon", NULL},
{"reset", 0, POPT_ARG_NONE, &reset, 0,
"Initialize all hardware to initial state then quit", NULL},
{"quiet", 0, POPT_ARG_NONE, &quiet, 0,
"Don't print any info messages, just warnings and errors", NULL},
{"version", 0, POPT_ARG_NONE, &print_version, 0,
"Print the application version string", NULL},
{NULL, 0, 0, NULL, 0}
Expand Down
20 changes: 11 additions & 9 deletions src/svxlink/svxlink/LinkManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,14 +191,14 @@ bool LinkManager::initialize(Async::Config &cfg,
link.logic_props.insert(make_pair(logic_name, logic_props));
if (!ret.second)
{
cout << "*** ERROR: Duplicate logic \"" << logic_name
cerr << "*** ERROR: Duplicate logic \"" << logic_name
<< "\" specified in link \"" << link.name << "\".\n";
init_ok = false;
}
}
else
{
cout << "*** ERROR: Bad configuration for " << link.name
cerr << "*** ERROR: Bad configuration for " << link.name
<< "/CONNECT_LOGICS=" << connect_logics << ". Legal format: "
<< "<logic name>:<command>:<announcement name>,...\n";
init_ok = false;
Expand Down Expand Up @@ -234,8 +234,8 @@ bool LinkManager::initialize(Async::Config &cfg,
// while so the TIMEOUT configuration variable must be set.
if (timeout <= 0)
{
cout << "*** WARNING: missing param " << link.name
<< "/TIMEOUT=??, set to default (30 sec)\n";
std::cerr << "*** WARNING: missing param " << link.name
<< "/TIMEOUT=??, set to default (30 sec)" << std::endl;
timeout = 30;
}
}
Expand Down Expand Up @@ -345,8 +345,9 @@ void LinkManager::addLogic(LogicBase *logic)
LinkCmd *link_cmd = new LinkCmd(cmd_logic, link);
if (!link_cmd->initialize(logic_props.cmd))
{
cout << "*** WARNING: Can not setup command " << logic_props.cmd
<< " for the logic " << logic->name() << endl;
std::cerr << "*** WARNING: Can not setup command "
<< logic_props.cmd << " for the logic " << logic->name()
<< std::endl;
}
}
}
Expand Down Expand Up @@ -429,9 +430,10 @@ void LinkManager::allLogicsStarted(void)
const string &logic_name = (*prop_it).first;
if (logic_map.find(logic_name) == logic_map.end())
{
cout << "*** WARNING: Logic " << logic_name
<< " has been specified in logic link " << link.name
<< " but that logic is missing. Removing logic from link.\n";
std::cerr << "*** WARNING: Logic " << logic_name
<< " has been specified in logic link " << link.name
<< " but that logic is missing. Removing logic from link."
<< std::endl;
LogicPropMap::iterator remove_it = prop_it++;
link.logic_props.erase(remove_it);
}
Expand Down
Loading

0 comments on commit d797049

Please sign in to comment.