system.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847
  1. /*
  2. * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
  3. * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU Lesser General Public License version 2.1
  7. * as published by the Free Software Foundation
  8. *
  9. * This program 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
  12. * GNU General Public License for more details.
  13. */
  14. #include <sys/utsname.h>
  15. #ifdef linux
  16. #include <sys/sysinfo.h>
  17. #endif
  18. #include <sys/ioctl.h>
  19. #include <sys/types.h>
  20. #include <sys/reboot.h>
  21. #include <sys/stat.h>
  22. #include <fcntl.h>
  23. #include <signal.h>
  24. #include <unistd.h>
  25. #include <stdlib.h>
  26. #include <json-c/json_tokener.h>
  27. #include <libubox/blobmsg_json.h>
  28. #include <libubox/uloop.h>
  29. #include "procd.h"
  30. #include "sysupgrade.h"
  31. #include "watchdog.h"
  32. static struct blob_buf b;
  33. static int notify;
  34. static struct ubus_context *_ctx;
  35. static int initramfs;
  36. enum vjson_state {
  37. VJSON_ERROR,
  38. VJSON_CONTINUE,
  39. VJSON_SUCCESS,
  40. };
  41. static const char *system_rootfs_type(void) {
  42. const char proc_mounts[] = "/proc/self/mounts";
  43. static char fstype[16] = { 0 };
  44. char *mountstr = NULL, *mp = "/", *pos, *tmp;
  45. FILE *mounts;
  46. ssize_t nread;
  47. size_t len = 0;
  48. bool found;
  49. if (initramfs)
  50. return "initramfs";
  51. if (fstype[0])
  52. return fstype;
  53. mounts = fopen(proc_mounts, "r");
  54. if (!mounts)
  55. return NULL;
  56. while ((nread = getline(&mountstr, &len, mounts)) != -1) {
  57. found = false;
  58. pos = strchr(mountstr, ' ');
  59. if (!pos)
  60. continue;
  61. tmp = pos + 1;
  62. pos = strchr(tmp, ' ');
  63. if (!pos)
  64. continue;
  65. *pos = '\0';
  66. if (strcmp(tmp, mp))
  67. continue;
  68. tmp = pos + 1;
  69. pos = strchr(tmp, ' ');
  70. if (!pos)
  71. continue;
  72. *pos = '\0';
  73. if (!strcmp(tmp, "overlay")) {
  74. /*
  75. * there is no point in parsing overlay option string for
  76. * lowerdir, as that can point to "/" being a previous
  77. * overlay mount (after firstboot or sysuprade config
  78. * restore). Hence just assume the lowerdir is "/rom" and
  79. * restart searching for that instead.
  80. */
  81. mp = "/rom";
  82. fseek(mounts, 0, SEEK_SET);
  83. continue;
  84. }
  85. found = true;
  86. break;
  87. }
  88. if (found)
  89. strncpy(fstype, tmp, sizeof(fstype) - 1);
  90. fstype[sizeof(fstype) - 1]= '\0';
  91. free(mountstr);
  92. fclose(mounts);
  93. if (found)
  94. return fstype;
  95. else
  96. return NULL;
  97. }
  98. static int system_board(struct ubus_context *ctx, struct ubus_object *obj,
  99. struct ubus_request_data *req, const char *method,
  100. struct blob_attr *msg)
  101. {
  102. void *c;
  103. char line[256];
  104. char *key, *val, *next;
  105. const char *rootfs_type = system_rootfs_type();
  106. struct utsname utsname;
  107. FILE *f;
  108. blob_buf_init(&b, 0);
  109. if (uname(&utsname) >= 0)
  110. {
  111. blobmsg_add_string(&b, "kernel", utsname.release);
  112. blobmsg_add_string(&b, "hostname", utsname.nodename);
  113. }
  114. if ((f = fopen("/proc/cpuinfo", "r")) != NULL)
  115. {
  116. while(fgets(line, sizeof(line), f))
  117. {
  118. key = strtok(line, "\t:");
  119. val = strtok(NULL, "\t\n");
  120. if (!key || !val)
  121. continue;
  122. #ifdef __aarch64__
  123. if (!strcasecmp(key, "CPU revision")) {
  124. snprintf(line, sizeof(line), "ARMv8 Processor rev %lu", strtoul(val + 2, NULL, 16));
  125. blobmsg_add_string(&b, "system", line);
  126. break;
  127. }
  128. #else
  129. if (!strcasecmp(key, "system type") ||
  130. !strcasecmp(key, "processor") ||
  131. !strcasecmp(key, "cpu") ||
  132. !strcasecmp(key, "model name"))
  133. {
  134. strtoul(val + 2, &key, 0);
  135. if (key == (val + 2) || *key != 0)
  136. {
  137. blobmsg_add_string(&b, "system", val + 2);
  138. break;
  139. }
  140. }
  141. #endif
  142. }
  143. fclose(f);
  144. }
  145. if ((f = fopen("/tmp/sysinfo/model", "r")) != NULL ||
  146. (f = fopen("/proc/device-tree/model", "r")) != NULL)
  147. {
  148. if (fgets(line, sizeof(line), f))
  149. {
  150. val = strtok(line, "\t\n");
  151. if (val)
  152. blobmsg_add_string(&b, "model", val);
  153. }
  154. fclose(f);
  155. }
  156. else if ((f = fopen("/proc/cpuinfo", "r")) != NULL)
  157. {
  158. while(fgets(line, sizeof(line), f))
  159. {
  160. key = strtok(line, "\t:");
  161. val = strtok(NULL, "\t\n");
  162. if (!key || !val)
  163. continue;
  164. if (!strcasecmp(key, "machine") ||
  165. !strcasecmp(key, "hardware"))
  166. {
  167. blobmsg_add_string(&b, "model", val + 2);
  168. break;
  169. }
  170. }
  171. fclose(f);
  172. }
  173. if ((f = fopen("/tmp/sysinfo/board_name", "r")) != NULL)
  174. {
  175. if (fgets(line, sizeof(line), f))
  176. {
  177. val = strtok(line, "\t\n");
  178. if (val)
  179. blobmsg_add_string(&b, "board_name", val);
  180. }
  181. fclose(f);
  182. }
  183. else if ((f = fopen("/proc/device-tree/compatible", "r")) != NULL)
  184. {
  185. if (fgets(line, sizeof(line), f))
  186. {
  187. val = strtok(line, "\t\n");
  188. if (val)
  189. {
  190. next = val;
  191. while ((next = strchr(next, ',')) != NULL)
  192. {
  193. *next = '-';
  194. next++;
  195. }
  196. blobmsg_add_string(&b, "board_name", val);
  197. }
  198. }
  199. fclose(f);
  200. }
  201. if (rootfs_type)
  202. blobmsg_add_string(&b, "rootfs_type", rootfs_type);
  203. if ((f = fopen("/etc/openwrt_release", "r")) != NULL)
  204. {
  205. c = blobmsg_open_table(&b, "release");
  206. while (fgets(line, sizeof(line), f))
  207. {
  208. char *dest;
  209. char ch;
  210. key = line;
  211. val = strchr(line, '=');
  212. if (!val)
  213. continue;
  214. *(val++) = 0;
  215. if (!strcasecmp(key, "DISTRIB_ID"))
  216. key = "distribution";
  217. else if (!strcasecmp(key, "DISTRIB_RELEASE"))
  218. key = "version";
  219. else if (!strcasecmp(key, "DISTRIB_REVISION"))
  220. key = "revision";
  221. else if (!strcasecmp(key, "DISTRIB_CODENAME"))
  222. key = "codename";
  223. else if (!strcasecmp(key, "DISTRIB_TARGET"))
  224. key = "target";
  225. else if (!strcasecmp(key, "DISTRIB_DESCRIPTION"))
  226. key = "description";
  227. else
  228. continue;
  229. dest = blobmsg_alloc_string_buffer(&b, key, strlen(val));
  230. if (!dest) {
  231. ERROR("Failed to allocate blob.\n");
  232. continue;
  233. }
  234. while (val && (ch = *(val++)) != 0) {
  235. switch (ch) {
  236. case '\'':
  237. case '"':
  238. next = strchr(val, ch);
  239. if (next)
  240. *next = 0;
  241. strcpy(dest, val);
  242. if (next)
  243. val = next + 1;
  244. dest += strlen(dest);
  245. break;
  246. case '\\':
  247. *(dest++) = *(val++);
  248. break;
  249. }
  250. }
  251. blobmsg_add_string_buffer(&b);
  252. }
  253. blobmsg_close_array(&b, c);
  254. fclose(f);
  255. }
  256. ubus_send_reply(ctx, req, b.head);
  257. return UBUS_STATUS_OK;
  258. }
  259. static int system_info(struct ubus_context *ctx, struct ubus_object *obj,
  260. struct ubus_request_data *req, const char *method,
  261. struct blob_attr *msg)
  262. {
  263. time_t now;
  264. struct tm *tm;
  265. #ifdef linux
  266. struct sysinfo info;
  267. void *c;
  268. char line[256];
  269. char *key, *val;
  270. unsigned long long available, cached;
  271. FILE *f;
  272. if (sysinfo(&info))
  273. return UBUS_STATUS_UNKNOWN_ERROR;
  274. if ((f = fopen("/proc/meminfo", "r")) == NULL)
  275. return UBUS_STATUS_UNKNOWN_ERROR;
  276. /* if linux < 3.14 MemAvailable is not in meminfo */
  277. available = 0;
  278. cached = 0;
  279. while (fgets(line, sizeof(line), f))
  280. {
  281. key = strtok(line, " :");
  282. val = strtok(NULL, " ");
  283. if (!key || !val)
  284. continue;
  285. if (!strcasecmp(key, "MemAvailable"))
  286. available = 1024 * atoll(val);
  287. else if (!strcasecmp(key, "Cached"))
  288. cached = 1024 * atoll(val);
  289. }
  290. fclose(f);
  291. #endif
  292. now = time(NULL);
  293. if (!(tm = localtime(&now)))
  294. return UBUS_STATUS_UNKNOWN_ERROR;
  295. blob_buf_init(&b, 0);
  296. blobmsg_add_u32(&b, "localtime", now + tm->tm_gmtoff);
  297. #ifdef linux
  298. blobmsg_add_u32(&b, "uptime", info.uptime);
  299. c = blobmsg_open_array(&b, "load");
  300. blobmsg_add_u32(&b, NULL, info.loads[0]);
  301. blobmsg_add_u32(&b, NULL, info.loads[1]);
  302. blobmsg_add_u32(&b, NULL, info.loads[2]);
  303. blobmsg_close_array(&b, c);
  304. c = blobmsg_open_table(&b, "memory");
  305. blobmsg_add_u64(&b, "total",
  306. (uint64_t)info.mem_unit * (uint64_t)info.totalram);
  307. blobmsg_add_u64(&b, "free",
  308. (uint64_t)info.mem_unit * (uint64_t)info.freeram);
  309. blobmsg_add_u64(&b, "shared",
  310. (uint64_t)info.mem_unit * (uint64_t)info.sharedram);
  311. blobmsg_add_u64(&b, "buffered",
  312. (uint64_t)info.mem_unit * (uint64_t)info.bufferram);
  313. blobmsg_add_u64(&b, "available", available);
  314. blobmsg_add_u64(&b, "cached", cached);
  315. blobmsg_close_table(&b, c);
  316. c = blobmsg_open_table(&b, "swap");
  317. blobmsg_add_u64(&b, "total",
  318. (uint64_t)info.mem_unit * (uint64_t)info.totalswap);
  319. blobmsg_add_u64(&b, "free",
  320. (uint64_t)info.mem_unit * (uint64_t)info.freeswap);
  321. blobmsg_close_table(&b, c);
  322. #endif
  323. ubus_send_reply(ctx, req, b.head);
  324. return UBUS_STATUS_OK;
  325. }
  326. static int system_reboot(struct ubus_context *ctx, struct ubus_object *obj,
  327. struct ubus_request_data *req, const char *method,
  328. struct blob_attr *msg)
  329. {
  330. procd_shutdown(RB_AUTOBOOT);
  331. return 0;
  332. }
  333. enum {
  334. WDT_FREQUENCY,
  335. WDT_TIMEOUT,
  336. WDT_MAGICCLOSE,
  337. WDT_STOP,
  338. __WDT_MAX
  339. };
  340. static const struct blobmsg_policy watchdog_policy[__WDT_MAX] = {
  341. [WDT_FREQUENCY] = { .name = "frequency", .type = BLOBMSG_TYPE_INT32 },
  342. [WDT_TIMEOUT] = { .name = "timeout", .type = BLOBMSG_TYPE_INT32 },
  343. [WDT_MAGICCLOSE] = { .name = "magicclose", .type = BLOBMSG_TYPE_BOOL },
  344. [WDT_STOP] = { .name = "stop", .type = BLOBMSG_TYPE_BOOL },
  345. };
  346. static int watchdog_set(struct ubus_context *ctx, struct ubus_object *obj,
  347. struct ubus_request_data *req, const char *method,
  348. struct blob_attr *msg)
  349. {
  350. struct blob_attr *tb[__WDT_MAX];
  351. const char *status;
  352. if (!msg)
  353. return UBUS_STATUS_INVALID_ARGUMENT;
  354. blobmsg_parse(watchdog_policy, __WDT_MAX, tb, blob_data(msg), blob_len(msg));
  355. if (tb[WDT_FREQUENCY]) {
  356. unsigned int timeout = tb[WDT_TIMEOUT] ? blobmsg_get_u32(tb[WDT_TIMEOUT]) :
  357. watchdog_timeout(0);
  358. unsigned int freq = blobmsg_get_u32(tb[WDT_FREQUENCY]);
  359. if (freq) {
  360. if (freq > timeout / 2)
  361. freq = timeout / 2;
  362. watchdog_frequency(freq);
  363. }
  364. }
  365. if (tb[WDT_TIMEOUT]) {
  366. unsigned int timeout = blobmsg_get_u32(tb[WDT_TIMEOUT]);
  367. unsigned int frequency = watchdog_frequency(0);
  368. if (timeout <= frequency)
  369. timeout = frequency * 2;
  370. watchdog_timeout(timeout);
  371. }
  372. if (tb[WDT_MAGICCLOSE])
  373. watchdog_set_magicclose(blobmsg_get_bool(tb[WDT_MAGICCLOSE]));
  374. if (tb[WDT_STOP])
  375. watchdog_set_stopped(blobmsg_get_bool(tb[WDT_STOP]));
  376. if (watchdog_fd() == NULL)
  377. status = "offline";
  378. else if (watchdog_get_stopped())
  379. status = "stopped";
  380. else
  381. status = "running";
  382. blob_buf_init(&b, 0);
  383. blobmsg_add_string(&b, "status", status);
  384. blobmsg_add_u32(&b, "timeout", watchdog_timeout(0));
  385. blobmsg_add_u32(&b, "frequency", watchdog_frequency(0));
  386. blobmsg_add_u8(&b, "magicclose", watchdog_get_magicclose());
  387. ubus_send_reply(ctx, req, b.head);
  388. return 0;
  389. }
  390. enum {
  391. SIGNAL_PID,
  392. SIGNAL_NUM,
  393. __SIGNAL_MAX
  394. };
  395. static const struct blobmsg_policy signal_policy[__SIGNAL_MAX] = {
  396. [SIGNAL_PID] = { .name = "pid", .type = BLOBMSG_TYPE_INT32 },
  397. [SIGNAL_NUM] = { .name = "signum", .type = BLOBMSG_TYPE_INT32 },
  398. };
  399. static int proc_signal(struct ubus_context *ctx, struct ubus_object *obj,
  400. struct ubus_request_data *req, const char *method,
  401. struct blob_attr *msg)
  402. {
  403. struct blob_attr *tb[__SIGNAL_MAX];
  404. if (!msg)
  405. return UBUS_STATUS_INVALID_ARGUMENT;
  406. blobmsg_parse(signal_policy, __SIGNAL_MAX, tb, blob_data(msg), blob_len(msg));
  407. if (!tb[SIGNAL_PID || !tb[SIGNAL_NUM]])
  408. return UBUS_STATUS_INVALID_ARGUMENT;
  409. kill(blobmsg_get_u32(tb[SIGNAL_PID]), blobmsg_get_u32(tb[SIGNAL_NUM]));
  410. return 0;
  411. }
  412. __attribute__((format (printf, 2, 3)))
  413. static enum vjson_state vjson_error(char **b, const char *fmt, ...)
  414. {
  415. static char buf[256] = { 0 };
  416. const char *pfx = "Firmware image couldn't be validated: ";
  417. va_list va;
  418. int r;
  419. r = snprintf(buf, sizeof(buf), "%s", pfx);
  420. if (r < 0) {
  421. *b = "vjson_error() snprintf failed";
  422. return VJSON_ERROR;
  423. }
  424. va_start(va, fmt);
  425. r = vsnprintf(buf+r, sizeof(buf)-r, fmt, va);
  426. if (r < 0) {
  427. *b = "vjson_error() vsnprintf failed";
  428. return VJSON_ERROR;
  429. }
  430. va_end(va);
  431. *b = buf;
  432. return VJSON_ERROR;
  433. }
  434. static enum vjson_state vjson_parse_token(json_tokener *tok, char *buf, ssize_t len, char **err)
  435. {
  436. json_object *jsobj = NULL;
  437. jsobj = json_tokener_parse_ex(tok, buf, len);
  438. if (json_tokener_get_error(tok) == json_tokener_continue)
  439. return VJSON_CONTINUE;
  440. if (json_tokener_get_error(tok) == json_tokener_success) {
  441. if (json_object_get_type(jsobj) != json_type_object) {
  442. json_object_put(jsobj);
  443. return vjson_error(err, "result is not an JSON object");
  444. }
  445. blobmsg_add_object(&b, jsobj);
  446. json_object_put(jsobj);
  447. return VJSON_SUCCESS;
  448. }
  449. return vjson_error(err, "failed to parse JSON: %s (%d)",
  450. json_tokener_error_desc(json_tokener_get_error(tok)),
  451. json_tokener_get_error(tok));
  452. }
  453. static enum vjson_state vjson_parse(int fd, char **err)
  454. {
  455. enum vjson_state r = VJSON_ERROR;
  456. size_t read_count = 0;
  457. char buf[64] = { 0 };
  458. json_tokener *tok;
  459. ssize_t len;
  460. int _errno;
  461. tok = json_tokener_new();
  462. if (!tok)
  463. return vjson_error(err, "json_tokener_new() failed");
  464. vjson_error(err, "incomplete JSON input");
  465. while ((len = read(fd, buf, sizeof(buf)))) {
  466. if (len < 0 && errno == EINTR)
  467. continue;
  468. if (len < 0) {
  469. _errno = errno;
  470. json_tokener_free(tok);
  471. return vjson_error(err, "read() failed: %s (%d)",
  472. strerror(_errno), _errno);
  473. }
  474. read_count += len;
  475. r = vjson_parse_token(tok, buf, len, err);
  476. if (r != VJSON_CONTINUE)
  477. break;
  478. memset(buf, 0, sizeof(buf));
  479. }
  480. if (read_count == 0)
  481. vjson_error(err, "no JSON input");
  482. json_tokener_free(tok);
  483. return r;
  484. }
  485. /**
  486. * validate_firmware_image_call - perform validation & store result in global b
  487. *
  488. * @file: firmware image path
  489. */
  490. static enum vjson_state validate_firmware_image_call(const char *file, char **err)
  491. {
  492. const char *path = "/usr/libexec/validate_firmware_image";
  493. enum vjson_state ret = VJSON_ERROR;
  494. int _errno;
  495. int fds[2];
  496. int fd;
  497. blob_buf_init(&b, 0);
  498. vjson_error(err, "unhandled error");
  499. if (pipe(fds)) {
  500. _errno = errno;
  501. return vjson_error(err, "pipe() failed: %s (%d)",
  502. strerror(_errno), _errno);
  503. }
  504. switch (fork()) {
  505. case -1:
  506. _errno = errno;
  507. close(fds[0]);
  508. close(fds[1]);
  509. return vjson_error(err, "fork() failed: %s (%d)",
  510. strerror(_errno), _errno);
  511. case 0:
  512. /* Set stdin & stderr to /dev/null */
  513. fd = open("/dev/null", O_RDWR);
  514. if (fd >= 0) {
  515. dup2(fd, 0);
  516. dup2(fd, 2);
  517. close(fd);
  518. }
  519. /* Set stdout to the shared pipe */
  520. dup2(fds[1], 1);
  521. close(fds[0]);
  522. close(fds[1]);
  523. execl(path, path, file, NULL);
  524. exit(errno);
  525. }
  526. /* Parent process */
  527. close(fds[1]);
  528. ret = vjson_parse(fds[0], err);
  529. close(fds[0]);
  530. return ret;
  531. }
  532. enum {
  533. VALIDATE_FIRMWARE_IMAGE_PATH,
  534. __VALIDATE_FIRMWARE_IMAGE_MAX,
  535. };
  536. static const struct blobmsg_policy validate_firmware_image_policy[__VALIDATE_FIRMWARE_IMAGE_MAX] = {
  537. [VALIDATE_FIRMWARE_IMAGE_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },
  538. };
  539. static int validate_firmware_image(struct ubus_context *ctx,
  540. struct ubus_object *obj,
  541. struct ubus_request_data *req,
  542. const char *method, struct blob_attr *msg)
  543. {
  544. struct blob_attr *tb[__VALIDATE_FIRMWARE_IMAGE_MAX];
  545. enum vjson_state ret = VJSON_ERROR;
  546. char *err;
  547. if (!msg)
  548. return UBUS_STATUS_INVALID_ARGUMENT;
  549. blobmsg_parse(validate_firmware_image_policy, __VALIDATE_FIRMWARE_IMAGE_MAX, tb, blob_data(msg), blob_len(msg));
  550. if (!tb[VALIDATE_FIRMWARE_IMAGE_PATH])
  551. return UBUS_STATUS_INVALID_ARGUMENT;
  552. ret = validate_firmware_image_call(blobmsg_get_string(tb[VALIDATE_FIRMWARE_IMAGE_PATH]), &err);
  553. if (ret != VJSON_SUCCESS)
  554. return UBUS_STATUS_UNKNOWN_ERROR;
  555. ubus_send_reply(ctx, req, b.head);
  556. return UBUS_STATUS_OK;
  557. }
  558. enum {
  559. SYSUPGRADE_PATH,
  560. SYSUPGRADE_FORCE,
  561. SYSUPGRADE_BACKUP,
  562. SYSUPGRADE_PREFIX,
  563. SYSUPGRADE_COMMAND,
  564. SYSUPGRADE_OPTIONS,
  565. __SYSUPGRADE_MAX
  566. };
  567. static const struct blobmsg_policy sysupgrade_policy[__SYSUPGRADE_MAX] = {
  568. [SYSUPGRADE_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },
  569. [SYSUPGRADE_FORCE] = { .name = "force", .type = BLOBMSG_TYPE_BOOL },
  570. [SYSUPGRADE_BACKUP] = { .name = "backup", .type = BLOBMSG_TYPE_STRING },
  571. [SYSUPGRADE_PREFIX] = { .name = "prefix", .type = BLOBMSG_TYPE_STRING },
  572. [SYSUPGRADE_COMMAND] = { .name = "command", .type = BLOBMSG_TYPE_STRING },
  573. [SYSUPGRADE_OPTIONS] = { .name = "options", .type = BLOBMSG_TYPE_TABLE },
  574. };
  575. static void sysupgrade_error(struct ubus_context *ctx,
  576. struct ubus_request_data *req,
  577. const char *message)
  578. {
  579. void *c;
  580. blob_buf_init(&b, 0);
  581. c = blobmsg_open_table(&b, "error");
  582. blobmsg_add_string(&b, "message", message);
  583. blobmsg_close_table(&b, c);
  584. ubus_send_reply(ctx, req, b.head);
  585. }
  586. static int sysupgrade(struct ubus_context *ctx, struct ubus_object *obj,
  587. struct ubus_request_data *req, const char *method,
  588. struct blob_attr *msg)
  589. {
  590. enum {
  591. VALIDATION_VALID,
  592. VALIDATION_FORCEABLE,
  593. VALIDATION_ALLOW_BACKUP,
  594. __VALIDATION_MAX
  595. };
  596. static const struct blobmsg_policy validation_policy[__VALIDATION_MAX] = {
  597. [VALIDATION_VALID] = { .name = "valid", .type = BLOBMSG_TYPE_BOOL },
  598. [VALIDATION_FORCEABLE] = { .name = "forceable", .type = BLOBMSG_TYPE_BOOL },
  599. [VALIDATION_ALLOW_BACKUP] = { .name = "allow_backup", .type = BLOBMSG_TYPE_BOOL },
  600. };
  601. struct blob_attr *validation[__VALIDATION_MAX];
  602. struct blob_attr *tb[__SYSUPGRADE_MAX];
  603. bool valid, forceable, allow_backup;
  604. enum vjson_state ret = VJSON_ERROR;
  605. char *err;
  606. if (!msg)
  607. return UBUS_STATUS_INVALID_ARGUMENT;
  608. blobmsg_parse(sysupgrade_policy, __SYSUPGRADE_MAX, tb, blob_data(msg), blob_len(msg));
  609. if (!tb[SYSUPGRADE_PATH] || !tb[SYSUPGRADE_PREFIX])
  610. return UBUS_STATUS_INVALID_ARGUMENT;
  611. ret = validate_firmware_image_call(blobmsg_get_string(tb[SYSUPGRADE_PATH]), &err);
  612. if (ret != VJSON_SUCCESS) {
  613. sysupgrade_error(ctx, req, err);
  614. return UBUS_STATUS_UNKNOWN_ERROR;
  615. }
  616. blobmsg_parse(validation_policy, __VALIDATION_MAX, validation, blob_data(b.head), blob_len(b.head));
  617. if (!validation[VALIDATION_VALID] || !validation[VALIDATION_FORCEABLE] ||
  618. !validation[VALIDATION_ALLOW_BACKUP]) {
  619. sysupgrade_error(ctx, req, "Validation script provided invalid input");
  620. return UBUS_STATUS_INVALID_ARGUMENT;
  621. }
  622. valid = validation[VALIDATION_VALID] && blobmsg_get_bool(validation[VALIDATION_VALID]);
  623. forceable = validation[VALIDATION_FORCEABLE] && blobmsg_get_bool(validation[VALIDATION_FORCEABLE]);
  624. allow_backup = validation[VALIDATION_ALLOW_BACKUP] && blobmsg_get_bool(validation[VALIDATION_ALLOW_BACKUP]);
  625. if (!valid) {
  626. if (!forceable) {
  627. sysupgrade_error(ctx, req, "Firmware image is broken and cannot be installed");
  628. return UBUS_STATUS_NOT_SUPPORTED;
  629. } else if (!tb[SYSUPGRADE_FORCE] || !blobmsg_get_bool(tb[SYSUPGRADE_FORCE])) {
  630. sysupgrade_error(ctx, req, "Firmware image is invalid");
  631. return UBUS_STATUS_NOT_SUPPORTED;
  632. }
  633. } else if (!allow_backup && tb[SYSUPGRADE_BACKUP]) {
  634. sysupgrade_error(ctx, req, "Firmware image doesn't allow preserving a backup");
  635. return UBUS_STATUS_NOT_SUPPORTED;
  636. }
  637. sysupgrade_exec_upgraded(blobmsg_get_string(tb[SYSUPGRADE_PREFIX]),
  638. blobmsg_get_string(tb[SYSUPGRADE_PATH]),
  639. tb[SYSUPGRADE_BACKUP] ? blobmsg_get_string(tb[SYSUPGRADE_BACKUP]) : NULL,
  640. tb[SYSUPGRADE_COMMAND] ? blobmsg_get_string(tb[SYSUPGRADE_COMMAND]) : NULL,
  641. tb[SYSUPGRADE_OPTIONS]);
  642. /* sysupgrade_exec_upgraded() will never return unless something has gone wrong */
  643. return UBUS_STATUS_UNKNOWN_ERROR;
  644. }
  645. static void
  646. procd_subscribe_cb(struct ubus_context *ctx, struct ubus_object *obj)
  647. {
  648. notify = obj->has_subscribers;
  649. }
  650. static const struct ubus_method system_methods[] = {
  651. UBUS_METHOD_NOARG("board", system_board),
  652. UBUS_METHOD_NOARG("info", system_info),
  653. UBUS_METHOD_NOARG("reboot", system_reboot),
  654. UBUS_METHOD("watchdog", watchdog_set, watchdog_policy),
  655. UBUS_METHOD("signal", proc_signal, signal_policy),
  656. UBUS_METHOD("validate_firmware_image", validate_firmware_image, validate_firmware_image_policy),
  657. UBUS_METHOD("sysupgrade", sysupgrade, sysupgrade_policy),
  658. };
  659. static struct ubus_object_type system_object_type =
  660. UBUS_OBJECT_TYPE("system", system_methods);
  661. static struct ubus_object system_object = {
  662. .name = "system",
  663. .type = &system_object_type,
  664. .methods = system_methods,
  665. .n_methods = ARRAY_SIZE(system_methods),
  666. .subscribe_cb = procd_subscribe_cb,
  667. };
  668. void
  669. procd_bcast_event(char *event, struct blob_attr *msg)
  670. {
  671. int ret;
  672. if (!notify)
  673. return;
  674. ret = ubus_notify(_ctx, &system_object, event, msg, -1);
  675. if (ret)
  676. fprintf(stderr, "Failed to notify log: %s\n", ubus_strerror(ret));
  677. }
  678. void ubus_init_system(struct ubus_context *ctx)
  679. {
  680. int ret;
  681. _ctx = ctx;
  682. initramfs = !!getenv("INITRAMFS");
  683. if (initramfs)
  684. unsetenv("INITRAMFS");
  685. ret = ubus_add_object(ctx, &system_object);
  686. if (ret)
  687. ERROR("Failed to add object: %s\n", ubus_strerror(ret));
  688. }