/* * Copyright 2007 Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: * Ramshankar, v.ramshankar@gmail.com * Stephan Aßmus */ //! BCommandPipe class to handle reading shell output // (stdout/stderr) of other programs into memory. #include "CommandPipe.h" #include #include #include #include #include #include BCommandPipe::BCommandPipe() : fStdOutOpen(false), fStdErrOpen(false) { } BCommandPipe::~BCommandPipe() { FlushArgs(); } status_t BCommandPipe::AddArg(const char* arg) { if (arg == NULL || arg[0] == '\0') return B_BAD_VALUE; char* argCopy = strdup(arg); if (argCopy == NULL) return B_NO_MEMORY; if (!fArgList.AddItem(reinterpret_cast(argCopy))) { free(argCopy); return B_NO_MEMORY; } return B_OK; } void BCommandPipe::PrintToStream() const { for (int32 i = 0L; i < fArgList.CountItems(); i++) printf("%s ", reinterpret_cast(fArgList.ItemAtFast(i))); printf("\n"); } void BCommandPipe::FlushArgs() { // Delete all arguments from the list for (int32 i = fArgList.CountItems() - 1; i >= 0; i--) free(fArgList.ItemAtFast(i)); fArgList.MakeEmpty(); Close(); } void BCommandPipe::Close() { if (fStdErrOpen) { close(fStdErr[0]); fStdErrOpen = false; } if (fStdOutOpen) { close(fStdOut[0]); fStdOutOpen = false; } } const char** BCommandPipe::Argv(int32& argc) const { // NOTE: Freeing is left to caller as indicated in the header! argc = fArgList.CountItems(); const char** argv = reinterpret_cast( malloc((argc + 1) * sizeof(char*))); for (int32 i = 0; i < argc; i++) argv[i] = reinterpret_cast(fArgList.ItemAtFast(i)); argv[argc] = NULL; return argv; } // #pragma mark - thread_id BCommandPipe::PipeAll(int* stdOutAndErr) const { // This function pipes both stdout and stderr to the same filedescriptor // (stdOut) int oldStdOut; int oldStdErr; pipe(stdOutAndErr); oldStdOut = dup(STDOUT_FILENO); oldStdErr = dup(STDERR_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); // TODO: This looks broken, using "stdOutAndErr[1]" twice! dup2(stdOutAndErr[1], STDOUT_FILENO); dup2(stdOutAndErr[1], STDERR_FILENO); // Construct the argv vector int32 argc; const char** argv = Argv(argc); // Load the app image... and pass the args thread_id appThread = load_image((int)argc, argv, const_cast(environ)); dup2(oldStdOut, STDOUT_FILENO); dup2(oldStdErr, STDERR_FILENO); close(oldStdOut); close(oldStdErr); free(argv); return appThread; } thread_id BCommandPipe::Pipe(int* stdOut, int* stdErr) const { int oldStdOut; int oldStdErr; pipe(stdOut); pipe(stdErr); oldStdOut = dup(STDOUT_FILENO); oldStdErr = dup(STDERR_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); dup2(stdOut[1], STDOUT_FILENO); dup2(stdErr[1], STDERR_FILENO); // Construct the argv vector int32 argc; const char** argv = Argv(argc); // Load the app image... and pass the args thread_id appThread = load_image((int)argc, argv, const_cast< const char**>(environ)); dup2(oldStdOut, STDOUT_FILENO); dup2(oldStdErr, STDERR_FILENO); close(oldStdOut); close(oldStdErr); free(argv); return appThread; } thread_id BCommandPipe::Pipe(int* stdOut) const { // Redirects only output (stdout) to caller, stderr is closed int stdErr[2]; thread_id tid = Pipe(stdOut, stdErr); close(stdErr[0]); close(stdErr[1]); return tid; } thread_id BCommandPipe::PipeInto(FILE** _out, FILE** _err) { Close(); thread_id tid = Pipe(fStdOut, fStdErr); if (tid >= 0) resume_thread(tid); close(fStdErr[1]); close(fStdOut[1]); fStdOutOpen = true; fStdErrOpen = true; *_out = fdopen(fStdOut[0], "r"); *_err = fdopen(fStdErr[0], "r"); return tid; } thread_id BCommandPipe::PipeInto(FILE** _outAndErr) { Close(); thread_id tid = PipeAll(fStdOut); if (tid >= 0) resume_thread(tid); close(fStdOut[1]); fStdOutOpen = true; *_outAndErr = fdopen(fStdOut[0], "r"); return tid; } // #pragma mark - void BCommandPipe::Run() { // Runs the command without bothering to redirect streams, this is similar // to system() but uses pipes and wait_for_thread.... Synchronous. int stdOut[2], stdErr[2]; status_t exitCode; wait_for_thread(Pipe(stdOut, stdErr), &exitCode); close(stdOut[0]); close(stdErr[0]); close(stdOut[1]); close(stdErr[1]); } void BCommandPipe::RunAsync() { // Runs the command without bothering to redirect streams, this is similar // to system() but uses pipes.... Asynchronous. Close(); FILE* f = NULL; PipeInto(&f); fclose(f); } // #pragma mark - status_t BCommandPipe::ReadLines(FILE* file, LineReader* lineReader) { // Reads output of file, line by line. Each line is passed to lineReader // for inspection, and the IsCanceled() method is repeatedly called. if (file == NULL || lineReader == NULL) return B_BAD_VALUE; BString line; while (!feof(file)) { if (lineReader->IsCanceled()) return B_CANCELED; unsigned char c = fgetc(file); // TODO: fgetc() blocks, is there a way to make it timeout? if (c != 255) line << (char)c; if (c == '\n') { status_t ret = lineReader->ReadLine(line); if (ret != B_OK) return ret; line = ""; } } return B_OK; } BString BCommandPipe::ReadLines(FILE* file) { class AllLinesReader : public LineReader { public: AllLinesReader() : fResult("") { } virtual bool IsCanceled() { return false; } virtual status_t ReadLine(const BString& line) { int lineLength = line.Length(); int resultLength = fResult.Length(); fResult << line; if (fResult.Length() != lineLength + resultLength) return B_NO_MEMORY; return B_OK; } BString Result() const { return fResult; } private: BString fResult; } lineReader; ReadLines(file, &lineReader); return lineReader.Result(); } // #pragma mark - BCommandPipe& BCommandPipe::operator<<(const char* arg) { AddArg(arg); return *this; } BCommandPipe& BCommandPipe::operator<<(const BString& arg) { AddArg(arg.String()); return *this; } BCommandPipe& BCommandPipe::operator<<(const BCommandPipe& arg) { int32 argc; const char** argv = arg.Argv(argc); for (int32 i = 0; i < argc; i++) AddArg(argv[i]); free(argv); return *this; }