1 /* SPDX-License-Identifier: GPL-2.0-only */
2 /*
3 * xenwatchdogd.c
4 *
5 * Watchdog based on Xen hypercall watchdog interface.
6 *
7 * Copyright 2010 Citrix Ltd
8 * Copyright 2024 Leigh Brown <leigh@solinno.co.uk>
9 *
10 */
11
12 #include <err.h>
13 #include <limits.h>
14 #include "xenctrl.h"
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <fcntl.h>
18 #include <stdlib.h>
19 #include <unistd.h>
20 #include <signal.h>
21 #include <stdio.h>
22 #include <stdbool.h>
23 #include <getopt.h>
24
25 #define WDOG_MIN_TIMEOUT 2
26 #define WDOG_MIN_SLEEP 1
27 #define WDOG_EXIT_TIMEOUT 300
28
29 static xc_interface *h;
30 static volatile bool safeexit = false;
31 static volatile bool done = false;
32
daemonize(void)33 static void daemonize(void)
34 {
35 switch (fork()) {
36 case -1:
37 err(EXIT_FAILURE, "fork");
38 case 0:
39 break;
40 default:
41 exit(EXIT_SUCCESS);
42 }
43 umask(0);
44 if (setsid() < 0)
45 err(EXIT_FAILURE, "setsid");
46 if (chdir("/") < 0)
47 err(EXIT_FAILURE, "chdir /");
48 if (freopen("/dev/null", "r", stdin) == NULL)
49 err(EXIT_FAILURE, "reopen stdin");
50 if(freopen("/dev/null", "w", stdout) == NULL)
51 err(EXIT_FAILURE, "reopen stdout");
52 if(freopen("/dev/null", "w", stderr) == NULL)
53 err(EXIT_FAILURE, "reopen stderr");
54 }
55
catch_exit(int sig)56 static void catch_exit(int sig)
57 {
58 done = true;
59 }
60
catch_usr1(int sig)61 static void catch_usr1(int sig)
62 {
63 safeexit = true;
64 done = true;
65 }
66
usage(int exit_code)67 static void __attribute__((noreturn)) usage(int exit_code)
68 {
69 FILE *out = exit_code ? stderr : stdout;
70
71 fprintf(out,
72 "Usage: xenwatchdog [OPTION]... <timeout> [<sleep>]\n"
73 " -h, --help\t\tDisplay this help text and exit.\n"
74 " -F, --foreground\tRun in foreground.\n"
75 " -x, --safe-exit\tDisable watchdog on orderly exit.\n"
76 "\t\t\tNote: default is to set a %d second timeout on exit.\n\n"
77 " timeout\t\tInteger seconds to arm the watchdog each time.\n"
78 "\t\t\tNote: minimum timeout is %d seconds.\n\n"
79 " sleep\t\t\tInteger seconds to sleep between arming the watchdog.\n"
80 "\t\t\tNote: sleep must be at least %d and less than timeout.\n"
81 "\t\t\tIf not specified then set to half the timeout.\n",
82 WDOG_EXIT_TIMEOUT, WDOG_MIN_TIMEOUT, WDOG_MIN_SLEEP
83 );
84 exit(exit_code);
85 }
86
parse_secs(const char * arg,const char * what)87 static int parse_secs(const char *arg, const char *what)
88 {
89 char *endptr;
90 unsigned long val;
91
92 val = strtoul(arg, &endptr, 0);
93 if (val > INT_MAX || *endptr)
94 errx(EXIT_FAILURE, "invalid %s: '%s'", what, arg);
95
96 return val;
97 }
98
main(int argc,char ** argv)99 int main(int argc, char **argv)
100 {
101 int id;
102 int t, s;
103 int ret;
104 bool daemon = true;
105
106 for ( ;; )
107 {
108 int option_index = 0, c;
109 static const struct option long_options[] =
110 {
111 { "help", no_argument, NULL, 'h' },
112 { "foreground", no_argument, NULL, 'F' },
113 { "safe-exit", no_argument, NULL, 'x' },
114 { NULL, 0, NULL, 0 },
115 };
116
117 c = getopt_long(argc, argv, "hFxD", long_options, &option_index);
118 if (c == -1)
119 break;
120
121 switch (c)
122 {
123 case 'h':
124 usage(EXIT_SUCCESS);
125
126 case 'F':
127 daemon = false;
128 break;
129
130 case 'x':
131 safeexit = true;
132 break;
133
134 default:
135 usage(EXIT_FAILURE);
136 }
137 }
138
139 if (argc - optind < 1)
140 errx(EXIT_FAILURE, "timeout must be specified");
141
142 if (argc - optind > 2)
143 errx(EXIT_FAILURE, "too many arguments");
144
145 t = parse_secs(argv[optind], "timeout");
146 if (t < WDOG_MIN_TIMEOUT)
147 errx(EXIT_FAILURE, "Error: timeout must be at least %d seconds",
148 WDOG_MIN_TIMEOUT);
149
150 ++optind;
151 if (optind < argc) {
152 s = parse_secs(argv[optind], "sleep");
153 if (s < WDOG_MIN_SLEEP)
154 errx(EXIT_FAILURE, "Error: sleep must be no less than %d",
155 WDOG_MIN_SLEEP);
156 if (s >= t)
157 errx(EXIT_FAILURE, "Error: sleep must be less than timeout");
158 }
159 else
160 s = t / 2;
161
162 if (daemon)
163 daemonize();
164
165 h = xc_interface_open(NULL, NULL, 0);
166 if (h == NULL)
167 err(EXIT_FAILURE, "xc_interface_open");
168
169 if (signal(SIGHUP, &catch_exit) == SIG_ERR)
170 err(EXIT_FAILURE, "signal");
171 if (signal(SIGINT, &catch_exit) == SIG_ERR)
172 err(EXIT_FAILURE, "signal");
173 if (signal(SIGQUIT, &catch_exit) == SIG_ERR)
174 err(EXIT_FAILURE, "signal");
175 if (signal(SIGTERM, &catch_exit) == SIG_ERR)
176 err(EXIT_FAILURE, "signal");
177 if (signal(SIGUSR1, &catch_usr1) == SIG_ERR)
178 err(EXIT_FAILURE, "signal");
179
180 id = xc_watchdog(h, 0, t);
181 if (id <= 0)
182 err(EXIT_FAILURE, "xc_watchdog setup");
183
184 while (!done) {
185 sleep(s);
186 ret = xc_watchdog(h, id, t);
187 if (ret != 0)
188 err(EXIT_FAILURE, "xc_watchdog");
189 }
190
191 // Zero seconds timeout will disarm the watchdog timer
192 xc_watchdog(h, id, safeexit ? 0 : WDOG_EXIT_TIMEOUT);
193 return 0;
194 }
195