+
+mach_port_t
+str2bsport(const char *s)
+{
+ bool getrootbs = strcmp(s, "/") == 0;
+ mach_port_t last_bport, bport = bootstrap_port;
+ task_t task = mach_task_self();
+ kern_return_t result;
+
+ if (strcmp(s, "..") == 0 || getrootbs) {
+ do {
+ last_bport = bport;
+ result = bootstrap_parent(last_bport, &bport);
+
+ if (result == BOOTSTRAP_NOT_PRIVILEGED) {
+ fprintf(stderr, "Permission denied\n");
+ return 1;
+ } else if (result != BOOTSTRAP_SUCCESS) {
+ fprintf(stderr, "bootstrap_parent() %d\n", result);
+ return 1;
+ }
+ } while (getrootbs && last_bport != bport);
+ } else {
+ int pid = atoi(s);
+
+ result = task_for_pid(mach_task_self(), pid, &task);
+
+ if (result != KERN_SUCCESS) {
+ fprintf(stderr, "task_for_pid() %s\n", mach_error_string(result));
+ return 1;
+ }
+
+ result = task_get_bootstrap_port(task, &bport);
+
+ if (result != KERN_SUCCESS) {
+ fprintf(stderr, "Couldn't get bootstrap port: %s\n", mach_error_string(result));
+ return 1;
+ }
+ }
+
+ return bport;
+}
+
+int
+bsexec_cmd(int argc, char *const argv[])
+{
+ kern_return_t result;
+ mach_port_t bport;
+
+ if (argc < 3) {
+ fprintf(stderr, "usage: %s bsexec <PID> prog...\n", getprogname());
+ return 1;
+ }
+
+ bport = str2bsport(argv[1]);
+
+ result = task_set_bootstrap_port(mach_task_self(), bport);
+
+ if (result != KERN_SUCCESS) {
+ fprintf(stderr, "Couldn't switch to new bootstrap port: %s\n", mach_error_string(result));
+ return 1;
+ }
+
+ setgid(getgid());
+ setuid(getuid());
+
+ execvp(argv[2], argv + 2);
+ fprintf(stderr, "execvp(): %s\n", strerror(errno));
+ return 1;
+}
+
+int
+bslist_cmd(int argc, char *const argv[])
+{
+ kern_return_t result;
+ mach_port_t bport = bootstrap_port;
+ name_array_t service_names;
+ mach_msg_type_number_t service_cnt, service_active_cnt;
+ bootstrap_status_array_t service_actives;
+ unsigned int i;
+
+ if (argc == 2)
+ bport = str2bsport(argv[1]);
+
+ if (bport == MACH_PORT_NULL) {
+ fprintf(stderr, "Invalid bootstrap port\n");
+ return 1;
+ }
+
+ result = bootstrap_info(bport, &service_names, &service_cnt, &service_actives, &service_active_cnt);
+ if (result != BOOTSTRAP_SUCCESS) {
+ fprintf(stderr, "bootstrap_info(): %d\n", result);
+ return 1;
+ }
+
+#define bport_state(x) (((x) == BOOTSTRAP_STATUS_ACTIVE) ? "A" : ((x) == BOOTSTRAP_STATUS_ON_DEMAND) ? "D" : "I")
+
+ for (i = 0; i < service_cnt ; i++)
+ fprintf(stdout, "%-3s%s\n", bport_state((service_actives[i])), service_names[i]);
+
+ return 0;
+}
+
+bool
+is_legacy_mach_job(launch_data_t obj)
+{
+ bool has_servicename = launch_data_dict_lookup(obj, MACHINIT_JOBKEY_SERVICENAME);
+ bool has_command = launch_data_dict_lookup(obj, MACHINIT_JOBKEY_COMMAND);
+ bool has_label = launch_data_dict_lookup(obj, LAUNCH_JOBKEY_LABEL);
+
+ return has_command && has_servicename && !has_label;
+}
+
+void
+_log_launchctl_bug(const char *rcs_rev, const char *path, unsigned int line, const char *test)
+{
+ int saved_errno = errno;
+ char buf[100];
+ const char *file = strrchr(path, '/');
+ char *rcs_rev_tmp = strchr(rcs_rev, ' ');
+
+ if (!file) {
+ file = path;
+ } else {
+ file += 1;
+ }
+
+ if (!rcs_rev_tmp) {
+ strlcpy(buf, rcs_rev, sizeof(buf));
+ } else {
+ strlcpy(buf, rcs_rev_tmp + 1, sizeof(buf));
+ rcs_rev_tmp = strchr(buf, ' ');
+ if (rcs_rev_tmp)
+ *rcs_rev_tmp = '\0';
+ }
+
+ fprintf(stderr, "Bug: %s:%u (%s):%u: %s\n", file, line, buf, saved_errno, test);
+}
+
+void
+loopback_setup_ipv4(void)
+{
+ struct ifaliasreq ifra;
+ struct ifreq ifr;
+ int s;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strcpy(ifr.ifr_name, "lo0");
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+ return;
+
+ if (assumes(ioctl(s, SIOCGIFFLAGS, &ifr) != -1)) {
+ ifr.ifr_flags |= IFF_UP;
+ assumes(ioctl(s, SIOCSIFFLAGS, &ifr) != -1);
+ }
+
+ memset(&ifra, 0, sizeof(ifra));
+ strcpy(ifra.ifra_name, "lo0");
+ ((struct sockaddr_in *)&ifra.ifra_addr)->sin_family = AF_INET;
+ ((struct sockaddr_in *)&ifra.ifra_addr)->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ ((struct sockaddr_in *)&ifra.ifra_addr)->sin_len = sizeof(struct sockaddr_in);
+ ((struct sockaddr_in *)&ifra.ifra_mask)->sin_family = AF_INET;
+ ((struct sockaddr_in *)&ifra.ifra_mask)->sin_addr.s_addr = htonl(IN_CLASSA_NET);
+ ((struct sockaddr_in *)&ifra.ifra_mask)->sin_len = sizeof(struct sockaddr_in);
+
+ assumes(ioctl(s, SIOCAIFADDR, &ifra) != -1);
+
+ assumes(close(s) == 0);
+}
+
+void
+loopback_setup_ipv6(void)
+{
+ struct in6_aliasreq ifra6;
+ struct ifreq ifr;
+ int s6;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strcpy(ifr.ifr_name, "lo0");
+
+ if ((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) == -1)
+ return;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strcpy(ifr.ifr_name, "lo0");
+
+ if (assumes(ioctl(s6, SIOCGIFFLAGS, &ifr) != -1)) {
+ ifr.ifr_flags |= IFF_UP;
+ assumes(ioctl(s6, SIOCSIFFLAGS, &ifr) != -1);
+ }
+
+ memset(&ifra6, 0, sizeof(ifra6));
+ strcpy(ifra6.ifra_name, "lo0");
+
+ ifra6.ifra_addr.sin6_family = AF_INET6;
+ ifra6.ifra_addr.sin6_addr = in6addr_loopback;
+ ifra6.ifra_addr.sin6_len = sizeof(struct sockaddr_in6);
+ ifra6.ifra_prefixmask.sin6_family = AF_INET6;
+ memset(&ifra6.ifra_prefixmask.sin6_addr, 0xff, sizeof(struct in6_addr));
+ ifra6.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
+ ifra6.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
+ ifra6.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
+
+ assumes(ioctl(s6, SIOCAIFADDR_IN6, &ifra6) != -1);
+
+ assumes(close(s6) == 0);
+}
+
+pid_t
+fwexec(const char *const *argv, bool _wait)
+{
+ int wstatus;
+ pid_t p;
+
+ switch ((p = fork())) {
+ case -1:
+ break;
+ case 0:
+ setsid();
+ execvp(argv[0], (char *const *)argv);
+ _exit(EXIT_FAILURE);
+ break;
+ default:
+ if (!_wait)
+ return p;
+ if (p == waitpid(p, &wstatus, 0)) {
+ if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == EXIT_SUCCESS)
+ return p;
+ }
+ break;
+ }
+
+ return -1;
+}
+
+void
+do_potential_fsck(void)
+{
+ const char *safe_fsck_tool[] = { "fsck", "-fy", NULL };
+ const char *fsck_tool[] = { "fsck", "-p", NULL };
+ const char *remount_tool[] = { "mount", "-uw", "/", NULL };
+ struct statfs sfs;
+
+ if (assumes(statfs("/", &sfs) != -1)) {
+ if (!(sfs.f_flags & MNT_RDONLY)) {
+ fprintf(stdout, "Root file system is read-write, skipping fsck.\n");
+ return;
+ }
+ }
+
+ if (!is_safeboot()) {
+ if (fwexec(fsck_tool, true) != -1)
+ goto out;
+ }
+
+ if (fwexec(safe_fsck_tool, true) != -1) {
+ goto out;
+ }
+
+ fprintf(stderr, "fsck failed! Leaving the root file system read-only...\n");
+
+ return;
+out:
+ assumes(fwexec(remount_tool, true) != -1);
+}
+
+bool
+path_check(const char *path)
+{
+ struct stat sb;
+
+ if (stat(path, &sb) == 0)
+ return true;
+ return false;
+}
+
+bool
+is_safeboot(void)
+{
+ int sbmib[] = { CTL_KERN, KERN_SAFEBOOT };
+ uint32_t sb = 0;
+ size_t sbsz = sizeof(sb);
+
+ if (!assumes(sysctl(sbmib, 2, &sb, &sbsz, NULL, 0) == 0))
+ return false;
+
+ return (bool)sb;
+}
+
+bool
+is_netboot(void)
+{
+ int nbmib[] = { CTL_KERN, KERN_NETBOOT };
+ uint32_t nb = 0;
+ size_t nbsz = sizeof(nb);
+
+ if (!assumes(sysctl(nbmib, 2, &nb, &nbsz, NULL, 0) == 0))
+ return false;
+
+ return (bool)nb;
+}
+
+void
+apply_func_to_dir(const char *thedir, void (*thefunc)(const char *))
+{
+ struct dirent *de;
+ DIR *od;
+ int currend_dir_fd;
+
+ if (!assumes((currend_dir_fd = open(".", 0)) != -1))
+ return;
+
+ if (!assumes(chdir(thedir) != -1))
+ goto out;
+
+ if (!assumes(od = opendir(".")))
+ goto out;
+
+ while ((de = readdir(od))) {
+ struct stat sb;
+
+ if (strcmp(de->d_name, ".") == 0)
+ continue;
+ if (strcmp(de->d_name, "..") == 0)
+ continue;
+
+ if (assumes(stat(de->d_name, &sb) != -1)) {
+ if (S_ISDIR(sb.st_mode))
+ apply_func_to_dir(de->d_name, thefunc);
+ thefunc(de->d_name);
+ }
+ }
+
+ assumes(closedir(od) != -1);
+
+out:
+ assumes(fchdir(currend_dir_fd) != -1);
+ assumes(close(currend_dir_fd) != -1);
+}
+
+void
+empty_dir(const char *path)
+{
+ assumes(chflags(path, 0) != -1);
+ assumes(remove(path) != -1);
+}
+
+int
+touch_file(const char *path, mode_t m)
+{
+ int fd = open(path, O_CREAT, m);
+
+ if (fd == -1)
+ return -1;
+
+ return close(fd);
+}
+
+void
+apply_sysctls_from_file(const char *thefile)
+{
+ const char *sysctl_tool[] = { "sysctl", "-w", NULL, NULL };
+ size_t ln_len = 0;
+ char *val, *tmpstr;
+ FILE *sf;
+
+ if (!(sf = fopen(thefile, "r")))
+ return;
+
+ while ((val = fgetln(sf, &ln_len))) {
+ if (ln_len == 0)
+ continue;
+ if (!assumes((tmpstr = malloc(ln_len + 1)) != NULL))
+ continue;
+ memcpy(tmpstr, val, ln_len);
+ tmpstr[ln_len] = 0;
+ val = tmpstr;
+
+ while (*val && isspace(*val))
+ val++;
+ if (*val == '\0' || *val == '#')
+ goto skip_sysctl_tool;
+ sysctl_tool[2] = val;
+ assumes(fwexec(sysctl_tool, true) != -1);
+skip_sysctl_tool:
+ free(tmpstr);
+ }
+
+ assumes(fclose(sf) == 0);
+}
+
+void
+do_sysversion_sysctl(void)
+{
+ int mib[] = { CTL_KERN, KERN_OSVERSION };
+ CFDictionaryRef versdict;
+ CFStringRef buildvers;
+ char buf[1024];
+ size_t bufsz = sizeof(buf);
+
+ /* <rdar://problem/4477682> ER: launchd should set kern.osversion very early in boot */
+
+ if (getuid() != 0)
+ return;
+
+ if (sysctl(mib, 2, buf, &bufsz, NULL, 0) == -1) {
+ fprintf(stderr, "sysctl(): %s\n", strerror(errno));
+ return;
+ }
+
+ if (buf[0] != '\0')
+ return;
+
+ versdict = _CFCopySystemVersionDictionary();
+ buildvers = CFDictionaryGetValue(versdict, _kCFSystemVersionBuildVersionKey);
+ CFStringGetCString(buildvers, buf, sizeof(buf), kCFStringEncodingUTF8);
+
+ if (sysctl(mib, 2, NULL, 0, buf, strlen(buf) + 1) == -1) {
+ fprintf(stderr, "sysctl(): %s\n", strerror(errno));
+ }
+
+ CFRelease(versdict);
+}