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