1    	/*
2    	 * Copyright (C) 2013 Lars Marowsky-Bree <lmb@suse.com>
3    	 *
4    	 * This program is free software; you can redistribute it and/or
5    	 * modify it under the terms of the GNU General Public
6    	 * License as published by the Free Software Foundation; either
7    	 * version 2 of the License, or (at your option) any later version.
8    	 *
9    	 * This software 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 GNU
12   	 * General Public License for more details.
13   	 *
14   	 * You should have received a copy of the GNU General Public License along
15   	 * with this program; if not, write to the Free Software Foundation, Inc.,
16   	 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17   	 */
18   	
19   	#include "sbd.h"
20   	#ifdef __GLIBC__
21   	#include <sys/sysmacros.h>
22   	#endif
23   	#include <dirent.h>
24   	#include <limits.h>
25   	
26   	/* possibly tunable defaults regarding watchdog operation
27   	   are found in sbd-common.c
28   	 */
29   	
30   	/* Global, non-tunable variables: */
31   	int  watchdogfd                         = -1;
32   	char *watchdogdev                       = NULL;
33   	bool watchdogdev_is_default             = false;
34   	bool do_calculate_timeout_watchdog_warn = true;
35   	int  timeout_watchdog_warn =
36   	        calculate_timeout_watchdog_warn(SBD_WATCHDOG_TIMEOUT_DEFAULT);
37   	
38   	#define MAX_WATCHDOGS 64
39   	#define SYS_CLASS_WATCHDOG "/sys/class/watchdog"
40   	#define SYS_CHAR_DEV_DIR "/sys/dev/char"
41   	#define WATCHDOG_NODEDIR "/dev/"
42   	
43   	static bool
44   	is_watchdog(dev_t device)
45   	{
46   	    static int num_watchdog_devs = 0;
47   	    static dev_t watchdog_devs[MAX_WATCHDOGS];
48   	    struct dirent *entry;
49   	    int i;
50   	
51   	    /* populate on first call */
52   	    if (num_watchdog_devs == 0) {
53   	        DIR *dp;
54   	
55   	        watchdog_devs[0] = makedev(10,130);
56   	        num_watchdog_devs = 1;
57   	
58   	        /* get additional devices from /sys/class/watchdog */
59   	        dp = opendir(SYS_CLASS_WATCHDOG);
60   	        if (dp) {
61   	            while ((entry = readdir(dp))) {
62   	                if (entry->d_type == DT_LNK) {
63   	                    FILE *file;
64   	                    char entry_name[NAME_MAX+sizeof(SYS_CLASS_WATCHDOG)+5];
65   	
66   	                    snprintf(entry_name, sizeof(entry_name),
67   	                             SYS_CLASS_WATCHDOG "/%s/dev", entry->d_name);
68   	                    file = fopen(entry_name, "r");
69   	                    if (file) {
70   	                        int major, minor;
71   	
72   	                        if (fscanf(file, "%d:%d", &major, &minor) == 2) {
73   	                            watchdog_devs[num_watchdog_devs++] =
74   	                                makedev(major, minor);
75   	                        }
76   	                        fclose(file);
77   	                        if (num_watchdog_devs == MAX_WATCHDOGS) {
78   	                            break;
79   	                        }
80   	                    }
81   	                }
82   	            }
83   	            closedir(dp);
84   	        }
85   	    }
86   	
87   	    for (i=0; i < num_watchdog_devs; i++) {
88   	        if (device == watchdog_devs[i]) {
89   	            return true;
90   	        }
91   	    }
92   	    return false;
93   	}
94   	
95   	static int
96   	watchdog_init_interval_fd(int wdfd, int timeout)
97   	{
98   	    if (ioctl(wdfd, WDIOC_SETTIMEOUT, &timeout) < 0) {
99   	        cl_perror( "WDIOC_SETTIMEOUT"
100  	                   ": Failed to set watchdog timer to %d seconds.",
101  	                   timeout);
102  	        cl_log(LOG_CRIT, "Please validate your watchdog configuration!");
103  	        cl_log(LOG_CRIT, "Choose a different watchdog driver or specify "
104  	                         "-T to skip this if you are completely sure.");
105  	        return -1;
106  	    }
107  	    return 0;
108  	}
109  	
110  	int
111  	watchdog_init_interval(void)
112  	{
113  	    if (watchdogfd < 0) {
114  	        return 0;
115  	    }
116  	
117  	    if (watchdog_set_timeout == 0) {
118  	        cl_log(LOG_INFO,
119  	               "NOT setting watchdog timeout on explicit user request!");
120  	        return 0;
121  	    }
122  	
123  	    if (watchdog_init_interval_fd(watchdogfd, timeout_watchdog) < 0) {
124  	        return -1;
125  	    }
126  	    cl_log(LOG_INFO, "Set watchdog timeout to %d seconds.", timeout_watchdog);
127  	    return 0;
128  	}
129  	
130  	static int
131  	watchdog_tickle_fd(int wdfd, char *wddev)
132  	{
133  	    if (write(wdfd, "", 1) != 1) {
134  	            cl_perror("Watchdog write failure: %s!", wddev);
135  	            return -1;
136  	        }
137  	    return 0;
138  	}
139  	
140  	int
141  	watchdog_tickle(void)
142  	{
143  	    if (watchdogfd >= 0) {
144  	        return watchdog_tickle_fd(watchdogfd, watchdogdev);
145  	    }
146  	    return 0;
147  	}
148  	
149  	static int
150  	watchdog_init_fd(char *wddev, int timeout)
151  	{
152  	    int wdfd;
153  	
154  	    wdfd = open(wddev, O_WRONLY);
155  	    if (wdfd >= 0) {
156  	        if (((timeout >= 0) &&
157  	             (watchdog_init_interval_fd(wdfd, timeout) < 0)) ||
158  	            (watchdog_tickle_fd(wdfd, wddev) < 0)) {
159  	            close(wdfd);
160  	            return -1;
161  	        }
162  	    } else {
163  	        struct stat statbuf;
164  	
165  	        if(!stat(wddev, &statbuf) && S_ISCHR(statbuf.st_mode) &&
166  	           is_watchdog(statbuf.st_rdev)) {
167  	            cl_perror("Cannot open watchdog device '%s'", wddev);
168  	        } else {
169  	            cl_perror("Seems as if '%s' isn't a valid watchdog-device", wddev);
170  	        }
171  	        return -1;
172  	    }
173  	    return wdfd;
174  	}
175  	
176  	int
177  	watchdog_init(void)
178  	{
179  	    if (watchdogfd < 0 && watchdogdev != NULL) {
180  	        int timeout = timeout_watchdog;
181  	
182  	        if (watchdog_set_timeout == 0) {
183  	            cl_log(LOG_INFO,
184  	                   "NOT setting watchdog timeout on explicit user request!");
185  	            timeout = -1;
186  	        }
187  	        watchdogfd = watchdog_init_fd(watchdogdev, timeout);
188  	        if (watchdogfd >= 0) {
189  	            cl_log(LOG_NOTICE, "Using watchdog device '%s'", watchdogdev);
190  	            if (watchdog_set_timeout) {
191  	                cl_log(LOG_INFO, "Set watchdog timeout to %d seconds.",
192  	                       timeout_watchdog);
193  	            }
194  	        } else {
195  	            return -1;
196  	        }
197  	    }
198  	    return 0;
199  	}
200  	
201  	static void
202  	watchdog_close_fd(int wdfd, char *wddev, bool disarm)
203  	{
204  	    if (disarm) {
205  	        int r;
206  	        int flags = WDIOS_DISABLECARD;;
207  	
208  	        /* Explicitly disarm it */
209  	        r = ioctl(wdfd, WDIOC_SETOPTIONS, &flags);
210  	        if (r < 0) {
211  	            cl_perror("Failed to disable hardware watchdog %s", wddev);
212  	        }
213  	
214  	        /* To be sure, use magic close logic, too */
215  	        for (;;) {
216  	            if (write(wdfd, "V", 1) > 0) {
217  	                break;
218  	            }
219  	            cl_perror("Cannot disable watchdog device %s", wddev);
220  	        }
221  	    }
222  	
223  	    if (close(wdfd) < 0) {
224  	        cl_perror("Watchdog close(%d) failed", wdfd);
225  	    }
226  	}
227  	
228  	void
229  	watchdog_close(bool disarm)
230  	{
231  	    if (watchdogfd < 0) {
232  	        return;
233  	    }
234  	
235  	    watchdog_close_fd(watchdogfd, watchdogdev, disarm);
236  	    watchdogfd = -1;
237  	}
238  	
239  	struct watchdog_list_item {
240  	    dev_t dev;
241  	    char *dev_node;
242  	    char *dev_ident;
243  	    char *dev_driver;
244  	    pid_t busy_pid;
245  	    char *busy_name;
246  	    struct watchdog_list_item *next;
247  	};
248  	
249  	struct link_list_item {
250  	    char *dev_node;
251  	    char *link_name;
252  	    struct link_list_item *next;
253  	};
254  	
255  	static struct watchdog_list_item *watchdog_list = NULL;
256  	static int watchdog_list_items = 0;
257  	
258  	static void
259  	watchdog_populate_list(void)
260  	{
261  	    struct dirent *entry;
262  	    char entry_name[sizeof(WATCHDOG_NODEDIR)+NAME_MAX];
263  	    DIR *dp;
264  	    char buf[NAME_MAX+sizeof(WATCHDOG_NODEDIR)] = "";
265  	    struct link_list_item *link_list = NULL;
266  	
(1) Event cond_false: Condition "watchdog_list != NULL", taking false branch.
267  	    if (watchdog_list != NULL) {
268  	        return;
(2) Event if_end: End of if statement.
269  	    }
270  	
271  	    /* search for watchdog nodes in /dev */
272  	    dp = opendir(WATCHDOG_NODEDIR);
(3) Event cond_true: Condition "dp", taking true branch.
273  	    if (dp) {
274  	        /* first go for links and memorize them */
(4) Event cond_true: Condition "entry = readdir(dp)", taking true branch.
(10) Event cond_true: Condition "entry = readdir(dp)", taking true branch.
275  	        while ((entry = readdir(dp))) {
(5) Event cond_true: Condition "entry->d_type == DT_LNK", taking true branch.
(11) Event cond_true: Condition "entry->d_type == DT_LNK", taking true branch.
276  	            if (entry->d_type == DT_LNK) {
277  	                int len;
278  	
279  	                snprintf(entry_name, sizeof(entry_name),
280  	                         WATCHDOG_NODEDIR "%s", entry->d_name);
281  	
282  	                /* realpath(entry_name, buf) unfortunately does a stat on
283  	                 * target so we can't really use it to check if links stay
284  	                 * within /dev without triggering e.g. AVC-logs (with
285  	                 * SELinux policy that just allows stat within /dev).
286  	                 * Without canonicalization that doesn't actually touch the
287  	                 * filesystem easily available introduce some limitations
288  	                 * for simplicity:
289  	                 * - just simple path without '..'
290  	                 * - just one level of symlinks (avoid e.g. loop-checking)
291  	                 */
292  	                len = readlink(entry_name, buf, sizeof(buf) - 1);
(6) Event cond_false: Condition "len < 1", taking false branch.
(7) Event cond_true: Condition "len > 253UL /* sizeof (buf) - sizeof ("/dev/") - 1 - 1 */", taking true branch.
(12) Event cond_false: Condition "len < 1", taking false branch.
(13) Event cond_false: Condition "len > 253UL /* sizeof (buf) - sizeof ("/dev/") - 1 - 1 */", taking false branch.
293  	                if ((len < 1) ||
294  	                    (len > sizeof(buf) - sizeof(WATCHDOG_NODEDIR) -1 - 1)) {
(8) Event continue: Continuing loop.
295  	                    continue;
(14) Event if_end: End of if statement.
296  	                }
297  	                buf[len] = '\0';
(15) Event cond_true: Condition "buf[0] != '/'", taking true branch.
298  	                if (buf[0] != '/') {
299  	                    memmove(&buf[sizeof(WATCHDOG_NODEDIR)-1], buf, len+1);
(16) Event string_copy: Calling "memcpy" copies a source string ""/dev/"" to "buf".
(17) Event string_null_source: The argument "buf" will not be null-terminated, because the length of source string ""/dev/"" (5 characters) is greater than or equal to the size argument (5).
Also see events: [string_null]
300  	                    memcpy(buf, WATCHDOG_NODEDIR, sizeof(WATCHDOG_NODEDIR)-1);
301  	                    len += sizeof(WATCHDOG_NODEDIR)-1;
302  	                }
(18) Event string_null: Passing unterminated string "buf" to "strstr", which expects a null-terminated string.
Also see events: [string_copy][string_null_source]
303  	                if (strstr(buf, "/../") ||
304  	                    strncmp(WATCHDOG_NODEDIR, buf,
305  	                            sizeof(WATCHDOG_NODEDIR)-1)) {
306  	                    continue;
307  	                } else {
308  	                    /* just memorize to avoid statting the target - SELinux */
309  	                    struct link_list_item *lli =
310  	                        calloc(1, sizeof(struct link_list_item));
311  	
312  	                    if (lli == NULL) {
313  	                        break;
314  	                    }
315  	                    lli->dev_node = strdup(buf);
316  	                    lli->link_name = strdup(entry_name);
317  	                    if ((lli->dev_node == NULL) || (lli->link_name == NULL)) {
318  	                        free(lli->dev_node);
319  	                        free(lli->link_name);
320  	                        free(lli);
321  	                        break;
322  	                    }
323  	                    lli->next = link_list;
324  	                    link_list = lli;
325  	                }
326  	            }
(9) Event loop: Looping back.
327  	        }
328  	
329  	        rewinddir(dp);
330  	
331  	        while ((entry = readdir(dp))) {
332  	            if (entry->d_type == DT_CHR) {
333  	                struct stat statbuf;
334  	
335  	                snprintf(entry_name, sizeof(entry_name),
336  	                            WATCHDOG_NODEDIR "%s", entry->d_name);
337  	                if(!stat(entry_name, &statbuf) && S_ISCHR(statbuf.st_mode) &&
338  	                   is_watchdog(statbuf.st_rdev)) {
339  	
340  	                    int wdfd;
341  	                    struct watchdog_list_item *wdg =
342  	                        calloc(1, sizeof(struct watchdog_list_item));
343  	                    int len;
344  	                    struct link_list_item *tmp_list = NULL;
345  	
346  	                    if (wdg == NULL) {
347  	                        break;
348  	                    }
349  	
350  	                    wdg->dev = statbuf.st_rdev;
351  	                    wdg->dev_node = strdup(entry_name);
352  	                    if (wdg->dev_node == NULL) {
353  	                        free(wdg);
354  	                        break;
355  	                    }
356  	                    wdg->next = watchdog_list;
357  	                    watchdog_list = wdg;
358  	                    watchdog_list_items++;
359  	
360  	                    wdfd = watchdog_init_fd(entry_name, -1);
361  	                    if (wdfd >= 0) {
362  	                        struct watchdog_info ident;
363  	
364  	                        ident.identity[0] = '\0';
365  	                        ioctl(wdfd, WDIOC_GETSUPPORT, &ident);
366  	                        watchdog_close_fd(wdfd, entry_name, true);
367  	                        if (ident.identity[0]) {
368  	                            wdg->dev_ident = strdup((char *) ident.identity);
369  	                        }
370  	                    }
371  	
372  	                    snprintf(entry_name, sizeof(entry_name),
373  	                                SYS_CHAR_DEV_DIR "/%d:%d/device/driver",
374  	                                major(wdg->dev), minor(wdg->dev));
375  	                    len = readlink(entry_name, buf, sizeof(buf) - 1);
376  	                    if (len > 0) {
377  	                        buf[len] = '\0';
378  	                        wdg->dev_driver = strdup(basename(buf));
379  	                    } else if ((wdg->dev_ident) &&
380  	                               (strcmp(wdg->dev_ident,
381  	                                       "Software Watchdog") == 0)) {
382  	                        wdg->dev_driver = strdup("softdog");
383  	                    }
384  	
385  	                    /* create dupes if we have memorized links
386  	                     * to this node
387  	                     */
388  	                    for (tmp_list = link_list; tmp_list;
389  	                            tmp_list = tmp_list->next) {
390  	                        if (!strcmp(tmp_list->dev_node,
391  	                                    wdg->dev_node)) {
392  	                            struct watchdog_list_item *dupe_wdg =
393  	                                calloc(1, sizeof(struct watchdog_list_item));
394  	
395  	                            if (dupe_wdg == NULL) {
396  	                                break;
397  	                            }
398  	                            /* as long as we never purge watchdog_list
399  	                             * there is no need to dupe strings
400  	                             */
401  	                            *dupe_wdg = *wdg;
402  	                            dupe_wdg->dev_node = strdup(tmp_list->link_name);
403  	                            if (dupe_wdg->dev_node == NULL) {
404  	                                free(dupe_wdg);
405  	                                break;
406  	                            }
407  	                            dupe_wdg->next = watchdog_list;
408  	                            watchdog_list = dupe_wdg;
409  	                            watchdog_list_items++;
410  	                        }
411  	                        /* for performance reasons we could remove
412  	                         * the link_list entry
413  	                         */
414  	                    }
415  	                }
416  	            }
417  	        }
418  	
419  	        closedir(dp);
420  	    }
421  	
422  	    /* cleanup link list */
423  	    while (link_list) {
424  	        struct link_list_item *tmp_list = link_list;
425  	
426  	        link_list = link_list->next;
427  	        free(tmp_list->dev_node);
428  	        free(tmp_list->link_name);
429  	        free(tmp_list);
430  	    }
431  	}
432  	
433  	static void
434  	watchdog_checkbusy()
435  	{
436  	    DIR *dproc;
437  	    struct dirent *entry;
438  	
439  	    dproc = opendir("/proc");
440  	    if (!dproc) {
441  	        /* no proc directory to search through */
442  	        return;
443  	    }
444  	
445  	    while ((entry = readdir(dproc)) != NULL) {
446  	        pid_t local_pid;
447  	        char *leftover;
448  	        DIR *dpid;
449  	        char procpath[NAME_MAX+10] = { 0 };
450  	
451  	        if (entry->d_name[0] == '.') {
452  	            continue;
453  	        }
454  	
455  	        local_pid = strtol(entry->d_name, &leftover, 10);
456  	        if (leftover[0] != '\0')
457  	            continue;
458  	
459  	        snprintf(procpath, sizeof(procpath), "/proc/%s/fd", entry->d_name);
460  	        dpid = opendir(procpath);
461  	        if (!dpid) {
462  	            /* silently continue - might be just a race */
463  	            continue;
464  	        }
465  	        while ((entry = readdir(dpid)) != NULL) {
466  	            struct watchdog_list_item *wdg;
467  	            char entry_name[sizeof(procpath)+NAME_MAX+1] = { 0 };
468  	            char buf[NAME_MAX+1] = { 0 };
469  	            int len;
470  	
471  	            if (entry->d_type != DT_LNK) {
472  	                continue;
473  	            }
474  	            snprintf(entry_name, sizeof(entry_name),
475  	                     "%s/%s", procpath, entry->d_name);
476  	            len = readlink(entry_name, buf, sizeof(buf) - 1);
477  	            if (len < 1) {
478  	                continue;
479  	            }
480  	            buf[len] = '\0';
481  	            for (wdg = watchdog_list; wdg != NULL; wdg = wdg->next) {
482  	                if (!strcmp(buf, wdg->dev_node)) {
483  	                    char name[16];
484  	                    FILE *file;
485  	
486  	                    wdg->busy_pid = local_pid;
487  	                    snprintf(procpath, sizeof(procpath), "/proc/%d/status",
488  	                             local_pid);
489  	                    file = fopen(procpath, "r");
490  	                    if (file) {
491  	                        if (fscanf(file, "Name:\t%15[a-zA-Z0-9 _-]",
492  	                                   name) == 1) {
493  	                            wdg->busy_name = strdup(name);
494  	                        }
495  	                        fclose(file);
496  	                    }
497  	                }
498  	            }
499  	        }
500  	        closedir(dpid);
501  	    }
502  	
503  	    closedir(dproc);
504  	
505  	    return;
506  	}
507  	
508  	int watchdog_info(void)
509  	{
510  	    struct watchdog_list_item *wdg;
511  	    int wdg_cnt = 0;
512  	
513  	    watchdog_populate_list();
514  	    watchdog_checkbusy();
515  	    printf("\nDiscovered %d watchdog devices:\n", watchdog_list_items);
516  	    for (wdg = watchdog_list; wdg != NULL; wdg = wdg->next) {
517  	        wdg_cnt++;
518  	        if (wdg->busy_pid) {
519  	            printf("\n[%d] %s\nIdentity: Busy: PID %d (%s)\nDriver: %s\n",
520  	                wdg_cnt, wdg->dev_node,
521  	                wdg->busy_pid,
522  	                wdg->busy_name?wdg->busy_name:"<unknown>",
523  	                wdg->dev_driver?wdg->dev_driver:"<unknown>");
524  	        } else {
525  	            printf("\n[%d] %s\nIdentity: %s\nDriver: %s\n",
526  	                wdg_cnt, wdg->dev_node,
527  	                wdg->dev_ident?wdg->dev_ident:
528  	                    "Error: device hogged via alias major/minor?",
529  	                wdg->dev_driver?wdg->dev_driver:"<unknown>");
530  	        }
531  	        if ((wdg->dev_driver) && (strcmp(wdg->dev_driver, "softdog") == 0)) {
532  	            printf("CAUTION: Not recommended for use with sbd.\n");
533  	        }
534  	    }
535  	
536  	    return 0;
537  	}
538  	
539  	int watchdog_test(void)
540  	{
541  	    int i;
542  	
543  	    if ((watchdog_set_timeout == 0) || !watchdog_use) {
544  	        printf("\nWatchdog is disabled - aborting test!!!\n");
545  	        return 0;
546  	    }
547  	    if (watchdogdev_is_default) {
548  	        watchdog_populate_list();
549  	        if (watchdog_list_items > 1) {
550  	            printf("\nError: Multiple watchdog devices discovered."
551  	                   "\n       Use -w <watchdog> or SBD_WATCHDOG_DEV to specify"
552  	                   "\n       which device to reset the system with\n");
553  	            watchdog_info();
554  	            return -1;
555  	        }
556  	    }
557  	    if ((isatty(fileno(stdin)))) {
558  	        char buffer[16];
559  	        printf("\n");
560  	        printf(
561  	            "WARNING: This operation is expected to force-reboot this system\n"
562  	            "         without following any shutdown procedures.\n\n"
563  	            "Proceed? [NO/Proceed] ");
564  	
565  	        if ((fgets(buffer, 16, stdin) == NULL) ||
566  	            strcmp(buffer, "Proceed\n")) {
567  	            printf("\nAborting watchdog test!!!\n");
568  	            return 0;
569  	        }
570  	        printf("\n");
571  	    }
572  	    printf("Initializing %s with a reset countdown of %d seconds ...\n",
573  	        watchdogdev, (int) timeout_watchdog);
574  	    if ((watchdog_init() < 0) || (watchdog_init_interval() < 0)) {
575  	        printf("Failed to initialize watchdog!!!\n");
576  	        watchdog_info();
577  	        return -1;
578  	    }
579  	    printf("\n");
580  	    printf(
581  	        "NOTICE: The watchdog device is expected to reset the system\n"
582  	        "        in %d seconds.  If system remains active beyond that time,\n"
583  	        "        watchdog may not be functional.\n\n", timeout_watchdog);
584  	    for (i=timeout_watchdog; i>1; i--) {
585  	        printf("Reset countdown ... %d seconds\n", i);
586  	        sleep(1);
587  	    }
588  	    for (i=2; i>0; i--) {
589  	        printf("System expected to reset any moment ...\n");
590  	        sleep(1);
591  	    }
592  	    for (i=5; i>0; i--) {
593  	        printf("System should have reset ...\n");
594  	        sleep(1);
595  	    }
596  	    printf("Error: The watchdog device has failed to reboot the system,\n"
597  	           "       and it may not be suitable for usage with sbd.\n");
598  	
599  	    /* test should trigger a reboot thus returning is actually bad */
600  	    return -1;
601  	}
602