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