#!/usr/bin/env seiscomp-python from __future__ import division, print_function import glob import importlib import math import os import platform import shutil import signal import socket import subprocess import sys import traceback import seiscomp.shell # Problem: if # import seiscomp.config # fails, then in any case a sometimes misleading exception # ImportError: No module named _config # is raised, even if the seiscomp._config module exists but for # another reason fails to import. We therefore... import seiscomp._config # ...here explicitly to get a meaningful exception if this fails. import seiscomp.config import seiscomp.kernel # Python version depended string conversion if sys.version_info[0] < 3: py3ustr = str else: py3ustr = lambda s: s.decode('utf-8', 'replace') try: real_raw_input = raw_input except NameError: real_raw_input = input # request and optionally enforce user input # @param question The question to be answered. # @param default The default value to use if no input was made # @param options List or string of available options. If defined the input must # match one of the options (unless a default is specified). If the input # is invalid the question is repeated. def getInput(question, default=None, options=None): def _default(text): # print default value to previous line if no input was made if default: print("\033[F\033[{}G{}".format(len(text)+1, default)) return default # no options: accept any type of input if not options: defaultStr = "" if default is None else " [{}]".format(default) question = "{}{}: ".format(question, defaultStr) return real_raw_input(question) or _default(question) if default is not None: default = str(default).lower() # options supplied: check and enforce input opts = [str(o).lower() for o in options] optStr = "/".join(o.upper() if o == default else o for o in opts) question = "{} [{}]: ".format(question, optStr) while True: res = real_raw_input(question) if not res and default: return _default(question) if res.lower() in opts: return res.lower() if sys.platform == "darwin": SysLibraryPathVar = "DYLD_FALLBACK_LIBRARY_PATH" SysFrameworkPathVar = "DYLD_FALLBACK_FRAMEWORK_PATH" else: SysLibraryPathVar = "LD_LIBRARY_PATH" SysFrameworkPathVar = None def get_library_path(): if sys.platform == "darwin": return LD_LIBRARY_PATH + ":" + DYLD_FALLBACK_FRAMEWORK_PATH return LD_LIBRARY_PATH def get_framework_path(): return DYLD_FALLBACK_FRAMEWORK_PATH # Python 3 compatible string check def is_string(variable): try: string_class = basestring except NameError: string_class = str return isinstance(variable, string_class) # ------------------------------------------------------------------------------ # Helper functions # ------------------------------------------------------------------------------ SIGTERM_SENT = False def sigterm_handler(_signum, _): # pylint: disable=W0603 global SIGTERM_SENT if not SIGTERM_SENT: SIGTERM_SENT = True os.killpg(0, signal.SIGTERM) sys.exit() def system(args): proc = subprocess.Popen(args, shell=False, env=os.environ) while True: try: return proc.wait() except KeyboardInterrupt: continue except Exception as e: try: proc.terminate() except Exception: pass sys.stderr.write("Exception: %s\n" % str(e)) continue # return subprocess.call(cmd, shell=True) def error(msg): sys.stderr.write("error: %s\n" % msg) sys.stderr.flush() def warning(msg): sys.stderr.write("warning: %s\n" % msg) sys.stderr.flush() # Returns a seiscomp.kernel.Module instance # from a given path with a given name def load_module(path): modname0 = os.path.splitext(os.path.basename(path))[0].replace('.', '_') modname = '__seiscomp_modules_' + modname0 if modname in sys.modules: mod = sys.modules[modname] else: if sys.path[0] != INIT_PATH: sys.path.insert(0, INIT_PATH) mod = importlib.import_module(modname0) mod.__file__ = path # store it in sys.modules sys.modules[modname] = mod module = mod.Module return module def module_key(module): return (module.order, module.name) def load_init_modules(path): modules = [] if not os.path.exists(path): error("Cannot load any module - path not existing: %s" % path) return modules files = glob.glob(os.path.join(path, "*.py")) for f in files: try: pmod = load_module(f) except Exception as exc: error(("%s: " % f) + str(exc)) continue try: mod = pmod(env) # .Module(env) except Exception as exc: error(("%s: " % f) + str(exc)) continue modules.append(mod) #mods = sorted(mods, key=lambda mod: mod.order) modules = sorted(modules, key=module_key) return modules def get_module(name): for module in mods: if module.name == name: return module return None def has_module(name): return get_module(name) is not None def dump_paths(): print('--------------------') print('SEISCOMP_ROOT="%s"' % SEISCOMP_ROOT) print('PATH="%s"' % os.environ["PATH"]) print('%s="%s"' % (SysLibraryPathVar, os.environ[SysLibraryPathVar])) if SysFrameworkPathVar: print( '%s="%s"' % (SysFrameworkPathVar, os.environ[SysFrameworkPathVar])) print('PYTHONPATH="%s"' % sys.path) print('CWD="%s"' % os.getcwd()) print('--------------------') # Returns whether a module should run or not. It simply returns if its # runfile exists. def shouldModuleRun(mod_name): return os.path.exists(env.runFile(mod_name)) def touch(filename): try: open(filename, 'w').close() except Exception as exc: error(str(exc)) def start_module(mod): # Create runfile touch(env.runFile(mod.name)) return mod.start() def stop_module(mod): try: if not mod.stop(): error("Failed to stop %s: unknown error" % mod.name) return 1 except Exception as e: error("Failed to stop %s: %s" % (mod.name, str(e))) return 1 # Delete runfile try: os.remove(env.runFile(mod.name)) except BaseException: return 1 return 0 def start_kernel_modules(): for mod in mods: if isinstance(mod, seiscomp.kernel.CoreModule): return start_module(mod) return 1 def stop_kernel_modules(): for mod in reversed(mods): if isinstance(mod, seiscomp.kernel.CoreModule): return stop_module(mod) return 1 def detectOS(): OSReleaseMap = { 'centos': 'rhel', 'rocky': 'rhel', 'raspbian': 'debian' } try: arch = platform.machine() except BaseException: arch = 'x86_64' data = {} with open('/etc/os-release', 'r') as f: for line in f: toks = line.split("=") if len(toks) != 2: continue data[toks[0].strip().upper()] = toks[1].strip() osID = OSReleaseMap.get(data['ID'].strip('"')) if not osID: osID = data['ID'].strip('"') version = data['VERSION_ID'].strip('"') if osID == 'rhel': try: version = str(math.floor(float(version))) except Exception: pass name = data['NAME'].strip('"') return name, osID, version, arch # ------------------------------------------------------------------------------ # Commandline action handler # ------------------------------------------------------------------------------ def on_setup(args, flags): # pylint: disable=W0621 import seiscomp.setup if "stdin" in flags: cfg = seiscomp.config.Config() if not cfg.readConfig("-"): error("invalid configuration from stdin") return 1 else: setup = seiscomp.setup.Simple() cfg = setup.run(env) retCode = 0 for mod in config_mods: if len(args) == 0 or mod.name in args: try: hasSetupHandler = callable(getattr(mod, 'setup')) except BaseException: hasSetupHandler = False if hasSetupHandler: print("* setup %s" % mod.name) if mod.setup(cfg) != 0: error("module '%s' failed to setup" % mod.name) retCode = 1 if retCode == 0: runpath = os.path.join(SEISCOMP_ROOT, "var", "run") if not os.path.exists(runpath): try: os.makedirs(runpath) except BaseException: error("failed to create directory: %s" % runpath) statfile = os.path.join(runpath, "seiscomp.init") if not os.path.exists(statfile): try: open(statfile, "w").close() except BaseException: error("failed to create status file: %s" % statfile) return retCode def on_setup_help(_): print("Initialize the configuration of all available modules. Each module") print("implements its own setup handler which is called at this point. The") print("initialization takes the installation directory into account and") print("should be repeated when copying the system to another directory.") print("NOTE:") print("Setup might overwrite already made settings with default values.") return 0 def on_shell(_args, _): shell = seiscomp.shell.CLI() try: shell.run(env) except Exception as e: error(str(e)) return 1 return 0 def on_shell_help(_): print("Launches the SeisComP shell, a commandline interface which allows") print("to manage modules configurations and bindings.") return 0 def on_enable(args, _): if not args: error("module name required") return 1 for name in args: modName = get_module(name) if modName is None: error("%s is not available" % name) elif isinstance(modName, seiscomp.kernel.CoreModule): error("%s is a kernel module and is enabled automatically" % name) else: env.enableModule(name) return 0 def on_enable_help(_): print("Enables all given modules to be started when 'seiscomp start' is") print("invoked without a module list.") print() print("Examples:") print("seiscomp enable seedlink slarchive") def on_disable(args, _): if not args: error("module name required") return 1 for name in args: modName = get_module(name) if modName is None: error("%s is not available" % modName) elif isinstance(modName, seiscomp.kernel.CoreModule): error("%s is a kernel module and cannot be disabled" % name) else: env.disableModule(name) return 0 def on_disable_help(_): print("Disables all given modules. See 'enable'.") print() print("Examples:") print("seiscomp disable seedlink slarchive") def on_start(args, _): cntStarted = 0 if not args: if start_kernel_modules() == 0: cntStarted += 1 for mod in mods: # Kernel modules have been started already if isinstance(mod, seiscomp.kernel.CoreModule): continue # Module in autorun? if env.isModuleEnabled(mod.name): if start_module(mod) == 0: cntStarted += 1 else: for mod in mods: if mod.name in args or len(args) == 0: if start_module(mod) == 0: cntStarted += 1 if not useCSV: print("Summary: {} modules started".format(cntStarted)) return 0 def on_start_help(_): print("Starts all enabled modules or a list of modules given.") print() print("Examples:") print("seiscomp start") print("seiscomp start seedlink slarchive") def on_stop(args, _): cntStopped = 0 if not args: for mod in reversed(mods): # Kernel modules will be stopped latter if isinstance(mod, seiscomp.kernel.CoreModule): continue if stop_module(mod) == 0: cntStopped += 1 # Stop all kernel modules if stop_kernel_modules() == 0: cntStopped += 1 else: for mod in reversed(mods): if mod.name in args or len(args) == 0: if stop_module(mod) == 0: cntStopped += 1 if not useCSV: print("Summary: {} modules stopped".format(cntStopped)) return 0 def on_stop_help(_): print("Stops all enabled modules or a list of modules given.") print() print("Examples:") print("seiscomp stop") print("seiscomp stop seedlink slarchive") def on_restart(args, flags): on_stop(args, flags) on_start(args, flags) return 0 def on_restart_help(_): print("Restarts all enabled modules or a list of modules given.") print("This command is equal to:") print("seiscomp stop {args}") print("seiscomp start {args}") print() print("Examples:") print("seiscomp restart") print("seiscomp restart seedlink slarchive") def on_reload(args, _): if not args: for mod in mods: # Reload not supported by kernel modules if isinstance(mod, seiscomp.kernel.CoreModule): continue if shouldModuleRun(mod.name): mod.reload() else: for mod in mods: if mod.name in args or len(args) == 0: mod.reload() return 0 def on_reload_help(_): print("Reloads all enabled modules or a list of modules given.") print("This operation is module specific and implemented only for some") print("modules.") print() print("Examples:") print("seiscomp reload") print("seiscomp reload fdsnws") def on_check(args, _): cntStarted = 0 for mod in mods: if mod.name in args or len(args) == 0: if shouldModuleRun(mod.name): cntStarted += 1 mod.check() if not useCSV: print("Summary: {} started modules checked".format(cntStarted)) return 0 def on_check_help(_): print("Checks if a started module is still running. If not, it is") print("restarted. If no modules are given, all started modules are") print("checked.") print() print("Examples:") print("$ seiscomp check seedlink") print("seedlink is already running") def on_exec(args, _): if len(args) < 1: error("no module name given") return False # Change back into the working dir env.chback() return system(args) def on_exec_help(_): print("Executes a command like calling a command from commandline.") print("It will setup all paths and execute the command.") print("'seiscomp run' will block until the command terminates.") print("Example:") print("seiscomp exec scolv") def on_list(args, _): if len(args) < 1: error("expected argument: {modules|aliases|enabled|disabled|started}") return 1 if args[0] == "modules": found = 0 for mod in mods: if env.isModuleEnabled(mod.name) or \ isinstance(mod, seiscomp.kernel.CoreModule): state = "enabled" else: state = "disabled" found += 1 print("%s is %s" % (mod.name, state)) if not useCSV: print("Summary: {} modules reported".format(found)) return 0 if args[0] == "aliases": f = open(ALIAS_FILE, 'r') lines = [line.rstrip() for line in f.readlines()] for line in lines: if line.lstrip().startswith('#') or not line.strip(): continue toks = [t.strip() for t in line.split('=')] # Remove invalid lines if len(toks) != 2: continue if useCSV: print("%s;%s" % (toks[0], toks[1])) else: print("%s -> %s" % (toks[0], toks[1])) f.close() return 0 if args[0] == "enabled": found = 0 for mod in mods: if env.isModuleEnabled(mod.name) or \ isinstance(mod, seiscomp.kernel.CoreModule): print(mod.name) found += 1 if not useCSV: print("Summary: {} modules enabled".format(found)) return 0 if args[0] == "disabled": found = 0 for mod in mods: if not env.isModuleEnabled(mod.name) and \ not isinstance(mod, seiscomp.kernel.CoreModule): print(mod.name) found += 1 if not useCSV: print("Summary: {} modules disabled".format(found)) return 0 if args[0] == "started": found = 0 for mod in mods: if shouldModuleRun(mod.name): print(mod.name) found += 1 if not useCSV: print("Summary: {} modules started".format(found)) return 0 error( "wrong argument: {modules|aliases|enabled|disabled|started} expected") return 1 def on_list_help(_): print("Prints the result of a query. 5 queries are currently supported:") print(" modules: lists all existing modules") print(" aliases: lists all existing aliases") print(" enabled: lists all enabled modules") print(" disabled: lists all disabled modules") print(" started: lists all started modules") print() print("Examples:") print("$ seiscomp list aliases") print("l1autopick -> scautopick") def on_status(args, _): found = 0 if len(args) > 0 and args[0] == "enabled": for mod in mods: if env.isModuleEnabled(mod.name) or isinstance( mod, seiscomp.kernel.CoreModule): mod.status(shouldModuleRun(mod.name)) found += 1 if not useCSV: print("Summary: {} modules enabled".format(found)) return 0 if len(args) > 0 and args[0] == "started": for mod in mods: if shouldModuleRun(mod.name): mod.status(shouldModuleRun(mod.name)) found += 1 if not useCSV: print("Summary: {} modules started".format(found)) return 0 for mod in mods: if mod.name in args or len(args) == 0: mod.status(shouldModuleRun(mod.name)) found += 1 if not useCSV: print("Summary: {} modules reported".format(found)) return 0 def on_status_help(_): print("Prints the status of ") print(" * all modules") print(" * all enabled modules") print(" * all started modules") print(" * a list of modules") print("and gives a warning if a module should run but doesn't.") print("This command supports csv formatted output via '--csv' switch.") print() print("Examples:") print("$ seiscomp status started") print("$ seiscomp status enabled") print("scmaster is not running [WARNING]") print("$ seiscomp status scautopick") print("scautopick is not running") print("$ seiscomp --csv status scautopick") print("scautopick;0;0;0") print() print("CSV format:") print(" column 1: module name") print(" column 2: running flag") print(" column 3: should run flag") print(" column 4: enabled flag") def on_print(args, _): if len(args) < 1: error("expected argument: {crontab|env}") return 1 if args[0] == "crontab": print("*/3 * * * * %s check >/dev/null 2>&1" % os.path.join(env.SEISCOMP_ROOT, "bin", "seiscomp")) for mod in mods: mod.printCrontab() elif args[0] == "env": print('export SEISCOMP_ROOT="%s"' % SEISCOMP_ROOT) print('export PATH="%s:$PATH"' % BIN_PATH) print('export %s="%s:$%s"' % (SysLibraryPathVar, get_library_path(), SysLibraryPathVar)) if sys.platform == "darwin": print( 'export %s="%s:$%s"' % (SysFrameworkPathVar, get_framework_path(), SysFrameworkPathVar)) print('export PYTHONPATH="%s:$PYTHONPATH"' % PYTHONPATH) print('export MANPATH="%s:$MANPATH"' % MANPATH) print( 'source "%s/share/shell-completion/seiscomp.bash"' % SEISCOMP_ROOT) hostenv = os.path.join(SEISCOMP_ROOT, "etc", "env", "by-hostname", socket.gethostname()) if os.path.isfile(hostenv): print('source %s' % hostenv) else: error("wrong argument: {crontab|env} expected") return 1 return 0 def on_print_help(_): print("seiscomp print {crontab|env}") print(" crontab: prints crontab entries of all registered or given modules.") print(" env: prints environment variables necessary to run SeisComP modules.") print() print("Examples:") print("Source SC environment into current bash session") print("$ eval $(seiscomp/bin/seiscomp print env)") def on_install_deps_linux(args, _): try: name, release, version, arch = detectOS() except BaseException as err: print("*********************************************************************") print("seiscomp was not able to figure out the installed distribution") print("You need to check the documentation for required packages and install") print("them manually.") print("Error: {}".format(err)) print("*********************************************************************") return 1 print("Distribution: {}-{}-{}({}-{})".format(name, version, arch, release, version)) for n in range(version.count('.') + 1): ver = version.rsplit('.', n)[0] script_dir = os.path.join( env.SEISCOMP_ROOT, "share", "deps", release.lower(), ver.lower()) if os.path.exists(script_dir): break if not os.path.exists(script_dir): print("*********************************************************************") print("Sorry, the installed distribution is not supported.") print("You need to check the documentation for required packages and install") print("them manually.") print("*********************************************************************") return 1 for pkg in args: script = os.path.join(script_dir, "install-" + pkg + ".sh") if not os.path.exists(script): error("no handler available for package '%s'" % pkg) return 1 if system(["sudo", "sh", script]) != 0: error("installation failed") return 1 return 0 def on_install_deps(args, flags): if not args: error("expected package list: PKG1 [PKG2 [..]]") print("Example: seiscomp install-deps base gui mysql-server") print("For a list of available packages issue: seiscomp help install-deps") if sys.platform.startswith("linux"): return on_install_deps_linux(args, flags) error("unsupported platform") print("*********************************************************************") print("The platform you are currently running on is not supported to install") print("dependencies automatically.") print("You need to check the documentation for required packages and install") print("them manually.") print("*********************************************************************") return 1 def on_install_deps_help(_): print("seiscomp install-deps PKG1 [PKG2 [..]]") print("Installs OS dependencies to run SeisComP. This requires either a 'sudo'") print("or root account. Available packages are:") print(" base: basic packages required by all installations") print(" gui: required by graphical user interfaces, e.g. on workstations") print(" [mysql,mariadb,postgresql]-server:") print(" database management system required by the machine running") print(" the SeisComP messaging system (scmaster)") print(" fdsnws: required for data sharing via the FDSN web services") return 0 def on_update_config(args, _): kernelModsStarted = False configuredMods = {} listOfMods = args if not listOfMods: listOfMods = [] for mod in config_mods: listOfMods.append(mod.name) while len(listOfMods) > 0: for mod in config_mods: if mod.name in listOfMods: if not kernelModsStarted and mod.requiresKernelModules(): print("* starting kernel modules") start_kernel_modules() kernelModsStarted = True print("* configure %s" % mod.name) proxy = None try: proxy = mod.updateConfigProxy() if is_string(proxy): configuredMods.setdefault(proxy, False) except Exception: pass if proxy is None: result = mod.updateConfig() try: error_code = int(result) except ValueError: error("unexpected return type when updating " "configuration of %s" % mod.name) return 1 if error_code != 0: error( "updating configuration for %s failed" % mod.name) return 1 configuredMods[mod.name] = True listOfMods = [] # Collect all unconfigured but indirectly requested mods for name, configured in configuredMods.items(): if not configured: listOfMods.append(name) return 0 def on_update_config_help(_): print("Updates the configuration of all available modules. This command") print("will convert the etc/*.cfg to the modules native configuration") print("including its bindings.") return 0 def on_alias(args, _): if len(args) < 2: error("expected arguments: {create|remove} ALIAS_NAME APP_NAME") return 1 aliasName = args[1] if args[0] == "create": if len(args) != 3: error("expected two arguments for create: ALIAS_NAME APP_NAME") return 1 mod = None for module in mods: if module.name == args[2]: mod = module break if not mod: error("module '%s' not found" % args[2]) return 1 supportsAliases = False try: supportsAliases = mod.supportsAliases() except BaseException: pass if not supportsAliases: error("module '%s' does not support aliases" % args[2]) return 1 mod2 = args[2] if os.path.exists(os.path.join("bin", mod2)): mod1 = os.path.join("bin", aliasName) elif os.path.exists(os.path.join("sbin", mod2)): mod1 = os.path.join("sbin", aliasName) else: error("no %s binary found (neither bin nor sbin)") return 1 # create alias line in etc/descriptions/aliases if not os.path.exists(DESC_PATH): try: os.makedirs(DESC_PATH) except Exception: error("failed to create directory: %s" % DESC_PATH) return 1 has_alias = False lines = [] new_lines = [] try: f = open(ALIAS_FILE, 'r') lines = [line.rstrip() for line in f.readlines()] for line in lines: if line.lstrip().startswith('#') or not line.strip(): # Keep comments or empty lines new_lines.append(line) continue toks = [t.strip() for t in line.split('=')] # Remove invalid lines if len(toks) != 2: continue if toks[0] == aliasName: has_alias = True break new_lines.append(line) f.close() except BaseException: pass if has_alias: warning("%s is already registered as alias for %s in " \ "$SEISCOMP_ROOT/etc/descriptions/aliases" % (aliasName, toks[1])) warning(" + do not register again but trying to link the required files") else: print( "Registered alias '%s' in $SEISCOMP_ROOT/etc/descriptions/aliases" % (aliasName)) # Check if target exists already if os.path.exists(os.path.join(SEISCOMP_ROOT, mod1)): warning( "link '%s' to '%s' exists already in %s/bin/" % (aliasName, mod2, SEISCOMP_ROOT)) warning(" + do not link again") try: f = open(ALIAS_FILE, 'w') except BaseException: error("failed to open/create alias file: %s" % ALIAS_FILE) return 1 new_lines.append("%s = %s" % (aliasName, args[2])) f.write("\n".join(new_lines) + "\n") f.close() # create symlink of defaults from etc/defaults/mod1.cfg to etc/defaults/mod2.cfg # use relative path to default_cfg2 cwdAlias = os.getcwd() os.chdir(os.path.join(SEISCOMP_ROOT, "etc", "defaults")) default_cfg1 = aliasName + ".cfg" default_cfg2 = args[2] + ".cfg" if os.path.exists(default_cfg2): print("Linking default configuration: %s -> %s" % (default_cfg2, default_cfg1)) # - first: remove target try: os.remove(default_cfg1) except BaseException: pass # create symlink os.symlink(os.path.relpath(default_cfg2), default_cfg1) else: print("No default configuration to link") # return to initial directory os.chdir(cwdAlias) # create symlink from bin/mod1 to bin/mod2 # - first: remove target try: os.remove(os.path.join(SEISCOMP_ROOT, mod1)) except BaseException: pass print("Creating app symlink: %s -> %s" % (mod2, mod1)) os.symlink(mod2, os.path.join(SEISCOMP_ROOT, mod1)) # create symlink from etc/init/mod1.py to etc/init/mod2.py cwdAlias = os.getcwd() os.chdir(os.path.join(SEISCOMP_ROOT, "etc", "init")) init1 = aliasName + ".py" init2 = args[2] + ".py" print("Linking init script: %s -> %s" % (init2, init1)) # - first: remove target try: os.remove(init1) except BaseException: pass # create symlink with relative path os.symlink(os.path.relpath(init2), init1) # return to initial directory os.chdir(cwdAlias) return 0 if args[0] == "remove": if len(args) != 2: error("expected one argument for remove: alias-name") return 1 print("Removing alias '%s'" % aliasName) # check and remove alias line in etc/descriptions/aliases has_alias = False lines = [] new_lines = [] try: f = open(ALIAS_FILE, 'r') lines = [line.rstrip() for line in f.readlines()] for line in lines: if line.lstrip().startswith('#') or not line.strip(): # Keep comments or empty lines new_lines.append(line) continue toks = [t.strip() for t in line.split('=')] # Remove invalid lines if len(toks) != 2: continue if toks[0] == aliasName: has_alias = True else: new_lines.append(line) f.close() except BaseException: pass if not has_alias: print(" + {} is not defined as an alias".format(aliasName)) if not interactiveMode: print(" + remove related configuration with '--interactive'") if len(lines) == len(new_lines): return 1 try: f = open(ALIAS_FILE, 'w') except BaseException: error(" + failed to open/create alias file: %s" % ALIAS_FILE) return 1 if len(lines) > 0: f.write("\n".join(new_lines) + "\n") f.close() if not has_alias: if not interactiveMode: return 1 # remove symlink from bin/mod1 if os.path.exists(os.path.join("bin", aliasName)): sym_link = os.path.join("bin", aliasName) elif os.path.exists(os.path.join("sbin", aliasName)): sym_link = os.path.join("sbin", aliasName) else: sym_link = "" if sym_link: print(" + removing app symlink: %s" % sym_link) try: os.remove(os.path.join(SEISCOMP_ROOT, sym_link)) except BaseException: pass # remove symlink from etc/init/mod1.py init_scr = os.path.join("etc", "init", aliasName + ".py") print(" + removing init script: %s" % init_scr) try: os.remove(os.path.join(SEISCOMP_ROOT, init_scr)) except BaseException: pass # delete defaults etc/defaults/mod1.cfg default_cfg = os.path.join("etc", "defaults", aliasName + ".cfg") print(" + removing default configuration: {}/{}" .format(SEISCOMP_ROOT, default_cfg)) try: os.remove(os.path.join(SEISCOMP_ROOT, default_cfg)) except BaseException as e: error(" + could not remove %s" % e) if not interactiveMode: warning("No other configuration removed for '%s' - interactive" " removal is supported by '--interactive'" % aliasName) return 0 # test module configuration files # SYSTEMCONFIGDIR cfg = os.path.join("etc", aliasName + ".cfg") if os.path.isfile(cfg): print(" + found module configuration file: {}/{}" .format(SEISCOMP_ROOT, cfg)) answer = getInput(" + do you wish to remove it?", 'n', 'yn') if answer == "y": try: os.remove(cfg) except Exception as e: error(" + could not remove '%s' - try manually" % e) # CONFIGDIR cfg = os.path.join( os.path.expanduser("~"), ".seiscomp", aliasName + ".cfg") if os.path.isfile(cfg): print(" + found module configuration file: {}".format(cfg)) answer = getInput(" + do you wish to remove it?", 'n', 'yn') if answer == "y": try: os.remove(cfg) except Exception as e: error(" + could not remove the file: %s - try manually" % e) # test module binding files bindingDir = os.path.join(SEISCOMP_ROOT, "etc", "key", aliasName) if os.path.exists(bindingDir): print(" + found binding directory: {}".format(bindingDir)) answer = getInput(" + do you wish to remove it?", 'n', 'yn') if answer == "y": try: shutil.rmtree(bindingDir) except Exception as e: error(" + could not remove the directory: %s - try manually" % e) # test key files keyDir = os.path.join(SEISCOMP_ROOT, 'etc', 'key') dirContent = os.listdir(keyDir) keyFiles = [] print(" + testing key files") for f in dirContent: if not os.path.isfile(os.path.join(keyDir, f)) or \ not f.startswith("station_"): continue keyFile = os.path.join(keyDir, f) with open(keyFile, 'r') as fp: # Read all lines in the file one by one for line in fp: # check if the line starts with the module name if line.startswith(aliasName): keyFiles.append(keyFile) print(" + found binding for '{}' in: {}".format(aliasName, keyFile)) if keyFiles: print(" + found {} bindings for '{}' in key files".format(len(keyFiles), aliasName)) question = " + remove all '{}' bindings from key files?".format(aliasName) answer = getInput(question, 'n', 'yn') if answer == "y": shell = seiscomp.shell.CLI(env) shell.commandRemove(["module", aliasName, "*.*"]) else: print(" + found no key files") return 0 error("Wrong command '%s': expected 'create' or 'remove'" % args[0]) return 1 def on_alias_help(_): print("seiscomp alias {create|remove} ALIAS_NAME APP_NAME") print("Creates/removes symlinks to applications. Symlinks to symlinks are not allowed.") print() print("Examples:") print("$ seiscomp alias create scautopick2 scautopick") print("Copy default configuration: etc/defaults/scautopick.cfg -> etc/defaults/scautopick2.cfg") print("Create app symlink: scautopick -> bin/scautopick2") print("Copy init script: etc/init/scautopick.py -> etc/init/scautopick2.py") print() print("$ seiscomp alias remove scautopick2") print("Remove default configuration: etc/defaults/scautopick2.cfg") print("Remove app symlink: bin/scautopick2") print("Remove init script: etc/init/scautopick2.py") allowed_actions = [ "install-deps", "setup", "shell", "enable", "disable", "start", "stop", "restart", "reload", "check", "status", "list", "exec", "update-config", "alias", "print", "help" ] # Define all actions that do not need locking of seiscomp actions_without_lock = [ # "install-deps", "help", "list", "exec", "print" ] def on_help(args, _): if not args: print("Name:") print(" seiscomp - Load the environment of the SeisComP installation from " \ "where seiscomp is executed and run a command") print("\nSynopsis:") print(" seiscomp [flags] [commands] [arguments]") print("\nFlags:") print(" --asroot Allow running a command as root") print(" --csv Print output as csv in machine-readable format") print(" -i, [--interactive] Interactive mode: Allow deleting files " \ "interactively when removing aliases") print(" --wait arg Define a timeout in seconds for acquiring the seiscomp " \ "lock file, e.g. `seiscomp --wait 10 update-config`") print("\nAvailable commands:") for helpAction in allowed_actions: print(" %s" % helpAction) print("\nUse 'help [command]' to get more help about a command") print("\nExamples:") print(" seiscomp help update-config Show help for update-config") print(" seiscomp update-config Run update-config for allmodules") print(" seiscomp update-config trunk Run update-config for all trunk modules") print(" seiscomp update-config scautopick Run update-config for scautopick") return 0 cmd = args[0] try: func = globals()["on_" + cmd.replace("-", "_") + "_help"] except BaseException: print("Sorry, no help available for %s" % cmd) return 1 func(args[1:]) return 0 def run_action(runAction, args, flags): try: func = globals()["on_" + runAction.replace("-", "_")] return func(args, flags) except Exception as exc: error("command '%s' failed: %s" % (runAction, str(exc))) if "debug" in flags: info = traceback.format_exception(*sys.exc_info()) for i in info: sys.stderr.write(i) return 2 def on_csv_help(_): print("If --csv is prepended to a usual command the internal output is") print("set to comma separated values. The only command that currently") print("uses this output format is 'status'.") print() print("Example:") print("seiscomp --csv status") return 0 # ------------------------------------------------------------------------------ # Check command line # ------------------------------------------------------------------------------ useCSV = False asRoot = False lockTimeout = None interactiveMode = False argv = sys.argv[1:] argflags = [] # Check for flags while argv: if argv[0] == "--csv": useCSV = True argv = argv[1:] elif argv[0] == "--asroot": asRoot = True argv = argv[1:] if argv[0] == "--interactive" or argv[0] == "-i": interactiveMode = True argv = argv[1:] elif argv[0] == "--wait": argv = argv[1:] if not argv: print("--wait expects an integer value in seconds") sys.exit(1) try: lockTimeout = int(argv[0]) except BaseException: print("Wait timeout is not an integer: %s" % argv[0]) sys.exit(1) if lockTimeout < 0: print("Wait timeout must be positive: %s" % argv[0]) sys.exit(1) argv = argv[1:] elif argv[0].startswith("--"): argflags.append(argv[0][2:]) argv = argv[1:] else: break if len(argv) < 1: print("seiscomp [flags] {%s} [args]" % "|".join(allowed_actions)) print("\nUse 'seiscomp help' to get more help") sys.exit(1) action = argv[0] arguments = argv[1:] if action not in allowed_actions: print("seiscomp [flags] {%s} [args]" % "|".join(allowed_actions)) sys.exit(1) if os.getuid() == 0 and not asRoot and action != "install-deps": print("Running 'seiscomp' as root is dangerous. Use --asroot only if you") print("know exactly what you are doing!") sys.exit(1) # ------------------------------------------------------------------------------ # Initialize the environment # ------------------------------------------------------------------------------ # Resolve symlinks to files (if any) if os.path.islink(sys.argv[0]): # Read the link target target = os.readlink(sys.argv[0]) # Is the target an absolute path then take it as is if os.path.isabs(target): sys.argv[0] = target # Otherwise join the dirname of the script with the target # to get the semi-real path of the seiscomp script. Semi-real # refers to the fact that symlinks are not completely resolved # and why the usage of os.path.realpath is avoided. If the # seiscomp directory itself is a symlink it should be preserved. else: sys.argv[0] = os.path.join(os.path.dirname(sys.argv[0]), target) # Guess SEISCOMP_ROOT from path of called script, directory links are not # resolved allowing to create separate SeisComP environments if os.path.isabs(sys.argv[0]): root_path = sys.argv[0] else: cwd = os.getenv('PWD') if cwd is None: cwd = os.getcwd() root_path = os.path.join(cwd, sys.argv[0]) SEISCOMP_ROOT = os.path.dirname(os.path.dirname(os.path.normpath(root_path))) INIT_PATH = os.path.join(SEISCOMP_ROOT, "etc", "init") DESC_PATH = os.path.join(SEISCOMP_ROOT, "etc", "descriptions") ALIAS_FILE = os.path.join(DESC_PATH, "aliases") BIN_PATH = os.path.join(SEISCOMP_ROOT, "bin") SBIN_PATH = os.path.join(SEISCOMP_ROOT, "sbin") PYTHONPATH = os.path.join(SEISCOMP_ROOT, "lib", "python") MANPATH = os.path.join(SEISCOMP_ROOT, "share", "man") LD_LIBRARY_PATH = os.path.join(SEISCOMP_ROOT, "lib") DYLD_FALLBACK_FRAMEWORK_PATH = os.path.join(SEISCOMP_ROOT, "lib", "3rd-party") # Run another process with proper LD_LIBRARY_PATH set otherwise the dynamic # linker will not find dependent SC3 libraries isWrapped = False try: if os.environ["SEISCOMP_WRAP"] == "TRUE": isWrapped = True except BaseException: pass # Setup signal handler #signal.signal(signal.SIGTERM, sigterm_handler) if not isWrapped: try: os.environ["PATH"] = BIN_PATH + ":" + os.environ["PATH"] except BaseException: os.environ["PATH"] = BIN_PATH try: os.environ[SysLibraryPathVar] = get_library_path() + ":" + \ os.environ[SysLibraryPathVar] except BaseException: os.environ[SysLibraryPathVar] = get_library_path() if sys.platform == "darwin": os.environ[SysFrameworkPathVar] = get_framework_path() try: os.environ["PYTHONPATH"] = PYTHONPATH + ":" + os.environ["PYTHONPATH"] except BaseException: os.environ["PYTHONPATH"] = PYTHONPATH try: os.environ["MANPATH"] = MANPATH + ":" + os.environ["MANPATH"] except BaseException: os.environ["MANPATH"] = MANPATH os.environ["SEISCOMP_WRAP"] = "TRUE" sys.exit(system(sys.argv)) # Register local lib/python in SEARCH PATH sys.path.insert(0, PYTHONPATH) # Create environment which supports queries for various SeisComP # directoris and sets PATH, LD_LIBRARY_PATH and PYTHONPATH env = seiscomp.kernel.Environment(SEISCOMP_ROOT) env.setCSVOutput(useCSV) # Check for lock file isChild = False if action in actions_without_lock: isChild = True else: try: isChild = os.environ["SEISCOMP_LOCK"] == "TRUE" except KeyError: pass if not isChild: if not env.tryLock("seiscomp", lockTimeout): error("Could not get lock %s - is another process using it?" % env.lockFile("seiscomp")) sys.exit(1) os.environ["SEISCOMP_LOCK"] = "TRUE" exitcode = system( ["run_with_lock", "-q", env.lockFile("seiscomp")] + sys.argv) sys.exit(exitcode) # Change into SEISCOMP_ROOT directory. The env instance will change # back into the current working directory automatically if destroyed. env.chroot() simpleCommand = (action == "install-deps") or \ (action == "print" and arguments == "env") if not simpleCommand: config_mods = load_init_modules(INIT_PATH) mods = [] for m in config_mods: if m.isConfigModule: continue mods.append(m) sys.exit(run_action(action, arguments, argflags))