From: Rafał Miłecki <ra...@milecki.pl> This commit adds "rc" ubus object with "exec" method that allows calling /etc/init.d/ scripts. It's useful for all kind of UIs (e.g. LuCI) and custom apps.
The next step should be "list" method support listing add init scripts. Signed-off-by: Rafał Miłecki <ra...@milecki.pl> --- I didn't implement custom timeout for waiting for init.d script yet. Two reasons: 1. ubus call will eventually timeout on its own 2. it'd be nice to have some uloop_process helper for that --- CMakeLists.txt | 2 +- include/rpcd/rc.h | 7 +++ main.c | 6 +- rc.c | 140 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 152 insertions(+), 3 deletions(-) create mode 100644 include/rpcd/rc.h create mode 100644 rc.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 3bfc286..26e011e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,7 +38,7 @@ INCLUDE_DIRECTORIES(${ubus_include_dir}) FIND_PATH(ubox_include_dir libubox/blobmsg_json.h) INCLUDE_DIRECTORIES(${ubox_include_dir}) -ADD_EXECUTABLE(rpcd main.c exec.c session.c uci.c plugin.c) +ADD_EXECUTABLE(rpcd main.c exec.c session.c uci.c rc.c plugin.c) TARGET_LINK_LIBRARIES(rpcd ${ubox} ${ubus} ${uci} ${blobmsg_json} ${json} ${crypt} dl) SET(PLUGINS "") diff --git a/include/rpcd/rc.h b/include/rpcd/rc.h new file mode 100644 index 0000000..ca00f56 --- /dev/null +++ b/include/rpcd/rc.h @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: ISC OR MIT +#ifndef __RPCD_RC_H +#define __RPCD_RC_H + +int rpc_rc_api_init(struct ubus_context *ctx); + +#endif diff --git a/main.c b/main.c index 9a177cf..d77a814 100644 --- a/main.c +++ b/main.c @@ -25,10 +25,11 @@ #include <signal.h> #include <sys/stat.h> +#include <rpcd/exec.h> +#include <rpcd/plugin.h> +#include <rpcd/rc.h> #include <rpcd/session.h> #include <rpcd/uci.h> -#include <rpcd/plugin.h> -#include <rpcd/exec.h> static struct ubus_context *ctx; static bool respawn = false; @@ -113,6 +114,7 @@ int main(int argc, char **argv) rpc_session_api_init(ctx); rpc_uci_api_init(ctx); + rpc_rc_api_init(ctx); rpc_plugin_api_init(ctx); hangup = getenv("RPC_HANGUP"); diff --git a/rc.c b/rc.c new file mode 100644 index 0000000..a354332 --- /dev/null +++ b/rc.c @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: ISC OR MIT +/* + * rpcd - UBUS RPC server + * + * Copyright (C) 2020 Rafał Miłecki <ra...@milecki.pl> + */ + +#include <fcntl.h> +#include <linux/limits.h> +#include <sys/stat.h> +#include <sys/wait.h> + +#include <libubox/blobmsg.h> +#include <libubox/uloop.h> +#include <libubus.h> + +#include <rpcd/rc.h> + +enum { + RC_EXEC_NAME, + RC_EXEC_ACTION, + __RC_EXEC_MAX +}; + +static const struct blobmsg_policy rc_exec_policy[] = { + [RC_EXEC_NAME] = { "name", BLOBMSG_TYPE_STRING }, + [RC_EXEC_ACTION] = { "action", BLOBMSG_TYPE_STRING }, +}; + +struct rc_exec_context { + struct uloop_process process; + struct ubus_context *ctx; + struct ubus_request_data req; +}; + +static void rc_exec_cb(struct uloop_process *p, int stat) +{ + struct rc_exec_context *c = container_of(p, struct rc_exec_context, process); + + ubus_complete_deferred_request(c->ctx, &c->req, UBUS_STATUS_OK); + + free(c); +} + +static int rc_exec(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + struct blob_attr *tb[__RC_EXEC_MAX]; + struct rc_exec_context *c; + struct stat s; + const char *action; + const char *name; + char script[PATH_MAX]; + pid_t pid; + const char *chr; + int fd; + + blobmsg_parse(rc_exec_policy, __RC_EXEC_MAX, tb, blobmsg_data(msg), blobmsg_data_len(msg)); + + if (!tb[RC_EXEC_NAME] || !tb[RC_EXEC_ACTION]) + return UBUS_STATUS_INVALID_ARGUMENT; + + name = blobmsg_get_string(tb[RC_EXEC_NAME]); + + /* Validate script name */ + for (chr = name; (chr = strchr(chr, '.')); chr++) { + if (*(chr + 1) == '.') + return UBUS_STATUS_INVALID_ARGUMENT; + } + if (strchr(name, '/')) + return UBUS_STATUS_INVALID_ARGUMENT; + + /* Validate script privileges */ + snprintf(script, sizeof(script), "/etc/init.d/%s", name); + if (stat(script, &s)) + return UBUS_STATUS_NOT_FOUND; + if (s.st_uid != 0 || s.st_gid != 0 || !(s.st_mode & S_IXUSR) || (s.st_mode & (S_IWGRP | S_IWOTH))) + return UBUS_STATUS_PERMISSION_DENIED; + + action = blobmsg_get_string(tb[RC_EXEC_ACTION]); + if (strcmp(action, "disable") && + strcmp(action, "enable") && + strcmp(action, "stop") && + strcmp(action, "start") && + strcmp(action, "restart") && + strcmp(action, "reload")) + return UBUS_STATUS_INVALID_ARGUMENT; + + c = calloc(1, sizeof(*c)); + if (!c) + return UBUS_STATUS_UNKNOWN_ERROR; + + pid = fork(); + switch (pid) { + case -1: + free(c); + return UBUS_STATUS_UNKNOWN_ERROR; + case 0: + /* Set stdin, stdout & stderr to /dev/null */ + fd = open("/dev/null", O_RDWR); + if (fd >= 0) { + dup2(fd, 0); + dup2(fd, 1); + dup2(fd, 2); + close(fd); + } + + execl(script, script, action, NULL); + exit(errno); + default: + c->ctx = ctx; + c->process.pid = pid; + c->process.cb = rc_exec_cb; + uloop_process_add(&c->process); + + ubus_defer_request(ctx, req, &c->req); + + return 0; /* Deferred */ + } +} + +int rpc_rc_api_init(struct ubus_context *ctx) +{ + static const struct ubus_method rc_methods[] = { + UBUS_METHOD("exec", rc_exec, rc_exec_policy), + }; + + static struct ubus_object_type rc_type = + UBUS_OBJECT_TYPE("rc", rc_methods); + + static struct ubus_object obj = { + .name = "rc", + .type = &rc_type, + .methods = rc_methods, + .n_methods = ARRAY_SIZE(rc_methods), + }; + + return ubus_add_object(ctx, &obj); +} -- 2.26.1 _______________________________________________ openwrt-devel mailing list openwrt-devel@lists.openwrt.org http://lists.infradead.org/mailman/listinfo/openwrt-devel