diff options
| author | Jonathan Weth <git@jonathanweth.de> | 2021-06-23 12:33:28 +0200 |
|---|---|---|
| committer | Jonathan Weth <git@jonathanweth.de> | 2021-06-23 12:33:28 +0200 |
| commit | ed1d15aa02b3c7c1350a5204861d1f8678550fbb (patch) | |
| tree | 51d4aa7cdee4f90b5f2c3a4188cb80519dd2342d /service.py | |
| parent | 74e02d1953c1ee03a4e7dfc73e80318a24ba56a7 (diff) | |
| download | RWA.Support.SessionService-ed1d15aa02b3c7c1350a5204861d1f8678550fbb.tar.gz RWA.Support.SessionService-ed1d15aa02b3c7c1350a5204861d1f8678550fbb.tar.bz2 RWA.Support.SessionService-ed1d15aa02b3c7c1350a5204861d1f8678550fbb.zip | |
Restructure project directory and provide script for session service
Diffstat (limited to 'service.py')
| -rwxr-xr-x | service.py | 337 |
1 files changed, 0 insertions, 337 deletions
diff --git a/service.py b/service.py deleted file mode 100755 index ada9b15..0000000 --- a/service.py +++ /dev/null @@ -1,337 +0,0 @@ -#!/usr/bin/env python3 - -# This file is part of Remote Support Desktop -# https://gitlab.das-netzwerkteam.de/RemoteWebApp/rwa.support.sessionservice -# Copyright 2020, 2021 Jonathan Weth <dev@jonathanweth.de> -# Copyright 2020 Daniel Teichmann <daniel.teichmann@das-netzwerkteam.de> -# Copyright 2020 Mike Gabriel <mike.gabriel@das-netzwerkteam.de> -# SPDX-License-Identifier: GPL-2.0-or-later -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the -# Free Software Foundation, Inc., -# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <https://www.gnu.org/licenses/>. - -import argparse -import json -import logging -import os -import signal -import tempfile -import time -from threading import Thread -from typing import Union, Any - -import dbus -import dbus.mainloop.glib -import dbus.service -from gi.repository import GLib - -from lock import is_locked, lock, unlock -from session import Session -from trigger import TriggerServerThread - -ALLOW_ONLY_ONE_SESSION = True - - -class RWASupportSessionService(dbus.service.Object): - """D-Bus Session Service for RWA.Support. - - D-Bus namespace: ``org.ArcticaProject.RWASupportSessionService`` - - D-Bus object name: ``/RWASupportSessionService`` - - :param loop: GLib main loop running the service - :param mockup_mode: Starts the service in mock up mode - """ - - def __init__( - self, loop: GLib.MainLoop, mockup_mode: bool = False, one_time: bool = False - ): - self.loop = loop - self.mockup_mode = mockup_mode - self.one_time = one_time - - self.bus = dbus.SessionBus() - name = dbus.service.BusName("org.ArcticaProject.RWASupportSessionService", bus=self.bus) - - self.check_lock_thread = Thread(target=self._check_lock) - self.check_lock_thread.start() - - self.trigger_service = TriggerServerThread(self._trigger) - self.trigger_service.start() - - self.update_service_running = False - self.sessions = {} - super().__init__(name, "/RWASupportSessionService") - - logging.info("D-Bus service has been started.") - - @dbus.service.method("org.ArcticaProject.RWASupportSessionService", out_signature="s") - def start(self) -> str: - """Start a new remote session and register it in RWA.Support.WebApp. - - :return: Result as JSON (D-Bus string) - - **Structure of returned JSON (success):** - - :: - - {"status": "success", "id": <pid>, "url": "<url>", "pin": <pin>} - - **Structure of returned JSON (error):** - - :: - - {"status": "error", "type": "<type>"} - - **Possible choices for error types:** ``multiple``, ``connection`` - """ - if ALLOW_ONLY_ONE_SESSION and len(self.sessions.values()) > 0: - logging.warning( - "There is already one session running and the service is configured to allow only one " - "session, so this session won't be started." - ) - return json.dumps({"status": "error", "type": "multiple"}) - - # Start session - try: - session = Session(self.trigger_service.port, mockup_mode) - - # Add session to sessions list - self.sessions[session.pid] = session - - # Start session update service - self._ensure_update_service() - - return_json = session.client_meta - return_json["status"] = "success" - - logging.info( - f"New session #{session.pid} was started with meta {return_json}." - ) - - return json.dumps(return_json) - except ConnectionError: - pass - - return json.dumps({"status": "error", "type": "connection"}) - - @dbus.service.method("org.ArcticaProject.RWASupportSessionService", in_signature="i", out_signature="s") - def status(self, pid: int) -> str: - """Return the status of a session. - - .. note:: - - This uses the last status version got by the update service in the background. - - :param pid: (Process) ID of session (D-Bus integer) - :return: Session status as JSON (D-Bus string) - - **Structure of returned JSON:** - - :: - - {"id": <pid>, "status": <status>} - - **Possible status options:** - - ============ ====================== - ``running`` The session is running and ready for connecting. - ``active`` The session is running and a the remote connected to the session. - ``stopped`` The session was stopped. - ``dead`` There was a problem, so that the session is dead. - ============ ====================== - """ - return self._get_status(pid) - - @dbus.service.method("org.ArcticaProject.RWASupportSessionService", in_signature="i", out_signature="s") - def refresh_status(self, pid: int) -> str: - """Same as :meth:`status`, but updates status from RWA.WebApp before returning it here. - """ - self._update_session(pid) - return self._get_status(pid) - - @dbus.service.method("org.ArcticaProject.RWASupportSessionService", in_signature="i", out_signature="s") - def stop(self, pid: int) -> str: - """Stop a remote session. - - :param pid: (Process) ID of session (D-Bus integer) - :return: Session status as JSON (D-Bus string) - - **Structure of returned JSON:** - - :: - - {"id": <pid>, "status": "stopped"} - """ - try: - session = self.sessions[pid] - except KeyError: - return json.dumps({"pid": pid, "status": "stopped"}, sort_keys=True) - session.stop() - return json.dumps({"id": pid, "status": "stopped"}, sort_keys=True) - - def _get_status(self, pid: int) -> str: - try: - session = self.sessions[pid] - except KeyError: - return json.dumps({"id": pid, "status": "dead"}, sort_keys=True) - return json.dumps(session.status) - - def _ensure_update_service(self): - """Start session update thread if it isn't already running.""" - if not self.update_service_running: - self.update_thread = Thread(target=self._update_sessions) - self.update_thread.start() - - def _update_session(self, pid: int): - """Update the status of a session.""" - - try: - session = self.sessions[pid] - except KeyError: - logging.info(f"Update status for session #{pid} …") - logging.warning(" Session is dead.") - return - - # Check if VNC process is still running - running = session.vnc_process_running - if running: - pass - elif session.status_text == "stopped" and session.pid in self.sessions: - logging.info(f"Update status for session #{pid} …") - logging.warning(" Session is dead.") - - del self.sessions[session.pid] - else: - logging.info(f"Update status for session #{pid} …") - logging.warning(" VNC was stopped, so session is dead.") - - session.stop() - del self.sessions[session.pid] - - def _update_sessions(self): - """Go through all running sessions and update their status using ``_update_session``.""" - logging.info("Started update service for sessions.") - while len(self.sessions.values()) > 0: - for session in list(self.sessions.values()): - self._update_session(session.pid) - - time.sleep(2) - - self.update_service_running = False - logging.info("Stopped update service for sessions.") - if self.one_time: - self._stop_all() - - def _trigger(self, session_id: int, data: dict, method: str = "trigger") -> Union[dict, bool]: - """Trigger a specific session via trigger token.""" - logging.info(f"Triggered with session ID {session_id} and {data}") - - for session in self.sessions.values(): - if session.session_id == session_id: - r = session.trigger(data, method) - logging.info(f"Session #{session.pid} matches the ID: {r}") - return r - - logging.warning(" No matching session found for this ID.") - return False - - def _stop_all(self): - """Stop all sessions and this daemon.""" - logging.info("Stop all sessions and exit service.") - for session in list(self.sessions.values()): - session.stop() - del self.sessions[session.pid] - self.trigger_service.shutdown() - self.loop.quit() - - def _check_lock(self): - """Check if lock file exists.""" - while True: - if not is_locked(): - logging.error("The lock file was removed, so stop this service.") - self._stop_all() - break - time.sleep(1) - - -def str2bool(v: Union[str, bool, int]) -> bool: - """Return true or false if the given string can be interpreted as a boolean otherwise raise an exception.""" - if isinstance(v, bool): - return v - if v.lower() in ("yes", "true", "t", "y", "1", 1): - return True - elif v.lower() in ("no", "false", "f", "n", "0", 0): - return False - else: - raise argparse.ArgumentTypeError("Boolean value expected.") - - -if __name__ == "__main__": - # Check for lock file - if is_locked(): - logging.error("The service is already running.") - exit(1) - - # Create lock file - lock() - - parser = argparse.ArgumentParser(description="D-Bus Session Service for RWA.Support") - parser.add_argument( - "-m", - "--mockup-mode", - type=str2bool, - nargs="?", - const=True, - default=False, - help="Activates mock up mode. Acts like the real session service but don't do changes or call RWA.", - ) - parser.add_argument( - "-o", - "--one-time", - type=str2bool, - nargs="?", - const=True, - default=False, - help="Runs as one-time-service. Stops after one session.", - ) - - args = parser.parse_args() - mockup_mode = args.mockup_mode - one_time = args.one_time - - if mockup_mode: - logging.warning( - "All API responses are faked and should NOT BE USED IN PRODUCTION!" - ) - - dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) - - loop = GLib.MainLoop() - object = RWASupportSessionService(loop, mockup_mode, one_time) - - def signal_handler(sig, frame): - logging.info("Service was terminated.") - object._stop_all() - - signal.signal(signal.SIGINT, signal_handler) - - loop.run() - - logging.info("Remove lock file ...") - unlock() |
