From 693c4f4c5c096e067b7d5621f595e26f2b794824 Mon Sep 17 00:00:00 2001 From: Alejandro Sirgo Rica Date: Wed, 13 Nov 2024 19:41:20 +0100 Subject: src: add user session detection implementation Detect user login and logout for Linux and Windows. Poll the session change in 5 second intervals in a thread. Use the same event socket previously used by the old session detection mechanism to notify a session change. Report an active interactive session through the /refresh response so a new ogserver instance can update the session status. --- src/linux/ogOperations.py | 30 +++++++++++++++++++++++++++++- src/live/ogOperations.py | 3 +++ src/ogClient.py | 24 +++++++++++++++++++++++- src/ogRest.py | 3 +++ src/virtual/ogOperations.py | 3 +++ src/windows/ogOperations.py | 31 ++++++++++++++++++++++++++++++- 6 files changed, 91 insertions(+), 3 deletions(-) diff --git a/src/linux/ogOperations.py b/src/linux/ogOperations.py index 4728201..10efaa4 100644 --- a/src/linux/ogOperations.py +++ b/src/linux/ogOperations.py @@ -7,6 +7,7 @@ # (at your option) any later version. import os +import psutil import subprocess from subprocess import CalledProcessError from src.log import OgError @@ -15,6 +16,9 @@ from src.ogRest import ThreadState class OgLinuxOperations: + def __init__(self): + self.session = False + def _restartBrowser(self, url): raise OgError('Function not implemented') @@ -66,4 +70,28 @@ class OgLinuxOperations: raise OgError('Function not implemented') def refresh(self, ogRest): - return {"status": "LINUX"} + if self.session: + session_value = 'LINUXS' + else: + session_value = 'LINUX' + return {"status": session_value} + + def check_interactive_session_change(self): + old_status = self.session + has_logged_user = False + for user in psutil.users(): + if user.terminal: + try: + process = psutil.Process(user.pid) + env = process.environ() + if "DISPLAY" in env or "WAYLAND_DISPLAY" in env: + has_logged_user = True + break + + except (psutil.NoSuchProcess, psutil.AccessDenied): + continue + self.session = has_logged_user + + if self.session != old_status: + return self.session + return None diff --git a/src/live/ogOperations.py b/src/live/ogOperations.py index 2666606..ef18e33 100644 --- a/src/live/ogOperations.py +++ b/src/live/ogOperations.py @@ -837,3 +837,6 @@ class OgLiveOperations: logging.info('Sending response to refresh request') return json_body + + def check_interactive_session_change(self): + return False diff --git a/src/ogClient.py b/src/ogClient.py index 51aa33b..2635a3f 100644 --- a/src/ogClient.py +++ b/src/ogClient.py @@ -6,6 +6,7 @@ # Free Software Foundation; either version 3 of the License, or # (at your option) any later version. +import threading import errno import select import socket @@ -26,6 +27,8 @@ class State(Enum): class ogClient: OG_PATH = '/opt/opengnsys/' + SESSION_POLL_INTERVAL = 5 + EVENT_SOCKET_PORT = 55885 def __init__(self, config): self.CONFIG = config @@ -36,7 +39,7 @@ class ogClient: if self.mode in {'linux', 'windows'}: self.event_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.event_sock.setblocking(0) - self.event_sock.bind(('127.0.0.1', 55885)) + self.event_sock.bind(('127.0.0.1', ogClient.EVENT_SOCKET_PORT)) else: self.event_sock = None @@ -49,6 +52,25 @@ class ogClient: self.ogrest = ogRest(self.CONFIG) self.seq = None + self.session_check_thread = threading.Thread(target=self._session_check_loop, daemon=True) + self.session_check_thread.start() + + def _session_check_loop(self): + while True: + session_status = self.ogrest.check_interactive_session_change() + if session_status is True: + message = "session start user" + elif session_status is False: + message = "session stop user" + else: + message = None + + if message: + self.event_sock.sendto(message.encode('utf-8'), + ('127.0.0.1', ogClient.EVENT_SOCKET_PORT)) + + time.sleep(ogClient.SESSION_POLL_INTERVAL) + def get_socket(self): return self.sock diff --git a/src/ogRest.py b/src/ogRest.py index 7511c76..5731350 100644 --- a/src/ogRest.py +++ b/src/ogRest.py @@ -443,3 +443,6 @@ class ogRest(): def process_refresh(self, client): threading.Thread(target=ogThread.refresh, args=(client, self,)).start() + + def check_interactive_session_change(self): + return self.operations.check_interactive_session_change() diff --git a/src/virtual/ogOperations.py b/src/virtual/ogOperations.py index c51f59d..86ae0ed 100644 --- a/src/virtual/ogOperations.py +++ b/src/virtual/ogOperations.py @@ -629,3 +629,6 @@ class OgVirtualOperations: for k, v in device_names.items(): f.write(f'{k}={v}\n') f.truncate() + + def check_interactive_session_change(self): + return None diff --git a/src/windows/ogOperations.py b/src/windows/ogOperations.py index bc7e954..ef4e308 100644 --- a/src/windows/ogOperations.py +++ b/src/windows/ogOperations.py @@ -8,11 +8,13 @@ import os import ctypes +import pythoncom import subprocess from subprocess import CalledProcessError import multiprocessing as mp from multiprocessing import Process, freeze_support from src.log import OgError +import wmi from PIL import Image, ImageDraw from pystray import Icon, Menu, MenuItem @@ -64,6 +66,7 @@ def create_systray(): class OgWindowsOperations: def __init__(self): + self.session = False freeze_support() mp.set_start_method('spawn') self.systray_p = Process(target=create_systray, daemon=True) @@ -125,4 +128,30 @@ class OgWindowsOperations: raise OgError('Function not implemented') def refresh(self, ogRest): - return {"status": "WIN"} + if self.session: + session_value = 'WINS' + else: + session_value = 'WIN' + return {"status": session_value} + + def check_interactive_session_change(self): + old_status = self.session + pythoncom.CoInitialize() + has_logged_user = False + try: + c = wmi.WMI() + sessions = c.Win32_LogonSession(LogonType=2) + + for session in sessions: + if has_logged_user: + break + for user in session.associators("Win32_LoggedOnUser"): + has_logged_user = True + break + finally: + pythoncom.CoUninitialize() + self.session = has_logged_user + + if self.session != old_status: + return self.session + return None -- cgit v1.2.3-18-g5258