Commit 0f0c92ba authored by Devaev Maxim's avatar Devaev Maxim

Added short functions for logging, split config loading on two stages, daemon…

Added short functions for logging, split config loading on two stages, daemon module, main() with getopt and interactive mode of service
parent f4daeb3e
# -*- coding: utf-8 -*-
import sys
import syslog
import getopt
from settingsd import const
from settingsd import config
from settingsd import logger
from settingsd import validators
from settingsd import application from settingsd import application
#from settingsd import daemon # TODO
##### 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-k, --kill -- Kill daemon process" % (const.MY_NAME) )
def version() :
print "%s version %s" % (const.MY_NAME, const.VERSION)
##### Main ######
if __name__ == "__main__" : if __name__ == "__main__" :
app = application.Application() log_level = None
app.exec_() use_syslog_flag = None
bus_type = None
daemon_mode_flag = False
try :
(opts_list, args_list) = getopt.getopt(sys.argv[1:], "hdk", ( "help", "version",
"log-level=", "use-syslog=", "bus-type=", "daemon", "kill" ))
for (opts_list_item, args_list_item) in opts_list :
if opts_list_item in ("-h", "--help") :
help()
elif opts_list_item in ("-v", "--version") :
version()
elif opts_list_item in ("--log-level") :
try :
log_level = validators.validRange(int(args_list_item), const.ALL_LOG_LEVELS_LIST)
except Exception, 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.validBool(args_list_item)
except Exception, err1 :
print "Incorrect option \"%s\": %s" % (opts_list_item, str(err1))
elif opts_list_item in ("--bus-type") :
try :
bus_type = validators.validRange(args_list_item, const.ALL_BUS_TYPES_LIST)
except Exception, 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 ("-k", "--kill") :
pass # TODO
else :
print "Unknown option \"%s\"" % (opts_list_item)
except Exception, err1 :
print "Bad command line options: %s" % (str(err1))
#####
if not daemon_mode_flag :
app = application.Application()
try :
app.loadApplicationConfigs()
except :
logger.error("Initialization error")
sys.exit(1)
if bus_type != None :
config.setValue(const.MY_NAME, "bus_type", bus_type)
if log_level != None :
config.setValue(const.MY_NAME, "log_level", log_level)
if use_syslog_flag :
syslog.openlog(const.MY_NAME, syslog.LOG_PID, syslog.LOG_USER)
config.setValue(const.RUNTIME_NAME, "use_syslog", True)
try :
app.loadModules()
app.loadServicesConfigs()
app.initBus()
app.initServices()
logger.info("Initialized")
except :
logger.error("Initialization error")
sys.exit(1)
try :
app.runLoop()
except (SystemExit, KeyboardInterrupt) :
try :
app.closeServices()
except :
logger.error("Critical error on services closing, abort all processes and go boom")
app.quitLoop()
logger.info("Closed")
except :
logger.error("Runtime error, trying to close services")
logger.attachException()
try :
app.closeServices()
except :
logger.error("Critical error on services closing, abort all processes and go boom")
app.quitLoop()
logger.info("Closed")
sys.exit(1)
else :
print "TODO"
...@@ -19,7 +19,7 @@ class Application(object) : ...@@ -19,7 +19,7 @@ class Application(object) :
def __init__(self) : def __init__(self) :
object.__init__(self) object.__init__(self)
self._bus_name = None #####
self._modules_list = [] self._modules_list = []
self._services_dict = {} self._services_dict = {}
...@@ -29,30 +29,13 @@ class Application(object) : ...@@ -29,30 +29,13 @@ class Application(object) :
### Public ### ### Public ###
def exec_(self) : def runLoop(self) :
self.init() logger.verbose("Running GObject loop...")
logger.message(logger.INFO_MESSAGE, "Initialized")
try :
self.run()
except KeyboardInterrupt :
self.close()
logger.message(logger.INFO_MESSAGE, "Closed")
### Private ###
def init(self) :
self.loadModules()
self.loadConfigs()
self.initBus()
self.initServices()
def run(self) :
self._main_loop.run() self._main_loop.run()
def close(self) : def quitLoop(self) :
self.closeServices()
self._main_loop.quit() self._main_loop.quit()
logger.verbose("GObject loop closed")
def loadModules(self) : def loadModules(self) :
sys.path.append(const.FUNCTIONS_DIR) sys.path.append(const.FUNCTIONS_DIR)
...@@ -62,46 +45,70 @@ class Application(object) : ...@@ -62,46 +45,70 @@ class Application(object) :
for module_name in [ item[:-3] for item in os.listdir(modules_path_list_item) if item.endswith(".py") ] : for module_name in [ item[:-3] for item in os.listdir(modules_path_list_item) if item.endswith(".py") ] :
try : try :
self._modules_list.append(__import__(module_name, globals(), locals(), [""])) self._modules_list.append(__import__(module_name, globals(), locals(), [""]))
except Exception : except :
logger.message(logger.ERROR_MESSAGE, "Import error on module \"%s\"" % (module_name)) logger.error("Import error on module \"%s\"" % (module_name))
logger.attachException() logger.attachException()
continue continue
self._services_dict[self._modules_list[-1].Service.serviceName()] = { self._services_dict[self._modules_list[-1].Service.serviceName()] = {
"service_class" : self._modules_list[-1].Service, "service_class" : self._modules_list[-1].Service,
"service" : None "service" : None
} }
logger.verbose("Loaded module: %s" % (module_name))
sys.path.remove(const.FUNCTIONS_DIR) sys.path.remove(const.FUNCTIONS_DIR)
sys.path.remove(const.ACTIONS_DIR) sys.path.remove(const.ACTIONS_DIR)
def loadConfigs(self) : def loadApplicationConfigs(self) :
config.loadConfigs(only_sections_list = (const.MY_NAME,))
def loadServicesConfigs(self) :
for service_name in self._services_dict.keys() : for service_name in self._services_dict.keys() :
service_options_list = list(self._services_dict[service_name]["service_class"].optionsList()) service_options_list = list(self._services_dict[service_name]["service_class"].optionsList())
service_options_list.append((service_name, "enabled", "no", validators.validBool)) service_options_list.append((service_name, "enabled", "no", validators.validBool))
for service_options_list_item in service_options_list : for service_options_list_item in service_options_list :
config.setValue(*service_options_list_item) try :
config.setValue(*service_options_list_item)
except :
logger.error("Error on set options tuple %s" % (str(service_options_list_item)))
logger.attachException()
config.loadConfig() config.loadConfigs(exclude_sections_list = (const.MY_NAME,))
def initBus(self) : def initBus(self) :
if config.value(const.MY_NAME, "bus_type") == const.BUS_TYPE_SYSTEM : bus_type = config.value(const.MY_NAME, "bus_type")
bus = dbus.SystemBus() service_name = config.value(const.MY_NAME, "service_name")
else :
bus = dbus.SessionBus() try :
self._bus_name = dbus.service.BusName(config.value(const.MY_NAME, "service_name"), bus = bus) config.setValue(const.RUNTIME_NAME, "bus_name", dbus.service.BusName(service_name,
config.setValue(const.RUNTIME_NAME, "bus_name", self._bus_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) : def initServices(self) :
for service_name in self._services_dict.keys() : for service_name in self._services_dict.keys() :
if config.value(service_name, "enabled") : if config.value(service_name, "enabled") :
self._services_dict[service_name]["service"] = self._services_dict[service_name]["service_class"]() try :
self._services_dict[service_name]["service"].initService() 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()
def closeServices(self) : def closeServices(self) :
for service_name in self._services_dict.keys() : for service_name in self._services_dict.keys() :
if self._services_dict[service_name]["service"] != None : if self._services_dict[service_name]["service"] != None :
self._services_dict[service_name]["service"].closeService() try :
del self._services_dict[service_name]["service"] 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 self._services_dict[service_name]["service"] = None
...@@ -18,8 +18,8 @@ ConfigDictObject = { ...@@ -18,8 +18,8 @@ ConfigDictObject = {
"log_level" : (const.DEFAULT_LOG_LEVEL, ( lambda arg : validators.validRange(int(arg), const.ALL_LOG_LEVELS_LIST) )) "log_level" : (const.DEFAULT_LOG_LEVEL, ( lambda arg : validators.validRange(int(arg), const.ALL_LOG_LEVELS_LIST) ))
}, },
const.RUNTIME_NAME : { const.RUNTIME_NAME : {
"bus_name" : None, "bus_name" : (None, None),
"use_syslog" : False "use_syslog" : (False, None)
} }
} }
...@@ -48,7 +48,11 @@ def value(section, option) : ...@@ -48,7 +48,11 @@ def value(section, option) :
def validator(section, option) : def validator(section, option) :
return ConfigDictObject[section][option][1] return ConfigDictObject[section][option][1]
def loadConfig() : 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(const.RUNTIME_NAME)
for config_files_list_item in os.listdir(const.CONFIGS_DIR) : for config_files_list_item in os.listdir(const.CONFIGS_DIR) :
if not config_files_list_item.endswith(const.CONFIG_FILE_POSTFIX) : if not config_files_list_item.endswith(const.CONFIG_FILE_POSTFIX) :
continue continue
...@@ -57,7 +61,7 @@ def loadConfig() : ...@@ -57,7 +61,7 @@ def loadConfig() :
config_parser.read(os.path.join(const.CONFIGS_DIR, config_files_list_item)) config_parser.read(os.path.join(const.CONFIGS_DIR, config_files_list_item))
for section in config_parser.sections() : for section in config_parser.sections() :
if section == const.RUNTIME_NAME : if (len(only_sections_list) != 0 and not section in only_sections_list) or section in exclude_sections_list :
continue continue
for option in config_parser.options(section): for option in config_parser.options(section):
......
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
MY_NAME = "settingsd" MY_NAME = "settingsd"
RUNTIME_NAME = "runtime" RUNTIME_NAME = "runtime"
VERSION = "0.1"
FUNCTIONS_DIR = "functions" FUNCTIONS_DIR = "functions"
ACTIONS_DIR = "actions" ACTIONS_DIR = "actions"
CONFIGS_DIR = "configs" CONFIGS_DIR = "configs"
......
# -*- coding: utf-8 -*-
import os
import signal
import errno
import resource
import logger
##### Private methods #####
def pidOfPythonProc(proc_name, uid = 0) :
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_mode != uid :
continue
cmdline_file = open(cmdline_file_path)
cmdline_list = cmdline_file.read().split("\0")
if len(cmdline_list) >= 2 and os.path.basename(cmdline_list[1]) == proc_name :
cmdline_file.close()
return proc_pid
cmdline_file.close()
return None
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 xrange(retries_count) :
try :
os.close(fd)
except OSError, err1 :
if err1.errno != errno.EBADF :
continue
break
##### Public methods #####
def startDaemon(init_function, close_function, work_dir_path = None, umask = None) :
pid = os.fork()
if pid > 0 :
try :
os.waitpid(pid, 0)
except OSError :
pass
os._exit(0)
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 xrange(maxFd()) :
closeFd(fd)
null_fd = os.open("/dev/null", os.O_RDWR)
for fd in (0, 1, 2) :
os.dup2(null_fd, fd)
try :
init_function()
except (SystemExit, KeyboardInterrupt) :
close_function()
except :
try :
close_function()
except :
pass
os._exit(1) # FIXME
else :
os._exit(1) # FIXME
else :
os._exit(1) # FIXME
def killDaemon() :
pid = pidOfPythonProc("main.py", os.getuid()) # FIXME
if pid != None :
os.kill(pid, signal.SIGTERM)
else :
logger.error("Cannot determine a daemon process of \"%s\"" % ("main.py")) # FIXME
...@@ -33,7 +33,31 @@ class UnknownMessageType(Exception) : ...@@ -33,7 +33,31 @@ class UnknownMessageType(Exception) :
##### Public methods ##### ##### Public methods #####
def message(message_type, message) : 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)
##### Private methods #####
def log(message_type, message) :
if not message_type in ALL_MESSAGES_LIST : if not message_type in ALL_MESSAGES_LIST :
raise UnknownMessageType("Message type \"%d\" not in list %s" % (message_type, ALL_MESSAGES_LIST)) raise UnknownMessageType("Message type \"%d\" not in list %s" % (message_type, ALL_MESSAGES_LIST))
...@@ -43,10 +67,6 @@ def message(message_type, message) : ...@@ -43,10 +67,6 @@ def message(message_type, message) :
print >> sys.stderr, const.MY_NAME, message print >> sys.stderr, const.MY_NAME, message
if config.value(const.RUNTIME, "use_syslog") : if config.value(const.RUNTIME_NAME, "use_syslog") :
syslog.syslog(message_type[1], message) syslog.syslog(message_type[1], message)
def attachException(message_type = ERROR_MESSAGE) :
for line in traceback.format_exc().splitlines() :
message(message_type, line)
...@@ -83,8 +83,8 @@ def tracer(function) : ...@@ -83,8 +83,8 @@ def tracer(function) :
def wrapper(self, *args_list, **kwargs_dict) : def wrapper(self, *args_list, **kwargs_dict) :
return_value = function(self, *args_list, **kwargs_dict) return_value = function(self, *args_list, **kwargs_dict)
if config.value(const.MY_NAME, "log_level") == const.LOG_LEVEL_DEBUG : if config.value(const.MY_NAME, "log_level") == const.LOG_LEVEL_DEBUG :
logger.message(logger.DEBUG_MESSAGE, "Called \"%s::%s\" with args (%s, %s) --> %s" % ( logger.debug("Called \"%s::%s\" with args (%s, %s) --> %s" % (self.__class__.__name__, function.__name__,
self.__class__.__name__, function.__name__, str(args_list), str(kwargs_dict), str(return_value) )) str(args_list), str(kwargs_dict), str(return_value) ))
return return_value return return_value
wrapper.__name__ = function.__name__ wrapper.__name__ = function.__name__
......
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