Browse Source

initial commit

master
Wesley Kerfoot 5 years ago
commit
edba571c69
  1. 3
      .gitignore
  2. 60
      client.py
  3. 134
      server.rkt

3
.gitignore

@ -0,0 +1,3 @@
*pyc
*~
venv

60
client.py

@ -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))

134
server.rkt

@ -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…
Cancel
Save