You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
377 lines
8.9 KiB
377 lines
8.9 KiB
//=============================================================================
|
|
//
|
|
// File : kvi_kvs_processmanager.cpp
|
|
// Created on Wed 07 Apr 2004 03:03:52 by Szymon Stefanek
|
|
//
|
|
// This file is part of the KVIrc IRC client distribution
|
|
// Copyright (C) 2004-2007 Szymon Stefanek <pragma at kvirc dot net>
|
|
//
|
|
// 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 2
|
|
// of the License, or (at your opinion) 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, write to the Free Software Foundation,
|
|
// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
//
|
|
//=============================================================================
|
|
|
|
#define __KVIRC__
|
|
|
|
#include "kvi_kvs_processmanager.h"
|
|
#include "kvi_window.h"
|
|
#include "kvi_out.h"
|
|
#include "kvi_locale.h"
|
|
#include "kvi_parameterlist.h"
|
|
#include "kvi_app.h"
|
|
#include "kvi_console.h"
|
|
#include "kvi_kvs_script.h"
|
|
//#include <TQSysInfo>
|
|
|
|
KviKvsProcessAsyncOperation::KviKvsProcessAsyncOperation(KviKvsProcessDescriptorData * d)
|
|
: KviKvsAsyncOperation(d->pWnd)
|
|
{
|
|
m_pData = d;
|
|
m_pProcess = 0;
|
|
m_pExtendedRunTimeData = new KviKvsExtendedRunTimeData(new KviKvsHash(),TRUE);
|
|
m_pPingTimer = 0;
|
|
m_pRunTimeTimer = 0;
|
|
m_bDeletePending = false;
|
|
}
|
|
|
|
KviKvsProcessAsyncOperation::~KviKvsProcessAsyncOperation()
|
|
{
|
|
if(m_pPingTimer)delete m_pPingTimer;
|
|
if(m_pRunTimeTimer)delete m_pRunTimeTimer;
|
|
if(m_pProcess)
|
|
{
|
|
TQObject::disconnect(m_pProcess,0,this,0);
|
|
m_pProcess->kill();
|
|
delete m_pProcess;
|
|
}
|
|
delete m_pExtendedRunTimeData;
|
|
if(m_pData->pCallback)delete m_pData->pCallback;
|
|
if(m_pData->pMagic)delete m_pData->pMagic;
|
|
delete m_pData;
|
|
}
|
|
|
|
bool KviKvsProcessAsyncOperation::start()
|
|
{
|
|
TQStringList args;
|
|
|
|
if(m_pData->iFlags & KVI_KVS_PROCESSDESCRIPTOR_NOSHELL)
|
|
{
|
|
args = TQStringList::split(" ",m_pData->szCommandline);
|
|
} else {
|
|
TQString szShell = m_pData->szShell;
|
|
if(szShell.isEmpty())
|
|
{
|
|
#ifdef COMPILE_ON_WINDOWS
|
|
// [01:26:00] <PragmaOff> btw, what is qt_wintqunicode ?
|
|
// [01:26:12] <kode54> TQt export specific to win32
|
|
// [01:26:27] <kode54> bool which indicates whether system is Unicode (NT) or not
|
|
// [01:26:58] <kode54> not sure if that's documented, but it is a public export
|
|
//
|
|
// [02:50:21] <kode54> if ( TQApplication::winVersion() & TQt::WV_NT_based )
|
|
// [02:50:41] <kode54> I see another implementation using that, maybe it is the official way of detecting that :[
|
|
#ifdef COMPILE_USE_QT4
|
|
szShell = !(TQSysInfo::WindowsVersion & TQSysInfo::WV_DOS_based) ? "cmd.exe /c" : "command.com /c";
|
|
#else
|
|
szShell = "command.com /c";
|
|
#endif
|
|
|
|
// Thnx kode54 :)
|
|
#else
|
|
szShell = "sh -c";
|
|
#endif
|
|
}
|
|
args = TQStringList::split(" ",szShell);
|
|
args.append(m_pData->szCommandline);
|
|
}
|
|
|
|
m_pProcess = new KviProcess(args);
|
|
int c = KviProcess::Stdin;
|
|
|
|
if(m_pData->iFlags & KVI_KVS_PROCESSDESCRIPTOR_TRIGGERSTDOUT)
|
|
{
|
|
connect(m_pProcess,TQT_SIGNAL(readyReadStdout()),this,TQT_SLOT(readStdout()));
|
|
c |= KviProcess::Stdout;
|
|
}
|
|
|
|
if(m_pData->iFlags & KVI_KVS_PROCESSDESCRIPTOR_TRIGGERSTDERR)
|
|
{
|
|
connect(m_pProcess,TQT_SIGNAL(readyReadStderr()),this,TQT_SLOT(readStderr()));
|
|
c |= KviProcess::Stderr;
|
|
}
|
|
|
|
m_pProcess->setCommunication(c);
|
|
|
|
connect(m_pProcess,TQT_SIGNAL(processExited()),this,TQT_SLOT(processExited()));
|
|
|
|
if(!m_pProcess->start())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(m_pData->iFlags & KVI_KVS_PROCESSDESCRIPTOR_TRIGGERSTARTED)
|
|
{
|
|
TQString szPid;
|
|
szPid.setNum((int)(m_pProcess->processIdentifier()));
|
|
if(trigger(EventStarted,szPid))
|
|
{
|
|
triggerSelfDelete();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if(m_pData->iMaxRunTime > 0)
|
|
{
|
|
m_pRunTimeTimer = new TQTimer(this);
|
|
connect(m_pRunTimeTimer,TQT_SIGNAL(timeout()),this,TQT_SLOT(maxRunTimeExpired()));
|
|
m_pRunTimeTimer->start(m_pData->iMaxRunTime);
|
|
}
|
|
|
|
if(m_pData->iPingTimeout > 0)
|
|
{
|
|
m_pPingTimer = new TQTimer(this);
|
|
connect(m_pPingTimer,TQT_SIGNAL(timeout()),this,TQT_SLOT(ping()));
|
|
m_pPingTimer->start(m_pData->iPingTimeout);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void KviKvsProcessAsyncOperation::ping()
|
|
{
|
|
if(trigger(EventPing,TQString()))
|
|
{
|
|
triggerSelfDelete();
|
|
}
|
|
}
|
|
|
|
void KviKvsProcessAsyncOperation::triggerSelfDelete()
|
|
{
|
|
if(m_bDeletePending)return;
|
|
m_bDeletePending = true;
|
|
TQTimer::singleShot(m_pData->iMaxRunTime,this,TQT_SLOT(selfDelete()));
|
|
}
|
|
|
|
void KviKvsProcessAsyncOperation::selfDelete()
|
|
{
|
|
delete this;
|
|
}
|
|
|
|
void KviKvsProcessAsyncOperation::maxRunTimeExpired()
|
|
{
|
|
trigger(EventTerminated,"0");
|
|
triggerSelfDelete();
|
|
}
|
|
|
|
bool KviKvsProcessAsyncOperation::trigger(CallbackEvent e,const TQString &szData)
|
|
{
|
|
if(m_bDeletePending)return false;
|
|
|
|
if(!g_pApp->windowExists(m_pData->pWnd))
|
|
{
|
|
if(m_pData->iFlags & KVI_KVS_PROCESSDESCRIPTOR_KILLIFNOWINDOW)
|
|
{
|
|
return true;
|
|
}
|
|
m_pData->pWnd = g_pApp->activeConsole();
|
|
}
|
|
|
|
if(m_pData->pCallback)
|
|
{
|
|
KviKvsVariantList params;
|
|
params.setAutoDelete(true);
|
|
|
|
switch(e)
|
|
{
|
|
case EventStdout:
|
|
params.append(new KviKvsVariant(TQString("stdout")));
|
|
break;
|
|
case EventStderr:
|
|
params.append(new KviKvsVariant(TQString("stderr")));
|
|
break;
|
|
case EventTerminated:
|
|
params.append(new KviKvsVariant(TQString("terminated")));
|
|
break;
|
|
case EventStarted:
|
|
params.append(new KviKvsVariant(TQString("started")));
|
|
break;
|
|
case EventPing:
|
|
params.append(new KviKvsVariant(TQString("ping")));
|
|
break;
|
|
default:
|
|
debug("Ops... unknown trigger() CallbackEvent parameter in KviProcessDescriptor::trigger()");
|
|
return false;
|
|
break;
|
|
}
|
|
|
|
params.append(new KviKvsVariant(szData));
|
|
if (m_pData->pMagic) params.append(m_pData->pMagic);
|
|
|
|
KviKvsVariant retVal;
|
|
int iRet = m_pData->pCallback->run(m_pData->pWnd,¶ms,&retVal,KviKvsScript::PreserveParams,m_pExtendedRunTimeData);
|
|
if(!iRet)
|
|
{
|
|
m_pData->pWnd->output(KVI_OUT_PARSERERROR,
|
|
__tr2qs("Error triggered from process callback handler: killing process"));
|
|
return true;
|
|
}
|
|
|
|
if(!retVal.isNothing())
|
|
{
|
|
TQString sz;
|
|
retVal.asString(sz);
|
|
m_pProcess->writeToStdin(sz);
|
|
}
|
|
|
|
if(iRet & KviKvsScript::HaltEncountered)
|
|
{
|
|
// halt encountered: kill the process
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void KviKvsProcessAsyncOperation::readStdout()
|
|
{
|
|
if(m_bDeletePending)return;
|
|
if(m_pData->iFlags & KVI_KVS_PROCESSDESCRIPTOR_OUTPUTBYBLOCKS)
|
|
{
|
|
TQByteArray a = m_pProcess->readStdout();
|
|
if(a.size() > 0)
|
|
m_szStdoutBuffer += TQString(a);
|
|
} else {
|
|
TQString l = m_pProcess->readLineStdout();
|
|
bool bBreak = false;
|
|
while((!l.isNull()) && (!bBreak))
|
|
{
|
|
if(m_pData->iFlags & KVI_KVS_PROCESSDESCRIPTOR_TRIGGERSTDOUT)
|
|
{
|
|
if(trigger(EventStdout,l))
|
|
{
|
|
bBreak = true;
|
|
triggerSelfDelete();
|
|
}
|
|
}
|
|
|
|
l = m_pProcess->readLineStdout();
|
|
}
|
|
}
|
|
}
|
|
|
|
void KviKvsProcessAsyncOperation::readStderr()
|
|
{
|
|
if(m_bDeletePending)return;
|
|
if(m_pData->iFlags & KVI_KVS_PROCESSDESCRIPTOR_OUTPUTBYBLOCKS)
|
|
{
|
|
TQByteArray a = m_pProcess->readStderr();
|
|
if(a.size() > 0)
|
|
m_szStderrBuffer += TQString(a);
|
|
} else {
|
|
TQString l = m_pProcess->readLineStderr();
|
|
bool bBreak = false;
|
|
while((!l.isNull()) && (!bBreak))
|
|
{
|
|
if(m_pData->iFlags & KVI_KVS_PROCESSDESCRIPTOR_TRIGGERSTDERR)
|
|
{
|
|
if(trigger(EventStderr,l))
|
|
{
|
|
bBreak = true;
|
|
triggerSelfDelete();
|
|
}
|
|
}
|
|
|
|
l = m_pProcess->readLineStderr();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void KviKvsProcessAsyncOperation::processExited()
|
|
{
|
|
if(m_bDeletePending)return;
|
|
|
|
readStdout(); // just to make sure
|
|
readStderr(); // just to make sure
|
|
|
|
if(m_pData->iFlags & KVI_KVS_PROCESSDESCRIPTOR_OUTPUTBYBLOCKS)
|
|
{
|
|
// trigger Stdout and Stderr once
|
|
if(m_pData->iFlags & KVI_KVS_PROCESSDESCRIPTOR_TRIGGERSTDOUT)
|
|
{
|
|
if(trigger(EventStdout,m_szStdoutBuffer))
|
|
{
|
|
triggerSelfDelete();
|
|
return;
|
|
}
|
|
}
|
|
|
|
if(m_pData->iFlags & KVI_KVS_PROCESSDESCRIPTOR_TRIGGERSTDERR)
|
|
{
|
|
if(trigger(EventStdout,m_szStderrBuffer))
|
|
{
|
|
triggerSelfDelete();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(m_pData->iFlags & KVI_KVS_PROCESSDESCRIPTOR_TRIGGERTERMINATED)
|
|
{
|
|
TQString szRetVal;
|
|
szRetVal.setNum(m_pProcess->exitStatus());
|
|
trigger(EventTerminated,szRetVal);
|
|
}
|
|
|
|
triggerSelfDelete();
|
|
}
|
|
|
|
|
|
/*
|
|
|
|
KviKvsProcessManager::KviKvsProcessManager()
|
|
: TQObject()
|
|
{
|
|
}
|
|
|
|
KviKvsProcessManager::~KviKvsProcessManager()
|
|
{
|
|
}
|
|
|
|
void KviKvsProcessManager::init()
|
|
{
|
|
if(m_pInstance)return;
|
|
m_pInstance = new KviProcessManager();
|
|
}
|
|
|
|
void KviKvsProcessManager::done()
|
|
{
|
|
if(!m_pInstance)return;
|
|
delete m_pInstance;
|
|
m_pInstance = 0;
|
|
}
|
|
|
|
bool KviKvsProcessManager::execute(KviKvsProcessAsyncOperationData * d)
|
|
{
|
|
KviKvsProcessAsyncOperation * pd = new KviKvsProcessAsyncOperation(d,this);
|
|
if(!pd->start())
|
|
{
|
|
//delete d; <-- delete by KviKvsProcessAsyncOperation
|
|
delete pd;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
*/
|