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