Author: trasz
Date: Sun Nov 29 12:01:36 2015
New Revision: 291447
URL: https://svnweb.freebsd.org/changeset/base/291447

Log:
  Rewrite the rctl(8) utility to make it possible to add multiple rules
  in a single run.  This speeds up operation with large rulesets.
  
  MFC after:    1 month
  Sponsored by: The FreeBSD Foundation

Modified:
  head/usr.bin/rctl/rctl.8
  head/usr.bin/rctl/rctl.c

Modified: head/usr.bin/rctl/rctl.8
==============================================================================
--- head/usr.bin/rctl/rctl.8    Sun Nov 29 11:37:25 2015        (r291446)
+++ head/usr.bin/rctl/rctl.8    Sun Nov 29 12:01:36 2015        (r291447)
@@ -25,7 +25,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd November 5, 2015
+.Dd November 29, 2015
 .Dt RCTL 8
 .Os
 .Sh NAME
@@ -35,22 +35,22 @@
 .Nm
 .Op Fl h
 .Op Fl n
-.Op Ar filter
+.Op Ar filter Ar ...
 .Nm
 .Fl a
-.Ar rule
+.Ar rule Ar ...
 .Nm
 .Fl l
 .Op Fl h
 .Op Fl n
-.Ar filter
+.Ar filter Ar ...
 .Nm
 .Fl r
-.Ar filter
+.Ar filter Ar ...
 .Nm
 .Fl u
 .Op Fl h
-.Ar filter
+.Ar filter Ar ...
 .Pp
 .Nm
 requires the kernel to be compiled with:

Modified: head/usr.bin/rctl/rctl.c
==============================================================================
--- head/usr.bin/rctl/rctl.c    Sun Nov 29 11:37:25 2015        (r291446)
+++ head/usr.bin/rctl/rctl.c    Sun Nov 29 12:01:36 2015        (r291447)
@@ -43,6 +43,7 @@ __FBSDID("$FreeBSD$");
 #include <grp.h>
 #include <libutil.h>
 #include <pwd.h>
+#include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -50,97 +51,60 @@ __FBSDID("$FreeBSD$");
 
 #define        RCTL_DEFAULT_BUFSIZE    128 * 1024
 
-static id_t
-parse_user(const char *s)
+static int
+parse_user(const char *s, id_t *uidp)
 {
-       id_t id;
        char *end;
        struct passwd *pwd;
 
        pwd = getpwnam(s);
-       if (pwd != NULL)
-               return (pwd->pw_uid);
+       if (pwd != NULL) {
+               *uidp = pwd->pw_uid;
+               return (0);
+       }
 
-       if (!isnumber(s[0]))
-               errx(1, "uknown user '%s'", s);
+       if (!isnumber(s[0])) {
+               warnx("uknown user '%s'", s);
+               return (1);
+       }
 
-       id = strtod(s, &end);
-       if ((size_t)(end - s) != strlen(s))
-               errx(1, "trailing characters after numerical id");
+       *uidp = strtod(s, &end);
+       if ((size_t)(end - s) != strlen(s)) {
+               warnx("trailing characters after numerical id");
+               return (1);
+       }
 
-       return (id);
+       return (0);
 }
 
