]>
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, | |
3d6a3d6d | 162 | _(" %s [options] [--<resource>=<limit>] [-p PID]\n"), program_invocation_short_name); |
53e1f461 | 163 | fprintf(out, |
3d6a3d6d | 164 | _(" %s [options] [--<resource>=<limit>] 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 | ||
3d6a3d6d | 169 | fputs(USAGE_OPTIONS, out); |
6bac2825 | 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 | 177 | |
3d6a3d6d | 178 | fputs(_("\nResources:\n"), out); |
6bac2825 DB |
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 | ||
3d6a3d6d KZ |
197 | fputs(USAGE_ARGUMENTS, out); |
198 | fputs(_( | |
199 | " <limit> is defined as a range soft:hard, soft:, :hard or a value to\n" | |
200 | " define both limits (e.g. -e=0:10 -r=:10).\n"), out); | |
201 | ||
c3a4cfc5 | 202 | fputs(USAGE_COLUMNS, out); |
7887c6e0 | 203 | for (i = 0; i < ARRAY_SIZE(infos); i++) |
6bac2825 DB |
204 | fprintf(out, " %11s %s\n", infos[i].name, _(infos[i].help)); |
205 | ||
f45f3ec3 | 206 | printf(USAGE_MAN_TAIL("prlimit(1)")); |
6bac2825 | 207 | |
86be6a32 | 208 | exit(EXIT_SUCCESS); |
6bac2825 DB |
209 | } |
210 | ||
211 | static inline int get_column_id(int num) | |
212 | { | |
6bac2825 | 213 | assert(num < ncolumns); |
7887c6e0 | 214 | assert(columns[num] < (int) ARRAY_SIZE(infos)); |
6bac2825 DB |
215 | |
216 | return columns[num]; | |
217 | } | |
218 | ||
219 | static inline struct colinfo *get_column_info(unsigned num) | |
220 | { | |
221 | return &infos[ get_column_id(num) ]; | |
222 | } | |
223 | ||
226d76e6 | 224 | static void add_scols_line(struct libscols_table *table, struct prlimit *l) |
6bac2825 DB |
225 | { |
226 | int i; | |
226d76e6 | 227 | struct libscols_line *line; |
6bac2825 | 228 | |
226d76e6 | 229 | assert(table); |
6bac2825 DB |
230 | assert(l); |
231 | ||
226d76e6 | 232 | line = scols_table_new_line(table, NULL); |
05186e79 | 233 | if (!line) |
780ce22c | 234 | err(EXIT_FAILURE, _("failed to allocate output line")); |
6bac2825 DB |
235 | |
236 | for (i = 0; i < ncolumns; i++) { | |
237 | char *str = NULL; | |
6bac2825 DB |
238 | |
239 | switch (get_column_id(i)) { | |
240 | case COL_RES: | |
91f6895f KZ |
241 | if (l->desc->name) |
242 | str = xstrdup(l->desc->name); | |
6bac2825 DB |
243 | break; |
244 | case COL_HELP: | |
91f6895f KZ |
245 | if (l->desc->help) |
246 | str = xstrdup(_(l->desc->help)); | |
6bac2825 DB |
247 | break; |
248 | case COL_SOFT: | |
76a9fcc9 | 249 | if (l->rlim.rlim_cur == RLIM_INFINITY) |
1e770851 | 250 | str = xstrdup(_("unlimited")); |
76a9fcc9 | 251 | else |
6f312c89 | 252 | xasprintf(&str, "%llu", (unsigned long long) l->rlim.rlim_cur); |
6bac2825 DB |
253 | break; |
254 | case COL_HARD: | |
76a9fcc9 | 255 | if (l->rlim.rlim_max == RLIM_INFINITY) |
1e770851 | 256 | str = xstrdup(_("unlimited")); |
76a9fcc9 | 257 | else |
6f312c89 | 258 | xasprintf(&str, "%llu", (unsigned long long) l->rlim.rlim_max); |
6bac2825 | 259 | break; |
d76f904a | 260 | case COL_UNITS: |
91f6895f KZ |
261 | if (l->desc->unit) |
262 | str = xstrdup(_(l->desc->unit)); | |
d76f904a | 263 | break; |
6bac2825 DB |
264 | default: |
265 | break; | |
266 | } | |
267 | ||
780ce22c KZ |
268 | if (str && scols_line_refer_data(line, i, str)) |
269 | err(EXIT_FAILURE, _("failed to add output data")); | |
6bac2825 DB |
270 | } |
271 | } | |
272 | ||
273 | static int column_name_to_id(const char *name, size_t namesz) | |
274 | { | |
275 | size_t i; | |
276 | ||
277 | assert(name); | |
278 | ||
7887c6e0 | 279 | for (i = 0; i < ARRAY_SIZE(infos); i++) { |
6bac2825 DB |
280 | const char *cn = infos[i].name; |
281 | ||
282 | if (!strncasecmp(name, cn, namesz) && !*(cn + namesz)) | |
283 | return i; | |
284 | } | |
285 | warnx(_("unknown column: %s"), name); | |
286 | return -1; | |
287 | } | |
288 | ||
a971fa18 DB |
289 | static void rem_prlim(struct prlimit *lim) |
290 | { | |
291 | if (!lim) | |
292 | return; | |
293 | list_del(&lim->lims); | |
294 | free(lim); | |
295 | } | |
296 | ||
226d76e6 | 297 | static int show_limits(struct list_head *lims) |
6bac2825 DB |
298 | { |
299 | int i; | |
94c01662 | 300 | struct list_head *p, *pnext; |
226d76e6 | 301 | struct libscols_table *table; |
6bac2825 | 302 | |
0925a9dd | 303 | table = scols_new_table(); |
05186e79 | 304 | if (!table) |
780ce22c | 305 | err(EXIT_FAILURE, _("failed to allocate output table")); |
05186e79 | 306 | |
0925a9dd KZ |
307 | scols_table_enable_raw(table, raw); |
308 | scols_table_enable_noheadings(table, no_headings); | |
6bac2825 DB |
309 | |
310 | for (i = 0; i < ncolumns; i++) { | |
311 | struct colinfo *col = get_column_info(i); | |
312 | ||
05186e79 | 313 | if (!scols_table_new_column(table, col->name, col->whint, col->flags)) |
780ce22c | 314 | err(EXIT_FAILURE, _("failed to allocate output column")); |
6bac2825 DB |
315 | } |
316 | ||
94c01662 KZ |
317 | list_for_each_safe(p, pnext, lims) { |
318 | struct prlimit *lim = list_entry(p, struct prlimit, lims); | |
319 | ||
226d76e6 | 320 | add_scols_line(table, lim); |
94c01662 KZ |
321 | rem_prlim(lim); |
322 | } | |
6bac2825 | 323 | |
226d76e6 | 324 | scols_print_table(table); |
226d76e6 | 325 | scols_unref_table(table); |
6bac2825 DB |
326 | return 0; |
327 | } | |
328 | ||
044bc8de BV |
329 | /* |
330 | * If one of the limits is unknown (default value for not being passed), we | |
331 | * need to get the current limit and use it. I see no other way other than | |
332 | * using prlimit(2). | |
333 | */ | |
334 | static void get_unknown_hardsoft(struct prlimit *lim) | |
335 | { | |
336 | struct rlimit old; | |
337 | ||
338 | if (prlimit(pid, lim->desc->resource, NULL, &old) == -1) | |
339 | err(EXIT_FAILURE, _("failed to get old %s limit"), | |
340 | lim->desc->name); | |
341 | ||
342 | if (!(lim->modify & PRLIMIT_SOFT)) | |
343 | lim->rlim.rlim_cur = old.rlim_cur; | |
344 | else if (!(lim->modify & PRLIMIT_HARD)) | |
345 | lim->rlim.rlim_max = old.rlim_max; | |
346 | } | |
347 | ||
94c01662 | 348 | static void do_prlimit(struct list_head *lims) |
6bac2825 | 349 | { |
94c01662 | 350 | struct list_head *p, *pnext; |
6bac2825 | 351 | |
94c01662 | 352 | list_for_each_safe(p, pnext, lims) { |
8b6e4503 | 353 | struct rlimit *new = NULL, *old = NULL; |
94c01662 | 354 | struct prlimit *lim = list_entry(p, struct prlimit, lims); |
6bac2825 | 355 | |
2f0948b1 KZ |
356 | if (lim->modify) { |
357 | if (lim->modify != (PRLIMIT_HARD | PRLIMIT_SOFT)) | |
358 | get_unknown_hardsoft(lim); | |
044bc8de | 359 | |
2f0948b1 KZ |
360 | if ((lim->rlim.rlim_cur > lim->rlim.rlim_max) && |
361 | (lim->rlim.rlim_cur != RLIM_INFINITY || | |
362 | lim->rlim.rlim_max != RLIM_INFINITY)) | |
044bc8de | 363 | errx(EXIT_FAILURE, _("the soft limit %s cannot exceed the hard limit"), |
2f0948b1 KZ |
364 | lim->desc->name); |
365 | new = &lim->rlim; | |
8b6e4503 KZ |
366 | } else |
367 | old = &lim->rlim; | |
6bac2825 DB |
368 | |
369 | if (verbose && new) { | |
288cb6f5 SK |
370 | printf(_("New %s limit for pid %d: "), lim->desc->name, |
371 | pid ? pid : getpid()); | |
6bac2825 DB |
372 | if (new->rlim_cur == RLIM_INFINITY) |
373 | printf("<%s", _("unlimited")); | |
374 | else | |
f2ff0adf | 375 | printf("<%ju", (uintmax_t)new->rlim_cur); |
6bac2825 DB |
376 | |
377 | if (new->rlim_max == RLIM_INFINITY) | |
378 | printf(":%s>\n", _("unlimited")); | |
379 | else | |
f2ff0adf | 380 | printf(":%ju>\n", (uintmax_t)new->rlim_max); |
6bac2825 DB |
381 | } |
382 | ||
8b6e4503 | 383 | if (prlimit(pid, lim->desc->resource, new, old) == -1) |
2f0948b1 | 384 | err(EXIT_FAILURE, lim->modify ? |
f88e44be KZ |
385 | _("failed to set the %s resource limit") : |
386 | _("failed to get the %s resource limit"), | |
2f0948b1 | 387 | lim->desc->name); |
6bac2825 | 388 | |
94c01662 KZ |
389 | if (lim->modify) |
390 | rem_prlim(lim); /* modify only; don't show */ | |
391 | } | |
6bac2825 DB |
392 | } |
393 | ||
6bac2825 DB |
394 | static int get_range(char *str, rlim_t *soft, rlim_t *hard, int *found) |
395 | { | |
396 | char *end = NULL; | |
397 | ||
398 | if (!str) | |
399 | return 0; | |
400 | ||
401 | *found = errno = 0; | |
402 | *soft = *hard = RLIM_INFINITY; | |
403 | ||
404 | if (!strcmp(str, INFINITY_STR)) { /* <unlimited> */ | |
405 | *found |= PRLIMIT_SOFT | PRLIMIT_HARD; | |
406 | return 0; | |
407 | ||
042f62df RP |
408 | } |
409 | ||
410 | if (*str == ':') { /* <:hard> */ | |
6bac2825 DB |
411 | str++; |
412 | ||
413 | if (strcmp(str, INFINITY_STR) != 0) { | |
414 | *hard = strtoull(str, &end, 10); | |
415 | ||
416 | if (errno || !end || *end || end == str) | |
417 | return -1; | |
418 | } | |
419 | *found |= PRLIMIT_HARD; | |
420 | return 0; | |
421 | ||
422 | } | |
423 | ||
424 | if (strncmp(str, INFINITY_STR, INFINITY_STRLEN) == 0) { | |
425 | /* <unlimited> or <unlimited:> */ | |
426 | end = str + INFINITY_STRLEN; | |
427 | } else { | |
428 | /* <value> or <soft:> */ | |
429 | *hard = *soft = strtoull(str, &end, 10); | |
430 | if (errno || !end || end == str) | |
431 | return -1; | |
432 | } | |
433 | ||
434 | if (*end == ':' && !*(end + 1)) /* <soft:> */ | |
435 | *found |= PRLIMIT_SOFT; | |
436 | ||
437 | else if (*end == ':') { /* <soft:hard> */ | |
438 | str = end + 1; | |
439 | ||
440 | if (!strcmp(str, INFINITY_STR)) | |
441 | *hard = RLIM_INFINITY; | |
442 | else { | |
443 | end = NULL; | |
444 | errno = 0; | |
445 | *hard = strtoull(str, &end, 10); | |
446 | ||
447 | if (errno || !end || *end || end == str) | |
448 | return -1; | |
449 | } | |
450 | *found |= PRLIMIT_SOFT | PRLIMIT_HARD; | |
451 | ||
452 | } else /* <value> */ | |
453 | *found |= PRLIMIT_SOFT | PRLIMIT_HARD; | |
454 | ||
455 | return 0; | |
456 | } | |
457 | ||
458 | ||
459 | static int parse_prlim(struct rlimit *lim, char *ops, size_t id) | |
460 | { | |
87559313 | 461 | rlim_t soft = 0, hard = 0; |
6bac2825 DB |
462 | int found = 0; |
463 | ||
1ad8db55 KZ |
464 | if (ops && *ops == '=') |
465 | ops++; | |
466 | ||
6bac2825 DB |
467 | if (get_range(ops, &soft, &hard, &found)) |
468 | errx(EXIT_FAILURE, _("failed to parse %s limit"), | |
469 | prlimit_desc[id].name); | |
470 | ||
6bac2825 DB |
471 | lim->rlim_cur = soft; |
472 | lim->rlim_max = hard; | |
473 | ||
044bc8de | 474 | return found; |
6bac2825 DB |
475 | } |
476 | ||
16fb9b3d | 477 | static int add_prlim(char *ops, struct list_head *lims, size_t id) |
6bac2825 | 478 | { |
212db7f2 | 479 | struct prlimit *lim = xcalloc(1, sizeof(*lim)); |
94c01662 KZ |
480 | |
481 | INIT_LIST_HEAD(&lim->lims); | |
6bac2825 DB |
482 | lim->desc = &prlimit_desc[id]; |
483 | ||
044bc8de BV |
484 | if (ops) |
485 | lim->modify = parse_prlim(&lim->rlim, ops, id); | |
6bac2825 | 486 | |
94c01662 | 487 | list_add_tail(&lim->lims, lims); |
6bac2825 DB |
488 | return 0; |
489 | } | |
490 | ||
491 | int main(int argc, char **argv) | |
492 | { | |
226d76e6 | 493 | int opt; |
94c01662 | 494 | struct list_head lims; |
6bac2825 DB |
495 | |
496 | enum { | |
d254c1db KZ |
497 | VERBOSE_OPTION = CHAR_MAX + 1, |
498 | RAW_OPTION, | |
499 | NOHEADINGS_OPTION | |
6bac2825 DB |
500 | }; |
501 | ||
502 | static const struct option longopts[] = { | |
503 | { "pid", required_argument, NULL, 'p' }, | |
504 | { "output", required_argument, NULL, 'o' }, | |
505 | { "as", optional_argument, NULL, 'v' }, | |
506 | { "core", optional_argument, NULL, 'c' }, | |
507 | { "cpu", optional_argument, NULL, 't' }, | |
508 | { "data", optional_argument, NULL, 'd' }, | |
509 | { "fsize", optional_argument, NULL, 'f' }, | |
510 | { "locks", optional_argument, NULL, 'x' }, | |
511 | { "memlock", optional_argument, NULL, 'l' }, | |
512 | { "msgqueue", optional_argument, NULL, 'q' }, | |
513 | { "nice", optional_argument, NULL, 'e' }, | |
514 | { "nofile", optional_argument, NULL, 'n' }, | |
515 | { "nproc", optional_argument, NULL, 'u' }, | |
516 | { "rss", optional_argument, NULL, 'm' }, | |
517 | { "rtprio", optional_argument, NULL, 'r' }, | |
518 | { "rttime", optional_argument, NULL, 'y' }, | |
519 | { "sigpending", optional_argument, NULL, 'i' }, | |
520 | { "stack", optional_argument, NULL, 's' }, | |
521 | { "version", no_argument, NULL, 'V' }, | |
522 | { "help", no_argument, NULL, 'h' }, | |
d254c1db KZ |
523 | { "noheadings", no_argument, NULL, NOHEADINGS_OPTION }, |
524 | { "raw", no_argument, NULL, RAW_OPTION }, | |
6bac2825 DB |
525 | { "verbose", no_argument, NULL, VERBOSE_OPTION }, |
526 | { NULL, 0, NULL, 0 } | |
527 | }; | |
528 | ||
529 | setlocale(LC_ALL, ""); | |
530 | bindtextdomain(PACKAGE, LOCALEDIR); | |
531 | textdomain(PACKAGE); | |
2c308875 | 532 | close_stdout_atexit(); |
6bac2825 | 533 | |
94c01662 KZ |
534 | INIT_LIST_HEAD(&lims); |
535 | ||
6bac2825 DB |
536 | /* |
537 | * Something is very wrong if this doesn't succeed, | |
538 | * assuming STACK is the last resource, of course. | |
539 | */ | |
540 | assert(MAX_RESOURCES == STACK + 1); | |
541 | ||
542 | while((opt = getopt_long(argc, argv, | |
53e1f461 | 543 | "+c::d::e::f::i::l::m::n::q::r::s::t::u::v::x::y::p:o:vVh", |
6bac2825 DB |
544 | longopts, NULL)) != -1) { |
545 | switch(opt) { | |
546 | case 'c': | |
94c01662 | 547 | add_prlim(optarg, &lims, CORE); |
6bac2825 DB |
548 | break; |
549 | case 'd': | |
94c01662 | 550 | add_prlim(optarg, &lims, DATA); |
6bac2825 DB |
551 | break; |
552 | case 'e': | |
94c01662 | 553 | add_prlim(optarg, &lims, NICE); |
6bac2825 DB |
554 | break; |
555 | case 'f': | |
94c01662 | 556 | add_prlim(optarg, &lims, FSIZE); |
6bac2825 DB |
557 | break; |
558 | case 'i': | |
94c01662 | 559 | add_prlim(optarg, &lims, SIGPENDING); |
6bac2825 DB |
560 | break; |
561 | case 'l': | |
94c01662 | 562 | add_prlim(optarg, &lims, MEMLOCK); |
6bac2825 DB |
563 | break; |
564 | case 'm': | |
94c01662 | 565 | add_prlim(optarg, &lims, RSS); |
6bac2825 DB |
566 | break; |
567 | case 'n': | |
94c01662 | 568 | add_prlim(optarg, &lims, NOFILE); |
6bac2825 DB |
569 | break; |
570 | case 'q': | |
94c01662 | 571 | add_prlim(optarg, &lims, MSGQUEUE); |
6bac2825 DB |
572 | break; |
573 | case 'r': | |
94c01662 | 574 | add_prlim(optarg, &lims, RTPRIO); |
6bac2825 DB |
575 | break; |
576 | case 's': | |
94c01662 | 577 | add_prlim(optarg, &lims, STACK); |
6bac2825 DB |
578 | break; |
579 | case 't': | |
94c01662 | 580 | add_prlim(optarg, &lims, CPU); |
6bac2825 DB |
581 | break; |
582 | case 'u': | |
94c01662 | 583 | add_prlim(optarg, &lims, NPROC); |
6bac2825 DB |
584 | break; |
585 | case 'v': | |
94c01662 | 586 | add_prlim(optarg, &lims, AS); |
6bac2825 DB |
587 | break; |
588 | case 'x': | |
94c01662 | 589 | add_prlim(optarg, &lims, LOCKS); |
6bac2825 DB |
590 | break; |
591 | case 'y': | |
94c01662 | 592 | add_prlim(optarg, &lims, RTTIME); |
6bac2825 DB |
593 | break; |
594 | ||
595 | case 'p': | |
2dac37dd | 596 | if (pid) |
a1ec6137 | 597 | errx(EXIT_FAILURE, _("option --pid may be specified only once")); |
20a39982 | 598 | pid = strtos32_or_err(optarg, _("invalid PID argument")); |
6bac2825 | 599 | break; |
6bac2825 DB |
600 | case 'o': |
601 | ncolumns = string_to_idarray(optarg, | |
602 | columns, ARRAY_SIZE(columns), | |
603 | column_name_to_id); | |
604 | if (ncolumns < 0) | |
605 | return EXIT_FAILURE; | |
606 | break; | |
d254c1db | 607 | case NOHEADINGS_OPTION: |
226d76e6 | 608 | no_headings = 1; |
d254c1db | 609 | break; |
6bac2825 DB |
610 | case VERBOSE_OPTION: |
611 | verbose++; | |
612 | break; | |
d254c1db | 613 | case RAW_OPTION: |
226d76e6 | 614 | raw = 1; |
d254c1db | 615 | break; |
2c308875 KZ |
616 | |
617 | case 'h': | |
618 | usage(); | |
619 | case 'V': | |
620 | print_version(EXIT_SUCCESS); | |
6bac2825 | 621 | default: |
677ec86c | 622 | errtryhelp(EXIT_FAILURE); |
6bac2825 DB |
623 | } |
624 | } | |
53e1f461 | 625 | if (argc > optind && pid) |
a1ec6137 | 626 | errx(EXIT_FAILURE, _("options --pid and COMMAND are mutually exclusive")); |
6bac2825 DB |
627 | if (!ncolumns) { |
628 | /* default columns */ | |
629 | columns[ncolumns++] = COL_RES; | |
630 | columns[ncolumns++] = COL_HELP; | |
631 | columns[ncolumns++] = COL_SOFT; | |
632 | columns[ncolumns++] = COL_HARD; | |
d76f904a | 633 | columns[ncolumns++] = COL_UNITS; |
6bac2825 DB |
634 | } |
635 | ||
710ed55d KZ |
636 | scols_init_debug(0); |
637 | ||
94c01662 | 638 | if (list_empty(&lims)) { |
6bac2825 | 639 | /* default is to print all resources */ |
94c01662 KZ |
640 | size_t n; |
641 | ||
642 | for (n = 0; n < MAX_RESOURCES; n++) | |
643 | add_prlim(NULL, &lims, n); | |
6bac2825 DB |
644 | } |
645 | ||
94c01662 KZ |
646 | do_prlimit(&lims); |
647 | ||
648 | if (!list_empty(&lims)) | |
226d76e6 | 649 | show_limits(&lims); |
6bac2825 | 650 | |
53e1f461 BV |
651 | if (argc > optind) { |
652 | /* prlimit [options] COMMAND */ | |
653 | execvp(argv[optind], &argv[optind]); | |
fd777151 | 654 | errexec(argv[optind]); |
53e1f461 BV |
655 | } |
656 | ||
6bac2825 DB |
657 | return EXIT_SUCCESS; |
658 | } |