@@ -8,7 +8,7 @@ Standards-Version: 4.0.0
Homepage: https://xfs.wiki.kernel.org/
Package: xfsprogs
-Depends: ${shlibs:Depends}, ${misc:Depends}, python3:any
+Depends: ${shlibs:Depends}, ${misc:Depends}, python3-dbus, python3:any
Provides: fsck-backend
Suggests: xfsdump, acl, attr, quota
Breaks: xfsdump (<< 3.0.0)
@@ -15,6 +15,7 @@ import sys
import os
import argparse
import signal
+import dbus
from io import TextIOWrapper
from pathlib import Path
from datetime import timedelta
@@ -168,25 +169,57 @@ class scrub_service(scrub_control):
'''Control object for xfs_scrub systemd service.'''
def __init__(self, mnt, scrub_media):
self.unitname = path_to_serviceunit(mnt, scrub_media)
+ self.prop = None
+ self.unit = None
+ self.bind()
+
+ def bind(self):
+ '''Bind to the dbus proxy object for this service.'''
+ sysbus = dbus.SystemBus()
+ systemd1 = sysbus.get_object('org.freedesktop.systemd1',
+ '/org/freedesktop/systemd1')
+ manager = dbus.Interface(systemd1,
+ 'org.freedesktop.systemd1.Manager')
+ path = manager.LoadUnit(self.unitname)
+
+ svc_obj = sysbus.get_object('org.freedesktop.systemd1', path)
+ self.prop = dbus.Interface(svc_obj,
+ 'org.freedesktop.DBus.Properties')
+ self.unit = dbus.Interface(svc_obj,
+ 'org.freedesktop.systemd1.Unit')
+
+ def state(self):
+ '''Retrieve the active state for a systemd service. As of
+ systemd 249, this is supposed to be one of the following:
+ "active", "reloading", "inactive", "failed", "activating",
+ or "deactivating". These strings are not localized.'''
+ global debug
+
+ try:
+ return self.prop.Get('org.freedesktop.systemd1.Unit', 'ActiveState')
+ except Exception as e:
+ if debug:
+ print(e, file = sys.stderr)
+ return 'failed'
def wait(self, interval = 1):
'''Wait until the service finishes.'''
+ global debug
- # As of systemd 249, the is-active command returns any of the
- # following states: active, reloading, inactive, failed,
- # activating, deactivating, or maintenance. Apparently these
- # strings are not localized.
- while True:
- try:
- for l in backtick(['systemctl', 'is-active', self.unitname]):
- if l == 'failed':
- return 1
- if l == 'inactive':
- return 0
- except:
- return -1
-
+ # Use a poll/sleep loop to wait for the service to finish.
+ # Avoid adding a dependency on python3 glib, which is required
+ # to use an event loop to receive a dbus signal.
+ s = self.state()
+ while s not in ['failed', 'inactive']:
+ if debug:
+ print('waiting %s %s' % (self.unitname, s))
time.sleep(interval)
+ s = self.state()
+ if debug:
+ print('waited %s %s' % (self.unitname, s))
+ if s == 'failed':
+ return 1
+ return 0
def start(self):
'''Start the service and wait for it to complete. Returns -1
@@ -194,34 +227,29 @@ class scrub_service(scrub_control):
failed.'''
global debug
- cmd = ['systemctl', 'start', self.unitname]
+ if debug:
+ print('starting %s' % self.unitname)
+
try:
- if debug:
- print(' '.join(cmd))
- proc = subprocess.Popen(cmd, stdout = DEVNULL())
- proc.wait()
- ret = proc.returncode
- except:
+ self.unit.Start('replace')
+ return self.wait()
+ except Exception as e:
+ print(e, file = sys.stderr)
return -1
- if ret != 1:
- return ret
-
- # If systemctl-start returns 1, it's possible that the service
- # failed or that dbus/systemd restarted and the client program
- # lost its connection -- according to the systemctl man page, 1
- # means "unit not failed".
- return self.wait()
-
def stop(self):
'''Stop the service.'''
global debug
- cmd = ['systemctl', 'stop', self.unitname]
if debug:
- print(' '.join(cmd))
- x = subprocess.Popen(cmd)
- x.wait()
+ print('stopping %s' % self.unitname)
+
+ try:
+ self.unit.Stop('replace')
+ return self.wait()
+ except Exception as e:
+ print(e, file = sys.stderr)
+ return -1
def run_service(mnt, scrub_media, killfuncs):
'''Run scrub as a service.'''