Bump procd-2016-07-29-2c9f5d4af1559b840c42f1443ede9f9fe809c58b

This commit is contained in:
jbnadal
2016-12-06 17:47:39 +01:00
parent 2cdc93987e
commit c52cb8abde
56 changed files with 8207 additions and 0 deletions

123
src/3P/procd/CMakeLists.txt Normal file
View 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()

View 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)

View 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
View 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
View 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
View 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("/");
}

View 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
View 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
View 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);
}

View 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;
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View 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);
}

View 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
View 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;
}

View 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

View 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
View 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

View 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
View 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 "};"

View 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
View 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);
}

View 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

View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

View 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

View 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);
}

View 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

View File

@@ -0,0 +1,6 @@
#include <stdio.h>
static void __attribute__((constructor)) setlbf(void)
{
setbuf(stdout, NULL);
}

View 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);
}
}

View 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);
}
}

View 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
View 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
View 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
View 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));
}

View 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
View 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
View 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();
}

View 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
)

View 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;
}

View 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
View 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;
}

View 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
View 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
View 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