-static id_t
-parse_group(const char *s)
+static int
+parse_group(const char *s, id_t *gidp)
 {
-       id_t id;
        char *end;
        struct group *grp;
 
        grp = getgrnam(s);
-       if (grp != NULL)
-               return (grp->gr_gid);
-
-       if (!isnumber(s[0]))
-               errx(1, "uknown group '%s'", s);
-
-       id = strtod(s, &end);
-       if ((size_t)(end - s) != strlen(s))
-               errx(1, "trailing characters after numerical id");
-
-       return (id);
-}
-
-/*
- * This routine replaces user/group name with numeric id.
- */
-static char *
-resolve_ids(char *rule)
-{
-       id_t id;
-       const char *subject, *textid, *rest;
-       char *resolved;
-
-       subject = strsep(&rule, ":");
-       textid = strsep(&rule, ":");
-       if (textid == NULL)
-               errx(1, "error in rule specification -- no subject");
-       if (rule != NULL)
-               rest = rule;
-       else
-               rest = "";
-
-       if (strcasecmp(subject, "u") == 0)
-               subject = "user";
-       else if (strcasecmp(subject, "g") == 0)
-               subject = "group";
-       else if (strcasecmp(subject, "p") == 0)
-               subject = "process";
-       else if (strcasecmp(subject, "l") == 0 ||
-           strcasecmp(subject, "c") == 0 ||
-           strcasecmp(subject, "class") == 0)
-               subject = "loginclass";
-       else if (strcasecmp(subject, "j") == 0)
-               subject = "jail";
+       if (grp != NULL) {
+               *gidp = grp->gr_gid;
+               return (0);
+       }
 
-       if (strcasecmp(subject, "user") == 0 && strlen(textid) > 0) {
-               id = parse_user(textid);
-               asprintf(&resolved, "%s:%d:%s", subject, (int)id, rest);
-       } else if (strcasecmp(subject, "group") == 0 && strlen(textid) > 0) {
-               id = parse_group(textid);
-               asprintf(&resolved, "%s:%d:%s", subject, (int)id, rest);
-       } else
-               asprintf(&resolved, "%s:%s:%s", subject, textid, rest);
+       if (!isnumber(s[0])) {
+               warnx("uknown group '%s'", s);
+               return (1);
+       }
 
-       if (resolved == NULL)
-               err(1, "asprintf");
+       *gidp = strtod(s, &end);
+       if ((size_t)(end - s) != strlen(s)) {
+               warnx("trailing characters after numerical id");
+               return (1);
+       }
 
-       return (resolved);
+       return (0);
 }
 
 /*
- * This routine replaces "human-readable" number with its expanded form.
+ * Replace human-readable number with its expanded form.
  */
 static char *
 expand_amount(char *rule)
@@ -150,8 +114,10 @@ expand_amount(char *rule)
        char *copy, *expanded;
 
        copy = strdup(rule);
-       if (copy == NULL)
-               err(1, "strdup");
+       if (copy == NULL) {
+               warn("strdup");
+               return (NULL);
+       }
 
        subject = strsep(&copy, ":");
        subject_id = strsep(&copy, ":");
@@ -170,8 +136,11 @@ expand_amount(char *rule)
        assert(resource != NULL);
        assert(action != NULL);
 
-       if (expand_number(amount, &num))
-               err(1, "expand_number");
+       if (expand_number(amount, &num)) {
+               warnx("invalid numeric value '%s'", amount);
+               free(copy);
+               return (NULL);
+       }
 
        if (per == NULL)
                asprintf(&expanded, "%s:%s:%s:%s=%ju", subject, subject_id,
@@ -180,12 +149,72 @@ expand_amount(char *rule)
                asprintf(&expanded, "%s:%s:%s:%s=%ju/%s", subject, subject_id,
                    resource, action, (uintmax_t)num, per);
 
-       if (expanded == NULL)
-               err(1, "asprintf");
+       if (expanded == NULL) {
+               warn("asprintf");
+               free(copy);
+               return (NULL);
+       }
 
        return (expanded);
 }
 
