+
+int
+coalition_ledger(__unused proc_t p, __unused struct coalition_ledger_args *uap, __unused int32_t *retval)
+{
+ user_addr_t cidp = uap->cid;
+ user_addr_t buffer = uap->buffer;
+ user_addr_t bufsizep = uap->bufsize;
+ user_size_t bufsize;
+ uint32_t operation = uap->operation;
+ int error;
+ uint64_t cid;
+ coalition_t coal = COALITION_NULL;
+
+ if (!kauth_cred_issuser(kauth_cred_get())) {
+ error = EPERM;
+ goto out;
+ }
+
+ error = copyin(cidp, &cid, sizeof(cid));
+ if (error) {
+ goto out;
+ }
+
+ coal = coalition_find_by_id(cid);
+ if (coal == COALITION_NULL) {
+ error = ESRCH;
+ goto out;
+ }
+
+ if (IS_64BIT_PROCESS(p)) {
+ user64_size_t size64;
+ error = copyin(bufsizep, &size64, sizeof(size64));
+ bufsize = (user_size_t)size64;
+ } else {
+ user32_size_t size32;
+ error = copyin(bufsizep, &size32, sizeof(size32));
+ bufsize = (user_size_t)size32;
+ }
+ if (error) {
+ goto out;
+ }
+
+ switch (operation) {
+ case COALITION_LEDGER_SET_LOGICAL_WRITES_LIMIT:
+ error = coalition_ledger_logical_writes_limit(coal, buffer, bufsize);
+ break;
+ default:
+ error = EINVAL;
+ }
+out:
+ if (coal != COALITION_NULL) {
+ coalition_release(coal);
+ }
+ return error;
+}
+#if DEVELOPMENT || DEBUG
+static int sysctl_coalition_get_ids SYSCTL_HANDLER_ARGS
+{
+#pragma unused(oidp, arg1, arg2)
+ int error, pid;
+ proc_t tproc;
+ uint64_t value;
+ uint64_t ids[COALITION_NUM_TYPES] = {};
+
+
+ error = SYSCTL_IN(req, &value, sizeof(value));
+ if (error) {
+ return error;
+ }
+ if (!req->newptr) {
+ pid = req->p->p_pid;
+ } else {
+ pid = (int)value;
+ }
+
+ coal_dbg("looking up coalitions for pid:%d", pid);
+ tproc = proc_find(pid);
+ if (tproc == NULL) {
+ coal_dbg("ERROR: Couldn't find pid:%d", pid);
+ return ESRCH;
+ }
+
+ task_coalition_ids(tproc->task, ids);
+ proc_rele(tproc);
+
+ return SYSCTL_OUT(req, ids, sizeof(ids));
+}
+
+SYSCTL_PROC(_kern, OID_AUTO, coalitions, CTLTYPE_QUAD | CTLFLAG_RW | CTLFLAG_LOCKED,
+ 0, 0, sysctl_coalition_get_ids, "Q", "coalition ids of a given process");
+
+
+static int sysctl_coalition_get_roles SYSCTL_HANDLER_ARGS
+{
+#pragma unused(oidp, arg1, arg2)
+ int error, pid;
+ proc_t tproc;
+ int value;
+ int roles[COALITION_NUM_TYPES] = {};
+
+
+ error = SYSCTL_IN(req, &value, sizeof(value));
+ if (error) {
+ return error;
+ }
+ if (!req->newptr) {
+ pid = req->p->p_pid;
+ } else {
+ pid = (int)value;
+ }
+
+ coal_dbg("looking up coalitions for pid:%d", pid);
+ tproc = proc_find(pid);
+ if (tproc == NULL) {
+ coal_dbg("ERROR: Couldn't find pid:%d", pid);
+ return ESRCH;
+ }
+
+ task_coalition_roles(tproc->task, roles);
+ proc_rele(tproc);
+
+ return SYSCTL_OUT(req, roles, sizeof(roles));
+}
+
+SYSCTL_PROC(_kern, OID_AUTO, coalition_roles, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED,
+ 0, 0, sysctl_coalition_get_roles, "I", "coalition roles of a given process");
+
+
+static int sysctl_coalition_get_page_count SYSCTL_HANDLER_ARGS
+{
+#pragma unused(oidp, arg1, arg2)
+ int error, pid;
+ proc_t tproc;
+ coalition_t coal;
+ uint64_t value;
+ uint64_t pgcount[COALITION_NUM_TYPES];
+
+
+ error = SYSCTL_IN(req, &value, sizeof(value));
+ if (error) {
+ return error;
+ }
+ if (!req->newptr) {
+ pid = req->p->p_pid;
+ } else {
+ pid = (int)value;
+ }
+
+ coal_dbg("looking up coalitions for pid:%d", pid);
+ tproc = proc_find(pid);
+ if (tproc == NULL) {
+ coal_dbg("ERROR: Couldn't find pid:%d", pid);
+ return ESRCH;
+ }
+
+ memset(pgcount, 0, sizeof(pgcount));
+
+ for (int t = 0; t < COALITION_NUM_TYPES; t++) {
+ coal = task_get_coalition(tproc->task, t);
+ if (coal != COALITION_NULL) {
+ int ntasks = 0;
+ pgcount[t] = coalition_get_page_count(coal, &ntasks);
+ coal_dbg("PID:%d, Coalition:%lld, type:%d, pgcount:%lld",
+ pid, coalition_id(coal), t, pgcount[t]);
+ }
+ }
+
+ proc_rele(tproc);
+
+ return SYSCTL_OUT(req, pgcount, sizeof(pgcount));
+}
+
+SYSCTL_PROC(_kern, OID_AUTO, coalition_page_count, CTLTYPE_QUAD | CTLFLAG_RW | CTLFLAG_LOCKED,
+ 0, 0, sysctl_coalition_get_page_count, "Q", "coalition page count of a specified process");
+
+
+static int sysctl_coalition_get_pid_list SYSCTL_HANDLER_ARGS
+{
+#pragma unused(oidp, arg1, arg2)
+ int error, type, sort_order, pid;
+ int value[3];
+ int has_pid = 1;
+
+ coalition_t coal = COALITION_NULL;
+ proc_t tproc = PROC_NULL;
+ int npids = 0;
+ int pidlist[100] = { 0, };
+
+
+ error = SYSCTL_IN(req, &value, sizeof(value));
+ if (error) {
+ has_pid = 0;
+ error = SYSCTL_IN(req, &value, sizeof(value) - sizeof(value[0]));
+ }
+ if (error) {
+ return error;
+ }
+ if (!req->newptr) {
+ type = COALITION_TYPE_RESOURCE;
+ sort_order = COALITION_SORT_DEFAULT;
+ pid = req->p->p_pid;
+ } else {
+ type = value[0];
+ sort_order = value[1];
+ if (has_pid) {
+ pid = value[2];
+ } else {
+ pid = req->p->p_pid;
+ }
+ }
+
+ if (type < 0 || type >= COALITION_NUM_TYPES) {
+ return EINVAL;
+ }
+
+ coal_dbg("getting constituent PIDS for coalition of type %d "
+ "containing pid:%d (sort:%d)", type, pid, sort_order);
+ tproc = proc_find(pid);
+ if (tproc == NULL) {
+ coal_dbg("ERROR: Couldn't find pid:%d", pid);
+ return ESRCH;
+ }
+
+ coal = task_get_coalition(tproc->task, type);
+ if (coal == COALITION_NULL) {
+ goto out;
+ }
+
+ npids = coalition_get_pid_list(coal, COALITION_ROLEMASK_ALLROLES, sort_order,
+ pidlist, sizeof(pidlist) / sizeof(pidlist[0]));
+ if (npids > (int)(sizeof(pidlist) / sizeof(pidlist[0]))) {
+ coal_dbg("Too many members in coalition %llu (from pid:%d): %d!",
+ coalition_id(coal), pid, npids);
+ npids = sizeof(pidlist) / sizeof(pidlist[0]);
+ }
+
+out:
+ proc_rele(tproc);
+
+ if (npids < 0) {
+ /* npids is a negative errno */
+ return -npids;
+ }
+
+ if (npids == 0) {
+ return ENOENT;
+ }
+
+ return SYSCTL_OUT(req, pidlist, sizeof(pidlist[0]) * npids);
+}
+
+SYSCTL_PROC(_kern, OID_AUTO, coalition_pid_list, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED,
+ 0, 0, sysctl_coalition_get_pid_list, "I", "list of PIDS which are members of the coalition of the current process");
+
+#if DEVELOPMENT
+static int sysctl_coalition_notify SYSCTL_HANDLER_ARGS
+{
+#pragma unused(oidp, arg1, arg2)
+ int error, should_set;
+ coalition_t coal;
+ uint64_t value[2];
+
+ should_set = 1;
+ error = SYSCTL_IN(req, value, sizeof(value));
+ if (error) {
+ error = SYSCTL_IN(req, value, sizeof(value) - sizeof(value[0]));
+ if (error) {
+ return error;
+ }
+ should_set = 0;
+ }
+ if (!req->newptr) {
+ return error;
+ }
+
+ coal = coalition_find_by_id(value[0]);
+ if (coal == COALITION_NULL) {
+ coal_dbg("Can't find coalition with ID:%lld", value[0]);
+ return ESRCH;
+ }
+
+ if (should_set) {
+ coalition_set_notify(coal, (int)value[1]);
+ }
+
+ value[0] = (uint64_t)coalition_should_notify(coal);
+
+ coalition_release(coal);
+
+ return SYSCTL_OUT(req, value, sizeof(value[0]));
+}
+
+SYSCTL_PROC(_kern, OID_AUTO, coalition_notify, CTLTYPE_QUAD | CTLFLAG_RW | CTLFLAG_LOCKED,
+ 0, 0, sysctl_coalition_notify, "Q", "get/set coalition notification flag");
+
+extern int unrestrict_coalition_syscalls;
+SYSCTL_INT(_kern, OID_AUTO, unrestrict_coalitions,
+ CTLFLAG_RW, &unrestrict_coalition_syscalls, 0,
+ "unrestrict the coalition interface");
+
+#endif /* DEVELOPMENT */
+
+#endif /* DEVELOPMENT || DEBUG */