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