prlimit: new command

This program uses the prlimit() system call to get and/or set resource limits
for a given process.

[kzak@redhat,com: - improve <soft:hard> parsing,
                  - use short cmdline options compatible with ulimits
                  - add --verbose mode
                  - fix gcc warnings]

Signed-off-by: Davidlohr Bueso <dave@gnu.org>
Signed-off-by: Karel Zak <kzak@redhat.com>
This commit is contained in:
Davidlohr Bueso 2011-10-19 16:02:35 -04:00 committed by Karel Zak
parent 6a2f2f58a4
commit 6bac2825af
4 changed files with 685 additions and 2 deletions

View File

@ -26,6 +26,7 @@ pivot_root
ppc32.8
ppc64.8
ppc.8
prlimit
readprofile
renice
rtcwake

View File

@ -2,11 +2,11 @@ include $(top_srcdir)/config/include-Makefile.am
bin_PROGRAMS =
sbin_PROGRAMS =
usrbin_exec_PROGRAMS = flock ipcrm ipcs ipcmk renice setsid
usrbin_exec_PROGRAMS = flock ipcrm ipcs ipcmk renice setsid prlimit
usrsbin_exec_PROGRAMS = readprofile
dist_man_MANS = flock.1 ipcrm.1 ipcs.1 ipcmk.1 renice.1 setsid.1 \
readprofile.8
readprofile.8 prlimit.1
if LINUX
bin_PROGRAMS += dmesg
@ -41,6 +41,9 @@ dmesg_SOURCES = dmesg.c $(top_srcdir)/lib/strutils.c
ipcmk_SOURCES = ipcmk.c $(top_srcdir)/lib/strutils.c
ipcrm_SOURCES = ipcrm.c $(top_srcdir)/lib/strutils.c
flock_SOURCES = flock.c $(top_srcdir)/lib/strutils.c
prlimit_SOURCES = prlimit.c $(top_srcdir)/lib/strutils.c \
$(top_srcdir)/lib/mbsalign.c \
$(top_srcdir)/lib/tt.c
if BUILD_MOUNTPOINT
bin_PROGRAMS += mountpoint

108
sys-utils/prlimit.1 Normal file
View File

