chore: cleanup the repository
For Liminix, we don't need a bunch of things that assumes an OpenWRT base. Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
This commit is contained in:
parent
bcc091d2f1
commit
5da2275a98
11 changed files with 5 additions and 5659 deletions
|
@ -8,8 +8,6 @@ INCLUDE_DIRECTORIES(include)
|
||||||
|
|
||||||
OPTION(FILE_SUPPORT "File plugin support" ON)
|
OPTION(FILE_SUPPORT "File plugin support" ON)
|
||||||
OPTION(IWINFO_SUPPORT "libiwinfo plugin support" ON)
|
OPTION(IWINFO_SUPPORT "libiwinfo plugin support" ON)
|
||||||
OPTION(RPCSYS_SUPPORT "rpc-sys plugin support" ON)
|
|
||||||
OPTION(UCODE_SUPPORT "ucode plugin support" ON)
|
|
||||||
|
|
||||||
SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
|
SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
|
||||||
|
|
||||||
|
@ -23,15 +21,10 @@ IF(HAVE_SHADOW)
|
||||||
ADD_DEFINITIONS(-DHAVE_SHADOW)
|
ADD_DEFINITIONS(-DHAVE_SHADOW)
|
||||||
ENDIF()
|
ENDIF()
|
||||||
|
|
||||||
FIND_LIBRARY(uci NAMES uci)
|
|
||||||
FIND_LIBRARY(ubus NAMES ubus)
|
FIND_LIBRARY(ubus NAMES ubus)
|
||||||
FIND_LIBRARY(ubox NAMES ubox)
|
FIND_LIBRARY(ubox NAMES ubox)
|
||||||
FIND_LIBRARY(blobmsg_json NAMES blobmsg_json)
|
FIND_LIBRARY(blobmsg_json NAMES blobmsg_json)
|
||||||
FIND_LIBRARY(json NAMES json-c json)
|
FIND_LIBRARY(json NAMES json-c json)
|
||||||
FIND_LIBRARY(crypt NAMES crypt)
|
|
||||||
IF(crypt STREQUAL "crypt-NOTFOUND")
|
|
||||||
SET(crypt "")
|
|
||||||
ENDIF()
|
|
||||||
|
|
||||||
FIND_PATH(ubus_include_dir libubus.h)
|
FIND_PATH(ubus_include_dir libubus.h)
|
||||||
INCLUDE_DIRECTORIES(${ubus_include_dir})
|
INCLUDE_DIRECTORIES(${ubus_include_dir})
|
||||||
|
@ -39,10 +32,8 @@ INCLUDE_DIRECTORIES(${ubus_include_dir})
|
||||||
FIND_PATH(ubox_include_dir libubox/blobmsg_json.h)
|
FIND_PATH(ubox_include_dir libubox/blobmsg_json.h)
|
||||||
INCLUDE_DIRECTORIES(${ubox_include_dir})
|
INCLUDE_DIRECTORIES(${ubox_include_dir})
|
||||||
|
|
||||||
ADD_EXECUTABLE(rpcd main.c exec.c session.c uci.c rc.c plugin.c)
|
ADD_EXECUTABLE(rpcd main.c plugin.c)
|
||||||
TARGET_LINK_LIBRARIES(rpcd ${ubox} ${ubus} ${uci} ${blobmsg_json} ${json} ${crypt} dl)
|
TARGET_LINK_LIBRARIES(rpcd ${ubox} ${ubus} ${blobmsg_json} ${json} dl)
|
||||||
|
|
||||||
SET(PLUGINS "")
|
|
||||||
|
|
||||||
IF(FILE_SUPPORT)
|
IF(FILE_SUPPORT)
|
||||||
SET(PLUGINS ${PLUGINS} file_plugin)
|
SET(PLUGINS ${PLUGINS} file_plugin)
|
||||||
|
@ -51,13 +42,6 @@ IF(FILE_SUPPORT)
|
||||||
SET_TARGET_PROPERTIES(file_plugin PROPERTIES OUTPUT_NAME file PREFIX "")
|
SET_TARGET_PROPERTIES(file_plugin PROPERTIES OUTPUT_NAME file PREFIX "")
|
||||||
ENDIF()
|
ENDIF()
|
||||||
|
|
||||||
IF(RPCSYS_SUPPORT)
|
|
||||||
SET(PLUGINS ${PLUGINS} rpcsys_plugin)
|
|
||||||
ADD_LIBRARY(rpcsys_plugin MODULE sys.c)
|
|
||||||
TARGET_LINK_LIBRARIES(rpcsys_plugin ${ubox} ${ubus})
|
|
||||||
SET_TARGET_PROPERTIES(rpcsys_plugin PROPERTIES OUTPUT_NAME rpcsys PREFIX "")
|
|
||||||
ENDIF()
|
|
||||||
|
|
||||||
IF(IWINFO_SUPPORT)
|
IF(IWINFO_SUPPORT)
|
||||||
FIND_LIBRARY(iwinfo NAMES iwinfo)
|
FIND_LIBRARY(iwinfo NAMES iwinfo)
|
||||||
SET(PLUGINS ${PLUGINS} iwinfo_plugin)
|
SET(PLUGINS ${PLUGINS} iwinfo_plugin)
|
||||||
|
@ -66,14 +50,6 @@ IF(IWINFO_SUPPORT)
|
||||||
SET_TARGET_PROPERTIES(iwinfo_plugin PROPERTIES OUTPUT_NAME iwinfo PREFIX "")
|
SET_TARGET_PROPERTIES(iwinfo_plugin PROPERTIES OUTPUT_NAME iwinfo PREFIX "")
|
||||||
ENDIF()
|
ENDIF()
|
||||||
|
|
||||||
IF(UCODE_SUPPORT)
|
|
||||||
FIND_LIBRARY(ucode NAMES ucode)
|
|
||||||
SET(PLUGINS ${PLUGINS} ucode_plugin)
|
|
||||||
ADD_LIBRARY(ucode_plugin MODULE ucode.c)
|
|
||||||
TARGET_LINK_LIBRARIES(ucode_plugin ${ucode})
|
|
||||||
SET_TARGET_PROPERTIES(ucode_plugin PROPERTIES OUTPUT_NAME ucode PREFIX "")
|
|
||||||
ENDIF()
|
|
||||||
|
|
||||||
INSTALL(TARGETS rpcd ${PLUGINS}
|
INSTALL(TARGETS rpcd ${PLUGINS}
|
||||||
RUNTIME DESTINATION sbin
|
RUNTIME DESTINATION sbin
|
||||||
LIBRARY DESTINATION lib/rpcd
|
LIBRARY DESTINATION lib/rpcd
|
||||||
|
|
3
README.md
Normal file
3
README.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# `rpcd`, an ubus RPC daemon for Liminix inspired by OpenWRT's `rpcd`
|
||||||
|
|
||||||
|
This is NOT the `rpcd` you are used to, this one is tailored to Liminix usecases.
|
390
exec.c
390
exec.c
|
@ -1,390 +0,0 @@
|
||||||
/*
|
|
||||||
* rpcd - UBUS RPC server
|
|
||||||
*
|
|
||||||
* Copyright (C) 2013-2014 Jo-Philipp Wich <jow@openwrt.org>
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
|
||||||
* copyright notice and this permission notice appear in all copies.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <dirent.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
|
|
||||||
#include <rpcd/exec.h>
|
|
||||||
|
|
||||||
static int
|
|
||||||
rpc_errno_status(void)
|
|
||||||
{
|
|
||||||
switch (errno)
|
|
||||||
{
|
|
||||||
case EACCES:
|
|
||||||
return UBUS_STATUS_PERMISSION_DENIED;
|
|
||||||
|
|
||||||
case ENOTDIR:
|
|
||||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
|
||||||
|
|
||||||
case ENOENT:
|
|
||||||
return UBUS_STATUS_NOT_FOUND;
|
|
||||||
|
|
||||||
case EINVAL:
|
|
||||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return UBUS_STATUS_UNKNOWN_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *
|
|
||||||
rpc_exec_lookup(const char *cmd)
|
|
||||||
{
|
|
||||||
struct stat s;
|
|
||||||
int plen = 0, clen = strlen(cmd) + 1;
|
|
||||||
char *search, *p;
|
|
||||||
static char path[PATH_MAX];
|
|
||||||
|
|
||||||
if (!stat(cmd, &s) && S_ISREG(s.st_mode))
|
|
||||||
return cmd;
|
|
||||||
|
|
||||||
search = getenv("PATH");
|
|
||||||
|
|
||||||
if (!search)
|
|
||||||
search = "/bin:/usr/bin:/sbin:/usr/sbin";
|
|
||||||
|
|
||||||
p = search;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if (*p != ':' && *p != '\0')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
plen = p - search;
|
|
||||||
|
|
||||||
if ((plen + clen) >= sizeof(path))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
strncpy(path, search, plen);
|
|
||||||
sprintf(path + plen, "/%s", cmd);
|
|
||||||
|
|
||||||
if (!stat(path, &s) && S_ISREG(s.st_mode))
|
|
||||||
return path;
|
|
||||||
|
|
||||||
search = p + 1;
|
|
||||||
}
|
|
||||||
while (*p++);
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
rpc_ustream_to_blobmsg(struct blob_buf *blob, struct ustream *s,
|
|
||||||
const char *name)
|
|
||||||
{
|
|
||||||
int len;
|
|
||||||
char *rbuf, *wbuf;
|
|
||||||
|
|
||||||
if ((len = ustream_pending_data(s, false)) > 0)
|
|
||||||
{
|
|
||||||
wbuf = blobmsg_alloc_string_buffer(blob, name, len + 1);
|
|
||||||
|
|
||||||
if (!wbuf)
|
|
||||||
return;
|
|
||||||
|
|
||||||
ustream_for_each_read_buffer(s, rbuf, len)
|
|
||||||
{
|
|
||||||
memcpy(wbuf, rbuf, len);
|
|
||||||
wbuf += len;
|
|
||||||
}
|
|
||||||
|
|
||||||
*wbuf = 0;
|
|
||||||
blobmsg_add_string_buffer(blob);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
rpc_exec_reply(struct rpc_exec_context *c, int rv)
|
|
||||||
{
|
|
||||||
uloop_timeout_cancel(&c->timeout);
|
|
||||||
uloop_process_delete(&c->process);
|
|
||||||
|
|
||||||
if (rv == UBUS_STATUS_OK)
|
|
||||||
{
|
|
||||||
if (!c->stdout_cb && !c->stderr_cb && !c->finish_cb)
|
|
||||||
{
|
|
||||||
blobmsg_add_u32(&c->blob, "code", WEXITSTATUS(c->stat));
|
|
||||||
rpc_ustream_to_blobmsg(&c->blob, &c->opipe.stream, "stdout");
|
|
||||||
rpc_ustream_to_blobmsg(&c->blob, &c->epipe.stream, "stderr");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c->finish_cb)
|
|
||||||
rv = c->finish_cb(&c->blob, c->stat, c->priv);
|
|
||||||
|
|
||||||
if (rv == UBUS_STATUS_OK)
|
|
||||||
ubus_send_reply(c->context, &c->request, c->blob.head);
|
|
||||||
|
|
||||||
ubus_complete_deferred_request(c->context, &c->request, rv);
|
|
||||||
|
|
||||||
blob_buf_free(&c->blob);
|
|
||||||
|
|
||||||
ustream_free(&c->opipe.stream);
|
|
||||||
ustream_free(&c->epipe.stream);
|
|
||||||
|
|
||||||
close(c->opipe.fd.fd);
|
|
||||||
close(c->epipe.fd.fd);
|
|
||||||
|
|
||||||
if (c->priv)
|
|
||||||
free(c->priv);
|
|
||||||
|
|
||||||
free(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
rpc_exec_timeout_cb(struct uloop_timeout *t)
|
|
||||||
{
|
|
||||||
struct rpc_exec_context *c =
|
|
||||||
container_of(t, struct rpc_exec_context, timeout);
|
|
||||||
|
|
||||||
kill(c->process.pid, SIGKILL);
|
|
||||||
rpc_exec_reply(c, UBUS_STATUS_TIMEOUT);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
rpc_exec_process_cb(struct uloop_process *p, int stat)
|
|
||||||
{
|
|
||||||
struct rpc_exec_context *c =
|
|
||||||
container_of(p, struct rpc_exec_context, process);
|
|
||||||
|
|
||||||
c->stat = stat;
|
|
||||||
|
|
||||||
ustream_poll(&c->opipe.stream);
|
|
||||||
ustream_poll(&c->epipe.stream);
|
|
||||||
|
|
||||||
close(c->opipe.fd.fd);
|
|
||||||
close(c->epipe.fd.fd);
|
|
||||||
|
|
||||||
ustream_poll(&c->opipe.stream);
|
|
||||||
ustream_poll(&c->epipe.stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
rpc_exec_ipipe_write_cb(struct ustream *s, int bytes)
|
|
||||||
{
|
|
||||||
struct rpc_exec_context *c =
|
|
||||||
container_of(s, struct rpc_exec_context, ipipe.stream);
|
|
||||||
|
|
||||||
if (c->stdin_cb(s, c->priv) <= 0)
|
|
||||||
{
|
|
||||||
ustream_free(&c->ipipe.stream);
|
|
||||||
close(c->ipipe.fd.fd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
rpc_exec_opipe_read_cb(struct ustream *s, int bytes)
|
|
||||||
{
|
|
||||||
int len, rv;
|
|
||||||
char *buf;
|
|
||||||
struct rpc_exec_context *c =
|
|
||||||
container_of(s, struct rpc_exec_context, opipe.stream);
|
|
||||||
|
|
||||||
if (c->stdout_cb)
|
|
||||||
{
|
|
||||||
do {
|
|
||||||
buf = ustream_get_read_buf(s, &len);
|
|
||||||
|
|
||||||
if (!buf || !len)
|
|
||||||
break;
|
|
||||||
|
|
||||||
rv = c->stdout_cb(&c->blob, buf, len, c->priv);
|
|
||||||
|
|
||||||
if (rv <= 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
ustream_consume(s, rv);
|
|
||||||
} while(1);
|
|
||||||
}
|
|
||||||
else if (ustream_read_buf_full(s))
|
|
||||||
{
|
|
||||||
rpc_exec_reply(c, UBUS_STATUS_NOT_SUPPORTED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
rpc_exec_epipe_read_cb(struct ustream *s, int bytes)
|
|
||||||
{
|
|
||||||
int len, rv;
|
|
||||||
char *buf;
|
|
||||||
struct rpc_exec_context *c =
|
|
||||||
container_of(s, struct rpc_exec_context, epipe.stream);
|
|
||||||
|
|
||||||
if (c->stderr_cb)
|
|
||||||
{
|
|
||||||
do {
|
|
||||||
buf = ustream_get_read_buf(s, &len);
|
|
||||||
|
|
||||||
if (!buf || !len)
|
|
||||||
break;
|
|
||||||
|
|
||||||
rv = c->stderr_cb(&c->blob, buf, len, c->priv);
|
|
||||||
|
|
||||||
if (rv <= 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
ustream_consume(s, rv);
|
|
||||||
} while(1);
|
|
||||||
}
|
|
||||||
else if (ustream_read_buf_full(s))
|
|
||||||
{
|
|
||||||
rpc_exec_reply(c, UBUS_STATUS_NOT_SUPPORTED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
rpc_exec_opipe_state_cb(struct ustream *s)
|
|
||||||
{
|
|
||||||
struct rpc_exec_context *c =
|
|
||||||
container_of(s, struct rpc_exec_context, opipe.stream);
|
|
||||||
|
|
||||||
if (c->opipe.stream.eof && c->epipe.stream.eof)
|
|
||||||
rpc_exec_reply(c, UBUS_STATUS_OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
rpc_exec_epipe_state_cb(struct ustream *s)
|
|
||||||
{
|
|
||||||
struct rpc_exec_context *c =
|
|
||||||
container_of(s, struct rpc_exec_context, epipe.stream);
|
|
||||||
|
|
||||||
if (c->opipe.stream.eof && c->epipe.stream.eof)
|
|
||||||
rpc_exec_reply(c, UBUS_STATUS_OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
rpc_exec(const char **args, rpc_exec_write_cb_t in,
|
|
||||||
rpc_exec_read_cb_t out, rpc_exec_read_cb_t err,
|
|
||||||
rpc_exec_done_cb_t end, void *priv, struct ubus_context *ctx,
|
|
||||||
struct ubus_request_data *req)
|
|
||||||
{
|
|
||||||
pid_t pid;
|
|
||||||
|
|
||||||
int ipipe[2];
|
|
||||||
int opipe[2];
|
|
||||||
int epipe[2];
|
|
||||||
|
|
||||||
const char *cmd;
|
|
||||||
struct rpc_exec_context *c;
|
|
||||||
|
|
||||||
cmd = rpc_exec_lookup(args[0]);
|
|
||||||
|
|
||||||
if (!cmd)
|
|
||||||
return UBUS_STATUS_NOT_FOUND;
|
|
||||||
|
|
||||||
c = malloc(sizeof(*c));
|
|
||||||
|
|
||||||
if (!c)
|
|
||||||
return UBUS_STATUS_UNKNOWN_ERROR;
|
|
||||||
|
|
||||||
if (pipe(ipipe))
|
|
||||||
goto fail_ipipe;
|
|
||||||
|
|
||||||
if (pipe(opipe))
|
|
||||||
goto fail_opipe;
|
|
||||||
|
|
||||||
if (pipe(epipe))
|
|
||||||
goto fail_epipe;
|
|
||||||
|
|
||||||
switch ((pid = fork()))
|
|
||||||
{
|
|
||||||
case -1:
|
|
||||||
goto fail_fork;
|
|
||||||
|
|
||||||
case 0:
|
|
||||||
uloop_done();
|
|
||||||
|
|
||||||
dup2(ipipe[0], 0);
|
|
||||||
dup2(opipe[1], 1);
|
|
||||||
dup2(epipe[1], 2);
|
|
||||||
|
|
||||||
close(ipipe[0]);
|
|
||||||
close(ipipe[1]);
|
|
||||||
close(opipe[0]);
|
|
||||||
close(opipe[1]);
|
|
||||||
close(epipe[0]);
|
|
||||||
close(epipe[1]);
|
|
||||||
|
|
||||||
if (execv(cmd, (char * const *)args))
|
|
||||||
return rpc_errno_status();
|
|
||||||
|
|
||||||
default:
|
|
||||||
memset(c, 0, sizeof(*c));
|
|
||||||
blob_buf_init(&c->blob, 0);
|
|
||||||
|
|
||||||
c->stdin_cb = in;
|
|
||||||
c->stdout_cb = out;
|
|
||||||
c->stderr_cb = err;
|
|
||||||
c->finish_cb = end;
|
|
||||||
c->priv = priv;
|
|
||||||
|
|
||||||
ustream_declare_read(c->opipe, opipe[0], opipe);
|
|
||||||
ustream_declare_read(c->epipe, epipe[0], epipe);
|
|
||||||
|
|
||||||
c->process.pid = pid;
|
|
||||||
c->process.cb = rpc_exec_process_cb;
|
|
||||||
uloop_process_add(&c->process);
|
|
||||||
|
|
||||||
c->timeout.cb = rpc_exec_timeout_cb;
|
|
||||||
uloop_timeout_set(&c->timeout, rpc_exec_timeout);
|
|
||||||
|
|
||||||
if (c->stdin_cb)
|
|
||||||
{
|
|
||||||
ustream_declare_write(c->ipipe, ipipe[1], ipipe);
|
|
||||||
rpc_exec_ipipe_write_cb(&c->ipipe.stream, 0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
close(ipipe[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
close(ipipe[0]);
|
|
||||||
close(opipe[1]);
|
|
||||||
close(epipe[1]);
|
|
||||||
|
|
||||||
c->context = ctx;
|
|
||||||
ubus_defer_request(ctx, req, &c->request);
|
|
||||||
}
|
|
||||||
|
|
||||||
return UBUS_STATUS_OK;
|
|
||||||
|
|
||||||
fail_fork:
|
|
||||||
close(epipe[0]);
|
|
||||||
close(epipe[1]);
|
|
||||||
|
|
||||||
fail_epipe:
|
|
||||||
close(opipe[0]);
|
|
||||||
close(opipe[1]);
|
|
||||||
|
|
||||||
fail_opipe:
|
|
||||||
close(ipipe[0]);
|
|
||||||
close(ipipe[1]);
|
|
||||||
|
|
||||||
fail_ipipe:
|
|
||||||
free(c);
|
|
||||||
return rpc_errno_status();
|
|
||||||
}
|
|
|
@ -1,86 +0,0 @@
|
||||||
/*
|
|
||||||
* rpcd - UBUS RPC server
|
|
||||||
*
|
|
||||||
* Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
|
|
||||||
* Copyright (C) 2013-2014 Jo-Philipp Wich <jow@openwrt.org>
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
|
||||||
* copyright notice and this permission notice appear in all copies.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __RPC_SESSION_H
|
|
||||||
#define __RPC_SESSION_H
|
|
||||||
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <dirent.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#include <libubox/avl.h>
|
|
||||||
#include <libubox/blobmsg_json.h>
|
|
||||||
|
|
||||||
#define RPC_SID_LEN 32
|
|
||||||
#define RPC_DEFAULT_SESSION_TIMEOUT 300
|
|
||||||
#define RPC_DEFAULT_SESSION_ID "00000000000000000000000000000000"
|
|
||||||
#define RPC_SESSION_DIRECTORY "/var/run/rpcd/sessions"
|
|
||||||
#define RPC_SESSION_ACL_DIR INSTALL_PREFIX "/share/rpcd/acl.d"
|
|
||||||
|
|
||||||
extern char apply_sid[RPC_SID_LEN + 1];
|
|
||||||
|
|
||||||
struct rpc_session {
|
|
||||||
struct avl_node avl;
|
|
||||||
char id[RPC_SID_LEN + 1];
|
|
||||||
|
|
||||||
struct uloop_timeout t;
|
|
||||||
struct avl_tree data;
|
|
||||||
struct avl_tree acls;
|
|
||||||
|
|
||||||
int timeout;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct rpc_session_data {
|
|
||||||
struct avl_node avl;
|
|
||||||
struct blob_attr attr[];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct rpc_session_acl_scope {
|
|
||||||
struct avl_node avl;
|
|
||||||
struct avl_tree acls;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct rpc_session_acl {
|
|
||||||
struct avl_node avl;
|
|
||||||
const char *object;
|
|
||||||
const char *function;
|
|
||||||
int sort_len;
|
|
||||||
};
|
|
||||||
|
|
||||||
int rpc_session_api_init(struct ubus_context *ctx);
|
|
||||||
|
|
||||||
bool rpc_session_access(const char *sid, const char *scope,
|
|
||||||
const char *object, const char *function);
|
|
||||||
|
|
||||||
struct rpc_session_cb {
|
|
||||||
struct list_head list;
|
|
||||||
void (*cb)(struct rpc_session *, void *);
|
|
||||||
void *priv;
|
|
||||||
};
|
|
||||||
|
|
||||||
void rpc_session_create_cb(struct rpc_session_cb *cb);
|
|
||||||
void rpc_session_destroy_cb(struct rpc_session_cb *cb);
|
|
||||||
|
|
||||||
void rpc_session_freeze(void);
|
|
||||||
void rpc_session_thaw(void);
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,44 +0,0 @@
|
||||||
/*
|
|
||||||
* rpcd - UBUS RPC server
|
|
||||||
*
|
|
||||||
* Copyright (C) 2013-2014 Jo-Philipp Wich <jow@openwrt.org>
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
|
||||||
* copyright notice and this permission notice appear in all copies.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __RPC_UCI_H
|
|
||||||
#define __RPC_UCI_H
|
|
||||||
|
|
||||||
#include <glob.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <dirent.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <libubus.h>
|
|
||||||
#include <uci.h>
|
|
||||||
|
|
||||||
#define RPC_UCI_DIR_PREFIX "/var/run/rpcd"
|
|
||||||
#define RPC_UCI_SAVEDIR_PREFIX RPC_UCI_DIR_PREFIX "/uci-"
|
|
||||||
#define RPC_SNAPSHOT_FILES RPC_UCI_DIR_PREFIX "/snapshot-files/"
|
|
||||||
#define RPC_SNAPSHOT_DELTA RPC_UCI_DIR_PREFIX "/snapshot-delta/"
|
|
||||||
#define RPC_UCI_DIR "/etc/config/"
|
|
||||||
#define RPC_APPLY_TIMEOUT 60
|
|
||||||
|
|
||||||
int rpc_uci_api_init(struct ubus_context *ctx);
|
|
||||||
|
|
||||||
void rpc_uci_purge_savedirs(void);
|
|
||||||
|
|
||||||
#endif
|
|
18
main.c
18
main.c
|
@ -25,12 +25,7 @@
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include <rpcd/exec.h>
|
|
||||||
#include <rpcd/plugin.h>
|
#include <rpcd/plugin.h>
|
||||||
#include <rpcd/rc.h>
|
|
||||||
#include <rpcd/session.h>
|
|
||||||
#include <rpcd/uci.h>
|
|
||||||
|
|
||||||
static struct ubus_context *ctx;
|
static struct ubus_context *ctx;
|
||||||
static bool respawn = false;
|
static bool respawn = false;
|
||||||
|
|
||||||
|
@ -39,7 +34,6 @@ int rpc_exec_timeout = RPC_EXEC_DEFAULT_TIMEOUT;
|
||||||
static void
|
static void
|
||||||
handle_signal(int sig)
|
handle_signal(int sig)
|
||||||
{
|
{
|
||||||
rpc_session_freeze();
|
|
||||||
uloop_cancelled = true;
|
uloop_cancelled = true;
|
||||||
respawn = (sig == SIGHUP);
|
respawn = (sig == SIGHUP);
|
||||||
}
|
}
|
||||||
|
@ -93,9 +87,6 @@ int main(int argc, char **argv)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stat(RPC_UCI_DIR_PREFIX, &s))
|
|
||||||
mkdir(RPC_UCI_DIR_PREFIX, 0700);
|
|
||||||
|
|
||||||
umask(0077);
|
umask(0077);
|
||||||
|
|
||||||
signal(SIGPIPE, SIG_IGN);
|
signal(SIGPIPE, SIG_IGN);
|
||||||
|
@ -112,18 +103,9 @@ int main(int argc, char **argv)
|
||||||
|
|
||||||
ubus_add_uloop(ctx);
|
ubus_add_uloop(ctx);
|
||||||
|
|
||||||
rpc_session_api_init(ctx);
|
|
||||||
rpc_uci_api_init(ctx);
|
|
||||||
rpc_rc_api_init(ctx);
|
|
||||||
rpc_plugin_api_init(ctx);
|
rpc_plugin_api_init(ctx);
|
||||||
|
|
||||||
hangup = getenv("RPC_HANGUP");
|
hangup = getenv("RPC_HANGUP");
|
||||||
|
|
||||||
if (!hangup || strcmp(hangup, "1"))
|
|
||||||
rpc_uci_purge_savedirs();
|
|
||||||
else
|
|
||||||
rpc_session_thaw();
|
|
||||||
|
|
||||||
uloop_run();
|
uloop_run();
|
||||||
ubus_free(ctx);
|
ubus_free(ctx);
|
||||||
uloop_done();
|
uloop_done();
|
||||||
|
|
409
rc.c
409
rc.c
|
@ -1,409 +0,0 @@
|
||||||
// SPDX-License-Identifier: ISC OR MIT
|
|
||||||
/*
|
|
||||||
* rpcd - UBUS RPC server
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 Rafał Miłecki <rafal@milecki.pl>
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <dirent.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <linux/limits.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
|
|
||||||
#include <libubox/blobmsg.h>
|
|
||||||
#include <libubox/ulog.h>
|
|
||||||
#include <libubox/uloop.h>
|
|
||||||
#include <libubus.h>
|
|
||||||
|
|
||||||
#include <rpcd/rc.h>
|
|
||||||
|
|
||||||
#define RC_LIST_EXEC_TIMEOUT_MS 3000
|
|
||||||
|
|
||||||
enum {
|
|
||||||
RC_LIST_NAME,
|
|
||||||
RC_LIST_SKIP_RUNNING_CHECK,
|
|
||||||
__RC_LIST_MAX
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct blobmsg_policy rc_list_policy[] = {
|
|
||||||
[RC_LIST_NAME] = { "name", BLOBMSG_TYPE_STRING },
|
|
||||||
[RC_LIST_SKIP_RUNNING_CHECK] = { "skip_running_check", BLOBMSG_TYPE_BOOL },
|
|
||||||
};
|
|
||||||
|
|
||||||
enum {
|
|
||||||
RC_INIT_NAME,
|
|
||||||
RC_INIT_ACTION,
|
|
||||||
__RC_INIT_MAX
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct blobmsg_policy rc_init_policy[] = {
|
|
||||||
[RC_INIT_NAME] = { "name", BLOBMSG_TYPE_STRING },
|
|
||||||
[RC_INIT_ACTION] = { "action", BLOBMSG_TYPE_STRING },
|
|
||||||
};
|
|
||||||
|
|
||||||
struct rc_list_context {
|
|
||||||
struct uloop_process process;
|
|
||||||
struct uloop_timeout timeout;
|
|
||||||
struct ubus_context *ctx;
|
|
||||||
struct ubus_request_data req;
|
|
||||||
struct blob_buf *buf;
|
|
||||||
DIR *dir;
|
|
||||||
bool skip_running_check;
|
|
||||||
const char *req_name;
|
|
||||||
|
|
||||||
/* Info about currently processed init.d entry */
|
|
||||||
struct {
|
|
||||||
char path[PATH_MAX];
|
|
||||||
const char *d_name;
|
|
||||||
int start;
|
|
||||||
int stop;
|
|
||||||
bool enabled;
|
|
||||||
bool running;
|
|
||||||
bool use_procd;
|
|
||||||
} entry;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void rc_list_readdir(struct rc_list_context *c);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* rc_check_script - check if script is safe to execute as root
|
|
||||||
*
|
|
||||||
* Check if it's owned by root and if only root can modify it.
|
|
||||||
*/
|
|
||||||
static int rc_check_script(const char *path)
|
|
||||||
{
|
|
||||||
struct stat s;
|
|
||||||
|
|
||||||
if (stat(path, &s))
|
|
||||||
return UBUS_STATUS_NOT_FOUND;
|
|
||||||
|
|
||||||
if (s.st_uid != 0 || s.st_gid != 0 || !(s.st_mode & S_IXUSR) || (s.st_mode & S_IWOTH))
|
|
||||||
return UBUS_STATUS_PERMISSION_DENIED;
|
|
||||||
|
|
||||||
return UBUS_STATUS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void rc_list_add_table(struct rc_list_context *c)
|
|
||||||
{
|
|
||||||
void *e;
|
|
||||||
|
|
||||||
e = blobmsg_open_table(c->buf, c->entry.d_name);
|
|
||||||
|
|
||||||
if (c->entry.start >= 0)
|
|
||||||
blobmsg_add_u16(c->buf, "start", c->entry.start);
|
|
||||||
if (c->entry.stop >= 0)
|
|
||||||
blobmsg_add_u16(c->buf, "stop", c->entry.stop);
|
|
||||||
blobmsg_add_u8(c->buf, "enabled", c->entry.enabled);
|
|
||||||
if (!c->skip_running_check && c->entry.use_procd)
|
|
||||||
blobmsg_add_u8(c->buf, "running", c->entry.running);
|
|
||||||
|
|
||||||
blobmsg_close_table(c->buf, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void rpc_list_exec_timeout_cb(struct uloop_timeout *t)
|
|
||||||
{
|
|
||||||
struct rc_list_context *c = container_of(t, struct rc_list_context, timeout);
|
|
||||||
|
|
||||||
ULOG_WARN("Timeout waiting for %s\n", c->entry.path);
|
|
||||||
|
|
||||||
uloop_process_delete(&c->process);
|
|
||||||
kill(c->process.pid, SIGKILL);
|
|
||||||
|
|
||||||
rc_list_readdir(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* rc_exec - execute a file and call callback on complete
|
|
||||||
*/
|
|
||||||
static int rc_list_exec(struct rc_list_context *c, const char *action, uloop_process_handler cb)
|
|
||||||
{
|
|
||||||
pid_t pid;
|
|
||||||
int err;
|
|
||||||
int fd;
|
|
||||||
|
|
||||||
pid = fork();
|
|
||||||
switch (pid) {
|
|
||||||
case -1:
|
|
||||||
return -errno;
|
|
||||||
case 0:
|
|
||||||
if (c->skip_running_check)
|
|
||||||
exit(-EFAULT);
|
|
||||||
|
|
||||||
if (!c->entry.use_procd)
|
|
||||||
exit(-EOPNOTSUPP);
|
|
||||||
|
|
||||||
/* 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);
|
|
||||||
if (fd > 2)
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
uloop_end();
|
|
||||||
|
|
||||||
execl(c->entry.path, c->entry.path, action, NULL);
|
|
||||||
exit(errno);
|
|
||||||
default:
|
|
||||||
c->process.pid = pid;
|
|
||||||
c->process.cb = cb;
|
|
||||||
|
|
||||||
err = uloop_process_add(&c->process);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
c->timeout.cb = rpc_list_exec_timeout_cb;
|
|
||||||
err = uloop_timeout_set(&c->timeout, RC_LIST_EXEC_TIMEOUT_MS);
|
|
||||||
if (err) {
|
|
||||||
uloop_process_delete(&c->process);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void rc_list_exec_running_cb(struct uloop_process *p, int stat)
|
|
||||||
{
|
|
||||||
struct rc_list_context *c = container_of(p, struct rc_list_context, process);
|
|
||||||
|
|
||||||
uloop_timeout_cancel(&c->timeout);
|
|
||||||
|
|
||||||
c->entry.running = !stat;
|
|
||||||
rc_list_add_table(c);
|
|
||||||
|
|
||||||
rc_list_readdir(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void rc_list_readdir(struct rc_list_context *c)
|
|
||||||
{
|
|
||||||
struct dirent *e;
|
|
||||||
FILE *fp;
|
|
||||||
|
|
||||||
e = readdir(c->dir);
|
|
||||||
/*
|
|
||||||
* If scanning for a specific script and entry.d_name is set
|
|
||||||
* we can assume we found a matching one in the previous
|
|
||||||
* iteration since entry.d_name is set only if a match is found.
|
|
||||||
*/
|
|
||||||
if (!e || (c->req_name && c->entry.d_name)) {
|
|
||||||
closedir(c->dir);
|
|
||||||
ubus_send_reply(c->ctx, &c->req, c->buf->head);
|
|
||||||
ubus_complete_deferred_request(c->ctx, &c->req, UBUS_STATUS_OK);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!strcmp(e->d_name, ".") || !strcmp(e->d_name, ".."))
|
|
||||||
goto next;
|
|
||||||
|
|
||||||
if (c->req_name && strcmp(e->d_name, c->req_name))
|
|
||||||
goto next;
|
|
||||||
|
|
||||||
memset(&c->entry, 0, sizeof(c->entry));
|
|
||||||
c->entry.start = -1;
|
|
||||||
c->entry.stop = -1;
|
|
||||||
|
|
||||||
snprintf(c->entry.path, sizeof(c->entry.path), "/etc/init.d/%s", e->d_name);
|
|
||||||
if (rc_check_script(c->entry.path))
|
|
||||||
goto next;
|
|
||||||
|
|
||||||
c->entry.d_name = e->d_name;
|
|
||||||
|
|
||||||
fp = fopen(c->entry.path, "r");
|
|
||||||
if (fp) {
|
|
||||||
struct stat s;
|
|
||||||
char path[PATH_MAX];
|
|
||||||
char line[255];
|
|
||||||
bool beginning;
|
|
||||||
int count = 0;
|
|
||||||
|
|
||||||
beginning = true;
|
|
||||||
while ((c->entry.start < 0 || c->entry.stop < 0 ||
|
|
||||||
(!c->skip_running_check && !c->entry.use_procd)) &&
|
|
||||||
count <= 10 && fgets(line, sizeof(line), fp)) {
|
|
||||||
if (beginning) {
|
|
||||||
if (!strncmp(line, "START=", 6)) {
|
|
||||||
c->entry.start = strtoul(line + 6, NULL, 0);
|
|
||||||
} else if (!strncmp(line, "STOP=", 5)) {
|
|
||||||
c->entry.stop = strtoul(line + 5, NULL, 0);
|
|
||||||
} else if (!c->skip_running_check && !strncmp(line, "USE_PROCD=", 10)) {
|
|
||||||
c->entry.use_procd = !!strtoul(line + 10, NULL, 0);
|
|
||||||
}
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
beginning = !!strchr(line, '\n');
|
|
||||||
}
|
|
||||||
fclose(fp);
|
|
||||||
|
|
||||||
if (c->entry.start >= 0) {
|
|
||||||
snprintf(path, sizeof(path), "/etc/rc.d/S%02d%s", c->entry.start, c->entry.d_name);
|
|
||||||
if (!stat(path, &s) && (s.st_mode & S_IXUSR))
|
|
||||||
c->entry.enabled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rc_list_exec(c, "running", rc_list_exec_running_cb))
|
|
||||||
goto next;
|
|
||||||
|
|
||||||
return;
|
|
||||||
next:
|
|
||||||
rc_list_readdir(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* rc_list - allocate listing context and start reading directory
|
|
||||||
*/
|
|
||||||
static int rc_list(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_LIST_MAX];
|
|
||||||
static struct blob_buf buf;
|
|
||||||
struct rc_list_context *c;
|
|
||||||
|
|
||||||
blobmsg_parse(rc_list_policy, __RC_LIST_MAX, tb, blobmsg_data(msg), blobmsg_data_len(msg));
|
|
||||||
|
|
||||||
blob_buf_init(&buf, 0);
|
|
||||||
|
|
||||||
c = calloc(1, sizeof(*c));
|
|
||||||
if (!c)
|
|
||||||
return UBUS_STATUS_UNKNOWN_ERROR;
|
|
||||||
|
|
||||||
c->ctx = ctx;
|
|
||||||
c->buf = &buf;
|
|
||||||
c->dir = opendir("/etc/init.d");
|
|
||||||
if (!c->dir) {
|
|
||||||
free(c);
|
|
||||||
return UBUS_STATUS_UNKNOWN_ERROR;
|
|
||||||
}
|
|
||||||
if (tb[RC_LIST_SKIP_RUNNING_CHECK])
|
|
||||||
c->skip_running_check = blobmsg_get_bool(tb[RC_LIST_SKIP_RUNNING_CHECK]);
|
|
||||||
if (tb[RC_LIST_NAME])
|
|
||||||
c->req_name = blobmsg_get_string(tb[RC_LIST_NAME]);
|
|
||||||
|
|
||||||
ubus_defer_request(ctx, req, &c->req);
|
|
||||||
|
|
||||||
rc_list_readdir(c);
|
|
||||||
|
|
||||||
return 0; /* Deferred */
|
|
||||||
}
|
|
||||||
|
|
||||||
struct rc_init_context {
|
|
||||||
struct uloop_process process;
|
|
||||||
struct ubus_context *ctx;
|
|
||||||
struct ubus_request_data req;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void rc_init_cb(struct uloop_process *p, int stat)
|
|
||||||
{
|
|
||||||
struct rc_init_context *c = container_of(p, struct rc_init_context, process);
|
|
||||||
|
|
||||||
ubus_complete_deferred_request(c->ctx, &c->req, UBUS_STATUS_OK);
|
|
||||||
|
|
||||||
free(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int rc_init(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_INIT_MAX];
|
|
||||||
struct rc_init_context *c;
|
|
||||||
char path[PATH_MAX];
|
|
||||||
const char *action;
|
|
||||||
const char *name;
|
|
||||||
const char *chr;
|
|
||||||
pid_t pid;
|
|
||||||
int err;
|
|
||||||
int fd;
|
|
||||||
|
|
||||||
blobmsg_parse(rc_init_policy, __RC_INIT_MAX, tb, blobmsg_data(msg), blobmsg_data_len(msg));
|
|
||||||
|
|
||||||
if (!tb[RC_INIT_NAME] || !tb[RC_INIT_ACTION])
|
|
||||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
|
||||||
|
|
||||||
name = blobmsg_get_string(tb[RC_INIT_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;
|
|
||||||
|
|
||||||
snprintf(path, sizeof(path), "/etc/init.d/%s", name);
|
|
||||||
|
|
||||||
/* Validate script privileges */
|
|
||||||
err = rc_check_script(path);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
action = blobmsg_get_string(tb[RC_INIT_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);
|
|
||||||
if (fd > 2)
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
uloop_end();
|
|
||||||
|
|
||||||
execl(path, path, action, NULL);
|
|
||||||
exit(errno);
|
|
||||||
default:
|
|
||||||
c->ctx = ctx;
|
|
||||||
c->process.pid = pid;
|
|
||||||
c->process.cb = rc_init_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("list", rc_list, rc_list_policy),
|
|
||||||
UBUS_METHOD("init", rc_init, rc_init_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);
|
|
||||||
}
|
|
366
sys.c
366
sys.c
|
@ -1,366 +0,0 @@
|
||||||
/*
|
|
||||||
* rpcd - UBUS RPC server
|
|
||||||
*
|
|
||||||
* Copyright (C) 2013-2014 Jo-Philipp Wich <jow@openwrt.org>
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
|
||||||
* copyright notice and this permission notice appear in all copies.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <libubus.h>
|
|
||||||
|
|
||||||
#include <rpcd/exec.h>
|
|
||||||
#include <rpcd/plugin.h>
|
|
||||||
#include <rpcd/session.h>
|
|
||||||
#include <sys/reboot.h>
|
|
||||||
|
|
||||||
static const struct rpc_daemon_ops *ops;
|
|
||||||
|
|
||||||
enum {
|
|
||||||
RPC_P_USER,
|
|
||||||
RPC_P_PASSWORD,
|
|
||||||
__RPC_P_MAX
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct blobmsg_policy rpc_password_policy[__RPC_P_MAX] = {
|
|
||||||
[RPC_P_USER] = { .name = "user", .type = BLOBMSG_TYPE_STRING },
|
|
||||||
[RPC_P_PASSWORD] = { .name = "password", .type = BLOBMSG_TYPE_STRING },
|
|
||||||
};
|
|
||||||
|
|
||||||
enum {
|
|
||||||
RPC_UPGRADE_KEEP,
|
|
||||||
__RPC_UPGRADE_MAX
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct blobmsg_policy rpc_upgrade_policy[__RPC_UPGRADE_MAX] = {
|
|
||||||
[RPC_UPGRADE_KEEP] = { .name = "keep", .type = BLOBMSG_TYPE_BOOL },
|
|
||||||
};
|
|
||||||
|
|
||||||
enum {
|
|
||||||
RPC_PACKAGELIST_ALL,
|
|
||||||
__RPC_PACKAGELIST_MAX
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct blobmsg_policy rpc_packagelist_policy[__RPC_PACKAGELIST_MAX] = {
|
|
||||||
[RPC_PACKAGELIST_ALL] = { .name = "all", .type = BLOBMSG_TYPE_BOOL },
|
|
||||||
};
|
|
||||||
|
|
||||||
static int
|
|
||||||
rpc_errno_status(void)
|
|
||||||
{
|
|
||||||
switch (errno)
|
|
||||||
{
|
|
||||||
case EACCES:
|
|
||||||
return UBUS_STATUS_PERMISSION_DENIED;
|
|
||||||
|
|
||||||
case ENOTDIR:
|
|
||||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
|
||||||
|
|
||||||
case ENOENT:
|
|
||||||
return UBUS_STATUS_NOT_FOUND;
|
|
||||||
|
|
||||||
case EINVAL:
|
|
||||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return UBUS_STATUS_UNKNOWN_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
rpc_cgi_password_set(struct ubus_context *ctx, struct ubus_object *obj,
|
|
||||||
struct ubus_request_data *req, const char *method,
|
|
||||||
struct blob_attr *msg)
|
|
||||||
{
|
|
||||||
pid_t pid;
|
|
||||||
int fd, fds[2];
|
|
||||||
struct stat s;
|
|
||||||
struct blob_attr *tb[__RPC_P_MAX];
|
|
||||||
ssize_t n;
|
|
||||||
int ret;
|
|
||||||
const char *const passwd = "/bin/passwd";
|
|
||||||
const struct timespec ts = {0, 100 * 1000 * 1000};
|
|
||||||
|
|
||||||
blobmsg_parse(rpc_password_policy, __RPC_P_MAX, tb,
|
|
||||||
blob_data(msg), blob_len(msg));
|
|
||||||
|
|
||||||
if (!tb[RPC_P_USER] || !tb[RPC_P_PASSWORD])
|
|
||||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
|
||||||
|
|
||||||
if (stat(passwd, &s))
|
|
||||||
return UBUS_STATUS_NOT_FOUND;
|
|
||||||
|
|
||||||
if (!(s.st_mode & S_IXUSR))
|
|
||||||
return UBUS_STATUS_PERMISSION_DENIED;
|
|
||||||
|
|
||||||
if (pipe(fds))
|
|
||||||
return rpc_errno_status();
|
|
||||||
|
|
||||||
switch ((pid = fork()))
|
|
||||||
{
|
|
||||||
case -1:
|
|
||||||
close(fds[0]);
|
|
||||||
close(fds[1]);
|
|
||||||
return rpc_errno_status();
|
|
||||||
|
|
||||||
case 0:
|
|
||||||
uloop_done();
|
|
||||||
|
|
||||||
dup2(fds[0], 0);
|
|
||||||
close(fds[0]);
|
|
||||||
close(fds[1]);
|
|
||||||
|
|
||||||
if ((fd = open("/dev/null", O_RDWR)) > -1)
|
|
||||||
{
|
|
||||||
dup2(fd, 1);
|
|
||||||
dup2(fd, 2);
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = chdir("/");
|
|
||||||
if (ret < 0)
|
|
||||||
return rpc_errno_status();
|
|
||||||
|
|
||||||
if (execl(passwd, passwd,
|
|
||||||
blobmsg_data(tb[RPC_P_USER]), NULL))
|
|
||||||
return rpc_errno_status();
|
|
||||||
|
|
||||||
default:
|
|
||||||
close(fds[0]);
|
|
||||||
|
|
||||||
n = write(fds[1], blobmsg_data(tb[RPC_P_PASSWORD]),
|
|
||||||
blobmsg_data_len(tb[RPC_P_PASSWORD]) - 1);
|
|
||||||
if (n < 0)
|
|
||||||
return rpc_errno_status();
|
|
||||||
|
|
||||||
n = write(fds[1], "\n", 1);
|
|
||||||
if (n < 0)
|
|
||||||
return rpc_errno_status();
|
|
||||||
|
|
||||||
nanosleep(&ts, NULL);
|
|
||||||
|
|
||||||
n = write(fds[1], blobmsg_data(tb[RPC_P_PASSWORD]),
|
|
||||||
blobmsg_data_len(tb[RPC_P_PASSWORD]) - 1);
|
|
||||||
if (n < 0)
|
|
||||||
return rpc_errno_status();
|
|
||||||
n = write(fds[1], "\n", 1);
|
|
||||||
if (n < 0)
|
|
||||||
return rpc_errno_status();
|
|
||||||
|
|
||||||
close(fds[1]);
|
|
||||||
|
|
||||||
waitpid(pid, NULL, 0);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
is_field(const char *type, const char *line)
|
|
||||||
{
|
|
||||||
return strncmp(line, type, strlen(type)) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
is_blank(const char *line)
|
|
||||||
{
|
|
||||||
for (; *line; line++)
|
|
||||||
if (!isspace(*line))
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
rpc_sys_packagelist(struct ubus_context *ctx, struct ubus_object *obj,
|
|
||||||
struct ubus_request_data *req, const char *method,
|
|
||||||
struct blob_attr *msg)
|
|
||||||
{
|
|
||||||
struct blob_attr *tb[__RPC_PACKAGELIST_MAX];
|
|
||||||
bool all = false, installed = false, auto_installed = false;
|
|
||||||
struct blob_buf buf = { 0 };
|
|
||||||
char line[256], tmp[128], pkg[128], ver[128];
|
|
||||||
char *pkg_abi;
|
|
||||||
void *tbl;
|
|
||||||
|
|
||||||
blobmsg_parse(rpc_packagelist_policy, __RPC_PACKAGELIST_MAX, tb,
|
|
||||||
blob_data(msg), blob_len(msg));
|
|
||||||
|
|
||||||
if (tb[RPC_PACKAGELIST_ALL] && blobmsg_get_bool(tb[RPC_PACKAGELIST_ALL]))
|
|
||||||
all = true;
|
|
||||||
|
|
||||||
FILE *f = fopen("/usr/lib/opkg/status", "r");
|
|
||||||
if (!f)
|
|
||||||
return UBUS_STATUS_NOT_FOUND;
|
|
||||||
|
|
||||||
blob_buf_init(&buf, 0);
|
|
||||||
tbl = blobmsg_open_table(&buf, "packages");
|
|
||||||
pkg[0] = ver[0] = '\0';
|
|
||||||
|
|
||||||
while (fgets(line, sizeof(line), f)) {
|
|
||||||
switch (line[0]) {
|
|
||||||
case 'A':
|
|
||||||
if (is_field("ABIVersion", line)) {
|
|
||||||
/* if there is ABIVersion, remove that suffix */
|
|
||||||
if (sscanf(line, "ABIVersion: %127s", tmp) == 1
|
|
||||||
&& strlen(tmp) < strlen(pkg)) {
|
|
||||||
pkg_abi = pkg + (strlen(pkg) - strlen(tmp));
|
|
||||||
if (strncmp(pkg_abi, tmp, strlen(tmp)) == 0)
|
|
||||||
pkg_abi[0] = '\0';
|
|
||||||
}
|
|
||||||
} else if (is_field("Auto-Installed", line))
|
|
||||||
if (sscanf(line, "Auto-Installed: %63s", tmp) == 1)
|
|
||||||
auto_installed = (strcmp(tmp, "yes") == 0);
|
|
||||||
break;
|
|
||||||
case 'P':
|
|
||||||
if (is_field("Package", line))
|
|
||||||
if (sscanf(line, "Package: %127s", pkg) != 1)
|
|
||||||
pkg[0] = '\0';
|
|
||||||
break;
|
|
||||||
case 'V':
|
|
||||||
if (is_field("Version", line))
|
|
||||||
if (sscanf(line, "Version: %127s", ver) != 1)
|
|
||||||
ver[0] = '\0';
|
|
||||||
break;
|
|
||||||
case 'S':
|
|
||||||
if (is_field("Status", line))
|
|
||||||
if (sscanf(line, "Status: install %63s installed", tmp) == 1)
|
|
||||||
installed = true;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (is_blank(line)) {
|
|
||||||
if (installed && (all || !auto_installed) && pkg[0] && ver[0])
|
|
||||||
blobmsg_add_string(&buf, pkg, ver);
|
|
||||||
pkg[0] = ver[0] = '\0';
|
|
||||||
installed = auto_installed = false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
blobmsg_close_table(&buf, tbl);
|
|
||||||
ubus_send_reply(ctx, req, buf.head);
|
|
||||||
blob_buf_free(&buf);
|
|
||||||
fclose(f);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
rpc_sys_upgrade_test(struct ubus_context *ctx, struct ubus_object *obj,
|
|
||||||
struct ubus_request_data *req, const char *method,
|
|
||||||
struct blob_attr *msg)
|
|
||||||
{
|
|
||||||
const char *cmd[4] = { "sysupgrade", "--test", "/tmp/firmware.bin", NULL };
|
|
||||||
return ops->exec(cmd, NULL, NULL, NULL, NULL, NULL, ctx, req);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
rpc_sys_upgrade_start(struct ubus_context *ctx, struct ubus_object *obj,
|
|
||||||
struct ubus_request_data *req, const char *method,
|
|
||||||
struct blob_attr *msg)
|
|
||||||
{
|
|
||||||
struct blob_attr *tb[__RPC_UPGRADE_MAX];
|
|
||||||
char * const cmd[4] = { "/sbin/sysupgrade", "-n", "/tmp/firmware.bin", NULL };
|
|
||||||
char * const cmd_keep[3] = { "/sbin/sysupgrade", "/tmp/firmware.bin", NULL };
|
|
||||||
char * const * c = cmd;
|
|
||||||
|
|
||||||
blobmsg_parse(rpc_upgrade_policy, __RPC_UPGRADE_MAX, tb,
|
|
||||||
blob_data(msg), blob_len(msg));
|
|
||||||
|
|
||||||
if (tb[RPC_UPGRADE_KEEP] && blobmsg_get_bool(tb[RPC_UPGRADE_KEEP]))
|
|
||||||
c = cmd_keep;
|
|
||||||
|
|
||||||
if (!fork()) {
|
|
||||||
/* wait for the RPC call to complete */
|
|
||||||
sleep(2);
|
|
||||||
return execv(c[0], c);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
rpc_sys_upgrade_clean(struct ubus_context *ctx, struct ubus_object *obj,
|
|
||||||
struct ubus_request_data *req, const char *method,
|
|
||||||
struct blob_attr *msg)
|
|
||||||
{
|
|
||||||
if (unlink("/tmp/firmware.bin"))
|
|
||||||
return rpc_errno_status();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
rpc_sys_factory(struct ubus_context *ctx, struct ubus_object *obj,
|
|
||||||
struct ubus_request_data *req, const char *method,
|
|
||||||
struct blob_attr *msg)
|
|
||||||
{
|
|
||||||
char * const cmd[4] = { "/sbin/jffs2reset", "-y", "-r", NULL };
|
|
||||||
|
|
||||||
if (!fork()) {
|
|
||||||
/* wait for the RPC call to complete */
|
|
||||||
sleep(2);
|
|
||||||
return execv(cmd[0], cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
rpc_sys_reboot(struct ubus_context *ctx, struct ubus_object *obj,
|
|
||||||
struct ubus_request_data *req, const char *method,
|
|
||||||
struct blob_attr *msg)
|
|
||||||
{
|
|
||||||
if (!fork()) {
|
|
||||||
sync();
|
|
||||||
sleep(2);
|
|
||||||
reboot(RB_AUTOBOOT);
|
|
||||||
while (1)
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
rpc_sys_api_init(const struct rpc_daemon_ops *o, struct ubus_context *ctx)
|
|
||||||
{
|
|
||||||
static const struct ubus_method sys_methods[] = {
|
|
||||||
UBUS_METHOD("packagelist", rpc_sys_packagelist, rpc_packagelist_policy),
|
|
||||||
UBUS_METHOD("password_set", rpc_cgi_password_set, rpc_password_policy),
|
|
||||||
UBUS_METHOD_NOARG("upgrade_test", rpc_sys_upgrade_test),
|
|
||||||
UBUS_METHOD("upgrade_start", rpc_sys_upgrade_start,
|
|
||||||
rpc_upgrade_policy),
|
|
||||||
UBUS_METHOD_NOARG("upgrade_clean", rpc_sys_upgrade_clean),
|
|
||||||
UBUS_METHOD_NOARG("factory", rpc_sys_factory),
|
|
||||||
UBUS_METHOD_NOARG("reboot", rpc_sys_reboot),
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct ubus_object_type sys_type =
|
|
||||||
UBUS_OBJECT_TYPE("rpcd-plugin-sys", sys_methods);
|
|
||||||
|
|
||||||
static struct ubus_object obj = {
|
|
||||||
.name = "rpc-sys",
|
|
||||||
.type = &sys_type,
|
|
||||||
.methods = sys_methods,
|
|
||||||
.n_methods = ARRAY_SIZE(sys_methods),
|
|
||||||
};
|
|
||||||
|
|
||||||
ops = o;
|
|
||||||
|
|
||||||
return ubus_add_object(ctx, &obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct rpc_plugin rpc_plugin = {
|
|
||||||
.init = rpc_sys_api_init
|
|
||||||
};
|
|
Loading…
Reference in a new issue