mtimeout.1: Use correct dash for number ranges.
[misc] / mprlimit.c
CommitLineData
2e169b7e
MW
1/* -*-c-*- *
2 *
3 * Change processes' resource limits.
4 *
5 * (c) 2011 Mark Wooding
6 */
7
8/*----- Licensing notice --------------------------------------------------*
9 *
10 * This file is part of the Toys utilties collection.
11 *
12 * Toys is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * Toys is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with Toys; if not, write to the Free Software Foundation,
24 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 */
26
27/*----- Header files ------------------------------------------------------*/
28
29#define _GNU_SOURCE
30#define _FILE_OFFSET_BITS 64
31
32#include <ctype.h>
33#include <errno.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37
38#include <sys/time.h>
39#include <sys/resource.h>
40
41#include <mLib/alloc.h>
42#include <mLib/mdwopt.h>
43#include <mLib/quis.h>
44#include <mLib/report.h>
45
46/*----- Static variables --------------------------------------------------*/
47
48/*----- Argument parsing functions ----------------------------------------*/
49
50static const struct limittab {
51 const char *name;
52 int id;
53} limittab[] = {
54 /* ;;; Emacs Lisp to generate the table below. Place your cursor just
55 ;;; after the closing `)' and press C-x C-e.
56
57 (let ((resources '(as core cpu data fsize locks memlock
58 msgqueue nice nofile nproc rss rtprio
59 rttime sigpending stack)))
60 (save-excursion
4254eec2
MW
61 (goto-char (point-min))
62 (search-forward (concat "***" "BEGIN rlimittab" "***"))
63 (beginning-of-line 2)
64 (delete-region (point)
2e169b7e
MW
65 (progn
66 (search-forward "***END***")
67 (beginning-of-line)
68 (point)))
69 (dolist (rsc (sort (copy-list resources) #'string<))
70 (let ((up (upcase (symbol-name rsc))))
71 (insert (format "#ifdef RLIMIT_%s\n" up))
72 (insert (format " { \"%s\", RLIMIT_%s },\n" rsc up))
73 (insert "#endif\n")))))
74 */
75 /***BEGIN rlimittab***/
76#ifdef RLIMIT_AS
77 { "as", RLIMIT_AS },
78#endif
79#ifdef RLIMIT_CORE
80 { "core", RLIMIT_CORE },
81#endif
82#ifdef RLIMIT_CPU
83 { "cpu", RLIMIT_CPU },
84#endif
85#ifdef RLIMIT_DATA
86 { "data", RLIMIT_DATA },
87#endif
88#ifdef RLIMIT_FSIZE
89 { "fsize", RLIMIT_FSIZE },
90#endif
91#ifdef RLIMIT_LOCKS
92 { "locks", RLIMIT_LOCKS },
93#endif
94#ifdef RLIMIT_MEMLOCK
95 { "memlock", RLIMIT_MEMLOCK },
96#endif
97#ifdef RLIMIT_MSGQUEUE
98 { "msgqueue", RLIMIT_MSGQUEUE },
99#endif
100#ifdef RLIMIT_NICE
101 { "nice", RLIMIT_NICE },
102#endif
103#ifdef RLIMIT_NOFILE
104 { "nofile", RLIMIT_NOFILE },
105#endif
106#ifdef RLIMIT_NPROC
107 { "nproc", RLIMIT_NPROC },
108#endif
109#ifdef RLIMIT_RSS
110 { "rss", RLIMIT_RSS },
111#endif
112#ifdef RLIMIT_RTPRIO
113 { "rtprio", RLIMIT_RTPRIO },
114#endif
115#ifdef RLIMIT_RTTIME
116 { "rttime", RLIMIT_RTTIME },
117#endif
118#ifdef RLIMIT_SIGPENDING
119 { "sigpending", RLIMIT_SIGPENDING },
120#endif
121#ifdef RLIMIT_STACK
122 { "stack", RLIMIT_STACK },
123#endif
124 /***END****/
125 { 0 }
126};
127
128static rlim_t parselong(const char *p, char **qq)
129{
130 char *q;
131 int err = errno;
132 rlim_t l;
133
134 if (strcmp(p, "inf") == 0) return (RLIM_INFINITY);
135 errno = 0;
136 l = strtol(p, &q, 0);
137 if (errno) goto err;
138 errno = err;
139 if (qq) *qq = q;
140 else if (*q) goto err;
141 return (l);
142
143err:
144 die(EXIT_FAILURE, "bad integer `%s'\n", p);
145 return (0);
146}
147
148static rlim_t parselimit(const char *p)
149{
150 char *q;
151 long l;
152
153 if (strcmp(p, "inf") == 0) return (RLIM_INFINITY);
154 l = parselong(p, &q);
155 switch (*q) {
156 case 't': case 'T': l *= 1024;
157 case 'g': case 'G': l *= 1024;
158 case 'm': case 'M': l *= 1024;
159 case 'k': case 'K': l *= 1024;
160 case 'b': case 'B': q++;
161 }
162 if (*q) goto err;
163 return (l);
164
165err:
166 die(EXIT_FAILURE, "bad size `%s'\n", p);
167 return (0);
168}
169
170static const struct limittab *findlimit(const char *p, size_t n)
171{
172 const struct limittab *lt;
173
174 for (lt = limittab; lt->name; lt++) {
175 if (strncmp(lt->name, p, n) == 0 && !lt->name[n])
176 return (lt);
177 }
5516b89d 178 die(EXIT_FAILURE, "unknown resource limit `%.*s'\n", (int)n, p);
2e169b7e
MW
179 return (0);
180}
181
182/*----- Help functions ----------------------------------------------------*/
183
184static void usage(FILE *fp)
11d4726f 185 { pquis(fp, "Usage: $ -l | "
2e169b7e
MW
186 "{hard | soft | both | PID | RSRC[=VALUE]}...\n"); }
187
188static void version(FILE *fp)
189 { pquis(fp, "$, version " VERSION "\n"); }
190
191static void help(FILE *fp)
192{
193 version(fp); putchar('\n');
194 usage(fp);
195 fputs("\n\
196Alter use limits for running processes. The resource assignments are\n\
197applied to the given process ids. Resource names without values cause\n\
198processes' current resource limits to be printed.\n\
199\n\
200Options:\n\
201\n\
202-h, --help Show this help text.\n\
203-v, --version Show the program's version number.\n\
204-u, --usage Show a terse usage reminder.\n\
205\n\
206-l, --list List the resource limit names.\n\
207", stderr);
208}
209
210/*----- Main program ------------------------------------------------------*/
211
212struct assign {
213 unsigned which;
214 const struct limittab *lt;
215 rlim_t val;
216};
217
218static void showlimit(const struct limittab *lt, rlim_t val)
219{
220 if (val == RLIM_INFINITY) printf("%s=inf", lt->name);
221 else {
222 static const char *suff[] = { "", "k", "M", "G", "T", 0 };
223 const char **s = suff;
224 while (s[1] && val && !(val&0x3ff)) { s++; val >>= 10; }
225 printf("%s=%lu%s", lt->name, (unsigned long)val, *s);
226 }
227}
228
229int main(int argc, char *argv[])
230{
231 struct rlimit lim;
232 const char *p;
233 const struct limittab *lt;
234 unsigned f = 0;
235 size_t nassign, npid;
236 struct assign *assign;
237 pid_t *pid;
238 size_t i, j;
239#define f_bogus 1u
240#define f_soft 2u
241#define f_hard 4u
242#define f_which (f_soft | f_hard)
243
244 ego(argv[0]);
245
246 for (;;) {
247 static const struct option opts[] = {
248 { "help", 0, 0, 'h' },
249 { "version", 0, 0, 'v' },
250 { "usage", 0, 0, 'u' },
251 { "list", 0, 0, 'l' },
252 { 0, 0, 0, 0 }
253 };
254 int i = mdwopt(argc, argv, "hvul", opts, 0, 0, 0);
255
256 if (i < 0) break;
257 switch (i) {
258 case 'h': help(stdout); exit(0);
259 case 'v': version(stdout); exit(0);
260 case 'u': usage(stdout); exit(0);
261 case 'l':
262 for (lt = limittab; lt->name; lt++) puts(lt->name);
263 exit(0);
264 default: f |= f_bogus; break;
265 }
266 }
267 if ((f & f_bogus) || (argc - optind) < 1) {
268 usage(stderr);
269 exit(EXIT_FAILURE);
270 }
271
272 pid = xmalloc(sizeof(*pid) * (argc - optind));
273 assign = xmalloc(sizeof(*assign) * (argc - optind));
274 npid = nassign = 0;
275 f |= f_hard | f_soft;
276
277 for (i = optind; i < argc; i++) {
278 if (strcmp(argv[i], "soft") == 0) f = (f & ~f_which) | f_soft;
279 else if (strcmp(argv[i], "hard") == 0) f = (f & ~f_which) | f_hard;
280 else if (strcmp(argv[i], "both") == 0) f |= f | f_soft | f_hard;
281 else if ((p = strchr(argv[i], '=')) != 0) {
282 lt = findlimit(argv[i], p - argv[i]);
283 assign[nassign].which = f & f_which;
284 assign[nassign].lt = lt;
285 assign[nassign].val = parselimit(p + 1);
286 nassign++;
287 } else if (isalpha((unsigned char)*argv[i])) {
288 lt = findlimit(argv[i], strlen(argv[i]));
289 assign[nassign].which = 0;
290 assign[nassign].lt = lt;
291 nassign++;
292 } else
293 pid[npid++] = parselong(argv[i], 0);
294 }
295
296 if (!npid) die(EXIT_FAILURE, "no processes to act on");
297 if (!nassign) die(EXIT_FAILURE, "no limits to apply or show");
298
299 for (i = 0; i < npid; i++) {
300 for (j = 0; j < nassign; j++) {
8ec37147
MW
301 lt = assign[j].lt;
302 if (prlimit(pid[i], lt->id, 0, &lim)) {
2e169b7e 303 moan("failed to read `%s' limit for pid %ld: %s",
8ec37147 304 lt->name, (long)pid[i], strerror(errno));
2e169b7e
MW
305 goto err;
306 }
307 if (!assign[j].which) {
308 printf("%ld soft ", (long)pid[i]); showlimit(lt, lim.rlim_cur);
309 printf(" hard "); showlimit(lt, lim.rlim_max); putchar('\n');
310 } else {
311 if (assign[j].which & f_soft) lim.rlim_cur = assign[j].val;
312 if (assign[j].which & f_hard) lim.rlim_max = assign[j].val;
8ec37147 313 if (prlimit(pid[i], lt->id, &lim, 0)) {
2e169b7e 314 moan("failed to set `%s' limit for pid %ld: %s\n",
8ec37147 315 lt->name, (long)pid[i], strerror(errno));
2e169b7e
MW
316 goto err;
317 }
318 }
319 continue;
320 err:
321 f |= f_bogus;
322 }
323 }
324
325 return (f & f_bogus ? EXIT_FAILURE : 0);
326}
327
328/*----- That's all, folks -------------------------------------------------*/