From owner-p4-projects@FreeBSD.ORG Tue Nov 1 10:29:34 2005 Return-Path: X-Original-To: p4-projects@freebsd.org Delivered-To: p4-projects@freebsd.org Received: by hub.freebsd.org (Postfix, from userid 32767) id 5DB3516A421; Tue, 1 Nov 2005 10:29:33 +0000 (GMT) X-Original-To: perforce@freebsd.org Delivered-To: perforce@freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id 31B4D16A41F for ; Tue, 1 Nov 2005 10:29:33 +0000 (GMT) (envelope-from soc-tyler@freebsd.org) Received: from repoman.freebsd.org (repoman.freebsd.org [216.136.204.115]) by mx1.FreeBSD.org (Postfix) with ESMTP id D49A343D45 for ; Tue, 1 Nov 2005 10:29:32 +0000 (GMT) (envelope-from soc-tyler@freebsd.org) Received: from repoman.freebsd.org (localhost [127.0.0.1]) by repoman.freebsd.org (8.13.1/8.13.1) with ESMTP id jA1ATWve043276 for ; Tue, 1 Nov 2005 10:29:32 GMT (envelope-from soc-tyler@freebsd.org) Received: (from perforce@localhost) by repoman.freebsd.org (8.13.1/8.13.1/Submit) id jA1ATWtq043273 for perforce@freebsd.org; Tue, 1 Nov 2005 10:29:32 GMT (envelope-from soc-tyler@freebsd.org) Date: Tue, 1 Nov 2005 10:29:32 GMT Message-Id: <200511011029.jA1ATWtq043273@repoman.freebsd.org> X-Authentication-Warning: repoman.freebsd.org: perforce set sender to soc-tyler@freebsd.org using -f From: soc-tyler To: Perforce Change Reviews Cc: Subject: PERFORCE change 86162 for review X-BeenThere: p4-projects@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: p4 projects tree changes List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 01 Nov 2005 10:29:34 -0000 http://perforce.freebsd.org/chv.cgi?CH=86162 Change 86162 by soc-tyler@soc-tyler_launchd on 2005/11/01 10:28:46 Start merging changes from most recent tarball from zarzycki@ (launchd-117.tar.gz to be linked to from wiki) Untested as of yet, merging changes as close to Darwin's launchd as to help backward compatibility and future vendor source code updates Affected files ... .. //depot/projects/soc2005/launchd/includes/launch.h#14 edit .. //depot/projects/soc2005/launchd/includes/launch_priv.h#4 edit .. //depot/projects/soc2005/launchd/includes/launchd.h#11 edit .. //depot/projects/soc2005/launchd/includes/launchd_core_logic.h#1 add .. //depot/projects/soc2005/launchd/includes/launchd_unix_ipc.h#1 add .. //depot/projects/soc2005/launchd/includes/pathnames.h#3 edit .. //depot/projects/soc2005/launchd/launchd.c#25 edit .. //depot/projects/soc2005/launchd/launchd_core_logic.c#1 add .. //depot/projects/soc2005/launchd/launchd_unix_ipc.c#1 add .. //depot/projects/soc2005/launchd/liblaunch.c#16 edit Differences ... ==== //depot/projects/soc2005/launchd/includes/launch.h#14 (text+ko) ==== ==== //depot/projects/soc2005/launchd/includes/launch_priv.h#4 (text+ko) ==== @@ -25,12 +25,15 @@ #include +#define LAUNCH_JOBKEY_FIRSTBORN "FirstBorn" + #define LAUNCH_KEY_GETUSERENVIRONMENT "GetUserEnvironment" #define LAUNCH_KEY_SETUSERENVIRONMENT "SetUserEnvironment" #define LAUNCH_KEY_UNSETUSERENVIRONMENT "UnsetUserEnvironment" #define LAUNCH_KEY_SETSTDOUT "SetStandardOut" #define LAUNCH_KEY_SETSTDERR "SetStandardError" -#define LAUNCH_KEY_SHUTDOWN "Shutdown" +#define LAUNCH_KEY_SHUTDOWN "Shutdown" +#define LAUNCH_KEY_SINGLEUSER "SingleUser" #define LAUNCH_KEY_GETRESOURCELIMITS "GetResourceLimits" #define LAUNCH_KEY_SETRESOURCELIMITS "SetResourceLimits" #define LAUNCH_KEY_RELOADTTYS "ReloadTTYS" ==== //depot/projects/soc2005/launchd/includes/launchd.h#11 (text+ko) ==== @@ -35,13 +35,31 @@ #include #include +/* + * Use launchd_assumes() when we can recover, even if it means we leak or limp along. + * + * Use launchd_assert() for core initialization routines. + */ +#define launchd_assumes(e) \ + (__builtin_expect(!(e), 0) ? syslog(LOG_NOTICE, "Please file a bug report: %s:%u in %s(): (%s) == %u", __FILE__, __LINE__, __func__, #e, errno), false : true) + +#define launchd_assert(e) launchd_assumes(e) ? true : abort(); + +#define PID1_REAP_ADOPTED_CHILDREN + +struct kevent; +struct conncb; + typedef void (*kq_callback)(void *, struct kevent *); extern kq_callback kqsimple_zombie_reaper; -extern sigset_t blocked_signals; +extern uint32_t blocked_signals; +extern bool shutdown_in_progress; +extern int batch_disabler_count; #ifdef PID1_REAP_ADOPTED_CHILDREN extern int pid1_child_exit_status; +bool init_check_pid(pid_t); #endif int kevent_mod(uintptr_t ident, short filter, u_short flags, u_int fflags, intptr_t data, void *udata); @@ -54,9 +72,19 @@ void clean_ttys(void); void catatonia(void); void death(void); -/* */ + +void batch_job_enable(bool e, struct conncb *c); + +launch_data_t launchd_setstdio(int d, launch_data_t o); +void launchd_SessionCreate(void); +void launchd_shutdown(void); +void launchd_single_user(void); +pid_t launchd_fork(void); +pid_t launchd_ws_fork(void); +int _fd(int fd); #ifdef _BUILD_DARWIN_ +boolean_t launchd_mach_ipc_demux(mach_msg_header_t *Request, mach_msg_header_t *Reply); extern mach_port_t launchd_bootstrap_port; void launchd_SessionCreate(const char *who); #endif ==== //depot/projects/soc2005/launchd/includes/pathnames.h#3 (text+ko) ==== ==== //depot/projects/soc2005/launchd/launchd.c#25 (text+ko) ==== @@ -32,13 +32,7 @@ #include #include #include - -#ifdef EVFILT_MACH_IMPLEMENTED -#include -#include -#endif #endif - #include #include #include @@ -56,226 +50,143 @@ #include #include #include +#include #include -#include #include #include #include #include #include +#include #include #include #include #include #include #include -#include -#include #include #include #include +#include #include #include +#include #include "launch.h" #include "launch_priv.h" #include "launchd.h" - -#ifdef _BUILD_DARWIN_ -#include "bootstrap_internal.h" -#endif +#include "launchd_core_logic.h" +#include "launchd_unix_ipc.h" -#define LAUNCHD_MIN_JOB_RUN_TIME 10 -#define LAUNCHD_REWARD_JOB_RUN_TIME 60 -#define LAUNCHD_FAILED_EXITS_THRESHOLD 10 #define PID1LAUNCHD_CONF "/etc/launchd.conf" #define LAUNCHD_CONF ".launchd.conf" - -#ifdef _BUILD_DARWIN_ +#ifndef LAUNCHCTL_PATH +#define LAUNCHCTL_PATH "/bin/launchctl" +#endif #define SECURITY_LIB "/System/Library/Frameworks/Security.framework/Versions/A/Security" #define VOLFSDIR "/.vol" -#endif extern char **environ; -//! launchd's job callback datastruct -/*! - * this structure contains all the necessary data for one of the job callbacks - * such as the PID, the kqueue callback (kq_callback) - */ -struct jobcb { - kq_callback kqjob_callback; - TAILQ_ENTRY(jobcb) tqe; - launch_data_t ldj; - pid_t p; - int last_exit_status; - int execfd; - time_t start_time; - size_t failed_exits; - int *vnodes; - size_t vnodes_cnt; - int *qdirs; - size_t qdirs_cnt; - unsigned int start_interval; - struct tm *start_cal_interval; - unsigned int checkedin:1, firstborn:1, debug:1, throttle:1, futureflags:28; - char label[0]; -}; - -//! launchd's socket connection callback -/*! - * this structure contains the basic data needed for our unix socket - * connections that we create to communicate with launchctl via - * "liblaunch" - */ -struct conncb { - kq_callback kqconn_callback; - TAILQ_ENTRY(conncb) tqe; - launch_t conn; - struct jobcb *j; - int disabled_batch:1, futureflags:31; -}; - -static TAILQ_HEAD(jobcbhead, jobcb) jobs = TAILQ_HEAD_INITIALIZER(jobs); -static TAILQ_HEAD(conncbhead, conncb) connections = TAILQ_HEAD_INITIALIZER(connections); -static int mainkq = 0; -static int asynckq = 0; -static int batch_disabler_count = 0; - -static launch_data_t load_job(launch_data_t pload); -static launch_data_t get_jobs(const char *which); -static launch_data_t setstdio(int d, launch_data_t o); -static launch_data_t adjust_rlimits(launch_data_t in); -static void batch_job_enable(bool e, struct conncb *c); -static void do_shutdown(void); - -static void listen_callback(void *, struct kevent *); static void async_callback(void); static void signal_callback(void *, struct kevent *); static void fs_callback(void); -static void simple_zombie_reaper(void *, struct kevent *); static void readcfg_callback(void *, struct kevent *); -static kq_callback kqlisten_callback = listen_callback; static kq_callback kqasync_callback = (kq_callback)async_callback; static kq_callback kqsignal_callback = signal_callback; static kq_callback kqfs_callback = (kq_callback)fs_callback; static kq_callback kqreadcfg_callback = readcfg_callback; -kq_callback kqsimple_zombie_reaper = simple_zombie_reaper; - -static void job_watch(struct jobcb *j); -static void job_ignore(struct jobcb *j); -static void job_start(struct jobcb *j); -static void job_start_child(struct jobcb *j, int execfd); -static void job_setup_attributes(struct jobcb *j); -static void job_stop(struct jobcb *j); -static void job_reap(struct jobcb *j); -static void job_remove(struct jobcb *j); -static void job_set_alarm(struct jobcb *j); -static launch_data_t job_export(struct jobcb *j); -static void job_callback(void *obj, struct kevent *kev); -static void job_log(struct jobcb *j, int pri, const char *msg, ...) __attribute__((format(printf, 3, 4))); -static void job_log_error(struct jobcb *j, int pri, const char *msg, ...) __attribute__((format(printf, 3, 4))); - -static void ipc_open(int fd, struct jobcb *j); -static void ipc_close(struct conncb *c); -static void ipc_callback(void *, struct kevent *); -static void ipc_readmsg(launch_data_t msg, void *context); -static void ipc_readmsg2(launch_data_t data, const char *cmd, void *context); +static kq_callback kqshutdown_callback = (kq_callback)launchd_shutdown; #ifdef PID1_REAP_ADOPTED_CHILDREN static void pid1waitpid(void); -static bool launchd_check_pid(pid_t p); #endif static void pid1_magic_init(bool sflag, bool vflag, bool xflag); -static void launchd_server_init(bool create_session); -static void conceive_firstborn(char *argv[]); +static struct jobcb *conceive_firstborn(char *argv[], const char *session_user); static void usage(FILE *where); -static int _fd(int fd); static void loopback_setup(void); +static void workaround3048875(int argc, char *argv[]); +static void testfd_or_openfd(int fd, const char *path, int flags); static void reload_launchd_config(void); -static int dir_has_files(const char *path); -static void setup_job_env(launch_data_t obj, const char *key, void *context); -static void unsetup_job_env(launch_data_t obj, const char *key, void *context); - -static size_t total_children = 0; +static int mainkq = 0; +static int asynckq = 0; static pid_t readcfg_pid = 0; -static pid_t launchd_proper_pid = 0; -static bool launchd_inited = false; -static bool shutdown_in_progress = false; +static bool re_exec_in_single_user_mode = false; +static char *pending_stdout = NULL; +static char *pending_stderr = NULL; +static struct jobcb *fbj = NULL; -sigset_t blocked_signals; - +/* Darwin's sigset_t datatype is a simple uint32_t */ #ifdef _BUILD_DARWIN_ - -#ifdef EVFILT_MACH_IMPLEMENTED -static void *mach_demand_loop(void *); -static void mach_callback(void *, struct kevent *); -static kq_callback kqmach_callback = mach_callback; +sigset_t blocked_signals = 0; +#else +/* FreeBSD's is a struct */ +uint32_t blocked_signals = 0; #endif +bool shutdown_in_progress = false; +int batch_disabler_count = 0; -// workaround for an OpenFirmware and Darwin kernel bug -static void workaround3048875(int argc, char *argv[]); -static pthread_t mach_server_loop_thread; -mach_port_t launchd_bootstrap_port = MACH_PORT_NULL; -blocked_signals = 0; -#endif - -static char *pending_stdout = NULL; -static char *pending_stderr = NULL; - -int main(int argc, char *argv[]) { +int main(int argc, char *argv[]) +{ static const int sigigns[] = { SIGHUP, SIGINT, SIGPIPE, SIGALRM, SIGTERM, SIGURG, SIGTSTP, SIGTSTP, SIGCONT, /*SIGCHLD,*/ SIGTTIN, SIGTTOU, SIGIO, SIGXCPU, SIGXFSZ, SIGVTALRM, SIGPROF, - SIGWINCH, SIGINFO, SIGUSR1, SIGUSR2 }; - void testfd_or_openfd(int fd, const char *path, int flags) { - int tmpfd; - - if (-1 != (tmpfd = dup(fd))) { - close(tmpfd); - } else { - if (-1 == (tmpfd = open(path, flags))) { - syslog(LOG_ERR, "open(\"%s\", ...): %m", path); - } else if (tmpfd != fd) { - dup2(tmpfd, fd); - close(tmpfd); - } - } + SIGWINCH, SIGINFO, SIGUSR1, SIGUSR2 }; + bool sflag = false, xflag = false, vflag = false, dflag = false; + const char *session_type = NULL; + const char *session_user = NULL; + const char *optargs = NULL; struct kevent kev; size_t i; - bool sflag = false, xflag = false, vflag = false, dflag = false; - int ch; + int ch, ker; + + /* main() phase one: sanitize the process */ -// workaround for that nasty OpenFirmware bug, zoinks! -#ifdef _BUILD_DARWIN_ - if (getpid() == 1) + if (getpid() == 1) { workaround3048875(argc, argv); -#endif + } else { + int sigi, fdi, dts = getdtablesize(); + sigset_t emptyset; - setegid(getgid()); - seteuid(getuid()); + for (fdi = STDERR_FILENO + 1; fdi < dts; fdi++) + launchd_assumes(close(fdi) == 0); + for (sigi = 1; sigi < NSIG; sigi++) + launchd_assumes(signal(sigi, SIG_DFL) != SIG_ERR); + sigemptyset(&emptyset); + launchd_assumes(sigprocmask(SIG_SETMASK, &emptyset, NULL) == 0); + } testfd_or_openfd(STDIN_FILENO, _PATH_DEVNULL, O_RDONLY); testfd_or_openfd(STDOUT_FILENO, _PATH_DEVNULL, O_WRONLY); testfd_or_openfd(STDERR_FILENO, _PATH_DEVNULL, O_WRONLY); - openlog(getprogname(), LOG_CONS|(getpid() != 1 ? LOG_PID|LOG_PERROR : 0), LOG_LAUNCHD); - setlogmask(LOG_UPTO(LOG_NOTICE)); - - while ((ch = getopt(argc, argv, "dhsvx")) != -1) { + /* main phase two: parse arguments */ + + if (getpid() == 1) { + optargs = "svx"; + } else if (getuid() == 0) { + optargs = "S:U:dh"; + } else { + optargs = "dh"; + } + + while ((ch = getopt(argc, argv, optargs)) != -1) { switch (ch) { - case 'd': dflag = true; break; - case 's': sflag = true; break; - case 'x': xflag = true; break; - case 'v': vflag = true; break; - case 'h': usage(stdout); break; + case 'S': session_type = optarg; break; /* what type of session we're creating */ + case 'U': session_user = optarg; break; /* which user to create a session as */ + case 'd': dflag = true; break; /* daemonize */ + case 's': sflag = true; break; /* single user */ + case 'x': xflag = true; break; /* safe boot */ + case 'v': vflag = true; break; /* verbose boot */ + case 'h': usage(stdout); break; /* help */ + case '?': /* we should do something with the global optopt variable here */ default: - syslog(LOG_WARNING, "ignoring unknown arguments"); + fprintf(stderr, "ignoring unknown arguments\n"); usage(stderr); break; } @@ -283,121 +194,149 @@ argc -= optind; argv += optind; - if (dflag && daemon(0, 0) == -1) - syslog(LOG_WARNING, "couldn't daemonize: %m"); + if ((session_type && !session_user) || (!session_user && session_type)) { + fprintf(stderr, "-S and -U must be used together\n"); + exit(EXIT_FAILURE); + } + + /* main phase three: if we need to become a user, do so ASAP */ + + if (session_user) { + struct passwd *pwe = getpwnam(session_user); + uid_t u = pwe ? pwe->pw_uid : 0; + gid_t g = pwe ? pwe->pw_gid : 0; + + if (pwe == NULL) { + fprintf(stderr, "lookup of user %s failed!\n", session_user); + exit(EXIT_FAILURE); + } + + launchd_assert(initgroups(session_user, g) != -1); + + launchd_assert(setgid(g) != -1); - if ((mainkq = kqueue()) == -1) { - syslog(LOG_EMERG, "kqueue(): %m"); - abort(); + launchd_assert(setuid(u) != -1); } - if ((asynckq = kqueue()) == -1) { - syslog(LOG_ERR, "kqueue(): %m"); - abort(); - } + /* main phase four: get the party started */ + + if (dflag) + launchd_assumes(daemon(0, 0) == 0); + + openlog(getprogname(), LOG_CONS|(getpid() != 1 ? LOG_PID|LOG_PERROR : 0), LOG_LAUNCHD); + setlogmask(LOG_UPTO(LOG_NOTICE)); + + launchd_assert((mainkq = kqueue()) != -1); + + launchd_assert((asynckq = kqueue()) != -1); - if (kevent_mod(asynckq, EVFILT_READ, EV_ADD, 0, 0, &kqasync_callback) == -1) { - syslog(LOG_ERR, "kevent_mod(asynckq, EVFILT_READ): %m"); - abort(); - } + launchd_assert(kevent_mod(asynckq, EVFILT_READ, EV_ADD, 0, 0, &kqasync_callback) != -1); sigemptyset(&blocked_signals); for (i = 0; i < (sizeof(sigigns) / sizeof(int)); i++) { - if (kevent_mod(sigigns[i], EVFILT_SIGNAL, EV_ADD, 0, 0, &kqsignal_callback) == -1) - syslog(LOG_ERR, "failed to add kevent for signal: %d: %m", sigigns[i]); + launchd_assumes(kevent_mod(sigigns[i], EVFILT_SIGNAL, EV_ADD, 0, 0, &kqsignal_callback) != -1); sigaddset(&blocked_signals, sigigns[i]); - signal(sigigns[i], SIG_IGN); + launchd_assumes(signal(sigigns[i], SIG_IGN) != SIG_ERR); } /* sigh... ignoring SIGCHLD has side effects: we can't call wait*() */ - if (kevent_mod(SIGCHLD, EVFILT_SIGNAL, EV_ADD, 0, 0, &kqsignal_callback) == -1) - syslog(LOG_ERR, "failed to add kevent for signal: %d: %m", SIGCHLD); - + launchd_assert(kevent_mod(SIGCHLD, EVFILT_SIGNAL, EV_ADD, 0, 0, &kqsignal_callback) != -1); + +#ifdef _BUILD_DARWIN_ + mach_init_init(); +#endif + + if (argv[0] || (session_type != NULL && 0 == strcasecmp(session_type, "tty"))) + fbj = conceive_firstborn(argv, session_user); + + if (NULL == getenv("PATH")) + setenv("PATH", _PATH_STDPATH, 1); + if (getpid() == 1) { pid1_magic_init(sflag, vflag, xflag); } else { -#ifdef _BUILD_DARWIN_ - launchd_bootstrap_port = bootstrap_port; -#endif - /* - * launchd_server_init() is called now if we're not assuming PID 1, - * i.e. all filesystems are mounted and marked rw; otherwise, we call - * launchd_server_init() from fs_callback() - */ - launchd_server_init(argv[0] ? true : false); + ipc_server_init(); } - /* this registers for the kqfs_callback (fs_callback()) function to be - * called when the mount table is updated, so we can call some PID 1 stuff */ - /* do this after pid1_magic_init() to not catch ourselves mounting stuff */ - if (kevent_mod(0, EVFILT_FS, EV_ADD, 0, 0, &kqfs_callback) == -1) - syslog(LOG_ERR, "kevent_mod(EVFILT_FS, &kqfs_callback): %m"); + launchd_assumes(kevent_mod(0, EVFILT_FS, EV_ADD, 0, 0, &kqfs_callback) != -1); + + if (session_type) { + pid_t pp = getppid(); + + /* As a per session launchd, we need to exit if our parent dies. + * + * Normally, in Unix, SIGHUP would cause us to exit, but we're a + * daemon, and daemons use SIGHUP to signal the need to reread + * configuration files. "Weee." + */ + + if (pp == 1) + exit(EXIT_SUCCESS); + ker = kevent_mod(pp, EVFILT_PROC, EV_ADD, 0, 0, &kqshutdown_callback); - if (argv[0]) - conceive_firstborn(argv); + if (ker == -1) + exit(launchd_assumes(errno == ESRCH) ? EXIT_SUCCESS : EXIT_FAILURE); + } reload_launchd_config(); - if (argv[0]) - job_start(TAILQ_FIRST(&jobs)); + if (fbj) + job_start(fbj); for (;;) { - static struct timespec timeout = { 30, 0 }; - struct timespec *timeoutp = NULL; + if (getpid() == 1 && readcfg_pid == 0) + init_pre_kevent(); + + if (shutdown_in_progress && total_children == 0) { + job_remove_all(); +#ifdef _BUILD_DARWIN_ + mach_init_reap(); +#endif + + shutdown_in_progress = false; - if (getpid() == 1) { - if (readcfg_pid == 0) - init_pre_kevent(); - } else { - /* in theory, this will make sure we don't exit if we - * have (a) any more jobs nd (b) open socket connections - * to say, something like, launchctl? ;) - */ - if (TAILQ_EMPTY(&jobs) && TAILQ_EMPTY(&connections)) { - /* liblaunch will restart us if we're needed again */ - timeoutp = &timeout; - } else if (shutdown_in_progress && total_children == 0) { + if (getpid() != 1) { exit(EXIT_SUCCESS); + } else if (re_exec_in_single_user_mode) { + re_exec_in_single_user_mode = false; + launchd_assumes(execl("/sbin/launchd", "/sbin/launchd", "-s", NULL) != -1); } } - - switch (kevent(mainkq, NULL, 0, &kev, 1, timeoutp)) { - case -1: - syslog(LOG_DEBUG, "kevent(): %m"); - break; - case 1: - (*((kq_callback *)kev.udata))(kev.udata, &kev); - break; - case 0: - /* we exit here if and once we're done processing all jobs - * assigned to us - */ - if (timeoutp) - exit(EXIT_SUCCESS); - else - syslog(LOG_DEBUG, "kevent(): spurious return with infinite timeout"); - break; - default: - syslog(LOG_DEBUG, "unexpected: kevent() returned something != 0, -1 or 1"); - break; - } + + if (launchd_assumes(kevent(mainkq, NULL, 0, &kev, 1, NULL) == 1)) + (*((kq_callback *)kev.udata))(kev.udata, &kev); } } static void pid1_magic_init(bool sflag, bool vflag, bool xflag) { - int memmib[2] = { CTL_HW, HW_PHYSMEM }; +#ifdef _BUILD_DARWIN_ + int memmib[2] = { CTL_HW, HW_MEMSIZE }; +#else + int memmib[2] = { CTL_HW, HW_REALMEM }; +#endif int mvnmib[2] = { CTL_KERN, KERN_MAXVNODES }; int hnmib[2] = { CTL_KERN, KERN_HOSTNAME }; uint64_t mem = 0; uint32_t mvn; size_t memsz = sizeof(mem); -#ifdef _BUILD_DARWIN_ - pthread_attr_t attr; - int pthr_r; +#ifdef KERN_TFP + struct group *tfp_gr; + + if (launchd_assumes((tfp_gr = getgrnam("procview")) != NULL)) { + int tfp_r_mib[3] = { CTL_KERN, KERN_TFP, KERN_TFP_READ_GROUP }; + gid_t tfp_r_gid = tfp_gr->gr_gid; + launchd_assumes(sysctl(tfp_r_mib, 3, NULL, NULL, &tfp_r_gid, sizeof(tfp_r_gid)) != -1); + } + + if (launchd_assumes((tfp_gr = getgrnam("procmod")) != NULL)) { + int tfp_rw_mib[3] = { CTL_KERN, KERN_TFP, KERN_TFP_RW_GROUP }; + gid_t tfp_rw_gid = tfp_gr->gr_gid; + launchd_assumes(sysctl(tfp_rw_mib, 3, NULL, NULL, &tfp_rw_gid, sizeof(tfp_rw_gid)) != -1); + } #endif setpriority(PRIO_PROCESS, 0, -1); @@ -411,1547 +350,141 @@ if (sysctl(memmib, 2, &mem, &memsz, NULL, 0) == -1) { syslog(LOG_WARNING, "sysctl(\"%s\"): %m", "hw.physmem"); } else { - /* The following assignment of mem to itself if the size - * of data returned is 32 bits instead of 64 is a clever - * C trick to move the 32 bits on big endian systems to - * the least significant bytes of the 64 mem variable. - * - * On little endian systems, this is effectively a no-op. - */ - if (memsz == 4) - mem = *(uint32_t *)&mem; mvn = mem / (64 * 1024) + 1024; - - // Performance note: change the max # of vnodes if (sysctl(mvnmib, 2, NULL, NULL, &mvn, sizeof(mvn)) == -1) syslog(LOG_WARNING, "sysctl(\"%s\"): %m", "kern.maxvnodes"); } - - // Performance note: set our hostname to localhost for now if (sysctl(hnmib, 2, NULL, NULL, "localhost", sizeof("localhost")) == -1) syslog(LOG_WARNING, "sysctl(\"%s\"): %m", "kern.hostname"); if (setlogin("root") == -1) syslog(LOG_ERR, "setlogin(\"root\"): %m"); - - // Performance note: setup our loopback interface inline + loopback_setup(); if (mount("fdesc", "/dev", MNT_UNION, NULL) == -1) syslog(LOG_ERR, "mount(\"%s\", \"%s\", ...): %m", "fdesc", "/dev/"); - setenv("PATH", _PATH_STDPATH, 1); -#ifdef _BUILD_DARWIN_ - launchd_bootstrap_port = mach_init_init(); - task_set_bootstrap_port(mach_task_self(), launchd_bootstrap_port); - bootstrap_port = MACH_PORT_NULL; - - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - - pthr_r = pthread_create(&mach_server_loop_thread, &attr, mach_server_loop, NULL); - if (pthr_r != 0) { - syslog(LOG_ERR, "pthread_create(mach_server_loop): %s", strerror(pthr_r)); - exit(EXIT_FAILURE); - } - - pthread_attr_destroy(&attr); -#endif - init_boot(sflag, vflag, xflag); } -#ifdef PID1_REAP_ADOPTED_CHILDREN -static bool launchd_check_pid(pid_t p) -{ - struct kevent kev; - struct jobcb *j; - TAILQ_FOREACH(j, &jobs, tqe) { - if (j->p == p) { - EV_SET(&kev, p, EVFILT_PROC, 0, 0, 0, j); - j->kqjob_callback(j, &kev); - return true; - } - } - - if (p == readcfg_pid) { - readcfg_callback(NULL, NULL); - return true; - } - - return false; -} -#endif - -static char *sockdir = NULL; -static char *sockpath = NULL; - -static void launchd_clean_up(void) +void usage(FILE *where) { - seteuid(0); - setegid(0); + const char *opts = "[-d]"; - if (-1 == unlink(sockpath)) - syslog(LOG_WARNING, "unlink(\"%s\"): %m", sockpath); - else if (-1 == rmdir(sockdir)) - syslog(LOG_WARNING, "rmdir(\"%s\"): %m", sockdir); + if (getuid() == 0) + opts = "[-d] [-S -U ]"; - setegid(getgid()); - seteuid(getuid()); -} + fprintf(where, "%s: %s [-- command [args ...]]\n", getprogname(), opts); -static void launchd_server_init(bool create_session) -{ - struct sockaddr_un sun; - mode_t oldmask; - int r, fd = -1, ourdirfd = -1; - char ourdir[1024]; + fprintf(where, "\t-d Daemonize.\n"); + fprintf(where, "\t-h This usage statement.\n"); - memset(&sun, 0, sizeof(sun)); - sun.sun_family = AF_UNIX; - - if (create_session) { - snprintf(ourdir, sizeof(ourdir), "%s/%u.%u", LAUNCHD_SOCK_PREFIX, getuid(), getpid()); - snprintf(sun.sun_path, sizeof(sun.sun_path), "%s/%u.%u/sock", LAUNCHD_SOCK_PREFIX, getuid(), getpid()); - setenv(LAUNCHD_SOCKET_ENV, sun.sun_path, 1); - } else { - snprintf(ourdir, sizeof(ourdir), "%s/%u", LAUNCHD_SOCK_PREFIX, getuid()); - snprintf(sun.sun_path, sizeof(sun.sun_path), "%s/%u/sock", LAUNCHD_SOCK_PREFIX, getuid()); - } - - seteuid(0); - setegid(0); - - if (mkdir(LAUNCHD_SOCK_PREFIX, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) == -1) { - if (errno == EROFS) { - goto out_bad; - } else if (errno == EEXIST) { - struct stat sb; - stat(LAUNCHD_SOCK_PREFIX, &sb); - if (!S_ISDIR(sb.st_mode)) { - errno = EEXIST; - syslog(LOG_ERR, "mkdir(\"%s\"): %m", LAUNCHD_SOCK_PREFIX); - goto out_bad; - } - } else { - syslog(LOG_ERR, "mkdir(\"%s\"): %m", LAUNCHD_SOCK_PREFIX); - goto out_bad; - } + if (getuid() == 0) { + fprintf(where, "\t-S What type of session to create (Aqua, tty or X11).\n"); + fprintf(where, "\t-U Which user to create the session as.\n"); } - unlink(ourdir); - if (mkdir(ourdir, S_IRWXU) == -1) { - if (errno == EROFS) { - goto out_bad; - } else if (errno == EEXIST) { - struct stat sb; - stat(ourdir, &sb); - if (!S_ISDIR(sb.st_mode)) { - errno = EEXIST; - syslog(LOG_ERR, "mkdir(\"%s\"): %m", LAUNCHD_SOCK_PREFIX); - goto out_bad; - } - } else { - syslog(LOG_ERR, "mkdir(\"%s\"): %m", ourdir); - goto out_bad; - } - } - - if (chown(ourdir, getuid(), getgid()) == -1) - syslog(LOG_WARNING, "chown(\"%s\"): %m", ourdir); - - setegid(getgid()); - seteuid(getuid()); - - ourdirfd = _fd(open(ourdir, O_RDONLY)); - if (ourdirfd == -1) { - syslog(LOG_ERR, "open(\"%s\"): %m", ourdir); - goto out_bad; - } - - if (flock(ourdirfd, LOCK_EX|LOCK_NB) == -1) { - if (errno == EWOULDBLOCK) { - exit(EXIT_SUCCESS); - } else { - syslog(LOG_ERR, "flock(\"%s\"): %m", ourdir); - goto out_bad; - } - } - - if (unlink(sun.sun_path) == -1 && errno != ENOENT) { - if (errno != EROFS) - syslog(LOG_ERR, "unlink(\"thesocket\"): %m"); - goto out_bad; - } - if ((fd = _fd(socket(AF_UNIX, SOCK_STREAM, 0))) == -1) { - syslog(LOG_ERR, "socket(\"thesocket\"): %m"); - goto out_bad; - } - oldmask = umask(077); - r = bind(fd, (struct sockaddr *)&sun, sizeof(sun)); - umask(oldmask); - if (r == -1) { - if (errno != EROFS) - syslog(LOG_ERR, "bind(\"thesocket\"): %m"); - goto out_bad; - } - - if (listen(fd, SOMAXCONN) == -1) { - syslog(LOG_ERR, "listen(\"thesocket\"): %m"); - goto out_bad; - } - - if (kevent_mod(fd, EVFILT_READ, EV_ADD, 0, 0, &kqlisten_callback) == -1) { - syslog(LOG_ERR, "kevent_mod(\"thesocket\", EVFILT_READ): %m"); - goto out_bad; - } - - launchd_inited = true; - - sockdir = strdup(ourdir); - sockpath = strdup(sun.sun_path); - - launchd_proper_pid = getpid(); - atexit(launchd_clean_up); - -out_bad: - setegid(getgid()); - seteuid(getuid()); - - if (!launchd_inited) { - if (fd != -1) - close(fd); - if (ourdirfd != -1) - close(ourdirfd); - } -} - -static long long job_get_integer(launch_data_t j, const char *key) -{ - launch_data_t t = launch_data_dict_lookup(j, key); - if (t) - return launch_data_get_integer(t); - else - return 0; -} - -static const char *job_get_string(launch_data_t j, const char *key) -{ - launch_data_t t = launch_data_dict_lookup(j, key); - if (t) - return launch_data_get_string(t); - else - return NULL; -} - -static const char *job_get_file2exec(launch_data_t j) -{ - launch_data_t tmpi, tmp = launch_data_dict_lookup(j, LAUNCH_JOBKEY_PROGRAM); - - if (tmp) { - return launch_data_get_string(tmp); - } else { - tmp = launch_data_dict_lookup(j, LAUNCH_JOBKEY_PROGRAMARGUMENTS); - if (tmp) { - tmpi = launch_data_array_get_index(tmp, 0); - if (tmpi) - return launch_data_get_string(tmpi); - } - return NULL; - } -} - -static bool job_get_bool(launch_data_t j, const char *key) -{ - launch_data_t t = launch_data_dict_lookup(j, key); - if (t) - return launch_data_get_bool(t); - else - return false; -} - -static void ipc_open(int fd, struct jobcb *j) -{ - struct conncb *c = calloc(1, sizeof(struct conncb)); - - fcntl(fd, F_SETFL, O_NONBLOCK); - - c->kqconn_callback = ipc_callback; - c->conn = launchd_fdopen(fd); - c->j = j; - TAILQ_INSERT_TAIL(&connections, c, tqe); - kevent_mod(fd, EVFILT_READ, EV_ADD, 0, 0, &c->kqconn_callback); -} - -static void simple_zombie_reaper(void *obj __attribute__((unused)), struct kevent *kev) -{ - waitpid(kev->ident, NULL, 0); -} - -static void listen_callback(void *obj __attribute__((unused)), struct kevent *kev) -{ - struct sockaddr_un sun; - socklen_t sl = sizeof(sun); - int cfd; - - if ((cfd = _fd(accept(kev->ident, (struct sockaddr *)&sun, &sl))) == -1) { - return; - } - - ipc_open(cfd, NULL); -} - -static void ipc_callback(void *obj, struct kevent *kev) -{ - struct conncb *c = obj; - int r; - - if (kev->filter == EVFILT_READ) { - if (launchd_msg_recv(c->conn, ipc_readmsg, c) == -1 && errno != EAGAIN) { - if (errno != ECONNRESET) - syslog(LOG_DEBUG, "%s(): recv: %m", __func__); - ipc_close(c); - } - } else if (kev->filter == EVFILT_WRITE) { - r = launchd_msg_send(c->conn, NULL); - if (r == -1) { - if (errno != EAGAIN) { - syslog(LOG_DEBUG, "%s(): send: %m", __func__); - ipc_close(c); - } - } else if (r == 0) { - kevent_mod(launchd_getfd(c->conn), EVFILT_WRITE, EV_DELETE, 0, 0, NULL); - } - } else { - syslog(LOG_DEBUG, "%s(): unknown filter type!", __func__); - ipc_close(c); - } -} - -static void set_user_env(launch_data_t obj, const char *key, void *context __attribute__((unused))) -{ - setenv(key, launch_data_get_string(obj), 1); -} - -static void launch_data_close_fds(launch_data_t o) -{ - size_t i; - - switch (launch_data_get_type(o)) { - case LAUNCH_DATA_DICTIONARY: - launch_data_dict_iterate(o, (void (*)(launch_data_t, const char *, void *))launch_data_close_fds, NULL); - break; - case LAUNCH_DATA_ARRAY: - for (i = 0; i < launch_data_array_get_count(o); i++) - launch_data_close_fds(launch_data_array_get_index(o, i)); - break; - case LAUNCH_DATA_FD: - if (launch_data_get_fd(o) != -1) - close(launch_data_get_fd(o)); - break; - default: - break; - } -} >>> TRUNCATED FOR MAIL (1000 lines) <<<