#include <kern/kern_types.h>
#include <mach/mach_types.h>
#include <mach/boolean.h>
#include <kern/coalition.h>
#include <sys/coalition.h>
#include <sys/errno.h>
#include <sys/kernel.h>
#include <sys/sysproto.h>
#include <sys/systm.h>
static
int
coalition_create_syscall(user_addr_t cidp, uint32_t flags)
{
int error = 0;
kern_return_t kr;
uint64_t cid;
coalition_t coal;
if ((flags & (~COALITION_CREATE_FLAG_MASK)) != 0) {
return EINVAL;
}
boolean_t privileged = flags & COALITION_CREATE_FLAG_PRIVILEGED;
kr = coalition_create_internal(&coal, privileged);
if (kr != KERN_SUCCESS) {
error = ENOMEM;
goto out;
}
cid = coalition_id(coal);
#if COALITION_DEBUG
printf("%s(addr, %u) -> %llu\n", __func__, flags, cid);
#endif
error = copyout(&cid, cidp, sizeof(cid));
out:
return error;
}
static
int
coalition_request_terminate_syscall(user_addr_t cidp, uint32_t flags)
{
kern_return_t kr;
int error = 0;
uint64_t cid;
coalition_t coal;
if (flags != 0) {
return EINVAL;
}
error = copyin(cidp, &cid, sizeof(cid));
if (error) {
return error;
}
coal = coalition_find_by_id(cid);
if (coal == COALITION_NULL) {
return ESRCH;
}
kr = coalition_request_terminate_internal(coal);
coalition_release(coal);
switch (kr) {
case KERN_SUCCESS:
break;
case KERN_DEFAULT_SET:
error = EPERM;
case KERN_TERMINATED:
error = EALREADY;
case KERN_INVALID_NAME:
error = ESRCH;
default:
error = EIO;
}
#if COALITION_DEBUG
printf("%s(%llu, %u) -> %d\n", __func__, cid, flags, error);
#endif
return error;
}
static
int
coalition_reap_syscall(user_addr_t cidp, uint32_t flags)
{
kern_return_t kr;
int error = 0;
uint64_t cid;
coalition_t coal;
if (flags != 0) {
return EINVAL;
}
error = copyin(cidp, &cid, sizeof(cid));
if (error) {
return error;
}
coal = coalition_find_by_id(cid);
if (coal == COALITION_NULL) {
return ESRCH;
}
kr = coalition_reap_internal(coal);
coalition_release(coal);
switch (kr) {
case KERN_SUCCESS:
break;
case KERN_DEFAULT_SET:
error = EPERM;
case KERN_TERMINATED:
error = ESRCH;
case KERN_FAILURE:
error = EBUSY;
default:
error = EIO;
}
#if COALITION_DEBUG
printf("%s(%llu, %u) -> %d\n", __func__, cid, flags, error);
#endif
return error;
}
int coalition(proc_t p, struct coalition_args *cap, __unused int32_t *retval)
{
uint32_t operation = cap->operation;
user_addr_t cidp = cap->cid;
uint32_t flags = cap->flags;
int error = 0;
if (!task_is_in_privileged_coalition(p->task)) {
return EPERM;
}
switch (operation) {
case COALITION_OP_CREATE:
error = coalition_create_syscall(cidp, flags);
break;
case COALITION_OP_REAP:
error = coalition_reap_syscall(cidp, flags);
break;
case COALITION_OP_TERMINATE:
error = coalition_request_terminate_syscall(cidp, flags);
break;
default:
error = ENOSYS;
}
return error;
}
static int __attribute__ ((noinline))
coalition_info_resource_usage(coalition_t coal, user_addr_t buffer, user_size_t bufsize)
{
kern_return_t kr;
struct coalition_resource_usage cru;
if (bufsize != sizeof(cru)) {
return EINVAL;
}
kr = coalition_resource_usage_internal(coal, &cru);
switch (kr) {
case KERN_INVALID_ARGUMENT:
return EINVAL;
case KERN_RESOURCE_SHORTAGE:
return ENOMEM;
case KERN_SUCCESS:
break;
default:
return EIO;
}
return copyout(&cru, buffer, bufsize);
}
int coalition_info(proc_t p, struct coalition_info_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 flavor = uap->flavor;
int error;
uint64_t cid;
coalition_t coal;
error = copyin(cidp, &cid, sizeof(cid));
if (error) {
return error;
}
coal = coalition_find_by_id(cid);
if (coal == COALITION_NULL) {
return ESRCH;
}
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 bad;
}
switch (flavor) {
case COALITION_INFO_RESOURCE_USAGE:
error = coalition_info_resource_usage(coal, buffer, bufsize);
break;
default:
error = EINVAL;
}
bad:
coalition_release(coal);
return error;
}