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