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 * 4. 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 struct timeval tv; 104 char *tvptr; 105 fd_set ifds; 106 ssize_t nread; 107 int sig; 108 109 rflag = 0; 110 prompt = NULL; 111 tv.tv_sec = -1; 112 tv.tv_usec = 0; 113 while ((i = nextopt("erp:t:")) != '\0') { 114 switch(i) { 115 case 'p': 116 prompt = shoptarg; 117 break; 118 case 'e': 119 break; 120 case 'r': 121 rflag = 1; 122 break; 123 case 't': 124 tv.tv_sec = strtol(shoptarg, &tvptr, 0); 125 if (tvptr == shoptarg) 126 error("timeout value"); 127 switch(*tvptr) { 128 case 0: 129 case 's': 130 break; 131 case 'h': 132 tv.tv_sec *= 60; 133 /* FALLTHROUGH */ 134 case 'm': 135 tv.tv_sec *= 60; 136 break; 137 default: 138 error("timeout unit"); 139 } 140 break; 141 } 142 } 143 if (prompt && isatty(0)) { 144 out2str(prompt); 145 flushall(); 146 } 147 if (*(ap = argptr) == NULL) 148 error("arg count"); 149 if ((ifs = bltinlookup("IFS", 1)) == NULL) 150 ifs = " \t\n"; 151 152 if (tv.tv_sec >= 0) { 153 /* 154 * Wait for something to become available. 155 */ 156 FD_ZERO(&ifds); 157 FD_SET(0, &ifds); 158 status = select(1, &ifds, NULL, NULL, &tv); 159 /* 160 * If there's nothing ready, return an error. 161 */ 162 if (status <= 0) { 163 sig = pendingsig; 164 return (128 + (sig != 0 ? sig : SIGALRM)); 165 } 166 } 167 168 status = 0; 169 startword = 2; 170 backslash = 0; 171 STARTSTACKSTR(p); 172 for (;;) { 173 nread = read(STDIN_FILENO, &c, 1); 174 if (nread == -1) { 175 if (errno == EINTR) { 176 sig = pendingsig; 177 if (sig == 0) 178 continue; 179 status = 128 + sig; 180 break; 181 } 182 warning("read error: %s", strerror(errno)); 183 status = 2; 184 break; 185 } else if (nread != 1) { 186 status = 1; 187 break; 188 } 189 if (c == '\0') 190 continue; 191 CHECKSTRSPACE(1, p); 192 if (backslash) { 193 backslash = 0; 194 if (c != '\n') { 195 startword = 0; 196 USTPUTC(c, p); 197 } 198 continue; 199 } 200 if (!rflag && c == '\\') { 201 backslash++; 202 continue; 203 } 204 if (c == '\n') 205 break; 206 if (strchr(ifs, c)) 207 is_ifs = strchr(" \t\n", c) ? 1 : 2; 208 else 209 is_ifs = 0; 210 211 if (startword != 0) { 212 if (is_ifs == 1) { 213 /* Ignore leading IFS whitespace */ 214 if (saveall) 215 USTPUTC(c, p); 216 continue; 217 } 218 if (is_ifs == 2 && startword == 1) { 219 /* Only one non-whitespace IFS per word */ 220 startword = 2; 221 if (saveall) 222 USTPUTC(c, p); 223 continue; 224 } 225 } 226 227 if (is_ifs == 0) { 228 /* append this character to the current variable */ 229 startword = 0; 230 if (saveall) 231 /* Not just a spare terminator */ 232 saveall++; 233 USTPUTC(c, p); 234 continue; 235 } 236 237 /* end of variable... */ 238 startword = is_ifs; 239 240 if (ap[1] == NULL) { 241 /* Last variable needs all IFS chars */ 242 saveall++; 243 USTPUTC(c, p); 244 continue; 245 } 246 247 STACKSTRNUL(p); 248 setvar(*ap, stackblock(), 0); 249 ap++; 250 STARTSTACKSTR(p); 251 } 252 STACKSTRNUL(p); 253 254 /* Remove trailing IFS chars */ 255 for (; stackblock() <= --p; *p = 0) { 256 if (!strchr(ifs, *p)) 257 break; 258 if (strchr(" \t\n", *p)) 259 /* Always remove whitespace */ 260 continue; 261 if (saveall > 1) 262 /* Don't remove non-whitespace unless it was naked */ 263 break; 264 } 265 setvar(*ap, stackblock(), 0); 266 267 /* Set any remaining args to "" */ 268 while (*++ap != NULL) 269 setvar(*ap, "", 0); 270 return status; 271 } 272 273 274 275 int 276 umaskcmd(int argc __unused, char **argv __unused) 277 { 278 char *ap; 279 int mask; 280 int i; 281 int symbolic_mode = 0; 282 283 while ((i = nextopt("S")) != '\0') { 284 symbolic_mode = 1; 285 } 286 287 INTOFF; 288 mask = umask(0); 289 umask(mask); 290 INTON; 291 292 if ((ap = *argptr) == NULL) { 293 if (symbolic_mode) { 294 char u[4], g[4], o[4]; 295 296 i = 0; 297 if ((mask & S_IRUSR) == 0) 298 u[i++] = 'r'; 299 if ((mask & S_IWUSR) == 0) 300 u[i++] = 'w'; 301 if ((mask & S_IXUSR) == 0) 302 u[i++] = 'x'; 303 u[i] = '\0'; 304 305 i = 0; 306 if ((mask & S_IRGRP) == 0) 307 g[i++] = 'r'; 308 if ((mask & S_IWGRP) == 0) 309 g[i++] = 'w'; 310 if ((mask & S_IXGRP) == 0) 311 g[i++] = 'x'; 312 g[i] = '\0'; 313 314 i = 0; 315 if ((mask & S_IROTH) == 0) 316 o[i++] = 'r'; 317 if ((mask & S_IWOTH) == 0) 318 o[i++] = 'w'; 319 if ((mask & S_IXOTH) == 0) 320 o[i++] = 'x'; 321 o[i] = '\0'; 322 323 out1fmt("u=%s,g=%s,o=%s\n", u, g, o); 324 } else { 325 out1fmt("%.4o\n", mask); 326 } 327 } else { 328 if (is_digit(*ap)) { 329 mask = 0; 330 do { 331 if (*ap >= '8' || *ap < '0') 332 error("Illegal number: %s", *argptr); 333 mask = (mask << 3) + (*ap - '0'); 334 } while (*++ap != '\0'); 335 umask(mask); 336 } else { 337 void *set; 338 INTOFF; 339 if ((set = setmode (ap)) == 0) 340 error("Illegal number: %s", ap); 341 342 mask = getmode (set, ~mask & 0777); 343 umask(~mask & 0777); 344 free(set); 345 INTON; 346 } 347 } 348 return 0; 349 } 350 351 /* 352 * ulimit builtin 353 * 354 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and 355 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with 356 * ash by J.T. Conklin. 357 * 358 * Public domain. 359 */ 360 361 struct limits { 362 const char *name; 363 const char *units; 364 int cmd; 365 int factor; /* multiply by to get rlim_{cur,max} values */ 366 char option; 367 }; 368 369 static const struct limits limits[] = { 370 #ifdef RLIMIT_CPU 371 { "cpu time", "seconds", RLIMIT_CPU, 1, 't' }, 372 #endif 373 #ifdef RLIMIT_FSIZE 374 { "file size", "512-blocks", RLIMIT_FSIZE, 512, 'f' }, 375 #endif 376 #ifdef RLIMIT_DATA 377 { "data seg size", "kbytes", RLIMIT_DATA, 1024, 'd' }, 378 #endif 379 #ifdef RLIMIT_STACK 380 { "stack size", "kbytes", RLIMIT_STACK, 1024, 's' }, 381 #endif 382 #ifdef RLIMIT_CORE 383 { "core file size", "512-blocks", RLIMIT_CORE, 512, 'c' }, 384 #endif 385 #ifdef RLIMIT_RSS 386 { "max memory size", "kbytes", RLIMIT_RSS, 1024, 'm' }, 387 #endif 388 #ifdef RLIMIT_MEMLOCK 389 { "locked memory", "kbytes", RLIMIT_MEMLOCK, 1024, 'l' }, 390 #endif 391 #ifdef RLIMIT_NPROC 392 { "max user processes", (char *)0, RLIMIT_NPROC, 1, 'u' }, 393 #endif 394 #ifdef RLIMIT_NOFILE 395 { "open files", (char *)0, RLIMIT_NOFILE, 1, 'n' }, 396 #endif 397 #ifdef RLIMIT_VMEM 398 { "virtual mem size", "kbytes", RLIMIT_VMEM, 1024, 'v' }, 399 #endif 400 #ifdef RLIMIT_SWAP 401 { "swap limit", "kbytes", RLIMIT_SWAP, 1024, 'w' }, 402 #endif 403 #ifdef RLIMIT_SBSIZE 404 { "sbsize", "bytes", RLIMIT_SBSIZE, 1, 'b' }, 405 #endif 406 #ifdef RLIMIT_NPTS 407 { "pseudo-terminals", (char *)0, RLIMIT_NPTS, 1, 'p' }, 408 #endif 409 #ifdef RLIMIT_KQUEUES 410 { "kqueues", (char *)0, RLIMIT_KQUEUES, 1, 'k' }, 411 #endif 412 { (char *) 0, (char *)0, 0, 0, '\0' } 413 }; 414 415 enum limithow { SOFT = 0x1, HARD = 0x2 }; 416 417 static void 418 printlimit(enum limithow how, const struct rlimit *limit, 419 const struct limits *l) 420 { 421 rlim_t val = 0; 422 423 if (how & SOFT) 424 val = limit->rlim_cur; 425 else if (how & HARD) 426 val = limit->rlim_max; 427 if (val == RLIM_INFINITY) 428 out1str("unlimited\n"); 429 else 430 { 431 val /= l->factor; 432 out1fmt("%jd\n", (intmax_t)val); 433 } 434 } 435 436 int 437 ulimitcmd(int argc __unused, char **argv __unused) 438 { 439 rlim_t val = 0; 440 enum limithow how = SOFT | HARD; 441 const struct limits *l; 442 int set, all = 0; 443 int optc, what; 444 struct rlimit limit; 445 446 what = 'f'; 447 while ((optc = nextopt("HSatfdsmcnuvlbpwk")) != '\0') 448 switch (optc) { 449 case 'H': 450 how = HARD; 451 break; 452 case 'S': 453 how = SOFT; 454 break; 455 case 'a': 456 all = 1; 457 break; 458 default: 459 what = optc; 460 } 461 462 for (l = limits; l->name && l->option != what; l++) 463 ; 464 if (!l->name) 465 error("internal error (%c)", what); 466 467 set = *argptr ? 1 : 0; 468 if (set) { 469 char *p = *argptr; 470 471 if (all || argptr[1]) 472 error("too many arguments"); 473 if (strcmp(p, "unlimited") == 0) 474 val = RLIM_INFINITY; 475 else { 476 char *end; 477 uintmax_t uval; 478 479 if (*p < '0' || *p > '9') 480 error("bad number"); 481 errno = 0; 482 uval = strtoumax(p, &end, 10); 483 if (errno != 0 || *end != '\0') 484 error("bad number"); 485 if (uval > UINTMAX_MAX / l->factor) 486 error("bad number"); 487 uval *= l->factor; 488 val = (rlim_t)uval; 489 if (val < 0 || (uintmax_t)val != uval || 490 val == RLIM_INFINITY) 491 error("bad number"); 492 } 493 } 494 if (all) { 495 for (l = limits; l->name; l++) { 496 char optbuf[40]; 497 if (getrlimit(l->cmd, &limit) < 0) 498 error("can't get limit: %s", strerror(errno)); 499 500 if (l->units) 501 snprintf(optbuf, sizeof(optbuf), 502 "(%s, -%c) ", l->units, l->option); 503 else 504 snprintf(optbuf, sizeof(optbuf), 505 "(-%c) ", l->option); 506 out1fmt("%-18s %18s ", l->name, optbuf); 507 printlimit(how, &limit, l); 508 } 509 return 0; 510 } 511 512 if (getrlimit(l->cmd, &limit) < 0) 513 error("can't get limit: %s", strerror(errno)); 514 if (set) { 515 if (how & SOFT) 516 limit.rlim_cur = val; 517 if (how & HARD) 518 limit.rlim_max = val; 519 if (setrlimit(l->cmd, &limit) < 0) 520 error("bad limit: %s", strerror(errno)); 521 } else 522 printlimit(how, &limit, l); 523 return 0; 524 } 525