3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering/***
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering This file is part of systemd.
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering Copyright 2012 Lennart Poettering
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering systemd is free software; you can redistribute it and/or modify it
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering under the terms of the GNU Lesser General Public License as published by
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering (at your option) any later version.
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering systemd is distributed in the hope that it will be useful, but
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering Lesser General Public License for more details.
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering You should have received a copy of the GNU Lesser General Public License
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering***/
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering#include <errno.h>
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering#include <fcntl.h>
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering#include <sys/ioctl.h>
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering#include <syslog.h>
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering#include <unistd.h>
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering#include <linux/watchdog.h>
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering#include "fd-util.h"
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering#include "log.h"
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering#include "time-util.h"
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering#include "watchdog.h"
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poetteringstatic int watchdog_fd = -1;
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poetteringstatic usec_t watchdog_timeout = USEC_INFINITY;
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poetteringstatic int update_timeout(void) {
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering int r;
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering if (watchdog_fd < 0)
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering return 0;
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering
a92ccc5ba22ec40fee560a46c478321d1c5df5afLennart Poettering if (watchdog_timeout == USEC_INFINITY)
a92ccc5ba22ec40fee560a46c478321d1c5df5afLennart Poettering return 0;
a92ccc5ba22ec40fee560a46c478321d1c5df5afLennart Poettering else if (watchdog_timeout == 0) {
a92ccc5ba22ec40fee560a46c478321d1c5df5afLennart Poettering int flags;
a92ccc5ba22ec40fee560a46c478321d1c5df5afLennart Poettering
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering flags = WDIOS_DISABLECARD;
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags);
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering if (r < 0)
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering return log_warning_errno(errno, "Failed to disable hardware watchdog: %m");
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering } else {
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering int sec, flags;
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering char buf[FORMAT_TIMESPAN_MAX];
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering sec = (int) ((watchdog_timeout + USEC_PER_SEC - 1) / USEC_PER_SEC);
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering r = ioctl(watchdog_fd, WDIOC_SETTIMEOUT, &sec);
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering if (r < 0)
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering return log_warning_errno(errno, "Failed to set timeout to %is: %m", sec);
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering watchdog_timeout = (usec_t) sec * USEC_PER_SEC;
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering log_info("Set hardware watchdog to %s.", format_timespan(buf, sizeof(buf), watchdog_timeout, 0));
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering
3d7415f43f0fe6a821d7bc4a341ba371e8a30ef3Lennart Poettering 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("/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_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);
}