commit
edba571c69
3 changed files with 197 additions and 0 deletions
@ -0,0 +1,3 @@ |
|||
*pyc |
|||
*~ |
|||
venv |
@ -0,0 +1,60 @@ |
|||
#! /usr/bin/env python3 |
|||
|
|||
import socket |
|||
import argparse |
|||
|
|||
from contextlib import closing |
|||
from json import loads |
|||
|
|||
def read_log(pid): |
|||
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s: |
|||
s.connect("/tmp/shelltalk.sock") |
|||
s.send(("read {0}\n".format(pid)).encode("utf-8")) |
|||
|
|||
buf = [] |
|||
|
|||
while True: |
|||
chunk = s.recv(1024) |
|||
buf.append(chunk) |
|||
if b"\n" in chunk: |
|||
break |
|||
|
|||
return loads(b"".join(buf).decode("utf-8")) |
|||
|
|||
def spawn(pid): |
|||
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s: |
|||
s.connect("/tmp/shelltalk.sock") |
|||
s.send(("spawn {0}\n".format(pid)).encode("utf-8")) |
|||
|
|||
def write(pid, msg): |
|||
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s: |
|||
s.connect("/tmp/shelltalk.sock") |
|||
s.send(("write {0} {1}\n".format(pid, msg)).encode("utf-8")) |
|||
|
|||
parser = argparse.ArgumentParser() |
|||
|
|||
parser.add_argument("-S", |
|||
"--spawn", |
|||
nargs=1, |
|||
help="Would you like to use me?") |
|||
|
|||
parser.add_argument("-W", |
|||
"--write", |
|||
nargs=2, |
|||
help="Would you like to log some things?") |
|||
|
|||
parser.add_argument("-R", |
|||
"--read", |
|||
nargs=1, |
|||
help="Would you like to read some logs?") |
|||
|
|||
args = parser.parse_args() |
|||
|
|||
if args.spawn: |
|||
print(spawn(*args.spawn)) |
|||
|
|||
if args.write: |
|||
print(write(*args.write)) |
|||
|
|||
if args.read: |
|||
print(read_log(*args.read)) |
@ -0,0 +1,134 @@ |
|||
#! /usr/bin/env racket |
|||
#lang racket |
|||
|
|||
(require racket/unix-socket) |
|||
(require json) |
|||
|
|||
;; Resource management functions |
|||
|
|||
(define (bail . args) |
|||
(displayln "/tmp/shelltalk.sock could not be created (may already exist)" |
|||
(current-error-port)) |
|||
(exit 1)) |
|||
|
|||
; All messages come through this socket |
|||
; It is cleaned up after execution finishes |
|||
(define |
|||
control-socket |
|||
(with-handlers ([exn:fail? bail]) |
|||
(unix-socket-listen "/tmp/shelltalk.sock"))) |
|||
|
|||
(define (close-socket in out) |
|||
(close-output-port out) |
|||
(close-input-port in)) |
|||
|
|||
(define (rm-socket . args) |
|||
; Removes the socket |
|||
(parameterize ([current-error-port |
|||
(open-output-string)]) |
|||
|
|||
(system "rm /tmp/shelltalk.sock"))) |
|||
|
|||
|
|||
;; Message handling functions |
|||
|
|||
(define (write-to entries out) |
|||
(with-handlers ([exn:fail? (const '())]) |
|||
(write-json entries out) |
|||
(display "\n" out))) |
|||
|
|||
(define (log pid entries) |
|||
(match (thread-receive) |
|||
[(cons 'read out) |
|||
(write-to entries out) |
|||
(log pid entries)] |
|||
|
|||
[entry |
|||
(log pid (cons entry entries))])) |
|||
|
|||
(define (logger-send loggers pid message) |
|||
(cond |
|||
[(hash-has-key? loggers pid) |
|||
(thread-send (hash-ref loggers pid) message)] |
|||
[else '()])) |
|||
|
|||
(define (handle-messages loggers) |
|||
(match (thread-receive) |
|||
[(list 'log pid entry) |
|||
(logger-send loggers pid entry) |
|||
(handle-messages loggers)] |
|||
|
|||
[(cons 'spawn pid) |
|||
;; XXX this should check if it exists already |
|||
(handle-messages (hash-set loggers |
|||
pid |
|||
(thread (lambda () (log pid '[])))))] |
|||
|
|||
[(cons 'kill pid) |
|||
(kill-thread (hash-ref loggers pid)) |
|||
(handle-messages |
|||
(hash-remove loggers pid))] |
|||
|
|||
[(list 'read pid out) |
|||
(displayln "got read message") |
|||
; Reads all the logs for a given pid |
|||
(logger-send loggers pid (cons 'read out)) |
|||
(handle-messages loggers)])) |
|||
|
|||
(define message-handler |
|||
(thread (lambda () |
|||
(handle-messages |
|||
(make-immutable-hash '[]))))) |
|||
|
|||
(define (handle-connection in out) |
|||
(define input-string (read-line in 'linefeed)) |
|||
(cond |
|||
[(eof-object? input-string) |
|||
(close-socket in out)] |
|||
[else |
|||
(match (string-split input-string) |
|||
[(list "spawn" pid) |
|||
(displayln "got spawn") |
|||
(thread-send message-handler (cons 'spawn pid)) |
|||
(handle-connection in out)] |
|||
|
|||
[(list "read" pid) |
|||
(displayln "got read") |
|||
(thread-send message-handler (list 'read pid out)) |
|||
(handle-connection in out)] |
|||
|
|||
[(list "write" pid message) |
|||
(displayln "got write") |
|||
(thread-send message-handler (list 'log pid message)) |
|||
(handle-connection in out)] |
|||
|
|||
[(list "close" pid) |
|||
(displayln (format "~a closed" pid)) |
|||
(thread-send message-handler (cons 'kill pid)) |
|||
(close-socket in out)] |
|||
|
|||
[other |
|||
(displayln other) |
|||
(handle-connection in out)])])) |
|||
|
|||
;; Socket handling |
|||
|
|||
(define (accept-logs) |
|||
(let-values |
|||
([(in out) (unix-socket-accept control-socket)]) |
|||
(thread |
|||
; hands the read capability over for this shell instance |
|||
(lambda () |
|||
(file-stream-buffer-mode out 'none) |
|||
(handle-connection in out)))) |
|||
(accept-logs)) |
|||
|
|||
; Start execution |
|||
; Use dynamic-wind to ensure socket is always cleaned up |
|||
(dynamic-wind |
|||
(const '()) |
|||
(lambda () |
|||
(with-handlers ([exn:break? rm-socket] |
|||
[exn:fail? rm-socket]) |
|||
(accept-logs))) |
|||
rm-socket) |
Loading…
Reference in new issue