Main Page | Namespace List | File List | File Members

util.php

Go to the documentation of this file.
00001 <?
00002 /**
00003  * General utility functions
00004  *
00005  * This file contains convenience functions used throughout the package. 
00006  *
00007  * @author José R. Valverde <jrvalverde@cnb.uam.es>
00008  * @version 1.0
00009  * @copyright CSIC - GPL
00010  */
00011 
00012 /**
00013  * include global configuration definitions
00014  */
00015 require_once("./config.php");
00016 
00017 // ------------- COMMODITY ROUTINES ----------------
00018 
00019 /**
00020  * Start the display of a www page
00021  *
00022  * We have it as a function so we can customise all pages generated as
00023  * needed. This routine will open HTML, create the page header, and 
00024  * include any needed style sheets (if any) to provide a common 
00025  * look-and-feel for all pages generated.
00026  */
00027 function set_header()
00028 {
00029     global $app_name, $app_dir;
00030     // Start HTML vx.xx output
00031     echo "<html>";
00032     // Print headers
00033     echo "<head>";
00034     echo "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=ISO-8859-1\">";
00035     echo "<meta name=\"DESCRIPTION\" content=\"a web interface to run TINKER molecular modelling on the EGEE grid\">";
00036     echo "<meta name=\"AUTHOR\" content=\"EMBnet/CNB\">";
00037     echo "<meta name=\"COPYRIGHT\" content=\"(c) 2004-5 by CSIC - Open Source Software\">";
00038     echo "<meta name=\"GENERATOR\" content=\"$app_name\">";
00039     echo "<link rel=\"StyleSheet\" href=\"$app_dir/style/style.css\" type=\"text/css\"/>";
00040     echo "<link rel=\"shortcut icon\" href=\"$app_dir/images/favicon.ico\"/>";
00041     echo "<title=\"$app_name\">";
00042     echo "</head>";
00043     // Prepare body
00044     echo "<body bgcolor=\"white\" background=\"$app_dir/images/6h2o-w-small.gif\" link=\"ffc600\" VLINK=\"#cc9900\" ALINK=\"#4682b4\">";
00045 }
00046 
00047 /**
00048  * close a web page
00049  *
00050  * Make sure we end the page with all the appropriate formulisms:
00051  * close the body, include copyright notice, state creator and
00052  * any needed details, and close the page.
00053  */
00054 function set_footer()
00055 {
00056         global $maintainer, $app_dir;
00057         // close body
00058         echo "</body><hr>";
00059         // footer
00060         echo "<center><table border=\"0\" width=\"90%\"><tr>";
00061             // Copyright and author
00062             echo "<td><A HREF=\"$app_dir/c/copyright.html\">&copy;</a>EMBnet/CNB</td>";
00063             // contact info
00064             echo "<td align=\"RIGHT\"><A HREF=\"emailto:$maintainer\">$maintainer</a></td>";
00065         echo "</tr></table></center>";
00066         // Page
00067         echo "</html>";
00068 }
00069 
00070 /**
00071  * print a warning
00072  *
00073  * Prints a warning in a separate pop-up window.
00074  * A warning is issued when a non-critical problem has been detected.
00075  * Execution can be resumed using some defaults, but the user should
00076  * be notified. In order to not disrupt the web page we are displaying
00077  * we use a JavaScript pop-up alert to notify the user.
00078  *
00079  * @param msg the warning message to send the user
00080  */
00081 function warning($msg)
00082 {
00083     echo "<script language=\"JavaScript\">";
00084     echo "alert(\"WARNING:\n $msg\");";
00085     echo "</script>";
00086 }
00087 
00088 /**
00089  * print an error message and exit
00090  *
00091  * Whenever we detect something wrong, we must tell the user. This function
00092  * will take an error message as its argument, format it suitably and
00093  * spit it out.
00094  *
00095  * @note This might look nicer using javascript to pop up a nice window with
00096  * the error message. Style sheets would be nice too.
00097  *
00098  * @param where the name of the caller routine or the process where the
00099  *              error occurred
00100  * @param what  a description of the abnormal condition that triggered
00101  *              the error
00102  */
00103 
00104 function error($where, $what)
00105 {
00106         // format the message
00107         echo "<p></p><center><table border=\"2\">\n";
00108         echo "<tr><td><center><font color=\"red\"><strong>\n";
00109 
00110         // output the message
00111         echo "ERROR - HORROR\n";
00112 
00113         // close format
00114         echo "</strong></font></center></td></tr>\n";
00115         echo "<tr><td><center><b>$where</b></center></td></tr>\n";
00116         echo "<tr><td><center>$what</center></td></tr>\n";
00117         echo "</table></center><p></p>\n";
00118 }
00119 
00120 /**
00121  * print a letal error message and die
00122  *
00123  * This function is called whenever a letal error (one that prevents
00124  * further processing) is detected. The function will spit out an
00125  * error message, close the page and exit the program.
00126  * It should seldomly be used, since it may potentially disrupt the
00127  * page layout (e.g. amid a table) by not closing open tags of which
00128  * it is unaware.
00129  * Actually it is a wrapper for error + terminate.
00130  *
00131  * @param where location (physical or logical) where the error was
00132  * detected: a physical location (routine name/line number) may be
00133  * helpful for debugging, a logical location (during which part of
00134  * the processing it happened) will be more helful to the user.
00135  *
00136  * @param what a descrition of the abnormality detected.
00137  */
00138 function letal($what, $where)
00139 {
00140         error($what, $where);
00141         set_footer();
00142         exit();
00143 }
00144 
00145 // ------------- SSH ROUTINES ----------------
00146 
00147 /**
00148  *  Execute a single command remotely using ssh and 
00149  * return its entire output (like passthru)
00150  *
00151  *  This might be done as well using a pipe on /tmp and
00152  * making the command 'cat' the pipe: when ssh runs, it
00153  * runs the command 'cat' on the pipe and hangs on read.
00154  *  Then we just need a thread to open the pipe, put the
00155  * password and close the pipe.
00156  *  This other way the password is never wirtten down.
00157  * But, OTOH, the file life is so ephemeral that most
00158  * of the time it will only exist in the internal system
00159  * cache, so this approach is not that bad either.
00160  *
00161  *  @param remote   The remote end to run the command, in
00162  *                  the form 'user@host' (or 'host' if the
00163  *                  username is the same).
00164  *  @param password The remote password. Note that if direct
00165  *                  RSA/DSA/.shosts/.rhosts login is enabled
00166  *                  then the password should be ignored as
00167  *                  SSH should not run the ASKPASS command).
00168  *  @param command  The command to execute on the remote end
00169  *                  NOTE: if you want to use redirection, the
00170  *                  entire remote command line should be 
00171  *                  enclosed in additional quotes!
00172  *  @param status   Optional, this will hold the termination
00173  *                  status of SSH after invocation, which
00174  *                  should be the exit status of the remote
00175  *                  command or 255 if an error occurred
00176  *  @return output  The output of the remote command.
00177  */
00178 function ssh_passthru($remote, $password, $command, $status)
00179 {
00180         umask(0077);
00181         $tmpfname = tempnam("/tmp", "egTinker");
00182         chmod($tmpfname, 0700);
00183         $fp = fopen($tmpfname, "w");
00184         fputs($fp, "#!/bin/sh\necho $password\n");
00185         fputs($fp, "rm -f $tmpfname\n");
00186         fclose($fp);
00187         putenv("DISPLAY=none:0.");
00188         putenv("SSH_ASKPASS=$tmpfname");
00189         return passthru("ssh -x -t -t $remote $command", $status);
00190 }
00191 
00192 // Do a remote scp connection with local password
00193 /**
00194  *  Copy a file or directory from one source to a destination
00195  *
00196  *  This function copies source to dest, where one of them is a
00197  * local filespec and the other a remote filespec of the form
00198  * [user@]host:path
00199  *
00200  *  If the original source is a directory, it will be copied
00201  * recursively to destination (hence easing file transfers).
00202  *
00203  *  The function returns TRUE on success or FALSE on failure.
00204  *
00205  */
00206 function ssh_copy($origin, $destination, $password)
00207 {
00208         umask(0077);
00209         $tmpfname = tempnam("/tmp", "egTinker");
00210         chmod($tmpfname, 0700);
00211         $fp = fopen($tmpfname, "w");
00212         fputs($fp, "#!/bin/sh\necho $password\n");
00213         fputs($fp, "rm $tmpfname\n");
00214         fclose($fp);
00215         putenv("DISPLAY=none:0.");
00216         putenv("SSH_ASKPASS=$tmpfname");
00217         system("scp -pqrC $origin $destination", $status);
00218         if ($status == 0)
00219             return TRUE;
00220         else
00221             return FALSE;
00222 }
00223 
00224 function ssh_open($remote, $password)
00225 {       
00226     global $php_version;
00227     global $debug;
00228 
00229     // Open a child process with the 'proc_open' function. 
00230     //
00231     // Some tricks: we must open the connection using '-x' to disable
00232     // X11 forwarding, and use '-t -t' to avoid SSH generating an error
00233     // because we are not connected to any terminal.
00234     // NOTE:
00235     //   We require users to have an account and password on
00236     //   the UI and provide their user/password through the web or
00237     //   otherwise (e.g. using myproxy)
00238     //
00239     // NOTE: if the web server is trusted remotely (i.e. it's SSH public 
00240     // key is accepted in ~user@host:.ssh/authorized_keys) then any 
00241     // password will do.
00242     if ($php_version < 5) {
00243         // Prepare I/O
00244         $descriptorspec = array(
00245             0 => array("pipe", "r"),  // connect child's stdin to the read end of a pipe
00246             1 => array("pipe", "w"),  // connect child's stdout to the write end of a pipe
00247             2 => array("pipe", "w")   // stderr is a pipe to read from
00248         );
00249         // prepare password
00250         putenv("DISPLAY=none:0.");
00251         putenv("SSH_ASKPASS=$tmpfname");
00252         umask(0077);
00253         $tmpfname = tempnam("/tmp", "egTinker");
00254         chmod($tmpfname, 0700);
00255         $fp = fopen($tmpfname, "w");
00256         fputs($fp, "#!/bin/sh\necho $password\n");
00257         fputs($fp, "rm $tmpfname\n");
00258         fclose($fp);
00259 
00260         $process = proc_open("ssh -x -t -t $remote", 
00261                          $descriptorspec,
00262                          $pipes);
00263         if ($debug) echo "ssh -x -t -t $remote<br />\n";
00264         // check status
00265         if (!is_resource($process)) 
00266         {
00267             letal("SSH::connect", "cannot connect to the remote host");
00268             return;
00269         }
00270         if ($debug) echo "proc_open<br />\n";
00271 
00272     }
00273     else {      /* php5 -- we can use PTYs */
00274             /* XXX -- untested -- probably unneeded */
00275         $descriptorspec = array(
00276             0 => array("pty"),
00277             1 => array("pty"),
00278             2 => array("pty")
00279         );
00280         // prepare password
00281         putenv("DISPLAY=none:0.");
00282         putenv("SSH_ASKPASS=$tmpfname");
00283         umask(0077);
00284         $tmpfname = tempnam("/tmp", "egTinker");
00285         chmod($tmpfname, 0700);
00286         $fp = fopen($tmpfname, "w");
00287         fputs($fp, "#!/bin/sh\necho $password\n");
00288         fputs($fp, "rm $tmpfname\n");
00289         fclose($fp);
00290 
00291         $process = proc_open("ssh -x -t -t $remote", 
00292                          $descriptorspec,
00293                          $pipes);
00294 
00295         // check status
00296         if (!is_resource($process)) 
00297         {
00298             letal("SSH::connect", "cannot connect to the remote host");
00299             return;
00300         }
00301 
00302         $status = proc_get_status($process);
00303         if ($status->running == FALSE) {
00304             fclose($pipes[0]); fclose($pipes[1]); fclose($pipes[2]);
00305             proc_close($process);
00306             letal("SSH::connect", "connection exited ".$status->exitcode);
00307             return;
00308         }
00309         if ($status->signaled) {
00310             fclose($pipes[0]); fclose($pipes[1]); fclose($pipes[2]);
00311             proc_close($process);
00312             letal("SSH::connect", "connection terminated by ".$status->termsig);
00313             return;
00314         }
00315         if ($status->stopped) {
00316             // Tell the user and hope for the best
00317             warning("SSH::connect stopped by ".$status->stopsig.
00318             " it may still have a chance though");
00319         }
00320 
00321     }
00322 
00323     // $pipes now looks like this:
00324     //   0 => writeable handle connected to child stdin
00325     //   1 => readable handle connected to child stdout
00326 
00327     // We now have a connection to the remote Grid User Interface
00328     // Server which we may use to send commands/receive output
00329     return array('stdin' => $pipes[0],
00330                  'stdout' => $pipes[1],
00331                  'stderr' => $pipes[2] );
00332 }
00333 
00334 ?>

Generated on Thu Jun 16 11:03:56 2005 for SExec by doxygen 1.3.6