source: trunk/src/kash/redir.c@ 3473

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

kash: Added two specialized shfile_stat variants: shfile_stat_isreg, shfile_stat_exists. Increased the pipe size for windows to 64KB (SHFILE_PIPE_SIZE).

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