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