xref: /haiku/src/kits/shared/CommandPipe.cpp (revision 3e50787c0eec50aea264d4f076d72aa73c0895b4)
1 /*
2  * Copyright 2007 Haiku, Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Ramshankar, v.ramshankar@gmail.com
7  *		Stephan Aßmus <superstippi@gmx.de>
8  */
9 
10 //! BCommandPipe class to handle reading shell output
11 //  (stdout/stderr) of other programs into memory.
12 #include "CommandPipe.h"
13 
14 #include <stdlib.h>
15 #include <unistd.h>
16 
17 #include <image.h>
18 #include <Message.h>
19 #include <Messenger.h>
20 #include <String.h>
21 
22 
BCommandPipe()23 BCommandPipe::BCommandPipe()
24 	:
25 	fStdOutOpen(false),
26 	fStdErrOpen(false)
27 {
28 }
29 
30 
~BCommandPipe()31 BCommandPipe::~BCommandPipe()
32 {
33 	FlushArgs();
34 }
35 
36 
37 status_t
AddArg(const char * arg)38 BCommandPipe::AddArg(const char* arg)
39 {
40 	if (arg == NULL || arg[0] == '\0')
41 		return B_BAD_VALUE;
42 
43 	char* argCopy = strdup(arg);
44 	if (argCopy == NULL)
45 		return B_NO_MEMORY;
46 
47 	if (!fArgList.AddItem(reinterpret_cast<void*>(argCopy))) {
48 		free(argCopy);
49 		return B_NO_MEMORY;
50 	}
51 
52 	return B_OK;
53 }
54 
55 
56 void
PrintToStream() const57 BCommandPipe::PrintToStream() const
58 {
59 	for (int32 i = 0L; i < fArgList.CountItems(); i++)
60 		printf("%s ", reinterpret_cast<char*>(fArgList.ItemAtFast(i)));
61 
62 	printf("\n");
63 }
64 
65 
66 void
FlushArgs()67 BCommandPipe::FlushArgs()
68 {
69 	// Delete all arguments from the list
70 	for (int32 i = fArgList.CountItems() - 1; i >= 0; i--)
71 		free(fArgList.ItemAtFast(i));
72 	fArgList.MakeEmpty();
73 
74 	Close();
75 }
76 
77 
78 void
Close()79 BCommandPipe::Close()
80 {
81 	if (fStdErrOpen) {
82 		close(fStdErr[0]);
83 		fStdErrOpen = false;
84 	}
85 
86 	if (fStdOutOpen) {
87 		close(fStdOut[0]);
88 		fStdOutOpen = false;
89 	}
90 }
91 
92 
93 const char**
Argv(int32 & argc) const94 BCommandPipe::Argv(int32& argc) const
95 {
96 	// NOTE: Freeing is left to caller as indicated in the header!
97 	argc = fArgList.CountItems();
98 	const char** argv = reinterpret_cast<const char**>(
99 		malloc((argc + 1) * sizeof(char*)));
100 	for (int32 i = 0; i < argc; i++)
101 		argv[i] = reinterpret_cast<const char*>(fArgList.ItemAtFast(i));
102 
103 	argv[argc] = NULL;
104 	return argv;
105 }
106 
107 
108 // #pragma mark -
109 
110 
111 thread_id
PipeAll(int * stdOutAndErr) const112 BCommandPipe::PipeAll(int* stdOutAndErr) const
113 {
114 	// This function pipes both stdout and stderr to the same filedescriptor
115 	// (stdOut)
116 	int oldStdOut;
117 	int oldStdErr;
118 	pipe(stdOutAndErr);
119 	oldStdOut = dup(STDOUT_FILENO);
120 	oldStdErr = dup(STDERR_FILENO);
121 	close(STDOUT_FILENO);
122 	close(STDERR_FILENO);
123 	// TODO: This looks broken, using "stdOutAndErr[1]" twice!
124 	dup2(stdOutAndErr[1], STDOUT_FILENO);
125 	dup2(stdOutAndErr[1], STDERR_FILENO);
126 
127 	// Construct the argv vector
128 	int32 argc;
129 	const char** argv = Argv(argc);
130 
131 	// Load the app image... and pass the args
132 	thread_id appThread = load_image((int)argc, argv,
133 		const_cast<const char**>(environ));
134 
135 	dup2(oldStdOut, STDOUT_FILENO);
136 	dup2(oldStdErr, STDERR_FILENO);
137 	close(oldStdOut);
138 	close(oldStdErr);
139 
140 	free(argv);
141 
142 	return appThread;
143 }
144 
145 
146 thread_id
Pipe(int * stdOut,int * stdErr) const147 BCommandPipe::Pipe(int* stdOut, int* stdErr) const
148 {
149 	int oldStdOut;
150 	int oldStdErr;
151 	pipe(stdOut);
152 	pipe(stdErr);
153 	oldStdOut = dup(STDOUT_FILENO);
154 	oldStdErr = dup(STDERR_FILENO);
155 	close(STDOUT_FILENO);
156 	close(STDERR_FILENO);
157 	dup2(stdOut[1], STDOUT_FILENO);
158 	dup2(stdErr[1], STDERR_FILENO);
159 
160 	// Construct the argv vector
161 	int32 argc;
162 	const char** argv = Argv(argc);
163 
164 	// Load the app image... and pass the args
165 	thread_id appThread = load_image((int)argc, argv, const_cast<
166 		const char**>(environ));
167 
168 	dup2(oldStdOut, STDOUT_FILENO);
169 	dup2(oldStdErr, STDERR_FILENO);
170 	close(oldStdOut);
171 	close(oldStdErr);
172 
173 	free(argv);
174 
175 	return appThread;
176 }
177 
178 
179 thread_id
Pipe(int * stdOut) const180 BCommandPipe::Pipe(int* stdOut) const
181 {
182 	// Redirects only output (stdout) to caller, stderr is closed
183 	int stdErr[2];
184 	thread_id tid = Pipe(stdOut, stdErr);
185 	close(stdErr[0]);
186 	close(stdErr[1]);
187 	return tid;
188 }
189 
190 
191 thread_id
PipeInto(FILE ** _out,FILE ** _err)192 BCommandPipe::PipeInto(FILE** _out, FILE** _err)
193 {
194 	Close();
195 
196 	thread_id tid = Pipe(fStdOut, fStdErr);
197 	if (tid >= 0)
198 		resume_thread(tid);
199 
200 	close(fStdErr[1]);
201 	close(fStdOut[1]);
202 
203 	fStdOutOpen = true;
204 	fStdErrOpen = true;
205 
206 	*_out = fdopen(fStdOut[0], "r");
207 	*_err = fdopen(fStdErr[0], "r");
208 
209 	return tid;
210 }
211 
212 
213 thread_id
PipeInto(FILE ** _outAndErr)214 BCommandPipe::PipeInto(FILE** _outAndErr)
215 {
216 	Close();
217 
218 	thread_id tid = PipeAll(fStdOut);
219 	if (tid >= 0)
220 		resume_thread(tid);
221 
222 	close(fStdOut[1]);
223 	fStdOutOpen = true;
224 
225 	*_outAndErr = fdopen(fStdOut[0], "r");
226 	return tid;
227 }
228 
229 
230 // #pragma mark -
231 
232 
233 void
Run()234 BCommandPipe::Run()
235 {
236 	// Runs the command without bothering to redirect streams, this is similar
237 	// to system() but uses pipes and wait_for_thread.... Synchronous.
238 	int stdOut[2], stdErr[2];
239 	status_t exitCode;
240 	wait_for_thread(Pipe(stdOut, stdErr), &exitCode);
241 
242 	close(stdOut[0]);
243 	close(stdErr[0]);
244 	close(stdOut[1]);
245 	close(stdErr[1]);
246 }
247 
248 
249 void
RunAsync()250 BCommandPipe::RunAsync()
251 {
252 	// Runs the command without bothering to redirect streams, this is similar
253 	// to system() but uses pipes.... Asynchronous.
254 	Close();
255 	FILE* f = NULL;
256 	PipeInto(&f);
257 	fclose(f);
258 }
259 
260 
261 // #pragma mark -
262 
263 
264 status_t
ReadLines(FILE * file,LineReader * lineReader)265 BCommandPipe::ReadLines(FILE* file, LineReader* lineReader)
266 {
267 	// Reads output of file, line by line. Each line is passed to lineReader
268 	// for inspection, and the IsCanceled() method is repeatedly called.
269 
270 	if (file == NULL || lineReader == NULL)
271 		return B_BAD_VALUE;
272 
273 	BString line;
274 
275 	while (!feof(file)) {
276 		if (lineReader->IsCanceled())
277 			return B_CANCELED;
278 
279 		unsigned char c = fgetc(file);
280 			// TODO: fgetc() blocks, is there a way to make it timeout?
281 
282 		if (c != 255)
283 			line << (char)c;
284 
285 		if (c == '\n') {
286 			status_t ret = lineReader->ReadLine(line);
287 			if (ret != B_OK)
288 				return ret;
289 			line = "";
290 		}
291 	}
292 
293 	return B_OK;
294 }
295 
296 
297 BString
ReadLines(FILE * file)298 BCommandPipe::ReadLines(FILE* file)
299 {
300 	class AllLinesReader : public LineReader {
301 	public:
302 		AllLinesReader()
303 			:
304 			fResult("")
305 		{
306 		}
307 
308 		virtual bool IsCanceled()
309 		{
310 			return false;
311 		}
312 
313 		virtual status_t ReadLine(const BString& line)
314 		{
315 			int lineLength = line.Length();
316 			int resultLength = fResult.Length();
317 			fResult << line;
318 			if (fResult.Length() != lineLength + resultLength)
319 				return B_NO_MEMORY;
320 			return B_OK;
321 		}
322 
323 		BString Result() const
324 		{
325 			return fResult;
326 		}
327 
328 	private:
329 		BString fResult;
330 	} lineReader;
331 
332 	ReadLines(file, &lineReader);
333 
334 	return lineReader.Result();
335 }
336 
337 
338 // #pragma mark -
339 
340 
341 BCommandPipe&
operator <<(const char * arg)342 BCommandPipe::operator<<(const char* arg)
343 {
344 	AddArg(arg);
345 	return *this;
346 }
347 
348 
349 BCommandPipe&
operator <<(const BString & arg)350 BCommandPipe::operator<<(const BString& arg)
351 {
352 	AddArg(arg.String());
353 	return *this;
354 }
355 
356 
357 BCommandPipe&
operator <<(const BCommandPipe & arg)358 BCommandPipe::operator<<(const BCommandPipe& arg)
359 {
360 	int32 argc;
361 	const char** argv = arg.Argv(argc);
362 	for (int32 i = 0; i < argc; i++)
363 		AddArg(argv[i]);
364 
365 	free(argv);
366 	return *this;
367 }
368 
369