aboutsummaryrefslogtreecommitdiff
path: root/service.py
diff options
context:
space:
mode:
authorJonathan Weth <git@jonathanweth.de>2021-06-23 12:33:28 +0200
committerJonathan Weth <git@jonathanweth.de>2021-06-23 12:33:28 +0200
commited1d15aa02b3c7c1350a5204861d1f8678550fbb (patch)
tree51d4aa7cdee4f90b5f2c3a4188cb80519dd2342d /service.py
parent74e02d1953c1ee03a4e7dfc73e80318a24ba56a7 (diff)
downloadRWA.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-xservice.py337
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()