Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 2 Aug 2009 10:16:35 GMT
From:      Edward Tomasz Napierala <trasz@FreeBSD.org>
To:        Perforce Change Reviews <perforce@freebsd.org>
Subject:   PERFORCE change 166914 for review
Message-ID:  <200908021016.n72AGZW0077407@repoman.freebsd.org>

next in thread | raw e-mail | index | archive | help
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 <sys/jail.h>
 #include <sys/ktr.h>
 #include <sys/lock.h>
+#include <sys/loginclass.h>
 #include <sys/mount.h>
 #include <sys/mutex.h>
 #include <sys/syscallsubr.h>

==== //depot/projects/soc2009/trasz_limits/sys/kern/kern_hrl.c#34 (text+ko) ====

@@ -31,9 +31,11 @@
 #include <sys/param.h>
 #include <sys/malloc.h>
 #include <sys/queue.h>
+#include <sys/refcount.h>
 #include <sys/jail.h>
 #include <sys/kernel.h>
 #include <sys/limits.h>
+#include <sys/loginclass.h>
 #include <sys/priv.h>
 #include <sys/proc.h>
 #include <sys/resourcevar.h>
@@ -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) <<<



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200908021016.n72AGZW0077407>