+/* ********* process control sets on self only */
+int
+proc_setcontrol(int pid, int flavor, uint64_t arg, user_addr_t buffer, uint32_t buffersize, __unused int32_t * retval)
+{
+ struct proc * pself = PROC_NULL;
+ int error = 0;
+ uint32_t pcontrol = (uint32_t)arg;
+ struct uthread *ut = NULL;
+
+
+ pself = current_proc();
+ if (pid != pself->p_pid)
+ return(EINVAL);
+
+ /* Do we have permission to look into this? */
+ if ((error = proc_security_policy(pself, PROC_INFO_CALL_SETCONTROL, flavor, NO_CHECK_SAME_USER)))
+ goto out;
+
+ switch (flavor) {
+ case PROC_SELFSET_PCONTROL: {
+ if (pcontrol > P_PCMAX)
+ return(EINVAL);
+ proc_lock(pself);
+ /* reset existing control setting while retaining action state */
+ pself->p_pcaction &= PROC_ACTION_MASK;
+ /* set new control state */
+ pself->p_pcaction |= pcontrol;
+ proc_unlock(pself);
+ }
+ break;
+
+ case PROC_SELFSET_THREADNAME: {
+ /* PROC_SELFSET_THREADNAME_SIZE = (MAXTHREADNAMESIZE -1) */
+ if(buffersize > PROC_SELFSET_THREADNAME_SIZE)
+ return ENAMETOOLONG;
+ ut = current_uthread();
+
+ if(!ut->pth_name)
+ {
+ ut->pth_name = (char*)kalloc(MAXTHREADNAMESIZE );
+ if(!ut->pth_name)
+ return ENOMEM;
+ }
+ bzero(ut->pth_name, MAXTHREADNAMESIZE);
+ error = copyin(buffer, ut->pth_name, buffersize);
+ }
+ break;
+
+ case PROC_SELFSET_VMRSRCOWNER: {
+ /* need to to be superuser */
+ if (suser(kauth_cred_get(), (u_short *)0) != 0) {
+ error = EPERM;
+ goto out;
+ }
+
+ proc_lock(pself);
+ /* reset existing control setting while retaining action state */
+ pself->p_lflag |= P_LVMRSRCOWNER;
+ proc_unlock(pself);
+ }
+ break;
+
+ case PROC_SELFSET_DELAYIDLESLEEP: {
+ /* mark or clear the process property to delay idle sleep disk IO */
+ if (pcontrol != 0)
+ OSBitOrAtomic(P_DELAYIDLESLEEP, &pself->p_flag);
+ else
+ OSBitAndAtomic(~((uint32_t)P_DELAYIDLESLEEP), &pself->p_flag);
+ }
+ break;
+
+ default:
+ error = ENOTSUP;
+ }
+
+out:
+ return(error);
+}
+
+#if CONFIG_MEMORYSTATUS
+
+int
+proc_dirtycontrol(int pid, int flavor, uint64_t arg, int32_t *retval) {
+ struct proc *target_p;
+ int error = 0;
+ uint32_t pcontrol = (uint32_t)arg;
+ kauth_cred_t my_cred, target_cred;
+ boolean_t self = FALSE;
+ boolean_t child = FALSE;
+ boolean_t zombref = FALSE;
+ pid_t selfpid;
+
+ target_p = proc_find(pid);
+
+ if (target_p == PROC_NULL) {
+ if (flavor == PROC_DIRTYCONTROL_GET) {
+ target_p = proc_find_zombref(pid);
+ zombref = 1;
+ }
+
+ if (target_p == PROC_NULL)
+ return(ESRCH);
+
+ }
+
+ my_cred = kauth_cred_get();
+ target_cred = kauth_cred_proc_ref(target_p);
+
+ /* Do we have permission to look into this? */
+ if ((error = proc_security_policy(target_p, PROC_INFO_CALL_DIRTYCONTROL, flavor, NO_CHECK_SAME_USER)))
+ goto out;
+
+ selfpid = proc_selfpid();
+ if (pid == selfpid) {
+ self = TRUE;
+ } else if (target_p->p_ppid == selfpid) {
+ child = TRUE;
+ }
+
+ switch (flavor) {
+ case PROC_DIRTYCONTROL_TRACK: {
+ /* Only allow the process itself, its parent, or root */
+ if ((self == FALSE) && (child == FALSE) && kauth_cred_issuser(kauth_cred_get()) != TRUE) {
+ error = EPERM;
+ goto out;
+ }
+
+ error = memorystatus_dirty_track(target_p, pcontrol);
+ }
+ break;
+
+ case PROC_DIRTYCONTROL_SET: {
+ /* Check privileges; use cansignal() here since the process could be terminated */
+ if (!cansignal(current_proc(), my_cred, target_p, SIGKILL, 0)) {
+ error = EPERM;
+ goto out;
+ }
+
+ error = memorystatus_dirty_set(target_p, self, pcontrol);
+ }
+ break;
+
+ case PROC_DIRTYCONTROL_GET: {
+ /* No permissions check - dirty state is freely available */
+ if (retval) {
+ *retval = memorystatus_dirty_get(target_p);
+ } else {
+ error = EINVAL;
+ }
+ }
+ break;
+
+ case PROC_DIRTYCONTROL_CLEAR: {
+ /* Check privileges; use cansignal() here since the process could be terminated */
+ if (!cansignal(current_proc(), my_cred, target_p, SIGKILL, 0)) {
+ error = EPERM;
+ goto out;
+ }
+
+ error = memorystatus_dirty_clear(target_p, pcontrol);
+ }
+ break;
+ }
+
+out:
+ if (zombref)
+ proc_drop_zombref(target_p);
+ else
+ proc_rele(target_p);
+
+ kauth_cred_unref(&target_cred);
+
+ return(error);
+}
+#else
+
+int
+proc_dirtycontrol(__unused int pid, __unused int flavor, __unused uint64_t arg, __unused int32_t *retval) {
+ return ENOTSUP;
+}
+
+#endif /* CONFIG_MEMORYSTATUS */
+
+/*
+ * proc_terminate() provides support for sudden termination.
+ * SIGKILL is issued to tracked, clean processes; otherwise,
+ * SIGTERM is sent.
+ */
+
+int
+proc_terminate(int pid, int32_t *retval)
+{
+ int error = 0;
+ proc_t p;
+ kauth_cred_t uc = kauth_cred_get();
+ int sig;
+
+#if 0
+ /* XXX: Check if these are necessary */
+ AUDIT_ARG(pid, pid);
+ AUDIT_ARG(signum, sig);
+#endif
+
+ if (pid <= 0 || retval == NULL) {
+ return (EINVAL);
+ }
+
+ if ((p = proc_find(pid)) == NULL) {
+ return (ESRCH);
+ }
+
+#if 0
+ /* XXX: Check if these are necessary */
+ AUDIT_ARG(process, p);
+#endif
+
+ /* Check privileges; if SIGKILL can be issued, then SIGTERM is also OK */
+ if (!cansignal(current_proc(), uc, p, SIGKILL, 0)) {
+ error = EPERM;
+ goto out;
+ }
+
+ /* Not allowed to sudden terminate yourself */
+ if (p == current_proc()) {
+ error = EPERM;
+ goto out;
+ }
+
+#if CONFIG_MEMORYSTATUS
+ /* Determine requisite signal to issue */
+ sig = memorystatus_on_terminate(p);
+#else
+ sig = SIGTERM;
+#endif
+
+ proc_set_task_policy(p->task, THREAD_NULL, TASK_POLICY_ATTRIBUTE,
+ TASK_POLICY_TERMINATED, TASK_POLICY_ENABLE);
+
+ psignal(p, sig);
+ *retval = sig;
+
+out:
+ proc_rele(p);
+
+ return error;
+}
+
+/*
+ * copy stat64 structure into vinfo_stat structure.
+ */
+static void
+munge_vinfo_stat(struct stat64 *sbp, struct vinfo_stat *vsbp)
+{
+ bzero(vsbp, sizeof(struct vinfo_stat));
+
+ vsbp->vst_dev = sbp->st_dev;
+ vsbp->vst_mode = sbp->st_mode;
+ vsbp->vst_nlink = sbp->st_nlink;
+ vsbp->vst_ino = sbp->st_ino;
+ vsbp->vst_uid = sbp->st_uid;
+ vsbp->vst_gid = sbp->st_gid;
+ vsbp->vst_atime = sbp->st_atimespec.tv_sec;
+ vsbp->vst_atimensec = sbp->st_atimespec.tv_nsec;
+ vsbp->vst_mtime = sbp->st_mtimespec.tv_sec;
+ vsbp->vst_mtimensec = sbp->st_mtimespec.tv_nsec;
+ vsbp->vst_ctime = sbp->st_ctimespec.tv_sec;
+ vsbp->vst_ctimensec = sbp->st_ctimespec.tv_nsec;
+ vsbp->vst_birthtime = sbp->st_birthtimespec.tv_sec;
+ vsbp->vst_birthtimensec = sbp->st_birthtimespec.tv_nsec;
+ vsbp->vst_size = sbp->st_size;
+ vsbp->vst_blocks = sbp->st_blocks;
+ vsbp->vst_blksize = sbp->st_blksize;
+ vsbp->vst_flags = sbp->st_flags;
+ vsbp->vst_gen = sbp->st_gen;
+ vsbp->vst_rdev = sbp->st_rdev;
+ vsbp->vst_qspare[0] = sbp->st_qspare[0];
+ vsbp->vst_qspare[1] = sbp->st_qspare[1];
+}
+
+int
+proc_pid_rusage(int pid, int flavor, user_addr_t buffer, __unused int32_t *retval)
+{
+ proc_t p;
+ int error;
+ int zombie = 0;
+
+ if ((p = proc_find(pid)) == PROC_NULL) {
+ if ((p = proc_find_zombref(pid)) == PROC_NULL) {
+ return (ESRCH);
+ }
+ zombie = 1;
+ }
+
+ /* Do we have permission to look into this? */
+ if ((error = proc_security_policy(p, PROC_INFO_CALL_PIDRUSAGE, flavor, CHECK_SAME_USER)))
+ goto out;
+
+ error = proc_get_rusage(p, flavor, buffer, zombie);
+
+out:
+ if (zombie)
+ proc_drop_zombref(p);
+ else
+ proc_rele(p);
+
+ return (error);
+}
+
+void
+proc_archinfo(proc_t p, struct proc_archinfo *pai)
+{
+ proc_lock(p);
+ pai->p_cputype = p->p_cputype;
+ pai->p_cpusubtype = p->p_cpusubtype;
+ proc_unlock(p);
+}
+
+void
+proc_pidcoalitioninfo(proc_t p, struct proc_pidcoalitioninfo *ppci)
+{
+ bzero(ppci, sizeof(*ppci));
+ ppci->coalition_id = proc_coalitionid(p);
+}
+
+
+
+/*
+ * Wrapper to provide NOTE_EXIT_DETAIL and NOTE_EXITSTATUS
+ * It mimics the data that is typically captured by the
+ * EVFILT_PROC, NOTE_EXIT event mechanism.
+ * See filt_proc() in kern_event.c.
+ */
+int
+proc_pidnoteexit(proc_t p, uint64_t flags, uint32_t *data)
+{
+ uint32_t exit_data = 0;
+ uint32_t exit_flags = (uint32_t)flags;
+
+ proc_lock(p);
+
+ /*
+ * Allow access to the parent of the exiting
+ * child or the parent debugger only.
+ */
+ do {
+ pid_t selfpid = proc_selfpid();
+
+ if (p->p_ppid == selfpid)
+ break; /* parent => ok */
+
+ if ((p->p_lflag & P_LTRACED) != 0 &&
+ (p->p_oppid == selfpid))
+ break; /* parent-in-waiting => ok */
+
+ proc_unlock(p);
+ return (EACCES);
+ } while (0);
+
+ if ((exit_flags & NOTE_EXITSTATUS) != 0) {
+ /* The signal and exit status */
+ exit_data |= (p->p_xstat & NOTE_PDATAMASK);
+ }
+
+ if ((exit_flags & NOTE_EXIT_DETAIL) != 0) {
+ /* The exit detail */
+ if ((p->p_lflag & P_LTERM_DECRYPTFAIL) != 0) {
+ exit_data |= NOTE_EXIT_DECRYPTFAIL;
+ }
+
+ if ((p->p_lflag & P_LTERM_JETSAM) != 0) {
+ exit_data |= NOTE_EXIT_MEMORY;
+
+ switch (p->p_lflag & P_JETSAM_MASK) {
+ case P_JETSAM_VMPAGESHORTAGE:
+ exit_data |= NOTE_EXIT_MEMORY_VMPAGESHORTAGE;
+ break;
+ case P_JETSAM_VMTHRASHING:
+ exit_data |= NOTE_EXIT_MEMORY_VMTHRASHING;
+ break;
+ case P_JETSAM_FCTHRASHING:
+ exit_data |= NOTE_EXIT_MEMORY_FCTHRASHING;
+ break;
+ case P_JETSAM_VNODE:
+ exit_data |= NOTE_EXIT_MEMORY_VNODE;
+ break;
+ case P_JETSAM_HIWAT:
+ exit_data |= NOTE_EXIT_MEMORY_HIWAT;
+ break;
+ case P_JETSAM_PID:
+ exit_data |= NOTE_EXIT_MEMORY_PID;
+ break;
+ case P_JETSAM_IDLEEXIT:
+ exit_data |= NOTE_EXIT_MEMORY_IDLE;
+ break;
+ }
+ }
+
+ if ((p->p_csflags & CS_KILLED) != 0) {
+ exit_data |= NOTE_EXIT_CSERROR;
+ }
+ }
+
+ proc_unlock(p);
+
+ *data = exit_data;
+
+ return (0);
+}
+