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
23BCommandPipe::BCommandPipe()
24	:
25	fStdOutOpen(false),
26	fStdErrOpen(false)
27{
28}
29
30
31BCommandPipe::~BCommandPipe()
32{
33	FlushArgs();
34}
35
36
37status_t
38BCommandPipe::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
56void
57BCommandPipe::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
66void
67BCommandPipe::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
78void
79BCommandPipe::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
93const char**
94BCommandPipe::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
111thread_id
112BCommandPipe::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
146thread_id
147BCommandPipe::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
179thread_id
180BCommandPipe::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
191thread_id
192BCommandPipe::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
213thread_id
214BCommandPipe::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
233void
234BCommandPipe::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
249void
250BCommandPipe::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
264status_t
265BCommandPipe::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
297BString
298BCommandPipe::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
341BCommandPipe&
342BCommandPipe::operator<<(const char* arg)
343{
344	AddArg(arg);
345	return *this;
346}
347
348
349BCommandPipe&
350BCommandPipe::operator<<(const BString& arg)
351{
352	AddArg(arg.String());
353	return *this;
354}
355
356
357BCommandPipe&
358BCommandPipe::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