@ -0,0 +1,108 @@
.\" prlimit.1 --
.\" Copyright 2011 Davidlohr Bueso <dave@gnu.org>
.\" May be distributed under the GNU General Public License
.TH PRLIMIT 1 "October 2011" "util-linux" "User Commands"
.SH NAME
prlimit \-
get and set a process resource limits.
.SH SYNOPSIS
.B prlimit
.RB [options]
.RB [ \-\-{resource_name}[=limits] ]
.SH DESCRIPTION
Given a process id and one or more resources, \fBprlimit\fP tries to retrieve
and/or modify the limits.
The \fIlimits\fP format is composed by a soft and a hard (ceiling) value, separated
by a semicolon (:), in order to modify the existing value(s). If no limits are
used, \fBprlimit\fP will only display the current values. If one of the values
is not used, then the existing one will be used. To specify the unlimited or
infinity limit (RLIM_INFINITY), the -1 or 'unlimited' string can be passed.
Because of the nature of limits, the soft value must be lower or equal to the
high limit. To see all the available resource limits, refer to the RESOURCE
OPTIONS section.
.IP "\fB<soft>:<hard>\fP Specify both limits"
.IP "\fB<soft>:\fP Specify only the soft limit"
.IP "\fB:<hard>\fP Specify only the hard limit"
.IP "\fB<value>\fP Specify both soft and hard limits to the same value"
.SH GENERAL OPTIONS
.IP "\fB\-p, \-\-pid\fP"
Specify the process id, if none is given, it will use the running process.
.IP "\fB\-o, \-\-output \fIlist\fP"
Define the output columns to use. If no output arrangement is specified, then a default set is used.
Use \fB\-\-help\fP to get list of all supported columns.
.IP "\fB\-V, \-\-version\fP"
Output version information and exit.
.IP "\fB\-v, \-\-verbose\fP"
Vebose mode.
.IP "\fB\-h, \-\-help\fP"
Print a help text and exit.
.SH RESOURCE OPTIONS
.IP "\fB\-c, \-\-core\fP[=limits]"
Maximum size of a core file.
.IP "\fB\-d, \-\-data\fP[=limits]"
Maximum data size.
.IP "\fB\-e, \-\-nice\fP[=limits]"
Maximum nice priority allowed to raise.
.IP "\fB\-f, \-\-fsize\fP[=limits]"
Maximum file size.
.IP "\fB\-i, \-\-sigpending\fP[=limits]"
Maximum amount of pending signals.
.IP "\fB\-l, \-\-memlock\fP[=limits]"
Maximum locked-in-memory address space.
.IP "\fB\-m, \-\-rss\fP[=limits]"
Maximum Resident Set Size (RSS).
.IP "\fB\-n, \-\-nofile\fP[=limits]"
Maximum amount of open files.
.IP "\fB\-q, \-\-msgqueue\fP[=limits]"
Maximum amount of bytes in POSIX message queues.
.IP "\fB\-r, \-\-rtprio\fP[=limits]"
Maximum real-time priority.
.IP "\fB\-s, \-\-stack\fP[=limits]"
Maximum size of the stack.
.IP "\fB\-t, \-\-cpu\fP[=limits]"
CPU time, in seconds.
.IP "\fB\-u, \-\-nproc\fP[=limits]"
Maximum amount of processes.
.IP "\fB\-v, \-\-as\fP[=limits]"
Address space limit.
.IP "\fB\-x, \-\-locks\fP[=limits]"
Maximum amount of file locks held.
.IP "\fB\-y, \-\-rttime\fP[=limits]"
Timeout for real-time tasks.
.RE
.SH EXAMPLES
.IP "\fBprlimit \-\-pid 13134\fP"
Display limit values for all current resources.
.IP "\fBprlimit \-\-pid 13134 \--rss --nofile=1024:4095\fP"
Display the limits of the RSS and set the soft and hard limits for the amount
of open files to 1024 and 4095, respectively.
.IP "\fBprlimit \-\-pid 13134 --nproc=512:\fP"
Modify only the soft limit for the amount of processes.
.IP "\fBprlimit \-\-pid $$ --nproc=unlimited\fP"
Set the amount of processes for both soft and ceiling values to unlimited.
.SH "SEE ALSO"
.BR prlimit (2),
.BR ulimit (1)
.SH NOTES
.nf
The prlimit system call is supported since Linux 2.6.36, previous kernels will
break this program.
.fi
.SH AUTHORS
.nf
Davidlohr Bueso <dave@gnu.org> - In memory of Dennis M. Ritchie.
.fi
.SH AVAILABILITY
The prlimit command is part of the util-linux package and is available from
ftp://ftp.kernel.org/pub/linux/utils/util-linux/.

571
sys-utils/prlimit.c Normal file
View File

