Add rctl(8), the utility to manage rctl rules.
Sponsored by: The FreeBSD Foundation Reviewed by: kib (earlier version)
This commit is contained in:
parent
4f8c32aa62
commit
6ec54a57b4
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=220166
@ -126,6 +126,7 @@ SUBDIR= alias \
|
|||||||
printenv \
|
printenv \
|
||||||
printf \
|
printf \
|
||||||
procstat \
|
procstat \
|
||||||
|
rctl \
|
||||||
renice \
|
renice \
|
||||||
rev \
|
rev \
|
||||||
revoke \
|
revoke \
|
||||||
|
11
usr.bin/rctl/Makefile
Normal file
11
usr.bin/rctl/Makefile
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# $FreeBSD$
|
||||||
|
|
||||||
|
PROG= rctl
|
||||||
|
MAN= rctl.8
|
||||||
|
|
||||||
|
DPADD= ${LIBUTIL}
|
||||||
|
LDADD= -lutil
|
||||||
|
|
||||||
|
WARNS?= 6
|
||||||
|
|
||||||
|
.include <bsd.prog.mk>
|
200
usr.bin/rctl/rctl.8
Normal file
200
usr.bin/rctl/rctl.8
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
.\"-
|
||||||
|
.\" Copyright (c) 2009 Edward Tomasz Napierala
|
||||||
|
.\" All rights reserved.
|
||||||
|
.\"
|
||||||
|
.\" Redistribution and use in source and binary forms, with or without
|
||||||
|
.\" modification, are permitted provided that the following conditions
|
||||||
|
.\" are met:
|
||||||
|
.\" 1. Redistributions of source code must retain the above copyright
|
||||||
|
.\" notice, this list of conditions and the following disclaimer.
|
||||||
|
.\" 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
.\" notice, this list of conditions and the following disclaimer in the
|
||||||
|
.\" documentation and/or other materials provided with the distribution.
|
||||||
|
.\"
|
||||||
|
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR THE VOICES IN HIS HEAD BE
|
||||||
|
.\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
.\" POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
.\"
|
||||||
|
.\" $FreeBSD$
|
||||||
|
.\"
|
||||||
|
.Dd February 13, 2011
|
||||||
|
.Dt RCTL 8
|
||||||
|
.Os
|
||||||
|
.Sh NAME
|
||||||
|
.Nm rctl
|
||||||
|
.Nd display and update resource limits database
|
||||||
|
.Sh SYNOPSIS
|
||||||
|
.Nm
|
||||||
|
.Op Fl h
|
||||||
|
.Op Fl n
|
||||||
|
.Op Ar filter
|
||||||
|
.Nm
|
||||||
|
.Fl a
|
||||||
|
.Op Ar rule
|
||||||
|
.Nm
|
||||||
|
.Op Fl h
|
||||||
|
.Op Fl n
|
||||||
|
.Fl l
|
||||||
|
.Op Ar filter
|
||||||
|
.Nm
|
||||||
|
.Fl r
|
||||||
|
.Op Ar filter
|
||||||
|
.Nm
|
||||||
|
.Op Fl h
|
||||||
|
.Fl u
|
||||||
|
.Op Ar filter
|
||||||
|
.Sh DESCRIPTION
|
||||||
|
When called without options, the
|
||||||
|
.Nm
|
||||||
|
command writes currently defined RCTL rules to standard output.
|
||||||
|
.Pp
|
||||||
|
If a
|
||||||
|
.Ar filter
|
||||||
|
argument is specified, only rules matching the filter are displayed.
|
||||||
|
The options are as follows:
|
||||||
|
.Bl -tag -width indent
|
||||||
|
.It Fl a Ar rule
|
||||||
|
Add
|
||||||
|
.Ar rule
|
||||||
|
to the RCTL database.
|
||||||
|
.It Fl l Ar filter
|
||||||
|
Display rules applicable to the process defined by
|
||||||
|
.Ar filter .
|
||||||
|
.It Fl r Ar filter
|
||||||
|
Remove rules matching
|
||||||
|
.Ar filter
|
||||||
|
from the RCTL database.
|
||||||
|
.It Fl u Ar filter
|
||||||
|
Display resource usage for a subject (process, user, login class
|
||||||
|
or jail) matching the
|
||||||
|
.Ar filter .
|
||||||
|
.It Fl h
|
||||||
|
"Human-readable" output.
|
||||||
|
Use unit suffixes: Byte, Kilobyte, Megabyte,
|
||||||
|
Gigabyte, Terabyte and Petabyte.
|
||||||
|
.It Fl n
|
||||||
|
Display user IDs numerically rather than converting them to a user name.
|
||||||
|
.Pp
|
||||||
|
.Sh RULE SYNTAX
|
||||||
|
Syntax for a rule is subject:subject-id:resource:action=amount/per.
|
||||||
|
.Pp
|
||||||
|
Subject defines the kind of entity the rule applies to.
|
||||||
|
It can be either process, user, login class, or jail.
|
||||||
|
.Pp
|
||||||
|
Subject ID identifies the subject. It can be user name,
|
||||||
|
numerical user ID, login class name, jail name, or numerical jail ID.
|
||||||
|
.Pp
|
||||||
|
Resource identifies the resource the rule controls.
|
||||||
|
.Pp
|
||||||
|
Action defines what will happen when a process exceeds the allowed amount.
|
||||||
|
.Pp
|
||||||
|
Amount defines how much of the resource a process can use before
|
||||||
|
the defined action triggers.
|
||||||
|
.Pp
|
||||||
|
The per field defines what entity the amount gets accounted for.
|
||||||
|
For example, rule "loginclass:users:vmem:deny=100M/process" means
|
||||||
|
that each process of any user belonging to login class "users" may allocate
|
||||||
|
up to 100MB of virtual memory.
|
||||||
|
Rule "loginclass:users:vmem:deny=100M/user" would mean that for each
|
||||||
|
user belonging to the login class "users", the sum of virtual memory allocated
|
||||||
|
by all the processes of a that user will not exceed 100MB.
|
||||||
|
Rule "loginclass:users:vmem:deny=100M/loginclass" would mean that the sum of
|
||||||
|
virtual memory allocated by all processes of all users belonging to that login
|
||||||
|
class will not exceed 100MB.
|
||||||
|
.Pp
|
||||||
|
Valid rule has all those fields specified, except for the per, which defaults
|
||||||
|
to the value of subject.
|
||||||
|
.Pp
|
||||||
|
A filter is a rule for which one of more fields other than per is left empty.
|
||||||
|
For example, a filter that matches every rule could be written as ":::=/",
|
||||||
|
or, in short, ":".
|
||||||
|
A filter that matches all the login classes would be "loginclass:".
|
||||||
|
A filter that matches all defined rules for nproc resource would be
|
||||||
|
"::nproc".
|
||||||
|
.Pp
|
||||||
|
.Sh RESOURCES
|
||||||
|
.Bl -column -offset 3n "msgqqueued"
|
||||||
|
.It cpu CPU time, in milliseconds
|
||||||
|
.It fsize maximum file size, in bytes
|
||||||
|
.It data data size, in bytes
|
||||||
|
.It stack stack size, in bytes
|
||||||
|
.It core core dump size, in bytes
|
||||||
|
.It rss resident set size, in bytes
|
||||||
|
.It memlock locked memory, in bytes
|
||||||
|
.It nproc number of processes
|
||||||
|
.It nofile file descriptor table size
|
||||||
|
.It sbsize memory consumed by socket buffers, in bytes
|
||||||
|
.It vmem address space limit, in bytes
|
||||||
|
.It npts number of PTYs
|
||||||
|
.It swap swap usage, in bytes
|
||||||
|
.It nthr number of threads
|
||||||
|
.It msgqqueued number of queued SysV messages
|
||||||
|
.It msgqsize SysV message queue size, in bytes
|
||||||
|
.It nmsgq number of SysV message queues
|
||||||
|
.It nsem number of SysV semaphores
|
||||||
|
.It nsemop number of SysV semaphores modified in a single semop(2) call
|
||||||
|
.It nshm number of SysV shared memory segments
|
||||||
|
.It shmsize SysV shared memory size, in bytes
|
||||||
|
.It wallclock wallclock time, in milliseconds
|
||||||
|
.It pctcpu %cpu time
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
.Sh ACTIONS
|
||||||
|
.Bl -column -offset 3n "msgqqueued"
|
||||||
|
.It deny deny the allocation; not supported for cpu and wallclock
|
||||||
|
.It log log a warning to the console
|
||||||
|
.It devctl send notification to
|
||||||
|
.Xr devd 8
|
||||||
|
.It sig* e.g. sigterm; send a signal to the offending process
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
See
|
||||||
|
.Xr signal 3
|
||||||
|
for a list of supported signals.
|
||||||
|
.Pp
|
||||||
|
Not all actions are supported for all resources.
|
||||||
|
Attempt to add rule with action not supported by a given resouce will result
|
||||||
|
in error.
|
||||||
|
.Pp
|
||||||
|
Note that limiting RSS may kill the machine due to thrashing.
|
||||||
|
.Pp
|
||||||
|
.Sh EXIT STATUS
|
||||||
|
.Ex -std
|
||||||
|
.Sh EXAMPLES
|
||||||
|
.Dl rctl -a user:joe:vmem:deny=1g
|
||||||
|
.Pp
|
||||||
|
Prevent user "joe" from allocating more than 1GB of virtual memory.
|
||||||
|
.Pp
|
||||||
|
.Dl rctl -r :
|
||||||
|
.Pp
|
||||||
|
Remove all RCTL rules.
|
||||||
|
.Pp
|
||||||
|
.Dl rctl -hu jail:5
|
||||||
|
.Pp
|
||||||
|
Display resource usage information for jail with JID 5.
|
||||||
|
.Pp
|
||||||
|
.Dl rctl -l process:512
|
||||||
|
.Pp
|
||||||
|
Display all the rules applicable to process with PID 512.
|
||||||
|
.Sh SEE ALSO
|
||||||
|
.Xr jailstat 8 ,
|
||||||
|
.Xr userstat 8
|
||||||
|
.Sh HISTORY
|
||||||
|
The
|
||||||
|
.Nm
|
||||||
|
command appeared in
|
||||||
|
.Fx 9.0.
|
||||||
|
.Sh AUTHORS
|
||||||
|
.An -nosplit
|
||||||
|
The
|
||||||
|
.Nm
|
||||||
|
command was written by
|
||||||
|
.An Edward Tomasz Napierala Aq trasz@FreeBSD.org .
|
539
usr.bin/rctl/rctl.c
Normal file
539
usr.bin/rctl/rctl.c
Normal file
@ -0,0 +1,539 @@
|
|||||||
|
/*-
|
||||||
|
* Copyright (c) 2010 The FreeBSD Foundation
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This software was developed by Edward Tomasz Napierala under sponsorship
|
||||||
|
* from the FreeBSD Foundation.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* $FreeBSD$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/cdefs.h>
|
||||||
|
__FBSDID("$FreeBSD$");
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/rctl.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <grp.h>
|
||||||
|
#include <libutil.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define RCTL_DEFAULT_BUFSIZE 4096
|
||||||
|
|
||||||
|
static id_t
|
||||||
|
parse_user(const char *s)
|
||||||
|
{
|
||||||
|
id_t id;
|
||||||
|
char *end;
|
||||||
|
struct passwd *pwd;
|
||||||
|
|
||||||
|
pwd = getpwnam(s);
|
||||||
|
if (pwd != NULL)
|
||||||
|
return (pwd->pw_uid);
|
||||||
|
|
||||||
|
if (!isnumber(s[0]))
|
||||||
|
errx(1, "uknown user '%s'", s);
|
||||||
|
|
||||||
|
id = strtod(s, &end);
|
||||||
|
if ((size_t)(end - s) != strlen(s))
|
||||||
|
errx(1, "trailing characters after numerical id");
|
||||||
|
|
||||||
|
return (id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static id_t
|
||||||
|
parse_group(const char *s)
|
||||||
|
{
|
||||||
|
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 (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 (resolved == NULL)
|
||||||
|
err(1, "asprintf");
|
||||||
|
|
||||||
|
return (resolved);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This routine replaces "human-readable" number with its expanded form.
|
||||||
|
*/
|
||||||
|
static char *
|
||||||
|
expand_amount(char *rule)
|
||||||
|
{
|
||||||
|
uint64_t num;
|
||||||
|
const char *subject, *subject_id, *resource, *action, *amount, *per;
|
||||||
|
char *copy, *expanded;
|
||||||
|
|
||||||
|
copy = strdup(rule);
|
||||||
|
if (copy == NULL)
|
||||||
|
err(1, "strdup");
|
||||||
|
|
||||||
|
subject = strsep(©, ":");
|
||||||
|
subject_id = strsep(©, ":");
|
||||||
|
resource = strsep(©, ":");
|
||||||
|
action = strsep(©, "=/");
|
||||||
|
amount = strsep(©, "/");
|
||||||
|
per = copy;
|
||||||
|
|
||||||
|
if (amount == NULL || strlen(amount) == 0) {
|
||||||
|
free(copy);
|
||||||
|
return (rule);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(subject != NULL);
|
||||||
|
assert(subject_id != NULL);
|
||||||
|
assert(resource != NULL);
|
||||||
|
assert(action != NULL);
|
||||||
|
|
||||||
|
if (expand_number(amount, &num))
|
||||||
|
err(1, "expand_number");
|
||||||
|
|
||||||
|
if (per == NULL)
|
||||||
|
asprintf(&expanded, "%s:%s:%s:%s=%ju", subject, subject_id,
|
||||||
|
resource, action, (uintmax_t)num);
|
||||||
|
else
|
||||||
|
asprintf(&expanded, "%s:%s:%s:%s=%ju/%s", subject, subject_id,
|
||||||
|
resource, action, (uintmax_t)num, per);
|
||||||
|
|
||||||
|
if (expanded == NULL)
|
||||||
|
err(1, "asprintf");
|
||||||
|
|
||||||
|
return (expanded);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
humanize_ids(char *rule)
|
||||||
|
{
|
||||||
|
id_t id;
|
||||||
|
struct passwd *pwd;
|
||||||
|
struct group *grp;
|
||||||
|
const char *subject, *textid, *rest;
|
||||||
|
char *humanized;
|
||||||
|
|
||||||
|
subject = strsep(&rule, ":");
|
||||||
|
textid = strsep(&rule, ":");
|
||||||
|
if (textid == NULL)
|
||||||
|
errx(1, "rule passed from the kernel didn't contain subject");
|
||||||
|
if (rule != NULL)
|
||||||
|
rest = rule;
|
||||||
|
else
|
||||||
|
rest = "";
|
||||||
|
|
||||||
|
/* Replace numerical user and group ids with names. */
|
||||||
|
if (strcasecmp(subject, "user") == 0) {
|
||||||
|
id = parse_user(textid);
|
||||||
|
pwd = getpwuid(id);
|
||||||
|
if (pwd != NULL)
|
||||||
|
textid = pwd->pw_name;
|
||||||
|
} else if (strcasecmp(subject, "group") == 0) {
|
||||||
|
id = parse_group(textid);
|
||||||
|
grp = getgrgid(id);
|
||||||
|
if (grp != NULL)
|
||||||
|
textid = grp->gr_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
asprintf(&humanized, "%s:%s:%s", subject, textid, rest);
|
||||||
|
|
||||||
|
if (humanized == NULL)
|
||||||
|
err(1, "asprintf");
|
||||||
|
|
||||||
|
return (humanized);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
str2int64(const char *str, int64_t *value)
|
||||||
|
{
|
||||||
|
char *end;
|
||||||
|
|
||||||
|
if (str == NULL)
|
||||||
|
return (EINVAL);
|
||||||
|
|
||||||
|
*value = strtoul(str, &end, 10);
|
||||||
|
if ((size_t)(end - str) != strlen(str))
|
||||||
|
return (EINVAL);
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
humanize_amount(char *rule)
|
||||||
|
{
|
||||||
|
int64_t num;
|
||||||
|
const char *subject, *subject_id, *resource, *action, *amount, *per;
|
||||||
|
char *copy, *humanized, buf[6];
|
||||||
|
|
||||||
|
copy = strdup(rule);
|
||||||
|
if (copy == NULL)
|
||||||
|
err(1, "strdup");
|
||||||
|
|
||||||
|
subject = strsep(©, ":");
|
||||||
|
subject_id = strsep(©, ":");
|
||||||
|
resource = strsep(©, ":");
|
||||||
|
action = strsep(©, "=/");
|
||||||
|
amount = strsep(©, "/");
|
||||||
|
per = copy;
|
||||||
|
|
||||||
|
if (amount == NULL || strlen(amount) == 0 ||
|
||||||
|
str2int64(amount, &num) != 0) {
|
||||||
|
free(copy);
|
||||||
|
return (rule);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(subject != NULL);
|
||||||
|
assert(subject_id != NULL);
|
||||||
|
assert(resource != NULL);
|
||||||
|
assert(action != NULL);
|
||||||
|
|
||||||
|
if (humanize_number(buf, sizeof(buf), num, "", HN_AUTOSCALE,
|
||||||
|
HN_DECIMAL | HN_NOSPACE) == -1)
|
||||||
|
err(1, "humanize_number");
|
||||||
|
|
||||||
|
if (per == NULL)
|
||||||
|
asprintf(&humanized, "%s:%s:%s:%s=%s", subject, subject_id,
|
||||||
|
resource, action, buf);
|
||||||
|
else
|
||||||
|
asprintf(&humanized, "%s:%s:%s:%s=%s/%s", subject, subject_id,
|
||||||
|
resource, action, buf, per);
|
||||||
|
|
||||||
|
if (humanized == NULL)
|
||||||
|
err(1, "asprintf");
|
||||||
|
|
||||||
|
return (humanized);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Print rules, one per line.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
print_rules(char *rules, int hflag, int nflag)
|
||||||
|
{
|
||||||
|
char *rule;
|
||||||
|
|
||||||
|
while ((rule = strsep(&rules, ",")) != NULL) {
|
||||||
|
if (rule[0] == '\0')
|
||||||
|
break; /* XXX */
|
||||||
|
if (nflag == 0)
|
||||||
|
rule = humanize_ids(rule);
|
||||||
|
if (hflag)
|
||||||
|
rule = humanize_amount(rule);
|
||||||
|
printf("%s\n", rule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
add_rule(char *rule)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
|
||||||
|
error = rctl_add_rule(rule, strlen(rule) + 1, NULL, 0);
|
||||||
|
if (error != 0)
|
||||||
|
err(1, "rctl_add_rule");
|
||||||
|
free(rule);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
show_limits(char *filter, int hflag, int nflag)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
char *outbuf = NULL;
|
||||||
|
size_t outbuflen = RCTL_DEFAULT_BUFSIZE / 4;
|
||||||
|
|
||||||
|
do {
|
||||||
|
outbuflen *= 4;
|
||||||
|
outbuf = realloc(outbuf, outbuflen);
|
||||||
|
if (outbuf == NULL)
|
||||||
|
err(1, "realloc");
|
||||||
|
|
||||||
|
error = rctl_get_limits(filter, strlen(filter) + 1, outbuf,
|
||||||
|
outbuflen);
|
||||||
|
if (error && errno != ERANGE)
|
||||||
|
err(1, "rctl_get_limits");
|
||||||
|
} while (error && errno == ERANGE);
|
||||||
|
|
||||||
|
print_rules(outbuf, hflag, nflag);
|
||||||
|
free(filter);
|
||||||
|
free(outbuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
remove_rule(char *filter)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
|
||||||
|
error = rctl_remove_rule(filter, strlen(filter) + 1, NULL, 0);
|
||||||
|
if (error != 0)
|
||||||
|
err(1, "rctl_remove_rule");
|
||||||
|
free(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
humanize_usage_amount(char *usage)
|
||||||
|
{
|
||||||
|
int64_t num;
|
||||||
|
const char *resource, *amount;
|
||||||
|
char *copy, *humanized, buf[6];
|
||||||
|
|
||||||
|
copy = strdup(usage);
|
||||||
|
if (copy == NULL)
|
||||||
|
err(1, "strdup");
|
||||||
|
|
||||||
|
resource = strsep(©, "=");
|
||||||
|
amount = copy;
|
||||||
|
|
||||||
|
assert(resource != NULL);
|
||||||
|
assert(amount != NULL);
|
||||||
|
|
||||||
|
if (str2int64(amount, &num) != 0 ||
|
||||||
|
humanize_number(buf, sizeof(buf), num, "", HN_AUTOSCALE,
|
||||||
|
HN_DECIMAL | HN_NOSPACE) == -1) {
|
||||||
|
free(copy);
|
||||||
|
return (usage);
|
||||||
|
}
|
||||||
|
|
||||||
|
asprintf(&humanized, "%s=%s", resource, buf);
|
||||||
|
if (humanized == NULL)
|
||||||
|
err(1, "asprintf");
|
||||||
|
|
||||||
|
return (humanized);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Query the kernel about a resource usage and print it out.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
show_usage(char *filter, int hflag)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
char *outbuf = NULL, *tmp;
|
||||||
|
size_t outbuflen = RCTL_DEFAULT_BUFSIZE / 4;
|
||||||
|
|
||||||
|
do {
|
||||||
|
outbuflen *= 4;
|
||||||
|
outbuf = realloc(outbuf, outbuflen);
|
||||||
|
if (outbuf == NULL)
|
||||||
|
err(1, "realloc");
|
||||||
|
|
||||||
|
error = rctl_get_racct(filter, strlen(filter) + 1, outbuf,
|
||||||
|
outbuflen);
|
||||||
|
if (error && errno != ERANGE)
|
||||||
|
err(1, "rctl_get_racct");
|
||||||
|
} while (error && errno == ERANGE);
|
||||||
|
|
||||||
|
while ((tmp = strsep(&outbuf, ",")) != NULL) {
|
||||||
|
if (tmp[0] == '\0')
|
||||||
|
break; /* XXX */
|
||||||
|
|
||||||
|
if (hflag)
|
||||||
|
tmp = humanize_usage_amount(tmp);
|
||||||
|
|
||||||
|
printf("%s\n", tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(filter);
|
||||||
|
free(outbuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Query the kernel about resource limit rules and print them out.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
show_rules(char *filter, int hflag, int nflag)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
char *outbuf = NULL;
|
||||||
|
size_t filterlen, outbuflen = RCTL_DEFAULT_BUFSIZE / 4;
|
||||||
|
|
||||||
|
if (filter != NULL)
|
||||||
|
filterlen = strlen(filter) + 1;
|
||||||
|
else
|
||||||
|
filterlen = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
outbuflen *= 4;
|
||||||
|
outbuf = realloc(outbuf, outbuflen);
|
||||||
|
if (outbuf == NULL)
|
||||||
|
err(1, "realloc");
|
||||||
|
|
||||||
|
error = rctl_get_rules(filter, filterlen, outbuf, outbuflen);
|
||||||
|
if (error && errno != ERANGE)
|
||||||
|
err(1, "rctl_get_rules");
|
||||||
|
} while (error && errno == ERANGE);
|
||||||
|
|
||||||
|
print_rules(outbuf, hflag, nflag);
|
||||||
|
free(outbuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
usage(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
fprintf(stderr, "usage: rctl [ -h ] [-a rule | -l filter | -r filter "
|
||||||
|
"| -u filter | filter]\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc __unused, char **argv __unused)
|
||||||
|
{
|
||||||
|
int ch, aflag = 0, hflag = 0, nflag = 0, lflag = 0, rflag = 0,
|
||||||
|
uflag = 0;
|
||||||
|
char *rule = NULL;
|
||||||
|
|
||||||
|
while ((ch = getopt(argc, argv, "a:hl:nr:u:")) != -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 '?':
|
||||||
|
default:
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
argc -= optind;
|
||||||
|
argv += optind;
|
||||||
|
|
||||||
|
if (argc > 1)
|
||||||
|
usage();
|
||||||
|
|
||||||
|
if (rule == NULL) {
|
||||||
|
if (argc == 1)
|
||||||
|
rule = strdup(argv[0]);
|
||||||
|
else
|
||||||
|
rule = strdup("::");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aflag + lflag + rflag + uflag + argc > 1)
|
||||||
|
errx(1, "only one flag or argument may be specified "
|
||||||
|
"at the same time");
|
||||||
|
|
||||||
|
rule = resolve_ids(rule);
|
||||||
|
rule = expand_amount(rule);
|
||||||
|
|
||||||
|
if (aflag) {
|
||||||
|
add_rule(rule);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lflag) {
|
||||||
|
show_limits(rule, hflag, nflag);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rflag) {
|
||||||
|
remove_rule(rule);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uflag) {
|
||||||
|
show_usage(rule, hflag);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
show_rules(rule, hflag, nflag);
|
||||||
|
return (0);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user