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 23 BCommandPipe::BCommandPipe() 24 : 25 fStdOutOpen(false), 26 fStdErrOpen(false) 27 { 28 } 29 30 31 BCommandPipe::~BCommandPipe() 32 { 33 FlushArgs(); 34 } 35 36 37 status_t 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 57 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 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 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** 94 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 112 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 147 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 180 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 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 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 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 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 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 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& 342 BCommandPipe::operator<<(const char* arg) 343 { 344 AddArg(arg); 345 return *this; 346 } 347 348 349 BCommandPipe& 350 BCommandPipe::operator<<(const BString& arg) 351 { 352 AddArg(arg.String()); 353 return *this; 354 } 355 356 357 BCommandPipe& 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 return *this; 366 } 367 368