aboutsummaryrefslogtreecommitdiff
path: root/toys/pending/watchdog.c
blob: 0813fe698b571e7697f9272d662a5e340188e600 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
/* watchdog - start a watchdog timer with configurable kick frequencies

 Author: Chris Sarra, chrissarra@google.com
 Date: 25 July 2019
 Ref: kernel.org/doc/Documentation/watchdog/watchdog-api.txt

USE_WATCHDOG(NEWTOY(watchdog, "Ft#T#", TOYFLAG_NEEDROOT|TOYFLAG_BIN))

config WATCHDOG
  bool "watchdog"
  default y
  help
    usage: watchdog [-F] [-t SW_TIMER_S] [-T HW_TIMER_S] DEV

    Start the watchdog timer at DEV with optional timeout parameters.
    -F run in the foreground (do not daemonize)
    -t software timer (in seconds)
    -T hardware timer (in seconds)
*/
#define FOR_watchdog
#include "toys.h"
#include "linux/watchdog.h"

// Make sure no DEBUG variable is set; change this if you need debug prints!
#undef WATCHDOG_DEBUG

// Default timeout values in seconds.
#define WATCHDOG_SW_TIMER_S_DEFAULT (4)
#define WATCHDOG_HW_TIMER_S_DEFAULT (60)

GLOBALS(
  long hw_timer_s;
  long sw_timer_s;
  int fd;
)

static int intercept_signals(void (*fn)(int)) {
  int rc = 0;
  struct sigaction sigact;
  memset(&sigact, 0, sizeof(sigact));
  sigact.sa_handler = fn;

  rc = sigaction(SIGTERM, &sigact, NULL);
#if defined(WATCHDOG_DEBUG)
  if ( rc ) {
    printf("failed to create new sigaction for SIGTERM: %d\n", rc);
  }
#endif
  return rc;
}

void safe_shutdown(int __attribute__((unused))ignored) {
  write(TT.fd, "V", 1);
  close(TT.fd);
  TT.fd = -1;
  error_exit("safely exited watchdog.");
}

void watchdog_main(void) {
  int rc = 0;
  long hw_timer_sec = 0;
  char *watchdog_dev = NULL;

  if ( toys.optc > 0 ) {
    watchdog_dev = toys.optargs[0];
#if defined(WATCHDOG_DEBUG)
    printf("using dev @ '%s'\n", watchdog_dev);
#endif
  } else {
    error_exit("watchdog dev required");
  }

  // Set default values for timeouts if no flags
  if (!(toys.optflags & FLAG_t)) {
    TT.sw_timer_s = WATCHDOG_SW_TIMER_S_DEFAULT;
#if defined(WATCHDOG_DEBUG)
    printf("using default sw_timer_s.\n");
#endif
  }

  if (!(toys.optflags & FLAG_T)) {
    TT.hw_timer_s = WATCHDOG_HW_TIMER_S_DEFAULT;
#if defined(WATCHDOG_DEBUG)
    printf("using default hw_timer_s.\n");
#endif
  }

#if defined(WATCHDOG_DEBUG)
    printf("hw timer: %ld seconds\n", TT.hw_timer_s);
    printf("sw timer: %ld seconds\n", TT.sw_timer_s);
#endif

  if (!(toys.optflags & FLAG_F)) {
#if defined(WATCHDOG_DEBUG)
      printf("daemonizing. so long, foreground!\n");
#endif
    // Attempt to daemonize
    rc = daemon(1, 1);
    if ( rc ) {
      perror_exit("failed to daemonize: %d", rc);
    }
  }

  // Intercept terminating signals so we can call our shutdown routine first.
  if ( intercept_signals(safe_shutdown) ) {
    perror_exit("failed to intercept desired signals: %d", rc);
  }
#if defined(WATCHDOG_DEBUG)
    printf("Successfully intercepted signals.\n");
#endif

  TT.fd = open(watchdog_dev, O_WRONLY);
  if ( TT.fd == -1 ) {
    perror_exit("failed to open '%s'", watchdog_dev);
  }

#if defined(WDIOC_SETTIMEOUT)
  // SETTIMEOUT takes time value in seconds: s = ms / (1000 ms/s)
  hw_timer_sec = TT.hw_timer_s;
  xioctl(TT.fd, WDIOC_SETTIMEOUT, (void *)&hw_timer_sec);
#endif // WDIOC_SETTIMEOUT

  // Now that we've got the watchdog device open, kick it periodically.
  while (1) {
    write(TT.fd, "\0", 1);
    usleep(TT.sw_timer_s * 1000 * 1000);
  }
}