ubihealthd.c 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. #include <errno.h>
  2. #include <fcntl.h>
  3. #include <getopt.h>
  4. #include <inttypes.h>
  5. #include <mtd/ubi-user.h>
  6. #include <poll.h>
  7. #include <signal.h>
  8. #include <stdarg.h>
  9. #include <stdint.h>
  10. #include <stdio.h>
  11. #include <stdlib.h>
  12. #include <string.h>
  13. #include <sys/ioctl.h>
  14. #include <syslog.h>
  15. #include <sys/random.h>
  16. #include <sys/signalfd.h>
  17. #include <sys/stat.h>
  18. #include <sys/timerfd.h>
  19. #include <sys/types.h>
  20. #include <unistd.h>
  21. #define PROGRAM_NAME "ubihealthd"
  22. #include "libubi.h"
  23. #include "common.h"
  24. #ifndef UBI_IOCRPEB
  25. #define UBI_IOCRPEB _IOW(UBI_IOC_MAGIC, 4, int32_t)
  26. #endif
  27. struct peb_state {
  28. int alive;
  29. int pnum;
  30. int last_errno;
  31. };
  32. static struct peb_state **peb_state_array;
  33. static int peb_state_array_len;
  34. static int cur_pos;
  35. static const char *ubi_device = "/dev/ubi0";
  36. static int ubi_fd;
  37. static int interval_secs = 120;
  38. static int nodaemon;
  39. static const char opt_string[] = "d:i:fh";
  40. static const struct option options[] = {
  41. {
  42. .name = "device",
  43. .has_arg = required_argument,
  44. .flag = NULL,
  45. .val = 'd'
  46. },
  47. {
  48. .name = "interval",
  49. .has_arg = required_argument,
  50. .flag = NULL,
  51. .val = 'i'
  52. },
  53. {
  54. .name = "help",
  55. .has_arg = no_argument,
  56. .flag = NULL,
  57. .val = 'h'
  58. },
  59. { /* sentinel */ }
  60. };
  61. static void dolog(const char *fmt, ...)
  62. {
  63. va_list ap;
  64. va_start(ap, fmt);
  65. if (nodaemon)
  66. vfprintf(stderr, fmt, ap);
  67. else
  68. vsyslog(LOG_DAEMON | LOG_WARNING, fmt, ap);
  69. va_end(ap);
  70. }
  71. static void build_peb_list(void)
  72. {
  73. int i, pos;
  74. struct peb_state *ps;
  75. peb_state_array = xmalloc(sizeof(ps) * peb_state_array_len);
  76. for (i = 0; i < peb_state_array_len; i++) {
  77. ps = xmalloc(sizeof(*ps));
  78. ps->pnum = i;
  79. ps->last_errno = 0;
  80. ps->alive = 1;
  81. peb_state_array[i] = ps;
  82. }
  83. /* Shuffle the list */
  84. for (i = 0; i < peb_state_array_len; i++) {
  85. pos = rand() % peb_state_array_len;
  86. ps = peb_state_array[pos];
  87. peb_state_array[pos] = peb_state_array[i];
  88. peb_state_array[i] = ps;
  89. }
  90. }
  91. static struct peb_state *__next_peb(void)
  92. {
  93. struct peb_state *ps = peb_state_array[cur_pos];
  94. cur_pos++;
  95. if (cur_pos >= peb_state_array_len)
  96. cur_pos = 0;
  97. return ps;
  98. }
  99. static struct peb_state *next_peb(void)
  100. {
  101. int i;
  102. struct peb_state *ps;
  103. /* Find next PEB in our list, skip bad PEBs */
  104. for (i = 0; i < peb_state_array_len; i++) {
  105. ps = __next_peb();
  106. if (ps->alive)
  107. return ps;
  108. }
  109. dolog("Fatal: All PEBs are gone?!\n");
  110. exit(1);
  111. return NULL;
  112. }
  113. static int process_one_peb(void)
  114. {
  115. int rc;
  116. struct peb_state *ps = next_peb();
  117. rc = ioctl(ubi_fd, UBI_IOCRPEB, &ps->pnum);
  118. if (!rc)
  119. return 0;
  120. else
  121. rc = errno;
  122. switch (rc) {
  123. case EINVAL: {
  124. dolog("Unable to check PEB %i for unknown reason!\n", ps->pnum);
  125. break;
  126. }
  127. case ENOENT: {
  128. /* UBI ignores this PEB */
  129. ps->alive = 0;
  130. break;
  131. }
  132. case EBUSY: {
  133. if (ps->last_errno == rc)
  134. dolog("Warning: Unable to check PEB %i\n", ps->pnum);
  135. break;
  136. }
  137. case EAGAIN: {
  138. if (ps->last_errno == rc)
  139. dolog("Warning: PEB %i has bitflips, but cannot scrub!\n", ps->pnum);
  140. break;
  141. }
  142. case EUCLEAN: {
  143. /* Scrub happened */
  144. break;
  145. }
  146. case ENOTTY: {
  147. dolog("Fatal: Kernel does not support this interface. Too old kernel?\n");
  148. exit(1);
  149. break;
  150. }
  151. case ENODEV: {
  152. dolog("Fatal: UBI device vanished under us.\n");
  153. exit(1);
  154. }
  155. default:
  156. dolog("Warning: Unknown return code from kernel: %i\n", rc);
  157. }
  158. ps->last_errno = rc;
  159. return 0;
  160. }
  161. static int get_peb_count(void)
  162. {
  163. libubi_t libubi = libubi_open();
  164. struct ubi_dev_info dev_info;
  165. if (!libubi) {
  166. fprintf(stderr, "Unable to init libubi, is UBI present?\n");
  167. exit(1);
  168. }
  169. if (ubi_get_dev_info(libubi, ubi_device, &dev_info)) {
  170. fprintf(stderr, "Fatal: Could not get ubi info for %s\n", ubi_device);
  171. exit(1);
  172. }
  173. libubi_close(libubi);
  174. return dev_info.total_lebs;
  175. }
  176. static void init_prng(void)
  177. {
  178. int ret, seed;
  179. ret = getrandom(&seed, sizeof(seed), 0);
  180. if (ret != sizeof(seed)) {
  181. if (ret == -1)
  182. fprintf(stderr, "Unable to get random seed: %m\n");
  183. else
  184. fprintf(stderr, "Unable to get %zi bytes random seed\n", sizeof(seed));
  185. exit(1);
  186. }
  187. srand(seed);
  188. }
  189. int main (int argc, char *argv[])
  190. {
  191. int c, i;
  192. while ((c = getopt_long(argc, argv, opt_string, options, &i)) != -1) {
  193. switch(c) {
  194. case 'd': {
  195. ubi_device = optarg;
  196. break;
  197. }
  198. case 'i': {
  199. interval_secs = atoi(optarg);
  200. if (!interval_secs) {
  201. fprintf(stderr, "Bad interval value! %s\n", optarg);
  202. exit(1);
  203. }
  204. break;
  205. }
  206. case 'f': {
  207. nodaemon = 1;
  208. break;
  209. }
  210. case 'h':
  211. default:
  212. fprintf(stderr, "Usage: %s [ -d UBI_DEVICE ] [-i INTERVAL_SEC ] [ -f ]\n", argv[0]);
  213. exit(1);
  214. break;
  215. }
  216. }
  217. ubi_fd = open(ubi_device, O_RDONLY);
  218. if (ubi_fd == -1) {
  219. fprintf(stderr, "Fatal: Unable to open %s: %m\n", ubi_device);
  220. exit(1);
  221. }
  222. init_prng();
  223. peb_state_array_len = get_peb_count();
  224. build_peb_list();
  225. if (!nodaemon) {
  226. if (daemon(0, 0) == -1) {
  227. fprintf(stderr, "Unable to become a daemon: %m\n");
  228. exit(1);
  229. }
  230. }
  231. for (;;) {
  232. process_one_peb();
  233. sleep(interval_secs);
  234. }
  235. return 0;
  236. }