Commit 6e38fe4b authored by Никита Ефремов's avatar Никита Ефремов Committed by Nick Yefremov

Working deb build setup

parent adae345f
*.pyc
debian/.debhelper
build
.pybuild
debian/settingsd
# -*- coding: utf-8 -*-
import os
import signal
import syslog
from . import const
from . import config
from . import logger
from . import server
from . import daemon
##### Public classes #####
class Application(object) :
def __init__(self, log_level, use_syslog_flag, bus_type, daemon_mode_flag) :
object.__init__(self)
#####
self.__log_level = log_level
self.__use_syslog_flag = use_syslog_flag
self.__bus_type = bus_type
self.__daemon_mode_flag = daemon_mode_flag
#####
self.__server = server.Server()
### Public ###
def run(self) :
self.prepare()
if self.__daemon_mode_flag :
logger.info("Run server in daemon mode")
self.runDaemon()
else :
logger.info("Run server in interactive mode")
self.runInteractive()
def server(self) :
return self.__server
###
def quit(self, signum = None, frame = None) :
if signum != None :
logger.info("Recieved signal %d, closing..." % (signum))
self.__server.closeServices()
self.__server.quitLoop()
logger.info("Closed")
### Private ###
def prepare(self) :
if self.__use_syslog_flag == None :
if self.__daemon_mode_flag :
syslog.openlog(const.MY_NAME, syslog.LOG_PID, syslog.LOG_DAEMON)
config.setValue(config.RUNTIME_SECTION, "use_syslog", True)
logger.verbose("Logger used syslog")
else :
syslog.openlog(const.MY_NAME, syslog.LOG_PID, ( syslog.LOG_DAEMON if self.__daemon_mode_flag else syslog.LOG_USER ))
config.setValue(config.RUNTIME_SECTION, "use_syslog", True)
logger.verbose("Logger used syslog")
try :
self.__server.loadServerConfigs()
except :
logger.error("Initialization error")
logger.attachException()
raise
logger.verbose("Preparing complete")
if self.__bus_type != None :
config.setValue(config.APPLICATION_SECTION, "bus_type", self.__bus_type)
if self.__log_level != None :
config.setValue(config.APPLICATION_SECTION, "log_level", self.__log_level)
config.setValue(config.RUNTIME_SECTION, "application", self)
###
def runInteractive(self) :
try :
self.__server.loadModules()
self.__server.loadServicesConfigs()
self.__server.initBus()
self.__server.initServices()
logger.info("Initialized")
except :
logger.error("Initialization error")
logger.attachException()
raise
try :
signal.signal(signal.SIGTERM, self.quit)
signal.signal(signal.SIGQUIT, self.quit)
except :
logger.error("signal() error")
logger.attachException()
try :
self.__server.runLoop()
except (SystemExit, KeyboardInterrupt) :
logger.info("Recieved KeyboardInterrupt or SystemExit, closing...")
self.quit()
except :
logger.error("Runtime error, trying to close services")
logger.attachException()
try :
self.quit()
except :
logger.attachException()
raise
raise
def runDaemon(self) :
work_dir_path = ( "/" if os.getuid() == 0 else None )
umask = ( 0o77 if os.getuid() == 0 else None )
logger.info("Run server as daemon: uid=%d, dir=%s, umask=%s" % ( os.getuid(),
( work_dir_path if work_dir_path != None else os.getcwd() ),
( str(umask) if umask != None else "%.4x" % os.umask(-1) ) ))
daemon.startDaemon(self.runInteractive, work_dir_path, umask)
# -*- coding: utf-8 -*-
import os
import configparser
from . import const
from . import validators
##### Public constants #####
APPLICATION_SECTION = const.MY_NAME
RUNTIME_SECTION = "runtime"
ALL_SECTIONS_LIST = (
APPLICATION_SECTION,
RUNTIME_SECTION
)
##### Private objects #####
ConfigDictObject = {
APPLICATION_SECTION : {
"service_name" : (const.DEFAULT_SERVICE_NAME, str),
"service_path" : (const.DEFAULT_SERVICE_PATH, str),
"bus_type" : (const.DEFAULT_BUS_TYPE, ( lambda arg : validators.common.validRange(arg, const.ALL_BUS_TYPES_LIST) )),
"log_level" : (const.DEFAULT_LOG_LEVEL, ( lambda arg : validators.common.validRange(int(arg), const.ALL_LOG_LEVELS_LIST) )),
"log_use_colors" :(const.DEFAULT_LOG_USE_COLORS_FLAG, validators.common.validBool)
},
RUNTIME_SECTION : {
"application" : (None, None),
"bus_name" : (None, None),
"use_syslog" : (False, None)
}
}
##### Public methods #####
def setValue(section, option, value, validator = None) :
global ConfigDictObject
if section not in ConfigDictObject :
ConfigDictObject[section] = {}
if option in ConfigDictObject[section] :
validator = ConfigDictObject[section][option][1]
if validator != None :
try :
value = validator(value)
except Exception as err1 :
raise validators.ValidatorError("Incorrect config option \"%s :: %s = %s\": %s" % (section, option, value, str(err1)))
ConfigDictObject[section][option] = (value, validator)
def value(section, option) :
return ConfigDictObject[section][option][0]
def validator(section, option) :
return ConfigDictObject[section][option][1]
def loadConfigs(only_sections_list = (), exclude_sections_list = ()) :
only_sections_list = list(only_sections_list)
exclude_sections_list = list(exclude_sections_list)
exclude_sections_list.append(RUNTIME_SECTION)
for config_files_list_item in os.listdir(const.CONFIGS_DIR) :
if not config_files_list_item.endswith(const.CONFIG_FILE_POSTFIX) :
continue
config_parser = configparser.ConfigParser()
config_parser.read(os.path.join(const.CONFIGS_DIR, config_files_list_item))
for section in config_parser.sections() :
if (len(only_sections_list) != 0 and not section in only_sections_list) or section in exclude_sections_list :
continue
for option in config_parser.options(section):
setValue(section, option, config_parser.get(section, option))
# -*- coding: utf-8 -*-
##### Public constants #####
MY_NAME = "settingsd"
VERSION = "0.3"
VERSION_STATUS = "beta"
FUNCTIONALITY_LEVEL = 132
FUNCTIONS_DIR = "plugins/functions"
ACTIONS_DIR = "plugins/actions"
CUSTOMS_DIR = "plugins/customs"
FUNCTIONS_DATA_DIR = "data/functions"
ACTIONS_DATA_DIR = "data/actions"
CUSTIOMS_DIR = "data/customs"
CONFIGS_DIR = "configs/settingsd"
CONFIG_FILE_POSTFIX = ".conf"
BUS_TYPE_SYSTEM = "system"
BUS_TYPE_SESSION = "session"
ALL_BUS_TYPES_LIST = (
BUS_TYPE_SYSTEM,
BUS_TYPE_SESSION
)
LOG_LEVEL_INFO = 0
LOG_LEVEL_VERBOSE = 1
LOG_LEVEL_DEBUG = 2
ALL_LOG_LEVELS_LIST = (
LOG_LEVEL_INFO,
LOG_LEVEL_VERBOSE,
LOG_LEVEL_DEBUG
)
DEFAULT_SERVICE_NAME = "org.etersoft.%s" % (MY_NAME)
DEFAULT_SERVICE_PATH = "/org/etersoft/%s" % (MY_NAME)
DEFAULT_BUS_TYPE = BUS_TYPE_SYSTEM
DEFAULT_LOG_LEVEL = LOG_LEVEL_INFO
DEFAULT_LOG_USE_COLORS_FLAG = True
# -*- coding: utf-8 -*-
import sys
import os
import signal
import errno
import resource
from . import const
from . import logger
##### Private methods #####
def pidsListOfPythonProc(proc_name, without_options_list = [], uid = 0) :
proc_name = os.path.basename(proc_name)
proc_pids_list = []
for proc_list_item in os.listdir("/proc") :
try :
proc_pid = int(proc_list_item)
except :
continue
cmdline_file_path = os.path.join("/proc", proc_list_item, "cmdline")
if os.stat(cmdline_file_path).st_uid != uid :
continue
cmdline_file = open(cmdline_file_path)
cmdline_list = cmdline_file.read().split("\0")
try :
cmdline_file.close()
except : pass
if len(cmdline_list) >= 2 and os.path.basename(cmdline_list[1]) == proc_name :
ignore_flag = False
for without_options_list_item in without_options_list :
if without_options_list_item in cmdline_list :
ignore_flag = True
break
if not ignore_flag :
proc_pids_list.append(proc_pid)
return proc_pids_list
def maxFd() :
max_fd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
if max_fd == resource.RLIM_INFINITY :
max_fd = 1024
try :
max_fd = os.sysconf("SC_OPEN_MAX")
except ValueError :
pass
if max_fd < 0 :
max_fd = 1024
return max_fd
def closeFd(fd, retries_count = 5) :
for count in range(retries_count) :
try :
os.close(fd)
except OSError as err1 :
if err1.errno != errno.EBADF :
continue
break
##### Public methods #####
def startDaemon(function, work_dir_path = None, umask = None) :
pid = os.fork()
if pid > 0 :
try :
os.waitpid(pid, 0)
except OSError :
pass
elif pid == 0 :
logger.verbose("First fork() to %d as session lead" % (os.getpid()))
os.setsid()
pid = os.fork()
if pid > 0 :
os._exit(0)
elif pid == 0 :
logger.verbose("Second fork() to %d as main process" % (os.getpid()))
if work_dir_path != None :
os.chdir(work_dir_path)
logger.verbose("New working directory: %s" % (work_dir_path))
if umask != None :
os.umask(umask)
logger.verbose("Accapted new umask: %.3o" % (umask))
for fd in range(maxFd()) :
closeFd(fd)
null_fd = os.open("/dev/null", os.O_RDWR)
for fd in (0, 1, 2) :
os.dup2(null_fd, fd)
function()
def killDaemon() :
pids_list = pidsListOfPythonProc(sys.argv[0], ["-k", "--kill"], os.getuid())
if len(pids_list) != 0 :
for pids_list_item in pids_list :
os.kill(pids_list_item, signal.SIGTERM)
logger.info("SIGTERM has been sended to %s process \"%s\" with pid \"%d\"" % (
const.MY_NAME, os.path.basename(sys.argv[0]), pids_list_item ))
return 0
else :
logger.error("Cannot determine a %s daemon process of \"%s\"" % (const.MY_NAME, os.path.basename(sys.argv[0])))
return -1
def daemonStatus() :
pids_list = pidsListOfPythonProc(sys.argv[0], ["-s", "--status"], os.getuid())
if len(pids_list) != 0 :
for pids_list_item in pids_list :
logger.info("%s daemon has been founded with pid \"%d\"" % (const.MY_NAME, pids_list_item))
return 0
else :
logger.error("Cannot determine a %s daemon process of \"%s\"" % (const.MY_NAME, os.path.basename(sys.argv[0])))
return -1
# -*- coding: utf-8 -*-
import sys
import traceback
import syslog
import inspect
import time
from . import const
from . import config
##### Public constants #####
ERROR_MESSAGE = (0, syslog.LOG_ERR, const.LOG_LEVEL_INFO)
WARNING_MESSAGE = (1, syslog.LOG_WARNING, const.LOG_LEVEL_INFO)
NOTICE_MESSAGE = (2, syslog.LOG_NOTICE, const.LOG_LEVEL_INFO)
INFO_MESSAGE = (3, syslog.LOG_INFO, const.LOG_LEVEL_INFO)
VERBOSE_MESSAGE = (4, syslog.LOG_INFO, const.LOG_LEVEL_VERBOSE)
DEBUG_MESSAGE = (5, syslog.LOG_INFO, const.LOG_LEVEL_DEBUG) # syslog.LOG_DEBUG
ALL_MESSAGES_LIST = (
ERROR_MESSAGE,
INFO_MESSAGE,
NOTICE_MESSAGE,
WARNING_MESSAGE,
VERBOSE_MESSAGE,
DEBUG_MESSAGE
)
ALL_MESSAGES_TEXTS_LIST = (
(" Error ", "\033[31m Error \033[0m"),
("Warning", "\033[33mWarning\033[0m"),
("Notice ", "\033[32mNotice \033[0m"),
(" Info ", "\033[32m Info \033[0m"),
("Details", "\033[36mDetails\033[0m"),
(" Debug ", " Debug ")
)
MODULE_CALLER_NAME_TAG = "{mod}"
SUBMODULE_CALLER_NAME_TAG = "{submod}"
CURRENT_TIME_TAG = "{time}"
ALL_TAGS_LIST = (
MODULE_CALLER_NAME_TAG,
SUBMODULE_CALLER_NAME_TAG,
CURRENT_TIME_TAG
)
##### Exceptions #####
class UnknownMessageType(Exception) :
pass
##### Private methods #####
def log(message_type, message) :
if not message_type in ALL_MESSAGES_LIST :
raise UnknownMessageType("Message type \"%s\" not in list %s" % (str(message_type), ALL_MESSAGES_LIST))
if message_type[2] <= config.value(config.APPLICATION_SECTION, "log_level") :
for all_tags_list_item in ALL_TAGS_LIST :
if all_tags_list_item == MODULE_CALLER_NAME_TAG :
try :
message = message.replace(MODULE_CALLER_NAME_TAG,
inspect.getmodule(inspect.currentframe().f_back.f_back).__name__)
except : pass
elif all_tags_list_item == SUBMODULE_CALLER_NAME_TAG :
try :
message = message.replace(SUBMODULE_CALLER_NAME_TAG,
inspect.getmodule(inspect.currentframe().f_back.f_back.f_back).__name__)
except : pass
elif all_tags_list_item == CURRENT_TIME_TAG :
message = message.replace(CURRENT_TIME_TAG, time.ctime())
colored_index = int(sys.stderr.isatty() and config.value(config.APPLICATION_SECTION, "log_use_colors"))
for message_list_item in message.split("\n") :
print("[ %s ] %s" % (ALL_MESSAGES_TEXTS_LIST[message_type[0]][colored_index], message_list_item), file=sys.stderr)
if config.value(config.RUNTIME_SECTION, "use_syslog") :
syslog.syslog(message_type[1], "[ %s ] %s" % (ALL_MESSAGES_TEXTS_LIST[message_type[0]][0], message_list_item))
##### Public methods #####
def error(message) :
log(ERROR_MESSAGE, message)
def info(message) :
log(INFO_MESSAGE, message)
def notice(message) :
log(NOTICE_MESSAGE, message)
def warning(message) :
log(WARNING_MESSAGE, message)
def verbose(message) :
log(VERBOSE_MESSAGE, message)
def debug(message) :
log(DEBUG_MESSAGE, message)
###
def attachException(message_type = ERROR_MESSAGE) :
for line in traceback.format_exc().splitlines() :
log(message_type, line)
# -*- coding: utf-8 -*-
import sys
import os
import dbus
import dbus.service
import dbus.glib
from gi.repository import GLib
from . import const
from . import config
from . import logger
from . import validators
##### Public classes #####
class Server(object) :
def __init__(self) :
object.__init__(self)
#####
self.__modules_list = []
self.__services_dict = {}
GLib.threads_init()
self.__main_loop = GLib.MainLoop()
### Public ###
def runLoop(self) :
logger.verbose("Running GObject loop...")
self.__main_loop.run()
def quitLoop(self) :
self.__main_loop.quit()
logger.verbose("GObject loop closed")
###
def loadModules(self) :
for modules_path_list_item in (const.FUNCTIONS_DIR, const.ACTIONS_DIR, const.CUSTOMS_DIR) :
logger.debug("Processing directory \"%s\"..." % (modules_path_list_item))
sys.path.append(modules_path_list_item)
for module_name in [ item[:-3] for item in os.listdir(modules_path_list_item)
if item.endswith(".py") and not item.startswith(".") ] :
try :
self.__modules_list.append(__import__(module_name, globals(), locals(), [""]))
except :
logger.error("Import error on module \"%s\"" % (module_name))
logger.attachException()
continue
self.__services_dict[self.__modules_list[-1].Service.serviceName()] = {
"service_class" : self.__modules_list[-1].Service,
"service" : None
}
logger.verbose("Loaded module: %s" % (module_name))
sys.path.remove(modules_path_list_item)
###
def loadServerConfigs(self) :
config.loadConfigs(only_sections_list = (config.APPLICATION_SECTION,))
def loadServicesConfigs(self) :
for service_name in list(self.__services_dict.keys()) :
service_options_list = list(self.__services_dict[service_name]["service_class"].options())
service_options_list.append((service_name, "enabled", "no", validators.common.validBool))
for service_options_list_item in service_options_list :
try :
config.setValue(*service_options_list_item)
except :
logger.error("Error on set options tuple %s" % (str(service_options_list_item)))
logger.attachException()
config.loadConfigs(exclude_sections_list = (config.APPLICATION_SECTION,))
###
def initBus(self) :
bus_type = config.value(config.APPLICATION_SECTION, "bus_type")
service_name = config.value(config.APPLICATION_SECTION, "service_name")
try :
config.setValue(config.RUNTIME_SECTION, "bus_name", dbus.service.BusName(service_name,
( dbus.SystemBus() if bus_type == const.BUS_TYPE_SYSTEM else dbus.SessionBus() )))
except :
logger.error("Could not connect to D-Bus \"%s\"" % (bus_type))
logger.attachException()
raise
logger.verbose("Connected to D-Bus \"%s\" as \"%s\"" % (bus_type, service_name))
###
def initServices(self) :
for service_name in list(self.__services_dict.keys()) :
if config.value(service_name, "enabled") :
logger.verbose("Enabling service \"%s\"..." % (service_name))
try :
self.__services_dict[service_name]["service"] = self.__services_dict[service_name]["service_class"]()
self.__services_dict[service_name]["service"].initService()
except :
logger.error("Cannot initialize service \"%s\"" % (service_name))
logger.attachException()
logger.info("Initialized service: %s" % (service_name))
def closeServices(self) :
for service_name in list(self.__services_dict.keys()) :
if self.__services_dict[service_name]["service"] != None :
logger.verbose("Disabling service \"%s\"..." % (service_name))
try :
self.__services_dict[service_name]["service"].closeService()
del self.__services_dict[service_name]["service"]
except :
logger.error("Cannot close service \"%s\"" % (service_name))
logger.attachException()
self.__services_dict[service_name]["service"] = None
logger.info("Closed service: %s" % (service_name))
# -*- coding: utf-8 -*-
import dbus
import dbus.service
import dbus.glib
import abc
from . import config
from . import tools
from .service_decorators import *
##### Private classes #####
class ServiceInterface(object, metaclass=abc.ABCMeta) :
@abc.abstractmethod
def initService(self) :
pass
def closeService(self) :
pass
class ServiceRequisitesInterface(object, metaclass=abc.ABCMeta) :
@classmethod
@abc.abstractmethod
def serviceName(self) :
pass
@classmethod
def options(self) :
return []
##### Public classes #####
class Service(ServiceInterface, ServiceRequisitesInterface) :
pass
#####
class CustomObject(dbus.service.Object) :
def __init__(self, object_path, service_object = None) :
dbus.service.Object.__init__(self, config.value(config.RUNTIME_SECTION, "bus_name"), object_path)
self.__object_path = object_path
self.__service_object = service_object
#####
self.__shared = None
### Public ###
def name(self) :
if self.shared() == None :
return None
for (shared_object_name, shared_object) in list(self.shared().sharedObjects().items()) :
if shared_object == self :
return shared_object_name
return None
def path(self) :
def build_path(shared) :
if shared != None :
path = build_path(shared.parentShared())
return ( shared.name() if path == None else tools.dbus.joinMethod(path, shared.name()) )
return None
return build_path(self.shared())
def objectPath(self) :
return self.__object_path
###
def setService(self, service_object) :
self.__service_object = service_object
def service(self) :
return self.__service_object
def setShared(self, shared) :
self.__shared = shared
def shared(self) :
return self.__shared
###
def addToConnection(self, connection = None, path = None) :
self.add_to_connection(connection, path)
def removeFromConnection(self, conneciton = None, path = None) :
self.remove_from_connection(conneciton, path)
class FunctionObject(CustomObject) :
def __init__(self, object_path, service_object = None) :
CustomObject.__init__(self, tools.dbus.joinPath(config.value(config.APPLICATION_SECTION, "service_path"),
"functions", object_path), service_object)
class ActionObject(CustomObject) :
def __init__(self, object_path, service_object = None) :
CustomObject.__init__(self, tools.dbus.joinPath(config.value(config.APPLICATION_SECTION, "service_path"),
"actions", object_path), service_object)
# -*- coding: utf-8 -*-
import dbus
import dbus.service
import dbus.glib
from . import const
from . import config
from . import logger
from . import tools
from .tools import dbus as dbus_tools
##### Private decorators #####
def tracer(function, statics_list=[0]) :
def wrapper(self, *args_list, **kwargs_dict) :
if config.value(config.APPLICATION_SECTION, "log_level") == const.LOG_LEVEL_DEBUG :
logger.debug("%s%s %s::%s" % ( " "*statics_list[0],
str(("_dbus_is_method" in function.__dict__ and "Called method") or
("_dbus_is_signal" in function.__dict__ and "Emited signal")),
self.objectPath(), dbus_tools.joinMethod(function._dbus_interface, function.__name__) ))
statics_list[0] += 1
try :
return_value = function(self, *args_list, **kwargs_dict)
except :
logger.attachException()
raise
finally :
statics_list[0] -= 1
logger.debug("%s... executed as %s::%s(%s, %s) --> %s" % ( " "*statics_list[0],
self.__class__.__name__, function.__name__, str(args_list), str(kwargs_dict), str(return_value) ))
return return_value
else :
try :
return function(self, *args_list, **kwargs_dict)
except :
logger.attachException()
raise
wrapper.__name__ = function.__name__
wrapper.__dict__ = function.__dict__
wrapper.__doc__ = function.__doc__
return wrapper
##### Public decorators #####
def customMethod(interface_name, **kwargs_dict) :
def decorator(function) :
return tracer(dbus.service.method(interface_name, **kwargs_dict)(function))
return decorator
def functionMethod(interface_name, **kwargs_dict) :
def decorator(function) :
return customMethod(tools.dbus.joinMethod(config.value(config.APPLICATION_SECTION, "service_name"),
"functions", interface_name), **kwargs_dict)(function)
return decorator
def actionMethod(interface_name, **kwargs_dict) :
def decorator(function) :
return customMethod(tools.dbus.joinMethod(config.value(config.APPLICATION_SECTION, "service_name"),
"actions", interface_name), **kwargs_dict)(function)
return decorator
###
def customSignal(interface_name, **kwargs_dict) :
def decorator(function) :
return tracer(dbus.service.signal(interface_name, **kwargs_dict)(function))
return decorator
def functionSignal(interface_name, **kwargs_dict) :
def decorator(function) :
return customSignal(tools.dbus.joinMethod(config.value(config.APPLICATION_SECTION, "service_name"),
"functions", interface_name), **kwargs_dict)(function)
return decorator
def actionSignal(interface_name, **kwargs_dict) :
def decorator(function) :
return customSignal(tools.dbus.joinMethod(config.value(config.APPLICATION_SECTION, "service_name"),
"actions", interface_name), **kwargs_dict)(function)
return decorator
# -*- coding: utf-8 -*-
##### Exceptions #####
class SharedsConflict(Exception) :
pass
class SharedNotExists(Exception) :
pass
class SharedObjectsConflict(Exception) :
pass
class SharedObjectNotExists(Exception) :
pass
##### Private classes #####
class SharedAbstract :
def __init__(entity) :
entity._shareds_dict = {}
entity._shared_objects_dict = {}
#####
entity._parent_shared = None
### Public ###
def setParentShared(entity, shared) :
entity._parent_shared = shared
def parentShared(entity) :
return entity._parent_shared
###
def name(entity) :
if entity.parentShared() == None :
return None
for (shared_name, shared) in list(entity.parentShared().shareds().items()) :
if shared == entity :
return shared_name
return None
###
def addShared(entity, shared_name) :
if shared_name in entity._shareds_dict :
raise SharedsConflict("Shared \"%s\" is already exists in collection \"%s\"" % (shared_name, entity.__name__))
entity._shareds_dict[shared_name] = Shared()
entity._shareds_dict[shared_name].setParentShared(entity)
setattr(entity, shared_name, entity._shareds_dict[shared_name])
def removeShared(entity, shared_name) :
if shared_name not in entity._shareds_dict :
raise SharedNotExists("Shared \"%s\" does not exist in collection \"%s\"" % (shared_name, entity.__name__))
entity._shareds_dict[shared_name].setParentShared(None)
entity._shareds_dict.pop(shared_name)
delattr(entity, shared_name)
def hasShared(entity, shared_name) :
return shared_name in entity._shareds_dict
def shared(entity, shared_name) :
return entity._shareds_dict[shared_name]
def shareds(entity) :
return entity._shareds_dict
###
def addSharedObject(entity, shared_object_name, shared_object) :
if shared_object_name in entity._shared_objects_dict :
raise SharedObjectsConflict("Shared object \"%s\" is already exists in collection \"%s\"" % (shared_object_name, entity.__name__))
entity._shared_objects_dict[shared_object_name] = shared_object
entity._shared_objects_dict[shared_object_name].setShared(entity)
setattr(entity, shared_object_name, entity._shared_objects_dict[shared_object_name])
def removeSharedObject(entity, shared_object_name) :
if shared_object_name not in entity._shared_objects_dict :
raise SharedObjectNotExists("Shared object \"%s\" does not exist in collection \"%s\"" % (shared_object_name, entity.__name__))
entity._shared_objects_dict[shared_object_name].setShared(None)
entity._shared_objects_dict.pop(shared_object_name)
delattr(entity, shared_object_name)
def hasSharedObject(entity, shared_object) :
return ( shared_object in entity._shared_objects_dict or shared_object in list(entity._shared_objects_dict.values()) )
def sharedObject(entity, shared_object_name) :
return entity._shared_objects_dict[shared_object_name]
def sharedObjects(entity) :
return entity._shared_objects_dict
class SharedRootMeta(type, SharedAbstract) :
def __init__(cls, name, bases_list, attrs_dict) :
type.__init__(cls, name, bases_list, attrs_dict)
SharedAbstract.__init__(cls)
class Shared(SharedAbstract) :
def __init__(self) :
object.__init__(self)
SharedAbstract.__init__(self)
##### Public classes #####
class Functions(object, metaclass=SharedRootMeta) :
@classmethod
def name(self) :
return "Functions"
class Actions(object, metaclass=SharedRootMeta) :
@classmethod
def name(self) :
return "Actions"
class Customs(object, metaclass=SharedRootMeta) :
@classmethod
def name(self) :
return "Customs"
# -*- coding: utf-8 -*-
__all__ = ["dbus", "process", "editors"]
# -*- coding: utf-8 -*-
##### Public methods #####
def joinPath(first, *others_list) :
return "/".join((first,) + others_list)
def joinMethod(first, *others_list) :
return ".".join((first,) + others_list)
# -*- coding: utf-8 -*-
import os
import re
import shutil
from .. import logger
##### Exceptions #####
class NotAssociated(Exception) :
pass
class AlreadyAssociated(Exception) :
pass
##### Public classes #####
class PlainEditor(object) :
def __init__(self, delimiter = "=", spaces_list = ["\\s"], quotes_list= ["\"", "\'"], comments_list = ["#"]) :
object.__init__(self)
#####
self.__delimiter = delimiter
self.__spaces_list = list(spaces_list)
self.__quotes_list = list(quotes_list)
self.__comments_list = list(comments_list)
#####
self.__config_file_path = None
self.__config_file_data_list = None
###
spaces = ( "[%s]*" % ("".join(self.__spaces_list)) if len(self.__spaces_list) > 0 else "" )
comments = "".join(self.__comments_list)
self.__comments_regexp = re.compile(r"%s(?<!\\)[%s]%s" % (spaces, comments, spaces))
self.__variable_regexp = re.compile(r"%s%s%s" % (spaces, self.__delimiter, spaces))
### Public ###
def open(self, config_file_path, sample_config_file_path = None) :
if self.__config_file_path != None :
raise AlreadyAssociated("This parser already associated with config \"%s\"" % self.__config_file_path)
if not os.access(config_file_path, os.F_OK) :
logger.debug("{submod}: Config file \"%s\" does not exist" % (config_file_path))
try :
if sample_config_file_path != None and os.access(sample_config_file_path, os.F_OK) :
shutil.copy2(sample_config_file_path, config_file_path)
logger.debug("{submod}: Config file \"%s\" has been created from sample \"%s\"" % (
config_file_path, sample_config_file_path ))
else :
open(config_file_path, "w").close()
logger.debug("{submod}: Created empty file \"%s\"" % (config_file_path))
except :
logger.error("Cannot create config file \"%s\"" % (config_file_path))
logger.attachException()
config_file = open(config_file_path, "r")
self.__config_file_data_list = config_file.read().split("\n")
self.__config_file_path = config_file_path
try :
config_file.close()
except : pass
logger.debug("{submod}: Cached and associated config file \"%s\"" % (config_file_path))
def save(self) :
if self.__config_file_path == None :
raise NotAssociated("This parser is not associated with config")
config_file = open(self.__config_file_path, "w")
config_file.write("\n".join(self.__config_file_data_list))
try :
config_file.close()
except : pass
logger.debug("{submod}: Saved config file \"%s\"" % (self.__config_file_path))
def close(self) :
if self.__config_file_path == None :
raise NotAssociated("This parser is not associated with config")
logger.debug("{submod}: Unassociated parser from config file \"%s\"" % (self.__config_file_path))
self.__config_file_data_list = None
self.__config_file_path = None
###
def setValue(self, variable_name, values_list) :
if self.__config_file_path == None :
raise NotAssociated("This parser is not associated with config")
if values_list == None :
values_list = []
elif not type(values_list).__name__ in ("list", "tuple") :
values_list = [values_list]
last_variable_index = len(self.__config_file_data_list) - 1
count = 0
while count < len(self.__config_file_data_list) :
variable = self.__comments_regexp.split(self.__config_file_data_list[count].strip(), 1)[0]
variable_parts_list = self.__variable_regexp.split(variable, 1)
if variable_parts_list[0] == variable_name :
self.__config_file_data_list.pop(count)
last_variable_index = count
else :
count += 1
space = ( " " if len(self.__spaces_list) > 0 else "" )
quote = ( self.__quotes_list[0] if len(self.__quotes_list) > 0 else "" )
for count in range(len(values_list)) :
variable = variable_name + space + self.__delimiter + space + quote + str(values_list[count]) + quote
self.__config_file_data_list.insert(last_variable_index + count, variable)
def value(self, variable_name) :
if self.__config_file_path == None :
raise NotAssociated("This parser is not associated with config")
values_list = []
for config_file_data_list_item in self.__config_file_data_list :
variable = self.__comments_regexp.split(config_file_data_list_item.strip(), 1)[0]
variable_parts_list = self.__variable_regexp.split(variable, 1)
if variable_parts_list[0] == variable_name :
if len(variable_parts_list) > 1 :
value = variable_parts_list[1]
for quotes_list_item in self.__quotes_list :
if len(value) > 2 and value[0] == value[-1] == quotes_list_item :
value = value[1:-1]
values_list.append(value)
else :
values_list.append("")
return values_list
class IniEditor(object) :
def __init__(self) :
object.__init__(self)
#####
self.__config_file_path = None
self.__config_file_data_list = None
###
self.__comments_regexp = re.compile(r"\s*(?<!\\)[;#]\s*")
self.__section_regexp = re.compile(r"^\s*\[([^];#]+)\]")
self.__variable_regexp = re.compile(r"\s*=\s*")
### Public ###
def open(self, config_file_path, sample_config_file_path = None) :
if self.__config_file_path != None :
raise AlreadyAssociated("This parser already associated with config \"%s\"" % self.__config_file_path)
if not os.access(config_file_path, os.F_OK) :
logger.debug("{submod}: Config file \"%s\" does not exist" % (config_file_path))
try :
if sample_config_file_path != None and os.access(sample_config_file_path, os.F_OK) :
shutil.copy2(sample_config_file_path, config_file_path)
logger.debug("{submod}: Config file \"%s\" has been created from sample \"%s\"" % (
config_file_path, sample_config_file_path ))
else :
open(config_file_path, "w").close()
logger.debug("{submod}: Created empty file \"%s\"" % (config_file_path))
except :
logger.error("Cannot create config file \"%s\"" % (config_file_path))
logger.attachException()
config_file = open(config_file_path, "r")
self.__config_file_data_list = config_file.read().split("\n")
self.__config_file_path = config_file_path
try :
config_file.close()
except : pass
logger.debug("{submod}: Cached and associated config file \"%s\"" % (config_file_path))
def save(self) :
if self.__config_file_path == None :
raise NotAssociated("This parser is not associated with config")
config_file = open(self.__config_file_path, "w")
config_file.write("\n".join(self.__config_file_data_list))
try :
config_file.close()
except : pass
logger.debug("{submod}: Saved config file \"%s\"" % (self.__config_file_path))
def close(self) :
if self.__config_file_path == None :
raise NotAssociated("This parser is not associated with config")
logger.debug("{submod}: Unassociated parser from config file \"%s\"" % (self.__config_file_path))
self.__config_file_data_list = None
self.__config_file_path = None
###
def setValue(self, section_name, variable_name, values_list) :
if self.__config_file_path == None :
raise NotAssociated("This parser is not associated with config")
if values_list == None :
values_list = []
elif not type(values_list).__name__ in ("list", "tuple") :
values_list = [values_list]
active_section_flag = False
section_exists_flag = False
last_variable_index = len(self.__config_file_data_list) - 1
count = 0
while count < len(self.__config_file_data_list) :
record = self.__comments_regexp.split(self.__config_file_data_list[count].strip(), 1)[0]
section_match = self.__section_regexp.search(record)
if section_match != None :
new_active_section_flag = ( section_match.group(1) == section_name )
if new_active_section_flag :
section_exists_flag = True
if active_section_flag and not new_active_section_flag :
last_variable_index = count
active_section_flag = new_active_section_flag
elif active_section_flag :
if self.__variable_regexp.split(record, 1)[0] == variable_name :
self.__config_file_data_list.pop(count)
last_variable_index = count
continue
count += 1
if not section_exists_flag :
self.__config_file_data_list.insert(last_variable_index, "")
self.__config_file_data_list.insert(last_variable_index + 1, "[%s]" % (section_name))
last_variable_index += 2
for count in range(len(values_list)) :
self.__config_file_data_list.insert(last_variable_index + count, "%s = %s" % (variable_name, str(values_list[count])))
def value(self, section_name, variable_name) :
if self.__config_file_path == None :
raise NotAssociated("This parser is not associated with config")
section_founded_flag = False
values_list = []
for config_file_data_list_item in self.__config_file_data_list :
record = self.__comments_regexp.split(config_file_data_list_item.strip(), 1)[0]
section_match = self.__section_regexp.search(record)
if section_match != None :
section_founded_flag = ( section_match.group(1) == section_name )
continue
if section_founded_flag :
variable_parts_list = self.__variable_regexp.split(record, 1)
if variable_parts_list[0] == variable_name :
if len(variable_parts_list) > 1 :
values_list.append(variable_parts_list[1])
else :
values_list.append("")
return values_list
# -*- coding: utf-8 -*-
import subprocess
from os import environ
from .. import const
from .. import config
from .. import logger
##### Exceptions #####
class SubprocessFailure(Exception) :
pass
##### Public methods #####
def execProcess(proc_args_list, proc_input = None, fatal_flag = True,
confidential_input_flag = False, inherit_env=False, shell=False) :
if shell and not isinstance(proc_args_list, str):
proc_args_list = ' '.join(proc_args_list)
logger.debug("{submod}: Executing child process \"%s\"" % (str(proc_args_list)))
env = { "LC_ALL" : "C" }
if inherit_env:
env = { **environ, **env }
proc = subprocess.Popen(proc_args_list, bufsize=1024, close_fds=True,
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
env=env, shell=shell)
(proc_stdout, proc_stderr) = proc.communicate(proc_input)
if proc.returncode != 0 :
if proc_input == None :
proc_input = ""
elif confidential_input_flag and config.value(config.APPLICATION_SECTION, "log_level") != const.LOG_LEVEL_DEBUG :
proc_input = "<CONFIDENTIAL>"
error_text = "Error while execute \"%s\"\nStdout: %s\nStderr: %s\nStdin: %s\nReturn code: %d" % (
str(proc_args_list), proc_stdout.strip(), proc_stderr.strip(), proc_input, proc.returncode )
if fatal_flag :
logger.error("{submod}: "+error_text)
raise SubprocessFailure(error_text)
logger.debug("{submod}: Child process \"%s\" finished, return_code=%d" % (str(proc_args_list), proc.returncode))
return (proc_stdout, proc_stderr, proc.returncode)
# -*- coding: utf-8 -*-
__all__ = ["common", "network", "os"]
from .common import ValidatorError
# -*- coding: utf-8 -*-
import re
##### Exceptions #####
class ValidatorError(Exception) :
pass
##### Public methods #####
def validBool(arg) :
arg = str(arg).lower()
true_args_list = ("1", "true", "yes")
false_args_list = ("0", "false", "no")
if not arg in true_args_list + false_args_list :
raise ValidatorError("Argument \"%s\" not in list %s or %s" % (arg, true_args_list, false_args_list))
return ( arg in true_args_list )
def validRange(arg, valid_args_list) :
if not arg in valid_args_list :
raise ValidatorError("Argument \"%s\" not in range %s" % (arg, str(valid_args_list)))
return arg
def validStringList(arg) :
return re.split(r"[,\t ]+", str(arg))
# -*- coding: utf-8 -*-
from .common import ValidatorError
##### Public methods #####
def validIpv4Address(arg) :
arg = str(arg).strip()
octets_list = arg.split(".")
if len(octets_list) != 4 :
raise ValidatorError("Argument \"%s\" is not valid IPv4 address" % (arg))
for count in range(4) :
try :
octets_list[count] = int(octets_list[count])
if not 0 <= octets_list[count] <= 255 :
raise Exception
except :
raise ValidatorError("Argument \"%s\" is not valid IPv4 address" % (arg))
return (arg, octets_list)
def validIpv4Netmask(arg) :
arg = str(arg).strip()
octets_list = arg.split(".")
if len(octets_list) == 1 :
try :
arg = int(arg)
if not 0 <= arg <= 32 :
raise ValidatorError("Argument \"%s\" is not valid IPv4 netmask" % (arg))
except :
raise ValidatorError("Argument \"%s\" is not valid IPv4 netmask" % (arg))
octets_list = [0] * 4
one_count = arg
for count in range(4) :
octet_one_count = 8
while one_count and octet_one_count :
octets_list[count] |= 128 >> 8 - octet_one_count
one_count -= 1
octet_one_count -= 1
elif len(octets_list) == 4 :
for count in range(4) :
try :
octets_list[count] = int(octets_list[count])
if not 0 <= octets_list[count] <= 255 :
raise Exception
except :
raise ValidatorError("Argument \"%s\" is not valid IPv4 netmask" % (arg))
else :
raise ValidatorError("Argument \"%s\" is not valid IPv4 netmask" % (arg))
return (arg, octets_list)
def validMacAddress(arg) :
arg = str(arg).strip()
octets_list = arg.split(":")
if len(octets_list) != 6 :
raise ValidatorError("Argument \"%s\" is not valid MAC address" % (arg))
for count in range(6) :
try :
octets_list[count] = int(octets_list[count], 16)
if not 0 <= octets_list[count] <= 255 :
raise Exception
except :
raise ValidatorError("Argument \"%s\" is not valid MAC address" % (arg))
return (arg, octets_list)
# -*- coding: utf-8 -*-
import re
from .common import ValidatorError
##### Public methods #####
def validUserName(arg) :
if re.match(r"^[a-z_][a-z0-9_-]*$", arg) == None :
raise ValidatorError("Argument \"%s\" is not valid UNIX user name" % (arg))
return arg
def validGroupName(arg) :
if re.match(r"^[a-z_][a-z0-9_-]*$", arg) == None :
raise ValidatorError("Argument \"%s\" is not valid UNIX group name" % (arg))
return arg
#!/usr/bin/python3.6
# -*- coding: utf-8 -*-
import sys
import getopt
from settingsd import const
from settingsd import validators
from settingsd import application
from settingsd import daemon
##### Private methods #####
def help() :
print(( "Usage: %s [options]\n"
"Options:\n"
"\t-h, --help -- Print this text\n"
"\t-v, --version -- Print version and license info\n"
"\t--log-level=<0|1|2> -- Log level, replace value from config\n"
"\t--use-syslog=<yes|no> -- Force enable or disable useage of syslog\n"
"\t--bus-type=<system|session> -- Use system or session bus, replace value from config\n"
"\t-d, --daemon -- Run application as daemon, by default using interactive mode\n"
"\t-s, --status -- Check status of daemon\n"
"\t-k, --kill -- Kill daemon process" % (const.MY_NAME) ))
def version() :
print("%s version %s-%s, functionality_level=%d" % (const.MY_NAME, const.VERSION, const.VERSION_STATUS, const.FUNCTIONALITY_LEVEL))
##### Main #####
if __name__ == "__main__" :
log_level = None
use_syslog_flag = None
bus_type = None
daemon_mode_flag = False
try :
(opts_list, args_list) = getopt.getopt(sys.argv[1:], "hdsk", ( "help", "version",
"log-level=", "use-syslog=", "bus-type=", "daemon", "status", "kill" ))
for (opts_list_item, args_list_item) in opts_list :
if opts_list_item in ("-h", "--help") :
help()
sys.exit(0)
elif opts_list_item in ("-v", "--version") :
version()
sys.exit(0)
elif opts_list_item in ("--log-level",) :
try :
log_level = validators.common.validRange(int(args_list_item), const.ALL_LOG_LEVELS_LIST)
except Exception as err1 :
print("Incorrect option \"%s\": %s" % (opts_list_item, str(err1)))
sys.exit(1)
elif opts_list_item in ("--use-syslog",) :
try :
use_syslog_flag = validators.common.validBool(args_list_item)
except Exception as err1 :
print("Incorrect option \"%s\": %s" % (opts_list_item, str(err1)))
sys.exit(1)
elif opts_list_item in ("--bus-type",) :
try :
bus_type = validators.common.validRange(args_list_item, const.ALL_BUS_TYPES_LIST)
except Exception as err1 :
print("Incorrect option \"%s\": %s" % (opts_list_item, str(err1)))
sys.exit(1)
elif opts_list_item in ("-d", "--daemon") :
daemon_mode_flag = True
elif opts_list_item in ("-s", "--status") :
try :
sys.exit(abs(daemon.daemonStatus()))
except Exception as err1 :
print("Daemon status error: %s" % (str(err1)))
sys.exit(1)
elif opts_list_item in ("-k", "--kill") :
try :
sys.exit(abs(daemon.killDaemon()))
except Exception as err1 :
print("Daemon kill error: %s" % (str(err1)))
sys.exit(1)
else :
print("Unknown option \"%s\"" % (opts_list_item))
except Exception as err1 :
print("Bad command line options: %s" % (str(err1)))
#####
application.Application(log_level, use_syslog_flag, bus_type, daemon_mode_flag).run()
settingsd (1.0) UNRELEASED; urgency=medium
* Initial release. (Closes: #XXXXXX)
-- <jarloon@ns1.photoninja.su> Wed, 10 Apr 2019 23:02:19 +0300
Source: settingsd
Maintainer: Andrey Shatokhin <shatokhin@etersoft.ru>
Section: python
Priority: optional
Build-Depends: debhelper(>= 8),python3-all (>= 2.6.6-3~)
Standards-Version: 3.9.5
Package: settingsd
Architecture: all
Depends: systemd , python3-psutil, python3-gi ,python3-pyinotify, python3-pyroute2, python3-yaml, python3-jinja2 ,python3-cryptography,nftables,postfix
Description: Settingsd test .deb package
settingsd_1.0_all.deb python optional
settingsd_1.0_amd64.buildinfo python optional
#!/usr/bin/make -f
#export DH_VERBOSE=1
%:
DEB_BUILD_OPTIONS=nocheck dh $@ --with python3 --with systemd --buildsystem=pybuild
misc:Depends=
misc:Pre-Depends=
[Unit]
Description=Settingsd-server
After=syslog.target network.target
[Service]
Type=simple
ExecStart=/usr/bin/python3 -u /usr/bin/settingsd-server.py
WorkingDirectory=/usr/bin
Restart=always
RestartSec=20
Environment=PYTHONPATH=vendor
KillSignal=SIGINT
[Install]
WantedBy=multi-user.target
......@@ -4,6 +4,7 @@
import sys
import os
import shutil
from distutils.core import setup
from distutils.command.install import install
......@@ -20,7 +21,7 @@ install_requires = ['file_read_backwards']
#####
data_files_list = [
("/etc/dbus-1/system.d", ["configs/dbus/org.etersoft.settingsd.conf"]),
("/etc/rc.d/init.d", ["init/settingsd"])
("/etc/rc.d/init.d", ["init/settingsd"]), ('/lib/systemd/system', ['settingsd.service'])
]
for maps_list_item in ( ("share/settingsd/plugins/functions", "plugins/functions"),
("share/settingsd/plugins/actions", "plugins/actions"),
......@@ -30,8 +31,8 @@ for maps_list_item in ( ("share/settingsd/plugins/functions", "plugins/functions
("share/settingsd/data/customs", "data/customs"),
("/etc/settingsd", "configs/settingsd") ) :
data_files_list.append(( maps_list_item[0], [ os.path.join(maps_list_item[1], item)
for item in os.listdir(maps_list_item[1]) if not item in (".gitignore",) ] ))
data_files_list.append(( maps_list_item[0], [ os.path.join(maps_list_item[1], item) for item
in os.listdir(maps_list_item[1]) if os.path.isfile(maps_list_item[1] + '/'+ item) and item not in (".gitignore", "__pycache__") ] ))
#####
......@@ -58,12 +59,20 @@ class SettingsdInstall(install) :
### Public ###
def run(self) :
self.preInstallHook()
install.run(self)
self.postInstallHooks()
### Private ###
def preInstallHook(self):
log.info("running pre-install hooks")
for data_item in data_files_list:
if not os.path.exists(self.install_data + "/" + data_item[0]):
os.makedirs(self.install_data + "/" + data_item[0])
def postInstallHooks(self) :
# FIXME: This is dirty hack. In normal realization, this code must be moved to build stage
......@@ -72,14 +81,15 @@ class SettingsdInstall(install) :
const_py_file = open(os.path.join(self.install_libbase, "settingsd/const.py"), "r+")
const_py_file_data = const_py_file.read()
for replaces_list_item in ( ("\"plugins/functions\"", "\"%s\"" % (os.path.join(self.install_data, "share/settingsd/plugins/functions"))),
("\"plugins/actions\"", "\"%s\"" % (os.path.join(self.install_data, "share/settingsd/plugins/actions"))),
("\"plugins/customs\"", "\"%s\"" % (os.path.join(self.install_data, "share/settingsd/plugins/customs"))),
("\"plugins/functions\"", "\"%s\"" % (os.path.join(self.install_data, "share/settingsd/plugins/functions"))),
("\"data/functions\"", "\"%s\"" % (os.path.join(self.install_data, "share/settingsd/data/functions"))),
("\"data/actions\"", "\"%s\"" % (os.path.join(self.install_data, "share/settingsd/data/actions"))),
("\"data/customs\"", "\"%s\"" % (os.path.join(self.install_data, "share/settingsd/data/customs"))),
("\"configs/settingsd\"", "\"%s\"" % (os.path.join(( self.root if self.root != None else "/" ), "etc/settingsd"))) ) :
for replaces_list_item in (
("\"plugins/functions\"", "\"%s\"" % "/usr/share/settingsd/plugins/functions"),
("\"plugins/actions\"", "\"%s\"" % "/usr/share/settingsd/plugins/actions"),
("\"plugins/customs\"", "\"%s\"" % "/usr/share/settingsd/plugins/customs"),
("\"plugins/functions\"", "\"%s\"" % "/usr/share/settingsd/plugins/functions"),
("\"data/functions\"", "\"%s\"" % "/usr/share/settingsd/data/functions"),
("\"data/actions\"", "\"%s\"" % "/usr/share/settingsd/data/actions"),
("\"data/customs\"", "\"%s\"" % "/usr/share/settingsd/data/customs"),
("\"configs/settingsd\"", "\"%s\"" % "/etc/settingsd") ) :
const_py_file_data = const_py_file_data.replace(replaces_list_item[0], replaces_list_item[1])
const_py_file.truncate()
......@@ -89,6 +99,8 @@ class SettingsdInstall(install) :
try :
const_py_file.close()
except : pass
shutil.copytree("vendor/file_read_backwards", os.path.join(self.install_libbase, 'file_read_backwards'))
##### Main #####
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment