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