3
\$\begingroup\$

I wanted a template for a service script that is versatile and easily configurable.

Along with producing similar outputs to the SSH and Samba daemons, I request the code to be as clean, simple and self explanatory as possible. Hence I removed all the comments.

Sample usage:

$ service mydaemon restart
mydaemon stop/waiting
mydaemon start/running, process 30549

Sample of similar command with the SSH daemon:

$ service ssh restart
ssh stop/waiting
ssh start/running, process 30577

Here is my service.php script:

#!/usr/bin/php
<?php

    define('NAME',      basename($argv[0]));
    define('RUNAS',     'root');
    define('DIR',       __DIR__);
    define('CMD',       __DIR__.'/thread.php');
    define('SUDO',      'sudo -u '.RUNAS.' ');
    define('LOG_FILE',  '/dev/null');
    define('PID_FILE',  '/var/run/'.NAME.'.pid');

    function writeln($string)
    {
        echo $string.PHP_EOL;
    }

    class service
    {
        public static function unset_pid()
        {
            if (is_file(PID_FILE)) unlink(PID_FILE);
        }

        public static function get_pid()
        {
            if (!is_file(PID_FILE)) return null;

            $pid=file_get_contents(PID_FILE);

            $process_status=null;
            if (!empty($pid) && ctype_digit($pid)) $process_status=shell_exec('ps '.$pid);

            if (empty($process_status) || strpos($process_status, CMD, 0)===false)
            {
                service::unset_pid();
                return null;
            }

            return $pid;
        }

        public static function is_running()
        {
            return (!empty(service::get_pid()));
        }

        public static function start()
        {
            if (service::is_running())
            {
                writeln('start: Job is already running: '.NAME);
                return true;
            }

            $new_pid=shell_exec('cd '.DIR.';'.SUDO.CMD.' >> '.LOG_FILE.' 2>&1 & printf $!;');
            file_put_contents(PID_FILE, $new_pid);
            service::status();

            return service::is_running();
        }

        public static function stop()
        {
            if (!service::is_running())
            {
                writeln('stop: Unknown instance:');
                return true;
            }

            shell_exec('kill '.service::get_pid());

            for ($seconds=0; $seconds<60; $seconds++)
            {
                if (!service::is_running()) break;
                sleep(1);
            }

            if (service::is_running())
            {
                writeln('Not stopped; may still be shutting down or shutdown may have failed.');
                return false;
            }

            service::status();

            return !service::is_running();
        }

        public static function restart()
        {
            if (!service::stop())
            {
                writeln('Aborting restart.');
                return false;
            }

            return service::start();
        }

        public static function status()
        {
            $running=service::is_running();
            if ($running) writeln(NAME.' start/running, process '.service::get_pid()); else writeln(NAME.' stop/waiting');
            return $running;
        }

    };

    if (count($argv)!=2)
    {
        if (count($argv)>2) writeln('Too many arguments given.');
        $argv=array($argv[0], '');
    }

    switch (strtolower($argv[1]))
    {   
        case 'start':

            service::start();
            break;

        case 'stop':

            service::stop();
            break;

        case 'restart':

            service::restart();
            break;

        case 'status':

            service::status();
            break;

        default:

            if (!empty($argv[1])) writeln('Unknown option: '.$argv[1]);
            writeln('Usage: '.$argv[0].' {start|stop|restart|status}');
            break;
    }

?>

Any suggestions for improvements will be appreciated.

\$\endgroup\$
4
  • 4
    \$\begingroup\$ Until there are answers posted, OP is allowed to modify his code. Comments are not answers. \$\endgroup\$
    – Dan
    Apr 8, 2016 at 8:16
  • 1
    \$\begingroup\$ Imagine if someone said the code was broken in a comment, the question would have to be closed forever because you can't edit the code, it would invalidate the comment... that'd be backwards \$\endgroup\$
    – Pimgd
    Apr 8, 2016 at 8:19
  • \$\begingroup\$ Tho things to consider - why you need /etc/init.d script, if you can add this in /etc/rc.local . Why you need this made in PHP and not in shell \$\endgroup\$
    – Nick
    Apr 8, 2016 at 8:28
  • \$\begingroup\$ I need the daemon to be remotely stopped, have some files sync to the remote server then have the daemon start again. I chose PHP because bash scripts are messy. \$\endgroup\$ Apr 8, 2016 at 15:39

1 Answer 1

2
\$\begingroup\$

from stop:

        if (service::is_running())
        {
            writeln('Not stopped; may still be shutting down or shutdown may have failed.');
            return false;
        }

        service::status();

        return !service::is_running();

from start:

        service::status();

        return service::is_running();

Hmm, looks like a similar pattern... Can we combine this in a separate function?

What does service::status do?

    public static function status()
    {
        $running=service::is_running();
        if ($running) writeln(NAME.' start/running, process '.service::get_pid()); else writeln(NAME.' stop/waiting');
        return $running;
    }

... Wait, it prints a few things and then returns whether it's running.

So why the double is_running call?

I'd just return the result of service::status in these cases. It'd be more correct too; what if a service stops between your logging and the return statement? You'd log that it's running and then return that it's ... not running. Good way to have logs showing "everything is fine" whilst things aren't doing what they're supposed to do.

\$\endgroup\$
1
  • \$\begingroup\$ Oh, well spotted. return service::status(); is logically correct. \$\endgroup\$ Apr 8, 2016 at 15:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.