Loading init/Android.mk +1 −0 Original line number Original line Diff line number Diff line Loading @@ -75,6 +75,7 @@ include $(BUILD_EXECUTABLE) include $(CLEAR_VARS) include $(CLEAR_VARS) LOCAL_MODULE := init_tests LOCAL_MODULE := init_tests LOCAL_SRC_FILES := \ LOCAL_SRC_FILES := \ init_parser_test.cpp \ util_test.cpp \ util_test.cpp \ LOCAL_SHARED_LIBRARIES += \ LOCAL_SHARED_LIBRARIES += \ Loading init/builtins.cpp +8 −3 Original line number Original line Diff line number Diff line Loading @@ -172,11 +172,16 @@ int do_enable(int nargs, char **args) return 0; return 0; } } int do_exec(int nargs, char **args) int do_exec(int nargs, char** args) { { service* svc = make_exec_oneshot_service(nargs, args); if (svc == NULL) { return -1; return -1; } } service_start(svc, NULL); return 0; } // TODO: remove execonce when exec is available. int do_execonce(int nargs, char **args) int do_execonce(int nargs, char **args) { { pid_t child; pid_t child; Loading init/init.cpp +95 −93 Original line number Original line Diff line number Diff line Loading @@ -14,40 +14,40 @@ * limitations under the License. * limitations under the License. */ */ #include <ctype.h> #include <dirent.h> #include <errno.h> #include <fcntl.h> #include <libgen.h> #include <signal.h> #include <stdarg.h> #include <stdio.h> #include <stdio.h> #include <stdlib.h> #include <stdlib.h> #include <string.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <ctype.h> #include <signal.h> #include <sys/wait.h> #include <sys/mount.h> #include <sys/mount.h> #include <sys/stat.h> #include <sys/poll.h> #include <sys/poll.h> #include <errno.h> #include <stdarg.h> #include <mtd/mtd-user.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/socket.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/un.h> #include <sys/un.h> #include <dirent.h> #include <sys/wait.h> #include <termios.h> #include <unistd.h> #include <memory> #include <memory> #include <mtd/mtd-user.h> #include <selinux/selinux.h> #include <selinux/selinux.h> #include <selinux/label.h> #include <selinux/label.h> #include <selinux/android.h> #include <selinux/android.h> #include <libgen.h> #include <cutils/list.h> #include <cutils/android_reboot.h> #include <cutils/android_reboot.h> #include <cutils/sockets.h> #include <cutils/iosched_policy.h> #include <cutils/fs.h> #include <cutils/fs.h> #include <cutils/iosched_policy.h> #include <cutils/list.h> #include <cutils/sockets.h> #include <private/android_filesystem_config.h> #include <private/android_filesystem_config.h> #include <termios.h> #include <utils/file.h> #include <utils/file.h> #include <utils/stringprintf.h> #include <utils/stringprintf.h> Loading Loading @@ -75,22 +75,35 @@ static char qemu[32]; static struct action *cur_action = NULL; static struct action *cur_action = NULL; static struct command *cur_command = NULL; static struct command *cur_command = NULL; void notify_service_state(const char *name, const char *state) { char pname[PROP_NAME_MAX]; int len = strlen(name); if ((len + 10) > PROP_NAME_MAX) return; snprintf(pname, sizeof(pname), "init.svc.%s", name); property_set(pname, state); } static int have_console; static int have_console; static char console_name[PROP_VALUE_MAX] = "/dev/console"; static char console_name[PROP_VALUE_MAX] = "/dev/console"; static time_t process_needs_restart; static time_t process_needs_restart; static const char *ENV[32]; static const char *ENV[32]; bool waiting_for_exec = false; void service::NotifyStateChange(const char* new_state) { if (!properties_inited()) { // If properties aren't available yet, we can't set them. return; } if ((flags & SVC_EXEC) != 0) { // 'exec' commands don't have properties tracking their state. return; } char prop_name[PROP_NAME_MAX]; if (snprintf(prop_name, sizeof(prop_name), "init.svc.%s", name) >= PROP_NAME_MAX) { // If the property name would be too long, we can't set it. ERROR("Property name \"init.svc.%s\" too long; not setting to %s\n", name, new_state); return; } property_set(prop_name, new_state); } /* add_environment - add "key=value" to the current environment */ /* add_environment - add "key=value" to the current environment */ int add_environment(const char *key, const char *val) int add_environment(const char *key, const char *val) { { Loading Loading @@ -163,35 +176,26 @@ static void publish_socket(const char *name, int fd) void service_start(struct service *svc, const char *dynamic_args) void service_start(struct service *svc, const char *dynamic_args) { { struct stat s; // Starting a service removes it from the disabled or reset state and pid_t pid; // immediately takes it out of the restarting state if it was in there. int needs_console; char *scon = NULL; int rc; /* starting a service removes it from the disabled or reset * state and immediately takes it out of the restarting * state if it was in there */ svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START)); svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START)); svc->time_started = 0; svc->time_started = 0; /* running processes require no additional work -- if // Running processes require no additional work --- if they're in the * they're in the process of exiting, we've ensured // process of exiting, we've ensured that they will immediately restart * that they will immediately restart on exit, unless // on exit, unless they are ONESHOT. * they are ONESHOT */ if (svc->flags & SVC_RUNNING) { if (svc->flags & SVC_RUNNING) { return; return; } } needs_console = (svc->flags & SVC_CONSOLE) ? 1 : 0; bool needs_console = (svc->flags & SVC_CONSOLE); if (needs_console && (!have_console)) { if (needs_console && !have_console) { ERROR("service '%s' requires console\n", svc->name); ERROR("service '%s' requires console\n", svc->name); svc->flags |= SVC_DISABLED; svc->flags |= SVC_DISABLED; return; return; } } struct stat s; if (stat(svc->args[0], &s) != 0) { if (stat(svc->args[0], &s) != 0) { ERROR("cannot find '%s', disabling '%s'\n", svc->args[0], svc->name); ERROR("cannot find '%s', disabling '%s'\n", svc->args[0], svc->name); svc->flags |= SVC_DISABLED; svc->flags |= SVC_DISABLED; Loading @@ -205,6 +209,7 @@ void service_start(struct service *svc, const char *dynamic_args) return; return; } } char* scon = NULL; if (is_selinux_enabled() > 0) { if (is_selinux_enabled() > 0) { if (svc->seclabel) { if (svc->seclabel) { scon = strdup(svc->seclabel); scon = strdup(svc->seclabel); Loading @@ -216,7 +221,7 @@ void service_start(struct service *svc, const char *dynamic_args) char *mycon = NULL, *fcon = NULL; char *mycon = NULL, *fcon = NULL; INFO("computing context for service '%s'\n", svc->args[0]); INFO("computing context for service '%s'\n", svc->args[0]); rc = getcon(&mycon); int rc = getcon(&mycon); if (rc < 0) { if (rc < 0) { ERROR("could not get context while starting '%s'\n", svc->name); ERROR("could not get context while starting '%s'\n", svc->name); return; return; Loading Loading @@ -244,8 +249,7 @@ void service_start(struct service *svc, const char *dynamic_args) NOTICE("starting '%s'\n", svc->name); NOTICE("starting '%s'\n", svc->name); pid = fork(); pid_t pid = fork(); if (pid == 0) { if (pid == 0) { struct socketinfo *si; struct socketinfo *si; struct svcenvinfo *ei; struct svcenvinfo *ei; Loading Loading @@ -301,7 +305,7 @@ void service_start(struct service *svc, const char *dynamic_args) setpgid(0, getpid()); setpgid(0, getpid()); /* as requested, set our gid, supplemental gids, and uid */ // As requested, set our gid, supplemental gids, and uid. if (svc->gid) { if (svc->gid) { if (setgid(svc->gid) != 0) { if (setgid(svc->gid) != 0) { ERROR("setgid failed: %s\n", strerror(errno)); ERROR("setgid failed: %s\n", strerror(errno)); Loading Loading @@ -364,8 +368,13 @@ void service_start(struct service *svc, const char *dynamic_args) svc->pid = pid; svc->pid = pid; svc->flags |= SVC_RUNNING; svc->flags |= SVC_RUNNING; if (properties_inited()) if ((svc->flags & SVC_EXEC) != 0) { notify_service_state(svc->name, "running"); INFO("SVC_EXEC pid %d (uid %d gid %d+%d context %s) started; waiting...\n", svc->pid, svc->uid, svc->gid, svc->nr_supp_gids, svc->seclabel); waiting_for_exec = true; } svc->NotifyStateChange("running"); } } /* The how field should be either SVC_DISABLED, SVC_RESET, or SVC_RESTART */ /* The how field should be either SVC_DISABLED, SVC_RESET, or SVC_RESTART */ Loading @@ -391,9 +400,9 @@ static void service_stop_or_reset(struct service *svc, int how) if (svc->pid) { if (svc->pid) { NOTICE("service '%s' is being killed\n", svc->name); NOTICE("service '%s' is being killed\n", svc->name); kill(-svc->pid, SIGKILL); kill(-svc->pid, SIGKILL); notify_service_state(svc->name, "stopping"); svc->NotifyStateChange("stopping"); } else { } else { notify_service_state(svc->name, "stopped"); svc->NotifyStateChange("stopped"); } } } } Loading Loading @@ -993,28 +1002,18 @@ static void selinux_initialize(void) security_setenforce(is_enforcing); security_setenforce(is_enforcing); } } int main(int argc, char **argv) int main(int argc, char** argv) { { size_t fd_count = 0; struct pollfd ufds[4]; int property_set_fd_init = 0; int signal_fd_init = 0; int keychord_fd_init = 0; bool is_charger = false; if (!strcmp(basename(argv[0]), "ueventd")) if (!strcmp(basename(argv[0]), "ueventd")) return ueventd_main(argc, argv); return ueventd_main(argc, argv); if (!strcmp(basename(argv[0]), "watchdogd")) if (!strcmp(basename(argv[0]), "watchdogd")) return watchdogd_main(argc, argv); return watchdogd_main(argc, argv); /* clear the umask */ // Clear the umask. umask(0); umask(0); /* Get the basic filesystem setup we need put // Get the basic filesystem setup we need put together in the initramdisk * together in the initramdisk on / and then we'll // on / and then we'll let the rc file figure out the rest. * let the rc file figure out the rest. */ mkdir("/dev", 0755); mkdir("/dev", 0755); mkdir("/proc", 0755); mkdir("/proc", 0755); mkdir("/sys", 0755); mkdir("/sys", 0755); Loading @@ -1026,15 +1025,13 @@ int main(int argc, char **argv) mount("proc", "/proc", "proc", 0, NULL); mount("proc", "/proc", "proc", 0, NULL); mount("sysfs", "/sys", "sysfs", 0, NULL); mount("sysfs", "/sys", "sysfs", 0, NULL); /* indicate that booting is in progress to background fw loaders, etc */ // Indicate that booting is in progress to background fw loaders, etc. close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000)); close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000)); /* We must have some place other than / to create the // We must have some place other than / to create the device nodes for * device nodes for kmsg and null, otherwise we won't // kmsg and null, otherwise we won't be able to remount / read-only * be able to remount / read-only later on. // later on. Now that tmpfs is mounted on /dev, we can actually talk * Now that tmpfs is mounted on /dev, we can actually // to the outside world. * talk to the outside world. */ open_devnull_stdio(); open_devnull_stdio(); klog_init(); klog_init(); property_init(); property_init(); Loading @@ -1050,25 +1047,22 @@ int main(int argc, char **argv) */ */ export_kernel_boot_props(); export_kernel_boot_props(); union selinux_callback cb; selinux_callback cb; cb.func_log = log_callback; cb.func_log = log_callback; selinux_set_callback(SELINUX_CB_LOG, cb); selinux_set_callback(SELINUX_CB_LOG, cb); cb.func_audit = audit_callback; cb.func_audit = audit_callback; selinux_set_callback(SELINUX_CB_AUDIT, cb); selinux_set_callback(SELINUX_CB_AUDIT, cb); selinux_initialize(); selinux_initialize(); /* These directories were necessarily created before initial policy load * and therefore need their security context restored to the proper value. // These directories were necessarily created before initial policy load * This must happen before /dev is populated by ueventd. // and therefore need their security context restored to the proper value. */ // This must happen before /dev is populated by ueventd. restorecon("/dev"); restorecon("/dev"); restorecon("/dev/socket"); restorecon("/dev/socket"); restorecon("/dev/__properties__"); restorecon("/dev/__properties__"); restorecon_recursive("/sys"); restorecon_recursive("/sys"); is_charger = !strcmp(bootmode, "charger"); INFO("property init\n"); INFO("property init\n"); property_load_boot_defaults(); property_load_boot_defaults(); Loading @@ -1081,50 +1075,58 @@ int main(int argc, char **argv) queue_builtin_action(keychord_init_action, "keychord_init"); queue_builtin_action(keychord_init_action, "keychord_init"); queue_builtin_action(console_init_action, "console_init"); queue_builtin_action(console_init_action, "console_init"); /* execute all the boot actions to get us started */ // Execute all the boot actions to get us started. action_for_each_trigger("init", action_add_queue_tail); action_for_each_trigger("init", action_add_queue_tail); /* Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random // Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random * wasn't ready immediately after wait_for_coldboot_done // wasn't ready immediately after wait_for_coldboot_done */ queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng"); queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng"); queue_builtin_action(property_service_init_action, "property_service_init"); queue_builtin_action(property_service_init_action, "property_service_init"); queue_builtin_action(signal_init_action, "signal_init"); queue_builtin_action(signal_init_action, "signal_init"); /* Don't mount filesystems or start core system services if in charger mode. */ // Don't mount filesystems or start core system services in charger mode. if (is_charger) { if (strcmp(bootmode, "charger") == 0) { action_for_each_trigger("charger", action_add_queue_tail); action_for_each_trigger("charger", action_add_queue_tail); } else { } else { action_for_each_trigger("late-init", action_add_queue_tail); action_for_each_trigger("late-init", action_add_queue_tail); } } /* run all property triggers based on current state of the properties */ // Run all property triggers based on current state of the properties. queue_builtin_action(queue_property_triggers_action, "queue_property_triggers"); queue_builtin_action(queue_property_triggers_action, "queue_property_triggers"); // TODO: why do we only initialize ufds after execute_one_command and restart_processes? size_t fd_count = 0; struct pollfd ufds[3]; bool property_set_fd_init = false; bool signal_fd_init = false; bool keychord_fd_init = false; for (;;) { for (;;) { if (!waiting_for_exec) { execute_one_command(); execute_one_command(); restart_processes(); restart_processes(); } if (!property_set_fd_init && get_property_set_fd() > 0) { if (!property_set_fd_init && get_property_set_fd() > 0) { ufds[fd_count].fd = get_property_set_fd(); ufds[fd_count].fd = get_property_set_fd(); ufds[fd_count].events = POLLIN; ufds[fd_count].events = POLLIN; ufds[fd_count].revents = 0; ufds[fd_count].revents = 0; fd_count++; fd_count++; property_set_fd_init = 1; property_set_fd_init = true; } } if (!signal_fd_init && get_signal_fd() > 0) { if (!signal_fd_init && get_signal_fd() > 0) { ufds[fd_count].fd = get_signal_fd(); ufds[fd_count].fd = get_signal_fd(); ufds[fd_count].events = POLLIN; ufds[fd_count].events = POLLIN; ufds[fd_count].revents = 0; ufds[fd_count].revents = 0; fd_count++; fd_count++; signal_fd_init = 1; signal_fd_init = true; } } if (!keychord_fd_init && get_keychord_fd() > 0) { if (!keychord_fd_init && get_keychord_fd() > 0) { ufds[fd_count].fd = get_keychord_fd(); ufds[fd_count].fd = get_keychord_fd(); ufds[fd_count].events = POLLIN; ufds[fd_count].events = POLLIN; ufds[fd_count].revents = 0; ufds[fd_count].revents = 0; fd_count++; fd_count++; keychord_fd_init = 1; keychord_fd_init = true; } } int timeout = -1; int timeout = -1; Loading init/init.h +28 −25 Original line number Original line Diff line number Diff line Loading @@ -17,13 +17,11 @@ #ifndef _INIT_INIT_H #ifndef _INIT_INIT_H #define _INIT_INIT_H #define _INIT_INIT_H #include <sys/types.h> #include <cutils/list.h> #include <cutils/list.h> #include <cutils/iosched_policy.h> #include <cutils/iosched_policy.h> #include <sys/stat.h> void handle_control_message(const char *msg, const char *arg); struct command struct command { { /* list of commands in an action */ /* list of commands in an action */ Loading Loading @@ -59,8 +57,6 @@ struct action { struct command *current; struct command *current; }; }; void build_triggers_string(char *name_str, int length, struct action *cur_action); struct socketinfo { struct socketinfo { struct socketinfo *next; struct socketinfo *next; const char *name; const char *name; Loading @@ -77,27 +73,29 @@ struct svcenvinfo { const char *value; const char *value; }; }; #define SVC_DISABLED 0x01 /* do not autostart with class */ #define SVC_DISABLED 0x001 // do not autostart with class #define SVC_ONESHOT 0x02 /* do not restart on exit */ #define SVC_ONESHOT 0x002 // do not restart on exit #define SVC_RUNNING 0x04 /* currently active */ #define SVC_RUNNING 0x004 // currently active #define SVC_RESTARTING 0x08 /* waiting to restart */ #define SVC_RESTARTING 0x008 // waiting to restart #define SVC_CONSOLE 0x10 /* requires console */ #define SVC_CONSOLE 0x010 // requires console #define SVC_CRITICAL 0x20 /* will reboot into recovery if keeps crashing */ #define SVC_CRITICAL 0x020 // will reboot into recovery if keeps crashing #define SVC_RESET 0x40 /* Use when stopping a process, but not disabling #define SVC_RESET 0x040 // Use when stopping a process, but not disabling so it can be restarted with its class. so it can be restarted with its class */ #define SVC_RC_DISABLED 0x080 // Remember if the disabled flag was set in the rc script. #define SVC_RC_DISABLED 0x80 /* Remember if the disabled flag was set in the rc script */ #define SVC_RESTART 0x100 // Use to safely restart (stop, wait, start) a service. #define SVC_RESTART 0x100 /* Use to safely restart (stop, wait, start) a service */ #define SVC_DISABLED_START 0x200 // A start was requested but it was disabled at the time. #define SVC_DISABLED_START 0x200 /* a start was requested but it was disabled at the time */ #define SVC_EXEC 0x400 // This synthetic service corresponds to an 'exec'. #define NR_SVC_SUPP_GIDS 12 /* twelve supplementary groups */ #define NR_SVC_SUPP_GIDS 12 /* twelve supplementary groups */ #define COMMAND_RETRY_TIMEOUT 5 #define COMMAND_RETRY_TIMEOUT 5 struct service { struct service { void NotifyStateChange(const char* new_state); /* list of all services */ /* list of all services */ struct listnode slist; struct listnode slist; const char *name; char *name; const char *classname; const char *classname; unsigned flags; unsigned flags; Loading @@ -111,7 +109,7 @@ struct service { gid_t supp_gids[NR_SVC_SUPP_GIDS]; gid_t supp_gids[NR_SVC_SUPP_GIDS]; size_t nr_supp_gids; size_t nr_supp_gids; char *seclabel; const char* seclabel; struct socketinfo *sockets; struct socketinfo *sockets; struct svcenvinfo *envvars; struct svcenvinfo *envvars; Loading @@ -131,7 +129,13 @@ struct service { char *args[1]; char *args[1]; }; /* ^-------'args' MUST be at the end of this struct! */ }; /* ^-------'args' MUST be at the end of this struct! */ void notify_service_state(const char *name, const char *state); extern bool waiting_for_exec; extern struct selabel_handle *sehandle; extern struct selabel_handle *sehandle_prop; void build_triggers_string(char *name_str, int length, struct action *cur_action); void handle_control_message(const char *msg, const char *arg); struct service *service_find_by_name(const char *name); struct service *service_find_by_name(const char *name); struct service *service_find_by_pid(pid_t pid); struct service *service_find_by_pid(pid_t pid); Loading @@ -147,9 +151,8 @@ void service_restart(struct service *svc); void service_start(struct service *svc, const char *dynamic_args); void service_start(struct service *svc, const char *dynamic_args); void property_changed(const char *name, const char *value); void property_changed(const char *name, const char *value); extern struct selabel_handle *sehandle; int selinux_reload_policy(void); extern struct selabel_handle *sehandle_prop; extern int selinux_reload_policy(void); void zap_stdio(void); void zap_stdio(void); #endif /* _INIT_INIT_H */ #endif /* _INIT_INIT_H */ init/init_parser.cpp +62 −2 Original line number Original line Diff line number Diff line Loading @@ -14,6 +14,7 @@ * limitations under the License. * limitations under the License. */ */ #include <errno.h> #include <stdio.h> #include <stdio.h> #include <stdlib.h> #include <stdlib.h> #include <unistd.h> #include <unistd.h> Loading Loading @@ -440,7 +441,7 @@ parser_done: } } int init_parse_config_file(const char* path) { int init_parse_config_file(const char* path) { INFO("Parsing %s...", path); INFO("Parsing %s...\n", path); std::string data; std::string data; if (!read_file(path, &data)) { if (!read_file(path, &data)) { return -1; return -1; Loading Loading @@ -663,6 +664,65 @@ int action_queue_empty() return list_empty(&action_queue); return list_empty(&action_queue); } } service* make_exec_oneshot_service(int nargs, char** args) { // Parse the arguments: exec [SECLABEL [UID [GID]*] --] COMMAND ARGS... int command_arg = 1; for (int i = 1; i < nargs; ++i) { if (strcmp(args[i], "--") == 0) { command_arg = i + 1; break; } } if (command_arg > 4 + NR_SVC_SUPP_GIDS) { ERROR("exec called with too many supplementary group ids\n"); return NULL; } int argc = nargs - command_arg; char** argv = (args + command_arg); if (argc < 1) { ERROR("exec called without command\n"); return NULL; } service* svc = (service*) calloc(1, sizeof(*svc) + sizeof(char*) * argc); if (svc == NULL) { ERROR("Couldn't allocate service for exec of '%s': %s", argv[0], strerror(errno)); return NULL; } if (command_arg > 2) { svc->seclabel = args[1]; } if (command_arg > 3) { svc->uid = decode_uid(args[2]); } if (command_arg > 4) { svc->gid = decode_uid(args[3]); svc->nr_supp_gids = command_arg - 1 /* -- */ - 4 /* exec SECLABEL UID GID */; for (size_t i = 0; i < svc->nr_supp_gids; ++i) { svc->supp_gids[i] = decode_uid(args[4 + i]); } } static int exec_count; // Every service needs a unique name. char* name = NULL; asprintf(&name, "exec %d (%s)", exec_count++, argv[0]); if (name == NULL) { ERROR("Couldn't allocate name for exec service '%s'\n", argv[0]); free(svc); return NULL; } svc->name = name; svc->classname = "default"; svc->flags = SVC_EXEC | SVC_ONESHOT; svc->nargs = argc; memcpy(svc->args, argv, sizeof(char*) * svc->nargs); svc->args[argc] = NULL; list_add_tail(&service_list, &svc->slist); return svc; } static void *parse_service(struct parse_state *state, int nargs, char **args) static void *parse_service(struct parse_state *state, int nargs, char **args) { { if (nargs < 3) { if (nargs < 3) { Loading @@ -686,7 +746,7 @@ static void *parse_service(struct parse_state *state, int nargs, char **args) parse_error(state, "out of memory\n"); parse_error(state, "out of memory\n"); return 0; return 0; } } svc->name = args[1]; svc->name = strdup(args[1]); svc->classname = "default"; svc->classname = "default"; memcpy(svc->args, args + 2, sizeof(char*) * nargs); memcpy(svc->args, args + 2, sizeof(char*) * nargs); trigger* cur_trigger = (trigger*) calloc(1, sizeof(*cur_trigger)); trigger* cur_trigger = (trigger*) calloc(1, sizeof(*cur_trigger)); Loading Loading
init/Android.mk +1 −0 Original line number Original line Diff line number Diff line Loading @@ -75,6 +75,7 @@ include $(BUILD_EXECUTABLE) include $(CLEAR_VARS) include $(CLEAR_VARS) LOCAL_MODULE := init_tests LOCAL_MODULE := init_tests LOCAL_SRC_FILES := \ LOCAL_SRC_FILES := \ init_parser_test.cpp \ util_test.cpp \ util_test.cpp \ LOCAL_SHARED_LIBRARIES += \ LOCAL_SHARED_LIBRARIES += \ Loading
init/builtins.cpp +8 −3 Original line number Original line Diff line number Diff line Loading @@ -172,11 +172,16 @@ int do_enable(int nargs, char **args) return 0; return 0; } } int do_exec(int nargs, char **args) int do_exec(int nargs, char** args) { { service* svc = make_exec_oneshot_service(nargs, args); if (svc == NULL) { return -1; return -1; } } service_start(svc, NULL); return 0; } // TODO: remove execonce when exec is available. int do_execonce(int nargs, char **args) int do_execonce(int nargs, char **args) { { pid_t child; pid_t child; Loading
init/init.cpp +95 −93 Original line number Original line Diff line number Diff line Loading @@ -14,40 +14,40 @@ * limitations under the License. * limitations under the License. */ */ #include <ctype.h> #include <dirent.h> #include <errno.h> #include <fcntl.h> #include <libgen.h> #include <signal.h> #include <stdarg.h> #include <stdio.h> #include <stdio.h> #include <stdlib.h> #include <stdlib.h> #include <string.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <ctype.h> #include <signal.h> #include <sys/wait.h> #include <sys/mount.h> #include <sys/mount.h> #include <sys/stat.h> #include <sys/poll.h> #include <sys/poll.h> #include <errno.h> #include <stdarg.h> #include <mtd/mtd-user.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/socket.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/un.h> #include <sys/un.h> #include <dirent.h> #include <sys/wait.h> #include <termios.h> #include <unistd.h> #include <memory> #include <memory> #include <mtd/mtd-user.h> #include <selinux/selinux.h> #include <selinux/selinux.h> #include <selinux/label.h> #include <selinux/label.h> #include <selinux/android.h> #include <selinux/android.h> #include <libgen.h> #include <cutils/list.h> #include <cutils/android_reboot.h> #include <cutils/android_reboot.h> #include <cutils/sockets.h> #include <cutils/iosched_policy.h> #include <cutils/fs.h> #include <cutils/fs.h> #include <cutils/iosched_policy.h> #include <cutils/list.h> #include <cutils/sockets.h> #include <private/android_filesystem_config.h> #include <private/android_filesystem_config.h> #include <termios.h> #include <utils/file.h> #include <utils/file.h> #include <utils/stringprintf.h> #include <utils/stringprintf.h> Loading Loading @@ -75,22 +75,35 @@ static char qemu[32]; static struct action *cur_action = NULL; static struct action *cur_action = NULL; static struct command *cur_command = NULL; static struct command *cur_command = NULL; void notify_service_state(const char *name, const char *state) { char pname[PROP_NAME_MAX]; int len = strlen(name); if ((len + 10) > PROP_NAME_MAX) return; snprintf(pname, sizeof(pname), "init.svc.%s", name); property_set(pname, state); } static int have_console; static int have_console; static char console_name[PROP_VALUE_MAX] = "/dev/console"; static char console_name[PROP_VALUE_MAX] = "/dev/console"; static time_t process_needs_restart; static time_t process_needs_restart; static const char *ENV[32]; static const char *ENV[32]; bool waiting_for_exec = false; void service::NotifyStateChange(const char* new_state) { if (!properties_inited()) { // If properties aren't available yet, we can't set them. return; } if ((flags & SVC_EXEC) != 0) { // 'exec' commands don't have properties tracking their state. return; } char prop_name[PROP_NAME_MAX]; if (snprintf(prop_name, sizeof(prop_name), "init.svc.%s", name) >= PROP_NAME_MAX) { // If the property name would be too long, we can't set it. ERROR("Property name \"init.svc.%s\" too long; not setting to %s\n", name, new_state); return; } property_set(prop_name, new_state); } /* add_environment - add "key=value" to the current environment */ /* add_environment - add "key=value" to the current environment */ int add_environment(const char *key, const char *val) int add_environment(const char *key, const char *val) { { Loading Loading @@ -163,35 +176,26 @@ static void publish_socket(const char *name, int fd) void service_start(struct service *svc, const char *dynamic_args) void service_start(struct service *svc, const char *dynamic_args) { { struct stat s; // Starting a service removes it from the disabled or reset state and pid_t pid; // immediately takes it out of the restarting state if it was in there. int needs_console; char *scon = NULL; int rc; /* starting a service removes it from the disabled or reset * state and immediately takes it out of the restarting * state if it was in there */ svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START)); svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START)); svc->time_started = 0; svc->time_started = 0; /* running processes require no additional work -- if // Running processes require no additional work --- if they're in the * they're in the process of exiting, we've ensured // process of exiting, we've ensured that they will immediately restart * that they will immediately restart on exit, unless // on exit, unless they are ONESHOT. * they are ONESHOT */ if (svc->flags & SVC_RUNNING) { if (svc->flags & SVC_RUNNING) { return; return; } } needs_console = (svc->flags & SVC_CONSOLE) ? 1 : 0; bool needs_console = (svc->flags & SVC_CONSOLE); if (needs_console && (!have_console)) { if (needs_console && !have_console) { ERROR("service '%s' requires console\n", svc->name); ERROR("service '%s' requires console\n", svc->name); svc->flags |= SVC_DISABLED; svc->flags |= SVC_DISABLED; return; return; } } struct stat s; if (stat(svc->args[0], &s) != 0) { if (stat(svc->args[0], &s) != 0) { ERROR("cannot find '%s', disabling '%s'\n", svc->args[0], svc->name); ERROR("cannot find '%s', disabling '%s'\n", svc->args[0], svc->name); svc->flags |= SVC_DISABLED; svc->flags |= SVC_DISABLED; Loading @@ -205,6 +209,7 @@ void service_start(struct service *svc, const char *dynamic_args) return; return; } } char* scon = NULL; if (is_selinux_enabled() > 0) { if (is_selinux_enabled() > 0) { if (svc->seclabel) { if (svc->seclabel) { scon = strdup(svc->seclabel); scon = strdup(svc->seclabel); Loading @@ -216,7 +221,7 @@ void service_start(struct service *svc, const char *dynamic_args) char *mycon = NULL, *fcon = NULL; char *mycon = NULL, *fcon = NULL; INFO("computing context for service '%s'\n", svc->args[0]); INFO("computing context for service '%s'\n", svc->args[0]); rc = getcon(&mycon); int rc = getcon(&mycon); if (rc < 0) { if (rc < 0) { ERROR("could not get context while starting '%s'\n", svc->name); ERROR("could not get context while starting '%s'\n", svc->name); return; return; Loading Loading @@ -244,8 +249,7 @@ void service_start(struct service *svc, const char *dynamic_args) NOTICE("starting '%s'\n", svc->name); NOTICE("starting '%s'\n", svc->name); pid = fork(); pid_t pid = fork(); if (pid == 0) { if (pid == 0) { struct socketinfo *si; struct socketinfo *si; struct svcenvinfo *ei; struct svcenvinfo *ei; Loading Loading @@ -301,7 +305,7 @@ void service_start(struct service *svc, const char *dynamic_args) setpgid(0, getpid()); setpgid(0, getpid()); /* as requested, set our gid, supplemental gids, and uid */ // As requested, set our gid, supplemental gids, and uid. if (svc->gid) { if (svc->gid) { if (setgid(svc->gid) != 0) { if (setgid(svc->gid) != 0) { ERROR("setgid failed: %s\n", strerror(errno)); ERROR("setgid failed: %s\n", strerror(errno)); Loading Loading @@ -364,8 +368,13 @@ void service_start(struct service *svc, const char *dynamic_args) svc->pid = pid; svc->pid = pid; svc->flags |= SVC_RUNNING; svc->flags |= SVC_RUNNING; if (properties_inited()) if ((svc->flags & SVC_EXEC) != 0) { notify_service_state(svc->name, "running"); INFO("SVC_EXEC pid %d (uid %d gid %d+%d context %s) started; waiting...\n", svc->pid, svc->uid, svc->gid, svc->nr_supp_gids, svc->seclabel); waiting_for_exec = true; } svc->NotifyStateChange("running"); } } /* The how field should be either SVC_DISABLED, SVC_RESET, or SVC_RESTART */ /* The how field should be either SVC_DISABLED, SVC_RESET, or SVC_RESTART */ Loading @@ -391,9 +400,9 @@ static void service_stop_or_reset(struct service *svc, int how) if (svc->pid) { if (svc->pid) { NOTICE("service '%s' is being killed\n", svc->name); NOTICE("service '%s' is being killed\n", svc->name); kill(-svc->pid, SIGKILL); kill(-svc->pid, SIGKILL); notify_service_state(svc->name, "stopping"); svc->NotifyStateChange("stopping"); } else { } else { notify_service_state(svc->name, "stopped"); svc->NotifyStateChange("stopped"); } } } } Loading Loading @@ -993,28 +1002,18 @@ static void selinux_initialize(void) security_setenforce(is_enforcing); security_setenforce(is_enforcing); } } int main(int argc, char **argv) int main(int argc, char** argv) { { size_t fd_count = 0; struct pollfd ufds[4]; int property_set_fd_init = 0; int signal_fd_init = 0; int keychord_fd_init = 0; bool is_charger = false; if (!strcmp(basename(argv[0]), "ueventd")) if (!strcmp(basename(argv[0]), "ueventd")) return ueventd_main(argc, argv); return ueventd_main(argc, argv); if (!strcmp(basename(argv[0]), "watchdogd")) if (!strcmp(basename(argv[0]), "watchdogd")) return watchdogd_main(argc, argv); return watchdogd_main(argc, argv); /* clear the umask */ // Clear the umask. umask(0); umask(0); /* Get the basic filesystem setup we need put // Get the basic filesystem setup we need put together in the initramdisk * together in the initramdisk on / and then we'll // on / and then we'll let the rc file figure out the rest. * let the rc file figure out the rest. */ mkdir("/dev", 0755); mkdir("/dev", 0755); mkdir("/proc", 0755); mkdir("/proc", 0755); mkdir("/sys", 0755); mkdir("/sys", 0755); Loading @@ -1026,15 +1025,13 @@ int main(int argc, char **argv) mount("proc", "/proc", "proc", 0, NULL); mount("proc", "/proc", "proc", 0, NULL); mount("sysfs", "/sys", "sysfs", 0, NULL); mount("sysfs", "/sys", "sysfs", 0, NULL); /* indicate that booting is in progress to background fw loaders, etc */ // Indicate that booting is in progress to background fw loaders, etc. close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000)); close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000)); /* We must have some place other than / to create the // We must have some place other than / to create the device nodes for * device nodes for kmsg and null, otherwise we won't // kmsg and null, otherwise we won't be able to remount / read-only * be able to remount / read-only later on. // later on. Now that tmpfs is mounted on /dev, we can actually talk * Now that tmpfs is mounted on /dev, we can actually // to the outside world. * talk to the outside world. */ open_devnull_stdio(); open_devnull_stdio(); klog_init(); klog_init(); property_init(); property_init(); Loading @@ -1050,25 +1047,22 @@ int main(int argc, char **argv) */ */ export_kernel_boot_props(); export_kernel_boot_props(); union selinux_callback cb; selinux_callback cb; cb.func_log = log_callback; cb.func_log = log_callback; selinux_set_callback(SELINUX_CB_LOG, cb); selinux_set_callback(SELINUX_CB_LOG, cb); cb.func_audit = audit_callback; cb.func_audit = audit_callback; selinux_set_callback(SELINUX_CB_AUDIT, cb); selinux_set_callback(SELINUX_CB_AUDIT, cb); selinux_initialize(); selinux_initialize(); /* These directories were necessarily created before initial policy load * and therefore need their security context restored to the proper value. // These directories were necessarily created before initial policy load * This must happen before /dev is populated by ueventd. // and therefore need their security context restored to the proper value. */ // This must happen before /dev is populated by ueventd. restorecon("/dev"); restorecon("/dev"); restorecon("/dev/socket"); restorecon("/dev/socket"); restorecon("/dev/__properties__"); restorecon("/dev/__properties__"); restorecon_recursive("/sys"); restorecon_recursive("/sys"); is_charger = !strcmp(bootmode, "charger"); INFO("property init\n"); INFO("property init\n"); property_load_boot_defaults(); property_load_boot_defaults(); Loading @@ -1081,50 +1075,58 @@ int main(int argc, char **argv) queue_builtin_action(keychord_init_action, "keychord_init"); queue_builtin_action(keychord_init_action, "keychord_init"); queue_builtin_action(console_init_action, "console_init"); queue_builtin_action(console_init_action, "console_init"); /* execute all the boot actions to get us started */ // Execute all the boot actions to get us started. action_for_each_trigger("init", action_add_queue_tail); action_for_each_trigger("init", action_add_queue_tail); /* Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random // Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random * wasn't ready immediately after wait_for_coldboot_done // wasn't ready immediately after wait_for_coldboot_done */ queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng"); queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng"); queue_builtin_action(property_service_init_action, "property_service_init"); queue_builtin_action(property_service_init_action, "property_service_init"); queue_builtin_action(signal_init_action, "signal_init"); queue_builtin_action(signal_init_action, "signal_init"); /* Don't mount filesystems or start core system services if in charger mode. */ // Don't mount filesystems or start core system services in charger mode. if (is_charger) { if (strcmp(bootmode, "charger") == 0) { action_for_each_trigger("charger", action_add_queue_tail); action_for_each_trigger("charger", action_add_queue_tail); } else { } else { action_for_each_trigger("late-init", action_add_queue_tail); action_for_each_trigger("late-init", action_add_queue_tail); } } /* run all property triggers based on current state of the properties */ // Run all property triggers based on current state of the properties. queue_builtin_action(queue_property_triggers_action, "queue_property_triggers"); queue_builtin_action(queue_property_triggers_action, "queue_property_triggers"); // TODO: why do we only initialize ufds after execute_one_command and restart_processes? size_t fd_count = 0; struct pollfd ufds[3]; bool property_set_fd_init = false; bool signal_fd_init = false; bool keychord_fd_init = false; for (;;) { for (;;) { if (!waiting_for_exec) { execute_one_command(); execute_one_command(); restart_processes(); restart_processes(); } if (!property_set_fd_init && get_property_set_fd() > 0) { if (!property_set_fd_init && get_property_set_fd() > 0) { ufds[fd_count].fd = get_property_set_fd(); ufds[fd_count].fd = get_property_set_fd(); ufds[fd_count].events = POLLIN; ufds[fd_count].events = POLLIN; ufds[fd_count].revents = 0; ufds[fd_count].revents = 0; fd_count++; fd_count++; property_set_fd_init = 1; property_set_fd_init = true; } } if (!signal_fd_init && get_signal_fd() > 0) { if (!signal_fd_init && get_signal_fd() > 0) { ufds[fd_count].fd = get_signal_fd(); ufds[fd_count].fd = get_signal_fd(); ufds[fd_count].events = POLLIN; ufds[fd_count].events = POLLIN; ufds[fd_count].revents = 0; ufds[fd_count].revents = 0; fd_count++; fd_count++; signal_fd_init = 1; signal_fd_init = true; } } if (!keychord_fd_init && get_keychord_fd() > 0) { if (!keychord_fd_init && get_keychord_fd() > 0) { ufds[fd_count].fd = get_keychord_fd(); ufds[fd_count].fd = get_keychord_fd(); ufds[fd_count].events = POLLIN; ufds[fd_count].events = POLLIN; ufds[fd_count].revents = 0; ufds[fd_count].revents = 0; fd_count++; fd_count++; keychord_fd_init = 1; keychord_fd_init = true; } } int timeout = -1; int timeout = -1; Loading
init/init.h +28 −25 Original line number Original line Diff line number Diff line Loading @@ -17,13 +17,11 @@ #ifndef _INIT_INIT_H #ifndef _INIT_INIT_H #define _INIT_INIT_H #define _INIT_INIT_H #include <sys/types.h> #include <cutils/list.h> #include <cutils/list.h> #include <cutils/iosched_policy.h> #include <cutils/iosched_policy.h> #include <sys/stat.h> void handle_control_message(const char *msg, const char *arg); struct command struct command { { /* list of commands in an action */ /* list of commands in an action */ Loading Loading @@ -59,8 +57,6 @@ struct action { struct command *current; struct command *current; }; }; void build_triggers_string(char *name_str, int length, struct action *cur_action); struct socketinfo { struct socketinfo { struct socketinfo *next; struct socketinfo *next; const char *name; const char *name; Loading @@ -77,27 +73,29 @@ struct svcenvinfo { const char *value; const char *value; }; }; #define SVC_DISABLED 0x01 /* do not autostart with class */ #define SVC_DISABLED 0x001 // do not autostart with class #define SVC_ONESHOT 0x02 /* do not restart on exit */ #define SVC_ONESHOT 0x002 // do not restart on exit #define SVC_RUNNING 0x04 /* currently active */ #define SVC_RUNNING 0x004 // currently active #define SVC_RESTARTING 0x08 /* waiting to restart */ #define SVC_RESTARTING 0x008 // waiting to restart #define SVC_CONSOLE 0x10 /* requires console */ #define SVC_CONSOLE 0x010 // requires console #define SVC_CRITICAL 0x20 /* will reboot into recovery if keeps crashing */ #define SVC_CRITICAL 0x020 // will reboot into recovery if keeps crashing #define SVC_RESET 0x40 /* Use when stopping a process, but not disabling #define SVC_RESET 0x040 // Use when stopping a process, but not disabling so it can be restarted with its class. so it can be restarted with its class */ #define SVC_RC_DISABLED 0x080 // Remember if the disabled flag was set in the rc script. #define SVC_RC_DISABLED 0x80 /* Remember if the disabled flag was set in the rc script */ #define SVC_RESTART 0x100 // Use to safely restart (stop, wait, start) a service. #define SVC_RESTART 0x100 /* Use to safely restart (stop, wait, start) a service */ #define SVC_DISABLED_START 0x200 // A start was requested but it was disabled at the time. #define SVC_DISABLED_START 0x200 /* a start was requested but it was disabled at the time */ #define SVC_EXEC 0x400 // This synthetic service corresponds to an 'exec'. #define NR_SVC_SUPP_GIDS 12 /* twelve supplementary groups */ #define NR_SVC_SUPP_GIDS 12 /* twelve supplementary groups */ #define COMMAND_RETRY_TIMEOUT 5 #define COMMAND_RETRY_TIMEOUT 5 struct service { struct service { void NotifyStateChange(const char* new_state); /* list of all services */ /* list of all services */ struct listnode slist; struct listnode slist; const char *name; char *name; const char *classname; const char *classname; unsigned flags; unsigned flags; Loading @@ -111,7 +109,7 @@ struct service { gid_t supp_gids[NR_SVC_SUPP_GIDS]; gid_t supp_gids[NR_SVC_SUPP_GIDS]; size_t nr_supp_gids; size_t nr_supp_gids; char *seclabel; const char* seclabel; struct socketinfo *sockets; struct socketinfo *sockets; struct svcenvinfo *envvars; struct svcenvinfo *envvars; Loading @@ -131,7 +129,13 @@ struct service { char *args[1]; char *args[1]; }; /* ^-------'args' MUST be at the end of this struct! */ }; /* ^-------'args' MUST be at the end of this struct! */ void notify_service_state(const char *name, const char *state); extern bool waiting_for_exec; extern struct selabel_handle *sehandle; extern struct selabel_handle *sehandle_prop; void build_triggers_string(char *name_str, int length, struct action *cur_action); void handle_control_message(const char *msg, const char *arg); struct service *service_find_by_name(const char *name); struct service *service_find_by_name(const char *name); struct service *service_find_by_pid(pid_t pid); struct service *service_find_by_pid(pid_t pid); Loading @@ -147,9 +151,8 @@ void service_restart(struct service *svc); void service_start(struct service *svc, const char *dynamic_args); void service_start(struct service *svc, const char *dynamic_args); void property_changed(const char *name, const char *value); void property_changed(const char *name, const char *value); extern struct selabel_handle *sehandle; int selinux_reload_policy(void); extern struct selabel_handle *sehandle_prop; extern int selinux_reload_policy(void); void zap_stdio(void); void zap_stdio(void); #endif /* _INIT_INIT_H */ #endif /* _INIT_INIT_H */
init/init_parser.cpp +62 −2 Original line number Original line Diff line number Diff line Loading @@ -14,6 +14,7 @@ * limitations under the License. * limitations under the License. */ */ #include <errno.h> #include <stdio.h> #include <stdio.h> #include <stdlib.h> #include <stdlib.h> #include <unistd.h> #include <unistd.h> Loading Loading @@ -440,7 +441,7 @@ parser_done: } } int init_parse_config_file(const char* path) { int init_parse_config_file(const char* path) { INFO("Parsing %s...", path); INFO("Parsing %s...\n", path); std::string data; std::string data; if (!read_file(path, &data)) { if (!read_file(path, &data)) { return -1; return -1; Loading Loading @@ -663,6 +664,65 @@ int action_queue_empty() return list_empty(&action_queue); return list_empty(&action_queue); } } service* make_exec_oneshot_service(int nargs, char** args) { // Parse the arguments: exec [SECLABEL [UID [GID]*] --] COMMAND ARGS... int command_arg = 1; for (int i = 1; i < nargs; ++i) { if (strcmp(args[i], "--") == 0) { command_arg = i + 1; break; } } if (command_arg > 4 + NR_SVC_SUPP_GIDS) { ERROR("exec called with too many supplementary group ids\n"); return NULL; } int argc = nargs - command_arg; char** argv = (args + command_arg); if (argc < 1) { ERROR("exec called without command\n"); return NULL; } service* svc = (service*) calloc(1, sizeof(*svc) + sizeof(char*) * argc); if (svc == NULL) { ERROR("Couldn't allocate service for exec of '%s': %s", argv[0], strerror(errno)); return NULL; } if (command_arg > 2) { svc->seclabel = args[1]; } if (command_arg > 3) { svc->uid = decode_uid(args[2]); } if (command_arg > 4) { svc->gid = decode_uid(args[3]); svc->nr_supp_gids = command_arg - 1 /* -- */ - 4 /* exec SECLABEL UID GID */; for (size_t i = 0; i < svc->nr_supp_gids; ++i) { svc->supp_gids[i] = decode_uid(args[4 + i]); } } static int exec_count; // Every service needs a unique name. char* name = NULL; asprintf(&name, "exec %d (%s)", exec_count++, argv[0]); if (name == NULL) { ERROR("Couldn't allocate name for exec service '%s'\n", argv[0]); free(svc); return NULL; } svc->name = name; svc->classname = "default"; svc->flags = SVC_EXEC | SVC_ONESHOT; svc->nargs = argc; memcpy(svc->args, argv, sizeof(char*) * svc->nargs); svc->args[argc] = NULL; list_add_tail(&service_list, &svc->slist); return svc; } static void *parse_service(struct parse_state *state, int nargs, char **args) static void *parse_service(struct parse_state *state, int nargs, char **args) { { if (nargs < 3) { if (nargs < 3) { Loading @@ -686,7 +746,7 @@ static void *parse_service(struct parse_state *state, int nargs, char **args) parse_error(state, "out of memory\n"); parse_error(state, "out of memory\n"); return 0; return 0; } } svc->name = args[1]; svc->name = strdup(args[1]); svc->classname = "default"; svc->classname = "default"; memcpy(svc->args, args + 2, sizeof(char*) * nargs); memcpy(svc->args, args + 2, sizeof(char*) * nargs); trigger* cur_trigger = (trigger*) calloc(1, sizeof(*cur_trigger)); trigger* cur_trigger = (trigger*) calloc(1, sizeof(*cur_trigger)); Loading