1 /*
2  * Copyright (c) 2012 Tim Ruehsen
3  * Copyright (c) 2015-2021 Free Software Foundation, Inc.
4  *
5  * This file is part of libwget.
6  *
7  * Libwget is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU Lesser General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * Libwget is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with libwget.  If not, see <https://www.gnu.org/licenses/>.
19  *
20  *
21  * a collection of pipe/popen routines
22  *
23  * Changelog
24  * 25.04.2012  Tim Ruehsen  created
25  *
26  */
27 
28 #include <config.h>
29 
30 #ifndef _WIN32
31 
32 #include <stddef.h>
33 #include <stdio.h>
34 #include <unistd.h>
35 #include <errno.h>
36 
37 #include <wget.h>
38 #include "private.h"
39 
40 
wget_vpopenf(const char * type,const char * fmt,va_list args)41 FILE *wget_vpopenf(const char *type, const char *fmt, va_list args)
42 {
43 	FILE *fp = NULL;
44 	char sbuf[1024];
45 	wget_buffer buf;
46 
47 	if (!type || !fmt)
48 		return NULL;
49 
50 	wget_buffer_init(&buf, sbuf, sizeof(sbuf));
51 	wget_buffer_vprintf(&buf, fmt, args);
52 
53 	fp = popen(buf.data, type);
54 
55 	wget_buffer_deinit(&buf);
56 
57 	return fp;
58 }
59 
wget_popenf(const char * type,const char * fmt,...)60 FILE *wget_popenf(const char *type, const char *fmt, ...)
61 {
62 	FILE *fp;
63 	va_list args;
64 
65 	va_start(args, fmt);
66 	fp = wget_vpopenf(type, fmt, args);
67 	va_end(args);
68 
69 	return fp;
70 }
71 
wget_fd_popen3(int * fdin,int * fdout,int * fderr,const char * const * argv)72 pid_t wget_fd_popen3(int *fdin, int *fdout, int *fderr, const char *const *argv)
73 {
74 	int pipefd_in[2]; // child's STDIN
75 	int pipefd_out[2]; // child's STDOUT
76 	int pipefd_err[2]; // child's STDERR
77 	pid_t pid;
78 
79 	if (!argv)
80 		return -1;
81 
82 	// create a pipe. the child writes into it and the parent read from it.
83 	// pipefd[0]=reader pipefd[1]=writer
84 	if (fdin && pipe(pipefd_in) == -1) {
85 		error_printf(_("Failed to create pipe for STDIN on %s\n"), argv[0]);
86 		return -1;
87 	}
88 	if (fdout && pipe(pipefd_out) == -1) {
89 		error_printf(_("Failed to create pipe for STDOUT on %s\n"), argv[0]);
90 		if (fdin) {
91 			close(pipefd_in[0]);
92 			close(pipefd_in[1]);
93 		}
94 		return -1;
95 	}
96 	if (fderr && fderr != fdout && pipe(pipefd_err) == -1) {
97 		error_printf(_("Failed to create pipe for STDERR on %s\n"), argv[0]);
98 		if (fdin) {
99 			close(pipefd_in[0]);
100 			close(pipefd_in[1]);
101 		}
102 		if (fdout) {
103 			close(pipefd_out[0]);
104 			close(pipefd_out[1]);
105 		}
106 		return -1;
107 	}
108 
109 	if ((pid = fork()) == 0) {
110 		if (fdin) {
111 			close(pipefd_in[1]); // the STDIN writer is not needed by the child
112 
113 			// redirect STDIN to reader
114 			if (dup2(pipefd_in[0], STDIN_FILENO) == -1)
115 				error_printf_exit(_("Failed to dup2(%d,%d) (%d)\n"), pipefd_in[0], STDIN_FILENO, errno);
116 
117 			close(pipefd_in[0]); // the old STDIN reader is not needed any more
118 		}
119 
120 		if (fdout) {
121 			close(pipefd_out[0]); // the STDOUT reader is not needed by the child
122 
123 			// redirect STDOUT to writer
124 			if (dup2(pipefd_out[1], STDOUT_FILENO) == -1)
125 				error_printf_exit(_("Failed to dup2(%d,%d) (%d)\n"), pipefd_out[1], STDOUT_FILENO, errno);
126 
127 			close(pipefd_out[1]); // the old STDOUT writer is not needed any more
128 		}
129 
130 		if (fderr) {
131 			if (fderr != fdout) {
132 				close(pipefd_err[0]); // the STDERR reader is not needed by the child
133 
134 				// redirect STDERR to writer
135 				if (dup2(pipefd_err[1], STDERR_FILENO) == -1)
136 					error_printf_exit(_("Failed to dup2(%d,%d) (%d)\n"), pipefd_err[1], STDERR_FILENO, errno);
137 
138 				close(pipefd_err[1]); // the old STDERR writer is not needed any more
139 			} else {
140 				// redirect STDERR to STDOUT
141 				if (dup2(STDOUT_FILENO, STDERR_FILENO) == -1)
142 					exit(EXIT_FAILURE);
143 			}
144 		}
145 
146 		execvp(argv[0], (char *const *)argv); // does only return on error
147 		//		error_printf(_("Failed to execute %s (%d)\n"),argv[0],errno);
148 		exit(EXIT_FAILURE);
149 	} else if (pid < 0) {
150 		// fork error
151 		if (fdin) {
152 			close(pipefd_in[0]);
153 			close(pipefd_in[1]);
154 		}
155 		if (fdout) {
156 			close(pipefd_out[0]);
157 			close(pipefd_out[1]);
158 		}
159 		if (fderr && fderr != fdout) {
160 			close(pipefd_err[0]);
161 			close(pipefd_err[1]);
162 		}
163 		error_printf(_("Failed to fork '%s'\n"), argv[0]);
164 		return pid;
165 	}
166 
167 	// parent
168 	if (fdin) {
169 		close(pipefd_in[0]); // the STDIN reader is not needed by the parent
170 		*fdin = pipefd_in[1];
171 	}
172 	if (fdout) {
173 		close(pipefd_out[1]); // the STDOUT writer is not needed by the parent
174 		*fdout = pipefd_out[0];
175 	}
176 	if (fderr && fderr != fdout) {
177 		close(pipefd_err[1]); // the STDERR writer is not needed by the parent
178 		*fderr = pipefd_err[0];
179 	}
180 
181 	return pid;
182 }
183 
184 // extended popen to have control over the child's STDIN, STDOUT and STDERR
185 // NULL to ignore child's STDxxx
186 // if fpout==fperr STDERR will be redirected to STDOUT
187 // fpin: child's stdin (that's where the calling process can write data into)
188 // fpout: child's stdout (that's where the calling process reads data from)
189 // fperr: child's stderr (that's where the calling process reads error messages from)
190 // argv: argument to execvp(), e.g. const char *argv[]={"ls","-la",NULL};
191 
wget_popen3(FILE ** fpin,FILE ** fpout,FILE ** fperr,const char * const * argv)192 pid_t wget_popen3(FILE **fpin, FILE **fpout, FILE **fperr, const char *const *argv)
193 {
194 	int fdin = -1, fdout = -1, fderr = -1;
195 	pid_t pid;
196 
197 	if (fpin) *fpin = NULL;
198 	if (fpout) *fpout = NULL;
199 	if (fperr) *fperr = NULL;
200 
201 	if ((pid = wget_fd_popen3(fpin ? &fdin : NULL, fpout ? &fdout : NULL, fperr ? (fperr != fpout ? &fderr : &fdout) : NULL, argv)) > 0) {
202 		if (fpin) *fpin = fdopen(fdin, "w");
203 		if (fpout) *fpout = fdopen(fdout, "r");
204 		if (fperr && fperr != fpout) *fperr = fdopen(fderr, "r");
205 	}
206 
207 	return pid;
208 }
209 
210 #endif
211