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