package PVE::API2::Services;

use strict;
use warnings;

use PVE::Tools;
use PVE::SafeSyslog;
use PVE::Cluster;
use PVE::INotify;
use PVE::Exception qw(raise_param_exc);
use PVE::RESTHandler;
use PVE::RPCEnvironment;
use PVE::JSONSchema qw(get_standard_option);
use PVE::AccessControl;
use IO::File;

use base qw(PVE::RESTHandler);

my $service_list = {
    pveproxy => { name => 'WWW', desc => 'Web/API server' },
    postfix => { name => 'SMTP', desc => 'Simple Mail Tranfer Protocol' },
    ntpd => { name => 'NTP', desc => 'Network time protocol' },
    sshd => { name => 'SSH', desc => 'Secure shell daemon' },
    syslog => { name => 'Syslog', desc => 'Syslog daemon' },
    cron => { name => 'CRON', desc => 'Daemon to execute scheduled commands' },
    pvedaemon => { name => 'NodeManager', desc => 'PVE node manager daemon' },
    corosync => { name => 'CMan', desc => 'CMan/Corosync cluster daemon' },
    # clvm => { name => 'CLVM', desc => 'LVM cluster locking daemon' },
    rgmanager => { name => 'RGManager', desc => 'Resource Group Manager daemon' },
    pvecluster => { name => 'PVECluster', desc => 'Proxmox VE cluster file system' },
};

my $service_prop_desc = {
    description => "Service ID",
    type => 'string',
    enum => [ keys %{$service_list} ],
};

my $service_cmd = sub {
    my ($service, $cmd) = @_;

    my $initd_cmd;

    die "unknown service command '$cmd'\n"
	if $cmd !~ m/^(start|stop|restart|reload)$/;

    if ($service eq 'postfix') {
	$initd_cmd = '/etc/init.d/postfix';
    } elsif ($service eq 'pvecluster') {
	if ($cmd eq 'restart') {    
	    $initd_cmd = '/etc/init.d/pve-cluster';
	} else {
	    die "invalid service cmd 'pve-cluster $cmd': ERROR";
	}
    } elsif ($service eq 'pvedaemon') {
	if ($cmd eq 'restart') {    
	    $initd_cmd = '/etc/init.d/pvedaemon';
	} else {
	    die "invalid service cmd '$service $cmd': ERROR";
	}
    } elsif  ($service eq 'pveproxy') {
	if ($cmd eq 'restart') {    
	    $initd_cmd = '/etc/init.d/pveproxy';
	} else {
	    die "invalid service cmd '$service $cmd': ERROR";
	}
    } elsif  ($service eq 'ntpd') {
	# debian start/stop scripts does not work for us
	if ($cmd eq 'stop') {
	    system ('/etc/init.d/ntp stop');
	    #system ('/usr/bin/killall /usr/sbin/ntpd'); 
	} elsif ($cmd eq 'start') {
	    system ('/etc/init.d/ntp start');
	    system ('/sbin/hwclock --systohc');
	} elsif ($cmd eq 'restart') {
	    system ('/etc/init.d/ntp restart');
	    system ('/sbin/hwclock --systohc');
	    # restart cron/syslog to get right schedules and log time/dates
	    system ('/etc/init.d/rsyslog restart');
	    system ('/etc/init.d/cron restart');
	}
	return 0;
    } elsif  ($service eq 'syslog') {
	$initd_cmd = '/etc/init.d/rsyslog';
    } elsif  ($service eq 'cron') {
	$initd_cmd = '/etc/init.d/cron';
    } elsif  ($service eq 'corosync') {
	$cmd = 'start' if $cmd eq 'restart';
	$initd_cmd = '/etc/init.d/cman';
    } elsif  ($service eq 'clvm') {
	$initd_cmd = '/etc/init.d/clvm';
    } elsif  ($service eq 'rgmanager') {
	$initd_cmd = '/etc/init.d/rgmanager';
    } elsif  ($service eq 'sshd') {
	$initd_cmd = '/etc/init.d/ssh';
    } else {
	die "unknown service '$service': ERROR";
    }    

    PVE::Tools::run_command ([$initd_cmd, $cmd]);
};

my $service_state = sub {
    my ($service) = @_;

    my $pid_file;

    if ($service eq 'postfix') {
	$pid_file = '/var/spool/postfix/pid/master.pid';
    } elsif  ($service eq 'pveproxy') {
	$pid_file = '/var/run/pveproxy/pveproxy.pid';
    } elsif  ($service eq 'pvedaemon') {
	$pid_file = '/var/run/pvedaemon.pid';
    } elsif  ($service eq 'pvecluster') {
	$pid_file = '/var/run/pve-cluster.pid';
    } elsif  ($service eq 'ntpd') {
	$pid_file = '/var/run/ntpd.pid';
    } elsif  ($service eq 'sshd') {
	$pid_file = '/var/run/sshd.pid';
    } elsif  ($service eq 'cron') {
	$pid_file = '/var/run/crond.pid';
    } elsif  ($service eq 'corosync') {
	$pid_file = '/var/run/corosync.pid';
    } elsif  ($service eq 'clvm') {
	$pid_file = '/var/run/clvmd.pid';
    } elsif  ($service eq 'rgmanager') {
	$pid_file = '/var/run/rgmanager.pid';
    } elsif  ($service eq 'syslog') {
	$pid_file = '/var/run/rsyslogd.pid';
    } else {
	die "unknown service '$service': ERROR";
    }    

    my $pid;
    if (my $fh = IO::File->new ($pid_file, "r")) {
	my $line = <$fh>;
	chomp $line;
	    
	if ($line  && ($line =~ m/^\s*(\d+)\s*$/)) {
	    $pid = $1;
	}
    }

    return 'running' if ($pid && kill (0, $pid));

    return 'stopped';
};

__PACKAGE__->register_method ({
    name => 'index', 
    path => '', 
    method => 'GET',
    permissions => {
	check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
    },
    description => "Service list.",
    proxyto => 'node',
    protected => 1,
    parameters => {
    	additionalProperties => 0,
	properties => {
	    node => get_standard_option('pve-node'),
	},
    },
    returns => {
	type => 'array',
	items => {
	    type => "object",
	    properties => {},
	},
	links => [ { rel => 'child', href => "{service}" } ],
    },
    code => sub {
	my ($param) = @_;
  
	my $res = [];

	foreach my $id (keys %{$service_list}) {
	    push @$res, { 
		service => $id,
		name => $service_list->{$id}->{name},
		desc => $service_list->{$id}->{desc},
		state => &$service_state($id),
	    };
	}

	return $res;
    }});

__PACKAGE__->register_method({
    name => 'srvcmdidx',
    path => '{service}', 
    method => 'GET',
    description => "Directory index",
    permissions => {
	check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
    },
    parameters => {
    	additionalProperties => 0,
	properties => {
	    node => get_standard_option('pve-node'),
	    service => $service_prop_desc,
	},
    },
    returns => {
	type => 'array',
	items => {
	    type => "object",
	    properties => {
		subdir => { type => 'string' },
	    },
	},
	links => [ { rel => 'child', href => "{subdir}" } ],
    },
    code => sub {
	my ($param) = @_;

	my $res = [
	    { subdir => 'state' },
	    { subdir => 'start' },
	    { subdir => 'stop' },
	    { subdir => 'restart' },
	    { subdir => 'reload' },
	    ];
	
	return $res;
    }});

__PACKAGE__->register_method ({
    name => 'service_state', 
    path => '{service}/state', 
    method => 'GET',
    permissions => {
	check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
    },
    description => "Read service properties",
    proxyto => 'node',
    protected => 1,
    parameters => {
    	additionalProperties => 0,
	properties => {
	    node => get_standard_option('pve-node'),
	    service => $service_prop_desc,
	},
    },
    returns => {
	type => "object",
	properties => {},
    },
    code => sub {
	my ($param) = @_;
  
	my $si = $service_list->{$param->{service}};
	return {
	    service => $param->{service},
	    name => $si->{name},
	    desc => $si->{desc},
	    state => &$service_state($param->{service}),
	};
    }});

__PACKAGE__->register_method ({
    name => 'service_start', 
    path => '{service}/start', 
    method => 'POST',
    description => "Start service.",
    permissions => {
	check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
    },
    proxyto => 'node',
    protected => 1,
    parameters => {
    	additionalProperties => 0,
	properties => {
	    node => get_standard_option('pve-node'),
	    service => $service_prop_desc,
	},
    },
    returns => { 
	type => 'string',
    },
    code => sub {
	my ($param) = @_;
  
	my $rpcenv = PVE::RPCEnvironment::get();

	my $user = $rpcenv->get_user();

	my $si = $service_list->{$param->{service}};

	my $realcmd = sub {
	    my $upid = shift;

	    syslog('info', "starting service $param->{service}: $upid\n");

	    &$service_cmd($param->{service}, 'start');

	};

	return $rpcenv->fork_worker('srvstart', $param->{service}, $user, $realcmd);
    }});

__PACKAGE__->register_method ({
    name => 'service_stop', 
    path => '{service}/stop', 
    method => 'POST',
    description => "Stop service.",
    permissions => {
	check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
    },
    proxyto => 'node',
    protected => 1,
    parameters => {
    	additionalProperties => 0,
	properties => {
	    node => get_standard_option('pve-node'),
	    service => $service_prop_desc,
	},
    },
    returns => { 
	type => 'string',
    },
    code => sub {
	my ($param) = @_;
  
	my $rpcenv = PVE::RPCEnvironment::get();

	my $user = $rpcenv->get_user();

	my $si = $service_list->{$param->{service}};

	my $realcmd = sub {
	    my $upid = shift;

	    syslog('info', "stoping service $param->{service}: $upid\n");

	    &$service_cmd($param->{service}, 'stop');

	};

	return $rpcenv->fork_worker('srvstop', $param->{service}, $user, $realcmd);
    }});

__PACKAGE__->register_method ({
    name => 'service_restart', 
    path => '{service}/restart', 
    method => 'POST',
    description => "Restart service.",
    permissions => {
	check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
    },
    proxyto => 'node',
    protected => 1,
    parameters => {
    	additionalProperties => 0,
	properties => {
	    node => get_standard_option('pve-node'),
	    service => $service_prop_desc,
	},
    },
    returns => { 
	type => 'string',
    },
    code => sub {
	my ($param) = @_;
  
	my $rpcenv = PVE::RPCEnvironment::get();

	my $user = $rpcenv->get_user();

	my $si = $service_list->{$param->{service}};

	my $realcmd = sub {
	    my $upid = shift;

	    syslog('info', "re-starting service $param->{service}: $upid\n");

	    &$service_cmd($param->{service}, 'restart');

	};

	return $rpcenv->fork_worker('srvrestart', $param->{service}, $user, $realcmd);
    }});

__PACKAGE__->register_method ({
    name => 'service_reload', 
    path => '{service}/reload', 
    method => 'POST',
    description => "Reload service.",
    permissions => {
	check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
    },
    proxyto => 'node',
    protected => 1,
    parameters => {
    	additionalProperties => 0,
	properties => {
	    node => get_standard_option('pve-node'),
	    service => $service_prop_desc,
	},
    },
    returns => { 
	type => 'string',
    },
    code => sub {
	my ($param) = @_;
  
	my $rpcenv = PVE::RPCEnvironment::get();

	my $user = $rpcenv->get_user();

	my $si = $service_list->{$param->{service}};

	my $realcmd = sub {
	    my $upid = shift;

	    syslog('info', "reloading service $param->{service}: $upid\n");

	    &$service_cmd($param->{service}, 'reload');

	};

	return $rpcenv->fork_worker('srvreload', $param->{service}, $user, $realcmd);
    }});
