Changeset View
Changeset View
Standalone View
Standalone View
head/sys/kern/kern_rctl.c
Show First 20 Lines • Show All 219 Lines • ▼ Show 20 Lines | |||||
*/ | */ | ||||
static int64_t | static int64_t | ||||
rctl_available_resource(const struct proc *p, const struct rctl_rule *rule) | rctl_available_resource(const struct proc *p, const struct rctl_rule *rule) | ||||
{ | { | ||||
int resource; | int resource; | ||||
int64_t available = INT64_MAX; | int64_t available = INT64_MAX; | ||||
struct ucred *cred = p->p_ucred; | struct ucred *cred = p->p_ucred; | ||||
ASSERT_RACCT_ENABLED(); | |||||
rw_assert(&rctl_lock, RA_LOCKED); | rw_assert(&rctl_lock, RA_LOCKED); | ||||
resource = rule->rr_resource; | resource = rule->rr_resource; | ||||
switch (rule->rr_per) { | switch (rule->rr_per) { | ||||
case RCTL_SUBJECT_TYPE_PROCESS: | case RCTL_SUBJECT_TYPE_PROCESS: | ||||
available = rule->rr_amount - | available = rule->rr_amount - | ||||
p->p_racct->r_resources[resource]; | p->p_racct->r_resources[resource]; | ||||
break; | break; | ||||
Show All 23 Lines | |||||
* resource limit specified by 'rule'. | * resource limit specified by 'rule'. | ||||
*/ | */ | ||||
static int | static int | ||||
rctl_would_exceed(const struct proc *p, const struct rctl_rule *rule, | rctl_would_exceed(const struct proc *p, const struct rctl_rule *rule, | ||||
int64_t amount) | int64_t amount) | ||||
{ | { | ||||
int64_t available; | int64_t available; | ||||
ASSERT_RACCT_ENABLED(); | |||||
rw_assert(&rctl_lock, RA_LOCKED); | rw_assert(&rctl_lock, RA_LOCKED); | ||||
available = rctl_available_resource(p, rule); | available = rctl_available_resource(p, rule); | ||||
if (available >= amount) | if (available >= amount) | ||||
return (0); | return (0); | ||||
return (1); | return (1); | ||||
} | } | ||||
/* | /* | ||||
* Special version of rctl_available() function for the %cpu resource. | * Special version of rctl_available() function for the %cpu resource. | ||||
* We slightly cheat here and return less than we normally would. | * We slightly cheat here and return less than we normally would. | ||||
*/ | */ | ||||
int64_t | int64_t | ||||
rctl_pcpu_available(const struct proc *p) { | rctl_pcpu_available(const struct proc *p) { | ||||
struct rctl_rule *rule; | struct rctl_rule *rule; | ||||
struct rctl_rule_link *link; | struct rctl_rule_link *link; | ||||
int64_t available, minavailable, limit; | int64_t available, minavailable, limit; | ||||
ASSERT_RACCT_ENABLED(); | |||||
minavailable = INT64_MAX; | minavailable = INT64_MAX; | ||||
limit = 0; | limit = 0; | ||||
rw_rlock(&rctl_lock); | rw_rlock(&rctl_lock); | ||||
LIST_FOREACH(link, &p->p_racct->r_rule_links, rrl_next) { | LIST_FOREACH(link, &p->p_racct->r_rule_links, rrl_next) { | ||||
rule = link->rrl_rule; | rule = link->rrl_rule; | ||||
if (rule->rr_resource != RACCT_PCTCPU) | if (rule->rr_resource != RACCT_PCTCPU) | ||||
Show All 35 Lines | rctl_enforce(struct proc *p, int resource, uint64_t amount) | ||||
struct rctl_rule *rule; | struct rctl_rule *rule; | ||||
struct rctl_rule_link *link; | struct rctl_rule_link *link; | ||||
struct sbuf sb; | struct sbuf sb; | ||||
int should_deny = 0; | int should_deny = 0; | ||||
char *buf; | char *buf; | ||||
static int curtime = 0; | static int curtime = 0; | ||||
static struct timeval lasttime; | static struct timeval lasttime; | ||||
ASSERT_RACCT_ENABLED(); | |||||
rw_rlock(&rctl_lock); | rw_rlock(&rctl_lock); | ||||
/* | /* | ||||
* There may be more than one matching rule; go through all of them. | * There may be more than one matching rule; go through all of them. | ||||
* Denial should be done last, after logging and sending signals. | * Denial should be done last, after logging and sending signals. | ||||
*/ | */ | ||||
LIST_FOREACH(link, &p->p_racct->r_rule_links, rrl_next) { | LIST_FOREACH(link, &p->p_racct->r_rule_links, rrl_next) { | ||||
rule = link->rrl_rule; | rule = link->rrl_rule; | ||||
▲ Show 20 Lines • Show All 107 Lines • ▼ Show 20 Lines | |||||
uint64_t | uint64_t | ||||
rctl_get_limit(struct proc *p, int resource) | rctl_get_limit(struct proc *p, int resource) | ||||
{ | { | ||||
struct rctl_rule *rule; | struct rctl_rule *rule; | ||||
struct rctl_rule_link *link; | struct rctl_rule_link *link; | ||||
uint64_t amount = UINT64_MAX; | uint64_t amount = UINT64_MAX; | ||||
ASSERT_RACCT_ENABLED(); | |||||
rw_rlock(&rctl_lock); | rw_rlock(&rctl_lock); | ||||
/* | /* | ||||
* There may be more than one matching rule; go through all of them. | * There may be more than one matching rule; go through all of them. | ||||
* Denial should be done last, after logging and sending signals. | * Denial should be done last, after logging and sending signals. | ||||
*/ | */ | ||||
LIST_FOREACH(link, &p->p_racct->r_rule_links, rrl_next) { | LIST_FOREACH(link, &p->p_racct->r_rule_links, rrl_next) { | ||||
rule = link->rrl_rule; | rule = link->rrl_rule; | ||||
Show All 14 Lines | |||||
rctl_get_available(struct proc *p, int resource) | rctl_get_available(struct proc *p, int resource) | ||||
{ | { | ||||
struct rctl_rule *rule; | struct rctl_rule *rule; | ||||
struct rctl_rule_link *link; | struct rctl_rule_link *link; | ||||
int64_t available, minavailable, allocated; | int64_t available, minavailable, allocated; | ||||
minavailable = INT64_MAX; | minavailable = INT64_MAX; | ||||
ASSERT_RACCT_ENABLED(); | |||||
rw_rlock(&rctl_lock); | rw_rlock(&rctl_lock); | ||||
/* | /* | ||||
* There may be more than one matching rule; go through all of them. | * There may be more than one matching rule; go through all of them. | ||||
* Denial should be done last, after logging and sending signals. | * Denial should be done last, after logging and sending signals. | ||||
*/ | */ | ||||
LIST_FOREACH(link, &p->p_racct->r_rule_links, rrl_next) { | LIST_FOREACH(link, &p->p_racct->r_rule_links, rrl_next) { | ||||
rule = link->rrl_rule; | rule = link->rrl_rule; | ||||
Show All 18 Lines | if (minavailable < 0) | ||||
minavailable = 0; | minavailable = 0; | ||||
return (minavailable); | return (minavailable); | ||||
} | } | ||||
static int | static int | ||||
rctl_rule_matches(const struct rctl_rule *rule, const struct rctl_rule *filter) | rctl_rule_matches(const struct rctl_rule *rule, const struct rctl_rule *filter) | ||||
{ | { | ||||
ASSERT_RACCT_ENABLED(); | |||||
if (filter->rr_subject_type != RCTL_SUBJECT_TYPE_UNDEFINED) { | if (filter->rr_subject_type != RCTL_SUBJECT_TYPE_UNDEFINED) { | ||||
if (rule->rr_subject_type != filter->rr_subject_type) | if (rule->rr_subject_type != filter->rr_subject_type) | ||||
return (0); | return (0); | ||||
switch (filter->rr_subject_type) { | switch (filter->rr_subject_type) { | ||||
case RCTL_SUBJECT_TYPE_PROCESS: | case RCTL_SUBJECT_TYPE_PROCESS: | ||||
if (filter->rr_subject.rs_proc != NULL && | if (filter->rr_subject.rs_proc != NULL && | ||||
rule->rr_subject.rs_proc != | rule->rr_subject.rs_proc != | ||||
▲ Show 20 Lines • Show All 98 Lines • ▼ Show 20 Lines | |||||
/* | /* | ||||
* Connect the rule to the racct, increasing refcount for the rule. | * Connect the rule to the racct, increasing refcount for the rule. | ||||
*/ | */ | ||||
static void | static void | ||||
rctl_racct_add_rule(struct racct *racct, struct rctl_rule *rule) | rctl_racct_add_rule(struct racct *racct, struct rctl_rule *rule) | ||||
{ | { | ||||
struct rctl_rule_link *link; | struct rctl_rule_link *link; | ||||
ASSERT_RACCT_ENABLED(); | |||||
KASSERT(rctl_rule_fully_specified(rule), ("rule not fully specified")); | KASSERT(rctl_rule_fully_specified(rule), ("rule not fully specified")); | ||||
rctl_rule_acquire(rule); | rctl_rule_acquire(rule); | ||||
link = uma_zalloc(rctl_rule_link_zone, M_WAITOK); | link = uma_zalloc(rctl_rule_link_zone, M_WAITOK); | ||||
link->rrl_rule = rule; | link->rrl_rule = rule; | ||||
link->rrl_exceeded = 0; | link->rrl_exceeded = 0; | ||||
rw_wlock(&rctl_lock); | rw_wlock(&rctl_lock); | ||||
LIST_INSERT_HEAD(&racct->r_rule_links, link, rrl_next); | LIST_INSERT_HEAD(&racct->r_rule_links, link, rrl_next); | ||||
rw_wunlock(&rctl_lock); | rw_wunlock(&rctl_lock); | ||||
} | } | ||||
static int | static int | ||||
rctl_racct_add_rule_locked(struct racct *racct, struct rctl_rule *rule) | rctl_racct_add_rule_locked(struct racct *racct, struct rctl_rule *rule) | ||||
{ | { | ||||
struct rctl_rule_link *link; | struct rctl_rule_link *link; | ||||
ASSERT_RACCT_ENABLED(); | |||||
KASSERT(rctl_rule_fully_specified(rule), ("rule not fully specified")); | KASSERT(rctl_rule_fully_specified(rule), ("rule not fully specified")); | ||||
rw_assert(&rctl_lock, RA_WLOCKED); | rw_assert(&rctl_lock, RA_WLOCKED); | ||||
link = uma_zalloc(rctl_rule_link_zone, M_NOWAIT); | link = uma_zalloc(rctl_rule_link_zone, M_NOWAIT); | ||||
if (link == NULL) | if (link == NULL) | ||||
return (ENOMEM); | return (ENOMEM); | ||||
rctl_rule_acquire(rule); | rctl_rule_acquire(rule); | ||||
link->rrl_rule = rule; | link->rrl_rule = rule; | ||||
Show All 10 Lines | |||||
*/ | */ | ||||
static int | static int | ||||
rctl_racct_remove_rules(struct racct *racct, | rctl_racct_remove_rules(struct racct *racct, | ||||
const struct rctl_rule *filter) | const struct rctl_rule *filter) | ||||
{ | { | ||||
int removed = 0; | int removed = 0; | ||||
struct rctl_rule_link *link, *linktmp; | struct rctl_rule_link *link, *linktmp; | ||||
ASSERT_RACCT_ENABLED(); | |||||
rw_assert(&rctl_lock, RA_WLOCKED); | rw_assert(&rctl_lock, RA_WLOCKED); | ||||
LIST_FOREACH_SAFE(link, &racct->r_rule_links, rrl_next, linktmp) { | LIST_FOREACH_SAFE(link, &racct->r_rule_links, rrl_next, linktmp) { | ||||
if (!rctl_rule_matches(link->rrl_rule, filter)) | if (!rctl_rule_matches(link->rrl_rule, filter)) | ||||
continue; | continue; | ||||
LIST_REMOVE(link, rrl_next); | LIST_REMOVE(link, rrl_next); | ||||
rctl_rule_release(link->rrl_rule); | rctl_rule_release(link->rrl_rule); | ||||
uma_zfree(rctl_rule_link_zone, link); | uma_zfree(rctl_rule_link_zone, link); | ||||
removed++; | removed++; | ||||
} | } | ||||
return (removed); | return (removed); | ||||
} | } | ||||
static void | static void | ||||
rctl_rule_acquire_subject(struct rctl_rule *rule) | rctl_rule_acquire_subject(struct rctl_rule *rule) | ||||
{ | { | ||||
ASSERT_RACCT_ENABLED(); | |||||
switch (rule->rr_subject_type) { | switch (rule->rr_subject_type) { | ||||
case RCTL_SUBJECT_TYPE_UNDEFINED: | case RCTL_SUBJECT_TYPE_UNDEFINED: | ||||
case RCTL_SUBJECT_TYPE_PROCESS: | case RCTL_SUBJECT_TYPE_PROCESS: | ||||
break; | break; | ||||
case RCTL_SUBJECT_TYPE_JAIL: | case RCTL_SUBJECT_TYPE_JAIL: | ||||
if (rule->rr_subject.rs_prison_racct != NULL) | if (rule->rr_subject.rs_prison_racct != NULL) | ||||
prison_racct_hold(rule->rr_subject.rs_prison_racct); | prison_racct_hold(rule->rr_subject.rs_prison_racct); | ||||
break; | break; | ||||
Show All 10 Lines | panic("rctl_rule_acquire_subject: unknown subject type %d", | ||||
rule->rr_subject_type); | rule->rr_subject_type); | ||||
} | } | ||||
} | } | ||||
static void | static void | ||||
rctl_rule_release_subject(struct rctl_rule *rule) | rctl_rule_release_subject(struct rctl_rule *rule) | ||||
{ | { | ||||
ASSERT_RACCT_ENABLED(); | |||||
switch (rule->rr_subject_type) { | switch (rule->rr_subject_type) { | ||||
case RCTL_SUBJECT_TYPE_UNDEFINED: | case RCTL_SUBJECT_TYPE_UNDEFINED: | ||||
case RCTL_SUBJECT_TYPE_PROCESS: | case RCTL_SUBJECT_TYPE_PROCESS: | ||||
break; | break; | ||||
case RCTL_SUBJECT_TYPE_JAIL: | case RCTL_SUBJECT_TYPE_JAIL: | ||||
if (rule->rr_subject.rs_prison_racct != NULL) | if (rule->rr_subject.rs_prison_racct != NULL) | ||||
prison_racct_free(rule->rr_subject.rs_prison_racct); | prison_racct_free(rule->rr_subject.rs_prison_racct); | ||||
break; | break; | ||||
Show All 11 Lines | rctl_rule_release_subject(struct rctl_rule *rule) | ||||
} | } | ||||
} | } | ||||
struct rctl_rule * | struct rctl_rule * | ||||
rctl_rule_alloc(int flags) | rctl_rule_alloc(int flags) | ||||
{ | { | ||||
struct rctl_rule *rule; | struct rctl_rule *rule; | ||||
ASSERT_RACCT_ENABLED(); | |||||
rule = uma_zalloc(rctl_rule_zone, flags); | rule = uma_zalloc(rctl_rule_zone, flags); | ||||
if (rule == NULL) | if (rule == NULL) | ||||
return (NULL); | return (NULL); | ||||
rule->rr_subject_type = RCTL_SUBJECT_TYPE_UNDEFINED; | rule->rr_subject_type = RCTL_SUBJECT_TYPE_UNDEFINED; | ||||
rule->rr_subject.rs_proc = NULL; | rule->rr_subject.rs_proc = NULL; | ||||
rule->rr_subject.rs_uip = NULL; | rule->rr_subject.rs_uip = NULL; | ||||
rule->rr_subject.rs_loginclass = NULL; | rule->rr_subject.rs_loginclass = NULL; | ||||
rule->rr_subject.rs_prison_racct = NULL; | rule->rr_subject.rs_prison_racct = NULL; | ||||
rule->rr_per = RCTL_SUBJECT_TYPE_UNDEFINED; | rule->rr_per = RCTL_SUBJECT_TYPE_UNDEFINED; | ||||
rule->rr_resource = RACCT_UNDEFINED; | rule->rr_resource = RACCT_UNDEFINED; | ||||
rule->rr_action = RCTL_ACTION_UNDEFINED; | rule->rr_action = RCTL_ACTION_UNDEFINED; | ||||
rule->rr_amount = RCTL_AMOUNT_UNDEFINED; | rule->rr_amount = RCTL_AMOUNT_UNDEFINED; | ||||
refcount_init(&rule->rr_refcount, 1); | refcount_init(&rule->rr_refcount, 1); | ||||
return (rule); | return (rule); | ||||
} | } | ||||
struct rctl_rule * | struct rctl_rule * | ||||
rctl_rule_duplicate(const struct rctl_rule *rule, int flags) | rctl_rule_duplicate(const struct rctl_rule *rule, int flags) | ||||
{ | { | ||||
struct rctl_rule *copy; | struct rctl_rule *copy; | ||||
ASSERT_RACCT_ENABLED(); | |||||
copy = uma_zalloc(rctl_rule_zone, flags); | copy = uma_zalloc(rctl_rule_zone, flags); | ||||
if (copy == NULL) | if (copy == NULL) | ||||
return (NULL); | return (NULL); | ||||
copy->rr_subject_type = rule->rr_subject_type; | copy->rr_subject_type = rule->rr_subject_type; | ||||
copy->rr_subject.rs_proc = rule->rr_subject.rs_proc; | copy->rr_subject.rs_proc = rule->rr_subject.rs_proc; | ||||
copy->rr_subject.rs_uip = rule->rr_subject.rs_uip; | copy->rr_subject.rs_uip = rule->rr_subject.rs_uip; | ||||
copy->rr_subject.rs_loginclass = rule->rr_subject.rs_loginclass; | copy->rr_subject.rs_loginclass = rule->rr_subject.rs_loginclass; | ||||
copy->rr_subject.rs_prison_racct = rule->rr_subject.rs_prison_racct; | copy->rr_subject.rs_prison_racct = rule->rr_subject.rs_prison_racct; | ||||
copy->rr_per = rule->rr_per; | copy->rr_per = rule->rr_per; | ||||
copy->rr_resource = rule->rr_resource; | copy->rr_resource = rule->rr_resource; | ||||
copy->rr_action = rule->rr_action; | copy->rr_action = rule->rr_action; | ||||
copy->rr_amount = rule->rr_amount; | copy->rr_amount = rule->rr_amount; | ||||
refcount_init(©->rr_refcount, 1); | refcount_init(©->rr_refcount, 1); | ||||
rctl_rule_acquire_subject(copy); | rctl_rule_acquire_subject(copy); | ||||
return (copy); | return (copy); | ||||
} | } | ||||
void | void | ||||
rctl_rule_acquire(struct rctl_rule *rule) | rctl_rule_acquire(struct rctl_rule *rule) | ||||
{ | { | ||||
ASSERT_RACCT_ENABLED(); | |||||
KASSERT(rule->rr_refcount > 0, ("rule->rr_refcount <= 0")); | KASSERT(rule->rr_refcount > 0, ("rule->rr_refcount <= 0")); | ||||
refcount_acquire(&rule->rr_refcount); | refcount_acquire(&rule->rr_refcount); | ||||
} | } | ||||
static void | static void | ||||
rctl_rule_free(void *context, int pending) | rctl_rule_free(void *context, int pending) | ||||
{ | { | ||||
struct rctl_rule *rule; | struct rctl_rule *rule; | ||||
rule = (struct rctl_rule *)context; | rule = (struct rctl_rule *)context; | ||||
ASSERT_RACCT_ENABLED(); | |||||
KASSERT(rule->rr_refcount == 0, ("rule->rr_refcount != 0")); | KASSERT(rule->rr_refcount == 0, ("rule->rr_refcount != 0")); | ||||
/* | /* | ||||
* We don't need locking here; rule is guaranteed to be inaccessible. | * We don't need locking here; rule is guaranteed to be inaccessible. | ||||
*/ | */ | ||||
rctl_rule_release_subject(rule); | rctl_rule_release_subject(rule); | ||||
uma_zfree(rctl_rule_zone, rule); | uma_zfree(rctl_rule_zone, rule); | ||||
} | } | ||||
void | void | ||||
rctl_rule_release(struct rctl_rule *rule) | rctl_rule_release(struct rctl_rule *rule) | ||||
{ | { | ||||
ASSERT_RACCT_ENABLED(); | |||||
KASSERT(rule->rr_refcount > 0, ("rule->rr_refcount <= 0")); | KASSERT(rule->rr_refcount > 0, ("rule->rr_refcount <= 0")); | ||||
if (refcount_release(&rule->rr_refcount)) { | if (refcount_release(&rule->rr_refcount)) { | ||||
/* | /* | ||||
* rctl_rule_release() is often called when iterating | * rctl_rule_release() is often called when iterating | ||||
* over all the uidinfo structures in the system, | * over all the uidinfo structures in the system, | ||||
* holding uihashtbl_lock. Since rctl_rule_free() | * holding uihashtbl_lock. Since rctl_rule_free() | ||||
* might end up calling uifree(), this would lead | * might end up calling uifree(), this would lead | ||||
* to lock recursion. Use taskqueue to avoid this. | * to lock recursion. Use taskqueue to avoid this. | ||||
*/ | */ | ||||
TASK_INIT(&rule->rr_task, 0, rctl_rule_free, rule); | TASK_INIT(&rule->rr_task, 0, rctl_rule_free, rule); | ||||
taskqueue_enqueue(taskqueue_thread, &rule->rr_task); | taskqueue_enqueue(taskqueue_thread, &rule->rr_task); | ||||
} | } | ||||
} | } | ||||
static int | static int | ||||
rctl_rule_fully_specified(const struct rctl_rule *rule) | rctl_rule_fully_specified(const struct rctl_rule *rule) | ||||
{ | { | ||||
ASSERT_RACCT_ENABLED(); | |||||
switch (rule->rr_subject_type) { | switch (rule->rr_subject_type) { | ||||
case RCTL_SUBJECT_TYPE_UNDEFINED: | case RCTL_SUBJECT_TYPE_UNDEFINED: | ||||
return (0); | return (0); | ||||
case RCTL_SUBJECT_TYPE_PROCESS: | case RCTL_SUBJECT_TYPE_PROCESS: | ||||
if (rule->rr_subject.rs_proc == NULL) | if (rule->rr_subject.rs_proc == NULL) | ||||
return (0); | return (0); | ||||
break; | break; | ||||
case RCTL_SUBJECT_TYPE_USER: | case RCTL_SUBJECT_TYPE_USER: | ||||
Show All 28 Lines | |||||
rctl_string_to_rule(char *rulestr, struct rctl_rule **rulep) | rctl_string_to_rule(char *rulestr, struct rctl_rule **rulep) | ||||
{ | { | ||||
int error = 0; | int error = 0; | ||||
char *subjectstr, *subject_idstr, *resourcestr, *actionstr, | char *subjectstr, *subject_idstr, *resourcestr, *actionstr, | ||||
*amountstr, *perstr; | *amountstr, *perstr; | ||||
struct rctl_rule *rule; | struct rctl_rule *rule; | ||||
id_t id; | id_t id; | ||||
ASSERT_RACCT_ENABLED(); | |||||
rule = rctl_rule_alloc(M_WAITOK); | rule = rctl_rule_alloc(M_WAITOK); | ||||
subjectstr = strsep(&rulestr, ":"); | subjectstr = strsep(&rulestr, ":"); | ||||
subject_idstr = strsep(&rulestr, ":"); | subject_idstr = strsep(&rulestr, ":"); | ||||
resourcestr = strsep(&rulestr, ":"); | resourcestr = strsep(&rulestr, ":"); | ||||
actionstr = strsep(&rulestr, "=/"); | actionstr = strsep(&rulestr, "=/"); | ||||
amountstr = strsep(&rulestr, "/"); | amountstr = strsep(&rulestr, "/"); | ||||
perstr = rulestr; | perstr = rulestr; | ||||
▲ Show 20 Lines • Show All 110 Lines • ▼ Show 20 Lines | rctl_rule_add(struct rctl_rule *rule) | ||||
struct ucred *cred; | struct ucred *cred; | ||||
struct uidinfo *uip; | struct uidinfo *uip; | ||||
struct prison *pr; | struct prison *pr; | ||||
struct prison_racct *prr; | struct prison_racct *prr; | ||||
struct loginclass *lc; | struct loginclass *lc; | ||||
struct rctl_rule *rule2; | struct rctl_rule *rule2; | ||||
int match; | int match; | ||||
ASSERT_RACCT_ENABLED(); | |||||
KASSERT(rctl_rule_fully_specified(rule), ("rule not fully specified")); | KASSERT(rctl_rule_fully_specified(rule), ("rule not fully specified")); | ||||
/* | /* | ||||
* Some rules just don't make sense. Note that the one below | * Some rules just don't make sense. Note that the one below | ||||
* cannot be rewritten using RACCT_IS_DENIABLE(); the RACCT_PCTCPU, | * cannot be rewritten using RACCT_IS_DENIABLE(); the RACCT_PCTCPU, | ||||
* for example, is not deniable in the racct sense, but the | * for example, is not deniable in the racct sense, but the | ||||
* limit is enforced in a different way, so "deny" rules for %CPU | * limit is enforced in a different way, so "deny" rules for %CPU | ||||
* do make sense. | * do make sense. | ||||
▲ Show 20 Lines • Show All 94 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
static void | static void | ||||
rctl_rule_remove_callback(struct racct *racct, void *arg2, void *arg3) | rctl_rule_remove_callback(struct racct *racct, void *arg2, void *arg3) | ||||
{ | { | ||||
struct rctl_rule *filter = (struct rctl_rule *)arg2; | struct rctl_rule *filter = (struct rctl_rule *)arg2; | ||||
int found = 0; | int found = 0; | ||||
ASSERT_RACCT_ENABLED(); | |||||
rw_wlock(&rctl_lock); | rw_wlock(&rctl_lock); | ||||
found += rctl_racct_remove_rules(racct, filter); | found += rctl_racct_remove_rules(racct, filter); | ||||
rw_wunlock(&rctl_lock); | rw_wunlock(&rctl_lock); | ||||
*((int *)arg3) += found; | *((int *)arg3) += found; | ||||
} | } | ||||
/* | /* | ||||
* Remove all rules that match the filter. | * Remove all rules that match the filter. | ||||
*/ | */ | ||||
int | int | ||||
rctl_rule_remove(struct rctl_rule *filter) | rctl_rule_remove(struct rctl_rule *filter) | ||||
{ | { | ||||
int found = 0; | int found = 0; | ||||
struct proc *p; | struct proc *p; | ||||
ASSERT_RACCT_ENABLED(); | |||||
if (filter->rr_subject_type == RCTL_SUBJECT_TYPE_PROCESS && | if (filter->rr_subject_type == RCTL_SUBJECT_TYPE_PROCESS && | ||||
filter->rr_subject.rs_proc != NULL) { | filter->rr_subject.rs_proc != NULL) { | ||||
p = filter->rr_subject.rs_proc; | p = filter->rr_subject.rs_proc; | ||||
rw_wlock(&rctl_lock); | rw_wlock(&rctl_lock); | ||||
found = rctl_racct_remove_rules(p->p_racct, filter); | found = rctl_racct_remove_rules(p->p_racct, filter); | ||||
rw_wunlock(&rctl_lock); | rw_wunlock(&rctl_lock); | ||||
if (found) | if (found) | ||||
return (0); | return (0); | ||||
Show All 22 Lines | |||||
/* | /* | ||||
* Appends a rule to the sbuf. | * Appends a rule to the sbuf. | ||||
*/ | */ | ||||
static void | static void | ||||
rctl_rule_to_sbuf(struct sbuf *sb, const struct rctl_rule *rule) | rctl_rule_to_sbuf(struct sbuf *sb, const struct rctl_rule *rule) | ||||
{ | { | ||||
int64_t amount; | int64_t amount; | ||||
ASSERT_RACCT_ENABLED(); | |||||
sbuf_printf(sb, "%s:", rctl_subject_type_name(rule->rr_subject_type)); | sbuf_printf(sb, "%s:", rctl_subject_type_name(rule->rr_subject_type)); | ||||
switch (rule->rr_subject_type) { | switch (rule->rr_subject_type) { | ||||
case RCTL_SUBJECT_TYPE_PROCESS: | case RCTL_SUBJECT_TYPE_PROCESS: | ||||
if (rule->rr_subject.rs_proc == NULL) | if (rule->rr_subject.rs_proc == NULL) | ||||
sbuf_printf(sb, ":"); | sbuf_printf(sb, ":"); | ||||
else | else | ||||
sbuf_printf(sb, "%d:", | sbuf_printf(sb, "%d:", | ||||
▲ Show 20 Lines • Show All 43 Lines • ▼ Show 20 Lines | |||||
* Routine used by RCTL syscalls to read in input string. | * Routine used by RCTL syscalls to read in input string. | ||||
*/ | */ | ||||
static int | static int | ||||
rctl_read_inbuf(char **inputstr, const char *inbufp, size_t inbuflen) | rctl_read_inbuf(char **inputstr, const char *inbufp, size_t inbuflen) | ||||
{ | { | ||||
int error; | int error; | ||||
char *str; | char *str; | ||||
ASSERT_RACCT_ENABLED(); | |||||
if (inbuflen <= 0) | if (inbuflen <= 0) | ||||
return (EINVAL); | return (EINVAL); | ||||
if (inbuflen > RCTL_MAX_INBUFLEN) | if (inbuflen > RCTL_MAX_INBUFLEN) | ||||
return (E2BIG); | return (E2BIG); | ||||
str = malloc(inbuflen + 1, M_RCTL, M_WAITOK); | str = malloc(inbuflen + 1, M_RCTL, M_WAITOK); | ||||
error = copyinstr(inbufp, str, inbuflen, NULL); | error = copyinstr(inbufp, str, inbuflen, NULL); | ||||
if (error != 0) { | if (error != 0) { | ||||
Show All 9 Lines | |||||
/* | /* | ||||
* Routine used by RCTL syscalls to write out output string. | * Routine used by RCTL syscalls to write out output string. | ||||
*/ | */ | ||||
static int | static int | ||||
rctl_write_outbuf(struct sbuf *outputsbuf, char *outbufp, size_t outbuflen) | rctl_write_outbuf(struct sbuf *outputsbuf, char *outbufp, size_t outbuflen) | ||||
{ | { | ||||
int error; | int error; | ||||
ASSERT_RACCT_ENABLED(); | |||||
if (outputsbuf == NULL) | if (outputsbuf == NULL) | ||||
return (0); | return (0); | ||||
sbuf_finish(outputsbuf); | sbuf_finish(outputsbuf); | ||||
if (outbuflen < sbuf_len(outputsbuf) + 1) { | if (outbuflen < sbuf_len(outputsbuf) + 1) { | ||||
sbuf_delete(outputsbuf); | sbuf_delete(outputsbuf); | ||||
return (ERANGE); | return (ERANGE); | ||||
} | } | ||||
error = copyout(sbuf_data(outputsbuf), outbufp, | error = copyout(sbuf_data(outputsbuf), outbufp, | ||||
sbuf_len(outputsbuf) + 1); | sbuf_len(outputsbuf) + 1); | ||||
sbuf_delete(outputsbuf); | sbuf_delete(outputsbuf); | ||||
return (error); | return (error); | ||||
} | } | ||||
static struct sbuf * | static struct sbuf * | ||||
rctl_racct_to_sbuf(struct racct *racct, int sloppy) | rctl_racct_to_sbuf(struct racct *racct, int sloppy) | ||||
{ | { | ||||
int i; | int i; | ||||
int64_t amount; | int64_t amount; | ||||
struct sbuf *sb; | struct sbuf *sb; | ||||
ASSERT_RACCT_ENABLED(); | |||||
sb = sbuf_new_auto(); | sb = sbuf_new_auto(); | ||||
for (i = 0; i <= RACCT_MAX; i++) { | for (i = 0; i <= RACCT_MAX; i++) { | ||||
if (sloppy == 0 && RACCT_IS_SLOPPY(i)) | if (sloppy == 0 && RACCT_IS_SLOPPY(i)) | ||||
continue; | continue; | ||||
amount = racct->r_resources[i]; | amount = racct->r_resources[i]; | ||||
if (RACCT_IS_IN_MILLIONS(i)) | if (RACCT_IS_IN_MILLIONS(i)) | ||||
amount /= 1000000; | amount /= 1000000; | ||||
sbuf_printf(sb, "%s=%jd,", rctl_resource_name(i), amount); | sbuf_printf(sb, "%s=%jd,", rctl_resource_name(i), amount); | ||||
Show All 9 Lines | sys_rctl_get_racct(struct thread *td, struct rctl_get_racct_args *uap) | ||||
char *inputstr; | char *inputstr; | ||||
struct rctl_rule *filter; | struct rctl_rule *filter; | ||||
struct sbuf *outputsbuf = NULL; | struct sbuf *outputsbuf = NULL; | ||||
struct proc *p; | struct proc *p; | ||||
struct uidinfo *uip; | struct uidinfo *uip; | ||||
struct loginclass *lc; | struct loginclass *lc; | ||||
struct prison_racct *prr; | struct prison_racct *prr; | ||||
if (!racct_enable) | |||||
return (ENOSYS); | |||||
error = priv_check(td, PRIV_RCTL_GET_RACCT); | error = priv_check(td, PRIV_RCTL_GET_RACCT); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
error = rctl_read_inbuf(&inputstr, uap->inbufp, uap->inbuflen); | error = rctl_read_inbuf(&inputstr, uap->inbufp, uap->inbuflen); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
▲ Show 20 Lines • Show All 54 Lines • ▼ Show 20 Lines | |||||
static void | static void | ||||
rctl_get_rules_callback(struct racct *racct, void *arg2, void *arg3) | rctl_get_rules_callback(struct racct *racct, void *arg2, void *arg3) | ||||
{ | { | ||||
struct rctl_rule *filter = (struct rctl_rule *)arg2; | struct rctl_rule *filter = (struct rctl_rule *)arg2; | ||||
struct rctl_rule_link *link; | struct rctl_rule_link *link; | ||||
struct sbuf *sb = (struct sbuf *)arg3; | struct sbuf *sb = (struct sbuf *)arg3; | ||||
ASSERT_RACCT_ENABLED(); | |||||
rw_rlock(&rctl_lock); | rw_rlock(&rctl_lock); | ||||
LIST_FOREACH(link, &racct->r_rule_links, rrl_next) { | LIST_FOREACH(link, &racct->r_rule_links, rrl_next) { | ||||
if (!rctl_rule_matches(link->rrl_rule, filter)) | if (!rctl_rule_matches(link->rrl_rule, filter)) | ||||
continue; | continue; | ||||
rctl_rule_to_sbuf(sb, link->rrl_rule); | rctl_rule_to_sbuf(sb, link->rrl_rule); | ||||
sbuf_printf(sb, ","); | sbuf_printf(sb, ","); | ||||
} | } | ||||
rw_runlock(&rctl_lock); | rw_runlock(&rctl_lock); | ||||
} | } | ||||
int | int | ||||
sys_rctl_get_rules(struct thread *td, struct rctl_get_rules_args *uap) | sys_rctl_get_rules(struct thread *td, struct rctl_get_rules_args *uap) | ||||
{ | { | ||||
int error; | int error; | ||||
size_t bufsize = RCTL_DEFAULT_BUFSIZE; | size_t bufsize = RCTL_DEFAULT_BUFSIZE; | ||||
char *inputstr, *buf; | char *inputstr, *buf; | ||||
struct sbuf *sb; | struct sbuf *sb; | ||||
struct rctl_rule *filter; | struct rctl_rule *filter; | ||||
struct rctl_rule_link *link; | struct rctl_rule_link *link; | ||||
struct proc *p; | struct proc *p; | ||||
if (!racct_enable) | |||||
return (ENOSYS); | |||||
error = priv_check(td, PRIV_RCTL_GET_RULES); | error = priv_check(td, PRIV_RCTL_GET_RULES); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
error = rctl_read_inbuf(&inputstr, uap->inbufp, uap->inbuflen); | error = rctl_read_inbuf(&inputstr, uap->inbufp, uap->inbuflen); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
▲ Show 20 Lines • Show All 58 Lines • ▼ Show 20 Lines | |||||
{ | { | ||||
int error; | int error; | ||||
size_t bufsize = RCTL_DEFAULT_BUFSIZE; | size_t bufsize = RCTL_DEFAULT_BUFSIZE; | ||||
char *inputstr, *buf; | char *inputstr, *buf; | ||||
struct sbuf *sb; | struct sbuf *sb; | ||||
struct rctl_rule *filter; | struct rctl_rule *filter; | ||||
struct rctl_rule_link *link; | struct rctl_rule_link *link; | ||||
if (!racct_enable) | |||||
return (ENOSYS); | |||||
error = priv_check(td, PRIV_RCTL_GET_LIMITS); | error = priv_check(td, PRIV_RCTL_GET_LIMITS); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
error = rctl_read_inbuf(&inputstr, uap->inbufp, uap->inbuflen); | error = rctl_read_inbuf(&inputstr, uap->inbufp, uap->inbuflen); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
▲ Show 20 Lines • Show All 55 Lines • ▼ Show 20 Lines | |||||
int | int | ||||
sys_rctl_add_rule(struct thread *td, struct rctl_add_rule_args *uap) | sys_rctl_add_rule(struct thread *td, struct rctl_add_rule_args *uap) | ||||
{ | { | ||||
int error; | int error; | ||||
struct rctl_rule *rule; | struct rctl_rule *rule; | ||||
char *inputstr; | char *inputstr; | ||||
if (!racct_enable) | |||||
return (ENOSYS); | |||||
error = priv_check(td, PRIV_RCTL_ADD_RULE); | error = priv_check(td, PRIV_RCTL_ADD_RULE); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
error = rctl_read_inbuf(&inputstr, uap->inbufp, uap->inbuflen); | error = rctl_read_inbuf(&inputstr, uap->inbufp, uap->inbuflen); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
Show All 26 Lines | |||||
int | int | ||||
sys_rctl_remove_rule(struct thread *td, struct rctl_remove_rule_args *uap) | sys_rctl_remove_rule(struct thread *td, struct rctl_remove_rule_args *uap) | ||||
{ | { | ||||
int error; | int error; | ||||
struct rctl_rule *filter; | struct rctl_rule *filter; | ||||
char *inputstr; | char *inputstr; | ||||
if (!racct_enable) | |||||
return (ENOSYS); | |||||
error = priv_check(td, PRIV_RCTL_REMOVE_RULE); | error = priv_check(td, PRIV_RCTL_REMOVE_RULE); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
error = rctl_read_inbuf(&inputstr, uap->inbufp, uap->inbuflen); | error = rctl_read_inbuf(&inputstr, uap->inbufp, uap->inbuflen); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
Show All 20 Lines | |||||
{ | { | ||||
int rulecnt, i; | int rulecnt, i; | ||||
struct rctl_rule_link *link, *newlink; | struct rctl_rule_link *link, *newlink; | ||||
struct uidinfo *newuip; | struct uidinfo *newuip; | ||||
struct loginclass *newlc; | struct loginclass *newlc; | ||||
struct prison_racct *newprr; | struct prison_racct *newprr; | ||||
LIST_HEAD(, rctl_rule_link) newrules; | LIST_HEAD(, rctl_rule_link) newrules; | ||||
ASSERT_RACCT_ENABLED(); | |||||
newuip = newcred->cr_ruidinfo; | newuip = newcred->cr_ruidinfo; | ||||
newlc = newcred->cr_loginclass; | newlc = newcred->cr_loginclass; | ||||
newprr = newcred->cr_prison->pr_prison_racct; | newprr = newcred->cr_prison->pr_prison_racct; | ||||
LIST_INIT(&newrules); | LIST_INIT(&newrules); | ||||
again: | again: | ||||
/* | /* | ||||
▲ Show 20 Lines • Show All 124 Lines • ▼ Show 20 Lines | |||||
rctl_proc_fork(struct proc *parent, struct proc *child) | rctl_proc_fork(struct proc *parent, struct proc *child) | ||||
{ | { | ||||
int error; | int error; | ||||
struct rctl_rule_link *link; | struct rctl_rule_link *link; | ||||
struct rctl_rule *rule; | struct rctl_rule *rule; | ||||
LIST_INIT(&child->p_racct->r_rule_links); | LIST_INIT(&child->p_racct->r_rule_links); | ||||
ASSERT_RACCT_ENABLED(); | |||||
KASSERT(parent->p_racct != NULL, ("process without racct; p = %p", parent)); | KASSERT(parent->p_racct != NULL, ("process without racct; p = %p", parent)); | ||||
rw_wlock(&rctl_lock); | rw_wlock(&rctl_lock); | ||||
/* | /* | ||||
* Go through limits applicable to the parent and assign them | * Go through limits applicable to the parent and assign them | ||||
* to the child. Rules with 'process' subject have to be duplicated | * to the child. Rules with 'process' subject have to be duplicated | ||||
* in order to make their rr_subject point to the new process. | * in order to make their rr_subject point to the new process. | ||||
Show All 37 Lines | |||||
/* | /* | ||||
* Release rules attached to the racct. | * Release rules attached to the racct. | ||||
*/ | */ | ||||
void | void | ||||
rctl_racct_release(struct racct *racct) | rctl_racct_release(struct racct *racct) | ||||
{ | { | ||||
struct rctl_rule_link *link; | struct rctl_rule_link *link; | ||||
ASSERT_RACCT_ENABLED(); | |||||
rw_wlock(&rctl_lock); | rw_wlock(&rctl_lock); | ||||
while (!LIST_EMPTY(&racct->r_rule_links)) { | while (!LIST_EMPTY(&racct->r_rule_links)) { | ||||
link = LIST_FIRST(&racct->r_rule_links); | link = LIST_FIRST(&racct->r_rule_links); | ||||
LIST_REMOVE(link, rrl_next); | LIST_REMOVE(link, rrl_next); | ||||
rctl_rule_release(link->rrl_rule); | rctl_rule_release(link->rrl_rule); | ||||
uma_zfree(rctl_rule_link_zone, link); | uma_zfree(rctl_rule_link_zone, link); | ||||
} | } | ||||
rw_wunlock(&rctl_lock); | rw_wunlock(&rctl_lock); | ||||
} | } | ||||
static void | static void | ||||
rctl_init(void) | rctl_init(void) | ||||
{ | { | ||||
if (!racct_enable) | |||||
return; | |||||
rctl_rule_link_zone = uma_zcreate("rctl_rule_link", | rctl_rule_link_zone = uma_zcreate("rctl_rule_link", | ||||
sizeof(struct rctl_rule_link), NULL, NULL, NULL, NULL, | sizeof(struct rctl_rule_link), NULL, NULL, NULL, NULL, | ||||
UMA_ALIGN_PTR, UMA_ZONE_NOFREE); | UMA_ALIGN_PTR, UMA_ZONE_NOFREE); | ||||
rctl_rule_zone = uma_zcreate("rctl_rule", sizeof(struct rctl_rule), | rctl_rule_zone = uma_zcreate("rctl_rule", sizeof(struct rctl_rule), | ||||
NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_NOFREE); | NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_NOFREE); | ||||
} | } | ||||
Show All 38 Lines |