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