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

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

kash: Use kHlpAssert instead of assert.h (debugger stops on the assertion rather than at exit process code).

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