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