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