master xplshn/aruu / cmd / posix / sh / miscbltin.c
  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}