From owner-p4-projects@FreeBSD.ORG Sun Aug 2 10:16:36 2009 Return-Path: Delivered-To: p4-projects@freebsd.org Received: by hub.freebsd.org (Postfix, from userid 32767) id 1724F1065673; Sun, 2 Aug 2009 10:16:36 +0000 (UTC) Delivered-To: perforce@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id CAE6C1065670 for ; Sun, 2 Aug 2009 10:16:35 +0000 (UTC) (envelope-from trasz@freebsd.org) Received: from repoman.freebsd.org (repoman.freebsd.org [IPv6:2001:4f8:fff6::29]) by mx1.freebsd.org (Postfix) with ESMTP id 7D56D8FC0A for ; Sun, 2 Aug 2009 10:16:35 +0000 (UTC) (envelope-from trasz@freebsd.org) Received: from repoman.freebsd.org (localhost [127.0.0.1]) by repoman.freebsd.org (8.14.3/8.14.3) with ESMTP id n72AGZjJ077409 for ; Sun, 2 Aug 2009 10:16:35 GMT (envelope-from trasz@freebsd.org) Received: (from perforce@localhost) by repoman.freebsd.org (8.14.3/8.14.3/Submit) id n72AGZW0077407 for perforce@freebsd.org; Sun, 2 Aug 2009 10:16:35 GMT (envelope-from trasz@freebsd.org) Date: Sun, 2 Aug 2009 10:16:35 GMT Message-Id: <200908021016.n72AGZW0077407@repoman.freebsd.org> X-Authentication-Warning: repoman.freebsd.org: perforce set sender to trasz@freebsd.org using -f From: Edward Tomasz Napierala To: Perforce Change Reviews Cc: Subject: PERFORCE change 166914 for review X-BeenThere: p4-projects@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: p4 projects tree changes List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 02 Aug 2009 10:16:36 -0000 http://perforce.freebsd.org/chv.cgi?CH=166914 Change 166914 by trasz@trasz_anger on 2009/08/02 10:16:29 Rework rule storage. It's not fully functional yet, but hey, at least it doesn't panic :-) Affected files ... .. //depot/projects/soc2009/trasz_limits/TODO#6 edit .. //depot/projects/soc2009/trasz_limits/sys/kern/init_main.c#8 edit .. //depot/projects/soc2009/trasz_limits/sys/kern/kern_hrl.c#34 edit .. //depot/projects/soc2009/trasz_limits/sys/kern/kern_loginclass.c#2 edit .. //depot/projects/soc2009/trasz_limits/sys/kern/kern_resource.c#14 edit .. //depot/projects/soc2009/trasz_limits/sys/sys/hrl.h#25 edit .. //depot/projects/soc2009/trasz_limits/sys/sys/loginclass.h#1 add .. //depot/projects/soc2009/trasz_limits/sys/sys/proc.h#10 edit .. //depot/projects/soc2009/trasz_limits/usr.bin/id/id.c#4 edit Differences ... ==== //depot/projects/soc2009/trasz_limits/TODO#6 (text+ko) ==== @@ -1,5 +1,8 @@ + - Rework rule storage. + - Make sure we have all the gidinfos we need in the 'struct ucred'. + - Fix up (add/remove resource counters) when: - Adding a group to a process, - Removing a group from a process, ==== //depot/projects/soc2009/trasz_limits/sys/kern/init_main.c#8 (text+ko) ==== @@ -56,6 +56,7 @@ #include #include #include +#include #include #include #include ==== //depot/projects/soc2009/trasz_limits/sys/kern/kern_hrl.c#34 (text+ko) ==== @@ -31,9 +31,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -58,11 +60,6 @@ TUNABLE_INT("kern.hrl_group_accounting", &hrl_group_accounting); SYSCTL_INT(_kern, OID_AUTO, hrl_group_accounting, CTLFLAG_RD, &hrl_group_accounting, 0, ""); -struct hrl_node { - struct hrl_rule hn_rule; - RB_ENTRY(hrl_node) hn_next; -}; - struct dict { const char *d_name; int d_value; @@ -104,57 +101,14 @@ { "sigxfsz", HRL_ACTION_SIGXFSZ }, { NULL, -1 }}; -/* - * XXX: This is silly. Some better way of organising these - * will be required. - */ -static int -hn_compare(const struct hrl_node *a, const struct hrl_node *b) -{ - if (a->hn_rule.hr_subject < b->hn_rule.hr_subject) - return (-1); - else if (a->hn_rule.hr_subject > b->hn_rule.hr_subject) - return (1); - - if (a->hn_rule.hr_subject_id < b->hn_rule.hr_subject_id) - return (-1); - else if (a->hn_rule.hr_subject_id > b->hn_rule.hr_subject_id) - return (1); - - if (a->hn_rule.hr_per < b->hn_rule.hr_per) - return (-1); - else if (a->hn_rule.hr_per > b->hn_rule.hr_per) - return (1); - - if (a->hn_rule.hr_resource < b->hn_rule.hr_resource) - return (-1); - else if (a->hn_rule.hr_resource > b->hn_rule.hr_resource) - return (1); - - if (a->hn_rule.hr_action < b->hn_rule.hr_action) - return (-1); - else if (a->hn_rule.hr_action > b->hn_rule.hr_action) - return (1); - - return (0); -} - -/* - * hrl_lock must be held during all operations on hrls. - */ -RB_HEAD(hrl_tree, hrl_node) hrls = RB_INITIALIZER(hrls); -RB_GENERATE_STATIC(hrl_tree, hrl_node, hn_next, hn_compare); - static const char * hrl_resource_name(int resource); static void hrl_init(void); SYSINIT(hrl, SI_SUB_CPU, SI_ORDER_FIRST, hrl_init, NULL); -static uma_zone_t hrl_node_zone; static uma_zone_t hrl_limit_zone; +static uma_zone_t hrl_rule_zone; static struct mtx hrl_lock; -static void hrl_rule_remove_limits(struct hrl_rule *rule); -static void hrl_rule_add_limits(struct hrl_rule *rule); static void hrl_compute_available(struct proc *p, int64_t (*availablep)[], struct hrl_rule *(*rulesp)[]); static struct sbuf *hrl_rules_to_sbuf(struct hrl_rule *usage, int nrules); @@ -184,10 +138,13 @@ cred = p->p_ucred; mtx_assert(&hrl_lock, MA_OWNED); for (resource = 0; resource < HRL_RESOURCE_MAX; resource++) - KASSERT(p->p_usage.hu_resources[resource] >= 0, ("resource usage propagation meltdown")); - KASSERT(cred->cr_ruidinfo->ui_usage.hu_resources[resource] >= 0, ("resource usage propagation meltdown")); + KASSERT(p->p_usage.hu_resources[resource] >= 0, + ("resource usage propagation meltdown")); + KASSERT(cred->cr_ruidinfo->ui_usage.hu_resources[resource] >= 0, + ("resource usage propagation meltdown")); for (pr = cred->cr_prison; pr != NULL; pr = pr->pr_parent) - KASSERT(pr->pr_usage.hu_resources[resource] >= 0, ("resource usage propagation meltdown")); + KASSERT(pr->pr_usage.hu_resources[resource] >= 0, + ("resource usage propagation meltdown")); if (hrl_group_accounting) { for (i = 0; i < cred->cr_ngroups; i++) { for (resource = 0; resource < HRL_RESOURCE_MAX; resource++) @@ -269,27 +226,6 @@ panic("hrl_resource_name: unknown resource"); } -void -hrl_proc_fork(struct proc *parent, struct proc *child) -{ - int i; - - mtx_lock(&hrl_lock); - /* - * Inherit resources from the parent. - */ - for (i = 0; i < HRL_RESOURCE_MAX; i++) { - if (parent->p_usage.hu_resources[i] != 0 && hrl_resource_inheritable(i)) - hrl_allocated_proc(child, i, parent->p_usage.hu_resources[i]); - } - - /* - * Assign rules appropriate for the process. - */ - LIST_INIT(&child->p_limits); - mtx_unlock(&hrl_lock); -} - static void hrl_deferred_psignal(struct proc *p, int signum) { @@ -368,6 +304,46 @@ } /* + * Go through all the rules applicable to the process, fill first array + * with amount of resource left before hitting next limit, and the second + * with pointers to the limit to be hit. + */ +/* + * XXX: We should sort the rules somewhat, so that 'log' rules are enforced + * before 'deny', if both are for the same subject, resource and amount. + */ +static void +hrl_compute_available(struct proc *p, int64_t (*availablep)[], + struct hrl_rule *(*rulesp)[]) +{ + int i, resource; + int64_t available; + struct hrl_limit *limit; + + mtx_assert(&hrl_lock, MA_OWNED); + + for (i = 0; i < HRL_RESOURCE_MAX; i++) { + (*availablep)[i] = INT64_MAX; + (*rulesp)[i] = NULL; + } + + mtx_assert(&hrl_lock, MA_OWNED); + + LIST_FOREACH(limit, &p->p_limits, hl_next) { + resource = limit->hl_rule->hr_resource; + available = limit->hl_rule->hr_amount - + p->p_usage.hu_resources[resource]; + /* Skip limits that have been already exceeded. */ + if (available < 0) + continue; + if (available < (*availablep)[resource]) { + (*availablep)[resource] = available; + (*rulesp)[resource] = limit->hl_rule; + } + } +} + +/* * Increase allocation of 'resource' by 'amount' for process 'p'. * Return 0 if it's below limits, or errno, if it's not. */ @@ -563,53 +539,6 @@ } void -hrl_adjust(int subject, id_t subject_id, int per, int resource, int action, int64_t amount) -{ - struct hrl_node searched, *node, *existing; - - searched.hn_rule.hr_subject = subject; - searched.hn_rule.hr_subject_id = subject_id; - searched.hn_rule.hr_per = per; - searched.hn_rule.hr_resource = resource; - searched.hn_rule.hr_action = action; - - /* - * Removing a limit. - */ - if (amount == -1) { - mtx_lock(&hrl_lock); - node = RB_FIND(hrl_tree, &hrls, &searched); - if (node != NULL) { - node = RB_REMOVE(hrl_tree, &hrls, node); - KASSERT(node != NULL, ("hrl_adjust: node removal failed")); - } - mtx_unlock(&hrl_lock); - if (node != NULL) { - hrl_rule_remove_limits(&node->hn_rule); - uma_zfree(hrl_node_zone, node); - } - return; - } - - /* - * Adding a new limit or changing existing one. - */ - node = uma_zalloc(hrl_node_zone, M_WAITOK); - *node = searched; - mtx_lock(&hrl_lock); - existing = RB_INSERT(hrl_tree, &hrls, node); - if (existing != NULL) - existing->hn_rule.hr_amount = amount; - else - node->hn_rule.hr_amount = amount; - mtx_unlock(&hrl_lock); - if (existing != NULL) - uma_zfree(hrl_node_zone, node); - else - hrl_rule_add_limits(&node->hn_rule); -} - -void hrl_usage_add(struct hrl_usage *dest, const struct hrl_usage *src) { int i; @@ -651,12 +580,13 @@ static int hrl_rule_matches(const struct hrl_rule *rule, const struct hrl_rule *filter) { + if (filter->hr_subject != HRL_SUBJECT_UNDEFINED) { if (rule->hr_subject != filter->hr_subject) return (0); } - if (filter->hr_subject_id >= 0) { + if (filter->hr_subject_id != HRL_SUBJECT_ID_UNDEFINED) { if (rule->hr_subject_id != filter->hr_subject_id) return (0); } @@ -733,120 +663,118 @@ } /* - * Add a new limit for a rule, if it's not already there. + * Add a new limit for a rule, increasing refcount for the rule. */ static void -hrl_limit_add_if_needed(struct hrl_limits_head *limits_head, struct hrl_rule *rule) +hrl_limit_add(struct hrl_limits_head *limits_head, struct hrl_rule *rule) +{ + struct hrl_limit *limit; + + hrl_rule_acquire(rule); + limit = uma_zalloc(hrl_limit_zone, M_WAITOK); + limit->hl_rule = rule; + + mtx_lock(&hrl_lock); + LIST_INSERT_HEAD(limits_head, limit, hl_next); + mtx_unlock(&hrl_lock); +} + +#if 0 +static int +hrl_limit_remove(struct hrl_limits_head *limits_head, struct hrl_rule *rule) { - struct hrl_limit *limit, *new_limit; + struct hrl_limit *limit, *limittmp; - new_limit = uma_zalloc(hrl_limit_zone, M_WAITOK); mtx_lock(&hrl_lock); - LIST_FOREACH(limit, limits_head, hl_next) { + LIST_FOREACH_SAFE(limit, limits_head, hl_next, limittmp) { if (limit->hl_rule == rule) { + LIST_REMOVE(limit, hl_next); mtx_unlock(&hrl_lock); - uma_zfree(hrl_limit_zone, new_limit); - return; + uma_zfree(hrl_limit_zone, limit); + hrl_rule_release(rule); + return (1); } } - new_limit->hl_rule = rule; - LIST_INSERT_HEAD(limits_head, new_limit, hl_next); mtx_unlock(&hrl_lock); + return (0); } +#endif /* - * Remove a limit for a rule. + * Remove limits for a rules matching the filter and release + * the refcounts for the rules, possibly freeing them. Returns + * the number of limit structures removed. */ -static void -hrl_limit_remove(struct hrl_limits_head *limits_head, struct hrl_rule *rule) +static int +hrl_limit_remove_matching(struct hrl_limits_head *limits_head, + const struct hrl_rule *filter) { + int removed = 0; struct hrl_limit *limit, *limittmp; +again: mtx_lock(&hrl_lock); LIST_FOREACH_SAFE(limit, limits_head, hl_next, limittmp) { - if (limit->hl_rule == rule) { + if (hrl_rule_matches(limit->hl_rule, filter)) { LIST_REMOVE(limit, hl_next); mtx_unlock(&hrl_lock); + hrl_rule_release(limit->hl_rule); uma_zfree(hrl_limit_zone, limit); - return; + removed++; + goto again; } } mtx_unlock(&hrl_lock); + return (removed); } -/* - * Bind a rule to subjects to which it applies. - */ -static void -hrl_rule_add_limits(struct hrl_rule *rule) +struct hrl_rule * +hrl_rule_alloc(void) { - struct proc *p; - struct uidinfo *uip; - struct gidinfo *gip; - struct prison *pr; + struct hrl_rule *rule; - switch (rule->hr_subject) { - case HRL_SUBJECT_PROCESS: - /* - * The sx lock is to keep the process from going away. - */ - sx_slock(&proctree_lock); - p = pfind(rule->hr_subject_id); - if (p == NULL) { - sx_sunlock(&proctree_lock); - return; - } + rule = uma_zalloc(hrl_rule_zone, M_WAITOK); + refcount_init(&rule->hr_refcount, 1); + return (rule); +} - PROC_UNLOCK(p); - hrl_limit_add_if_needed(&p->p_limits, rule); - sx_sunlock(&proctree_lock); - return; +void +hrl_rule_acquire(struct hrl_rule *rule) +{ - case HRL_SUBJECT_USER: - uip = uifind_existing(rule->hr_subject_id); - if (uip == NULL) - return; - hrl_limit_add_if_needed(&uip->ui_limits, rule); - uifree(uip); - return; + KASSERT(rule->hr_refcount > 0, ("rule->hr_refcount > 0")); - case HRL_SUBJECT_GROUP: - gip = gifind_existing(rule->hr_subject_id); - if (gip == NULL) - return; - hrl_limit_add_if_needed(&gip->gi_limits, rule); - gifree(gip); - return; + refcount_acquire(&rule->hr_refcount); +} - case HRL_SUBJECT_LOGINCLASS: - panic("hrl_rule_add_limits: HRL_SUBJECT_LOGINCLASS unimplemented"); - return; +void +hrl_rule_release(struct hrl_rule *rule) +{ - case HRL_SUBJECT_JAIL: - sx_xlock(&allprison_lock); - pr = prison_find(rule->hr_subject_id); - if (pr == NULL) { - sx_xunlock(&allprison_lock); - return; - } - hrl_limit_add_if_needed(&pr->pr_limits, rule); - prison_free(pr); - sx_xunlock(&allprison_lock); - return; + KASSERT(rule->hr_refcount > 0, ("rule->hr_refcount > 0")); - default: - panic("hrl_rule_add_limits: unknown subject %d", rule->hr_subject); - } + if (refcount_release(&rule->hr_refcount)) + uma_zfree(hrl_rule_zone, rule); } -static void -hrl_rule_remove_limits(struct hrl_rule *rule) +/* + * Link a rule with subjects to which it applies. + */ +int +hrl_rule_add(struct hrl_rule *rule) { struct proc *p; + struct ucred *cred; struct uidinfo *uip; struct gidinfo *gip; struct prison *pr; + struct loginclass *lc; + /* + * Make sure there are no duplicated rules. + */ + hrl_rule_remove(rule); + switch (rule->hr_subject) { case HRL_SUBJECT_PROCESS: /* @@ -856,83 +784,142 @@ p = pfind(rule->hr_subject_id); if (p == NULL) { sx_sunlock(&proctree_lock); - return; + return (ESRCH); } PROC_UNLOCK(p); - hrl_limit_remove(&p->p_limits, rule); + hrl_limit_add(&p->p_limits, rule); sx_sunlock(&proctree_lock); - return; + /* + * In case of per-process rule, we don't have anything more + * to do. + */ + return (0); case HRL_SUBJECT_USER: - uip = uifind_existing(rule->hr_subject_id); - if (uip == NULL) - return; - hrl_limit_remove(&uip->ui_limits, rule); - uifree(uip); - return; + uip = uifind(rule->hr_subject_id); + KASSERT(uip == NULL, ("hrl_rule_add: uifind failed")); + hrl_limit_add(&uip->ui_limits, rule); + /* + * Don't call uifree(2); we don't want the uidinfo + * to go away, because the rule should stay there even + * if there are no processes with that uid. The same + * applies to the cases below. + */ + break; case HRL_SUBJECT_GROUP: gip = gifind_existing(rule->hr_subject_id); - if (gip == NULL) - return; - hrl_limit_remove(&gip->gi_limits, rule); - gifree(gip); - return; + KASSERT(gip == NULL, ("hrl_rule_add: gifind failed")); + hrl_limit_add(&gip->gi_limits, rule); + break; case HRL_SUBJECT_LOGINCLASS: - panic("hrl_rule_remove: HRL_SUBJECT_LOGINCLASS unimplemented"); - return; + KASSERT(rule->hr_subject_id > 0, ("rule->hr_subject_id > 0")); + lc = (struct loginclass *)(long)rule->hr_subject_id; + hrl_limit_add(&lc->lc_limits, rule); + break; case HRL_SUBJECT_JAIL: sx_xlock(&allprison_lock); pr = prison_find(rule->hr_subject_id); if (pr == NULL) { sx_xunlock(&allprison_lock); - return; + return (ESRCH); } - hrl_limit_remove(&pr->pr_limits, rule); - prison_free(pr); + hrl_limit_add(&pr->pr_limits, rule); sx_xunlock(&allprison_lock); - return; + break; default: - panic("hrl_rule_remove: unknown subject %d", rule->hr_subject); + panic("hrl_rule_add_limits: unknown subject %d", + rule->hr_subject); + } + + /* + * Now go through all the processes and add the new rule to the ones + * it applies to. + */ + sx_slock(&proctree_lock); + FOREACH_PROC_IN_SYSTEM(p) { + cred = p->p_ucred; + switch (rule->hr_subject) { + case HRL_SUBJECT_USER: + if (cred->cr_uid == rule->hr_subject_id || + cred->cr_ruid == rule->hr_subject_id) + break; + continue; + case HRL_SUBJECT_GROUP: + if (groupmember(rule->hr_subject_id, cred)) + break; + continue; + case HRL_SUBJECT_LOGINCLASS: + lc = (struct loginclass *)(long)rule->hr_subject_id; /* XXX: This line is here to remove cc warning; investigate. */ + if (p->p_loginclass == lc) + break; + continue; + case HRL_SUBJECT_JAIL: + for (pr = cred->cr_prison; pr != NULL; pr = pr->pr_parent) + if (pr->pr_id == rule->hr_subject_id) + break; + continue; + default: + panic("hrl_rule_add_limits: unknown subject %d", + rule->hr_subject); + } + + hrl_limit_add(&p->p_limits, rule); } + sx_sunlock(&proctree_lock); + + return (0); } /* - * Add a rule to the ruleset or change the 'hr_amount' of existing - * rule. + * Remove all rules that match the filter. */ -static int -hrl_rule_add(struct hrl_rule *rule) +int +hrl_rule_remove(const struct hrl_rule *filter) { - struct hrl_node *node, *existing; + int error, found = 0; + struct proc *p; + + if (filter->hr_subject == HRL_SUBJECT_PROCESS && + filter->hr_subject_id != HRL_SUBJECT_ID_UNDEFINED) { + sx_slock(&proctree_lock); + p = pfind(filter->hr_subject_id); + if (p == NULL) + return (ESRCH); + PROC_UNLOCK(p); + error = hrl_limit_remove_matching(&p->p_limits, filter); + sx_sunlock(&proctree_lock); + return (error); + } - node = uma_zalloc(hrl_node_zone, M_WAITOK); - node->hn_rule = *rule; + /* + * XXX: per-process, per-group, per-jail and per-class limits. + */ - mtx_lock(&hrl_lock); - existing = RB_INSERT(hrl_tree, &hrls, node); - if (existing != NULL) - existing->hn_rule.hr_amount = rule->hr_amount; - mtx_unlock(&hrl_lock); + sx_slock(&proctree_lock); + FOREACH_PROC_IN_SYSTEM(p) { + error = hrl_limit_remove_matching(&p->p_limits, filter); + if (error == 0) + found = 1; + } + sx_sunlock(&proctree_lock); - if (existing != NULL) - uma_zfree(hrl_node_zone, node); + if (found) + return (0); else - hrl_rule_add_limits(rule); - - return (0); + return (ESRCH); } - static int hrl_rule_parse(struct hrl_rule *rule, char *rulestr) { int error; - char *subjectstr, *subject_idstr, *resourcestr, *actionstr, *amountstr, *perstr; + char *subjectstr, *subject_idstr, *resourcestr, *actionstr, + *amountstr, *perstr; subjectstr = strsep(&rulestr, ":"); subject_idstr = strsep(&rulestr, ":"); @@ -950,7 +937,7 @@ } if (subject_idstr == NULL || subject_idstr[0] == '\0') - rule->hr_subject_id = -1; + rule->hr_subject_id = HRL_SUBJECT_ID_UNDEFINED; else { error = str2id(subject_idstr, &rule->hr_subject_id); if (error) @@ -960,7 +947,8 @@ if (resourcestr == NULL || resourcestr[0] == '\0') rule->hr_resource = HRL_RESOURCE_UNDEFINED; else { - error = str2value(resourcestr, &rule->hr_resource, resourcenames); + error = str2value(resourcestr, &rule->hr_resource, + resourcenames); if (error) return (EINVAL); } @@ -989,7 +977,8 @@ return (EINVAL); } - if (rule->hr_subject_id > 0 && rule->hr_subject == HRL_SUBJECT_UNDEFINED) + if (rule->hr_subject_id != HRL_SUBJECT_ID_UNDEFINED && + rule->hr_subject == HRL_SUBJECT_UNDEFINED) return (EINVAL); return (0); @@ -1021,105 +1010,6 @@ } /* - * XXX: We should sort the rules somewhat, so that 'log' rules are enforced - * before 'deny', if both are for the same subject, resource and amount. - */ -static void -_hrl_compute_available(struct hrl_limits_head *limits_head, - struct hrl_usage *usage, int64_t (*availablep)[], - struct hrl_rule *(*rulesp)[]) -{ - int resource; - int64_t available; - struct hrl_limit *limit; - - mtx_assert(&hrl_lock, MA_OWNED); - - LIST_FOREACH(limit, limits_head, hl_next) { - resource = limit->hl_rule->hr_resource; - available = limit->hl_rule->hr_amount - - usage->hu_resources[resource]; - /* Skip limits that have been already exceeded. */ - if (available < 0) - continue; - if (available < (*availablep)[resource]) { - (*availablep)[resource] = available; - (*rulesp)[resource] = limit->hl_rule; - } - } -} - -/* - * Go through all the rules applicable to the process, fill first array - * with amount of resource left before hitting next limit, and the second - * with pointers to the limit to be hit. - */ -static void -hrl_compute_available(struct proc *p, int64_t (*availablep)[], - struct hrl_rule *(*rulesp)[]) -{ - int i; - struct ucred *cred; - struct uidinfo *ui, *rui; - struct prison *pr; - - mtx_assert(&hrl_lock, MA_OWNED); - - for (i = 0; i < HRL_RESOURCE_MAX; i++) { - (*availablep)[i] = INT64_MAX; - (*rulesp)[i] = NULL; - } - - cred = p->p_ucred; - ui = cred->cr_uidinfo; - rui = cred->cr_ruidinfo; - pr = cred->cr_prison; - _hrl_compute_available(&p->p_limits, &p->p_usage, availablep, rulesp); - _hrl_compute_available(&ui->ui_limits, &ui->ui_usage, availablep, rulesp); - _hrl_compute_available(&rui->ui_limits, &rui->ui_usage, availablep, - rulesp); - if (hrl_group_accounting) { - /* - * XXX: Groups. - */ - } - _hrl_compute_available(&pr->pr_limits, &pr->pr_usage, availablep, - rulesp); -} - -/* - * Remove a rule from the ruleset. - */ -static int -hrl_rule_remove(struct hrl_rule *rule) -{ - int found = 1; - struct hrl_node *node, *next; - -restart: - mtx_lock(&hrl_lock); - for (node = RB_MIN(hrl_tree, &hrls); node != NULL; node = next) { - next = RB_NEXT(hrl_tree, &hrls, node); - if (!hrl_rule_matches(&node->hn_rule, rule)) - continue; - found = 1; - node = RB_REMOVE(hrl_tree, &hrls, node); - KASSERT(node != NULL, ("hrl_rule_remove: node removal failed")); - - mtx_unlock(&hrl_lock); - hrl_rule_remove_limits(&node->hn_rule); - uma_zfree(hrl_node_zone, node); - goto restart; - } - mtx_unlock(&hrl_lock); - - if (found == 0) - return (ENOENT); - - return (0); -} - -/* * Routine used by HRL syscalls to read in input string. */ static int @@ -1159,7 +1049,8 @@ sbuf_delete(outputsbuf); return (ERANGE); } - error = copyout(sbuf_data(outputsbuf), outbufp, sbuf_len(outputsbuf) + 1); + error = copyout(sbuf_data(outputsbuf), outbufp, + sbuf_len(outputsbuf) + 1); sbuf_delete(outputsbuf); return (error); } @@ -1280,91 +1171,141 @@ return (EINVAL); } + if (error) + return (error); + error = hrl_write_outbuf(outputsbuf, uap->outbufp, uap->outbuflen); return (error); } -static int -hrl_get_rules_2(struct thread *td, char *inputstr, struct sbuf **outputsbuf) +int +hrl_get_rules(struct thread *td, struct hrl_get_rules_args *uap) { - int error = 0, copied = 0; - struct hrl_rule *buf, filter; - struct hrl_node *node; + int error, copied, maxcopied = HRL_MAX_RULES; + char *inputstr; + struct sbuf *outputsbuf; + struct hrl_rule filter, *buf; + struct hrl_limit *limit; + struct proc *p; + + error = hrl_read_inbuf(&inputstr, uap->inbufp, uap->inbuflen); + if (error) + return (error); error = hrl_rule_parse(&filter, inputstr); + free(inputstr, M_HRL); if (error) return (error); buf = malloc(HRL_MAX_RULES * sizeof(struct hrl_rule), M_HRL, M_WAITOK); - /* - * Copy the limits to the temporary buffer. We cannot - * copy it directly to the userland because of the mutex. - */ - mtx_lock(&hrl_lock); - RB_FOREACH(node, hrl_tree, &hrls) { - /* - * XXX: Do not show everything to the client; just the - * nodes that affect him. - */ - if (copied >= HRL_MAX_RULES) { - /* - * XXX: Hey, what to do now? - */ - error = EDOOFUS; - break; +again: + buf = malloc(maxcopied * sizeof(*buf), M_HRL, M_WAITOK); + copied = 0; + sx_slock(&proctree_lock); + FOREACH_PROC_IN_SYSTEM(p) { + mtx_lock(&hrl_lock); + LIST_FOREACH(limit, &p->p_limits, hl_next) { + if (copied >= HRL_MAX_RULES) { + mtx_unlock(&hrl_lock); + sx_sunlock(&proctree_lock); + maxcopied *= 4; + free(buf, M_HRL); + goto again; + } + if (!hrl_rule_matches(limit->hl_rule, &filter)) + continue; + *(buf + copied) = *limit->hl_rule; + copied++; } - - if (!hrl_rule_matches(&node->hn_rule, &filter)) - continue; - - *(buf + copied) = node->hn_rule; - copied++; + mtx_unlock(&hrl_lock); } - mtx_unlock(&hrl_lock); + sx_sunlock(&proctree_lock); if (error) goto out; - *outputsbuf = hrl_rules_to_sbuf(buf, copied); + /* + * XXX: Iterate over the rest (other than per-process) of the rules. + */ + + outputsbuf = hrl_rules_to_sbuf(buf, copied); + + error = hrl_write_outbuf(outputsbuf, uap->outbufp, uap->outbuflen); out: free(buf, M_HRL); - return (error); } - int -hrl_get_rules(struct thread *td, struct hrl_get_rules_args *uap) +hrl_get_limits(struct thread *td, struct hrl_get_limits_args *uap) { - int error; + int error, copied, maxcopied = HRL_MAX_RULES; char *inputstr; struct sbuf *outputsbuf; + struct hrl_rule filter, *buf; + struct hrl_limit *limit; + struct proc *p; error = hrl_read_inbuf(&inputstr, uap->inbufp, uap->inbuflen); if (error) return (error); - error = hrl_get_rules_2(td, inputstr, &outputsbuf); + error = hrl_rule_parse(&filter, inputstr); free(inputstr, M_HRL); if (error) return (error); + if (filter.hr_subject == HRL_SUBJECT_UNDEFINED) + return (EINVAL); + + if (filter.hr_subject_id == HRL_SUBJECT_ID_UNDEFINED) + return (EINVAL); + + if (filter.hr_subject != HRL_SUBJECT_PROCESS) + return (EOPNOTSUPP); + +again: + buf = malloc(maxcopied * sizeof(*buf), M_HRL, M_WAITOK); + copied = 0; + + p = pfind(filter.hr_subject_id); + if (p == NULL) { + error = ESRCH; + goto out; + } + mtx_lock(&hrl_lock); + LIST_FOREACH(limit, &p->p_limits, hl_next) { + if (copied >= HRL_MAX_RULES) { + mtx_unlock(&hrl_lock); + PROC_UNLOCK(p); + maxcopied *= 4; + free(buf, M_HRL); + goto again; + } + if (!hrl_rule_matches(limit->hl_rule, &filter)) + continue; + *(buf + copied) = *limit->hl_rule; + copied++; + } + mtx_unlock(&hrl_lock); + PROC_UNLOCK(p); + if (error) + goto out; + + outputsbuf = hrl_rules_to_sbuf(buf, copied); + error = hrl_write_outbuf(outputsbuf, uap->outbufp, uap->outbuflen); +out: + free(buf, M_HRL); return (error); } int -hrl_get_limits(struct thread *td, struct hrl_get_limits_args *uap) -{ - return (EDOOFUS); -} - -int hrl_add_rule(struct thread *td, struct hrl_add_rule_args *uap) { int error; - struct hrl_rule rule; + struct hrl_rule *rule; char *inputstr; error = priv_check(td, PRIV_HRL_SET); @@ -1375,20 +1316,36 @@ if (error) return (error); - error = hrl_rule_parse(&rule, inputstr); + rule = hrl_rule_alloc(); + + error = hrl_rule_parse(rule, inputstr); free(inputstr, M_HRL); if (error) - return (error); >>> TRUNCATED FOR MAIL (1000 lines) <<<