xref: /haiku/src/kits/shared/CommandPipe.cpp (revision cda5b8808fd0262f0fac472f6cfa809f846a83cf)
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  */
8 
9 //! BCommandPipe class to handle reading shell output
10 //  (stdout/stderr) of other programs into memory.
11 #include "CommandPipe.h"
12 
13 #include <stdlib.h>
14 #include <unistd.h>
15 
16 #include <image.h>
17 #include <Message.h>
18 #include <Messenger.h>
19 #include <String.h>
20 
21 
22 BCommandPipe::BCommandPipe()
23 	: fOutDesOpen(false)
24 	, fErrDesOpen(false)
25 {
26 }
27 
28 
29 BCommandPipe::~BCommandPipe()
30 {
31 	FlushArgs();
32 }
33 
34 
35 status_t
36 BCommandPipe::AddArg(const char* arg)
37 {
38 	return (fArgList.AddItem(reinterpret_cast<void*>(strdup(arg))) == true ?
39 		B_OK : B_ERROR);
40 }
41 
42 
43 void
44 BCommandPipe::PrintToStream() const
45 {
46 	for (int32 i = 0L; i < fArgList.CountItems(); i++)
47 		printf("%s ", (char*)fArgList.ItemAtFast(i));
48 
49 	printf("\n");
50 }
51 
52 
53 void
54 BCommandPipe::FlushArgs()
55 {
56 	// Delete all arguments from the list
57 	for(int32 i = 0; i < fArgList.CountItems(); i++)
58 		free(fArgList.RemoveItem(0L));
59 
60 	fArgList.MakeEmpty();
61 	Close();
62 }
63 
64 
65 void
66 BCommandPipe::Close()
67 {
68 	if (fErrDesOpen) {
69 		close(fErrDes[0]);
70 		fErrDesOpen = false;
71 	}
72 
73 	if (fOutDesOpen) {
74 		close(fOutDes[0]);
75 		fOutDesOpen = false;
76 	}
77 }
78 
79 
80 const char**
81 BCommandPipe::Argv(int32& _argc) const
82 {
83 	// *** Warning *** Freeing is left to caller!! Indicated in Header
84 	int32 argc = fArgList.CountItems();
85 	const char **argv = (const char**)malloc((argc + 1) * sizeof(char*));
86 	for (int32 i = 0; i < argc; i++)
87 		argv[i] = (const char*)fArgList.ItemAtFast(i);
88 
89 	argv[argc] = NULL;
90 	_argc = argc;
91 	return argv;
92 }
93 
94 
95 // #pragma mark -
96 
97 
98 thread_id
99 BCommandPipe::PipeAll(int* outAndErrDes) const
100 {
101 	// This function pipes both stdout and stderr to the same filedescriptor
102 	// (outdes)
103 	int oldstdout;
104 	int oldstderr;
105 	pipe(outAndErrDes);
106 	oldstdout = dup(STDOUT_FILENO);
107 	oldstderr = dup(STDERR_FILENO);
108 	close(STDOUT_FILENO);
109 	close(STDERR_FILENO);
110 	dup2(outAndErrDes[1], STDOUT_FILENO);
111 	dup2(outAndErrDes[1], STDERR_FILENO);
112 
113 	// Construct the argv vector
114 	int32 argc = fArgList.CountItems();
115 	const char **argv = (const char**)malloc((argc + 1) * sizeof(char*));
116 	for (int32 i = 0; i < argc; i++)
117 		argv[i] = (const char*)fArgList.ItemAtFast(i);
118 
119 	argv[argc] = NULL;
120 
121 	// Load the app image... and pass the args
122 	thread_id appThread = load_image((int)argc, argv, const_cast<
123 		const char**>(environ));
124 
125 	dup2(oldstdout, STDOUT_FILENO);
126 	dup2(oldstderr, STDERR_FILENO);
127 	close(oldstdout);
128 	close(oldstderr);
129 
130 	delete[] argv;
131 
132 	return appThread;
133 }
134 
135 
136 thread_id
137 BCommandPipe::Pipe(int* outdes, int* errdes) const
138 {
139 	int oldstdout;
140 	int oldstderr;
141 	pipe(outdes);
142 	pipe(errdes);
143 	oldstdout = dup(STDOUT_FILENO);
144 	oldstderr = dup(STDERR_FILENO);
145 	close(STDOUT_FILENO);
146 	close(STDERR_FILENO);
147 	dup2(outdes[1], STDOUT_FILENO);
148 	dup2(errdes[1], STDERR_FILENO);
149 
150 	// Construct the argv vector
151 	int32 argc = fArgList.CountItems();
152 	const char **argv = (const char**)malloc((argc + 1) * sizeof(char*));
153 	for (int32 i = 0; i < argc; i++)
154 		argv[i] = (const char*)fArgList.ItemAtFast(i);
155 
156 	argv[argc] = NULL;
157 
158 	// Load the app image... and pass the args
159 	thread_id appThread = load_image((int)argc, argv, const_cast<
160 		const char**>(environ));
161 
162 	dup2(oldstdout, STDOUT_FILENO);
163 	dup2(oldstderr, STDERR_FILENO);
164 	close(oldstdout);
165 	close(oldstderr);
166 
167 	delete[] argv;
168 
169 	return appThread;
170 }
171 
172 
173 thread_id
174 BCommandPipe::Pipe(int* outdes) const
175 {
176 	// Redirects only output (stdout) to caller, stderr is closed
177 	int errdes[2];
178 	thread_id tid = Pipe(outdes, errdes);
179 	close(errdes[0]);
180 	close(errdes[1]);
181 	return tid;
182 }
183 
184 
185 thread_id
186 BCommandPipe::PipeInto(FILE** _out, FILE** _err)
187 {
188 	Close();
189 	thread_id tid = Pipe(fOutDes, fErrDes);
190 
191 	resume_thread(tid);
192 
193 	close(fErrDes[1]);
194 	close(fOutDes[1]);
195 
196 	fOutDesOpen = true;
197 	fErrDesOpen = true;
198 
199 	*_out = fdopen(fOutDes[0], "r");
200 	*_err = fdopen(fErrDes[0], "r");
201 
202 	return tid;
203 }
204 
205 
206 thread_id
207 BCommandPipe::PipeInto(FILE** _outAndErr)
208 {
209 	Close();
210 	thread_id tid = PipeAll(fOutDes);
211 
212 	if (tid == B_ERROR || tid == B_NO_MEMORY)
213 		return tid;
214 
215 	resume_thread(tid);
216 
217 	close(fOutDes[1]);
218 	fOutDesOpen = true;
219 
220 	*_outAndErr = fdopen(fOutDes[0], "r");
221 	return tid;
222 }
223 
224 
225 // #pragma mark -
226 
227 
228 void
229 BCommandPipe::Run()
230 {
231 	// Runs the command without bothering to redirect streams, this is similar
232 	// to system() but uses pipes and wait_for_thread.... Synchronous.
233 	int outdes[2], errdes[2];
234 	status_t exitCode;
235 	wait_for_thread(Pipe(outdes, errdes), &exitCode);
236 
237 	close(outdes[0]);
238 	close(errdes[0]);
239 	close(outdes[1]);
240 	close(errdes[1]);
241 }
242 
243 
244 void
245 BCommandPipe::RunAsync()
246 {
247 	// Runs the command without bothering to redirect streams, this is similar
248 	// to system() but uses pipes.... Asynchronous.
249 	Close();
250 	FILE* f = NULL;
251 	PipeInto(&f);
252 	fclose(f);
253 }
254 
255 
256 // #pragma mark -
257 
258 
259 BString
260 BCommandPipe::ReadLines(FILE* file, bool* cancel, BMessenger& target,
261 	const BMessage& message, const BString& stringFieldName)
262 {
263 	// Reads output of file, line by line. The entire output is returned
264 	// and as each line is being read "target" (if any) is informed,
265 	// with "message" i.e. AddString (stringFieldName, <line read from pipe>)
266 
267 	// "cancel" cancels the reading process, when it becomes true (unless its
268 	// waiting on fgetc()) and I don't know how to cancel the waiting fgetc()
269 	// call.
270 
271 	BString result;
272 	BString line;
273 	BMessage updateMsg(message);
274 
275 	while (!feof(file)) {
276 		if (cancel != NULL && *cancel == true)
277 			break;
278 
279 		unsigned char c = fgetc(file);
280 
281 		if (c != 255) {
282 			line << (char)c;
283 			result << (char)c;
284 		}
285 
286 		if (c == '\n') {
287 			updateMsg.RemoveName(stringFieldName.String());
288 			updateMsg.AddString(stringFieldName.String(), line);
289 			target.SendMessage(&updateMsg);
290 			line = "";
291 		}
292 	}
293 
294 	return result;
295 }
296 
297 
298 BCommandPipe&
299 BCommandPipe::operator<<(const char* _arg)
300 {
301 	AddArg(_arg);
302 	return *this;
303 }
304 
305 
306 BCommandPipe&
307 BCommandPipe::operator<<(const BString& _arg)
308 {
309 	AddArg(_arg.String());
310 	return *this;
311 }
312 
313 
314 BCommandPipe&
315 BCommandPipe::operator<<(const BCommandPipe& _arg)
316 {
317 	int32 argc;
318 	const char** argv = _arg.Argv(argc);
319 	for (int32 i = 0; i < argc; i++)
320 		AddArg(argv[i]);
321 
322 	return *this;
323 }
324 
325