2 * prlimit - get/set process resource limits.
4 * Copyright (C) 2011 Davidlohr Bueso <dave@gnu.org>
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.
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.
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.
29 #include <sys/types.h>
30 #include <sys/resource.h>
32 #include <libsmartcols.h>
39 #include "closestream.h"
42 # define RLIMIT_RTTIME 15
64 /* basic output flags */
65 static int no_headings
;
75 static struct prlimit_desc prlimit_desc
[] =
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
}
95 #define MAX_RESOURCES ARRAY_SIZE(prlimit_desc)
98 struct list_head lims
;
101 struct prlimit_desc
*desc
;
102 int modify
; /* PRLIMIT_{SOFT,HARD} mask */
105 #define PRLIMIT_EMPTY_LIMIT {{ 0, 0, }, NULL, 0 }
117 const char * const name
; /* header */
118 double whint
; /* width hint (N < 1 is in percent of termwidth) */
119 int flags
; /* SCOLS_FL_* */
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")},
132 static int columns
[ARRAY_SIZE(infos
) * 2];
137 #define INFINITY_STR "unlimited"
138 #define INFINITY_STRLEN (sizeof(INFINITY_STR) - 1)
140 #define PRLIMIT_SOFT (1 << 1)
141 #define PRLIMIT_HARD (1 << 2)
143 static pid_t pid
; /* calling process (default) */
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
)
154 return syscall(SYS_prlimit64
, p
, resource
, new_limit
, old_limit
);
156 # endif /* !HAVE_PRLIMIT */
157 # endif /* SYS_prlimit64 */
158 #endif /* HAVE_SYS_SYSCALL_H */
160 static void __attribute__((__noreturn__
)) usage(void)
165 fputs(USAGE_HEADER
, out
);
168 _(" %s [options] [--<resource>=<limit>] [-p PID]\n"), program_invocation_short_name
);
170 _(" %s [options] [--<resource>=<limit>] COMMAND\n"), program_invocation_short_name
);
172 fputs(USAGE_SEPARATOR
, out
);
173 fputs(_("Show or change the resource limits of a process.\n"), out
);
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"
182 printf(USAGE_HELP_OPTIONS(24));
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
);
203 fputs(USAGE_ARGUMENTS
, out
);
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
);
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
));
212 printf(USAGE_MAN_TAIL("prlimit(1)"));
217 static inline int get_column_id(int num
)
219 assert(num
< ncolumns
);
220 assert(columns
[num
] < (int) ARRAY_SIZE(infos
));
225 static inline const struct colinfo
*get_column_info(unsigned num
)
227 return &infos
[ get_column_id(num
) ];
230 static void add_scols_line(struct libscols_table
*table
, struct prlimit
*l
)
233 struct libscols_line
*line
;
238 line
= scols_table_new_line(table
, NULL
);
240 err(EXIT_FAILURE
, _("failed to allocate output line"));
242 for (i
= 0; i
< ncolumns
; i
++) {
245 switch (get_column_id(i
)) {
248 str
= xstrdup(l
->desc
->name
);
252 str
= xstrdup(_(l
->desc
->help
));
255 if (l
->rlim
.rlim_cur
== RLIM_INFINITY
)
256 str
= xstrdup(_("unlimited"));
258 xasprintf(&str
, "%llu", (unsigned long long) l
->rlim
.rlim_cur
);
261 if (l
->rlim
.rlim_max
== RLIM_INFINITY
)
262 str
= xstrdup(_("unlimited"));
264 xasprintf(&str
, "%llu", (unsigned long long) l
->rlim
.rlim_max
);
268 str
= xstrdup(_(l
->desc
->unit
));
274 if (str
&& scols_line_refer_data(line
, i
, str
))
275 err(EXIT_FAILURE
, _("failed to add output data"));
279 static int column_name_to_id(const char *name
, size_t namesz
)
285 for (i
= 0; i
< ARRAY_SIZE(infos
); i
++) {
286 const char *cn
= infos
[i
].name
;
288 if (!strncasecmp(name
, cn
, namesz
) && !*(cn
+ namesz
))
291 warnx(_("unknown column: %s"), name
);
295 static void rem_prlim(struct prlimit
*lim
)
299 list_del(&lim
->lims
);
303 static int show_limits(struct list_head
*lims
)
306 struct list_head
*p
, *pnext
;
307 struct libscols_table
*table
;
309 table
= scols_new_table();
311 err(EXIT_FAILURE
, _("failed to allocate output table"));
313 scols_table_enable_raw(table
, raw
);
314 scols_table_enable_noheadings(table
, no_headings
);
316 for (i
= 0; i
< ncolumns
; i
++) {
317 const struct colinfo
*col
= get_column_info(i
);
319 if (!scols_table_new_column(table
, col
->name
, col
->whint
, col
->flags
))
320 err(EXIT_FAILURE
, _("failed to allocate output column"));
323 list_for_each_safe(p
, pnext
, lims
) {
324 struct prlimit
*lim
= list_entry(p
, struct prlimit
, lims
);
326 add_scols_line(table
, lim
);
330 scols_print_table(table
);
331 scols_unref_table(table
);
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
340 static void get_unknown_hardsoft(struct prlimit
*lim
)
344 if (prlimit(pid
, lim
->desc
->resource
, NULL
, &old
) == -1)
345 err(EXIT_FAILURE
, _("failed to get old %s limit"),
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
;
354 static void do_prlimit(struct list_head
*lims
)
356 struct list_head
*p
, *pnext
;
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
);
363 if (lim
->modify
!= (PRLIMIT_HARD
| PRLIMIT_SOFT
))
364 get_unknown_hardsoft(lim
);
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"),
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"));
381 printf("<%ju", (uintmax_t)new->rlim_cur
);
383 if (new->rlim_max
== RLIM_INFINITY
)
384 printf(":%s>\n", _("unlimited"));
386 printf(":%ju>\n", (uintmax_t)new->rlim_max
);
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"),
396 rem_prlim(lim
); /* modify only; don't show */
400 static int get_range(char *str
, rlim_t
*soft
, rlim_t
*hard
, int *found
)
408 *soft
= *hard
= RLIM_INFINITY
;
410 if (!strcmp(str
, INFINITY_STR
)) { /* <unlimited> */
411 *found
|= PRLIMIT_SOFT
| PRLIMIT_HARD
;
416 if (*str
== ':') { /* <:hard> */
419 if (strcmp(str
, INFINITY_STR
) != 0) {
420 *hard
= strtoull(str
, &end
, 10);
422 if (errno
|| !end
|| *end
|| end
== str
)
425 *found
|= PRLIMIT_HARD
;
430 if (strncmp(str
, INFINITY_STR
, INFINITY_STRLEN
) == 0) {
431 /* <unlimited> or <unlimited:> */
432 end
= str
+ INFINITY_STRLEN
;
434 /* <value> or <soft:> */
435 *hard
= *soft
= strtoull(str
, &end
, 10);
436 if (errno
|| !end
|| end
== str
)
440 if (*end
== ':' && !*(end
+ 1)) /* <soft:> */
441 *found
|= PRLIMIT_SOFT
;
443 else if (*end
== ':') { /* <soft:hard> */
446 if (!strcmp(str
, INFINITY_STR
))
447 *hard
= RLIM_INFINITY
;
451 *hard
= strtoull(str
, &end
, 10);
453 if (errno
|| !end
|| *end
|| end
== str
)
456 *found
|= PRLIMIT_SOFT
| PRLIMIT_HARD
;
458 } else if (!*end
) /* <value> */
459 *found
|= PRLIMIT_SOFT
| PRLIMIT_HARD
;
467 static int parse_prlim(struct rlimit
*lim
, char *ops
, size_t id
)
469 rlim_t soft
= 0, hard
= 0;
472 if (ops
&& *ops
== '=')
475 if (get_range(ops
, &soft
, &hard
, &found
))
476 errx(EXIT_FAILURE
, _("failed to parse %s limit"),
477 prlimit_desc
[id
].name
);
479 lim
->rlim_cur
= soft
;
480 lim
->rlim_max
= hard
;
485 static int add_prlim(char *ops
, struct list_head
*lims
, size_t id
)
487 struct prlimit
*lim
= xcalloc(1, sizeof(*lim
));
489 INIT_LIST_HEAD(&lim
->lims
);
490 lim
->desc
= &prlimit_desc
[id
];
493 lim
->modify
= parse_prlim(&lim
->rlim
, ops
, id
);
495 list_add_tail(&lim
->lims
, lims
);
499 int main(int argc
, char **argv
)
502 struct list_head lims
;
505 VERBOSE_OPTION
= CHAR_MAX
+ 1,
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
},
537 setlocale(LC_ALL
, "");
538 bindtextdomain(PACKAGE
, LOCALEDIR
);
540 close_stdout_atexit();
542 INIT_LIST_HEAD(&lims
);
545 * Something is very wrong if this doesn't succeed,
546 * assuming STACK is the last resource, of course.
548 assert(MAX_RESOURCES
== STACK
+ 1);
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) {
555 add_prlim(optarg
, &lims
, CORE
);
558 add_prlim(optarg
, &lims
, DATA
);
561 add_prlim(optarg
, &lims
, NICE
);
564 add_prlim(optarg
, &lims
, FSIZE
);
567 add_prlim(optarg
, &lims
, SIGPENDING
);
570 add_prlim(optarg
, &lims
, MEMLOCK
);
573 add_prlim(optarg
, &lims
, RSS
);
576 add_prlim(optarg
, &lims
, NOFILE
);
579 add_prlim(optarg
, &lims
, MSGQUEUE
);
582 add_prlim(optarg
, &lims
, RTPRIO
);
585 add_prlim(optarg
, &lims
, STACK
);
588 add_prlim(optarg
, &lims
, CPU
);
591 add_prlim(optarg
, &lims
, NPROC
);
594 add_prlim(optarg
, &lims
, AS
);
597 add_prlim(optarg
, &lims
, LOCKS
);
600 add_prlim(optarg
, &lims
, RTTIME
);
605 errx(EXIT_FAILURE
, _("option --pid may be specified only once"));
606 pid
= strtos32_or_err(optarg
, _("invalid PID argument"));
609 ncolumns
= string_to_idarray(optarg
,
610 columns
, ARRAY_SIZE(columns
),
615 case NOHEADINGS_OPTION
:
628 print_version(EXIT_SUCCESS
);
630 errtryhelp(EXIT_FAILURE
);
633 if (argc
> optind
&& pid
)
634 errx(EXIT_FAILURE
, _("options --pid and COMMAND are mutually exclusive"));
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
;
646 if (list_empty(&lims
)) {
647 /* default is to print all resources */
650 for (n
= 0; n
< MAX_RESOURCES
; n
++)
651 add_prlim(NULL
, &lims
, n
);
656 if (!list_empty(&lims
))
660 /* prlimit [options] COMMAND */
661 execvp(argv
[optind
], &argv
[optind
]);
662 errexec(argv
[optind
]);