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