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