File indexing completed on 2024-12-01 06:51:47
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"