+
+static char *
+expand_rule(char *rule, bool resolve_ids)
+{
+       id_t id;
+       const char *subject, *textid, *rest;
+       char *resolved;
+       int error;
+
+       subject = strsep(&rule, ":");
+       textid = strsep(&rule, ":");
+       if (textid == NULL) {
+               warnx("error in rule specification -- no subject");
+               return (NULL);
+       }
+       if (rule != NULL)
+               rest = rule;
+       else
+               rest = "";
+
+       if (strcasecmp(subject, "u") == 0)
+               subject = "user";
+       else if (strcasecmp(subject, "g") == 0)
+               subject = "group";
+       else if (strcasecmp(subject, "p") == 0)
+               subject = "process";
+       else if (strcasecmp(subject, "l") == 0 ||
+           strcasecmp(subject, "c") == 0 ||
+           strcasecmp(subject, "class") == 0)
+               subject = "loginclass";
+       else if (strcasecmp(subject, "j") == 0)
+               subject = "jail";
+
+       if (resolve_ids &&
+           strcasecmp(subject, "user") == 0 && strlen(textid) > 0) {
+               error = parse_user(textid, &id);
+               if (error != 0)
+                       return (NULL);
+               asprintf(&resolved, "%s:%d:%s", subject, (int)id, rest);
+       } else if (resolve_ids &&
+           strcasecmp(subject, "group") == 0 && strlen(textid) > 0) {
+               error = parse_group(textid, &id);
+               if (error != 0)
+                       return (NULL);
+               asprintf(&resolved, "%s:%d:%s", subject, (int)id, rest);
+       } else {
+               asprintf(&resolved, "%s:%s:%s", subject, textid, rest);
+       }
+
+       if (resolved == NULL) {
+               warn("asprintf");
+               return (NULL);
+       }
+
+       return (expand_amount(resolved));
+}
+
 static char *
 humanize_ids(char *rule)
 {
@@ -330,8 +359,8 @@ enosys(void)
                errx(1, "RACCT/RCTL present, but disabled; enable using 
kern.racct.enable=1 tunable");
 }
 
-static void
-add_rule(char *rule)
+static int
+add_rule(const char *rule)
 {
        int error;
 
@@ -339,13 +368,14 @@ add_rule(char *rule)
        if (error != 0) {
                if (errno == ENOSYS)
                        enosys();
-               err(1, "rctl_add_rule");
+               warn("rctl_add_rule");
        }
-       free(rule);
+
+       return (error);
 }
 
-static void
-show_limits(char *filter, int hflag, int nflag)
+static int
+show_limits(const char *filter, int hflag, int nflag)
 {
        int error;
        char *outbuf = NULL;
@@ -362,17 +392,18 @@ show_limits(char *filter, int hflag, int
                if (error && errno != ERANGE) {
                        if (errno == ENOSYS)
                                enosys();
-                       err(1, "rctl_get_limits");
+                       warn("rctl_get_limits");
                }
        } while (error && errno == ERANGE);
 
        print_rules(outbuf, hflag, nflag);
-       free(filter);
        free(outbuf);
+
+       return (error);
 }
 
-static void
-remove_rule(char *filter)
+static int
+remove_rule(const char *filter)
 {
        int error;
 
@@ -380,9 +411,10 @@ remove_rule(char *filter)
        if (error != 0) {
                if (errno == ENOSYS)
                        enosys();
-               err(1, "rctl_remove_rule");
+               warn("rctl_remove_rule");
        }
-       free(filter);
+
+       return (error);
 }
 
 static char *
@@ -419,8 +451,8 @@ humanize_usage_amount(char *usage)
 /*
  * Query the kernel about a resource usage and print it out.
  */
-static void
-show_usage(char *filter, int hflag)
+static int
+show_usage(const char *filter, int hflag)
 {
        int error;
        char *outbuf = NULL, *tmp;
@@ -437,7 +469,7 @@ show_usage(char *filter, int hflag)
                if (error && errno != ERANGE) {
                        if (errno == ENOSYS)
                                enosys();
-                       err(1, "rctl_get_racct");
+                       warn("rctl_get_racct");
                }
        } while (error && errno == ERANGE);
 
@@ -451,15 +483,16 @@ show_usage(char *filter, int hflag)
                printf("%s\n", tmp);
        }
 
-       free(filter);
        free(outbuf);
+
+       return (error);
 }
 
 /*
  * Query the kernel about resource limit rules and print them out.
  */
