miscbltin.c revision 1.54 1 /* $NetBSD: miscbltin.c,v 1.54 2023/10/05 20:33:31 kre Exp $ */
2
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 #include <sys/cdefs.h>
36 #ifndef lint
37 #if 0
38 static char sccsid[] = "@(#)miscbltin.c 8.4 (Berkeley) 5/4/95";
39 #else
40 __RCSID("$NetBSD: miscbltin.c,v 1.54 2023/10/05 20:33:31 kre Exp $");
41 #endif
42 #endif /* not lint */
43
44 /*
45 * Miscellaneous builtins.
46 */
47
48 #include <sys/types.h> /* quad_t */
49 #include <sys/param.h> /* BSD4_4 */
50 #include <sys/stat.h>
51 #include <sys/time.h>
52 #include <sys/resource.h>
53 #include <unistd.h>
54 #include <stdlib.h>
55 #include <ctype.h>
56 #include <errno.h>
57
58 #include "shell.h"
59 #include "options.h"
60 #include "var.h"
61 #include "output.h"
62 #include "memalloc.h"
63 #include "error.h"
64 #include "builtins.h"
65 #include "mystring.h"
66 #include "redir.h" /* for user_fd_limit */
67
68 #undef rflag
69
70
71
72 /*
73 * The read builtin.
74 * Backslashes escape the next char unless -r is specified.
75 *
76 * This uses unbuffered input, which may be avoidable in some cases.
77 *
78 * Note that if IFS=' :' then read x y should work so that:
79 * 'a b' x='a', y='b'
80 * ' a b ' x='a', y='b'
81 * ':b' x='', y='b'
82 * ':' x='', y=''
83 * '::' x='', y=''
84 * ': :' x='', y=''
85 * ':::' x='', y='::'
86 * ':b c:' x='', y='b c:'
87 */
88
89 int
90 readcmd(int argc, char **argv)
91 {
92 char **ap;
93 char c;
94 char end;
95 int rflag;
96 char *prompt;
97 const char *ifs;
98 char *p;
99 int startword;
100 int status;
101 int i;
102 int is_ifs;
103 int saveall = 0;
104 ptrdiff_t wordlen = 0;
105 char *newifs = NULL;
106 struct stackmark mk;
107
108 end = '\n'; /* record delimiter */
109 rflag = 0;
110 prompt = NULL;
111 while ((i = nextopt("d:p:r")) != '\0') {
112 switch (i) {
113 case 'd':
114 end = *optionarg; /* even if '\0' */
115 break;
116 case 'p':
117 prompt = optionarg;
118 break;
119 case 'r':
120 rflag = 1;
121 break;
122 }
123 }
124
125 if (*(ap = argptr) == NULL)
126 error("variable name required\n"
127 "Usage: read [-r] [-p prompt] var...");
128
129 if (prompt && isatty(0)) {
130 out2str(prompt);
131 flushall();
132 }
133
134 if ((ifs = bltinlookup("IFS", 1)) == NULL)
135 ifs = " \t\n";
136
137 setstackmark(&mk);
138 status = 0;
139 startword = 2;
140 STARTSTACKSTR(p);
141 for (;;) {
142 if (read(0, &c, 1) != 1) {
143 status = 1;
144 break;
145 }
146 if (c == '\\' && c != end && !rflag) {
147 if (read(0, &c, 1) != 1) {
148 status = 1;
149 break;
150 }
151 if (c != '\n') /* \ \n is always just removed */
152 goto wdch;
153 continue;
154 }
155 if (c == end)
156 break;
157 if (c == '\0')
158 continue;
159 if (strchr(ifs, c))
160 is_ifs = strchr(" \t\n", c) ? 1 : 2;
161 else
162 is_ifs = 0;
163
164 if (startword != 0) {
165 if (is_ifs == 1) {
166 /* Ignore leading IFS whitespace */
167 if (saveall)
168 STPUTC(c, p);
169 continue;
170 }
171 if (is_ifs == 2 && startword == 1) {
172 /* Only one non-whitespace IFS per word */
173 startword = 2;
174 if (saveall)
175 STPUTC(c, p);
176 continue;
177 }
178 }
179
180 if (is_ifs == 0) {
181 wdch:;
182 if (c == '\0') /* always ignore attempts to input \0 */
183 continue;
184 /* append this character to the current variable */
185 startword = 0;
186 if (saveall)
187 /* Not just a spare terminator */
188 saveall++;
189 STPUTC(c, p);
190 wordlen = p - stackblock();
191 continue;
192 }
193
194 /* end of variable... */
195 startword = is_ifs;
196
197 if (ap[1] == NULL) {
198 /* Last variable needs all IFS chars */
199 saveall++;
200 STPUTC(c, p);
201 continue;
202 }
203
204 if (equal(*ap, "IFS")) {
205 /*
206 * we must not alter the value of IFS, as our
207 * local "ifs" var is (perhaps) pointing at it,
208 * at best we would be using data after free()
209 * the next time we reference ifs - but that mem
210 * may have been reused for something different.
211 *
212 * note that this might occur several times
213 */
214 STPUTC('\0', p);
215 newifs = grabstackstr(p);
216 } else {
217 STACKSTRNUL(p);
218 setvar(*ap, stackblock(), 0);
219 }
220 ap++;
221 STARTSTACKSTR(p);
222 wordlen = 0;
223 }
224 STACKSTRNUL(p);
225
226 /* Remove trailing IFS chars */
227 for (; stackblock() + wordlen <= --p; *p = 0) {
228 if (!strchr(ifs, *p))
229 break;
230 if (strchr(" \t\n", *p))
231 /* Always remove whitespace */
232 continue;
233 if (saveall > 1)
234 /* Don't remove non-whitespace unless it was naked */
235 break;
236 }
237
238 /*
239 * If IFS was one of the variables named, we can finally set it now
240 * (no further references to ifs will be made)
241 */
242 if (newifs != NULL)
243 setvar("IFS", newifs, 0);
244
245 /*
246 * Now we can assign to the final variable (which might
247 * also be IFS, hence the ordering here)
248 */
249 setvar(*ap, stackblock(), 0);
250
251 /* Set any remaining args to "" */
252 while (*++ap != NULL)
253 setvar(*ap, nullstr, 0);
254
255 popstackmark(&mk);
256 return status;
257 }
258
259
260
261 int
262 umaskcmd(int argc, char **argv)
263 {
264 char *ap;
265 mode_t mask;
266 int i;
267 int symbolic_mode = 0;
268
269 while ((i = nextopt("S")) != '\0') {
270 symbolic_mode = 1;
271 }
272
273 INTOFF;
274 mask = umask(0);
275 umask(mask);
276 INTON;
277
278 if ((ap = *argptr) == NULL) {
279 if (symbolic_mode) {
280 char u[4], g[4], o[4];
281
282 i = 0;
283 if ((mask & S_IRUSR) == 0)
284 u[i++] = 'r';
285 if ((mask & S_IWUSR) == 0)
286 u[i++] = 'w';
287 if ((mask & S_IXUSR) == 0)
288 u[i++] = 'x';
289 u[i] = '\0';
290
291 i = 0;
292 if ((mask & S_IRGRP) == 0)
293 g[i++] = 'r';
294 if ((mask & S_IWGRP) == 0)
295 g[i++] = 'w';
296 if ((mask & S_IXGRP) == 0)
297 g[i++] = 'x';
298 g[i] = '\0';
299
300 i = 0;
301 if ((mask & S_IROTH) == 0)
302 o[i++] = 'r';
303 if ((mask & S_IWOTH) == 0)
304 o[i++] = 'w';
305 if ((mask & S_IXOTH) == 0)
306 o[i++] = 'x';
307 o[i] = '\0';
308
309 out1fmt("u=%s,g=%s,o=%s\n", u, g, o);
310 } else {
311 out1fmt("%.4o\n", mask);
312 }
313 } else {
314 if (isdigit((unsigned char)*ap)) {
315 int range = 0;
316
317 mask = 0;
318 do {
319 if (*ap >= '8' || *ap < '0')
320 error("Not a valid octal number: '%s'",
321 *argptr);
322 mask = (mask << 3) + (*ap - '0');
323 if (mask & ~07777)
324 range = 1;
325 } while (*++ap != '\0');
326 if (range)
327 error("Mask constant '%s' out of range", *argptr);
328 umask(mask);
329 } else {
330 void *set;
331
332 INTOFF;
333 if ((set = setmode(ap)) != 0) {
334 mask = getmode(set, ~mask & 0777);
335 ckfree(set);
336 }
337 INTON;
338 if (!set)
339 error("Cannot set mode `%s' (%s)", ap,
340 strerror(errno));
341
342 umask(~mask & 0777);
343 }
344 }
345 flushout(out1);
346 if (io_err(out1)) {
347 out2str("umask: I/O error\n");
348 return 1;
349 }
350 return 0;
351 }
352
353 /*
354 * ulimit builtin
355 *
356 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
357 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
358 * ash by J.T. Conklin.
359 *
360 * Public domain.
361 */
362
363 struct limits {
364 const char *name;
365 const char *unit;
366 char option;
367 int8_t cmd; /* all RLIMIT_xxx are <= 127 */
368 unsigned short factor; /* multiply by to get rlim_{cur,max} values */
369 };
370
371 #define OPTSTRING_BASE "HSa"
372
373 static const struct limits limits[] = {
374 #ifdef RLIMIT_CPU
375 { "time", "seconds", 't', RLIMIT_CPU, 1 },
376 #define OPTSTRING_t OPTSTRING_BASE "t"
377 #else
378 #define OPTSTRING_t OPTSTRING_BASE
379 #endif
380 #ifdef RLIMIT_FSIZE
381 { "file", "blocks", 'f', RLIMIT_FSIZE, 512 },
382 #define OPTSTRING_f OPTSTRING_t "f"
383 #else
384 #define OPTSTRING_f OPTSTRING_t
385 #endif
386 #ifdef RLIMIT_DATA
387 { "data", "kbytes", 'd', RLIMIT_DATA, 1024 },
388 #define OPTSTRING_d OPTSTRING_f "d"
389 #else
390 #define OPTSTRING_d OPTSTRING_f
391 #endif
392 #ifdef RLIMIT_STACK
393 { "stack", "kbytes", 's', RLIMIT_STACK, 1024 },
394 #define OPTSTRING_s OPTSTRING_d "s"
395 #else
396 #define OPTSTRING_s OPTSTRING_d
397 #endif
398 #ifdef RLIMIT_CORE
399 { "coredump", "blocks", 'c', RLIMIT_CORE, 512 },
400 #define OPTSTRING_c OPTSTRING_s "c"
401 #else
402 #define OPTSTRING_c OPTSTRING_s
403 #endif
404 #ifdef RLIMIT_RSS
405 { "memory", "kbytes", 'm', RLIMIT_RSS, 1024 },
406 #define OPTSTRING_m OPTSTRING_c "m"
407 #else
408 #define OPTSTRING_m OPTSTRING_c
409 #endif
410 #ifdef RLIMIT_MEMLOCK
411 { "locked memory","kbytes", 'l', RLIMIT_MEMLOCK, 1024 },
412 #define OPTSTRING_l OPTSTRING_m "l"
413 #else
414 #define OPTSTRING_l OPTSTRING_m
415 #endif
416 #ifdef RLIMIT_NTHR
417 { "thread", "threads", 'r', RLIMIT_NTHR, 1 },
418 #define OPTSTRING_r OPTSTRING_l "r"
419 #else
420 #define OPTSTRING_r OPTSTRING_l
421 #endif
422 #ifdef RLIMIT_NPROC
423 { "process", "processes", 'p', RLIMIT_NPROC, 1 },
424 #define OPTSTRING_p OPTSTRING_r "p"
425 #else
426 #define OPTSTRING_p OPTSTRING_r
427 #endif
428 #ifdef RLIMIT_NOFILE
429 { "nofiles", "descriptors", 'n', RLIMIT_NOFILE, 1 },
430 #define OPTSTRING_n OPTSTRING_p "n"
431 #else
432 #define OPTSTRING_n OPTSTRING_p
433 #endif
434 #ifdef RLIMIT_VMEM
435 { "vmemory", "kbytes", 'v', RLIMIT_VMEM, 1024 },
436 #define OPTSTRING_v OPTSTRING_n "v"
437 #else
438 #define OPTSTRING_v OPTSTRING_n
439 #endif
440 #ifdef RLIMIT_SWAP
441 { "swap", "kbytes", 'w', RLIMIT_SWAP, 1024 },
442 #define OPTSTRING_w OPTSTRING_v "w"
443 #else
444 #define OPTSTRING_w OPTSTRING_v
445 #endif
446 #ifdef RLIMIT_SBSIZE
447 { "sbsize", "bytes", 'b', RLIMIT_SBSIZE, 1 },
448 #define OPTSTRING_b OPTSTRING_w "b"
449 #else
450 #define OPTSTRING_b OPTSTRING_w
451 #endif
452 { NULL, NULL, '\0', 0, 0 }
453 };
454 #define OPTSTRING OPTSTRING_b
455
456 int
457 ulimitcmd(int argc, char **argv)
458 {
459 int c;
460 rlim_t val = 0;
461 enum { SOFT = 0x1, HARD = 0x2 }
462 how = 0, which;
463 const struct limits *l;
464 int set, all = 0;
465 int optc, what;
466 struct rlimit limit;
467
468 what = 'f';
469 while ((optc = nextopt(OPTSTRING)) != '\0')
470 switch (optc) {
471 case 'H':
472 how |= HARD;
473 break;
474 case 'S':
475 how |= SOFT;
476 break;
477 case 'a':
478 all = 1;
479 break;
480 default:
481 what = optc;
482 }
483
484 for (l = limits; l->name && l->option != what; l++)
485 ;
486 if (!l->name)
487 error("internal error (%c)", what);
488
489 set = *argptr ? 1 : 0;
490 if (set) {
491 char *p = *argptr;
492
493 if (all || argptr[1])
494 error("too many arguments");
495 if (how == 0)
496 how = HARD | SOFT;
497
498 if (strcmp(p, "unlimited") == 0)
499 val = RLIM_INFINITY;
500 else {
501 val = (rlim_t) 0;
502
503 while ((c = *p++) >= '0' && c <= '9') {
504 if (val >= RLIM_INFINITY/10)
505 error("%s: value overflow", *argptr);
506 val = (val * 10);
507 if (val >= RLIM_INFINITY - (long)(c - '0'))
508 error("%s: value overflow", *argptr);
509 val += (long)(c - '0');
510 }
511 if (c)
512 error("%s: bad number", *argptr);
513 if (val > RLIM_INFINITY / l->factor)
514 error("%s: value overflow", *argptr);
515 val *= l->factor;
516 }
517 } else if (how == 0)
518 how = SOFT;
519
520 if (all) {
521 for (l = limits; l->name; l++) {
522 getrlimit(l->cmd, &limit);
523 out1fmt("%-13s (-%c %-11s) ", l->name, l->option,
524 l->unit);
525
526 which = how;
527 while (which != 0) {
528 if (which & SOFT) {
529 val = limit.rlim_cur;
530 which &= ~SOFT;
531 } else if (which & HARD) {
532 val = limit.rlim_max;
533 which &= ~HARD;
534 }
535
536 if (val == RLIM_INFINITY)
537 out1fmt("unlimited");
538 else {
539 val /= l->factor;
540 #ifdef BSD4_4
541 out1fmt("%9lld", (long long) val);
542 #else
543 out1fmt("%9ld", (long) val);
544 #endif
545 }
546 out1fmt("%c", which ? '\t' : '\n');
547 }
548 }
549 goto done;
550 }
551
552 if (getrlimit(l->cmd, &limit) == -1)
553 error("error getting limit (%s)", strerror(errno));
554 if (set) {
555 if (how & HARD)
556 limit.rlim_max = val;
557 if (how & SOFT)
558 limit.rlim_cur = val;
559 if (setrlimit(l->cmd, &limit) < 0)
560 error("error setting limit (%s)", strerror(errno));
561 if (l->cmd == RLIMIT_NOFILE)
562 user_fd_limit = sysconf(_SC_OPEN_MAX);
563 } else {
564 if (how & SOFT)
565 val = limit.rlim_cur;
566 else if (how & HARD)
567 val = limit.rlim_max;
568
569 if (val == RLIM_INFINITY)
570 out1fmt("unlimited\n");
571 else
572 {
573 val /= l->factor;
574 #ifdef BSD4_4
575 out1fmt("%lld\n", (long long) val);
576 #else
577 out1fmt("%ld\n", (long) val);
578 #endif
579 }
580 }
581 done:;
582 flushout(out1);
583 if (io_err(out1)) {
584 out2str("ulimit: I/O error (stdout)\n");
585 return 1;
586 }
587 return 0;
588 }
589