// ----------------------------------------------------------------------
// File: IoPipe.hh
// Author: Andreas-Joachim Peters - CERN
// ----------------------------------------------------------------------
/************************************************************************
* EOS - the CERN Disk Storage System *
* Copyright (C) 2011 CERN/Switzerland *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see .*
************************************************************************/
/**
* @file IoPipe.hh
*
* @brief Class providing one-directional pipes for stdout,stderr & retc bridging between processes.
*
*
*/
#ifndef __EOSCOMMON_IOPIPE__
#define __EOSCOMMON_IOPIPE__
/*----------------------------------------------------------------------------*/
#include "common/Namespace.hh"
#include "common/Path.hh"
/*----------------------------------------------------------------------------*/
#include "XrdOuc/XrdOucString.hh"
#include "XrdSys/XrdSysLogger.hh"
#include "XrdNet/XrdNetOpts.hh"
#include "XrdNet/XrdNetSocket.hh"
/*----------------------------------------------------------------------------*/
#include
#include
#include
#include
#include
#include
#include
#include
/*----------------------------------------------------------------------------*/
EOSCOMMONNAMESPACE_BEGIN
/*----------------------------------------------------------------------------*/
//! Class Implementing communication via local unix pipes.
//! Each IoPipe uses three pipes to bridge stdout,stderr & return code.
//! IoPipes need to be locked because there can be only one Producer and one Consumer.
//! IoPipe is used by console/ConsoleMain.cc to allow persistent connection of the xrootd client.
/*----------------------------------------------------------------------------*/
class IoPipe {
public:
XrdOucString lPrefix; //< directory to store pipes
XrdOucString lPipeDir; //< directory including process uid and parent pid dependent
XrdOucString lPipeProducerLock; //< lock for the producer
XrdOucString lPipeConsumerLock; //< lock for the consumer
XrdOucString lStdInName ;
XrdOucString lStdOutName;
XrdOucString lStdErrName;
XrdOucString lRetcName ;
XrdNetSocket* stdinsocket;
XrdNetSocket* stdoutsocket;
XrdNetSocket* stderrsocket;
XrdNetSocket* retcsocket;
int lConsumerFd;
int lProducerFd;
// ---------------------------------------------------------------------------
//! Constructor
// ---------------------------------------------------------------------------
IoPipe(XrdOucString prefix = "/tmp/eos") {
lPrefix = prefix;
lPipeDir = prefix;
lPipeDir += ".";
lPipeDir += (int) getuid();
lPipeDir += "/";
lPipeDir += (int) getppid();
lPipeDir += "/";
lPipeProducerLock = lPipeDir + "producer.lock";
lPipeConsumerLock = lPipeDir + "consumer.lock";
lStdInName = "xstdin";
lStdOutName = "xstdout";
lStdErrName = "xstderr";
lRetcName = "xretc";
stdinsocket = stdoutsocket = stderrsocket = retcsocket = 0;
lProducerFd = lConsumerFd = 0;
}
// ---------------------------------------------------------------------------
//! Write a pid into a file
// ---------------------------------------------------------------------------
void WritePid(const char* path, pid_t pid) {
std::ofstream pidfile(path, ios::binary);
pidfile << pid ;
}
// ---------------------------------------------------------------------------
//! Read a pid from a file
// ---------------------------------------------------------------------------
pid_t ReadPid(const char* path) {
pid_t lpid=0;
std::ifstream pidfile(path);
pidfile >> lpid;
return lpid;
}
// ---------------------------------------------------------------------------
//! Initialize IoPipe creating all needed parent directories
// ---------------------------------------------------------------------------
bool Init() {
XrdOucString dummypipedir = lPipeDir; dummypipedir += "/dummy";
eos::common::Path dPath(dummypipedir.c_str());
if(!dPath.MakeParentPath(S_IRWXU)) {
return false;
}
return true;
}
// ---------------------------------------------------------------------------
//! Write our Pid into the producer pid file
// ---------------------------------------------------------------------------
bool LockProducer() {
int fd = open (lPipeProducerLock.c_str(),O_EXCL| O_CREAT, S_IRWXU);
if (fd>=0) {
close(fd);
WritePid(lPipeProducerLock.c_str(), getpid());
return true;
}
return false;
}
// ---------------------------------------------------------------------------
//! Check if the stored producer is alive
// ---------------------------------------------------------------------------
bool CheckProducer() {
pid_t pid = ReadPid(lPipeProducerLock.c_str());
if (pid) {
if (!kill(pid,0))
return true;
}
UnLockProducer();
return false;
}
// ---------------------------------------------------------------------------
//! Kill the current producer
// ---------------------------------------------------------------------------
bool KillProducer() {
pid_t pid = ReadPid(lPipeProducerLock.c_str());
if (pid) {
if (!kill(pid,3))
return true;
}
UnLockProducer();
return false;
}
// ---------------------------------------------------------------------------
//! Write our Pid into the consumer pid file
// ---------------------------------------------------------------------------
bool LockConsumer() {
do {
int fd = open (lPipeConsumerLock.c_str(),O_EXCL| O_CREAT, S_IRWXU);
if (fd>=0) {
close(fd);
WritePid(lPipeConsumerLock.c_str(), getpid());
return true;
}
usleep(100000);
} while (1);
return false;
}
// ---------------------------------------------------------------------------
//! Remove the producer pid file
// ---------------------------------------------------------------------------
bool UnLockProducer() {
int rc = unlink(lPipeProducerLock.c_str());
if (!rc)
return true;
else
return false;
}
// ---------------------------------------------------------------------------
//! Remove the consumer pid file
// ---------------------------------------------------------------------------
bool UnLockConsumer() {
int rc = unlink(lPipeConsumerLock.c_str());
if (!rc)
return true;
else
return false;
}
// ---------------------------------------------------------------------------
//! Attach to the stdin pipe
// ---------------------------------------------------------------------------
int AttachStdin(XrdSysError &eDest) {
XrdNetSocket* socket = XrdNetSocket::Create(&eDest, lPipeDir.c_str(), lStdInName.c_str(), S_IRWXU, XRDNET_FIFO);
if (socket) {
stdinsocket = socket;
return socket->SockNum();
} else {
return -1;
}
}
// ---------------------------------------------------------------------------
//! Attach to the stdout pipe
// ---------------------------------------------------------------------------
int AttachStdout(XrdSysError &eDest) {
XrdNetSocket* socket = XrdNetSocket::Create(&eDest, lPipeDir.c_str(), lStdOutName.c_str(), S_IRWXU, XRDNET_FIFO);
if (socket) {
stdoutsocket = socket;
return socket->SockNum();
} else {
return -1;
}
}
// ---------------------------------------------------------------------------
//! Attach to the stderr pipe
// ---------------------------------------------------------------------------
int AttachStderr(XrdSysError &eDest) {
XrdNetSocket* socket = XrdNetSocket::Create(&eDest, lPipeDir.c_str(), lStdErrName.c_str(), S_IRWXU, XRDNET_FIFO);
if (socket) {
stderrsocket = socket;
return socket->SockNum();
} else {
return -1;
}
}
// ---------------------------------------------------------------------------
//! Attach to the retc pipe
// ---------------------------------------------------------------------------
int AttachRetc(XrdSysError &eDest) {
XrdNetSocket* socket = XrdNetSocket::Create(&eDest, lPipeDir.c_str(), lRetcName.c_str(), S_IRWXU, XRDNET_FIFO);
if (socket) {
retcsocket = socket;
return socket->SockNum();
} else {
return -1;
}
}
// ---------------------------------------------------------------------------
//! Destructor
// ---------------------------------------------------------------------------
~IoPipe() {};
};
/*----------------------------------------------------------------------------*/
EOSCOMMONNAMESPACE_END
#endif