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