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