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