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