Jump to content

Unzipping a file downloaded to a server using ssh2_exec


Recommended Posts

Hi,

 

Here's my script:

 

ssh2_exec($connection, 'wget http://file.zip);
ssh2_exec($connection, 'unzip file.zip);

 

 

I've even tried:

 

ssh2_exec($connection, 'wget http://unzip.zip);
ssh2_exec($connection, 'wget http://.../unzip');
ssh2_exec($connection, 'chmod +x unzip');
ssh2_exec($connection, './unzipr &');

 

Untar has "unzip unzip.zip" in it.

 

Nothing works. :(

When I login manually to the SSH via PuTTY - the unzipping works, of course.

 

what can I do?

First, try using the absolute paths to the programs...aka /usr/bin/wget. you can find the full paths by PUTTYing in and running:

which wget

do this for unzip too

 

next step is to look at the output from the STDOUT and STDERR streams...more on that here:

http://us2.php.net/manual/en/function.ssh2-fetch-stream.php

Let's try a different approach. Here is a class I wrote a while back.

 

#1) Create a file for the class...let's call it ssh.class.php, and put this in it:

<?php


/**************************************************************************************************
* SSH Class
* 
* @author Aaron Rhodes (aaron@vectorloft.com)
* @version 1.0
* 
* This class was writen to be used in conjuction with the SSH2 PECL extension. It does a pretty 
* good job handling bi-directional pipeing in the cmd() function. If you find ways to improve 
* this class, please notify me so I can spread the word.
* 
* NOTE: PasswordAuthentication must be set to yes in the sshd_config file of the server the 
*       script is connecting to.
*************************************************************************************************/
class ssh {
  
  protected static $instances = array();

  /***************************************************************************
   * CONSTANTS
   * 
   * DEFAULT_TIMEOUT: Default Timeout for commands (20 seconds) 
   **************************************************************************/
  const DEFAULT_TIMEOUT = 20;

  /***************************************************************************
   * MEMBERS
   **************************************************************************/
  protected $host;
  protected $port;
  protected $user;
  protected $pass;
  protected $cnx;
  protected $stream;
  protected $timeout = self :: DEFAULT_TIMEOUT;

  /***************************************************************************
   * METHODS
   **************************************************************************/
  public static function getInstance($id = null) {
    if (empty ($id))
      $id = 0;
    if (isset (self :: $instances[$id]))
      return self :: $instances[$id];
    return false;
  }

  public function registerInstance($id = null) {
    if (empty ($id)) {
      if (isset (self :: $instances[0]))
        throw neException("Default instance already exists");
      self :: $instances[0] = $this;
      return true;
    }
    if (isset (self :: $instances[$id]))
      throw new Exception("Instance '{$id}' already exists");
    self :: $instances[$id] = $this;
    return true;
  }

  #--------------------------------------------------------------------------------------------------
  # __construct(string $host,[string $port])
  #
  #     HOST - Hostname used for the ssh connection. IP address is also acceptable.
  #     PORT - (Optional) Port to use for the ssh connection. If omitted, the default port (22) will
  #            be used
  #
  #       Class constructor. Checks the SSH2 extension is loaded then executes the ssh2_connect
  #       command to establish a connection with the specified host at the specified port.
  #--------------------------------------------------------------------------------------------------
  public function __construct($host, $port = '22') {
    //Check to make sure the SSH2 
    if (!extension_loaded('ssh2'))
      throw new Exception("SSH2 Extension must be installed");
    //Save host/port
    $this->host = $host;
    if (strlen($port))
      $this->port = $port;
    //Establish SSH connection
    if (!($this->cnx = ssh2_connect($this->host, $this->port)))
      throw new Exception("Connection failed to host '{$this->host}:{$this->port}'");
  }

  #--------------------------------------------------------------------------------------------------
  # boolean getFingerprint()
  #
  #       Returns the fingerprint of the remote server.
  #--------------------------------------------------------------------------------------------------
  public function getFingerprint() {
    return @ ssh2_fingerprint($this->cnx);
  }

  #--------------------------------------------------------------------------------------------------
  # boolean auth(string $user,[string $password])
  #
  #     USER - Username used to authenticate the current connection.
  #     PASSWORD - (Optional) Password used to authenticate user at the current connection. If 
  #            omitted, no password is used durring authentication.
  #
  #       Tries to authenticate with the specified user and optional password. If successful, it 
  #       opens a shell and stores the stream into a variable. Returns true if sucessful, false if 
  #       there is an error.
  #--------------------------------------------------------------------------------------------------
  public function auth($user, $password = null) {
    if (!@ ssh2_auth_password($this->cnx, $user, $password))
      throw new Exception("Authorization for '$user' failed at host '{$this->host}:{$this->port}'");
    $this->user = $user;
    $this->pass = $password;
    return true;
  }

  #--------------------------------------------------------------------------------------------------
  # boolean setTimeout([int seconds])
  #
  #     SECONDS - (Optional) Number of seconds until a cmd() command times out.
  #
  #       Sets the timeout variable used in the cmd() function. If seconds is specified, it confirms 
  #       the value is an integer then sets the timeout. If ommited, it sets timeout to the default 
  #       value. Returns true if successful, otherwise returns false.
  #--------------------------------------------------------------------------------------------------
  public function setTimeout($seconds = self :: DEFAULT_TIMEOUT) {
    if (is_numeric($seconds) && $seconds > 0)
      return $this->timeout = $seconds;
    return false;
  }

  #--------------------------------------------------------------------------------------------------
  # boolean cmd(string $cmd,[ref $output],[ref $error],[string $user],[string $pass])
  #
  #     CMD - Command to run on the remote system.
  #     OUTPUT - (Optional) Reference to a variable in which any output from the remote terminal will
  #            be stored.
  #     RC - (Optional) Reference to a variable in which the command's exit code will be stored.
  #     USER - (Optional) User to run the command as.
  #     PASS - (Optional) Password for the user above. If user is null but a password is specified, 
  #            sudo is assumed.
  #
  #       Runs a command on the remote system and gathers any output from the command. Returns true 
  #       if sucessful, false if there is an error.
  #--------------------------------------------------------------------------------------------------
  public function cmd($cmd, & $output = null, & $rc = null, $user = null, $pass = null) {
    //Confirm we have a stream open
    if (!$this->stream) {
      if (!($this->stream = @ ssh2_shell($this->cnx)))
        throw new Exception("Could not open a shell for '$user' at host '{$this->host}:{$this->port}'");
    }

    //Generate a random string to use as a key we can parse for.
    $prefix = md5(microtime());
    $suffix = md5(microtime());
    $fail = md5(microtime());

    //Set some variables
    $output = null;
    $rc = null;
    $start = time();

    //Generate the command
    //    It wraps the command with echo statements in order to determine the begining 
    //    and end of the output from running the command.
    $command = $cmd;
    if (strlen($user) && strlen($pass)) //Run as other user
      $command = sprintf('su %s -c %s', escapeshellarg($user), escapeshellarg($command));
    elseif (strlen($pass)) //Sudo
      $command = sprintf("sudo %s", escapeshellarg($command));
    $command = sprintf("echo %s && %s && echo %s || echo %s\n", $prefix, $command, $suffix . ':$?', $fail . ':$?');
    fwrite($this->stream, $command);

    //Start the inifinite loop
    while (1) {

      //Get some output from shell
      $output .= stream_get_contents($this->stream);

      //Flush the output
      //    Found the prefix key. Strip everything up to and including the prefix key from output
      //    The \r\n is to make sure we get the new line feed after the echo
      if (preg_match(sprintf('/%s\r?\n(.*)/s', $prefix), $output, $matches))
        $output = $matches[1];

      //Finished
      //    Found the suffix key so the command is done. Strip the suffix key and everything after from output
      if (preg_match(sprintf('/(.*)%s:(\d*)\r?\n/s', $suffix), $output, $matches)) {
        $output = $matches[1];
        $rc = $matches[2];
        return true;
      }

      //Failed
      //    Found the failed suffix key so the command errored out for some reason. 
      //    Strip the failed suffix key and everything after from output and return false.
      if (preg_match(sprintf('/(.*)%s:(\d*)\r?\n/s', $fail), $output, $matches)) {
        $output = $matches[1];
        $rc = $matches[2];
        return false;
      }

      //Check for password prompt
      if (strlen($pass) && preg_match('/password:\s*$/i', $output)) {
        $output = null;
        fwrite($this->stream, "{$pass}\n");
        $pass = null;
      }

      //Check for timeout
      if (time() > $start + $this->timeout) 
        throw new Exception("Command '{$cmd}' timed out");

      //Sleep for a micro second to save the processor
      usleep(1);
    }

    //If we get here something weird happened.
    return false;
  }

  #--------------------------------------------------------------------------------------------------
  # boolean su_cmd(string $cmd,string $user,string $pass,[ref $output],[ref $error])
  #
  #     CMD - Command to run on the remote system.
  #     USER - User to run the command as.
  #     PASS - Password for the user above.
  #     OUTPUT - (Optional) Reference to a variable in which any output from the remote terminal will
  #            be stored.
  #     RC - (Optional) Reference to a variable in which the command's exit code will be stored.
  #
  #       Runs the cmd() function.
  #--------------------------------------------------------------------------------------------------
  public function su_cmd($cmd, $user, $pass, & $output = null, & $rc = null) {
    return $this->cmd($cmd, $output, $rc, $user, $pass);
  }

  #--------------------------------------------------------------------------------------------------
  # boolean sudo_cmd(string $cmd,string $pass,[ref $output],[ref $error])
  #
  #     CMD - Command to run on the remote system.
  #     OUTPUT - (Optional) Reference to a variable in which any output from the remote terminal will
  #            be stored.
  #     RC - (Optional) Reference to a variable in which the command's exit code will be stored.
  #
  #       Runs the cmd() function.
  #--------------------------------------------------------------------------------------------------
  public function sudo_cmd($cmd, & $output = null, & $rc = null) {
    return $this->cmd($cmd, $output, $rc, null, $this->pass);
  }

  #--------------------------------------------------------------------------------------------------
  # boolean put(string $local,string $remote,[int $mode])
  #
  #     LOCAL - Local path to the file that will be sent.
  #     REMOTE - Remote path where the file being sent will be stored.
  #     MODE - (Optional) Mode to create the file with. If ommited a default of 700 is used.
  #
  #       Checks to make sure the local file exists then sends it via SCP.  Returns true if 
  #       sucessful, false if there is an error.
  #--------------------------------------------------------------------------------------------------
  public function put($local, $remote, $mode = 0700) {
    if (!is_file($local))
      throw new Exception("Tried sending '{$local}' but it does not exist");
    if (!$remote)
      $remote = basename($local);
    if (@ ssh2_scp_send($this->cnx, $local, $remote, $mode))
      return true;
    throw new Exception("Failed to send '{$local}'");
  }

  #--------------------------------------------------------------------------------------------------
  # boolean get(string $remote,string $local)
  #
  #     REMOTE - Remote path to the file being received.
  #     LOCAL - Local path where the file being received will be stored.
  #
  #       Receives a file via SCP. Returns true if sucessful, false if there is an error.
  #--------------------------------------------------------------------------------------------------
  public function get($remote, $local) {
    if (@ ssh2_scp_recv($this->cnx, $remote, $local))
      return true;
    throw new Exception("Failed to get '{$remote}'");
  }
}
?>

 

now, update your script to use the class. it should look something like:

<?php
//Include the class
require_once('ssh.class.php');

//Connect
$ssh = new ssh($hostname);
if (!$ssh->auth($user,$pass))
  die("Failed to authenticate as '{$user}'\n");

//Get the file
$output = $rc = null;
if(!$ssh->cmd('/usr/bin/wget http://file.zip', $output, $rc))
  die("Failed to run command {$cmd}");
print "<pre>Return Code: {$rc}\n======= Output =======\n$output\n======================\n</pre>";

//Unzip it
$output = $rc = null;
if(!$ssh->cmd('/usr/bin/unzip file.zip', $output, $rc))
  die("Failed to run command {$cmd}");
print "<pre>Return Code: {$rc}\n======= Output =======\n$output\n======================\n</pre>";
?>

ssh is a class. once you connect to the host and auth as a user, you will have a continues shell. so you can use cd for instance, and the next command will be run for that directory. $output is the output of the command obviously. $rc is the return code, so you can programatically check to see if the command completed successfully. the rest of the functions are pretty well explained (in my opinion)...let me know if you have any specific questions though

 

edit: make sure you look at the get/put functions too which use scp to transfer files to/from the remote system

Okay, Thanks!

 

Now, when I try to use your class, it says:

 

 

Fatal error: Uncaught exception 'Exception' with message 'Command '/usr/bin/unzip samp.zip' timed out' in /home/amirtuk/public_html/db/ssh.class.php:209 Stack trace: #0 /home/amirtuk/public_html/db/install_samp.php(98): ssh->cmd('/usr/bin/unzip ...', 'Archive: samp....', NULL) #1 {main} thrown in /home/amirtuk/public_html/db/ssh.class.php on line 209

 

 

what is it?

i use Exceptions, it's a way of error handling. when my class find an error, you can 'catch' it. don't worry so much about that.

 

your problem is that the default timeout for commands is 20 seconds. is this zip file huge and takes longer then that? assuming it is, no problem, just change the timeout to longer:

//Unzip it
$ssh->setTimeout(120); //120 seconds
$output = $rc = null;
if(!$ssh->cmd('/usr/bin/unzip file.zip', $output, $rc))
  die("Failed to run command {$cmd}");
print "<pre>Return Code: {$rc}\n======= Output =======\n$output\n======================\n</pre>";
$ssh->setTimeout(); //Return it back to the default

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.