From fa4c1e81dae3ffb7711be38c64eacf8ac3856e42 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Tue, 23 Apr 2013 20:52:03 +0200 Subject: [PATCH 1/1] initial import of uboox utilities Signed-off-by: John Crispin --- .gitignore | 10 + CMakeLists.txt | 46 ++++ Makefile | 331 ++++++++++++++++++++++++++++ board.c | 207 ++++++++++++++++++ kmodloader.c | 110 ++++++++++ lsbloader.c | 187 ++++++++++++++++ mount_root.c | 675 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 1566 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 Makefile create mode 100644 board.c create mode 100644 kmodloader.c create mode 100644 lsbloader.c create mode 100644 mount_root.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c76b578 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +mount_root +board +kmodloader +lsbloader +.* +Makefile +CMakeCache.txt +CMakeFiles +*.cmake +install_manifest.txt diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..847ce08 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,46 @@ +cmake_minimum_required(VERSION 2.6) + +PROJECT(procd C) +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() + +find_library(json NAMES json-c json) +SET(LIBS ubox ubus ${json} blobmsg_json) + +IF(DEBUG) + ADD_DEFINITIONS(-DDEBUG -g3) +ENDIF() + +ADD_EXECUTABLE(mount_root mount_root.c) +TARGET_LINK_LIBRARIES(mount_root ${LIBS}) + +INSTALL(TARGETS mount_root + RUNTIME DESTINATION sbin +) + +ADD_EXECUTABLE(kmodloader kmodloader.c) +TARGET_LINK_LIBRARIES(kmodloader ${LIBS}) + +INSTALL(TARGETS kmodloader + RUNTIME DESTINATION sbin +) + +ADD_EXECUTABLE(board board.c) +TARGET_LINK_LIBRARIES(board ${LIBS}) + +INSTALL(TARGETS board + RUNTIME DESTINATION sbin +) + +ADD_EXECUTABLE(lsbloader lsbloader.c) +TARGET_LINK_LIBRARIES(lsbloader ${LIBS}) + +INSTALL(TARGETS lsbloader + RUNTIME DESTINATION sbin +) diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8357c9f --- /dev/null +++ b/Makefile @@ -0,0 +1,331 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 2.8 + +# Default target executed when no arguments are given to make. +default_target: all +.PHONY : default_target + +#============================================================================= +# Special targets provided by cmake. + +# Disable implicit rules so canonical targets will work. +.SUFFIXES: + +# Remove some rules from gmake that .SUFFIXES does not remove. +SUFFIXES = + +.SUFFIXES: .hpux_make_needs_suffix_list + +# Suppress display of executed commands. +$(VERBOSE).SILENT: + +# A target that is always out of date. +cmake_force: +.PHONY : cmake_force + +#============================================================================= +# Set environment variables for the build. + +# The shell in which to execute make rules. +SHELL = /bin/sh + +# The CMake executable. +CMAKE_COMMAND = /usr/bin/cmake + +# The command to remove a file. +RM = /usr/bin/cmake -E remove -f + +# Escaping for special characters. +EQUALS = = + +# The top-level source directory on which CMake was run. +CMAKE_SOURCE_DIR = /openwrt/ubox + +# The top-level build directory on which CMake was run. +CMAKE_BINARY_DIR = /openwrt/ubox + +#============================================================================= +# Targets provided globally by CMake. + +# Special rule for the target edit_cache +edit_cache: + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running interactive CMake command-line interface..." + /usr/bin/cmake -i . +.PHONY : edit_cache + +# Special rule for the target edit_cache +edit_cache/fast: edit_cache +.PHONY : edit_cache/fast + +# Special rule for the target install +install: preinstall + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Install the project..." + /usr/bin/cmake -P cmake_install.cmake +.PHONY : install + +# Special rule for the target install +install/fast: preinstall/fast + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Install the project..." + /usr/bin/cmake -P cmake_install.cmake +.PHONY : install/fast + +# Special rule for the target install/local +install/local: preinstall + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Installing only the local directory..." + /usr/bin/cmake -DCMAKE_INSTALL_LOCAL_ONLY=1 -P cmake_install.cmake +.PHONY : install/local + +# Special rule for the target install/local +install/local/fast: install/local +.PHONY : install/local/fast + +# Special rule for the target install/strip +install/strip: preinstall + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Installing the project stripped..." + /usr/bin/cmake -DCMAKE_INSTALL_DO_STRIP=1 -P cmake_install.cmake +.PHONY : install/strip + +# Special rule for the target install/strip +install/strip/fast: install/strip +.PHONY : install/strip/fast + +# Special rule for the target list_install_components +list_install_components: + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Available install components are: \"Unspecified\"" +.PHONY : list_install_components + +# Special rule for the target list_install_components +list_install_components/fast: list_install_components +.PHONY : list_install_components/fast + +# Special rule for the target rebuild_cache +rebuild_cache: + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running CMake to regenerate build system..." + /usr/bin/cmake -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) +.PHONY : rebuild_cache + +# Special rule for the target rebuild_cache +rebuild_cache/fast: rebuild_cache +.PHONY : rebuild_cache/fast + +# The main all target +all: cmake_check_build_system + $(CMAKE_COMMAND) -E cmake_progress_start /openwrt/ubox/CMakeFiles /openwrt/ubox/CMakeFiles/progress.marks + $(MAKE) -f CMakeFiles/Makefile2 all + $(CMAKE_COMMAND) -E cmake_progress_start /openwrt/ubox/CMakeFiles 0 +.PHONY : all + +# The main clean target +clean: + $(MAKE) -f CMakeFiles/Makefile2 clean +.PHONY : clean + +# The main clean target +clean/fast: clean +.PHONY : clean/fast + +# Prepare targets for installation. +preinstall: all + $(MAKE) -f CMakeFiles/Makefile2 preinstall +.PHONY : preinstall + +# Prepare targets for installation. +preinstall/fast: + $(MAKE) -f CMakeFiles/Makefile2 preinstall +.PHONY : preinstall/fast + +# clear depends +depend: + $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 1 +.PHONY : depend + +#============================================================================= +# Target rules for targets named board + +# Build rule for target. +board: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 board +.PHONY : board + +# fast build rule for target. +board/fast: + $(MAKE) -f CMakeFiles/board.dir/build.make CMakeFiles/board.dir/build +.PHONY : board/fast + +#============================================================================= +# Target rules for targets named kmodloader + +# Build rule for target. +kmodloader: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 kmodloader +.PHONY : kmodloader + +# fast build rule for target. +kmodloader/fast: + $(MAKE) -f CMakeFiles/kmodloader.dir/build.make CMakeFiles/kmodloader.dir/build +.PHONY : kmodloader/fast + +#============================================================================= +# Target rules for targets named lsbloader + +# Build rule for target. +lsbloader: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 lsbloader +.PHONY : lsbloader + +# fast build rule for target. +lsbloader/fast: + $(MAKE) -f CMakeFiles/lsbloader.dir/build.make CMakeFiles/lsbloader.dir/build +.PHONY : lsbloader/fast + +#============================================================================= +# Target rules for targets named mount_root + +# Build rule for target. +mount_root: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 mount_root +.PHONY : mount_root + +# fast build rule for target. +mount_root/fast: + $(MAKE) -f CMakeFiles/mount_root.dir/build.make CMakeFiles/mount_root.dir/build +.PHONY : mount_root/fast + +board.o: board.c.o +.PHONY : board.o + +# target to build an object file +board.c.o: + $(MAKE) -f CMakeFiles/board.dir/build.make CMakeFiles/board.dir/board.c.o +.PHONY : board.c.o + +board.i: board.c.i +.PHONY : board.i + +# target to preprocess a source file +board.c.i: + $(MAKE) -f CMakeFiles/board.dir/build.make CMakeFiles/board.dir/board.c.i +.PHONY : board.c.i + +board.s: board.c.s +.PHONY : board.s + +# target to generate assembly for a file +board.c.s: + $(MAKE) -f CMakeFiles/board.dir/build.make CMakeFiles/board.dir/board.c.s +.PHONY : board.c.s + +kmodloader.o: kmodloader.c.o +.PHONY : kmodloader.o + +# target to build an object file +kmodloader.c.o: + $(MAKE) -f CMakeFiles/kmodloader.dir/build.make CMakeFiles/kmodloader.dir/kmodloader.c.o +.PHONY : kmodloader.c.o + +kmodloader.i: kmodloader.c.i +.PHONY : kmodloader.i + +# target to preprocess a source file +kmodloader.c.i: + $(MAKE) -f CMakeFiles/kmodloader.dir/build.make CMakeFiles/kmodloader.dir/kmodloader.c.i +.PHONY : kmodloader.c.i + +kmodloader.s: kmodloader.c.s +.PHONY : kmodloader.s + +# target to generate assembly for a file +kmodloader.c.s: + $(MAKE) -f CMakeFiles/kmodloader.dir/build.make CMakeFiles/kmodloader.dir/kmodloader.c.s +.PHONY : kmodloader.c.s + +lsbloader.o: lsbloader.c.o +.PHONY : lsbloader.o + +# target to build an object file +lsbloader.c.o: + $(MAKE) -f CMakeFiles/lsbloader.dir/build.make CMakeFiles/lsbloader.dir/lsbloader.c.o +.PHONY : lsbloader.c.o + +lsbloader.i: lsbloader.c.i +.PHONY : lsbloader.i + +# target to preprocess a source file +lsbloader.c.i: + $(MAKE) -f CMakeFiles/lsbloader.dir/build.make CMakeFiles/lsbloader.dir/lsbloader.c.i +.PHONY : lsbloader.c.i + +lsbloader.s: lsbloader.c.s +.PHONY : lsbloader.s + +# target to generate assembly for a file +lsbloader.c.s: + $(MAKE) -f CMakeFiles/lsbloader.dir/build.make CMakeFiles/lsbloader.dir/lsbloader.c.s +.PHONY : lsbloader.c.s + +mount_root.o: mount_root.c.o +.PHONY : mount_root.o + +# target to build an object file +mount_root.c.o: + $(MAKE) -f CMakeFiles/mount_root.dir/build.make CMakeFiles/mount_root.dir/mount_root.c.o +.PHONY : mount_root.c.o + +mount_root.i: mount_root.c.i +.PHONY : mount_root.i + +# target to preprocess a source file +mount_root.c.i: + $(MAKE) -f CMakeFiles/mount_root.dir/build.make CMakeFiles/mount_root.dir/mount_root.c.i +.PHONY : mount_root.c.i + +mount_root.s: mount_root.c.s +.PHONY : mount_root.s + +# target to generate assembly for a file +mount_root.c.s: + $(MAKE) -f CMakeFiles/mount_root.dir/build.make CMakeFiles/mount_root.dir/mount_root.c.s +.PHONY : mount_root.c.s + +# Help Target +help: + @echo "The following are some of the valid targets for this Makefile:" + @echo "... all (the default if no target is provided)" + @echo "... clean" + @echo "... depend" + @echo "... board" + @echo "... edit_cache" + @echo "... install" + @echo "... install/local" + @echo "... install/strip" + @echo "... kmodloader" + @echo "... list_install_components" + @echo "... lsbloader" + @echo "... mount_root" + @echo "... rebuild_cache" + @echo "... board.o" + @echo "... board.i" + @echo "... board.s" + @echo "... kmodloader.o" + @echo "... kmodloader.i" + @echo "... kmodloader.s" + @echo "... lsbloader.o" + @echo "... lsbloader.i" + @echo "... lsbloader.s" + @echo "... mount_root.o" + @echo "... mount_root.i" + @echo "... mount_root.s" +.PHONY : help + + + +#============================================================================= +# Special targets to cleanup operation of make. + +# Special rule to run CMake to check the build system integrity. +# No rule that depends on this can have commands that come from listfiles +# because they might be regenerated. +cmake_check_build_system: + $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 0 +.PHONY : cmake_check_build_system + diff --git a/board.c b/board.c new file mode 100644 index 0000000..2dabc49 --- /dev/null +++ b/board.c @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2013 Felix Fietkau + * Copyright (C) 2013 John Crispin + * + * 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 +#include + +#include +#include +#include + +#include + +#define CONFIG_FILE "/etc/boards.json" +#define MODEL_FILE "/proc/devicetree/model" +#define CPU_INFO "/proc/cpuinfo" + +enum { + BOARD_ID, + __BOARD_MAX +}; + +static struct blob_buf b; + +static const struct blobmsg_policy board_policy[__BOARD_MAX] = { + [BOARD_ID] = { .name = "id", .type = BLOBMSG_TYPE_STRING }, +}; + +static char* detect_mips_machine(void) +{ + FILE *fp; + static char line[64]; + char *ret = NULL; + + fp = fopen(CPU_INFO, "r"); + if (!fp) { + perror("fopen"); + return NULL; + } + + while (fgets(line, sizeof(line), fp)) { + if (!strncmp(line, "machine", 7)) { + char *machine = strstr(line, ": "); + + if (!machine) + continue; + + machine[strlen(machine) - 1] = '\0'; + + ret = &machine[2]; + break; + } + } + + fclose(fp); + + return ret; +} + +static char* detect_devicetree_model(void) +{ + FILE *fp; + static char line[64]; + char *ret = NULL; + struct stat s; + + if (stat(MODEL_FILE, &s) || !S_ISREG(s.st_mode)) + return NULL; + + fp = fopen(MODEL_FILE, "r"); + if (!fp) { + perror("fopen"); + return NULL; + } + + if (!fgets(line, sizeof(line), fp)) + ret = line; + + fclose(fp); + + return ret; +} + +static char* board_id(struct blob_attr *msg) +{ + struct blob_attr *tb[__BOARD_MAX]; + + blobmsg_parse(board_policy, __BOARD_MAX, tb, blobmsg_data(msg), blobmsg_data_len(msg)); + if (tb[BOARD_ID]) + return blobmsg_data(tb[BOARD_ID]); + + return NULL; +} + +static void write_file(char *file, char *val) +{ + FILE *fp; + + fp = fopen(file, "w"); + if (fp) { + fprintf(fp, val); + fclose(fp); + } else { + perror("fopen"); + } + +} + +static int set_board(char *board, char *boardid) +{ + if (!boardid) { + fprintf(stderr, "failed to detect board %s\n", board); + return -1; + } + printf("detected %s - %s\n", board, boardid); + + mkdir("/tmp/sysinfo", 0755); + + write_file("/tmp/sysinfo/model", board); + write_file("/tmp/sysinfo/board_name", boardid); + + return 0; +} + +static int load_json(char *file) +{ + struct stat s; + int fd; + char *buf; + + if (stat(file, &s) || !S_ISREG(s.st_mode)) { + fprintf(stderr, "failed to open %s\n", file); + return -1; + } + + buf = malloc(s.st_size + 1); + if (!buf) { + perror("malloc"); + return -1; + } + + fd = open(file, O_RDONLY); + if (!fd) { + perror("open"); + return -1; + } + + if (read(fd, buf, s.st_size) != s.st_size) { + fprintf(stderr, "failed to read %s - %d bytes\n", file, (int)s.st_size); + close(fd); + return -1; + } + close(fd); + buf[s.st_size] = '\0'; + + blob_buf_init(&b, 0); + if (!blobmsg_add_json_from_string(&b, buf)) { + fprintf(stderr, "Failed to read json\n"); + return - 1; + } + free(buf); + + return 0; +} + +int main(int argc, char **argv) +{ + struct blob_attr *cur; + char *board; + int rem; + + board = detect_mips_machine(); + if (!board) + detect_devicetree_model(); + if (!board) { + fprintf(stderr, "failed to detect board\n"); + return -1; + } + + if (load_json(CONFIG_FILE)) + return -1; + + blob_for_each_attr(cur, b.head, rem) { + if (blobmsg_type(cur) != BLOBMSG_TYPE_TABLE) + continue; + if (!strcmp(blobmsg_name(cur), board)) { + char *id = board_id(cur); + + if (id) + return set_board(board, id); + } + } + + fprintf(stderr, "failed to identify %s\n", board); + + return -1; +} diff --git a/kmodloader.c b/kmodloader.c new file mode 100644 index 0000000..62b2d5b --- /dev/null +++ b/kmodloader.c @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2013 Felix Fietkau + * Copyright (C) 2013 John Crispin + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEF_MOD_PATH "/lib/modules/%s/%s.ko" +static int insmod(char *module, const char *options) +{ + struct utsname ver; + char path[256]; + void *data = 0; + struct stat s; + int fd, ret = -1; + + uname(&ver); + snprintf(path, 256, DEF_MOD_PATH, ver.release, module); + + if (stat(path, &s)) { + fprintf(stderr, "missing module %s\n", path); + return ret; + } + + fd = open(path, O_RDONLY); + if (!fd) { + fprintf(stderr, "cannot open %s\n", path); + return ret; + } + + data = malloc(s.st_size); + if (read(fd, data, s.st_size) == s.st_size) { + ret = syscall(__NR_init_module, data, s.st_size, options); + if (ret) + fprintf(stderr, "failed insert %s\n", module); + } else { + fprintf(stderr, "failed to read full module %s\n", path); + } + + close(fd); + free(data); + + return ret; +} + +/*static void rmmod(char *module) +{ + syscall(__NR_delete_module, module, 0); +}*/ + +int main(int argc, char **argv) +{ + glob_t gl; + int gl_flags = GLOB_NOESCAPE | GLOB_MARK; + char *tmp = malloc(256); + char *dir = "/etc/modules.d/*"; + + if (argc > 1) + dir = argv[1]; + + syslog(0, "kmodloader: loading kernel modules from %s\n", dir); + + if (glob(dir, gl_flags, NULL, &gl) >= 0) { + int j; + + for (j = 0; j < gl.gl_pathc; j++) { + FILE *fp = fopen(gl.gl_pathv[j], "r"); + + if (!fp) { + fprintf(stderr, "failed to open %s\n", gl.gl_pathv[j]); + } else { + char mod[64]; + + while (fgets(mod, 64, fp)) { + mod[strlen(mod) - 1] = '\0'; + insmod(mod, ""); + } + fclose(fp); + } + } + } + + globfree(&gl); + free(tmp); + + return 0; +} diff --git a/lsbloader.c b/lsbloader.c new file mode 100644 index 0000000..b40a505 --- /dev/null +++ b/lsbloader.c @@ -0,0 +1,187 @@ +/* + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) 2012 John Crispin + */ + +#include +#include +#define __USE_GNU +#include +#include +#include + +#include +#include +#include "libubus.h" + +struct initd { + struct list_head list; + + char *name; + char *exec; + char *desc; + char *tpl; + char **deps; + int start; + int stop; +}; + +static LIST_HEAD(initds); +static regex_t pat_provides, pat_require, pat_start, pat_stop, pat_desc, pat_exec, pat_tpl; +static struct ubus_context *ctx; +static struct blob_buf b; +static uint32_t service; + +static void initd_free(struct initd *i) +{ + if (i->name) + free(i->name); + if (i->exec) + free(i->exec); + if (i->desc) + free(i->desc); + if (i->tpl) + free(i->tpl); +} + +static int initd_parse(const char *file) +{ + FILE *fp; + struct initd *i; + regmatch_t matches[2]; + char buffer[1024]; + ssize_t len; + + fp = fopen(file, "r"); + if (!fp) { + fprintf(stderr, "failed to open %s\n", file); + return -1; + } + len = fread(buffer, 1, sizeof(buffer) - 1, fp); + fclose(fp); + + if (len < 1) { + fprintf(stderr, "failed to read from %s\n", file); + return -1; + } + buffer[len] = '\0'; + + i = malloc(sizeof(struct initd)); + if (!i) { + fprintf(stderr, "failed to alloc initd struct\n"); + return -1; + } + memset(i, 0, sizeof(*i)); + + if (!regexec(&pat_provides, buffer, 2, matches, 0)) + i->name = strndup(buffer + matches[1].rm_so, (size_t)matches[1].rm_eo - matches[1].rm_so); + if (!regexec(&pat_exec, buffer, 2, matches, 0)) + i->exec = strndup(buffer + matches[1].rm_so, matches[1].rm_eo - matches[1].rm_so); + if (!regexec(&pat_desc, buffer, 2, matches, 0)) + i->desc = strndup(buffer + matches[1].rm_so, matches[1].rm_eo - matches[1].rm_so); + if (!regexec(&pat_tpl, buffer, 2, matches, 0)) + i->tpl = strndup(buffer + matches[1].rm_so, matches[1].rm_eo - matches[1].rm_so); + if (!regexec(&pat_start, buffer, 2, matches, 0)) + i->start += atoi(buffer + matches[1].rm_so); + if (!regexec(&pat_stop, buffer, 2, matches, 0)) + i->stop += atoi(buffer + matches[1].rm_so); + + if (i->name && i->exec) + list_add(&i->list, &initds); + else + initd_free(i); + + return 0; +} + +static void initd_init(void) +{ + int gl_flags = GLOB_NOESCAPE | GLOB_MARK; + glob_t gl; + + regcomp(&pat_provides, "# Provides:[ \t]*([a-zA-Z0-9]+)", REG_EXTENDED); + regcomp(&pat_require, "# Required-Start:[ \t]*([a-zA-Z0-9 ]+)", REG_EXTENDED); + regcomp(&pat_start, "# Default-Start:[ \t]*([0-9])", REG_EXTENDED); + regcomp(&pat_stop, "# Default-Stop:[ \t]*([0-9])", REG_EXTENDED); + regcomp(&pat_desc, "# Description:[ \t]*([a-zA-Z0-9 ]+)", REG_EXTENDED); + regcomp(&pat_exec, "# X-Exec:[ \t]*([a-zA-Z0-9/ ]+)", REG_EXTENDED); + regcomp(&pat_tpl, "# X-Template:[ \t]*([a-zA-Z0-9/.]+)", REG_EXTENDED); + + if (glob("/etc/rc.d/P*", gl_flags, NULL, &gl) >= 0) { + int j; + for (j = 0; j < gl.gl_pathc; j++) + initd_parse(gl.gl_pathv[j]); + } + globfree(&gl); + + regfree(&pat_provides); + regfree(&pat_require); + regfree(&pat_start); + regfree(&pat_stop); + regfree(&pat_desc); + regfree(&pat_exec); + regfree(&pat_tpl); +} + +static int init_services(void) +{ + struct initd *i; + void *instances, *instance, *command; + + list_for_each_entry(i, &initds, list) { + char *t; + + blob_buf_init(&b, 0); + blobmsg_add_string(&b, "name", i->name); + instances = blobmsg_open_table(&b, "instances"); + instance = blobmsg_open_table(&b, "instance"); + command = blobmsg_open_array(&b, "command"); + t = strtok(i->exec, " "); + while (t) { + blobmsg_add_string(&b, NULL, t); + t = strtok(NULL, " "); + } + blobmsg_close_array(&b, command); + blobmsg_close_table(&b, instance); + blobmsg_close_table(&b, instances); + ubus_invoke(ctx, service, "add", b.head, NULL, 0, 1000); + } + + return 0; +} + +int main(int argc, char **argv) +{ + int ret; + + initd_init(); + + if (list_empty(&initds)) + return 0; + + ctx = ubus_connect(NULL); + if (!ctx) { + fprintf(stderr, "Failed to connect to ubus\n"); + return -1; + } + + ret = ubus_lookup_id(ctx, "service", &service); + if (ret) { + fprintf(stderr, "Failed to find service object: %s\n", ubus_strerror(ret)); + return -1; + } + + return init_services(); +} diff --git a/mount_root.c b/mount_root.c new file mode 100644 index 0000000..dc05368 --- /dev/null +++ b/mount_root.c @@ -0,0 +1,675 @@ +/* + * Copyright (C) 2013 Felix Fietkau + * Copyright (C) 2013 John Crispin + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include + +#define DEBUG(level, fmt, ...) do { \ + if (debug >= level) \ + fprintf(stderr, "%s %s(%d): " fmt, argv0, __func__, __LINE__, ## __VA_ARGS__); \ + } while (0) + +#define LOG(fmt, ...) do { \ + syslog(LOG_INFO, fmt, ## __VA_ARGS__); \ + fprintf(stderr, "%s: "fmt, argv0, ## __VA_ARGS__); \ + } while (0) + +#define ERROR(fmt, ...) do { \ + syslog(LOG_ERR, fmt, ## __VA_ARGS__); \ + fprintf(stderr, "%s: "fmt, argv0, ## __VA_ARGS__); \ + } while (0) + +enum { + FS_NONE, + FS_JFFS2, + FS_DEADCODE, +}; + +static const char *argv0; + +/* this is a raw syscall - man 2 pivot_root */ +extern int pivot_root(const char *new_root, const char *put_old); + +static int debug = 0; + +static void foreachdir(const char *dir, int (*cb)(const char*)) +{ + char globdir[256]; + glob_t gl; + int j; + + if (dir[strlen(dir) - 1] == '/') + snprintf(globdir, 256, "%s*", dir); + else + snprintf(globdir, 256, "%s/*", dir); + + if (!glob(globdir, GLOB_NOESCAPE | GLOB_MARK | GLOB_ONLYDIR, NULL, &gl)) + for (j = 0; j < gl.gl_pathc; j++) + foreachdir(gl.gl_pathv[j], cb); + + cb(dir); +} + +static char* find_mount_point(char *block, char *fs) +{ + FILE *fp = fopen("/proc/mounts", "r"); + static char line[256]; + int len = strlen(block); + char *point = NULL; + + if(!fp) + return NULL; + + while (fgets(line, sizeof(line), fp)) { + if (!strncmp(line, block, len)) { + char *p = &line[len + 1]; + char *t = strstr(p, " "); + + if (!t) + return NULL; + + *t = '\0'; + t++; + + if (fs && strncmp(t, fs, strlen(fs))) { + ERROR("block is mounted with wrong fs\n"); + return NULL; + } + point = p; + break; + } + } + + fclose(fp); + + return point; +} + +static char* find_mtd_index(char *name) +{ + FILE *fp = fopen("/proc/mtd", "r"); + static char line[256]; + char *index = NULL; + + if(!fp) + return index; + + while (!index && fgets(line, sizeof(line), fp)) { + if (strstr(line, name)) { + char *eol = strstr(line, ":"); + + if (!eol) + continue; + + *eol = '\0'; + index = &line[3]; + DEBUG(1, "found %s -> index:%s\n", name, index); + } + } + + fclose(fp); + + return index; +} + +static int find_mtd_block(char *name, char *part, int plen) +{ + char *index = find_mtd_index(name); + + if (!index) + return -1; + + snprintf(part, plen, "/dev/mtdblock%s", index); + DEBUG(1, "found %s -> %s\n", name, part); + + return 0; +} + +static int find_mtd_char(char *name, char *part, int plen) +{ + char *index = find_mtd_index(name); + + if (!index) + return -1; + + snprintf(part, plen, "/dev/mtd%s", index); + DEBUG(1, "found %s -> %s\n", name, part); + + return 0; +} + +static int mtd_unlock(char *mtd) +{ + struct erase_info_user mtdlock; + struct mtd_info_user mtdinfo; + int fd = open(mtd, O_RDWR | O_SYNC); + int ret = -1; + + DEBUG(1, "%s\n", mtd); + + if (!fd) { + ERROR("failed to open %s: %s\n", mtd, strerror(errno)); + return -1; + } + + ret = ioctl(fd, MEMGETINFO, &mtdinfo); + if (ret) { + ERROR("ioctl(%s, MEMGETINFO) failed: %s\n", mtd, strerror(errno)); + goto err_out; + } + + mtdlock.start = 0; + mtdlock.length = mtdinfo.size; + ioctl(fd, MEMUNLOCK, &mtdlock); + +err_out: + close(fd); + + return ret; +} + +static int mtd_erase(const char *mtd) +{ + int fd = open(mtd, O_RDWR | O_SYNC); + struct mtd_info_user i; + struct erase_info_user e; + int ret; + + if (!fd) { + ERROR("failed to open %s: %s\n", mtd, strerror(errno)); + return -1; + } + + ret = ioctl(fd, MEMGETINFO, &i); + if (ret) { + ERROR("ioctl(%s, MEMGETINFO) failed: %s\n", mtd, strerror(errno)); + return -1; + } + + e.length = i.erasesize; + for (e.start = 0; e.start < i.size; e.start += i.erasesize) { + ioctl(fd, MEMUNLOCK, &e); + if(ioctl(fd, MEMERASE, &e)) + ERROR("Failed to erase block on %s at 0x%x\n", mtd, e.start); + } + + close(fd); + return 0; +} + +static int do_mount_jffs2(void) +{ + char rootfs_data[32]; + + + if (mkdir("/tmp/overlay", 0755)) { + ERROR("failed to mkdir /tmp/overlay: %s\n", strerror(errno)); + return -1; + } + + if (find_mtd_block("rootfs_data", rootfs_data, sizeof(rootfs_data))) { + ERROR("rootfs_data does not exist\n"); + return -1; + } + + if (mount(rootfs_data, "/tmp/overlay", "jffs2", MS_NOATIME, NULL)) { + ERROR("failed to mount -t jffs2 %s /tmp/overlay: %s\n", rootfs_data, strerror(errno)); + return -1; + } + + find_mtd_char("rootfs_data", rootfs_data, sizeof(rootfs_data)); + + return mtd_unlock(rootfs_data); +} + +static int jffs2_ready(char *mtd) +{ + FILE *fp = fopen(mtd, "r"); + __u32 deadc0de; + __u16 jffs2; + size_t sz; + + if (!fp) { + ERROR("reading %s failed\n", mtd); + exit(-1); + } + + sz = fread(&deadc0de, sizeof(deadc0de), 1, fp); + fclose(fp); + + if (sz != 1) { + ERROR("reading %s failed: %s\n", mtd, strerror(errno)); + exit(-1); + } + + deadc0de = __be32_to_cpu(deadc0de); + jffs2 = __be16_to_cpu(deadc0de >> 16); + + if (jffs2 == 0x1985) { + LOG("jffs2 is ready\n"); + return FS_JFFS2; + } + + if (deadc0de == 0xdeadc0de) { + LOG("jffs2 is not ready - marker found\n"); + return FS_DEADCODE; + } + + ERROR("No jffs2 marker was found\n"); + + return FS_NONE; +} + +static int check_fs_exists(char *fs) +{ + FILE *fp = fopen("/proc/filesystems", "r"); + static char line[256]; + int ret = -1; + + DEBUG(2, "%s\n", fs); + + if (!fp) { + ERROR("opening /proc/filesystems failed: %s\n", strerror(errno)); + goto out; + } + + while (ret && fgets(line, sizeof(line), fp)) + if (strstr(line, fs)) + ret = 0; + + fclose(fp); + +out: + return ret; +} + +static int mount_move(char *oldroot, char *newroot, char *dir) +{ +#ifndef MS_MOVE +#define MS_MOVE (1 << 13) +#endif + struct stat s; + char olddir[64]; + char newdir[64]; + int ret; + + DEBUG(2, "%s %s\n", oldroot, dir); + + snprintf(olddir, sizeof(olddir), "%s%s", oldroot, dir); + snprintf(newdir, sizeof(newdir), "%s%s", newroot, dir); + + if (stat(olddir, &s) || !S_ISDIR(s.st_mode)) + return -1; + + if (stat(newdir, &s) || !S_ISDIR(s.st_mode)) + return -1; + + ret = mount(olddir, newdir, NULL, MS_NOATIME | MS_MOVE, NULL); + + if (ret) + DEBUG(1, "failed %s %s: %s\n", olddir, newdir, strerror(errno)); + + return ret; +} + +static int pivot(char *new, char *old) +{ + char pivotdir[64]; + int ret; + + DEBUG(2, "%s %s\n", new, old); + + if (mount_move("", new, "/proc")) + return -1; + + snprintf(pivotdir, sizeof(pivotdir), "%s%s", new, old); + + ret = pivot_root(new, pivotdir); + + if (ret < 0) { + ERROR("pivot_root failed %s %s: %s\n", new, pivotdir, strerror(errno)); + return -1; + } + + mount_move(old, "", "/dev"); + mount_move(old, "", "/tmp"); + mount_move(old, "", "/sys"); + mount_move(old, "", "/overlay"); + + return 0; +} + + +static int fopivot(char *rw_root, char *ro_root) +{ + char overlay[64], lowerdir[64]; + + DEBUG(2, "%s %s\n", rw_root, ro_root); + + if (check_fs_exists("overlay")) { + ERROR("BUG: no suitable fs found\n"); + return -1; + } + + snprintf(overlay, sizeof(overlay), "overlayfs:%s", rw_root); + snprintf(lowerdir, sizeof(lowerdir), "lowerdir=/,upperdir=%s", rw_root); + + if (mount(overlay, "/mnt", "overlayfs", MS_NOATIME, lowerdir)) { + ERROR("mount failed: %s\n", strerror(errno)); + return -1; + } + + return pivot("/mnt", ro_root); +} + +static int ramoverlay(void) +{ + DEBUG(2, "\n"); + + mkdir("/tmp/root", 0755); + mount("tmpfs", "/tmp/root", "tmpfs", MS_NOATIME, "mode=0755"); + + return fopivot("/tmp/root", "/rom"); +} + +static int switch2jffs(void) +{ + char mtd[32]; + + if (find_mtd_block("rootfs_data", mtd, sizeof(mtd))) { + ERROR("no rootfs_data was found\n"); + return -1; + } + + if (mount(mtd, "/rom/overlay", "jffs2", MS_NOATIME, NULL)) { + ERROR("failed - mount -t jffs2 %s /rom/overlay: %s\n", mtd, strerror(errno)); + return -1; + } + + if (mount("none", "/", NULL, MS_NOATIME | MS_REMOUNT, 0)) { + ERROR("failed - mount -o remount,ro none: %s\n", strerror(errno)); + return -1; + } + + system("cp -a /tmp/root/* /rom/overlay"); + + if (pivot("/rom", "/mnt")) { + ERROR("failed - pivot /rom /mnt: %s\n", strerror(errno)); + return -1; + } + + if (mount_move("/mnt", "/tmp/root", "")) { + ERROR("failed - mount -o move /mnt /tmp/root %s\n", strerror(errno)); + return -1; + } + + return fopivot("/overlay", "/rom"); +} + +static int handle_whiteout(const char *dir) +{ + struct stat s; + char link[256]; + ssize_t sz; + struct dirent **namelist; + int n; + + n = scandir(dir, &namelist, NULL, NULL); + + if (n < 1) + return -1; + + while (n--) { + char file[256]; + + snprintf(file, sizeof(file), "%s%s", dir, namelist[n]->d_name); + if (!lstat(file, &s) && S_ISLNK(s.st_mode)) { + sz = readlink(file, link, sizeof(link) - 1); + if (sz > 0) { + char *orig; + + link[sz] = '\0'; + orig = strstr(&file[1], "/"); + if (orig && !strcmp(link, "(overlay-whiteout)")) { + DEBUG(1, "unlinking %s\n", orig); + unlink(orig); + } + } + } + free(namelist[n]); + } + free(namelist); + + return 0; +} + +static int main_switch2jffs(int argc, char **argv) +{ + char mtd[32]; + char *mp; + int ret = -1; + + if (check_fs_exists("overlay")) { + ERROR("overlayfs not found\n"); + return ret; + } + + find_mtd_block("rootfs_data", mtd, sizeof(mtd)); + mp = find_mount_point(mtd, NULL); + if (mp) { + LOG("rootfs_data:%s is already mounted as %s\n", mtd, mp); + return -1; + } + + if (find_mtd_char("rootfs_data", mtd, sizeof(mtd))) { + ERROR("no rootfs_data was found\n"); + return ret; + } + + switch (jffs2_ready(mtd)) { + case FS_NONE: + ERROR("no jffs2 marker found\n"); + /* fall through */ + + case FS_DEADCODE: + ret = switch2jffs(); + if (!ret) { + DEBUG(1, "doing fo cleanup\n"); + umount2("/tmp/root", MNT_DETACH); + foreachdir("/overlay/", handle_whiteout); + } + break; + + case FS_JFFS2: + ret = do_mount_jffs2(); + if (ret) + break; + if (mount_move("/tmp", "", "/overlay") || fopivot("/overlay", "/rom")) { + ERROR("switching to jffs2 failed\n"); + ret = -1; + } + break; + } + + return ret; +} + +static int ask_user(int argc, char **argv) +{ + if ((argc < 2) || strcmp(argv[1], "-y")) { + LOG("This will erase all settings and remove any installed packages. Are you sure? [N/y]\n"); + if (getchar() != 'y') + return -1; + } + return 0; + +} + +static int handle_rmdir(const char *dir) +{ + struct stat s; + struct dirent **namelist; + int n; + + n = scandir(dir, &namelist, NULL, NULL); + + if (n < 1) + return -1; + + while (n--) { + char file[256]; + + snprintf(file, sizeof(file), "%s%s", dir, namelist[n]->d_name); + if (!lstat(file, &s) && !S_ISDIR(s.st_mode)) { + DEBUG(1, "unlinking %s\n", file); + unlink(file); + } + free(namelist[n]); + } + free(namelist); + + DEBUG(1, "rmdir %s\n", dir); + rmdir(dir); + + return 0; +} + +static int main_jffs2reset(int argc, char **argv) +{ + char mtd[32]; + char *mp; + + if (ask_user(argc, argv)) + return -1; + + if (check_fs_exists("overlay")) { + ERROR("overlayfs not found\n"); + return -1; + } + + if (find_mtd_block("rootfs_data", mtd, sizeof(mtd))) { + ERROR("no rootfs_data was found\n"); + return -1; + } + + mp = find_mount_point(mtd, "jffs2"); + if (mp) { + LOG("%s is mounted as %s, only ereasing files\n", mtd, mp); + foreachdir(mp, handle_rmdir); + mount(mp, "/", NULL, MS_REMOUNT, 0); + } else { + LOG("%s is not mounted, erasing it\n", mtd); + find_mtd_char("rootfs_data", mtd, sizeof(mtd)); + mtd_erase(mtd); + } + + return 0; +} + +static int main_jffs2mark(int argc, char **argv) +{ + FILE *fp; + __u32 deadc0de = __cpu_to_be32(0xdeadc0de); + char mtd[32]; + size_t sz; + + if (ask_user(argc, argv)) + return -1; + + if (find_mtd_block("rootfs_data", mtd, sizeof(mtd))) { + ERROR("no rootfs_data was found\n"); + return -1; + } + + fp = fopen(mtd, "w"); + LOG("%s - marking with deadc0de\n", mtd); + if (!fp) { + ERROR("opening %s failed\n", mtd); + return -1; + } + + sz = fwrite(&deadc0de, sizeof(deadc0de), 1, fp); + fclose(fp); + + if (sz != 1) { + ERROR("writing %s failed: %s\n", mtd, strerror(errno)); + return -1; + } + + return 0; +} + +int main(int argc, char **argv) +{ + char *mp; + char mtd[32]; + + argv0 = basename(*argv); + + if (!strcmp(basename(*argv), "switch2jffs")) + return main_switch2jffs(argc, argv); + + if (!strcmp(basename(*argv), "jffs2mark")) + return main_jffs2mark(argc, argv); + + if (!strcmp(basename(*argv), "jffs2reset")) + return main_jffs2reset(argc, argv); + + if (find_mtd_char("rootfs_data", mtd, sizeof(mtd))) { + if (!find_mtd_char("rootfs", mtd, sizeof(mtd))) + mtd_unlock(mtd); + LOG("mounting /dev/root\n"); + mount("/dev/root", "/", NULL, MS_NOATIME | MS_REMOUNT, 0); + } else { + switch (jffs2_ready(mtd)) { + case FS_NONE: + case FS_DEADCODE: + return ramoverlay(); + + case FS_JFFS2: + find_mtd_block("rootfs_data", mtd, sizeof(mtd)); + mp = find_mount_point(mtd, NULL); + if (mp) { + LOG("rootfs_data:%s is already mounted as %s\n", mtd, mp); + return -1; + } + + do_mount_jffs2(); + DEBUG(1, "switching to jffs2\n"); + if (mount_move("/tmp", "", "/overlay") || fopivot("/overlay", "/rom")) { + ERROR("switching to jffs2 failed - fallback to ramoverlay\n"); + return ramoverlay(); + } + } + } + + return 0; +} -- 2.11.0