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