1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
|
"""Main entry point for running mock server."""
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free
# Software Foundation; either version 3 of the License, or (at your option) any
# later version. See http://www.gnu.org/copyleft/lgpl.html for the full text
# of the license.
__author__ = "Martin Pitt"
__copyright__ = """
(c) 2012 Canonical Ltd.
(c) 2017 - 2022 Martin Pitt <martin@piware.de>
"""
import argparse
import json
import os
import platform
import subprocess
import sys
import dbusmock.mockobject
import dbusmock.testcase
def parse_args():
"""Parse command line arguments"""
parser = argparse.ArgumentParser(description="mock D-Bus object")
parser.add_argument(
"-s",
"--system",
action="store_true",
help="put object(s) on system bus (default: session bus or template's SYSTEM_BUS flag)",
)
parser.add_argument(
"--session",
action="store_true",
help="put object(s) on session bus (default without template; overrides template's SYSTEM_BUS flag)",
)
parser.add_argument(
"-l",
"--logfile",
metavar="PATH",
help="path of log file",
)
parser.add_argument(
"-t",
"--template",
metavar="NAME",
help="template to load (instead of specifying name, path, interface)",
)
parser.add_argument(
"name",
metavar="NAME",
nargs="?",
help='D-Bus name to claim (e. g. "com.example.MyService") (if not using -t)',
)
parser.add_argument(
"path",
metavar="PATH",
nargs="?",
help="D-Bus object path for initial/main object (if not using -t)",
)
parser.add_argument(
"interface",
metavar="INTERFACE",
nargs="?",
help="main D-Bus interface name for initial object (if not using -t)",
)
parser.add_argument(
"-m",
"--is-object-manager",
action="store_true",
help="automatically implement the org.freedesktop.DBus.ObjectManager interface",
)
parser.add_argument(
"-p",
"--parameters",
help="JSON dictionary of parameters to pass to the template",
)
parser.add_argument(
"-e",
"--exec",
nargs=argparse.REMAINDER,
help="Command to run in the mock environment",
)
arguments = parser.parse_args()
if arguments.template:
if arguments.name or arguments.path or arguments.interface:
parser.error("--template and specifying NAME/PATH/INTERFACE are mutually exclusive")
else:
if not arguments.name or not arguments.path or not arguments.interface:
parser.error("Not using a template, you must specify NAME, PATH, and INTERFACE")
if arguments.system and arguments.session:
parser.error("--system and --session are mutually exclusive")
return arguments
if __name__ == "__main__":
import ctypes
import dbus.mainloop.glib
import dbus.service
args = parse_args()
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
system_bus = args.system
if args.template:
module = dbusmock.mockobject.load_module(args.template)
args.name = module.BUS_NAME
args.path = module.MAIN_OBJ
if not args.session and not args.system:
system_bus = module.SYSTEM_BUS
if hasattr(module, "IS_OBJECT_MANAGER"):
args.is_object_manager = module.IS_OBJECT_MANAGER
else:
args.is_object_manager = False
if args.is_object_manager and not hasattr(module, "MAIN_IFACE"):
args.interface = dbusmock.mockobject.OBJECT_MANAGER_IFACE
else:
args.interface = module.MAIN_IFACE
bus = dbusmock.testcase.DBusTestCase.get_dbus(system_bus)
# quit mock when the bus is going down
should_run = {True}
bus.add_signal_receiver(
should_run.pop,
signal_name="Disconnected",
path="/org/freedesktop/DBus/Local",
dbus_interface="org.freedesktop.DBus.Local",
)
bus_name = dbus.service.BusName(args.name, bus, allow_replacement=True, replace_existing=True, do_not_queue=True)
main_object = dbusmock.mockobject.DBusMockObject(
bus_name, args.path, args.interface, {}, args.logfile, args.is_object_manager
)
parameters = None
if args.parameters:
try:
parameters = json.loads(args.parameters)
except ValueError as detail:
sys.stderr.write(f"Malformed JSON given for parameters: {detail}\n")
sys.exit(2)
if not isinstance(parameters, dict):
sys.stderr.write("JSON parameters must be a dictionary\n")
sys.exit(2)
if args.template:
main_object.AddTemplate(args.template, parameters)
if platform.system() == "Darwin":
libglib = ctypes.cdll.LoadLibrary("libglib-2.0.dylib")
else:
libglib = ctypes.cdll.LoadLibrary("libglib-2.0.so.0")
dbusmock.mockobject.objects[args.path] = main_object
if args.exec:
with subprocess.Popen(args.exec) as exec_proc:
exit_status = set()
@ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_int)
def on_process_watch(_pid, status):
"""Check if the launched process is still alive"""
if os.WIFEXITED(status):
exit_status.add(os.WEXITSTATUS(status))
else:
exit_status.add(1)
should_run.pop()
libglib.g_child_watch_add(exec_proc.pid, on_process_watch)
while should_run:
libglib.g_main_context_iteration(None, True)
try:
exec_proc.terminate()
exec_proc.wait()
except ProcessLookupError:
pass
sys.exit(exit_status.pop() if exit_status else exec_proc.returncode)
else:
while should_run:
libglib.g_main_context_iteration(None, True)
|