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