]> git.ipfire.org Git - thirdparty/util-linux.git/blame - sys-utils/prlimit.c
prlimit: improve --help output
[thirdparty/util-linux.git] / sys-utils / prlimit.c
CommitLineData
6bac2825
DB
1/*
2 * prlimit - get/set process resource limits.
3 *
4 * Copyright (C) 2011 Davidlohr Bueso <dave@gnu.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
7cebf0bb
SK
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
6bac2825
DB
19 */
20
21#include <errno.h>
22#include <getopt.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <ctype.h>
26#include <assert.h>
27#include <unistd.h>
28#include <sys/resource.h>
29
226d76e6
OO
30#include <libsmartcols.h>
31
6bac2825
DB
32#include "c.h"
33#include "nls.h"
6bac2825
DB
34#include "xalloc.h"
35#include "strutils.h"
94c01662 36#include "list.h"
efb8854f 37#include "closestream.h"
6bac2825 38
945ac250
KZ
39#ifndef RLIMIT_RTTIME
40# define RLIMIT_RTTIME 15
41#endif
42
6bac2825
DB
43enum {
44 AS,
45 CORE,
46 CPU,
47 DATA,
48 FSIZE,
49 LOCKS,
50 MEMLOCK,
51 MSGQUEUE,
52 NICE,
53 NOFILE,
54 NPROC,
55 RSS,
56 RTPRIO,
57 RTTIME,
58 SIGPENDING,
59 STACK
60};
61
226d76e6
OO
62/* basic output flags */
63static int no_headings;
64static int raw;
65
6bac2825
DB
66struct prlimit_desc {
67 const char *name;
68 const char *help;
d76f904a 69 const char *unit;
6bac2825
DB
70 int resource;
71};
72
73static struct prlimit_desc prlimit_desc[] =
74{
d76f904a 75 [AS] = { "AS", N_("address space limit"), N_("bytes"), RLIMIT_AS },
803172f6 76 [CORE] = { "CORE", N_("max core file size"), N_("bytes"), RLIMIT_CORE },
d76f904a
DB
77 [CPU] = { "CPU", N_("CPU time"), N_("seconds"), RLIMIT_CPU },
78 [DATA] = { "DATA", N_("max data size"), N_("bytes"), RLIMIT_DATA },
803172f6
KZ
79 [FSIZE] = { "FSIZE", N_("max file size"), N_("bytes"), RLIMIT_FSIZE },
80 [LOCKS] = { "LOCKS", N_("max number of file locks held"), N_("locks"), RLIMIT_LOCKS },
d76f904a
DB
81 [MEMLOCK] = { "MEMLOCK", N_("max locked-in-memory address space"), N_("bytes"), RLIMIT_MEMLOCK },
82 [MSGQUEUE] = { "MSGQUEUE", N_("max bytes in POSIX mqueues"), N_("bytes"), RLIMIT_MSGQUEUE },
83 [NICE] = { "NICE", N_("max nice prio allowed to raise"), NULL, RLIMIT_NICE },
803172f6
KZ
84 [NOFILE] = { "NOFILE", N_("max number of open files"), N_("files"), RLIMIT_NOFILE },
85 [NPROC] = { "NPROC", N_("max number of processes"), N_("processes"), RLIMIT_NPROC },
86 [RSS] = { "RSS", N_("max resident set size"), N_("bytes"), RLIMIT_RSS },
d76f904a
DB
87 [RTPRIO] = { "RTPRIO", N_("max real-time priority"), NULL, RLIMIT_RTPRIO },
88 [RTTIME] = { "RTTIME", N_("timeout for real-time tasks"), N_("microsecs"), RLIMIT_RTTIME },
803172f6 89 [SIGPENDING] = { "SIGPENDING", N_("max number of pending signals"), N_("signals"), RLIMIT_SIGPENDING },
d76f904a 90 [STACK] = { "STACK", N_("max stack size"), N_("bytes"), RLIMIT_STACK }
6bac2825
DB
91};
92
7887c6e0
KZ
93#define MAX_RESOURCES ARRAY_SIZE(prlimit_desc)
94
6bac2825 95struct prlimit {
94c01662
KZ
96 struct list_head lims;
97
6bac2825
DB
98 struct rlimit rlim;
99 struct prlimit_desc *desc;
044bc8de 100 int modify; /* PRLIMIT_{SOFT,HARD} mask */
6bac2825
DB
101};
102
103#define PRLIMIT_EMPTY_LIMIT {{ 0, 0, }, NULL, 0 }
104
105enum {
106 COL_HELP,
107 COL_RES,
108 COL_SOFT,
109 COL_HARD,
d76f904a 110 COL_UNITS,
6bac2825
DB
111};
112
113/* column names */
114struct colinfo {
115 const char *name; /* header */
116 double whint; /* width hint (N < 1 is in percent of termwidth) */
226d76e6 117 int flags; /* SCOLS_FL_* */
6bac2825
DB
118 const char *help;
119};
120
121/* columns descriptions */
2ba641e5 122static struct colinfo infos[] = {
226d76e6
OO
123 [COL_RES] = { "RESOURCE", 0.25, SCOLS_FL_TRUNC, N_("resource name") },
124 [COL_HELP] = { "DESCRIPTION", 0.1, SCOLS_FL_TRUNC, N_("resource description")},
125 [COL_SOFT] = { "SOFT", 0.1, SCOLS_FL_RIGHT, N_("soft limit")},
126 [COL_HARD] = { "HARD", 1, SCOLS_FL_RIGHT, N_("hard limit (ceiling)")},
127 [COL_UNITS] = { "UNITS", 0.1, SCOLS_FL_TRUNC, N_("units")},
6bac2825
DB
128};
129
7887c6e0
KZ
130static int columns[ARRAY_SIZE(infos) * 2];
131static int ncolumns;
132
133
6bac2825
DB
134
135#define INFINITY_STR "unlimited"
136#define INFINITY_STRLEN (sizeof(INFINITY_STR) - 1)
137
138#define PRLIMIT_SOFT (1 << 1)
139#define PRLIMIT_HARD (1 << 2)
140
6bac2825
DB
141static pid_t pid; /* calling process (default) */
142static int verbose;
143
945ac250
KZ
144#ifndef HAVE_PRLIMIT
145# include <sys/syscall.h>
146static int prlimit(pid_t p, int resource,
147 const struct rlimit *new_limit,
148 struct rlimit *old_limit)
149{
ebdd79c9 150 return syscall(SYS_prlimit64, p, resource, new_limit, old_limit);
945ac250
KZ
151}
152#endif
153
86be6a32 154static void __attribute__((__noreturn__)) usage(void)
6bac2825 155{
86be6a32 156 FILE *out = stdout;
6bac2825
DB
157 size_t i;
158
159 fputs(USAGE_HEADER, out);
160
161 fprintf(out,
3d6a3d6d 162 _(" %s [options] [--<resource>=<limit>] [-p PID]\n"), program_invocation_short_name);
53e1f461 163 fprintf(out,
3d6a3d6d 164 _(" %s [options] [--<resource>=<limit>] COMMAND\n"), program_invocation_short_name);
6bac2825 165
451dbcfa
BS
166 fputs(USAGE_SEPARATOR, out);
167 fputs(_("Show or change the resource limits of a process.\n"), out);
168
3d6a3d6d 169 fputs(USAGE_OPTIONS, out);
6bac2825 170 fputs(_(" -p, --pid <pid> process id\n"
d254c1db
KZ
171 " -o, --output <list> define which output columns to use\n"
172 " --noheadings don't print headings\n"
173 " --raw use the raw output format\n"
6bac2825 174 " --verbose verbose output\n"
b3054454 175 ), out);
f45f3ec3 176 printf(USAGE_HELP_OPTIONS(24));
6bac2825 177
3d6a3d6d 178 fputs(_("\nResources:\n"), out);
6bac2825
DB
179 fputs(_(" -c, --core maximum size of core files created\n"
180 " -d, --data maximum size of a process's data segment\n"
181 " -e, --nice maximum nice priority allowed to raise\n"
182 " -f, --fsize maximum size of files written by the process\n"
1023db50 183 " -i, --sigpending maximum number of pending signals\n"
6bac2825
DB
184 " -l, --memlock maximum size a process may lock into memory\n"
185 " -m, --rss maximum resident set size\n"
1023db50 186 " -n, --nofile maximum number of open files\n"
6bac2825
DB
187 " -q, --msgqueue maximum bytes in POSIX message queues\n"
188 " -r, --rtprio maximum real-time scheduling priority\n"
189 " -s, --stack maximum stack size\n"
190 " -t, --cpu maximum amount of CPU time in seconds\n"
191 " -u, --nproc maximum number of user processes\n"
192 " -v, --as size of virtual memory\n"
193 " -x, --locks maximum number of file locks\n"
194 " -y, --rttime CPU time in microseconds a process scheduled\n"
195 " under real-time scheduling\n"), out);
196
3d6a3d6d
KZ
197 fputs(USAGE_ARGUMENTS, out);
198 fputs(_(
199 " <limit> is defined as a range soft:hard, soft:, :hard or a value to\n"
200 " define both limits (e.g. -e=0:10 -r=:10).\n"), out);
201
c3a4cfc5 202 fputs(USAGE_COLUMNS, out);
7887c6e0 203 for (i = 0; i < ARRAY_SIZE(infos); i++)
6bac2825
DB
204 fprintf(out, " %11s %s\n", infos[i].name, _(infos[i].help));
205
f45f3ec3 206 printf(USAGE_MAN_TAIL("prlimit(1)"));
6bac2825 207
86be6a32 208 exit(EXIT_SUCCESS);
6bac2825
DB
209}
210
211static inline int get_column_id(int num)
212{
6bac2825 213 assert(num < ncolumns);
7887c6e0 214 assert(columns[num] < (int) ARRAY_SIZE(infos));
6bac2825
DB
215
216 return columns[num];
217}
218
219static inline struct colinfo *get_column_info(unsigned num)
220{
221 return &infos[ get_column_id(num) ];
222}
223
226d76e6 224static void add_scols_line(struct libscols_table *table, struct prlimit *l)
6bac2825
DB
225{
226 int i;
226d76e6 227 struct libscols_line *line;
6bac2825 228
226d76e6 229 assert(table);
6bac2825
DB
230 assert(l);
231
226d76e6 232 line = scols_table_new_line(table, NULL);
05186e79 233 if (!line)
780ce22c 234 err(EXIT_FAILURE, _("failed to allocate output line"));
6bac2825
DB
235
236 for (i = 0; i < ncolumns; i++) {
237 char *str = NULL;
6bac2825
DB
238
239 switch (get_column_id(i)) {
240 case COL_RES:
91f6895f
KZ
241 if (l->desc->name)
242 str = xstrdup(l->desc->name);
6bac2825
DB
243 break;
244 case COL_HELP:
91f6895f
KZ
245 if (l->desc->help)
246 str = xstrdup(_(l->desc->help));
6bac2825
DB
247 break;
248 case COL_SOFT:
76a9fcc9 249 if (l->rlim.rlim_cur == RLIM_INFINITY)
1e770851 250 str = xstrdup(_("unlimited"));
76a9fcc9 251 else
6f312c89 252 xasprintf(&str, "%llu", (unsigned long long) l->rlim.rlim_cur);
6bac2825
DB
253 break;
254 case COL_HARD:
76a9fcc9 255 if (l->rlim.rlim_max == RLIM_INFINITY)
1e770851 256 str = xstrdup(_("unlimited"));
76a9fcc9 257 else
6f312c89 258 xasprintf(&str, "%llu", (unsigned long long) l->rlim.rlim_max);
6bac2825 259 break;
d76f904a 260 case COL_UNITS:
91f6895f
KZ
261 if (l->desc->unit)
262 str = xstrdup(_(l->desc->unit));
d76f904a 263 break;
6bac2825
DB
264 default:
265 break;
266 }
267
780ce22c
KZ
268 if (str && scols_line_refer_data(line, i, str))
269 err(EXIT_FAILURE, _("failed to add output data"));
6bac2825
DB
270 }
271}
272
273static int column_name_to_id(const char *name, size_t namesz)
274{
275 size_t i;
276
277 assert(name);
278
7887c6e0 279 for (i = 0; i < ARRAY_SIZE(infos); i++) {
6bac2825
DB
280 const char *cn = infos[i].name;
281
282 if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
283 return i;
284 }
285 warnx(_("unknown column: %s"), name);
286 return -1;
287}
288
a971fa18
DB
289static void rem_prlim(struct prlimit *lim)
290{
291 if (!lim)
292 return;
293 list_del(&lim->lims);
294 free(lim);
295}
296
226d76e6 297static int show_limits(struct list_head *lims)
6bac2825
DB
298{
299 int i;
94c01662 300 struct list_head *p, *pnext;
226d76e6 301 struct libscols_table *table;
6bac2825 302
0925a9dd 303 table = scols_new_table();
05186e79 304 if (!table)
780ce22c 305 err(EXIT_FAILURE, _("failed to allocate output table"));
05186e79 306
0925a9dd
KZ
307 scols_table_enable_raw(table, raw);
308 scols_table_enable_noheadings(table, no_headings);
6bac2825
DB
309
310 for (i = 0; i < ncolumns; i++) {
311 struct colinfo *col = get_column_info(i);
312
05186e79 313 if (!scols_table_new_column(table, col->name, col->whint, col->flags))
780ce22c 314 err(EXIT_FAILURE, _("failed to allocate output column"));
6bac2825
DB
315 }
316
94c01662
KZ
317 list_for_each_safe(p, pnext, lims) {
318 struct prlimit *lim = list_entry(p, struct prlimit, lims);
319
226d76e6 320 add_scols_line(table, lim);
94c01662
KZ
321 rem_prlim(lim);
322 }
6bac2825 323
226d76e6 324 scols_print_table(table);
226d76e6 325 scols_unref_table(table);
6bac2825
DB
326 return 0;
327}
328
044bc8de
BV
329/*
330 * If one of the limits is unknown (default value for not being passed), we
331 * need to get the current limit and use it. I see no other way other than
332 * using prlimit(2).
333 */
334static void get_unknown_hardsoft(struct prlimit *lim)
335{
336 struct rlimit old;
337
338 if (prlimit(pid, lim->desc->resource, NULL, &old) == -1)
339 err(EXIT_FAILURE, _("failed to get old %s limit"),
340 lim->desc->name);
341
342 if (!(lim->modify & PRLIMIT_SOFT))
343 lim->rlim.rlim_cur = old.rlim_cur;
344 else if (!(lim->modify & PRLIMIT_HARD))
345 lim->rlim.rlim_max = old.rlim_max;
346}
347
94c01662 348static void do_prlimit(struct list_head *lims)
6bac2825 349{
94c01662 350 struct list_head *p, *pnext;
6bac2825 351
94c01662 352 list_for_each_safe(p, pnext, lims) {
8b6e4503 353 struct rlimit *new = NULL, *old = NULL;
94c01662 354 struct prlimit *lim = list_entry(p, struct prlimit, lims);
6bac2825 355
2f0948b1
KZ
356 if (lim->modify) {
357 if (lim->modify != (PRLIMIT_HARD | PRLIMIT_SOFT))
358 get_unknown_hardsoft(lim);
044bc8de 359
2f0948b1
KZ
360 if ((lim->rlim.rlim_cur > lim->rlim.rlim_max) &&
361 (lim->rlim.rlim_cur != RLIM_INFINITY ||
362 lim->rlim.rlim_max != RLIM_INFINITY))
044bc8de 363 errx(EXIT_FAILURE, _("the soft limit %s cannot exceed the hard limit"),
2f0948b1
KZ
364 lim->desc->name);
365 new = &lim->rlim;
8b6e4503
KZ
366 } else
367 old = &lim->rlim;
6bac2825
DB
368
369 if (verbose && new) {
288cb6f5
SK
370 printf(_("New %s limit for pid %d: "), lim->desc->name,
371 pid ? pid : getpid());
6bac2825
DB
372 if (new->rlim_cur == RLIM_INFINITY)
373 printf("<%s", _("unlimited"));
374 else
f2ff0adf 375 printf("<%ju", (uintmax_t)new->rlim_cur);
6bac2825
DB
376
377 if (new->rlim_max == RLIM_INFINITY)
378 printf(":%s>\n", _("unlimited"));
379 else
f2ff0adf 380 printf(":%ju>\n", (uintmax_t)new->rlim_max);
6bac2825
DB
381 }
382
8b6e4503 383 if (prlimit(pid, lim->desc->resource, new, old) == -1)
2f0948b1 384 err(EXIT_FAILURE, lim->modify ?
f88e44be
KZ
385 _("failed to set the %s resource limit") :
386 _("failed to get the %s resource limit"),
2f0948b1 387 lim->desc->name);
6bac2825 388
94c01662
KZ
389 if (lim->modify)
390 rem_prlim(lim); /* modify only; don't show */
391 }
6bac2825
DB
392}
393
6bac2825
DB
394static int get_range(char *str, rlim_t *soft, rlim_t *hard, int *found)
395{
396 char *end = NULL;
397
398 if (!str)
399 return 0;
400
401 *found = errno = 0;
402 *soft = *hard = RLIM_INFINITY;
403
404 if (!strcmp(str, INFINITY_STR)) { /* <unlimited> */
405 *found |= PRLIMIT_SOFT | PRLIMIT_HARD;
406 return 0;
407
042f62df
RP
408 }
409
410 if (*str == ':') { /* <:hard> */
6bac2825
DB
411 str++;
412
413 if (strcmp(str, INFINITY_STR) != 0) {
414 *hard = strtoull(str, &end, 10);
415
416 if (errno || !end || *end || end == str)
417 return -1;
418 }
419 *found |= PRLIMIT_HARD;
420 return 0;
421
422 }
423
424 if (strncmp(str, INFINITY_STR, INFINITY_STRLEN) == 0) {
425 /* <unlimited> or <unlimited:> */
426 end = str + INFINITY_STRLEN;
427 } else {
428 /* <value> or <soft:> */
429 *hard = *soft = strtoull(str, &end, 10);
430 if (errno || !end || end == str)
431 return -1;
432 }
433
434 if (*end == ':' && !*(end + 1)) /* <soft:> */
435 *found |= PRLIMIT_SOFT;
436
437 else if (*end == ':') { /* <soft:hard> */
438 str = end + 1;
439
440 if (!strcmp(str, INFINITY_STR))
441 *hard = RLIM_INFINITY;
442 else {
443 end = NULL;
444 errno = 0;
445 *hard = strtoull(str, &end, 10);
446
447 if (errno || !end || *end || end == str)
448 return -1;
449 }
450 *found |= PRLIMIT_SOFT | PRLIMIT_HARD;
451
452 } else /* <value> */
453 *found |= PRLIMIT_SOFT | PRLIMIT_HARD;
454
455 return 0;
456}
457
458
459static int parse_prlim(struct rlimit *lim, char *ops, size_t id)
460{
87559313 461 rlim_t soft = 0, hard = 0;
6bac2825
DB
462 int found = 0;
463
1ad8db55
KZ
464 if (ops && *ops == '=')
465 ops++;
466
6bac2825
DB
467 if (get_range(ops, &soft, &hard, &found))
468 errx(EXIT_FAILURE, _("failed to parse %s limit"),
469 prlimit_desc[id].name);
470
6bac2825
DB
471 lim->rlim_cur = soft;
472 lim->rlim_max = hard;
473
044bc8de 474 return found;
6bac2825
DB
475}
476
16fb9b3d 477static int add_prlim(char *ops, struct list_head *lims, size_t id)
6bac2825 478{
212db7f2 479 struct prlimit *lim = xcalloc(1, sizeof(*lim));
94c01662
KZ
480
481 INIT_LIST_HEAD(&lim->lims);
6bac2825
DB
482 lim->desc = &prlimit_desc[id];
483
044bc8de
BV
484 if (ops)
485 lim->modify = parse_prlim(&lim->rlim, ops, id);
6bac2825 486
94c01662 487 list_add_tail(&lim->lims, lims);
6bac2825
DB
488 return 0;
489}
490
491int main(int argc, char **argv)
492{
226d76e6 493 int opt;
94c01662 494 struct list_head lims;
6bac2825
DB
495
496 enum {
d254c1db
KZ
497 VERBOSE_OPTION = CHAR_MAX + 1,
498 RAW_OPTION,
499 NOHEADINGS_OPTION
6bac2825
DB
500 };
501
502 static const struct option longopts[] = {
503 { "pid", required_argument, NULL, 'p' },
504 { "output", required_argument, NULL, 'o' },
505 { "as", optional_argument, NULL, 'v' },
506 { "core", optional_argument, NULL, 'c' },
507 { "cpu", optional_argument, NULL, 't' },
508 { "data", optional_argument, NULL, 'd' },
509 { "fsize", optional_argument, NULL, 'f' },
510 { "locks", optional_argument, NULL, 'x' },
511 { "memlock", optional_argument, NULL, 'l' },
512 { "msgqueue", optional_argument, NULL, 'q' },
513 { "nice", optional_argument, NULL, 'e' },
514 { "nofile", optional_argument, NULL, 'n' },
515 { "nproc", optional_argument, NULL, 'u' },
516 { "rss", optional_argument, NULL, 'm' },
517 { "rtprio", optional_argument, NULL, 'r' },
518 { "rttime", optional_argument, NULL, 'y' },
519 { "sigpending", optional_argument, NULL, 'i' },
520 { "stack", optional_argument, NULL, 's' },
521 { "version", no_argument, NULL, 'V' },
522 { "help", no_argument, NULL, 'h' },
d254c1db
KZ
523 { "noheadings", no_argument, NULL, NOHEADINGS_OPTION },
524 { "raw", no_argument, NULL, RAW_OPTION },
6bac2825
DB
525 { "verbose", no_argument, NULL, VERBOSE_OPTION },
526 { NULL, 0, NULL, 0 }
527 };
528
529 setlocale(LC_ALL, "");
530 bindtextdomain(PACKAGE, LOCALEDIR);
531 textdomain(PACKAGE);
2c308875 532 close_stdout_atexit();
6bac2825 533
94c01662
KZ
534 INIT_LIST_HEAD(&lims);
535
6bac2825
DB
536 /*
537 * Something is very wrong if this doesn't succeed,
538 * assuming STACK is the last resource, of course.
539 */
540 assert(MAX_RESOURCES == STACK + 1);
541
542 while((opt = getopt_long(argc, argv,
53e1f461 543 "+c::d::e::f::i::l::m::n::q::r::s::t::u::v::x::y::p:o:vVh",
6bac2825
DB
544 longopts, NULL)) != -1) {
545 switch(opt) {
546 case 'c':
94c01662 547 add_prlim(optarg, &lims, CORE);
6bac2825
DB
548 break;
549 case 'd':
94c01662 550 add_prlim(optarg, &lims, DATA);
6bac2825
DB
551 break;
552 case 'e':
94c01662 553 add_prlim(optarg, &lims, NICE);
6bac2825
DB
554 break;
555 case 'f':
94c01662 556 add_prlim(optarg, &lims, FSIZE);
6bac2825
DB
557 break;
558 case 'i':
94c01662 559 add_prlim(optarg, &lims, SIGPENDING);
6bac2825
DB
560 break;
561 case 'l':
94c01662 562 add_prlim(optarg, &lims, MEMLOCK);
6bac2825
DB
563 break;
564 case 'm':
94c01662 565 add_prlim(optarg, &lims, RSS);
6bac2825
DB
566 break;
567 case 'n':
94c01662 568 add_prlim(optarg, &lims, NOFILE);
6bac2825
DB
569 break;
570 case 'q':
94c01662 571 add_prlim(optarg, &lims, MSGQUEUE);
6bac2825
DB
572 break;
573 case 'r':
94c01662 574 add_prlim(optarg, &lims, RTPRIO);
6bac2825
DB
575 break;
576 case 's':
94c01662 577 add_prlim(optarg, &lims, STACK);
6bac2825
DB
578 break;
579 case 't':
94c01662 580 add_prlim(optarg, &lims, CPU);
6bac2825
DB
581 break;
582 case 'u':
94c01662 583 add_prlim(optarg, &lims, NPROC);
6bac2825
DB
584 break;
585 case 'v':
94c01662 586 add_prlim(optarg, &lims, AS);
6bac2825
DB
587 break;
588 case 'x':
94c01662 589 add_prlim(optarg, &lims, LOCKS);
6bac2825
DB
590 break;
591 case 'y':
94c01662 592 add_prlim(optarg, &lims, RTTIME);
6bac2825
DB
593 break;
594
595 case 'p':
2dac37dd 596 if (pid)
a1ec6137 597 errx(EXIT_FAILURE, _("option --pid may be specified only once"));
20a39982 598 pid = strtos32_or_err(optarg, _("invalid PID argument"));
6bac2825 599 break;
6bac2825
DB
600 case 'o':
601 ncolumns = string_to_idarray(optarg,
602 columns, ARRAY_SIZE(columns),
603 column_name_to_id);
604 if (ncolumns < 0)
605 return EXIT_FAILURE;
606 break;
d254c1db 607 case NOHEADINGS_OPTION:
226d76e6 608 no_headings = 1;
d254c1db 609 break;
6bac2825
DB
610 case VERBOSE_OPTION:
611 verbose++;
612 break;
d254c1db 613 case RAW_OPTION:
226d76e6 614 raw = 1;
d254c1db 615 break;
2c308875
KZ
616
617 case 'h':
618 usage();
619 case 'V':
620 print_version(EXIT_SUCCESS);
6bac2825 621 default:
677ec86c 622 errtryhelp(EXIT_FAILURE);
6bac2825
DB
623 }
624 }
53e1f461 625 if (argc > optind && pid)
a1ec6137 626 errx(EXIT_FAILURE, _("options --pid and COMMAND are mutually exclusive"));
6bac2825
DB
627 if (!ncolumns) {
628 /* default columns */
629 columns[ncolumns++] = COL_RES;
630 columns[ncolumns++] = COL_HELP;
631 columns[ncolumns++] = COL_SOFT;
632 columns[ncolumns++] = COL_HARD;
d76f904a 633 columns[ncolumns++] = COL_UNITS;
6bac2825
DB
634 }
635
710ed55d
KZ
636 scols_init_debug(0);
637
94c01662 638 if (list_empty(&lims)) {
6bac2825 639 /* default is to print all resources */
94c01662
KZ
640 size_t n;
641
642 for (n = 0; n < MAX_RESOURCES; n++)
643 add_prlim(NULL, &lims, n);
6bac2825
DB
644 }
645
94c01662
KZ
646 do_prlimit(&lims);
647
648 if (!list_empty(&lims))
226d76e6 649 show_limits(&lims);
6bac2825 650
53e1f461
BV
651 if (argc > optind) {
652 /* prlimit [options] COMMAND */
653 execvp(argv[optind], &argv[optind]);
fd777151 654 errexec(argv[optind]);
53e1f461
BV
655 }
656
6bac2825
DB
657 return EXIT_SUCCESS;
658}