jail: don't load libpreload-seccomp.so if it doesn't exist
[oweals/procd.git] / watchdog.c
1 /*
2  * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
3  * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU Lesser General Public License version 2.1
7  * as published by the Free Software Foundation
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  */
14
15 #include <linux/watchdog.h>
16
17 #include <sys/ioctl.h>
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <fcntl.h>
21
22 #include <unistd.h>
23
24 #include <libubox/uloop.h>
25
26 #include "procd.h"
27 #include "watchdog.h"
28
29 #define WDT_PATH        "/dev/watchdog"
30
31 static struct uloop_timeout wdt_timeout;
32 static int wdt_fd = -1;
33 static int wdt_drv_timeout = 30;
34 static int wdt_frequency = 5;
35 static bool wdt_magicclose = false;
36
37 void watchdog_ping(void)
38 {
39         DEBUG(4, "Ping\n");
40         if (wdt_fd >= 0 && write(wdt_fd, "X", 1) < 0)
41                 ERROR("WDT failed to write: %m\n");
42 }
43
44 static void watchdog_timeout_cb(struct uloop_timeout *t)
45 {
46         watchdog_ping();
47         uloop_timeout_set(t, wdt_frequency * 1000);
48 }
49
50 static int watchdog_open(bool cloexec)
51 {
52         char *env = getenv("WDTFD");
53
54         if (wdt_fd >= 0)
55                 return wdt_fd;
56
57         if (env) {
58                 DEBUG(2, "Watchdog handover: fd=%s\n", env);
59                 wdt_fd = atoi(env);
60                 unsetenv("WDTFD");
61         } else {
62                 wdt_fd = open(WDT_PATH, O_WRONLY);
63         }
64
65         if (wdt_fd < 0)
66                 return wdt_fd;
67
68         if (cloexec)
69                 fcntl(wdt_fd, F_SETFD, fcntl(wdt_fd, F_GETFD) | FD_CLOEXEC);
70
71         return wdt_fd;
72 }
73
74 static void watchdog_close(void)
75 {
76         if (wdt_fd < 0)
77                 return;
78
79         if (write(wdt_fd, "V", 1) < 0)
80                 ERROR("WDT failed to write release: %m\n");
81
82         if (close(wdt_fd) == -1)
83                 ERROR("WDT failed to close watchdog: %m\n");
84
85         wdt_fd = -1;
86 }
87
88 static int watchdog_set_drv_timeout(void)
89 {
90         if (wdt_fd < 0)
91                 return -1;
92
93         return ioctl(wdt_fd, WDIOC_SETTIMEOUT, &wdt_drv_timeout);
94 }
95
96 void watchdog_set_magicclose(bool val)
97 {
98         wdt_magicclose = val;
99 }
100
101 bool watchdog_get_magicclose(void)
102 {
103         return wdt_magicclose;
104 }
105
106 void watchdog_set_stopped(bool val)
107 {
108         if (val) {
109                 uloop_timeout_cancel(&wdt_timeout);
110
111                 if (wdt_magicclose)
112                         watchdog_close();
113         }
114         else {
115                 watchdog_open(true);
116                 watchdog_set_drv_timeout();
117                 watchdog_timeout_cb(&wdt_timeout);
118         }
119 }
120
121 bool watchdog_get_stopped(void)
122 {
123         return !wdt_timeout.pending;
124 }
125
126 int watchdog_timeout(int timeout)
127 {
128         if (timeout) {
129                 DEBUG(4, "Set watchdog timeout: %ds\n", timeout);
130                 wdt_drv_timeout = timeout;
131
132                 if (wdt_fd >= 0)
133                         watchdog_set_drv_timeout();
134         }
135
136         return wdt_drv_timeout;
137 }
138
139 int watchdog_frequency(int frequency)
140 {
141         if (frequency) {
142                 DEBUG(4, "Set watchdog frequency: %ds\n", frequency);
143                 wdt_frequency = frequency;
144         }
145
146         return wdt_frequency;
147 }
148
149 char* watchdog_fd(void)
150 {
151         static char fd_buf[12];
152
153         if (wdt_fd < 0)
154                 return NULL;
155
156         snprintf(fd_buf, sizeof(fd_buf), "%d", wdt_fd);
157
158         return fd_buf;
159 }
160
161 void watchdog_init(int preinit)
162 {
163         wdt_timeout.cb = watchdog_timeout_cb;
164
165         if (watchdog_open(!preinit) < 0)
166                 return;
167
168         LOG("- watchdog -\n");
169         watchdog_set_drv_timeout();
170         watchdog_timeout_cb(&wdt_timeout);
171
172         DEBUG(4, "Opened watchdog with timeout %ds\n", watchdog_timeout(0));
173 }
174
175
176 void watchdog_set_cloexec(bool val)
177 {
178         if (wdt_fd < 0)
179                 return;
180
181         int flags = fcntl(wdt_fd, F_GETFD);
182         if (val)
183                 flags |= FD_CLOEXEC;
184         else
185                 flags &= ~FD_CLOEXEC;
186         fcntl(wdt_fd, F_SETFD,  flags);
187 }