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