+#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;
+}