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