Systemd/src/shared/watchdog.c
Lennart Poettering 818bf54632 tree-wide: drop 'This file is part of systemd' blurb
This part of the copyright blurb stems from the GPL use recommendations:

https://www.gnu.org/licenses/gpl-howto.en.html

The concept appears to originate in times where version control was per
file, instead of per tree, and was a way to glue the files together.
Ultimately, we nowadays don't live in that world anymore, and this
information is entirely useless anyway, as people are very welcome to
copy these files into any projects they like, and they shouldn't have to
change bits that are part of our copyright header for that.

hence, let's just get rid of this old cruft, and shorten our codebase a
bit.
2018-06-14 10:20:20 +02:00

158 lines
4.5 KiB
C

/* SPDX-License-Identifier: LGPL-2.1+ */
/***
Copyright 2012 Lennart Poettering
***/
#include <errno.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <syslog.h>
#include <unistd.h>
#include <linux/watchdog.h>
#include "fd-util.h"
#include "log.h"
#include "string-util.h"
#include "time-util.h"
#include "watchdog.h"
static int watchdog_fd = -1;
static char *watchdog_device = NULL;
static usec_t watchdog_timeout = USEC_INFINITY;
static int update_timeout(void) {
int r;
if (watchdog_fd < 0)
return 0;
if (watchdog_timeout == USEC_INFINITY)
return 0;
else if (watchdog_timeout == 0) {
int flags;
flags = WDIOS_DISABLECARD;
r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags);
if (r < 0)
return log_warning_errno(errno, "Failed to disable hardware watchdog: %m");
} else {
int sec, flags;
char buf[FORMAT_TIMESPAN_MAX];
sec = (int) DIV_ROUND_UP(watchdog_timeout, USEC_PER_SEC);
r = ioctl(watchdog_fd, WDIOC_SETTIMEOUT, &sec);
if (r < 0)
return log_warning_errno(errno, "Failed to set timeout to %is: %m", sec);
watchdog_timeout = (usec_t) sec * USEC_PER_SEC;
log_info("Set hardware watchdog to %s.", format_timespan(buf, sizeof(buf), watchdog_timeout, 0));
flags = WDIOS_ENABLECARD;
r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags);
if (r < 0) {
/* ENOTTY means the watchdog is always enabled so we're fine */
log_full(errno == ENOTTY ? LOG_DEBUG : LOG_WARNING,
"Failed to enable hardware watchdog: %m");
if (errno != ENOTTY)
return -errno;
}
r = ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0);
if (r < 0)
return log_warning_errno(errno, "Failed to ping hardware watchdog: %m");
}
return 0;
}
static int open_watchdog(void) {
struct watchdog_info ident;
if (watchdog_fd >= 0)
return 0;
watchdog_fd = open(watchdog_device ?: "/dev/watchdog",
O_WRONLY|O_CLOEXEC);
if (watchdog_fd < 0)
return -errno;
if (ioctl(watchdog_fd, WDIOC_GETSUPPORT, &ident) >= 0)
log_info("Hardware watchdog '%s', version %x",
ident.identity,
ident.firmware_version);
return update_timeout();
}
int watchdog_set_device(char *path) {
return free_and_strdup(&watchdog_device, path);
}
int watchdog_set_timeout(usec_t *usec) {
int r;
watchdog_timeout = *usec;
/* If we didn't open the watchdog yet and didn't get any
* explicit timeout value set, don't do anything */
if (watchdog_fd < 0 && watchdog_timeout == USEC_INFINITY)
return 0;
if (watchdog_fd < 0)
r = open_watchdog();
else
r = update_timeout();
*usec = watchdog_timeout;
return r;
}
int watchdog_ping(void) {
int r;
if (watchdog_fd < 0) {
r = open_watchdog();
if (r < 0)
return r;
}
r = ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0);
if (r < 0)
return log_warning_errno(errno, "Failed to ping hardware watchdog: %m");
return 0;
}
void watchdog_close(bool disarm) {
int r;
if (watchdog_fd < 0)
return;
if (disarm) {
int flags;
/* Explicitly disarm it */
flags = WDIOS_DISABLECARD;
r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags);
if (r < 0)
log_warning_errno(errno, "Failed to disable hardware watchdog: %m");
/* To be sure, use magic close logic, too */
for (;;) {
static const char v = 'V';
if (write(watchdog_fd, &v, 1) > 0)
break;
if (errno != EINTR) {
log_error_errno(errno, "Failed to disarm watchdog timer: %m");
break;
}
}
}
watchdog_fd = safe_close(watchdog_fd);
}