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