# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved. # # This copyrighted material is made available to anyone wishing to use, # modify, copy, or redistribute it subject to the terms and conditions # of the GNU General Public License v.2. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . from . import cfg from . import objectmanager from . import utils from .cfg import BUS_NAME, BASE_INTERFACE, BASE_OBJ_PATH, MANAGER_OBJ_PATH import threading from . import cmdhandler import time import signal import dbus import dbus.mainloop.glib from . import lvmdb # noinspection PyUnresolvedReferences from gi.repository import GLib from .fetch import StateUpdate from .manager import Manager import traceback import queue from . import udevwatch from .utils import log_debug, log_error import argparse import os import sys from .cmdhandler import LvmFlightRecorder, supports_vdo from .request import RequestEntry class Lvm(objectmanager.ObjectManager): def __init__(self, object_path): super(Lvm, self).__init__(object_path, BASE_INTERFACE) def process_request(): while cfg.run.value != 0: # noinspection PyBroadException try: req = cfg.worker_q.get(True, 5) log_debug( "Method start: %s with args %s (callback = %s)" % (str(req.method), str(req.arguments), str(req.cb))) req.run_cmd() log_debug("Method complete: %s" % str(req.method)) except queue.Empty: pass except Exception: st = traceback.format_exc() utils.log_error("process_request exception: \n%s" % st) def check_bb_size(value): v = int(value) if v < 0: raise argparse.ArgumentTypeError( "positive integers only ('%s' invalid)" % value) return v def install_signal_handlers(): # Because of the glib main loop stuff the python signal handler code is # apparently not usable and we need to use the glib calls instead signal_add = None if hasattr(GLib, 'unix_signal_add'): signal_add = GLib.unix_signal_add elif hasattr(GLib, 'unix_signal_add_full'): signal_add = GLib.unix_signal_add_full if signal_add: signal_add(GLib.PRIORITY_HIGH, signal.SIGHUP, utils.handler, signal.SIGHUP) signal_add(GLib.PRIORITY_HIGH, signal.SIGINT, utils.handler, signal.SIGINT) signal_add(GLib.PRIORITY_HIGH, signal.SIGUSR1, utils.handler, signal.SIGUSR1) else: log_error("GLib.unix_signal_[add|add_full] are NOT available!") def main(): start = time.time() # Add simple command line handling parser = argparse.ArgumentParser() parser.add_argument( "--udev", action='store_true', help="Use udev for updating state", default=False, dest='use_udev') parser.add_argument( "--debug", action='store_true', help="Dump debug messages", default=False, dest='debug') parser.add_argument( "--nojson", action='store_false', help="Do not use LVM JSON output (disables lvmshell)", default=True, dest='use_json') parser.add_argument( "--lvmshell", action='store_true', help="Use the lvm shell, not fork & exec lvm", default=False, dest='use_lvm_shell') parser.add_argument( "--blackboxsize", help="Size of the black box flight recorder, 0 to disable", default=10, type=check_bb_size, dest='bb_size') use_session = os.getenv('LVMDBUSD_USE_SESSION', False) # Ensure that we get consistent output for parsing stdout/stderr os.environ["LC_ALL"] = "C" cfg.args = parser.parse_args() cfg.create_request_entry = RequestEntry # We create a flight recorder in cmdhandler too, but we replace it here # as the user may be specifying a different size. The default one in # cmdhandler is for when we are running other code with a different main. cfg.blackbox = LvmFlightRecorder(cfg.args.bb_size) if cfg.args.use_lvm_shell and not cfg.args.use_json: log_error("You cannot specify --lvmshell and --nojson") sys.exit(1) # We will dynamically add interfaces which support vdo if it # exists. cfg.vdo_support = supports_vdo() if cfg.vdo_support and not cfg.args.use_json: log_error("You cannot specify --nojson when lvm has VDO support") sys.exit(1) # List of threads that we start up thread_list = [] install_signal_handlers() dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) dbus.mainloop.glib.threads_init() cmdhandler.set_execution(cfg.args.use_lvm_shell) if use_session: cfg.bus = dbus.SessionBus() else: cfg.bus = dbus.SystemBus() # The base name variable needs to exist for things to work. # noinspection PyUnusedLocal base_name = dbus.service.BusName(BUS_NAME, cfg.bus) cfg.om = Lvm(BASE_OBJ_PATH) cfg.om.register_object(Manager(MANAGER_OBJ_PATH)) cfg.db = lvmdb.DataStore(cfg.args.use_json, cfg.vdo_support) # Using a thread to process requests, we cannot hang the dbus library # thread that is handling the dbus interface thread_list.append( threading.Thread(target=process_request, name='process_request')) # Have a single thread handling updating lvm and the dbus model so we # don't have multiple threads doing this as the same time updater = StateUpdate() thread_list.append(updater.thread) cfg.load = updater.load cfg.loop = GLib.MainLoop() for thread in thread_list: thread.damon = True thread.start() # Add udev watching if cfg.args.use_udev: log_debug('Utilizing udev to trigger updates') # In all cases we are going to monitor for udev until we get an # ExternalEvent. In the case where we get an external event and the user # didn't specify --udev we will stop monitoring udev udevwatch.add() end = time.time() log_debug( 'Service ready! total time= %.4f, lvm time= %.4f count= %d' % (end - start, cmdhandler.total_time, cmdhandler.total_count), 'bg_black', 'fg_light_green') try: if cfg.run.value != 0: cfg.loop.run() udevwatch.remove() for thread in thread_list: thread.join() except KeyboardInterrupt: # If we are unable to register signal handler, we will end up here when # the service gets a ^C or a kill -2 utils.handler(signal.SIGINT) return 0