vlimit.c (11553B)
1 // $Id$ 2 3 // Copyright (C) 2003 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de> 4 // 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) 8 // any later version. 9 // 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. 14 // 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. 18 19 /* 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 22 one. 23 24 It uses the same options as ulimit, when possible 25 */ 26 #ifdef HAVE_CONFIG_H 27 # include <config.h> 28 #endif 29 #include "compat.h" 30 31 #include "vserver.h" 32 #include "internal.h" 33 #include "util.h" 34 35 #include <getopt.h> 36 #include <string.h> 37 #include <unistd.h> 38 #include <assert.h> 39 #include <stdbool.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <libgen.h> 43 #include <sys/resource.h> 44 #include <errno.h> 45 #include <fcntl.h> 46 #include <sys/stat.h> 47 #include <ctype.h> 48 49 #define ENSC_WRAPPERS_PREFIX "vlimit: " 50 #define ENSC_WRAPPERS_UNISTD 1 51 #define ENSC_WRAPPERS_VSERVER 1 52 #include <wrappers.h> 53 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 59 60 int wrapper_exit_code = 255; 61 62 #ifndef RLIMIT_MSGQUEUE 63 # define RLIMIT_MSGQUEUE 12 64 #endif 65 66 #define NUMLIM(X) \ 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 } 72 73 static struct option const 74 CMDLINE_OPTIONS[] = { 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), 89 OPT_RESLIM(cpu, CPU), 90 OPT_RESLIM(fsize, FSIZE), 91 OPT_RESLIM(data, DATA), 92 OPT_RESLIM(stack, STACK), 93 OPT_RESLIM(core, CORE), 94 OPT_RESLIM(rss, RSS), 95 OPT_RESLIM(nproc, NPROC), 96 OPT_RESLIM(nofile, NOFILE), 97 OPT_RESLIM(memlock, MEMLOCK), 98 OPT_RESLIM(as, AS), 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), 108 { 0,0,0,0 } 109 }; 110 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), 119 }; 120 121 static void 122 showHelp(int fd, char const *cmd, int res) 123 { 124 VSERVER_DECLARE_CMD(cmd); 125 126 WRITE_MSG(fd, "Usage: "); 127 WRITE_STR(fd, cmd); 128 WRITE_MSG(fd, 129 " [--xid|-c <xid>] [-nd] [-a|--all] [[-MSH] --(<resource>|<nr>) <value>]*\n" 130 " [--dir <pathname> [--missingok]] [--] [<program> <args>*]\n\n" 131 "Options:\n" 132 " -c|--xid <xid>\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" 145 " will be ignored\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"); 153 exit(res); 154 } 155 156 static void 157 showVersion() 158 { 159 WRITE_MSG(1, 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); 164 exit(0); 165 } 166 167 static size_t 168 fmtHex(char *ptr, vc_limit_t lim) 169 { 170 memcpy(ptr, "0x", 2); 171 return utilvserver_fmt_xuint64(ptr+2, lim) + 2; 172 } 173 174 static bool do_resolve = true; 175 static size_t (*fmt_func)(char *, vc_limit_t) = fmtHex; 176 177 static void * 178 appendLimit(char *ptr, bool do_it, vc_limit_t lim) 179 { 180 memcpy(ptr, " ", 2); 181 ptr += 2; 182 if (do_it) { 183 if (lim==VC_LIM_INFINITY) { 184 memcpy(ptr, "inf", 3); 185 ptr += 3; 186 } 187 else { 188 ptr += (*fmt_func)(ptr, lim); 189 *ptr = ' '; 190 } 191 } 192 else { 193 memcpy(ptr, "N/A", 3); 194 ptr += 3; 195 } 196 197 return ptr; 198 } 199 200 static void 201 showAll(int ctx) 202 { 203 struct vc_rlimit_mask mask; 204 size_t i; 205 206 if (vc_get_rlimit_mask(ctx, &mask)==-1) { 207 perror("vc_get_rlimit_mask()"); 208 exit(wrapper_exit_code); 209 } 210 211 for (i=0; i<32; ++i) { 212 uint32_t bitmask = (1<<i); 213 struct vc_rlimit limit; 214 char buf[128], *ptr=buf; 215 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()"); 219 continue; 220 } 221 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); 226 ptr += l; 227 } 228 else { 229 ptr += utilvserver_fmt_uint(ptr, i); 230 *ptr = ' '; 231 } 232 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); 236 237 *ptr++ = '\n'; 238 Vwrite(1, buf, ptr-buf); 239 } 240 } 241 242 static void 243 setLimits(int ctx, struct vc_rlimit const limits[], uint32_t mask) 244 { 245 size_t i; 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()"); 250 } 251 } 252 } 253 254 static vc_limit_t 255 readValue(int fd, char const *filename) 256 { 257 char buf[128]; 258 size_t len = Eread(fd, buf, sizeof(buf)-1); 259 vc_limit_t res; 260 261 buf[len] = '\0'; 262 263 if (!vc_parseLimit(buf, &res)) { 264 WRITE_MSG(2, "Invalid limit in '"); 265 WRITE_STR(2, filename); 266 WRITE_STR(2, "'\n"); 267 exit(wrapper_exit_code); 268 } 269 270 return res; 271 } 272 273 static bool 274 readFile(char const *file, char *base, char const *suffix, 275 vc_limit_t *limit) 276 { 277 int fd; 278 279 strcpy(base, suffix); 280 fd = open(file, O_RDONLY); 281 if (fd!=-1) { 282 *limit = readValue(fd, file); 283 Eclose(fd); 284 } 285 286 return fd!=-1; 287 } 288 289 static void 290 readFromDir(struct vc_rlimit limits[32], uint_least32_t *mask, 291 char const *pathname, bool missing_ok) 292 { 293 struct stat st; 294 size_t i; 295 size_t l_pathname = strlen(pathname); 296 char buf[l_pathname + sizeof("/memlock.hard") + 32]; 297 298 if (stat(pathname, &st)==-1) { 299 if (errno==ENOENT && missing_ok) return; 300 PERROR_Q("vlimit: fstat", pathname); 301 exit(wrapper_exit_code); 302 } 303 304 memcpy(buf, pathname, l_pathname); 305 if (l_pathname>0 && pathname[l_pathname-1]!='/') 306 buf[l_pathname++] = '/'; 307 308 for (i=0; i<DIM_OF(LIMIT_STR); ++i) { 309 size_t l_res; 310 char * ptr = buf+l_pathname; 311 312 // ignore unimplemented limits 313 if (LIMIT_STR[i]==0) continue; 314 315 // ignore limits set on cli already 316 if (*mask & (1<<i)) continue; 317 318 l_res = strlen(LIMIT_STR[i]); 319 memcpy(ptr, LIMIT_STR[i], l_res+1); 320 while (*ptr) { 321 *ptr = tolower(*ptr); 322 ++ptr; 323 } 324 325 if (readFile(buf, ptr, "", &limits[i].min)) { 326 limits[i].soft = limits[i].hard = limits[i].min; 327 *mask |= (1<<i); 328 } 329 330 if (readFile(buf, ptr, ".min", &limits[i].min)) 331 *mask |= (1<<i); 332 333 if (readFile(buf, ptr, ".soft", &limits[i].soft)) 334 *mask |= (1<<i); 335 336 if (readFile(buf, ptr, ".hard", &limits[i].hard)) 337 *mask |= (1<<i); 338 } 339 } 340 341 int main (int argc, char *argv[]) 342 { 343 // overall used limits 344 uint32_t lim_mask = 0; 345 int set_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; 351 352 { 353 size_t i; 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; 358 } 359 } 360 361 while (1) { 362 int c = getopt_long(argc, argv, "+MSHndac:", CMDLINE_OPTIONS, 0); 363 if (c==-1) break; 364 365 if (2048<=c && c<2048+32) { 366 int id = c-2048; 367 vc_limit_t val; 368 369 if (!vc_parseLimit(optarg, &val)) { 370 WRITE_MSG(2, "Can not parse limit '"); 371 WRITE_STR(2, optarg); 372 WRITE_STR(2, "'\n"); 373 exit(wrapper_exit_code); 374 } 375 376 if (set_mask==0) set_mask=4; 377 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; 381 382 lim_mask |= (1<<id); 383 set_mask = 0; 384 } 385 else switch (c) { 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; 395 case 'M' : 396 case 'S' : 397 case 'H' : 398 switch (c) { 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); 403 } 404 break; 405 406 default : 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) ; 411 break; 412 } 413 } 414 415 if (ctx==VC_NOCTX) 416 ctx = Evc_get_task_xid(0); 417 418 if (dir) 419 readFromDir(limits, &lim_mask, dir, missing_ok); 420 421 setLimits(ctx, limits, lim_mask); 422 if (show_all) showAll(ctx); 423 424 if (optind<argc) 425 EexecvpD(argv[optind], argv+optind); 426 427 return EXIT_SUCCESS; 428 }