home-backup/home_backup/utils.py

87 lines
2.9 KiB
Python

import asyncio
import functools
import json
import os
import socket
import struct
async def get_user_home(name:str):
# Check args
if not isinstance(name, str):
raise TypeError("name have to be a string.")
# Find home
proc = await asyncio.create_subprocess_exec(b"getent", b"passwd", name.encode(), stdout=asyncio.subprocess.PIPE)
proc_data = await proc.communicate()
if not isinstance(proc_data, tuple):
raise RuntimeError("Internal value isn't the expected type.")
if proc.returncode != 0:
raise RuntimeError("getent didn't work.")
return proc_data[0].decode().split(":")[6]
async def run_access_socket(path:str, async_callback, fork:bool):
async def run_func(read, write):
# Get user id
sock = write.get_extra_info("socket")
tmp = sock.getsockopt(socket.SOL_SOCKET, socket.SO_PEERCRED, struct.calcsize('3i'))
tmp = struct.unpack('3i', tmp)[1]
uid = str(tmp)
# Run callback
await async_callback(read, write, uid)
server = await asyncio.start_unix_server(run_func, path=path)
os.chmod(path, 0o666)
if fork:
if os.fork() != 0:
exit(0)
await server.serve_forever()
_format_length = struct.Struct(">I")
def rpc_callback(async_func):
@functools.wraps(async_func)
async def wrap_func(read, write, uid):
while not read.at_eof():
# Read data
try:
size = _format_length.unpack(await read.readexactly(_format_length.size))
except asyncio.exceptions.IncompleteReadError:
return # Client closed normaly
data = json.loads((await read.readexactly(size)).decode("UTF-8"))
# Callback and return result
result = json.dumps(await async_func(data, uid)).encode("UTF-8")
write.write(_format_length.pack(len(result)))
write.write(result)
await write.drain()
return wrap_func
class Connection():
__read = None
__write = None
__lock = asyncio.Lock()
async def init(self, sys_socket):
self.__read, self.__write = await asyncio.open_unix_connection(path=sys_socket)
async def call(self, data:dict):
# Check data and state
if self.__read is None or self.__write is None:
raise RuntimeError("Connection isn't inited.")
if not isinstance(data, dict):
raise TypeError("data have to be a dict")
# Wait for access
async with self.__lock:
# Send data
data = json.dumps(data).encode("UTF-8")
self.__write.write(_format_length.pack(len(data)))
self.__write.write(data)
await self.__write.drain()
# Recive date and return
size = _format_length.unpack(await self.__read.readexactly(_format_length.size))
return json.loads((await self.__read.readexactly(size)).decode("UTF-8"))