#!/usr/bin/env python3
import asyncio
import os
import sys
from typing import Any, Callable, Coroutine
HOST_COLORS = {
"tozt": "\x1b[1;33m",
"mail": "\x1b[32m",
"partofme": "\x1b[35m",
}
def color_host(host: str):
return HOST_COLORS[host] + host + "\x1b[m"
async def unlock_rbw():
proc = await asyncio.create_subprocess_exec("rbw", "unlock")
await proc.wait()
async def get_password(host: str):
proc = await asyncio.create_subprocess_exec(
"rbw",
"get",
host,
os.environ["USER"],
stdout=asyncio.subprocess.PIPE,
)
stdout, _ = await proc.communicate()
return stdout.rstrip()
async def read_stream(
stream: asyncio.StreamReader,
print_cb: Callable[[str], None],
sudo_cb: Coroutine[Any, Any, None] | None,
):
buf = b""
while True:
read = await stream.read(1024)
if len(read) == 0:
if len(buf) > 0:
print_cb(buf.decode())
break
buf += read
lines = buf.split(b"\n")
buf = lines.pop()
for line in lines:
print_cb(line.decode())
if sudo_cb and buf == f"[sudo] password for {os.environ['USER']}: ".encode():
await sudo_cb
sudo_cb = None
buf = b""
async def puppet_tozt(host: str):
password = await get_password(host)
proc = await asyncio.create_subprocess_exec(
"ssh",
host,
"sudo",
"--stdin",
*sys.argv[1:],
stdin=asyncio.subprocess.PIPE,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
)
assert proc.stdout is not None
assert proc.stderr is not None
colored_host = color_host(host)
async def sudo_cb():
assert proc.stdin is not None
proc.stdin.write(password + b"\n")
await proc.stdin.drain()
await asyncio.gather(
read_stream(
proc.stdout,
lambda line: print(f"[{colored_host}:out] {line}"),
None,
),
read_stream(
proc.stderr,
lambda line: print(f"[{colored_host}:\x1b[31merr\x1b[m] {line}"),
sudo_cb(),
),
)
ret = await proc.wait()
if ret == 0:
print(f"[{colored_host}] Exited successfully")
else:
print(f"[{colored_host}] \x1b[31mExited with code {ret}\x1b[m")
async def main():
await unlock_rbw()
await asyncio.gather(
puppet_tozt("tozt"),
puppet_tozt("mail"),
puppet_tozt("partofme"),
)
asyncio.run(main())