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