3 // Copyright (C) 2003 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2, or (at your option)
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 Set the global per context limit of a resource (memory, file handle).
21 This utility can do it either for the current context or a selected
24 It uses the same options as ulimit, when possible
43 #include <sys/resource.h>
49 #define ENSC_WRAPPERS_PREFIX "vlimit: "
50 #define ENSC_WRAPPERS_UNISTD 1
51 #define ENSC_WRAPPERS_VSERVER 1
54 #define CMD_HELP 0x1000
55 #define CMD_VERSION 0x1001
56 #define CMD_XID 0x4000
57 #define CMD_DIR 0x8000
58 #define CMD_MISSINGOK 0x8001
60 int wrapper_exit_code = 255;
62 #ifndef RLIMIT_MSGQUEUE
63 # define RLIMIT_MSGQUEUE 12
67 { #X, required_argument, 0, 2048|X }
68 #define OPT_RESLIM(RES,V) \
69 { #RES, required_argument, 0, 2048|RLIMIT_##V }
70 #define OPT_VLIMIT(RES,V) \
71 { #RES, required_argument, 0, 2048|VC_VLIMIT_##V }
73 static struct option const
75 { "help", no_argument, 0, CMD_HELP },
76 { "version", no_argument, 0, CMD_VERSION },
77 { "all", no_argument, 0, 'a' },
78 { "xid", required_argument, 0, CMD_XID },
79 { "dir", required_argument, 0, CMD_DIR },
80 { "missingok", no_argument, 0, CMD_MISSINGOK },
81 NUMLIM( 0), NUMLIM( 1), NUMLIM( 2), NUMLIM( 3),
82 NUMLIM( 4), NUMLIM( 5), NUMLIM( 6), NUMLIM( 7),
83 NUMLIM( 8), NUMLIM( 9), NUMLIM(10), NUMLIM(11),
84 NUMLIM(12), NUMLIM(13), NUMLIM(14), NUMLIM(15),
85 NUMLIM(16), NUMLIM(17), NUMLIM(18), NUMLIM(19),
86 NUMLIM(20), NUMLIM(21), NUMLIM(22), NUMLIM(23),
87 NUMLIM(24), NUMLIM(25), NUMLIM(26), NUMLIM(27),
88 NUMLIM(28), NUMLIM(29), NUMLIM(30), NUMLIM(31),
90 OPT_RESLIM(fsize, FSIZE),
91 OPT_RESLIM(data, DATA),
92 OPT_RESLIM(stack, STACK),
93 OPT_RESLIM(core, CORE),
95 OPT_RESLIM(nproc, NPROC),
96 OPT_RESLIM(nofile, NOFILE),
97 OPT_RESLIM(memlock, MEMLOCK),
99 OPT_RESLIM(locks, LOCKS),
100 OPT_RESLIM(msgqueue, MSGQUEUE),
101 OPT_VLIMIT(nsock, NSOCK),
102 OPT_VLIMIT(openfd, OPENFD),
103 OPT_VLIMIT(anon, ANON),
104 OPT_VLIMIT(shmem, SHMEM),
105 OPT_VLIMIT(semary, SEMARY),
106 OPT_VLIMIT(nsems, NSEMS),
107 OPT_VLIMIT(dentry, DENTRY),
111 #define REV_RESLIM(X) [RLIMIT_##X] = #X
112 #define REV_VLIMIT(X) [VC_VLIMIT_##X] = #X
113 static char const * const LIMIT_STR[] = {
114 REV_RESLIM(CPU), REV_RESLIM(FSIZE), REV_RESLIM(DATA), REV_RESLIM(STACK),
115 REV_RESLIM(CORE), REV_RESLIM(RSS), REV_RESLIM(NPROC), REV_RESLIM(NOFILE),
116 REV_RESLIM(MEMLOCK), REV_RESLIM(AS), REV_RESLIM(LOCKS), REV_RESLIM(MSGQUEUE),
117 REV_VLIMIT(NSOCK), REV_VLIMIT(OPENFD), REV_VLIMIT(ANON), REV_VLIMIT(SHMEM),
118 REV_VLIMIT(SEMARY), REV_VLIMIT(NSEMS), REV_VLIMIT(DENTRY),
122 showHelp(int fd, char const *cmd, int res)
124 VSERVER_DECLARE_CMD(cmd);
126 WRITE_MSG(fd, "Usage: ");
129 " [--xid|-c <xid>] [-nd] [-a|--all] [[-MSH] --(<resource>|<nr>) <value>]*\n"
130 " [--dir <pathname> [--missingok]] [--] [<program> <args>*]\n\n"
133 " ... operate on context <xid>\n"
134 " -a|--all ... show all available limits\n"
135 " -n ... do not resolve limit-names\n"
136 " -d ... show limits in decimal\n"
137 " -M ... set Minimum limit\n"
138 " -S ... set Soft limit\n"
139 " -H ... set Hard limit (assumed by default, when neither\n"
140 " M nor S was requested)\n"
141 " --dir <pathname>\n"
142 " ... read limits from <pathname>/; allowed filenames are\n"
143 " <resource> and <resource>.{min,soft,hard}. When a limit\n"
144 " was set by the CLI already, the corresponding file\n"
146 " --missingok ... do not fail when <pathname> does not exist\n"
147 " --<resource>|<nr> <value>\n"
148 " ... set specified (MSH) limit for <resource> to <value>\n\n"
149 "Valid values for resource are cpu, fsize, data, stack, core, rss, nproc,\n"
150 "nofile, memlock, as, locks, msgqueue, nsock, openfd, anon, shmem, semary,\n"
151 "nsems, and dentry.\n\n"
152 "Please report bugs to " PACKAGE_BUGREPORT "\n");
160 "vlimit " VERSION " -- limits context-resources\n"
161 "This program is part of " PACKAGE_STRING "\n\n"
162 "Copyright (C) 2003 Enrico Scholz\n"
163 VERSION_COPYRIGHT_DISCLAIMER);
168 fmtHex(char *ptr, vc_limit_t lim)
170 memcpy(ptr, "0x", 2);
171 return utilvserver_fmt_xuint64(ptr+2, lim) + 2;
174 static bool do_resolve = true;
175 static size_t (*fmt_func)(char *, vc_limit_t) = fmtHex;
178 appendLimit(char *ptr, bool do_it, vc_limit_t lim)
183 if (lim==VC_LIM_INFINITY) {
184 memcpy(ptr, "inf", 3);
188 ptr += (*fmt_func)(ptr, lim);
193 memcpy(ptr, "N/A", 3);
203 struct vc_rlimit_mask mask;
206 if (vc_get_rlimit_mask(ctx, &mask)==-1) {
207 perror("vc_get_rlimit_mask()");
208 exit(wrapper_exit_code);
211 for (i=0; i<32; ++i) {
212 uint32_t bitmask = (1<<i);
213 struct vc_rlimit limit;
214 char buf[128], *ptr=buf;
216 if (((mask.min|mask.soft|mask.hard) & bitmask)==0) continue;
217 if (vc_get_rlimit(ctx, i, &limit)==-1) {
218 perror("vc_get_rlimit()");
222 memset(buf, ' ', sizeof buf);
223 if (do_resolve && i<DIM_OF(LIMIT_STR)) {
224 size_t l = strlen(LIMIT_STR[i]);
225 memcpy(ptr, LIMIT_STR[i], l);
229 ptr += utilvserver_fmt_uint(ptr, i);
233 ptr = appendLimit(buf+10, mask.min &bitmask, limit.min);
234 ptr = appendLimit(buf+30, mask.soft&bitmask, limit.soft);
235 ptr = appendLimit(buf+50, mask.hard&bitmask, limit.hard);
238 Vwrite(1, buf, ptr-buf);
243 setLimits(int ctx, struct vc_rlimit const limits[], uint32_t mask)
246 for (i=0; i<32; ++i) {
247 if ((mask & (1<<i))==0) continue;
248 if (vc_set_rlimit(ctx, i, limits+i)) {
249 perror("vc_set_rlimit()");
255 readValue(int fd, char const *filename)
258 size_t len = Eread(fd, buf, sizeof(buf)-1);
263 if (!vc_parseLimit(buf, &res)) {
264 WRITE_MSG(2, "Invalid limit in '");
265 WRITE_STR(2, filename);
267 exit(wrapper_exit_code);
274 readFile(char const *file, char *base, char const *suffix,
279 strcpy(base, suffix);
280 fd = open(file, O_RDONLY);
282 *limit = readValue(fd, file);
290 readFromDir(struct vc_rlimit limits[32], uint_least32_t *mask,
291 char const *pathname, bool missing_ok)
295 size_t l_pathname = strlen(pathname);
296 char buf[l_pathname + sizeof("/memlock.hard") + 32];
298 if (stat(pathname, &st)==-1) {
299 if (errno==ENOENT && missing_ok) return;
300 PERROR_Q("vlimit: fstat", pathname);
301 exit(wrapper_exit_code);
304 memcpy(buf, pathname, l_pathname);
305 if (l_pathname>0 && pathname[l_pathname-1]!='/')
306 buf[l_pathname++] = '/';
308 for (i=0; i<DIM_OF(LIMIT_STR); ++i) {
310 char * ptr = buf+l_pathname;
312 // ignore unimplemented limits
313 if (LIMIT_STR[i]==0) continue;
315 // ignore limits set on cli already
316 if (*mask & (1<<i)) continue;
318 l_res = strlen(LIMIT_STR[i]);
319 memcpy(ptr, LIMIT_STR[i], l_res+1);
321 *ptr = tolower(*ptr);
325 if (readFile(buf, ptr, "", &limits[i].min)) {
326 limits[i].soft = limits[i].hard = limits[i].min;
330 if (readFile(buf, ptr, ".min", &limits[i].min))
333 if (readFile(buf, ptr, ".soft", &limits[i].soft))
336 if (readFile(buf, ptr, ".hard", &limits[i].hard))
341 int main (int argc, char *argv[])
343 // overall used limits
344 uint32_t lim_mask = 0;
346 struct vc_rlimit limits[32];
347 bool show_all = false;
348 xid_t ctx = VC_NOCTX;
349 char const * dir = 0;
350 bool missing_ok = false;
354 for (i=0; i<32; ++i) {
355 limits[i].min = VC_LIM_KEEP;
356 limits[i].soft = VC_LIM_KEEP;
357 limits[i].hard = VC_LIM_KEEP;
362 int c = getopt_long(argc, argv, "+MSHndac:", CMDLINE_OPTIONS, 0);
365 if (2048<=c && c<2048+32) {
369 if (!vc_parseLimit(optarg, &val)) {
370 WRITE_MSG(2, "Can not parse limit '");
371 WRITE_STR(2, optarg);
373 exit(wrapper_exit_code);
376 if (set_mask==0) set_mask=4;
378 if (set_mask & 1) limits[id].min = val;
379 if (set_mask & 2) limits[id].soft = val;
380 if (set_mask & 4) limits[id].hard = val;
386 case CMD_HELP : showHelp(1, argv[0], 0);
387 case CMD_VERSION : showVersion();
388 case 'a' : show_all = true; break;
389 case 'n' : do_resolve = false; break;
390 case CMD_DIR : dir = optarg; break;
391 case CMD_MISSINGOK: missing_ok = true; break;
392 case CMD_XID : /*@fallthrough@*/
393 case 'c' : ctx = Evc_xidopt2xid(optarg,true); break;
394 case 'd' : fmt_func = utilvserver_fmt_uint64; break;
399 case 'M' : set_mask |= 1; break;
400 case 'S' : set_mask |= 2; break;
401 case 'H' : set_mask |= 4; break;
402 default : assert(false);
407 WRITE_MSG(2, "Try '");
408 WRITE_STR(2, argv[0]);
409 WRITE_MSG(2, " --help' for more information.\n");
410 exit(wrapper_exit_code) ;
416 ctx = Evc_get_task_xid(0);
419 readFromDir(limits, &lim_mask, dir, missing_ok);
421 setLimits(ctx, limits, lim_mask);
422 if (show_all) showAll(ctx);
425 EexecvpD(argv[optind], argv+optind);