Skip to content

Commit

Permalink
Be able to give lock to son processes detached by systemctl (#367)
Browse files Browse the repository at this point in the history
* Give lock to systemctl sons
* Get the 'need_lock' flag from services.yml
* Don't need the lock for enable/disable and other stuff
  • Loading branch information
alexAubin committed Oct 8, 2017
1 parent 5ae558e commit 9903c48
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 3 deletions.
1 change: 1 addition & 0 deletions data/templates/yunohost/services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ yunohost-api:
log: /var/log/yunohost/yunohost-api.log
yunohost-firewall:
status: service
need_lock: true
nslcd:
status: service
log: /var/log/syslog
Expand Down
57 changes: 54 additions & 3 deletions src/yunohost/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@

from yunohost.hook import hook_callback


BASE_CONF_PATH = '/home/yunohost.conf'
BACKUP_CONF_DIR = os.path.join(BASE_CONF_PATH, 'backup')
PENDING_CONF_DIR = os.path.join(BASE_CONF_PATH, 'pending')
MOULINETTE_LOCK = "/var/run/moulinette_yunohost.lock"

logger = log.getActionLogger('yunohost.service')

Expand Down Expand Up @@ -493,7 +493,8 @@ def _run_service_command(action, service):
service -- Service name
"""
if service not in _get_services().keys():
services = _get_services()
if service not in services.keys():
raise MoulinetteError(errno.EINVAL, m18n.n('service_unknown', service=service))

cmd = None
Expand All @@ -505,15 +506,65 @@ def _run_service_command(action, service):
else:
raise ValueError("Unknown action '%s'" % action)

need_lock = (services[service].get('need_lock') or False) \
and action in ['start', 'stop', 'restart', 'reload']

try:
ret = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
# Launch the command
logger.debug("Running '%s'" % cmd)
p = subprocess.Popen(cmd.split(), stderr=subprocess.STDOUT)
# If this command needs a lock (because the service uses yunohost
# commands inside), find the PID and add a lock for it
if need_lock:
PID = _give_lock(action, service, p)
# Wait for the command to complete
p.communicate()
# Remove the lock if one was given
if need_lock and PID != 0:
_remove_lock(PID)

except subprocess.CalledProcessError as e:
# TODO: Log output?
logger.warning(m18n.n('service_cmd_exec_failed', command=' '.join(e.cmd)))
return False
return True


def _give_lock(action, service, p):

# Depending of the action, systemctl calls the PID differently :/
if action == "start" or action == "restart":
systemctl_PID_name = "MainPID"
else:
systemctl_PID_name = "ControlPID"

cmd_get_son_PID ="systemctl show %s -p %s" % (service, systemctl_PID_name)
son_PID = 0
# As long as we did not found the PID and that the command is still running
while son_PID == 0 and p.poll() == None:
# Call systemctl to get the PID
# Output of the command is e.g. ControlPID=1234
son_PID = subprocess.check_output(cmd_get_son_PID.split()) \
.strip().split("=")[1]
son_PID = int(son_PID)
time.sleep(1)

# If we found a PID
if son_PID != 0:
# Append the PID to the lock file
logger.debug("Giving a lock to PID %s for service %s !"
% (str(son_PID), service))
filesystem.append_to_file(MOULINETTE_LOCK, "\n%s" % str(son_PID))

return son_PID

def _remove_lock(PID_to_remove):

PIDs = filesystem.read_file(MOULINETTE_LOCK).split("\n")
PIDs_to_keep = [ PID for PID in PIDs if int(PID) != PID_to_remove ]
filesystem.write_to_file(MOULINETTE_LOCK, '\n'.join(PIDs_to_keep))


def _get_services():
"""
Get a dict of managed services with their parameters
Expand Down

0 comments on commit 9903c48

Please sign in to comment.