File indexing completed on 2024-04-28 04:03:00

0001 /***************************************************************************
0002                           crunningscript.cpp  -  running script
0003                              -------------------
0004     begin                : Ne dec 8 2002
0005     copyright            : (C) 2002-2009 by Tomas Mecir
0006     email                : kmuddy@kmuddy.com
0007  ***************************************************************************/
0008 
0009 /***************************************************************************
0010  *                                                                         *
0011  *   This program is free software; you can redistribute it and/or modify  *
0012  *   it under the terms of the GNU General Public License as published by  *
0013  *   the Free Software Foundation; either version 2 of the License, or     *
0014  *   (at your option) any later version.                                   *
0015  *                                                                         *
0016  ***************************************************************************/
0017 
0018 #include "crunningscript.h"
0019 
0020 #include "cactionmanager.h"
0021 #include "cscript.h"
0022 #include "cunixsocket.h"
0023 
0024 #include <QProcess>
0025 
0026 #include <stdlib.h>
0027 
0028 cRunningScript::cRunningScript (cScript *s)
0029 {
0030   script = s;
0031   process = 0;
0032   scriptDying    = false;
0033   dontSignal     = false;
0034   sendInProgress = false; // No data being sent yet
0035   stdinBuffer = "";       // Ensure buffer is empty
0036   unixsocket = 0;
0037   launchAfter = 0;
0038 }
0039 
0040 cRunningScript::~cRunningScript ()
0041 {
0042   dontSignal = true;
0043   process->close ();
0044   process->deleteLater();;
0045   if (unixsocket) delete unixsocket;
0046   unixsocket  = 0;
0047   scriptDying = true; // Prevent further data being sent to this script
0048   
0049   if (sendInProgress && flowcontrol)
0050   {
0051     //some text was about to be sent, but it won't be sent anymore...
0052     //we behave as if it was sent, to prevent deadlock in cRunningList
0053     // (it would endlessly wait for the signal, and no new text would
0054     // be sent to any other script)
0055     //
0056     // scriptDying flag set to prevent the signal causing
0057     // further data to be sent to this script.
0058     emit textSent ();
0059   }  
0060 }
0061 
0062 void cRunningScript::launch (int fcState)
0063 {
0064   // Register the script as started, even if it's actually delayed by flow
0065   // control.  This prevents multiple instances of a single instance script.
0066   script->scriptIsStarting ();
0067   
0068   if (!flowcontrol)
0069     doLaunch ();
0070   else
0071   {
0072     //we'll launch when flow control reaches the current point
0073     launchAfter = fcState;
0074     if (fcState == 0)
0075       doLaunch ();
0076   }
0077 }
0078 
0079 void cRunningScript::doLaunch ()
0080 {
0081   launchAfter = 0;
0082   
0083   // connect some signals
0084   connect (process, SIGNAL (readyReadStandardOutput ()),
0085       this, SLOT (processScriptStdOutput ()));
0086   connect (process, SIGNAL (readyReadStandardError ()),
0087       this, SLOT (processScriptStdError ()));
0088   connect(process, SIGNAL(bytesWritten (qint64)),
0089       this, SLOT(stdinReady()));
0090   connect (process, SIGNAL (finished (int, QProcess::ExitStatus)),
0091       this, SLOT (finished (int, QProcess::ExitStatus)));
0092   connect (process, SIGNAL (error (QProcess::ProcessError)),
0093       this, SLOT (failed (QProcess::ProcessError)));
0094 
0095   //start the process
0096   process->start (command, args);
0097 }
0098 
0099 void cRunningScript::cleanup ()
0100 {
0101   if (unixsocket) delete unixsocket;
0102   unixsocket  = 0;
0103   scriptDying = true; // Prevent further data being sent to this script
0104   script->scriptIsTerminating ();
0105   cleanupSend ();
0106 }
0107 
0108 void cRunningScript::finished (int exitCode, QProcess::ExitStatus status)
0109 {
0110   cleanup ();
0111   if (dontSignal) return;
0112 
0113   if (status == QProcess::NormalExit)
0114     emit scriptFinished (this, exitCode);
0115   else
0116     emit scriptKilled (this);
0117 }
0118 
0119 void cRunningScript::failed (QProcess::ProcessError)
0120 {
0121   if (isRunning()) return;
0122   cleanup ();
0123   if (dontSignal) return;
0124   emit scriptFailed (this);
0125 }
0126 
0127 //improvements here by Alex Bache
0128 void cRunningScript::sendCommandToScript (const QString &command, char type)
0129 {
0130   actuallySent = false;
0131   
0132   if (process == 0)
0133     return;
0134 
0135   //are we waiting for flow control?
0136   if (launchAfter)
0137   {
0138     launchAfter--;
0139     if (launchAfter == 0)
0140       //we're in sync - start the script!
0141       doLaunch ();
0142     //do not send current line... if we have started the script
0143     //now, next line will be the first one that it will get
0144     return;
0145   }
0146 
0147   if ((type == USERCOMMAND) && (!sendusercommands))
0148       //type = user command, user commands are not sent
0149     return;
0150 /*  no, we'll send prompt even if there's no adv.comm
0151   if ((type == PROMPT) && (!useadvcomm))
0152       //prompt only sent if using adv.communication
0153     return;
0154 */
0155   
0156   // don't send anything if the script is in the process of shutting down
0157   if (scriptDying)
0158     return;
0159   
0160   QString txt = command;
0161   //advanced communication?
0162   if (useadvcomm)
0163   {
0164     QString beg = QChar (type);
0165     beg += QChar (' ');
0166     txt = beg + txt;
0167   }
0168   
0169   //send it if nothing else in progress
0170   
0171   if (sendInProgress)
0172   {
0173     stdinBuffer.append(txt);
0174   } // endif must buffer for later
0175   else
0176   {
0177     stdinSending   = txt;
0178     sendInProgress = true;
0179     actuallySent   = true;
0180     process->write (stdinSending.toLocal8Bit());
0181   } // endelse able to send immediately
0182   if (flowcontrol)
0183     emit textAccepted ();
0184 }
0185 
0186 //function written by Alex Bache
0187 // stdinReady() called when script has processed the data on its STDIN and is ready for more
0188 void cRunningScript::stdinReady()
0189 {
0190   //only if we're sending something
0191   if (!sendInProgress) return;
0192   if (stdinBuffer.length() > 0)
0193   {
0194     //this shouldn't happen with the new input flow control, but just in case
0195     stdinSending = stdinBuffer;
0196     stdinBuffer  = "";
0197     process->write (stdinSending.toLocal8Bit());
0198   } // endif more data to send to script's STDIN
0199   else
0200   {
0201     sendInProgress = false;
0202   } // endelse no more data to send at this time
0203 
0204   // inform cRunningList only when ALL buffered text has been sent
0205   if ((!sendInProgress) && (flowcontrol))
0206     emit textSent ();
0207 } // cRunningScript::stdinReady
0208 
0209 void cRunningScript::processScriptStdOutput ()
0210 {
0211   processScriptOutput (process->readAllStandardOutput(), true);
0212 }
0213 
0214 void cRunningScript::processScriptStdError ()
0215 {
0216   processScriptOutput (process->readAllStandardError(), false);
0217 }
0218 
0219 void cRunningScript::processScriptOutput (const QByteArray &output, bool sendoutput)
0220 {
0221   QString *line = sendoutput ? &outLine : &errLine;
0222   // cScript::prepareToLaunch connects to processScriptStdOutput
0223   // and processScriptStdError. They call this and tell if
0224   // the output needs to be sent to the MUD.
0225   int size = output.size();
0226   for (int i = 0; i < size; i++)
0227   {
0228     if (output[i] != '\n')
0229       *line += QString::fromLocal8Bit (output.data()+i, 1);
0230     else
0231     {
0232       //these will be connected by cRunningList
0233       if (sendoutput)
0234         emit sendText (*line);
0235       else
0236         emit displayText (*line);
0237       *line = QString();
0238     }
0239   }
0240 }
0241 
0242 const QString cRunningScript::name () const
0243 {
0244   return script->name();
0245 }
0246 
0247 
0248 bool cRunningScript::isRunning () const
0249 {
0250   if (process == 0)
0251     return false;
0252   //nothing if the script waits for FC
0253   if (launchAfter) return false;
0254   return (process->state() == QProcess::Running);
0255 }
0256 
0257 void cRunningScript::terminate ()
0258 {
0259   if (process == 0)
0260     return;
0261     
0262   scriptDying = true; // Prevent further data being sent to this script
0263   if (isRunning ())
0264     process->terminate ();
0265   cleanupSend ();
0266 }
0267 
0268 void cRunningScript::kill ()
0269 {
0270   if (process == 0)
0271     return;
0272 
0273   scriptDying = true; // Prevent further data being sent to this script
0274   //now go and kill it
0275   if (isRunning ())
0276     process->kill ();
0277   cleanupSend ();
0278 }
0279 
0280 void cRunningScript::cleanupSend ()
0281 {
0282   if (!sendInProgress) return;
0283   // if some text was being sent
0284   // we behave as if the text was delivered successfully, to avoid deadlocking
0285   sendInProgress = false;
0286   if (flowcontrol)
0287     emit textSent ();
0288 }
0289 
0290 void cRunningScript::establishSocket (int sess)
0291 {
0292   //we create a new socket
0293   unixsocket = new cUnixSocket (sess, this);
0294   
0295   //then we get its name
0296   QString sname = unixsocket->getName ();
0297   
0298   //and store it in environment for the script to pick up
0299   QStringList env = QProcess::systemEnvironment();
0300   env << "KMUDDY_SOCKET="+sname;
0301   process->setEnvironment(env);
0302 }
0303 
0304 #include "moc_crunningscript.cpp"