1/* vi: set sw=4 ts=4: */ 2/* 3 * Adapted from ash applet code 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Kenneth Almquist. 7 * 8 * Copyright (c) 1989, 1991, 1993, 1994 9 * The Regents of the University of California. All rights reserved. 10 * 11 * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au> 12 * was re-ported from NetBSD and debianized. 13 * 14 * Copyright (c) 2010 Denys Vlasenko 15 * Split from ash.c 16 * 17 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. 18 */ 19#include "libbb.h" 20#include "shell_common.h" 21 22const char defifsvar[] ALIGN1 = "IFS= \t\n"; 23 24 25int FAST_FUNC is_well_formed_var_name(const char *s, char terminator) 26{ 27 if (!s || !(isalpha(*s) || *s == '_')) 28 return 0; 29 30 do 31 s++; 32 while (isalnum(*s) || *s == '_'); 33 34 return *s == terminator; 35} 36 37/* read builtin */ 38 39//TODO: use more efficient setvar() which takes a pointer to malloced "VAR=VAL" 40//string. hush naturally has it, and ash has setvareq(). 41//Here we can simply store "VAR=" at buffer start and store read data directly 42//after "=", then pass buffer to setvar() to consume. 43const char* FAST_FUNC 44shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), 45 char **argv, 46 const char *ifs, 47 int read_flags, 48 const char *opt_n, 49 const char *opt_p, 50 const char *opt_t, 51 const char *opt_u 52) 53{ 54 unsigned end_ms; /* -t TIMEOUT */ 55 int fd; /* -u FD */ 56 int nchars; /* -n NUM */ 57 char **pp; 58 char *buffer; 59 struct termios tty, old_tty; 60 const char *retval; 61 int bufpos; /* need to be able to hold -1 */ 62 int startword; 63 smallint backslash; 64 65 pp = argv; 66 while (*pp) { 67 if (!is_well_formed_var_name(*pp, '\0')) { 68 /* Mimic bash message */ 69 bb_error_msg("read: '%s': not a valid identifier", *pp); 70 return (const char *)(uintptr_t)1; 71 } 72 pp++; 73 } 74 75 nchars = 0; /* if != 0, -n is in effect */ 76 if (opt_n) { 77 nchars = bb_strtou(opt_n, NULL, 10); 78 if (nchars < 0 || errno) 79 return "invalid count"; 80 /* note: "-n 0": off (bash 3.2 does this too) */ 81 } 82 end_ms = 0; 83 if (opt_t) { 84 end_ms = bb_strtou(opt_t, NULL, 10); 85 if (errno || end_ms > UINT_MAX / 2048) 86 return "invalid timeout"; 87 end_ms *= 1000; 88#if 0 /* even bash has no -t N.NNN support */ 89 ts.tv_sec = bb_strtou(opt_t, &p, 10); 90 ts.tv_usec = 0; 91 /* EINVAL means number is ok, but not terminated by NUL */ 92 if (*p == '.' && errno == EINVAL) { 93 char *p2; 94 if (*++p) { 95 int scale; 96 ts.tv_usec = bb_strtou(p, &p2, 10); 97 if (errno) 98 return "invalid timeout"; 99 scale = p2 - p; 100 /* normalize to usec */ 101 if (scale > 6) 102 return "invalid timeout"; 103 while (scale++ < 6) 104 ts.tv_usec *= 10; 105 } 106 } else if (ts.tv_sec < 0 || errno) { 107 return "invalid timeout"; 108 } 109 if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */ 110 return "invalid timeout"; 111 } 112#endif /* if 0 */ 113 } 114 fd = STDIN_FILENO; 115 if (opt_u) { 116 fd = bb_strtou(opt_u, NULL, 10); 117 if (fd < 0 || errno) 118 return "invalid file descriptor"; 119 } 120 121 if (opt_p && isatty(fd)) { 122 fputs(opt_p, stderr); 123 fflush_all(); 124 } 125 126 if (ifs == NULL) 127 ifs = defifs; 128 129 if (nchars || (read_flags & BUILTIN_READ_SILENT)) { 130 tcgetattr(fd, &tty); 131 old_tty = tty; 132 if (nchars) { 133 tty.c_lflag &= ~ICANON; 134 tty.c_cc[VMIN] = nchars < 256 ? nchars : 255; 135 } 136 if (read_flags & BUILTIN_READ_SILENT) { 137 tty.c_lflag &= ~(ECHO | ECHOK | ECHONL); 138 } 139 /* This forces execution of "restoring" tcgetattr later */ 140 read_flags |= BUILTIN_READ_SILENT; 141 /* if tcgetattr failed, tcsetattr will fail too. 142 * Ignoring, it's harmless. */ 143 tcsetattr(fd, TCSANOW, &tty); 144 } 145 146 retval = (const char *)(uintptr_t)0; 147 startword = 1; 148 backslash = 0; 149 if (end_ms) /* NB: end_ms stays nonzero: */ 150 end_ms = ((unsigned)monotonic_ms() + end_ms) | 1; 151 buffer = NULL; 152 bufpos = 0; 153 do { 154 char c; 155 156 if (end_ms) { 157 int timeout; 158 struct pollfd pfd[1]; 159 160 pfd[0].fd = fd; 161 pfd[0].events = POLLIN; 162 timeout = end_ms - (unsigned)monotonic_ms(); 163 if (timeout <= 0 /* already late? */ 164 || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */ 165 ) { /* timed out! */ 166 retval = (const char *)(uintptr_t)1; 167 goto ret; 168 } 169 } 170 171 if ((bufpos & 0xff) == 0) 172 buffer = xrealloc(buffer, bufpos + 0x100); 173 if (nonblock_safe_read(fd, &buffer[bufpos], 1) != 1) { 174 retval = (const char *)(uintptr_t)1; 175 break; 176 } 177 c = buffer[bufpos]; 178 if (c == '\0') 179 continue; 180 if (backslash) { 181 backslash = 0; 182 if (c != '\n') 183 goto put; 184 continue; 185 } 186 if (!(read_flags & BUILTIN_READ_RAW) && c == '\\') { 187 backslash = 1; 188 continue; 189 } 190 if (c == '\n') 191 break; 192 193 /* $IFS splitting. NOT done if we run "read" 194 * without variable names (bash compat). 195 * Thus, "read" and "read REPLY" are not the same. 196 */ 197 if (argv[0]) { 198/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05 */ 199 const char *is_ifs = strchr(ifs, c); 200 if (startword && is_ifs) { 201 if (isspace(c)) 202 continue; 203 /* it is a non-space ifs char */ 204 startword--; 205 if (startword == 1) /* first one? */ 206 continue; /* yes, it is not next word yet */ 207 } 208 startword = 0; 209 if (argv[1] != NULL && is_ifs) { 210 buffer[bufpos] = '\0'; 211 bufpos = 0; 212 setvar(*argv, buffer); 213 argv++; 214 /* can we skip one non-space ifs char? (2: yes) */ 215 startword = isspace(c) ? 2 : 1; 216 continue; 217 } 218 } 219 put: 220 bufpos++; 221 } while (--nchars); 222 223 if (argv[0]) { 224 /* Remove trailing space $IFS chars */ 225 while (--bufpos >= 0 && isspace(buffer[bufpos]) && strchr(ifs, buffer[bufpos]) != NULL) 226 continue; 227 buffer[bufpos + 1] = '\0'; 228 /* Use the remainder as a value for the next variable */ 229 setvar(*argv, buffer); 230 /* Set the rest to "" */ 231 while (*++argv) 232 setvar(*argv, ""); 233 } else { 234 /* Note: no $IFS removal */ 235 buffer[bufpos] = '\0'; 236 setvar("REPLY", buffer); 237 } 238 239 ret: 240 free(buffer); 241 if (read_flags & BUILTIN_READ_SILENT) 242 tcsetattr(fd, TCSANOW, &old_tty); 243 return retval; 244} 245 246/* ulimit builtin */ 247 248struct limits { 249 uint8_t cmd; /* RLIMIT_xxx fit into it */ 250 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */ 251 char option; 252 const char *name; 253}; 254 255static const struct limits limits_tbl[] = { 256#ifdef RLIMIT_FSIZE 257 { RLIMIT_FSIZE, 9, 'f', "file size (blocks)" }, 258#endif 259#ifdef RLIMIT_CPU 260 { RLIMIT_CPU, 0, 't', "cpu time (seconds)" }, 261#endif 262#ifdef RLIMIT_DATA 263 { RLIMIT_DATA, 10, 'd', "data seg size (kb)" }, 264#endif 265#ifdef RLIMIT_STACK 266 { RLIMIT_STACK, 10, 's', "stack size (kb)" }, 267#endif 268#ifdef RLIMIT_CORE 269 { RLIMIT_CORE, 9, 'c', "core file size (blocks)" }, 270#endif 271#ifdef RLIMIT_RSS 272 { RLIMIT_RSS, 10, 'm', "resident set size (kb)" }, 273#endif 274#ifdef RLIMIT_MEMLOCK 275 { RLIMIT_MEMLOCK, 10, 'l', "locked memory (kb)" }, 276#endif 277#ifdef RLIMIT_NPROC 278 { RLIMIT_NPROC, 0, 'p', "processes" }, 279#endif 280#ifdef RLIMIT_NOFILE 281 { RLIMIT_NOFILE, 0, 'n', "file descriptors" }, 282#endif 283#ifdef RLIMIT_AS 284 { RLIMIT_AS, 10, 'v', "address space (kb)" }, 285#endif 286#ifdef RLIMIT_LOCKS 287 { RLIMIT_LOCKS, 0, 'w', "locks" }, 288#endif 289}; 290 291enum { 292 OPT_hard = (1 << 0), 293 OPT_soft = (1 << 1), 294}; 295 296/* "-": treat args as parameters of option with ASCII code 1 */ 297static const char ulimit_opt_string[] = "-HSa" 298#ifdef RLIMIT_FSIZE 299 "f::" 300#endif 301#ifdef RLIMIT_CPU 302 "t::" 303#endif 304#ifdef RLIMIT_DATA 305 "d::" 306#endif 307#ifdef RLIMIT_STACK 308 "s::" 309#endif 310#ifdef RLIMIT_CORE 311 "c::" 312#endif 313#ifdef RLIMIT_RSS 314 "m::" 315#endif 316#ifdef RLIMIT_MEMLOCK 317 "l::" 318#endif 319#ifdef RLIMIT_NPROC 320 "p::" 321#endif 322#ifdef RLIMIT_NOFILE 323 "n::" 324#endif 325#ifdef RLIMIT_AS 326 "v::" 327#endif 328#ifdef RLIMIT_LOCKS 329 "w::" 330#endif 331 ; 332 333static void printlim(unsigned opts, const struct rlimit *limit, 334 const struct limits *l) 335{ 336 rlim_t val; 337 338 val = limit->rlim_max; 339 if (!(opts & OPT_hard)) 340 val = limit->rlim_cur; 341 342 if (val == RLIM_INFINITY) 343 printf("unlimited\n"); 344 else { 345 val >>= l->factor_shift; 346 printf("%llu\n", (long long) val); 347 } 348} 349 350int FAST_FUNC 351shell_builtin_ulimit(char **argv) 352{ 353 unsigned opts; 354 unsigned argc; 355 356 /* We can't use getopt32: need to handle commands like 357 * ulimit 123 -c2 -l 456 358 */ 359 360 /* In case getopt was already called: 361 * reset the libc getopt() function, which keeps internal state. 362 */ 363#ifdef __GLIBC__ 364 optind = 0; 365#else /* BSD style */ 366 optind = 1; 367 /* optreset = 1; */ 368#endif 369 /* optarg = NULL; opterr = 0; optopt = 0; - do we need this?? */ 370 371 argc = 1; 372 while (argv[argc]) 373 argc++; 374 375 opts = 0; 376 while (1) { 377 struct rlimit limit; 378 const struct limits *l; 379 int opt_char = getopt(argc, argv, ulimit_opt_string); 380 381 if (opt_char == -1) 382 break; 383 if (opt_char == 'H') { 384 opts |= OPT_hard; 385 continue; 386 } 387 if (opt_char == 'S') { 388 opts |= OPT_soft; 389 continue; 390 } 391 392 if (opt_char == 'a') { 393 for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) { 394 getrlimit(l->cmd, &limit); 395 printf("-%c: %-30s ", l->option, l->name); 396 printlim(opts, &limit, l); 397 } 398 continue; 399 } 400 401 if (opt_char == 1) 402 opt_char = 'f'; 403 for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) { 404 if (opt_char == l->option) { 405 char *val_str; 406 407 getrlimit(l->cmd, &limit); 408 409 val_str = optarg; 410 if (!val_str && argv[optind] && argv[optind][0] != '-') 411 val_str = argv[optind++]; /* ++ skips NN in "-c NN" case */ 412 if (val_str) { 413 rlim_t val; 414 415 if (strcmp(val_str, "unlimited") == 0) 416 val = RLIM_INFINITY; 417 else { 418 if (sizeof(val) == sizeof(int)) 419 val = bb_strtou(val_str, NULL, 10); 420 else if (sizeof(val) == sizeof(long)) 421 val = bb_strtoul(val_str, NULL, 10); 422 else 423 val = bb_strtoull(val_str, NULL, 10); 424 if (errno) { 425 bb_error_msg("bad number"); 426 return EXIT_FAILURE; 427 } 428 val <<= l->factor_shift; 429 } 430//bb_error_msg("opt %c val_str:'%s' val:%lld", opt_char, val_str, (long long)val); 431 /* from man bash: "If neither -H nor -S 432 * is specified, both the soft and hard 433 * limits are set. */ 434 if (!opts) 435 opts = OPT_hard + OPT_soft; 436 if (opts & OPT_hard) 437 limit.rlim_max = val; 438 if (opts & OPT_soft) 439 limit.rlim_cur = val; 440//bb_error_msg("setrlimit(%d, %lld, %lld)", l->cmd, (long long)limit.rlim_cur, (long long)limit.rlim_max); 441 if (setrlimit(l->cmd, &limit) < 0) { 442 bb_perror_msg("error setting limit"); 443 return EXIT_FAILURE; 444 } 445 } else { 446 printlim(opts, &limit, l); 447 } 448 break; 449 } 450 } /* for (every possible opt) */ 451 452 if (l == &limits_tbl[ARRAY_SIZE(limits_tbl)]) { 453 /* bad option. getopt already complained. */ 454 break; 455 } 456 457 } /* while (there are options) */ 458 459 return 0; 460} 461