Question

I'm tearing my hair trying to figure out why this could possibly not work when it works when executing from the command line. I'm sending a POV-Ray animation to the user after it is done rendering and compiling the frames into a GIF using ImageMagick. I have the email that is sent through mutt sleep for an hour so that it allows time for the user animation to be compiled. (I've changed it to 120 seconds however so that I don't have to wait an hour to troubleshoot. In that respect I also made the animation only have 3 frames until I figure out the issue.)

A new bash file, animation_done_bash.sh, is created each time a user clicks animate on my site which in turn creates a new folder that stores the frames for the animation and with animation_done_bash.sh in it. To make the PHP page advance to the next page the bash file is executed in dev null. I already tested if that was the problem on the command line and it works there so I really have no idea what is the issue.

Below is the code and the output (I echoed this out onto my php page to see what was wrong) that is produced that is being executed with shell_exec:

$animation_done_email = "#!/usr/bin/bash \nsleep 120; mutt -s \"Animation Finished\" -a animation.gif -- ".$email." <email.txt;";

        /*Creates new animation_done_bash each time user hits animate*/
        $directory_bash = "./User_Files/Animation/".$location."/";
        $filename_bash = 'animation_done_bash.sh';  
        $handler_bash = fopen($directory_bash.$filename_bash, "w");  
        fwrite($handler_bash, $animation_done_email);  
        fclose($handler_bash);

        /*Need to change the permissions on bash file so it can execute*/
        chmod($directory_bash."/animation_done_bash.sh", 0777);

        $command_5 = 'cd /home/ouraccount/public_html/User_Files/Animation/'.$location.'/; bash animation_done_bash.sh > /dev/null 2>/dev/null &';
        $shellOutput = shell_exec($command_5);

Where $email is the user's email and $location is the folder in which the frames are stored. email.txt is stored within the same folder.

Output: cd /home/ouraccount/public_html/User_Files/Animation/ani_51/; bash animation_done_bash.sh > /dev/null 2>/dev/null &

Any guidance would be much appreciated. Thanks!

Was it helpful?

Solution

In this kind of situations pushing (invoking a script when the rendering operation is complete) is preferrable to polling (periodically checking if the rendering operation is complete).

If you cannot push, do it in one language only, don't create a hybrid of bash and PHP. Here is are 2 examples you could try which might suit your situation:

Example if rendering command returns after finishing:

   <?php
   /** PHP wrapper around rendering command X that mails the output **/
   // Don't write attachment code yourself, use a class like PHPMailer: https://github.com/Synchro/PHPMailer
   require_once('class.phpmailer.php');

   // Ignore user browser close, rendering takes a long time
   ignore_user_abort(true);
   // On windows you'll also need to set_time_limit to something large. On Unix PHP doesn't count things like database queries and shell commands, on Windows it does

   // Execute render command, don't forget to escape if you use user input
   // Script will only continue once renderer returns. If renderer return before rendering is finished you cannot use this
   $render_output = shell_exec('renderer input.file output.file');
   // Could also be done in PHP for finer control and error handling
   $imagemagick_output = shell_exec("convert output.file animation.gif");
   unlink("output.file");

   $mail = new PHPMailer();
   $mail->addAttachment('animation.gif');
   // etc.

   unlink("animation.gif");
   ?>

Example if rendering command returns before finishing:

   <?php
   /** PHP wrapper around rendering command X that mails the output **/
   // Don't write attachment code yourself, use a class like PHPMailer: https://github.com/Synchro/PHPMailer
   require_once('class.phpmailer.php');

   // Ignore user browser close
   ignore_user_abort(true);

   // Execute render command, don't forget to escape if you use user input
   // If renderer returns before file is finished use this
   $render_output = shell_exec('renderer input.file output.file 2> error.file');

   // Wait for rendering to finish
   // Implement these function, e.g. file_exists for output.file or error.file
   while(!rendering_has_failed() && !rendering_is_finished()) {
        sleep(15*60);    // Low-resource wait for 15 minutes
   }

   // Could also be done in PHP for finer control and error handling
   $imagemagick_output = shell_exec("convert output.file animation.gif");
   unlink("output.file");

   $mail = new PHPMailer();
   $mail->addAttachment('animation.gif');
   // etc.

   unlink("animation.gif");
   ?>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top