1642 lines
49 KiB
Python
Executable File
1642 lines
49 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
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
|
|
|
|
|
|
# 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(f"\033[F\033[{len(text) + 1}G{default}")
|
|
return default
|
|
|
|
# no options: accept any type of input
|
|
if not options:
|
|
defaultStr = "" if default is None else f" [{default}]"
|
|
question = f"{question}{defaultStr}: "
|
|
return 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 = f"{question} [{optStr}]: "
|
|
while True:
|
|
res = 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):
|
|
with subprocess.Popen(args, shell=False, env=os.environ) as proc:
|
|
while True:
|
|
try:
|
|
return proc.wait()
|
|
except KeyboardInterrupt:
|
|
continue
|
|
except Exception as e:
|
|
try:
|
|
proc.terminate()
|
|
except Exception:
|
|
pass
|
|
sys.stderr.write(f"Exception: {e}\n")
|
|
continue
|
|
|
|
# return subprocess.call(cmd, shell=True)
|
|
|
|
|
|
def info(msg):
|
|
sys.stderr.write(f"info: {msg}\n")
|
|
sys.stderr.flush()
|
|
|
|
|
|
def error(msg):
|
|
sys.stderr.write(f"error: {msg}\n")
|
|
sys.stderr.flush()
|
|
|
|
|
|
def warning(msg):
|
|
sys.stderr.write(f"warning: {msg}\n")
|
|
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.isdir(path):
|
|
error(f"Failed to load modules, no such directory: {path}")
|
|
return modules
|
|
|
|
files = glob.glob(os.path.join(path, "*.py"))
|
|
for f in files:
|
|
try:
|
|
pmod = load_module(f)
|
|
except Exception as exc:
|
|
warning(f"Failed to load module from file {f}: {exc}")
|
|
continue
|
|
|
|
try:
|
|
mod = pmod(env) # .Module(env)
|
|
except Exception as exc:
|
|
warning(f"Failed to instantiate module from file {f}: {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(f'SEISCOMP_ROOT="{SEISCOMP_ROOT}"')
|
|
print(f"PATH=\"{os.environ['PATH']}\"")
|
|
print(f'{SysLibraryPathVar}="{os.environ[SysLibraryPathVar]}"')
|
|
if SysFrameworkPathVar:
|
|
print(f'{SysFrameworkPathVar}="{os.environ[SysFrameworkPathVar]}"')
|
|
print(f'PYTHONPATH="{sys.path}"')
|
|
print(f'CWD="{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:
|
|
with open(filename, "w", encoding="utf8") as _:
|
|
pass
|
|
except OSError as exc:
|
|
error(str(exc))
|
|
|
|
|
|
def start_module(mod):
|
|
try:
|
|
r = mod.start()
|
|
if r is None:
|
|
# Not supported
|
|
return 1
|
|
# Create runfile
|
|
touch(env.runFile(mod.name))
|
|
return r
|
|
except Exception as e:
|
|
error(f"Failed to start {mod.name}: {str(e)}")
|
|
return 1
|
|
|
|
|
|
def stop_module(mod):
|
|
returncode = 0
|
|
try:
|
|
r = mod.stop()
|
|
if r is None:
|
|
# Not supported
|
|
return 1
|
|
elif type(r) == type(True):
|
|
# Boolean type: True = success, False = error
|
|
if not r:
|
|
returncode = 1
|
|
elif type(r) == type(0):
|
|
# Integer type: 0 = success, error otherwise
|
|
if r:
|
|
returncode = 1
|
|
else:
|
|
# Any other type: Truthy = success, Falsy = error
|
|
if not r:
|
|
returncode = 1
|
|
except Exception as e:
|
|
error(f"Failed to stop {mod.name}: {str(e)}")
|
|
return 1
|
|
|
|
# Delete runfile and pidfile
|
|
try:
|
|
runFile = env.runFile(mod.name)
|
|
if os.path.isfile(runFile):
|
|
os.remove(runFile)
|
|
pidFile = f"{runFile.split('.')[0]}.pid"
|
|
if os.path.isfile(pidFile):
|
|
os.remove(pidFile)
|
|
except BaseException:
|
|
returncode = 1
|
|
|
|
return returncode
|
|
|
|
|
|
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", encoding="utf8") 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=C0415,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(args)
|
|
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(f"* setup {mod.name}")
|
|
if mod.setup(cfg) != 0:
|
|
error(f"module '{mod.name}' failed to setup")
|
|
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(f"failed to create directory: {runpath}")
|
|
|
|
statfile = os.path.join(runpath, "seiscomp.init")
|
|
if not os.path.exists(statfile):
|
|
try:
|
|
with open(statfile, "w", encoding="utf8") as _:
|
|
pass
|
|
except BaseException:
|
|
error(f"failed to create status file: {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 command-line 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(f"{name} is not available")
|
|
elif isinstance(modName, seiscomp.kernel.CoreModule):
|
|
error(f"{name} is a kernel module and is enabled automatically")
|
|
else:
|
|
try:
|
|
r = modName.enable()
|
|
if r is None:
|
|
# Not supported
|
|
return 1
|
|
except:
|
|
pass
|
|
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(f"{modName} is not available")
|
|
elif isinstance(modName, seiscomp.kernel.CoreModule):
|
|
error(f"{name} is a kernel module and cannot be disabled")
|
|
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(f"Summary: {cntStarted} modules started")
|
|
|
|
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(f"Summary: {cntStopped} modules stopped")
|
|
|
|
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(f"Summary: {cntStarted} started modules checked")
|
|
|
|
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 command-line.")
|
|
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(f"{mod.name} is {state}")
|
|
|
|
if not useCSV:
|
|
print(f"Summary: {found} modules reported")
|
|
|
|
return 0
|
|
|
|
if args[0] == "aliases":
|
|
try:
|
|
with open(ALIAS_FILE, "r", encoding="utf8") as f:
|
|
lines = [line.rstrip() for line in f.readlines()]
|
|
except OSError:
|
|
print(f"No aliases found: Cannot open file {ALIAS_FILE}", file=sys.stderr)
|
|
return 1
|
|
|
|
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(f"{toks[0]};{toks[1]}")
|
|
else:
|
|
print(f"{toks[0]} -> {toks[1]}")
|
|
|
|
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(f"Summary: {found} modules enabled")
|
|
|
|
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(f"Summary: {found} modules disabled")
|
|
|
|
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(f"Summary: {found} modules started")
|
|
|
|
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(f"Summary: {found} modules enabled")
|
|
|
|
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(f"Summary: {found} modules started")
|
|
|
|
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(f"Summary: {found} modules reported")
|
|
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|variables}")
|
|
return 1
|
|
|
|
if args[0] == "crontab":
|
|
path = os.path.join(env.SEISCOMP_ROOT, "bin", "seiscomp")
|
|
print(f"*/3 * * * * {path} check >/dev/null 2>&1")
|
|
for mod in mods:
|
|
mod.printCrontab()
|
|
|
|
elif args[0] == "env":
|
|
print(f'export SEISCOMP_ROOT="{SEISCOMP_ROOT}"')
|
|
print(f'export PATH="{BIN_PATH}:$PATH"')
|
|
print(f'export {SysLibraryPathVar}="{get_library_path()}:${SysLibraryPathVar}"')
|
|
if sys.platform == "darwin":
|
|
print(
|
|
f'export {SysFrameworkPathVar}="{get_framework_path()}:'
|
|
f'${SysFrameworkPathVar}"'
|
|
)
|
|
|
|
print(f'export PYTHONPATH="{PYTHONPATH}:$PYTHONPATH"')
|
|
print(f'export MANPATH="{MANPATH}:$MANPATH"')
|
|
print(f'source "{SEISCOMP_ROOT}/share/shell-completion/seiscomp.bash"')
|
|
hostenv = os.path.join(
|
|
SEISCOMP_ROOT, "etc", "env", "by-hostname", socket.gethostname()
|
|
)
|
|
if os.path.isfile(hostenv):
|
|
print(f"source {hostenv}")
|
|
|
|
elif args[0] == "variables":
|
|
try:
|
|
# pylint: disable=C0415
|
|
import seiscomp.system as scsys
|
|
except ImportError as e:
|
|
# unavailable without trunk installation, e.g., seedlink-only
|
|
error(
|
|
"wrong argument: variables is unavailable without trunk installation\n"
|
|
f" + {e}"
|
|
)
|
|
return 1
|
|
sysenv = scsys.Environment.Instance()
|
|
print(
|
|
f"""@HOMEDIR@ : {sysenv.homeDir()}
|
|
@ROOTDIR@ : {sysenv.installDir()}
|
|
@SYSTEMCONFIGDIR@ : {sysenv.appConfigDir()}
|
|
@DEFAULTCONFIGDIR@ : {sysenv.globalConfigDir()}
|
|
@KEYDIR@ : {sysenv.appConfigDir()}/key
|
|
@DATADIR@ : {sysenv.shareDir()}
|
|
@CONFIGDIR@ : {sysenv.configDir()}
|
|
@LOGDIR@ : {sysenv.logDir()}"""
|
|
)
|
|
|
|
else:
|
|
error("wrong argument: {crontab|env|variables} expected")
|
|
return 1
|
|
|
|
return 0
|
|
|
|
|
|
def on_print_help(_):
|
|
print("seiscomp print {crontab|env|variables}")
|
|
print(" crontab: suggests crontab entries of all registered or given modules.")
|
|
print(" env: prints environment variables necessary to run SeisComP modules.")
|
|
print(" variables: prints internal SeisComP variables.")
|
|
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(f"Error: {err}")
|
|
print("*********************************************************************")
|
|
|
|
return 1
|
|
|
|
print(f"Distribution: {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(f"no handler available for package '{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 mariadb-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(" [mariadb,mysql,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
|
|
|
|
availableMods = {mod.name: mod for mod in config_mods}
|
|
selectedMods = args if args else list(availableMods.keys())
|
|
configuredMods = set()
|
|
|
|
while selectedMods:
|
|
name = selectedMods.pop(0)
|
|
if name not in availableMods:
|
|
print(f"* module {name} not available")
|
|
continue
|
|
if name in configuredMods:
|
|
print(f"* module {name} already configured")
|
|
continue
|
|
|
|
mod = availableMods[name]
|
|
if not kernelModsStarted and mod.requiresKernelModules():
|
|
print("* starting kernel modules")
|
|
start_kernel_modules()
|
|
kernelModsStarted = True
|
|
|
|
print(f"* configure {name}")
|
|
|
|
proxy = mod.updateConfigProxy()
|
|
if proxy:
|
|
if not is_string(proxy):
|
|
error(f"module {name} returned invalid proxy of type {type(proxy)}")
|
|
return 1
|
|
if proxy not in availableMods:
|
|
error(
|
|
f"module {name} returned proxy {proxy} which is not available "
|
|
"for configuration"
|
|
)
|
|
return 1
|
|
if proxy not in configuredMods and proxy not in selectedMods:
|
|
selectedMods.append(proxy)
|
|
|
|
result = mod.updateConfig()
|
|
|
|
try:
|
|
error_code = int(result)
|
|
except ValueError:
|
|
error(f"unexpected return type when updating configuration of {name}")
|
|
return 1
|
|
|
|
if error_code:
|
|
error(f"updating configuration for {name} failed")
|
|
return 1
|
|
|
|
configuredMods.add(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(f"module '{args[2]}' not found")
|
|
return 1
|
|
|
|
supportsAliases = False
|
|
try:
|
|
supportsAliases = mod.supportsAliases()
|
|
except BaseException:
|
|
pass
|
|
|
|
if not supportsAliases:
|
|
error(f"module '{args[2]}' does not support aliases")
|
|
return 1
|
|
|
|
if len(aliasName) > 20:
|
|
error(
|
|
f"rejecting alias name '{aliasName}' exceeding 20 characters since "
|
|
"bindings will not be written to database"
|
|
)
|
|
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(f"no {mod2} 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(f"failed to create directory: {DESC_PATH}")
|
|
return 1
|
|
|
|
has_alias = False
|
|
lines = []
|
|
new_lines = []
|
|
try:
|
|
with open(ALIAS_FILE, "r", encoding="utf8") as f:
|
|
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)
|
|
except BaseException:
|
|
pass
|
|
|
|
print(f"Creating alias '{aliasName}' for '{mod2}':")
|
|
if has_alias:
|
|
warning(
|
|
f" + {aliasName} is already registered as alias for {mod2} in "
|
|
"$SEISCOMP_ROOT/etc/descriptions/aliases"
|
|
)
|
|
warning(" do not register again but trying to link the required files")
|
|
else:
|
|
print(
|
|
f" + registering alias '{aliasName}' in "
|
|
"$SEISCOMP_ROOT/etc/descriptions/aliases"
|
|
)
|
|
|
|
# Check if target exists already
|
|
if os.path.exists(os.path.join(SEISCOMP_ROOT, mod1)):
|
|
warning(
|
|
f" + link '{aliasName}' to '{mod2}' exists already in {SEISCOMP_ROOT}"
|
|
"/bin/"
|
|
)
|
|
warning(" do not link again")
|
|
|
|
try:
|
|
with open(ALIAS_FILE, "w", encoding="utf8") as f:
|
|
new_lines.append(f"{aliasName} = {args[2]}")
|
|
f.write("\n".join(new_lines) + "\n")
|
|
except BaseException:
|
|
error(f" + failed to open/create alias file: {ALIAS_FILE}")
|
|
return 1
|
|
|
|
# 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(
|
|
f" + linking default configuration: {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(f" + creating alias to application: {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(f" + linking init script: {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(f"Removing alias '{aliasName}':")
|
|
# check and remove alias line in etc/descriptions/aliases
|
|
has_alias = False
|
|
lines = []
|
|
new_lines = []
|
|
try:
|
|
with open(ALIAS_FILE, "r", encoding="utf8") as f:
|
|
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)
|
|
except BaseException:
|
|
pass
|
|
|
|
if not has_alias:
|
|
print(f" + {aliasName} is not defined as an alias")
|
|
if not interactiveMode:
|
|
print(" + remove related configuration with '--interactive'")
|
|
if len(lines) == len(new_lines):
|
|
return 1
|
|
|
|
try:
|
|
with open(ALIAS_FILE, "w", encoding="utf8") as f:
|
|
if len(lines) > 0:
|
|
f.write("\n".join(new_lines) + "\n")
|
|
except OSError:
|
|
error(f" + failed to open/create alias file: {ALIAS_FILE}")
|
|
return 1
|
|
|
|
if not has_alias and 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(f" + removing alias application: {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(f" + removing init script: {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(f" + removing default configuration: {SEISCOMP_ROOT}/{default_cfg}")
|
|
try:
|
|
os.remove(os.path.join(SEISCOMP_ROOT, default_cfg))
|
|
except BaseException as e:
|
|
error(f" + could not remove {e}")
|
|
|
|
if not interactiveMode:
|
|
warning(
|
|
f"No other configuration removed for '{aliasName}' - interactive"
|
|
" removal is supported by '--interactive'"
|
|
)
|
|
return 0
|
|
|
|
# test module configuration files
|
|
# SYSTEMCONFIGDIR
|
|
cfg = os.path.join("etc", aliasName + ".cfg")
|
|
if os.path.isfile(cfg):
|
|
print(f" + found module configuration file: {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(f" + could not remove '{e}' - try manually")
|
|
|
|
# CONFIGDIR
|
|
cfg = os.path.join(os.path.expanduser("~"), ".seiscomp", aliasName + ".cfg")
|
|
if os.path.isfile(cfg):
|
|
print(f" + found module configuration file: {cfg}")
|
|
answer = getInput(" + do you wish to remove it?", "n", "yn")
|
|
if answer == "y":
|
|
try:
|
|
os.remove(cfg)
|
|
except Exception as e:
|
|
error(f" + could not remove the file: {e} - try manually")
|
|
|
|
# test module binding files
|
|
bindingDir = os.path.join(SEISCOMP_ROOT, "etc", "key", aliasName)
|
|
if os.path.exists(bindingDir):
|
|
print(f" + found binding directory: {bindingDir}")
|
|
answer = getInput(" + do you wish to remove it?", "n", "yn")
|
|
if answer == "y":
|
|
try:
|
|
shutil.rmtree(bindingDir)
|
|
except Exception as e:
|
|
error(f" + could not remove the directory: {e} - try manually")
|
|
|
|
# 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", encoding="utf8") 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(f" + found binding for '{aliasName}' in: {keyFile}")
|
|
|
|
if keyFiles:
|
|
print(
|
|
f" + found {len(keyFiles)} bindings for '{aliasName}' in key files"
|
|
)
|
|
question = f" + remove all '{aliasName}' bindings from key files?"
|
|
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(f"Wrong command '{args[0]}': expected 'create' or 'remove'")
|
|
return 1
|
|
|
|
|
|
def on_alias_help(_):
|
|
print("seiscomp alias {create|remove} ALIAS_NAME APP_NAME")
|
|
print(
|
|
"Creates/removes aliases of applications as symlinks allowing to run multiple "
|
|
"application instances with different parameter sets. Aliases of aliases are "
|
|
"not allowed."
|
|
)
|
|
print()
|
|
print("Examples:")
|
|
print("$ seiscomp alias create l1autopick scautopick")
|
|
print("Creating alias 'l1autopick' for 'scautopick'':")
|
|
print(
|
|
" + registering alias 'l1autopick' in $SEISCOMP_ROOT/etc/descriptions/aliases"
|
|
)
|
|
print(" + linking default configuration: scautopick.cfg -> l1autopick.cfg")
|
|
print(" + creating alias to application: scautopick -> bin/l1autopick")
|
|
print(" + linking init script: scautopick.py -> l1autopick.py")
|
|
print()
|
|
print("$ seiscomp alias remove l1autopick")
|
|
print("Removing alias 'l1autopick':")
|
|
print(" + removing alias application: bin/l1autopick")
|
|
print(" + removing init script: etc/init/l1autopick.py")
|
|
print(" + removing default configuration: etc/defaults/l1autopick.cfg")
|
|
|
|
|
|
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(" -h, [--help] Produce this help message.")
|
|
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 configurations "
|
|
"interactively when removing aliases."
|
|
)
|
|
print(
|
|
" --invert Invert the selection of the specified module names "
|
|
"when using any of the commands: start, stop, check, status, reload, or "
|
|
"restart."
|
|
)
|
|
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(f" {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 all modules"
|
|
)
|
|
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(f"Sorry, no help available for {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(f"command '{runAction}' failed: {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
|
|
invert = False
|
|
|
|
argv = sys.argv[1:]
|
|
argflags = []
|
|
|
|
# Check for flags
|
|
while argv:
|
|
if argv[0] == "--help" or argv[0] == "-h":
|
|
on_help([], "")
|
|
sys.exit(1)
|
|
if argv[0] == "--csv":
|
|
useCSV = True
|
|
argv = argv[1:]
|
|
elif argv[0] == "--asroot":
|
|
asRoot = True
|
|
argv = argv[1:]
|
|
elif argv[0] == "--interactive" or argv[0] == "-i":
|
|
interactiveMode = True
|
|
argv = argv[1:]
|
|
elif argv[0] == "--invert":
|
|
invert = 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(f"Wait timeout is not an integer: {argv[0]}")
|
|
sys.exit(1)
|
|
if lockTimeout < 0:
|
|
print(f"Wait timeout must be positive: {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:]
|
|
inverted = []
|
|
|
|
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)
|
|
|
|
if invert and action in ["start", "stop", "check", "status", "reload", "restart"]:
|
|
inverted = arguments
|
|
arguments = []
|
|
else:
|
|
invert = False
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# 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
|
|
# directories 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(
|
|
f"Could not get lock {env.lockFile('seiscomp')} - is another process "
|
|
"using it?"
|
|
)
|
|
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
|
|
if invert and m.name in inverted:
|
|
continue
|
|
mods.append(m)
|
|
|
|
sys.exit(run_action(action, arguments, argflags))
|