-static void
-show_rules(char *filter, int hflag, int nflag)
+static int
+show_rules(const char *filter, int hflag, int nflag)
 {
        int error;
        char *outbuf = NULL;
@@ -480,12 +513,14 @@ show_rules(char *filter, int hflag, int 
                if (error && errno != ERANGE) {
                        if (errno == ENOSYS)
                                enosys();
-                       err(1, "rctl_get_rules");
+                       warn("rctl_get_rules");
                }
        } while (error && errno == ERANGE);
 
        print_rules(outbuf, hflag, nflag);
        free(outbuf);
+
+       return (error);
 }
 
 static void
@@ -503,30 +538,27 @@ main(int argc __unused, char **argv __un
        int ch, aflag = 0, hflag = 0, nflag = 0, lflag = 0, rflag = 0,
            uflag = 0;
        char *rule = NULL;
+       int i, cumulated_error;
 
-       while ((ch = getopt(argc, argv, "a:hl:nr:u:")) != -1) {
+       while ((ch = getopt(argc, argv, "ahlnru")) != -1) {
                switch (ch) {
                case 'a':
                        aflag = 1;
-                       rule = strdup(optarg);
                        break;
                case 'h':
                        hflag = 1;
                        break;
                case 'l':
                        lflag = 1;
-                       rule = strdup(optarg);
                        break;
                case 'n':
                        nflag = 1;
                        break;
                case 'r':
                        rflag = 1;
-                       rule = strdup(optarg);
                        break;
                case 'u':
                        uflag = 1;
-                       rule = strdup(optarg);
                        break;
 
                case '?':
@@ -537,44 +569,55 @@ main(int argc __unused, char **argv __un
 
        argc -= optind;
        argv += optind;
+       
+       if (aflag + lflag + rflag + uflag > 1)
+               errx(1, "at most one of -a, -l, -r, or -u may be specified");
 
-       if (argc > 1)
-               usage();
-
-       if (rule == NULL) {
-               if (argc == 1)
-                       rule = strdup(argv[0]);
-               else
+       if (argc == 0) {
+               if (aflag + lflag + rflag + uflag == 0) {
                        rule = strdup("::");
+                       show_rules(rule, hflag, nflag);
+
+                       return (0);
+               }
+
+               usage();
        }
 
-       if (aflag + lflag + rflag + uflag + argc > 1)
-               errx(1, "only one flag or argument may be specified "
-                   "at the same time");
+       cumulated_error = 0;
 
-       rule = resolve_ids(rule);
-       rule = expand_amount(rule);
+       for (i = 0; i < argc; i++) {
+               rule = argv[i];
 
-       if (aflag) {
-               add_rule(rule);
-               return (0);
-       }
+               /*
+                * Skip resolving if passed -n _and_ -a.  Ignore -n otherwise,
+                * so we can still do "rctl -n u:root" and see the rules without
+                * resolving the UID.
+                */
+               if (aflag != 0 && nflag != 0)
+                       rule = expand_rule(rule, false);
+               else
+                       rule = expand_rule(rule, true);
 
-       if (lflag) {
-               show_limits(rule, hflag, nflag);
-               return (0);
-       }
+               if (rule == NULL) {
+                       cumulated_error++;
+                       continue;
+               }
 
-       if (rflag) {
-               remove_rule(rule);
-               return (0);
-       }
+               if (aflag) {
+                       cumulated_error += add_rule(rule);
+               } else if (lflag) {
+                       cumulated_error += show_limits(rule, hflag, nflag);
+               } else if (rflag) {
+                       cumulated_error += remove_rule(rule);
+               } else if (uflag) {
+                       cumulated_error += show_usage(rule, hflag);
+               } else  {
+                       cumulated_error += show_rules(rule, hflag, nflag);
+               }
 
-       if (uflag) {
-               show_usage(rule, hflag);
-               return (0);
+               free(rule);
        }
 
-       show_rules(rule, hflag, nflag);
-       return (0);
+       return (cumulated_error);
 }
_______________________________________________
[email protected] mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "[email protected]"

Reply via email to