summaryrefslogtreecommitdiff
path: root/backend/localsocket.py
diff options
context:
space:
mode:
authorsuchmememanyskill <38142618+suchmememanyskill@users.noreply.github.com>2023-03-22 01:37:23 +0100
committerGitHub <noreply@github.com>2023-03-21 17:37:23 -0700
commitfd325ef1cc1d3e78b5e7686819e05606cc79d963 (patch)
tree1372e0efa0ca47b0045b4d29c40bb3a8caeadfc1 /backend/localsocket.py
parentfaf46ba53354b6dcfbfae25e605bf567acd19376 (diff)
downloaddecky-loader-fd325ef1cc1d3e78b5e7686819e05606cc79d963.tar.gz
decky-loader-fd325ef1cc1d3e78b5e7686819e05606cc79d963.zip
Add cross-platform support to decky (#387)
* Import generic watchdog observer over platform specific import * Use os.path rather than genericpath * Split off socket management in plugin.py * Don't specify multiprocessing start type Default on linux is already fork * Move all platform-specific functions to seperate files TODO: make plugin.py platform agnostic * fix import * add backwards compat to helpers.py * add backwards compatibility to helpers.py harder * Testing autobuild for win * Testing autobuild for win, try 2 * Testing autobuild for win, try 3 * Testing autobuild for win, try 4 * Create the plugins folder before attempting to use it * Implement win get_username() * Create win install script * Fix branch guess from version * Create .loader.version in install script * Add .cmd shim to facilitate auto-restarts * Properly fix branch guess from version * Fix updater on windows * Try 2 of fixing updates for windows * Test * pain * Update install script * Powershell doesn't believe in utf8 * Powershell good * add ON_LINUX variable to localplatform * Fix more merge issues * test * Move custom imports to main.py * Move custom imports to after __main__ check Due to windows' default behaviour being spawn, it will spawn a new process and thus import into sys.path multiple times * Log errors in get_system_pythonpaths() and get_loader_version() + split get_system_pythonpaths() on newline * Remove whitespace in result of get_system_pythonpaths() * use python3 on linux and python on windows in get_system_pythonpaths() * Remove fork-specific urls * Fix MIME types not working on Windows
Diffstat (limited to 'backend/localsocket.py')
-rw-r--r--backend/localsocket.py132
1 files changed, 132 insertions, 0 deletions
diff --git a/backend/localsocket.py b/backend/localsocket.py
new file mode 100644
index 00000000..ef0e3933
--- /dev/null
+++ b/backend/localsocket.py
@@ -0,0 +1,132 @@
+import asyncio, time, random
+from localplatform import ON_WINDOWS
+
+BUFFER_LIMIT = 2 ** 20 # 1 MiB
+
+class UnixSocket:
+ def __init__(self, on_new_message):
+ '''
+ on_new_message takes 1 string argument.
+ It's return value gets used, if not None, to write data to the socket.
+ Method should be async
+ '''
+ self.socket_addr = f"/tmp/plugin_socket_{time.time()}"
+ self.on_new_message = on_new_message
+ self.socket = None
+ self.reader = None
+ self.writer = None
+
+ async def setup_server(self):
+ self.socket = await asyncio.start_unix_server(self._listen_for_method_call, path=self.socket_addr, limit=BUFFER_LIMIT)
+
+ async def _open_socket_if_not_exists(self):
+ if not self.reader:
+ retries = 0
+ while retries < 10:
+ try:
+ self.reader, self.writer = await asyncio.open_unix_connection(self.socket_addr, limit=BUFFER_LIMIT)
+ return True
+ except:
+ await asyncio.sleep(2)
+ retries += 1
+ return False
+ else:
+ return True
+
+ async def get_socket_connection(self):
+ if not await self._open_socket_if_not_exists():
+ return None, None
+
+ return self.reader, self.writer
+
+ async def close_socket_connection(self):
+ if self.writer != None:
+ self.writer.close()
+
+ self.reader = None
+
+ async def read_single_line(self) -> str|None:
+ reader, writer = await self.get_socket_connection()
+
+ if self.reader == None:
+ return None
+
+ return await self._read_single_line(reader)
+
+ async def write_single_line(self, message : str):
+ reader, writer = await self.get_socket_connection()
+
+ if self.writer == None:
+ return;
+
+ await self._write_single_line(writer, message)
+
+ async def _read_single_line(self, reader) -> str:
+ line = bytearray()
+ while True:
+ try:
+ line.extend(await reader.readuntil())
+ except asyncio.LimitOverrunError:
+ line.extend(await reader.read(reader._limit))
+ continue
+ except asyncio.IncompleteReadError as err:
+ line.extend(err.partial)
+ break
+ else:
+ break
+
+ return line.decode("utf-8")
+
+ async def _write_single_line(self, writer, message : str):
+ if not message.endswith("\n"):
+ message += "\n"
+
+ writer.write(message.encode("utf-8"))
+ await writer.drain()
+
+ async def _listen_for_method_call(self, reader, writer):
+ while True:
+ line = await self._read_single_line(reader)
+
+ try:
+ res = await self.on_new_message(line)
+ except Exception as e:
+ return
+
+ if res != None:
+ await self._write_single_line(writer, res)
+
+class PortSocket (UnixSocket):
+ def __init__(self, on_new_message):
+ '''
+ on_new_message takes 1 string argument.
+ It's return value gets used, if not None, to write data to the socket.
+ Method should be async
+ '''
+ super().__init__(on_new_message)
+ self.host = "127.0.0.1"
+ self.port = random.sample(range(40000, 60000), 1)[0]
+
+ async def setup_server(self):
+ self.socket = await asyncio.start_server(self._listen_for_method_call, host=self.host, port=self.port, limit=BUFFER_LIMIT)
+
+ async def _open_socket_if_not_exists(self):
+ if not self.reader:
+ retries = 0
+ while retries < 10:
+ try:
+ self.reader, self.writer = await asyncio.open_connection(host=self.host, port=self.port, limit=BUFFER_LIMIT)
+ return True
+ except:
+ await asyncio.sleep(2)
+ retries += 1
+ return False
+ else:
+ return True
+
+if ON_WINDOWS:
+ class LocalSocket (PortSocket):
+ pass
+else:
+ class LocalSocket (UnixSocket):
+ pass \ No newline at end of file