2 # libvirt 'run' programs locally script
3 # Copyright (C) 2012-2021 Red Hat, Inc.
5 # This library is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU Lesser General Public
7 # License as published by the Free Software Foundation; either
8 # version 2.1 of the License, or (at your option) any later version.
10 # This library is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 # Lesser General Public License for more details.
15 # You should have received a copy of the GNU Lesser General Public
16 # License along with this library; If not, see
17 # <http://www.gnu.org/licenses/>.
19 # ----------------------------------------------------------------------
21 # With this script you can run libvirt programs without needing to
22 # install them first. You just have to do for example:
24 # ./run virsh [args ...]
26 # Note that this runs the locally compiled copy of virsh which
27 # is usually want you want.
29 # You can also run the C programs under valgrind like this:
31 # ./run valgrind [valgrind opts...] ./program
35 # ./run gdb --args ./program
37 # This also works with sudo (eg. if you need root access for libvirt):
39 # sudo ./run virsh list --all
41 # ----------------------------------------------------------------------
53 # Function to intelligently prepend a path to an environment variable.
54 # See https://stackoverflow.com/a/9631350
55 def prepend(env, varname, extradir):
56 if varname in os.environ:
57 env[varname] = extradir + ":" + env[varname]
59 env[varname] = extradir
62 here = "@abs_builddir@"
64 parser = argparse.ArgumentParser(add_help=False, allow_abbrev=False)
65 parser.add_argument('--selinux',
67 help='Run in the appropriate selinux context')
69 opts, args = parser.parse_known_args()
72 print("syntax: %s [--selinux] BINARY [ARGS...]" % sys.argv[0], file=sys.stderr)
78 prepend(env, "LD_LIBRARY_PATH", os.path.join(here, "src"))
79 prepend(env, "PKG_CONFIG_PATH", os.path.join(here, "src"))
80 prepend(env, "PATH", os.path.join(here, "tools"))
81 prepend(env, "PATH", os.path.join(here, "src"))
83 # Ensure that any 3rd party apps using libvirt.so from the build tree get
84 # files resolved to the build/source tree too. Typically useful for language
85 # bindings running tests against non-installed libvirt.
86 env["LIBVIRT_DIR_OVERRIDE"] = "1"
88 # This is a cheap way to find some use-after-free and uninitialized
89 # read problems when using glibc.
90 env["MALLOC_PERTURB_"] = "%d" % random.randint(1, 255)
92 env["abs_builddir"] = "@abs_builddir@"
93 env["abs_top_builddir"] = "@abs_top_builddir@"
111 def is_modular_daemon(name):
112 return name in modular_daemons
115 def is_monolithic_daemon(name):
116 return name == "libvirtd"
119 def is_systemd_host():
122 return os.path.exists("/run/systemd/system")
125 def daemon_units(name):
126 return [name + suffix for suffix in [
127 ".service", ".socket", "-ro.socket", "-admin.socket"]]
130 def is_unit_active(name):
131 ret = subprocess.call(["systemctl", "is-active", "-q", name])
135 def change_unit(name, action):
136 ret = subprocess.call(["systemctl", action, "-q", name])
140 def chcon(path, user, role, type):
141 print("Setting file context of {} to u={}, r={}, t={}...".format(progpath,
145 ret = subprocess.call(["chcon", "-u", user, "-r", role, "-t", type, path])
149 def restorecon(path):
150 print("Restoring selinux context for {}...".format(path))
151 ret = subprocess.call(["restorecon", path])
156 if is_systemd_host():
157 maybe_stopped_units = []
159 name = os.path.basename(arg)
160 if is_modular_daemon(name):
161 # Only need to stop libvirtd or this specific modular unit
162 maybe_stopped_units += daemon_units("libvirtd")
163 maybe_stopped_units += daemon_units(name)
164 elif is_monolithic_daemon(name):
165 # Need to stop libvirtd and/or all modular units
166 maybe_stopped_units += daemon_units("libvirtd")
167 for entry in modular_daemons:
168 maybe_stopped_units += daemon_units(entry)
170 for unit in maybe_stopped_units:
171 if is_unit_active(unit):
172 try_stop_units.append(unit)
174 if len(try_stop_units) == 0 and not opts.selinux:
175 # Run the program directly, replacing ourselves
176 os.execvpe(prog, args, env)
180 def sighandler(signum, frame):
181 raise OSError("Signal %d received, terminating" % signum)
183 signal.signal(signal.SIGHUP, sighandler)
184 signal.signal(signal.SIGTERM, sighandler)
185 signal.signal(signal.SIGQUIT, sighandler)
189 progpath = shutil.which(prog)
190 if len(try_stop_units):
191 print("Temporarily stopping systemd units...")
193 for unit in try_stop_units:
194 print(" > %s" % unit)
195 if not change_unit(unit, "stop"):
196 raise Exception("Unable to stop '%s'" % unit)
198 stopped_units.append(unit)
201 # if using a wrapper command like 'gdb', setting the selinux
202 # context won't work because the wrapper command will not be a
203 # valid entrypoint for the virtd_t context
204 if os.path.basename(prog) not in ["libvirtd", *modular_daemons]:
205 raise Exception("'{}' is not recognized as a valid daemon. "
206 "Selinux process context can only be set when "
207 "executing a daemon directly without wrapper "
208 "commands".format(prog))
211 raise Exception("Can't find executable {} for selinux labeling"
214 if not progpath.startswith(os.path.abspath(here)):
215 raise Exception("Refusing to change selinux context of file "
216 "'{}' outside build directory"
219 # selinux won't allow us to transition to the virtd_t context from
220 # e.g. the user_home_t context (the likely label of the local
222 if not chcon(progpath, "system_u", "object_r", "virtd_exec_t"):
223 raise Exception("Failed to change selinux context of binary")
229 '-t', 'virtd_t', *args]
231 print("Running '%s'..." % str(" ".join(args)))
232 ret = subprocess.call(args, env=env)
233 except KeyboardInterrupt:
235 except Exception as e:
236 print("%s" % e, file=sys.stderr)
238 if len(stopped_units):
239 print("Re-starting original systemd units...")
240 stopped_units.reverse()
241 for unit in stopped_units:
242 print(" > %s" % unit)
243 if not change_unit(unit, "start"):
244 print(" ! unable to restart %s" % unit, file=sys.stderr)