├── service ├── setlbf.c ├── service.h ├── watch.c ├── instance.h ├── validate.c └── trigger.c ├── .gitignore ├── libc-compat.h ├── make_capabilities_h.sh ├── upgraded ├── CMakeLists.txt └── upgraded.c ├── .github └── workflows │ └── build.yml ├── jail ├── seccomp.h ├── cgroups-bpf.h ├── jail.h ├── netifd.h ├── cgroups.h ├── seccomp-oci.h ├── seccomp-syscalls-helpers.h ├── elf.h ├── log.h ├── seccomp.c ├── capabilities.h ├── fs.h ├── preload.c ├── seccomp-bpf.h ├── capabilities.c ├── elf.c ├── seccomp-oci.c └── cgroups-bpf.c ├── sysupgrade.h ├── rcS.h ├── initd ├── init.h ├── early.c ├── mkdev.c ├── preinit.c └── init.c ├── container.h ├── log.h ├── utils ├── askfirst.c ├── utils.h └── utils.c ├── plug ├── hotplug.h ├── coldplug.c └── udevtrigger.c ├── preload.h ├── watchdog.h ├── procd.h ├── trace ├── preload.c └── trace.c ├── make_syscall_h.sh ├── ubus.c ├── signal.c ├── sysupgrade.c ├── procd.c ├── CMakeLists.txt ├── watchdog.c ├── state.c ├── rcS.c ├── inittab.c └── hotplug-dispatch.c /service/setlbf.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static void __attribute__((constructor)) setlbf(void) 4 | { 5 | setbuf(stdout, NULL); 6 | } 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | procd 2 | askfirst 3 | udevtrigger 4 | init 5 | upgraded/upgraded 6 | .* 7 | Makefile 8 | CMakeCache.txt 9 | CMakeFiles 10 | utrace 11 | ujail 12 | *.so 13 | *.cmake 14 | install_manifest.txt 15 | -------------------------------------------------------------------------------- /libc-compat.h: -------------------------------------------------------------------------------- 1 | #ifndef __PROCD_LIBC_COMPAT_H 2 | #define __PROCD_LIBC_COMPAT_H 3 | 4 | #if defined(__GLIBC__) && !defined(__UCLIBC__) 5 | static inline int ignore(int x) {return x;} 6 | #else 7 | #define ignore(x) x 8 | #endif 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /make_capabilities_h.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | CC=$1 4 | [ -n "$TARGET_CC_NOCACHE" ] && CC=$TARGET_CC_NOCACHE 5 | 6 | echo "#include " 7 | echo "static const char *capabilities_names[] = {" 8 | echo "#include " | ${CC} -E -dM - | grep '#define CAP' | grep -vE '(CAP_TO|CAP_LAST_CAP)' | \ 9 | awk '{print $3" "$2}' | sort -n | awk '{print " ["$1"]\t= \""tolower($2)"\","}' 10 | echo "};" 11 | -------------------------------------------------------------------------------- /upgraded/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | PROJECT(upgraded C) 4 | FIND_PATH(ubox_include_dir libubox/uloop.h) 5 | INCLUDE_DIRECTORIES(${ubox_include_dir}) 6 | ADD_DEFINITIONS(-Os -ggdb -Wall -Werror --std=gnu99 -Wmissing-declarations) 7 | ADD_EXECUTABLE(upgraded upgraded.c ../watchdog.c) 8 | TARGET_LINK_LIBRARIES(upgraded ${ubox}) 9 | INSTALL(TARGETS upgraded 10 | RUNTIME DESTINATION sbin 11 | ) 12 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: OpenWrt CI testing 2 | 3 | on: [ push, pull_request ] 4 | env: 5 | CI_ENABLE_UNIT_TESTING: 0 6 | CI_TARGET_BUILD_DEPENDS: libubox 7 | CI_CMAKE_EXTRA_BUILD_ARGS: -DJAIL_SUPPORT=1 8 | 9 | jobs: 10 | native_testing: 11 | name: Various native checks 12 | runs-on: ubuntu-20.04 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | 17 | - uses: ynezz/gh-actions-openwrt-ci-native@v0.0.1 18 | 19 | - name: Upload build artifacts 20 | uses: actions/upload-artifact@v2 21 | if: failure() 22 | with: 23 | name: native-build-artifacts 24 | if-no-files-found: ignore 25 | path: | 26 | build/scan 27 | tests/cram/**/*.t.err 28 | -------------------------------------------------------------------------------- /jail/seccomp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 John Crispin 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License version 2.1 6 | * as published by the Free Software Foundation 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | #ifndef _JAIL_SECCOMP_H_ 14 | #define _JAIL_SECCOMP_H_ 15 | 16 | #include 17 | #include 18 | 19 | int install_syscall_filter(const char *argv, const char *file); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /jail/cgroups-bpf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Daniel Golle 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License version 2.1 6 | * as published by the Free Software Foundation 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | #ifndef _JAIL_CGROUPS_BPF_H 15 | #define _JAIL_CGROUPS_BPF_H 16 | 17 | int parseOCIlinuxcgroups_devices(struct blob_attr *msg); 18 | int attach_cgroups_ebpf(int cgroup_dirfd); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /jail/jail.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Etienne Champetier 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License version 2.1 6 | * as published by the Free Software Foundation 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | #ifndef _JAIL_JAIL_H_ 14 | #define _JAIL_JAIL_H_ 15 | 16 | int mount_bind(const char *root, const char *path, int readonly, int error); 17 | int ns_open_pid(const char *nstype, const pid_t target_ns); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /jail/netifd.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Daniel Golle 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License version 2.1 6 | * as published by the Free Software Foundation 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | #ifndef _JAIL_NETIFD_H 15 | #define _JAIL_NETIFD_H 16 | #include 17 | 18 | int jail_network_start(struct ubus_context *new_ctx, char *new_jail_name, pid_t new_ns_pid); 19 | int jail_network_stop(void); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /jail/cgroups.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Daniel Golle 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License version 2.1 6 | * as published by the Free Software Foundation 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | #ifndef _JAIL_CGROUPS_H 15 | #define _JAIL_CGROUPS_H 16 | 17 | void cgroups_init(const char *p); 18 | int parseOCIlinuxcgroups(struct blob_attr *msg); 19 | void cgroups_apply(pid_t pid); 20 | void cgroups_free(void); 21 | void cgroups_prepare(void); 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /sysupgrade.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 Matthias Schiffer 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License version 2.1 6 | * as published by the Free Software Foundation 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | #ifndef __PROCD_SYSUPGRADE_H 15 | #define __PROCD_SYSUPGRADE_H 16 | 17 | struct blob_attr; 18 | 19 | void sysupgrade_exec_upgraded(const char *prefix, char *path, 20 | const char *backup, char *command, 21 | struct blob_attr *options); 22 | 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /rcS.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Felix Fietkau 3 | * Copyright (C) 2013 John Crispin 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public License version 2.1 7 | * as published by the Free Software Foundation 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | */ 14 | 15 | #ifndef __PROCD_RCS_H 16 | #define __PROCD_RCS_H 17 | 18 | #include 19 | 20 | extern int rcS(char *pattern, char *param, void (*q_empty)(struct runqueue *)); 21 | extern int rc(const char *file, char *param); 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /initd/init.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 John Crispin 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License version 2.1 6 | * as published by the Free Software Foundation 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | #ifndef _INIT_H__ 15 | #define _INIT_H__ 16 | 17 | #include 18 | 19 | #include "../log.h" 20 | 21 | #ifndef EARLY_PATH 22 | #define EARLY_PATH "/usr/sbin:/sbin:/usr/bin:/bin" 23 | #endif 24 | 25 | void preinit(void); 26 | void early(void); 27 | int mkdev(const char *progname, int progmode); 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /container.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 Paul Spooren 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License version 2.1 6 | * as published by the Free Software Foundation 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | #ifndef __CONTAINER_H 15 | #define __CONTAINER_H 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | static inline bool is_container() { 22 | struct stat s; 23 | int r = stat("/.dockerenv", &s); 24 | int pv_r = stat("/pantavisor", &s); 25 | return !!getenv("container") || r == 0 || pv_r == 0; 26 | } 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /jail/seccomp-oci.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Daniel Golle 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License version 2.1 6 | * as published by the Free Software Foundation 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | #ifndef _JAIL_SECCOMP_OCI_H_ 14 | #define _JAIL_SECCOMP_OCI_H_ 15 | 16 | #include 17 | 18 | struct sock_fprog *parseOCIlinuxseccomp(struct blob_attr *msg); 19 | int applyOCIlinuxseccomp(struct sock_fprog *prog); 20 | 21 | #ifndef SECCOMP_SUPPORT 22 | struct sock_fprog *parseOCIlinuxseccomp(struct blob_attr *msg) { 23 | return NULL; 24 | } 25 | 26 | int applyOCIlinuxseccomp(struct sock_fprog *prog) { 27 | return ENOTSUP; 28 | } 29 | #endif 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /jail/seccomp-syscalls-helpers.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 John Crispin 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License version 2.1 6 | * as published by the Free Software Foundation 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | #ifndef _JAIL_SECCOMP_HELPERS_H_ 14 | #define _JAIL_SECCOMP_HELPERS_H_ 15 | 16 | static int find_syscall(const char *name) 17 | { 18 | int i; 19 | 20 | for (i = 0; i < SYSCALL_COUNT; i++) { 21 | int sc = syscall_index_to_number(i); 22 | if (syscall_name(sc) && !strcmp(syscall_name(sc), name)) 23 | return sc; 24 | } 25 | 26 | return -1; 27 | } 28 | 29 | static void set_filter(struct sock_filter *filter, __u16 code, __u8 jt, __u8 jf, __u32 k) 30 | { 31 | filter->code = code; 32 | filter->jt = jt; 33 | filter->jf = jf; 34 | filter->k = k; 35 | } 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /log.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Felix Fietkau 3 | * Copyright (C) 2013 John Crispin 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public License version 2.1 7 | * as published by the Free Software Foundation 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | */ 14 | 15 | #ifndef __LOG_H 16 | #define __LOG_H 17 | 18 | #include 19 | 20 | #define DEBUG(level, fmt, ...) do { \ 21 | if (debug >= level) { \ 22 | ulog(LOG_NOTICE, fmt, ## __VA_ARGS__); \ 23 | } } while (0) 24 | 25 | #define P_DEBUG(level, fmt, ...) do { \ 26 | if (debug >= level) { \ 27 | ulog(LOG_NOTICE, fmt, ## __VA_ARGS__); \ 28 | } else { \ 29 | procd_udebug_printf(fmt, ## __VA_ARGS__); \ 30 | } } while (0) 31 | 32 | #define LOG ULOG_INFO 33 | #define ERROR ULOG_ERR 34 | 35 | extern unsigned int debug; 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /jail/elf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 John Crispin 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License version 2.1 6 | * as published by the Free Software Foundation 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | #ifndef _JAIL_ELF_H_ 14 | #define _JAIL_ELF_H_ 15 | 16 | #include 17 | #include 18 | 19 | struct library { 20 | struct avl_node avl; 21 | char *name; 22 | char *path; 23 | }; 24 | 25 | struct library_path { 26 | struct list_head list; 27 | char *path; 28 | }; 29 | 30 | extern struct avl_tree libraries; 31 | 32 | void alloc_library(const char *path, const char *name); 33 | int elf_load_deps(const char *path, const char *map); 34 | const char* find_lib(const char *file); 35 | void init_library_search(void); 36 | int lib_open(char **fullpath, const char *file); 37 | void free_library_search(void); 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /jail/log.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 John Crispin 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License version 2.1 6 | * as published by the Free Software Foundation 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | #ifndef _JAIL_LOG_H_ 14 | #define _JAIL_LOG_H_ 15 | 16 | extern int debug; 17 | #include 18 | #include 19 | 20 | #define INFO(fmt, ...) do { \ 21 | printf("jail: "fmt, ## __VA_ARGS__); \ 22 | } while (0) 23 | #define WARNING(fmt, ...) do { \ 24 | syslog(LOG_WARNING, "jail: "fmt, ## __VA_ARGS__); \ 25 | printf("jail: "fmt, ## __VA_ARGS__); \ 26 | } while (0) 27 | #define ERROR(fmt, ...) do { \ 28 | syslog(LOG_ERR, "jail: "fmt, ## __VA_ARGS__); \ 29 | fprintf(stderr,"jail: "fmt, ## __VA_ARGS__); \ 30 | } while (0) 31 | #define DEBUG(fmt, ...) do { \ 32 | if (debug) printf("jail: "fmt, ## __VA_ARGS__); \ 33 | } while (0) 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /utils/askfirst.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Felix Fietkau 3 | * Copyright (C) 2013 John Crispin 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public License version 2.1 7 | * as published by the Free Software Foundation 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | */ 14 | 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | int main(int argc, char **argv) 23 | { 24 | int c; 25 | 26 | printf("Please press Enter to activate this console.\n"); 27 | do { 28 | c = getchar(); 29 | if (c == EOF) 30 | return -1; 31 | } 32 | while (c != 0xA); 33 | 34 | if (argc < 2) { 35 | printf("%s needs to be called with at least 1 parameter\n", argv[0]); 36 | return -1; 37 | } 38 | 39 | execvp(argv[1], &argv[1]); 40 | printf("Failed to execute %s\n", argv[1]); 41 | 42 | return -1; 43 | } 44 | -------------------------------------------------------------------------------- /jail/seccomp.c: -------------------------------------------------------------------------------- 1 | /* 2 | * seccomp example with syscall reporting 3 | * 4 | * Copyright (c) 2012 The Chromium OS Authors 5 | * Authors: 6 | * Kees Cook 7 | * Will Drewry 8 | * 9 | * Use of this source code is governed by a BSD-style license that can be 10 | * found in the LICENSE file. 11 | */ 12 | #define _GNU_SOURCE 1 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include "log.h" 22 | #include "seccomp.h" 23 | #include "seccomp-oci.h" 24 | 25 | int install_syscall_filter(const char *argv, const char *file) 26 | { 27 | struct blob_buf b = { 0 }; 28 | struct sock_fprog *prog = NULL; 29 | 30 | DEBUG("%s: setting up syscall filter\n", argv); 31 | 32 | blob_buf_init(&b, 0); 33 | if (!blobmsg_add_json_from_file(&b, file)) { 34 | ERROR("%s: failed to load %s\n", argv, file); 35 | return -1; 36 | } 37 | 38 | prog = parseOCIlinuxseccomp(b.head); 39 | if (!prog) { 40 | ERROR("%s: failed to parse seccomp filter rules %s\n", argv, file); 41 | return -1; 42 | } 43 | 44 | return applyOCIlinuxseccomp(prog); 45 | } 46 | -------------------------------------------------------------------------------- /plug/hotplug.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Felix Fietkau 3 | * Copyright (C) 2013 John Crispin 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public License version 2.1 7 | * as published by the Free Software Foundation 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | */ 14 | 15 | #ifndef __PROCD_HOTPLUG_H 16 | #define __PROCD_HOTPLUG_H 17 | 18 | #include 19 | 20 | #ifndef DISABLE_INIT 21 | void hotplug(char *rules); 22 | int hotplug_run(char *rules); 23 | void hotplug_shutdown(void); 24 | void hotplug_last_event(uloop_timeout_handler handler); 25 | void procd_coldplug(void); 26 | #else 27 | static inline void hotplug(char *rules) 28 | { 29 | } 30 | 31 | static inline int hotplug_run(char *rules) 32 | { 33 | return 0; 34 | } 35 | 36 | static inline void hotplug_shutdown(void) 37 | { 38 | } 39 | 40 | static inline void hotplug_last_event(uloop_timeout_handler handler) 41 | { 42 | } 43 | 44 | static inline void procd_coldplug(void) 45 | { 46 | } 47 | #endif 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /jail/capabilities.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Etienne CHAMPETIER 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License version 2.1 6 | * as published by the Free Software Foundation 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | #ifndef _JAIL_CAPABILITIES_H_ 14 | #define _JAIL_CAPABILITIES_H_ 15 | 16 | #include 17 | #include 18 | 19 | struct jail_capset { 20 | uint64_t bounding; 21 | uint64_t effective; 22 | uint64_t inheritable; 23 | uint64_t permitted; 24 | uint64_t ambient; 25 | uint8_t apply; 26 | }; 27 | 28 | int parseOCIcapabilities(struct jail_capset *capset, struct blob_attr *msg); 29 | int parseOCIcapabilities_from_file(struct jail_capset *capset, const char *file); 30 | int applyOCIcapabilities(struct jail_capset capset, uint64_t retain); 31 | 32 | /* capget/capset syscall wrappers are provided by libc */ 33 | extern int capget(cap_user_header_t header, cap_user_data_t data); 34 | extern int capset(cap_user_header_t header, const cap_user_data_t data); 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /jail/fs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Etienne Champetier 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License version 2.1 6 | * as published by the Free Software Foundation 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | #ifndef _JAIL_FS_H_ 14 | #define _JAIL_FS_H_ 15 | 16 | #include 17 | #include 18 | 19 | int add_mount(const char *source, const char *target, const char *filesystemtype, 20 | unsigned long mountflags, unsigned long propflags, const char *optstr, int error); 21 | int add_mount_inner(const char *source, const char *target, const char *filesystemtype, 22 | unsigned long mountflags, unsigned long propflags, const char *optstr, int error); 23 | int add_mount_bind(const char *path, int readonly, int error); 24 | int parseOCImount(struct blob_attr *msg); 25 | int add_2paths_and_deps(const char *path, const char *path2, int readonly, int error, int lib); 26 | 27 | static inline int add_path_and_deps(const char *path, int readonly, int error, int lib) 28 | { 29 | return add_2paths_and_deps(path, path, readonly, error, lib); 30 | } 31 | 32 | int mount_all(const char *jailroot); 33 | void mount_list_init(void); 34 | void mount_free(void); 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /preload.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 John Crispin 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License version 2.1 6 | * as published by the Free Software Foundation 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | #include 15 | 16 | #ifndef __unbounded 17 | #define __unbounded 18 | #endif 19 | 20 | #ifndef attribute_unused 21 | #define attribute_unused __attribute__ ((unused)) 22 | #endif 23 | typedef int (*main_t)(int, char **, char **); 24 | 25 | typedef int (*start_main_t)(main_t main, int, char *__unbounded *__unbounded, 26 | ElfW(auxv_t) *, 27 | __typeof (main), 28 | void (*fini) (void), 29 | void (*rtld_fini) (void), 30 | void *__unbounded stack_end); 31 | 32 | int __libc_start_main(main_t main, 33 | int argc, 34 | char **argv, 35 | ElfW(auxv_t) *auxvec, 36 | __typeof (main) init, 37 | void (*fini) (void), 38 | void (*rtld_fini) (void), 39 | void *stack_end); 40 | 41 | 42 | typedef void (*uClibc_main)(main_t main, 43 | int argc, 44 | char **argv, 45 | void (*app_init)(void), 46 | void (*app_fini)(void), 47 | void (*rtld_fini)(void), 48 | void *stack_end attribute_unused); 49 | 50 | void __uClibc_main(main_t main, 51 | int argc, 52 | char **argv, 53 | void (*app_init)(void), 54 | void (*app_fini)(void), 55 | void (*rtld_fini)(void), 56 | void *stack_end attribute_unused); 57 | -------------------------------------------------------------------------------- /watchdog.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Felix Fietkau 3 | * Copyright (C) 2013 John Crispin 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public License version 2.1 7 | * as published by the Free Software Foundation 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | */ 14 | 15 | #ifndef __PROCD_WATCHDOG_H 16 | #define __PROCD_WATCHDOG_H 17 | 18 | #include 19 | 20 | #ifndef DISABLE_INIT 21 | void watchdog_init(int preinit); 22 | char* watchdog_fd(void); 23 | int watchdog_timeout(int timeout); 24 | int watchdog_frequency(int frequency); 25 | void watchdog_set_magicclose(bool val); 26 | bool watchdog_get_magicclose(void); 27 | void watchdog_set_stopped(bool val); 28 | bool watchdog_get_stopped(void); 29 | void watchdog_set_cloexec(bool val); 30 | void watchdog_ping(void); 31 | #else 32 | static inline void watchdog_init(int preinit) 33 | { 34 | } 35 | 36 | static inline char* watchdog_fd(void) 37 | { 38 | return ""; 39 | } 40 | 41 | static inline int watchdog_timeout(int timeout) 42 | { 43 | return 0; 44 | } 45 | 46 | static inline int watchdog_frequency(int frequency) 47 | { 48 | return 0; 49 | } 50 | 51 | static inline void watchdog_set_magicclose(bool val) 52 | { 53 | } 54 | 55 | static inline bool watchdog_get_magicclose(void) 56 | { 57 | return false; 58 | } 59 | 60 | static inline void watchdog_set_stopped(bool val) 61 | { 62 | } 63 | 64 | static inline bool watchdog_get_stopped(void) 65 | { 66 | return true; 67 | } 68 | 69 | static inline void watchdog_set_cloexec(bool val) 70 | { 71 | } 72 | 73 | static inline void watchdog_ping(void) 74 | { 75 | } 76 | 77 | #endif 78 | 79 | #endif 80 | -------------------------------------------------------------------------------- /service/service.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Felix Fietkau 3 | * Copyright (C) 2013 John Crispin 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public License version 2.1 7 | * as published by the Free Software Foundation 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | */ 14 | 15 | #ifndef __PROCD_SERVICE_H 16 | #define __PROCD_SERVICE_H 17 | 18 | #include 19 | #include 20 | #include 21 | #include "../utils/utils.h" 22 | 23 | extern struct avl_tree services; 24 | extern struct avl_tree containers; 25 | 26 | struct vrule { 27 | struct avl_node avl; 28 | char *option; 29 | char *rule; 30 | }; 31 | 32 | struct validate { 33 | struct avl_node avl; 34 | struct list_head list; 35 | 36 | char *package; 37 | char *type; 38 | 39 | struct avl_tree rules; 40 | }; 41 | 42 | struct service { 43 | struct avl_node avl; 44 | const char *name; 45 | bool deleted; 46 | bool autostart; 47 | bool container; 48 | 49 | struct blob_attr *trigger; 50 | struct vlist_tree instances; 51 | struct list_head validators; 52 | struct blob_attr *data; 53 | struct blobmsg_list data_blob; 54 | }; 55 | 56 | void service_validate_add(struct service *s, struct blob_attr *attr); 57 | void service_validate_dump(struct blob_buf *b, struct service *s); 58 | void service_validate_dump_all(struct blob_buf *b, char *p, char *s); 59 | int service_start_early(char *name, char *cmdline, char *user, char *group); 60 | void service_stopped(struct service *s); 61 | void service_validate_del(struct service *s); 62 | void service_event(const char *type, const char *service, const char *instance); 63 | void service_data_trigger(struct blobmsg_list *list); 64 | void service_stop_all(void); 65 | 66 | 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /procd.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Felix Fietkau 3 | * Copyright (C) 2013 John Crispin 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public License version 2.1 7 | * as published by the Free Software Foundation 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | */ 14 | 15 | #ifndef __PROCD_H 16 | #define __PROCD_H 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | 26 | #include "log.h" 27 | 28 | #define __init __attribute__((constructor)) 29 | 30 | extern char *ubus_socket; 31 | 32 | void procd_connect_ubus(void); 33 | void procd_reconnect_ubus(int reconnect); 34 | void ubus_init_hotplug(struct ubus_context *ctx); 35 | void ubus_init_service(struct ubus_context *ctx); 36 | void ubus_init_system(struct ubus_context *ctx); 37 | #ifndef DISABLE_INIT 38 | void hotplug_ubus_event(struct blob_attr *data); 39 | #else 40 | static inline void hotplug_ubus_event(struct blob_attr *data) 41 | { 42 | } 43 | #endif 44 | 45 | void procd_state_next(void); 46 | void procd_state_ubus_connect(void); 47 | void procd_shutdown(int event); 48 | void procd_early(void); 49 | void procd_preinit(void); 50 | void procd_signal(void); 51 | void procd_signal_preinit(void); 52 | void procd_inittab(void); 53 | void procd_inittab_run(const char *action); 54 | void procd_inittab_kill(void); 55 | void procd_bcast_event(char *event, struct blob_attr *msg); 56 | 57 | struct trigger; 58 | void trigger_event(const char *type, struct blob_attr *data); 59 | void trigger_add(struct blob_attr *rule, void *id); 60 | void trigger_del(void *id); 61 | 62 | void watch_add(const char *_name, void *id); 63 | void watch_del(void *id); 64 | void watch_ubus(struct ubus_context *ctx); 65 | 66 | void procd_udebug_printf(const char *format, ...); 67 | void procd_udebug_set_enabled(bool val); 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /plug/coldplug.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Felix Fietkau 3 | * Copyright (C) 2013 John Crispin 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public License version 2.1 7 | * as published by the Free Software Foundation 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | */ 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | #include 21 | 22 | #include "../procd.h" 23 | #include "../libc-compat.h" 24 | 25 | #include "hotplug.h" 26 | #include "../container.h" 27 | 28 | static struct uloop_process udevtrigger; 29 | 30 | static void coldplug_complete(struct uloop_timeout *t) 31 | { 32 | P_DEBUG(4, "Coldplug complete\n"); 33 | hotplug_last_event(NULL); 34 | procd_state_next(); 35 | } 36 | 37 | static void udevtrigger_complete(struct uloop_process *proc, int ret) 38 | { 39 | P_DEBUG(4, "Finished udevtrigger\n"); 40 | hotplug_last_event(coldplug_complete); 41 | } 42 | 43 | void procd_coldplug(void) 44 | { 45 | char *argv[] = { "udevtrigger", NULL }; 46 | unsigned int oldumask = umask(0); 47 | 48 | if (!is_container()) { 49 | umount2("/dev/pts", MNT_DETACH); 50 | umount2("/dev/", MNT_DETACH); 51 | mount("tmpfs", "/dev", "tmpfs", MS_NOATIME | MS_NOEXEC | MS_NOSUID, "mode=0755,size=512K"); 52 | mkdir("/dev/pts", 0755); 53 | mount("devpts", "/dev/pts", "devpts", MS_NOATIME | MS_NOEXEC | MS_NOSUID, 0); 54 | } 55 | 56 | ignore(symlink("/tmp/shm", "/dev/shm")); 57 | umask(oldumask); 58 | udevtrigger.cb = udevtrigger_complete; 59 | udevtrigger.pid = fork(); 60 | if (!udevtrigger.pid) { 61 | execvp(argv[0], argv); 62 | ERROR("Failed to start coldplug: %m\n"); 63 | exit(EXIT_FAILURE); 64 | } 65 | 66 | if (udevtrigger.pid <= 0) { 67 | ERROR("Failed to start new coldplug instance: %m\n"); 68 | return; 69 | } 70 | 71 | uloop_process_add(&udevtrigger); 72 | 73 | P_DEBUG(4, "Launched coldplug instance, pid=%d\n", (int) udevtrigger.pid); 74 | } 75 | -------------------------------------------------------------------------------- /trace/preload.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 John Crispin 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License version 2.1 6 | * as published by the Free Software Foundation 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | #define _GNU_SOURCE 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "../preload.h" 25 | 26 | #define ERROR(fmt, ...) do { \ 27 | fprintf(stderr,"perload-jail: "fmt, ## __VA_ARGS__); \ 28 | } while (0) 29 | 30 | static main_t __main__; 31 | 32 | static int __preload_main__(int argc, char **argv, char **envp) 33 | { 34 | unsetenv("LD_PRELOAD"); 35 | kill(getpid(), SIGSTOP); 36 | 37 | return (*__main__)(argc, argv, envp); 38 | } 39 | 40 | int __libc_start_main(main_t main, 41 | int argc, 42 | char **argv, 43 | ElfW(auxv_t) *auxvec, 44 | __typeof (main) init, 45 | void (*fini) (void), 46 | void (*rtld_fini) (void), 47 | void *stack_end) 48 | { 49 | start_main_t __start_main__; 50 | 51 | __start_main__ = dlsym(RTLD_NEXT, "__libc_start_main"); 52 | if (!__start_main__) { 53 | ERROR("failed to find __libc_start_main %s\n", dlerror()); 54 | return -1; 55 | } 56 | __main__ = main; 57 | 58 | return (*__start_main__)(__preload_main__, argc, argv, auxvec, 59 | init, fini, rtld_fini, stack_end); 60 | } 61 | 62 | void __uClibc_main(main_t main, 63 | int argc, 64 | char **argv, 65 | void (*app_init)(void), 66 | void (*app_fini)(void), 67 | void (*rtld_fini)(void), 68 | void *stack_end attribute_unused) 69 | { 70 | uClibc_main __start_main__; 71 | 72 | __start_main__ = dlsym(RTLD_NEXT, "__uClibc_main"); 73 | if (!__start_main__) { 74 | ERROR("failed to find __uClibc_main %s\n", dlerror()); 75 | return; 76 | } 77 | 78 | __main__ = main; 79 | 80 | return (*__start_main__)(__preload_main__, argc, argv, 81 | app_init, app_fini, rtld_fini, stack_end); 82 | } 83 | -------------------------------------------------------------------------------- /utils/utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Felix Fietkau 3 | * Copyright (C) 2013 John Crispin 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public License version 2.1 7 | * as published by the Free Software Foundation 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | */ 14 | 15 | #ifndef __PROCD_UTILS_H 16 | #define __PROCD_UTILS_H 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #define CMDLINE_SIZE 2048 23 | 24 | struct blobmsg_list_node { 25 | struct avl_node avl; 26 | struct blob_attr *data; 27 | }; 28 | 29 | typedef bool (*blobmsg_list_cmp)(struct blobmsg_list_node *l1, struct blobmsg_list_node *l2); 30 | typedef void (*blobmsg_update_cb)(struct blobmsg_list_node *n); 31 | 32 | struct blobmsg_list { 33 | struct avl_tree avl; 34 | int node_offset; 35 | int node_len; 36 | 37 | blobmsg_list_cmp cmp; 38 | }; 39 | 40 | #define blobmsg_list_simple_init(list) \ 41 | __blobmsg_list_init(list, 0, sizeof(struct blobmsg_list_node), NULL) 42 | 43 | #define blobmsg_list_init(list, type, field, cmp) \ 44 | __blobmsg_list_init(list, offsetof(type, field), sizeof(type), cmp) 45 | 46 | #define blobmsg_list_for_each(list, element) \ 47 | avl_for_each_element(&(list)->avl, element, avl) 48 | 49 | void __blobmsg_list_init(struct blobmsg_list *list, int offset, int len, blobmsg_list_cmp cmp); 50 | int blobmsg_list_fill(struct blobmsg_list *list, void *data, int len, bool array); 51 | void blobmsg_list_free(struct blobmsg_list *list); 52 | bool blobmsg_list_equal(struct blobmsg_list *l1, struct blobmsg_list *l2); 53 | void blobmsg_list_move(struct blobmsg_list *list, struct blobmsg_list *src); 54 | char *get_cmdline_val_offset(const char *name, char *out, int len, int offset); 55 | char *get_active_console(char *out, int len); 56 | 57 | #define get_cmdline_val(name, out, len) \ 58 | get_cmdline_val_offset(name, out, len, 0) 59 | 60 | int patch_fd(const char *device, int fd, int flags); 61 | int patch_stdio(const char *device); 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /make_syscall_h.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # syscall reporting example for seccomp 3 | # 4 | # Copyright (c) 2012 The Chromium OS Authors 5 | # Authors: 6 | # Kees Cook 7 | # 8 | # Use of this source code is governed by a BSD-style license that can be 9 | # found in the LICENSE file. 10 | 11 | CC=$1 12 | [ -n "$TARGET_CC_NOCACHE" ] && CC=$TARGET_CC_NOCACHE 13 | 14 | echo "#include " 15 | 16 | # for loongarch __NR3264_* macros 17 | echo "#include " | ${CC} -E -dM - | grep '^#define __NR3264_[a-z0-9_]\+[ \t].*[0-9].*$' 18 | 19 | echo "static const char *__syscall_names[] = {" 20 | echo "#include " | ${CC} -E -dM - | grep '^#define __NR_[a-z0-9_]\+[ \t].*[0-9].*$' | \ 21 | LC_ALL=C sed -r -n -e 's/^\#define[ \t]+__NR_([a-z0-9_]+)[ \t]+([ ()+0-9a-zNR_LSYCABE]+)(.*)/ [\2] = "\1",/p' 22 | echo "};" 23 | 24 | extra_syscalls="$(echo "#include " | ${CC} -E -dM - | sed -r -n -e 's/^#define __ARM_NR_([a-z0-9_]+)/\1/p')" 25 | 26 | cat < 3 | * Copyright (C) 2013 John Crispin 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public License version 2.1 7 | * as published by the Free Software Foundation 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | */ 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "procd.h" 21 | 22 | char *ubus_socket = NULL; 23 | static struct ubus_context *ctx; 24 | static struct uloop_timeout ubus_timer; 25 | static int timeout; 26 | static struct udebug_ubus udebug; 27 | 28 | static void 29 | procd_udebug_cb(struct udebug_ubus *ctx, struct blob_attr *data, bool enabled) 30 | { 31 | procd_udebug_set_enabled(enabled); 32 | } 33 | 34 | static void reset_timeout(void) 35 | { 36 | timeout = 50; 37 | } 38 | 39 | static void timeout_retry(void) 40 | { 41 | uloop_timeout_set(&ubus_timer, timeout); 42 | timeout *= 2; 43 | if (timeout > 1000) 44 | timeout = 1000; 45 | } 46 | 47 | static void 48 | ubus_reconnect_cb(struct uloop_timeout *timeout) 49 | { 50 | if (!ubus_reconnect(ctx, ubus_socket)) { 51 | ubus_add_uloop(ctx); 52 | return; 53 | } 54 | 55 | timeout_retry(); 56 | } 57 | 58 | static void 59 | ubus_disconnect_cb(struct ubus_context *ctx) 60 | { 61 | ubus_timer.cb = ubus_reconnect_cb; 62 | reset_timeout(); 63 | timeout_retry(); 64 | } 65 | 66 | static void 67 | ubus_connect_cb(struct uloop_timeout *timeout) 68 | { 69 | ctx = ubus_connect(ubus_socket); 70 | 71 | if (!ctx) { 72 | DEBUG(4, "Connection to ubus failed\n"); 73 | timeout_retry(); 74 | return; 75 | } 76 | 77 | udebug_ubus_init(&udebug, ctx, "procd", procd_udebug_cb); 78 | ctx->connection_lost = ubus_disconnect_cb; 79 | ubus_init_hotplug(ctx); 80 | ubus_init_service(ctx); 81 | ubus_init_system(ctx); 82 | watch_ubus(ctx); 83 | 84 | DEBUG(2, "Connected to ubus, id=%08x\n", ctx->local_id); 85 | reset_timeout(); 86 | ubus_add_uloop(ctx); 87 | procd_state_ubus_connect(); 88 | } 89 | 90 | void 91 | procd_connect_ubus(void) 92 | { 93 | ubus_timer.cb = ubus_connect_cb; 94 | reset_timeout(); 95 | timeout_retry(); 96 | } 97 | -------------------------------------------------------------------------------- /jail/preload.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 John Crispin 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License version 2.1 6 | * as published by the Free Software Foundation 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | #define _GNU_SOURCE 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "log.h" 22 | #include "seccomp.h" 23 | #include "../preload.h" 24 | 25 | static main_t __main__; 26 | int debug; 27 | 28 | static int __preload_main__(int argc, char **argv, char **envp) 29 | { 30 | char *env_file = getenv("SECCOMP_FILE"); 31 | char *env_debug = getenv("SECCOMP_DEBUG"); 32 | 33 | if (!env_file || !env_file[0]) { 34 | ERROR("SECCOMP_FILE not specified\n"); 35 | return -1; 36 | } 37 | 38 | if (env_debug) 39 | debug = atoi(env_debug); 40 | else 41 | debug = 0; 42 | 43 | if (install_syscall_filter(*argv, env_file)) 44 | return -1; 45 | 46 | unsetenv("LD_PRELOAD"); 47 | unsetenv("SECCOMP_DEBUG"); 48 | unsetenv("SECCOMP_FILE"); 49 | 50 | return (*__main__)(argc, argv, envp); 51 | } 52 | 53 | int __libc_start_main(main_t main, 54 | int argc, 55 | char **argv, 56 | ElfW(auxv_t) *auxvec, 57 | __typeof (main) init, 58 | void (*fini) (void), 59 | void (*rtld_fini) (void), 60 | void *stack_end) 61 | { 62 | start_main_t __start_main__; 63 | 64 | __start_main__ = dlsym(RTLD_NEXT, "__libc_start_main"); 65 | if (!__start_main__) { 66 | INFO("failed to find __libc_start_main %s\n", dlerror()); 67 | return -1; 68 | } 69 | 70 | __main__ = main; 71 | 72 | return (*__start_main__)(__preload_main__, argc, argv, auxvec, 73 | init, fini, rtld_fini, stack_end); 74 | } 75 | 76 | void __uClibc_main(main_t main, 77 | int argc, 78 | char **argv, 79 | void (*app_init)(void), 80 | void (*app_fini)(void), 81 | void (*rtld_fini)(void), 82 | void *stack_end attribute_unused) 83 | { 84 | uClibc_main __start_main__; 85 | 86 | __start_main__ = dlsym(RTLD_NEXT, "__uClibc_main"); 87 | if (!__start_main__) { 88 | INFO("failed to find __uClibc_main %s\n", dlerror()); 89 | return; 90 | } 91 | 92 | __main__ = main; 93 | 94 | return (*__start_main__)(__preload_main__, argc, argv, 95 | app_init, app_fini, rtld_fini, stack_end); 96 | } 97 | -------------------------------------------------------------------------------- /upgraded/upgraded.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Felix Fietkau 3 | * Copyright (C) 2013 John Crispin 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public License version 2.1 7 | * as published by the Free Software Foundation 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | */ 14 | 15 | #define _GNU_SOURCE 16 | 17 | #include 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | 28 | #include "../watchdog.h" 29 | 30 | #ifndef O_PATH 31 | #define O_PATH 010000000 32 | #endif 33 | 34 | static struct uloop_process upgrade_proc; 35 | unsigned int debug = 2; 36 | 37 | static void upgrade_proc_cb(struct uloop_process *proc, int ret) 38 | { 39 | if (ret) 40 | fprintf(stderr, "sysupgrade aborted with return code: %d\n", ret); 41 | uloop_end(); 42 | } 43 | 44 | static void sysupgrade(char *path, char *command) 45 | { 46 | char *args[] = { "/lib/upgrade/stage2", path, command, NULL }; 47 | 48 | upgrade_proc.cb = upgrade_proc_cb; 49 | upgrade_proc.pid = fork(); 50 | if (upgrade_proc.pid < 0) { 51 | fprintf(stderr, "Failed to fork sysupgrade\n"); 52 | return; 53 | } 54 | 55 | if (!upgrade_proc.pid) { 56 | /* Child */ 57 | execvp(args[0], args); 58 | fprintf(stderr, "Failed to exec sysupgrade\n"); 59 | _exit(EXIT_FAILURE); 60 | } 61 | 62 | uloop_process_add(&upgrade_proc); 63 | uloop_run(); 64 | } 65 | 66 | int main(int argc, char **argv) 67 | { 68 | pid_t p = getpid(); 69 | 70 | if (p != 1) { 71 | fprintf(stderr, "this tool needs to run as pid 1\n"); 72 | return 1; 73 | } 74 | 75 | int fd = open("/", O_DIRECTORY|O_PATH); 76 | if (fd < 0) { 77 | fprintf(stderr, "unable to open prefix directory: %m\n"); 78 | return 1; 79 | } 80 | 81 | if (chroot(".") < 0) { 82 | fprintf(stderr, "failed to chroot: %m\n"); 83 | return 1; 84 | } 85 | 86 | if (fchdir(fd) == -1) { 87 | fprintf(stderr, "failed to chdir to prefix directory: %m\n"); 88 | return 1; 89 | } 90 | close(fd); 91 | 92 | if (argc != 3) { 93 | fprintf(stderr, "sysupgrade stage 2 failed, invalid command line\n"); 94 | return 1; 95 | } 96 | 97 | uloop_init(); 98 | watchdog_init(0); 99 | sysupgrade(argv[1], argv[2]); 100 | 101 | reboot(RB_AUTOBOOT); 102 | 103 | return 0; 104 | } 105 | -------------------------------------------------------------------------------- /signal.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Felix Fietkau 3 | * Copyright (C) 2013 John Crispin 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public License version 2.1 7 | * as published by the Free Software Foundation 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | */ 14 | 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | #include "procd.h" 21 | 22 | static void do_reboot(void) 23 | { 24 | LOG("reboot\n"); 25 | fflush(stderr); 26 | sync(); 27 | sleep(2); 28 | reboot(RB_AUTOBOOT); 29 | while (1) 30 | ; 31 | } 32 | 33 | static void signal_shutdown(int signal, siginfo_t *siginfo, void *data) 34 | { 35 | int event = 0; 36 | char *msg = NULL; 37 | 38 | #ifndef DISABLE_INIT 39 | switch(signal) { 40 | case SIGINT: 41 | case SIGTERM: 42 | event = RB_AUTOBOOT; 43 | msg = "reboot"; 44 | break; 45 | case SIGUSR1: 46 | case SIGUSR2: 47 | case SIGPWR: 48 | event = RB_POWER_OFF; 49 | msg = "poweroff"; 50 | break; 51 | } 52 | #endif 53 | 54 | DEBUG(1, "Triggering %s\n", msg); 55 | if (event) 56 | procd_shutdown(event); 57 | } 58 | 59 | struct sigaction sa_shutdown = { 60 | .sa_sigaction = signal_shutdown, 61 | .sa_flags = SA_SIGINFO 62 | }; 63 | 64 | static void signal_crash(int signal, siginfo_t *siginfo, void *data) 65 | { 66 | ERROR("Rebooting as procd has crashed\n"); 67 | do_reboot(); 68 | } 69 | 70 | struct sigaction sa_crash = { 71 | .sa_sigaction = signal_crash, 72 | .sa_flags = SA_SIGINFO 73 | }; 74 | 75 | static void signal_dummy(int signal, siginfo_t *siginfo, void *data) 76 | { 77 | ERROR("Got unexpected signal %d\n", signal); 78 | } 79 | 80 | struct sigaction sa_dummy = { 81 | .sa_sigaction = signal_dummy, 82 | .sa_flags = SA_SIGINFO 83 | }; 84 | 85 | void procd_signal(void) 86 | { 87 | signal(SIGPIPE, SIG_IGN); 88 | if (getpid() != 1) 89 | return; 90 | sigaction(SIGTERM, &sa_shutdown, NULL); 91 | sigaction(SIGINT, &sa_shutdown, NULL); 92 | sigaction(SIGUSR1, &sa_shutdown, NULL); 93 | sigaction(SIGUSR2, &sa_shutdown, NULL); 94 | sigaction(SIGPWR, &sa_shutdown, NULL); 95 | sigaction(SIGSEGV, &sa_crash, NULL); 96 | sigaction(SIGBUS, &sa_crash, NULL); 97 | sigaction(SIGHUP, &sa_dummy, NULL); 98 | sigaction(SIGKILL, &sa_dummy, NULL); 99 | sigaction(SIGSTOP, &sa_dummy, NULL); 100 | #ifndef DISABLE_INIT 101 | reboot(RB_DISABLE_CAD); 102 | #endif 103 | } 104 | -------------------------------------------------------------------------------- /initd/early.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Felix Fietkau 3 | * Copyright (C) 2013 John Crispin 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public License version 2.1 7 | * as published by the Free Software Foundation 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | */ 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "../utils/utils.h" 26 | #include "init.h" 27 | #include "../libc-compat.h" 28 | #include "../container.h" 29 | 30 | static void 31 | early_dev(void) 32 | { 33 | mkdev("*", 0600); 34 | mknod("/dev/null", 0666, makedev(1, 3)); 35 | } 36 | 37 | static void 38 | early_console(const char *dev) 39 | { 40 | struct stat s; 41 | 42 | if (stat(dev, &s)) { 43 | ERROR("Failed to stat %s: %m\n", dev); 44 | return; 45 | } 46 | 47 | if (patch_stdio(dev)) { 48 | ERROR("Failed to setup i/o redirection\n"); 49 | return; 50 | } 51 | 52 | fcntl(STDERR_FILENO, F_SETFL, fcntl(STDERR_FILENO, F_GETFL) | O_NONBLOCK); 53 | } 54 | 55 | static void 56 | early_mounts(void) 57 | { 58 | unsigned int oldumask = umask(0); 59 | 60 | if (!is_container()) { 61 | mount("proc", "/proc", "proc", MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_RELATIME, NULL); 62 | mount("sysfs", "/sys", "sysfs", MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_RELATIME, NULL); 63 | mount("efivars", "/sys/firmware/efi/efivars", "efivarfs", MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_RELATIME, NULL); 64 | mount("cgroup2", "/sys/fs/cgroup", "cgroup2", MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_RELATIME, "nsdelegate"); 65 | mount("tmpfs", "/dev", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_RELATIME, "mode=0755,size=512K"); 66 | ignore(symlink("/tmp/shm", "/dev/shm")); 67 | mkdir("/dev/pts", 0755); 68 | mount("devpts", "/dev/pts", "devpts", MS_NOEXEC | MS_NOSUID | MS_RELATIME, NULL); 69 | 70 | early_dev(); 71 | } 72 | 73 | early_console("/dev/console"); 74 | 75 | mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_NODEV | MS_NOATIME, "mode=01777"); 76 | mkdir("/tmp/shm", 01777); 77 | 78 | mkdir("/tmp/run", 0755); 79 | mkdir("/tmp/lock", 0755); 80 | mkdir("/tmp/state", 0755); 81 | umask(oldumask); 82 | } 83 | 84 | static void 85 | early_env(void) 86 | { 87 | setenv("PATH", EARLY_PATH, 1); 88 | } 89 | 90 | void 91 | early(void) 92 | { 93 | if (getpid() != 1) 94 | return; 95 | 96 | early_mounts(); 97 | early_env(); 98 | 99 | LOG("Console is alive\n"); 100 | } 101 | -------------------------------------------------------------------------------- /initd/mkdev.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Felix Fietkau 3 | * Copyright (C) 2013 John Crispin 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public License version 2.1 7 | * as published by the Free Software Foundation 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | */ 14 | 15 | #define _DEFAULT_SOURCE 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "init.h" 31 | 32 | static char **patterns; 33 | static int n_patterns; 34 | static char buf[PATH_MAX]; 35 | static char buf2[PATH_MAX]; 36 | static unsigned int mode = 0600; 37 | 38 | static bool find_pattern(const char *name) 39 | { 40 | int i; 41 | 42 | for (i = 0; i < n_patterns; i++) 43 | if (!fnmatch(patterns[i], name, 0)) 44 | return true; 45 | 46 | return false; 47 | } 48 | 49 | static void make_dev(const char *path, bool block, int major, int minor) 50 | { 51 | unsigned int oldumask = umask(0); 52 | unsigned int _mode = mode | (block ? S_IFBLK : S_IFCHR); 53 | 54 | DEBUG(4, "Creating %s device %s(%d,%d)\n", 55 | block ? "block" : "character", 56 | path, major, minor); 57 | 58 | mknod(path, _mode, makedev(major, minor)); 59 | umask(oldumask); 60 | } 61 | 62 | static void find_devs(bool block) 63 | { 64 | char *path = block ? "/sys/dev/block" : "/sys/dev/char"; 65 | struct dirent *dp; 66 | DIR *dir; 67 | 68 | dir = opendir(path); 69 | if (!dir) 70 | return; 71 | 72 | path = buf2 + sprintf(buf2, "%s/", path); 73 | while ((dp = readdir(dir)) != NULL) { 74 | char *c; 75 | int major = 0, minor = 0; 76 | int len; 77 | 78 | if (dp->d_type != DT_LNK) 79 | continue; 80 | 81 | if (sscanf(dp->d_name, "%d:%d", &major, &minor) != 2) 82 | continue; 83 | 84 | strcpy(path, dp->d_name); 85 | len = readlink(buf2, buf, sizeof(buf) - 1); 86 | if (len <= 0) 87 | continue; 88 | 89 | buf[len] = 0; 90 | if (!find_pattern(buf)) 91 | continue; 92 | 93 | c = strrchr(buf, '/'); 94 | if (!c) 95 | continue; 96 | 97 | c++; 98 | make_dev(c, block, major, minor); 99 | } 100 | closedir(dir); 101 | } 102 | 103 | static char *add_pattern(const char *name) 104 | { 105 | char *str = malloc(strlen(name) + 2); 106 | 107 | str[0] = '*'; 108 | strcpy(str + 1, name); 109 | return str; 110 | } 111 | 112 | int mkdev(const char *name, int _mode) 113 | { 114 | char *pattern; 115 | 116 | if (chdir("/dev")) 117 | return 1; 118 | 119 | pattern = add_pattern(name); 120 | patterns = &pattern; 121 | mode = _mode; 122 | n_patterns = 1; 123 | find_devs(true); 124 | find_devs(false); 125 | free(pattern); 126 | return chdir("/"); 127 | } 128 | -------------------------------------------------------------------------------- /sysupgrade.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Felix Fietkau 3 | * Copyright (C) 2013 John Crispin 4 | * Copyright (C) 2017 Matthias Schiffer 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License version 2.1 8 | * as published by the Free Software Foundation 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | 17 | #define _GNU_SOURCE 18 | #include "watchdog.h" 19 | #include "sysupgrade.h" 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | 28 | void sysupgrade_exec_upgraded(const char *prefix, char *path, 29 | const char *backup, char *command, 30 | struct blob_attr *options) 31 | { 32 | char *wdt_fd = watchdog_fd(); 33 | char *argv[] = { "/sbin/upgraded", NULL, NULL, NULL}; 34 | struct blob_attr *option; 35 | int rem; 36 | int ret; 37 | 38 | ret = chroot(prefix); 39 | if (ret < 0) { 40 | fprintf(stderr, "Failed to chroot for upgraded exec.\n"); 41 | return; 42 | } 43 | 44 | argv[1] = path; 45 | argv[2] = command; 46 | 47 | if (wdt_fd) { 48 | watchdog_set_cloexec(false); 49 | setenv("WDTFD", wdt_fd, 1); 50 | } 51 | 52 | if (backup) 53 | setenv("UPGRADE_BACKUP", backup, 1); 54 | 55 | blobmsg_for_each_attr(option, options, rem) { 56 | const char *prefix = "UPGRADE_OPT_"; 57 | char value[11]; 58 | char *name; 59 | char *c; 60 | int tmp; 61 | 62 | if (asprintf(&name, "%s%s", prefix, blobmsg_name(option)) <= 0) 63 | continue; 64 | for (c = name + strlen(prefix); *c; c++) { 65 | if (isalnum(*c) || *c == '_') { 66 | *c = toupper(*c); 67 | } else { 68 | c = NULL; 69 | break; 70 | } 71 | } 72 | 73 | if (!c) { 74 | fprintf(stderr, "Option \"%s\" contains invalid characters\n", 75 | blobmsg_name(option)); 76 | free(name); 77 | continue; 78 | } 79 | 80 | switch (blobmsg_type(option)) { 81 | case BLOBMSG_TYPE_INT32: 82 | tmp = blobmsg_get_u32(option); 83 | break; 84 | case BLOBMSG_TYPE_INT16: 85 | tmp = blobmsg_get_u16(option); 86 | break; 87 | case BLOBMSG_TYPE_INT8: 88 | tmp = blobmsg_get_u8(option); 89 | break; 90 | default: 91 | fprintf(stderr, "Option \"%s\" has unsupported type: %d\n", 92 | blobmsg_name(option), blobmsg_type(option)); 93 | free(name); 94 | continue; 95 | } 96 | snprintf(value, sizeof(value), "%u", tmp); 97 | 98 | setenv(name, value, 1); 99 | 100 | free(name); 101 | } 102 | 103 | execvp(argv[0], argv); 104 | 105 | /* Cleanup on failure */ 106 | fprintf(stderr, "Failed to exec upgraded.\n"); 107 | unsetenv("WDTFD"); 108 | watchdog_set_cloexec(true); 109 | ret = chroot("."); 110 | if (ret < 0) { 111 | fprintf(stderr, "Failed to reset chroot, exiting.\n"); 112 | exit(EXIT_FAILURE); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /jail/seccomp-bpf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * seccomp example for x86 (32-bit and 64-bit) with BPF macros 3 | * 4 | * Copyright (c) 2012 The Chromium OS Authors 5 | * Authors: 6 | * Will Drewry 7 | * Kees Cook 8 | * 9 | * Use of this source code is governed by a BSD-style license that can be 10 | * found in the LICENSE file. 11 | */ 12 | #ifndef _SECCOMP_BPF_H_ 13 | #define _SECCOMP_BPF_H_ 14 | 15 | #define _GNU_SOURCE 1 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #ifndef PR_SET_NO_NEW_PRIVS 27 | # define PR_SET_NO_NEW_PRIVS 38 28 | #endif 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | #ifdef HAVE_LINUX_SECCOMP_H 35 | # include 36 | #endif 37 | 38 | #ifndef SECCOMP_MODE_FILTER 39 | #define SECCOMP_MODE_FILTER 2 /* uses user-supplied filter. */ 40 | #define SECCOMP_RET_KILL 0x00000000U /* kill the task immediately */ 41 | #define SECCOMP_RET_TRAP 0x00030000U /* disallow and force a SIGSYS */ 42 | #define SECCOMP_RET_ERRNO 0x00050000U /* returns an errno */ 43 | #define SECCOMP_RET_LOG 0x00070000U 44 | #define SECCOMP_RET_LOGALLOW 0x7ffc0000U 45 | #define SECCOMP_RET_TRACE 0x7ff00000U /* pass to a tracer or disallow */ 46 | #define SECCOMP_RET_ALLOW 0x7fff0000U /* allow */ 47 | #define SECCOMP_RET_KILLPROCESS 0x80000000U 48 | #define SECCOMP_RET_ERROR(x) (SECCOMP_RET_ERRNO | ((x) & 0x0000ffffU)) 49 | #define SECCOMP_RET_LOGGER(x) (SECCOMP_RET_LOG | ((x) & 0x0000ffffU)) 50 | 51 | struct seccomp_data { 52 | int nr; 53 | __u32 arch; 54 | __u64 instruction_pointer; 55 | __u64 args[6]; 56 | }; 57 | #endif 58 | 59 | #ifndef SYS_SECCOMP 60 | # define SYS_SECCOMP 1 61 | #endif 62 | 63 | #define syscall_nr (offsetof(struct seccomp_data, nr)) 64 | #define arch_nr (offsetof(struct seccomp_data, arch)) 65 | #define syscall_arg(x) (offsetof(struct seccomp_data, args[x])) 66 | 67 | #if defined(__aarch64__) 68 | # define REG_SYSCALL regs.regs[8] 69 | # define ARCH_NR AUDIT_ARCH_AARCH64 70 | #elif defined(__amd64__) 71 | # define REG_SYSCALL REG_RAX 72 | # define ARCH_NR AUDIT_ARCH_X86_64 73 | #elif defined(__arm__) && (defined(__ARM_EABI__) || defined(__thumb__)) 74 | # define REG_SYSCALL regs.uregs[7] 75 | # if __BYTE_ORDER == __LITTLE_ENDIAN 76 | # define ARCH_NR AUDIT_ARCH_ARM 77 | # else 78 | # define ARCH_NR AUDIT_ARCH_ARMEB 79 | # endif 80 | #elif defined(__i386__) 81 | # define REG_SYSCALL REG_EAX 82 | # define ARCH_NR AUDIT_ARCH_I386 83 | #elif defined(__loongarch_lp64) 84 | # define REG_SYSCALL regs[11] 85 | # define ARCH_NR AUDIT_ARCH_LOONGARCH64 86 | #elif defined(__mips__) 87 | # define REG_SYSCALL regs[2] 88 | # if __BYTE_ORDER == __LITTLE_ENDIAN 89 | # define ARCH_NR AUDIT_ARCH_MIPSEL 90 | # else 91 | # define ARCH_NR AUDIT_ARCH_MIPS 92 | # endif 93 | #elif defined(__PPC__) 94 | # define REG_SYSCALL regs.gpr[0] 95 | # define ARCH_NR AUDIT_ARCH_PPC 96 | #else 97 | # warning "Platform does not support seccomp filter yet" 98 | # define REG_SYSCALL 0 99 | # define ARCH_NR 0 100 | #endif 101 | 102 | #endif /* _SECCOMP_BPF_H_ */ 103 | -------------------------------------------------------------------------------- /service/watch.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Felix Fietkau 3 | * Copyright (C) 2013 John Crispin 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public License version 2.1 7 | * as published by the Free Software Foundation 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | */ 14 | 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | #include "../procd.h" 21 | 22 | struct watch_object { 23 | struct list_head list; 24 | 25 | void *id; 26 | char *name; 27 | }; 28 | 29 | static struct ubus_event_handler watch_event; 30 | static struct ubus_subscriber watch_subscribe; 31 | static LIST_HEAD(watch_objects); 32 | 33 | static void watch_subscribe_cb(struct ubus_context *ctx, struct ubus_event_handler *ev, 34 | const char *type, struct blob_attr *msg) 35 | { 36 | static const struct blobmsg_policy policy = { 37 | "path", BLOBMSG_TYPE_STRING 38 | }; 39 | struct watch_object *o; 40 | struct blob_attr *attr; 41 | const char *path; 42 | 43 | P_DEBUG(3, "ubus event %s\n", type); 44 | if (strcmp(type, "ubus.object.add") != 0) 45 | return; 46 | 47 | blobmsg_parse(&policy, 1, &attr, blob_data(msg), blob_len(msg)); 48 | if (!attr) 49 | return; 50 | 51 | path = blobmsg_data(attr); 52 | P_DEBUG(3, "ubus path %s\n", path); 53 | 54 | list_for_each_entry(o, &watch_objects, list) { 55 | unsigned int id; 56 | 57 | if (strcmp(o->name, path)) 58 | continue; 59 | if (ubus_lookup_id(ctx, path, &id)) 60 | continue; 61 | if (!ubus_subscribe(ctx, &watch_subscribe, id)) 62 | return; 63 | ERROR("failed to subscribe %d\n", id); 64 | } 65 | } 66 | 67 | void 68 | watch_add(const char *_name, void *id) 69 | { 70 | int len = strlen(_name); 71 | char *name; 72 | struct watch_object *o = calloc_a(sizeof(*o), &name, len + 1); 73 | 74 | o->name = name; 75 | strcpy(name, _name); 76 | o->id = id; 77 | list_add(&o->list, &watch_objects); 78 | } 79 | 80 | void 81 | watch_del(void *id) 82 | { 83 | struct watch_object *t, *n; 84 | 85 | list_for_each_entry_safe(t, n, &watch_objects, list) { 86 | if (t->id != id) 87 | continue; 88 | list_del(&t->list); 89 | free(t); 90 | } 91 | } 92 | 93 | static int 94 | watch_notify_cb(struct ubus_context *ctx, struct ubus_object *obj, 95 | struct ubus_request_data *req, const char *method, 96 | struct blob_attr *msg) 97 | { 98 | if (debug >= 3) { 99 | char *str; 100 | 101 | str = blobmsg_format_json(msg, true); 102 | P_DEBUG(3, "Received ubus notify '%s': %s\n", method, str); 103 | free(str); 104 | } 105 | 106 | trigger_event(method, msg); 107 | return 0; 108 | } 109 | 110 | void 111 | watch_ubus(struct ubus_context *ctx) 112 | { 113 | watch_event.cb = watch_subscribe_cb; 114 | watch_subscribe.cb = watch_notify_cb; 115 | if (ubus_register_subscriber(ctx, &watch_subscribe)) 116 | ERROR("failed to register ubus subscriber\n"); 117 | if (ubus_register_event_handler(ctx, &watch_event, "ubus.object.add")) 118 | ERROR("failed to add ubus event handler\n"); 119 | } 120 | -------------------------------------------------------------------------------- /procd.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Felix Fietkau 3 | * Copyright (C) 2013 John Crispin 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public License version 2.1 7 | * as published by the Free Software Foundation 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | */ 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include "procd.h" 25 | #include "watchdog.h" 26 | #include "plug/hotplug.h" 27 | 28 | unsigned int debug; 29 | 30 | static struct udebug ud; 31 | static struct udebug_buf udb; 32 | static bool udebug_enabled; 33 | 34 | static void procd_udebug_vprintf(const char *format, va_list ap) 35 | { 36 | if (!udebug_enabled) 37 | return; 38 | 39 | udebug_entry_init(&udb); 40 | udebug_entry_vprintf(&udb, format, ap); 41 | udebug_entry_add(&udb); 42 | } 43 | 44 | void procd_udebug_printf(const char *format, ...) 45 | { 46 | va_list ap; 47 | 48 | va_start(ap, format); 49 | procd_udebug_vprintf(format, ap); 50 | va_end(ap); 51 | } 52 | 53 | void procd_udebug_set_enabled(bool val) 54 | { 55 | static const struct udebug_buf_meta meta = { 56 | .name = "procd_log", 57 | .format = UDEBUG_FORMAT_STRING, 58 | }; 59 | 60 | if (udebug_enabled == val) 61 | return; 62 | 63 | udebug_enabled = val; 64 | if (!val) { 65 | ulog_udebug(NULL); 66 | udebug_buf_free(&udb); 67 | udebug_free(&ud); 68 | return; 69 | } 70 | 71 | udebug_init(&ud); 72 | udebug_auto_connect(&ud, NULL); 73 | udebug_buf_init(&udb, 1024, 64 * 1024); 74 | udebug_buf_add(&ud, &udb, &meta); 75 | ulog_udebug(&udb); 76 | } 77 | 78 | 79 | static int usage(const char *prog) 80 | { 81 | fprintf(stderr, "Usage: %s [options]\n" 82 | "Options:\n" 83 | " -s Path to ubus socket\n" 84 | " -h run as hotplug daemon\n" 85 | " -d Enable debug messages\n" 86 | " -S Print messages to stdout\n" 87 | "\n", prog); 88 | return 1; 89 | } 90 | 91 | int main(int argc, char **argv) 92 | { 93 | int ch; 94 | char *dbglvl = getenv("DBGLVL"); 95 | int ulog_channels = ULOG_KMSG; 96 | 97 | if (dbglvl) { 98 | debug = atoi(dbglvl); 99 | unsetenv("DBGLVL"); 100 | } 101 | 102 | while ((ch = getopt(argc, argv, "d:s:h:S")) != -1) { 103 | switch (ch) { 104 | case 'h': 105 | return hotplug_run(optarg); 106 | case 's': 107 | ubus_socket = optarg; 108 | break; 109 | case 'd': 110 | debug = atoi(optarg); 111 | break; 112 | case 'S': 113 | ulog_channels = ULOG_STDIO; 114 | break; 115 | default: 116 | return usage(argv[0]); 117 | } 118 | } 119 | 120 | ulog_open(ulog_channels, LOG_DAEMON, "procd"); 121 | ulog_threshold(LOG_DEBUG + 1); 122 | 123 | setsid(); 124 | uloop_init(); 125 | procd_signal(); 126 | procd_udebug_set_enabled(true); 127 | if (getpid() != 1) 128 | procd_connect_ubus(); 129 | else 130 | procd_state_next(); 131 | uloop_run(); 132 | uloop_done(); 133 | 134 | return 0; 135 | } 136 | -------------------------------------------------------------------------------- /service/instance.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Felix Fietkau 3 | * Copyright (C) 2013 John Crispin 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public License version 2.1 7 | * as published by the Free Software Foundation 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | */ 14 | 15 | #ifndef __PROCD_INSTANCE_H 16 | #define __PROCD_INSTANCE_H 17 | 18 | #include 19 | #include 20 | #include 21 | #include "../utils/utils.h" 22 | 23 | #define RESPAWN_ERROR (5 * 60) 24 | #define SIGNALLED_OFFSET 128 25 | 26 | struct jail { 27 | union { 28 | struct { 29 | uint32_t procfs:1; 30 | uint32_t sysfs:1; 31 | uint32_t ubus:1; 32 | uint32_t udebug:1; 33 | uint32_t log:1; 34 | uint32_t ronly:1; 35 | uint32_t netns:1; 36 | uint32_t userns:1; 37 | uint32_t cgroupsns:1; 38 | uint32_t console:1; 39 | }; 40 | uint32_t flags; 41 | }; 42 | char *name; 43 | char *hostname; 44 | char *pidfile; 45 | struct blobmsg_list mount; 46 | struct blobmsg_list setns; 47 | int argc; 48 | }; 49 | 50 | typedef enum instance_watchdog { 51 | INSTANCE_WATCHDOG_MODE_DISABLED, 52 | INSTANCE_WATCHDOG_MODE_PASSIVE, 53 | INSTANCE_WATCHDOG_MODE_ACTIVE, 54 | __INSTANCE_WATCHDOG_MODE_MAX, 55 | } instance_watchdog_mode_t; 56 | 57 | struct watchdog { 58 | instance_watchdog_mode_t mode; 59 | uint32_t freq; 60 | struct uloop_timeout timeout; 61 | }; 62 | 63 | struct service_instance { 64 | struct vlist_node node; 65 | struct service *srv; 66 | const char *name; 67 | 68 | int8_t nice; 69 | bool valid; 70 | 71 | char *user; 72 | uid_t uid; 73 | gid_t pw_gid; 74 | char *group; 75 | gid_t gr_gid; 76 | 77 | bool halt; 78 | bool restart; 79 | bool respawn; 80 | int respawn_count; 81 | int reload_signal; 82 | struct timespec start; 83 | 84 | bool trace; 85 | bool has_jail; 86 | bool require_jail; 87 | bool immediately; 88 | bool no_new_privs; 89 | struct jail jail; 90 | char *seccomp; 91 | char *capabilities; 92 | char *pidfile; 93 | char *extroot; 94 | char *overlaydir; 95 | char *tmpoverlaysize; 96 | char *bundle; 97 | int syslog_facility; 98 | int exit_code; 99 | 100 | uint32_t term_timeout; 101 | uint32_t respawn_timeout; 102 | uint32_t respawn_threshold; 103 | uint32_t respawn_retry; 104 | 105 | struct blob_attr *config; 106 | struct uloop_process proc; 107 | struct uloop_timeout timeout; 108 | struct ustream_fd _stdout; 109 | struct ustream_fd _stderr; 110 | struct ustream_fd console; 111 | struct ustream_fd console_client; 112 | 113 | struct blob_attr *command; 114 | struct blob_attr *trigger; 115 | struct blobmsg_list env; 116 | struct blobmsg_list data; 117 | struct blobmsg_list netdev; 118 | struct blobmsg_list file; 119 | struct blobmsg_list limits; 120 | struct blobmsg_list errors; 121 | 122 | struct watchdog watchdog; 123 | }; 124 | 125 | void instance_start(struct service_instance *in); 126 | void instance_stop(struct service_instance *in, bool halt); 127 | void instance_update(struct service_instance *in, struct service_instance *in_new); 128 | void instance_init(struct service_instance *in, struct service *s, struct blob_attr *config); 129 | void instance_free(struct service_instance *in); 130 | void instance_dump(struct blob_buf *b, struct service_instance *in, int debug); 131 | 132 | #endif 133 | -------------------------------------------------------------------------------- /initd/preinit.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Felix Fietkau 3 | * Copyright (C) 2013 John Crispin 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public License version 2.1 7 | * as published by the Free Software Foundation 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | */ 14 | #define _GNU_SOURCE 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | #include "init.h" 30 | #include "../watchdog.h" 31 | #include "../sysupgrade.h" 32 | 33 | static struct uloop_process preinit_proc; 34 | static struct uloop_process plugd_proc; 35 | 36 | static void 37 | check_dbglvl(void) 38 | { 39 | FILE *fp = fopen("/tmp/debug_level", "r"); 40 | int lvl = 0; 41 | 42 | if (!fp) 43 | return; 44 | if (fscanf(fp, "%d", &lvl) == EOF) 45 | ERROR("failed to read debug level: %m\n"); 46 | fclose(fp); 47 | unlink("/tmp/debug_level"); 48 | 49 | if (lvl > 0 && lvl < 5) 50 | debug = lvl; 51 | } 52 | 53 | static void 54 | check_sysupgrade(void) 55 | { 56 | char *prefix = NULL, *path = NULL, *command = NULL; 57 | size_t n; 58 | 59 | if (chdir("/")) 60 | return; 61 | 62 | FILE *sysupgrade = fopen("/tmp/sysupgrade", "r"); 63 | if (!sysupgrade) 64 | return; 65 | 66 | n = 0; 67 | if (getdelim(&prefix, &n, 0, sysupgrade) < 0) 68 | goto fail; 69 | n = 0; 70 | if (getdelim(&path, &n, 0, sysupgrade) < 0) 71 | goto fail; 72 | n = 0; 73 | if (getdelim(&command, &n, 0, sysupgrade) < 0) 74 | goto fail; 75 | 76 | fclose(sysupgrade); 77 | 78 | sysupgrade_exec_upgraded(prefix, path, NULL, command, NULL); 79 | 80 | while (true) 81 | sleep(1); 82 | 83 | fail: 84 | fclose(sysupgrade); 85 | free(prefix); 86 | free(path); 87 | free(command); 88 | } 89 | 90 | static void 91 | spawn_procd(struct uloop_process *proc, int ret) 92 | { 93 | char *wdt_fd = watchdog_fd(); 94 | char *argv[] = { "/sbin/procd", NULL}; 95 | char dbg[2]; 96 | 97 | if (plugd_proc.pid > 0) 98 | kill(plugd_proc.pid, SIGKILL); 99 | 100 | unsetenv("PREINIT"); 101 | unlink("/tmp/.preinit"); 102 | 103 | check_sysupgrade(); 104 | 105 | DEBUG(2, "Exec to real procd now\n"); 106 | if (wdt_fd) 107 | setenv("WDTFD", wdt_fd, 1); 108 | check_dbglvl(); 109 | if (debug > 0) { 110 | snprintf(dbg, 2, "%d", debug); 111 | setenv("DBGLVL", dbg, 1); 112 | } 113 | 114 | execvp(argv[0], argv); 115 | } 116 | 117 | static void 118 | plugd_proc_cb(struct uloop_process *proc, int ret) 119 | { 120 | proc->pid = 0; 121 | } 122 | 123 | void 124 | preinit(void) 125 | { 126 | char *init[] = { "/bin/sh", "/etc/preinit", NULL }; 127 | char *plug[] = { "/sbin/procd", "-h", "/etc/hotplug-preinit.json", NULL }; 128 | int fd; 129 | 130 | LOG("- preinit -\n"); 131 | 132 | plugd_proc.cb = plugd_proc_cb; 133 | plugd_proc.pid = fork(); 134 | if (!plugd_proc.pid) { 135 | execvp(plug[0], plug); 136 | ERROR("Failed to start plugd: %m\n"); 137 | exit(EXIT_FAILURE); 138 | } 139 | if (plugd_proc.pid <= 0) { 140 | ERROR("Failed to start new plugd instance: %m\n"); 141 | return; 142 | } 143 | uloop_process_add(&plugd_proc); 144 | 145 | setenv("PREINIT", "1", 1); 146 | 147 | fd = creat("/tmp/.preinit", 0600); 148 | 149 | if (fd < 0) 150 | ERROR("Failed to create sentinel file: %m\n"); 151 | else 152 | close(fd); 153 | 154 | preinit_proc.cb = spawn_procd; 155 | preinit_proc.pid = fork(); 156 | if (!preinit_proc.pid) { 157 | execvp(init[0], init); 158 | ERROR("Failed to start preinit: %m\n"); 159 | exit(EXIT_FAILURE); 160 | } 161 | if (preinit_proc.pid <= 0) { 162 | ERROR("Failed to start new preinit instance: %m\n"); 163 | return; 164 | } 165 | uloop_process_add(&preinit_proc); 166 | 167 | DEBUG(4, "Launched preinit instance, pid=%d\n", (int) preinit_proc.pid); 168 | } 169 | -------------------------------------------------------------------------------- /initd/init.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Felix Fietkau 3 | * Copyright (C) 2013 John Crispin 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public License version 2.1 7 | * as published by the Free Software Foundation 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | */ 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #if defined(WITH_SELINUX) 33 | #include 34 | #include 35 | #include 36 | #endif 37 | 38 | #include "../utils/utils.h" 39 | #include "init.h" 40 | #include "../watchdog.h" 41 | 42 | unsigned int debug = 0; 43 | 44 | static void 45 | signal_shutdown(int signal, siginfo_t *siginfo, void *data) 46 | { 47 | fprintf(stderr, "reboot\n"); 48 | fflush(stderr); 49 | sync(); 50 | sleep(2); 51 | reboot(RB_AUTOBOOT); 52 | while (1) 53 | ; 54 | } 55 | 56 | static struct sigaction sa_shutdown = { 57 | .sa_sigaction = signal_shutdown, 58 | .sa_flags = SA_SIGINFO 59 | }; 60 | 61 | static void 62 | cmdline(void) 63 | { 64 | char line[20]; 65 | char* res; 66 | long r; 67 | 68 | res = get_cmdline_val("init_debug", line, sizeof(line)); 69 | if (res != NULL) { 70 | r = strtol(line, NULL, 10); 71 | if ((r != LONG_MIN) && (r != LONG_MAX)) 72 | debug = (int) r; 73 | } 74 | } 75 | 76 | #if defined(WITH_SELINUX) 77 | static int 78 | selinux(char **argv) 79 | { 80 | int ret; 81 | int enforce = selinux_status_getenforce(); 82 | 83 | /* is SELinux already initialized? */ 84 | if (getenv("SELINUX_INIT")) { 85 | /* have initramfs permissions already been restored? */ 86 | if (!getenv("INITRAMFS") || getenv("SELINUX_RESTORECON")) { 87 | unsetenv("SELINUX_INIT"); 88 | unsetenv("SELINUX_RESTORECON"); 89 | return 0; 90 | } 91 | /* Second call (initramfs only): restore filesystem labels */ 92 | const char *exclude_list[] = { "/dev/console", "/proc", "/sys", 0 }; 93 | selinux_restorecon_set_exclude_list(exclude_list); 94 | ret = selinux_restorecon("/", SELINUX_RESTORECON_RECURSE | SELINUX_RESTORECON_MASS_RELABEL); 95 | putenv("SELINUX_RESTORECON=1"); 96 | } else { 97 | /* First call: load policy */ 98 | ret = selinux_init_load_policy(&enforce); 99 | putenv("SELINUX_INIT=1"); 100 | } 101 | 102 | if (ret == 0) 103 | execv(argv[0], argv); 104 | 105 | if (enforce > 0) { 106 | fprintf(stderr, "Cannot load SELinux policy, but system in enforcing mode. Halting.\n"); 107 | return 1; 108 | } 109 | 110 | return 0; 111 | } 112 | #else 113 | static int 114 | selinux(char **argv) 115 | { 116 | return 0; 117 | } 118 | #endif 119 | 120 | int 121 | main(int argc, char **argv) 122 | { 123 | pid_t pid; 124 | 125 | ulog_open(ULOG_KMSG, LOG_DAEMON, "init"); 126 | 127 | sigaction(SIGTERM, &sa_shutdown, NULL); 128 | sigaction(SIGUSR1, &sa_shutdown, NULL); 129 | sigaction(SIGUSR2, &sa_shutdown, NULL); 130 | sigaction(SIGPWR, &sa_shutdown, NULL); 131 | 132 | if (selinux(argv)) 133 | exit(-1); 134 | early(); 135 | cmdline(); 136 | watchdog_init(1); 137 | 138 | pid = fork(); 139 | if (!pid) { 140 | char *kmod[] = { "/sbin/kmodloader", "/etc/modules-boot.d/", NULL }; 141 | 142 | if (debug < 3) 143 | patch_stdio("/dev/null"); 144 | 145 | execvp(kmod[0], kmod); 146 | ERROR("Failed to start kmodloader: %m\n"); 147 | exit(EXIT_FAILURE); 148 | } 149 | if (pid <= 0) { 150 | ERROR("Failed to start kmodloader instance: %m\n"); 151 | } else { 152 | const struct timespec req = {0, 10 * 1000 * 1000}; 153 | int i; 154 | 155 | for (i = 0; i < 1200; i++) { 156 | if (waitpid(pid, NULL, WNOHANG) > 0) 157 | break; 158 | nanosleep(&req, NULL); 159 | watchdog_ping(); 160 | } 161 | } 162 | uloop_init(); 163 | preinit(); 164 | uloop_run(); 165 | 166 | return 0; 167 | } 168 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | PROJECT(procd C) 4 | INCLUDE(GNUInstallDirs) 5 | ADD_DEFINITIONS(-Os -ggdb -Wall -Werror --std=gnu99 -Wmissing-declarations) 6 | 7 | SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") 8 | 9 | IF(APPLE) 10 | INCLUDE_DIRECTORIES(/opt/local/include) 11 | LINK_DIRECTORIES(/opt/local/lib) 12 | ENDIF() 13 | 14 | 15 | ADD_LIBRARY(setlbf SHARED service/setlbf.c) 16 | INSTALL(TARGETS setlbf 17 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 18 | ) 19 | 20 | 21 | SET(SOURCES procd.c signal.c state.c hotplug-dispatch.c inittab.c rcS.c ubus.c 22 | system.c sysupgrade.c service/service.c service/instance.c 23 | service/validate.c service/trigger.c service/watch.c utils/utils.c) 24 | IF(NOT DISABLE_INIT) 25 | SET(SOURCES ${SOURCES} watchdog.c plug/coldplug.c plug/hotplug.c) 26 | ENDIF() 27 | 28 | FIND_LIBRARY(ubox NAMES ubox) 29 | FIND_LIBRARY(ubus NAMES ubus) 30 | FIND_LIBRARY(uci NAMES uci) 31 | FIND_LIBRARY(blobmsg_json NAMES blobmsg_json) 32 | FIND_LIBRARY(json_script NAMES json_script) 33 | FIND_LIBRARY(json NAMES json-c json) 34 | FIND_LIBRARY(udebug NAMES udebug) 35 | 36 | SET(LIBS ${ubox} ${ubus} ${json} ${blobmsg_json} ${json_script} ${udebug}) 37 | 38 | IF(DEBUG) 39 | ADD_DEFINITIONS(-DUDEV_DEBUG -g3) 40 | ENDIF() 41 | 42 | IF(EARLY_PATH) 43 | ADD_DEFINITIONS(-DEARLY_PATH="${EARLY_PATH}") 44 | ENDIF() 45 | 46 | IF(SELINUX) 47 | include(FindPkgConfig) 48 | pkg_search_module(SELINUX REQUIRED libselinux) 49 | add_compile_definitions(WITH_SELINUX) 50 | ENDIF() 51 | 52 | add_subdirectory(upgraded) 53 | 54 | ADD_EXECUTABLE(procd ${SOURCES}) 55 | TARGET_LINK_LIBRARIES(procd ${LIBS}) 56 | SET_TARGET_PROPERTIES(procd PROPERTIES COMPILE_DEFINITIONS "HAS_UDEBUG") 57 | INSTALL(TARGETS procd 58 | RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR} 59 | ) 60 | 61 | FIND_PATH(ubox_include_dir libubox/uloop.h) 62 | FIND_PATH(udebug_include_dir NAMES udebug.h) 63 | INCLUDE_DIRECTORIES(${ubox_include_dir} ${udebug_include_dir}) 64 | 65 | IF(DISABLE_INIT) 66 | ADD_DEFINITIONS(-DDISABLE_INIT) 67 | ELSE() 68 | ADD_EXECUTABLE(init initd/init.c initd/early.c initd/preinit.c initd/mkdev.c sysupgrade.c watchdog.c 69 | utils/utils.c) 70 | TARGET_INCLUDE_DIRECTORIES(init PUBLIC ${SELINUX_INCLUDE_DIRS}) 71 | TARGET_LINK_LIBRARIES(init ${LIBS} ${SELINUX_LIBRARIES}) 72 | INSTALL(TARGETS init 73 | RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR} 74 | ) 75 | 76 | ADD_EXECUTABLE(udevtrigger plug/udevtrigger.c) 77 | INSTALL(TARGETS udevtrigger 78 | RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR} 79 | ) 80 | ENDIF() 81 | 82 | 83 | ADD_EXECUTABLE(askfirst utils/askfirst.c) 84 | INSTALL(TARGETS askfirst 85 | RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR} 86 | ) 87 | 88 | ADD_CUSTOM_COMMAND( 89 | OUTPUT syscall-names.h 90 | COMMAND ./make_syscall_h.sh ${CMAKE_C_COMPILER} > ./syscall-names.h 91 | DEPENDS ./make_syscall_h.sh 92 | ) 93 | ADD_CUSTOM_TARGET(syscall-names-h DEPENDS syscall-names.h) 94 | 95 | ADD_CUSTOM_COMMAND( 96 | OUTPUT capabilities-names.h 97 | COMMAND ./make_capabilities_h.sh ${CMAKE_C_COMPILER} > ./capabilities-names.h 98 | DEPENDS ./make_capabilities_h.sh 99 | ) 100 | ADD_CUSTOM_TARGET(capabilities-names-h DEPENDS capabilities-names.h) 101 | 102 | IF(SECCOMP_SUPPORT) 103 | ADD_DEFINITIONS(-DSECCOMP_SUPPORT) 104 | ADD_LIBRARY(preload-seccomp SHARED jail/preload.c jail/seccomp.c jail/seccomp-oci.c) 105 | TARGET_LINK_LIBRARIES(preload-seccomp dl ${ubox} ${blobmsg_json}) 106 | INSTALL(TARGETS preload-seccomp 107 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 108 | ) 109 | ADD_DEPENDENCIES(preload-seccomp syscall-names-h) 110 | SET(SOURCES_OCI_SECCOMP jail/seccomp-oci.c) 111 | ENDIF() 112 | 113 | IF(JAIL_SUPPORT) 114 | ADD_EXECUTABLE(ujail jail/jail.c jail/cgroups.c jail/cgroups-bpf.c jail/elf.c jail/fs.c jail/capabilities.c jail/netifd.c ${SOURCES_OCI_SECCOMP}) 115 | TARGET_LINK_LIBRARIES(ujail ${ubox} ${ubus} ${uci} ${blobmsg_json}) 116 | INSTALL(TARGETS ujail 117 | RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR} 118 | ) 119 | ADD_DEPENDENCIES(ujail capabilities-names-h) 120 | IF(SECCOMP_SUPPORT) 121 | ADD_DEPENDENCIES(ujail syscall-names-h) 122 | ENDIF() 123 | 124 | ADD_EXECUTABLE(uxc uxc.c) 125 | TARGET_LINK_LIBRARIES(uxc ${ubox} ${ubus} ${blobmsg_json}) 126 | INSTALL(TARGETS uxc 127 | RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR} 128 | ) 129 | endif() 130 | 131 | IF(UTRACE_SUPPORT) 132 | ADD_EXECUTABLE(utrace trace/trace.c) 133 | TARGET_LINK_LIBRARIES(utrace ${ubox} ${json} ${blobmsg_json}) 134 | INSTALL(TARGETS utrace 135 | RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR} 136 | ) 137 | ADD_DEPENDENCIES(utrace syscall-names-h) 138 | 139 | ADD_LIBRARY(preload-trace SHARED trace/preload.c) 140 | TARGET_LINK_LIBRARIES(preload-trace dl) 141 | INSTALL(TARGETS preload-trace 142 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 143 | ) 144 | endif() 145 | -------------------------------------------------------------------------------- /service/validate.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 John Crispin 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License version 2.1 6 | * as published by the Free Software Foundation 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | #include "../procd.h" 19 | 20 | #include "service.h" 21 | 22 | enum { 23 | SERVICE_VAL_PACKAGE, 24 | SERVICE_VAL_TYPE, 25 | SERVICE_VAL_DATA, 26 | __SERVICE_VAL_MAX 27 | }; 28 | 29 | static const struct blobmsg_policy service_validate_attrs[__SERVICE_VAL_MAX] = { 30 | [SERVICE_VAL_PACKAGE] = { "package", BLOBMSG_TYPE_STRING }, 31 | [SERVICE_VAL_TYPE] = { "type", BLOBMSG_TYPE_STRING }, 32 | [SERVICE_VAL_DATA] = { "data", BLOBMSG_TYPE_TABLE }, 33 | }; 34 | 35 | static AVL_TREE(validators, avl_strcmp, true, NULL); 36 | 37 | void 38 | service_validate_dump_all(struct blob_buf *b, char *p, char *s) 39 | { 40 | struct json_object *r = json_object_new_object(); 41 | struct validate *v; 42 | 43 | if (!r) 44 | return; 45 | 46 | avl_for_each_element(&validators, v, avl) { 47 | struct json_object *o, *t; 48 | struct vrule *vr; 49 | 50 | if (p && strcmp(p, v->package)) 51 | continue; 52 | 53 | if (s && strcmp(s, v->type)) 54 | continue; 55 | 56 | json_object_object_get_ex(r, v->package, &o); 57 | if (!o) { 58 | o = json_object_new_object(); 59 | json_object_object_add(r, v->package, o); 60 | } 61 | json_object_object_get_ex(o, v->type, &t); 62 | if (!t) { 63 | t = json_object_new_object(); 64 | json_object_object_add(o, v->type, t); 65 | } 66 | avl_for_each_element(&v->rules, vr, avl) 67 | json_object_object_add(t, vr->option, json_object_new_string(vr->rule)); 68 | } 69 | blobmsg_add_object(b, r); 70 | json_object_put(r); 71 | } 72 | 73 | void 74 | service_validate_dump(struct blob_buf *b, struct service *s) 75 | { 76 | struct validate *v; 77 | void *i = blobmsg_open_array(b, "validate"); 78 | 79 | list_for_each_entry(v, &s->validators, list) { 80 | struct vrule *vr; 81 | void *k, *j = blobmsg_open_table(b, "validate"); 82 | 83 | blobmsg_add_string(b, "package", v->package); 84 | blobmsg_add_string(b, "type", v->type); 85 | k = blobmsg_open_table(b, "rules"); 86 | avl_for_each_element(&v->rules, vr, avl) 87 | blobmsg_add_string(b, vr->option, vr->rule); 88 | blobmsg_close_table(b, k); 89 | blobmsg_close_table(b, j); 90 | } 91 | blobmsg_close_array(b, i); 92 | } 93 | 94 | void 95 | service_validate_del(struct service *s) 96 | { 97 | struct validate *v, *n; 98 | 99 | if (list_empty(&s->validators)) 100 | return; 101 | 102 | list_for_each_entry_safe(v, n, &s->validators, list) { 103 | struct vrule *vr, *a; 104 | 105 | avl_remove_all_elements(&v->rules, vr, avl, a) 106 | free(vr); 107 | 108 | avl_delete(&validators, &v->avl); 109 | list_del(&v->list); 110 | free(v); 111 | } 112 | } 113 | 114 | void 115 | service_validate_add(struct service *s, struct blob_attr *msg) 116 | { 117 | struct blob_attr *tb[__SERVICE_VAL_MAX]; 118 | struct validate *v; 119 | char *type, *package; 120 | struct blob_attr *cur; 121 | int rem; 122 | 123 | blobmsg_parse(service_validate_attrs, __SERVICE_VAL_MAX, tb, blobmsg_data(msg), blobmsg_data_len(msg)); 124 | if (!tb[SERVICE_VAL_PACKAGE] || !tb[SERVICE_VAL_TYPE] || !tb[SERVICE_VAL_DATA]) 125 | return; 126 | 127 | v = calloc_a(sizeof(*v), &package, blobmsg_data_len(tb[SERVICE_VAL_PACKAGE]) + 1, 128 | &type, blobmsg_data_len(tb[SERVICE_VAL_TYPE]) + 1); 129 | if (!v) 130 | return; 131 | 132 | v->type = type; 133 | v->avl.key = v->package = package; 134 | strcpy(v->package, blobmsg_get_string(tb[SERVICE_VAL_PACKAGE])); 135 | strcpy(v->type, blobmsg_get_string(tb[SERVICE_VAL_TYPE])); 136 | 137 | list_add(&v->list, &s->validators); 138 | if (avl_insert(&validators, &v->avl)) { 139 | free(v); 140 | return; 141 | } 142 | avl_init(&v->rules, avl_strcmp, false, NULL); 143 | 144 | blobmsg_for_each_attr(cur, tb[SERVICE_VAL_DATA], rem) { 145 | char *option; 146 | char *rule; 147 | struct vrule *vr = calloc_a(sizeof(*vr), &option, strlen(blobmsg_name(cur)) + 1, 148 | &rule, strlen(blobmsg_get_string(cur)) + 1); 149 | 150 | vr->avl.key = vr->option = option; 151 | vr->rule = rule; 152 | strcpy(vr->option, blobmsg_name(cur)); 153 | strcpy(vr->rule, blobmsg_get_string(cur)); 154 | if (avl_insert(&v->rules, &vr->avl)) 155 | free(vr); 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /watchdog.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Felix Fietkau 3 | * Copyright (C) 2013 John Crispin 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public License version 2.1 7 | * as published by the Free Software Foundation 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | */ 14 | 15 | #include 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | 24 | #include 25 | 26 | #include "procd.h" 27 | #include "watchdog.h" 28 | 29 | #define WDT_PATH "/dev/watchdog" 30 | 31 | static struct uloop_timeout wdt_timeout; 32 | static int wdt_fd = -1; 33 | static int wdt_drv_timeout = 30; 34 | static int wdt_frequency = 5; 35 | static bool wdt_magicclose = false; 36 | 37 | void watchdog_ping(void) 38 | { 39 | DEBUG(4, "Ping\n"); 40 | if (wdt_fd >= 0 && write(wdt_fd, "X", 1) < 0) 41 | ERROR("WDT failed to write: %m\n"); 42 | } 43 | 44 | static void watchdog_timeout_cb(struct uloop_timeout *t) 45 | { 46 | watchdog_ping(); 47 | uloop_timeout_set(t, wdt_frequency * 1000); 48 | } 49 | 50 | static int watchdog_open(bool cloexec) 51 | { 52 | char *env = getenv("WDTFD"); 53 | 54 | if (wdt_fd >= 0) 55 | return wdt_fd; 56 | 57 | if (env) { 58 | DEBUG(2, "Watchdog handover: fd=%s\n", env); 59 | wdt_fd = atoi(env); 60 | unsetenv("WDTFD"); 61 | } else { 62 | wdt_fd = open(WDT_PATH, O_WRONLY); 63 | } 64 | 65 | if (wdt_fd < 0) 66 | return wdt_fd; 67 | 68 | if (cloexec) 69 | fcntl(wdt_fd, F_SETFD, fcntl(wdt_fd, F_GETFD) | FD_CLOEXEC); 70 | 71 | return wdt_fd; 72 | } 73 | 74 | static void watchdog_close(bool with_release) 75 | { 76 | if (wdt_fd < 0) 77 | return; 78 | 79 | if (with_release) { 80 | if (write(wdt_fd, "V", 1) < 0) 81 | ERROR("WDT failed to write release: %m\n"); 82 | } 83 | 84 | if (close(wdt_fd) == -1) 85 | ERROR("WDT failed to close watchdog: %m\n"); 86 | 87 | wdt_fd = -1; 88 | } 89 | 90 | static int watchdog_set_drv_timeout(void) 91 | { 92 | if (wdt_fd < 0) 93 | return -1; 94 | 95 | return ioctl(wdt_fd, WDIOC_SETTIMEOUT, &wdt_drv_timeout); 96 | } 97 | 98 | static void watchdog_print_status(void) 99 | { 100 | struct watchdog_info wdt_info; 101 | int bootstatus; 102 | 103 | if (wdt_fd < 0) 104 | return; 105 | 106 | if (ioctl(wdt_fd, WDIOC_GETSUPPORT, &wdt_info)) { 107 | DEBUG(2, "Watchdog GETSUPPORT failed\n"); 108 | return; 109 | } 110 | 111 | if (!(wdt_info.options & WDIOF_CARDRESET)) { 112 | DEBUG(2, "Watchdog does not have CARDRESET support\n"); 113 | return; 114 | } 115 | 116 | if (ioctl(wdt_fd, WDIOC_GETBOOTSTATUS, &bootstatus)) { 117 | DEBUG(2, "Watchdog GETBOOTSTATUS failed\n"); 118 | return; 119 | } 120 | 121 | if (bootstatus & WDIOF_CARDRESET) 122 | LOG("Watchdog has previously reset the system\n"); 123 | else 124 | DEBUG(2, "Watchdog did not previously reset the system\n"); 125 | } 126 | 127 | void watchdog_set_magicclose(bool val) 128 | { 129 | wdt_magicclose = val; 130 | } 131 | 132 | bool watchdog_get_magicclose(void) 133 | { 134 | return wdt_magicclose; 135 | } 136 | 137 | void watchdog_set_stopped(bool val) 138 | { 139 | if (val) { 140 | uloop_timeout_cancel(&wdt_timeout); 141 | 142 | watchdog_close(wdt_magicclose); 143 | } 144 | else { 145 | watchdog_open(true); 146 | watchdog_set_drv_timeout(); 147 | watchdog_timeout_cb(&wdt_timeout); 148 | } 149 | } 150 | 151 | bool watchdog_get_stopped(void) 152 | { 153 | return !wdt_timeout.pending; 154 | } 155 | 156 | int watchdog_timeout(int timeout) 157 | { 158 | if (timeout) { 159 | DEBUG(4, "Set watchdog timeout: %ds\n", timeout); 160 | wdt_drv_timeout = timeout; 161 | 162 | if (wdt_fd >= 0) 163 | watchdog_set_drv_timeout(); 164 | } 165 | 166 | return wdt_drv_timeout; 167 | } 168 | 169 | int watchdog_frequency(int frequency) 170 | { 171 | if (frequency) { 172 | DEBUG(4, "Set watchdog frequency: %ds\n", frequency); 173 | wdt_frequency = frequency; 174 | } 175 | 176 | return wdt_frequency; 177 | } 178 | 179 | char* watchdog_fd(void) 180 | { 181 | static char fd_buf[12]; 182 | 183 | if (wdt_fd < 0) 184 | return NULL; 185 | 186 | snprintf(fd_buf, sizeof(fd_buf), "%d", wdt_fd); 187 | 188 | return fd_buf; 189 | } 190 | 191 | void watchdog_init(int preinit) 192 | { 193 | wdt_timeout.cb = watchdog_timeout_cb; 194 | 195 | if (watchdog_open(!preinit) < 0) 196 | return; 197 | 198 | LOG("- watchdog -\n"); 199 | watchdog_set_drv_timeout(); 200 | watchdog_timeout_cb(&wdt_timeout); 201 | 202 | DEBUG(4, "Opened watchdog with timeout %ds\n", watchdog_timeout(0)); 203 | 204 | watchdog_print_status(); 205 | } 206 | 207 | 208 | void watchdog_set_cloexec(bool val) 209 | { 210 | if (wdt_fd < 0) 211 | return; 212 | 213 | int flags = fcntl(wdt_fd, F_GETFD); 214 | if (val) 215 | flags |= FD_CLOEXEC; 216 | else 217 | flags &= ~FD_CLOEXEC; 218 | fcntl(wdt_fd, F_SETFD, flags); 219 | } 220 | -------------------------------------------------------------------------------- /state.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Felix Fietkau 3 | * Copyright (C) 2013 John Crispin 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public License version 2.1 7 | * as published by the Free Software Foundation 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | */ 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "container.h" 25 | #include "procd.h" 26 | #include "syslog.h" 27 | #include "plug/hotplug.h" 28 | #include "watchdog.h" 29 | #include "service/service.h" 30 | #include "utils/utils.h" 31 | 32 | enum { 33 | STATE_NONE = 0, 34 | STATE_EARLY, 35 | STATE_UBUS, 36 | STATE_INIT, 37 | STATE_RUNNING, 38 | STATE_SHUTDOWN, 39 | STATE_HALT, 40 | __STATE_MAX, 41 | }; 42 | 43 | static int state = STATE_NONE; 44 | static int reboot_event; 45 | 46 | static void set_stdio(const char* tty) 47 | { 48 | if (chdir("/dev") || 49 | !freopen(tty, "r", stdin) || 50 | !freopen(tty, "w", stdout) || 51 | !freopen(tty, "w", stderr)) 52 | ERROR("failed to set stdio: %m\n"); 53 | else 54 | fcntl(STDERR_FILENO, F_SETFL, fcntl(STDERR_FILENO, F_GETFL) | O_NONBLOCK); 55 | if (chdir("/")) 56 | ERROR("failed to change dir to /: %m\n"); 57 | } 58 | 59 | static void set_console(void) 60 | { 61 | const char* tty; 62 | char* split; 63 | char line[ 20 ]; 64 | const char* try[] = { "tty0", "console", NULL }; /* Try the most common outputs */ 65 | int f, i = 0; 66 | 67 | tty = get_cmdline_val("console",line,sizeof(line)); 68 | if (tty != NULL) { 69 | split = strchr(tty, ','); 70 | if ( split != NULL ) 71 | *split = '\0'; 72 | } else { 73 | // Try a default 74 | tty=try[i]; 75 | i++; 76 | } 77 | 78 | if (chdir("/dev")) { 79 | ERROR("failed to change dir to /dev: %m\n"); 80 | return; 81 | } 82 | while (tty!=NULL) { 83 | f = open(tty, O_RDONLY|O_NOCTTY); 84 | if (f >= 0) { 85 | close(f); 86 | break; 87 | } 88 | 89 | tty=try[i]; 90 | i++; 91 | } 92 | if (chdir("/")) 93 | ERROR("failed to change dir to /: %m\n"); 94 | 95 | if (tty != NULL) 96 | set_stdio(tty); 97 | } 98 | 99 | static void perform_halt() 100 | { 101 | if (reboot_event == RB_POWER_OFF) 102 | LOG("- power down -\n"); 103 | else 104 | LOG("- reboot -\n"); 105 | 106 | /* Allow time for last message to reach serial console, etc */ 107 | sleep(1); 108 | 109 | if (is_container()) { 110 | reboot(reboot_event); 111 | exit(EXIT_SUCCESS); 112 | return; 113 | } 114 | 115 | /* We have to fork here, since the kernel calls do_exit(EXIT_SUCCESS) 116 | * in linux/kernel/sys.c, which can cause the machine to panic when 117 | * the init process exits... */ 118 | if (!vfork()) { /* child */ 119 | reboot(reboot_event); 120 | _exit(EXIT_SUCCESS); 121 | } 122 | 123 | while (1) 124 | sleep(1); 125 | } 126 | 127 | static void state_enter(void) 128 | { 129 | char ubus_cmd[] = "/sbin/ubusd"; 130 | struct passwd *p; 131 | 132 | switch (state) { 133 | case STATE_EARLY: 134 | LOG("- early -\n"); 135 | watchdog_init(0); 136 | hotplug("/etc/hotplug.json"); 137 | procd_coldplug(); 138 | break; 139 | 140 | case STATE_UBUS: 141 | // try to reopen incase the wdt was not available before coldplug 142 | watchdog_init(0); 143 | set_stdio("console"); 144 | p = getpwnam("ubus"); 145 | if (p) { 146 | int ret; 147 | LOG("- ubus -\n"); 148 | mkdir(p->pw_dir, 0755); 149 | ret = chown(p->pw_dir, p->pw_uid, p->pw_gid); 150 | if (ret) 151 | LOG("- ubus - failed to chown(%s)\n", p->pw_dir); 152 | } else { 153 | LOG("- ubus (running as root!) -\n"); 154 | } 155 | 156 | procd_connect_ubus(); 157 | service_start_early("ubus", ubus_cmd, p?"ubus":NULL, p?"ubus":NULL); 158 | break; 159 | 160 | case STATE_INIT: 161 | LOG("- init -\n"); 162 | procd_inittab(); 163 | procd_inittab_run("respawn"); 164 | procd_inittab_run("askconsole"); 165 | procd_inittab_run("askfirst"); 166 | procd_inittab_run("sysinit"); 167 | 168 | // switch to syslog log channel 169 | ulog_open(ULOG_SYSLOG, LOG_DAEMON, "procd"); 170 | break; 171 | 172 | case STATE_RUNNING: 173 | LOG("- init complete -\n"); 174 | procd_inittab_run("respawnlate"); 175 | procd_inittab_run("askconsolelate"); 176 | break; 177 | 178 | case STATE_SHUTDOWN: 179 | /* Redirect output to the console for the users' benefit */ 180 | set_console(); 181 | LOG("- shutdown -\n"); 182 | procd_inittab_run("shutdown"); 183 | sync(); 184 | break; 185 | 186 | case STATE_HALT: 187 | // To prevent killed processes from interrupting the sleep 188 | signal(SIGCHLD, SIG_IGN); 189 | LOG("- SIGTERM processes -\n"); 190 | kill(-1, SIGTERM); 191 | sync(); 192 | sleep(1); 193 | LOG("- SIGKILL processes -\n"); 194 | kill(-1, SIGKILL); 195 | sync(); 196 | sleep(1); 197 | #ifndef DISABLE_INIT 198 | perform_halt(); 199 | #else 200 | exit(EXIT_SUCCESS); 201 | #endif 202 | break; 203 | 204 | default: 205 | ERROR("Unhandled state %d\n", state); 206 | return; 207 | }; 208 | } 209 | 210 | void procd_state_next(void) 211 | { 212 | DEBUG(4, "Change state %d -> %d\n", state, state + 1); 213 | state++; 214 | state_enter(); 215 | } 216 | 217 | void procd_state_ubus_connect(void) 218 | { 219 | if (state == STATE_UBUS) 220 | procd_state_next(); 221 | } 222 | 223 | void procd_shutdown(int event) 224 | { 225 | if (state >= STATE_SHUTDOWN) 226 | return; 227 | DEBUG(2, "Shutting down system with event %x\n", event); 228 | reboot_event = event; 229 | state = STATE_SHUTDOWN; 230 | state_enter(); 231 | } 232 | -------------------------------------------------------------------------------- /utils/utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Felix Fietkau 3 | * Copyright (C) 2013 John Crispin 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public License version 2.1 7 | * as published by the Free Software Foundation 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | */ 14 | 15 | #define _GNU_SOURCE 16 | #include 17 | #include 18 | #include "utils.h" 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "../log.h" 28 | 29 | #ifndef O_PATH 30 | #define O_PATH 010000000 31 | #endif 32 | 33 | void 34 | __blobmsg_list_init(struct blobmsg_list *list, int offset, int len, blobmsg_list_cmp cmp) 35 | { 36 | avl_init(&list->avl, avl_strcmp, false, NULL); 37 | list->node_offset = offset; 38 | list->node_len = len; 39 | list->cmp = cmp; 40 | } 41 | 42 | int 43 | blobmsg_list_fill(struct blobmsg_list *list, void *data, int len, bool array) 44 | { 45 | struct avl_tree *tree = &list->avl; 46 | struct blobmsg_list_node *node; 47 | struct blob_attr *cur; 48 | void *ptr; 49 | int count = 0; 50 | int rem = len; 51 | 52 | __blob_for_each_attr(cur, data, rem) { 53 | if (!blobmsg_check_attr(cur, !array)) 54 | continue; 55 | 56 | ptr = calloc(1, list->node_len); 57 | if (!ptr) 58 | return -1; 59 | 60 | node = (void *) ((char *)ptr + list->node_offset); 61 | if (array) 62 | node->avl.key = blobmsg_data(cur); 63 | else 64 | node->avl.key = blobmsg_name(cur); 65 | node->data = cur; 66 | if (avl_insert(tree, &node->avl)) { 67 | free(ptr); 68 | continue; 69 | } 70 | 71 | count++; 72 | } 73 | 74 | return count; 75 | } 76 | 77 | void 78 | blobmsg_list_move(struct blobmsg_list *list, struct blobmsg_list *src) 79 | { 80 | struct blobmsg_list_node *node, *tmp; 81 | void *ptr; 82 | 83 | avl_remove_all_elements(&src->avl, node, avl, tmp) { 84 | if (avl_insert(&list->avl, &node->avl)) { 85 | ptr = ((char *) node - list->node_offset); 86 | free(ptr); 87 | } 88 | } 89 | } 90 | 91 | void 92 | blobmsg_list_free(struct blobmsg_list *list) 93 | { 94 | struct blobmsg_list_node *node, *tmp; 95 | void *ptr; 96 | 97 | avl_remove_all_elements(&list->avl, node, avl, tmp) { 98 | ptr = ((char *) node - list->node_offset); 99 | free(ptr); 100 | } 101 | } 102 | 103 | bool 104 | blobmsg_list_equal(struct blobmsg_list *l1, struct blobmsg_list *l2) 105 | { 106 | struct blobmsg_list_node *n1, *n2; 107 | int count = l1->avl.count; 108 | 109 | if (count != l2->avl.count) 110 | return false; 111 | 112 | n1 = avl_first_element(&l1->avl, n1, avl); 113 | n2 = avl_first_element(&l2->avl, n2, avl); 114 | 115 | while (count-- > 0) { 116 | int len; 117 | 118 | len = blob_len(n1->data); 119 | if (len != blob_len(n2->data)) 120 | return false; 121 | 122 | if (memcmp(n1->data, n2->data, len) != 0) 123 | return false; 124 | 125 | if (l1->cmp && !l1->cmp(n1, n2)) 126 | return false; 127 | 128 | if (!count) 129 | break; 130 | 131 | n1 = avl_next_element(n1, avl); 132 | n2 = avl_next_element(n2, avl); 133 | } 134 | 135 | return true; 136 | } 137 | 138 | char *get_active_console(char *out, int len) 139 | { 140 | char line[CMDLINE_SIZE + 1]; 141 | int fd = open("/sys/class/tty/console/active", O_RDONLY); 142 | ssize_t r; 143 | 144 | if (fd < 0) 145 | return NULL; 146 | 147 | r = read(fd, line, sizeof(line) - 1); 148 | line[CMDLINE_SIZE] = '\0'; 149 | 150 | close(fd); 151 | 152 | if (r <= 0) 153 | return NULL; 154 | 155 | /* The active file is terminated by a newline which we need to strip */ 156 | char *newline = strtok(line, "\n"); 157 | 158 | if (newline != NULL) { 159 | strlcpy(out, newline, len); 160 | return out; 161 | } 162 | 163 | return NULL; 164 | } 165 | 166 | char *get_cmdline_val_offset(const char *name, char *out, int len, int offset) 167 | { 168 | char line[CMDLINE_SIZE + 1], *c, *sptr; 169 | int i, fd = open("/proc/cmdline", O_RDONLY); 170 | ssize_t r; 171 | 172 | if (fd < 0) 173 | return NULL; 174 | 175 | r = read(fd, line, sizeof(line) - 1); 176 | close(fd); 177 | 178 | if (r <= 0) 179 | return NULL; 180 | 181 | line[r] = 0; 182 | 183 | for (i = 0, c = strtok_r(line, " \t\n", &sptr); c; 184 | c = strtok_r(NULL, " \t\n", &sptr)) { 185 | char *sep = strchr(c, '='); 186 | if (sep == NULL) 187 | continue; 188 | 189 | ssize_t klen = sep - c; 190 | if (strncmp(name, c, klen) || name[klen] != 0) 191 | continue; 192 | 193 | if (i++ < offset) 194 | continue; 195 | strlcpy(out, &sep[1], len); 196 | return out; 197 | } 198 | 199 | return NULL; 200 | } 201 | 202 | int patch_fd(const char *device, int fd, int flags) 203 | { 204 | int dfd, nfd; 205 | 206 | flags |= O_NOCTTY; 207 | 208 | if (device == NULL) 209 | device = "/dev/null"; 210 | 211 | if (*device != '/') { 212 | dfd = open("/dev", O_PATH|O_DIRECTORY); 213 | 214 | if (dfd < 0) 215 | return -1; 216 | 217 | nfd = openat(dfd, device, flags); 218 | 219 | close(dfd); 220 | } else { 221 | nfd = open(device, flags); 222 | } 223 | 224 | if (nfd < 0 && strcmp(device, "/dev/null")) 225 | nfd = open("/dev/null", flags); 226 | 227 | if (nfd < 0) 228 | return -1; 229 | 230 | fd = dup2(nfd, fd); 231 | 232 | if (nfd > STDERR_FILENO) 233 | close(nfd); 234 | 235 | return (fd < 0) ? -1 : 0; 236 | } 237 | 238 | int patch_stdio(const char *device) 239 | { 240 | int fd, rv = 0; 241 | const char *fdname[3] = { "stdin", "stdout", "stderr" }; 242 | 243 | for (fd = STDIN_FILENO; fd <= STDERR_FILENO; fd++) { 244 | if (patch_fd(device, fd, fd ? O_WRONLY : O_RDONLY)) { 245 | ERROR("Failed to redirect %s to %s: %m\n", 246 | fdname[fd], device); 247 | rv = -1; 248 | } 249 | } 250 | 251 | return rv; 252 | } 253 | -------------------------------------------------------------------------------- /rcS.c: -------------------------------------------------------------------------------- 1 | /* 2 | * runqueue-example.c 3 | * 4 | * Copyright (C) 2013 Felix Fietkau 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | 33 | #include "procd.h" 34 | #include "rcS.h" 35 | 36 | static struct runqueue q, r; 37 | 38 | struct initd { 39 | struct ustream_fd fd; 40 | struct runqueue_process proc; 41 | struct timespec ts_start; 42 | char *file; 43 | char *param; 44 | }; 45 | 46 | static void pipe_cb(struct ustream *s, int bytes) 47 | { 48 | struct initd *initd = container_of(s, struct initd, fd.stream); 49 | char *newline, *str; 50 | int len; 51 | 52 | do { 53 | str = ustream_get_read_buf(s, NULL); 54 | if (!str) 55 | break; 56 | newline = strchr(str, '\n'); 57 | if (!newline) 58 | break; 59 | *newline = 0; 60 | len = newline + 1 - str; 61 | ULOG_NOTE("%s: %s", initd->file, str); 62 | #ifdef SHOW_BOOT_ON_CONSOLE 63 | fprintf(stderr, "%s: %s\n", initd->file, str); 64 | #endif 65 | ustream_consume(s, len); 66 | } while (1); 67 | } 68 | 69 | static void q_initd_run(struct runqueue *q, struct runqueue_task *t) 70 | { 71 | struct initd *s = container_of(t, struct initd, proc.task); 72 | int pipefd[2]; 73 | pid_t pid; 74 | 75 | clock_gettime(CLOCK_MONOTONIC_RAW, &s->ts_start); 76 | DEBUG(2, "start %s %s \n", s->file, s->param); 77 | if (pipe(pipefd) == -1) { 78 | ERROR("Failed to create pipe: %m\n"); 79 | return; 80 | } 81 | 82 | pid = fork(); 83 | if (pid < 0) 84 | return; 85 | 86 | if (pid) { 87 | close(pipefd[1]); 88 | fcntl(pipefd[0], F_SETFD, FD_CLOEXEC); 89 | s->fd.stream.string_data = true, 90 | s->fd.stream.notify_read = pipe_cb, 91 | runqueue_process_add(q, &s->proc, pid); 92 | ustream_fd_init(&s->fd, pipefd[0]); 93 | return; 94 | } 95 | close(pipefd[0]); 96 | 97 | int devnull = open("/dev/null", O_RDONLY); 98 | dup2(devnull, STDIN_FILENO); 99 | dup2(pipefd[1], STDOUT_FILENO); 100 | dup2(pipefd[1], STDERR_FILENO); 101 | 102 | if (devnull > STDERR_FILENO) 103 | close(devnull); 104 | 105 | execlp(s->file, s->file, s->param, NULL); 106 | exit(1); 107 | } 108 | 109 | static void q_initd_complete(struct runqueue *q, struct runqueue_task *p) 110 | { 111 | struct initd *s = container_of(p, struct initd, proc.task); 112 | struct timespec ts_stop, ts_res; 113 | 114 | clock_gettime(CLOCK_MONOTONIC_RAW, &ts_stop); 115 | ts_res.tv_sec = ts_stop.tv_sec - s->ts_start.tv_sec; 116 | ts_res.tv_nsec = ts_stop.tv_nsec - s->ts_start.tv_nsec; 117 | if (ts_res.tv_nsec < 0) { 118 | --ts_res.tv_sec; 119 | ts_res.tv_nsec += 1000000000; 120 | } 121 | 122 | DEBUG(2, "stop %s %s - took %" PRId64 ".%09" PRId64 "s\n", s->file, s->param, (int64_t)ts_res.tv_sec, (int64_t)ts_res.tv_nsec); 123 | ustream_free(&s->fd.stream); 124 | close(s->fd.fd.fd); 125 | free(s); 126 | } 127 | 128 | static bool find_runqueue_list_entry(struct list_head *list, char *file, char *param) 129 | { 130 | struct initd *s; 131 | 132 | list_for_each_entry(s, list, proc.task.list.list) 133 | if (!strcmp(s->file, file) && !strcmp(s->param, param)) 134 | return true; 135 | return false; 136 | } 137 | 138 | static void add_initd(struct runqueue *q, char *file, char *param) 139 | { 140 | static const struct runqueue_task_type initd_type = { 141 | .run = q_initd_run, 142 | .cancel = runqueue_process_cancel_cb, 143 | .kill = runqueue_process_kill_cb, 144 | }; 145 | struct initd *s; 146 | char *p, *f; 147 | 148 | if (!strcmp(param, "running") && 149 | (find_runqueue_list_entry(&q->tasks_active.list, file, param) || 150 | find_runqueue_list_entry(&q->tasks_inactive.list, file, param))) 151 | return; 152 | 153 | s = calloc_a(sizeof(*s), &f, strlen(file) + 1, &p, strlen(param) + 1); 154 | if (!s) { 155 | ERROR("Out of memory in %s.\n", file); 156 | return; 157 | } 158 | s->proc.task.type = &initd_type; 159 | s->proc.task.complete = q_initd_complete; 160 | if (!strcmp(param, "stop") || !strcmp(param, "shutdown")) { 161 | s->proc.task.run_timeout = 15000; 162 | s->proc.task.cancel_timeout = 10000; 163 | } 164 | s->param = p; 165 | s->file = f; 166 | strcpy(s->param, param); 167 | strcpy(s->file, file); 168 | runqueue_task_add(q, &s->proc.task, false); 169 | } 170 | 171 | static int _rc(struct runqueue *q, char *path, const char *file, char *pattern, char *param) 172 | { 173 | char *dir = alloca(2 + strlen(path) + strlen(file) + strlen(pattern)); 174 | glob_t gl; 175 | int j; 176 | 177 | if (!dir) { 178 | ERROR("Out of memory in %s.\n", file); 179 | return -1; 180 | } 181 | 182 | DEBUG(2, "running %s/%s%s %s\n", path, file, pattern, param); 183 | sprintf(dir, "%s/%s%s", path, file, pattern); 184 | if (glob(dir, GLOB_NOESCAPE | GLOB_MARK, NULL, &gl)) { 185 | DEBUG(2, "glob failed on %s\n", dir); 186 | return -1; 187 | } 188 | 189 | for (j = 0; j < gl.gl_pathc; j++) 190 | add_initd(q, gl.gl_pathv[j], param); 191 | 192 | globfree(&gl); 193 | 194 | return 0; 195 | } 196 | 197 | int rcS(char *pattern, char *param, void (*q_empty)(struct runqueue *)) 198 | { 199 | runqueue_init(&q); 200 | q.empty_cb = q_empty; 201 | q.max_running_tasks = 1; 202 | 203 | return _rc(&q, "/etc/rc.d", pattern, "*", param); 204 | } 205 | 206 | int rc(const char *file, char *param) 207 | { 208 | return _rc(&r, "/etc/init.d", file, "", param); 209 | } 210 | 211 | static void r_empty(struct runqueue *q) 212 | { 213 | 214 | } 215 | 216 | static void __attribute__((constructor)) rc_init() { 217 | runqueue_init(&r); 218 | r.empty_cb = r_empty; 219 | r.max_running_tasks = 8; 220 | } 221 | -------------------------------------------------------------------------------- /plug/udevtrigger.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2004-2006 Kay Sievers 3 | * Copyright (C) 2006 Hannes Reinecke 4 | * 5 | * This program is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License as published by the 7 | * Free Software Foundation version 2 of the License. 8 | * 9 | * This program is distributed in the hope that it will be useful, but 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | * 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #define PATH_SIZE 512 36 | 37 | #ifndef strlcpy 38 | #define strlcpy(d,s,l) (strncpy(d,s,l), (d)[(l)-1] = '\0') 39 | #endif 40 | 41 | #ifndef strlcat 42 | #define strlcat(d,s,l) strncat(d,s,(l)-strlen(d)-1) 43 | #endif 44 | 45 | static int verbose; 46 | static int dry_run; 47 | 48 | static void log_message(int priority, const char *format, ...) 49 | { 50 | va_list args; 51 | 52 | va_start(args, format); 53 | vsyslog(priority, format, args); 54 | va_end(args); 55 | } 56 | 57 | #undef err 58 | #define err(format, arg...) \ 59 | do { \ 60 | log_message(LOG_ERR ,"%s: " format ,__FUNCTION__ ,## arg); \ 61 | } while (0) 62 | 63 | #undef info 64 | #define info(format, arg...) \ 65 | do { \ 66 | log_message(LOG_INFO ,"%s: " format ,__FUNCTION__ ,## arg); \ 67 | } while (0) 68 | 69 | #ifdef UDEV_DEBUG 70 | #undef dbg 71 | #define dbg(format, arg...) \ 72 | do { \ 73 | log_message(LOG_DEBUG ,"%s: " format ,__FUNCTION__ ,## arg); \ 74 | } while (0) 75 | #else 76 | #define dbg(...) do {} while(0) 77 | #endif 78 | 79 | 80 | static void trigger_uevent(const char *devpath) 81 | { 82 | char filename[PATH_SIZE]; 83 | int fd; 84 | 85 | strlcpy(filename, "/sys", sizeof(filename)); 86 | strlcat(filename, devpath, sizeof(filename)); 87 | strlcat(filename, "/uevent", sizeof(filename)); 88 | 89 | if (verbose) 90 | printf("%s\n", devpath); 91 | 92 | if (dry_run) 93 | return; 94 | 95 | fd = open(filename, O_WRONLY); 96 | if (fd < 0) { 97 | dbg("error on opening %s: %m\n", filename); 98 | return; 99 | } 100 | 101 | if (write(fd, "add", 3) < 0) 102 | info("error on triggering %s: %m\n", filename); 103 | 104 | close(fd); 105 | } 106 | 107 | static int sysfs_resolve_link(char *devpath, size_t size) 108 | { 109 | char link_path[PATH_SIZE]; 110 | char link_target[PATH_SIZE]; 111 | int len; 112 | int i; 113 | int back; 114 | 115 | strlcpy(link_path, "/sys", sizeof(link_path)); 116 | strlcat(link_path, devpath, sizeof(link_path)); 117 | len = readlink(link_path, link_target, sizeof(link_target) - 1); 118 | if (len <= 0) 119 | return -1; 120 | link_target[len] = '\0'; 121 | dbg("path link '%s' points to '%s'", devpath, link_target); 122 | 123 | for (back = 0; strncmp(&link_target[back * 3], "../", 3) == 0; back++) 124 | ; 125 | dbg("base '%s', tail '%s', back %i", devpath, &link_target[back * 3], back); 126 | for (i = 0; i <= back; i++) { 127 | char *pos = strrchr(devpath, '/'); 128 | 129 | if (pos == NULL) 130 | return -1; 131 | pos[0] = '\0'; 132 | } 133 | dbg("after moving back '%s'", devpath); 134 | strlcat(devpath, "/", size); 135 | strlcat(devpath, &link_target[back * 3], size); 136 | return 0; 137 | } 138 | 139 | static bool device_has_attribute(const char *path, const char *attr, 140 | mode_t mode) 141 | { 142 | char filename[PATH_SIZE]; 143 | struct stat statbuf; 144 | 145 | strlcpy(filename, path, sizeof(filename)); 146 | strlcat(filename, attr, sizeof(filename)); 147 | 148 | if (stat(filename, &statbuf) < 0) 149 | return false; 150 | 151 | if (!(statbuf.st_mode & mode)) 152 | return false; 153 | 154 | return true; 155 | } 156 | 157 | static int device_list_insert(const char *path) 158 | { 159 | char devpath[PATH_SIZE]; 160 | struct stat statbuf; 161 | 162 | dbg("add '%s'" , path); 163 | 164 | /* we only have a device, if we have a dev and an uevent file */ 165 | if (!device_has_attribute(path, "/dev", S_IRUSR) || 166 | !device_has_attribute(path, "/uevent", S_IWUSR)) 167 | return -1; 168 | 169 | strlcpy(devpath, &path[4], sizeof(devpath)); 170 | 171 | /* resolve possible link to real target */ 172 | if (lstat(path, &statbuf) < 0) 173 | return -1; 174 | if (S_ISLNK(statbuf.st_mode)) 175 | if (sysfs_resolve_link(devpath, sizeof(devpath)) != 0) 176 | return -1; 177 | 178 | trigger_uevent(devpath); 179 | return 0; 180 | } 181 | 182 | static void scan_subdir(const char *base, const char *subdir, 183 | bool insert, int depth) 184 | { 185 | DIR *dir; 186 | struct dirent *dent; 187 | 188 | dir = opendir(base); 189 | if (dir == NULL) 190 | return; 191 | 192 | for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { 193 | char dirname[PATH_SIZE]; 194 | 195 | if (dent->d_name[0] == '.') 196 | continue; 197 | 198 | strlcpy(dirname, base, sizeof(dirname)); 199 | strlcat(dirname, "/", sizeof(dirname)); 200 | strlcat(dirname, dent->d_name, sizeof(dirname)); 201 | 202 | if (insert) { 203 | int err; 204 | 205 | err = device_list_insert(dirname); 206 | if (err) 207 | continue; 208 | } 209 | 210 | if (subdir) 211 | strlcat(dirname, subdir, sizeof(base)); 212 | 213 | if (depth) 214 | scan_subdir(dirname, NULL, true, depth - 1); 215 | } 216 | 217 | closedir(dir); 218 | } 219 | 220 | int main(int argc, char *argv[], char *envp[]) 221 | { 222 | struct stat statbuf; 223 | int option; 224 | 225 | openlog("udevtrigger", LOG_PID | LOG_CONS, LOG_DAEMON); 226 | 227 | while (1) { 228 | option = getopt(argc, argv, "vnh"); 229 | if (option == -1) 230 | break; 231 | 232 | switch (option) { 233 | case 'v': 234 | verbose = 1; 235 | break; 236 | case 'n': 237 | dry_run = 1; 238 | break; 239 | case 'h': 240 | printf("Usage: udevtrigger OPTIONS\n" 241 | " -v print the list of devices while running\n" 242 | " -n do not actually trigger the events\n" 243 | " -h print this text\n" 244 | "\n"); 245 | goto exit; 246 | default: 247 | goto exit; 248 | } 249 | } 250 | 251 | 252 | /* if we have /sys/subsystem, forget all the old stuff */ 253 | scan_subdir("/sys/bus", "/devices", false, 1); 254 | scan_subdir("/sys/class", NULL, false, 1); 255 | 256 | /* scan "block" if it isn't a "class" */ 257 | if (stat("/sys/class/block", &statbuf) != 0) 258 | scan_subdir("/sys/block", NULL, true, 1); 259 | 260 | exit: 261 | 262 | closelog(); 263 | return 0; 264 | } 265 | -------------------------------------------------------------------------------- /jail/capabilities.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Etienne CHAMPETIER 3 | * Copyright (C) 2020 Daniel Golle 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public License version 2.1 7 | * as published by the Free Software Foundation 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | */ 14 | 15 | #define _GNU_SOURCE 1 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "log.h" 22 | #include "../capabilities-names.h" 23 | #include "capabilities.h" 24 | 25 | #define JAIL_CAP_ERROR (1LLU << (CAP_LAST_CAP+1)) 26 | #define JAIL_CAP_ALL (0xffffffffffffffffLLU) 27 | 28 | static int find_capabilities(const char *name) 29 | { 30 | int i; 31 | 32 | for (i = 0; i <= CAP_LAST_CAP; i++) 33 | if (capabilities_names[i] && !strcasecmp(capabilities_names[i], name)) 34 | return i; 35 | 36 | return -1; 37 | } 38 | 39 | enum { 40 | OCI_CAPABILITIES_BOUNDING, 41 | OCI_CAPABILITIES_EFFECTIVE, 42 | OCI_CAPABILITIES_INHERITABLE, 43 | OCI_CAPABILITIES_PERMITTED, 44 | OCI_CAPABILITIES_AMBIENT, 45 | __OCI_CAPABILITIES_MAX 46 | }; 47 | 48 | static const struct blobmsg_policy oci_capabilities_policy[] = { 49 | [OCI_CAPABILITIES_BOUNDING] = { "bounding", BLOBMSG_TYPE_ARRAY }, 50 | [OCI_CAPABILITIES_EFFECTIVE] = { "effective", BLOBMSG_TYPE_ARRAY }, 51 | [OCI_CAPABILITIES_INHERITABLE] = { "inheritable", BLOBMSG_TYPE_ARRAY }, 52 | [OCI_CAPABILITIES_PERMITTED] = { "permitted", BLOBMSG_TYPE_ARRAY }, 53 | [OCI_CAPABILITIES_AMBIENT] = { "ambient", BLOBMSG_TYPE_ARRAY }, 54 | }; 55 | 56 | static uint64_t parseOCIcap(struct blob_attr *msg) 57 | { 58 | struct blob_attr *cur; 59 | int rem; 60 | uint64_t caps = 0; 61 | int capnum; 62 | 63 | /* each capset is optional, set all-1 mask if absent */ 64 | if (!msg) 65 | return JAIL_CAP_ALL; 66 | 67 | blobmsg_for_each_attr(cur, msg, rem) { 68 | capnum = find_capabilities(blobmsg_get_string(cur)); 69 | if (capnum < 0) 70 | return JAIL_CAP_ERROR; 71 | 72 | caps |= (1LLU << capnum); 73 | } 74 | 75 | return caps; 76 | } 77 | 78 | int parseOCIcapabilities(struct jail_capset *capset, struct blob_attr *msg) 79 | { 80 | struct blob_attr *tb[__OCI_CAPABILITIES_MAX]; 81 | uint64_t caps; 82 | blobmsg_parse(oci_capabilities_policy, __OCI_CAPABILITIES_MAX, tb, blobmsg_data(msg), blobmsg_len(msg)); 83 | 84 | caps = parseOCIcap(tb[OCI_CAPABILITIES_BOUNDING]); 85 | if (caps == JAIL_CAP_ERROR) 86 | return EINVAL; 87 | else 88 | capset->bounding = caps; 89 | 90 | caps = parseOCIcap(tb[OCI_CAPABILITIES_EFFECTIVE]); 91 | if (caps == JAIL_CAP_ERROR) 92 | return EINVAL; 93 | else 94 | capset->effective = caps; 95 | 96 | caps = parseOCIcap(tb[OCI_CAPABILITIES_INHERITABLE]); 97 | if (caps == JAIL_CAP_ERROR) 98 | return EINVAL; 99 | else 100 | capset->inheritable = caps; 101 | 102 | caps = parseOCIcap(tb[OCI_CAPABILITIES_PERMITTED]); 103 | if (caps == JAIL_CAP_ERROR) 104 | return EINVAL; 105 | else 106 | capset->permitted = caps; 107 | 108 | caps = parseOCIcap(tb[OCI_CAPABILITIES_AMBIENT]); 109 | if (caps == JAIL_CAP_ERROR) 110 | return EINVAL; 111 | else 112 | capset->ambient = caps; 113 | 114 | capset->apply = 1; 115 | 116 | return 0; 117 | } 118 | 119 | 120 | int applyOCIcapabilities(struct jail_capset ocicapset, uint64_t retain) 121 | { 122 | struct __user_cap_header_struct uh = {}; 123 | struct __user_cap_data_struct ud[2]; 124 | int cap; 125 | int is_set; 126 | 127 | if (!ocicapset.apply) 128 | return 0; 129 | 130 | /* drop from bounding set */ 131 | if (ocicapset.bounding != JAIL_CAP_ALL) { 132 | for (cap = 0; cap <= CAP_LAST_CAP; cap++) { 133 | if (!prctl(PR_CAPBSET_READ, cap, 0, 0, 0)) { 134 | /* can't raise */ 135 | if (ocicapset.bounding & (1LLU << cap)) 136 | ERROR("capability %s (%d) is not in bounding set\n", capabilities_names[cap], cap); 137 | 138 | continue; 139 | } 140 | if ( ((ocicapset.bounding | retain) & (1LLU << cap)) == 0) { 141 | DEBUG("dropping capability %s (%d) from bounding set\n", capabilities_names[cap], cap); 142 | if (prctl(PR_CAPBSET_DROP, cap, 0, 0, 0)) { 143 | ERROR("prctl(PR_CAPBSET_DROP, %d) failed: %m\n", cap); 144 | return errno; 145 | } 146 | } else { 147 | DEBUG("keeping capability %s (%d)\n", capabilities_names[cap], cap); 148 | } 149 | } 150 | } 151 | 152 | /* set effective, permitted and inheritable */ 153 | uh.version = _LINUX_CAPABILITY_VERSION_3; 154 | uh.pid = getpid(); 155 | 156 | if (capget(&uh, ud)) { 157 | ERROR("capget() failed\n"); 158 | return -1; 159 | } 160 | 161 | DEBUG("old capabilities: Pe=%016llx Pp=%016llx Pi=%016llx\n", 162 | 0LLU | ud[0].effective | (0LLU | ud[1].effective) << 32, 163 | 0LLU | ud[0].permitted | (0LLU | ud[1].permitted) << 32, 164 | 0LLU | ud[0].inheritable | (0LLU | ud[1].inheritable) << 32); 165 | 166 | if (ocicapset.effective != JAIL_CAP_ALL) { 167 | ud[0].effective = (ocicapset.effective | retain) & 0xFFFFFFFFU; 168 | ud[1].effective = ((ocicapset.effective | retain) >> 32) & 0xFFFFFFFFU; 169 | } 170 | 171 | if (ocicapset.permitted != JAIL_CAP_ALL) { 172 | ud[0].permitted = (ocicapset.permitted | retain) & 0xFFFFFFFFU; 173 | ud[1].permitted = ((ocicapset.permitted | retain) >> 32) & 0xFFFFFFFFU; 174 | } 175 | 176 | if (ocicapset.inheritable != JAIL_CAP_ALL) { 177 | ud[0].inheritable = (ocicapset.inheritable | retain) & 0xFFFFFFFFU; 178 | ud[1].inheritable = ((ocicapset.inheritable | retain) >> 32) & 0xFFFFFFFFU; 179 | } 180 | 181 | DEBUG("new capabilities: Pe=%016llx Pp=%016llx Pi=%016llx\n", 182 | 0LLU | ud[0].effective | (0LLU | ud[1].effective) << 32, 183 | 0LLU | ud[0].permitted | (0LLU | ud[1].permitted) << 32, 184 | 0LLU | ud[0].inheritable | (0LLU | ud[1].inheritable) << 32); 185 | 186 | if (capset(&uh, ud)) { 187 | ERROR("capset() failed\n"); 188 | return -1; 189 | } 190 | 191 | /* edit ambient set */ 192 | if (ocicapset.ambient != JAIL_CAP_ALL) { 193 | for (cap = 0; cap <= CAP_LAST_CAP; cap++) { 194 | is_set = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, cap, 0, 0); 195 | if ( (ocicapset.ambient & (1LLU << cap)) == 0) { 196 | if (is_set) { 197 | DEBUG("dropping capability %s (%d) from ambient set\n", capabilities_names[cap], cap); 198 | if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_LOWER, cap, 0, 0)) { 199 | ERROR("prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_LOWER, %d, 0, 0) failed: %m\n", cap); 200 | return errno; 201 | } 202 | } 203 | } else { 204 | if (!is_set) { 205 | DEBUG("raising capability %s (%d) to ambient set\n", capabilities_names[cap], cap); 206 | if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap, 0, 0)) {\ 207 | ERROR("prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, %d, 0, 0) failed: %m\n", cap); 208 | return errno; 209 | } 210 | } 211 | } 212 | } 213 | } 214 | 215 | return 0; 216 | } 217 | 218 | int parseOCIcapabilities_from_file(struct jail_capset *capset, const char *file) 219 | { 220 | struct blob_buf b = { 0 }; 221 | int ret; 222 | 223 | blob_buf_init(&b, 0); 224 | ret = !blobmsg_add_json_from_file(&b, file); 225 | if (ret) { 226 | ERROR("failed to load %s\n", file); 227 | goto err; 228 | } 229 | 230 | ret = parseOCIcapabilities(capset, b.head); 231 | 232 | err: 233 | blob_buf_free(&b); 234 | return ret; 235 | } 236 | -------------------------------------------------------------------------------- /service/trigger.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Felix Fietkau 3 | * Copyright (C) 2013 John Crispin 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public License version 2.1 7 | * as published by the Free Software Foundation 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | */ 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "../procd.h" 33 | 34 | struct trigger { 35 | struct list_head list; 36 | 37 | void *id; 38 | char *type; 39 | int timeout; 40 | 41 | struct blob_attr *rule; 42 | struct blob_attr *data; 43 | 44 | struct json_script_ctx jctx; 45 | }; 46 | 47 | struct trigger_command { 48 | struct avl_node avl; 49 | struct uloop_timeout delay; 50 | bool requeue; 51 | 52 | struct runqueue_process proc; 53 | struct json_script_ctx jctx; 54 | 55 | struct blob_attr data[]; 56 | }; 57 | 58 | static LIST_HEAD(triggers); 59 | static RUNQUEUE(q, 1); 60 | static AVL_TREE(trigger_pending, avl_blobcmp, false, NULL); 61 | 62 | static const char* rule_handle_var(struct json_script_ctx *ctx, const char *name, struct blob_attr *vars) 63 | { 64 | return NULL; 65 | } 66 | 67 | static struct json_script_file * 68 | rule_load_script(struct json_script_ctx *ctx, const char *name) 69 | { 70 | struct trigger *t = container_of(ctx, struct trigger, jctx); 71 | 72 | if (strcmp(name, t->type) != 0) 73 | return NULL; 74 | 75 | return json_script_file_from_blobmsg(t->type, t->rule, blob_pad_len(t->rule)); 76 | } 77 | 78 | static void trigger_free(struct trigger *t) 79 | { 80 | json_script_free(&t->jctx); 81 | free(t->data); 82 | list_del(&t->list); 83 | free(t); 84 | } 85 | 86 | static void trigger_command_complete(struct runqueue *q, struct runqueue_task *p) 87 | { 88 | struct trigger_command *cmd = container_of(p, struct trigger_command, proc.task); 89 | 90 | if (cmd->requeue) { 91 | cmd->requeue = false; 92 | runqueue_task_add(q, p, false); 93 | return; 94 | } 95 | 96 | avl_delete(&trigger_pending, &cmd->avl); 97 | free(cmd); 98 | } 99 | 100 | static void trigger_command_run(struct runqueue *q, struct runqueue_task *t) 101 | { 102 | struct trigger_command *cmd = container_of(t, struct trigger_command, proc.task); 103 | struct blob_attr *cur; 104 | char **argv; 105 | pid_t pid; 106 | int n = 0; 107 | int rem; 108 | int fd; 109 | 110 | pid = fork(); 111 | if (pid < 0) { 112 | trigger_command_complete(q, t); 113 | return; 114 | } 115 | 116 | if (pid) { 117 | runqueue_process_add(q, &cmd->proc, pid); 118 | return; 119 | } 120 | 121 | if (debug < 3 && (fd = open("/dev/null", O_RDWR)) >= 0) { 122 | dup2(fd, STDIN_FILENO); 123 | dup2(fd, STDOUT_FILENO); 124 | dup2(fd, STDERR_FILENO); 125 | if (fd > STDERR_FILENO) 126 | close(fd); 127 | } 128 | 129 | blobmsg_for_each_attr(cur, cmd->data, rem) 130 | n++; 131 | 132 | argv = alloca((n + 1) * sizeof(*argv)); 133 | n = 0; 134 | blobmsg_for_each_attr(cur, cmd->data, rem) 135 | argv[n++] = blobmsg_get_string(cur); 136 | argv[n] = NULL; 137 | 138 | if (n > 0) 139 | execvp(argv[0], &argv[0]); 140 | 141 | exit(1); 142 | } 143 | 144 | static void trigger_command_start(struct uloop_timeout *timeout) 145 | { 146 | static const struct runqueue_task_type trigger_command_type = { 147 | .run = trigger_command_run, 148 | .cancel = runqueue_process_cancel_cb, 149 | .kill = runqueue_process_kill_cb, 150 | }; 151 | struct trigger_command *cmd = container_of(timeout, struct trigger_command, delay); 152 | 153 | cmd->proc.task.type = &trigger_command_type; 154 | cmd->proc.task.complete = trigger_command_complete; 155 | runqueue_task_add(&q, &cmd->proc.task, false); 156 | } 157 | 158 | static void trigger_command_add(struct trigger *t, struct blob_attr *data) 159 | { 160 | struct trigger_command *cmd; 161 | int64_t remaining; 162 | 163 | cmd = avl_find_element(&trigger_pending, data, cmd, avl); 164 | if (cmd) { 165 | /* Command currently running? */ 166 | if (!cmd->delay.pending) { 167 | cmd->requeue = true; 168 | return; 169 | } 170 | 171 | /* Extend timer if trigger timeout is bigger than remaining time */ 172 | remaining = uloop_timeout_remaining64(&cmd->delay); 173 | if (remaining < t->timeout) 174 | uloop_timeout_set(&cmd->delay, t->timeout); 175 | 176 | return; 177 | } 178 | 179 | cmd = calloc(1, sizeof(*cmd) + blob_pad_len(data)); 180 | if (!cmd) 181 | return; 182 | 183 | cmd->avl.key = cmd->data; 184 | cmd->delay.cb = trigger_command_start; 185 | memcpy(cmd->data, data, blob_pad_len(data)); 186 | avl_insert(&trigger_pending, &cmd->avl); 187 | uloop_timeout_set(&cmd->delay, t->timeout > 0 ? t->timeout : 1); 188 | } 189 | 190 | static void rule_handle_command(struct json_script_ctx *ctx, const char *name, 191 | struct blob_attr *exec, struct blob_attr *vars) 192 | { 193 | struct trigger *t = container_of(ctx, struct trigger, jctx); 194 | 195 | if (!strcmp(name, "run_script")) { 196 | trigger_command_add(t, exec); 197 | return; 198 | } 199 | } 200 | 201 | static void rule_handle_error(struct json_script_ctx *ctx, const char *msg, 202 | struct blob_attr *context) 203 | { 204 | char *s; 205 | 206 | s = blobmsg_format_json(context, false); 207 | ERROR("ERROR: %s in block: %s\n", msg, s); 208 | free(s); 209 | } 210 | 211 | static struct trigger* _trigger_add(char *type, struct blob_attr *rule, int timeout, void *id) 212 | { 213 | char *_t; 214 | struct blob_attr *_r; 215 | struct trigger *t = calloc_a(sizeof(*t), &_t, strlen(type) + 1, &_r, blob_pad_len(rule)); 216 | 217 | t->type = _t; 218 | t->rule = _r; 219 | t->timeout = timeout; 220 | t->id = id; 221 | t->jctx.handle_var = rule_handle_var, 222 | t->jctx.handle_error = rule_handle_error, 223 | t->jctx.handle_command = rule_handle_command, 224 | t->jctx.handle_file = rule_load_script, 225 | 226 | strcpy(t->type, type); 227 | memcpy(t->rule, rule, blob_pad_len(rule)); 228 | 229 | list_add(&t->list, &triggers); 230 | json_script_init(&t->jctx); 231 | 232 | return t; 233 | } 234 | 235 | void trigger_add(struct blob_attr *rule, void *id) 236 | { 237 | struct blob_attr *cur; 238 | int rem; 239 | 240 | blobmsg_for_each_attr(cur, rule, rem) { 241 | struct blob_attr *_cur, *type = NULL, *script = NULL, *timeout = NULL; 242 | int _rem; 243 | int i = 0; 244 | 245 | if (blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY) 246 | continue; 247 | 248 | blobmsg_for_each_attr(_cur, cur, _rem) { 249 | switch (i++) { 250 | case 0: 251 | if (blobmsg_type(_cur) == BLOBMSG_TYPE_STRING) 252 | type = _cur; 253 | break; 254 | 255 | case 1: 256 | if (blobmsg_type(_cur) == BLOBMSG_TYPE_ARRAY) 257 | script = _cur; 258 | break; 259 | 260 | case 2: 261 | if (blobmsg_type(_cur) == BLOBMSG_TYPE_INT32) 262 | timeout = _cur; 263 | break; 264 | } 265 | } 266 | 267 | if (type && script) { 268 | int t = 0; 269 | 270 | if (timeout) 271 | t = blobmsg_get_u32(timeout); 272 | _trigger_add(blobmsg_get_string(type), script, t, id); 273 | } 274 | } 275 | } 276 | 277 | void trigger_del(void *id) 278 | { 279 | struct trigger *t, *n; 280 | 281 | list_for_each_entry_safe(t, n, &triggers, list) { 282 | if (t->id != id) 283 | continue; 284 | 285 | trigger_free(t); 286 | } 287 | } 288 | 289 | static bool trigger_match(const char *event, const char *match) 290 | { 291 | char *wildcard = strstr(match, ".*"); 292 | if (wildcard) 293 | return !strncmp(event, match, wildcard - match); 294 | return !strcmp(event, match); 295 | } 296 | 297 | void trigger_event(const char *type, struct blob_attr *data) 298 | { 299 | struct trigger *t; 300 | 301 | list_for_each_entry(t, &triggers, list) { 302 | if (!trigger_match(type, t->type)) 303 | continue; 304 | json_script_run(&t->jctx, t->type, data); 305 | } 306 | } 307 | -------------------------------------------------------------------------------- /jail/elf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 John Crispin 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License version 2.1 6 | * as published by the Free Software Foundation 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | #define _GNU_SOURCE 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | 25 | #include "elf.h" 26 | #include "fs.h" 27 | #include "log.h" 28 | 29 | struct avl_tree libraries; 30 | static LIST_HEAD(library_paths); 31 | 32 | static void alloc_library_path(const char *path) 33 | { 34 | struct stat s; 35 | if (stat(path, &s)) 36 | return; 37 | 38 | struct library_path *p; 39 | char *_path; 40 | 41 | p = calloc_a(sizeof(*p), 42 | &_path, strlen(path) + 1); 43 | if (!p) 44 | return; 45 | 46 | p->path = strcpy(_path, path); 47 | 48 | list_add_tail(&p->list, &library_paths); 49 | DEBUG("adding ld.so path %s\n", path); 50 | } 51 | 52 | /* 53 | * path = full path 54 | * name = soname/avl key 55 | */ 56 | void alloc_library(const char *path, const char *name) 57 | { 58 | struct library *l; 59 | char *_name, *_path; 60 | 61 | l = calloc_a(sizeof(*l), 62 | &_path, strlen(path) + 1, 63 | &_name, strlen(name) + 1); 64 | if (!l) 65 | return; 66 | 67 | l->avl.key = l->name = strcpy(_name, name); 68 | l->path = strcpy(_path, path); 69 | 70 | avl_insert(&libraries, &l->avl); 71 | DEBUG("adding library %s (%s)\n", path, name); 72 | } 73 | 74 | int lib_open(char **fullpath, const char *file) 75 | { 76 | struct library_path *p; 77 | char path[PATH_MAX]; 78 | int fd = -1; 79 | 80 | *fullpath = NULL; 81 | 82 | list_for_each_entry(p, &library_paths, list) { 83 | snprintf(path, sizeof(path), "%s/%s", p->path, file); 84 | fd = open(path, O_RDONLY|O_CLOEXEC); 85 | if (fd >= 0) { 86 | *fullpath = strdup(path); 87 | break; 88 | } 89 | } 90 | 91 | return fd; 92 | } 93 | 94 | const char* find_lib(const char *file) 95 | { 96 | struct library *l; 97 | 98 | l = avl_find_element(&libraries, file, l, avl); 99 | if (!l) 100 | return NULL; 101 | 102 | return l->path; 103 | } 104 | 105 | static int elf64_find_section(const char *map, unsigned int type, unsigned long *offset, unsigned long *size, unsigned long *vaddr) 106 | { 107 | Elf64_Ehdr *e; 108 | Elf64_Phdr *ph; 109 | int i; 110 | 111 | e = (Elf64_Ehdr *) map; 112 | ph = (Elf64_Phdr *) (map + e->e_phoff); 113 | 114 | for (i = 0; i < e->e_phnum; i++) { 115 | if (ph[i].p_type == type) { 116 | *offset = ph[i].p_offset; 117 | if (size) 118 | *size = ph[i].p_filesz; 119 | if (vaddr) 120 | *vaddr = ph[i].p_vaddr; 121 | return 0; 122 | } 123 | } 124 | 125 | return -1; 126 | } 127 | 128 | static int elf32_find_section(const char *map, unsigned int type, unsigned long *offset, unsigned long *size, unsigned long *vaddr) 129 | { 130 | Elf32_Ehdr *e; 131 | Elf32_Phdr *ph; 132 | int i; 133 | 134 | e = (Elf32_Ehdr *) map; 135 | ph = (Elf32_Phdr *) (map + e->e_phoff); 136 | 137 | for (i = 0; i < e->e_phnum; i++) { 138 | if (ph[i].p_type == type) { 139 | *offset = ph[i].p_offset; 140 | if (size) 141 | *size = ph[i].p_filesz; 142 | if (vaddr) 143 | *vaddr = ph[i].p_vaddr; 144 | return 0; 145 | } 146 | } 147 | 148 | return -1; 149 | } 150 | 151 | static int elf_find_section(const char *map, unsigned int type, unsigned long *offset, unsigned long *size, unsigned long *vaddr) 152 | { 153 | int clazz = map[EI_CLASS]; 154 | 155 | if (clazz == ELFCLASS32) 156 | return elf32_find_section(map, type, offset, size, vaddr); 157 | else if (clazz == ELFCLASS64) 158 | return elf64_find_section(map, type, offset, size, vaddr); 159 | 160 | ERROR("unknown elf format %d\n", clazz); 161 | 162 | return -1; 163 | } 164 | 165 | static int elf32_scan_dynamic(const char *map, unsigned long dyn_offset, unsigned long dyn_size, long load_offset) 166 | { 167 | Elf32_Dyn *dynamic = (Elf32_Dyn *) (map + dyn_offset); 168 | const char *strtab = NULL; 169 | 170 | while ((void *) dynamic < (void *) (map + dyn_offset + dyn_size)) { 171 | Elf32_Dyn *curr = dynamic; 172 | 173 | dynamic++; 174 | if (curr->d_tag != DT_STRTAB) 175 | continue; 176 | 177 | strtab = map + (curr->d_un.d_ptr - load_offset); 178 | break; 179 | } 180 | 181 | if (!strtab) 182 | return -1; 183 | 184 | dynamic = (Elf32_Dyn *) (map + dyn_offset); 185 | while ((void *) dynamic < (void *) (map + dyn_offset + dyn_size)) { 186 | Elf32_Dyn *curr = dynamic; 187 | 188 | dynamic++; 189 | if (curr->d_tag != DT_NEEDED) 190 | continue; 191 | 192 | if (add_path_and_deps(&strtab[curr->d_un.d_val], 1, -1, 1)) 193 | return -1; 194 | } 195 | 196 | return 0; 197 | } 198 | 199 | static int elf64_scan_dynamic(const char *map, unsigned long dyn_offset, unsigned long dyn_size, long load_offset) 200 | { 201 | Elf64_Dyn *dynamic = (Elf64_Dyn *) (map + dyn_offset); 202 | const char *strtab = NULL; 203 | 204 | while ((void *) dynamic < (void *) (map + dyn_offset + dyn_size)) { 205 | Elf64_Dyn *curr = dynamic; 206 | 207 | dynamic++; 208 | if (curr->d_tag != DT_STRTAB) 209 | continue; 210 | 211 | strtab = map + (curr->d_un.d_ptr - load_offset); 212 | break; 213 | } 214 | 215 | if (!strtab) 216 | return -1; 217 | 218 | dynamic = (Elf64_Dyn *) (map + dyn_offset); 219 | while ((void *) dynamic < (void *) (map + dyn_offset + dyn_size)) { 220 | Elf64_Dyn *curr = dynamic; 221 | 222 | dynamic++; 223 | if (curr->d_tag != DT_NEEDED) 224 | continue; 225 | 226 | if (add_path_and_deps(&strtab[curr->d_un.d_val], 1, -1, 1)) 227 | return -1; 228 | } 229 | 230 | return 0; 231 | } 232 | 233 | int elf_load_deps(const char *path, const char *map) 234 | { 235 | unsigned long dyn_offset, dyn_size; 236 | unsigned long load_offset, load_vaddr; 237 | unsigned long interp_offset; 238 | 239 | if (elf_find_section(map, PT_INTERP, &interp_offset, NULL, NULL) == 0) { 240 | add_path_and_deps(map+interp_offset, 1, -1, 0); 241 | } 242 | 243 | if (elf_find_section(map, PT_LOAD, &load_offset, NULL, &load_vaddr)) { 244 | DEBUG("failed to load the .load section from %s\n", path); 245 | return 0; 246 | } 247 | 248 | if (elf_find_section(map, PT_DYNAMIC, &dyn_offset, &dyn_size, NULL)) { 249 | DEBUG("failed to load the .dynamic section from %s\n", path); 250 | return 0; 251 | } 252 | 253 | int clazz = map[EI_CLASS]; 254 | 255 | if (clazz == ELFCLASS32) 256 | return elf32_scan_dynamic(map, dyn_offset, dyn_size, load_vaddr - load_offset); 257 | else if (clazz == ELFCLASS64) 258 | return elf64_scan_dynamic(map, dyn_offset, dyn_size, load_vaddr - load_offset); 259 | 260 | ERROR("unknown elf format %d\n", clazz); 261 | return -1; 262 | } 263 | 264 | static void load_ldso_conf(const char *conf) 265 | { 266 | FILE* fp = fopen(conf, "r"); 267 | char line[PATH_MAX]; 268 | 269 | if (!fp) { 270 | DEBUG("failed to open %s\n", conf); 271 | return; 272 | } 273 | 274 | while (!feof(fp)) { 275 | int len; 276 | 277 | if (!fgets(line, sizeof(line), fp)) 278 | break; 279 | len = strlen(line); 280 | if (len < 2) 281 | continue; 282 | if (*line == '#') 283 | continue; 284 | if (line[len - 1] == '\n') 285 | line[len - 1] = '\0'; 286 | if (!strncmp(line, "include ", 8)) { 287 | char *sep = strstr(line, " "); 288 | glob_t gl; 289 | int i; 290 | 291 | if (!sep) 292 | continue;; 293 | while (*sep == ' ') 294 | sep++; 295 | if (glob(sep, GLOB_NOESCAPE | GLOB_MARK, NULL, &gl)) { 296 | ERROR("glob failed on %s\n", sep); 297 | continue; 298 | } 299 | for (i = 0; i < gl.gl_pathc; i++) 300 | load_ldso_conf(gl.gl_pathv[i]); 301 | globfree(&gl); 302 | } else { 303 | alloc_library_path(line); 304 | } 305 | } 306 | 307 | fclose(fp); 308 | } 309 | 310 | void init_library_search(void) 311 | { 312 | avl_init(&libraries, avl_strcmp, false, NULL); 313 | alloc_library_path("/lib"); 314 | alloc_library_path("/lib64"); 315 | alloc_library_path("/usr/lib"); 316 | load_ldso_conf("/etc/ld.so.conf"); 317 | } 318 | 319 | void free_library_search(void) 320 | { 321 | struct library_path *p, *ptmp; 322 | struct library *l, *tmp; 323 | 324 | list_for_each_entry_safe(p, ptmp, &library_paths, list) 325 | free(p); 326 | 327 | avl_remove_all_elements(&libraries, l, avl, tmp) 328 | free(l); 329 | } 330 | -------------------------------------------------------------------------------- /inittab.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Felix Fietkau 3 | * Copyright (C) 2013 John Crispin 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public License version 2.1 7 | * as published by the Free Software Foundation 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | */ 14 | 15 | #define _GNU_SOURCE 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | #include 29 | 30 | #include "utils/utils.h" 31 | #include "procd.h" 32 | #include "rcS.h" 33 | 34 | #ifndef O_PATH 35 | #define O_PATH 010000000 36 | #endif 37 | 38 | #define TAG_ID 0 39 | #define TAG_RUNLVL 1 40 | #define TAG_ACTION 2 41 | #define TAG_PROCESS 3 42 | 43 | #define MAX_ARGS 8 44 | 45 | struct init_action; 46 | char *console = NULL; 47 | 48 | struct init_handler { 49 | const char *name; 50 | void (*cb) (struct init_action *a); 51 | int multi; 52 | }; 53 | 54 | struct init_action { 55 | struct list_head list; 56 | 57 | char *id; 58 | char *argv[MAX_ARGS]; 59 | char *line; 60 | 61 | struct init_handler *handler; 62 | struct uloop_process proc; 63 | 64 | int respawn; 65 | struct uloop_timeout tout; 66 | }; 67 | 68 | static const char *tab = "/etc/inittab"; 69 | static char *ask = "/sbin/askfirst"; 70 | 71 | static LIST_HEAD(actions); 72 | 73 | static int dev_exist(const char *dev) 74 | { 75 | int dfd, fd; 76 | 77 | dfd = open("/dev", O_PATH|O_DIRECTORY); 78 | 79 | if (dfd < 0) 80 | return 0; 81 | 82 | fd = openat(dfd, dev, O_RDONLY|O_NOCTTY); 83 | close(dfd); 84 | 85 | if (fd < 0) 86 | return 0; 87 | 88 | close(fd); 89 | return 1; 90 | } 91 | 92 | static void fork_worker(struct init_action *a) 93 | { 94 | pid_t p; 95 | 96 | a->proc.pid = fork(); 97 | if (!a->proc.pid) { 98 | p = setsid(); 99 | 100 | if (patch_stdio(a->id)) 101 | ERROR("Failed to setup i/o redirection\n"); 102 | 103 | ioctl(STDIN_FILENO, TIOCSCTTY, 1); 104 | tcsetpgrp(STDIN_FILENO, p); 105 | 106 | execvp(a->argv[0], a->argv); 107 | ERROR("Failed to execute %s: %m\n", a->argv[0]); 108 | exit(-1); 109 | } 110 | 111 | if (a->proc.pid > 0) { 112 | DEBUG(4, "Launched new %s action, pid=%d\n", 113 | a->handler->name, 114 | (int) a->proc.pid); 115 | uloop_process_add(&a->proc); 116 | } 117 | } 118 | 119 | static void child_exit(struct uloop_process *proc, int ret) 120 | { 121 | struct init_action *a = container_of(proc, struct init_action, proc); 122 | 123 | DEBUG(4, "pid:%d, exitcode:%d\n", proc->pid, ret); 124 | proc->pid = 0; 125 | 126 | if (a->respawn < 0) 127 | return; 128 | 129 | if (!dev_exist(a->id)) { 130 | DEBUG(4, "Skipping respawn: device '%s' does not exist anymore\n", a->id); 131 | return; 132 | } 133 | 134 | uloop_timeout_set(&a->tout, a->respawn); 135 | } 136 | 137 | static void respawn(struct uloop_timeout *tout) 138 | { 139 | struct init_action *a = container_of(tout, struct init_action, tout); 140 | if (!a->proc.pid) 141 | fork_worker(a); 142 | } 143 | 144 | static void rcdone(struct runqueue *q) 145 | { 146 | procd_state_next(); 147 | } 148 | 149 | static void runrc(struct init_action *a) 150 | { 151 | if (!a->argv[1] || !a->argv[2]) { 152 | ERROR("valid format is rcS \n"); 153 | return; 154 | } 155 | 156 | /* proceed even if no init or shutdown scripts run */ 157 | if (rcS(a->argv[1], a->argv[2], rcdone)) 158 | rcdone(NULL); 159 | } 160 | 161 | static void askfirst(struct init_action *a) 162 | { 163 | int i; 164 | 165 | if (!dev_exist(a->id) || (console && !strcmp(console, a->id))) { 166 | DEBUG(4, "Skipping %s\n", a->id); 167 | return; 168 | } 169 | 170 | a->tout.cb = respawn; 171 | /* shift arguments only if not yet done */ 172 | if (a->argv[0] != ask) { 173 | for (i = MAX_ARGS - 1; i >= 1; i--) 174 | a->argv[i] = a->argv[i - 1]; 175 | a->argv[0] = ask; 176 | } 177 | a->respawn = 500; 178 | 179 | a->proc.cb = child_exit; 180 | if (!a->proc.pid) 181 | fork_worker(a); 182 | } 183 | 184 | static void askconsole(struct init_action *a) 185 | { 186 | char line[256], *tty, *split; 187 | int i; 188 | 189 | /* First, try console= on the kernel command line, 190 | * then fallback to /sys/class/tty/console/active, 191 | * which should work when linux,stdout-path (or equivalent) 192 | * is in the device tree 193 | */ 194 | tty = get_cmdline_val("console", line, sizeof(line)); 195 | if (tty == NULL || 196 | get_cmdline_val_offset("console", line, sizeof(line), 1)) { 197 | if (dev_exist("console")) 198 | tty = "console"; 199 | else 200 | tty = get_active_console(line, sizeof(line)); 201 | } 202 | if (tty != NULL) { 203 | split = strchr(tty, ','); 204 | if (split != NULL) 205 | *split = '\0'; 206 | 207 | if (!dev_exist(tty)) { 208 | DEBUG(4, "skipping %s\n", tty); 209 | return; 210 | } 211 | 212 | console = strdup(tty); 213 | a->id = strdup(tty); 214 | } 215 | else { 216 | console = NULL; 217 | a->id = NULL; 218 | } 219 | 220 | a->tout.cb = respawn; 221 | /* shift arguments only if not yet done */ 222 | if (a->argv[0] != ask) { 223 | for (i = MAX_ARGS - 1; i >= 1; i--) 224 | a->argv[i] = a->argv[i - 1]; 225 | a->argv[0] = ask; 226 | } 227 | a->respawn = 500; 228 | 229 | a->proc.cb = child_exit; 230 | if (!a->proc.pid) 231 | fork_worker(a); 232 | } 233 | 234 | static void rcrespawn(struct init_action *a) 235 | { 236 | a->tout.cb = respawn; 237 | a->respawn = 500; 238 | 239 | a->proc.cb = child_exit; 240 | if (!a->proc.pid) 241 | fork_worker(a); 242 | } 243 | 244 | static struct init_handler handlers[] = { 245 | { 246 | .name = "sysinit", 247 | .cb = runrc, 248 | }, { 249 | .name = "shutdown", 250 | .cb = runrc, 251 | }, { 252 | .name = "askfirst", 253 | .cb = askfirst, 254 | .multi = 1, 255 | }, { 256 | .name = "askconsole", 257 | .cb = askconsole, 258 | .multi = 1, 259 | }, { 260 | .name = "respawn", 261 | .cb = rcrespawn, 262 | .multi = 1, 263 | }, { 264 | .name = "askconsolelate", 265 | .cb = askconsole, 266 | .multi = 1, 267 | }, { 268 | .name = "respawnlate", 269 | .cb = rcrespawn, 270 | .multi = 1, 271 | } 272 | }; 273 | 274 | static int add_action(struct init_action *a, const char *name) 275 | { 276 | int i; 277 | 278 | for (i = 0; i < ARRAY_SIZE(handlers); i++) 279 | if (!strcmp(handlers[i].name, name)) { 280 | a->handler = &handlers[i]; 281 | list_add_tail(&a->list, &actions); 282 | return 0; 283 | } 284 | ERROR("Unknown init handler %s\n", name); 285 | return -1; 286 | } 287 | 288 | void procd_inittab_run(const char *handler) 289 | { 290 | struct init_action *a; 291 | 292 | list_for_each_entry(a, &actions, list) 293 | if (!strcmp(a->handler->name, handler)) { 294 | a->handler->cb(a); 295 | if (!a->handler->multi) 296 | break; 297 | } 298 | } 299 | 300 | void procd_inittab_kill(void) 301 | { 302 | struct init_action *a; 303 | 304 | list_for_each_entry(a, &actions, list) { 305 | a->respawn = -1; 306 | if (a->proc.pid) 307 | kill(a->proc.pid, SIGKILL); 308 | } 309 | } 310 | 311 | void procd_inittab(void) 312 | { 313 | #define LINE_LEN 128 314 | FILE *fp = fopen(tab, "r"); 315 | struct init_action *a; 316 | regex_t pat_inittab; 317 | regmatch_t matches[5]; 318 | char *line; 319 | 320 | if (!fp) { 321 | ERROR("Failed to open %s: %m\n", tab); 322 | return; 323 | } 324 | 325 | regcomp(&pat_inittab, "([a-zA-Z0-9]*):([a-zA-Z0-9]*):([a-zA-Z0-9]*):(.*)", REG_EXTENDED); 326 | line = malloc(LINE_LEN); 327 | a = calloc(1, sizeof(struct init_action)); 328 | 329 | while (fgets(line, LINE_LEN, fp)) { 330 | char *tags[TAG_PROCESS + 1]; 331 | char *tok; 332 | int i; 333 | int len = strlen(line); 334 | 335 | while (isspace(line[len - 1])) 336 | len--; 337 | line[len] = 0; 338 | 339 | if (*line == '#') 340 | continue; 341 | 342 | if (regexec(&pat_inittab, line, 5, matches, 0)) 343 | continue; 344 | 345 | DEBUG(4, "Parsing inittab - %s\n", line); 346 | 347 | for (i = TAG_ID; i <= TAG_PROCESS; i++) { 348 | line[matches[i].rm_eo] = '\0'; 349 | tags[i] = &line[matches[i + 1].rm_so]; 350 | }; 351 | 352 | tok = strtok(tags[TAG_PROCESS], " "); 353 | for (i = 0; i < (MAX_ARGS - 1) && tok; i++) { 354 | a->argv[i] = tok; 355 | tok = strtok(NULL, " "); 356 | } 357 | a->argv[i] = NULL; 358 | a->id = tags[TAG_ID]; 359 | a->line = line; 360 | 361 | if (add_action(a, tags[TAG_ACTION])) 362 | continue; 363 | line = malloc(LINE_LEN); 364 | a = calloc(1, sizeof(struct init_action)); 365 | } 366 | 367 | fclose(fp); 368 | free(line); 369 | free(a); 370 | regfree(&pat_inittab); 371 | } 372 | -------------------------------------------------------------------------------- /hotplug-dispatch.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Daniel Golle 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License version 2.1 6 | * as published by the Free Software Foundation 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | #define _GNU_SOURCE 15 | 16 | #include 17 | #include 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #include "procd.h" 36 | 37 | #define HOTPLUG_BASEDIR "/etc/hotplug.d" 38 | #define HOTPLUG_OBJECT_PREFIX "hotplug." 39 | 40 | #define INOTIFY_SZ (sizeof(struct inotify_event) + PATH_MAX + 1) 41 | 42 | struct ubus_context *ctx; 43 | static char *inotify_buffer; 44 | static struct uloop_fd fd_inotify_read; 45 | 46 | static AVL_TREE(subsystems, avl_strcmp, false, NULL); 47 | 48 | extern char **environ; 49 | 50 | struct hotplug_subsys { 51 | struct avl_node node; 52 | struct ubus_object ubus; 53 | }; 54 | 55 | struct envlist { 56 | struct avl_node avl; 57 | char *env; 58 | }; 59 | 60 | struct hotplug_process { 61 | struct ubus_object *ubus; 62 | char **envp; 63 | struct uloop_timeout timeout; 64 | struct uloop_process process; 65 | glob_t globbuf; 66 | unsigned int cnt; 67 | int ret; 68 | }; 69 | 70 | static void env_free(char **envp) 71 | { 72 | char **tmp; 73 | 74 | tmp = envp; 75 | while (*tmp) 76 | free(*(tmp++)); 77 | free(envp); 78 | } 79 | 80 | static void hotplug_free(struct hotplug_process *pc) 81 | { 82 | env_free(pc->envp); 83 | globfree(&pc->globbuf); 84 | free(pc); 85 | } 86 | 87 | static void hotplug_done(struct uloop_process *c, int ret) 88 | { 89 | struct hotplug_process *pc = container_of(c, struct hotplug_process, process); 90 | 91 | pc->ret = ret; 92 | 93 | uloop_timeout_set(&pc->timeout, 50); 94 | } 95 | 96 | static void hotplug_exec(struct uloop_timeout *t) 97 | { 98 | struct hotplug_process *pc = container_of(t, struct hotplug_process, timeout); 99 | char *script; 100 | char *exec_argv[4]; 101 | /* we have reached the last entry in the globbuf */ 102 | if (pc->cnt == pc->globbuf.gl_pathc) { 103 | hotplug_free(pc); 104 | return; 105 | } 106 | 107 | if (asprintf(&script, ". /lib/functions.sh\n. %s\n", pc->globbuf.gl_pathv[pc->cnt++]) == -1) { 108 | pc->ret = ENOMEM; 109 | return; 110 | } 111 | 112 | /* prepare for execve() */ 113 | exec_argv[0] = "/bin/sh"; 114 | exec_argv[1] = "-c"; 115 | exec_argv[2] = script; 116 | exec_argv[3] = NULL; 117 | 118 | /* set callback in uloop_process */ 119 | pc->process.cb = hotplug_done; 120 | pc->process.pid = fork(); 121 | if (pc->process.pid == 0) { 122 | /* child */ 123 | exit(execve(exec_argv[0], exec_argv, pc->envp)); 124 | } else if (pc->process.pid < 0) { 125 | /* fork error */ 126 | free(script); 127 | hotplug_free(pc); 128 | return; 129 | } 130 | /* parent */ 131 | free(script); 132 | uloop_process_add(&pc->process); 133 | } 134 | 135 | static int avl_envcmp(const void *k1, const void *k2, void *ptr) 136 | { 137 | const char *tmp; 138 | 139 | tmp = strchr(k1, '='); 140 | if (!tmp) 141 | return -1; 142 | 143 | /* 144 | * compare the variable name only, ie. limit strncmp to check 145 | * only up to and including the '=' sign 146 | */ 147 | return strncmp(k1, k2, (tmp - (char *)k1) + 1); 148 | } 149 | 150 | /* validate NULL-terminated environment variable name */ 151 | static int validate_envvarname(const char *envvarname) 152 | { 153 | const char *tmp = envvarname; 154 | 155 | /* check for illegal characters in env variable name */ 156 | while (tmp[0] != '\0') { 157 | if (!((tmp[0] >= 'a' && tmp[0] <= 'z') || 158 | (tmp[0] >= 'A' && tmp[0] <= 'Z') || 159 | (tmp[0] == '_') || 160 | /* allow numbers unless they are at the first character */ 161 | ((tmp != envvarname) && tmp[0] >= '0' && tmp[0] <= '9'))) 162 | return EINVAL; 163 | ++tmp; 164 | } 165 | 166 | return 0; 167 | } 168 | 169 | enum { 170 | HOTPLUG_ENV, 171 | __HOTPLUG_MAX 172 | }; 173 | 174 | static const struct blobmsg_policy hotplug_policy[__HOTPLUG_MAX] = { 175 | [HOTPLUG_ENV] = { .name = "env", .type = BLOBMSG_TYPE_ARRAY }, 176 | }; 177 | 178 | static int hotplug_call(struct ubus_context *ctx, struct ubus_object *obj, 179 | struct ubus_request_data *req, const char *method, 180 | struct blob_attr *msg) 181 | { 182 | const char *subsys = &obj->name[strlen(HOTPLUG_OBJECT_PREFIX)]; 183 | struct blob_attr *tb[__HOTPLUG_MAX], *cur; 184 | AVL_TREE(env, avl_envcmp, false, NULL); 185 | struct envlist *envle, *p; 186 | int rem; 187 | char **envp, *globstr, *tmp, **tmpenv; 188 | size_t envz = 0; 189 | struct hotplug_process *pc; 190 | bool async = true; 191 | int err = UBUS_STATUS_UNKNOWN_ERROR; 192 | 193 | blobmsg_parse(hotplug_policy, __HOTPLUG_MAX, tb, blobmsg_data(msg), blobmsg_len(msg)); 194 | 195 | if (!tb[HOTPLUG_ENV]) 196 | return UBUS_STATUS_INVALID_ARGUMENT; 197 | 198 | tmpenv = environ; 199 | 200 | /* first adding existing environment to avl_tree */ 201 | while (*tmpenv) { 202 | envle = calloc(1, sizeof(struct envlist)); 203 | if (!envle) 204 | goto err_envle; 205 | 206 | envle->env = strdup(*tmpenv); 207 | if (!envle->env) { 208 | free(envle); 209 | goto err_envle; 210 | } 211 | envle->avl.key = envle->env; 212 | if (avl_insert(&env, &envle->avl) == -1) { 213 | free(envle->env); 214 | free(envle); 215 | goto err_envle; 216 | } 217 | 218 | ++tmpenv; 219 | } 220 | 221 | /* then adding additional variables from ubus call */ 222 | blobmsg_for_each_attr(cur, tb[HOTPLUG_ENV], rem) { 223 | char *enve = blobmsg_get_string(cur); 224 | if (!enve) 225 | continue; 226 | 227 | if (!strncmp(enve, "LD_", 3)) 228 | continue; 229 | 230 | if (!strcmp(enve, "PATH")) 231 | continue; 232 | 233 | if (strlen(enve) < 3) 234 | continue; 235 | 236 | if (!(tmp = strchr(enve, '='))) 237 | continue; 238 | 239 | *tmp = '\0'; 240 | if (validate_envvarname(enve)) 241 | continue; 242 | *tmp = '='; 243 | 244 | if (!strcmp(enve, "ASYNC=0")) 245 | async = false; 246 | 247 | envle = calloc(1, sizeof(struct envlist)); 248 | if (!envle) 249 | goto err_envle; 250 | 251 | envle->env = strdup(enve); 252 | if (!envle->env) { 253 | free(envle); 254 | goto err_envle; 255 | } 256 | envle->avl.key = envle->env; 257 | if (avl_insert(&env, &envle->avl)) { 258 | /* do not override existing env values, just skip */ 259 | free((void*)envle->env); 260 | free(envle); 261 | } 262 | } 263 | 264 | /* synchronous calls are unsupported for now */ 265 | if (!async) { 266 | err = UBUS_STATUS_NOT_SUPPORTED; 267 | goto err_envle; 268 | } 269 | 270 | /* allocating new environment */ 271 | avl_for_each_element(&env, envle, avl) 272 | ++envz; 273 | 274 | envp = calloc(envz + 1, sizeof(char *)); 275 | if (!envp) 276 | goto err_envle; 277 | 278 | /* populating new environment */ 279 | envz = 0; 280 | avl_for_each_element_safe(&env, envle, avl, p) { 281 | envp[envz++] = envle->env; 282 | avl_delete(&env, &envle->avl); 283 | free(envle); 284 | } 285 | 286 | pc = calloc(1, sizeof(struct hotplug_process)); 287 | if (!pc) { 288 | env_free(envp); 289 | return UBUS_STATUS_UNKNOWN_ERROR; 290 | } 291 | pc->timeout.cb = hotplug_exec; 292 | pc->envp = envp; 293 | pc->cnt = 0; 294 | pc->ubus = obj; 295 | 296 | /* glob'ing for hotplug scripts */ 297 | if (asprintf(&globstr, "%s/%s/*", HOTPLUG_BASEDIR, subsys) == -1) { 298 | hotplug_free(pc); 299 | return UBUS_STATUS_UNKNOWN_ERROR; 300 | } 301 | 302 | if (glob(globstr, GLOB_DOOFFS, NULL, &pc->globbuf)) { 303 | free(globstr); 304 | hotplug_free(pc); 305 | return UBUS_STATUS_OK; 306 | } 307 | 308 | free(globstr); 309 | 310 | /* asynchronous call to hotplug_exec() */ 311 | uloop_timeout_set(&pc->timeout, 50); 312 | 313 | return UBUS_STATUS_OK; 314 | 315 | err_envle: 316 | avl_for_each_element_safe(&env, envle, avl, p) { 317 | if (envle->env) 318 | free(envle->env); 319 | 320 | avl_delete(&env, &envle->avl); 321 | free(envle); 322 | } 323 | 324 | return err; 325 | } 326 | 327 | static const struct ubus_method hotplug_methods[] = { 328 | UBUS_METHOD("call", hotplug_call, hotplug_policy), 329 | }; 330 | 331 | static struct ubus_object_type hotplug_object_type = 332 | UBUS_OBJECT_TYPE("hotplug", hotplug_methods); 333 | 334 | static void add_subsystem(const char *name) 335 | { 336 | struct hotplug_subsys *nh; 337 | char *name_buf; 338 | 339 | nh = avl_find_element(&subsystems, name, nh, node); 340 | if (nh) 341 | return; 342 | 343 | nh = calloc_a(sizeof(struct hotplug_subsys), 344 | &name_buf, sizeof(HOTPLUG_OBJECT_PREFIX) + 1 + strlen(name)); 345 | if (!nh) 346 | return; 347 | 348 | nh->ubus.name = name_buf; 349 | name_buf += sprintf(name_buf, "%s", HOTPLUG_OBJECT_PREFIX); 350 | nh->node.key = strcpy(name_buf, name); 351 | 352 | nh->ubus.type = &hotplug_object_type; 353 | nh->ubus.methods = hotplug_object_type.methods; 354 | nh->ubus.n_methods = hotplug_object_type.n_methods; 355 | ubus_add_object(ctx, &nh->ubus); 356 | avl_insert(&subsystems, &nh->node); 357 | } 358 | 359 | static void free_subsystem(struct hotplug_subsys *h) 360 | { 361 | ubus_remove_object(ctx, &h->ubus); 362 | free(h); 363 | } 364 | 365 | static void remove_all_subsystems(void) 366 | { 367 | struct hotplug_subsys *n, *h; 368 | 369 | /* find match subsystem object by name or any if not given */ 370 | avl_remove_all_elements(&subsystems, h, node, n) 371 | free_subsystem(h); 372 | } 373 | 374 | static void remove_subsystem(char *name) 375 | { 376 | struct hotplug_subsys *h; 377 | 378 | if (!strcmp(name, "button")) 379 | return; 380 | 381 | h = avl_find_element(&subsystems, name, h, node); 382 | if (!h) 383 | return; 384 | 385 | avl_delete(&subsystems, &h->node); 386 | free_subsystem(h); 387 | } 388 | 389 | static int init_subsystems(void) 390 | { 391 | DIR *dir; 392 | struct dirent *dirent; 393 | 394 | add_subsystem("button"); 395 | 396 | dir = opendir(HOTPLUG_BASEDIR); 397 | if (dir == NULL) 398 | return ENOENT; 399 | 400 | while ((dirent = readdir(dir))) { 401 | /* skip everything but directories */ 402 | if (dirent->d_type != DT_DIR) 403 | continue; 404 | 405 | /* skip '.' and '..' as well as hidden files */ 406 | if (dirent->d_name[0] == '.') 407 | continue; 408 | 409 | add_subsystem(dirent->d_name); 410 | } 411 | closedir(dir); 412 | 413 | return 0; 414 | } 415 | 416 | static void inotify_read_handler(struct uloop_fd *u, unsigned int events) 417 | { 418 | int rc; 419 | char *p; 420 | struct inotify_event *in; 421 | 422 | /* read inotify events */ 423 | while ((rc = read(u->fd, inotify_buffer, INOTIFY_SZ)) == -1 && errno == EINTR); 424 | 425 | if (rc <= 0) 426 | return; 427 | 428 | /* process events from buffer */ 429 | for (p = inotify_buffer; 430 | rc - (p - inotify_buffer) >= (int)sizeof(struct inotify_event); 431 | p += sizeof(struct inotify_event) + in->len) { 432 | in = (struct inotify_event*)p; 433 | 434 | /* skip everything but directories */ 435 | if (!(in->mask & IN_ISDIR)) 436 | continue; 437 | 438 | if (in->len < 1) 439 | continue; 440 | 441 | /* skip hidden files */ 442 | if (in->name[0] == '.') 443 | continue; 444 | 445 | /* add/remove subsystem objects */ 446 | if (in->mask & (IN_CREATE | IN_MOVED_TO)) 447 | add_subsystem(in->name); 448 | else if (in->mask & (IN_DELETE | IN_MOVED_FROM)) 449 | remove_subsystem(in->name); 450 | } 451 | } 452 | 453 | void hotplug_ubus_event(struct blob_attr *data) 454 | { 455 | static const struct blobmsg_policy policy = 456 | { "SUBSYSTEM", BLOBMSG_TYPE_STRING }; 457 | struct hotplug_subsys *h; 458 | struct blob_attr *attr; 459 | const char *subsys; 460 | 461 | blobmsg_parse_attr(&policy, 1, &attr, data); 462 | if (!attr) 463 | return; 464 | 465 | subsys = blobmsg_get_string(attr); 466 | h = avl_find_element(&subsystems, subsys, h, node); 467 | if (!h) 468 | return; 469 | 470 | ubus_notify(ctx, &h->ubus, "event", data, -1); 471 | } 472 | 473 | void ubus_init_hotplug(struct ubus_context *newctx) 474 | { 475 | ctx = newctx; 476 | remove_all_subsystems(); 477 | if (init_subsystems()) { 478 | printf("failed to initialize hotplug subsystems from %s\n", HOTPLUG_BASEDIR); 479 | return; 480 | } 481 | fd_inotify_read.fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); 482 | fd_inotify_read.cb = inotify_read_handler; 483 | if (fd_inotify_read.fd == -1) { 484 | printf("failed to initialize inotify handler for %s\n", HOTPLUG_BASEDIR); 485 | return; 486 | } 487 | 488 | inotify_buffer = calloc(1, INOTIFY_SZ); 489 | if (!inotify_buffer) 490 | return; 491 | 492 | if (inotify_add_watch(fd_inotify_read.fd, HOTPLUG_BASEDIR, 493 | IN_CREATE | IN_MOVED_TO | IN_DELETE | IN_MOVED_FROM | IN_ONLYDIR) == -1) 494 | return; 495 | 496 | uloop_fd_add(&fd_inotify_read, ULOOP_READ); 497 | } 498 | -------------------------------------------------------------------------------- /trace/trace.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 John Crispin 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License version 2.1 6 | * as published by the Free Software Foundation 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | #define _GNU_SOURCE 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #ifndef PTRACE_EVENT_STOP 31 | /* PTRACE_EVENT_STOP is defined in linux/ptrace.h, but this header 32 | * collides with musl's sys/ptrace.h */ 33 | #define PTRACE_EVENT_STOP 128 34 | #endif 35 | 36 | #ifndef PTRACE_EVENT_SECCOMP 37 | /* undefined with uClibc-ng */ 38 | #define PTRACE_EVENT_SECCOMP 7 39 | #endif 40 | 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | #include "../syscall-names.h" 47 | 48 | #define _offsetof(a, b) __builtin_offsetof(a,b) 49 | #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) 50 | 51 | #if defined (__aarch64__) || defined(__loongarch_lp64) 52 | #include 53 | #elif defined(__amd64__) 54 | #define reg_syscall_nr _offsetof(struct user, regs.orig_rax) 55 | #elif defined(__arm__) 56 | #include /* for PTRACE_SET_SYSCALL */ 57 | #define reg_syscall_nr _offsetof(struct user, regs.uregs[7]) 58 | # if defined(__ARM_EABI__) 59 | # define reg_retval_nr _offsetof(struct user, regs.uregs[0]) 60 | # endif 61 | #elif defined(__i386__) 62 | #define reg_syscall_nr _offsetof(struct user, regs.orig_eax) 63 | #elif defined(__mips) 64 | # ifndef EF_REG2 65 | # define EF_REG2 8 66 | # endif 67 | #define reg_syscall_nr (EF_REG2 / 4) 68 | #elif defined(__PPC__) 69 | #define reg_syscall_nr _offsetof(struct user, regs.gpr[0]) 70 | #define reg_retval_nr _offsetof(struct user, regs.gpr[3]) 71 | #else 72 | #error tracing is not supported on this architecture 73 | #endif 74 | 75 | enum mode { 76 | UTRACE, 77 | SECCOMP_TRACE, 78 | } mode = UTRACE; 79 | 80 | struct tracee { 81 | struct uloop_process proc; 82 | int in_syscall; 83 | }; 84 | 85 | static struct tracee tracer; 86 | static int syscall_count[SYSCALL_COUNT]; 87 | static int violation_count; 88 | static struct blob_buf b; 89 | static int debug; 90 | char *json = NULL; 91 | int ptrace_restart; 92 | 93 | static void set_syscall(const char *name, int val) 94 | { 95 | int i; 96 | 97 | for (i = 0; i < SYSCALL_COUNT; i++) { 98 | int sc = syscall_index_to_number(i); 99 | if (syscall_name(sc) && !strcmp(syscall_name(sc), name)) { 100 | syscall_count[i] = val; 101 | return; 102 | } 103 | } 104 | } 105 | 106 | struct syscall { 107 | int syscall; 108 | int count; 109 | }; 110 | 111 | static int cmp_count(const void *a, const void *b) 112 | { 113 | return ((struct syscall*)b)->count - ((struct syscall*)a)->count; 114 | } 115 | 116 | static void print_syscalls(int policy, const char *json) 117 | { 118 | void *c, *d, *e; 119 | int i; 120 | char *tmp; 121 | 122 | if (mode == UTRACE) { 123 | set_syscall("rt_sigaction", 1); 124 | set_syscall("sigreturn", 1); 125 | set_syscall("rt_sigreturn", 1); 126 | set_syscall("exit_group", 1); 127 | set_syscall("exit", 1); 128 | } 129 | 130 | struct syscall sorted[SYSCALL_COUNT]; 131 | 132 | for (i = 0; i < SYSCALL_COUNT; i++) { 133 | sorted[i].syscall = syscall_index_to_number(i); 134 | sorted[i].count = syscall_count[i]; 135 | } 136 | 137 | qsort(sorted, SYSCALL_COUNT, sizeof(sorted[0]), cmp_count); 138 | 139 | blob_buf_init(&b, 0); 140 | blobmsg_add_string(&b, "defaultAction", "SCMP_ACT_KILL_PROCESS"); 141 | c = blobmsg_open_array(&b, "syscalls"); 142 | d = blobmsg_open_table(&b, ""); 143 | e = blobmsg_open_array(&b, "names"); 144 | 145 | for (i = 0; i < SYSCALL_COUNT; i++) { 146 | int sc = sorted[i].syscall; 147 | if (!sorted[i].count) 148 | break; 149 | if (syscall_name(sc)) { 150 | if (debug) 151 | printf("syscall %d (%s) was called %d times\n", 152 | sc, syscall_name(sc), sorted[i].count); 153 | blobmsg_add_string(&b, NULL, syscall_name(sc)); 154 | } else { 155 | ULOG_ERR("no name found for syscall(%d)\n", sc); 156 | } 157 | } 158 | blobmsg_close_array(&b, e); 159 | blobmsg_add_string(&b, "action", "SCMP_ACT_ALLOW"); 160 | blobmsg_close_table(&b, d); 161 | blobmsg_close_array(&b, c); 162 | if (json) { 163 | FILE *fp = fopen(json, "w"); 164 | if (fp) { 165 | tmp = blobmsg_format_json_indent(b.head, true, 0); 166 | if (!tmp) { 167 | fclose(fp); 168 | return; 169 | } 170 | 171 | fprintf(fp, "%s\n", tmp); 172 | free(tmp); 173 | fclose(fp); 174 | ULOG_INFO("saving syscall trace to %s\n", json); 175 | } else { 176 | ULOG_ERR("failed to open %s\n", json); 177 | } 178 | } else { 179 | tmp = blobmsg_format_json_indent(b.head, true, 0); 180 | if (!tmp) 181 | return; 182 | 183 | printf("%s\n", tmp); 184 | free(tmp); 185 | } 186 | } 187 | 188 | static void report_seccomp_vialation(pid_t pid, unsigned syscall) 189 | { 190 | char buf[200]; 191 | snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid); 192 | int f = open(buf, O_RDONLY); 193 | if (f < 0) 194 | return; 195 | 196 | int r = read(f, buf, sizeof(buf) - 1); 197 | buf[sizeof(buf) - 1] = '\0'; 198 | 199 | if (r >= 0) 200 | buf[r] = 0; 201 | else 202 | strcpy(buf, "unknown?"); 203 | close(f); 204 | 205 | if (violation_count < INT_MAX) 206 | violation_count++; 207 | int i = syscall_index(syscall); 208 | if (i >= 0) { 209 | syscall_count[i]++; 210 | ULOG_ERR("%s[%u] tried to call non-whitelisted syscall: %s (see %s)\n", 211 | buf, pid, syscall_name(syscall), json); 212 | } else { 213 | ULOG_ERR("%s[%u] tried to call non-whitelisted syscall: %d (see %s)\n", 214 | buf, pid, syscall, json); 215 | } 216 | } 217 | 218 | static void tracer_cb(struct uloop_process *c, int ret) 219 | { 220 | struct tracee *tracee = container_of(c, struct tracee, proc); 221 | int inject_signal = 0; 222 | 223 | /* We explicitely check for events in upper 16 bits, because 224 | * musl (as opposed to glibc) does not report 225 | * PTRACE_EVENT_STOP as WIFSTOPPED */ 226 | if (WIFSTOPPED(ret) || (ret >> 16)) { 227 | if (WSTOPSIG(ret) & 0x80) { 228 | if (!tracee->in_syscall) { 229 | #if defined(__aarch64__) || defined(__loongarch_lp64) 230 | int syscall = -1; 231 | struct ptrace_syscall_info ptsi = {.op=PTRACE_SYSCALL_INFO_ENTRY}; 232 | if (ptrace(PTRACE_GET_SYSCALL_INFO, c->pid, sizeof(ptsi), &ptsi) != -1) 233 | syscall = ptsi.entry.nr; 234 | #else 235 | int syscall = ptrace(PTRACE_PEEKUSER, c->pid, reg_syscall_nr); 236 | #endif 237 | int i = syscall_index(syscall); 238 | if (i >= 0) { 239 | syscall_count[i]++; 240 | if (debug) 241 | fprintf(stderr, "%s()\n", syscall_name(syscall)); 242 | } else if (debug) { 243 | fprintf(stderr, "syscal(%d)\n", syscall); 244 | } 245 | } 246 | tracee->in_syscall = !tracee->in_syscall; 247 | } else if ((ret >> 8) == (SIGTRAP | (PTRACE_EVENT_FORK << 8)) || 248 | (ret >> 8) == (SIGTRAP | (PTRACE_EVENT_VFORK << 8)) || 249 | (ret >> 8) == (SIGTRAP | (PTRACE_EVENT_CLONE << 8))) { 250 | struct tracee *child = calloc(1, sizeof(struct tracee)); 251 | 252 | unsigned long msg; 253 | ptrace(PTRACE_GETEVENTMSG, c->pid, 0, &msg); 254 | child->proc.pid = msg; 255 | child->proc.cb = tracer_cb; 256 | ptrace(ptrace_restart, child->proc.pid, 0, 0); 257 | uloop_process_add(&child->proc); 258 | if (debug) 259 | fprintf(stderr, "Tracing new child %d\n", child->proc.pid); 260 | } else if ((ret >> 16) == PTRACE_EVENT_STOP) { 261 | /* Nothing special to do here */ 262 | } else if ((ret >> 8) == (SIGTRAP | (PTRACE_EVENT_SECCOMP << 8))) { 263 | #if defined(__aarch64__) || defined(__loongarch_lp64) 264 | int syscall = -1; 265 | struct ptrace_syscall_info ptsi = {.op=PTRACE_SYSCALL_INFO_SECCOMP}; 266 | if (ptrace(PTRACE_GET_SYSCALL_INFO, c->pid, sizeof(ptsi), &ptsi) != -1) 267 | syscall = ptsi.entry.nr; 268 | #else 269 | int syscall = ptrace(PTRACE_PEEKUSER, c->pid, reg_syscall_nr); 270 | #if defined(__arm__) 271 | ptrace(PTRACE_SET_SYSCALL, c->pid, 0, -1); 272 | ptrace(PTRACE_POKEUSER, c->pid, reg_retval_nr, -ENOSYS); 273 | #else 274 | ptrace(PTRACE_POKEUSER, c->pid, reg_syscall_nr, -1); 275 | #endif 276 | #endif 277 | report_seccomp_vialation(c->pid, syscall); 278 | } else { 279 | inject_signal = WSTOPSIG(ret); 280 | if (debug) 281 | fprintf(stderr, "Injecting signal %d into pid %d\n", 282 | inject_signal, tracee->proc.pid); 283 | } 284 | } else if (WIFEXITED(ret) || (WIFSIGNALED(ret) && WTERMSIG(ret))) { 285 | if (tracee == &tracer) { 286 | uloop_end(); /* Main process exit */ 287 | } else { 288 | if (debug) 289 | fprintf(stderr, "Child %d exited\n", tracee->proc.pid); 290 | free(tracee); 291 | } 292 | return; 293 | } 294 | 295 | ptrace(ptrace_restart, c->pid, 0, inject_signal); 296 | uloop_process_add(c); 297 | } 298 | 299 | static void sigterm_handler(int signum) 300 | { 301 | /* When we receive SIGTERM, we forward it to the tracee. After 302 | * the tracee exits, trace_cb() will be called and make us 303 | * exit too. */ 304 | kill(tracer.proc.pid, SIGTERM); 305 | } 306 | 307 | 308 | int main(int argc, char **argv, char **envp) 309 | { 310 | int status, ch, policy = EPERM; 311 | pid_t child; 312 | 313 | /* When invoked via seccomp-trace symlink, work as seccomp 314 | * violation logger rather than as syscall tracer */ 315 | if (strstr(argv[0], "seccomp-trace")) 316 | mode = SECCOMP_TRACE; 317 | 318 | while ((ch = getopt(argc, argv, "f:p:")) != -1) { 319 | switch (ch) { 320 | case 'f': 321 | json = optarg; 322 | break; 323 | case 'p': 324 | policy = atoi(optarg); 325 | break; 326 | } 327 | } 328 | 329 | if (!json) 330 | json = getenv("SECCOMP_FILE"); 331 | 332 | argc -= optind; 333 | argv += optind; 334 | 335 | if (!argc) 336 | return -1; 337 | 338 | if (getenv("TRACE_DEBUG")) 339 | debug = 1; 340 | unsetenv("TRACE_DEBUG"); 341 | 342 | child = fork(); 343 | 344 | if (child == 0) { 345 | char **_argv = calloc(argc + 1, sizeof(char *)); 346 | char **_envp; 347 | char *preload = NULL; 348 | const char *old_preload = getenv("LD_PRELOAD"); 349 | int newenv = 0; 350 | int envc = 0; 351 | int ret; 352 | 353 | memcpy(_argv, argv, argc * sizeof(char *)); 354 | 355 | while (envp[envc++]) 356 | ; 357 | 358 | _envp = calloc(envc + 2, sizeof(char *)); 359 | switch (mode) { 360 | case UTRACE: 361 | preload = "/lib/libpreload-trace.so"; 362 | newenv = 1; 363 | break; 364 | case SECCOMP_TRACE: 365 | preload = "/lib/libpreload-seccomp.so"; 366 | newenv = 2; 367 | if (asprintf(&_envp[1], "SECCOMP_FILE=%s", json ? json : "") < 0) 368 | ULOG_ERR("failed to allocate SECCOMP_FILE env: %m\n"); 369 | 370 | kill(getpid(), SIGSTOP); 371 | break; 372 | } 373 | if (asprintf(&_envp[0], "LD_PRELOAD=%s%s%s", preload, 374 | old_preload ? ":" : "", 375 | old_preload ? old_preload : "") < 0) 376 | ULOG_ERR("failed to allocate LD_PRELOAD env: %m\n"); 377 | 378 | memcpy(&_envp[newenv], envp, envc * sizeof(char *)); 379 | 380 | ret = execve(_argv[0], _argv, _envp); 381 | ULOG_ERR("failed to exec %s: %m\n", _argv[0]); 382 | 383 | free(_argv); 384 | if (_envp[0]) 385 | free(_envp[0]); 386 | if (newenv == 2 && _envp[1]) 387 | free(_envp[1]); 388 | free(_envp); 389 | return ret; 390 | } 391 | 392 | if (child < 0) 393 | return -1; 394 | 395 | waitpid(child, &status, WUNTRACED); 396 | if (!WIFSTOPPED(status)) { 397 | ULOG_ERR("failed to start %s\n", *argv); 398 | return -1; 399 | } 400 | 401 | /* Initialize uloop to catch all ptrace stops from now on. */ 402 | uloop_init(); 403 | 404 | int ptrace_options = PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE; 405 | switch (mode) { 406 | case UTRACE: 407 | ptrace_options |= PTRACE_O_TRACESYSGOOD; 408 | ptrace_restart = PTRACE_SYSCALL; 409 | break; 410 | case SECCOMP_TRACE: 411 | ptrace_options |= PTRACE_O_TRACESECCOMP; 412 | ptrace_restart = PTRACE_CONT; 413 | break; 414 | } 415 | if (ptrace(PTRACE_SEIZE, child, 0, ptrace_options) == -1) { 416 | ULOG_ERR("PTRACE_SEIZE: %m\n"); 417 | return -1; 418 | } 419 | if (ptrace(ptrace_restart, child, 0, SIGCONT) == -1) { 420 | ULOG_ERR("ptrace_restart: %m\n"); 421 | return -1; 422 | } 423 | 424 | tracer.proc.pid = child; 425 | tracer.proc.cb = tracer_cb; 426 | uloop_process_add(&tracer.proc); 427 | signal(SIGTERM, sigterm_handler); /* Override uloop's SIGTERM handler */ 428 | uloop_run(); 429 | uloop_done(); 430 | 431 | 432 | switch (mode) { 433 | case UTRACE: 434 | if (!json) 435 | if (asprintf(&json, "/tmp/%s.%u.json", basename(*argv), child) < 0) 436 | ULOG_ERR("failed to allocate output path: %m\n"); 437 | break; 438 | case SECCOMP_TRACE: 439 | if (!violation_count) 440 | return 0; 441 | if (asprintf(&json, "/tmp/%s.%u.violations.json", basename(*argv), child) < 0) 442 | ULOG_ERR("failed to allocate violations output path: %m\n"); 443 | break; 444 | } 445 | print_syscalls(policy, json); 446 | return 0; 447 | } 448 | -------------------------------------------------------------------------------- /jail/seccomp-oci.c: -------------------------------------------------------------------------------- 1 | /* 2 | * parse and setup OCI seccomp filter 3 | * Copyright (c) 2020 Daniel Golle 4 | * seccomp example with syscall reporting 5 | * Copyright (c) 2012 The Chromium OS Authors 6 | * Authors: 7 | * Kees Cook 8 | * Will Drewry 9 | * 10 | * Use of this source code is governed by a BSD-style license that can be 11 | * found in the LICENSE file. 12 | * 13 | * BPF control flow 14 | * 15 | * (check_arch)---(check_syscall)---+----[...]---(return default_action) 16 | * | | | 17 | * KILL (check_argument)--+ 18 | * | 19 | * [...] 20 | * | 21 | * (return action) 22 | */ 23 | #define _GNU_SOURCE 1 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | #include 31 | #include 32 | 33 | #include "log.h" 34 | #include "seccomp-bpf.h" 35 | #include "seccomp-oci.h" 36 | #include "../syscall-names.h" 37 | #include "seccomp-syscalls-helpers.h" 38 | 39 | static uint32_t resolve_action(char *actname) 40 | { 41 | if (!strcmp(actname, "SCMP_ACT_KILL")) 42 | return SECCOMP_RET_KILL; 43 | else if (!strcmp(actname, "SCMP_ACT_KILL_PROCESS")) 44 | return SECCOMP_RET_KILLPROCESS; 45 | else if (!strcmp(actname, "SCMP_ACT_TRAP")) 46 | return SECCOMP_RET_TRAP; 47 | else if (!strcmp(actname, "SCMP_ACT_ERRNO")) 48 | return SECCOMP_RET_ERRNO; 49 | else if (!strcmp(actname, "SCMP_ACT_ERROR")) 50 | return SECCOMP_RET_ERRNO; 51 | else if (!strcmp(actname, "SCMP_ACT_TRACE")) 52 | return SECCOMP_RET_TRACE; 53 | else if (!strcmp(actname, "SCMP_ACT_ALLOW")) 54 | return SECCOMP_RET_ALLOW; 55 | else if (!strcmp(actname, "SCMP_ACT_LOG")) 56 | return SECCOMP_RET_LOGALLOW; 57 | else { 58 | ERROR("unknown seccomp action %s\n", actname); 59 | return SECCOMP_RET_KILL; 60 | } 61 | } 62 | 63 | static uint8_t resolve_op_ins(const char *op) 64 | { 65 | if (!strcmp(op, "SCMP_CMP_NE")) /* invert EQ */ 66 | return BPF_JEQ; 67 | else if (!strcmp(op, "SCMP_CMP_LT")) /* invert GE */ 68 | return BPF_JGE; 69 | else if (!strcmp(op, "SCMP_CMP_LE")) /* invert GT */ 70 | return BPF_JGT; 71 | else if (!strcmp(op, "SCMP_CMP_EQ")) 72 | return BPF_JEQ; 73 | else if (!strcmp(op, "SCMP_CMP_GE")) 74 | return BPF_JGE; 75 | else if (!strcmp(op, "SCMP_CMP_GT")) 76 | return BPF_JGT; 77 | else if (!strcmp(op, "SCMP_CMP_MASKED_EQ")) 78 | return BPF_JEQ; 79 | else { 80 | ERROR("unknown seccomp op %s\n", op); 81 | return 0; 82 | } 83 | } 84 | 85 | static bool resolve_op_is_masked(const char *op) 86 | { 87 | if (!strcmp(op, "SCMP_CMP_MASKED_EQ")) 88 | return true; 89 | 90 | return false; 91 | } 92 | 93 | static bool resolve_op_inv(const char *op) 94 | { 95 | if (!strcmp(op, "SCMP_CMP_NE") || 96 | !strcmp(op, "SCMP_CMP_LT") || 97 | !strcmp(op, "SCMP_CMP_LE")) 98 | return true; 99 | 100 | return false; 101 | } 102 | 103 | static uint32_t resolve_architecture(char *archname) 104 | { 105 | if (!archname) 106 | return 0; 107 | 108 | if (!strcmp(archname, "SCMP_ARCH_X86")) 109 | return AUDIT_ARCH_I386; 110 | else if (!strcmp(archname, "SCMP_ARCH_X86_64")) 111 | return AUDIT_ARCH_X86_64; 112 | else if (!strcmp(archname, "SCMP_ARCH_X32")) 113 | /* 114 | * return AUDIT_ARCH_X86_64; 115 | * 32-bit userland on 64-bit kernel is not supported yet 116 | */ 117 | return 0; 118 | else if (!strcmp(archname, "SCMP_ARCH_ARM")) 119 | return AUDIT_ARCH_ARM; 120 | else if (!strcmp(archname, "SCMP_ARCH_AARCH64")) 121 | return AUDIT_ARCH_AARCH64; 122 | else if (!strcmp(archname, "SCMP_ARCH_LOONGARCH64")) 123 | return AUDIT_ARCH_LOONGARCH64; 124 | else if (!strcmp(archname, "SCMP_ARCH_MIPS")) 125 | return AUDIT_ARCH_MIPS; 126 | else if (!strcmp(archname, "SCMP_ARCH_MIPS64")) 127 | return AUDIT_ARCH_MIPS64; 128 | else if (!strcmp(archname, "SCMP_ARCH_MIPS64N32")) 129 | return AUDIT_ARCH_MIPS64N32; 130 | else if (!strcmp(archname, "SCMP_ARCH_MIPSEL")) 131 | return AUDIT_ARCH_MIPSEL; 132 | else if (!strcmp(archname, "SCMP_ARCH_MIPSEL64")) 133 | return AUDIT_ARCH_MIPSEL64; 134 | else if (!strcmp(archname, "SCMP_ARCH_MIPSEL64N32")) 135 | return AUDIT_ARCH_MIPSEL64N32; 136 | else if (!strcmp(archname, "SCMP_ARCH_PPC")) 137 | return AUDIT_ARCH_PPC; 138 | else if (!strcmp(archname, "SCMP_ARCH_PPC64")) 139 | return AUDIT_ARCH_PPC64; 140 | else if (!strcmp(archname, "SCMP_ARCH_PPC64LE")) 141 | return AUDIT_ARCH_PPC64LE; 142 | else if (!strcmp(archname, "SCMP_ARCH_S390")) 143 | return AUDIT_ARCH_S390; 144 | else if (!strcmp(archname, "SCMP_ARCH_S390X")) 145 | return AUDIT_ARCH_S390X; 146 | else if (!strcmp(archname, "SCMP_ARCH_PARISC")) 147 | return AUDIT_ARCH_PARISC; 148 | else if (!strcmp(archname, "SCMP_ARCH_PARISC64")) 149 | return AUDIT_ARCH_PARISC64; 150 | else { 151 | ERROR("unknown seccomp architecture %s\n", archname); 152 | return 0; 153 | } 154 | } 155 | 156 | enum { 157 | OCI_LINUX_SECCOMP_DEFAULTACTION, 158 | OCI_LINUX_SECCOMP_ARCHITECTURES, 159 | OCI_LINUX_SECCOMP_FLAGS, 160 | OCI_LINUX_SECCOMP_SYSCALLS, 161 | __OCI_LINUX_SECCOMP_MAX, 162 | }; 163 | 164 | static const struct blobmsg_policy oci_linux_seccomp_policy[] = { 165 | [OCI_LINUX_SECCOMP_DEFAULTACTION] = { "defaultAction", BLOBMSG_TYPE_STRING }, 166 | [OCI_LINUX_SECCOMP_ARCHITECTURES] = { "architectures", BLOBMSG_TYPE_ARRAY }, 167 | [OCI_LINUX_SECCOMP_FLAGS] = { "flags", BLOBMSG_TYPE_ARRAY }, 168 | [OCI_LINUX_SECCOMP_SYSCALLS] = { "syscalls", BLOBMSG_TYPE_ARRAY }, 169 | }; 170 | 171 | enum { 172 | OCI_LINUX_SECCOMP_SYSCALLS_NAMES, 173 | OCI_LINUX_SECCOMP_SYSCALLS_ACTION, 174 | OCI_LINUX_SECCOMP_SYSCALLS_ERRNORET, 175 | OCI_LINUX_SECCOMP_SYSCALLS_ARGS, 176 | __OCI_LINUX_SECCOMP_SYSCALLS_MAX 177 | }; 178 | 179 | static const struct blobmsg_policy oci_linux_seccomp_syscalls_policy[] = { 180 | [OCI_LINUX_SECCOMP_SYSCALLS_NAMES] = { "names", BLOBMSG_TYPE_ARRAY }, 181 | [OCI_LINUX_SECCOMP_SYSCALLS_ERRNORET] = { "errnoRet", BLOBMSG_TYPE_INT32 }, 182 | [OCI_LINUX_SECCOMP_SYSCALLS_ARGS] = { "args", BLOBMSG_TYPE_ARRAY }, 183 | [OCI_LINUX_SECCOMP_SYSCALLS_ACTION] = { "action", BLOBMSG_TYPE_STRING }, 184 | }; 185 | 186 | enum { 187 | OCI_LINUX_SECCOMP_SYSCALLS_ARGS_INDEX, 188 | OCI_LINUX_SECCOMP_SYSCALLS_ARGS_VALUE, 189 | OCI_LINUX_SECCOMP_SYSCALLS_ARGS_VALUETWO, 190 | OCI_LINUX_SECCOMP_SYSCALLS_ARGS_OP, 191 | __OCI_LINUX_SECCOMP_SYSCALLS_ARGS_MAX 192 | }; 193 | 194 | static const struct blobmsg_policy oci_linux_seccomp_syscalls_args_policy[] = { 195 | [OCI_LINUX_SECCOMP_SYSCALLS_ARGS_INDEX] = { "index", BLOBMSG_TYPE_INT32 }, 196 | [OCI_LINUX_SECCOMP_SYSCALLS_ARGS_VALUE] = { "value", BLOBMSG_CAST_INT64 }, 197 | [OCI_LINUX_SECCOMP_SYSCALLS_ARGS_VALUETWO] = { "valueTwo", BLOBMSG_CAST_INT64 }, 198 | [OCI_LINUX_SECCOMP_SYSCALLS_ARGS_OP] = { "op", BLOBMSG_TYPE_STRING }, 199 | }; 200 | 201 | struct sock_fprog *parseOCIlinuxseccomp(struct blob_attr *msg) 202 | { 203 | struct blob_attr *tb[__OCI_LINUX_SECCOMP_MAX]; 204 | struct blob_attr *tbn[__OCI_LINUX_SECCOMP_SYSCALLS_MAX]; 205 | struct blob_attr *tba[__OCI_LINUX_SECCOMP_SYSCALLS_ARGS_MAX]; 206 | struct blob_attr *cur, *curn, *curarg; 207 | int rem, remn, remargs, sc; 208 | struct sock_filter *filter; 209 | struct sock_fprog *prog; 210 | int sz = 4, idx = 0; 211 | uint32_t default_policy = 0; 212 | uint32_t seccomp_arch; 213 | bool arch_matched; 214 | char *op_str; 215 | 216 | blobmsg_parse(oci_linux_seccomp_policy, __OCI_LINUX_SECCOMP_MAX, 217 | tb, blobmsg_data(msg), blobmsg_len(msg)); 218 | 219 | if (!tb[OCI_LINUX_SECCOMP_DEFAULTACTION]) { 220 | ERROR("seccomp: no default action set\n"); 221 | return NULL; 222 | } 223 | 224 | default_policy = resolve_action(blobmsg_get_string(tb[OCI_LINUX_SECCOMP_DEFAULTACTION])); 225 | 226 | /* verify architecture while ignoring the x86_64 anomaly for now */ 227 | if (tb[OCI_LINUX_SECCOMP_ARCHITECTURES]) { 228 | arch_matched = false; 229 | blobmsg_for_each_attr(cur, tb[OCI_LINUX_SECCOMP_ARCHITECTURES], rem) { 230 | seccomp_arch = resolve_architecture(blobmsg_get_string(cur)); 231 | if (ARCH_NR == seccomp_arch) { 232 | arch_matched = true; 233 | break; 234 | } 235 | } 236 | if (!arch_matched) { 237 | ERROR("seccomp architecture doesn't match system\n"); 238 | return NULL; 239 | } 240 | } 241 | 242 | blobmsg_for_each_attr(cur, tb[OCI_LINUX_SECCOMP_SYSCALLS], rem) { 243 | sz += 2; /* load and return */ 244 | 245 | blobmsg_parse(oci_linux_seccomp_syscalls_policy, 246 | __OCI_LINUX_SECCOMP_SYSCALLS_MAX, 247 | tbn, blobmsg_data(cur), blobmsg_len(cur)); 248 | blobmsg_for_each_attr(curn, tbn[OCI_LINUX_SECCOMP_SYSCALLS_NAMES], remn) { 249 | sc = find_syscall(blobmsg_get_string(curn)); 250 | if (sc == -1) { 251 | DEBUG("unknown syscall '%s'\n", blobmsg_get_string(curn)); 252 | /* TODO: support run.oci.seccomp_fail_unknown_syscall=1 annotation */ 253 | continue; 254 | } 255 | ++sz; 256 | } 257 | 258 | if (tbn[OCI_LINUX_SECCOMP_SYSCALLS_ARGS]) { 259 | blobmsg_for_each_attr(curarg, tbn[OCI_LINUX_SECCOMP_SYSCALLS_ARGS], remargs) { 260 | sz += 2; /* load and compare */ 261 | 262 | blobmsg_parse(oci_linux_seccomp_syscalls_args_policy, 263 | __OCI_LINUX_SECCOMP_SYSCALLS_ARGS_MAX, 264 | tba, blobmsg_data(curarg), blobmsg_len(curarg)); 265 | if (!tba[OCI_LINUX_SECCOMP_SYSCALLS_ARGS_INDEX] || 266 | !tba[OCI_LINUX_SECCOMP_SYSCALLS_ARGS_VALUE] || 267 | !tba[OCI_LINUX_SECCOMP_SYSCALLS_ARGS_OP]) 268 | return NULL; 269 | 270 | if (blobmsg_get_u32(tba[OCI_LINUX_SECCOMP_SYSCALLS_ARGS_INDEX]) > 5) 271 | return NULL; 272 | 273 | op_str = blobmsg_get_string(tba[OCI_LINUX_SECCOMP_SYSCALLS_ARGS_OP]); 274 | if (!resolve_op_ins(op_str)) 275 | return NULL; 276 | 277 | if (resolve_op_is_masked(op_str)) 278 | ++sz; /* SCMP_CMP_MASKED_EQ needs an extra BPF_AND op */ 279 | } 280 | } 281 | } 282 | 283 | if (sz < 6) 284 | return NULL; 285 | 286 | prog = malloc(sizeof(struct sock_fprog)); 287 | if (!prog) 288 | return NULL; 289 | 290 | filter = calloc(sz, sizeof(struct sock_filter)); 291 | if (!filter) { 292 | ERROR("failed to allocate memory for seccomp filter\n"); 293 | goto errout2; 294 | } 295 | 296 | /* validate arch */ 297 | set_filter(&filter[idx++], BPF_LD + BPF_W + BPF_ABS, 0, 0, arch_nr); 298 | set_filter(&filter[idx++], BPF_JMP + BPF_JEQ + BPF_K, 1, 0, ARCH_NR); 299 | set_filter(&filter[idx++], BPF_RET + BPF_K, 0, 0, SECCOMP_RET_KILL); 300 | 301 | blobmsg_for_each_attr(cur, tb[OCI_LINUX_SECCOMP_SYSCALLS], rem) { 302 | uint32_t action; 303 | uint32_t op_idx; 304 | uint8_t op_ins; 305 | bool op_inv, op_masked; 306 | uint64_t op_val, op_val2; 307 | int start_rule_idx; 308 | int next_rule_idx; 309 | 310 | blobmsg_parse(oci_linux_seccomp_syscalls_policy, 311 | __OCI_LINUX_SECCOMP_SYSCALLS_MAX, 312 | tbn, blobmsg_data(cur), blobmsg_len(cur)); 313 | action = resolve_action(blobmsg_get_string( 314 | tbn[OCI_LINUX_SECCOMP_SYSCALLS_ACTION])); 315 | if (tbn[OCI_LINUX_SECCOMP_SYSCALLS_ERRNORET]) { 316 | if (action != SECCOMP_RET_ERRNO) 317 | goto errout1; 318 | 319 | action = SECCOMP_RET_ERROR(blobmsg_get_u32( 320 | tbn[OCI_LINUX_SECCOMP_SYSCALLS_ERRNORET])); 321 | } else if (action == SECCOMP_RET_ERRNO) 322 | action = SECCOMP_RET_ERROR(EPERM); 323 | 324 | /* load syscall */ 325 | set_filter(&filter[idx++], BPF_LD + BPF_W + BPF_ABS, 0, 0, syscall_nr); 326 | 327 | /* get number of syscall names */ 328 | next_rule_idx = idx; 329 | blobmsg_for_each_attr(curn, tbn[OCI_LINUX_SECCOMP_SYSCALLS_NAMES], remn) { 330 | if (find_syscall(blobmsg_get_string(curn)) == -1) 331 | continue; 332 | 333 | ++next_rule_idx; 334 | } 335 | start_rule_idx = next_rule_idx; 336 | 337 | /* calculate length of argument filter rules */ 338 | blobmsg_for_each_attr(curn, tbn[OCI_LINUX_SECCOMP_SYSCALLS_ARGS], remn) { 339 | blobmsg_parse(oci_linux_seccomp_syscalls_args_policy, 340 | __OCI_LINUX_SECCOMP_SYSCALLS_ARGS_MAX, 341 | tba, blobmsg_data(curn), blobmsg_len(curn)); 342 | next_rule_idx += 2; 343 | op_str = blobmsg_get_string(tba[OCI_LINUX_SECCOMP_SYSCALLS_ARGS_OP]); 344 | if (resolve_op_is_masked(op_str)) 345 | ++next_rule_idx; 346 | } 347 | 348 | ++next_rule_idx; /* account for return action */ 349 | 350 | blobmsg_for_each_attr(curn, tbn[OCI_LINUX_SECCOMP_SYSCALLS_NAMES], remn) { 351 | sc = find_syscall(blobmsg_get_string(curn)); 352 | if (sc == -1) 353 | continue; 354 | /* 355 | * check syscall, skip other syscall checks if match is found. 356 | * if no match is found, jump to next section 357 | */ 358 | set_filter(&filter[idx], BPF_JMP + BPF_JEQ + BPF_K, 359 | start_rule_idx - (idx + 1), 360 | ((idx + 1) == start_rule_idx)?(next_rule_idx - (idx + 1)):0, 361 | sc); 362 | ++idx; 363 | } 364 | 365 | assert(idx = start_rule_idx); 366 | 367 | /* generate argument filter rules */ 368 | blobmsg_for_each_attr(curn, tbn[OCI_LINUX_SECCOMP_SYSCALLS_ARGS], remn) { 369 | blobmsg_parse(oci_linux_seccomp_syscalls_args_policy, 370 | __OCI_LINUX_SECCOMP_SYSCALLS_ARGS_MAX, 371 | tba, blobmsg_data(curn), blobmsg_len(curn)); 372 | 373 | op_str = blobmsg_get_string(tba[OCI_LINUX_SECCOMP_SYSCALLS_ARGS_OP]); 374 | op_ins = resolve_op_ins(op_str); 375 | op_inv = resolve_op_inv(op_str); 376 | op_masked = resolve_op_is_masked(op_str); 377 | op_idx = blobmsg_get_u32(tba[OCI_LINUX_SECCOMP_SYSCALLS_ARGS_INDEX]); 378 | op_val = blobmsg_cast_u64(tba[OCI_LINUX_SECCOMP_SYSCALLS_ARGS_VALUE]); 379 | if (tba[OCI_LINUX_SECCOMP_SYSCALLS_ARGS_VALUETWO]) 380 | op_val2 = blobmsg_cast_u64(tba[OCI_LINUX_SECCOMP_SYSCALLS_ARGS_VALUETWO]); 381 | else 382 | op_val2 = 0; 383 | 384 | /* load argument */ 385 | set_filter(&filter[idx++], BPF_LD + BPF_W + BPF_ABS, 0, 0, syscall_arg(op_idx)); 386 | 387 | /* apply mask */ 388 | if (op_masked) 389 | set_filter(&filter[idx++], BPF_ALU + BPF_K + BPF_AND, 0, 0, op_val); 390 | 391 | set_filter(&filter[idx], BPF_JMP + op_ins + BPF_K, 392 | op_inv?(next_rule_idx - (idx + 1)):0, 393 | op_inv?0:(next_rule_idx - (idx + 1)), 394 | op_masked?op_val2:op_val); 395 | ++idx; 396 | } 397 | 398 | /* if we have reached until here, all conditions were met and we can return */ 399 | set_filter(&filter[idx++], BPF_RET + BPF_K, 0, 0, action); 400 | 401 | assert(idx == next_rule_idx); 402 | } 403 | 404 | set_filter(&filter[idx++], BPF_RET + BPF_K, 0, 0, default_policy); 405 | 406 | assert(idx == sz); 407 | 408 | prog->len = (unsigned short) idx; 409 | prog->filter = filter; 410 | 411 | DEBUG("generated seccomp-bpf program:\n"); 412 | if (debug) { 413 | fprintf(stderr, " [idx]\tcode\t jt\t jf\tk\n"); 414 | for (idx=0; idxfilter); 449 | free(prog); 450 | return errno; 451 | } 452 | -------------------------------------------------------------------------------- /jail/cgroups-bpf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Daniel Golle 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License version 2.1 6 | * as published by the Free Software Foundation 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * somehow emulate devices.allow/devices.deny using eBPF 14 | * 15 | * OCI run-time spec defines the syntax for allowing/denying access 16 | * to devices according to the definition of cgroup-v1 in the Kernel 17 | * as described in Documentation/admin-guide/cgroup-v1. 18 | */ 19 | 20 | #include 21 | #include 22 | #ifdef __GLIBC__ 23 | #include 24 | #else 25 | #include 26 | #endif 27 | #include 28 | 29 | #include 30 | #include 31 | #include 32 | 33 | #include "cgroups.h" 34 | #include "cgroups-bpf.h" 35 | #include "log.h" 36 | 37 | static struct bpf_insn *program = NULL; 38 | static int bpf_total_insn = 0; 39 | static const char *license = "GPL"; 40 | 41 | static int 42 | syscall_bpf (int cmd, union bpf_attr *attr, unsigned int size) 43 | { 44 | return (int) syscall (__NR_bpf, cmd, attr, size); 45 | } 46 | 47 | /* from crun/src/libcrun/ebpf.c */ 48 | #define BPF_ALU32_IMM(OP, DST, IMM) \ 49 | ((struct bpf_insn){ .code = BPF_ALU | BPF_OP (OP) | BPF_K, .dst_reg = DST, .src_reg = 0, .off = 0, .imm = IMM }) 50 | 51 | #define BPF_LDX_MEM(SIZE, DST, SRC, OFF) \ 52 | ((struct bpf_insn){ \ 53 | .code = BPF_LDX | BPF_SIZE (SIZE) | BPF_MEM, .dst_reg = DST, .src_reg = SRC, .off = OFF, .imm = 0 }) 54 | 55 | #define BPF_MOV64_REG(DST, SRC) \ 56 | ((struct bpf_insn){ .code = BPF_ALU64 | BPF_MOV | BPF_X, .dst_reg = DST, .src_reg = SRC, .off = 0, .imm = 0 }) 57 | 58 | #define BPF_JMP_A(OFF) \ 59 | ((struct bpf_insn){ .code = BPF_JMP | BPF_JA, .dst_reg = 0, .src_reg = 0, .off = OFF, .imm = 0 }) 60 | 61 | #define BPF_JMP_IMM(OP, DST, IMM, OFF) \ 62 | ((struct bpf_insn){ .code = BPF_JMP | BPF_OP (OP) | BPF_K, .dst_reg = DST, .src_reg = 0, .off = OFF, .imm = IMM }) 63 | 64 | #define BPF_JMP_REG(OP, DST, SRC, OFF) \ 65 | ((struct bpf_insn){ .code = BPF_JMP | BPF_OP (OP) | BPF_X, .dst_reg = DST, .src_reg = SRC, .off = OFF, .imm = 0 }) 66 | 67 | #define BPF_MOV64_IMM(DST, IMM) \ 68 | ((struct bpf_insn){ .code = BPF_ALU64 | BPF_MOV | BPF_K, .dst_reg = DST, .src_reg = 0, .off = 0, .imm = IMM }) 69 | 70 | #define BPF_MOV32_REG(DST, SRC) \ 71 | ((struct bpf_insn){ .code = BPF_ALU | BPF_MOV | BPF_X, .dst_reg = DST, .src_reg = SRC, .off = 0, .imm = 0 }) 72 | 73 | #define BPF_EXIT_INSN() \ 74 | ((struct bpf_insn){ .code = BPF_JMP | BPF_EXIT, .dst_reg = 0, .src_reg = 0, .off = 0, .imm = 0 }) 75 | 76 | /* taken from systemd. */ 77 | static const struct bpf_insn pre_insn[] = { 78 | /* type -> R2. */ 79 | BPF_LDX_MEM (BPF_W, BPF_REG_2, BPF_REG_1, 0), 80 | BPF_ALU32_IMM (BPF_AND, BPF_REG_2, 0xFFFF), 81 | /* access -> R3. */ 82 | BPF_LDX_MEM (BPF_W, BPF_REG_3, BPF_REG_1, 0), 83 | BPF_ALU32_IMM (BPF_RSH, BPF_REG_3, 16), 84 | /* major -> R4. */ 85 | BPF_LDX_MEM (BPF_W, BPF_REG_4, BPF_REG_1, 4), 86 | /* minor -> R5. */ 87 | BPF_LDX_MEM (BPF_W, BPF_REG_5, BPF_REG_1, 8), 88 | }; 89 | 90 | enum { 91 | OCI_LINUX_CGROUPS_DEVICES_ALLOW, 92 | OCI_LINUX_CGROUPS_DEVICES_TYPE, 93 | OCI_LINUX_CGROUPS_DEVICES_MAJOR, 94 | OCI_LINUX_CGROUPS_DEVICES_MINOR, 95 | OCI_LINUX_CGROUPS_DEVICES_ACCESS, 96 | __OCI_LINUX_CGROUPS_DEVICES_MAX, 97 | }; 98 | 99 | static const struct blobmsg_policy oci_linux_cgroups_devices_policy[] = { 100 | [OCI_LINUX_CGROUPS_DEVICES_ALLOW] = { "allow", BLOBMSG_TYPE_BOOL }, 101 | [OCI_LINUX_CGROUPS_DEVICES_TYPE] = { "type", BLOBMSG_TYPE_STRING }, 102 | [OCI_LINUX_CGROUPS_DEVICES_MAJOR] = { "major", BLOBMSG_CAST_INT64 }, 103 | [OCI_LINUX_CGROUPS_DEVICES_MINOR] = { "minor", BLOBMSG_CAST_INT64 }, 104 | [OCI_LINUX_CGROUPS_DEVICES_ACCESS] = { "access", BLOBMSG_TYPE_STRING }, 105 | }; 106 | 107 | /* 108 | * cgroup-v1 devices got a (default) behaviour and a list of exceptions. 109 | * define datatypes similar to the legacy kernel code. 110 | */ 111 | #define DEVCG_DEV_ALL (BPF_DEVCG_DEV_BLOCK | BPF_DEVCG_DEV_CHAR) 112 | #define DEVCG_ACC_ALL (BPF_DEVCG_ACC_READ | BPF_DEVCG_ACC_WRITE | BPF_DEVCG_ACC_MKNOD) 113 | 114 | enum devcg_behavior { 115 | DEVCG_DEFAULT_NONE, 116 | DEVCG_DEFAULT_ALLOW, 117 | DEVCG_DEFAULT_DENY, 118 | }; 119 | 120 | struct dev_exception_item { 121 | uint32_t major, minor; 122 | short type; 123 | short access; 124 | struct list_head list; 125 | bool allow; 126 | }; 127 | 128 | /* 129 | * add a bunch of default rules 130 | */ 131 | static int add_default_exceptions(struct list_head *exceptions) 132 | { 133 | int i, ret = 0; 134 | struct dev_exception_item *cur; 135 | /* from crun/src/libcrun/cgroup.c */ 136 | const struct dev_exception_item defrules[] = { 137 | /* always allow mknod */ 138 | { .allow = true, .type = BPF_DEVCG_DEV_CHAR, .major = ~0, .minor = ~0, .access = BPF_DEVCG_ACC_MKNOD }, 139 | { .allow = true, .type = BPF_DEVCG_DEV_BLOCK, .major = ~0, .minor = ~0, .access = BPF_DEVCG_ACC_MKNOD }, 140 | /* /dev/null */ 141 | { .allow = true, .type = BPF_DEVCG_DEV_CHAR, .major = 1, .minor = 3, .access = DEVCG_ACC_ALL }, 142 | /* /dev/random */ 143 | { .allow = true, .type = BPF_DEVCG_DEV_CHAR, .major = 1, .minor = 8, .access = DEVCG_ACC_ALL }, 144 | /* /dev/full */ 145 | { .allow = true, .type = BPF_DEVCG_DEV_CHAR, .major = 1, .minor = 7, .access = DEVCG_ACC_ALL }, 146 | /* /dev/tty */ 147 | { .allow = true, .type = BPF_DEVCG_DEV_CHAR, .major = 5, .minor = 0, .access = DEVCG_ACC_ALL }, 148 | /* /dev/zero */ 149 | { .allow = true, .type = BPF_DEVCG_DEV_CHAR, .major = 1, .minor = 5, .access = DEVCG_ACC_ALL }, 150 | /* /dev/urandom */ 151 | { .allow = true, .type = BPF_DEVCG_DEV_CHAR, .major = 1, .minor = 9, .access = DEVCG_ACC_ALL }, 152 | /* /dev/console */ 153 | { .allow = true, .type = BPF_DEVCG_DEV_CHAR, .major = 5, .minor = 1, .access = DEVCG_ACC_ALL }, 154 | /* /dev/pts/[0-255] */ 155 | { .allow = true, .type = BPF_DEVCG_DEV_CHAR, .major = 136, .minor = ~0, .access = DEVCG_ACC_ALL }, 156 | /* /dev/ptmx */ 157 | { .allow = true, .type = BPF_DEVCG_DEV_CHAR, .major = 5, .minor = 2, .access = DEVCG_ACC_ALL }, 158 | /* /dev/net/tun */ 159 | { .allow = true, .type = BPF_DEVCG_DEV_CHAR, .major = 10, .minor = 200, .access = DEVCG_ACC_ALL }, 160 | }; 161 | 162 | for (i = 0; i < (sizeof(defrules) / sizeof(struct dev_exception_item)); ++i) { 163 | cur = malloc(sizeof(struct dev_exception_item)); 164 | if (!cur) { 165 | ret = ENOMEM; 166 | break; 167 | } 168 | /* add defaults to list in reverse order (last item will be first in list) */ 169 | memcpy(cur, &defrules[i], sizeof(struct dev_exception_item)); 170 | list_add(&cur->list, exceptions); 171 | } 172 | 173 | return ret; 174 | } 175 | 176 | /* 177 | * free all exceptions in the list 178 | */ 179 | static void flush_exceptions(struct list_head *freelist) 180 | { 181 | struct dev_exception_item *dl, *dln; 182 | 183 | if (!list_empty(freelist)) 184 | list_for_each_entry_safe(dl, dln, freelist, list) { 185 | list_del(&dl->list); 186 | free(dl); 187 | } 188 | } 189 | 190 | /* 191 | * parse OCI cgroups devices and translate into cgroups-v2 eBPF program 192 | */ 193 | int parseOCIlinuxcgroups_devices(struct blob_attr *msg) 194 | { 195 | struct blob_attr *tb[__OCI_LINUX_CGROUPS_DEVICES_MAX]; 196 | struct blob_attr *cur; 197 | int rem, ret = 0; 198 | int bpf_type, bpf_access; 199 | unsigned char acidx; 200 | bool allow = false, 201 | has_access = false, 202 | has_type = false, 203 | has_major = false, 204 | has_minor = false; 205 | int total_ins = 0, 206 | cur_ins = 0, 207 | pre_insn_len = sizeof(pre_insn) / sizeof(struct bpf_insn), 208 | next_ins; 209 | char *access, *devtype; 210 | uint32_t devmajor, devminor; 211 | struct dev_exception_item *dl; 212 | struct list_head exceptions; 213 | enum devcg_behavior behavior = DEVCG_DEFAULT_ALLOW; 214 | INIT_LIST_HEAD(&exceptions); 215 | 216 | /* parse according to OCI spec */ 217 | blobmsg_for_each_attr(cur, msg, rem) { 218 | blobmsg_parse(oci_linux_cgroups_devices_policy, __OCI_LINUX_CGROUPS_DEVICES_MAX, 219 | tb, blobmsg_data(cur), blobmsg_len(cur)); 220 | 221 | if (!tb[OCI_LINUX_CGROUPS_DEVICES_ALLOW]) { 222 | ret = EINVAL; 223 | goto out; 224 | } 225 | 226 | allow = blobmsg_get_bool(tb[OCI_LINUX_CGROUPS_DEVICES_ALLOW]); 227 | 228 | bpf_access = 0; 229 | if (tb[OCI_LINUX_CGROUPS_DEVICES_ACCESS]) { 230 | access = blobmsg_get_string(tb[OCI_LINUX_CGROUPS_DEVICES_ACCESS]); 231 | if ((strlen(access) > 3) || (strlen(access) == 0)) { 232 | ret = EINVAL; 233 | goto out; 234 | } 235 | 236 | for (acidx = 0; acidx < strlen(access); ++acidx) { 237 | switch (access[acidx]) { 238 | case 'r': 239 | bpf_access |= BPF_DEVCG_ACC_READ; 240 | break; 241 | case 'w': 242 | bpf_access |= BPF_DEVCG_ACC_WRITE; 243 | break; 244 | case 'm': 245 | bpf_access |= BPF_DEVCG_ACC_MKNOD; 246 | break; 247 | default: 248 | ret = EINVAL; 249 | goto out; 250 | } 251 | } 252 | } 253 | 254 | if (!bpf_access) 255 | bpf_access = DEVCG_ACC_ALL; 256 | 257 | bpf_type = 0; 258 | if (tb[OCI_LINUX_CGROUPS_DEVICES_TYPE]) { 259 | devtype = blobmsg_get_string(tb[OCI_LINUX_CGROUPS_DEVICES_TYPE]); 260 | 261 | switch (devtype[0]) { 262 | case 'c': 263 | bpf_type = BPF_DEVCG_DEV_CHAR; 264 | break; 265 | case 'b': 266 | bpf_type = BPF_DEVCG_DEV_BLOCK; 267 | break; 268 | case 'a': 269 | bpf_type = DEVCG_DEV_ALL; 270 | break; 271 | default: 272 | ret = EINVAL; 273 | goto out; 274 | } 275 | } 276 | 277 | if (!bpf_type) 278 | bpf_type = DEVCG_DEV_ALL; 279 | 280 | if (tb[OCI_LINUX_CGROUPS_DEVICES_MAJOR]) 281 | devmajor = blobmsg_cast_u64(tb[OCI_LINUX_CGROUPS_DEVICES_MAJOR]); 282 | else 283 | devmajor = ~0; 284 | 285 | if (tb[OCI_LINUX_CGROUPS_DEVICES_MINOR]) 286 | devminor = blobmsg_cast_u64(tb[OCI_LINUX_CGROUPS_DEVICES_MINOR]); 287 | else 288 | devminor = ~0; 289 | 290 | if (bpf_type == DEVCG_DEV_ALL) { 291 | /* wildcard => change default policy and flush all existing rules */ 292 | flush_exceptions(&exceptions); 293 | behavior = allow?DEVCG_DEFAULT_ALLOW:DEVCG_DEFAULT_DENY; 294 | } else { 295 | /* allocate and populate record for exception */ 296 | dl = malloc(sizeof(struct dev_exception_item)); 297 | if (!dl) { 298 | ret = ENOSPC; 299 | break; 300 | } 301 | dl->allow = allow; 302 | dl->type = bpf_type; 303 | dl->access = bpf_access; 304 | dl->major = devmajor; 305 | dl->minor = devminor; 306 | 307 | /* push to exceptions list, last goes first */ 308 | list_add(&dl->list, &exceptions); 309 | } 310 | } 311 | if (ret) 312 | goto out; 313 | 314 | /* add default rules */ 315 | ret = add_default_exceptions(&exceptions); 316 | if (ret) 317 | goto out; 318 | 319 | /* calculate number of instructions to allocate */ 320 | list_for_each_entry(dl, &exceptions, list) { 321 | has_access = dl->access != DEVCG_ACC_ALL; 322 | has_type = dl->type != DEVCG_DEV_ALL; 323 | has_major = dl->major != ~0; 324 | has_minor = dl->minor != ~0; 325 | 326 | total_ins += (has_type ? 1 : 0) + (has_access ? 3 : 0) + (has_major ? 1 : 0) + (has_minor ? 1 : 0) + 2; 327 | } 328 | 329 | /* acccount for loader instructions */ 330 | total_ins += pre_insn_len; 331 | 332 | /* final accept/deny block */ 333 | total_ins += 2; 334 | 335 | /* allocate memory for eBPF program */ 336 | program = calloc(total_ins, sizeof(struct bpf_insn)); 337 | if (!program) { 338 | ret = ENOMEM; 339 | goto out; 340 | } 341 | 342 | /* copy program loader instructions */ 343 | memcpy(program, &pre_insn, sizeof(pre_insn)); 344 | cur_ins = pre_insn_len; 345 | 346 | /* generate eBPF program */ 347 | list_for_each_entry(dl, &exceptions, list) { 348 | has_access = dl->access != DEVCG_ACC_ALL; 349 | has_type = dl->type != DEVCG_DEV_ALL; 350 | has_major = dl->major != ~0; 351 | has_minor = dl->minor != ~0; 352 | 353 | next_ins = (has_type ? 1 : 0) + (has_access ? 3 : 0) + (has_major ? 1 : 0) + (has_minor ? 1 : 0) + 1; 354 | 355 | if (has_type) { 356 | program[cur_ins++] = BPF_JMP_IMM(BPF_JNE, BPF_REG_2, dl->type, next_ins); 357 | --next_ins; 358 | } 359 | 360 | if (has_access) { 361 | program[cur_ins++] = BPF_MOV32_REG(BPF_REG_1, BPF_REG_3); 362 | program[cur_ins++] = BPF_ALU32_IMM(BPF_AND, BPF_REG_1, dl->access); 363 | program[cur_ins++] = BPF_JMP_REG(BPF_JNE, BPF_REG_1, BPF_REG_3, next_ins - 2); 364 | next_ins -= 3; 365 | } 366 | 367 | if (has_major) { 368 | program[cur_ins++] = BPF_JMP_IMM(BPF_JNE, BPF_REG_4, dl->major, next_ins); 369 | --next_ins; 370 | } 371 | 372 | if (has_minor) { 373 | program[cur_ins++] = BPF_JMP_IMM(BPF_JNE, BPF_REG_5, dl->minor, next_ins); 374 | --next_ins; 375 | } 376 | 377 | program[cur_ins++] = BPF_MOV64_IMM(BPF_REG_0, dl->allow ? 1 : 0); 378 | program[cur_ins++] = BPF_EXIT_INSN(); 379 | } 380 | 381 | /* default behavior */ 382 | program[cur_ins++] = BPF_MOV64_IMM(BPF_REG_0, (behavior == DEVCG_DEFAULT_ALLOW)?1:0); 383 | program[cur_ins++] = BPF_EXIT_INSN(); 384 | 385 | if (debug) { 386 | fprintf(stderr, "cgroup devices:\na > devices.%s\n", 387 | (behavior == DEVCG_DEFAULT_ALLOW)?"allow":"deny"); 388 | 389 | list_for_each_entry(dl, &exceptions, list) 390 | fprintf(stderr, "%c %d:%d %s%s%s > devices.%s\n", 391 | (dl->type == DEVCG_DEV_ALL)?'a': 392 | (dl->type == BPF_DEVCG_DEV_CHAR)?'c':'b', 393 | (dl->major == ~0)?-1:dl->major, 394 | (dl->minor == ~0)?-1:dl->minor, 395 | (dl->access & BPF_DEVCG_ACC_READ)?"r":"", 396 | (dl->access & BPF_DEVCG_ACC_WRITE)?"w":"", 397 | (dl->access & BPF_DEVCG_ACC_MKNOD)?"m":"", 398 | (dl->allow)?"allow":"deny"); 399 | 400 | fprintf(stderr, "generated cgroup-devices eBPF program:\n"); 401 | fprintf(stderr, " [idx]\tcode\t dest\t src\t off\t imm\n"); 402 | for (cur_ins=0; cur_ins