14b88c807SRodney W. Grimes /*-
2*8a16b7a1SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause
3*8a16b7a1SPedro F. Giffuni *
44b88c807SRodney W. Grimes * Copyright (c) 1991, 1993
54b88c807SRodney W. Grimes * The Regents of the University of California. All rights reserved.
64b88c807SRodney W. Grimes *
74b88c807SRodney W. Grimes * This code is derived from software contributed to Berkeley by
84b88c807SRodney W. Grimes * Kenneth Almquist.
94b88c807SRodney W. Grimes *
104b88c807SRodney W. Grimes * Redistribution and use in source and binary forms, with or without
114b88c807SRodney W. Grimes * modification, are permitted provided that the following conditions
124b88c807SRodney W. Grimes * are met:
134b88c807SRodney W. Grimes * 1. Redistributions of source code must retain the above copyright
144b88c807SRodney W. Grimes * notice, this list of conditions and the following disclaimer.
154b88c807SRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright
164b88c807SRodney W. Grimes * notice, this list of conditions and the following disclaimer in the
174b88c807SRodney W. Grimes * documentation and/or other materials provided with the distribution.
18fbbd9655SWarner Losh * 3. Neither the name of the University nor the names of its contributors
194b88c807SRodney W. Grimes * may be used to endorse or promote products derived from this software
204b88c807SRodney W. Grimes * without specific prior written permission.
214b88c807SRodney W. Grimes *
224b88c807SRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
234b88c807SRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
244b88c807SRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
254b88c807SRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
264b88c807SRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
274b88c807SRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
284b88c807SRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
294b88c807SRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
304b88c807SRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
314b88c807SRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
324b88c807SRodney W. Grimes * SUCH DAMAGE.
334b88c807SRodney W. Grimes */
344b88c807SRodney W. Grimes
35aa9caaf6SPeter Wemm #include <sys/types.h>
361a958c66STim J. Robbins #include <sys/stat.h>
37aa9caaf6SPeter Wemm #include <signal.h>
38aa9caaf6SPeter Wemm #include <string.h>
39aa9caaf6SPeter Wemm #include <fcntl.h>
40aa9caaf6SPeter Wemm #include <errno.h>
41aa9caaf6SPeter Wemm #include <unistd.h>
42aa9caaf6SPeter Wemm #include <stdlib.h>
43aa9caaf6SPeter Wemm
444b88c807SRodney W. Grimes /*
454b88c807SRodney W. Grimes * Code for dealing with input/output redirection.
464b88c807SRodney W. Grimes */
474b88c807SRodney W. Grimes
484b88c807SRodney W. Grimes #include "shell.h"
494b88c807SRodney W. Grimes #include "nodes.h"
504b88c807SRodney W. Grimes #include "jobs.h"
514b88c807SRodney W. Grimes #include "expand.h"
524b88c807SRodney W. Grimes #include "redir.h"
534b88c807SRodney W. Grimes #include "output.h"
544b88c807SRodney W. Grimes #include "memalloc.h"
554b88c807SRodney W. Grimes #include "error.h"
561a958c66STim J. Robbins #include "options.h"
574b88c807SRodney W. Grimes
584b88c807SRodney W. Grimes
594b88c807SRodney W. Grimes #define EMPTY -2 /* marks an unused slot in redirtab */
60e1ef3141SJilles Tjoelker #define CLOSED -1 /* fd was not open before redir */
614b88c807SRodney W. Grimes
624b88c807SRodney W. Grimes
634b88c807SRodney W. Grimes struct redirtab {
644b88c807SRodney W. Grimes struct redirtab *next;
655b99fa05STim J. Robbins int renamed[10];
661b57cec7SJilles Tjoelker int fd0_redirected;
67bd9b38d1SJilles Tjoelker unsigned int empty_redirs;
684b88c807SRodney W. Grimes };
694b88c807SRodney W. Grimes
704b88c807SRodney W. Grimes
710bdd3871SJilles Tjoelker static struct redirtab *redirlist;
724b88c807SRodney W. Grimes
734b88c807SRodney W. Grimes /*
744b88c807SRodney W. Grimes * We keep track of whether or not fd0 has been redirected. This is for
754b88c807SRodney W. Grimes * background commands, where we want to redirect fd0 to /dev/null only
764b88c807SRodney W. Grimes * if it hasn't already been redirected.
774b88c807SRodney W. Grimes */
78aa7b6f82SDavid E. O'Brien static int fd0_redirected = 0;
794b88c807SRodney W. Grimes
80bd9b38d1SJilles Tjoelker /* Number of redirtabs that have not been allocated. */
81bd9b38d1SJilles Tjoelker static unsigned int empty_redirs = 0;
82bd9b38d1SJilles Tjoelker
8388328642SDavid E. O'Brien static void openredirect(union node *, char[10 ]);
8488328642SDavid E. O'Brien static int openhere(union node *);
854b88c807SRodney W. Grimes
864b88c807SRodney W. Grimes
874b88c807SRodney W. Grimes /*
884b88c807SRodney W. Grimes * Process a list of redirection commands. If the REDIR_PUSH flag is set,
894b88c807SRodney W. Grimes * old file descriptors are stashed away so that the redirection can be
904b88c807SRodney W. Grimes * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
914b88c807SRodney W. Grimes * standard output, and the standard error if it becomes a duplicate of
924b88c807SRodney W. Grimes * stdout, is saved in memory.
931632bf1aSJilles Tjoelker *
941632bf1aSJilles Tjoelker * We suppress interrupts so that we won't leave open file
951632bf1aSJilles Tjoelker * descriptors around. Because the signal handler remains
961632bf1aSJilles Tjoelker * installed and we do not use system call restart, interrupts
971632bf1aSJilles Tjoelker * will still abort blocking opens such as fifos (they will fail
981632bf1aSJilles Tjoelker * with EINTR). There is, however, a race condition if an interrupt
991632bf1aSJilles Tjoelker * arrives after INTOFF and before open blocks.
1004b88c807SRodney W. Grimes */
1014b88c807SRodney W. Grimes
1024b88c807SRodney W. Grimes void
redirect(union node * redir,int flags)1035134c3f7SWarner Losh redirect(union node *redir, int flags)
1044b88c807SRodney W. Grimes {
1054b88c807SRodney W. Grimes union node *n;
106aa9caaf6SPeter Wemm struct redirtab *sv = NULL;
1074b88c807SRodney W. Grimes int i;
1084b88c807SRodney W. Grimes int fd;
1094b88c807SRodney W. Grimes char memory[10]; /* file descriptors to write to memory */
1104b88c807SRodney W. Grimes
1111632bf1aSJilles Tjoelker INTOFF;
1124b88c807SRodney W. Grimes for (i = 10 ; --i >= 0 ; )
1134b88c807SRodney W. Grimes memory[i] = 0;
1144b88c807SRodney W. Grimes memory[1] = flags & REDIR_BACKQ;
1154b88c807SRodney W. Grimes if (flags & REDIR_PUSH) {
116bd9b38d1SJilles Tjoelker empty_redirs++;
117bd9b38d1SJilles Tjoelker if (redir != NULL) {
1184b88c807SRodney W. Grimes sv = ckmalloc(sizeof (struct redirtab));
1194b88c807SRodney W. Grimes for (i = 0 ; i < 10 ; i++)
1204b88c807SRodney W. Grimes sv->renamed[i] = EMPTY;
1211b57cec7SJilles Tjoelker sv->fd0_redirected = fd0_redirected;
122bd9b38d1SJilles Tjoelker sv->empty_redirs = empty_redirs - 1;
1234b88c807SRodney W. Grimes sv->next = redirlist;
1244b88c807SRodney W. Grimes redirlist = sv;
125bd9b38d1SJilles Tjoelker empty_redirs = 0;
126bd9b38d1SJilles Tjoelker }
1274b88c807SRodney W. Grimes }
1284b88c807SRodney W. Grimes for (n = redir ; n ; n = n->nfile.next) {
1294b88c807SRodney W. Grimes fd = n->nfile.fd;
1301b57cec7SJilles Tjoelker if (fd == 0)
1311b57cec7SJilles Tjoelker fd0_redirected = 1;
132769bbc65SJoerg Wunsch if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD) &&
133769bbc65SJoerg Wunsch n->ndup.dupfd == fd)
134ab0a2172SSteve Price continue; /* redirect from/to same file descriptor */
13579f56947SSteve Price
1364b88c807SRodney W. Grimes if ((flags & REDIR_PUSH) && sv->renamed[fd] == EMPTY) {
1374b88c807SRodney W. Grimes INTOFF;
1385aa6dfdaSJilles Tjoelker if ((i = fcntl(fd, F_DUPFD_CLOEXEC, 10)) == -1) {
13979f56947SSteve Price switch (errno) {
14079f56947SSteve Price case EBADF:
141e1ef3141SJilles Tjoelker i = CLOSED;
142e1ef3141SJilles Tjoelker break;
14379f56947SSteve Price default:
14479f56947SSteve Price INTON;
14579f56947SSteve Price error("%d: %s", fd, strerror(errno));
14679f56947SSteve Price break;
14779f56947SSteve Price }
1485aa6dfdaSJilles Tjoelker }
1494b88c807SRodney W. Grimes sv->renamed[fd] = i;
1504b88c807SRodney W. Grimes INTON;
1514b88c807SRodney W. Grimes }
1524b88c807SRodney W. Grimes openredirect(n, memory);
1531632bf1aSJilles Tjoelker INTON;
1541632bf1aSJilles Tjoelker INTOFF;
1554b88c807SRodney W. Grimes }
1564b88c807SRodney W. Grimes if (memory[1])
1574b88c807SRodney W. Grimes out1 = &memout;
1584b88c807SRodney W. Grimes if (memory[2])
1594b88c807SRodney W. Grimes out2 = &memout;
1601632bf1aSJilles Tjoelker INTON;
1614b88c807SRodney W. Grimes }
1624b88c807SRodney W. Grimes
1634b88c807SRodney W. Grimes
16488328642SDavid E. O'Brien static void
openredirect(union node * redir,char memory[10])1655134c3f7SWarner Losh openredirect(union node *redir, char memory[10])
1664b88c807SRodney W. Grimes {
1671a958c66STim J. Robbins struct stat sb;
1684b88c807SRodney W. Grimes int fd = redir->nfile.fd;
16961346cbdSJilles Tjoelker const char *fname;
1704b88c807SRodney W. Grimes int f;
17109683f46SJilles Tjoelker int e;
1724b88c807SRodney W. Grimes
1734b88c807SRodney W. Grimes memory[fd] = 0;
1744b88c807SRodney W. Grimes switch (redir->nfile.type) {
1754b88c807SRodney W. Grimes case NFROM:
1764b88c807SRodney W. Grimes fname = redir->nfile.expfname;
1774b88c807SRodney W. Grimes if ((f = open(fname, O_RDONLY)) < 0)
1781c59560dSTim J. Robbins error("cannot open %s: %s", fname, strerror(errno));
1794b88c807SRodney W. Grimes break;
1804682f420SBrian Somers case NFROMTO:
1814682f420SBrian Somers fname = redir->nfile.expfname;
1824682f420SBrian Somers if ((f = open(fname, O_RDWR|O_CREAT, 0666)) < 0)
1831c59560dSTim J. Robbins error("cannot create %s: %s", fname, strerror(errno));
18433c5acf0SJilles Tjoelker break;
1854b88c807SRodney W. Grimes case NTO:
186deb090cbSJilles Tjoelker if (Cflag) {
1874b88c807SRodney W. Grimes fname = redir->nfile.expfname;
188deb090cbSJilles Tjoelker if (stat(fname, &sb) == -1) {
189deb090cbSJilles Tjoelker if ((f = open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666)) < 0)
190deb090cbSJilles Tjoelker error("cannot create %s: %s", fname, strerror(errno));
191deb090cbSJilles Tjoelker } else if (!S_ISREG(sb.st_mode)) {
192deb090cbSJilles Tjoelker if ((f = open(fname, O_WRONLY, 0666)) < 0)
193deb090cbSJilles Tjoelker error("cannot create %s: %s", fname, strerror(errno));
194deb090cbSJilles Tjoelker if (fstat(f, &sb) != -1 && S_ISREG(sb.st_mode)) {
195deb090cbSJilles Tjoelker close(f);
1961a958c66STim J. Robbins error("cannot create %s: %s", fname,
1971c59560dSTim J. Robbins strerror(EEXIST));
198deb090cbSJilles Tjoelker }
199deb090cbSJilles Tjoelker } else
200deb090cbSJilles Tjoelker error("cannot create %s: %s", fname,
201deb090cbSJilles Tjoelker strerror(EEXIST));
20233c5acf0SJilles Tjoelker break;
203deb090cbSJilles Tjoelker }
204deb090cbSJilles Tjoelker /* FALLTHROUGH */
2051a958c66STim J. Robbins case NCLOBBER:
2061a958c66STim J. Robbins fname = redir->nfile.expfname;
2071a958c66STim J. Robbins if ((f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
2081c59560dSTim J. Robbins error("cannot create %s: %s", fname, strerror(errno));
20933c5acf0SJilles Tjoelker break;
2104b88c807SRodney W. Grimes case NAPPEND:
2114b88c807SRodney W. Grimes fname = redir->nfile.expfname;
2124b88c807SRodney W. Grimes if ((f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0)
2131c59560dSTim J. Robbins error("cannot create %s: %s", fname, strerror(errno));
21433c5acf0SJilles Tjoelker break;
2154b88c807SRodney W. Grimes case NTOFD:
2164b88c807SRodney W. Grimes case NFROMFD:
2174b88c807SRodney W. Grimes if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
2184b88c807SRodney W. Grimes if (memory[redir->ndup.dupfd])
2194b88c807SRodney W. Grimes memory[fd] = 1;
2203dec7d0cSJilles Tjoelker else {
2213dec7d0cSJilles Tjoelker if (dup2(redir->ndup.dupfd, fd) < 0)
2223dec7d0cSJilles Tjoelker error("%d: %s", redir->ndup.dupfd,
2233dec7d0cSJilles Tjoelker strerror(errno));
2243dec7d0cSJilles Tjoelker }
2257e1c7266SDag-Erling Smørgrav } else {
22663f901efSSergey Babkin close(fd);
2274b88c807SRodney W. Grimes }
22833c5acf0SJilles Tjoelker return;
2294b88c807SRodney W. Grimes case NHERE:
2304b88c807SRodney W. Grimes case NXHERE:
2314b88c807SRodney W. Grimes f = openhere(redir);
23233c5acf0SJilles Tjoelker break;
2334b88c807SRodney W. Grimes default:
2344b88c807SRodney W. Grimes abort();
2354b88c807SRodney W. Grimes }
23633c5acf0SJilles Tjoelker if (f != fd) {
23733c5acf0SJilles Tjoelker if (dup2(f, fd) == -1) {
23833c5acf0SJilles Tjoelker e = errno;
23933c5acf0SJilles Tjoelker close(f);
24033c5acf0SJilles Tjoelker error("%d: %s", fd, strerror(e));
24133c5acf0SJilles Tjoelker }
24233c5acf0SJilles Tjoelker close(f);
24333c5acf0SJilles Tjoelker }
2444b88c807SRodney W. Grimes }
2454b88c807SRodney W. Grimes
2464b88c807SRodney W. Grimes
2474b88c807SRodney W. Grimes /*
2484b88c807SRodney W. Grimes * Handle here documents. Normally we fork off a process to write the
2494b88c807SRodney W. Grimes * data to a pipe. If the document is short, we can stuff the data in
2504b88c807SRodney W. Grimes * the pipe without forking.
2514b88c807SRodney W. Grimes */
2524b88c807SRodney W. Grimes
25388328642SDavid E. O'Brien static int
openhere(union node * redir)2545134c3f7SWarner Losh openhere(union node *redir)
2554b88c807SRodney W. Grimes {
25661346cbdSJilles Tjoelker const char *p;
2574b88c807SRodney W. Grimes int pip[2];
258c6a453a4SJilles Tjoelker size_t len = 0;
259c6a453a4SJilles Tjoelker int flags;
260c6a453a4SJilles Tjoelker ssize_t written = 0;
2614b88c807SRodney W. Grimes
2624b88c807SRodney W. Grimes if (pipe(pip) < 0)
2636c48b6cfSMartin Cracauer error("Pipe call failed: %s", strerror(errno));
2644dc6bdd3SJilles Tjoelker
2654dc6bdd3SJilles Tjoelker if (redir->type == NXHERE)
2664dc6bdd3SJilles Tjoelker p = redir->nhere.expdoc;
2674dc6bdd3SJilles Tjoelker else
2684dc6bdd3SJilles Tjoelker p = redir->nhere.doc->narg.text;
2694dc6bdd3SJilles Tjoelker len = strlen(p);
270c6a453a4SJilles Tjoelker if (len == 0)
2714b88c807SRodney W. Grimes goto out;
272c6a453a4SJilles Tjoelker flags = fcntl(pip[1], F_GETFL, 0);
273c6a453a4SJilles Tjoelker if (flags != -1 && fcntl(pip[1], F_SETFL, flags | O_NONBLOCK) != -1) {
274c6a453a4SJilles Tjoelker written = write(pip[1], p, len);
275c6a453a4SJilles Tjoelker if (written < 0)
276c6a453a4SJilles Tjoelker written = 0;
277c6a453a4SJilles Tjoelker if ((size_t)written == len)
278c6a453a4SJilles Tjoelker goto out;
279c6a453a4SJilles Tjoelker fcntl(pip[1], F_SETFL, flags);
2804b88c807SRodney W. Grimes }
2814dc6bdd3SJilles Tjoelker
2824b88c807SRodney W. Grimes if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
2834b88c807SRodney W. Grimes close(pip[0]);
2844b88c807SRodney W. Grimes signal(SIGINT, SIG_IGN);
2854b88c807SRodney W. Grimes signal(SIGQUIT, SIG_IGN);
2864b88c807SRodney W. Grimes signal(SIGHUP, SIG_IGN);
2874b88c807SRodney W. Grimes signal(SIGTSTP, SIG_IGN);
2884b88c807SRodney W. Grimes signal(SIGPIPE, SIG_DFL);
289c6a453a4SJilles Tjoelker xwrite(pip[1], p + written, len - written);
2904b88c807SRodney W. Grimes _exit(0);
2914b88c807SRodney W. Grimes }
2924b88c807SRodney W. Grimes out:
2934b88c807SRodney W. Grimes close(pip[1]);
2944b88c807SRodney W. Grimes return pip[0];
2954b88c807SRodney W. Grimes }
2964b88c807SRodney W. Grimes
2974b88c807SRodney W. Grimes
2984b88c807SRodney W. Grimes
2994b88c807SRodney W. Grimes /*
3004b88c807SRodney W. Grimes * Undo the effects of the last redirection.
3014b88c807SRodney W. Grimes */
3024b88c807SRodney W. Grimes
3034b88c807SRodney W. Grimes void
popredir(void)3045134c3f7SWarner Losh popredir(void)
3055134c3f7SWarner Losh {
30679f56947SSteve Price struct redirtab *rp = redirlist;
3074b88c807SRodney W. Grimes int i;
3084b88c807SRodney W. Grimes
309bd9b38d1SJilles Tjoelker INTOFF;
310bd9b38d1SJilles Tjoelker if (empty_redirs > 0) {
311bd9b38d1SJilles Tjoelker empty_redirs--;
312bd9b38d1SJilles Tjoelker INTON;
313bd9b38d1SJilles Tjoelker return;
314bd9b38d1SJilles Tjoelker }
3154b88c807SRodney W. Grimes for (i = 0 ; i < 10 ; i++) {
3164b88c807SRodney W. Grimes if (rp->renamed[i] != EMPTY) {
3174b88c807SRodney W. Grimes if (rp->renamed[i] >= 0) {
3187e1c7266SDag-Erling Smørgrav dup2(rp->renamed[i], i);
3194b88c807SRodney W. Grimes close(rp->renamed[i]);
3207e1c7266SDag-Erling Smørgrav } else {
3217e1c7266SDag-Erling Smørgrav close(i);
3224b88c807SRodney W. Grimes }
3234b88c807SRodney W. Grimes }
3244b88c807SRodney W. Grimes }
3251b57cec7SJilles Tjoelker fd0_redirected = rp->fd0_redirected;
326bd9b38d1SJilles Tjoelker empty_redirs = rp->empty_redirs;
3274b88c807SRodney W. Grimes redirlist = rp->next;
3284b88c807SRodney W. Grimes ckfree(rp);
3294b88c807SRodney W. Grimes INTON;
3304b88c807SRodney W. Grimes }
3314b88c807SRodney W. Grimes
3324b88c807SRodney W. Grimes /* Return true if fd 0 has already been redirected at least once. */
3334b88c807SRodney W. Grimes int
fd0_redirected_p(void)3345134c3f7SWarner Losh fd0_redirected_p(void)
3355134c3f7SWarner Losh {
3364b88c807SRodney W. Grimes return fd0_redirected != 0;
3374b88c807SRodney W. Grimes }
3384b88c807SRodney W. Grimes
3394b88c807SRodney W. Grimes /*
3404b88c807SRodney W. Grimes * Discard all saved file descriptors.
3414b88c807SRodney W. Grimes */
3424b88c807SRodney W. Grimes
3434b88c807SRodney W. Grimes void
clearredir(void)3445134c3f7SWarner Losh clearredir(void)
3455134c3f7SWarner Losh {
34679f56947SSteve Price struct redirtab *rp;
3474b88c807SRodney W. Grimes int i;
3484b88c807SRodney W. Grimes
3494b88c807SRodney W. Grimes for (rp = redirlist ; rp ; rp = rp->next) {
3504b88c807SRodney W. Grimes for (i = 0 ; i < 10 ; i++) {
3514b88c807SRodney W. Grimes if (rp->renamed[i] >= 0) {
3524b88c807SRodney W. Grimes close(rp->renamed[i]);
3534b88c807SRodney W. Grimes }
3544b88c807SRodney W. Grimes rp->renamed[i] = EMPTY;
3554b88c807SRodney W. Grimes }
3564b88c807SRodney W. Grimes }
3574b88c807SRodney W. Grimes }
358