Note: I'm migrating from gonzalo123.com to here. When I finish I'll swap the DNS to here. The "official" blog will be always gonzalo123.com

      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.

      comments powered by Disqus