/* * This file is a part of Winix * and is distributed under the 2-Clause BSD licence. * Author: Tomasz Sowa */ /* * Copyright (c) 2011-2014, Tomasz Sowa * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ #include #include #include #include #include #include "run.h" namespace Winix { Run::Run() { Clear(); } void Run::Clear() { parlen = 0; envlen = 0; command = 0; last_status = 0; last_return = 255; } void Run::SetName() { size_t i=0; while( command[i] ) i += 1; while( i>0 && command[i-1]!='/' ) i -= 1; par[0] = const_cast(command + i); } void Run::Par(const char * p) { if( parlen < WINIX_RUN_MAX_PARAMETERS ) { // they will be copied (fork) // the first (zero) parameter is a program name par[1 + parlen++] = const_cast(p); } } void Run::Env(const char * e) { if( envlen < WINIX_RUN_MAX_PARAMETERS ) { // they will be copied (fork) env[envlen++] = const_cast(e); } } void Run::Cmd(const char * c) { command = c; SetName(); } void Run::Par(const std::string & p) { Par(p.c_str()); } void Run::Env(const std::string & e) { Env(e.c_str()); } void Run::Cmd(const std::string & c) { Cmd(c.c_str()); } int Run::LastStatus() { return last_status; } int Run::LastReturn() { return last_return; } void Run::Write(const char * in, size_t inlen) { ssize_t len; while( inlen > 0 ) { len = write(desout[1], in, inlen); if( len < 0 || len > (ssize_t)inlen ) { // something wrong last_status = 3; break; } in += len; inlen -= len; } } void Run::Read(std::string & out) { char buf[1024]; size_t buflen = sizeof(buf) / sizeof(char); ssize_t len; do { len = read(desin[0], buf, buflen); if( len < 0 || len > (ssize_t)buflen ) { // something wrong last_status = 4; break; } if( len > 0 ) out.append(buf, len); } while( len > 0 ); } void Run::ChildThrow() { if( dup2(desout[0], 0) == -1 ) return; if( dup2(desin[1], 1) == -1 ) return; close(2); close(desin[0]); close(desout[1]); par[parlen+1] = 0; env[envlen] = 0; execve(command, par, env); // if we are here then there is something wrong (execve failed) // !! IMPROVE ME may add exit() ? } void Run::Child() { try { ChildThrow(); } catch(...) { } exit(255); } bool Run::CreatePipes() { int res = pipe(desin); if( res != 0 ) { log << log1 << "Run: pipe failed (desin)" << logend; last_status = 1; return false; } res = pipe(desout); if( res != 0 ) { log << log1 << "Run: pipe failed (desout)" << logend; last_status = 1; close(desin[0]); close(desin[1]); return false; } return true; } bool Run::Fork() { childpid = fork(); if( childpid == -1 ) { log << log1 << "Run: fork failed" << logend; last_status = 2; close(desin[0]); close(desin[1]); close(desout[0]); close(desout[1]); return false; } if( childpid == 0 ) Child(); return true; } void Run::WriteRead(const char * in, size_t inlen, std::string * out) { if( in && inlen>0 ) Write(in, inlen); close(desout[1]); if( out ) Read(*out); close(desin[0]); } void Run::CheckStatus() { int status; pid_t res = waitpid(childpid, &status, 0); if( res == -1 ) { log << log1 << "Run: waitpid failed" << logend; last_status = 6; } else { if( WIFEXITED(status) ) { last_return = WEXITSTATUS(status); } else { if( WIFCONTINUED(status) ) log << log1 << "Run: child error: WIFCONTINUED" << logend; else if( WIFSIGNALED(status) ) log << log1 << "Run: child error: WIFSIGNALED" << logend; else if( WIFSTOPPED(status) ) log << log1 << "Run: child error: WIFSTOPPED" << logend; last_status = 5; } } } int Run::Go(const char * in, size_t inlen, std::string * out) { last_status = 0; last_return = 255; if( out ) out->clear(); if( !command ) { last_status = 7; return last_return; } if( !CreatePipes() ) return last_return; if( !Fork() ) return last_return; // here goes the parent close(desin[1]); close(desout[0]); WriteRead(in, inlen, out); CheckStatus(); if( last_status != 0 ) log << log1 << "Run: a problem with the command, last_status: " << last_status << logend; return last_return; } int Run::Go(const char * in, size_t inlen, std::string & out) { return Go(in, inlen, &out); } int Run::Go(const char * in, std::string & out) { size_t len = strlen(in); return Go(in, len, &out); } int Run::Go(const char * in, size_t inlen) { return Go(in, inlen, 0); } int Run::Go(const char * in) { size_t len = strlen(in); return Go(in, len, 0); } int Run::Go() { return Go(0, 0, 0); } int Run::Go(const std::string in, std::string & out) { return Go(in.c_str(), in.size(), &out); } int Run::Go(const std::string in) { return Go(in.c_str(), in.size(), 0); } int Run::Go(std::string & out) { return Go(0, 0, &out); } } // namespace Winix