void runIpmgmtd(void) { pid_t pid; int status; int tmplfd; tmplfd = init_template(0); if ((pid = fork()) == -1) { fatal(ERR_FORK_FAILED, "fork() failed: %s\n", strerror(errno)); } if (pid == 0) { /* child */ char cmd[MAXPATHLEN]; char *const argv[] = { "ipmgmtd", NULL }; char *const envp[] = { "SMF_FMRI=svc:/network/ip-interface-management:default", NULL }; (void) ct_tmpl_clear(tmplfd); (void) close(tmplfd); makePath(IPMGMTD, cmd, sizeof (cmd)); execve(cmd, argv, envp); fatal(ERR_EXEC_FAILED, "execve(%s) failed: %s\n", cmd, strerror(errno)); } /* parent */ (void) ct_tmpl_clear(tmplfd); (void) close(tmplfd); dlog("INFO started ipmgmtd[%d]\n", (int)pid); while (wait(&status) != pid) { /* EMPTY */; } if (WIFEXITED(status)) { dlog("INFO ipmgmtd[%d] exited: %d\n", (int)pid, WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) { fatal(ERR_IPMGMTD_DIED, "ipmgmtd[%d] died on signal: %d\n", (int)pid, WTERMSIG(status)); } else { fatal(ERR_IPMGMTD_CRASHED, "ipmgmtd[%d] failed in unknown way\n", (int)pid); } }
void solaris_contract_post_fork_parent(pid_t pid) { ctid_t ctid; char ctl_path[256]; int r, ctl_fd = -1, stat_fd = -1; debug2("%s: clearing template (fd %d)", __func__, tmpl_fd); if (tmpl_fd == -1) return; /* First clear the active template. */ if ((r = ct_tmpl_clear(tmpl_fd)) != 0) error("%s: Error clearing active process contract " "template: %s", __func__, strerror(errno)); close(tmpl_fd); tmpl_fd = -1; /* * If either the fork didn't succeed (pid < 0), or clearing * th active contract failed (r != 0), then we have nothing * more do. */ if (r != 0 || pid <= 0) return; /* Now lookup and abandon the contract we've created. */ ctid = get_active_process_contract_id(); debug2("%s: abandoning contract id %ld", __func__, ctid); snprintf(ctl_path, sizeof(ctl_path), CTFS_ROOT "/process/%ld/ctl", ctid); if ((ctl_fd = open64(ctl_path, O_WRONLY)) < 0) { error("%s: Error opening process contract " "ctl file: %s", __func__, strerror(errno)); goto fail; } if (ct_ctl_abandon(ctl_fd) < 0) { error("%s: Error abandoning process contract: %s", __func__, strerror(errno)); goto fail; } close(ctl_fd); return; fail: if (tmpl_fd != -1) { close(tmpl_fd); tmpl_fd = -1; } if (stat_fd != -1) close(stat_fd); if (ctl_fd != -1) close(ctl_fd); }
static void rtSolarisContractPostForkChild(int templateFd) { if (templateFd == -1) return; /* Clear the active template. */ ct_tmpl_clear(templateFd); close(templateFd); }
void solaris_contract_post_fork_child() { debug2("%s: clearing process contract template on fd %d", __func__, tmpl_fd); /* Clear the active template. */ if (ct_tmpl_clear(tmpl_fd) != 0) error("%s: Error clearing active process contract " "template: %s", __func__, strerror(errno)); close(tmpl_fd); tmpl_fd = -1; }
static void rtSolarisContractPostForkParent(int templateFd, pid_t pid) { if (templateFd == -1) return; /* Clear the active template. */ int cleared = ct_tmpl_clear(templateFd); close(templateFd); /* If the clearing failed or the fork failed there's nothing more to do. */ if (cleared || pid <= 0) return; /* Look up the contract which was created by this thread. */ int statFd = open64(CTFS_ROOT "/process/latest", O_RDONLY); if (statFd == -1) return; ct_stathdl_t statHdl; if (ct_status_read(statFd, CTD_COMMON, &statHdl)) { close(statFd); return; } ctid_t ctId = ct_status_get_id(statHdl); ct_status_free(statHdl); close(statFd); if (ctId < 0) return; /* Abandon this contract we just created. */ char ctlPath[PATH_MAX]; size_t len = snprintf(ctlPath, sizeof(ctlPath), CTFS_ROOT "/process/%ld/ctl", (long)ctId); if (len >= sizeof(ctlPath)) return; int ctlFd = open64(ctlPath, O_WRONLY); if (statFd == -1) return; if (ct_ctl_abandon(ctlFd) < 0) { close(ctlFd); return; } close(ctlFd); }
static void doNfsMount(const char *nfsvolume, const char *mountpoint, boolean_t readonly) { pid_t pid; int status; int ret; int tmplfd; dlog("INFO mounting %s on %s\n", nfsvolume, mountpoint); /* ensure the directory exists */ ret = mkdir(mountpoint, 0755); if (ret == -1 && errno != EEXIST) { fatal(ERR_MKDIR, "failed to mkdir(%s): (%d) %s\n", mountpoint, errno, strerror(errno)); } /* do the mount */ tmplfd = init_template(0); if ((pid = fork()) == -1) { fatal(ERR_FORK_FAILED, "fork() failed: %s\n", strerror(errno)); } if (pid == 0) { /* child */ char cmd[MAXPATHLEN]; char *const argv[] = { "mount", "-o", (readonly == B_TRUE) ? "vers=3,sec=sys,ro" : "vers=3,sec=sys", (char *)nfsvolume, (char *)mountpoint, NULL }; (void) ct_tmpl_clear(tmplfd); (void) close(tmplfd); makePath(NFS_MOUNT, cmd, sizeof (cmd)); execv(cmd, argv); fatal(ERR_EXEC_FAILED, "execv(%s) failed: %s\n", cmd, strerror(errno)); } /* parent */ (void) ct_tmpl_clear(tmplfd); (void) close(tmplfd); dlog("INFO started mount[%d]\n", (int)pid); while (wait(&status) != pid) { /* EMPTY */; } if (WIFEXITED(status)) { dlog("INFO mount[%d] exited: %d\n", (int)pid, WEXITSTATUS(status)); if (WEXITSTATUS(status) != 0) { fatal(ERR_MOUNT_NFS_VOLUME, "mount[%d] exited non-zero (%d)\n", (int)pid, WEXITSTATUS(status)); } } else if (WIFSIGNALED(status)) { fatal(ERR_EXEC_FAILED, "mount[%d] died on signal: %d\n", (int)pid, WTERMSIG(status)); } else { fatal(ERR_EXEC_FAILED, "mount[%d] failed in unknown way\n", (int)pid); } }
int _z_zone_exec(int *r_status, char **r_results, char *a_inputFile, char *a_path, char *a_argv[], const char *a_zoneName, int *a_fds) { struct sigaction nact; struct sigaction oact; char *buffer; char *thisZoneName; int bufferIndex; int bufferSize; int exit_no; int ipipe[2] = {0, 0}; int lerrno; int n; int status; int stdinfile = -1; int tmpl_fd; pid_t child_pid; pid_t result_pid; void (*funcSighup)(); void (*funcSigint)(); /* entry assertions */ assert(a_path != (char *)NULL); assert(*a_path != '\0'); assert(a_argv != (char **)NULL); assert(a_argv[0] != (char *)NULL); assert(*a_argv[0] != '\0'); assert(a_zoneName != (char *)NULL); /* * if requested to execute in current zone name, directly execute */ thisZoneName = z_get_zonename(); status = (strcmp(a_zoneName, thisZoneName) == 0); /* entry debugging info */ _z_echoDebug(DBG_ZONE_EXEC_CMD_ENTER, a_path, a_zoneName, thisZoneName); (void) free(thisZoneName); for (n = 0; a_argv[n]; n++) { _z_echoDebug(DBG_ARG, n, a_argv[n]); } /* if this zone, just exec the command directly */ if (status != 0) { return (z_ExecCmdArray(r_status, r_results, a_inputFile, a_path, a_argv)); } /* reset return results buffer pointer */ if (r_results != (char **)NULL) { *r_results = (char *)NULL; } *r_status = -1; /* -1 : failure to exec process */ /* if zones are not implemented, return TRUE */ if (!z_zones_are_implemented()) { return (-6); /* -6 : zones are not supported */ } if ((tmpl_fd = _zexec_init_template()) == -1) { _z_program_error(ERR_CANNOT_CREATE_CONTRACT, strerror(errno)); return (-2); /* -2 : cannot create greenline contract */ } /* * See if input file exists */ if (a_inputFile != (char *)NULL) { stdinfile = open(a_inputFile, O_RDONLY); } else { stdinfile = open("/dev/null", O_RDONLY); /* stdin = /dev/null */ } if (stdinfile < 0) { return (-4); /* -4 : could not open stdin source file */ } /* * Create a pipe to be used to capture the command output */ if (pipe(ipipe) != 0) { (void) close(stdinfile); return (-1); } bufferSize = PIPE_BUFFER_INCREMENT; bufferIndex = 0; buffer = calloc(1, bufferSize); if (buffer == (char *)NULL) { (void) close(stdinfile); return (-1); } /* flush standard i/o before creating new process */ (void) fflush(stderr); (void) fflush(stdout); /* * hold SIGINT/SIGHUP signals and reset signal received counter; * after the fork1() the parent and child need to setup their respective * interrupt handling and release the hold on the signals */ (void) sighold(SIGINT); (void) sighold(SIGHUP); _z_global_data._z_SigReceived = 0; /* no signals received */ /* * fork off a new process to execute command in; * fork1() is used instead of vfork() so the child process can * perform operations that would modify the parent process if * vfork() were used */ child_pid = fork1(); if (child_pid < 0) { /* * ************************************************************* * fork failed! * ************************************************************* */ (void) ct_tmpl_clear(tmpl_fd); (void) close(tmpl_fd); (void) free(buffer); _z_program_error(ERR_FORK, strerror(errno)); /* release hold on signals */ (void) sigrelse(SIGHUP); (void) sigrelse(SIGINT); return (-3); /* -3 : fork() failed */ } if (child_pid == 0) { int i; /* * ************************************************************* * This is the forked (child) process * ************************************************************* */ (void) ct_tmpl_clear(tmpl_fd); (void) close(tmpl_fd); /* reset any signals to default */ for (i = 0; i < NSIG; i++) { (void) sigset(i, SIG_DFL); } /* assign stdin, stdout, stderr as appropriate */ (void) dup2(stdinfile, STDIN_FILENO); (void) close(ipipe[0]); /* close out pipe reader side */ (void) dup2(ipipe[1], STDOUT_FILENO); (void) dup2(ipipe[1], STDERR_FILENO); /* * close all file descriptors not in the a_fds list */ (void) fdwalk(&_z_close_file_descriptors, (void *)a_fds); /* release all held signals */ (void) sigrelse(SIGHUP); (void) sigrelse(SIGINT); /* execute command in the specified non-global zone */ _exit(_zexec(a_zoneName, a_path, a_argv)); } /* * ********************************************************************* * This is the forking (parent) process * ********************************************************************* */ /* register child process i.d. so signal handlers can pass signal on */ _z_global_data._z_ChildProcessId = child_pid; /* * setup signal handlers for SIGINT and SIGHUP and release hold */ /* hook SIGINT to _z_sig_trap() */ nact.sa_handler = _z_sig_trap; nact.sa_flags = SA_RESTART; (void) sigemptyset(&nact.sa_mask); if (sigaction(SIGINT, &nact, &oact) < 0) { funcSigint = SIG_DFL; } else { funcSigint = oact.sa_handler; } /* hook SIGHUP to _z_sig_trap() */ nact.sa_handler = _z_sig_trap; nact.sa_flags = SA_RESTART; (void) sigemptyset(&nact.sa_mask); if (sigaction(SIGHUP, &nact, &oact) < 0) { funcSighup = SIG_DFL; } else { funcSighup = oact.sa_handler; } /* release hold on signals */ (void) sigrelse(SIGHUP); (void) sigrelse(SIGINT); (void) ct_tmpl_clear(tmpl_fd); (void) close(tmpl_fd); (void) close(stdinfile); (void) close(ipipe[1]); /* Close write side of pipe */ /* * Spin reading data from the child into the buffer - when the read eofs * the child has exited */ for (;;) { ssize_t bytesRead; /* read as much child data as there is available buffer space */ bytesRead = read(ipipe[0], buffer + bufferIndex, bufferSize - bufferIndex); /* break out of read loop if end-of-file encountered */ if (bytesRead == 0) { break; } /* if error, continue if recoverable, else break out of loop */ if (bytesRead == -1) { /* try again: EAGAIN - insufficient resources */ if (errno == EAGAIN) { continue; } /* try again: EINTR - interrupted system call */ if (errno == EINTR) { continue; } /* break out of loop - error not recoverable */ break; } /* at least 1 byte read: expand buffer if at end */ bufferIndex += bytesRead; if (bufferIndex >= bufferSize) { buffer = realloc(buffer, bufferSize += PIPE_BUFFER_INCREMENT); (void) memset(buffer + bufferIndex, 0, bufferSize - bufferIndex); } } (void) close(ipipe[0]); /* Close read side of pipe */ /* * wait for the process to exit, reap child exit status */ for (;;) { result_pid = waitpid(child_pid, &status, 0L); lerrno = (result_pid == -1 ? errno : 0); /* break loop if child process status reaped */ if (result_pid != -1) { break; } /* break loop if not interrupted out of waitpid */ if (errno != EINTR) { break; } } /* reset child process i.d. so signal handlers do not pass signals on */ _z_global_data._z_ChildProcessId = -1; /* * If the child process terminated due to a call to exit(), then * set results equal to the 8-bit exit status of the child process; * otherwise, set the exit status to "-1" indicating that the child * exited via a signal. */ if (WIFEXITED(status)) { *r_status = WEXITSTATUS(status); if ((_z_global_data._z_SigReceived != 0) && (*r_status == 0)) { *r_status = 1; } } else { *r_status = -1; /* -1 : failure to exec process */ } /* determine proper exit code */ if (result_pid == -1) { exit_no = -5; /* -5 : error from 'waitpid' other than EINTR */ } else if (_z_global_data._z_SigReceived != 0) { exit_no = -7; /* -7 : interrupt received */ } else { exit_no = 0; } /* return appropriate output */ if (!*buffer) { /* No contents in output buffer - discard */ free(buffer); } else if (r_results == (char **)NULL) { /* Not requested to return results - discard */ free(buffer); } else { /* have output and request to return: pass to calling method */ *r_results = buffer; } /* * reset signal handlers */ /* reset SIGINT */ nact.sa_handler = funcSigint; nact.sa_flags = SA_RESTART; (void) sigemptyset(&nact.sa_mask); (void) sigaction(SIGINT, &nact, (struct sigaction *)NULL); /* reset SIGHUP */ nact.sa_handler = funcSighup; nact.sa_flags = SA_RESTART; (void) sigemptyset(&nact.sa_mask); (void) sigaction(SIGHUP, &nact, (struct sigaction *)NULL); /* * if signal received during command execution, interrupt * this process now. */ if (_z_global_data._z_SigReceived != 0) { (void) kill(getpid(), SIGINT); } /* set errno and return */ errno = lerrno; return (exit_no); }
/* * Execute an operation on filename relative to zoneid's zone root. If the * file is in the global zone, then the zfcb() callback will simply be called * directly. If the file is in a non-global zone, then zfcb() will be called * both from the global zone's context, and from the non-global zone's context * (from a fork()'ed child that has entered the non-global zone). This is * done to allow the callback to communicate with itself if needed (e.g. to * pass back the file descriptor of an opened file). */ static int dlmgmt_zfop(const char *filename, zoneid_t zoneid, zfcb_t *zfcb, zfoparg_t *zfoparg) { int ctfd; int err; pid_t childpid; siginfo_t info; zfarg_t zfarg; ctid_t ct; if (zoneid != GLOBAL_ZONEID) { /* * We need to access a file that isn't in the global zone. * Accessing non-global zone files from the global zone is * unsafe (due to symlink attacks), we'll need to fork a child * that enters the zone in question and executes the callback * that will operate on the file. * * Before we proceed with this zone tango, we need to create a * new process contract for the child, as required by * zone_enter(). */ errno = 0; ctfd = open64("/system/contract/process/template", O_RDWR); if (ctfd == -1) return (errno); if ((err = ct_tmpl_set_critical(ctfd, 0)) != 0 || (err = ct_tmpl_set_informative(ctfd, 0)) != 0 || (err = ct_pr_tmpl_set_fatal(ctfd, CT_PR_EV_HWERR)) != 0 || (err = ct_pr_tmpl_set_param(ctfd, CT_PR_PGRPONLY)) != 0 || (err = ct_tmpl_activate(ctfd)) != 0) { (void) close(ctfd); return (err); } childpid = fork(); switch (childpid) { case -1: (void) ct_tmpl_clear(ctfd); (void) close(ctfd); return (err); case 0: (void) ct_tmpl_clear(ctfd); (void) close(ctfd); /* * Elevate our privileges as zone_enter() requires all * privileges. */ if ((err = dlmgmt_elevate_privileges()) != 0) _exit(err); if (zone_enter(zoneid) == -1) _exit(errno); if ((err = dlmgmt_drop_privileges()) != 0) _exit(err); break; default: if (contract_latest(&ct) == -1) ct = -1; (void) ct_tmpl_clear(ctfd); (void) close(ctfd); if (waitid(P_PID, childpid, &info, WEXITED) == -1) { (void) contract_abandon_id(ct); return (errno); } (void) contract_abandon_id(ct); if (info.si_status != 0) return (info.si_status); } } zfarg.zfarg_inglobalzone = (zoneid == GLOBAL_ZONEID || childpid != 0); zfarg.zfarg_finglobalzone = (zoneid == GLOBAL_ZONEID); zfarg.zfarg_filename = filename; zfarg.zfarg_oparg = zfoparg; err = zfcb(&zfarg); if (!zfarg.zfarg_inglobalzone) _exit(err); return (err); }
static int zsocket(zoneid_t zoneid, const char *path) { char c = 0; ctid_t ct = -1; int _errno = 0; int pid = 0; int sock_fd = 0; int sockfd[2] = {0}; int stat = 0; int tmpl_fd = 0; int flags; struct sockaddr_un addr; size_t addr_len = 0; if (zoneid < 0) { return (-1); } if (path == NULL) { return (-1); } bzero(&addr, sizeof (addr)); pthread_mutex_lock(&lock); if ((tmpl_fd = init_template()) < 0) { pthread_mutex_unlock(&lock); return (-1); } if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd) != 0) { (void) ct_tmpl_clear(tmpl_fd); pthread_mutex_unlock(&lock); return (-1); } pid = fork(); debug("fork returned: %d\n", pid); if (pid < 0) { _errno = errno; (void) ct_tmpl_clear(tmpl_fd); close(sockfd[0]); close(sockfd[1]); errno = _errno; pthread_mutex_unlock(&lock); return (-1); } if (pid == 0) { (void) ct_tmpl_clear(tmpl_fd); (void) close(tmpl_fd); (void) close(sockfd[0]); if (zone_enter(zoneid) != 0) { debug("CHILD: zone_enter(%d) => %s\n", zoneid, strerror(errno)); _exit(1); } debug("CHILD: zone_enter(%d) => %d\n", zoneid, 0); (void) unlink(path); sock_fd = socket(PF_UNIX, SOCK_STREAM, 0); if (sock_fd < 0) { debug("CHILD: socket => %d\n", errno); _exit(2); } fcntl(sock_fd, F_SETFL, O_NONBLOCK); addr.sun_family = AF_UNIX; addr_len = sizeof (addr.sun_family) + snprintf(addr.sun_path, sizeof (addr.sun_path), path); if (bind(sock_fd, (struct sockaddr *) &addr, addr_len) != 0) { debug("CHILD: bind => %d\n", errno); _exit(3); } if (write_fd(sockfd[1], (void *)"", 1, sock_fd) < 0) { debug("CHILD: write_fd => %d\n", errno); _exit(4); } debug("CHILD: write_fd => %d\n", errno); _exit(0); } if (contract_latest(&ct) == -1) { ct = -1; } (void) ct_tmpl_clear(tmpl_fd); (void) close(tmpl_fd); (void) contract_abandon_id(ct); (void) close(sockfd[1]); debug("PARENT: waitforpid(%d)\n", pid); while ((waitpid(pid, &stat, 0) != pid) && errno != ECHILD) { /* DO NOTHING */; } if (WIFEXITED(stat) == 0) { debug("PARENT: Child didn't exit\n"); _errno = ECHILD; sock_fd = -1; } else { stat = WEXITSTATUS(stat); debug("PARENT: Child exit status %d\n", stat); if (stat == 0) { read_fd(sockfd[0], &c, 1, &sock_fd); } else { _errno = stat; sock_fd = -1; } } close(sockfd[0]); pthread_mutex_unlock(&lock); if (sock_fd < 0) { errno = _errno; } else { if ((flags = fcntl(sock_fd, F_GETFD)) != -1) { flags |= FD_CLOEXEC; (void) fcntl(sock_fd, F_SETFD, flags); } errno = 0; } debug("zsocket returning fd=%d, errno=%d\n", sock_fd, errno); return (sock_fd); }
/* * Note to future maintainers: with the change of wall to use the * getutxent() API, the forked children (created by this function) * must call _exit as opposed to exit. This is necessary to avoid * unwanted fflushing of getutxent's stdio stream (caused by atexit * processing). */ static void sendmes(struct utmpx *p, zoneid_t zid) { int i; char *s; static char device[LMAX + 6]; char *bp; int ibp; FILE *f; int fd, tmpl_fd; boolean_t zoneenter = B_FALSE; if (zid != getzoneid()) { zoneenter = B_TRUE; tmpl_fd = init_template(); if (tmpl_fd == -1) { (void) fprintf(stderr, "Could not initialize " "process contract"); return; } } while ((i = (int)fork()) == -1) { (void) alarm(60); (void) wait((int *)0); (void) alarm(0); } if (i) return; if (zoneenter && zone_enter(zid) == -1) { char zonename[ZONENAME_MAX]; (void) getzonenamebyid(zid, zonename, ZONENAME_MAX); (void) fprintf(stderr, "Could not enter zone " "%s\n", zonename); } if (zoneenter) (void) ct_tmpl_clear(tmpl_fd); if (gflag) if (!chkgrp(p->ut_user)) _exit(0); (void) signal(SIGHUP, SIG_IGN); (void) alarm(60); s = &device[0]; (void) snprintf(s, sizeof (device), "/dev/%.*s", LMAX, p->ut_line); /* check if the device is really a tty */ if ((fd = open(s, O_WRONLY|O_NOCTTY|O_NONBLOCK)) == -1) { (void) fprintf(stderr, "Cannot send to %.*s on %s\n", NMAX, p->ut_user, s); perror("open"); (void) fflush(stderr); _exit(1); } else { if (!isatty(fd)) { (void) fprintf(stderr, "Cannot send to device %.*s %s\n", LMAX, p->ut_line, "because it's not a tty"); openlog("wall", 0, LOG_AUTH); syslog(LOG_CRIT, "%.*s in utmpx is not a tty\n", LMAX, p->ut_line); closelog(); (void) fflush(stderr); _exit(1); } } #ifdef DEBUG (void) close(fd); f = fopen("wall.debug", "a"); #else f = fdopen(fd, "w"); #endif if (f == NULL) { (void) fprintf(stderr, "Cannot send to %-.*s on %s\n", NMAX, &p->ut_user[0], s); perror("open"); (void) fflush(stderr); _exit(1); } (void) fprintf(f, "\07\07\07Broadcast Message from %s (%s) on %s %19.19s", who, line, systm, time_buf); if (gflag) (void) fprintf(f, " to group %s", grpname); (void) fprintf(f, "...\n"); #ifdef DEBUG (void) fprintf(f, "DEBUG: To %.*s on %s\n", NMAX, p->ut_user, s); #endif i = strlen(mesg); for (bp = mesg; --i >= 0; bp++) { ibp = (unsigned int)((unsigned char) *bp); if (*bp == '\n') (void) putc('\r', f); if (isprint(ibp) || *bp == '\r' || *bp == '\013' || *bp == ' ' || *bp == '\t' || *bp == '\n' || *bp == '\007') { (void) putc(*bp, f); } else { if (!isascii(*bp)) { (void) fputs("M-", f); *bp = toascii(*bp); } if (iscntrl(*bp)) { (void) putc('^', f); (void) putc(*bp + 0100, f); } else (void) putc(*bp, f); } if (*bp == '\n') (void) fflush(f); if (ferror(f) || feof(f)) { (void) printf("\n\007Write failed\n"); (void) fflush(stdout); _exit(1); } } (void) fclose(f); (void) close(fd); _exit(0); }