int api_init(uev_ctx_t *ctx) { int sd; struct sockaddr_un sun = { .sun_family = AF_UNIX, .sun_path = INIT_SOCKET, }; sd = socket(AF_UNIX, SOCK_STREAM, 0); if (-1 == sd) { _pe("Failed starting external API socket"); return 1; } erase(INIT_SOCKET); if (-1 == bind(sd, (struct sockaddr*)&sun, sizeof(sun))) goto error; if (-1 == listen(sd, 10)) goto error; return uev_io_init(ctx, &api_watcher, cb, NULL, sd, UEV_READ); error: _pe("Failed intializing API socket"); close(sd); return 1; }
/** * service_enabled - Should the service run? * @svc: Pointer to &svc_t object * @event: Dynamic event, opaque flag passed to callback * @arg: Event argument, used only by external service plugins. * * This method calls an associated service callback, if registered by a * plugin, and returns the &svc_cmd_t status. If no plugin is registered * the service is statically enabled in /etc/finit.conf and the result * will always be %SVC_START. * * Returns: * Either one of %SVC_START, %SVC_STOP, %SVC_RELOAD. */ svc_cmd_t service_enabled(svc_t *svc, int event, void *arg) { if (!svc) { errno = EINVAL; return SVC_STOP; } if (!svc_in_runlevel(svc, runlevel)) return SVC_STOP; /* * Event conditions for services are ignored during bootstrap. */ if (runlevel && !event_service_cond(svc->events)) return SVC_STOP; /* Is there a service plugin registered? */ if (svc->cb) { int status; pid_t pid; /* Let callback run in separate process so it doesn't crash PID 1 */ pid = fork(); if (-1 == pid) { _pe("Failed in %s callback", svc->cmd); return SVC_STOP; } if (!pid) { status = svc->cb(svc, event, arg); exit(status); } if (waitpid(pid, &status, 0) == -1) { _pe("Failed reading status from %s callback", svc->cmd); return SVC_STOP; } /* Callback normally exits here. */ if (WIFEXITED(status)) return WEXITSTATUS(status); /* Check for SEGFAULT or other error ... */ if (WIFSIGNALED(status) && WCOREDUMP(status)) _e("Callback to %s crashed!\n", svc->cmd); else _e("Callback to %s did not exit normally!\n", svc->cmd); return SVC_STOP; } /* No service plugin, default to start, since listed in finit.conf */ return SVC_START; }
static int parse_conf_dynamic(char *file, time_t mtime) { FILE *fp = fopen(file, "r"); if (!fp) { _pe("Failed opening %s", file); return 1; } while (!feof(fp)) { char line[LINE_SIZE] = ""; if (!fgets(line, sizeof(line), fp)) continue; chomp(line); _d("dyn conf: %s", line); parse_dynamic(line, mtime); } fclose(fp); return 0; }
int watchdog(char *progname) { int pid; pid = fork(); if (pid == 0) { int fd, ret; fd = init(progname, WDT_DEVNODE); if (fd == -1) { if (ENOENT != errno) _pe("Failed connecting to watchdog %s", WDT_DEVNODE); _exit(1); } ret = loop(fd, WDT_TIMEOUT); while (!handover) { /* Waiting for SIGTERM ... */ sleep(1); /* Set lowest possible timeout on SIGTERM */ ioctl(fd, WDIOC_SETTIMEOUT, &shutdown); } close(fd); _exit(ret); } return pid; }
static void setup(void) { if (plugin.io.fd > 0) close(plugin.io.fd); plugin.io.fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); if (-1 == plugin.io.fd || inotify_add_watch(plugin.io.fd, "/dev", IN_CREATE | IN_DELETE) < 0) _pe("Failed starting TTY watcher"); }
static void pidfile_init(void *arg) { struct context *ctx = arg; ctx->wd = inotify_add_watch(ctx->fd, _PATH_VARRUN, IN_CREATE | IN_ATTRIB | IN_DELETE); if (ctx->wd < 0) { _pe("inotify_add_watch()"); close(ctx->fd); return; } _d("pidfile monitor active"); }
static int kprobe_attach_one(kprobe_t *kp, const char *func) { struct perf_event_attr attr = {}; int efd, i, id; id = kprobe_event_id(kp, func); if (id < 0) return id; attr.type = PERF_TYPE_TRACEPOINT; attr.sample_type = PERF_SAMPLE_RAW; attr.sample_period = 1; attr.wakeup_events = 1; attr.config = id; for (i = 0; i < /* sysconf(_SC_NPROCESSORS_ONLN) */ 1; i++) { efd = perf_event_open(&attr, -1/*pid*/, i/*cpu*/, -1/*group_fd*/, 0); if (efd < 0) { perror("perf_event_open"); return -errno; } if (ioctl(efd, PERF_EVENT_IOC_ENABLE, 0)) { perror("perf enable"); close(efd); return -errno; } if (!i && ioctl(efd, PERF_EVENT_IOC_SET_BPF, kp->bfd)) { _pe("perf-set-bpf: %s", func); close(efd); return -errno; } if (kp->efds.len == kp->efds.cap) { size_t sz = kp->efds.cap * sizeof(*kp->efds.fds); kp->efds.fds = realloc(kp->efds.fds, sz << 1); assert(kp->efds.fds); kp->efds.cap <<= 1; } kp->efds.fds[kp->efds.len++] = efd; } return 1; }
/* * Check all filesystems in /etc/fstab with a fs_passno > 0 */ static int fsck(int pass) { // int save; struct fstab *fs; if (!setfsent()) { _pe("Failed opening fstab"); return 1; } // if ((save = log_is_debug())) // log_debug(); while ((fs = getfsent())) { char cmd[80]; struct stat st; if (fs->fs_passno != pass) continue; errno = 0; if (stat(fs->fs_spec, &st) || !S_ISBLK(st.st_mode)) { if (!string_match(fs->fs_spec, "UUID=") && !string_match(fs->fs_spec, "LABEL=")) { _d("Cannot fsck %s, not a block device: %s", fs->fs_spec, strerror(errno)); continue; } } if (fismnt(fs->fs_file)) { _d("Skipping fsck of %s, already mounted on %s.", fs->fs_spec, fs->fs_file); continue; } snprintf(cmd, sizeof(cmd), "fsck -a %s", fs->fs_spec); run_interactive(cmd, "Checking filesystem %.13s", fs->fs_spec); } // if (save) // log_debug(); endfsent(); return 0; }
static void pidfile_callback(void *UNUSED(arg), int fd, int UNUSED(events)) { static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1]; static char cond[MAX_ARG_LEN]; struct inotify_event *ev; ssize_t sz, len; char *basename; svc_t *svc; sz = read(fd, ev_buf, sizeof(ev_buf) - 1); if (sz <= 0) { _pe("invalid inotify event"); return; } ev_buf[sz] = 0; for (ev = (void *)ev_buf; sz > (ssize_t)sizeof(*ev); len = sizeof(*ev) + ev->len, ev = (void *)ev + len, sz -= len) { /* ev = (void *)(ev + 1) + ev->len, sz -= sizeof(*ev) + ev->len) { */ if (!ev->mask || !strstr(ev->name, ".pid")) continue; basename = strtok(ev->name, "."); if (!basename) continue; svc = svc_find_by_nameid(basename, 1); if (!svc) continue; /* TODO FIXME XXX WKZ check that pid is controlled by finit */ _d("%s: match %s", basename, svc->cmd); snprintf(cond, sizeof(cond), "svc%s", svc->cmd); if (ev->mask & (IN_CREATE | IN_ATTRIB)) { svc_started(svc); cond_set(cond); } else if (ev->mask & IN_DELETE) cond_clear(cond); } }
static int kprobe_event_id(kprobe_t *kp, const char *func) { FILE *fp; char *ev_id, ev_str[16]; fprintf(kp->ctrl, "%s %s\n", kp->type, func); fflush(kp->ctrl); asprintf(&ev_id, "/sys/kernel/debug/tracing/events/kprobes/%s_%s_0/id", kp->type, func); fp = fopen(ev_id, "r"); free(ev_id); if (!fp) { _pe("unable to create kprobe for \"%s\"", func); return -EIO; } fgets(ev_str, sizeof(ev_str), fp); fclose(fp); return strtol(ev_str, NULL, 0); }
static void nl_callback(void *arg, int sd, int events) { ssize_t len; static char buf[4096]; struct nlmsghdr *nh; memset(buf, 0, sizeof(buf)); len = recv(sd, buf, sizeof(buf), 0); if (len < 0) { if (errno != EINTR) /* Signal */ _pe("recv()"); return; } for (nh = (struct nlmsghdr *)buf; !nlmsg_validate(nh, len); nh = NLMSG_NEXT(nh, len)) { //_d("Well formed netlink message received. type %d ...", nh->nlmsg_type); if (nh->nlmsg_type == RTM_NEWROUTE || nh->nlmsg_type == RTM_DELROUTE) nl_route(nh); else nl_link(nh); } }
int main(int argc, char* argv[]) { char *path; char cmd[256]; int udev = 0; uev_t timer; /* Bootstrap timer, on timeout call finalize() */ uev_ctx_t loop; /* * finit/init/telinit client tool uses /dev/initctl pipe * for compatibility but initctl client tool uses socket */ if (getpid() != 1) return client(argc, argv); /* * Initalize event context. */ uev_init(&loop); ctx = &loop; /* * Set PATH and SHELL early to something sane */ setenv("PATH", _PATH_STDPATH, 1); setenv("SHELL", _PATH_BSHELL, 1); /* * Mount base file system, kernel is assumed to run devtmpfs for /dev */ chdir("/"); umask(0); mount("none", "/proc", "proc", 0, NULL); mount("none", "/sys", "sysfs", 0, NULL); if (fisdir("/proc/bus/usb")) mount("none", "/proc/bus/usb", "usbfs", 0, NULL); /* * Parse kernel command line (debug, rescue, splash, etc.) * Also calls log_init() to set correct log level */ conf_parse_cmdline(); /* Set up canvas */ if (!rescue && !log_is_debug()) screen_init(); /* * In case of emergency. */ emergency_shell(); /* * Initial setup of signals, ignore all until we're up. */ sig_init(); /* * Load plugins early, finit.conf may contain references to * features implemented by plugins. */ plugin_init(&loop); /* * Hello world. */ banner(); /* * Check file filesystems in /etc/fstab */ for (int pass = 1; pass < 10 && !rescue; pass++) { if (fsck(pass)) break; } /* * Initialize .conf system and load static /etc/finit.conf * Also initializes global_rlimit[] for udevd, below. */ conf_init(); /* * Some non-embedded systems without an initramfs may not have /dev mounted yet * If they do, check if system has udevadm and perform cleanup from initramfs */ if (!fismnt("/dev")) mount("udev", "/dev", "devtmpfs", MS_RELATIME, "size=10%,nr_inodes=61156,mode=755"); else if (whichp("udevadm")) run_interactive("udevadm info --cleanup-db", "Cleaning up udev db"); /* Some systems use /dev/pts */ makedir("/dev/pts", 0755); mount("devpts", "/dev/pts", "devpts", 0, "gid=5,mode=620"); /* * Some systems rely on us to both create /dev/shm and, to mount * a tmpfs there. Any system with dbus needs shared memory, so * mount it, unless its already mounted, but not if listed in * the /etc/fstab file already. */ makedir("/dev/shm", 0755); if (!fismnt("/dev/shm") && !ismnt("/etc/fstab", "/dev/shm")) mount("shm", "/dev/shm", "tmpfs", 0, NULL); /* * New tmpfs based /run for volatile runtime data * For details, see http://lwn.net/Articles/436012/ */ if (fisdir("/run") && !fismnt("/run")) mount("tmpfs", "/run", "tmpfs", MS_NODEV, "mode=0755,size=10%"); umask(022); /* Bootstrap conditions, needed for hooks */ cond_init(); /* * Populate /dev and prepare for runtime events from kernel. * Prefer udev if mdev is also available on the system. */ path = which("udevd"); if (!path) path = which("/lib/systemd/systemd-udevd"); if (path) { /* Desktop and server distros usually have a variant of udev */ udev = 1; /* Register udevd as a monitored service */ snprintf(cmd, sizeof(cmd), "[S12345789] pid:udevd %s -- Device event managing daemon", path); if (service_register(SVC_TYPE_SERVICE, cmd, global_rlimit, NULL)) { _pe("Failed registering %s", path); udev = 0; } else { snprintf(cmd, sizeof(cmd), ":1 [S] <svc%s> " "udevadm trigger -c add -t devices " "-- Requesting device events", path); service_register(SVC_TYPE_RUN, cmd, global_rlimit, NULL); snprintf(cmd, sizeof(cmd), ":2 [S] <svc%s> " "udevadm trigger -c add -t subsystems " "-- Requesting subsystem events", path); service_register(SVC_TYPE_RUN, cmd, global_rlimit, NULL); } free(path); } else { path = which("mdev"); if (path) { /* Embedded Linux systems usually have BusyBox mdev */ if (log_is_debug()) touch("/dev/mdev.log"); snprintf(cmd, sizeof(cmd), "%s -s", path); free(path); run_interactive(cmd, "Populating device tree"); } } /* * Start built-in watchdog as soon as possible, if enabled */ wdogpid = watchdog(argv[0]); /* * Mount filesystems */ if (!rescue) { #ifdef REMOUNT_ROOTFS run("mount -n -o remount,rw /"); #endif #ifdef SYSROOT mount(SYSROOT, "/", NULL, MS_MOVE, NULL); #endif } if (!rescue) { _d("Root FS up, calling hooks ..."); plugin_run_hooks(HOOK_ROOTFS_UP); umask(0); if (run_interactive("mount -na", "Mounting filesystems")) plugin_run_hooks(HOOK_MOUNT_ERROR); _d("Calling extra mount hook, after mount -a ..."); plugin_run_hooks(HOOK_MOUNT_POST); run("swapon -ea"); umask(0022); } /* Base FS up, enable standard SysV init signals */ sig_setup(&loop); if (!rescue) { _d("Base FS up, calling hooks ..."); plugin_run_hooks(HOOK_BASEFS_UP); } /* * Set up inotify watcher for /etc/finit.d and read all .conf * files to figure out how to bootstrap the system. */ conf_monitor(&loop); /* * Initalize state machine and start all bootstrap tasks * NOTE: no network available! */ sm_init(&sm); sm_step(&sm); /* Debian has this little script to copy generated rules while the system was read-only */ if (udev && fexist("/lib/udev/udev-finish")) run_interactive("/lib/udev/udev-finish", "Finalizing udev"); /* Start new initctl API responder */ api_init(&loop); umask(022); /* * Wait for all SVC_TYPE_RUNTASK to have completed their work in * [S], or timeout, before calling finalize() */ _d("Starting bootstrap finalize timer ..."); uev_timer_init(&loop, &timer, service_bootstrap_cb, finalize, 1000, 1000); /* * Enter main loop to monior /dev/initctl and services */ _d("Entering main loop ..."); return uev_run(&loop, 0); }
static void cb(uev_t *w, void *UNUSED(arg), int UNUSED(events)) { int sd; struct init_request rq; sd = accept(w->fd, NULL, NULL); if (sd < 0) { _pe("Failed serving API request"); return; } while (1) { int result = 0; ssize_t len; len = read(sd, &rq, sizeof(rq)); if (len <= 0) { if (-1 == len) { if (EINTR == errno) continue; if (EAGAIN == errno) break; _e("Failed reading initctl request, error %d: %s", errno, strerror(errno)); } break; } if (rq.magic != INIT_MAGIC || len != sizeof(rq)) { _e("Invalid initctl request."); break; } switch (rq.cmd) { case INIT_CMD_RUNLVL: switch (rq.runlevel) { case '0': _d("Halting system (SIGUSR2)"); do_shutdown(SIGUSR2); break; case 's': case 'S': _d("Cannot enter bootstrap after boot ..."); rq.runlevel = '1'; /* Fall through to regular processing */ case '1'...'5': case '7'...'9': _d("Setting new runlevel %c", rq.runlevel); service_runlevel(rq.runlevel - '0'); break; case '6': _d("Rebooting system (SIGUSR1)"); do_shutdown(SIGUSR1); break; default: _d("Unsupported runlevel: %d", rq.runlevel); break; } break; case INIT_CMD_DEBUG: debug = !debug; if (debug) silent = 0; else silent = quiet ? 1 : SILENT_MODE; break; case INIT_CMD_RELOAD: /* 'init q' and 'initctl reload' */ service_reload_dynamic(); break; case INIT_CMD_START_SVC: result = do_start(rq.data, sizeof(rq.data)); break; case INIT_CMD_STOP_SVC: result = do_pause(rq.data, sizeof(rq.data)); break; case INIT_CMD_RESTART_SVC: result = do_restart(rq.data, sizeof(rq.data)); break; #ifndef INETD_DISABLED case INIT_CMD_QUERY_INETD: result = do_query_inetd(rq.data, sizeof(rq.data)); break; #endif case INIT_CMD_EMIT: result = do_handle_emit(rq.data, sizeof(rq.data)); break; case INIT_CMD_GET_RUNLEVEL: rq.runlevel = runlevel; result = 0; break; case INIT_CMD_ACK: _d("Client failed reading ACK."); goto leave; default: _d("Unsupported cmd: %d", rq.cmd); break; } if (result) rq.cmd = INIT_CMD_NACK; else rq.cmd = INIT_CMD_ACK; len = write(sd, &rq, sizeof(rq)); if (len != sizeof(rq)) _d("Failed sending ACK/NACK back to client."); } leave: close(sd); }
int run(char *cmd) { int status, result, i = 0; char *args[NUM_ARGS + 1], *arg, *backup; pid_t pid; /* We must create a copy that is possible to modify. */ backup = arg = strdup(cmd); if (!arg) return 1; /* Failed allocating a string to be modified. */ /* Split command line into tokens of an argv[] array. */ args[i++] = strsep(&arg, "\t "); while (arg && i < NUM_ARGS) { /* Handle run("su -c \"dbus-daemon --system\" messagebus"); * => "su", "-c", "\"dbus-daemon --system\"", "messagebus" */ if (*arg == '\'' || *arg == '"') { char *p, delim[2] = " "; delim[0] = arg[0]; args[i++] = arg++; strsep(&arg, delim); p = arg - 1; *p = *delim; *arg++ = 0; } else { args[i++] = strsep(&arg, "\t "); } } args[i] = NULL; #if 0 _e("Splitting: '%s' =>", cmd); for (i = 0; args[i]; i++) _e("\t%s", args[i]); #endif if (i == NUM_ARGS && args[i]) { _e("Command too long: %s", cmd); free(backup); errno = EOVERFLOW; return 1; } pid = fork(); if (0 == pid) { int i; FILE *fp; struct sigaction sa; /* Reset signal handlers that were set by the parent process */ for (i = 1; i < NSIG; i++) DFLSIG(sa, i, 0); /* Always redirect stdio for run() */ fp = fopen("/dev/null", "w"); if (fp) { int fd = fileno(fp); dup2(fd, STDIN_FILENO); dup2(fd, STDOUT_FILENO); dup2(fd, STDERR_FILENO); } sig_unblock(); execvp(args[0], args); _exit(1); /* Only if execv() fails. */ } else if (-1 == pid) { _pe("%s", args[0]); free(backup); return -1; } status = complete(args[0], pid); if (-1 == status) { free(backup); return 1; } result = WEXITSTATUS(status); if (WIFEXITED(status)) { _d("Started %s and ended OK: %d", args[0], result); } else if (WIFSIGNALED(status)) { _d("Process %s terminated by signal %d", args[0], WTERMSIG(status)); if (!result) result = 1; /* Must alert callee that the command did complete successfully. * This is necessary since not all programs trap signals and * change their return code accordingly. --Jocke */ } free(backup); return result; }
pid_t run_getty(char *cmd, char *args[], int console) { pid_t pid = fork(); if (!pid) { int fd; char c; if (console) { /* Detach from initial controlling TTY */ vhangup(); close(STDERR_FILENO); close(STDOUT_FILENO); close(STDIN_FILENO); /* Attach TTY to console */ fd = open(CONSOLE, O_RDWR); if (fd != STDIN_FILENO) exit(1); dup2(0, STDIN_FILENO); dup2(0, STDOUT_FILENO); dup2(0, STDERR_FILENO); prctl(PR_SET_NAME, "console", 0, 0, 0); } sig_unblock(); setsid(); if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) < 0) _pe("Failed TIOCSCTTY"); while (!fexist(SYNC_SHUTDOWN)) { static const char msg[] = "\nPlease press Enter to activate this console."; if (fexist(SYNC_STOPPED)) { sleep(1); continue; } if (console) { (void)write(STDERR_FILENO, msg, sizeof(msg)); while (read(STDIN_FILENO, &c, 1) == 1 && c != '\n') continue; } if (fexist(SYNC_STOPPED)) continue; execv(cmd, args); } if (console) close(fd); exit(0); } return pid; }
/** * service_enabled - Should the service run? * @svc: Pointer to &svc_t object * @event: Dynamic event, opaque flag passed to callback * @arg: Event argument, used only by external service plugins. * * This method calls an associated service callback, if registered by a * plugin, and returns the &svc_cmd_t status. If no plugin is registered * the service is statically enabled in /etc/finit.conf and the result * will always be %SVC_START. * * Returns: * Either one of %SVC_START, %SVC_STOP, %SVC_RELOAD. */ svc_cmd_t service_enabled(svc_t *svc, int event, void *arg) { svc_cmd_t cmd = SVC_START; /* Default to start, since listed in finit.conf */ if (!svc) { errno = EINVAL; return SVC_STOP; } if (!svc_in_runlevel(svc, runlevel)) return SVC_STOP; /* * Event conditions for services are ignored during bootstrap. */ _d("Checking %s runlevel %d and events %s", svc->cmd, runlevel, svc->events); if (runlevel && !event_service_cond(svc->events)) return SVC_STOP; if (svc->state == SVC_RELOAD_STATE) cmd = SVC_RELOAD; if (svc->state == SVC_PAUSED_STATE) cmd = SVC_STOP; /* Is there a service plugin registered? */ if (svc->cb) { int status; pid_t pid; /* Let callback run in separate process so it doesn't crash PID 1 */ pid = fork(); if (-1 == pid) { _pe("Failed in %s callback", svc->cmd); return SVC_STOP; } if (!pid) _exit(svc->cb(svc, event, arg)); if (waitpid(pid, &status, 0) == -1) { _pe("Failed reading status from %s callback", svc->cmd); return SVC_STOP; } /* Callback normally exits here. */ if (WIFEXITED(status)) { svc_cmd_t tmp = WEXITSTATUS(status); return tmp == SVC_START ? cmd : tmp; } /* Check for SEGFAULT or other error ... */ if (WIFSIGNALED(status) && WCOREDUMP(status)) _e("Callback to %s crashed!\n", svc->cmd); else _e("Callback to %s did not exit normally!\n", svc->cmd); return SVC_STOP; } _d("%s => %s", svc->cmd, (cmd == SVC_START ? "SVC_START" : (cmd == SVC_RELOAD ? "SVC_RELOAD" : "SVC_STOP"))); return cmd; }