asuswrt-merlin.ng/release/src-rt-5.04axhnd.675x/router-sysdep.rt-ax82u_v2/wdtctl/wdtctl.c
2023-09-21 00:22:26 +02:00

412 lines
12 KiB
C

/***********************************************************************
*
* Copyright (c) 2007-2016 Broadcom Corporation
* All Rights Reserved
*
<:label-BRCM:2016:DUAL/GPL:standard
Unless you and Broadcom execute a separate written software license
agreement governing use of this software, this software is licensed
to you under the terms of the GNU General Public License version 2
(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php,
with the following added to such license:
As a special exception, the copyright holders of this software give
you permission to link this software with independent modules, and
to copy and distribute the resulting executable under terms of your
choice, provided that you also meet, for each linked independent
module, the terms and conditions of the license of that module.
An independent module is a module which is not derived from this
software. The special exception does not apply to any modifications
of the software.
Not withstanding the above, under no circumstances may you combine
this software in any way with any other Broadcom software provided
under a license other than the GPL, without Broadcom's express prior
written consent.
:>
*
************************************************************************/
#include <stdio.h>
#include <string.h>
#include <linux/watchdog.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <syslog.h>
#include <signal.h>
#include <errno.h>
#define WDT_DBG 0
#define MIN_TIMEOUT_SECS 4
#define MAX_TIMEOUT_SECS 80
#define DEFAULT_TIMEOUT_SECS MAX_TIMEOUT_SECS
#define WDTD_SLEEP_DIV 4
#define WDTD_NAME "wdtd"
#define WDTD_DEVICE_NAME "/dev/watchdog"
#define WDTD_PID_FILE "/var/run/wdtd.pid"
#define EXIT_SUCCESS 0
#define EXIT_FAILURE 1
// Global file descriptor into the watchdog device in kernel
int wd_fd = -1;
unsigned int gtimeout = DEFAULT_TIMEOUT_SECS;
// Macros to make the important actions of this app more visible.
// Assumes global vars wd_fd and gtimeout are set appropriately.
#define WD_OPEN() wd_open()
#define WD_START() ioctl(wd_fd, WDIOC_SETTIMEOUT, &gtimeout)
#define WD_PING() write(wd_fd, "\0", 1)
#define WD_STOP() {int opts = WDIOS_DISABLECARD; \
ioctl(wd_fd, WDIOC_SETOPTIONS, &opts); }
static void wd_open()
{
if (wd_fd != -1)
{
// Code path trying to open twice. Just use the existing fd.
printf("wdtctl: (internal error) wd_fd is already opened at %d\n", wd_fd);
return;
}
// Just opening the device also pings it (assuming someone had already
// opened it and set a timeout). So for the "ping" operation,
// all we do is open. (But maybe more clear if we also ping it.)
wd_fd = open(WDTD_DEVICE_NAME, O_WRONLY);
if (wd_fd == -1)
{
if (errno == EBUSY)
{
printf("Could not open %s (EBUSY). Is there a wdtd running?\n",
WDTD_DEVICE_NAME);
}
else
{
perror("watchdog");
}
exit(EXIT_FAILURE);
}
}
void usage(char * appName)
{
printf("Usage: %s [-d] [-t <timeout>] start \n", appName);
printf(" %s [-d] [-p <pid of wdtd daemon>] stop \n", appName);
printf(" %s ping \n", appName);
printf(" \n" );
printf("options: \n" );
printf(" -t : Specify a timeout in [%d-%d] seconds for the watchdog timer \n", MIN_TIMEOUT_SECS, MAX_TIMEOUT_SECS);
printf(" -d : if used with start, start the wdtd daemon which pings the watchdog\n" );
printf(" every timeout/4 seconds. \n" );
printf(" : if used with stop, automatically find the pid of the wdtd daemon\n");
printf(" and stop it, which also stops the watchdog timer itself.\n");
printf(" -p : stop the wdtd daemon which has the specified pid \n" );
printf(" This is the old way, using -d stop is easier. \n");
printf(" \n" );
printf("Note: \n" );
printf(" The watchdog timer can be controlled using 2 mechanisms: \n" );
printf(" (1) Direct control: In this method the watchdog is \n" );
printf(" directly controlled by wdtctl \n" );
printf(" \n" );
printf(" %s -t 10 start \n", appName);
printf(" %s ping \n", appName);
printf(" %s ping \n", appName);
printf(" ... \n" );
printf(" %s stop \n", appName);
printf(" \n" );
printf(" (2) Daemonized: In this method the watchdog is pinged by the wdtd\n" );
printf(" daemon which runs every timeout/4 seconds \n" );
printf(" %s -d -t 10 start \n", appName);
printf(" ... \n" );
printf(" %s -d stop (or... below) \n", appName);
printf(" %s -p <pid of wdtd daemon> stop (this is the old way, using -d stop is easier) \n", appName);
exit(1);
}
// Write out the pid of the wdtd daemon to a pid file so it is easier to
// find it and kill it later. Assumes there is not wdtd daemon running
// already, which should be safe assumption since app will try to open the
// /dev/watchdog file and if wdtd daemon is already running, the open will fail.
void write_wdpid()
{
int fd;
char buf[32]={0};
// Maybe could get fancier and look up the pid in /proc to make sure it
// is not running.
unlink(WDTD_PID_FILE);
fd = open(WDTD_PID_FILE, O_RDWR|O_CREAT, 0644);
if (fd < 0)
{
fprintf(stderr, "%s: could not open pid file!\n", WDTD_NAME);
return;
}
snprintf(buf, sizeof(buf), "%d", getpid());
write(fd, buf, strlen(buf));
close(fd);
return;
}
// Return the pid of the wdtd daemon, or -1 if not found.
int get_wdpid()
{
int fd;
int rc;
int pid = -1;
char buf[32]={0};
fd = open(WDTD_PID_FILE, O_RDONLY);
if (fd < 0)
{
return -1;
}
rc = read(fd, buf, sizeof(buf));
if (rc > 0)
{
pid = atoi(buf);
}
close(fd);
return pid;
}
void sighandler (int signum, siginfo_t *info, void *context)
{
if (info->si_signo == SIGHUP)
fprintf(stderr, "%s: Recieved SIGHUP from pid %u\n",
WDTD_NAME, info->si_pid);
else
fprintf(stderr, "%s: Recieved signal %d from pid %u\n",
WDTD_NAME, info->si_signo, info->si_pid);
WD_STOP();
unlink(WDTD_PID_FILE);
fprintf(stderr, "%s: stopped watchdog timer and exit.\n", WDTD_NAME);
exit(EXIT_SUCCESS);
}
void install_sighandler (void)
{
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_sigaction = sighandler;
sa.sa_flags = SA_SIGINFO;
sigaction(SIGHUP, &sa, NULL);
}
void daemonize(char * name)
{
pid_t pid, sid;
int fd;
pid = fork();
if (pid < 0)
{
fprintf(stderr, "%s: failed to fork/daemonize!\n", WDTD_NAME);
exit(EXIT_FAILURE);
}
if (pid > 0)
{
// Parent is done, just exit now.
exit(EXIT_SUCCESS);
}
/*
* At this point we are executing as the child process.
*/
fprintf(stderr, "%s started at pid %d (timeout=%d seconds)\n",
WDTD_NAME, getpid(), gtimeout);
/* Init signal handler */
install_sighandler();
#if WDT_DBG
setlogmask (LOG_UPTO (LOG_NOTICE));
openlog (WDTD_NAME, LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1);
syslog (LOG_NOTICE,
"Daemon started with ping interval = %ds, gtimeout = %ds",
gtimeout/WDTD_SLEEP_DIV, gtimeout);
#endif
/* Create a new SID for the child process */
sid = setsid();
if (sid < 0)
{
exit(EXIT_FAILURE);
}
/* Change the process name */
sprintf(name, WDTD_NAME);
/* Change the current working directory. */
if ((chdir("/")) < 0)
{
exit(EXIT_FAILURE);
}
/* Handle opened filed descriptors */
fd = open("/dev/null",O_RDWR, 0);
if (fd != -1)
{
dup2 (fd, STDIN_FILENO);
dup2 (fd, STDOUT_FILENO);
// Do not close stderr on daemon so it can still print some info
// dup2 (fd, STDERR_FILENO);
if (fd > 2)
{
close (fd);
}
}
/* Write out my pidfile */
write_wdpid();
/*resetting File Creation Mask */
umask(0);
/* Ping watchdog every x seconds */
while(1)
{
#if WDT_DBG
syslog (LOG_NOTICE, "Pinging Watchdog every %ds", gtimeout/WDTD_SLEEP_DIV);
#endif
WD_PING();
sleep(gtimeout/WDTD_SLEEP_DIV);
}
}
int main(int argc, char *argv[])
{
int c, i;
int daemon_mode = 0;
int pid = -1;
int ret = 0;
/* Basic argument check */
if (argc < 2)
{
usage(argv[0]);
}
// ping is very easy to handle, just do that first and exit.
if (!strcmp(argv[argc-1], "ping"))
{
if (argc != 2) // ping takes no options
{
usage(argv[0]);
}
WD_OPEN();
WD_PING();
exit(EXIT_SUCCESS);
}
// parse args
while ( (c = getopt(argc, argv, "dp:t:")) != -1)
{
switch (c)
{
case 'd':
{
daemon_mode = 1; // used in both start and stop
break;
}
case 't':
{
if (strcmp(argv[argc-1], "start")) // used only for start
usage(argv[0]);
gtimeout = atoi(optarg);
if ( gtimeout > MAX_TIMEOUT_SECS || gtimeout < MIN_TIMEOUT_SECS )
{
printf("Invalid Timeout value %d!\n", gtimeout);
usage(argv[0]);
}
break;
}
case 'p':
{
if (strcmp(argv[argc-1], "stop")) // used only for stop
usage(argv[0]);
pid = atoi(optarg);
break;
}
default:
usage(argv[0]);
}
}
// There is only two possible commands at this point, start or stop.
if (!strcmp(argv[argc-1], "start"))
{
// If we are able to open the watchdog device, that means there is no
// other wdtd daemon running. So this acts as a check for daemon_mode.
WD_OPEN();
WD_START(); // uses global var gtimeout
/* Start wdtd daemon */
if (daemon_mode)
{
for( i=0; i<argc; i++)
{
memset( argv[i], '\0', strlen(argv[i]));
}
daemonize(argv[0]);
}
else
{
printf("Started watchdog timer (%d seconds)\n", gtimeout);
}
}
else
{
// This must be the STOP command
if (pid != -1)
{
kill(pid, SIGHUP);
}
else if (daemon_mode)
{
pid = get_wdpid();
if (pid == -1)
{
printf("Could not find pid to kill\n");
exit(EXIT_FAILURE);
}
kill(pid, SIGHUP);
}
else
{
WD_OPEN();
WD_STOP();
printf("Stopped watchdog timer\n");
}
}
return ret;
}