@ -0,0 +1,571 @@
/*
* prlimit - get/set process resource limits.
*
* Copyright (C) 2011 Davidlohr Bueso <dave@gnu.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <errno.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <assert.h>
#include <unistd.h>
#include <sys/resource.h>
#include "c.h"
#include "nls.h"
#include "tt.h"
#include "xalloc.h"
#include "strutils.h"
enum {
AS,
CORE,
CPU,
DATA,
FSIZE,
LOCKS,
MEMLOCK,
MSGQUEUE,
NICE,
NOFILE,
NPROC,
RSS,
RTPRIO,
RTTIME,
SIGPENDING,
STACK
};
struct prlimit_desc {
const char *name;
const char *help;
int resource;
};
static struct prlimit_desc prlimit_desc[] =
{
[AS] = { "AS", N_("address space limit"), RLIMIT_AS },
[CORE] = { "CORE", N_("max core file size"), RLIMIT_CORE },
[CPU] = { "CPU", N_("CPU time in secs"), RLIMIT_CPU },
[DATA] = { "DATA", N_("max data size"), RLIMIT_DATA },
[FSIZE] = { "FSIZE", N_("max file size"), RLIMIT_FSIZE },
[LOCKS] = { "LOCKS", N_("max amount of file locks held"), RLIMIT_LOCKS },
[MEMLOCK] = { "MEMLOCK", N_("max locked-in-memory address space"), RLIMIT_MEMLOCK },
[MSGQUEUE] = { "MSGQUEUE", N_("max bytes in POSIX mqueues"), RLIMIT_MSGQUEUE },
[NICE] = { "NICE", N_("max nice prio allowed to raise"), RLIMIT_NICE },
[NOFILE] = { "NOFILE", N_("max amount of open files"), RLIMIT_NOFILE },
[NPROC] = { "NPROC", N_("max number of processes"), RLIMIT_NPROC },
[RSS] = { "RSS", N_("max resident set size"), RLIMIT_RSS },
[RTPRIO] = { "RTPRIO", N_("max real-time priority"), RLIMIT_RTPRIO },
[RTTIME] = { "RTTIME", N_("timeout for real-time tasks"), RLIMIT_RTTIME },
[SIGPENDING] = { "SIGPENDING", N_("max amount of pending signals"), RLIMIT_SIGPENDING },
[STACK] = { "STACK", N_("max stack size"), RLIMIT_STACK }
};
struct prlimit {
struct rlimit rlim;
struct prlimit_desc *desc;
int modify;
};
#define PRLIMIT_EMPTY_LIMIT {{ 0, 0, }, NULL, 0 }
enum {
COL_HELP,
COL_RES,
COL_SOFT,
COL_HARD,
};
/* column names */
struct colinfo {
const char *name; /* header */
double whint; /* width hint (N < 1 is in percent of termwidth) */
int flags; /* TT_FL_* */
const char *help;
};
/* columns descriptions */
struct colinfo infos[] = {
[COL_RES] = { "RESOURCE", 0.25, TT_FL_TRUNC, N_("resource name") },
[COL_HELP] = { "DESCRIPTION", 0.1, TT_FL_TRUNC, N_("resource description")},
[COL_SOFT] = { "SOFT", 0.1, TT_FL_RIGHT, N_("soft limit")},
[COL_HARD] = { "HARD", 1, TT_FL_RIGHT, N_("hard limit (ceiling)")},
};
#define NCOLS ARRAY_SIZE(infos)
#define MAX_RESOURCES ARRAY_SIZE(prlimit_desc)
#define INFINITY_STR "unlimited"
#define INFINITY_STRLEN (sizeof(INFINITY_STR) - 1)
#define PRLIMIT_SOFT (1 << 1)
#define PRLIMIT_HARD (1 << 2)
/* array with IDs of enabled columns */
static int columns[NCOLS], ncolumns;
static pid_t pid; /* calling process (default) */
static int verbose;
static void __attribute__ ((__noreturn__)) usage(FILE * out)
{
size_t i;
fputs(USAGE_HEADER, out);
fprintf(out,
_(" %s [options]\n"), program_invocation_short_name);
fputs(_("\nGeneral Options:\n"), out);
fputs(_(" -p, --pid <pid> process id\n"
" -o, --output <type> define which output columns to use\n"
" --verbose verbose output\n"
" -h, --help display this help and exit\n"
" -V, --version output version information and exit\n"), out);
fputs(_("\nResources Options:\n"), out);
fputs(_(" -c, --core maximum size of core files created\n"
" -d, --data maximum size of a process's data segment\n"
" -e, --nice maximum nice priority allowed to raise\n"
" -f, --fsize maximum size of files written by the process\n"
" -i, --sigpending maximum amount of pending signals\n"
" -l, --memlock maximum size a process may lock into memory\n"
" -m, --rss maximum resident set size\n"
" -n, --nofile maximum amount of open files\n"
" -q, --msgqueue maximum bytes in POSIX message queues\n"
" -r, --rtprio maximum real-time scheduling priority\n"
" -s, --stack maximum stack size\n"
" -t, --cpu maximum amount of CPU time in seconds\n"
" -u, --nproc maximum number of user processes\n"
" -v, --as size of virtual memory\n"
" -x, --locks maximum number of file locks\n"
" -y, --rttime CPU time in microseconds a process scheduled\n"
" under real-time scheduling\n"), out);
fputs(_("\nAvailable columns (for --output):\n"), out);
for (i = 0; i < NCOLS; i++)
fprintf(out, " %11s %s\n", infos[i].name, _(infos[i].help));
fprintf(out, USAGE_MAN_TAIL("prlimit(1)"));
exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
}
static inline int get_column_id(int num)
{
assert(ARRAY_SIZE(columns) == NCOLS);
assert(num < ncolumns);
assert(columns[num] < (int) NCOLS);
return columns[num];
}
static inline struct colinfo *get_column_info(unsigned num)
{
return &infos[ get_column_id(num) ];
}
static void add_tt_line(struct tt *tt, struct prlimit *l)
{
int i;
struct tt_line *line;
assert(tt);
assert(l);
line = tt_add_line(tt, NULL);
if (!line) {
warn(_("failed to add line to output"));
return;
}
for (i = 0; i < ncolumns; i++) {
char *str = NULL;
int rc = 0;
switch (get_column_id(i)) {
case COL_RES:
rc = asprintf(&str, "%s", l->desc->name);
break;
case COL_HELP:
rc = asprintf(&str, "%s", l->desc->help);
break;
case COL_SOFT:
rc = l->rlim.rlim_cur == RLIM_INFINITY ?
asprintf(&str, "%s", "unlimited") :
asprintf(&str, "%llu", (unsigned long long) l->rlim.rlim_cur);
break;
case COL_HARD:
rc = l->rlim.rlim_max == RLIM_INFINITY ?
asprintf(&str, "%s", "unlimited") :
asprintf(&str, "%llu", (unsigned long long) l->rlim.rlim_max);
break;
default:
break;
}
if (rc || str)
tt_line_set_data(line, i, str);
}
}
static int column_name_to_id(const char *name, size_t namesz)
{
size_t i;
assert(name);
for (i = 0; i < NCOLS; i++) {
const char *cn = infos[i].name;
if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
return i;
}
warnx(_("unknown column: %s"), name);
return -1;
}
static int show_limits(struct prlimit lims[], int tt_flags, size_t n)
{
int i;
struct tt *tt;
tt = tt_new_table(tt_flags);
if (!tt) {
warn(_("failed to initialize output table"));
return -1;
}
for (i = 0; i < ncolumns; i++) {
struct colinfo *col = get_column_info(i);
if (!tt_define_column(tt, col->name, col->whint, col->flags)) {
warnx(_("failed to initialize output column"));
goto done;
}
}
for (i = 0; (size_t) i < n; i++)
if (!lims[i].modify) /* only display old limits */
add_tt_line(tt, &lims[i]);
tt_print_table(tt);
done:
tt_free_table(tt);
return 0;
}
static void do_prlimit(struct prlimit lims[], size_t n)
{
size_t i, nshows = 0;
for (i = 0; i < n; i++) {
struct rlimit *new = NULL;
if (lims[i].modify)
new = &lims[i].rlim;
else
nshows++;
if (verbose && new) {
printf(_("New %s limit: "), lims[i].desc->name);
if (new->rlim_cur == RLIM_INFINITY)
printf("<%s", _("unlimited"));
else
printf("<%ju", new->rlim_cur);
if (new->rlim_max == RLIM_INFINITY)
printf(":%s>\n", _("unlimited"));
else
printf(":%ju>\n", new->rlim_max);
}
if (prlimit(pid, lims[i].desc->resource, new, &lims[i].rlim) == -1)
err(EXIT_FAILURE, _("failed to get resource limits for PID %d"), pid);
}
if (nshows)
show_limits(lims, 0, n);
}
static int get_range(char *str, rlim_t *soft, rlim_t *hard, int *found)
{
char *end = NULL;
if (!str)
return 0;
*found = errno = 0;
*soft = *hard = RLIM_INFINITY;
if (!strcmp(str, INFINITY_STR)) { /* <unlimited> */
*found |= PRLIMIT_SOFT | PRLIMIT_HARD;
return 0;
} else if (*str == ':') { /* <:hard> */
str++;
if (strcmp(str, INFINITY_STR) != 0) {
*hard = strtoull(str, &end, 10);
if (errno || !end || *end || end == str)
return -1;
}
*found |= PRLIMIT_HARD;
return 0;
}
if (strncmp(str, INFINITY_STR, INFINITY_STRLEN) == 0) {
/* <unlimited> or <unlimited:> */
end = str + INFINITY_STRLEN;
} else {
/* <value> or <soft:> */
*hard = *soft = strtoull(str, &end, 10);
if (errno || !end || end == str)
return -1;
}
if (*end == ':' && !*(end + 1)) /* <soft:> */
*found |= PRLIMIT_SOFT;
else if (*end == ':') { /* <soft:hard> */
str = end + 1;
if (!strcmp(str, INFINITY_STR))
*hard = RLIM_INFINITY;
else {
end = NULL;
errno = 0;
*hard = strtoull(str, &end, 10);
if (errno || !end || *end || end == str)
return -1;
}
*found |= PRLIMIT_SOFT | PRLIMIT_HARD;
} else /* <value> */
*found |= PRLIMIT_SOFT | PRLIMIT_HARD;
return 0;
}
static int parse_prlim(struct rlimit *lim, char *ops, size_t id)
{
rlim_t soft, hard;
int found = 0;
if (get_range(ops, &soft, &hard, &found))
errx(EXIT_FAILURE, _("failed to parse %s limit"),
prlimit_desc[id].name);
/*
* If one of the limits is unknown (default value for not being passed), we need
* to get the current limit and use it.
* I see no other way other than using prlimit(2).
*/
if (found != (PRLIMIT_HARD | PRLIMIT_SOFT)) {
struct rlimit old;
if (prlimit(pid, prlimit_desc[id].resource, NULL, &old) == -1)
errx(EXIT_FAILURE, _("failed to get old %s limit"),
prlimit_desc[id].name);
if (!(found & PRLIMIT_SOFT))
soft = old.rlim_cur;
else if (!(found & PRLIMIT_HARD))
hard = old.rlim_max;
}
if (soft > hard && (soft != RLIM_INFINITY || hard != RLIM_INFINITY))
errx(EXIT_FAILURE, _("the soft limit cannot exceed the ceiling value"));
lim->rlim_cur = soft;
lim->rlim_max = hard;
return 0;
}
/*
* Add a resource limit to the limits array
*/
static int add_prlim(char *ops, struct prlimit *lim, size_t id)
{
lim->desc = &prlimit_desc[id];
if (ops) { /* planning on modifying a limit? */
lim->modify = 1;
parse_prlim(&lim->rlim, ops, id);
}
return 0;
}
int main(int argc, char **argv)
{
int opt;
size_t n = 0;
struct prlimit lims[MAX_RESOURCES] = { PRLIMIT_EMPTY_LIMIT };
enum {
VERBOSE_OPTION = CHAR_MAX + 1
};
static const struct option longopts[] = {
{ "pid", required_argument, NULL, 'p' },
{ "output", required_argument, NULL, 'o' },
{ "as", optional_argument, NULL, 'v' },
{ "core", optional_argument, NULL, 'c' },
{ "cpu", optional_argument, NULL, 't' },
{ "data", optional_argument, NULL, 'd' },
{ "fsize", optional_argument, NULL, 'f' },
{ "locks", optional_argument, NULL, 'x' },
{ "memlock", optional_argument, NULL, 'l' },
{ "msgqueue", optional_argument, NULL, 'q' },
{ "nice", optional_argument, NULL, 'e' },
{ "nofile", optional_argument, NULL, 'n' },
{ "nproc", optional_argument, NULL, 'u' },
{ "rss", optional_argument, NULL, 'm' },
{ "rtprio", optional_argument, NULL, 'r' },
{ "rttime", optional_argument, NULL, 'y' },
{ "sigpending", optional_argument, NULL, 'i' },
{ "stack", optional_argument, NULL, 's' },
{ "version", no_argument, NULL, 'V' },
{ "help", no_argument, NULL, 'h' },
{ "verbose", no_argument, NULL, VERBOSE_OPTION },
{ NULL, 0, NULL, 0 }
};
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
/*
* Something is very wrong if this doesn't succeed,
* assuming STACK is the last resource, of course.
*/
assert(MAX_RESOURCES == STACK + 1);
while((opt = getopt_long(argc, argv,
"c::d::e::f::i::l::m::n::q::r::s::t::u::v::x::y::p:o:vVh",
longopts, NULL)) != -1) {
switch(opt) {
case 'c':
add_prlim(optarg, &lims[n++], CORE);
break;
case 'd':
add_prlim(optarg, &lims[n++], DATA);
break;
case 'e':
add_prlim(optarg, &lims[n++], NICE);
break;
case 'f':
add_prlim(optarg, &lims[n++], FSIZE);
break;
case 'i':
add_prlim(optarg, &lims[n++], SIGPENDING);
break;
case 'l':
add_prlim(optarg, &lims[n++], MEMLOCK);
break;
case 'm':
add_prlim(optarg, &lims[n++], RSS);
break;
case 'n':
add_prlim(optarg, &lims[n++], NOFILE);
break;
case 'q':
add_prlim(optarg, &lims[n++], MSGQUEUE);
break;
case 'r':
add_prlim(optarg, &lims[n++], RTPRIO);
break;
case 's':
add_prlim(optarg, &lims[n++], STACK);
break;
case 't':
add_prlim(optarg, &lims[n++], CPU);
break;
case 'u':
add_prlim(optarg, &lims[n++], NPROC);
break;
case 'v':
add_prlim(optarg, &lims[n++], AS);
break;
case 'x':
add_prlim(optarg, &lims[n++], LOCKS);
break;
case 'y':
add_prlim(optarg, &lims[n++], RTTIME);
break;
case 'p':
if (pid) /* we only work one pid at a time */
errx(EXIT_FAILURE, _("only use one PID at a time"));
pid = strtol_or_err(optarg, _("cannot parse PID"));
break;
case 'h':
usage(stdout);
break;
case 'o':
ncolumns = string_to_idarray(optarg,
columns, ARRAY_SIZE(columns),
column_name_to_id);
if (ncolumns < 0)
return EXIT_FAILURE;
break;
case 'V':
printf(UTIL_LINUX_VERSION);
return EXIT_SUCCESS;
case VERBOSE_OPTION:
verbose++;
break;
default:
usage(stderr);
break;
}
}
if (argc == 1)
usage(stderr);
if (!ncolumns) {
/* default columns */
columns[ncolumns++] = COL_RES;
columns[ncolumns++] = COL_HELP;
columns[ncolumns++] = COL_SOFT;
columns[ncolumns++] = COL_HARD;
}
if (!n) {
/* default is to print all resources */
for (; n < MAX_RESOURCES; n++)
add_prlim(NULL, &lims[n], n);
}
do_prlimit(lims, n);
return EXIT_SUCCESS;
}