1/*-
2 * Copyright (c) 1991, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 * Copyright (c) 1997-2005
5 *	Herbert Xu <herbert@gondor.apana.org.au>.  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#include <sys/stat.h>
36#include <sys/types.h>
37#include <sys/param.h>	/* PIPE_BUF */
38#include <signal.h>
39#include <string.h>
40#include <fcntl.h>
41#include <unistd.h>
42#include <stdlib.h>
43
44/*
45 * Code for dealing with input/output redirection.
46 */
47
48#include "main.h"
49#include "shell.h"
50#include "nodes.h"
51#include "jobs.h"
52#include "options.h"
53#include "expand.h"
54#include "redir.h"
55#include "output.h"
56#include "memalloc.h"
57#include "error.h"
58
59
60#define REALLY_CLOSED -3	/* fd that was closed and still is */
61#define EMPTY -2		/* marks an unused slot in redirtab */
62#define CLOSED -1		/* fd opened for redir needs to be closed */
63
64#ifndef PIPE_BUF
65# define PIPESIZE 4096		/* amount of buffering in a pipe */
66#else
67# define PIPESIZE PIPE_BUF
68#endif
69
70
71MKINIT
72struct redirtab {
73	struct redirtab *next;
74	int renamed[10];
75};
76
77
78MKINIT struct redirtab *redirlist;
79
80STATIC int openredirect(union node *);
81#ifdef notyet
82STATIC void dupredirect(union node *, int, char[10]);
83#else
84STATIC void dupredirect(union node *, int);
85#endif
86STATIC int openhere(union node *);
87
88
89/*
90 * Process a list of redirection commands.  If the REDIR_PUSH flag is set,
91 * old file descriptors are stashed away so that the redirection can be
92 * undone by calling popredir.  If the REDIR_BACKQ flag is set, then the
93 * standard output, and the standard error if it becomes a duplicate of
94 * stdout, is saved in memory.
95 */
96
97void
98redirect(union node *redir, int flags)
99{
100	union node *n;
101	struct redirtab *sv;
102	int i;
103	int fd;
104	int newfd;
105	int *p;
106#if notyet
107	char memory[10];	/* file descriptors to write to memory */
108
109	for (i = 10 ; --i >= 0 ; )
110		memory[i] = 0;
111	memory[1] = flags & REDIR_BACKQ;
112#endif
113	if (!redir)
114		return;
115	sv = NULL;
116	INTOFF;
117	if (likely(flags & REDIR_PUSH))
118		sv = redirlist;
119	n = redir;
120	do {
121		newfd = openredirect(n);
122		if (newfd < -1)
123			continue;
124
125		fd = n->nfile.fd;
126
127		if (sv) {
128			p = &sv->renamed[fd];
129			i = *p;
130
131			if (likely(i == EMPTY)) {
132				i = CLOSED;
133				if (fd != newfd) {
134					i = savefd(fd, fd);
135					fd = -1;
136				}
137			}
138
139			if (i == newfd)
140				/* Can only happen if i == newfd == CLOSED */
141				i = REALLY_CLOSED;
142
143			*p = i;
144		}
145
146		if (fd == newfd)
147			continue;
148
149#ifdef notyet
150		dupredirect(n, newfd, memory);
151#else
152		dupredirect(n, newfd);
153#endif
154	} while ((n = n->nfile.next));
155	INTON;
156#ifdef notyet
157	if (memory[1])
158		out1 = &memout;
159	if (memory[2])
160		out2 = &memout;
161#endif
162	if (flags & REDIR_SAVEFD2 && sv->renamed[2] >= 0)
163		preverrout.fd = sv->renamed[2];
164}
165
166
167STATIC int
168openredirect(union node *redir)
169{
170	struct stat sb;
171	char *fname;
172	int f;
173
174	switch (redir->nfile.type) {
175	case NFROM:
176		fname = redir->nfile.expfname;
177		if ((f = open(fname, O_RDONLY)) < 0)
178			goto eopen;
179		break;
180	case NFROMTO:
181		fname = redir->nfile.expfname;
182		if ((f = open(fname, O_RDWR|O_CREAT, 0666)) < 0)
183			goto ecreate;
184		break;
185	case NTO:
186		/* Take care of noclobber mode. */
187		if (Cflag) {
188			fname = redir->nfile.expfname;
189			if (stat(fname, &sb) < 0) {
190				if ((f = open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666)) < 0)
191					goto ecreate;
192			} else if (!S_ISREG(sb.st_mode)) {
193				if ((f = open(fname, O_WRONLY, 0666)) < 0)
194					goto ecreate;
195				if (fstat(f, &sb) < 0 && S_ISREG(sb.st_mode)) {
196					close(f);
197					errno = EEXIST;
198					goto ecreate;
199				}
200			} else {
201				errno = EEXIST;
202				goto ecreate;
203			}
204			break;
205		}
206		/* FALLTHROUGH */
207	case NCLOBBER:
208		fname = redir->nfile.expfname;
209		if ((f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
210			goto ecreate;
211		break;
212	case NAPPEND:
213		fname = redir->nfile.expfname;
214		if ((f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0)
215			goto ecreate;
216		break;
217	case NTOFD:
218	case NFROMFD:
219		f = redir->ndup.dupfd;
220		if (f == redir->nfile.fd)
221			f = -2;
222		break;
223	default:
224#ifdef DEBUG
225		abort();
226#endif
227		/* Fall through to eliminate warning. */
228	case NHERE:
229	case NXHERE:
230		f = openhere(redir);
231		break;
232	}
233
234	return f;
235ecreate:
236	sh_error("cannot create %s: %s", fname, errmsg(errno, E_CREAT));
237eopen:
238	sh_error("cannot open %s: %s", fname, errmsg(errno, E_OPEN));
239}
240
241
242STATIC void
243#ifdef notyet
244dupredirect(redir, f, memory)
245#else
246dupredirect(redir, f)
247#endif
248	union node *redir;
249	int f;
250#ifdef notyet
251	char memory[10];
252#endif
253	{
254	int fd = redir->nfile.fd;
255	int err = 0;
256
257#ifdef notyet
258	memory[fd] = 0;
259#endif
260	if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
261		/* if not ">&-" */
262		if (f >= 0) {
263#ifdef notyet
264			if (memory[f])
265				memory[fd] = 1;
266			else
267#endif
268				if (dup2(f, fd) < 0) {
269					err = errno;
270					goto err;
271				}
272			return;
273		}
274		f = fd;
275	} else if (dup2(f, fd) < 0)
276		err = errno;
277
278	close(f);
279	if (err < 0)
280		goto err;
281
282	return;
283
284err:
285	sh_error("%d: %s", f, strerror(err));
286}
287
288
289/*
290 * Handle here documents.  Normally we fork off a process to write the
291 * data to a pipe.  If the document is short, we can stuff the data in
292 * the pipe without forking.
293 */
294
295STATIC int
296openhere(union node *redir)
297{
298	char *p;
299	int pip[2];
300	size_t len = 0;
301
302	if (pipe(pip) < 0)
303		sh_error("Pipe call failed");
304
305	p = redir->nhere.doc->narg.text;
306	if (redir->type == NXHERE) {
307		expandarg(redir->nhere.doc, NULL, EXP_QUOTED);
308		p = stackblock();
309	}
310
311	len = strlen(p);
312	if (len <= PIPESIZE) {
313		xwrite(pip[1], p, len);
314		goto out;
315	}
316
317	if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
318		close(pip[0]);
319		signal(SIGINT, SIG_IGN);
320		signal(SIGQUIT, SIG_IGN);
321		signal(SIGHUP, SIG_IGN);
322#ifdef SIGTSTP
323		signal(SIGTSTP, SIG_IGN);
324#endif
325		signal(SIGPIPE, SIG_DFL);
326		xwrite(pip[1], p, len);
327		_exit(0);
328	}
329out:
330	close(pip[1]);
331	return pip[0];
332}
333
334
335
336/*
337 * Undo the effects of the last redirection.
338 */
339
340void
341popredir(int drop)
342{
343	struct redirtab *rp;
344	int i;
345
346	INTOFF;
347	rp = redirlist;
348	for (i = 0 ; i < 10 ; i++) {
349		switch (rp->renamed[i]) {
350		case CLOSED:
351			if (!drop)
352				close(i);
353			break;
354		case EMPTY:
355		case REALLY_CLOSED:
356			break;
357		default:
358			if (!drop)
359				dup2(rp->renamed[i], i);
360			close(rp->renamed[i]);
361			break;
362		}
363	}
364	redirlist = rp->next;
365	ckfree(rp);
366	INTON;
367}
368
369/*
370 * Undo all redirections.  Called on error or interrupt.
371 */
372
373#ifdef mkinit
374
375INCLUDE "redir.h"
376
377RESET {
378	/*
379	 * Discard all saved file descriptors.
380	 */
381	unwindredir(0);
382}
383
384#endif
385
386
387
388/*
389 * Move a file descriptor to > 10.  Invokes sh_error on error unless
390 * the original file dscriptor is not open.
391 */
392
393int
394savefd(int from, int ofd)
395{
396	int newfd;
397	int err;
398
399	newfd = fcntl(from, F_DUPFD, 10);
400	err = newfd < 0 ? errno : 0;
401	if (err != EBADF) {
402		close(ofd);
403		if (err)
404			sh_error("%d: %s", from, strerror(err));
405		else
406			fcntl(newfd, F_SETFD, FD_CLOEXEC);
407	}
408
409	return newfd;
410}
411
412
413int
414redirectsafe(union node *redir, int flags)
415{
416	int err;
417	volatile int saveint;
418	struct jmploc *volatile savehandler = handler;
419	struct jmploc jmploc;
420
421	SAVEINT(saveint);
422	if (!(err = setjmp(jmploc.loc) * 2)) {
423		handler = &jmploc;
424		redirect(redir, flags);
425	}
426	handler = savehandler;
427	if (err && exception != EXERROR)
428		longjmp(handler->loc, 1);
429	RESTOREINT(saveint);
430	return err;
431}
432
433
434void unwindredir(struct redirtab *stop)
435{
436	while (redirlist != stop)
437		popredir(0);
438}
439
440
441struct redirtab *pushredir(union node *redir)
442{
443	struct redirtab *sv;
444	struct redirtab *q;
445	int i;
446
447	q = redirlist;
448	if (!redir)
449		goto out;
450
451	sv = ckmalloc(sizeof (struct redirtab));
452	sv->next = q;
453	redirlist = sv;
454	for (i = 0; i < 10; i++)
455		sv->renamed[i] = EMPTY;
456
457out:
458	return q;
459}
460