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