Bump procd-2016-07-29-2c9f5d4af1559b840c42f1443ede9f9fe809c58b
This commit is contained in:
123
src/3P/procd/CMakeLists.txt
Normal file
123
src/3P/procd/CMakeLists.txt
Normal file
@@ -0,0 +1,123 @@
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
|
||||
PROJECT(procd C)
|
||||
INCLUDE(GNUInstallDirs)
|
||||
ADD_DEFINITIONS(-Os -ggdb -Wall -Werror --std=gnu99 -Wmissing-declarations)
|
||||
|
||||
SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
|
||||
|
||||
IF(APPLE)
|
||||
INCLUDE_DIRECTORIES(/opt/local/include)
|
||||
LINK_DIRECTORIES(/opt/local/lib)
|
||||
ENDIF()
|
||||
|
||||
|
||||
ADD_LIBRARY(setlbf SHARED service/setlbf.c)
|
||||
INSTALL(TARGETS setlbf
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
)
|
||||
|
||||
|
||||
SET(SOURCES procd.c signal.c state.c inittab.c rcS.c ubus.c system.c
|
||||
service/service.c service/instance.c service/validate.c service/trigger.c service/watch.c
|
||||
utils/utils.c)
|
||||
IF(NOT DISABLE_INIT)
|
||||
SET(SOURCES ${SOURCES} watchdog.c plug/coldplug.c plug/hotplug.c)
|
||||
ENDIF()
|
||||
|
||||
SET(LIBS ubox ubus json-c blobmsg_json json_script)
|
||||
|
||||
IF(DEBUG)
|
||||
ADD_DEFINITIONS(-DDEBUG -g3)
|
||||
ENDIF()
|
||||
|
||||
IF(EARLY_PATH)
|
||||
ADD_DEFINITIONS(-DEARLY_PATH="${EARLY_PATH}")
|
||||
ENDIF()
|
||||
|
||||
IF(ZRAM_TMPFS)
|
||||
ADD_DEFINITIONS(-DZRAM_TMPFS)
|
||||
SET(SOURCES_ZRAM initd/zram.c)
|
||||
ENDIF()
|
||||
|
||||
IF(BUILD_UPGRADED)
|
||||
add_subdirectory(upgraded)
|
||||
ENDIF()
|
||||
|
||||
ADD_EXECUTABLE(procd ${SOURCES})
|
||||
TARGET_LINK_LIBRARIES(procd ${LIBS})
|
||||
INSTALL(TARGETS procd
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR}
|
||||
)
|
||||
|
||||
FIND_PATH(ubox_include_dir libubox/uloop.h)
|
||||
INCLUDE_DIRECTORIES(${ubox_include_dir})
|
||||
|
||||
IF(DISABLE_INIT)
|
||||
ADD_DEFINITIONS(-DDISABLE_INIT)
|
||||
ELSE()
|
||||
ADD_EXECUTABLE(init initd/init.c initd/early.c initd/preinit.c initd/mkdev.c watchdog.c
|
||||
utils/utils.c ${SOURCES_ZRAM})
|
||||
TARGET_LINK_LIBRARIES(init ${LIBS})
|
||||
INSTALL(TARGETS init
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR}
|
||||
)
|
||||
|
||||
ADD_EXECUTABLE(udevtrigger plug/udevtrigger.c)
|
||||
INSTALL(TARGETS udevtrigger
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR}
|
||||
)
|
||||
ENDIF()
|
||||
|
||||
|
||||
ADD_EXECUTABLE(askfirst utils/askfirst.c)
|
||||
INSTALL(TARGETS askfirst
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR}
|
||||
)
|
||||
|
||||
ADD_CUSTOM_COMMAND(
|
||||
OUTPUT syscall-names.h
|
||||
COMMAND ./make_syscall_h.sh ${CMAKE_C_COMPILER} > ./syscall-names.h
|
||||
DEPENDS ./make_syscall_h.sh
|
||||
)
|
||||
ADD_CUSTOM_TARGET(syscall-names-h DEPENDS syscall-names.h)
|
||||
|
||||
ADD_CUSTOM_COMMAND(
|
||||
OUTPUT capabilities-names.h
|
||||
COMMAND ./make_capabilities_h.sh ${CMAKE_C_COMPILER} > ./capabilities-names.h
|
||||
DEPENDS ./make_capabilities_h.sh
|
||||
)
|
||||
ADD_CUSTOM_TARGET(capabilities-names-h DEPENDS capabilities-names.h)
|
||||
|
||||
IF(SECCOMP_SUPPORT)
|
||||
ADD_LIBRARY(preload-seccomp SHARED jail/preload.c jail/seccomp.c)
|
||||
TARGET_LINK_LIBRARIES(preload-seccomp dl ubox blobmsg_json)
|
||||
INSTALL(TARGETS preload-seccomp
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
)
|
||||
ADD_DEPENDENCIES(preload-seccomp syscall-names-h)
|
||||
endif()
|
||||
|
||||
IF(JAIL_SUPPORT)
|
||||
ADD_EXECUTABLE(ujail jail/jail.c jail/elf.c jail/fs.c jail/capabilities.c)
|
||||
TARGET_LINK_LIBRARIES(ujail ubox blobmsg_json)
|
||||
INSTALL(TARGETS ujail
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR}
|
||||
)
|
||||
ADD_DEPENDENCIES(ujail capabilities-names-h)
|
||||
endif()
|
||||
|
||||
IF(UTRACE_SUPPORT)
|
||||
ADD_EXECUTABLE(utrace trace/trace.c)
|
||||
TARGET_LINK_LIBRARIES(utrace ubox ${json} blobmsg_json)
|
||||
INSTALL(TARGETS utrace
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR}
|
||||
)
|
||||
ADD_DEPENDENCIES(utrace syscall-names-h)
|
||||
|
||||
ADD_LIBRARY(preload-trace SHARED trace/preload.c)
|
||||
TARGET_LINK_LIBRARIES(preload-trace dl)
|
||||
INSTALL(TARGETS preload-trace
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
)
|
||||
endif()
|
||||
78
src/3P/procd/builders/cmake/CMakeLists.txt
Normal file
78
src/3P/procd/builders/cmake/CMakeLists.txt
Normal file
@@ -0,0 +1,78 @@
|
||||
cmake_minimum_required (VERSION 3.0)
|
||||
|
||||
project (procd)
|
||||
|
||||
ADD_DEFINITIONS(-Os -ggdb -Wall -Werror --std=gnu99 -Wmissing-declarations)
|
||||
|
||||
set (CMAKE_MODULE_PATH "${MODULE_PATH}")
|
||||
|
||||
set(DISABLE_TARGET_OPTIMIZATION ON)
|
||||
|
||||
include (aw)
|
||||
|
||||
file (
|
||||
GLOB_RECURSE
|
||||
init_source_files
|
||||
|
||||
$ENV{AWOXCVS}/AwoxAudio/Products/External/procd/initd/init.c
|
||||
$ENV{AWOXCVS}/AwoxAudio/Products/External/procd/initd/early.c
|
||||
$ENV{AWOXCVS}/AwoxAudio/Products/External/procd/initd/preinit.c
|
||||
$ENV{AWOXCVS}/AwoxAudio/Products/External/procd/initd/mkdev.c
|
||||
$ENV{AWOXCVS}/AwoxAudio/Products/External/procd/watchdog.c
|
||||
$ENV{AWOXCVS}/AwoxAudio/Products/External/procd/utils/utils.c
|
||||
)
|
||||
|
||||
# Init
|
||||
IF(NOT DISABLE_INIT)
|
||||
add_executable (init ${init_source_files})
|
||||
target_link_libraries (init ubox ubus)
|
||||
|
||||
install (TARGETS init RUNTIME DESTINATION ../sbin)
|
||||
ENDIF()
|
||||
|
||||
# procd
|
||||
file (
|
||||
GLOB_RECURSE
|
||||
procd_source_files
|
||||
|
||||
$ENV{AWOXCVS}/AwoxAudio/Products/External/procd/procd.c
|
||||
$ENV{AWOXCVS}/AwoxAudio/Products/External/procd/signal.c
|
||||
$ENV{AWOXCVS}/AwoxAudio/Products/External/procd/state.c
|
||||
$ENV{AWOXCVS}/AwoxAudio/Products/External/procd/inittab.c
|
||||
$ENV{AWOXCVS}/AwoxAudio/Products/External/procd/rcS.c
|
||||
$ENV{AWOXCVS}/AwoxAudio/Products/External/procd/ubus.c
|
||||
$ENV{AWOXCVS}/AwoxAudio/Products/External/procd/system.c
|
||||
$ENV{AWOXCVS}/AwoxAudio/Products/External/procd/service/service.c
|
||||
$ENV{AWOXCVS}/AwoxAudio/Products/External/procd/service/instance.c
|
||||
$ENV{AWOXCVS}/AwoxAudio/Products/External/procd/service/validate.c
|
||||
$ENV{AWOXCVS}/AwoxAudio/Products/External/procd/service/trigger.c
|
||||
$ENV{AWOXCVS}/AwoxAudio/Products/External/procd/service/watch.c
|
||||
$ENV{AWOXCVS}/AwoxAudio/Products/External/procd/utils/utils.c
|
||||
)
|
||||
|
||||
IF(DISABLE_INIT)
|
||||
ADD_DEFINITIONS(-DDISABLE_INIT)
|
||||
ENDIF()
|
||||
|
||||
IF(NOT DISABLE_INIT)
|
||||
list (APPEND procd_source_files
|
||||
|
||||
$ENV{AWOXCVS}/AwoxAudio/Products/External/procd/watchdog.c
|
||||
$ENV{AWOXCVS}/AwoxAudio/Products/External/procd/plug/coldplug.c
|
||||
$ENV{AWOXCVS}/AwoxAudio/Products/External/procd/plug/hotplug.c
|
||||
)
|
||||
ENDIF()
|
||||
|
||||
IF(DEBUG)
|
||||
ADD_DEFINITIONS(-DDEBUG -g3)
|
||||
ENDIF()
|
||||
|
||||
add_executable (procd ${procd_source_files})
|
||||
target_link_libraries (procd ubox ubus json-c blobmsg_json json_script)
|
||||
|
||||
install (TARGETS procd RUNTIME DESTINATION ../sbin)
|
||||
|
||||
# udevtrigger
|
||||
add_executable (udevtrigger $ENV{AWOXCVS}/AwoxAudio/Products/External/procd/plug/udevtrigger.c)
|
||||
install (TARGETS udevtrigger
|
||||
RUNTIME DESTINATION ../sbin)
|
||||
98
src/3P/procd/initd/early.c
Normal file
98
src/3P/procd/initd/early.c
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
|
||||
* Copyright (C) 2013 John Crispin <blogic@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <sys/mount.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "../utils/utils.h"
|
||||
#include "init.h"
|
||||
#include "../libc-compat.h"
|
||||
|
||||
static void
|
||||
early_dev(void)
|
||||
{
|
||||
mkdev("*", 0600);
|
||||
mknod("/dev/null", 0666, makedev(1, 3));
|
||||
}
|
||||
|
||||
static void
|
||||
early_console(const char *dev)
|
||||
{
|
||||
struct stat s;
|
||||
|
||||
if (stat(dev, &s)) {
|
||||
ERROR("Failed to stat %s\n", dev);
|
||||
return;
|
||||
}
|
||||
|
||||
if (patch_stdio(dev)) {
|
||||
ERROR("Failed to setup i/o redirection\n");
|
||||
return;
|
||||
}
|
||||
|
||||
fcntl(STDERR_FILENO, F_SETFL, fcntl(STDERR_FILENO, F_GETFL) | O_NONBLOCK);
|
||||
}
|
||||
|
||||
static void
|
||||
early_mounts(void)
|
||||
{
|
||||
unsigned int oldumask = umask(0);
|
||||
|
||||
mount("proc", "/proc", "proc", MS_NOATIME | MS_NODEV | MS_NOEXEC | MS_NOSUID, 0);
|
||||
mount("sysfs", "/sys", "sysfs", MS_NOATIME | MS_NODEV | MS_NOEXEC | MS_NOSUID, 0);
|
||||
mount("cgroup", "/sys/fs/cgroup", "cgroup", MS_NODEV | MS_NOEXEC | MS_NOSUID, 0);
|
||||
mount("tmpfs", "/dev", "tmpfs", MS_NOATIME | MS_NOSUID, "mode=0755,size=512K");
|
||||
ignore(symlink("/tmp/shm", "/dev/shm"));
|
||||
mkdir("/dev/pts", 0755);
|
||||
mount("devpts", "/dev/pts", "devpts", MS_NOATIME | MS_NOEXEC | MS_NOSUID, "mode=600");
|
||||
early_dev();
|
||||
|
||||
early_console("/dev/console");
|
||||
if (mount_zram_on_tmp()) {
|
||||
mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_NODEV | MS_NOATIME, 0);
|
||||
mkdir("/tmp/shm", 01777);
|
||||
} else {
|
||||
mkdir("/tmp/shm", 01777);
|
||||
mount("tmpfs", "/tmp/shm", "tmpfs", MS_NOSUID | MS_NODEV | MS_NOATIME,
|
||||
"mode=01777");
|
||||
}
|
||||
mkdir("/tmp/run", 0755);
|
||||
mkdir("/tmp/lock", 0755);
|
||||
mkdir("/tmp/state", 0755);
|
||||
umask(oldumask);
|
||||
}
|
||||
|
||||
static void
|
||||
early_env(void)
|
||||
{
|
||||
setenv("PATH", EARLY_PATH, 1);
|
||||
}
|
||||
|
||||
void
|
||||
early(void)
|
||||
{
|
||||
if (getpid() != 1)
|
||||
return;
|
||||
|
||||
early_mounts();
|
||||
early_env();
|
||||
|
||||
LOG("Console is alive\n");
|
||||
}
|
||||
113
src/3P/procd/initd/init.c
Normal file
113
src/3P/procd/initd/init.c
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
|
||||
* Copyright (C) 2013 John Crispin <blogic@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <sys/wait.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/reboot.h>
|
||||
|
||||
#include <libubox/uloop.h>
|
||||
#include <ubus/libubus.h>
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <libgen.h>
|
||||
#include <regex.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "../utils/utils.h"
|
||||
#include "init.h"
|
||||
#include "../watchdog.h"
|
||||
|
||||
unsigned int debug = 0;
|
||||
|
||||
static void
|
||||
signal_shutdown(int signal, siginfo_t *siginfo, void *data)
|
||||
{
|
||||
fprintf(stderr, "reboot\n");
|
||||
fflush(stderr);
|
||||
sync();
|
||||
sleep(2);
|
||||
reboot(RB_AUTOBOOT);
|
||||
while (1)
|
||||
;
|
||||
}
|
||||
|
||||
static struct sigaction sa_shutdown = {
|
||||
.sa_sigaction = signal_shutdown,
|
||||
.sa_flags = SA_SIGINFO
|
||||
};
|
||||
|
||||
static void
|
||||
cmdline(void)
|
||||
{
|
||||
char line[20];
|
||||
char* res;
|
||||
long r;
|
||||
|
||||
res = get_cmdline_val("init_debug", line, sizeof(line));
|
||||
if (res != NULL) {
|
||||
r = strtol(line, NULL, 10);
|
||||
if ((r != LONG_MIN) && (r != LONG_MAX))
|
||||
debug = (int) r;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
pid_t pid;
|
||||
|
||||
ulog_open(ULOG_KMSG, LOG_DAEMON, "init");
|
||||
|
||||
sigaction(SIGTERM, &sa_shutdown, NULL);
|
||||
sigaction(SIGUSR1, &sa_shutdown, NULL);
|
||||
sigaction(SIGUSR2, &sa_shutdown, NULL);
|
||||
|
||||
early();
|
||||
cmdline();
|
||||
watchdog_init(1);
|
||||
|
||||
pid = fork();
|
||||
if (!pid) {
|
||||
char *kmod[] = { "/sbin/kmodloader", "/etc/modules-boot.d/", NULL };
|
||||
|
||||
if (debug < 3)
|
||||
patch_stdio("/dev/null");
|
||||
|
||||
execvp(kmod[0], kmod);
|
||||
ERROR("Failed to start kmodloader\n");
|
||||
exit(-1);
|
||||
}
|
||||
if (pid <= 0) {
|
||||
ERROR("Failed to start kmodloader instance\n");
|
||||
} else {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 1200; i++) {
|
||||
if (waitpid(pid, NULL, WNOHANG) > 0)
|
||||
break;
|
||||
usleep(10 * 1000);
|
||||
watchdog_ping();
|
||||
}
|
||||
}
|
||||
uloop_init();
|
||||
preinit();
|
||||
uloop_run();
|
||||
|
||||
return 0;
|
||||
}
|
||||
36
src/3P/procd/initd/init.h
Normal file
36
src/3P/procd/initd/init.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2013 John Crispin <blogic@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef _INIT_H__
|
||||
#define _INIT_H__
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include "../log.h"
|
||||
|
||||
#ifndef EARLY_PATH
|
||||
#define EARLY_PATH "/usr/sbin:/sbin:/usr/bin:/bin"
|
||||
#endif
|
||||
|
||||
void preinit(void);
|
||||
void early(void);
|
||||
int mkdev(const char *progname, int progmode);
|
||||
|
||||
#ifdef ZRAM_TMPFS
|
||||
int mount_zram_on_tmp(void);
|
||||
#else
|
||||
static inline int mount_zram_on_tmp(void) {
|
||||
return -ENOSYS;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
125
src/3P/procd/initd/mkdev.c
Normal file
125
src/3P/procd/initd/mkdev.c
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
|
||||
* Copyright (C) 2013 John Crispin <blogic@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#define _DEFAULT_SOURCE
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdbool.h>
|
||||
#include <dirent.h>
|
||||
#include <limits.h>
|
||||
#include <fnmatch.h>
|
||||
|
||||
#include "init.h"
|
||||
|
||||
static char **patterns;
|
||||
static int n_patterns;
|
||||
static char buf[PATH_MAX];
|
||||
static char buf2[PATH_MAX];
|
||||
static unsigned int mode = 0600;
|
||||
|
||||
static bool find_pattern(const char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < n_patterns; i++)
|
||||
if (!fnmatch(patterns[i], name, 0))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void make_dev(const char *path, bool block, int major, int minor)
|
||||
{
|
||||
unsigned int oldumask = umask(0);
|
||||
unsigned int _mode = mode | (block ? S_IFBLK : S_IFCHR);
|
||||
|
||||
DEBUG(4, "Creating %s device %s(%d,%d)\n",
|
||||
block ? "block" : "character",
|
||||
path, major, minor);
|
||||
|
||||
mknod(path, _mode, makedev(major, minor));
|
||||
umask(oldumask);
|
||||
}
|
||||
|
||||
static void find_devs(bool block)
|
||||
{
|
||||
char *path = block ? "/sys/dev/block" : "/sys/dev/char";
|
||||
struct dirent *dp;
|
||||
DIR *dir;
|
||||
|
||||
dir = opendir(path);
|
||||
if (!dir)
|
||||
return;
|
||||
|
||||
path = buf2 + sprintf(buf2, "%s/", path);
|
||||
while ((dp = readdir(dir)) != NULL) {
|
||||
char *c;
|
||||
int major = 0, minor = 0;
|
||||
int len;
|
||||
|
||||
if (dp->d_type != DT_LNK)
|
||||
continue;
|
||||
|
||||
if (sscanf(dp->d_name, "%d:%d", &major, &minor) != 2)
|
||||
continue;
|
||||
|
||||
strcpy(path, dp->d_name);
|
||||
len = readlink(buf2, buf, sizeof(buf));
|
||||
if (len <= 0)
|
||||
continue;
|
||||
|
||||
buf[len] = 0;
|
||||
if (!find_pattern(buf))
|
||||
continue;
|
||||
|
||||
c = strrchr(buf, '/');
|
||||
if (!c)
|
||||
continue;
|
||||
|
||||
c++;
|
||||
make_dev(c, block, major, minor);
|
||||
}
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
static char *add_pattern(const char *name)
|
||||
{
|
||||
char *str = malloc(strlen(name) + 2);
|
||||
|
||||
str[0] = '*';
|
||||
strcpy(str + 1, name);
|
||||
return str;
|
||||
}
|
||||
|
||||
int mkdev(const char *name, int _mode)
|
||||
{
|
||||
char *pattern;
|
||||
|
||||
if (chdir("/dev"))
|
||||
return 1;
|
||||
|
||||
pattern = add_pattern(name);
|
||||
patterns = &pattern;
|
||||
mode = _mode;
|
||||
n_patterns = 1;
|
||||
find_devs(true);
|
||||
find_devs(false);
|
||||
return chdir("/");
|
||||
}
|
||||
132
src/3P/procd/initd/preinit.c
Normal file
132
src/3P/procd/initd/preinit.c
Normal file
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
|
||||
* Copyright (C) 2013 John Crispin <blogic@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/mount.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <libubox/uloop.h>
|
||||
#include <libubox/utils.h>
|
||||
#include <ubus/libubus.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "init.h"
|
||||
#include "../watchdog.h"
|
||||
|
||||
static struct uloop_process preinit_proc;
|
||||
static struct uloop_process plugd_proc;
|
||||
|
||||
static void
|
||||
check_dbglvl(void)
|
||||
{
|
||||
FILE *fp = fopen("/tmp/debug_level", "r");
|
||||
int lvl = 0;
|
||||
|
||||
if (!fp)
|
||||
return;
|
||||
if (fscanf(fp, "%d", &lvl) == EOF)
|
||||
ERROR("failed to read debug level\n");
|
||||
fclose(fp);
|
||||
unlink("/tmp/debug_level");
|
||||
|
||||
if (lvl > 0 && lvl < 5)
|
||||
debug = lvl;
|
||||
}
|
||||
|
||||
static void
|
||||
spawn_procd(struct uloop_process *proc, int ret)
|
||||
{
|
||||
char *wdt_fd = watchdog_fd();
|
||||
char *argv[] = { "/sbin/procd", NULL};
|
||||
struct stat s;
|
||||
char dbg[2];
|
||||
|
||||
if (plugd_proc.pid > 0)
|
||||
kill(plugd_proc.pid, SIGKILL);
|
||||
|
||||
if (!stat("/tmp/sysupgrade", &s))
|
||||
while (true)
|
||||
sleep(1);
|
||||
|
||||
unsetenv("INITRAMFS");
|
||||
unsetenv("PREINIT");
|
||||
unlink("/tmp/.preinit");
|
||||
DEBUG(2, "Exec to real procd now\n");
|
||||
if (wdt_fd)
|
||||
setenv("WDTFD", wdt_fd, 1);
|
||||
check_dbglvl();
|
||||
if (debug > 0) {
|
||||
snprintf(dbg, 2, "%d", debug);
|
||||
setenv("DBGLVL", dbg, 1);
|
||||
}
|
||||
|
||||
execvp(argv[0], argv);
|
||||
}
|
||||
|
||||
static void
|
||||
plugd_proc_cb(struct uloop_process *proc, int ret)
|
||||
{
|
||||
proc->pid = 0;
|
||||
}
|
||||
|
||||
void
|
||||
preinit(void)
|
||||
{
|
||||
char *init[] = { "/bin/sh", "/etc/preinit", NULL };
|
||||
char *plug[] = { "/sbin/procd", "-h", "/etc/hotplug-preinit.json", NULL };
|
||||
int fd;
|
||||
|
||||
LOG("- preinit -\n");
|
||||
|
||||
plugd_proc.cb = plugd_proc_cb;
|
||||
plugd_proc.pid = fork();
|
||||
if (!plugd_proc.pid) {
|
||||
execvp(plug[0], plug);
|
||||
ERROR("Failed to start plugd\n");
|
||||
exit(-1);
|
||||
}
|
||||
if (plugd_proc.pid <= 0) {
|
||||
ERROR("Failed to start new plugd instance\n");
|
||||
return;
|
||||
}
|
||||
uloop_process_add(&plugd_proc);
|
||||
|
||||
setenv("PREINIT", "1", 1);
|
||||
|
||||
fd = creat("/tmp/.preinit", 0600);
|
||||
|
||||
if (fd < 0)
|
||||
ERROR("Failed to create sentinel file\n");
|
||||
else
|
||||
close(fd);
|
||||
|
||||
preinit_proc.cb = spawn_procd;
|
||||
preinit_proc.pid = fork();
|
||||
if (!preinit_proc.pid) {
|
||||
execvp(init[0], init);
|
||||
ERROR("Failed to start preinit\n");
|
||||
exit(-1);
|
||||
}
|
||||
if (preinit_proc.pid <= 0) {
|
||||
ERROR("Failed to start new preinit instance\n");
|
||||
return;
|
||||
}
|
||||
uloop_process_add(&preinit_proc);
|
||||
|
||||
DEBUG(4, "Launched preinit instance, pid=%d\n", (int) preinit_proc.pid);
|
||||
}
|
||||
128
src/3P/procd/initd/zram.c
Normal file
128
src/3P/procd/initd/zram.c
Normal file
@@ -0,0 +1,128 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <sys/utsname.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "../log.h"
|
||||
|
||||
#include "init.h"
|
||||
|
||||
#define KB(x) (x * 1024)
|
||||
|
||||
#define ZRAM_MOD_PATH "/lib/modules/%s/zram.ko"
|
||||
#define EXT4_MOD_PATH "/lib/modules/%s/ext4.ko"
|
||||
|
||||
static long
|
||||
proc_meminfo(void)
|
||||
{
|
||||
FILE *fp;
|
||||
char line[256];
|
||||
char *key;
|
||||
long val = KB(16);
|
||||
|
||||
fp = fopen("/proc/meminfo", "r");
|
||||
if (fp == NULL) {
|
||||
ERROR("Can't open /proc/meminfo: %s\n", strerror(errno));
|
||||
return errno;
|
||||
}
|
||||
|
||||
while (fgets(line, sizeof(line), fp)) {
|
||||
key = strtok(line, ":");
|
||||
if (strcasecmp(key, "MemTotal"))
|
||||
continue;
|
||||
val = atol(strtok(NULL, " kB\n"));
|
||||
break;
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
if (val > KB(32))
|
||||
val = KB(32);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static int
|
||||
early_insmod(char *module)
|
||||
{
|
||||
pid_t pid = fork();
|
||||
|
||||
if (!pid) {
|
||||
char *modprobe[] = { "/usr/sbin/modprobe", NULL, NULL };
|
||||
char *path;
|
||||
struct utsname ver;
|
||||
|
||||
uname(&ver);
|
||||
path = alloca(sizeof(module) + strlen(ver.release) + 1);
|
||||
sprintf(path, module, ver.release);
|
||||
modprobe[1] = path;
|
||||
execvp(modprobe[0], modprobe);
|
||||
ERROR("Can't exec /usr/sbin/modprobe\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (pid <= 0) {
|
||||
ERROR("Can't exec /usr/sbin/modprobe\n");
|
||||
return -1;
|
||||
} else {
|
||||
waitpid(pid, NULL, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
mount_zram_on_tmp(void)
|
||||
{
|
||||
char *mkfs[] = { "/usr/sbin/mkfs.ext4", "-b", "4096", "-F", "-L", "TEMP", "-m", "0", "/dev/zram0", NULL };
|
||||
FILE *fp;
|
||||
long zramsize;
|
||||
pid_t pid;
|
||||
int ret;
|
||||
|
||||
if (early_insmod(ZRAM_MOD_PATH) || early_insmod(EXT4_MOD_PATH)) {
|
||||
ERROR("failed to insmod zram support\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
mkdev("*", 0600);
|
||||
|
||||
zramsize = proc_meminfo() / 2;
|
||||
fp = fopen("/sys/block/zram0/disksize", "r+");
|
||||
if (fp == NULL) {
|
||||
ERROR("Can't open /sys/block/zram0/disksize: %s\n", strerror(errno));
|
||||
return errno;
|
||||
}
|
||||
fprintf(fp, "%ld", KB(zramsize));
|
||||
fclose(fp);
|
||||
|
||||
pid = fork();
|
||||
if (!pid) {
|
||||
execvp(mkfs[0], mkfs);
|
||||
ERROR("Can't exec /sbin/mkfs.ext4\n");
|
||||
exit(-1);
|
||||
} else if (pid <= 0) {
|
||||
ERROR("Can't exec /sbin/mkfs.ext4\n");
|
||||
return -1;
|
||||
} else {
|
||||
waitpid(pid, NULL, 0);
|
||||
}
|
||||
|
||||
ret = mount("/dev/zram0", "/tmp", "ext4", MS_NOSUID | MS_NODEV | MS_NOATIME, "errors=continue,noquota");
|
||||
if (ret < 0) {
|
||||
ERROR("Can't mount /dev/zram0 on /tmp: %s\n", strerror(errno));
|
||||
return errno;
|
||||
}
|
||||
|
||||
LOG("Using up to %ld kB of RAM as ZRAM storage on /mnt\n", zramsize);
|
||||
|
||||
return 0;
|
||||
}
|
||||
321
src/3P/procd/inittab.c
Normal file
321
src/3P/procd/inittab.c
Normal file
@@ -0,0 +1,321 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
|
||||
* Copyright (C) 2013 John Crispin <blogic@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
//AWOX #define _GNU_SOURCE
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <regex.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <libubox/utils.h>
|
||||
#include <libubox/list.h>
|
||||
|
||||
#include "utils/utils.h"
|
||||
#include "procd.h"
|
||||
#include "rcS.h"
|
||||
|
||||
#define TAG_ID 0
|
||||
#define TAG_RUNLVL 1
|
||||
#define TAG_ACTION 2
|
||||
#define TAG_PROCESS 3
|
||||
|
||||
#define MAX_ARGS 8
|
||||
|
||||
struct init_action;
|
||||
char *console = NULL;
|
||||
|
||||
struct init_handler {
|
||||
const char *name;
|
||||
void (*cb) (struct init_action *a);
|
||||
int multi;
|
||||
};
|
||||
|
||||
struct init_action {
|
||||
struct list_head list;
|
||||
|
||||
char *id;
|
||||
char *argv[MAX_ARGS];
|
||||
char *line;
|
||||
|
||||
struct init_handler *handler;
|
||||
struct uloop_process proc;
|
||||
|
||||
int respawn;
|
||||
struct uloop_timeout tout;
|
||||
};
|
||||
|
||||
static const char *tab = "/etc/inittab";
|
||||
static char *ask = "/sbin/askfirst";
|
||||
|
||||
static LIST_HEAD(actions);
|
||||
|
||||
static int dev_exist(const char *dev)
|
||||
{
|
||||
int dfd, fd;
|
||||
|
||||
dfd = open("/dev", O_PATH|O_DIRECTORY);
|
||||
|
||||
if (dfd < 0)
|
||||
return 0;
|
||||
|
||||
fd = openat(dfd, dev, O_RDONLY);
|
||||
close(dfd);
|
||||
|
||||
if (fd < 0)
|
||||
return 0;
|
||||
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void fork_worker(struct init_action *a)
|
||||
{
|
||||
pid_t p;
|
||||
|
||||
a->proc.pid = fork();
|
||||
if (!a->proc.pid) {
|
||||
p = setsid();
|
||||
|
||||
if (patch_stdio(a->id))
|
||||
ERROR("Failed to setup i/o redirection\n");
|
||||
|
||||
ioctl(STDIN_FILENO, TIOCSCTTY, 1);
|
||||
tcsetpgrp(STDIN_FILENO, p);
|
||||
|
||||
execvp(a->argv[0], a->argv);
|
||||
ERROR("Failed to execute %s\n", a->argv[0]);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (a->proc.pid > 0) {
|
||||
DEBUG(4, "Launched new %s action, pid=%d\n",
|
||||
a->handler->name,
|
||||
(int) a->proc.pid);
|
||||
uloop_process_add(&a->proc);
|
||||
}
|
||||
}
|
||||
|
||||
static void child_exit(struct uloop_process *proc, int ret)
|
||||
{
|
||||
struct init_action *a = container_of(proc, struct init_action, proc);
|
||||
|
||||
DEBUG(4, "pid:%d\n", proc->pid);
|
||||
uloop_timeout_set(&a->tout, a->respawn);
|
||||
}
|
||||
|
||||
static void respawn(struct uloop_timeout *tout)
|
||||
{
|
||||
struct init_action *a = container_of(tout, struct init_action, tout);
|
||||
fork_worker(a);
|
||||
}
|
||||
|
||||
static void rcdone(struct runqueue *q)
|
||||
{
|
||||
procd_state_next();
|
||||
}
|
||||
|
||||
static void runrc(struct init_action *a)
|
||||
{
|
||||
if (!a->argv[1] || !a->argv[2]) {
|
||||
ERROR("valid format is rcS <S|K> <param>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* proceed even if no init or shutdown scripts run */
|
||||
if (rcS(a->argv[1], a->argv[2], rcdone))
|
||||
rcdone(NULL);
|
||||
}
|
||||
|
||||
static void askfirst(struct init_action *a)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!dev_exist(a->id) || (console && !strcmp(console, a->id))) {
|
||||
DEBUG(4, "Skipping %s\n", a->id);
|
||||
return;
|
||||
}
|
||||
|
||||
a->tout.cb = respawn;
|
||||
for (i = MAX_ARGS - 1; i >= 1; i--)
|
||||
a->argv[i] = a->argv[i - 1];
|
||||
a->argv[0] = ask;
|
||||
a->respawn = 500;
|
||||
|
||||
a->proc.cb = child_exit;
|
||||
fork_worker(a);
|
||||
}
|
||||
|
||||
static void askconsole(struct init_action *a)
|
||||
{
|
||||
char line[256], *tty, *split;
|
||||
int i;
|
||||
|
||||
tty = get_cmdline_val("console", line, sizeof(line));
|
||||
if (tty != NULL) {
|
||||
split = strchr(tty, ',');
|
||||
if (split != NULL)
|
||||
*split = '\0';
|
||||
|
||||
if (!dev_exist(tty)) {
|
||||
DEBUG(4, "skipping %s\n", tty);
|
||||
return;
|
||||
}
|
||||
|
||||
console = strdup(tty);
|
||||
a->id = strdup(tty);
|
||||
}
|
||||
else {
|
||||
console = NULL;
|
||||
a->id = NULL;
|
||||
}
|
||||
|
||||
a->tout.cb = respawn;
|
||||
for (i = MAX_ARGS - 1; i >= 1; i--)
|
||||
a->argv[i] = a->argv[i - 1];
|
||||
a->argv[0] = ask;
|
||||
a->respawn = 500;
|
||||
|
||||
a->proc.cb = child_exit;
|
||||
fork_worker(a);
|
||||
}
|
||||
|
||||
static void rcrespawn(struct init_action *a)
|
||||
{
|
||||
a->tout.cb = respawn;
|
||||
a->respawn = 500;
|
||||
|
||||
a->proc.cb = child_exit;
|
||||
fork_worker(a);
|
||||
}
|
||||
|
||||
static struct init_handler handlers[] = {
|
||||
{
|
||||
.name = "sysinit",
|
||||
.cb = runrc,
|
||||
}, {
|
||||
.name = "shutdown",
|
||||
.cb = runrc,
|
||||
}, {
|
||||
.name = "askfirst",
|
||||
.cb = askfirst,
|
||||
.multi = 1,
|
||||
}, {
|
||||
.name = "askconsole",
|
||||
.cb = askconsole,
|
||||
.multi = 1,
|
||||
}, {
|
||||
.name = "respawn",
|
||||
.cb = rcrespawn,
|
||||
.multi = 1,
|
||||
}
|
||||
};
|
||||
|
||||
static int add_action(struct init_action *a, const char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(handlers); i++)
|
||||
if (!strcmp(handlers[i].name, name)) {
|
||||
a->handler = &handlers[i];
|
||||
list_add_tail(&a->list, &actions);
|
||||
return 0;
|
||||
}
|
||||
ERROR("Unknown init handler %s\n", name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void procd_inittab_run(const char *handler)
|
||||
{
|
||||
struct init_action *a;
|
||||
|
||||
list_for_each_entry(a, &actions, list)
|
||||
if (!strcmp(a->handler->name, handler)) {
|
||||
if (a->handler->multi) {
|
||||
a->handler->cb(a);
|
||||
continue;
|
||||
}
|
||||
a->handler->cb(a);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void procd_inittab(void)
|
||||
{
|
||||
#define LINE_LEN 128
|
||||
FILE *fp = fopen(tab, "r");
|
||||
struct init_action *a;
|
||||
regex_t pat_inittab;
|
||||
regmatch_t matches[5];
|
||||
char *line;
|
||||
|
||||
if (!fp) {
|
||||
ERROR("Failed to open %s\n", tab);
|
||||
return;
|
||||
}
|
||||
|
||||
regcomp(&pat_inittab, "([a-zA-Z0-9]*):([a-zA-Z0-9]*):([a-zA-Z0-9]*):(.*)", REG_EXTENDED);
|
||||
line = malloc(LINE_LEN);
|
||||
a = malloc(sizeof(struct init_action));
|
||||
memset(a, 0, sizeof(struct init_action));
|
||||
|
||||
while (fgets(line, LINE_LEN, fp)) {
|
||||
char *tags[TAG_PROCESS + 1];
|
||||
char *tok;
|
||||
int i;
|
||||
int len = strlen(line);
|
||||
|
||||
while (isspace(line[len - 1]))
|
||||
len--;
|
||||
line[len] = 0;
|
||||
|
||||
if (*line == '#')
|
||||
continue;
|
||||
|
||||
if (regexec(&pat_inittab, line, 5, matches, 0))
|
||||
continue;
|
||||
|
||||
DEBUG(4, "Parsing inittab - %s", line);
|
||||
|
||||
for (i = TAG_ID; i <= TAG_PROCESS; i++) {
|
||||
line[matches[i].rm_eo] = '\0';
|
||||
tags[i] = &line[matches[i + 1].rm_so];
|
||||
};
|
||||
|
||||
tok = strtok(tags[TAG_PROCESS], " ");
|
||||
for (i = 0; i < (MAX_ARGS - 1) && tok; i++) {
|
||||
a->argv[i] = tok;
|
||||
tok = strtok(NULL, " ");
|
||||
}
|
||||
a->argv[i] = NULL;
|
||||
a->id = tags[TAG_ID];
|
||||
a->line = line;
|
||||
|
||||
if (add_action(a, tags[TAG_ACTION]))
|
||||
continue;
|
||||
line = malloc(LINE_LEN);
|
||||
a = malloc(sizeof(struct init_action));
|
||||
memset(a, 0, sizeof(struct init_action));
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
free(line);
|
||||
free(a);
|
||||
regfree(&pat_inittab);
|
||||
}
|
||||
116
src/3P/procd/jail/capabilities.c
Normal file
116
src/3P/procd/jail/capabilities.c
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Etienne CHAMPETIER <champetier.etienne@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE 1
|
||||
#include <syslog.h>
|
||||
#include <sys/prctl.h>
|
||||
|
||||
#include <libubox/blobmsg.h>
|
||||
#include <libubox/blobmsg_json.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "../capabilities-names.h"
|
||||
#include "capabilities.h"
|
||||
|
||||
static int find_capabilities(const char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i <= CAP_LAST_CAP; i++)
|
||||
if (capabilities_names[i] && !strcmp(capabilities_names[i], name))
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int drop_capabilities(const char *file)
|
||||
{
|
||||
enum {
|
||||
CAP_KEEP,
|
||||
CAP_DROP,
|
||||
__CAP_MAX
|
||||
};
|
||||
static const struct blobmsg_policy policy[__CAP_MAX] = {
|
||||
[CAP_KEEP] = { .name = "cap.keep", .type = BLOBMSG_TYPE_ARRAY },
|
||||
[CAP_DROP] = { .name = "cap.drop", .type = BLOBMSG_TYPE_ARRAY },
|
||||
};
|
||||
struct blob_buf b = { 0 };
|
||||
struct blob_attr *tb[__CAP_MAX];
|
||||
struct blob_attr *cur;
|
||||
int rem, cap;
|
||||
char *name;
|
||||
uint64_t capdrop = 0LLU;
|
||||
|
||||
DEBUG("dropping capabilities\n");
|
||||
|
||||
blob_buf_init(&b, 0);
|
||||
if (!blobmsg_add_json_from_file(&b, file)) {
|
||||
ERROR("failed to load %s\n", file);
|
||||
return -1;
|
||||
}
|
||||
|
||||
blobmsg_parse(policy, __CAP_MAX, tb, blob_data(b.head), blob_len(b.head));
|
||||
if (!tb[CAP_KEEP] && !tb[CAP_DROP]) {
|
||||
ERROR("failed to parse %s\n", file);
|
||||
return -1;
|
||||
}
|
||||
|
||||
blobmsg_for_each_attr(cur, tb[CAP_KEEP], rem) {
|
||||
name = blobmsg_get_string(cur);
|
||||
if (!name) {
|
||||
ERROR("invalid capability name in cap.keep\n");
|
||||
return -1;
|
||||
}
|
||||
cap = find_capabilities(name);
|
||||
if (cap == -1) {
|
||||
ERROR("unknown capability %s in cap.keep\n", name);
|
||||
return -1;
|
||||
}
|
||||
capdrop |= (1LLU << cap);
|
||||
}
|
||||
|
||||
if (capdrop == 0LLU) {
|
||||
DEBUG("cap.keep empty -> only dropping capabilities from cap.drop (blacklist)\n");
|
||||
capdrop = 0xffffffffffffffffLLU;
|
||||
} else {
|
||||
DEBUG("cap.keep has at least one capability -> dropping every capabilities not in cap.keep (whitelist)\n");
|
||||
}
|
||||
|
||||
blobmsg_for_each_attr(cur, tb[CAP_DROP], rem) {
|
||||
name = blobmsg_get_string(cur);
|
||||
if (!name) {
|
||||
ERROR("invalid capability name in cap.drop\n");
|
||||
return -1;
|
||||
}
|
||||
cap = find_capabilities(name);
|
||||
if (cap == -1) {
|
||||
ERROR("unknown capability %s in cap.drop\n", name);
|
||||
return -1;
|
||||
}
|
||||
capdrop &= ~(1LLU << cap);
|
||||
}
|
||||
|
||||
for (cap = 0; cap <= CAP_LAST_CAP; cap++) {
|
||||
if ( (capdrop & (1LLU << cap)) == 0) {
|
||||
DEBUG("dropping capability %s (%d)\n", capabilities_names[cap], cap);
|
||||
if (prctl(PR_CAPBSET_DROP, cap, 0, 0, 0)) {
|
||||
ERROR("prctl(PR_CAPBSET_DROP, %d) failed: %s\n", cap, strerror(errno));
|
||||
return errno;
|
||||
}
|
||||
} else {
|
||||
DEBUG("keeping capability %s (%d)\n", capabilities_names[cap], cap);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
18
src/3P/procd/jail/capabilities.h
Normal file
18
src/3P/procd/jail/capabilities.h
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Etienne CHAMPETIER <champetier.etienne@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
#ifndef _JAIL_CAPABILITIES_H_
|
||||
#define _JAIL_CAPABILITIES_H_
|
||||
|
||||
int drop_capabilities(const char *file);
|
||||
|
||||
#endif
|
||||
317
src/3P/procd/jail/elf.c
Normal file
317
src/3P/procd/jail/elf.c
Normal file
@@ -0,0 +1,317 @@
|
||||
/*
|
||||
* Copyright (C) 2015 John Crispin <blogic@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <glob.h>
|
||||
#include <elf.h>
|
||||
#include <linux/limits.h>
|
||||
|
||||
#include <libubox/utils.h>
|
||||
|
||||
#include "elf.h"
|
||||
#include "fs.h"
|
||||
#include "log.h"
|
||||
|
||||
struct avl_tree libraries;
|
||||
static LIST_HEAD(library_paths);
|
||||
|
||||
static void alloc_library_path(const char *path)
|
||||
{
|
||||
struct stat s;
|
||||
if (stat(path, &s))
|
||||
return;
|
||||
|
||||
struct library_path *p;
|
||||
char *_path;
|
||||
|
||||
p = calloc_a(sizeof(*p),
|
||||
&_path, strlen(path) + 1);
|
||||
if (!p)
|
||||
return;
|
||||
|
||||
p->path = strcpy(_path, path);
|
||||
|
||||
list_add_tail(&p->list, &library_paths);
|
||||
DEBUG("adding ld.so path %s\n", path);
|
||||
}
|
||||
|
||||
/*
|
||||
* path = full path
|
||||
* name = soname/avl key
|
||||
*/
|
||||
void alloc_library(const char *path, const char *name)
|
||||
{
|
||||
struct library *l;
|
||||
char *_name, *_path;
|
||||
|
||||
l = calloc_a(sizeof(*l),
|
||||
&_path, strlen(path) + 1,
|
||||
&_name, strlen(name) + 1);
|
||||
if (!l)
|
||||
return;
|
||||
|
||||
l->avl.key = l->name = strcpy(_name, name);
|
||||
l->path = strcpy(_path, path);
|
||||
|
||||
avl_insert(&libraries, &l->avl);
|
||||
DEBUG("adding library %s (%s)\n", path, name);
|
||||
}
|
||||
|
||||
int lib_open(char **fullpath, const char *file)
|
||||
{
|
||||
struct library_path *p;
|
||||
char path[PATH_MAX];
|
||||
int fd = -1;
|
||||
|
||||
*fullpath = NULL;
|
||||
|
||||
list_for_each_entry(p, &library_paths, list) {
|
||||
snprintf(path, sizeof(path), "%s/%s", p->path, file);
|
||||
fd = open(path, O_RDONLY|O_CLOEXEC);
|
||||
if (fd >= 0) {
|
||||
*fullpath = strdup(path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
const char* find_lib(const char *file)
|
||||
{
|
||||
struct library *l;
|
||||
|
||||
l = avl_find_element(&libraries, file, l, avl);
|
||||
if (!l)
|
||||
return NULL;
|
||||
|
||||
return l->path;
|
||||
}
|
||||
|
||||
static int elf64_find_section(const char *map, unsigned int type, unsigned int *offset, unsigned int *size, unsigned int *vaddr)
|
||||
{
|
||||
Elf64_Ehdr *e;
|
||||
Elf64_Phdr *ph;
|
||||
int i;
|
||||
|
||||
e = (Elf64_Ehdr *) map;
|
||||
ph = (Elf64_Phdr *) (map + e->e_phoff);
|
||||
|
||||
for (i = 0; i < e->e_phnum; i++) {
|
||||
if (ph[i].p_type == type) {
|
||||
*offset = ph[i].p_offset;
|
||||
if (size)
|
||||
*size = ph[i].p_filesz;
|
||||
if (vaddr)
|
||||
*vaddr = ph[i].p_vaddr;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int elf32_find_section(const char *map, unsigned int type, unsigned int *offset, unsigned int *size, unsigned int *vaddr)
|
||||
{
|
||||
Elf32_Ehdr *e;
|
||||
Elf32_Phdr *ph;
|
||||
int i;
|
||||
|
||||
e = (Elf32_Ehdr *) map;
|
||||
ph = (Elf32_Phdr *) (map + e->e_phoff);
|
||||
|
||||
for (i = 0; i < e->e_phnum; i++) {
|
||||
if (ph[i].p_type == type) {
|
||||
*offset = ph[i].p_offset;
|
||||
if (size)
|
||||
*size = ph[i].p_filesz;
|
||||
if (vaddr)
|
||||
*vaddr = ph[i].p_vaddr;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int elf_find_section(const char *map, unsigned int type, unsigned int *offset, unsigned int *size, unsigned int *vaddr)
|
||||
{
|
||||
int clazz = map[EI_CLASS];
|
||||
|
||||
if (clazz == ELFCLASS32)
|
||||
return elf32_find_section(map, type, offset, size, vaddr);
|
||||
else if (clazz == ELFCLASS64)
|
||||
return elf64_find_section(map, type, offset, size, vaddr);
|
||||
|
||||
ERROR("unknown elf format %d\n", clazz);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int elf32_scan_dynamic(const char *map, int dyn_offset, int dyn_size, int load_offset)
|
||||
{
|
||||
Elf32_Dyn *dynamic = (Elf32_Dyn *) (map + dyn_offset);
|
||||
const char *strtab = NULL;
|
||||
|
||||
while ((void *) dynamic < (void *) (map + dyn_offset + dyn_size)) {
|
||||
Elf32_Dyn *curr = dynamic;
|
||||
|
||||
dynamic++;
|
||||
if (curr->d_tag != DT_STRTAB)
|
||||
continue;
|
||||
|
||||
strtab = map + (curr->d_un.d_ptr - load_offset);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!strtab)
|
||||
return -1;
|
||||
|
||||
dynamic = (Elf32_Dyn *) (map + dyn_offset);
|
||||
while ((void *) dynamic < (void *) (map + dyn_offset + dyn_size)) {
|
||||
Elf32_Dyn *curr = dynamic;
|
||||
|
||||
dynamic++;
|
||||
if (curr->d_tag != DT_NEEDED)
|
||||
continue;
|
||||
|
||||
if (add_path_and_deps(&strtab[curr->d_un.d_val], 1, -1, 1))
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int elf64_scan_dynamic(const char *map, int dyn_offset, int dyn_size, int load_offset)
|
||||
{
|
||||
Elf64_Dyn *dynamic = (Elf64_Dyn *) (map + dyn_offset);
|
||||
const char *strtab = NULL;
|
||||
|
||||
while ((void *) dynamic < (void *) (map + dyn_offset + dyn_size)) {
|
||||
Elf64_Dyn *curr = dynamic;
|
||||
|
||||
dynamic++;
|
||||
if (curr->d_tag != DT_STRTAB)
|
||||
continue;
|
||||
|
||||
strtab = map + (curr->d_un.d_ptr - load_offset);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!strtab)
|
||||
return -1;
|
||||
|
||||
dynamic = (Elf64_Dyn *) (map + dyn_offset);
|
||||
while ((void *) dynamic < (void *) (map + dyn_offset + dyn_size)) {
|
||||
Elf64_Dyn *curr = dynamic;
|
||||
|
||||
dynamic++;
|
||||
if (curr->d_tag != DT_NEEDED)
|
||||
continue;
|
||||
|
||||
if (add_path_and_deps(&strtab[curr->d_un.d_val], 1, -1, 1))
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int elf_load_deps(const char *path, const char *map)
|
||||
{
|
||||
unsigned int dyn_offset, dyn_size;
|
||||
unsigned int load_offset, load_vaddr;
|
||||
unsigned int interp_offset;
|
||||
|
||||
if (elf_find_section(map, PT_LOAD, &load_offset, NULL, &load_vaddr)) {
|
||||
ERROR("failed to load the .load section from %s\n", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (elf_find_section(map, PT_DYNAMIC, &dyn_offset, &dyn_size, NULL)) {
|
||||
ERROR("failed to load the .dynamic section from %s\n", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (elf_find_section(map, PT_INTERP, &interp_offset, NULL, NULL) == 0) {
|
||||
add_path_and_deps(map+interp_offset, 1, -1, 0);
|
||||
}
|
||||
|
||||
int clazz = map[EI_CLASS];
|
||||
|
||||
if (clazz == ELFCLASS32)
|
||||
return elf32_scan_dynamic(map, dyn_offset, dyn_size, load_vaddr - load_offset);
|
||||
else if (clazz == ELFCLASS64)
|
||||
return elf64_scan_dynamic(map, dyn_offset, dyn_size, load_vaddr - load_offset);
|
||||
|
||||
ERROR("unknown elf format %d\n", clazz);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void load_ldso_conf(const char *conf)
|
||||
{
|
||||
FILE* fp = fopen(conf, "r");
|
||||
char line[PATH_MAX];
|
||||
|
||||
if (!fp) {
|
||||
DEBUG("failed to open %s\n", conf);
|
||||
return;
|
||||
}
|
||||
|
||||
while (!feof(fp)) {
|
||||
int len;
|
||||
|
||||
if (!fgets(line, sizeof(line), fp))
|
||||
break;
|
||||
len = strlen(line);
|
||||
if (len < 2)
|
||||
continue;
|
||||
if (*line == '#')
|
||||
continue;
|
||||
if (line[len - 1] == '\n')
|
||||
line[len - 1] = '\0';
|
||||
if (!strncmp(line, "include ", 8)) {
|
||||
char *sep = strstr(line, " ");
|
||||
glob_t gl;
|
||||
int i;
|
||||
|
||||
if (!sep)
|
||||
continue;;
|
||||
while (*sep == ' ')
|
||||
sep++;
|
||||
if (glob(sep, GLOB_NOESCAPE | GLOB_MARK, NULL, &gl)) {
|
||||
ERROR("glob failed on %s\n", sep);
|
||||
continue;
|
||||
}
|
||||
for (i = 0; i < gl.gl_pathc; i++)
|
||||
load_ldso_conf(gl.gl_pathv[i]);
|
||||
globfree(&gl);
|
||||
} else {
|
||||
alloc_library_path(line);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
void init_library_search(void)
|
||||
{
|
||||
avl_init(&libraries, avl_strcmp, false, NULL);
|
||||
alloc_library_path("/lib");
|
||||
alloc_library_path("/lib64");
|
||||
alloc_library_path("/usr/lib");
|
||||
load_ldso_conf("/etc/ld.so.conf");
|
||||
}
|
||||
38
src/3P/procd/jail/elf.h
Normal file
38
src/3P/procd/jail/elf.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2015 John Crispin <blogic@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
#ifndef _JAIL_ELF_H_
|
||||
#define _JAIL_ELF_H_
|
||||
|
||||
#include <libubox/avl.h>
|
||||
#include <libubox/avl-cmp.h>
|
||||
|
||||
struct library {
|
||||
struct avl_node avl;
|
||||
char *name;
|
||||
char *path;
|
||||
};
|
||||
|
||||
struct library_path {
|
||||
struct list_head list;
|
||||
char *path;
|
||||
};
|
||||
|
||||
extern struct avl_tree libraries;
|
||||
|
||||
void alloc_library(const char *path, const char *name);
|
||||
int elf_load_deps(const char *path, const char *map);
|
||||
const char* find_lib(const char *file);
|
||||
void init_library_search(void);
|
||||
int lib_open(char **fullpath, const char *file);
|
||||
|
||||
#endif
|
||||
180
src/3P/procd/jail/fs.c
Normal file
180
src/3P/procd/jail/fs.c
Normal file
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Copyright (C) 2015 John Crispin <blogic@openwrt.org>
|
||||
* Copyright (C) 2015 Etienne Champetier <champetier.etienne@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <assert.h>
|
||||
#include <elf.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <libubox/avl.h>
|
||||
#include <libubox/avl-cmp.h>
|
||||
|
||||
#include "elf.h"
|
||||
#include "fs.h"
|
||||
#include "jail.h"
|
||||
#include "log.h"
|
||||
|
||||
struct mount {
|
||||
struct avl_node avl;
|
||||
const char *path;
|
||||
int readonly;
|
||||
int error;
|
||||
};
|
||||
|
||||
struct avl_tree mounts;
|
||||
|
||||
int add_mount(const char *path, int readonly, int error)
|
||||
{
|
||||
assert(path != NULL);
|
||||
|
||||
if (avl_find(&mounts, path))
|
||||
return 1;
|
||||
|
||||
struct mount *m;
|
||||
m = calloc(1, sizeof(struct mount));
|
||||
assert(m != NULL);
|
||||
m->avl.key = m->path = strdup(path);
|
||||
m->readonly = readonly;
|
||||
m->error = error;
|
||||
|
||||
avl_insert(&mounts, &m->avl);
|
||||
DEBUG("adding mount %s ro(%d) err(%d)\n", m->path, m->readonly, m->error != 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mount_all(const char *jailroot) {
|
||||
struct library *l;
|
||||
struct mount *m;
|
||||
|
||||
avl_for_each_element(&libraries, l, avl)
|
||||
add_mount(l->path, 1, -1);
|
||||
|
||||
avl_for_each_element(&mounts, m, avl)
|
||||
if (mount_bind(jailroot, m->path, m->readonly, m->error))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mount_list_init(void) {
|
||||
avl_init(&mounts, avl_strcmp, false, NULL);
|
||||
}
|
||||
|
||||
static int add_script_interp(const char *path, const char *map, int size)
|
||||
{
|
||||
int start = 2;
|
||||
while (start < size && map[start] != '/') {
|
||||
start++;
|
||||
}
|
||||
if (start >= size) {
|
||||
ERROR("bad script interp (%s)\n", path);
|
||||
return -1;
|
||||
}
|
||||
int stop = start + 1;
|
||||
while (stop < size && map[stop] > 0x20 && map[stop] <= 0x7e) {
|
||||
stop++;
|
||||
}
|
||||
if (stop >= size || (stop-start) > PATH_MAX) {
|
||||
ERROR("bad script interp (%s)\n", path);
|
||||
return -1;
|
||||
}
|
||||
char buf[PATH_MAX];
|
||||
strncpy(buf, map+start, stop-start);
|
||||
return add_path_and_deps(buf, 1, -1, 0);
|
||||
}
|
||||
|
||||
int add_path_and_deps(const char *path, int readonly, int error, int lib)
|
||||
{
|
||||
assert(path != NULL);
|
||||
|
||||
if (lib == 0 && path[0] != '/') {
|
||||
ERROR("%s is not an absolute path\n", path);
|
||||
return error;
|
||||
}
|
||||
|
||||
char *map = NULL;
|
||||
int fd, ret = -1;
|
||||
if (path[0] == '/') {
|
||||
if (avl_find(&mounts, path))
|
||||
return 0;
|
||||
fd = open(path, O_RDONLY|O_CLOEXEC);
|
||||
if (fd == -1)
|
||||
return error;
|
||||
add_mount(path, readonly, error);
|
||||
} else {
|
||||
if (avl_find(&libraries, path))
|
||||
return 0;
|
||||
char *fullpath;
|
||||
fd = lib_open(&fullpath, path);
|
||||
if (fd == -1)
|
||||
return error;
|
||||
if (fullpath) {
|
||||
alloc_library(fullpath, path);
|
||||
free(fullpath);
|
||||
}
|
||||
}
|
||||
|
||||
struct stat s;
|
||||
if (fstat(fd, &s) == -1) {
|
||||
ERROR("fstat(%s) failed: %s\n", path, strerror(errno));
|
||||
ret = error;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!S_ISREG(s.st_mode)) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* too small to be an ELF or a script -> "normal" file */
|
||||
if (s.st_size < 4) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
map = mmap(NULL, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
if (map == MAP_FAILED) {
|
||||
ERROR("failed to mmap %s\n", path);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (map[0] == '#' && map[1] == '!') {
|
||||
ret = add_script_interp(path, map, s.st_size);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (map[0] == ELFMAG0 && map[1] == ELFMAG1 && map[2] == ELFMAG2 && map[3] == ELFMAG3) {
|
||||
ret = elf_load_deps(path, map);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
if (map)
|
||||
munmap(map, s.st_size);
|
||||
|
||||
return ret;
|
||||
}
|
||||
21
src/3P/procd/jail/fs.h
Normal file
21
src/3P/procd/jail/fs.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Etienne Champetier <champetier.etienne@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
#ifndef _JAIL_FS_H_
|
||||
#define _JAIL_FS_H_
|
||||
|
||||
int add_mount(const char *path, int readonly, int error);
|
||||
int add_path_and_deps(const char *path, int readonly, int error, int lib);
|
||||
int mount_all(const char *jailroot);
|
||||
void mount_list_init(void);
|
||||
|
||||
#endif
|
||||
415
src/3P/procd/jail/jail.c
Normal file
415
src/3P/procd/jail/jail.c
Normal file
@@ -0,0 +1,415 @@
|
||||
/*
|
||||
* Copyright (C) 2015 John Crispin <blogic@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <sys/mount.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <libgen.h>
|
||||
#include <sched.h>
|
||||
#include <linux/limits.h>
|
||||
|
||||
#include "capabilities.h"
|
||||
#include "elf.h"
|
||||
#include "fs.h"
|
||||
#include "jail.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <libubox/uloop.h>
|
||||
|
||||
#define STACK_SIZE (1024 * 1024)
|
||||
#define OPT_ARGS "S:C:n:h:r:w:d:psuloc"
|
||||
|
||||
static struct {
|
||||
char *name;
|
||||
char *hostname;
|
||||
char **jail_argv;
|
||||
char *seccomp;
|
||||
char *capabilities;
|
||||
int no_new_privs;
|
||||
int namespace;
|
||||
int procfs;
|
||||
int ronly;
|
||||
int sysfs;
|
||||
} opts;
|
||||
|
||||
extern int pivot_root(const char *new_root, const char *put_old);
|
||||
|
||||
int debug = 0;
|
||||
|
||||
static char child_stack[STACK_SIZE];
|
||||
|
||||
static int mkdir_p(char *dir, mode_t mask)
|
||||
{
|
||||
char *l = strrchr(dir, '/');
|
||||
int ret;
|
||||
|
||||
if (!l)
|
||||
return 0;
|
||||
|
||||
*l = '\0';
|
||||
|
||||
if (mkdir_p(dir, mask))
|
||||
return -1;
|
||||
|
||||
*l = '/';
|
||||
|
||||
ret = mkdir(dir, mask);
|
||||
if (ret && errno == EEXIST)
|
||||
return 0;
|
||||
|
||||
if (ret)
|
||||
ERROR("mkdir(%s, %d) failed: %s\n", dir, mask, strerror(errno));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int mount_bind(const char *root, const char *path, int readonly, int error)
|
||||
{
|
||||
struct stat s;
|
||||
char new[PATH_MAX];
|
||||
int fd;
|
||||
|
||||
if (stat(path, &s)) {
|
||||
ERROR("stat(%s) failed: %s\n", path, strerror(errno));
|
||||
return error;
|
||||
}
|
||||
|
||||
snprintf(new, sizeof(new), "%s%s", root, path);
|
||||
if (S_ISDIR(s.st_mode)) {
|
||||
mkdir_p(new, 0755);
|
||||
} else {
|
||||
mkdir_p(dirname(new), 0755);
|
||||
snprintf(new, sizeof(new), "%s%s", root, path);
|
||||
fd = creat(new, 0644);
|
||||
if (fd == -1) {
|
||||
ERROR("creat(%s) failed: %s\n", new, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
|
||||
if (mount(path, new, NULL, MS_BIND, NULL)) {
|
||||
ERROR("failed to mount -B %s %s: %s\n", path, new, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (readonly && mount(NULL, new, NULL, MS_BIND | MS_REMOUNT | MS_RDONLY, NULL)) {
|
||||
ERROR("failed to remount ro %s: %s\n", new, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
DEBUG("mount -B %s %s (%s)\n", path, new, readonly?"ro":"rw");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int build_jail_fs(void)
|
||||
{
|
||||
char jail_root[] = "/tmp/ujail-XXXXXX";
|
||||
if (mkdtemp(jail_root) == NULL) {
|
||||
ERROR("mkdtemp(%s) failed: %s\n", jail_root, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* oldroot can't be MS_SHARED else pivot_root() fails */
|
||||
if (mount("none", "/", NULL, MS_REC|MS_PRIVATE, NULL)) {
|
||||
ERROR("private mount failed %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (mount("tmpfs", jail_root, "tmpfs", MS_NOATIME, "mode=0755")) {
|
||||
ERROR("tmpfs mount failed %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (chdir(jail_root)) {
|
||||
ERROR("chdir(%s) (jail_root) failed: %s\n", jail_root, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (mount_all(jail_root)) {
|
||||
ERROR("mount_all() failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
char dirbuf[sizeof(jail_root) + 4];
|
||||
snprintf(dirbuf, sizeof(dirbuf), "%s/old", jail_root);
|
||||
mkdir(dirbuf, 0755);
|
||||
|
||||
if (pivot_root(jail_root, dirbuf) == -1) {
|
||||
ERROR("pivot_root(%s, %s) failed: %s\n", jail_root, dirbuf, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (chdir("/")) {
|
||||
ERROR("chdir(/) (after pivot_root) failed: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
snprintf(dirbuf, sizeof(dirbuf), "/old%s", jail_root);
|
||||
rmdir(dirbuf);
|
||||
umount2("/old", MNT_DETACH);
|
||||
rmdir("/old");
|
||||
|
||||
if (opts.procfs) {
|
||||
mkdir("/proc", 0755);
|
||||
mount("proc", "/proc", "proc", MS_NOATIME | MS_NODEV | MS_NOEXEC | MS_NOSUID, 0);
|
||||
}
|
||||
if (opts.sysfs) {
|
||||
mkdir("/sys", 0755);
|
||||
mount("sysfs", "/sys", "sysfs", MS_NOATIME | MS_NODEV | MS_NOEXEC | MS_NOSUID, 0);
|
||||
}
|
||||
if (opts.ronly)
|
||||
mount(NULL, "/", NULL, MS_RDONLY | MS_REMOUNT, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define MAX_ENVP 8
|
||||
static char** build_envp(const char *seccomp)
|
||||
{
|
||||
static char *envp[MAX_ENVP];
|
||||
static char preload_var[PATH_MAX];
|
||||
static char seccomp_var[PATH_MAX];
|
||||
static char debug_var[] = "LD_DEBUG=all";
|
||||
const char *preload_lib = find_lib("libpreload-seccomp.so");
|
||||
int count = 0;
|
||||
|
||||
if (seccomp && !preload_lib) {
|
||||
ERROR("failed to add preload-lib to env\n");
|
||||
return NULL;
|
||||
}
|
||||
if (seccomp) {
|
||||
snprintf(seccomp_var, sizeof(seccomp_var), "SECCOMP_FILE=%s", seccomp);
|
||||
envp[count++] = seccomp_var;
|
||||
snprintf(preload_var, sizeof(preload_var), "LD_PRELOAD=%s", preload_lib);
|
||||
envp[count++] = preload_var;
|
||||
}
|
||||
if (debug > 1)
|
||||
envp[count++] = debug_var;
|
||||
|
||||
return envp;
|
||||
}
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
fprintf(stderr, "ujail <options> -- <binary> <params ...>\n");
|
||||
fprintf(stderr, " -d <num>\tshow debug log (increase num to increase verbosity)\n");
|
||||
fprintf(stderr, " -S <file>\tseccomp filter config\n");
|
||||
fprintf(stderr, " -C <file>\tcapabilities drop config\n");
|
||||
fprintf(stderr, " -c\t\tset PR_SET_NO_NEW_PRIVS\n");
|
||||
fprintf(stderr, " -n <name>\tthe name of the jail\n");
|
||||
fprintf(stderr, "namespace jail options:\n");
|
||||
fprintf(stderr, " -h <hostname>\tchange the hostname of the jail\n");
|
||||
fprintf(stderr, " -r <file>\treadonly files that should be staged\n");
|
||||
fprintf(stderr, " -w <file>\twriteable files that should be staged\n");
|
||||
fprintf(stderr, " -p\t\tjail has /proc\n");
|
||||
fprintf(stderr, " -s\t\tjail has /sys\n");
|
||||
fprintf(stderr, " -l\t\tjail has /dev/log\n");
|
||||
fprintf(stderr, " -u\t\tjail has a ubus socket\n");
|
||||
fprintf(stderr, " -o\t\tremont jail root (/) read only\n");
|
||||
fprintf(stderr, "\nWarning: by default root inside the jail is the same\n\
|
||||
and he has the same powers as root outside the jail,\n\
|
||||
thus he can escape the jail and/or break stuff.\n\
|
||||
Please use seccomp/capabilities (-S/-C) to restrict his powers\n\n\
|
||||
If you use none of the namespace jail options,\n\
|
||||
ujail will not use namespace/build a jail,\n\
|
||||
and will only drop capabilities/apply seccomp filter.\n\n");
|
||||
}
|
||||
|
||||
static int exec_jail(void *_notused)
|
||||
{
|
||||
if (opts.capabilities && drop_capabilities(opts.capabilities))
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
if (opts.no_new_privs && prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
|
||||
ERROR("prctl(PR_SET_NO_NEW_PRIVS) failed: %s\n", strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (opts.namespace && opts.hostname && strlen(opts.hostname) > 0
|
||||
&& sethostname(opts.hostname, strlen(opts.hostname))) {
|
||||
ERROR("sethostname(%s) failed: %s\n", opts.hostname, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (opts.namespace && build_jail_fs()) {
|
||||
ERROR("failed to build jail fs\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
char **envp = build_envp(opts.seccomp);
|
||||
if (!envp)
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
INFO("exec-ing %s\n", *opts.jail_argv);
|
||||
execve(*opts.jail_argv, opts.jail_argv, envp);
|
||||
/* we get there only if execve fails */
|
||||
ERROR("failed to execve %s: %s\n", *opts.jail_argv, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static int jail_running = 1;
|
||||
static int jail_return_code = 0;
|
||||
|
||||
static void jail_process_handler(struct uloop_process *c, int ret)
|
||||
{
|
||||
if (WIFEXITED(ret)) {
|
||||
jail_return_code = WEXITSTATUS(ret);
|
||||
INFO("jail (%d) exited with exit: %d\n", c->pid, jail_return_code);
|
||||
} else {
|
||||
jail_return_code = WTERMSIG(ret);
|
||||
INFO("jail (%d) exited with signal: %d\n", c->pid, jail_return_code);
|
||||
}
|
||||
jail_running = 0;
|
||||
uloop_end();
|
||||
}
|
||||
|
||||
static struct uloop_process jail_process = {
|
||||
.cb = jail_process_handler,
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
uid_t uid = getuid();
|
||||
char log[] = "/dev/log";
|
||||
char ubus[] = "/var/run/ubus.sock";
|
||||
int ch;
|
||||
|
||||
if (uid) {
|
||||
ERROR("not root, aborting: %s\n", strerror(errno));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
umask(022);
|
||||
mount_list_init();
|
||||
init_library_search();
|
||||
|
||||
while ((ch = getopt(argc, argv, OPT_ARGS)) != -1) {
|
||||
switch (ch) {
|
||||
case 'd':
|
||||
debug = atoi(optarg);
|
||||
break;
|
||||
case 'p':
|
||||
opts.namespace = 1;
|
||||
opts.procfs = 1;
|
||||
break;
|
||||
case 'o':
|
||||
opts.namespace = 1;
|
||||
opts.ronly = 1;
|
||||
break;
|
||||
case 's':
|
||||
opts.namespace = 1;
|
||||
opts.sysfs = 1;
|
||||
break;
|
||||
case 'S':
|
||||
opts.seccomp = optarg;
|
||||
add_mount(optarg, 1, -1);
|
||||
break;
|
||||
case 'C':
|
||||
opts.capabilities = optarg;
|
||||
break;
|
||||
case 'c':
|
||||
opts.no_new_privs = 1;
|
||||
break;
|
||||
case 'n':
|
||||
opts.name = optarg;
|
||||
break;
|
||||
case 'h':
|
||||
opts.hostname = optarg;
|
||||
break;
|
||||
case 'r':
|
||||
opts.namespace = 1;
|
||||
add_path_and_deps(optarg, 1, 0, 0);
|
||||
break;
|
||||
case 'w':
|
||||
opts.namespace = 1;
|
||||
add_path_and_deps(optarg, 0, 0, 0);
|
||||
break;
|
||||
case 'u':
|
||||
opts.namespace = 1;
|
||||
add_mount(ubus, 0, -1);
|
||||
break;
|
||||
case 'l':
|
||||
opts.namespace = 1;
|
||||
add_mount(log, 0, -1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* no <binary> param found */
|
||||
if (argc - optind < 1) {
|
||||
usage();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (!(opts.namespace||opts.capabilities||opts.seccomp)) {
|
||||
ERROR("Not using namespaces, capabilities or seccomp !!!\n\n");
|
||||
usage();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
DEBUG("Using namespaces(%d), capabilities(%d), seccomp(%d)\n",
|
||||
opts.namespace,
|
||||
opts.capabilities != 0,
|
||||
opts.seccomp != 0);
|
||||
|
||||
opts.jail_argv = &argv[optind];
|
||||
|
||||
if (opts.namespace && add_path_and_deps(*opts.jail_argv, 1, -1, 0)) {
|
||||
ERROR("failed to load dependencies\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (opts.namespace && opts.seccomp && add_path_and_deps("libpreload-seccomp.so", 1, -1, 1)) {
|
||||
ERROR("failed to load libpreload-seccomp.so\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (opts.name)
|
||||
prctl(PR_SET_NAME, opts.name, NULL, NULL, NULL);
|
||||
|
||||
uloop_init();
|
||||
if (opts.namespace) {
|
||||
int flags = CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWIPC | SIGCHLD;
|
||||
if (opts.hostname)
|
||||
flags |= CLONE_NEWUTS;
|
||||
jail_process.pid = clone(exec_jail, child_stack + STACK_SIZE, flags, NULL);
|
||||
} else {
|
||||
jail_process.pid = fork();
|
||||
}
|
||||
|
||||
if (jail_process.pid > 0) {
|
||||
/* parent process */
|
||||
uloop_process_add(&jail_process);
|
||||
uloop_run();
|
||||
uloop_done();
|
||||
if (jail_running) {
|
||||
DEBUG("uloop interrupted, killing jail process\n");
|
||||
kill(jail_process.pid, SIGTERM);
|
||||
waitpid(jail_process.pid, NULL, 0);
|
||||
}
|
||||
return jail_return_code;
|
||||
} else if (jail_process.pid == 0) {
|
||||
/* fork child process */
|
||||
return exec_jail(NULL);
|
||||
} else {
|
||||
ERROR("failed to clone/fork: %s\n", strerror(errno));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
18
src/3P/procd/jail/jail.h
Normal file
18
src/3P/procd/jail/jail.h
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Etienne Champetier <champetier.etienne@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
#ifndef _JAIL_JAIL_H_
|
||||
#define _JAIL_JAIL_H_
|
||||
|
||||
int mount_bind(const char *root, const char *path, int readonly, int error);
|
||||
|
||||
#endif
|
||||
31
src/3P/procd/jail/log.h
Normal file
31
src/3P/procd/jail/log.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2015 John Crispin <blogic@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
#ifndef _JAIL_LOG_H_
|
||||
#define _JAIL_LOG_H_
|
||||
|
||||
extern int debug;
|
||||
#include <stdio.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#define INFO(fmt, ...) do { \
|
||||
printf("jail: "fmt, ## __VA_ARGS__); \
|
||||
} while (0)
|
||||
#define ERROR(fmt, ...) do { \
|
||||
syslog(LOG_ERR, "jail: "fmt, ## __VA_ARGS__); \
|
||||
fprintf(stderr,"jail: "fmt, ## __VA_ARGS__); \
|
||||
} while (0)
|
||||
#define DEBUG(fmt, ...) do { \
|
||||
if (debug) printf("jail: "fmt, ## __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
78
src/3P/procd/jail/preload.c
Normal file
78
src/3P/procd/jail/preload.c
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (C) 2015 John Crispin <blogic@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <sys/types.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "seccomp.h"
|
||||
#include "../preload.h"
|
||||
|
||||
static main_t __main__;
|
||||
|
||||
static int __preload_main__(int argc, char **argv, char **envp)
|
||||
{
|
||||
char *env_file = getenv("SECCOMP_FILE");
|
||||
|
||||
if (install_syscall_filter(*argv, env_file))
|
||||
return -1;
|
||||
|
||||
unsetenv("LD_PRELOAD");
|
||||
unsetenv("SECCOMP_FILE");
|
||||
|
||||
return (*__main__)(argc, argv, envp);
|
||||
}
|
||||
|
||||
int __libc_start_main(main_t main,
|
||||
int argc,
|
||||
char **argv,
|
||||
ElfW(auxv_t) *auxvec,
|
||||
__typeof (main) init,
|
||||
void (*fini) (void),
|
||||
void (*rtld_fini) (void),
|
||||
void *stack_end)
|
||||
{
|
||||
start_main_t __start_main__;
|
||||
|
||||
__start_main__ = dlsym(RTLD_NEXT, "__libc_start_main");
|
||||
if (!__start_main__)
|
||||
INFO("failed to find __libc_start_main %s\n", dlerror());
|
||||
|
||||
__main__ = main;
|
||||
|
||||
return (*__start_main__)(__preload_main__, argc, argv, auxvec,
|
||||
init, fini, rtld_fini, stack_end);
|
||||
}
|
||||
|
||||
void __uClibc_main(main_t main,
|
||||
int argc,
|
||||
char **argv,
|
||||
void (*app_init)(void),
|
||||
void (*app_fini)(void),
|
||||
void (*rtld_fini)(void),
|
||||
void *stack_end attribute_unused)
|
||||
{
|
||||
uClibc_main __start_main__;
|
||||
|
||||
__start_main__ = dlsym(RTLD_NEXT, "__uClibc_main");
|
||||
if (!__start_main__)
|
||||
INFO("failed to find __uClibc_main %s\n", dlerror());
|
||||
|
||||
__main__ = main;
|
||||
|
||||
return (*__start_main__)(__preload_main__, argc, argv,
|
||||
app_init, app_fini, rtld_fini, stack_end);
|
||||
}
|
||||
89
src/3P/procd/jail/seccomp-bpf.h
Normal file
89
src/3P/procd/jail/seccomp-bpf.h
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* seccomp example for x86 (32-bit and 64-bit) with BPF macros
|
||||
*
|
||||
* Copyright (c) 2012 The Chromium OS Authors <chromium-os-dev@chromium.org>
|
||||
* Authors:
|
||||
* Will Drewry <wad@chromium.org>
|
||||
* Kees Cook <keescook@chromium.org>
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
#ifndef _SECCOMP_BPF_H_
|
||||
#define _SECCOMP_BPF_H_
|
||||
|
||||
#define _GNU_SOURCE 1
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <endian.h>
|
||||
|
||||
#include <sys/prctl.h>
|
||||
#ifndef PR_SET_NO_NEW_PRIVS
|
||||
# define PR_SET_NO_NEW_PRIVS 38
|
||||
#endif
|
||||
|
||||
#include <linux/unistd.h>
|
||||
#include <linux/audit.h>
|
||||
#include <linux/filter.h>
|
||||
|
||||
#ifdef HAVE_LINUX_SECCOMP_H
|
||||
# include <linux/seccomp.h>
|
||||
#endif
|
||||
|
||||
#ifndef SECCOMP_MODE_FILTER
|
||||
#define SECCOMP_MODE_FILTER 2 /* uses user-supplied filter. */
|
||||
#define SECCOMP_RET_KILL 0x00000000U /* kill the task immediately */
|
||||
#define SECCOMP_RET_TRAP 0x00030000U /* disallow and force a SIGSYS */
|
||||
#define SECCOMP_RET_ERRNO 0x00050000U /* returns an errno */
|
||||
#define SECCOMP_RET_LOG 0x00070000U
|
||||
#define SECCOMP_RET_ALLOW 0x7fff0000U /* allow */
|
||||
#define SECCOMP_RET_ERROR(x) (SECCOMP_RET_ERRNO | ((x) & 0x0000ffffU))
|
||||
#define SECCOMP_RET_LOGGER(x) (SECCOMP_RET_LOG | ((x) & 0x0000ffffU))
|
||||
|
||||
struct seccomp_data {
|
||||
int nr;
|
||||
__u32 arch;
|
||||
__u64 instruction_pointer;
|
||||
__u64 args[6];
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifndef SYS_SECCOMP
|
||||
# define SYS_SECCOMP 1
|
||||
#endif
|
||||
|
||||
#define syscall_nr (offsetof(struct seccomp_data, nr))
|
||||
#define arch_nr (offsetof(struct seccomp_data, arch))
|
||||
|
||||
#if defined(__i386__)
|
||||
# define REG_SYSCALL REG_EAX
|
||||
# define ARCH_NR AUDIT_ARCH_I386
|
||||
#elif defined(__x86_64__)
|
||||
# define REG_SYSCALL REG_RAX
|
||||
# define ARCH_NR AUDIT_ARCH_X86_64
|
||||
#elif defined(__mips__)
|
||||
# define REG_SYSCALL regs[2]
|
||||
# if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
# define ARCH_NR AUDIT_ARCH_MIPSEL
|
||||
# else
|
||||
# define ARCH_NR AUDIT_ARCH_MIPS
|
||||
# endif
|
||||
#elif defined(__arm__) && (defined(__ARM_EABI__) || defined(__thumb__))
|
||||
# define REG_SYSCALL regs.uregs[7]
|
||||
# if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
# define ARCH_NR AUDIT_ARCH_ARM
|
||||
# else
|
||||
# define ARCH_NR AUDIT_ARCH_ARMEB
|
||||
# endif
|
||||
#else
|
||||
# warning "Platform does not support seccomp filter yet"
|
||||
# define REG_SYSCALL 0
|
||||
# define ARCH_NR 0
|
||||
#endif
|
||||
|
||||
#endif /* _SECCOMP_BPF_H_ */
|
||||
140
src/3P/procd/jail/seccomp.c
Normal file
140
src/3P/procd/jail/seccomp.c
Normal file
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* seccomp example with syscall reporting
|
||||
*
|
||||
* Copyright (c) 2012 The Chromium OS Authors <chromium-os-dev@chromium.org>
|
||||
* Authors:
|
||||
* Kees Cook <keescook@chromium.org>
|
||||
* Will Drewry <wad@chromium.org>
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
#define _GNU_SOURCE 1
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <libubox/utils.h>
|
||||
#include <libubox/blobmsg.h>
|
||||
#include <libubox/blobmsg_json.h>
|
||||
|
||||
#include "seccomp-bpf.h"
|
||||
#include "seccomp.h"
|
||||
#include "../syscall-names.h"
|
||||
|
||||
static int max_syscall = ARRAY_SIZE(syscall_names);
|
||||
|
||||
static int find_syscall(const char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < max_syscall; i++)
|
||||
if (syscall_names[i] && !strcmp(syscall_names[i], name))
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void set_filter(struct sock_filter *filter, __u16 code, __u8 jt, __u8 jf, __u32 k)
|
||||
{
|
||||
filter->code = code;
|
||||
filter->jt = jt;
|
||||
filter->jf = jf;
|
||||
filter->k = k;
|
||||
}
|
||||
|
||||
int install_syscall_filter(const char *argv, const char *file)
|
||||
{
|
||||
enum {
|
||||
SECCOMP_WHITELIST,
|
||||
SECCOMP_POLICY,
|
||||
__SECCOMP_MAX
|
||||
};
|
||||
static const struct blobmsg_policy policy[__SECCOMP_MAX] = {
|
||||
[SECCOMP_WHITELIST] = { .name = "whitelist", .type = BLOBMSG_TYPE_ARRAY },
|
||||
[SECCOMP_POLICY] = { .name = "policy", .type = BLOBMSG_TYPE_INT32 },
|
||||
};
|
||||
struct blob_buf b = { 0 };
|
||||
struct blob_attr *tb[__SECCOMP_MAX];
|
||||
struct blob_attr *cur;
|
||||
int rem;
|
||||
|
||||
struct sock_filter *filter;
|
||||
struct sock_fprog prog = { 0 };
|
||||
int sz = 5, idx = 0, default_policy = 0;
|
||||
|
||||
INFO("%s: setting up syscall filter\n", argv);
|
||||
|
||||
blob_buf_init(&b, 0);
|
||||
if (!blobmsg_add_json_from_file(&b, file)) {
|
||||
INFO("%s: failed to load %s\n", argv, file);
|
||||
return -1;
|
||||
}
|
||||
|
||||
blobmsg_parse(policy, __SECCOMP_MAX, tb, blob_data(b.head), blob_len(b.head));
|
||||
if (!tb[SECCOMP_WHITELIST]) {
|
||||
INFO("%s: %s is missing the syscall table\n", argv, file);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (tb[SECCOMP_POLICY])
|
||||
default_policy = blobmsg_get_u32(tb[SECCOMP_POLICY]);
|
||||
|
||||
blobmsg_for_each_attr(cur, tb[SECCOMP_WHITELIST], rem)
|
||||
sz += 2;
|
||||
|
||||
filter = calloc(sz, sizeof(struct sock_filter));
|
||||
if (!filter) {
|
||||
INFO("failed to allocate filter memory\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* validate arch */
|
||||
set_filter(&filter[idx++], BPF_LD + BPF_W + BPF_ABS, 0, 0, arch_nr);
|
||||
set_filter(&filter[idx++], BPF_JMP + BPF_JEQ + BPF_K, 1, 0, ARCH_NR);
|
||||
set_filter(&filter[idx++], BPF_RET + BPF_K, 0, 0, SECCOMP_RET_KILL);
|
||||
|
||||
/* get syscall */
|
||||
set_filter(&filter[idx++], BPF_LD + BPF_W + BPF_ABS, 0, 0, syscall_nr);
|
||||
|
||||
blobmsg_for_each_attr(cur, tb[SECCOMP_WHITELIST], rem) {
|
||||
char *name = blobmsg_get_string(cur);
|
||||
int nr;
|
||||
|
||||
if (!name) {
|
||||
INFO("%s: invalid syscall name\n", argv);
|
||||
continue;
|
||||
}
|
||||
|
||||
nr = find_syscall(name);
|
||||
if (nr == -1) {
|
||||
INFO("%s: unknown syscall %s\n", argv, name);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* add whitelist */
|
||||
set_filter(&filter[idx++], BPF_JMP + BPF_JEQ + BPF_K, 0, 1, nr);
|
||||
set_filter(&filter[idx++], BPF_RET + BPF_K, 0, 0, SECCOMP_RET_ALLOW);
|
||||
}
|
||||
|
||||
if (default_policy)
|
||||
/* return -1 and set errno */
|
||||
set_filter(&filter[idx], BPF_RET + BPF_K, 0, 0, SECCOMP_RET_LOGGER(default_policy));
|
||||
else
|
||||
/* kill the process */
|
||||
set_filter(&filter[idx], BPF_RET + BPF_K, 0, 0, SECCOMP_RET_KILL);
|
||||
|
||||
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
|
||||
INFO("%s: prctl(PR_SET_NO_NEW_PRIVS) failed: %s\n", argv, strerror(errno));
|
||||
return errno;
|
||||
}
|
||||
|
||||
prog.len = (unsigned short) idx + 1;
|
||||
prog.filter = filter;
|
||||
|
||||
if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) {
|
||||
INFO("%s: prctl(PR_SET_SECCOMP) failed: %s\n", argv, strerror(errno));
|
||||
return errno;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
26
src/3P/procd/jail/seccomp.h
Normal file
26
src/3P/procd/jail/seccomp.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2015 John Crispin <blogic@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
#ifndef _JAIL_SECCOMP_H_
|
||||
#define _JAIL_SECCOMP_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#define INFO(fmt, ...) do { \
|
||||
syslog(LOG_INFO,"preload-seccomp: "fmt, ## __VA_ARGS__); \
|
||||
fprintf(stderr,"preload-seccomp: "fmt, ## __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
int install_syscall_filter(const char *argv, const char *file);
|
||||
|
||||
#endif
|
||||
10
src/3P/procd/libc-compat.h
Normal file
10
src/3P/procd/libc-compat.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef __PROCD_LIBC_COMPAT_H
|
||||
#define __PROCD_LIBC_COMPAT_H
|
||||
|
||||
#if defined(__GLIBC__) && !defined(__UCLIBC__)
|
||||
static inline int ignore(int x) {return x;}
|
||||
#else
|
||||
#define ignore(x) x
|
||||
#endif
|
||||
|
||||
#endif
|
||||
30
src/3P/procd/log.h
Normal file
30
src/3P/procd/log.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
|
||||
* Copyright (C) 2013 John Crispin <blogic@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __LOG_H
|
||||
#define __LOG_H
|
||||
|
||||
#include <libubox/ulog.h>
|
||||
|
||||
#define DEBUG(level, fmt, ...) do { \
|
||||
if (debug >= level) { \
|
||||
ulog(LOG_DEBUG, fmt, ## __VA_ARGS__); \
|
||||
} } while (0)
|
||||
|
||||
#define LOG ULOG_INFO
|
||||
#define ERROR ULOG_ERR
|
||||
|
||||
extern unsigned int debug;
|
||||
|
||||
#endif
|
||||
10
src/3P/procd/make_capabilities_h.sh
Executable file
10
src/3P/procd/make_capabilities_h.sh
Executable file
@@ -0,0 +1,10 @@
|
||||
#!/bin/sh
|
||||
|
||||
CC=$1
|
||||
[ -n "$TARGET_CC_NOCACHE" ] && CC=$TARGET_CC_NOCACHE
|
||||
|
||||
echo "#include <linux/capability.h>"
|
||||
echo "static const char *capabilities_names[] = {"
|
||||
echo "#include <linux/capability.h>" | ${CC} -E -dM - | grep '#define CAP' | grep -vE '(CAP_TO|CAP_LAST_CAP)' | \
|
||||
awk '{print $3" "$2}' | sort -n | awk '{print " ["$1"]\t= \""tolower($2)"\","}'
|
||||
echo "};"
|
||||
18
src/3P/procd/make_syscall_h.sh
Executable file
18
src/3P/procd/make_syscall_h.sh
Executable file
@@ -0,0 +1,18 @@
|
||||
#!/bin/sh
|
||||
# syscall reporting example for seccomp
|
||||
#
|
||||
# Copyright (c) 2012 The Chromium OS Authors <chromium-os-dev@chromium.org>
|
||||
# Authors:
|
||||
# Kees Cook <keescook@chromium.org>
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
CC=$1
|
||||
[ -n "$TARGET_CC_NOCACHE" ] && CC=$TARGET_CC_NOCACHE
|
||||
|
||||
echo "#include <asm/unistd.h>"
|
||||
echo "static const char *syscall_names[] = {"
|
||||
echo "#include <sys/syscall.h>" | ${CC} -E -dM - | grep '^#define __NR_' | \
|
||||
LC_ALL=C sed -r -n -e 's/^\#define[ \t]+__NR_([a-z0-9_]+)[ \t]+([ ()+0-9a-zNR_Linux]+)(.*)/ [\2] = "\1",/p'
|
||||
echo "};"
|
||||
69
src/3P/procd/plug/coldplug.c
Normal file
69
src/3P/procd/plug/coldplug.c
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
|
||||
* Copyright (C) 2013 John Crispin <blogic@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/mount.h>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../procd.h"
|
||||
#include "../libc-compat.h"
|
||||
|
||||
#include "hotplug.h"
|
||||
|
||||
static struct uloop_process udevtrigger;
|
||||
|
||||
static void coldplug_complete(struct uloop_timeout *t)
|
||||
{
|
||||
DEBUG(4, "Coldplug complete\n");
|
||||
hotplug_last_event(NULL);
|
||||
procd_state_next();
|
||||
}
|
||||
|
||||
static void udevtrigger_complete(struct uloop_process *proc, int ret)
|
||||
{
|
||||
DEBUG(4, "Finished udevtrigger\n");
|
||||
hotplug_last_event(coldplug_complete);
|
||||
}
|
||||
|
||||
void procd_coldplug(void)
|
||||
{
|
||||
char *argv[] = { "udevtrigger", NULL };
|
||||
unsigned int oldumask = umask(0);
|
||||
|
||||
umount2("/dev/pts", MNT_DETACH);
|
||||
umount2("/dev/", MNT_DETACH);
|
||||
mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755,size=512K");
|
||||
ignore(symlink("/tmp/shm", "/dev/shm"));
|
||||
mkdir("/dev/pts", 0755);
|
||||
umask(oldumask);
|
||||
mount("devpts", "/dev/pts", "devpts", MS_NOEXEC | MS_NOSUID, 0);
|
||||
udevtrigger.cb = udevtrigger_complete;
|
||||
udevtrigger.pid = fork();
|
||||
if (!udevtrigger.pid) {
|
||||
execvp(argv[0], argv);
|
||||
ERROR("Failed to start coldplug\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (udevtrigger.pid <= 0) {
|
||||
ERROR("Failed to start new coldplug instance\n");
|
||||
return;
|
||||
}
|
||||
|
||||
uloop_process_add(&udevtrigger);
|
||||
|
||||
DEBUG(4, "Launched coldplug instance, pid=%d\n", (int) udevtrigger.pid);
|
||||
}
|
||||
622
src/3P/procd/plug/hotplug.c
Normal file
622
src/3P/procd/plug/hotplug.c
Normal file
@@ -0,0 +1,622 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
|
||||
* Copyright (C) 2013 John Crispin <blogic@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/netlink.h>
|
||||
|
||||
#include <libubox/blobmsg_json.h>
|
||||
#include <libubox/json_script.h>
|
||||
#include <libubox/uloop.h>
|
||||
#include <json-c/json.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <libgen.h>
|
||||
#include <grp.h>
|
||||
|
||||
#include "../procd.h"
|
||||
|
||||
#include "hotplug.h"
|
||||
|
||||
#define HOTPLUG_WAIT 500
|
||||
|
||||
struct cmd_handler;
|
||||
struct cmd_queue {
|
||||
struct list_head list;
|
||||
|
||||
struct blob_attr *msg;
|
||||
struct blob_attr *data;
|
||||
int timeout;
|
||||
|
||||
void (*handler)(struct blob_attr *msg, struct blob_attr *data);
|
||||
void (*start)(struct blob_attr *msg, struct blob_attr *data);
|
||||
void (*complete)(struct blob_attr *msg, struct blob_attr *data, int ret);
|
||||
};
|
||||
|
||||
struct button_timeout {
|
||||
struct list_head list;
|
||||
struct uloop_timeout timeout;
|
||||
char *name;
|
||||
int seen;
|
||||
struct blob_attr *data;
|
||||
};
|
||||
|
||||
static LIST_HEAD(cmd_queue);
|
||||
static LIST_HEAD(button_timer);
|
||||
static struct uloop_process queue_proc;
|
||||
static struct uloop_timeout last_event;
|
||||
static struct blob_buf b, button_buf;
|
||||
static char *rule_file;
|
||||
static struct blob_buf script;
|
||||
static struct cmd_queue *current;
|
||||
|
||||
static void queue_add(struct cmd_handler *h, struct blob_attr *msg, struct blob_attr *data);
|
||||
static void handle_button_complete(struct blob_attr *msg, struct blob_attr *data, int ret);
|
||||
|
||||
static void button_free(struct button_timeout *b)
|
||||
{
|
||||
uloop_timeout_cancel(&b->timeout);
|
||||
list_del(&b->list);
|
||||
free(b->data);
|
||||
free(b->name);
|
||||
free(b);
|
||||
}
|
||||
|
||||
static void button_timeout_remove(char *button)
|
||||
{
|
||||
struct button_timeout *b, *c;
|
||||
|
||||
if (!list_empty(&button_timer)) list_for_each_entry_safe(b, c, &button_timer, list) {
|
||||
if (!strcmp(b->name, button))
|
||||
button_free(b);
|
||||
}
|
||||
}
|
||||
|
||||
static char *hotplug_msg_find_var(struct blob_attr *msg, const char *name)
|
||||
{
|
||||
struct blob_attr *cur;
|
||||
int rem;
|
||||
|
||||
blobmsg_for_each_attr(cur, msg, rem) {
|
||||
if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
|
||||
continue;
|
||||
|
||||
if (strcmp(blobmsg_name(cur), name) != 0)
|
||||
continue;
|
||||
|
||||
return blobmsg_data(cur);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void mkdir_p(char *dir)
|
||||
{
|
||||
char *l = strrchr(dir, '/');
|
||||
|
||||
if (l) {
|
||||
*l = '\0';
|
||||
mkdir_p(dir);
|
||||
*l = '/';
|
||||
mkdir(dir, 0755);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_makedev(struct blob_attr *msg, struct blob_attr *data)
|
||||
{
|
||||
unsigned int oldumask = umask(0);
|
||||
static struct blobmsg_policy mkdev_policy[3] = {
|
||||
{ .type = BLOBMSG_TYPE_STRING },
|
||||
{ .type = BLOBMSG_TYPE_STRING },
|
||||
{ .type = BLOBMSG_TYPE_STRING },
|
||||
};
|
||||
struct blob_attr *tb[3];
|
||||
char *minor = hotplug_msg_find_var(msg, "MINOR");
|
||||
char *major = hotplug_msg_find_var(msg, "MAJOR");
|
||||
char *subsystem = hotplug_msg_find_var(msg, "SUBSYSTEM");
|
||||
|
||||
blobmsg_parse_array(mkdev_policy, 3, tb, blobmsg_data(data), blobmsg_data_len(data));
|
||||
if (tb[0] && tb[1] && minor && major && subsystem) {
|
||||
mode_t m = S_IFCHR;
|
||||
char *d = strdup(blobmsg_get_string(tb[0]));
|
||||
|
||||
d = dirname(d);
|
||||
mkdir_p(d);
|
||||
free(d);
|
||||
|
||||
if (!strcmp(subsystem, "block"))
|
||||
m = S_IFBLK;
|
||||
mknod(blobmsg_get_string(tb[0]),
|
||||
m | strtoul(blobmsg_data(tb[1]), NULL, 8),
|
||||
makedev(atoi(major), atoi(minor)));
|
||||
if (tb[2]) {
|
||||
struct group *g = getgrnam(blobmsg_get_string(tb[2]));
|
||||
|
||||
if (g)
|
||||
chown(blobmsg_get_string(tb[0]), 0, g->gr_gid);
|
||||
else
|
||||
ERROR("cannot set group %s for %s\n",
|
||||
blobmsg_get_string(tb[2]),
|
||||
blobmsg_get_string(tb[0]));
|
||||
}
|
||||
}
|
||||
umask(oldumask);
|
||||
}
|
||||
|
||||
static void handle_rm(struct blob_attr *msg, struct blob_attr *data)
|
||||
{
|
||||
static struct blobmsg_policy rm_policy = {
|
||||
.type = BLOBMSG_TYPE_STRING,
|
||||
};
|
||||
struct blob_attr *tb;
|
||||
|
||||
blobmsg_parse_array(&rm_policy, 1, &tb, blobmsg_data(data), blobmsg_data_len(data));
|
||||
if (tb)
|
||||
unlink(blobmsg_data(tb));
|
||||
}
|
||||
|
||||
static void handle_exec(struct blob_attr *msg, struct blob_attr *data)
|
||||
{
|
||||
char *argv[8];
|
||||
struct blob_attr *cur;
|
||||
int rem, fd;
|
||||
int i = 0;
|
||||
|
||||
blobmsg_for_each_attr(cur, msg, rem)
|
||||
setenv(blobmsg_name(cur), blobmsg_data(cur), 1);
|
||||
|
||||
blobmsg_for_each_attr(cur, data, rem) {
|
||||
argv[i] = blobmsg_data(cur);
|
||||
i++;
|
||||
if (i == 7)
|
||||
break;
|
||||
}
|
||||
|
||||
if (debug < 3) {
|
||||
fd = open("/dev/null", O_RDWR);
|
||||
if (fd > -1) {
|
||||
dup2(fd, STDIN_FILENO);
|
||||
dup2(fd, STDOUT_FILENO);
|
||||
dup2(fd, STDERR_FILENO);
|
||||
if (fd > STDERR_FILENO)
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
if (i > 0) {
|
||||
argv[i] = NULL;
|
||||
execvp(argv[0], &argv[0]);
|
||||
}
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
static void handle_button_start(struct blob_attr *msg, struct blob_attr *data)
|
||||
{
|
||||
char *button = hotplug_msg_find_var(msg, "BUTTON");
|
||||
|
||||
if (button)
|
||||
button_timeout_remove(button);
|
||||
}
|
||||
|
||||
static void handle_firmware(struct blob_attr *msg, struct blob_attr *data)
|
||||
{
|
||||
char *dir = blobmsg_get_string(blobmsg_data(data));
|
||||
char *file = hotplug_msg_find_var(msg, "FIRMWARE");
|
||||
char *dev = hotplug_msg_find_var(msg, "DEVPATH");
|
||||
struct stat s = { 0 };
|
||||
char *path, loadpath[256], syspath[256];
|
||||
int fw, src, load, len;
|
||||
static char buf[4096];
|
||||
|
||||
DEBUG(2, "Firmware request for %s/%s\n", dir, file);
|
||||
|
||||
if (!file || !dir || !dev) {
|
||||
ERROR("Request for unknown firmware %s/%s\n", dir, file);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
path = alloca(strlen(dir) + strlen(file) + 2);
|
||||
sprintf(path, "%s/%s", dir, file);
|
||||
|
||||
if (stat(path, &s)) {
|
||||
ERROR("Could not find firmware %s\n", path);
|
||||
src = -1;
|
||||
s.st_size = 0;
|
||||
goto send_to_kernel;
|
||||
}
|
||||
|
||||
src = open(path, O_RDONLY);
|
||||
if (src < 0) {
|
||||
ERROR("Failed to open %s\n", path);
|
||||
s.st_size = 0;
|
||||
goto send_to_kernel;
|
||||
}
|
||||
|
||||
send_to_kernel:
|
||||
snprintf(loadpath, sizeof(loadpath), "/sys/%s/loading", dev);
|
||||
load = open(loadpath, O_WRONLY);
|
||||
if (!load) {
|
||||
ERROR("Failed to open %s\n", loadpath);
|
||||
exit(-1);
|
||||
}
|
||||
if (write(load, "1", 1) == -1) {
|
||||
ERROR("Failed to write to %s\n", loadpath);
|
||||
exit(-1);
|
||||
}
|
||||
close(load);
|
||||
|
||||
snprintf(syspath, sizeof(syspath), "/sys/%s/data", dev);
|
||||
fw = open(syspath, O_WRONLY);
|
||||
if (fw < 0) {
|
||||
ERROR("Failed to open %s\n", syspath);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
len = s.st_size;
|
||||
while (len) {
|
||||
len = read(src, buf, sizeof(buf));
|
||||
if (len <= 0)
|
||||
break;
|
||||
|
||||
if (write(fw, buf, len) == -1) {
|
||||
ERROR("failed to write firmware file %s/%s to %s\n", dir, file, dev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (src >= 0)
|
||||
close(src);
|
||||
close(fw);
|
||||
|
||||
load = open(loadpath, O_WRONLY);
|
||||
if (write(load, "0", 1) == -1)
|
||||
ERROR("failed to write to %s\n", loadpath);
|
||||
close(load);
|
||||
|
||||
DEBUG(2, "Done loading %s\n", path);
|
||||
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
enum {
|
||||
HANDLER_MKDEV = 0,
|
||||
HANDLER_RM,
|
||||
HANDLER_EXEC,
|
||||
HANDLER_BUTTON,
|
||||
HANDLER_FW,
|
||||
};
|
||||
|
||||
static struct cmd_handler {
|
||||
char *name;
|
||||
int atomic;
|
||||
void (*handler)(struct blob_attr *msg, struct blob_attr *data);
|
||||
void (*start)(struct blob_attr *msg, struct blob_attr *data);
|
||||
void (*complete)(struct blob_attr *msg, struct blob_attr *data, int ret);
|
||||
} handlers[] = {
|
||||
[HANDLER_MKDEV] = {
|
||||
.name = "makedev",
|
||||
.atomic = 1,
|
||||
.handler = handle_makedev,
|
||||
},
|
||||
[HANDLER_RM] = {
|
||||
.name = "rm",
|
||||
.atomic = 1,
|
||||
.handler = handle_rm,
|
||||
},
|
||||
[HANDLER_EXEC] = {
|
||||
.name = "exec",
|
||||
.handler = handle_exec,
|
||||
},
|
||||
[HANDLER_BUTTON] = {
|
||||
.name = "button",
|
||||
.handler = handle_exec,
|
||||
.start = handle_button_start,
|
||||
.complete = handle_button_complete,
|
||||
},
|
||||
[HANDLER_FW] = {
|
||||
.name = "load-firmware",
|
||||
.handler = handle_firmware,
|
||||
},
|
||||
};
|
||||
|
||||
static void queue_next(void)
|
||||
{
|
||||
struct cmd_queue *c;
|
||||
|
||||
if (queue_proc.pending || list_empty(&cmd_queue))
|
||||
return;
|
||||
|
||||
c = list_first_entry(&cmd_queue, struct cmd_queue, list);
|
||||
|
||||
queue_proc.pid = fork();
|
||||
if (!queue_proc.pid) {
|
||||
uloop_done();
|
||||
c->handler(c->msg, c->data);
|
||||
exit(0);
|
||||
}
|
||||
if (c->start)
|
||||
c->start(c->msg, c->data);
|
||||
list_del(&c->list);
|
||||
if (c->complete)
|
||||
current = c;
|
||||
else
|
||||
free(c);
|
||||
if (queue_proc.pid <= 0) {
|
||||
queue_next();
|
||||
return;
|
||||
}
|
||||
|
||||
uloop_process_add(&queue_proc);
|
||||
|
||||
DEBUG(4, "Launched hotplug exec instance, pid=%d\n", (int) queue_proc.pid);
|
||||
}
|
||||
|
||||
static void queue_proc_cb(struct uloop_process *c, int ret)
|
||||
{
|
||||
DEBUG(4, "Finished hotplug exec instance, pid=%d\n", (int) c->pid);
|
||||
|
||||
if (current) {
|
||||
current->complete(current->msg, current->data, ret);
|
||||
free(current);
|
||||
current = NULL;
|
||||
}
|
||||
queue_next();
|
||||
}
|
||||
|
||||
static void queue_add(struct cmd_handler *h, struct blob_attr *msg, struct blob_attr *data)
|
||||
{
|
||||
struct cmd_queue *c = NULL;
|
||||
struct blob_attr *_msg, *_data;
|
||||
|
||||
c = calloc_a(sizeof(struct cmd_queue),
|
||||
&_msg, blob_pad_len(msg),
|
||||
&_data, blob_pad_len(data),
|
||||
NULL);
|
||||
|
||||
c->msg = _msg;
|
||||
c->data = _data;
|
||||
|
||||
if (!c)
|
||||
return;
|
||||
|
||||
memcpy(c->msg, msg, blob_pad_len(msg));
|
||||
memcpy(c->data, data, blob_pad_len(data));
|
||||
c->handler = h->handler;
|
||||
c->complete = h->complete;
|
||||
c->start = h->start;
|
||||
list_add_tail(&c->list, &cmd_queue);
|
||||
queue_next();
|
||||
}
|
||||
|
||||
static void handle_button_timeout(struct uloop_timeout *t)
|
||||
{
|
||||
struct button_timeout *b;
|
||||
char seen[16];
|
||||
|
||||
b = container_of(t, struct button_timeout, timeout);
|
||||
blob_buf_init(&button_buf, 0);
|
||||
blobmsg_add_string(&button_buf, "BUTTON", b->name);
|
||||
blobmsg_add_string(&button_buf, "ACTION", "timeout");
|
||||
snprintf(seen, sizeof(seen), "%d", b->seen);
|
||||
blobmsg_add_string(&button_buf, "SEEN", seen);
|
||||
queue_add(&handlers[HANDLER_EXEC], button_buf.head, b->data);
|
||||
button_free(b);
|
||||
}
|
||||
|
||||
static void handle_button_complete(struct blob_attr *msg, struct blob_attr *data, int ret)
|
||||
{
|
||||
char *name = hotplug_msg_find_var(msg, "BUTTON");
|
||||
struct button_timeout *b;
|
||||
int timeout = ret >> 8;
|
||||
|
||||
if (!timeout)
|
||||
return;
|
||||
|
||||
b = malloc(sizeof(*b));
|
||||
if (!b || !name)
|
||||
return;
|
||||
|
||||
memset(b, 0, sizeof(*b));
|
||||
|
||||
b->data = malloc(blob_pad_len(data));
|
||||
b->name = strdup(name);
|
||||
b->seen = timeout;
|
||||
|
||||
memcpy(b->data, data, blob_pad_len(data));
|
||||
b->timeout.cb = handle_button_timeout;
|
||||
|
||||
uloop_timeout_set(&b->timeout, timeout * 1000);
|
||||
list_add(&b->list, &button_timer);
|
||||
}
|
||||
|
||||
static const char* rule_handle_var(struct json_script_ctx *ctx, const char *name, struct blob_attr *vars)
|
||||
{
|
||||
const char *str, *sep;
|
||||
|
||||
if (!strcmp(name, "DEVICENAME") || !strcmp(name, "DEVNAME")) {
|
||||
str = json_script_find_var(ctx, vars, "DEVPATH");
|
||||
if (!str)
|
||||
return NULL;
|
||||
|
||||
sep = strrchr(str, '/');
|
||||
if (sep)
|
||||
return sep + 1;
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct json_script_file *
|
||||
rule_handle_file(struct json_script_ctx *ctx, const char *name)
|
||||
{
|
||||
json_object *obj;
|
||||
|
||||
obj = json_object_from_file((char*)name);
|
||||
if (!obj)
|
||||
return NULL;
|
||||
|
||||
blob_buf_init(&script, 0);
|
||||
blobmsg_add_json_element(&script, "", obj);
|
||||
|
||||
return json_script_file_from_blobmsg(name, blob_data(script.head), blob_len(script.head));
|
||||
}
|
||||
|
||||
static void rule_handle_command(struct json_script_ctx *ctx, const char *name,
|
||||
struct blob_attr *data, struct blob_attr *vars)
|
||||
{
|
||||
struct blob_attr *cur;
|
||||
int rem, i;
|
||||
|
||||
if (debug > 3) {
|
||||
DEBUG(4, "Command: %s", name);
|
||||
blobmsg_for_each_attr(cur, data, rem)
|
||||
DEBUG(4, " %s", (char *) blobmsg_data(cur));
|
||||
DEBUG(4, "\n");
|
||||
|
||||
DEBUG(4, "Message:");
|
||||
blobmsg_for_each_attr(cur, vars, rem)
|
||||
DEBUG(4, " %s=%s", blobmsg_name(cur), (char *) blobmsg_data(cur));
|
||||
DEBUG(4, "\n");
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(handlers); i++)
|
||||
if (!strcmp(handlers[i].name, name)) {
|
||||
if (handlers[i].atomic)
|
||||
handlers[i].handler(vars, data);
|
||||
else
|
||||
queue_add(&handlers[i], vars, data);
|
||||
break;
|
||||
}
|
||||
|
||||
if (last_event.cb)
|
||||
uloop_timeout_set(&last_event, HOTPLUG_WAIT);
|
||||
}
|
||||
|
||||
static void rule_handle_error(struct json_script_ctx *ctx, const char *msg,
|
||||
struct blob_attr *context)
|
||||
{
|
||||
char *s;
|
||||
|
||||
s = blobmsg_format_json(context, false);
|
||||
ERROR("ERROR: %s in block: %s\n", msg, s);
|
||||
free(s);
|
||||
}
|
||||
|
||||
static struct json_script_ctx jctx = {
|
||||
.handle_var = rule_handle_var,
|
||||
.handle_error = rule_handle_error,
|
||||
.handle_command = rule_handle_command,
|
||||
.handle_file = rule_handle_file,
|
||||
};
|
||||
|
||||
static void hotplug_handler_debug(struct blob_attr *data)
|
||||
{
|
||||
char *str;
|
||||
|
||||
if (debug < 3)
|
||||
return;
|
||||
|
||||
str = blobmsg_format_json(data, true);
|
||||
DEBUG(3, "%s\n", str);
|
||||
free(str);
|
||||
}
|
||||
|
||||
static void hotplug_handler(struct uloop_fd *u, unsigned int ev)
|
||||
{
|
||||
int i = 0;
|
||||
static char buf[4096];
|
||||
int len = recv(u->fd, buf, sizeof(buf), MSG_DONTWAIT);
|
||||
void *index;
|
||||
if (len < 1)
|
||||
return;
|
||||
|
||||
blob_buf_init(&b, 0);
|
||||
index = blobmsg_open_table(&b, NULL);
|
||||
while (i < len) {
|
||||
int l = strlen(buf + i) + 1;
|
||||
char *e = strstr(&buf[i], "=");
|
||||
|
||||
if (e) {
|
||||
*e = '\0';
|
||||
blobmsg_add_string(&b, &buf[i], &e[1]);
|
||||
}
|
||||
i += l;
|
||||
}
|
||||
blobmsg_close_table(&b, index);
|
||||
hotplug_handler_debug(b.head);
|
||||
json_script_run(&jctx, rule_file, blob_data(b.head));
|
||||
}
|
||||
|
||||
static struct uloop_fd hotplug_fd = {
|
||||
.cb = hotplug_handler,
|
||||
};
|
||||
|
||||
void hotplug_last_event(uloop_timeout_handler handler)
|
||||
{
|
||||
last_event.cb = handler;
|
||||
if (handler)
|
||||
uloop_timeout_set(&last_event, HOTPLUG_WAIT);
|
||||
else
|
||||
uloop_timeout_cancel(&last_event);
|
||||
}
|
||||
|
||||
void hotplug(char *rules)
|
||||
{
|
||||
struct sockaddr_nl nls;
|
||||
int nlbufsize = 512 * 1024;
|
||||
|
||||
rule_file = strdup(rules);
|
||||
memset(&nls,0,sizeof(struct sockaddr_nl));
|
||||
nls.nl_family = AF_NETLINK;
|
||||
nls.nl_pid = getpid();
|
||||
nls.nl_groups = -1;
|
||||
|
||||
if ((hotplug_fd.fd = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT)) == -1) {
|
||||
ERROR("Failed to open hotplug socket: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
if (bind(hotplug_fd.fd, (void *)&nls, sizeof(struct sockaddr_nl))) {
|
||||
ERROR("Failed to bind hotplug socket: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (setsockopt(hotplug_fd.fd, SOL_SOCKET, SO_RCVBUFFORCE, &nlbufsize, sizeof(nlbufsize)))
|
||||
ERROR("Failed to resize receive buffer: %s\n", strerror(errno));
|
||||
|
||||
json_script_init(&jctx);
|
||||
queue_proc.cb = queue_proc_cb;
|
||||
uloop_fd_add(&hotplug_fd, ULOOP_READ);
|
||||
}
|
||||
|
||||
int hotplug_run(char *rules)
|
||||
{
|
||||
uloop_init();
|
||||
hotplug(rules);
|
||||
uloop_run();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hotplug_shutdown(void)
|
||||
{
|
||||
uloop_fd_delete(&hotplug_fd);
|
||||
close(hotplug_fd.fd);
|
||||
}
|
||||
49
src/3P/procd/plug/hotplug.h
Normal file
49
src/3P/procd/plug/hotplug.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
|
||||
* Copyright (C) 2013 John Crispin <blogic@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __PROCD_HOTPLUG_H
|
||||
#define __PROCD_HOTPLUG_H
|
||||
|
||||
#include <libubox/uloop.h>
|
||||
|
||||
#ifndef DISABLE_INIT
|
||||
void hotplug(char *rules);
|
||||
int hotplug_run(char *rules);
|
||||
void hotplug_shutdown(void);
|
||||
void hotplug_last_event(uloop_timeout_handler handler);
|
||||
void procd_coldplug(void);
|
||||
#else
|
||||
static inline void hotplug(char *rules)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int hotplug_run(char *rules)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void hotplug_shutdown(void)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void hotplug_last_event(uloop_timeout_handler handler)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void procd_coldplug(void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
264
src/3P/procd/plug/udevtrigger.c
Normal file
264
src/3P/procd/plug/udevtrigger.c
Normal file
@@ -0,0 +1,264 @@
|
||||
/*
|
||||
* Copyright (C) 2004-2006 Kay Sievers <kay@vrfy.org>
|
||||
* Copyright (C) 2006 Hannes Reinecke <hare@suse.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <errno.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <syslog.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
|
||||
#define PATH_SIZE 512
|
||||
|
||||
#ifndef strlcpy
|
||||
#define strlcpy(d,s,l) (strncpy(d,s,l), (d)[(l)-1] = '\0')
|
||||
#endif
|
||||
|
||||
#ifndef strlcat
|
||||
#define strlcat(d,s,l) strncat(d,s,(l)-strlen(d)-1)
|
||||
#endif
|
||||
|
||||
static int verbose;
|
||||
static int dry_run;
|
||||
|
||||
static void log_message(int priority, const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, format);
|
||||
vsyslog(priority, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
#undef err
|
||||
#define err(format, arg...) \
|
||||
do { \
|
||||
log_message(LOG_ERR ,"%s: " format ,__FUNCTION__ ,## arg); \
|
||||
} while (0)
|
||||
|
||||
#undef info
|
||||
#define info(format, arg...) \
|
||||
do { \
|
||||
log_message(LOG_INFO ,"%s: " format ,__FUNCTION__ ,## arg); \
|
||||
} while (0)
|
||||
|
||||
#ifdef DEBUG
|
||||
#undef dbg
|
||||
#define dbg(format, arg...) \
|
||||
do { \
|
||||
log_message(LOG_DEBUG ,"%s: " format ,__FUNCTION__ ,## arg); \
|
||||
} while (0)
|
||||
#else
|
||||
#define dbg(...) do {} while(0)
|
||||
#endif
|
||||
|
||||
|
||||
static void trigger_uevent(const char *devpath)
|
||||
{
|
||||
char filename[PATH_SIZE];
|
||||
int fd;
|
||||
|
||||
strlcpy(filename, "/sys", sizeof(filename));
|
||||
strlcat(filename, devpath, sizeof(filename));
|
||||
strlcat(filename, "/uevent", sizeof(filename));
|
||||
|
||||
if (verbose)
|
||||
printf("%s\n", devpath);
|
||||
|
||||
if (dry_run)
|
||||
return;
|
||||
|
||||
fd = open(filename, O_WRONLY);
|
||||
if (fd < 0) {
|
||||
dbg("error on opening %s: %s\n", filename, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
if (write(fd, "add", 3) < 0)
|
||||
info("error on triggering %s: %s\n", filename, strerror(errno));
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static int sysfs_resolve_link(char *devpath, size_t size)
|
||||
{
|
||||
char link_path[PATH_SIZE];
|
||||
char link_target[PATH_SIZE];
|
||||
int len;
|
||||
int i;
|
||||
int back;
|
||||
|
||||
strlcpy(link_path, "/sys", sizeof(link_path));
|
||||
strlcat(link_path, devpath, sizeof(link_path));
|
||||
len = readlink(link_path, link_target, sizeof(link_target) - 1);
|
||||
if (len <= 0)
|
||||
return -1;
|
||||
link_target[len] = '\0';
|
||||
dbg("path link '%s' points to '%s'", devpath, link_target);
|
||||
|
||||
for (back = 0; strncmp(&link_target[back * 3], "../", 3) == 0; back++)
|
||||
;
|
||||
dbg("base '%s', tail '%s', back %i", devpath, &link_target[back * 3], back);
|
||||
for (i = 0; i <= back; i++) {
|
||||
char *pos = strrchr(devpath, '/');
|
||||
|
||||
if (pos == NULL)
|
||||
return -1;
|
||||
pos[0] = '\0';
|
||||
}
|
||||
dbg("after moving back '%s'", devpath);
|
||||
strlcat(devpath, "/", size);
|
||||
strlcat(devpath, &link_target[back * 3], size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool device_has_attribute(const char *path, const char *attr,
|
||||
mode_t mode)
|
||||
{
|
||||
char filename[PATH_SIZE];
|
||||
struct stat statbuf;
|
||||
|
||||
strlcpy(filename, path, sizeof(filename));
|
||||
strlcat(filename, attr, sizeof(filename));
|
||||
|
||||
if (stat(filename, &statbuf) < 0)
|
||||
return false;
|
||||
|
||||
if (!(statbuf.st_mode & mode))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int device_list_insert(const char *path)
|
||||
{
|
||||
char devpath[PATH_SIZE];
|
||||
struct stat statbuf;
|
||||
|
||||
dbg("add '%s'" , path);
|
||||
|
||||
/* we only have a device, if we have a dev and an uevent file */
|
||||
if (!device_has_attribute(path, "/dev", S_IRUSR) ||
|
||||
!device_has_attribute(path, "/uevent", S_IWUSR))
|
||||
return -1;
|
||||
|
||||
strlcpy(devpath, &path[4], sizeof(devpath));
|
||||
|
||||
/* resolve possible link to real target */
|
||||
if (lstat(path, &statbuf) < 0)
|
||||
return -1;
|
||||
if (S_ISLNK(statbuf.st_mode))
|
||||
if (sysfs_resolve_link(devpath, sizeof(devpath)) != 0)
|
||||
return -1;
|
||||
|
||||
trigger_uevent(devpath);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void scan_subdir(const char *base, const char *subdir,
|
||||
bool insert, int depth)
|
||||
{
|
||||
DIR *dir;
|
||||
struct dirent *dent;
|
||||
|
||||
dir = opendir(base);
|
||||
if (dir == NULL)
|
||||
return;
|
||||
|
||||
for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
|
||||
char dirname[PATH_SIZE];
|
||||
|
||||
if (dent->d_name[0] == '.')
|
||||
continue;
|
||||
|
||||
strlcpy(dirname, base, sizeof(dirname));
|
||||
strlcat(dirname, "/", sizeof(dirname));
|
||||
strlcat(dirname, dent->d_name, sizeof(dirname));
|
||||
|
||||
if (insert) {
|
||||
int err;
|
||||
|
||||
err = device_list_insert(dirname);
|
||||
if (err)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (subdir)
|
||||
strlcat(dirname, subdir, sizeof(base));
|
||||
|
||||
if (depth)
|
||||
scan_subdir(dirname, NULL, true, depth - 1);
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[], char *envp[])
|
||||
{
|
||||
struct stat statbuf;
|
||||
int option;
|
||||
|
||||
openlog("udevtrigger", LOG_PID | LOG_CONS, LOG_DAEMON);
|
||||
|
||||
while (1) {
|
||||
option = getopt(argc, argv, "vnh");
|
||||
if (option == -1)
|
||||
break;
|
||||
|
||||
switch (option) {
|
||||
case 'v':
|
||||
verbose = 1;
|
||||
break;
|
||||
case 'n':
|
||||
dry_run = 1;
|
||||
break;
|
||||
case 'h':
|
||||
printf("Usage: udevtrigger OPTIONS\n"
|
||||
" -v print the list of devices while running\n"
|
||||
" -n do not actually trigger the events\n"
|
||||
" -h print this text\n"
|
||||
"\n");
|
||||
goto exit;
|
||||
default:
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* if we have /sys/subsystem, forget all the old stuff */
|
||||
scan_subdir("/sys/bus", "/devices", false, 1);
|
||||
scan_subdir("/sys/class", NULL, false, 1);
|
||||
|
||||
/* scan "block" if it isn't a "class" */
|
||||
if (stat("/sys/class/block", &statbuf) != 0)
|
||||
scan_subdir("/sys/block", NULL, true, 1);
|
||||
|
||||
exit:
|
||||
|
||||
closelog();
|
||||
return 0;
|
||||
}
|
||||
56
src/3P/procd/preload.h
Normal file
56
src/3P/procd/preload.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2015 John Crispin <blogic@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <link.h>
|
||||
|
||||
#ifndef __unbounded
|
||||
#define __unbounded
|
||||
#endif
|
||||
|
||||
#ifndef attribute_unused
|
||||
#define attribute_unused __attribute__ ((unused))
|
||||
#endif
|
||||
typedef int (*main_t)(int, char **, char **);
|
||||
|
||||
typedef int (*start_main_t)(main_t main, int, char *__unbounded *__unbounded,
|
||||
ElfW(auxv_t) *,
|
||||
__typeof (main),
|
||||
void (*fini) (void),
|
||||
void (*rtld_fini) (void),
|
||||
void *__unbounded stack_end);
|
||||
|
||||
int __libc_start_main(main_t main,
|
||||
int argc,
|
||||
char **argv,
|
||||
ElfW(auxv_t) *auxvec,
|
||||
__typeof (main) init,
|
||||
void (*fini) (void),
|
||||
void (*rtld_fini) (void),
|
||||
void *stack_end);
|
||||
|
||||
|
||||
typedef void (*uClibc_main)(main_t main,
|
||||
int argc,
|
||||
char **argv,
|
||||
void (*app_init)(void),
|
||||
void (*app_fini)(void),
|
||||
void (*rtld_fini)(void),
|
||||
void *stack_end attribute_unused);
|
||||
|
||||
void __uClibc_main(main_t main,
|
||||
int argc,
|
||||
char **argv,
|
||||
void (*app_init)(void),
|
||||
void (*app_fini)(void),
|
||||
void (*rtld_fini)(void),
|
||||
void *stack_end attribute_unused);
|
||||
84
src/3P/procd/procd.c
Normal file
84
src/3P/procd/procd.c
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
|
||||
* Copyright (C) 2013 John Crispin <blogic@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <sys/wait.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/reboot.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <libgen.h>
|
||||
|
||||
#include "procd.h"
|
||||
#include "watchdog.h"
|
||||
#include "plug/hotplug.h"
|
||||
|
||||
unsigned int debug;
|
||||
|
||||
static int usage(const char *prog)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [options]\n"
|
||||
"Options:\n"
|
||||
" -s <path> Path to ubus socket\n"
|
||||
" -h <path> run as hotplug daemon\n"
|
||||
" -d <level> Enable debug messages\n"
|
||||
" -S Print messages to stdout\n"
|
||||
"\n", prog);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int ch;
|
||||
char *dbglvl = getenv("DBGLVL");
|
||||
int ulog_channels = ULOG_KMSG;
|
||||
|
||||
if (dbglvl) {
|
||||
debug = atoi(dbglvl);
|
||||
unsetenv("DBGLVL");
|
||||
}
|
||||
|
||||
while ((ch = getopt(argc, argv, "d:s:h:S")) != -1) {
|
||||
switch (ch) {
|
||||
case 'h':
|
||||
return hotplug_run(optarg);
|
||||
case 's':
|
||||
ubus_socket = optarg;
|
||||
break;
|
||||
case 'd':
|
||||
debug = atoi(optarg);
|
||||
break;
|
||||
case 'S':
|
||||
ulog_channels = ULOG_STDIO;
|
||||
break;
|
||||
default:
|
||||
return usage(argv[0]);
|
||||
}
|
||||
}
|
||||
|
||||
ulog_open(ulog_channels, LOG_DAEMON, "procd");
|
||||
|
||||
setsid();
|
||||
uloop_init();
|
||||
procd_signal();
|
||||
if (getpid() != 1)
|
||||
procd_connect_ubus();
|
||||
else
|
||||
procd_state_next();
|
||||
uloop_run();
|
||||
uloop_done();
|
||||
|
||||
return 0;
|
||||
}
|
||||
57
src/3P/procd/procd.h
Normal file
57
src/3P/procd/procd.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
|
||||
* Copyright (C) 2013 John Crispin <blogic@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __PROCD_H
|
||||
#define __PROCD_H
|
||||
|
||||
#include <libubox/uloop.h>
|
||||
#include <libubox/utils.h>
|
||||
#include <ubus/libubus.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include "log.h"
|
||||
|
||||
#define __init __attribute__((constructor))
|
||||
|
||||
extern char *ubus_socket;
|
||||
extern int upgrade_running;
|
||||
|
||||
void procd_connect_ubus(void);
|
||||
void procd_reconnect_ubus(int reconnect);
|
||||
void ubus_init_service(struct ubus_context *ctx);
|
||||
void ubus_init_system(struct ubus_context *ctx);
|
||||
|
||||
void procd_state_next(void);
|
||||
void procd_state_ubus_connect(void);
|
||||
void procd_shutdown(int event);
|
||||
void procd_early(void);
|
||||
void procd_preinit(void);
|
||||
void procd_signal(void);
|
||||
void procd_signal_preinit(void);
|
||||
void procd_inittab(void);
|
||||
void procd_inittab_run(const char *action);
|
||||
void procd_bcast_event(char *event, struct blob_attr *msg);
|
||||
|
||||
struct trigger;
|
||||
void trigger_event(const char *type, struct blob_attr *data);
|
||||
void trigger_add(struct blob_attr *rule, void *id);
|
||||
void trigger_del(void *id);
|
||||
|
||||
void watch_add(const char *_name, void *id);
|
||||
void watch_del(void *id);
|
||||
void watch_ubus(struct ubus_context *ctx);
|
||||
|
||||
#endif
|
||||
182
src/3P/procd/rcS.c
Normal file
182
src/3P/procd/rcS.c
Normal file
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* runqueue-example.c
|
||||
*
|
||||
* Copyright (C) 2013 Felix Fietkau <nbd@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 <libubox/uloop.h>
|
||||
#include <libubox/runqueue.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <glob.h>
|
||||
|
||||
#include <libubox/ustream.h>
|
||||
|
||||
#include "procd.h"
|
||||
#include "rcS.h"
|
||||
|
||||
static struct runqueue q, r;
|
||||
|
||||
struct initd {
|
||||
struct ustream_fd fd;
|
||||
struct runqueue_process proc;
|
||||
char *file;
|
||||
char *param;
|
||||
};
|
||||
|
||||
static void pipe_cb(struct ustream *s, int bytes)
|
||||
{
|
||||
char *newline, *str;
|
||||
int len;
|
||||
|
||||
do {
|
||||
str = ustream_get_read_buf(s, NULL);
|
||||
if (!str)
|
||||
break;
|
||||
newline = strchr(str, '\n');
|
||||
if (!newline)
|
||||
break;
|
||||
*newline = 0;
|
||||
len = newline + 1 - str;
|
||||
syslog(LOG_NOTICE, "%s", str);
|
||||
#ifdef SHOW_BOOT_ON_CONSOLE
|
||||
fprintf(stderr, "%s\n", str);
|
||||
#endif
|
||||
ustream_consume(s, len);
|
||||
} while (1);
|
||||
}
|
||||
|
||||
static void q_initd_run(struct runqueue *q, struct runqueue_task *t)
|
||||
{
|
||||
struct initd *s = container_of(t, struct initd, proc.task);
|
||||
int pipefd[2];
|
||||
pid_t pid;
|
||||
|
||||
DEBUG(2, "start %s %s \n", s->file, s->param);
|
||||
if (pipe(pipefd) == -1) {
|
||||
ERROR("Failed to create pipe\n");
|
||||
return;
|
||||
}
|
||||
|
||||
pid = fork();
|
||||
if (pid < 0)
|
||||
return;
|
||||
|
||||
if (pid) {
|
||||
close(pipefd[1]);
|
||||
s->fd.stream.string_data = true,
|
||||
s->fd.stream.notify_read = pipe_cb,
|
||||
runqueue_process_add(q, &s->proc, pid);
|
||||
ustream_fd_init(&s->fd, pipefd[0]);
|
||||
return;
|
||||
}
|
||||
close(pipefd[0]);
|
||||
dup2(pipefd[1], STDOUT_FILENO);
|
||||
dup2(pipefd[1], STDERR_FILENO);
|
||||
|
||||
execlp(s->file, s->file, s->param, NULL);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void q_initd_complete(struct runqueue *q, struct runqueue_task *p)
|
||||
{
|
||||
struct initd *s = container_of(p, struct initd, proc.task);
|
||||
|
||||
DEBUG(2, "stop %s %s \n", s->file, s->param);
|
||||
ustream_free(&s->fd.stream);
|
||||
close(s->fd.fd.fd);
|
||||
free(s);
|
||||
}
|
||||
|
||||
static void add_initd(struct runqueue *q, char *file, char *param)
|
||||
{
|
||||
static const struct runqueue_task_type initd_type = {
|
||||
.run = q_initd_run,
|
||||
.cancel = runqueue_process_cancel_cb,
|
||||
.kill = runqueue_process_kill_cb,
|
||||
};
|
||||
struct initd *s;
|
||||
char *p, *f;
|
||||
|
||||
s = calloc_a(sizeof(*s), &f, strlen(file) + 1, &p, strlen(param) + 1);
|
||||
if (!s) {
|
||||
ERROR("Out of memory in %s.\n", file);
|
||||
return;
|
||||
}
|
||||
s->proc.task.type = &initd_type;
|
||||
s->proc.task.complete = q_initd_complete;
|
||||
if (!strcmp(param, "stop") || !strcmp(param, "shutdown"))
|
||||
s->proc.task.run_timeout = 15000;
|
||||
s->param = p;
|
||||
s->file = f;
|
||||
strcpy(s->param, param);
|
||||
strcpy(s->file, file);
|
||||
runqueue_task_add(q, &s->proc.task, false);
|
||||
}
|
||||
|
||||
static int _rc(struct runqueue *q, char *path, const char *file, char *pattern, char *param)
|
||||
{
|
||||
char *dir = alloca(2 + strlen(path) + strlen(file) + strlen(pattern));
|
||||
glob_t gl;
|
||||
int j;
|
||||
|
||||
if (!dir) {
|
||||
ERROR("Out of memory in %s.\n", file);
|
||||
return -1;
|
||||
}
|
||||
|
||||
DEBUG(2, "running %s/%s%s %s\n", path, file, pattern, param);
|
||||
sprintf(dir, "%s/%s%s", path, file, pattern);
|
||||
if (glob(dir, GLOB_NOESCAPE | GLOB_MARK, NULL, &gl)) {
|
||||
DEBUG(2, "glob failed on %s\n", dir);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (j = 0; j < gl.gl_pathc; j++)
|
||||
add_initd(q, gl.gl_pathv[j], param);
|
||||
|
||||
globfree(&gl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rcS(char *pattern, char *param, void (*q_empty)(struct runqueue *))
|
||||
{
|
||||
runqueue_init(&q);
|
||||
q.empty_cb = q_empty;
|
||||
q.max_running_tasks = 1;
|
||||
|
||||
return _rc(&q, "/etc/rc.d", pattern, "*", param);
|
||||
}
|
||||
|
||||
int rc(const char *file, char *param)
|
||||
{
|
||||
return _rc(&r, "/etc/init.d", file, "", param);
|
||||
}
|
||||
|
||||
static void r_empty(struct runqueue *q)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void __attribute__((constructor)) rc_init() {
|
||||
runqueue_init(&r);
|
||||
r.empty_cb = r_empty;
|
||||
r.max_running_tasks = 8;
|
||||
}
|
||||
23
src/3P/procd/rcS.h
Normal file
23
src/3P/procd/rcS.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
|
||||
* Copyright (C) 2013 John Crispin <blogic@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __PROCD_RCS_H
|
||||
#define __PROCD_RCS_H
|
||||
|
||||
#include <libubox/runqueue.h>
|
||||
|
||||
extern int rcS(char *pattern, char *param, void (*q_empty)(struct runqueue *));
|
||||
extern int rc(const char *file, char *param);
|
||||
|
||||
#endif
|
||||
1061
src/3P/procd/service/instance.c
Normal file
1061
src/3P/procd/service/instance.c
Normal file
File diff suppressed because it is too large
Load Diff
88
src/3P/procd/service/instance.h
Normal file
88
src/3P/procd/service/instance.h
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
|
||||
* Copyright (C) 2013 John Crispin <blogic@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __PROCD_INSTANCE_H
|
||||
#define __PROCD_INSTANCE_H
|
||||
|
||||
#include <libubox/vlist.h>
|
||||
#include <libubox/uloop.h>
|
||||
#include <libubox/ustream.h>
|
||||
#include "../utils/utils.h"
|
||||
|
||||
#define RESPAWN_ERROR (5 * 60)
|
||||
|
||||
struct jail {
|
||||
bool procfs;
|
||||
bool sysfs;
|
||||
bool ubus;
|
||||
bool log;
|
||||
bool ronly;
|
||||
char *name;
|
||||
char *hostname;
|
||||
struct blobmsg_list mount;
|
||||
int argc;
|
||||
};
|
||||
|
||||
struct service_instance {
|
||||
struct vlist_node node;
|
||||
struct service *srv;
|
||||
const char *name;
|
||||
|
||||
int8_t nice;
|
||||
bool valid;
|
||||
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
|
||||
bool halt;
|
||||
bool restart;
|
||||
bool respawn;
|
||||
int respawn_count;
|
||||
struct timespec start;
|
||||
|
||||
bool trace;
|
||||
bool has_jail;
|
||||
bool no_new_privs;
|
||||
struct jail jail;
|
||||
char *seccomp;
|
||||
char *pidfile;
|
||||
|
||||
uint32_t respawn_timeout;
|
||||
uint32_t respawn_threshold;
|
||||
uint32_t respawn_retry;
|
||||
|
||||
struct blob_attr *config;
|
||||
struct uloop_process proc;
|
||||
struct uloop_timeout timeout;
|
||||
struct ustream_fd _stdout;
|
||||
struct ustream_fd _stderr;
|
||||
|
||||
struct blob_attr *command;
|
||||
struct blob_attr *trigger;
|
||||
struct blobmsg_list env;
|
||||
struct blobmsg_list data;
|
||||
struct blobmsg_list netdev;
|
||||
struct blobmsg_list file;
|
||||
struct blobmsg_list limits;
|
||||
struct blobmsg_list errors;
|
||||
};
|
||||
|
||||
void instance_start(struct service_instance *in);
|
||||
void instance_stop(struct service_instance *in);
|
||||
bool instance_update(struct service_instance *in, struct service_instance *in_new);
|
||||
void instance_init(struct service_instance *in, struct service *s, struct blob_attr *config);
|
||||
void instance_free(struct service_instance *in);
|
||||
void instance_dump(struct blob_buf *b, struct service_instance *in, int debug);
|
||||
|
||||
#endif
|
||||
554
src/3P/procd/service/service.c
Normal file
554
src/3P/procd/service/service.c
Normal file
@@ -0,0 +1,554 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
|
||||
* Copyright (C) 2013 John Crispin <blogic@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <libubox/blobmsg_json.h>
|
||||
#include <libubox/avl-cmp.h>
|
||||
|
||||
#include "../procd.h"
|
||||
|
||||
#include "service.h"
|
||||
#include "instance.h"
|
||||
|
||||
#include "../rcS.h"
|
||||
|
||||
AVL_TREE(services, avl_strcmp, false, NULL);
|
||||
static struct blob_buf b;
|
||||
static struct ubus_context *ctx;
|
||||
|
||||
static void
|
||||
service_instance_add(struct service *s, struct blob_attr *attr)
|
||||
{
|
||||
struct service_instance *in;
|
||||
|
||||
if (blobmsg_type(attr) != BLOBMSG_TYPE_TABLE)
|
||||
return;
|
||||
|
||||
in = calloc(1, sizeof(*in));
|
||||
if (!in)
|
||||
return;
|
||||
|
||||
instance_init(in, s, attr);
|
||||
vlist_add(&s->instances, &in->node, (void *) in->name);
|
||||
}
|
||||
|
||||
static void
|
||||
service_instance_update(struct vlist_tree *tree, struct vlist_node *node_new,
|
||||
struct vlist_node *node_old)
|
||||
{
|
||||
struct service_instance *in_o = NULL, *in_n = NULL;
|
||||
|
||||
if (node_old)
|
||||
in_o = container_of(node_old, struct service_instance, node);
|
||||
|
||||
if (node_new)
|
||||
in_n = container_of(node_new, struct service_instance, node);
|
||||
|
||||
if (in_o && in_n) {
|
||||
DEBUG(2, "Update instance %s::%s\n", in_o->srv->name, in_o->name);
|
||||
instance_update(in_o, in_n);
|
||||
instance_free(in_n);
|
||||
} else if (in_o) {
|
||||
DEBUG(2, "Free instance %s::%s\n", in_o->srv->name, in_o->name);
|
||||
instance_stop(in_o);
|
||||
instance_free(in_o);
|
||||
} else if (in_n) {
|
||||
DEBUG(2, "Create instance %s::%s\n", in_n->srv->name, in_n->name);
|
||||
instance_start(in_n);
|
||||
}
|
||||
blob_buf_init(&b, 0);
|
||||
trigger_event("instance.update", b.head);
|
||||
}
|
||||
|
||||
static struct service *
|
||||
service_alloc(const char *name)
|
||||
{
|
||||
struct service *s;
|
||||
char *new_name;
|
||||
|
||||
s = calloc_a(sizeof(*s), &new_name, strlen(name) + 1);
|
||||
strcpy(new_name, name);
|
||||
|
||||
vlist_init(&s->instances, avl_strcmp, service_instance_update);
|
||||
s->instances.keep_old = true;
|
||||
s->name = new_name;
|
||||
s->avl.key = s->name;
|
||||
INIT_LIST_HEAD(&s->validators);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
enum {
|
||||
SERVICE_SET_NAME,
|
||||
SERVICE_SET_SCRIPT,
|
||||
SERVICE_SET_INSTANCES,
|
||||
SERVICE_SET_TRIGGER,
|
||||
SERVICE_SET_VALIDATE,
|
||||
__SERVICE_SET_MAX
|
||||
};
|
||||
|
||||
static const struct blobmsg_policy service_set_attrs[__SERVICE_SET_MAX] = {
|
||||
[SERVICE_SET_NAME] = { "name", BLOBMSG_TYPE_STRING },
|
||||
[SERVICE_SET_SCRIPT] = { "script", BLOBMSG_TYPE_STRING },
|
||||
[SERVICE_SET_INSTANCES] = { "instances", BLOBMSG_TYPE_TABLE },
|
||||
[SERVICE_SET_TRIGGER] = { "triggers", BLOBMSG_TYPE_ARRAY },
|
||||
[SERVICE_SET_VALIDATE] = { "validate", BLOBMSG_TYPE_ARRAY },
|
||||
};
|
||||
|
||||
static int
|
||||
service_update(struct service *s, struct blob_attr **tb, bool add)
|
||||
{
|
||||
struct blob_attr *cur;
|
||||
int rem;
|
||||
|
||||
if (s->trigger) {
|
||||
trigger_del(s);
|
||||
free(s->trigger);
|
||||
s->trigger = NULL;
|
||||
}
|
||||
|
||||
service_validate_del(s);
|
||||
|
||||
if (tb[SERVICE_SET_TRIGGER] && blobmsg_data_len(tb[SERVICE_SET_TRIGGER])) {
|
||||
s->trigger = blob_memdup(tb[SERVICE_SET_TRIGGER]);
|
||||
if (!s->trigger)
|
||||
return -1;
|
||||
trigger_add(s->trigger, s);
|
||||
}
|
||||
|
||||
if (tb[SERVICE_SET_VALIDATE] && blobmsg_data_len(tb[SERVICE_SET_VALIDATE])) {
|
||||
blobmsg_for_each_attr(cur, tb[SERVICE_SET_VALIDATE], rem)
|
||||
service_validate_add(s, cur);
|
||||
}
|
||||
|
||||
if (tb[SERVICE_SET_INSTANCES]) {
|
||||
if (!add)
|
||||
vlist_update(&s->instances);
|
||||
blobmsg_for_each_attr(cur, tb[SERVICE_SET_INSTANCES], rem) {
|
||||
service_instance_add(s, cur);
|
||||
}
|
||||
if (!add)
|
||||
vlist_flush(&s->instances);
|
||||
}
|
||||
|
||||
rc(s->name, "running");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
service_delete(struct service *s)
|
||||
{
|
||||
service_event("service.stop", s->name, NULL);
|
||||
vlist_flush_all(&s->instances);
|
||||
avl_delete(&services, &s->avl);
|
||||
trigger_del(s);
|
||||
free(s->trigger);
|
||||
free(s);
|
||||
service_validate_del(s);
|
||||
}
|
||||
|
||||
enum {
|
||||
SERVICE_ATTR_NAME,
|
||||
__SERVICE_ATTR_MAX,
|
||||
};
|
||||
|
||||
static const struct blobmsg_policy service_attrs[__SERVICE_ATTR_MAX] = {
|
||||
[SERVICE_ATTR_NAME] = { "name", BLOBMSG_TYPE_STRING },
|
||||
};
|
||||
|
||||
enum {
|
||||
SERVICE_DEL_ATTR_NAME,
|
||||
SERVICE_DEL_ATTR_INSTANCE,
|
||||
__SERVICE_DEL_ATTR_MAX,
|
||||
};
|
||||
|
||||
static const struct blobmsg_policy service_del_attrs[__SERVICE_DEL_ATTR_MAX] = {
|
||||
[SERVICE_DEL_ATTR_NAME] = { "name", BLOBMSG_TYPE_STRING },
|
||||
[SERVICE_DEL_ATTR_INSTANCE] = { "instance", BLOBMSG_TYPE_STRING },
|
||||
};
|
||||
|
||||
enum {
|
||||
SERVICE_LIST_ATTR_NAME,
|
||||
SERVICE_LIST_ATTR_VERBOSE,
|
||||
__SERVICE_LIST_ATTR_MAX,
|
||||
};
|
||||
|
||||
static const struct blobmsg_policy service_list_attrs[__SERVICE_LIST_ATTR_MAX] = {
|
||||
[SERVICE_LIST_ATTR_NAME] = { "name", BLOBMSG_TYPE_STRING },
|
||||
[SERVICE_LIST_ATTR_VERBOSE] = { "verbose", BLOBMSG_TYPE_BOOL },
|
||||
};
|
||||
|
||||
enum {
|
||||
EVENT_TYPE,
|
||||
EVENT_DATA,
|
||||
__EVENT_MAX
|
||||
};
|
||||
|
||||
static const struct blobmsg_policy event_policy[__EVENT_MAX] = {
|
||||
[EVENT_TYPE] = { .name = "type", .type = BLOBMSG_TYPE_STRING },
|
||||
[EVENT_DATA] = { .name = "data", .type = BLOBMSG_TYPE_TABLE },
|
||||
};
|
||||
|
||||
enum {
|
||||
VALIDATE_PACKAGE,
|
||||
VALIDATE_TYPE,
|
||||
VALIDATE_SERVICE,
|
||||
__VALIDATE_MAX
|
||||
};
|
||||
|
||||
static const struct blobmsg_policy validate_policy[__VALIDATE_MAX] = {
|
||||
[VALIDATE_PACKAGE] = { .name = "package", .type = BLOBMSG_TYPE_STRING },
|
||||
[VALIDATE_TYPE] = { .name = "type", .type = BLOBMSG_TYPE_STRING },
|
||||
[VALIDATE_SERVICE] = { .name = "service", .type = BLOBMSG_TYPE_STRING },
|
||||
};
|
||||
|
||||
enum {
|
||||
DATA_NAME,
|
||||
DATA_INSTANCE,
|
||||
DATA_TYPE,
|
||||
__DATA_MAX
|
||||
};
|
||||
|
||||
static const struct blobmsg_policy get_data_policy[] = {
|
||||
[DATA_NAME] = { "name", BLOBMSG_TYPE_STRING },
|
||||
[DATA_INSTANCE] = { "instance", BLOBMSG_TYPE_STRING },
|
||||
[DATA_TYPE] = { "type", BLOBMSG_TYPE_STRING },
|
||||
};
|
||||
|
||||
static int
|
||||
service_handle_set(struct ubus_context *ctx, struct ubus_object *obj,
|
||||
struct ubus_request_data *req, const char *method,
|
||||
struct blob_attr *msg)
|
||||
{
|
||||
struct blob_attr *tb[__SERVICE_SET_MAX], *cur;
|
||||
struct service *s = NULL;
|
||||
const char *name;
|
||||
bool add = !strcmp(method, "add");
|
||||
int ret;
|
||||
|
||||
blobmsg_parse(service_set_attrs, __SERVICE_SET_MAX, tb, blob_data(msg), blob_len(msg));
|
||||
cur = tb[SERVICE_ATTR_NAME];
|
||||
if (!cur)
|
||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
name = blobmsg_data(cur);
|
||||
|
||||
s = avl_find_element(&services, name, s, avl);
|
||||
if (s) {
|
||||
DEBUG(2, "Update service %s\n", name);
|
||||
return service_update(s, tb, add);
|
||||
}
|
||||
|
||||
DEBUG(2, "Create service %s\n", name);
|
||||
s = service_alloc(name);
|
||||
if (!s)
|
||||
return UBUS_STATUS_UNKNOWN_ERROR;
|
||||
|
||||
ret = service_update(s, tb, add);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
avl_insert(&services, &s->avl);
|
||||
|
||||
service_event("service.start", s->name, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
service_dump(struct service *s, bool verbose)
|
||||
{
|
||||
struct service_instance *in;
|
||||
void *c, *i;
|
||||
|
||||
c = blobmsg_open_table(&b, s->name);
|
||||
|
||||
if (!avl_is_empty(&s->instances.avl)) {
|
||||
i = blobmsg_open_table(&b, "instances");
|
||||
vlist_for_each_element(&s->instances, in, node)
|
||||
instance_dump(&b, in, verbose);
|
||||
blobmsg_close_table(&b, i);
|
||||
}
|
||||
if (verbose && s->trigger)
|
||||
blobmsg_add_blob(&b, s->trigger);
|
||||
if (verbose && !list_empty(&s->validators))
|
||||
service_validate_dump(&b, s);
|
||||
blobmsg_close_table(&b, c);
|
||||
}
|
||||
|
||||
static int
|
||||
service_handle_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[__SERVICE_LIST_ATTR_MAX];
|
||||
struct service *s;
|
||||
const char *name = NULL;
|
||||
bool verbose = false;
|
||||
|
||||
blobmsg_parse(service_list_attrs, __SERVICE_LIST_ATTR_MAX, tb, blob_data(msg), blob_len(msg));
|
||||
|
||||
if (tb[SERVICE_LIST_ATTR_VERBOSE])
|
||||
verbose = blobmsg_get_bool(tb[SERVICE_LIST_ATTR_VERBOSE]);
|
||||
if (tb[SERVICE_LIST_ATTR_NAME])
|
||||
name = blobmsg_get_string(tb[SERVICE_LIST_ATTR_NAME]);
|
||||
|
||||
blob_buf_init(&b, 0);
|
||||
avl_for_each_element(&services, s, avl) {
|
||||
if (name && strcmp(s->name, name) != 0)
|
||||
continue;
|
||||
|
||||
service_dump(s, verbose);
|
||||
}
|
||||
|
||||
ubus_send_reply(ctx, req, b.head);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
service_handle_delete(struct ubus_context *ctx, struct ubus_object *obj,
|
||||
struct ubus_request_data *req, const char *method,
|
||||
struct blob_attr *msg)
|
||||
{
|
||||
struct blob_attr *tb[__SERVICE_DEL_ATTR_MAX], *cur;
|
||||
struct service *s;
|
||||
struct service_instance *in;
|
||||
|
||||
blobmsg_parse(service_del_attrs, __SERVICE_DEL_ATTR_MAX, tb, blob_data(msg), blob_len(msg));
|
||||
|
||||
cur = tb[SERVICE_DEL_ATTR_NAME];
|
||||
if (!cur)
|
||||
return UBUS_STATUS_NOT_FOUND;
|
||||
|
||||
s = avl_find_element(&services, blobmsg_data(cur), s, avl);
|
||||
if (!s)
|
||||
return UBUS_STATUS_NOT_FOUND;
|
||||
|
||||
cur = tb[SERVICE_DEL_ATTR_INSTANCE];
|
||||
if (!cur) {
|
||||
service_delete(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
in = vlist_find(&s->instances, blobmsg_data(cur), in, node);
|
||||
if (!in) {
|
||||
ERROR("instance %s not found\n", (char *) blobmsg_data(cur));
|
||||
return UBUS_STATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
vlist_delete(&s->instances, &in->node);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
service_handle_update(struct ubus_context *ctx, struct ubus_object *obj,
|
||||
struct ubus_request_data *req, const char *method,
|
||||
struct blob_attr *msg)
|
||||
{
|
||||
struct blob_attr *tb[__SERVICE_ATTR_MAX], *cur;
|
||||
struct service *s;
|
||||
|
||||
blobmsg_parse(service_attrs, __SERVICE_ATTR_MAX, tb, blob_data(msg), blob_len(msg));
|
||||
|
||||
cur = tb[SERVICE_ATTR_NAME];
|
||||
if (!cur)
|
||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
s = avl_find_element(&services, blobmsg_data(cur), s, avl);
|
||||
if (!s)
|
||||
return UBUS_STATUS_NOT_FOUND;
|
||||
|
||||
if (!strcmp(method, "update_start"))
|
||||
vlist_update(&s->instances);
|
||||
else
|
||||
vlist_flush(&s->instances);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
service_handle_event(struct ubus_context *ctx, struct ubus_object *obj,
|
||||
struct ubus_request_data *req, const char *method,
|
||||
struct blob_attr *msg)
|
||||
{
|
||||
struct blob_attr *tb[__EVENT_MAX];
|
||||
|
||||
if (!msg)
|
||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
blobmsg_parse(event_policy, __EVENT_MAX, tb, blob_data(msg), blob_len(msg));
|
||||
if (!tb[EVENT_TYPE] || !tb[EVENT_DATA])
|
||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
trigger_event(blobmsg_get_string(tb[EVENT_TYPE]), tb[EVENT_DATA]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
service_handle_validate(struct ubus_context *ctx, struct ubus_object *obj,
|
||||
struct ubus_request_data *req, const char *method,
|
||||
struct blob_attr *msg)
|
||||
{
|
||||
struct blob_attr *tb[__VALIDATE_MAX];
|
||||
char *p = NULL, *t = NULL;
|
||||
|
||||
if (!msg)
|
||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
blobmsg_parse(validate_policy, __VALIDATE_MAX, tb, blob_data(msg), blob_len(msg));
|
||||
if (tb[VALIDATE_SERVICE]) {
|
||||
return 0;
|
||||
}
|
||||
if (tb[VALIDATE_PACKAGE])
|
||||
p = blobmsg_get_string(tb[VALIDATE_PACKAGE]);
|
||||
|
||||
if (tb[VALIDATE_TYPE])
|
||||
t = blobmsg_get_string(tb[VALIDATE_TYPE]);
|
||||
|
||||
blob_buf_init(&b, 0);
|
||||
service_validate_dump_all(&b, p, t);
|
||||
ubus_send_reply(ctx, req, b.head);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
service_get_data(struct ubus_context *ctx, struct ubus_object *obj,
|
||||
struct ubus_request_data *req, const char *method,
|
||||
struct blob_attr *msg)
|
||||
{
|
||||
struct service_instance *in;
|
||||
struct service *s;
|
||||
struct blob_attr *tb[__DATA_MAX];
|
||||
const char *name = NULL;
|
||||
const char *instance = NULL;
|
||||
const char *type = NULL;
|
||||
|
||||
blobmsg_parse(get_data_policy, __DATA_MAX, tb, blob_data(msg), blob_len(msg));
|
||||
if (tb[DATA_NAME])
|
||||
name = blobmsg_data(tb[DATA_NAME]);
|
||||
if (tb[DATA_INSTANCE])
|
||||
instance = blobmsg_data(tb[DATA_INSTANCE]);
|
||||
if (tb[DATA_TYPE])
|
||||
type = blobmsg_data(tb[DATA_TYPE]);
|
||||
|
||||
blob_buf_init(&b, 0);
|
||||
avl_for_each_element(&services, s, avl) {
|
||||
void *cs = NULL;
|
||||
|
||||
if (name && strcmp(name, s->name))
|
||||
continue;
|
||||
|
||||
vlist_for_each_element(&s->instances, in, node) {
|
||||
struct blobmsg_list_node *var;
|
||||
void *ci = NULL;
|
||||
|
||||
if (instance && strcmp(instance, in->name))
|
||||
continue;
|
||||
|
||||
blobmsg_list_for_each(&in->data, var) {
|
||||
if (type &&
|
||||
strcmp(blobmsg_name(var->data), type))
|
||||
continue;
|
||||
|
||||
if (!cs)
|
||||
cs = blobmsg_open_table(&b, s->name);
|
||||
if (!ci)
|
||||
ci = blobmsg_open_table(&b, in->name);
|
||||
|
||||
blobmsg_add_blob(&b, var->data);
|
||||
}
|
||||
|
||||
if (ci)
|
||||
blobmsg_close_table(&b, ci);
|
||||
}
|
||||
|
||||
if (cs)
|
||||
blobmsg_close_table(&b, cs);
|
||||
}
|
||||
|
||||
ubus_send_reply(ctx, req, b.head);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ubus_method main_object_methods[] = {
|
||||
UBUS_METHOD("set", service_handle_set, service_set_attrs),
|
||||
UBUS_METHOD("add", service_handle_set, service_set_attrs),
|
||||
UBUS_METHOD("list", service_handle_list, service_list_attrs),
|
||||
UBUS_METHOD("delete", service_handle_delete, service_del_attrs),
|
||||
UBUS_METHOD("update_start", service_handle_update, service_attrs),
|
||||
UBUS_METHOD("update_complete", service_handle_update, service_attrs),
|
||||
UBUS_METHOD("event", service_handle_event, event_policy),
|
||||
UBUS_METHOD("validate", service_handle_validate, validate_policy),
|
||||
UBUS_METHOD("get_data", service_get_data, get_data_policy),
|
||||
};
|
||||
|
||||
static struct ubus_object_type main_object_type =
|
||||
UBUS_OBJECT_TYPE("service", main_object_methods);
|
||||
|
||||
static struct ubus_object main_object = {
|
||||
.name = "service",
|
||||
.type = &main_object_type,
|
||||
.methods = main_object_methods,
|
||||
.n_methods = ARRAY_SIZE(main_object_methods),
|
||||
};
|
||||
|
||||
int
|
||||
service_start_early(char *name, char *cmdline)
|
||||
{
|
||||
void *instances, *instance, *command, *respawn;
|
||||
char *t;
|
||||
|
||||
blob_buf_init(&b, 0);
|
||||
blobmsg_add_string(&b, "name", name);
|
||||
instances = blobmsg_open_table(&b, "instances");
|
||||
instance = blobmsg_open_table(&b, "instance1");
|
||||
command = blobmsg_open_array(&b, "command");
|
||||
t = strtok(cmdline, " ");
|
||||
while (t) {
|
||||
blobmsg_add_string(&b, NULL, t);
|
||||
t = strtok(NULL, " ");
|
||||
}
|
||||
blobmsg_close_array(&b, command);
|
||||
respawn = blobmsg_open_array(&b, "respawn");
|
||||
blobmsg_add_string(&b, NULL, "3600");
|
||||
blobmsg_add_string(&b, NULL, "1");
|
||||
blobmsg_add_string(&b, NULL, "0");
|
||||
blobmsg_close_array(&b, respawn);
|
||||
blobmsg_close_table(&b, instance);
|
||||
blobmsg_close_table(&b, instances);
|
||||
|
||||
return service_handle_set(NULL, NULL, NULL, "add", b.head);
|
||||
}
|
||||
|
||||
void service_event(const char *type, const char *service, const char *instance)
|
||||
{
|
||||
if (!ctx)
|
||||
return;
|
||||
|
||||
blob_buf_init(&b, 0);
|
||||
blobmsg_add_string(&b, "service", service);
|
||||
if (instance)
|
||||
blobmsg_add_string(&b, "instance", instance);
|
||||
ubus_notify(ctx, &main_object, type, b.head, -1);
|
||||
}
|
||||
|
||||
void ubus_init_service(struct ubus_context *_ctx)
|
||||
{
|
||||
ctx = _ctx;
|
||||
ubus_add_object(ctx, &main_object);
|
||||
}
|
||||
56
src/3P/procd/service/service.h
Normal file
56
src/3P/procd/service/service.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
|
||||
* Copyright (C) 2013 John Crispin <blogic@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __PROCD_SERVICE_H
|
||||
#define __PROCD_SERVICE_H
|
||||
|
||||
#include <libubox/avl.h>
|
||||
#include <libubox/vlist.h>
|
||||
#include <libubox/list.h>
|
||||
|
||||
extern struct avl_tree services;
|
||||
|
||||
struct vrule {
|
||||
struct avl_node avl;
|
||||
char *option;
|
||||
char *rule;
|
||||
};
|
||||
|
||||
struct validate {
|
||||
struct avl_node avl;
|
||||
struct list_head list;
|
||||
|
||||
char *package;
|
||||
char *type;
|
||||
|
||||
struct avl_tree rules;
|
||||
};
|
||||
|
||||
struct service {
|
||||
struct avl_node avl;
|
||||
const char *name;
|
||||
|
||||
struct blob_attr *trigger;
|
||||
struct vlist_tree instances;
|
||||
struct list_head validators;
|
||||
};
|
||||
|
||||
void service_validate_add(struct service *s, struct blob_attr *attr);
|
||||
void service_validate_dump(struct blob_buf *b, struct service *s);
|
||||
void service_validate_dump_all(struct blob_buf *b, char *p, char *s);
|
||||
int service_start_early(char *name, char *cmdline);
|
||||
void service_validate_del(struct service *s);
|
||||
void service_event(const char *type, const char *service, const char *instance);
|
||||
|
||||
#endif
|
||||
6
src/3P/procd/service/setlbf.c
Normal file
6
src/3P/procd/service/setlbf.c
Normal file
@@ -0,0 +1,6 @@
|
||||
#include <stdio.h>
|
||||
|
||||
static void __attribute__((constructor)) setlbf(void)
|
||||
{
|
||||
setbuf(stdout, NULL);
|
||||
}
|
||||
303
src/3P/procd/service/trigger.c
Normal file
303
src/3P/procd/service/trigger.c
Normal file
@@ -0,0 +1,303 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
|
||||
* Copyright (C) 2013 John Crispin <blogic@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <libubox/blobmsg_json.h>
|
||||
#include <libubox/json_script.h>
|
||||
#include <libubox/runqueue.h>
|
||||
#include <libubox/ustream.h>
|
||||
#include <libubox/uloop.h>
|
||||
#include <libubox/avl.h>
|
||||
#include <libubox/avl-cmp.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <libgen.h>
|
||||
|
||||
#include "../procd.h"
|
||||
|
||||
struct trigger {
|
||||
struct list_head list;
|
||||
|
||||
void *id;
|
||||
char *type;
|
||||
int timeout;
|
||||
|
||||
struct blob_attr *rule;
|
||||
struct blob_attr *data;
|
||||
|
||||
struct json_script_ctx jctx;
|
||||
};
|
||||
|
||||
struct trigger_command {
|
||||
struct avl_node avl;
|
||||
struct uloop_timeout delay;
|
||||
bool requeue;
|
||||
|
||||
struct runqueue_process proc;
|
||||
struct json_script_ctx jctx;
|
||||
|
||||
struct blob_attr data[];
|
||||
};
|
||||
|
||||
static LIST_HEAD(triggers);
|
||||
static RUNQUEUE(q, 1);
|
||||
static AVL_TREE(trigger_pending, avl_blobcmp, false, NULL);
|
||||
|
||||
static const char* rule_handle_var(struct json_script_ctx *ctx, const char *name, struct blob_attr *vars)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct json_script_file *
|
||||
rule_load_script(struct json_script_ctx *ctx, const char *name)
|
||||
{
|
||||
struct trigger *t = container_of(ctx, struct trigger, jctx);
|
||||
|
||||
if (strcmp(name, t->type) != 0)
|
||||
return NULL;
|
||||
|
||||
return json_script_file_from_blobmsg(t->type, t->rule, blob_pad_len(t->rule));
|
||||
}
|
||||
|
||||
static void trigger_free(struct trigger *t)
|
||||
{
|
||||
json_script_free(&t->jctx);
|
||||
free(t->data);
|
||||
list_del(&t->list);
|
||||
free(t);
|
||||
}
|
||||
|
||||
static void trigger_command_complete(struct runqueue *q, struct runqueue_task *p)
|
||||
{
|
||||
struct trigger_command *cmd = container_of(p, struct trigger_command, proc.task);
|
||||
|
||||
if (cmd->requeue) {
|
||||
cmd->requeue = false;
|
||||
runqueue_task_add(q, p, false);
|
||||
return;
|
||||
}
|
||||
|
||||
avl_delete(&trigger_pending, &cmd->avl);
|
||||
free(cmd);
|
||||
}
|
||||
|
||||
static void trigger_command_run(struct runqueue *q, struct runqueue_task *t)
|
||||
{
|
||||
struct trigger_command *cmd = container_of(t, struct trigger_command, proc.task);
|
||||
struct blob_attr *cur;
|
||||
char **argv;
|
||||
pid_t pid;
|
||||
int n = 0;
|
||||
int rem;
|
||||
|
||||
pid = fork();
|
||||
if (pid < 0) {
|
||||
trigger_command_complete(q, t);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pid) {
|
||||
runqueue_process_add(q, &cmd->proc, pid);
|
||||
return;
|
||||
}
|
||||
|
||||
if (debug < 3) {
|
||||
close(STDIN_FILENO);
|
||||
close(STDOUT_FILENO);
|
||||
close(STDERR_FILENO);
|
||||
}
|
||||
|
||||
blobmsg_for_each_attr(cur, cmd->data, rem)
|
||||
n++;
|
||||
|
||||
argv = alloca((n + 1) * sizeof(*argv));
|
||||
n = 0;
|
||||
blobmsg_for_each_attr(cur, cmd->data, rem)
|
||||
argv[n++] = blobmsg_get_string(cur);
|
||||
argv[n] = NULL;
|
||||
|
||||
if (n > 0)
|
||||
execvp(argv[0], &argv[0]);
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void trigger_command_start(struct uloop_timeout *timeout)
|
||||
{
|
||||
static const struct runqueue_task_type trigger_command_type = {
|
||||
.run = trigger_command_run,
|
||||
.cancel = runqueue_process_cancel_cb,
|
||||
.kill = runqueue_process_kill_cb,
|
||||
};
|
||||
struct trigger_command *cmd = container_of(timeout, struct trigger_command, delay);
|
||||
|
||||
cmd->proc.task.type = &trigger_command_type;
|
||||
cmd->proc.task.complete = trigger_command_complete;
|
||||
runqueue_task_add(&q, &cmd->proc.task, false);
|
||||
}
|
||||
|
||||
static void trigger_command_add(struct trigger *t, struct blob_attr *data)
|
||||
{
|
||||
struct trigger_command *cmd;
|
||||
int remaining;
|
||||
|
||||
cmd = avl_find_element(&trigger_pending, data, cmd, avl);
|
||||
if (cmd) {
|
||||
/* Command currently running? */
|
||||
if (!cmd->delay.pending) {
|
||||
cmd->requeue = true;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Extend timer if trigger timeout is bigger than remaining time */
|
||||
remaining = uloop_timeout_remaining(&cmd->delay);
|
||||
if (remaining < t->timeout)
|
||||
uloop_timeout_set(&cmd->delay, t->timeout);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
cmd = calloc(1, sizeof(*cmd) + blob_pad_len(data));
|
||||
if (!cmd)
|
||||
return;
|
||||
|
||||
cmd->avl.key = cmd->data;
|
||||
cmd->delay.cb = trigger_command_start;
|
||||
memcpy(cmd->data, data, blob_pad_len(data));
|
||||
avl_insert(&trigger_pending, &cmd->avl);
|
||||
uloop_timeout_set(&cmd->delay, t->timeout > 0 ? t->timeout : 1);
|
||||
}
|
||||
|
||||
static void rule_handle_command(struct json_script_ctx *ctx, const char *name,
|
||||
struct blob_attr *exec, struct blob_attr *vars)
|
||||
{
|
||||
struct trigger *t = container_of(ctx, struct trigger, jctx);
|
||||
|
||||
if (!strcmp(name, "run_script")) {
|
||||
trigger_command_add(t, exec);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void rule_handle_error(struct json_script_ctx *ctx, const char *msg,
|
||||
struct blob_attr *context)
|
||||
{
|
||||
char *s;
|
||||
|
||||
s = blobmsg_format_json(context, false);
|
||||
ERROR("ERROR: %s in block: %s\n", msg, s);
|
||||
free(s);
|
||||
}
|
||||
|
||||
static struct trigger* _trigger_add(char *type, struct blob_attr *rule, int timeout, void *id)
|
||||
{
|
||||
char *_t;
|
||||
struct blob_attr *_r;
|
||||
struct trigger *t = calloc_a(sizeof(*t), &_t, strlen(type) + 1, &_r, blob_pad_len(rule));
|
||||
|
||||
t->type = _t;
|
||||
t->rule = _r;
|
||||
t->timeout = timeout;
|
||||
t->id = id;
|
||||
t->jctx.handle_var = rule_handle_var,
|
||||
t->jctx.handle_error = rule_handle_error,
|
||||
t->jctx.handle_command = rule_handle_command,
|
||||
t->jctx.handle_file = rule_load_script,
|
||||
|
||||
strcpy(t->type, type);
|
||||
memcpy(t->rule, rule, blob_pad_len(rule));
|
||||
|
||||
list_add(&t->list, &triggers);
|
||||
json_script_init(&t->jctx);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
void trigger_add(struct blob_attr *rule, void *id)
|
||||
{
|
||||
struct blob_attr *cur;
|
||||
int rem;
|
||||
|
||||
blobmsg_for_each_attr(cur, rule, rem) {
|
||||
struct blob_attr *_cur, *type = NULL, *script = NULL, *timeout = NULL;
|
||||
int _rem;
|
||||
int i = 0;
|
||||
|
||||
if (blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY)
|
||||
continue;
|
||||
|
||||
blobmsg_for_each_attr(_cur, cur, _rem) {
|
||||
switch (i++) {
|
||||
case 0:
|
||||
if (blobmsg_type(_cur) == BLOBMSG_TYPE_STRING)
|
||||
type = _cur;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
if (blobmsg_type(_cur) == BLOBMSG_TYPE_ARRAY)
|
||||
script = _cur;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
if (blobmsg_type(_cur) == BLOBMSG_TYPE_INT32)
|
||||
timeout = _cur;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (type && script) {
|
||||
int t = 0;
|
||||
|
||||
if (timeout)
|
||||
t = blobmsg_get_u32(timeout);
|
||||
_trigger_add(blobmsg_get_string(type), script, t, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void trigger_del(void *id)
|
||||
{
|
||||
struct trigger *t, *n;
|
||||
|
||||
list_for_each_entry_safe(t, n, &triggers, list) {
|
||||
if (t->id != id)
|
||||
continue;
|
||||
|
||||
trigger_free(t);
|
||||
}
|
||||
}
|
||||
|
||||
static bool trigger_match(const char *event, const char *match)
|
||||
{
|
||||
char *wildcard = strstr(match, ".*");
|
||||
if (wildcard)
|
||||
return !strncmp(event, match, wildcard - match);
|
||||
return !strcmp(event, match);
|
||||
}
|
||||
|
||||
void trigger_event(const char *type, struct blob_attr *data)
|
||||
{
|
||||
struct trigger *t;
|
||||
|
||||
list_for_each_entry(t, &triggers, list) {
|
||||
if (!trigger_match(type, t->type))
|
||||
continue;
|
||||
json_script_run(&t->jctx, t->type, data);
|
||||
}
|
||||
}
|
||||
157
src/3P/procd/service/validate.c
Normal file
157
src/3P/procd/service/validate.c
Normal file
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (C) 2013 John Crispin <blogic@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <libubox/blobmsg_json.h>
|
||||
#include <libubox/avl-cmp.h>
|
||||
#include <json-c/json.h>
|
||||
|
||||
#include "../procd.h"
|
||||
|
||||
#include "service.h"
|
||||
|
||||
enum {
|
||||
SERVICE_VAL_PACKAGE,
|
||||
SERVICE_VAL_TYPE,
|
||||
SERVICE_VAL_DATA,
|
||||
__SERVICE_VAL_MAX
|
||||
};
|
||||
|
||||
static const struct blobmsg_policy service_validate_attrs[__SERVICE_VAL_MAX] = {
|
||||
[SERVICE_VAL_PACKAGE] = { "package", BLOBMSG_TYPE_STRING },
|
||||
[SERVICE_VAL_TYPE] = { "type", BLOBMSG_TYPE_STRING },
|
||||
[SERVICE_VAL_DATA] = { "data", BLOBMSG_TYPE_TABLE },
|
||||
};
|
||||
|
||||
static AVL_TREE(validators, avl_strcmp, true, NULL);
|
||||
|
||||
void
|
||||
service_validate_dump_all(struct blob_buf *b, char *p, char *s)
|
||||
{
|
||||
struct json_object *r = json_object_new_object();
|
||||
struct validate *v;
|
||||
|
||||
if (!r)
|
||||
return;
|
||||
|
||||
avl_for_each_element(&validators, v, avl) {
|
||||
struct json_object *o, *t;
|
||||
struct vrule *vr;
|
||||
|
||||
if (p && strcmp(p, v->package))
|
||||
continue;
|
||||
|
||||
if (s && strcmp(s, v->type))
|
||||
continue;
|
||||
|
||||
json_object_object_get_ex(r, v->package, &o);
|
||||
if (!o) {
|
||||
o = json_object_new_object();
|
||||
json_object_object_add(r, v->package, o);
|
||||
}
|
||||
json_object_object_get_ex(o, v->type, &t);
|
||||
if (!t) {
|
||||
t = json_object_new_object();
|
||||
json_object_object_add(o, v->type, t);
|
||||
}
|
||||
avl_for_each_element(&v->rules, vr, avl)
|
||||
json_object_object_add(t, vr->option, json_object_new_string(vr->rule));
|
||||
}
|
||||
blobmsg_add_object(b, r);
|
||||
json_object_put(r);
|
||||
}
|
||||
|
||||
void
|
||||
service_validate_dump(struct blob_buf *b, struct service *s)
|
||||
{
|
||||
struct validate *v;
|
||||
void *i = blobmsg_open_array(b, "validate");
|
||||
|
||||
list_for_each_entry(v, &s->validators, list) {
|
||||
struct vrule *vr;
|
||||
void *k, *j = blobmsg_open_table(b, "validate");
|
||||
|
||||
blobmsg_add_string(b, "package", v->package);
|
||||
blobmsg_add_string(b, "type", v->type);
|
||||
k = blobmsg_open_table(b, "rules");
|
||||
avl_for_each_element(&v->rules, vr, avl)
|
||||
blobmsg_add_string(b, vr->option, vr->rule);
|
||||
blobmsg_close_table(b, k);
|
||||
blobmsg_close_table(b, j);
|
||||
}
|
||||
blobmsg_close_array(b, i);
|
||||
}
|
||||
|
||||
void
|
||||
service_validate_del(struct service *s)
|
||||
{
|
||||
struct validate *v, *n;
|
||||
|
||||
if (list_empty(&s->validators))
|
||||
return;
|
||||
|
||||
list_for_each_entry_safe(v, n, &s->validators, list) {
|
||||
struct vrule *vr, *a;
|
||||
|
||||
avl_remove_all_elements(&v->rules, vr, avl, a)
|
||||
free(vr);
|
||||
|
||||
avl_delete(&validators, &v->avl);
|
||||
list_del(&v->list);
|
||||
free(v);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
service_validate_add(struct service *s, struct blob_attr *msg)
|
||||
{
|
||||
struct blob_attr *tb[__SERVICE_VAL_MAX];
|
||||
struct validate *v;
|
||||
char *type, *package;
|
||||
struct blob_attr *cur;
|
||||
int rem;
|
||||
|
||||
blobmsg_parse(service_validate_attrs, __SERVICE_VAL_MAX, tb, blobmsg_data(msg), blobmsg_data_len(msg));
|
||||
if (!tb[SERVICE_VAL_PACKAGE] || !tb[SERVICE_VAL_TYPE] || !tb[SERVICE_VAL_DATA])
|
||||
return;
|
||||
|
||||
v = calloc_a(sizeof(*v), &package, blobmsg_data_len(tb[SERVICE_VAL_PACKAGE]) + 1,
|
||||
&type, blobmsg_data_len(tb[SERVICE_VAL_TYPE]) + 1);
|
||||
if (!v)
|
||||
return;
|
||||
|
||||
v->type = type;
|
||||
v->avl.key = v->package = package;
|
||||
strcpy(v->package, blobmsg_get_string(tb[SERVICE_VAL_PACKAGE]));
|
||||
strcpy(v->type, blobmsg_get_string(tb[SERVICE_VAL_TYPE]));
|
||||
|
||||
list_add(&v->list, &s->validators);
|
||||
if (avl_insert(&validators, &v->avl)) {
|
||||
free(v);
|
||||
return;
|
||||
}
|
||||
avl_init(&v->rules, avl_strcmp, false, NULL);
|
||||
|
||||
blobmsg_for_each_attr(cur, tb[SERVICE_VAL_DATA], rem) {
|
||||
char *option;
|
||||
char *rule;
|
||||
struct vrule *vr = calloc_a(sizeof(*vr), &option, strlen(blobmsg_name(cur)) + 1,
|
||||
&rule, strlen(blobmsg_get_string(cur)) + 1);
|
||||
|
||||
vr->avl.key = vr->option = option;
|
||||
vr->rule = rule;
|
||||
strcpy(vr->option, blobmsg_name(cur));
|
||||
strcpy(vr->rule, blobmsg_get_string(cur));
|
||||
if (avl_insert(&v->rules, &vr->avl))
|
||||
free(vr);
|
||||
}
|
||||
}
|
||||
119
src/3P/procd/service/watch.c
Normal file
119
src/3P/procd/service/watch.c
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
|
||||
* Copyright (C) 2013 John Crispin <blogic@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <libubox/blobmsg_json.h>
|
||||
|
||||
#include "../procd.h"
|
||||
|
||||
struct watch_object {
|
||||
struct list_head list;
|
||||
|
||||
void *id;
|
||||
char *name;
|
||||
};
|
||||
|
||||
static struct ubus_event_handler watch_event;
|
||||
static struct ubus_subscriber watch_subscribe;
|
||||
static LIST_HEAD(watch_objects);
|
||||
|
||||
static void watch_subscribe_cb(struct ubus_context *ctx, struct ubus_event_handler *ev,
|
||||
const char *type, struct blob_attr *msg)
|
||||
{
|
||||
static const struct blobmsg_policy policy = {
|
||||
"path", BLOBMSG_TYPE_STRING
|
||||
};
|
||||
struct watch_object *o;
|
||||
struct blob_attr *attr;
|
||||
const char *path;
|
||||
|
||||
DEBUG(3, "ubus event %s\n", type);
|
||||
if (strcmp(type, "ubus.object.add") != 0)
|
||||
return;
|
||||
|
||||
blobmsg_parse(&policy, 1, &attr, blob_data(msg), blob_len(msg));
|
||||
if (!attr)
|
||||
return;
|
||||
|
||||
path = blobmsg_data(attr);
|
||||
DEBUG(3, "ubus path %s\n", path);
|
||||
|
||||
list_for_each_entry(o, &watch_objects, list) {
|
||||
unsigned int id;
|
||||
|
||||
if (strcmp(o->name, path))
|
||||
continue;
|
||||
if (ubus_lookup_id(ctx, path, &id))
|
||||
continue;
|
||||
if (!ubus_subscribe(ctx, &watch_subscribe, id))
|
||||
return;
|
||||
ERROR("failed to subscribe %d\n", id);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
watch_add(const char *_name, void *id)
|
||||
{
|
||||
int len = strlen(_name);
|
||||
char *name;
|
||||
struct watch_object *o = calloc_a(sizeof(*o), &name, len + 1);
|
||||
|
||||
o->name = name;
|
||||
strcpy(name, _name);
|
||||
o->id = id;
|
||||
list_add(&o->list, &watch_objects);
|
||||
}
|
||||
|
||||
void
|
||||
watch_del(void *id)
|
||||
{
|
||||
struct watch_object *t, *n;
|
||||
|
||||
list_for_each_entry_safe(t, n, &watch_objects, list) {
|
||||
if (t->id != id)
|
||||
continue;
|
||||
list_del(&t->list);
|
||||
free(t);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
watch_notify_cb(struct ubus_context *ctx, struct ubus_object *obj,
|
||||
struct ubus_request_data *req, const char *method,
|
||||
struct blob_attr *msg)
|
||||
{
|
||||
if (debug >= 3) {
|
||||
char *str;
|
||||
|
||||
str = blobmsg_format_json(msg, true);
|
||||
DEBUG(3, "Received ubus notify '%s': %s\n", method, str);
|
||||
free(str);
|
||||
}
|
||||
|
||||
trigger_event(method, msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
watch_ubus(struct ubus_context *ctx)
|
||||
{
|
||||
watch_event.cb = watch_subscribe_cb;
|
||||
watch_subscribe.cb = watch_notify_cb;
|
||||
if (ubus_register_subscriber(ctx, &watch_subscribe))
|
||||
ERROR("failed to register ubus subscriber\n");
|
||||
if (ubus_register_event_handler(ctx, &watch_event, "ubus.object.add"))
|
||||
ERROR("failed to add ubus event handler\n");
|
||||
}
|
||||
101
src/3P/procd/signal.c
Normal file
101
src/3P/procd/signal.c
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
|
||||
* Copyright (C) 2013 John Crispin <blogic@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <sys/reboot.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "procd.h"
|
||||
|
||||
static void do_reboot(void)
|
||||
{
|
||||
LOG("reboot\n");
|
||||
fflush(stderr);
|
||||
sync();
|
||||
sleep(2);
|
||||
reboot(RB_AUTOBOOT);
|
||||
while (1)
|
||||
;
|
||||
}
|
||||
|
||||
static void signal_shutdown(int signal, siginfo_t *siginfo, void *data)
|
||||
{
|
||||
int event = 0;
|
||||
char *msg = NULL;
|
||||
|
||||
#ifndef DISABLE_INIT
|
||||
switch(signal) {
|
||||
case SIGINT:
|
||||
case SIGTERM:
|
||||
event = RB_AUTOBOOT;
|
||||
msg = "reboot";
|
||||
break;
|
||||
case SIGUSR1:
|
||||
case SIGUSR2:
|
||||
event = RB_POWER_OFF;
|
||||
msg = "poweroff";
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
DEBUG(1, "Triggering %s\n", msg);
|
||||
if (event)
|
||||
procd_shutdown(event);
|
||||
}
|
||||
|
||||
struct sigaction sa_shutdown = {
|
||||
.sa_sigaction = signal_shutdown,
|
||||
.sa_flags = SA_SIGINFO
|
||||
};
|
||||
|
||||
static void signal_crash(int signal, siginfo_t *siginfo, void *data)
|
||||
{
|
||||
ERROR("Rebooting as procd has crashed\n");
|
||||
do_reboot();
|
||||
}
|
||||
|
||||
struct sigaction sa_crash = {
|
||||
.sa_sigaction = signal_crash,
|
||||
.sa_flags = SA_SIGINFO
|
||||
};
|
||||
|
||||
static void signal_dummy(int signal, siginfo_t *siginfo, void *data)
|
||||
{
|
||||
ERROR("Got unexpected signal %d\n", signal);
|
||||
}
|
||||
|
||||
struct sigaction sa_dummy = {
|
||||
.sa_sigaction = signal_dummy,
|
||||
.sa_flags = SA_SIGINFO
|
||||
};
|
||||
|
||||
void procd_signal(void)
|
||||
{
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
if (getpid() != 1)
|
||||
return;
|
||||
sigaction(SIGTERM, &sa_shutdown, NULL);
|
||||
sigaction(SIGINT, &sa_shutdown, NULL);
|
||||
sigaction(SIGUSR1, &sa_shutdown, NULL);
|
||||
sigaction(SIGUSR2, &sa_shutdown, NULL);
|
||||
sigaction(SIGSEGV, &sa_crash, NULL);
|
||||
sigaction(SIGBUS, &sa_crash, NULL);
|
||||
sigaction(SIGHUP, &sa_dummy, NULL);
|
||||
sigaction(SIGKILL, &sa_dummy, NULL);
|
||||
sigaction(SIGSTOP, &sa_dummy, NULL);
|
||||
#ifndef DISABLE_INIT
|
||||
reboot(RB_DISABLE_CAD);
|
||||
#endif
|
||||
}
|
||||
202
src/3P/procd/state.c
Normal file
202
src/3P/procd/state.c
Normal file
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
|
||||
* Copyright (C) 2013 John Crispin <blogic@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/reboot.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "procd.h"
|
||||
#include "syslog.h"
|
||||
#include "plug/hotplug.h"
|
||||
#include "watchdog.h"
|
||||
#include "service/service.h"
|
||||
#include "utils/utils.h"
|
||||
|
||||
enum {
|
||||
STATE_NONE = 0,
|
||||
STATE_EARLY,
|
||||
STATE_UBUS,
|
||||
STATE_INIT,
|
||||
STATE_RUNNING,
|
||||
STATE_SHUTDOWN,
|
||||
STATE_HALT,
|
||||
__STATE_MAX,
|
||||
};
|
||||
|
||||
static int state = STATE_NONE;
|
||||
static int reboot_event;
|
||||
|
||||
static void set_stdio(const char* tty)
|
||||
{
|
||||
if (chdir("/dev") ||
|
||||
!freopen(tty, "r", stdin) ||
|
||||
!freopen(tty, "w", stdout) ||
|
||||
!freopen(tty, "w", stderr) ||
|
||||
chdir("/"))
|
||||
ERROR("failed to set stdio\n");
|
||||
else
|
||||
fcntl(STDERR_FILENO, F_SETFL, fcntl(STDERR_FILENO, F_GETFL) | O_NONBLOCK);
|
||||
}
|
||||
|
||||
static void set_console(void)
|
||||
{
|
||||
const char* tty;
|
||||
char* split;
|
||||
char line[ 20 ];
|
||||
const char* try[] = { "tty0", "console", NULL }; /* Try the most common outputs */
|
||||
int f, i = 0;
|
||||
|
||||
tty = get_cmdline_val("console",line,sizeof(line));
|
||||
if (tty != NULL) {
|
||||
split = strchr(tty, ',');
|
||||
if ( split != NULL )
|
||||
*split = '\0';
|
||||
} else {
|
||||
// Try a default
|
||||
tty=try[i];
|
||||
i++;
|
||||
}
|
||||
|
||||
if (chdir("/dev")) {
|
||||
ERROR("failed to change dir to /dev\n");
|
||||
return;
|
||||
}
|
||||
while (tty!=NULL) {
|
||||
f = open(tty, O_RDONLY);
|
||||
if (f >= 0) {
|
||||
close(f);
|
||||
break;
|
||||
}
|
||||
|
||||
tty=try[i];
|
||||
i++;
|
||||
}
|
||||
if (chdir("/"))
|
||||
ERROR("failed to change dir to /\n");
|
||||
|
||||
if (tty != NULL)
|
||||
set_stdio(tty);
|
||||
}
|
||||
|
||||
static void state_enter(void)
|
||||
{
|
||||
char ubus_cmd[] = "/sbin/ubusd";
|
||||
|
||||
switch (state) {
|
||||
case STATE_EARLY:
|
||||
LOG("- early -\n");
|
||||
watchdog_init(0);
|
||||
hotplug("/etc/hotplug.json");
|
||||
procd_coldplug();
|
||||
break;
|
||||
|
||||
case STATE_UBUS:
|
||||
// try to reopen incase the wdt was not available before coldplug
|
||||
watchdog_init(0);
|
||||
set_stdio("console");
|
||||
LOG("- ubus -\n");
|
||||
procd_connect_ubus();
|
||||
service_start_early("ubus", ubus_cmd);
|
||||
break;
|
||||
|
||||
case STATE_INIT:
|
||||
LOG("- init -\n");
|
||||
procd_inittab();
|
||||
procd_inittab_run("respawn");
|
||||
procd_inittab_run("askconsole");
|
||||
procd_inittab_run("askfirst");
|
||||
procd_inittab_run("sysinit");
|
||||
|
||||
// switch to syslog log channel
|
||||
ulog_open(ULOG_SYSLOG, LOG_DAEMON, "procd");
|
||||
break;
|
||||
|
||||
case STATE_RUNNING:
|
||||
LOG("- init complete -\n");
|
||||
break;
|
||||
|
||||
case STATE_SHUTDOWN:
|
||||
/* Redirect output to the console for the users' benefit */
|
||||
set_console();
|
||||
LOG("- shutdown -\n");
|
||||
procd_inittab_run("shutdown");
|
||||
sync();
|
||||
break;
|
||||
|
||||
case STATE_HALT:
|
||||
// To prevent killed processes from interrupting the sleep
|
||||
signal(SIGCHLD, SIG_IGN);
|
||||
LOG("- SIGTERM processes -\n");
|
||||
kill(-1, SIGTERM);
|
||||
sync();
|
||||
sleep(1);
|
||||
LOG("- SIGKILL processes -\n");
|
||||
kill(-1, SIGKILL);
|
||||
sync();
|
||||
sleep(1);
|
||||
#ifndef DISABLE_INIT
|
||||
if (reboot_event == RB_POWER_OFF)
|
||||
LOG("- power down -\n");
|
||||
else
|
||||
LOG("- reboot -\n");
|
||||
|
||||
/* Allow time for last message to reach serial console, etc */
|
||||
sleep(1);
|
||||
|
||||
/* We have to fork here, since the kernel calls do_exit(EXIT_SUCCESS)
|
||||
* in linux/kernel/sys.c, which can cause the machine to panic when
|
||||
* the init process exits... */
|
||||
if (!vfork( )) { /* child */
|
||||
reboot(reboot_event);
|
||||
_exit(EXIT_SUCCESS);
|
||||
}
|
||||
while (1)
|
||||
sleep(1);
|
||||
#else
|
||||
exit(0);
|
||||
#endif
|
||||
break;
|
||||
|
||||
default:
|
||||
ERROR("Unhandled state %d\n", state);
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
void procd_state_next(void)
|
||||
{
|
||||
DEBUG(4, "Change state %d -> %d\n", state, state + 1);
|
||||
state++;
|
||||
state_enter();
|
||||
}
|
||||
|
||||
void procd_state_ubus_connect(void)
|
||||
{
|
||||
if (state == STATE_UBUS)
|
||||
procd_state_next();
|
||||
}
|
||||
|
||||
void procd_shutdown(int event)
|
||||
{
|
||||
if (state >= STATE_SHUTDOWN)
|
||||
return;
|
||||
DEBUG(2, "Shutting down system with event %x\n", event);
|
||||
reboot_event = event;
|
||||
state = STATE_SHUTDOWN;
|
||||
state_enter();
|
||||
}
|
||||
444
src/3P/procd/system.c
Normal file
444
src/3P/procd/system.c
Normal file
@@ -0,0 +1,444 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
|
||||
* Copyright (C) 2013 John Crispin <blogic@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <sys/utsname.h>
|
||||
#ifdef linux
|
||||
#include <sys/sysinfo.h>
|
||||
#endif
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/reboot.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <libubox/uloop.h>
|
||||
|
||||
#include "procd.h"
|
||||
#include "watchdog.h"
|
||||
|
||||
static struct blob_buf b;
|
||||
static int notify;
|
||||
static struct ubus_context *_ctx;
|
||||
|
||||
int upgrade_running = 0;
|
||||
|
||||
static int system_board(struct ubus_context *ctx, struct ubus_object *obj,
|
||||
struct ubus_request_data *req, const char *method,
|
||||
struct blob_attr *msg)
|
||||
{
|
||||
void *c;
|
||||
char line[256];
|
||||
char *key, *val, *next;
|
||||
struct utsname utsname;
|
||||
FILE *f;
|
||||
|
||||
blob_buf_init(&b, 0);
|
||||
|
||||
if (uname(&utsname) >= 0)
|
||||
{
|
||||
blobmsg_add_string(&b, "kernel", utsname.release);
|
||||
blobmsg_add_string(&b, "hostname", utsname.nodename);
|
||||
}
|
||||
|
||||
if ((f = fopen("/proc/cpuinfo", "r")) != NULL)
|
||||
{
|
||||
while(fgets(line, sizeof(line), f))
|
||||
{
|
||||
key = strtok(line, "\t:");
|
||||
val = strtok(NULL, "\t\n");
|
||||
|
||||
if (!key || !val)
|
||||
continue;
|
||||
|
||||
if (!strcasecmp(key, "system type") ||
|
||||
!strcasecmp(key, "processor") ||
|
||||
!strcasecmp(key, "model name"))
|
||||
{
|
||||
strtoul(val + 2, &key, 0);
|
||||
|
||||
if (key == (val + 2) || *key != 0)
|
||||
{
|
||||
blobmsg_add_string(&b, "system", val + 2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
if ((f = fopen("/tmp/sysinfo/model", "r")) != NULL ||
|
||||
(f = fopen("/proc/device-tree/model", "r")) != NULL)
|
||||
{
|
||||
if (fgets(line, sizeof(line), f))
|
||||
{
|
||||
val = strtok(line, "\t\n");
|
||||
|
||||
if (val)
|
||||
blobmsg_add_string(&b, "model", val);
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
else if ((f = fopen("/proc/cpuinfo", "r")) != NULL)
|
||||
{
|
||||
while(fgets(line, sizeof(line), f))
|
||||
{
|
||||
key = strtok(line, "\t:");
|
||||
val = strtok(NULL, "\t\n");
|
||||
|
||||
if (!key || !val)
|
||||
continue;
|
||||
|
||||
if (!strcasecmp(key, "machine") ||
|
||||
!strcasecmp(key, "hardware"))
|
||||
{
|
||||
blobmsg_add_string(&b, "model", val + 2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
if ((f = fopen("/etc/openwrt_release", "r")) != NULL)
|
||||
{
|
||||
c = blobmsg_open_table(&b, "release");
|
||||
|
||||
while (fgets(line, sizeof(line), f))
|
||||
{
|
||||
char *dest;
|
||||
char ch;
|
||||
|
||||
key = line;
|
||||
val = strchr(line, '=');
|
||||
if (!val)
|
||||
continue;
|
||||
|
||||
*(val++) = 0;
|
||||
|
||||
if (!strcasecmp(key, "DISTRIB_ID"))
|
||||
key = "distribution";
|
||||
else if (!strcasecmp(key, "DISTRIB_RELEASE"))
|
||||
key = "version";
|
||||
else if (!strcasecmp(key, "DISTRIB_REVISION"))
|
||||
key = "revision";
|
||||
else if (!strcasecmp(key, "DISTRIB_CODENAME"))
|
||||
key = "codename";
|
||||
else if (!strcasecmp(key, "DISTRIB_TARGET"))
|
||||
key = "target";
|
||||
else if (!strcasecmp(key, "DISTRIB_DESCRIPTION"))
|
||||
key = "description";
|
||||
else
|
||||
continue;
|
||||
|
||||
dest = blobmsg_alloc_string_buffer(&b, key, strlen(val));
|
||||
if (!dest) {
|
||||
ERROR("Failed to allocate blob.\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
while (val && (ch = *(val++)) != 0) {
|
||||
switch (ch) {
|
||||
case '\'':
|
||||
case '"':
|
||||
next = strchr(val, ch);
|
||||
if (next)
|
||||
*next = 0;
|
||||
|
||||
strcpy(dest, val);
|
||||
|
||||
if (next)
|
||||
val = next + 1;
|
||||
|
||||
dest += strlen(dest);
|
||||
break;
|
||||
case '\\':
|
||||
*(dest++) = *(val++);
|
||||
break;
|
||||
}
|
||||
}
|
||||
blobmsg_add_string_buffer(&b);
|
||||
}
|
||||
|
||||
blobmsg_close_array(&b, c);
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
ubus_send_reply(ctx, req, b.head);
|
||||
|
||||
return UBUS_STATUS_OK;
|
||||
}
|
||||
|
||||
static int system_info(struct ubus_context *ctx, struct ubus_object *obj,
|
||||
struct ubus_request_data *req, const char *method,
|
||||
struct blob_attr *msg)
|
||||
{
|
||||
time_t now;
|
||||
struct tm *tm;
|
||||
#ifdef linux
|
||||
struct sysinfo info;
|
||||
void *c;
|
||||
|
||||
if (sysinfo(&info))
|
||||
return UBUS_STATUS_UNKNOWN_ERROR;
|
||||
#endif
|
||||
|
||||
now = time(NULL);
|
||||
|
||||
if (!(tm = localtime(&now)))
|
||||
return UBUS_STATUS_UNKNOWN_ERROR;
|
||||
|
||||
blob_buf_init(&b, 0);
|
||||
|
||||
blobmsg_add_u32(&b, "localtime", now + tm->tm_gmtoff);
|
||||
|
||||
#ifdef linux
|
||||
blobmsg_add_u32(&b, "uptime", info.uptime);
|
||||
|
||||
c = blobmsg_open_array(&b, "load");
|
||||
blobmsg_add_u32(&b, NULL, info.loads[0]);
|
||||
blobmsg_add_u32(&b, NULL, info.loads[1]);
|
||||
blobmsg_add_u32(&b, NULL, info.loads[2]);
|
||||
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_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_close_table(&b, c);
|
||||
#endif
|
||||
|
||||
ubus_send_reply(ctx, req, b.head);
|
||||
|
||||
return UBUS_STATUS_OK;
|
||||
}
|
||||
|
||||
static int system_upgrade(struct ubus_context *ctx, struct ubus_object *obj,
|
||||
struct ubus_request_data *req, const char *method,
|
||||
struct blob_attr *msg)
|
||||
{
|
||||
upgrade_running = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int system_reboot(struct ubus_context *ctx, struct ubus_object *obj,
|
||||
struct ubus_request_data *req, const char *method,
|
||||
struct blob_attr *msg)
|
||||
{
|
||||
procd_shutdown(RB_AUTOBOOT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum {
|
||||
WDT_FREQUENCY,
|
||||
WDT_TIMEOUT,
|
||||
WDT_STOP,
|
||||
__WDT_MAX
|
||||
};
|
||||
|
||||
static const struct blobmsg_policy watchdog_policy[__WDT_MAX] = {
|
||||
[WDT_FREQUENCY] = { .name = "frequency", .type = BLOBMSG_TYPE_INT32 },
|
||||
[WDT_TIMEOUT] = { .name = "timeout", .type = BLOBMSG_TYPE_INT32 },
|
||||
[WDT_STOP] = { .name = "stop", .type = BLOBMSG_TYPE_BOOL },
|
||||
};
|
||||
|
||||
static int watchdog_set(struct ubus_context *ctx, struct ubus_object *obj,
|
||||
struct ubus_request_data *req, const char *method,
|
||||
struct blob_attr *msg)
|
||||
{
|
||||
struct blob_attr *tb[__WDT_MAX];
|
||||
const char *status;
|
||||
|
||||
if (!msg)
|
||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
blobmsg_parse(watchdog_policy, __WDT_MAX, tb, blob_data(msg), blob_len(msg));
|
||||
if (tb[WDT_FREQUENCY]) {
|
||||
unsigned int timeout = watchdog_timeout(0);
|
||||
unsigned int freq = blobmsg_get_u32(tb[WDT_FREQUENCY]);
|
||||
|
||||
if (freq) {
|
||||
if (freq > timeout / 2)
|
||||
freq = timeout / 2;
|
||||
watchdog_frequency(freq);
|
||||
}
|
||||
}
|
||||
|
||||
if (tb[WDT_TIMEOUT]) {
|
||||
unsigned int timeout = blobmsg_get_u32(tb[WDT_TIMEOUT]);
|
||||
unsigned int frequency = watchdog_frequency(0);
|
||||
|
||||
if (timeout <= frequency)
|
||||
timeout = frequency * 2;
|
||||
watchdog_timeout(timeout);
|
||||
}
|
||||
|
||||
if (tb[WDT_STOP])
|
||||
watchdog_set_stopped(blobmsg_get_bool(tb[WDT_STOP]));
|
||||
|
||||
if (watchdog_fd() == NULL)
|
||||
status = "offline";
|
||||
else if (watchdog_get_stopped())
|
||||
status = "stopped";
|
||||
else
|
||||
status = "running";
|
||||
|
||||
blob_buf_init(&b, 0);
|
||||
blobmsg_add_string(&b, "status", status);
|
||||
blobmsg_add_u32(&b, "timeout", watchdog_timeout(0));
|
||||
blobmsg_add_u32(&b, "frequency", watchdog_frequency(0));
|
||||
ubus_send_reply(ctx, req, b.head);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum {
|
||||
SIGNAL_PID,
|
||||
SIGNAL_NUM,
|
||||
__SIGNAL_MAX
|
||||
};
|
||||
|
||||
static const struct blobmsg_policy signal_policy[__SIGNAL_MAX] = {
|
||||
[SIGNAL_PID] = { .name = "pid", .type = BLOBMSG_TYPE_INT32 },
|
||||
[SIGNAL_NUM] = { .name = "signum", .type = BLOBMSG_TYPE_INT32 },
|
||||
};
|
||||
|
||||
static int proc_signal(struct ubus_context *ctx, struct ubus_object *obj,
|
||||
struct ubus_request_data *req, const char *method,
|
||||
struct blob_attr *msg)
|
||||
{
|
||||
struct blob_attr *tb[__SIGNAL_MAX];
|
||||
|
||||
if (!msg)
|
||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
blobmsg_parse(signal_policy, __SIGNAL_MAX, tb, blob_data(msg), blob_len(msg));
|
||||
if (!tb[SIGNAL_PID || !tb[SIGNAL_NUM]])
|
||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
kill(blobmsg_get_u32(tb[SIGNAL_PID]), blobmsg_get_u32(tb[SIGNAL_NUM]));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum {
|
||||
NAND_PATH,
|
||||
__NAND_MAX
|
||||
};
|
||||
|
||||
static const struct blobmsg_policy nand_policy[__NAND_MAX] = {
|
||||
[NAND_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },
|
||||
};
|
||||
|
||||
static void
|
||||
procd_spawn_upgraded(char *path)
|
||||
{
|
||||
char *wdt_fd = watchdog_fd();
|
||||
char *argv[] = { "/tmp/upgraded", NULL, NULL};
|
||||
|
||||
argv[1] = path;
|
||||
|
||||
DEBUG(2, "Exec to upgraded now\n");
|
||||
if (wdt_fd) {
|
||||
watchdog_no_cloexec();
|
||||
setenv("WDTFD", wdt_fd, 1);
|
||||
}
|
||||
execvp(argv[0], argv);
|
||||
}
|
||||
|
||||
static int nand_set(struct ubus_context *ctx, struct ubus_object *obj,
|
||||
struct ubus_request_data *req, const char *method,
|
||||
struct blob_attr *msg)
|
||||
{
|
||||
struct blob_attr *tb[__NAND_MAX];
|
||||
|
||||
if (!msg)
|
||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
blobmsg_parse(nand_policy, __NAND_MAX, tb, blob_data(msg), blob_len(msg));
|
||||
if (!tb[NAND_PATH])
|
||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
procd_spawn_upgraded(blobmsg_get_string(tb[NAND_PATH]));
|
||||
fprintf(stderr, "Yikees, something went wrong. no /sbin/upgraded ?\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
procd_subscribe_cb(struct ubus_context *ctx, struct ubus_object *obj)
|
||||
{
|
||||
notify = obj->has_subscribers;
|
||||
}
|
||||
|
||||
|
||||
static const struct ubus_method system_methods[] = {
|
||||
UBUS_METHOD_NOARG("board", system_board),
|
||||
UBUS_METHOD_NOARG("info", system_info),
|
||||
UBUS_METHOD_NOARG("upgrade", system_upgrade),
|
||||
UBUS_METHOD_NOARG("reboot", system_reboot),
|
||||
UBUS_METHOD("watchdog", watchdog_set, watchdog_policy),
|
||||
UBUS_METHOD("signal", proc_signal, signal_policy),
|
||||
|
||||
/* must remain at the end as it ia not always loaded */
|
||||
UBUS_METHOD("nandupgrade", nand_set, nand_policy),
|
||||
};
|
||||
|
||||
static struct ubus_object_type system_object_type =
|
||||
UBUS_OBJECT_TYPE("system", system_methods);
|
||||
|
||||
static struct ubus_object system_object = {
|
||||
.name = "system",
|
||||
.type = &system_object_type,
|
||||
.methods = system_methods,
|
||||
.n_methods = ARRAY_SIZE(system_methods),
|
||||
.subscribe_cb = procd_subscribe_cb,
|
||||
};
|
||||
|
||||
void
|
||||
procd_bcast_event(char *event, struct blob_attr *msg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!notify)
|
||||
return;
|
||||
|
||||
ret = ubus_notify(_ctx, &system_object, event, msg, -1);
|
||||
if (ret)
|
||||
fprintf(stderr, "Failed to notify log: %s\n", ubus_strerror(ret));
|
||||
}
|
||||
|
||||
void ubus_init_system(struct ubus_context *ctx)
|
||||
{
|
||||
struct stat s;
|
||||
int ret;
|
||||
|
||||
if (stat("/sbin/upgraded", &s))
|
||||
system_object.n_methods -= 1;
|
||||
|
||||
_ctx = ctx;
|
||||
ret = ubus_add_object(ctx, &system_object);
|
||||
if (ret)
|
||||
ERROR("Failed to add object: %s\n", ubus_strerror(ret));
|
||||
}
|
||||
80
src/3P/procd/trace/preload.c
Normal file
80
src/3P/procd/trace/preload.c
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (C) 2015 John Crispin <blogic@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/types.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "../preload.h"
|
||||
|
||||
#define ERROR(fmt, ...) do { \
|
||||
fprintf(stderr,"perload-jail: "fmt, ## __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
static main_t __main__;
|
||||
|
||||
static int __preload_main__(int argc, char **argv, char **envp)
|
||||
{
|
||||
unsetenv("LD_PRELOAD");
|
||||
ptrace(PTRACE_TRACEME);
|
||||
kill(getpid(), SIGSTOP);
|
||||
|
||||
return (*__main__)(argc, argv, envp);
|
||||
}
|
||||
|
||||
int __libc_start_main(main_t main,
|
||||
int argc,
|
||||
char **argv,
|
||||
ElfW(auxv_t) *auxvec,
|
||||
__typeof (main) init,
|
||||
void (*fini) (void),
|
||||
void (*rtld_fini) (void),
|
||||
void *stack_end)
|
||||
{
|
||||
start_main_t __start_main__;
|
||||
|
||||
__start_main__ = dlsym(RTLD_NEXT, "__libc_start_main");
|
||||
if (!__start_main__)
|
||||
ERROR("failed to find __libc_start_main %s\n", dlerror());
|
||||
|
||||
__main__ = main;
|
||||
|
||||
return (*__start_main__)(__preload_main__, argc, argv, auxvec,
|
||||
init, fini, rtld_fini, stack_end);
|
||||
}
|
||||
|
||||
void __uClibc_main(main_t main,
|
||||
int argc,
|
||||
char **argv,
|
||||
void (*app_init)(void),
|
||||
void (*app_fini)(void),
|
||||
void (*rtld_fini)(void),
|
||||
void *stack_end attribute_unused)
|
||||
{
|
||||
uClibc_main __start_main__;
|
||||
|
||||
__start_main__ = dlsym(RTLD_NEXT, "__uClibc_main");
|
||||
if (!__start_main__)
|
||||
ERROR("failed to find __uClibc_main %s\n", dlerror());
|
||||
|
||||
__main__ = main;
|
||||
|
||||
return (*__start_main__)(__preload_main__, argc, argv,
|
||||
app_init, app_fini, rtld_fini, stack_end);
|
||||
}
|
||||
225
src/3P/procd/trace/trace.c
Normal file
225
src/3P/procd/trace/trace.c
Normal file
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
* Copyright (C) 2015 John Crispin <blogic@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stddef.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/user.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include <libubox/uloop.h>
|
||||
#include <libubox/blobmsg.h>
|
||||
#include <libubox/blobmsg_json.h>
|
||||
|
||||
#include "../syscall-names.h"
|
||||
|
||||
#define _offsetof(a, b) __builtin_offsetof(a,b)
|
||||
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
|
||||
|
||||
#ifdef __amd64__
|
||||
#define reg_syscall_nr _offsetof(struct user, regs.orig_rax)
|
||||
#elif defined(__i386__)
|
||||
#define reg_syscall_nr _offsetof(struct user, regs.orig_eax)
|
||||
#elif defined(__mips)
|
||||
# ifndef EF_REG2
|
||||
# define EF_REG2 8
|
||||
# endif
|
||||
#define reg_syscall_nr (EF_REG2 / 4)
|
||||
#elif defined(__arm__)
|
||||
#define reg_syscall_nr _offsetof(struct user, regs.uregs[7])
|
||||
#else
|
||||
#error tracing is not supported on this architecture
|
||||
#endif
|
||||
|
||||
#define INFO(fmt, ...) do { \
|
||||
fprintf(stderr, "utrace: "fmt, ## __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define ERROR(fmt, ...) do { \
|
||||
syslog(LOG_ERR, "utrace: "fmt, ## __VA_ARGS__); \
|
||||
fprintf(stderr, "utrace: "fmt, ## __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
static struct uloop_process tracer;
|
||||
static int *syscall_count;
|
||||
static struct blob_buf b;
|
||||
static int syscall_max;
|
||||
static int in_syscall;
|
||||
static int debug;
|
||||
|
||||
static int max_syscall = ARRAY_SIZE(syscall_names);
|
||||
|
||||
static void set_syscall(const char *name, int val)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < max_syscall; i++)
|
||||
if (syscall_names[i] && !strcmp(syscall_names[i], name)) {
|
||||
syscall_count[i] = val;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void print_syscalls(int policy, const char *json)
|
||||
{
|
||||
void *c;
|
||||
int i;
|
||||
|
||||
set_syscall("rt_sigaction", 1);
|
||||
set_syscall("sigreturn", 1);
|
||||
set_syscall("rt_sigreturn", 1);
|
||||
set_syscall("exit_group", 1);
|
||||
set_syscall("exit", 1);
|
||||
|
||||
blob_buf_init(&b, 0);
|
||||
c = blobmsg_open_array(&b, "whitelist");
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(syscall_names); i++) {
|
||||
if (!syscall_count[i])
|
||||
continue;
|
||||
if (syscall_names[i]) {
|
||||
if (debug)
|
||||
printf("syscall %d (%s) was called %d times\n",
|
||||
i, syscall_names[i], syscall_count[i]);
|
||||
blobmsg_add_string(&b, NULL, syscall_names[i]);
|
||||
} else {
|
||||
ERROR("no name found for syscall(%d)\n", i);
|
||||
}
|
||||
}
|
||||
blobmsg_close_array(&b, c);
|
||||
blobmsg_add_u32(&b, "policy", policy);
|
||||
if (json) {
|
||||
FILE *fp = fopen(json, "w");
|
||||
if (fp) {
|
||||
fprintf(fp, "%s", blobmsg_format_json_indent(b.head, true, 0));
|
||||
fclose(fp);
|
||||
INFO("saving syscall trace to %s\n", json);
|
||||
} else {
|
||||
ERROR("failed to open %s\n", json);
|
||||
}
|
||||
} else {
|
||||
printf("%s\n",
|
||||
blobmsg_format_json_indent(b.head, true, 0));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void tracer_cb(struct uloop_process *c, int ret)
|
||||
{
|
||||
if (WIFSTOPPED(ret) && WSTOPSIG(ret) & 0x80) {
|
||||
if (!in_syscall) {
|
||||
int syscall = ptrace(PTRACE_PEEKUSER, c->pid, reg_syscall_nr);
|
||||
|
||||
if (syscall < syscall_max) {
|
||||
syscall_count[syscall]++;
|
||||
if (debug)
|
||||
fprintf(stderr, "%s()\n", syscall_names[syscall]);
|
||||
} else if (debug) {
|
||||
fprintf(stderr, "syscal(%d)\n", syscall);
|
||||
}
|
||||
}
|
||||
in_syscall = !in_syscall;
|
||||
} else if (WIFEXITED(ret)) {
|
||||
uloop_end();
|
||||
return;
|
||||
}
|
||||
ptrace(PTRACE_SYSCALL, c->pid, 0, 0);
|
||||
uloop_process_add(&tracer);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv, char **envp)
|
||||
{
|
||||
char *json = NULL;
|
||||
int status, ch, policy = EPERM;
|
||||
pid_t child;
|
||||
|
||||
while ((ch = getopt(argc, argv, "f:p:")) != -1) {
|
||||
switch (ch) {
|
||||
case 'f':
|
||||
json = optarg;
|
||||
break;
|
||||
case 'p':
|
||||
policy = atoi(optarg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if (!argc)
|
||||
return -1;
|
||||
|
||||
if (getenv("TRACE_DEBUG"))
|
||||
debug = 1;
|
||||
unsetenv("TRACE_DEBUG");
|
||||
|
||||
child = fork();
|
||||
|
||||
if (child == 0) {
|
||||
char **_argv = calloc(argc + 1, sizeof(char *));
|
||||
char **_envp;
|
||||
char preload[] = "LD_PRELOAD=/lib/libpreload-trace.so";
|
||||
int envc = 1;
|
||||
int ret;
|
||||
|
||||
memcpy(_argv, argv, argc * sizeof(char *));
|
||||
|
||||
while (envp[envc++])
|
||||
;
|
||||
|
||||
_envp = calloc(envc, sizeof(char *));
|
||||
memcpy(&_envp[1], _envp, envc * sizeof(char *));
|
||||
*envp = preload;
|
||||
|
||||
ret = execve(_argv[0], _argv, envp);
|
||||
ERROR("failed to exec %s: %s\n", _argv[0], strerror(errno));
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (child < 0)
|
||||
return -1;
|
||||
|
||||
syscall_max = ARRAY_SIZE(syscall_names);
|
||||
syscall_count = calloc(syscall_max, sizeof(int));
|
||||
waitpid(child, &status, 0);
|
||||
if (!WIFSTOPPED(status)) {
|
||||
ERROR("failed to start %s\n", *argv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ptrace(PTRACE_SETOPTIONS, child, 0, PTRACE_O_TRACESYSGOOD);
|
||||
ptrace(PTRACE_SYSCALL, child, 0, 0);
|
||||
|
||||
uloop_init();
|
||||
tracer.pid = child;
|
||||
tracer.cb = tracer_cb;
|
||||
uloop_process_add(&tracer);
|
||||
uloop_run();
|
||||
uloop_done();
|
||||
|
||||
if (!json)
|
||||
if (asprintf(&json, "/tmp/%s.%u.json", basename(*argv), child) < 0)
|
||||
ERROR("failed to allocate output path: %s\n", strerror(errno));
|
||||
|
||||
print_syscalls(policy, json);
|
||||
|
||||
return 0;
|
||||
}
|
||||
87
src/3P/procd/ubus.c
Normal file
87
src/3P/procd/ubus.c
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
|
||||
* Copyright (C) 2013 John Crispin <blogic@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <sys/resource.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "procd.h"
|
||||
|
||||
char *ubus_socket = NULL;
|
||||
static struct ubus_context *ctx;
|
||||
static struct uloop_timeout ubus_timer;
|
||||
static int timeout;
|
||||
|
||||
static void reset_timeout(void)
|
||||
{
|
||||
timeout = 50;
|
||||
}
|
||||
|
||||
static void timeout_retry(void)
|
||||
{
|
||||
uloop_timeout_set(&ubus_timer, timeout);
|
||||
timeout *= 2;
|
||||
if (timeout > 1000)
|
||||
timeout = 1000;
|
||||
}
|
||||
|
||||
static void
|
||||
ubus_reconnect_cb(struct uloop_timeout *timeout)
|
||||
{
|
||||
if (!ubus_reconnect(ctx, ubus_socket)) {
|
||||
ubus_add_uloop(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
timeout_retry();
|
||||
}
|
||||
|
||||
static void
|
||||
ubus_disconnect_cb(struct ubus_context *ctx)
|
||||
{
|
||||
ubus_timer.cb = ubus_reconnect_cb;
|
||||
reset_timeout();
|
||||
timeout_retry();
|
||||
}
|
||||
|
||||
static void
|
||||
ubus_connect_cb(struct uloop_timeout *timeout)
|
||||
{
|
||||
ctx = ubus_connect(ubus_socket);
|
||||
|
||||
if (!ctx) {
|
||||
DEBUG(4, "Connection to ubus failed\n");
|
||||
timeout_retry();
|
||||
return;
|
||||
}
|
||||
|
||||
ctx->connection_lost = ubus_disconnect_cb;
|
||||
ubus_init_service(ctx);
|
||||
ubus_init_system(ctx);
|
||||
watch_ubus(ctx);
|
||||
|
||||
DEBUG(2, "Connected to ubus, id=%08x\n", ctx->local_id);
|
||||
reset_timeout();
|
||||
ubus_add_uloop(ctx);
|
||||
procd_state_ubus_connect();
|
||||
}
|
||||
|
||||
void
|
||||
procd_connect_ubus(void)
|
||||
{
|
||||
ubus_timer.cb = ubus_connect_cb;
|
||||
reset_timeout();
|
||||
timeout_retry();
|
||||
}
|
||||
17
src/3P/procd/upgraded/CMakeLists.txt
Normal file
17
src/3P/procd/upgraded/CMakeLists.txt
Normal file
@@ -0,0 +1,17 @@
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
|
||||
PROJECT(upgraded C)
|
||||
ADD_DEFINITIONS(-Os -ggdb -Wall -Werror --std=gnu99 -Wmissing-declarations)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "-static -fPIC")
|
||||
set(CMAKE_FIND_LIBRARY_SUFFIXES .a)
|
||||
set(CMAKE_EXE_LINK_DYNAMIC_C_FLAGS)
|
||||
set(CMAKE_EXE_LINK_DYNAMIC_CXX_FLAGS)
|
||||
set(CMAKE_SHARED_LIBRARY_C_FLAGS)
|
||||
set(CMAKE_SHARED_LIBRARY_CXX_FLAGS)
|
||||
set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS)
|
||||
set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS)
|
||||
ADD_EXECUTABLE(upgraded upgraded.c ../watchdog.c)
|
||||
TARGET_LINK_LIBRARIES(upgraded ubox rt -lc -lgcc_pic)
|
||||
INSTALL(TARGETS upgraded
|
||||
RUNTIME DESTINATION sbin
|
||||
)
|
||||
80
src/3P/procd/upgraded/upgraded.c
Normal file
80
src/3P/procd/upgraded/upgraded.c
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
|
||||
* Copyright (C) 2013 John Crispin <blogic@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <sys/reboot.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <libubox/uloop.h>
|
||||
|
||||
#include "../watchdog.h"
|
||||
|
||||
static struct uloop_process upgrade_proc;
|
||||
unsigned int debug = 2;
|
||||
|
||||
static void upgrade_proc_cb(struct uloop_process *proc, int ret)
|
||||
{
|
||||
if (ret)
|
||||
fprintf(stderr, "sysupgrade aborted with return code: %d\n", ret);
|
||||
uloop_end();
|
||||
}
|
||||
|
||||
static void sysupgarde(char *folder)
|
||||
{
|
||||
char *args[] = { "/sbin/sysupgrade", "nand", NULL, NULL };
|
||||
|
||||
args[2] = folder;
|
||||
upgrade_proc.cb = upgrade_proc_cb;
|
||||
upgrade_proc.pid = fork();
|
||||
if (!upgrade_proc.pid) {
|
||||
execvp(args[0], args);
|
||||
fprintf(stderr, "Failed to fork sysupgrade\n");
|
||||
exit(-1);
|
||||
}
|
||||
if (upgrade_proc.pid <= 0) {
|
||||
fprintf(stderr, "Failed to start sysupgarde\n");
|
||||
uloop_end();
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
pid_t p = getpid();
|
||||
|
||||
if (p != 1) {
|
||||
fprintf(stderr, "this tool needs to run as pid 1\n");
|
||||
return -1;
|
||||
}
|
||||
if (chdir("/tmp") == -1) {
|
||||
fprintf(stderr, "failed to chdir to /tmp: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "sysupgrade stage 2 failed, no folder specified\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
uloop_init();
|
||||
watchdog_init(0);
|
||||
sysupgarde(argv[1]);
|
||||
uloop_run();
|
||||
|
||||
reboot(RB_AUTOBOOT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
43
src/3P/procd/utils/askfirst.c
Normal file
43
src/3P/procd/utils/askfirst.c
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
|
||||
* Copyright (C) 2013 John Crispin <blogic@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int c;
|
||||
|
||||
printf("Please press Enter to activate this console.\n");
|
||||
do {
|
||||
c = getchar();
|
||||
if (c == EOF)
|
||||
return -1;
|
||||
}
|
||||
while (c != 0xA);
|
||||
|
||||
if (argc < 2) {
|
||||
printf("%s needs to be called with at least 1 parameter\n", argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
execvp(argv[1], &argv[1]);
|
||||
printf("Failed to execute %s\n", argv[1]);
|
||||
|
||||
return -1;
|
||||
}
|
||||
209
src/3P/procd/utils/utils.c
Normal file
209
src/3P/procd/utils/utils.c
Normal file
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
|
||||
* Copyright (C) 2013 John Crispin <blogic@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
//AWOX #define _GNU_SOURCE
|
||||
#include <libubox/avl.h>
|
||||
#include <libubox/avl-cmp.h>
|
||||
#include "utils.h"
|
||||
#include <regex.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "../log.h"
|
||||
|
||||
void
|
||||
__blobmsg_list_init(struct blobmsg_list *list, int offset, int len, blobmsg_list_cmp cmp)
|
||||
{
|
||||
avl_init(&list->avl, avl_strcmp, false, NULL);
|
||||
list->node_offset = offset;
|
||||
list->node_len = len;
|
||||
list->cmp = cmp;
|
||||
}
|
||||
|
||||
int
|
||||
blobmsg_list_fill(struct blobmsg_list *list, void *data, int len, bool array)
|
||||
{
|
||||
struct avl_tree *tree = &list->avl;
|
||||
struct blobmsg_list_node *node;
|
||||
struct blob_attr *cur;
|
||||
void *ptr;
|
||||
int count = 0;
|
||||
int rem = len;
|
||||
|
||||
__blob_for_each_attr(cur, data, rem) {
|
||||
if (!blobmsg_check_attr(cur, !array))
|
||||
continue;
|
||||
|
||||
ptr = calloc(1, list->node_len);
|
||||
if (!ptr)
|
||||
return -1;
|
||||
|
||||
node = (void *) ((char *)ptr + list->node_offset);
|
||||
if (array)
|
||||
node->avl.key = blobmsg_data(cur);
|
||||
else
|
||||
node->avl.key = blobmsg_name(cur);
|
||||
node->data = cur;
|
||||
if (avl_insert(tree, &node->avl)) {
|
||||
free(ptr);
|
||||
continue;
|
||||
}
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
void
|
||||
blobmsg_list_move(struct blobmsg_list *list, struct blobmsg_list *src)
|
||||
{
|
||||
struct blobmsg_list_node *node, *tmp;
|
||||
void *ptr;
|
||||
|
||||
avl_remove_all_elements(&src->avl, node, avl, tmp) {
|
||||
if (avl_insert(&list->avl, &node->avl)) {
|
||||
ptr = ((char *) node - list->node_offset);
|
||||
free(ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
blobmsg_list_free(struct blobmsg_list *list)
|
||||
{
|
||||
struct blobmsg_list_node *node, *tmp;
|
||||
void *ptr;
|
||||
|
||||
avl_remove_all_elements(&list->avl, node, avl, tmp) {
|
||||
ptr = ((char *) node - list->node_offset);
|
||||
free(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
blobmsg_list_equal(struct blobmsg_list *l1, struct blobmsg_list *l2)
|
||||
{
|
||||
struct blobmsg_list_node *n1, *n2;
|
||||
int count = l1->avl.count;
|
||||
|
||||
if (count != l2->avl.count)
|
||||
return false;
|
||||
|
||||
n1 = avl_first_element(&l1->avl, n1, avl);
|
||||
n2 = avl_first_element(&l2->avl, n2, avl);
|
||||
|
||||
while (count-- > 0) {
|
||||
int len;
|
||||
|
||||
len = blob_len(n1->data);
|
||||
if (len != blob_len(n2->data))
|
||||
return false;
|
||||
|
||||
if (memcmp(n1->data, n2->data, len) != 0)
|
||||
return false;
|
||||
|
||||
if (l1->cmp && !l1->cmp(n1, n2))
|
||||
return false;
|
||||
|
||||
if (!count)
|
||||
break;
|
||||
|
||||
n1 = avl_next_element(n1, avl);
|
||||
n2 = avl_next_element(n2, avl);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
char* get_cmdline_val(const char* name, char* out, int len)
|
||||
{
|
||||
char line[CMDLINE_SIZE + 1], *c, *sptr;
|
||||
int fd = open("/proc/cmdline", O_RDONLY);
|
||||
ssize_t r = read(fd, line, sizeof(line) - 1);
|
||||
close(fd);
|
||||
|
||||
if (r <= 0)
|
||||
return NULL;
|
||||
|
||||
line[r] = 0;
|
||||
|
||||
for (c = strtok_r(line, " \t\n", &sptr); c;
|
||||
c = strtok_r(NULL, " \t\n", &sptr)) {
|
||||
char *sep = strchr(c, '=');
|
||||
ssize_t klen = sep - c;
|
||||
if (klen < 0 || strncmp(name, c, klen) || name[klen] != 0)
|
||||
continue;
|
||||
|
||||
strncpy(out, &sep[1], len);
|
||||
out[len-1] = 0;
|
||||
return out;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int patch_fd(const char *device, int fd, int flags)
|
||||
{
|
||||
int dfd, nfd;
|
||||
|
||||
if (device == NULL)
|
||||
device = "/dev/null";
|
||||
|
||||
if (*device != '/') {
|
||||
dfd = open("/dev", O_PATH|O_DIRECTORY);
|
||||
|
||||
if (dfd < 0)
|
||||
return -1;
|
||||
|
||||
nfd = openat(dfd, device, flags);
|
||||
|
||||
close(dfd);
|
||||
} else {
|
||||
nfd = open(device, flags);
|
||||
}
|
||||
|
||||
if (nfd < 0 && strcmp(device, "/dev/null"))
|
||||
nfd = open("/dev/null", flags);
|
||||
|
||||
if (nfd < 0)
|
||||
return -1;
|
||||
|
||||
fd = dup2(nfd, fd);
|
||||
|
||||
if (nfd > STDERR_FILENO)
|
||||
close(nfd);
|
||||
|
||||
return (fd < 0) ? -1 : 0;
|
||||
}
|
||||
|
||||
int patch_stdio(const char *device)
|
||||
{
|
||||
int fd, rv = 0;
|
||||
const char *fdname[3] = { "stdin", "stdout", "stderr" };
|
||||
|
||||
for (fd = STDIN_FILENO; fd <= STDERR_FILENO; fd++) {
|
||||
if (patch_fd(device, fd, fd ? O_WRONLY : O_RDONLY)) {
|
||||
ERROR("Failed to redirect %s to %s: %d (%s)\n",
|
||||
fdname[fd], device, errno, strerror(errno));
|
||||
rv = -1;
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
59
src/3P/procd/utils/utils.h
Normal file
59
src/3P/procd/utils/utils.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
|
||||
* Copyright (C) 2013 John Crispin <blogic@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __PROCD_UTILS_H
|
||||
#define __PROCD_UTILS_H
|
||||
|
||||
#include <libubox/avl.h>
|
||||
#include <libubox/blob.h>
|
||||
#include <libubox/blobmsg.h>
|
||||
|
||||
#define CMDLINE_SIZE 2048
|
||||
|
||||
struct blobmsg_list_node {
|
||||
struct avl_node avl;
|
||||
struct blob_attr *data;
|
||||
};
|
||||
|
||||
typedef bool (*blobmsg_list_cmp)(struct blobmsg_list_node *l1, struct blobmsg_list_node *l2);
|
||||
typedef void (*blobmsg_update_cb)(struct blobmsg_list_node *n);
|
||||
|
||||
struct blobmsg_list {
|
||||
struct avl_tree avl;
|
||||
int node_offset;
|
||||
int node_len;
|
||||
|
||||
blobmsg_list_cmp cmp;
|
||||
};
|
||||
|
||||
#define blobmsg_list_simple_init(list) \
|
||||
__blobmsg_list_init(list, 0, sizeof(struct blobmsg_list_node), NULL)
|
||||
|
||||
#define blobmsg_list_init(list, type, field, cmp) \
|
||||
__blobmsg_list_init(list, offsetof(type, field), sizeof(type), cmp)
|
||||
|
||||
#define blobmsg_list_for_each(list, element) \
|
||||
avl_for_each_element(&(list)->avl, element, avl)
|
||||
|
||||
void __blobmsg_list_init(struct blobmsg_list *list, int offset, int len, blobmsg_list_cmp cmp);
|
||||
int blobmsg_list_fill(struct blobmsg_list *list, void *data, int len, bool array);
|
||||
void blobmsg_list_free(struct blobmsg_list *list);
|
||||
bool blobmsg_list_equal(struct blobmsg_list *l1, struct blobmsg_list *l2);
|
||||
void blobmsg_list_move(struct blobmsg_list *list, struct blobmsg_list *src);
|
||||
char* get_cmdline_val(const char* name, char* out, int len);
|
||||
|
||||
int patch_fd(const char *device, int fd, int flags);
|
||||
int patch_stdio(const char *device);
|
||||
|
||||
#endif
|
||||
135
src/3P/procd/watchdog.c
Normal file
135
src/3P/procd/watchdog.c
Normal file
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
|
||||
* Copyright (C) 2013 John Crispin <blogic@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/watchdog.h>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <libubox/uloop.h>
|
||||
|
||||
#include "procd.h"
|
||||
#include "watchdog.h"
|
||||
|
||||
#define WDT_PATH "/dev/watchdog"
|
||||
|
||||
static struct uloop_timeout wdt_timeout;
|
||||
static int wdt_fd = -1;
|
||||
static int wdt_frequency = 5;
|
||||
|
||||
void watchdog_ping(void)
|
||||
{
|
||||
DEBUG(4, "Ping\n");
|
||||
if (wdt_fd >= 0 && write(wdt_fd, "X", 1) < 0)
|
||||
ERROR("WDT failed to write: %s\n", strerror(errno));
|
||||
}
|
||||
|
||||
static void watchdog_timeout_cb(struct uloop_timeout *t)
|
||||
{
|
||||
watchdog_ping();
|
||||
uloop_timeout_set(t, wdt_frequency * 1000);
|
||||
}
|
||||
|
||||
void watchdog_set_stopped(bool val)
|
||||
{
|
||||
if (val)
|
||||
uloop_timeout_cancel(&wdt_timeout);
|
||||
else
|
||||
watchdog_timeout_cb(&wdt_timeout);
|
||||
}
|
||||
|
||||
bool watchdog_get_stopped(void)
|
||||
{
|
||||
return !wdt_timeout.pending;
|
||||
}
|
||||
|
||||
int watchdog_timeout(int timeout)
|
||||
{
|
||||
if (wdt_fd < 0)
|
||||
return 0;
|
||||
|
||||
if (timeout) {
|
||||
DEBUG(4, "Set watchdog timeout: %ds\n", timeout);
|
||||
ioctl(wdt_fd, WDIOC_SETTIMEOUT, &timeout);
|
||||
}
|
||||
ioctl(wdt_fd, WDIOC_GETTIMEOUT, &timeout);
|
||||
|
||||
return timeout;
|
||||
}
|
||||
|
||||
int watchdog_frequency(int frequency)
|
||||
{
|
||||
if (wdt_fd < 0)
|
||||
return 0;
|
||||
|
||||
if (frequency) {
|
||||
DEBUG(4, "Set watchdog frequency: %ds\n", frequency);
|
||||
wdt_frequency = frequency;
|
||||
}
|
||||
|
||||
return wdt_frequency;
|
||||
}
|
||||
|
||||
char* watchdog_fd(void)
|
||||
{
|
||||
static char fd_buf[3];
|
||||
|
||||
if (wdt_fd < 0)
|
||||
return NULL;
|
||||
snprintf(fd_buf, sizeof(fd_buf), "%d", wdt_fd);
|
||||
|
||||
return fd_buf;
|
||||
}
|
||||
|
||||
void watchdog_init(int preinit)
|
||||
{
|
||||
char *env = getenv("WDTFD");
|
||||
|
||||
if (wdt_fd >= 0)
|
||||
return;
|
||||
|
||||
wdt_timeout.cb = watchdog_timeout_cb;
|
||||
if (env) {
|
||||
DEBUG(2, "Watchdog handover: fd=%s\n", env);
|
||||
wdt_fd = atoi(env);
|
||||
unsetenv("WDTFD");
|
||||
} else {
|
||||
wdt_fd = open("/dev/watchdog", O_WRONLY);
|
||||
}
|
||||
|
||||
if (wdt_fd < 0)
|
||||
return;
|
||||
|
||||
if (!preinit)
|
||||
fcntl(wdt_fd, F_SETFD, fcntl(wdt_fd, F_GETFD) | FD_CLOEXEC);
|
||||
|
||||
LOG("- watchdog -\n");
|
||||
watchdog_timeout(30);
|
||||
watchdog_timeout_cb(&wdt_timeout);
|
||||
|
||||
DEBUG(4, "Opened watchdog with timeout %ds\n", watchdog_timeout(0));
|
||||
}
|
||||
|
||||
|
||||
void watchdog_no_cloexec(void)
|
||||
{
|
||||
if (wdt_fd < 0)
|
||||
return;
|
||||
|
||||
fcntl(wdt_fd, F_SETFD, fcntl(wdt_fd, F_GETFD) & ~FD_CLOEXEC);
|
||||
}
|
||||
66
src/3P/procd/watchdog.h
Normal file
66
src/3P/procd/watchdog.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
|
||||
* Copyright (C) 2013 John Crispin <blogic@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __PROCD_WATCHDOG_H
|
||||
#define __PROCD_WATCHDOG_H
|
||||
|
||||
#ifndef DISABLE_INIT
|
||||
void watchdog_init(int preinit);
|
||||
char* watchdog_fd(void);
|
||||
int watchdog_timeout(int timeout);
|
||||
int watchdog_frequency(int frequency);
|
||||
void watchdog_set_stopped(bool val);
|
||||
bool watchdog_get_stopped(void);
|
||||
void watchdog_no_cloexec(void);
|
||||
void watchdog_ping(void);
|
||||
#else
|
||||
static inline void watchdog_init(int preinit)
|
||||
{
|
||||
}
|
||||
|
||||
static inline char* watchdog_fd(void)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
static inline int watchdog_timeout(int timeout)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int watchdog_frequency(int frequency)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void watchdog_set_stopped(bool val)
|
||||
{
|
||||
}
|
||||
|
||||
static inline bool watchdog_get_stopped(void)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void watchdog_no_cloexec(void)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void watchdog_ping(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user