VirtualBox

source: kBuild/trunk/src/kash/redir.c@ 3454

Last change on this file since 3454 was 3449, checked in by bird, 5 years ago

kash: Eliminate the 'temp' node field in nfile so we can share node trees later.

  • Property svn:eol-style set to LF
  • Property svn:keywords set to Id
File size: 11.9 KB
Line 
1/* $NetBSD: redir.c,v 1.29 2004/07/08 03:57:33 christos 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#if 0
36#ifndef lint
37static char sccsid[] = "@(#)redir.c 8.2 (Berkeley) 5/4/95";
38#else
39__RCSID("$NetBSD: redir.c,v 1.29 2004/07/08 03:57:33 christos Exp $");
40#endif /* not lint */
41#endif
42
43#include <sys/types.h>
44#include <limits.h> /* PIPE_BUF */
45#include <assert.h>
46#include <string.h>
47#include <errno.h>
48#include <stddef.h>
49#include <stdlib.h>
50
51/*
52 * Code for dealing with input/output redirection.
53 */
54
55#include "main.h"
56#include "shell.h"
57#include "nodes.h"
58#include "jobs.h"
59#include "options.h"
60#include "expand.h"
61#include "redir.h"
62#include "output.h"
63#include "memalloc.h"
64#include "error.h"
65#include "shinstance.h"
66
67
68#define EMPTY -2 /* marks an unused slot in redirtab */
69#ifndef PIPE_BUF
70# define PIPESIZE 4096 /* amount of buffering in a pipe */
71#else
72# define PIPESIZE PIPE_BUF
73#endif
74
75
76MKINIT
77struct redirtab {
78 struct redirtab *next;
79 short renamed[10];
80};
81
82
83//MKINIT struct redirtab *redirlist;
84
85/*
86 * We keep track of whether or not fd0 has been redirected. This is for
87 * background commands, where we want to redirect fd0 to /dev/null only
88 * if it hasn't already been redirected.
89*/
90//int fd0_redirected = 0;
91
92STATIC void openredirect(shinstance *, union node *, char[10], int, const char *);
93STATIC int openhere(shinstance *, union node *);
94
95
96#ifndef SH_FORKED_MODE
97void
98subshellinitredir(shinstance *psh, shinstance *inherit)
99{
100 /* We can have a redirlist here if we're handling backtick while expanding
101 arguments, just copy it even if the subshell probably doesn't need it. */
102 struct redirtab *src = inherit->redirlist;
103 if (src)
104 {
105 struct redirtab **dstp = &psh->redirlist;
106 do
107 {
108 struct redirtab *dst = ckmalloc(psh, sizeof(*dst));
109 memcpy(dst->renamed, src->renamed, sizeof(dst->renamed));
110 *dstp = dst;
111 dstp = &dst->next;
112 src = src->next;
113 } while (src);
114 *dstp = NULL;
115
116 psh->fd0_redirected = inherit->fd0_redirected;
117 }
118
119 /* Copy the expanded redirection filenames (stack), but only the last entry
120 as the subshell does not have the ability to unwind stack in the parent
121 and therefore cannot get to the earlier redirection stuff: */
122 if (inherit->expfnames)
123 {
124 redirexpfnames * const expfnamesrc = inherit->expfnames;
125 unsigned i = expfnamesrc->count;
126 redirexpfnames *dst = stalloc(psh, offsetof(redirexpfnames, names) + sizeof(dst->names[0]) * i);
127 dst->count = i;
128 dst->depth = 1;
129 dst->prev = NULL;
130 while (i-- > 0)
131 dst->names[i] = stsavestr(psh, expfnamesrc->names[i]);
132 psh->expfnames = dst;
133 }
134}
135#endif /* !SH_FORKED_MODE */
136
137
138/*
139 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
140 * old file descriptors are stashed away so that the redirection can be
141 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
142 * standard output, and the standard error if it becomes a duplicate of
143 * stdout, is saved in memory.
144 */
145
146void
147redirect(shinstance *psh, union node *redir, int flags)
148{
149 union node *n;
150 struct redirtab *sv = NULL;
151 int i;
152 int fd;
153 int try;
154 char memory[10]; /* file descriptors to write to memory */
155 unsigned idxexpfname;
156
157 for (i = 10 ; --i >= 0 ; )
158 memory[i] = 0;
159 memory[1] = flags & REDIR_BACKQ;
160 if (flags & REDIR_PUSH) {
161 sv = ckmalloc(psh, sizeof (struct redirtab));
162 for (i = 0 ; i < 10 ; i++)
163 sv->renamed[i] = EMPTY;
164 sv->next = psh->redirlist;
165 psh->redirlist = sv;
166 }
167 idxexpfname = 0;
168 for (n = redir, idxexpfname = 0 ; n ; n = n->nfile.next, idxexpfname++) {
169 assert(idxexpfname < psh->expfnames->count);
170 fd = n->nfile.fd;
171 try = 0;
172 if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD) &&
173 n->ndup.dupfd == fd)
174 continue; /* redirect from/to same file descriptor */
175
176 if ((flags & REDIR_PUSH) && sv->renamed[fd] == EMPTY) {
177 INTOFF;
178again:
179 if ((i = shfile_fcntl(&psh->fdtab, fd, F_DUPFD, 10)) == -1) {
180 switch (errno) {
181 case EBADF:
182 if (!try) {
183 openredirect(psh, n, memory, flags, psh->expfnames->names[idxexpfname]);
184 try++;
185 goto again;
186 }
187 /* FALLTHROUGH*/
188 default:
189 INTON;
190 error(psh, "%d: %s", fd, sh_strerror(psh, errno));
191 /* NOTREACHED */
192 }
193 }
194 if (!try) {
195 sv->renamed[fd] = i;
196 shfile_close(&psh->fdtab, fd);
197 }
198 INTON;
199 } else {
200 shfile_close(&psh->fdtab, fd);
201 }
202 if (fd == 0)
203 psh->fd0_redirected++;
204 if (!try)
205 openredirect(psh, n, memory, flags, psh->expfnames->names[idxexpfname]);
206 }
207 assert(!redir || idxexpfname == psh->expfnames->count);
208 if (memory[1])
209 psh->out1 = &psh->memout;
210 if (memory[2])
211 psh->out2 = &psh->memout;
212}
213
214
215STATIC void
216openredirect(shinstance *psh, union node *redir, char memory[10], int flags, const char *fname)
217{
218 int fd = redir->nfile.fd;
219 int f;
220 int oflags = O_WRONLY|O_CREAT|O_TRUNC;
221
222 /*
223 * We suppress interrupts so that we won't leave open file
224 * descriptors around. This may not be such a good idea because
225 * an open of a device or a fifo can block indefinitely.
226 */
227 INTOFF;
228 memory[fd] = 0;
229 switch (redir->nfile.type) {
230 case NFROM:
231 if ((f = shfile_open(&psh->fdtab, fname, O_RDONLY, 0)) < 0)
232 goto eopen;
233 break;
234 case NFROMTO:
235 if ((f = shfile_open(&psh->fdtab, fname, O_RDWR|O_CREAT|O_TRUNC, 0666)) < 0)
236 goto ecreate;
237 break;
238 case NTO:
239 if (Cflag(psh))
240 oflags |= O_EXCL;
241 /* FALLTHROUGH */
242 case NCLOBBER:
243 if ((f = shfile_open(&psh->fdtab, fname, oflags, 0666)) < 0)
244 goto ecreate;
245 break;
246 case NAPPEND:
247 if ((f = shfile_open(&psh->fdtab, fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0)
248 goto ecreate;
249 break;
250 case NTOFD:
251 case NFROMFD:
252 if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
253 if (memory[redir->ndup.dupfd])
254 memory[fd] = 1;
255 else
256 copyfd(psh, redir->ndup.dupfd, fd);
257 }
258 INTON;
259 return;
260 case NHERE:
261 case NXHERE:
262 f = openhere(psh, redir);
263 break;
264 default:
265 sh_abort(psh);
266 }
267
268 if (f != fd) {
269 movefd(psh, f, fd);
270 }
271 INTON;
272 return;
273ecreate:
274 error(psh, "cannot create %s: %s", fname, errmsg(psh, errno, E_CREAT));
275eopen:
276 error(psh, "cannot open %s: %s", fname, errmsg(psh, errno, E_OPEN));
277}
278
279#ifdef KASH_USE_FORKSHELL2
280struct openherechild
281{
282 int pip[2];
283 size_t len;
284};
285static int openhere_child(shinstance *psh, union node *n, void *argp)
286{
287 struct openherechild args = *(struct openherechild *)argp;
288
289 shfile_close(&psh->fdtab, args.pip[0]);
290 sh_signal(psh, SIGINT, SH_SIG_IGN);
291 sh_signal(psh, SIGQUIT, SH_SIG_IGN);
292 sh_signal(psh, SIGHUP, SH_SIG_IGN);
293# ifdef SIGTSTP
294 sh_signal(psh, SIGTSTP, SH_SIG_IGN);
295# endif
296 sh_signal(psh, SIGPIPE, SH_SIG_DFL);
297 if (n->type == NHERE)
298 xwrite(psh, args.pip[1], n->nhere.doc->narg.text, args.len);
299 else
300 expandhere(psh, n->nhere.doc, args.pip[1]);
301 return 0;
302}
303
304#endif /* KASH_USE_FORKSHELL2*/
305
306/*
307 * Handle here documents. Normally we fork off a process to write the
308 * data to a pipe. If the document is short, we can stuff the data in
309 * the pipe without forking.
310 */
311
312STATIC int
313openhere(shinstance *psh, union node *redir)
314{
315 int pip[2];
316 size_t len = 0;
317
318 if (shfile_pipe(&psh->fdtab, pip) < 0)
319 error(psh, "Pipe call failed");
320 if (redir->type == NHERE) {
321 len = strlen(redir->nhere.doc->narg.text);
322 if (len <= PIPESIZE) {
323 xwrite(psh, pip[1], redir->nhere.doc->narg.text, len);
324 goto out;
325 }
326 }
327#ifdef KASH_USE_FORKSHELL2
328 {
329 struct openherechild args;
330 args.pip[0] = pip[0];
331 args.pip[1] = pip[1];
332 args.len = len;
333 forkshell2(psh, (struct job *)NULL, redir,
334 FORK_NOJOB | FORK_JUST_IO,
335 openhere_child, redir, &args, sizeof(args), NULL);
336 }
337#else
338 if (forkshell(psh, (struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
339 shfile_close(&psh->fdtab, pip[0]);
340 sh_signal(psh, SIGINT, SH_SIG_IGN);
341 sh_signal(psh, SIGQUIT, SH_SIG_IGN);
342 sh_signal(psh, SIGHUP, SH_SIG_IGN);
343#ifdef SIGTSTP
344 sh_signal(psh, SIGTSTP, SH_SIG_IGN);
345#endif
346 sh_signal(psh, SIGPIPE, SH_SIG_DFL);
347 if (redir->type == NHERE)
348 xwrite(psh, pip[1], redir->nhere.doc->narg.text, len);
349 else
350 expandhere(psh, redir->nhere.doc, pip[1]);
351 sh__exit(psh, 0);
352 }
353#endif
354out:
355 shfile_close(&psh->fdtab, pip[1]);
356 return pip[0];
357}
358
359
360
361/*
362 * Undo the effects of the last redirection.
363 */
364
365void
366popredir(shinstance *psh)
367{
368 struct redirtab *rp = psh->redirlist;
369 int i;
370
371 for (i = 0 ; i < 10 ; i++) {
372 if (rp->renamed[i] != EMPTY) {
373 if (i == 0)
374 psh->fd0_redirected--;
375 if (rp->renamed[i] >= 0) {
376 movefd(psh, rp->renamed[i], i);
377 } else {
378 shfile_close(&psh->fdtab, i);
379 }
380 }
381 }
382 INTOFF;
383 psh->redirlist = rp->next;
384 ckfree(psh, rp);
385 INTON;
386}
387
388/*
389 * Undo all redirections. Called on error or interrupt.
390 */
391
392#ifdef mkinit
393
394INCLUDE "redir.h"
395
396RESET {
397 while (psh->redirlist)
398 popredir(psh);
399}
400
401SHELLPROC {
402 clearredir(psh);
403}
404
405#endif
406
407/* Return true if fd 0 has already been redirected at least once. */
408int
409fd0_redirected_p(shinstance *psh) {
410 return psh->fd0_redirected != 0;
411}
412
413/*
414 * Discard all saved file descriptors.
415 */
416
417void
418clearredir(shinstance *psh)
419{
420 struct redirtab *rp;
421 int i;
422
423 for (rp = psh->redirlist ; rp ; rp = rp->next) {
424 for (i = 0 ; i < 10 ; i++) {
425 if (rp->renamed[i] >= 0) {
426 shfile_close(&psh->fdtab, rp->renamed[i]);
427 }
428 rp->renamed[i] = EMPTY;
429 }
430 }
431}
432
433
434
435/*
436 * Copy a file descriptor to be >= to. Returns -1
437 * if the source file descriptor is closed, EMPTY if there are no unused
438 * file descriptors left.
439 */
440
441int
442copyfd(shinstance *psh, int from, int to)
443{
444 int newfd;
445
446 newfd = shfile_fcntl(&psh->fdtab, from, F_DUPFD, to);
447 if (newfd < 0) {
448 if (errno == EMFILE)
449 return EMPTY;
450 error(psh, "%d: %s", from, sh_strerror(psh, errno));
451 }
452 return newfd;
453}
454
455
456/*
457 * Move a file descriptor to be == to. Returns -1
458 * if the source file descriptor is closed, EMPTY if there are no unused
459 * file descriptors left.
460 */
461
462int
463movefd(shinstance *psh, int from, int to)
464{
465 int newfd;
466
467 newfd = shfile_movefd(&psh->fdtab, from, to);
468 if (newfd < 0) {
469 if (errno == EMFILE)
470 return EMPTY;
471 error(psh, "%d: %s", from, sh_strerror(psh, errno));
472 }
473 return newfd;
474}
475
476
477/*
478 * Move a file descriptor to be >= to. Returns -1
479 * if the source file descriptor is closed, EMPTY if there are no unused
480 * file descriptors left.
481 */
482
483int
484movefd_above(shinstance *psh, int from, int to)
485{
486 int newfd;
487
488 newfd = shfile_movefd_above(&psh->fdtab, from, to);
489 if (newfd < 0) {
490 if (errno == EMFILE)
491 return EMPTY;
492 error(psh, "%d: %s", from, sh_strerror(psh, errno));
493 }
494 return newfd;
495}
496
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette