20#include <zypp/base/LogTools.h>
21#include <zypp-core/base/DefaultIntegral>
22#include <zypp/base/String.h>
24#include <zypp/base/IOStream.h>
27#include <zypp/ExternalProgram.h>
28#include <zypp/PathInfo.h>
32#undef ZYPP_BASE_LOGGER_LOGGROUP
33#define ZYPP_BASE_LOGGER_LOGGROUP "zypp::plugin"
41 const char * PLUGIN_DEBUG = getenv(
"ZYPP_PLUGIN_DEBUG" );
46 struct PluginDebugBuffer
48 PluginDebugBuffer(
const std::string & buffer_r ) :
_buffer( buffer_r ) {}
55 L_DBG(
"PLUGIN") <<
"< (empty)" << endl;
59 std::istringstream datas(
_buffer );
70 struct PluginDumpStderr
72 PluginDumpStderr( ExternalProgramWithStderr & prog_r ) :
_prog( prog_r ) {}
76 while (
_prog.stderrGetline( line ) )
77 L_WAR(
"PLUGIN") <<
"! " << line << endl;
79 ExternalProgramWithStderr &
_prog;
82 inline void setBlocking( FILE * file_r,
bool yesno_r =
true )
85 ZYPP_THROW( PluginScriptException(
"setNonBlocking" ) );
87 int fd = ::fileno( file_r );
89 ZYPP_THROW( PluginScriptException(
"setNonBlocking" ) );
91 int flags = ::fcntl( fd, F_GETFL );
93 ZYPP_THROW( PluginScriptException(
"setNonBlocking" ) );
97 else if ( flags & O_NONBLOCK )
100 flags = ::fcntl( fd, F_SETFL, flags );
102 ZYPP_THROW( PluginScriptException(
"setNonBlocking" ) );
105 inline void setNonBlocking( FILE * file_r,
bool yesno_r =
true )
106 { setBlocking( file_r, !yesno_r ); }
125 {
try {
close(); }
catch(...) {} }
145 {
return _cmd !=
nullptr; }
165 scoped_ptr<ExternalProgramWithStderr>
_cmd;
181 const long PLUGIN_TIMEOUT = str::strtonum<long>( getenv(
"ZYPP_PLUGIN_TIMEOUT" ) );
182 const long PLUGIN_SEND_TIMEOUT = str::strtonum<long>( getenv(
"ZYPP_PLUGIN_SEND_TIMEOUT" ) );
183 const long PLUGIN_RECEIVE_TIMEOUT = str::strtonum<long>( getenv(
"ZYPP_PLUGIN_RECEIVE_TIMEOUT" ) );
187 : ( PLUGIN_TIMEOUT > 0 ? PLUGIN_TIMEOUT : 30 ) );
189 : ( PLUGIN_TIMEOUT > 0 ? PLUGIN_TIMEOUT : 30 ) );
195 dumpRangeLine(
DBG <<
"Open " << script_r, args_r.begin(), args_r.end() ) << endl;
209 args.reserve( args_r.size()+1 );
211 args.insert(
args.end(), args_r.begin(), args_r.end() );
215 setNonBlocking(
_cmd->outputFile() );
216 setNonBlocking(
_cmd->inputFile() );
231 DBG <<
"Close:" << *
this << endl;
241 _lastExecError = ret.
body();
250 _lastReturn = _cmd->close();
251 _lastExecError = _cmd->execError();
253 DBG << *
this <<
" -> [" << _lastReturn <<
"] " << _lastExecError << endl;
264 if ( frame_r.
command().empty() )
265 WAR <<
"Send: No command in frame" << frame_r << endl;
270 std::ostringstream datas;
272 datas.str().swap( data );
274 DBG << *
this <<
" ->send " << frame_r << endl;
278 std::istringstream datas( data );
283 FILE * filep = _cmd->outputFile();
287 int fd = ::fileno( filep );
293 PluginDumpStderr _dump( *_cmd );
295 const char * buffer = data.c_str();
296 ssize_t buffsize = data.size();
300 watchFd.events = G_IO_OUT | G_IO_ERR;
303 int retval = g_poll( &watchFd, 1, _sendTimeout * 1000 );
307 ssize_t ret = ::write( fd, buffer, buffsize );
308 if ( ret == buffsize )
323 if ( errno != EINTR )
325 ERR <<
"write(): " <<
Errno() << endl;
326 if ( errno == EPIPE )
333 else if ( retval == 0 )
335 WAR <<
"Not ready to write within timeout." << endl;
336 ZYPP_THROW( PluginScriptSendTimeout(
"Not ready to write within timeout." ) );
340 if ( errno != EINTR )
342 ERR <<
"select(): " <<
Errno() << endl;
356 FILE * filep = _cmd->inputFile();
360 int fd = ::fileno( filep );
367 PluginDebugBuffer _debug( data );
368 PluginDumpStderr _dump( *_cmd );
370 int ch = fgetc( filep );
373 data.push_back( ch );
377 else if ( ::feof( filep ) )
379 WAR <<
"Unexpected EOF" << endl;
382 else if ( errno != EINTR )
384 if ( errno == EWOULDBLOCK )
389 rfd.events = G_IO_IN | G_IO_HUP | G_IO_ERR;
392 int retval = g_poll( &rfd, 1, _receiveTimeout * 1000 );
397 else if ( retval == 0 )
399 WAR <<
"Not ready to read within timeout." << endl;
400 ZYPP_THROW( PluginScriptReceiveTimeout(
"Not ready to read within timeout." ) );
404 if ( errno != EINTR )
406 ERR <<
"select(): " <<
Errno() << endl;
413 ERR <<
"read(): " <<
Errno() << endl;
420 std::istringstream datas( data );
422 DBG << *
this <<
" <-" << ret << endl;
447 {
return _pimpl->_sendTimeout; }
450 {
return _pimpl->_receiveTimeout; }
453 {
_pimpl->_sendTimeout = newval_r > 0 ? newval_r : 0; }
456 {
_pimpl->_receiveTimeout = newval_r > 0 ? newval_r : 0; }
463 : _pimpl( new
Impl( script_r ) )
467 : _pimpl( new
Impl( script_r, args_r ) )
471 {
return _pimpl->script(); }
474 {
return _pimpl->args(); }
477 {
return _pimpl->isOpen(); }
480 {
return _pimpl->getPid(); }
483 {
return _pimpl->lastReturn(); }
486 {
return _pimpl->lastExecError(); }
492 {
_pimpl->open( script_r ); }
495 {
_pimpl->open( script_r, args_r ); }
498 {
return _pimpl->close(); }
501 {
_pimpl->send( frame_r ); }
504 {
return _pimpl->receive(); }
ExternalProgramWithStderr & _prog
const std::string & _buffer
Integral type with defined initial value when default constructed.
DefaultIntegral & reset()
Reset to the defined initial value.
Convenience errno wrapper.
ExternalProgram extended to offer reading programs stderr.
Command frame for communication with PluginScript.
const std::string & body() const
Return the frame body.
const std::string & command() const
Return the frame command.
const std::string & getHeaderNT(const std::string &key_r, const std::string &default_r=std::string()) const
Not throwing version returing one of the matching header values or default_r string.
bool isAckCommand() const
Convenience to identify an ACK command.
std::ostream & writeTo(std::ostream &stream_r) const
Write frame to stream.
Base class for PluginScript Exception.
Interface to plugin scripts using a Stomp inspired communication protocol.
PluginFrame receive() const
Receive a PluginFrame.
long sendTimeout() const
Local default timeout (sec.) when sending data.
void send(const PluginFrame &frame_r) const
Send a PluginFrame.
PluginScript()
Default ctor.
RW_pointer< Impl > _pimpl
Pointer to implementation.
std::vector< std::string > Arguments
Commandline arguments passed to a script on open.
int lastReturn() const
Remembers a scripts return value after close until next open.
static long defaultReceiveTimeout()
Global default timeout (sec.) when receiving data.
static const pid_t NotConnected
pid_t(-1) constant indicating no connection.
int close()
Close any open connection.
const std::string & lastExecError() const
Remembers a scripts execError string after close until next open.
pid_t getPid() const
Return a connected scripts pid or NotConnected.
void open()
Setup connection and execute script.
const Pathname & script() const
Return the script path if set.
static long defaultSendTimeout()
Global default timeout (sec.) when sending data.
bool isOpen() const
Whether we are connected to a script.
long receiveTimeout() const
Local default timeout (sec.) when receiving data.
const Arguments & args() const
Return the script arguments if set.
Exception safe signal handler save/restore.
Wrapper class for stat/lstat.
const std::string & asString() const
String representation.
String related utilities and Regular expression matching.
std::ostream & copyIndent(std::istream &from_r, std::ostream &to_r, const std::string &indent_r="> ")
Copy istream to ostream, prefixing each line with indent_r (default "> " ).
TInt strtonum(const C_Str &str)
Parsing numbers from string.
Easy-to use interface to the ZYPP dependency resolver.
std::ostream & dumpRangeLine(std::ostream &str, TIterator begin, TIterator end)
Print range defined by iterators (single line style).
std::ostream & operator<<(std::ostream &str, const SerialNumber &obj)
PluginScript implementation.
PluginFrame receive() const
std::string _lastExecError
static long _defaultSendTimeout
DefaultIntegral< int, 0 > _lastReturn
const Pathname & script() const
scoped_ptr< ExternalProgramWithStderr > _cmd
void open(const Pathname &script_r=Pathname(), const Arguments &args_r=Arguments())
void send(const PluginFrame &frame_r) const
static long _defaultReceiveTimeout
const std::string & lastExecError() const
Impl(const Pathname &script_r=Pathname(), const Arguments &args_r=Arguments())
const Arguments & args() const
Convenient building of std::string via std::ostringstream Basically a std::ostringstream autoconverti...
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.