1 /*- 2 * Copyright (c) 1991, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Kenneth Almquist. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #ifndef lint 34 #if 0 35 static char sccsid[] = "@(#)miscbltin.c 8.4 (Berkeley) 5/4/95"; 36 #endif 37 #endif /* not lint */ 38 #include <sys/cdefs.h> 39 __FBSDID("$FreeBSD$"); 40 41 /* 42 * Miscellaneous builtins. 43 */ 44 45 #include <sys/types.h> 46 #include <sys/stat.h> 47 #include <sys/time.h> 48 #include <sys/resource.h> 49 #include <unistd.h> 50 #include <errno.h> 51 #include <stdint.h> 52 #include <stdio.h> 53 #include <stdlib.h> 54 55 #include "shell.h" 56 #include "options.h" 57 #include "var.h" 58 #include "output.h" 59 #include "memalloc.h" 60 #include "error.h" 61 #include "mystring.h" 62 #include "syntax.h" 63 #include "trap.h" 64 65 #undef eflag 66 67 int readcmd(int, char **); 68 int umaskcmd(int, char **); 69 int ulimitcmd(int, char **); 70 71 /* 72 * The read builtin. The -r option causes backslashes to be treated like 73 * ordinary characters. 74 * 75 * This uses unbuffered input, which may be avoidable in some cases. 76 * 77 * Note that if IFS=' :' then read x y should work so that: 78 * 'a b' x='a', y='b' 79 * ' a b ' x='a', y='b' 80 * ':b' x='', y='b' 81 * ':' x='', y='' 82 * '::' x='', y='' 83 * ': :' x='', y='' 84 * ':::' x='', y='::' 85 * ':b c:' x='', y='b c:' 86 */ 87 88 int 89 readcmd(int argc __unused, char **argv __unused) 90 { 91 char **ap; 92 int backslash; 93 char c; 94 int rflag; 95 char *prompt; 96 const char *ifs; 97 char *p; 98 int startword; 99 int status; 100 int i; 101 int is_ifs; 102 int saveall = 0; 103 ptrdiff_t lastnonifs, lastnonifsws; 104 struct timeval tv; 105 char *tvptr; 106 fd_set ifds; 107 ssize_t nread; 108 int sig; 109 110 rflag = 0; 111 prompt = NULL; 112 tv.tv_sec = -1; 113 tv.tv_usec = 0; 114 while ((i = nextopt("erp:t:")) != '\0') { 115 switch(i) { 116 case 'p': 117 prompt = shoptarg; 118 break; 119 case 'e': 120 break; 121 case 'r': 122 rflag = 1; 123 break; 124 case 't': 125 tv.tv_sec = strtol(shoptarg, &tvptr, 0); 126 if (tvptr == shoptarg) 127 error("timeout value"); 128 switch(*tvptr) { 129 case 0: 130 case 's': 131 break; 132 case 'h': 133 tv.tv_sec *= 60; 134 /* FALLTHROUGH */ 135 case 'm': 136 tv.tv_sec *= 60; 137 break; 138 default: 139 error("timeout unit"); 140 } 141 break; 142 } 143 } 144 if (prompt && isatty(0)) { 145 out2str(prompt); 146 flushall(); 147 } 148 if (*(ap = argptr) == NULL) 149 error("arg count"); 150 if ((ifs = bltinlookup("IFS", 1)) == NULL) 151 ifs = " \t\n"; 152 153 if (tv.tv_sec >= 0) { 154 /* 155 * Wait for something to become available. 156 */ 157 FD_ZERO(&ifds); 158 FD_SET(0, &ifds); 159 status = select(1, &ifds, NULL, NULL, &tv); 160 /* 161 * If there's nothing ready, return an error. 162 */ 163 if (status <= 0) { 164 sig = pendingsig; 165 return (128 + (sig != 0 ? sig : SIGALRM)); 166 } 167 } 168 169 status = 0; 170 startword = 2; 171 backslash = 0; 172 STARTSTACKSTR(p); 173 lastnonifs = lastnonifsws = -1; 174 for (;;) { 175 nread = read(STDIN_FILENO, &c, 1); 176 if (nread == -1) { 177 if (errno == EINTR) { 178 sig = pendingsig; 179 if (sig == 0) 180 continue; 181 status = 128 + sig; 182 break; 183 } 184 warning("read error: %s", strerror(errno)); 185 status = 2; 186 break; 187 } else if (nread != 1) { 188 status = 1; 189 break; 190 } 191 if (c == '\0') 192 continue; 193 CHECKSTRSPACE(1, p); 194 if (backslash) { 195 backslash = 0; 196 if (c != '\n') { 197 startword = 0; 198 lastnonifs = lastnonifsws = p - stackblock(); 199 USTPUTC(c, p); 200 } 201 continue; 202 } 203 if (!rflag && c == '\\') { 204 backslash++; 205 continue; 206 } 207 if (c == '\n') 208 break; 209 if (strchr(ifs, c)) 210 is_ifs = strchr(" \t\n", c) ? 1 : 2; 211 else 212 is_ifs = 0; 213 214 if (startword != 0) { 215 if (is_ifs == 1) { 216 /* Ignore leading IFS whitespace */ 217 if (saveall) 218 USTPUTC(c, p); 219 continue; 220 } 221 if (is_ifs == 2 && startword == 1) { 222 /* Only one non-whitespace IFS per word */ 223 startword = 2; 224 if (saveall) { 225 lastnonifsws = p - stackblock(); 226 USTPUTC(c, p); 227 } 228 continue; 229 } 230 } 231 232 if (is_ifs == 0) { 233 /* append this character to the current variable */ 234 startword = 0; 235 if (saveall) 236 /* Not just a spare terminator */ 237 saveall++; 238 lastnonifs = lastnonifsws = p - stackblock(); 239 USTPUTC(c, p); 240 continue; 241 } 242 243 /* end of variable... */ 244 startword = is_ifs; 245 246 if (ap[1] == NULL) { 247 /* Last variable needs all IFS chars */ 248 saveall++; 249 if (is_ifs == 2) 250 lastnonifsws = p - stackblock(); 251 USTPUTC(c, p); 252 continue; 253 } 254 255 STACKSTRNUL(p); 256 setvar(*ap, stackblock(), 0); 257 ap++; 258 STARTSTACKSTR(p); 259 lastnonifs = lastnonifsws = -1; 260 } 261 STACKSTRNUL(p); 262 263 /* 264 * Remove trailing IFS chars: always remove whitespace, don't remove 265 * non-whitespace unless it was naked 266 */ 267 if (saveall <= 1) 268 lastnonifsws = lastnonifs; 269 stackblock()[lastnonifsws + 1] = '\0'; 270 setvar(*ap, stackblock(), 0); 271 272 /* Set any remaining args to "" */ 273 while (*++ap != NULL) 274 setvar(*ap, "", 0); 275 return status; 276 } 277 278 279 280 int 281 umaskcmd(int argc __unused, char **argv __unused) 282 { 283 char *ap; 284 int mask; 285 int i; 286 int symbolic_mode = 0; 287 288 while ((i = nextopt("S")) != '\0') { 289 symbolic_mode = 1; 290 } 291 292 INTOFF; 293 mask = umask(0); 294 umask(mask); 295 INTON; 296 297 if ((ap = *argptr) == NULL) { 298 if (symbolic_mode) { 299 char u[4], g[4], o[4]; 300 301 i = 0; 302 if ((mask & S_IRUSR) == 0) 303 u[i++] = 'r'; 304 if ((mask & S_IWUSR) == 0) 305 u[i++] = 'w'; 306 if ((mask & S_IXUSR) == 0) 307 u[i++] = 'x'; 308 u[i] = '\0'; 309 310 i = 0; 311 if ((mask & S_IRGRP) == 0) 312 g[i++] = 'r'; 313 if ((mask & S_IWGRP) == 0) 314 g[i++] = 'w'; 315 if ((mask & S_IXGRP) == 0) 316 g[i++] = 'x'; 317 g[i] = '\0'; 318 319 i = 0; 320 if ((mask & S_IROTH) == 0) 321 o[i++] = 'r'; 322 if ((mask & S_IWOTH) == 0) 323 o[i++] = 'w'; 324 if ((mask & S_IXOTH) == 0) 325 o[i++] = 'x'; 326 o[i] = '\0'; 327 328 out1fmt("u=%s,g=%s,o=%s\n", u, g, o); 329 } else { 330 out1fmt("%.4o\n", mask); 331 } 332 } else { 333 if (is_digit(*ap)) { 334 mask = 0; 335 do { 336 if (*ap >= '8' || *ap < '0') 337 error("Illegal number: %s", *argptr); 338 mask = (mask << 3) + (*ap - '0'); 339 } while (*++ap != '\0'); 340 umask(mask); 341 } else { 342 void *set; 343 INTOFF; 344 if ((set = setmode (ap)) == NULL) 345 error("Illegal number: %s", ap); 346 347 mask = getmode (set, ~mask & 0777); 348 umask(~mask & 0777); 349 free(set); 350 INTON; 351 } 352 } 353 return 0; 354 } 355 356 /* 357 * ulimit builtin 358 * 359 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and 360 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with 361 * ash by J.T. Conklin. 362 * 363 * Public domain. 364 */ 365 366 struct limits { 367 const char *name; 368 const char *units; 369 int cmd; 370 short factor; /* multiply by to get rlim_{cur,max} values */ 371 char option; 372 }; 373 374 static const struct limits limits[] = { 375 #ifdef RLIMIT_CPU 376 { "cpu time", "seconds", RLIMIT_CPU, 1, 't' }, 377 #endif 378 #ifdef RLIMIT_FSIZE 379 { "file size", "512-blocks", RLIMIT_FSIZE, 512, 'f' }, 380 #endif 381 #ifdef RLIMIT_DATA 382 { "data seg size", "kbytes", RLIMIT_DATA, 1024, 'd' }, 383 #endif 384 #ifdef RLIMIT_STACK 385 { "stack size", "kbytes", RLIMIT_STACK, 1024, 's' }, 386 #endif 387 #ifdef RLIMIT_CORE 388 { "core file size", "512-blocks", RLIMIT_CORE, 512, 'c' }, 389 #endif 390 #ifdef RLIMIT_RSS 391 { "max memory size", "kbytes", RLIMIT_RSS, 1024, 'm' }, 392 #endif 393 #ifdef RLIMIT_MEMLOCK 394 { "locked memory", "kbytes", RLIMIT_MEMLOCK, 1024, 'l' }, 395 #endif 396 #ifdef RLIMIT_NPROC 397 { "max user processes", (char *)0, RLIMIT_NPROC, 1, 'u' }, 398 #endif 399 #ifdef RLIMIT_NOFILE 400 { "open files", (char *)0, RLIMIT_NOFILE, 1, 'n' }, 401 #endif 402 #ifdef RLIMIT_VMEM 403 { "virtual mem size", "kbytes", RLIMIT_VMEM, 1024, 'v' }, 404 #endif 405 #ifdef RLIMIT_SWAP 406 { "swap limit", "kbytes", RLIMIT_SWAP, 1024, 'w' }, 407 #endif 408 #ifdef RLIMIT_SBSIZE 409 { "socket buffer size", "bytes", RLIMIT_SBSIZE, 1, 'b' }, 410 #endif 411 #ifdef RLIMIT_NPTS 412 { "pseudo-terminals", (char *)0, RLIMIT_NPTS, 1, 'p' }, 413 #endif 414 #ifdef RLIMIT_KQUEUES 415 { "kqueues", (char *)0, RLIMIT_KQUEUES, 1, 'k' }, 416 #endif 417 #ifdef RLIMIT_UMTXP 418 { "umtx shared locks", (char *)0, RLIMIT_UMTXP, 1, 'o' }, 419 #endif 420 { (char *) 0, (char *)0, 0, 0, '\0' } 421 }; 422 423 enum limithow { SOFT = 0x1, HARD = 0x2 }; 424 425 static void 426 printlimit(enum limithow how, const struct rlimit *limit, 427 const struct limits *l) 428 { 429 rlim_t val = 0; 430 431 if (how & SOFT) 432 val = limit->rlim_cur; 433 else if (how & HARD) 434 val = limit->rlim_max; 435 if (val == RLIM_INFINITY) 436 out1str("unlimited\n"); 437 else 438 { 439 val /= l->factor; 440 out1fmt("%jd\n", (intmax_t)val); 441 } 442 } 443 444 int 445 ulimitcmd(int argc __unused, char **argv __unused) 446 { 447 rlim_t val = 0; 448 enum limithow how = SOFT | HARD; 449 const struct limits *l; 450 int set, all = 0; 451 int optc, what; 452 struct rlimit limit; 453 454 what = 'f'; 455 while ((optc = nextopt("HSatfdsmcnuvlbpwko")) != '\0') 456 switch (optc) { 457 case 'H': 458 how = HARD; 459 break; 460 case 'S': 461 how = SOFT; 462 break; 463 case 'a': 464 all = 1; 465 break; 466 default: 467 what = optc; 468 } 469 470 for (l = limits; l->name && l->option != what; l++) 471 ; 472 if (!l->name) 473 error("internal error (%c)", what); 474 475 set = *argptr ? 1 : 0; 476 if (set) { 477 char *p = *argptr; 478 479 if (all || argptr[1]) 480 error("too many arguments"); 481 if (strcmp(p, "unlimited") == 0) 482 val = RLIM_INFINITY; 483 else { 484 char *end; 485 uintmax_t uval; 486 487 if (*p < '0' || *p > '9') 488 error("bad number"); 489 errno = 0; 490 uval = strtoumax(p, &end, 10); 491 if (errno != 0 || *end != '\0') 492 error("bad number"); 493 if (uval > UINTMAX_MAX / l->factor) 494 error("bad number"); 495 uval *= l->factor; 496 val = (rlim_t)uval; 497 if (val < 0 || (uintmax_t)val != uval || 498 val == RLIM_INFINITY) 499 error("bad number"); 500 } 501 } 502 if (all) { 503 for (l = limits; l->name; l++) { 504 char optbuf[40]; 505 if (getrlimit(l->cmd, &limit) < 0) 506 error("can't get limit: %s", strerror(errno)); 507 508 if (l->units) 509 snprintf(optbuf, sizeof(optbuf), 510 "(%s, -%c) ", l->units, l->option); 511 else 512 snprintf(optbuf, sizeof(optbuf), 513 "(-%c) ", l->option); 514 out1fmt("%-18s %18s ", l->name, optbuf); 515 printlimit(how, &limit, l); 516 } 517 return 0; 518 } 519 520 if (getrlimit(l->cmd, &limit) < 0) 521 error("can't get limit: %s", strerror(errno)); 522 if (set) { 523 if (how & SOFT) 524 limit.rlim_cur = val; 525 if (how & HARD) 526 limit.rlim_max = val; 527 if (setrlimit(l->cmd, &limit) < 0) 528 error("bad limit: %s", strerror(errno)); 529 } else 530 printlimit(how, &limit, l); 531 return 0; 532 } 533