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