1 //****************************************************************************** 2 /// 3 /// @file frontend/shelloutprocessing.h 4 /// 5 /// @todo What's in here? 6 /// 7 /// @copyright 8 /// @parblock 9 /// 10 /// Persistence of Vision Ray Tracer ('POV-Ray') version 3.8. 11 /// Copyright 1991-2017 Persistence of Vision Raytracer Pty. Ltd. 12 /// 13 /// POV-Ray is free software: you can redistribute it and/or modify 14 /// it under the terms of the GNU Affero General Public License as 15 /// published by the Free Software Foundation, either version 3 of the 16 /// License, or (at your option) any later version. 17 /// 18 /// POV-Ray is distributed in the hope that it will be useful, 19 /// but WITHOUT ANY WARRANTY; without even the implied warranty of 20 /// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 /// GNU Affero General Public License for more details. 22 /// 23 /// You should have received a copy of the GNU Affero General Public License 24 /// along with this program. If not, see <http://www.gnu.org/licenses/>. 25 /// 26 /// ---------------------------------------------------------------------------- 27 /// 28 /// POV-Ray is based on the popular DKB raytracer version 2.12. 29 /// DKBTrace was originally written by David K. Buck. 30 /// DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins. 31 /// 32 /// @endparblock 33 /// 34 //****************************************************************************** 35 36 #ifndef POVRAY_FRONTEND_SHELLOUTPROCESSING_H 37 #define POVRAY_FRONTEND_SHELLOUTPROCESSING_H 38 39 // Module config header file must be the first file included within POV-Ray unit header files 40 #include "frontend/configfrontend.h" 41 42 #include <boost/format.hpp> 43 44 #include "povms/povmscpp.h" 45 46 namespace pov_frontend 47 { 48 49 class ShelloutProcessing; 50 51 class ShelloutAction 52 { 53 public: 54 typedef enum 55 { 56 ignore = 'i', 57 skipOne = 's', 58 skipAll = 'a', 59 quit = 'q', 60 abort = 'u', 61 fatal ='f' 62 } Action; 63 64 ShelloutAction(const ShelloutProcessing *sp, unsigned int attribID, POVMS_Object& opts); ~ShelloutAction()65 ~ShelloutAction() {} 66 ReturnAction(void)67 Action ReturnAction(void) const { return returnAction; } IsSet(void)68 bool IsSet(void) const { return isSet; } RawCommand(void)69 const string& RawCommand(void) const { return rawCommand; } Command(void)70 const string& Command(void) const { return command; } RawParameters(void)71 const string& RawParameters(void) const { return rawParameters; } Parameters(void)72 const string& Parameters(void) const { return parameters; } ReturnNegate(void)73 bool ReturnNegate(void) const { return returnNegate; } 74 void ExpandParameters(const string& scene, const string& ofn, unsigned int w, unsigned int h, float clock, unsigned int frame); 75 76 private: 77 bool isSet; 78 bool returnNegate; 79 string command; 80 string rawCommand; 81 string parameters; 82 string rawParameters; 83 Action returnAction; 84 85 ShelloutAction(); 86 }; 87 88 class ShelloutProcessing 89 { 90 friend class ShelloutAction; 91 92 public: 93 typedef shared_ptr<ShelloutAction> ShelloutPtr; 94 95 typedef enum 96 { 97 preScene, 98 postScene, 99 preFrame, 100 postFrame, 101 userAbort, 102 fatalError, 103 lastShelloutEvent 104 } shelloutEvent; 105 106 // we use strings rather than UCS2Strings for the scene name and parameters since the passed 107 // parameters (via POVMS) are also strings. 108 ShelloutProcessing(POVMS_Object& opts, const string& scene, unsigned int width, unsigned int height); 109 110 // you should reap any processes here as needed, and forcefully terminate ones still running. 111 virtual ~ShelloutProcessing(); 112 113 // true if a shellout command was specified for the given phase IsSet(shelloutEvent which)114 bool IsSet(shelloutEvent which) const { return shellouts[which]->IsSet(); } 115 116 // retrieve details for each type of shellout. ReturnAction(shelloutEvent which)117 ShelloutAction::Action ReturnAction(shelloutEvent which) const { return shellouts[which]->ReturnAction(); } 118 119 // return the command string as passed from the option parser; i.e. complete with parameters RawCommand(shelloutEvent which)120 string RawCommand(shelloutEvent which) const { return shellouts[which]->RawCommand(); } 121 122 // the command itself, separated from its parameters. quotes around the command will have been removed. Command(shelloutEvent which)123 string Command(shelloutEvent which) const { return shellouts[which]->Command(); } 124 125 // the raw parameters after separation from the command. any quotes will remain in place. RawParameters(shelloutEvent which)126 string RawParameters(shelloutEvent which) const { return shellouts[which]->RawParameters(); } 127 128 // the parameters after expansion of terms; e.g. %s to scene name. SetOutputFile() and 129 // SetFrameClock() (if relevant) must be called prior to calling this method. Parameters(shelloutEvent which)130 string Parameters(shelloutEvent which) const { return shellouts[which]->Parameters(); } 131 132 // returns true if all frames should be skipped. if so, any subsequent calls for 133 // pre-frame and post-frame actions will be ignored (and preferebly should not be 134 // made). the post-scene action should always be called; internal logic will determine 135 // if it will do anything. SkipAllFrames(void)136 bool SkipAllFrames(void) const { return skipAllFrames; } 137 138 // returns true if next frame should be skipped. if so, pre-frame and post-frame actions 139 // will be ignored. the internal skip frame flag is only reset when the frame number is 140 // updated. NB skip all frames does not imply skip next frame since they are handled differently. SkipNextFrame(void)141 bool SkipNextFrame(void) const { return skipNextFrame; } 142 143 // returns the exit code that POV-Ray should return at the end of the render. 144 // 0 means normal exit, 1 means fatal error, and 2 means user abort. ExitCode(void)145 int ExitCode(void) const { return exitCode; } 146 147 // returns a string representation of the exit code; e.g. 'user abort' ExitDesc(void)148 string ExitDesc(void) const { return exitCode == 0 ? "SUCCESS" : exitCode == 2 ? "USER ABORT" : "FATAL ERROR"; } 149 150 // returns true if the render should be halted. RenderCancelled(void)151 bool RenderCancelled(void) const { return cancelRender; } 152 153 // return true if the pre-scene event has been seen HadPreScene(void)154 bool HadPreScene(void) const { return hadPreScene; } 155 156 // return true if the post-scene event has been seen HadPostScene(void)157 bool HadPostScene(void) const { return hadPostScene; } 158 159 // if there is no output file, it is not required to call SetOutputFile(). 160 // if there is an output file, this method must be called prior to the pre-scene 161 // action with the value of the first output file, and prior to pre-frame for each 162 // subsequent output file if the render is an animation. SetOutputFile(const string & filename)163 void SetOutputFile(const string& filename) { outputFile = filename; } 164 165 // if the render is not an animation, there is no need to call SetFrameClock(). 166 // if it is an animation, it must be called prior to the pre-scene action, and 167 // then prior to pre-frame for each frame of the animation. note that if an action 168 // returns the 'skip next frame' option, the SkipNextFrame() method will continue 169 // to return true until the frame number supplied via this method has changed. SetFrameClock(unsigned int frame,float clock)170 void SetFrameClock(unsigned int frame, float clock) { if (frameNo != frame) skipNextFrame = false; frameNo = frame; clockVal = clock; } 171 172 // shutdown any currently-running shellouts. if force is true, force them to exit. 173 // in either case, don't wait more than timeout seconds. return true if there are 174 // no more processes running afterwards. 175 bool KillShellouts(int timeout, bool force = false); 176 177 // the message is constructed as per the documentation for the boost::format class. 178 // the positional parameters are as follows: 179 // 1: the event causing the cancel (as a string), e.g. "pre-scene" 180 // 2: the POV-Ray return code (as an integer) 181 // 3: the return code (as an upper-case string), e.g. "USER ABORT" 182 // 4: the return code (as a lower-case string). 183 // 5: the reason for the cancel (as a string), e.g. "generate a user abort" 184 // 6: the command name that generated the cancel 185 // 7: the command parameters (CAUTION: may contain escape codes) 186 // 8: the command return code (as an integer) 187 // 9: output text from the command, as returned by LastShelloutResult() 188 virtual string GetCancelMessage(void); SetCancelMessage(const string & format)189 virtual void SetCancelMessage(const string& format) { cancelFormat.parse(format); } 190 191 // the positional parameters are as follows: 192 // 1: the event causing the skip (as a string), e.g. "pre-scene" 193 // 2: the type of the skip (as a string); e.g. "skip frame 11" or "skip all remaining frames" 194 // 3: the command name that generated the skip 195 // 4: the command parameters (CAUTION: may contain escape codes) 196 // 5: the command return code (as an integer) 197 // 6: output text from the command, as returned by LastShelloutResult() 198 virtual string GetSkipMessage(void); SetSkipMessage(const string & format)199 virtual void SetSkipMessage(const string& format) { skipFormat.parse(format); } 200 201 // advise the code that a particular event should be handled now; e.g. pre-scene, post-scene 202 // and so forth. this method should be called even if the platform indicates it does not 203 // support shellouts; if a render defines one, the code will thrown a kCannotOpenFileErr 204 // exception at the time (which should be handled by the caller). this method does not block 205 // the caller during processing of shellouts as they are run in the background. the return 206 // value indicates whether or not rendering should be cancelled as a result of a shellout: 207 // generally this won't be known at the time of return, however it could be set early if 208 // a shellout could not be started and the INI file indicated that render should be halted 209 // on failure. ProcessEvent(shelloutEvent event)210 virtual bool ProcessEvent(shelloutEvent event) { return HandleProcessEvent(event, false); } 211 212 // returns true if a shellout is currently running. if this method is being called in an 213 // event loop to wait until a shellout has completed, it is the responsibility of the event 214 // loop to perform appropriate sleeps to avoid wasting CPU time. platforms that implement 215 // shellout support MUST override this method to return an appropriate value by actually 216 // checking the process each time it's called. 217 virtual bool ShelloutRunning(void); 218 219 // return the name of the currently running shellout (without parameters) 220 // if no shellout is running, an empty string should be returned. ProcessName(void)221 virtual string ProcessName(void) { return ShelloutRunning() ? runningProcessName : string(); } 222 223 // return the PID of the currently running shellout (or equivalent thereof). 224 // returns 0 if no process is running, and -1 of the platform has no PID equivalent 225 // or this method is not implemented. ProcessID(void)226 virtual int ProcessID(void) { return -1; } 227 228 // return a descriptive string detailing the result of the last shellout command 229 // in a form suitable for display on the console or UI message log - preferably 230 // no more than a single line (width unimportant). if not implemented, an empty 231 // string should be returned. LastShelloutResult(void)232 virtual string LastShelloutResult(void) { return string(); } 233 234 // return true if this platform supports shellouts. ShelloutsSupported(void)235 virtual bool ShelloutsSupported(void) { return false; } 236 237 protected: 238 int exitCode; 239 int cancelReturn; 240 int skipReturn; 241 bool skipAllFrames; 242 bool skipNextFrame; 243 bool cancelRender; 244 bool skipCallouts; 245 bool killRequested; 246 bool hadPreScene; 247 bool hadPostScene; 248 bool hadUserAbort; 249 bool hadFatalError; 250 bool commandProhibited; 251 unsigned int frameNo; 252 unsigned int imageWidth; 253 unsigned int imageHeight; 254 float clockVal; 255 string sceneName; 256 string outputFile; 257 string runningProcessName; 258 string cancelPhase; 259 string cancelReason; 260 string cancelCommand; 261 string cancelParameters; 262 string cancelOutput; 263 string skipPhase; 264 string skipReason; 265 string skipCommand; 266 string skipParameters; 267 string skipOutput; 268 boost::format cancelFormat; 269 boost::format skipFormat; 270 ShelloutPtr shellouts[lastShelloutEvent]; 271 272 // helper method 273 string GetPhaseName(shelloutEvent event); 274 275 // execute the given command with the supplied parameters, which have already 276 // been expanded as per the docs, and immediately return true without waiting 277 // for completion of the process. if the command can't be run other than for 278 // one of the reasons documented below, return false (in which case CollectCommand 279 // should return -2 if called later on). 280 // 281 // if shellouts are not supported, or access to the executable is prohibited by 282 // POV-Ray internal rules (not the OS), throw a kCannotOpenFile exception with an 283 // appropriate message. you should also throw a (different) exception if a process 284 // is still running. any exception thrown will cancel a render (including remaining 285 // frames of an animation) and the fatal error shellout (if defined) will not be 286 // called. 287 // 288 // you should reap any processes in your destructor in case CollectCommand doesn't 289 // get called. 290 // 291 // if the platform implemeting a subclass of this method has the equivalent of a 292 // system log (e.g. syslog on unix, event log on windows), the implementation should 293 // consider providing a user-controllable option to log any commands using such. 294 virtual bool ExecuteCommand(const string& cmd, const string& params); 295 296 // shutdown any currently-running shellouts. if force is true, force them to exit. 297 // in either case, don't wait more than timeout milliseconds. return true if there 298 // are no more processes running afterwards. 299 virtual bool KillCommand(int timeout, bool force = false) { return true; } 300 301 // returns true if a shellout is currently running. if this method is being called in an 302 // event loop to wait until a shellout has completed, it is the responsibility of the event 303 // loop to perform appropriate sleeps to avoid wasting CPU time. platforms that implement 304 // shellout support MUST override this method to return an appropriate value by actually 305 // checking the process each time it's called. CommandRunning(void)306 virtual bool CommandRunning(void) { return false; } 307 308 // if no process is running or has already been reaped, return -2. if a process 309 // is still running, return -1. if the process is complete, place the output into 310 // output then return the process's exit code. if the process failed, it would help 311 // if the output string included (or was only) stderr, but this is not a requirement. 312 // 313 // if the platform does not support capturing output of processes (or the 314 // processes are GUI-based), there is no requirement to return any output. CollectCommand(string & output)315 virtual int CollectCommand(string& output) { return -2; } CollectCommand(void)316 virtual int CollectCommand(void) { return -2; } 317 318 // return true if the requested shellout command is permitted. this method is 319 // called just before a shellout runs. if it fails, an exception will generally 320 // be thrown by the caller (the method itself should not throw an exception). CommandPermitted(const string & command,const string & parameters)321 virtual bool CommandPermitted(const string& command, const string& parameters) { return true; } 322 323 // called by the internal parser during construction to separate commands from parameters. 324 // given a raw string in the form returned from the POV INI file, extract the command and any parameters. 325 // the default version of this method should suffice for most implementations, however some platforms 326 // may need different treatment of quotes or escapes. for example, the windows platform may wish to 327 // provide a special-case for strings that look like windows paths, and exempt them from escaping of 328 // the backslash. 329 // 330 // the default method will trim the source and then search it for the first whitespace character 331 // that is both outside of a quoted string and not escaped. this forms the boundary between the 332 // command and the parameters (if not found, the entire source is considered the command). the code 333 // accepts single and double quotes as acceptable delimiters. if quotes are found around the command, 334 // they are removed. no other quotes (including any within the parameters) will be altered. 335 // 336 // when parsing the command portion of the string, any backslashes found are considered to be escapes 337 // which remove the special meaning of the following character. the escape is removed and the next 338 // character will not be subject to special interpretation (this affects single quote, double quote, 339 // backslashes, and any whitespace characters. any escapes in the text after the point where the 340 // parameters start will not be removed. 341 // 342 // this method should return true if the command is non-empty upon completion. 343 virtual bool ExtractCommand(const string& src, string& command, string& parameters) const; 344 345 private: 346 bool processStartRequested; 347 shelloutEvent postProcessEvent; 348 349 bool HandleProcessEvent(shelloutEvent which, bool internalCall); 350 bool PostProcessEvent(void); 351 }; 352 353 } 354 355 #endif // POVRAY_FRONTEND_SHELLOUTPROCESSING_H 356