]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
prlimit: new command
authorDavidlohr Bueso <dave@gnu.org>
Wed, 19 Oct 2011 20:02:35 +0000 (16:02 -0400)
committerKarel Zak <kzak@redhat.com>
Fri, 21 Oct 2011 21:19:51 +0000 (23:19 +0200)
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>
sys-utils/.gitignore
sys-utils/Makefile.am
sys-utils/prlimit.1 [new file with mode: 0644]
sys-utils/prlimit.c [new file with mode: 0644]

index febeb380517c21c497b71c00bf03a405e2a96a53..01a3df9860be23d72a44c8c8659ee5b428454488 100644 (file)
@@ -26,6 +26,7 @@ pivot_root
 ppc32.8
 ppc64.8
 ppc.8
+prlimit
 readprofile
 renice
 rtcwake
index 7da9ff0cf1d5eebc36c69437852ca4bcb41e8bf3..48595dab5521eaec80d1898c8c2afe372ac3d595 100644 (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
diff --git a/sys-utils/prlimit.1 b/sys-utils/prlimit.1
new file mode 100644 (file)
index 0000000..5c20012
--- /dev/null
@@ -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/.
diff --git a/sys-utils/prlimit.c b/sys-utils/prlimit.c
new file mode 100644 (file)
index 0000000..74c7fc9
--- /dev/null
@@ -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;
+}