]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - usr.bin/rctl/rctl.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / usr.bin / rctl / rctl.c
1 /*-
2  * Copyright (c) 2010 The FreeBSD Foundation
3  * All rights reserved.
4  *
5  * This software was developed by Edward Tomasz Napierala under sponsorship
6  * from the FreeBSD Foundation.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * $FreeBSD$
30  */
31
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34
35 #include <sys/types.h>
36 #include <sys/rctl.h>
37 #include <assert.h>
38 #include <ctype.h>
39 #include <err.h>
40 #include <errno.h>
41 #include <getopt.h>
42 #include <grp.h>
43 #include <libutil.h>
44 #include <pwd.h>
45 #include <stdint.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49
50 #define RCTL_DEFAULT_BUFSIZE    4096
51
52 static id_t
53 parse_user(const char *s)
54 {
55         id_t id;
56         char *end;
57         struct passwd *pwd;
58
59         pwd = getpwnam(s);
60         if (pwd != NULL)
61                 return (pwd->pw_uid);
62
63         if (!isnumber(s[0]))
64                 errx(1, "uknown user '%s'", s);
65
66         id = strtod(s, &end);
67         if ((size_t)(end - s) != strlen(s))
68                 errx(1, "trailing characters after numerical id");
69
70         return (id);
71 }
72
73 static id_t
74 parse_group(const char *s)
75 {
76         id_t id;
77         char *end;
78         struct group *grp;
79
80         grp = getgrnam(s);
81         if (grp != NULL)
82                 return (grp->gr_gid);
83
84         if (!isnumber(s[0]))
85                 errx(1, "uknown group '%s'", s);
86
87         id = strtod(s, &end);
88         if ((size_t)(end - s) != strlen(s))
89                 errx(1, "trailing characters after numerical id");
90
91         return (id);
92 }
93
94 /*
95  * This routine replaces user/group name with numeric id.
96  */
97 static char *
98 resolve_ids(char *rule)
99 {
100         id_t id;
101         const char *subject, *textid, *rest;
102         char *resolved;
103
104         subject = strsep(&rule, ":");
105         textid = strsep(&rule, ":");
106         if (textid == NULL)
107                 errx(1, "error in rule specification -- no subject");
108         if (rule != NULL)
109                 rest = rule;
110         else
111                 rest = "";
112
113         if (strcasecmp(subject, "u") == 0)
114                 subject = "user";
115         else if (strcasecmp(subject, "g") == 0)
116                 subject = "group";
117         else if (strcasecmp(subject, "p") == 0)
118                 subject = "process";
119         else if (strcasecmp(subject, "l") == 0 ||
120             strcasecmp(subject, "c") == 0 ||
121             strcasecmp(subject, "class") == 0)
122                 subject = "loginclass";
123         else if (strcasecmp(subject, "j") == 0)
124                 subject = "jail";
125
126         if (strcasecmp(subject, "user") == 0 && strlen(textid) > 0) {
127                 id = parse_user(textid);
128                 asprintf(&resolved, "%s:%d:%s", subject, (int)id, rest);
129         } else if (strcasecmp(subject, "group") == 0 && strlen(textid) > 0) {
130                 id = parse_group(textid);
131                 asprintf(&resolved, "%s:%d:%s", subject, (int)id, rest);
132         } else
133                 asprintf(&resolved, "%s:%s:%s", subject, textid, rest);
134
135         if (resolved == NULL)
136                 err(1, "asprintf");
137
138         return (resolved);
139 }
140
141 /*
142  * This routine replaces "human-readable" number with its expanded form.
143  */
144 static char *
145 expand_amount(char *rule)
146 {
147         uint64_t num;
148         const char *subject, *subject_id, *resource, *action, *amount, *per;
149         char *copy, *expanded;
150
151         copy = strdup(rule);
152         if (copy == NULL)
153                 err(1, "strdup");
154
155         subject = strsep(&copy, ":");
156         subject_id = strsep(&copy, ":");
157         resource = strsep(&copy, ":");
158         action = strsep(&copy, "=/");
159         amount = strsep(&copy, "/");
160         per = copy;
161
162         if (amount == NULL || strlen(amount) == 0) {
163                 free(copy);
164                 return (rule);
165         }
166
167         assert(subject != NULL);
168         assert(subject_id != NULL);
169         assert(resource != NULL);
170         assert(action != NULL);
171
172         if (expand_number(amount, &num))
173                 err(1, "expand_number");
174
175         if (per == NULL)
176                 asprintf(&expanded, "%s:%s:%s:%s=%ju", subject, subject_id,
177                     resource, action, (uintmax_t)num);
178         else
179                 asprintf(&expanded, "%s:%s:%s:%s=%ju/%s", subject, subject_id,
180                     resource, action, (uintmax_t)num, per);
181
182         if (expanded == NULL)
183                 err(1, "asprintf");
184
185         return (expanded);
186 }
187
188 static char *
189 humanize_ids(char *rule)
190 {
191         id_t id;
192         struct passwd *pwd;
193         struct group *grp;
194         const char *subject, *textid, *rest;
195         char *humanized;
196
197         subject = strsep(&rule, ":");
198         textid = strsep(&rule, ":");
199         if (textid == NULL)
200                 errx(1, "rule passed from the kernel didn't contain subject");
201         if (rule != NULL)
202                 rest = rule;
203         else
204                 rest = "";
205
206         /* Replace numerical user and group ids with names. */
207         if (strcasecmp(subject, "user") == 0) {
208                 id = parse_user(textid);
209                 pwd = getpwuid(id);
210                 if (pwd != NULL)
211                         textid = pwd->pw_name;
212         } else if (strcasecmp(subject, "group") == 0) {
213                 id = parse_group(textid);
214                 grp = getgrgid(id);
215                 if (grp != NULL)
216                         textid = grp->gr_name;
217         }
218
219         asprintf(&humanized, "%s:%s:%s", subject, textid, rest);
220
221         if (humanized == NULL)
222                 err(1, "asprintf");
223
224         return (humanized);
225 }
226
227 static int
228 str2int64(const char *str, int64_t *value)
229 {
230         char *end;
231
232         if (str == NULL)
233                 return (EINVAL);
234
235         *value = strtoul(str, &end, 10);
236         if ((size_t)(end - str) != strlen(str))
237                 return (EINVAL);
238
239         return (0);
240 }
241
242 static char *
243 humanize_amount(char *rule)
244 {
245         int64_t num;
246         const char *subject, *subject_id, *resource, *action, *amount, *per;
247         char *copy, *humanized, buf[6];
248
249         copy = strdup(rule);
250         if (copy == NULL)
251                 err(1, "strdup");
252
253         subject = strsep(&copy, ":");
254         subject_id = strsep(&copy, ":");
255         resource = strsep(&copy, ":");
256         action = strsep(&copy, "=/");
257         amount = strsep(&copy, "/");
258         per = copy;
259
260         if (amount == NULL || strlen(amount) == 0 ||
261             str2int64(amount, &num) != 0) {
262                 free(copy);
263                 return (rule);
264         }
265
266         assert(subject != NULL);
267         assert(subject_id != NULL);
268         assert(resource != NULL);
269         assert(action != NULL);
270
271         if (humanize_number(buf, sizeof(buf), num, "", HN_AUTOSCALE,
272             HN_DECIMAL | HN_NOSPACE) == -1)
273                 err(1, "humanize_number");
274
275         if (per == NULL)
276                 asprintf(&humanized, "%s:%s:%s:%s=%s", subject, subject_id,
277                     resource, action, buf);
278         else
279                 asprintf(&humanized, "%s:%s:%s:%s=%s/%s", subject, subject_id,
280                     resource, action, buf, per);
281
282         if (humanized == NULL)
283                 err(1, "asprintf");
284
285         return (humanized);
286 }
287
288 /*
289  * Print rules, one per line.
290  */
291 static void
292 print_rules(char *rules, int hflag, int nflag)
293 {
294         char *rule;
295
296         while ((rule = strsep(&rules, ",")) != NULL) {
297                 if (rule[0] == '\0')
298                         break; /* XXX */
299                 if (nflag == 0)
300                         rule = humanize_ids(rule);
301                 if (hflag)
302                         rule = humanize_amount(rule);
303                 printf("%s\n", rule);
304         }
305 }
306
307 static void
308 add_rule(char *rule)
309 {
310         int error;
311
312         error = rctl_add_rule(rule, strlen(rule) + 1, NULL, 0);
313         if (error != 0)
314                 err(1, "rctl_add_rule");
315         free(rule);
316 }
317
318 static void
319 show_limits(char *filter, int hflag, int nflag)
320 {
321         int error;
322         char *outbuf = NULL;
323         size_t outbuflen = RCTL_DEFAULT_BUFSIZE / 4;
324
325         do {
326                 outbuflen *= 4;
327                 outbuf = realloc(outbuf, outbuflen);
328                 if (outbuf == NULL)
329                         err(1, "realloc");
330
331                 error = rctl_get_limits(filter, strlen(filter) + 1, outbuf,
332                     outbuflen);
333                 if (error && errno != ERANGE)
334                         err(1, "rctl_get_limits");
335         } while (error && errno == ERANGE);
336
337         print_rules(outbuf, hflag, nflag);
338         free(filter);
339         free(outbuf);
340 }
341
342 static void
343 remove_rule(char *filter)
344 {
345         int error;
346
347         error = rctl_remove_rule(filter, strlen(filter) + 1, NULL, 0);
348         if (error != 0)
349                 err(1, "rctl_remove_rule");
350         free(filter);
351 }
352
353 static char *
354 humanize_usage_amount(char *usage)
355 {
356         int64_t num;
357         const char *resource, *amount;
358         char *copy, *humanized, buf[6];
359
360         copy = strdup(usage);
361         if (copy == NULL)
362                 err(1, "strdup");
363
364         resource = strsep(&copy, "=");
365         amount = copy;
366
367         assert(resource != NULL);
368         assert(amount != NULL);
369
370         if (str2int64(amount, &num) != 0 || 
371             humanize_number(buf, sizeof(buf), num, "", HN_AUTOSCALE,
372             HN_DECIMAL | HN_NOSPACE) == -1) {
373                 free(copy);
374                 return (usage);
375         }
376
377         asprintf(&humanized, "%s=%s", resource, buf);
378         if (humanized == NULL)
379                 err(1, "asprintf");
380
381         return (humanized);
382 }
383
384 /*
385  * Query the kernel about a resource usage and print it out.
386  */
387 static void
388 show_usage(char *filter, int hflag)
389 {
390         int error;
391         char *outbuf = NULL, *tmp;
392         size_t outbuflen = RCTL_DEFAULT_BUFSIZE / 4;
393
394         do {
395                 outbuflen *= 4;
396                 outbuf = realloc(outbuf, outbuflen);
397                 if (outbuf == NULL)
398                         err(1, "realloc");
399
400                 error = rctl_get_racct(filter, strlen(filter) + 1, outbuf,
401                     outbuflen);
402                 if (error && errno != ERANGE)
403                         err(1, "rctl_get_racct");
404         } while (error && errno == ERANGE);
405
406         while ((tmp = strsep(&outbuf, ",")) != NULL) {
407                 if (tmp[0] == '\0')
408                         break; /* XXX */
409
410                 if (hflag)
411                         tmp = humanize_usage_amount(tmp);
412
413                 printf("%s\n", tmp);
414         }
415
416         free(filter);
417         free(outbuf);
418 }
419
420 /*
421  * Query the kernel about resource limit rules and print them out.
422  */
423 static void
424 show_rules(char *filter, int hflag, int nflag)
425 {
426         int error;
427         char *outbuf = NULL;
428         size_t filterlen, outbuflen = RCTL_DEFAULT_BUFSIZE / 4;
429
430         if (filter != NULL)
431                 filterlen = strlen(filter) + 1;
432         else
433                 filterlen = 0;
434
435         do {
436                 outbuflen *= 4;
437                 outbuf = realloc(outbuf, outbuflen);
438                 if (outbuf == NULL)
439                         err(1, "realloc");
440
441                 error = rctl_get_rules(filter, filterlen, outbuf, outbuflen);
442                 if (error && errno != ERANGE)
443                         err(1, "rctl_get_rules");
444         } while (error && errno == ERANGE);
445
446         print_rules(outbuf, hflag, nflag);
447         free(outbuf);
448 }
449
450 static void
451 usage(void)
452 {
453
454         fprintf(stderr, "usage: rctl [ -h ] [-a rule | -l filter | -r filter "
455             "| -u filter | filter]\n");
456         exit(1);
457 }
458
459 int
460 main(int argc __unused, char **argv __unused)
461 {
462         int ch, aflag = 0, hflag = 0, nflag = 0, lflag = 0, rflag = 0,
463             uflag = 0;
464         char *rule = NULL;
465
466         while ((ch = getopt(argc, argv, "a:hl:nr:u:")) != -1) {
467                 switch (ch) {
468                 case 'a':
469                         aflag = 1;
470                         rule = strdup(optarg);
471                         break;
472                 case 'h':
473                         hflag = 1;
474                         break;
475                 case 'l':
476                         lflag = 1;
477                         rule = strdup(optarg);
478                         break;
479                 case 'n':
480                         nflag = 1;
481                         break;
482                 case 'r':
483                         rflag = 1;
484                         rule = strdup(optarg);
485                         break;
486                 case 'u':
487                         uflag = 1;
488                         rule = strdup(optarg);
489                         break;
490
491                 case '?':
492                 default:
493                         usage();
494                 }
495         }
496
497         argc -= optind;
498         argv += optind;
499
500         if (argc > 1)
501                 usage();
502
503         if (rule == NULL) {
504                 if (argc == 1)
505                         rule = strdup(argv[0]);
506                 else
507                         rule = strdup("::");
508         }
509
510         if (aflag + lflag + rflag + uflag + argc > 1)
511                 errx(1, "only one flag or argument may be specified "
512                     "at the same time");
513
514         rule = resolve_ids(rule);
515         rule = expand_amount(rule);
516
517         if (aflag) {
518                 add_rule(rule);
519                 return (0);
520         }
521
522         if (lflag) {
523                 show_limits(rule, hflag, nflag);
524                 return (0);
525         }
526
527         if (rflag) {
528                 remove_rule(rule);
529                 return (0);
530         }
531
532         if (uflag) {
533                 show_usage(rule, hflag);
534                 return (0);
535         }
536
537         show_rules(rule, hflag, nflag);
538         return (0);
539 }