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