/*============================================================================ * Hook to the ioctl file operation */ int vmiwdt_ioctl(struct inode *inode, struct file *file_ptr, uint32_t cmd, unsigned long arg) { int timeout, status = 0; static struct watchdog_info wdt_info = { .options = WDIOF_SETTIMEOUT, .firmware_version = 0, .identity = MOD_NAME }; switch (cmd) { case WDIOC_GETSUPPORT: if (copy_to_user((void *) arg, &wdt_info, sizeof (wdt_info))) return -EFAULT; break; case WDIOC_GETSTATUS: if (copy_to_user((void *) arg, &status, sizeof (int))) return -EFAULT; break; case WDIOC_KEEPALIVE: wdt_keepalive(); break; case WDIOC_SETTIMEOUT: if (copy_from_user(&timeout, (void *) arg, sizeof (int))) return -EFAULT; /* The wdt_set_timeout function expects milliseconds */ timeout *= 1000; wdt_set_timeout(&timeout); timeout /= 1000; if (copy_to_user((void *) arg, &timeout, sizeof (int))) return -EFAULT; break; #ifdef WDIOC_SETTIMEOUT_MS case WDIOC_SETTIMEOUT_MS: #endif if (copy_from_user(&timeout, (void *) arg, sizeof (int))) return -EFAULT; wdt_set_timeout(&timeout); if (copy_to_user((void *) arg, &timeout, sizeof (int))) return -EFAULT; break; case WDIOC_GETTIMEOUT: /* The wdt_set_timeout function returns milliseconds */ timeout = wdt_get_timeout(); timeout /= 1000; if (copy_to_user((void *) arg, &timeout, sizeof (timeout))) return -EFAULT; break; #ifdef WDIOC_GETTIMEOUT_MS case WDIOC_GETTIMEOUT_MS: #endif timeout = wdt_get_timeout(); if (copy_to_user((void *) arg, &timeout, sizeof (timeout))) return -EFAULT; break; default: return -ENOTTY; } return 0; } /*============================================================================ * Hook for the open file operation */ static int vmiwdt_open(struct inode *inode, struct file *file_ptr) { #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,0) MOD_INC_USE_COUNT; #endif if (1 == ++vmiwdt_count) { wdt_enable(); } return 0; }
int main(int argc, char *argv[]) { int timeout = WDT_TIMEOUT_DEFAULT; int real_timeout = 0; int T; int background = 1; int use_syslog = 1; int c, status; int log_opts = LOG_NDELAY | LOG_NOWAIT | LOG_PID; struct option long_options[] = { {"load-average", 1, 0, 'a'}, {"foreground", 0, 0, 'n'}, {"help", 0, 0, 'h'}, {"interval", 1, 0, 't'}, {"loglevel", 1, 0, 'l'}, {"meminfo", 1, 0, 'm'}, {"filenr", 1, 0, 'f'}, {"pmon", 2, 0, 'p'}, {"safe-exit", 0, 0, 'e'}, {"syslog", 0, 0, 's'}, #ifndef TESTMODE_DISABLED {"test-mode", 0, 0, 'S'}, /* Hidden test mode, not for public use. */ #endif {"version", 0, 0, 'v'}, {"timeout", 1, 0, 'T'}, {NULL, 0, 0, 0} }; uev_ctx_t ctx; while ((c = getopt_long(argc, argv, "a:f:Fhl:Lm:np::sSt:T:Vvx?", long_options, NULL)) != EOF) { switch (c) { case 'a': if (loadavg_set(optarg)) return usage(1); break; case 'f': if (filenr_set(optarg)) return usage(1); break; case 'h': return usage(0); case 'l': loglevel = __wdog_loglevel(optarg); if (-1 == loglevel) return usage(1); break; case 'm': if (meminfo_set(optarg)) return usage(1); break; case 'F': /* BusyBox watchdogd compat. */ case 'n': /* Run in foreground */ background = 0; use_syslog--; break; case 'p': if (pmon_set(optarg)) return usage(1); break; case 's': use_syslog++; break; #ifndef TESTMODE_DISABLED case 'S': /* Simulate: no interaction with kernel, for testing pmon */ __wdt_testmode = 1; break; #endif case 't': /* Watchdog kick interval */ if (!optarg) { ERROR("Missing interval argument."); return usage(1); } period = atoi(optarg); break; case 'T': /* Watchdog timeout */ if (!optarg) { ERROR("Missing timeout argument."); return usage(1); } timeout = atoi(optarg); break; case 'v': printf("v%s\n", VERSION); return 0; case 'x': /* Safe exit, i.e., don't reboot if we exit and close device */ magic = 1; break; default: printf("Unrecognized option \"-%c\".\n", c); return usage(1); } } /* BusyBox watchdogd compat. */ if (optind < argc) { char *dev = argv[optind]; if (!strncmp(dev, "/dev", 4)) strlcpy(devnode, dev, sizeof(devnode)); } if (background) { DEBUG("Daemonizing ..."); if (-1 == daemon(0, 0)) { PERROR("Failed daemonizing"); return 1; } } if (!background && use_syslog < 1) log_opts |= LOG_PERROR; setlogmask(LOG_UPTO(loglevel)); openlog(NULL, log_opts, LOG_DAEMON); INFO("watchdogd v%s %s ...", PACKAGE_VERSION, wdt_testmode() ? "test mode" : "starting"); uev_init(&ctx); /* Setup callbacks for SIGUSR1 and, optionally, exit magic on SIGINT/SIGTERM */ setup_signals(&ctx); if (wdt_init()) { PERROR("Failed connecting to kernel watchdog driver"); return 1; } /* Set requested WDT timeout right before we enter the event loop. */ if (wdt_set_timeout(timeout)) PERROR("Failed setting HW watchdog timeout: %d", timeout); /* Sanity check with driver that setting actually took. */ real_timeout = wdt_get_timeout(); if (real_timeout < 0) { PERROR("Failed reading current watchdog timeout"); } else { if (real_timeout <= period) { ERROR("Warning, watchdog timeout <= kick interval: %d <= %d", real_timeout, period); } } /* If user did not provide '-k' argument, set to half actual timeout */ if (-1 == period) { if (real_timeout < 0) period = WDT_KICK_DEFAULT; else period = real_timeout / 2; if (!period) period = 1; } /* Calculate period (T) in milliseconds for libuEv */ T = period * 1000; DEBUG("Watchdog kick interval set to %d sec.", period); /* Read boot cause from watchdog and save in /var/run/watchdogd.status */ create_bootstatus(real_timeout, period); /* Every period (T) seconds we kick the wdt */ uev_timer_init(&ctx, &period_watcher, period_cb, NULL, T, T); /* Start all enabled plugins */ wdt_plugins_init(&ctx, T); /* Only create pidfile when we're done with all set up. */ if (pidfile(NULL) && !wdt_testmode()) PERROR("Cannot create pidfile"); status = uev_run(&ctx, 0); if (wdt_testmode()) return status; while (wait_reboot) { int reboot_in = 3 * real_timeout; INFO("Waiting for HW WDT reboot ..."); while (reboot_in > 0) { unsigned int rest = sleep(real_timeout); while (rest) rest = sleep(rest); reboot_in -= real_timeout; } INFO("HW WDT dit not reboot, forcing reboot now ..."); reboot(RB_AUTOBOOT); } return status; }