Managing Windows services with Symfony/Process and PHP


Sometimes I need to stop/start remote Windows services with PHP. It’s quite easy to do it with net commnand. This command is a tool for administration of Samba and remote CIFS servers. It’s pretty straightforward to handle them from Linux command line:

net rpc service --help
Usage:
net rpc service list
    View configured Win32 services
net rpc service start
    Start a service
net rpc service stop
    Stop a service
net rpc service pause
    Pause a service
net rpc service resume
    Resume a service
net rpc service status
    View current status of a service
net rpc service delete
    Deletes a service
net rpc service create
    Creates a service

Today we are going to create a PHP wrapper for this tool. Our NetService library will have two classes: One Parser and one Service class.

The Parser’s responsibility will be create the command line instruction. I will use Behat in the developing process of Parser class. Here we can see the feature file:

Feature: command line parser

  Scenario: net service list
    Given windows server host called "windowshost.com"
    And credentials are "myDomanin/user%password"
    And action is "list"
    Then command line is "net rpc service list -S windowshost.com -U myDomanin/user%password"

  Scenario: net service start
    Given windows server host called "windowshost.com"
    And service name called "ServiceName"
    And credentials are "myDomanin/user%password"
    And action is "start"
    Then command line is "net rpc service start ServiceName -S windowshost.com -U myDomanin/user%password"

  Scenario: net service stop
    Given windows server host called "windowshost.com"
    And service name called "ServiceName"
    And credentials are "myDomanin/user%password"
    And action is "stop"
    Then command line is "net rpc service stop ServiceName -S windowshost.com -U myDomanin/user%password"

  Scenario: net service pause
    Given windows server host called "windowshost.com"
    And service name called "ServiceName"
    And credentials are "myDomanin/user%password"
    And action is "pause"
    Then command line is "net rpc service pause ServiceName -S windowshost.com -U myDomanin/user%password"

  Scenario: net service resume
    Given windows server host called "windowshost.com"
    And service name called "ServiceName"
    And credentials are "myDomanin/user%password"
    And action is "resume"
    Then command line is "net rpc service resume ServiceName -S windowshost.com -U myDomanin/user%password"

  Scenario: net service status
    Given windows server host called "windowshost.com"
    And service name called "ServiceName"
    And credentials are "myDomanin/user%password"
    And action is "status"
    Then command line is "net rpc service status ServiceName -S windowshost.com -U myDomanin/user%password"

The implementation of the feature file:

namespace NetService;

class Parser
{
    private $host;
    private $credentials;

    public function __construct($host, $credentials)
    {
        $this->host        = $host;
        $this->credentials = $credentials;
    }

    public function getCommandLineForAction($action, $service = NULL)
    {
        if (!is_null($service)) $service = " {$service}";
        return "net rpc service {$action}{$service} -S {$this->host} -U {$this->credentials}";
    }
}

and finally our Service class:

namespace NetService;

use Symfony\Component\Process\Process,
    NetService\Parser;

class Service
{
    private $parser;
    private $timeout;

    const START = 'start';
    const STOP = 'stop';
    const STATUS = 'status';
    const LIST_SERVICES = 'list';
    const PAUSE = 'pause';
    const RESUME = 'resume';

    const DEFAULT_TIMEOUT = 3600;

    public function __construct(Parser $parser)
    {
        $this->parser = $parser;
        $this->timeout = self::DEFAULT_TIMEOUT;
    }

    public function start($service)
    {
        return $this->runProcess($this->parser->getCommandLineForAction(self::START, $service));
    }

    public function stop($service)
    {
        return $this->runProcess($this->parser->getCommandLineForAction(self::STOP, $service));
    }

    public function pause($service)
    {
        return $this->runProcess($this->parser->getCommandLineForAction(self::PAUSE, $service));
    }

    public function resume($service)
    {
        return $this->runProcess($this->parser->getCommandLineForAction(self::RESUME, $service));
    }

    public function status($service)
    {
        return $this->runProcess($this->parser->getCommandLineForAction(self::STATUS, $service));
    }

    public function listServices()
    {
        return $this->runProcess($this->parser->getCommandLineForAction(self::LIST_SERVICES));
    }

    public function isRunning($service)
    {
        $status = explode("\n", $this->status($service));
        if (isset($status[0]) && strpos(strtolower($status[0]), "running") !== FALSE) {
            return TRUE;
        } else {
            return FALSE;
        }
    }

    public function setTimeout($timeout)
    {
        $this->timeout = $timeout;
    }

    private function runProcess($commandLine)
    {
        $process = new Process($commandLine);
        $process->setTimeout($this->timeout);
        $process->run();

        if (!$process->isSuccessful()) {
            throw new RuntimeException($process->getErrorOutput());
        }

        return $process->getOutput();
    }

    private function parseStatus($status)
    {
        return explode("\n", $status);
    }
}

And that’s all. Now a couple of examples:

include __DIR__ . '/../vendor/autoload.php';

use NetService\Service,
    NetService\Parser;

$host        = 'windowshost.com';
$serviceName = 'ServiceName';
$credentials = '{domain}/{user}%{password}';

$service = new Service(new Parser($host, $credentials));

if ($service->isRunning($serviceName)) {
    echo "Service is running. Let's stop";
    $service->stop($serviceName);

} else {
    echo "Service isn't running. Let's start";
    $service->start($serviceName);
}

//dumps status output
echo $service->status($serviceName);
include __DIR__ . '/../vendor/autoload.php';

use NetService\Service,
    NetService\Parser;

$host        = 'windowshost.com';
$credentials = '{domain}/{user}%{password}';

$service = new Service(new Parser($host, $credentials));
echo $service->listServices();

You can see the full code in github here. The package is also available for composer at Packaist.

About these ads

About Gonzalo Ayuso

Web Architect specialized in Open Source technologies. PHP, Python, JQuery, Dojo, PostgreSQL, CouchDB and node.js but always learning.

Posted on October 29, 2012, in php, Technology and tagged , , , , , , . Bookmark the permalink. 1 Comment.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 949 other followers

%d bloggers like this: