# Copyright 2026 SigmaSRC, Inc. All Rights Reserved.

"""Remote scheduler API.

Exports:
Scheduler -- remote scheduler API

"""

import os
import socket
import xmlrpc.client as xmlrpclib

from sigma.util import config
from sigma.sched import tasks

class NoPasswordError(xmlrpclib.Error):
    def __str__(self):
        return Exception.__str__(self)

class Scheduler(object):

    """Remote scheduler API.

    Remote methods:

    ping() -- returns status (os.times() tuple)
    shutdown() -- shuts down server
    restart() -- restarts server
    add() -- adds, replaces or deletes tasks from XML text
    listing() -- returns task list as XML

    Additional methods:

    isalive() -- returns whether the scheduler is alive
    addtasks() -- adds a list of Task objects
    listtasks() -- returns a dict mapping task ids to tasks
    delete() -- deletes a task by id
    """

    def __init__(self, server=None, options=None):
        if options is None:
            options = config.Options()
        self.options = options
        if server is None:
            server = options.scheduler
        self.realproxy = xmlrpclib.ServerProxy("http://" + server)

    def isalive(self):
        try:
            self.ping()
        except Exception:
            return False
        else:
            return True

    def addtasks(self, tasklist):
        xmltext = str(tasks.Tasks(tasks=tasklist))
        return self.add(xmltext)

    def listtasks(self):
        xmltext = self.listing()
        tasklist = tasks.parse_tasks(xmltext)
        d = {}
        for task in tasklist:
            d[task.id] = task
        return d

    def delete(self, id):
        return self.addtasks([tasks.newtask(id)])

    def ping(self):
        return self.realproxy.ping(self.password())

    def shutdown(self):
        # Don't try to use the Windows service API here!
        # The service API uses this API to do the real shutdown. :-)
        return self.realproxy.shutdown(self.password())

    def restart(self):
        if os.name == "nt":
            # On Windows, if running as a service, restart the service
            import win32api, win32service, win32serviceutil
            svcname = self.options.service_name
            try:
                status = win32serviceutil.QueryServiceStatus(svcname)[1]
            except win32api.error:
                pass
            else:
                if status == win32service.SERVICE_RUNNING:
                    win32serviceutil.RestartService(svcname)
                    return 0
        return self.realproxy.restart(self.password())

    def listing(self):
        return self.realproxy.listing(self.password())

    def add(self, xmltext):
        return self.realproxy.add(self.password(), xmltext)

    def password(self):
        pwfile = self.options.datapath(".sched.passwd")
        try:
            f = open(pwfile)
        except IOError as err:
            raise NoPasswordError("can't read .sched.passwd file")
        password = f.readline().rstrip("\n")
        f.close()
        return password
