]>
Commit | Line | Data |
---|---|---|
71aad674 A |
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 | * 4. 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 ((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 | } |