]>
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 | ||
86be6a32 | 154 | static void __attribute__((__noreturn__)) usage(void) |
6bac2825 | 155 | { |
86be6a32 | 156 | FILE *out = stdout; |
6bac2825 DB |
157 | size_t i; |
158 | ||
159 | fputs(USAGE_HEADER, out); | |
160 | ||
161 | fprintf(out, | |
53e1f461 BV |
162 | _(" %s [options] [-p PID]\n"), program_invocation_short_name); |
163 | fprintf(out, | |
164 | _(" %s [options] COMMAND\n"), program_invocation_short_name); | |
6bac2825 | 165 | |
451dbcfa BS |
166 | fputs(USAGE_SEPARATOR, out); |
167 | fputs(_("Show or change the resource limits of a process.\n"), out); | |
168 | ||
6bac2825 DB |
169 | fputs(_("\nGeneral Options:\n"), out); |
170 | fputs(_(" -p, --pid <pid> process id\n" | |
d254c1db KZ |
171 | " -o, --output <list> define which output columns to use\n" |
172 | " --noheadings don't print headings\n" | |
173 | " --raw use the raw output format\n" | |
6bac2825 | 174 | " --verbose verbose output\n" |
b3054454 | 175 | ), out); |
f45f3ec3 | 176 | printf(USAGE_HELP_OPTIONS(24)); |
6bac2825 DB |
177 | |
178 | fputs(_("\nResources Options:\n"), out); | |
179 | fputs(_(" -c, --core maximum size of core files created\n" | |
180 | " -d, --data maximum size of a process's data segment\n" | |
181 | " -e, --nice maximum nice priority allowed to raise\n" | |
182 | " -f, --fsize maximum size of files written by the process\n" | |
1023db50 | 183 | " -i, --sigpending maximum number of pending signals\n" |
6bac2825 DB |
184 | " -l, --memlock maximum size a process may lock into memory\n" |
185 | " -m, --rss maximum resident set size\n" | |
1023db50 | 186 | " -n, --nofile maximum number of open files\n" |
6bac2825 DB |
187 | " -q, --msgqueue maximum bytes in POSIX message queues\n" |
188 | " -r, --rtprio maximum real-time scheduling priority\n" | |
189 | " -s, --stack maximum stack size\n" | |
190 | " -t, --cpu maximum amount of CPU time in seconds\n" | |
191 | " -u, --nproc maximum number of user processes\n" | |
192 | " -v, --as size of virtual memory\n" | |
193 | " -x, --locks maximum number of file locks\n" | |
194 | " -y, --rttime CPU time in microseconds a process scheduled\n" | |
195 | " under real-time scheduling\n"), out); | |
196 | ||
c3a4cfc5 | 197 | fputs(USAGE_COLUMNS, out); |
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 | ||
f45f3ec3 | 201 | printf(USAGE_MAN_TAIL("prlimit(1)")); |
6bac2825 | 202 | |
86be6a32 | 203 | exit(EXIT_SUCCESS); |
6bac2825 DB |
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 | 228 | if (!line) |
780ce22c | 229 | err(EXIT_FAILURE, _("failed to allocate 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 | ||
780ce22c KZ |
260 | if (str && scols_line_refer_data(line, i, str)) |
261 | err(EXIT_FAILURE, _("failed to add output data")); | |
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 | 296 | if (!table) |
780ce22c | 297 | err(EXIT_FAILURE, _("failed to allocate output table")); |
05186e79 | 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 | 305 | if (!scols_table_new_column(table, col->name, col->whint, col->flags)) |
780ce22c | 306 | err(EXIT_FAILURE, _("failed to allocate output column")); |
6bac2825 DB |
307 | } |
308 | ||
94c01662 KZ |
309 | list_for_each_safe(p, pnext, lims) { |
310 | struct prlimit *lim = list_entry(p, struct prlimit, lims); | |
311 | ||
226d76e6 | 312 | add_scols_line(table, lim); |
94c01662 KZ |
313 | rem_prlim(lim); |
314 | } | |
6bac2825 | 315 | |
226d76e6 | 316 | scols_print_table(table); |
226d76e6 | 317 | scols_unref_table(table); |
6bac2825 DB |
318 | return 0; |
319 | } | |
320 | ||
044bc8de BV |
321 | /* |
322 | * If one of the limits is unknown (default value for not being passed), we | |
323 | * need to get the current limit and use it. I see no other way other than | |
324 | * using prlimit(2). | |
325 | */ | |
326 | static void get_unknown_hardsoft(struct prlimit *lim) | |
327 | { | |
328 | struct rlimit old; | |
329 | ||
330 | if (prlimit(pid, lim->desc->resource, NULL, &old) == -1) | |
331 | err(EXIT_FAILURE, _("failed to get old %s limit"), | |
332 | lim->desc->name); | |
333 | ||
334 | if (!(lim->modify & PRLIMIT_SOFT)) | |
335 | lim->rlim.rlim_cur = old.rlim_cur; | |
336 | else if (!(lim->modify & PRLIMIT_HARD)) | |
337 | lim->rlim.rlim_max = old.rlim_max; | |
338 | } | |
339 | ||
94c01662 | 340 | static void do_prlimit(struct list_head *lims) |
6bac2825 | 341 | { |
94c01662 | 342 | struct list_head *p, *pnext; |
6bac2825 | 343 | |
94c01662 | 344 | list_for_each_safe(p, pnext, lims) { |
8b6e4503 | 345 | struct rlimit *new = NULL, *old = NULL; |
94c01662 | 346 | struct prlimit *lim = list_entry(p, struct prlimit, lims); |
6bac2825 | 347 | |
2f0948b1 KZ |
348 | if (lim->modify) { |
349 | if (lim->modify != (PRLIMIT_HARD | PRLIMIT_SOFT)) | |
350 | get_unknown_hardsoft(lim); | |
044bc8de | 351 | |
2f0948b1 KZ |
352 | if ((lim->rlim.rlim_cur > lim->rlim.rlim_max) && |
353 | (lim->rlim.rlim_cur != RLIM_INFINITY || | |
354 | lim->rlim.rlim_max != RLIM_INFINITY)) | |
044bc8de | 355 | errx(EXIT_FAILURE, _("the soft limit %s cannot exceed the hard limit"), |
2f0948b1 KZ |
356 | lim->desc->name); |
357 | new = &lim->rlim; | |
8b6e4503 KZ |
358 | } else |
359 | old = &lim->rlim; | |
6bac2825 DB |
360 | |
361 | if (verbose && new) { | |
288cb6f5 SK |
362 | printf(_("New %s limit for pid %d: "), lim->desc->name, |
363 | pid ? pid : getpid()); | |
6bac2825 DB |
364 | if (new->rlim_cur == RLIM_INFINITY) |
365 | printf("<%s", _("unlimited")); | |
366 | else | |
f2ff0adf | 367 | printf("<%ju", (uintmax_t)new->rlim_cur); |
6bac2825 DB |
368 | |
369 | if (new->rlim_max == RLIM_INFINITY) | |
370 | printf(":%s>\n", _("unlimited")); | |
371 | else | |
f2ff0adf | 372 | printf(":%ju>\n", (uintmax_t)new->rlim_max); |
6bac2825 DB |
373 | } |
374 | ||
8b6e4503 | 375 | if (prlimit(pid, lim->desc->resource, new, old) == -1) |
2f0948b1 | 376 | err(EXIT_FAILURE, lim->modify ? |
f88e44be KZ |
377 | _("failed to set the %s resource limit") : |
378 | _("failed to get the %s resource limit"), | |
2f0948b1 | 379 | lim->desc->name); |
6bac2825 | 380 | |
94c01662 KZ |
381 | if (lim->modify) |
382 | rem_prlim(lim); /* modify only; don't show */ | |
383 | } | |
6bac2825 DB |
384 | } |
385 | ||
6bac2825 DB |
386 | static int get_range(char *str, rlim_t *soft, rlim_t *hard, int *found) |
387 | { | |
388 | char *end = NULL; | |
389 | ||
390 | if (!str) | |
391 | return 0; | |
392 | ||
393 | *found = errno = 0; | |
394 | *soft = *hard = RLIM_INFINITY; | |
395 | ||
396 | if (!strcmp(str, INFINITY_STR)) { /* <unlimited> */ | |
397 | *found |= PRLIMIT_SOFT | PRLIMIT_HARD; | |
398 | return 0; | |
399 | ||
400 | } else if (*str == ':') { /* <:hard> */ | |
401 | str++; | |
402 | ||
403 | if (strcmp(str, INFINITY_STR) != 0) { | |
404 | *hard = strtoull(str, &end, 10); | |
405 | ||
406 | if (errno || !end || *end || end == str) | |
407 | return -1; | |
408 | } | |
409 | *found |= PRLIMIT_HARD; | |
410 | return 0; | |
411 | ||
412 | } | |
413 | ||
414 | if (strncmp(str, INFINITY_STR, INFINITY_STRLEN) == 0) { | |
415 | /* <unlimited> or <unlimited:> */ | |
416 | end = str + INFINITY_STRLEN; | |
417 | } else { | |
418 | /* <value> or <soft:> */ | |
419 | *hard = *soft = strtoull(str, &end, 10); | |
420 | if (errno || !end || end == str) | |
421 | return -1; | |
422 | } | |
423 | ||
424 | if (*end == ':' && !*(end + 1)) /* <soft:> */ | |
425 | *found |= PRLIMIT_SOFT; | |
426 | ||
427 | else if (*end == ':') { /* <soft:hard> */ | |
428 | str = end + 1; | |
429 | ||
430 | if (!strcmp(str, INFINITY_STR)) | |
431 | *hard = RLIM_INFINITY; | |
432 | else { | |
433 | end = NULL; | |
434 | errno = 0; | |
435 | *hard = strtoull(str, &end, 10); | |
436 | ||
437 | if (errno || !end || *end || end == str) | |
438 | return -1; | |
439 | } | |
440 | *found |= PRLIMIT_SOFT | PRLIMIT_HARD; | |
441 | ||
442 | } else /* <value> */ | |
443 | *found |= PRLIMIT_SOFT | PRLIMIT_HARD; | |
444 | ||
445 | return 0; | |
446 | } | |
447 | ||
448 | ||
449 | static int parse_prlim(struct rlimit *lim, char *ops, size_t id) | |
450 | { | |
451 | rlim_t soft, hard; | |
452 | int found = 0; | |
453 | ||
454 | if (get_range(ops, &soft, &hard, &found)) | |
455 | errx(EXIT_FAILURE, _("failed to parse %s limit"), | |
456 | prlimit_desc[id].name); | |
457 | ||
6bac2825 DB |
458 | lim->rlim_cur = soft; |
459 | lim->rlim_max = hard; | |
460 | ||
044bc8de | 461 | return found; |
6bac2825 DB |
462 | } |
463 | ||
16fb9b3d | 464 | static int add_prlim(char *ops, struct list_head *lims, size_t id) |
6bac2825 | 465 | { |
212db7f2 | 466 | struct prlimit *lim = xcalloc(1, sizeof(*lim)); |
94c01662 KZ |
467 | |
468 | INIT_LIST_HEAD(&lim->lims); | |
6bac2825 DB |
469 | lim->desc = &prlimit_desc[id]; |
470 | ||
044bc8de BV |
471 | if (ops) |
472 | lim->modify = parse_prlim(&lim->rlim, ops, id); | |
6bac2825 | 473 | |
94c01662 | 474 | list_add_tail(&lim->lims, lims); |
6bac2825 DB |
475 | return 0; |
476 | } | |
477 | ||
478 | int main(int argc, char **argv) | |
479 | { | |
226d76e6 | 480 | int opt; |
94c01662 | 481 | struct list_head lims; |
6bac2825 DB |
482 | |
483 | enum { | |
d254c1db KZ |
484 | VERBOSE_OPTION = CHAR_MAX + 1, |
485 | RAW_OPTION, | |
486 | NOHEADINGS_OPTION | |
6bac2825 DB |
487 | }; |
488 | ||
489 | static const struct option longopts[] = { | |
490 | { "pid", required_argument, NULL, 'p' }, | |
491 | { "output", required_argument, NULL, 'o' }, | |
492 | { "as", optional_argument, NULL, 'v' }, | |
493 | { "core", optional_argument, NULL, 'c' }, | |
494 | { "cpu", optional_argument, NULL, 't' }, | |
495 | { "data", optional_argument, NULL, 'd' }, | |
496 | { "fsize", optional_argument, NULL, 'f' }, | |
497 | { "locks", optional_argument, NULL, 'x' }, | |
498 | { "memlock", optional_argument, NULL, 'l' }, | |
499 | { "msgqueue", optional_argument, NULL, 'q' }, | |
500 | { "nice", optional_argument, NULL, 'e' }, | |
501 | { "nofile", optional_argument, NULL, 'n' }, | |
502 | { "nproc", optional_argument, NULL, 'u' }, | |
503 | { "rss", optional_argument, NULL, 'm' }, | |
504 | { "rtprio", optional_argument, NULL, 'r' }, | |
505 | { "rttime", optional_argument, NULL, 'y' }, | |
506 | { "sigpending", optional_argument, NULL, 'i' }, | |
507 | { "stack", optional_argument, NULL, 's' }, | |
508 | { "version", no_argument, NULL, 'V' }, | |
509 | { "help", no_argument, NULL, 'h' }, | |
d254c1db KZ |
510 | { "noheadings", no_argument, NULL, NOHEADINGS_OPTION }, |
511 | { "raw", no_argument, NULL, RAW_OPTION }, | |
6bac2825 DB |
512 | { "verbose", no_argument, NULL, VERBOSE_OPTION }, |
513 | { NULL, 0, NULL, 0 } | |
514 | }; | |
515 | ||
516 | setlocale(LC_ALL, ""); | |
517 | bindtextdomain(PACKAGE, LOCALEDIR); | |
518 | textdomain(PACKAGE); | |
efb8854f | 519 | atexit(close_stdout); |
6bac2825 | 520 | |
94c01662 KZ |
521 | INIT_LIST_HEAD(&lims); |
522 | ||
6bac2825 DB |
523 | /* |
524 | * Something is very wrong if this doesn't succeed, | |
525 | * assuming STACK is the last resource, of course. | |
526 | */ | |
527 | assert(MAX_RESOURCES == STACK + 1); | |
528 | ||
529 | while((opt = getopt_long(argc, argv, | |
53e1f461 | 530 | "+c::d::e::f::i::l::m::n::q::r::s::t::u::v::x::y::p:o:vVh", |
6bac2825 DB |
531 | longopts, NULL)) != -1) { |
532 | switch(opt) { | |
533 | case 'c': | |
94c01662 | 534 | add_prlim(optarg, &lims, CORE); |
6bac2825 DB |
535 | break; |
536 | case 'd': | |
94c01662 | 537 | add_prlim(optarg, &lims, DATA); |
6bac2825 DB |
538 | break; |
539 | case 'e': | |
94c01662 | 540 | add_prlim(optarg, &lims, NICE); |
6bac2825 DB |
541 | break; |
542 | case 'f': | |
94c01662 | 543 | add_prlim(optarg, &lims, FSIZE); |
6bac2825 DB |
544 | break; |
545 | case 'i': | |
94c01662 | 546 | add_prlim(optarg, &lims, SIGPENDING); |
6bac2825 DB |
547 | break; |
548 | case 'l': | |
94c01662 | 549 | add_prlim(optarg, &lims, MEMLOCK); |
6bac2825 DB |
550 | break; |
551 | case 'm': | |
94c01662 | 552 | add_prlim(optarg, &lims, RSS); |
6bac2825 DB |
553 | break; |
554 | case 'n': | |
94c01662 | 555 | add_prlim(optarg, &lims, NOFILE); |
6bac2825 DB |
556 | break; |
557 | case 'q': | |
94c01662 | 558 | add_prlim(optarg, &lims, MSGQUEUE); |
6bac2825 DB |
559 | break; |
560 | case 'r': | |
94c01662 | 561 | add_prlim(optarg, &lims, RTPRIO); |
6bac2825 DB |
562 | break; |
563 | case 's': | |
94c01662 | 564 | add_prlim(optarg, &lims, STACK); |
6bac2825 DB |
565 | break; |
566 | case 't': | |
94c01662 | 567 | add_prlim(optarg, &lims, CPU); |
6bac2825 DB |
568 | break; |
569 | case 'u': | |
94c01662 | 570 | add_prlim(optarg, &lims, NPROC); |
6bac2825 DB |
571 | break; |
572 | case 'v': | |
94c01662 | 573 | add_prlim(optarg, &lims, AS); |
6bac2825 DB |
574 | break; |
575 | case 'x': | |
94c01662 | 576 | add_prlim(optarg, &lims, LOCKS); |
6bac2825 DB |
577 | break; |
578 | case 'y': | |
94c01662 | 579 | add_prlim(optarg, &lims, RTTIME); |
6bac2825 DB |
580 | break; |
581 | ||
582 | case 'p': | |
2dac37dd | 583 | if (pid) |
a1ec6137 | 584 | errx(EXIT_FAILURE, _("option --pid may be specified only once")); |
20a39982 | 585 | pid = strtos32_or_err(optarg, _("invalid PID argument")); |
6bac2825 DB |
586 | break; |
587 | case 'h': | |
86be6a32 | 588 | usage(); |
6bac2825 DB |
589 | case 'o': |
590 | ncolumns = string_to_idarray(optarg, | |
591 | columns, ARRAY_SIZE(columns), | |
592 | column_name_to_id); | |
593 | if (ncolumns < 0) | |
594 | return EXIT_FAILURE; | |
595 | break; | |
596 | case 'V': | |
597 | printf(UTIL_LINUX_VERSION); | |
598 | return EXIT_SUCCESS; | |
d254c1db KZ |
599 | |
600 | case NOHEADINGS_OPTION: | |
226d76e6 | 601 | no_headings = 1; |
d254c1db | 602 | break; |
6bac2825 DB |
603 | case VERBOSE_OPTION: |
604 | verbose++; | |
605 | break; | |
d254c1db | 606 | case RAW_OPTION: |
226d76e6 | 607 | raw = 1; |
d254c1db | 608 | break; |
6bac2825 | 609 | default: |
677ec86c | 610 | errtryhelp(EXIT_FAILURE); |
6bac2825 DB |
611 | } |
612 | } | |
53e1f461 | 613 | if (argc > optind && pid) |
a1ec6137 | 614 | errx(EXIT_FAILURE, _("options --pid and COMMAND are mutually exclusive")); |
6bac2825 DB |
615 | if (!ncolumns) { |
616 | /* default columns */ | |
617 | columns[ncolumns++] = COL_RES; | |
618 | columns[ncolumns++] = COL_HELP; | |
619 | columns[ncolumns++] = COL_SOFT; | |
620 | columns[ncolumns++] = COL_HARD; | |
d76f904a | 621 | columns[ncolumns++] = COL_UNITS; |
6bac2825 DB |
622 | } |
623 | ||
710ed55d KZ |
624 | scols_init_debug(0); |
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)) | |
226d76e6 | 637 | show_limits(&lims); |
6bac2825 | 638 | |
53e1f461 BV |
639 | if (argc > optind) { |
640 | /* prlimit [options] COMMAND */ | |
641 | execvp(argv[optind], &argv[optind]); | |
fd777151 | 642 | errexec(argv[optind]); |
53e1f461 BV |
643 | } |
644 | ||
6bac2825 DB |
645 | return EXIT_SUCCESS; |
646 | } |