#include <sys/types.h>
#include <sys/reboot.h>
#include <sys/stat.h>
+#include <sys/statvfs.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include "procd.h"
#include "sysupgrade.h"
#include "watchdog.h"
+#include "service/service.h"
static struct blob_buf b;
static int notify;
static struct ubus_context *_ctx;
+static int initramfs;
+
+enum vjson_state {
+ VJSON_ERROR,
+ VJSON_CONTINUE,
+ VJSON_SUCCESS,
+};
+
+static const char *system_rootfs_type(void) {
+ const char proc_mounts[] = "/proc/self/mounts";
+ static char fstype[16] = { 0 };
+ char *mountstr = NULL, *mp = "/", *pos, *tmp;
+ FILE *mounts;
+ size_t len = 0;
+ bool found = false;
+
+ if (initramfs)
+ return "initramfs";
+
+ if (fstype[0])
+ return fstype;
+
+ mounts = fopen(proc_mounts, "r");
+ if (!mounts)
+ return NULL;
+
+ while (getline(&mountstr, &len, mounts) != -1) {
+ pos = strchr(mountstr, ' ');
+ if (!pos)
+ continue;
+
+ tmp = pos + 1;
+ pos = strchr(tmp, ' ');
+ if (!pos)
+ continue;
+
+ *pos = '\0';
+ if (strcmp(tmp, mp))
+ continue;
+
+ tmp = pos + 1;
+ pos = strchr(tmp, ' ');
+ if (!pos)
+ continue;
+
+ *pos = '\0';
+
+ if (!strcmp(tmp, "overlay")) {
+ /*
+ * there is no point in parsing overlay option string for
+ * lowerdir, as that can point to "/" being a previous
+ * overlay mount (after firstboot or sysuprade config
+ * restore). Hence just assume the lowerdir is "/rom" and
+ * restart searching for that instead if that's not
+ * already the case.
+ */
+ if (!strcmp(mp, "/rom"))
+ break;
+
+ mp = "/rom";
+ fseek(mounts, 0, SEEK_SET);
+ continue;
+ }
+
+ found = true;
+ break;
+ }
+
+ if (found)
+ strncpy(fstype, tmp, sizeof(fstype) - 1);
+
+ fstype[sizeof(fstype) - 1]= '\0';
+ free(mountstr);
+ fclose(mounts);
+
+ if (found)
+ return fstype;
+ else
+ return NULL;
+}
static int system_board(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
void *c;
char line[256];
char *key, *val, *next;
+ const char *rootfs_type = system_rootfs_type();
struct utsname utsname;
FILE *f;
if (!key || !val)
continue;
+#ifdef __aarch64__
+ if (!strcasecmp(key, "CPU revision")) {
+ snprintf(line, sizeof(line), "ARMv8 Processor rev %lu", strtoul(val + 2, NULL, 16));
+ blobmsg_add_string(&b, "system", line);
+ break;
+ }
+#elif __riscv
+ if (!strcasecmp(key, "isa")) {
+ snprintf(line, sizeof(line), "RISC-V (%s)", val + 2);
+ blobmsg_add_string(&b, "system", line);
+ break;
+ }
+#else
if (!strcasecmp(key, "system type") ||
!strcasecmp(key, "processor") ||
!strcasecmp(key, "cpu") ||
break;
}
}
+#endif
}
fclose(f);
fclose(f);
}
+ if (rootfs_type)
+ blobmsg_add_string(&b, "rootfs_type", rootfs_type);
+
if ((f = fopen("/etc/openwrt_release", "r")) != NULL)
{
c = blobmsg_open_table(&b, "release");
return UBUS_STATUS_OK;
}
+static unsigned long
+kscale(unsigned long b, unsigned long bs)
+{
+ return (b * (unsigned long long) bs + 1024/2) / 1024;
+}
+
static int system_info(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
#ifdef linux
struct sysinfo info;
void *c;
+ char line[256];
+ char *key, *val;
+ unsigned long long available, cached;
+ FILE *f;
+ int i;
+ struct statvfs s;
+ const char *fslist[] = {
+ "/", "root",
+ "/tmp", "tmp",
+ };
if (sysinfo(&info))
return UBUS_STATUS_UNKNOWN_ERROR;
+
+ if ((f = fopen("/proc/meminfo", "r")) == NULL)
+ return UBUS_STATUS_UNKNOWN_ERROR;
+
+ /* if linux < 3.14 MemAvailable is not in meminfo */
+ available = 0;
+ cached = 0;
+
+ while (fgets(line, sizeof(line), f))
+ {
+ key = strtok(line, " :");
+ val = strtok(NULL, " ");
+
+ if (!key || !val)
+ continue;
+
+ if (!strcasecmp(key, "MemAvailable"))
+ available = 1024 * atoll(val);
+ else if (!strcasecmp(key, "Cached"))
+ cached = 1024 * atoll(val);
+ }
+
+ fclose(f);
#endif
now = time(NULL);
blobmsg_close_array(&b, c);
c = blobmsg_open_table(&b, "memory");
- blobmsg_add_u64(&b, "total", info.mem_unit * info.totalram);
- blobmsg_add_u64(&b, "free", info.mem_unit * info.freeram);
- blobmsg_add_u64(&b, "shared", info.mem_unit * info.sharedram);
- blobmsg_add_u64(&b, "buffered", info.mem_unit * info.bufferram);
+ blobmsg_add_u64(&b, "total",
+ (uint64_t)info.mem_unit * (uint64_t)info.totalram);
+ blobmsg_add_u64(&b, "free",
+ (uint64_t)info.mem_unit * (uint64_t)info.freeram);
+ blobmsg_add_u64(&b, "shared",
+ (uint64_t)info.mem_unit * (uint64_t)info.sharedram);
+ blobmsg_add_u64(&b, "buffered",
+ (uint64_t)info.mem_unit * (uint64_t)info.bufferram);
+ blobmsg_add_u64(&b, "available", available);
+ blobmsg_add_u64(&b, "cached", cached);
blobmsg_close_table(&b, c);
+ for (i = 0; i < sizeof(fslist) / sizeof(fslist[0]); i += 2) {
+ if (statvfs(fslist[i], &s))
+ continue;
+
+ c = blobmsg_open_table(&b, fslist[i+1]);
+
+ if (!s.f_frsize)
+ s.f_frsize = s.f_bsize;
+
+ blobmsg_add_u64(&b, "total", kscale(s.f_blocks, s.f_frsize));
+ blobmsg_add_u64(&b, "free", kscale(s.f_bfree, s.f_frsize));
+ blobmsg_add_u64(&b, "used", kscale(s.f_blocks - s.f_bfree, s.f_frsize));
+ blobmsg_add_u64(&b, "avail", kscale(s.f_bavail, s.f_frsize));
+
+ blobmsg_close_table(&b, c);
+ }
+
c = blobmsg_open_table(&b, "swap");
- blobmsg_add_u64(&b, "total", info.mem_unit * info.totalswap);
- blobmsg_add_u64(&b, "free", info.mem_unit * info.freeswap);
+ blobmsg_add_u64(&b, "total",
+ (uint64_t)info.mem_unit * (uint64_t)info.totalswap);
+ blobmsg_add_u64(&b, "free",
+ (uint64_t)info.mem_unit * (uint64_t)info.freeswap);
blobmsg_close_table(&b, c);
#endif
if (timeout <= frequency)
timeout = frequency * 2;
- watchdog_timeout(timeout);
+ watchdog_timeout(timeout);
}
if (tb[WDT_MAGICCLOSE])
return 0;
}
+__attribute__((format (printf, 2, 3)))
+static enum vjson_state vjson_error(char **b, const char *fmt, ...)
+{
+ static char buf[256] = { 0 };
+ const char *pfx = "Firmware image couldn't be validated: ";
+ va_list va;
+ int r;
+
+ r = snprintf(buf, sizeof(buf), "%s", pfx);
+ if (r < 0) {
+ *b = "vjson_error() snprintf failed";
+ return VJSON_ERROR;
+ }
+
+ va_start(va, fmt);
+ r = vsnprintf(buf+r, sizeof(buf)-r, fmt, va);
+ if (r < 0) {
+ *b = "vjson_error() vsnprintf failed";
+ return VJSON_ERROR;
+ }
+ va_end(va);
+
+ *b = buf;
+ return VJSON_ERROR;
+}
+
+static enum vjson_state vjson_parse_token(json_tokener *tok, char *buf, ssize_t len, char **err)
+{
+ json_object *jsobj = NULL;
+
+ jsobj = json_tokener_parse_ex(tok, buf, len);
+ if (json_tokener_get_error(tok) == json_tokener_continue)
+ return VJSON_CONTINUE;
+
+ if (json_tokener_get_error(tok) == json_tokener_success) {
+ if (json_object_get_type(jsobj) != json_type_object) {
+ json_object_put(jsobj);
+ return vjson_error(err, "result is not an JSON object");
+ }
+
+ blobmsg_add_object(&b, jsobj);
+ json_object_put(jsobj);
+ return VJSON_SUCCESS;
+ }
+
+ return vjson_error(err, "failed to parse JSON: %s (%d)",
+ json_tokener_error_desc(json_tokener_get_error(tok)),
+ json_tokener_get_error(tok));
+}
+
+static enum vjson_state vjson_parse(int fd, char **err)
+{
+ enum vjson_state r = VJSON_ERROR;
+ size_t read_count = 0;
+ char buf[64] = { 0 };
+ json_tokener *tok;
+ ssize_t len;
+ int _errno;
+
+ tok = json_tokener_new();
+ if (!tok)
+ return vjson_error(err, "json_tokener_new() failed");
+
+ vjson_error(err, "incomplete JSON input");
+
+ while ((len = read(fd, buf, sizeof(buf)))) {
+ if (len < 0 && errno == EINTR)
+ continue;
+
+ if (len < 0) {
+ _errno = errno;
+ json_tokener_free(tok);
+ return vjson_error(err, "read() failed: %s (%d)",
+ strerror(_errno), _errno);
+ }
+
+ read_count += len;
+ r = vjson_parse_token(tok, buf, len, err);
+ if (r != VJSON_CONTINUE)
+ break;
+
+ memset(buf, 0, sizeof(buf));
+ }
+
+ if (read_count == 0)
+ vjson_error(err, "no JSON input");
+
+ json_tokener_free(tok);
+ return r;
+}
+
/**
* validate_firmware_image_call - perform validation & store result in global b
*
* @file: firmware image path
*/
-static int validate_firmware_image_call(const char *file)
+static enum vjson_state validate_firmware_image_call(const char *file, char **err)
{
const char *path = "/usr/libexec/validate_firmware_image";
- json_object *jsobj = NULL;
- json_tokener *tok;
- char buf[64];
- ssize_t len;
+ enum vjson_state ret = VJSON_ERROR;
+ int _errno;
int fds[2];
- int err;
int fd;
- if (pipe(fds))
- return -errno;
+ blob_buf_init(&b, 0);
+ vjson_error(err, "unhandled error");
+
+ if (pipe(fds)) {
+ _errno = errno;
+ return vjson_error(err, "pipe() failed: %s (%d)",
+ strerror(_errno), _errno);
+ }
switch (fork()) {
case -1:
- return -errno;
+ _errno = errno;
+
+ close(fds[0]);
+ close(fds[1]);
+
+ return vjson_error(err, "fork() failed: %s (%d)",
+ strerror(_errno), _errno);
case 0:
/* Set stdin & stderr to /dev/null */
fd = open("/dev/null", O_RDWR);
}
/* Parent process */
-
- tok = json_tokener_new();
- if (!tok) {
- close(fds[0]);
- close(fds[1]);
- return -ENOMEM;
- }
-
- blob_buf_init(&b, 0);
- while ((len = read(fds[0], buf, sizeof(buf)))) {
- jsobj = json_tokener_parse_ex(tok, buf, len);
-
- if (json_tokener_get_error(tok) == json_tokener_success)
- break;
- else if (json_tokener_get_error(tok) == json_tokener_continue)
- continue;
- else
- fprintf(stderr, "Failed to parse JSON: %d\n",
- json_tokener_get_error(tok));
- }
-
- close(fds[0]);
close(fds[1]);
- err = -ENOENT;
- if (jsobj) {
- if (json_object_get_type(jsobj) == json_type_object) {
- blobmsg_add_object(&b, jsobj);
- err = 0;
- }
-
- json_object_put(jsobj);
- }
-
- json_tokener_free(tok);
+ ret = vjson_parse(fds[0], err);
+ close(fds[0]);
- return err;
+ return ret;
}
enum {
const char *method, struct blob_attr *msg)
{
struct blob_attr *tb[__VALIDATE_FIRMWARE_IMAGE_MAX];
+ enum vjson_state ret = VJSON_ERROR;
+ char *err;
if (!msg)
return UBUS_STATUS_INVALID_ARGUMENT;
if (!tb[VALIDATE_FIRMWARE_IMAGE_PATH])
return UBUS_STATUS_INVALID_ARGUMENT;
- if (validate_firmware_image_call(blobmsg_get_string(tb[VALIDATE_FIRMWARE_IMAGE_PATH])))
+ ret = validate_firmware_image_call(blobmsg_get_string(tb[VALIDATE_FIRMWARE_IMAGE_PATH]), &err);
+ if (ret != VJSON_SUCCESS)
return UBUS_STATUS_UNKNOWN_ERROR;
ubus_send_reply(ctx, req, b.head);
struct blob_attr *validation[__VALIDATION_MAX];
struct blob_attr *tb[__SYSUPGRADE_MAX];
bool valid, forceable, allow_backup;
+ enum vjson_state ret = VJSON_ERROR;
+ char *err;
if (!msg)
return UBUS_STATUS_INVALID_ARGUMENT;
if (!tb[SYSUPGRADE_PATH] || !tb[SYSUPGRADE_PREFIX])
return UBUS_STATUS_INVALID_ARGUMENT;
- if (validate_firmware_image_call(blobmsg_get_string(tb[SYSUPGRADE_PATH]))) {
- sysupgrade_error(ctx, req, "Firmware image couldn't be validated");
+ ret = validate_firmware_image_call(blobmsg_get_string(tb[SYSUPGRADE_PATH]), &err);
+ if (ret != VJSON_SUCCESS) {
+ sysupgrade_error(ctx, req, err);
return UBUS_STATUS_UNKNOWN_ERROR;
}
blobmsg_parse(validation_policy, __VALIDATION_MAX, validation, blob_data(b.head), blob_len(b.head));
+ if (!validation[VALIDATION_VALID] || !validation[VALIDATION_FORCEABLE] ||
+ !validation[VALIDATION_ALLOW_BACKUP]) {
+ sysupgrade_error(ctx, req, "Validation script provided invalid input");
+ return UBUS_STATUS_INVALID_ARGUMENT;
+ }
+
valid = validation[VALIDATION_VALID] && blobmsg_get_bool(validation[VALIDATION_VALID]);
forceable = validation[VALIDATION_FORCEABLE] && blobmsg_get_bool(validation[VALIDATION_FORCEABLE]);
allow_backup = validation[VALIDATION_ALLOW_BACKUP] && blobmsg_get_bool(validation[VALIDATION_ALLOW_BACKUP]);
return UBUS_STATUS_NOT_SUPPORTED;
}
+ service_stop_all();
+
sysupgrade_exec_upgraded(blobmsg_get_string(tb[SYSUPGRADE_PREFIX]),
blobmsg_get_string(tb[SYSUPGRADE_PATH]),
tb[SYSUPGRADE_BACKUP] ? blobmsg_get_string(tb[SYSUPGRADE_BACKUP]) : NULL,
int ret;
_ctx = ctx;
+
+ initramfs = !!getenv("INITRAMFS");
+ if (initramfs)
+ unsetenv("INITRAMFS");
+
ret = ubus_add_object(ctx, &system_object);
if (ret)
ERROR("Failed to add object: %s\n", ubus_strerror(ret));