python: Added the beginning of a client library for ashd.pdm.
[ashd.git] / python / ashd / pdm.py
CommitLineData
6983e036
FT
1"""Management for daemon processes
2
3This module contains a utility to listen for management commands on a
4socket, lending itself to managing daemon processes.
5"""
6
7import os, sys, socket, threading, grp, select
8import types, pprint, traceback
9
10class repl(object):
11 def __init__(self, cl):
12 self.cl = cl
13 self.mod = types.ModuleType("repl")
14 self.mod.echo = self.echo
15 self.printer = pprint.PrettyPrinter(indent = 4, depth = 6)
16 cl.sk.send("+REPL\n")
17
158cbdf5
FT
18 def sendlines(self, text):
19 for line in text.split("\n"):
20 self.cl.sk.send(" " + line + "\n")
21
6983e036 22 def echo(self, ob):
158cbdf5 23 self.sendlines(self.printer.pformat(ob))
6983e036
FT
24
25 def command(self, cmd):
26 try:
27 try:
28 ccode = compile(cmd, "PDM Input", "eval")
29 except SyntaxError:
30 ccode = compile(cmd, "PDM Input", "exec")
31 exec ccode in self.mod.__dict__
32 self.cl.sk.send("+OK\n")
33 else:
34 self.echo(eval(ccode, self.mod.__dict__))
35 self.cl.sk.send("+OK\n")
36 except:
37 for line in traceback.format_exception(*sys.exc_info()):
158cbdf5 38 self.cl.sk.send(" " + line)
6983e036
FT
39 self.cl.sk.send("+EXC\n")
40
41 def handle(self, buf):
42 p = buf.find("\n\n")
43 if p < 0:
44 return buf
45 cmd = buf[:p + 1]
46 self.command(cmd)
47 return buf[p + 2:]
48
49class client(threading.Thread):
50 def __init__(self, sk):
51 super(client, self).__init__(name = "Management client")
52 self.setDaemon(True)
53 self.sk = sk
54 self.handler = self
55
56 def choose(self, proto):
57 if proto == "repl":
58 self.handler = repl(self)
59 else:
60 self.sk.send("-ERR Unknown protocol: %s\n" % proto)
61 raise Exception()
62
63 def handle(self, buf):
64 p = buf.find("\n")
65 if p >= 0:
66 proto = buf[:p]
67 buf = buf[p + 1:]
68 self.choose(proto)
69 return buf
70
71 def run(self):
72 try:
73 buf = ""
74 self.sk.send("+PDM1\n")
75 while True:
76 ret = self.sk.recv(1024)
77 if ret == "":
78 return
79 buf += ret
80 while True:
81 try:
82 nbuf = self.handler.handle(buf)
83 except:
84 return
85 if nbuf == buf:
86 break
87 buf = nbuf
88 finally:
89 self.sk.close()
90
91class listener(threading.Thread):
92 def __init__(self):
93 super(listener, self).__init__(name = "Management listener")
94 self.setDaemon(True)
95
96 def listen(self, sk):
97 self.running = True
98 while self.running:
99 rfd, wfd, efd = select.select([sk], [], [sk], 1)
100 for fd in rfd:
101 if fd == sk:
102 nsk, addr = sk.accept()
103 self.accept(nsk, addr)
104
105 def stop(self):
106 self.running = False
107 self.join()
108
109 def accept(self, sk, addr):
110 cl = client(sk)
111 cl.start()
112
113class unixlistener(listener):
114 def __init__(self, name, mode = 0600, group = None):
115 super(unixlistener, self).__init__()
116 self.name = name
117 self.mode = mode
118 self.group = group
119
120 def run(self):
121 sk = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
122 ul = False
123 try:
124 if os.path.exists(self.name) and os.path.stat.S_ISSOCK(os.stat(self.name).st_mode):
125 os.unlink(self.name)
126 sk.bind(self.name)
127 ul = True
128 os.chmod(self.name, self.mode)
129 if self.group is not None:
130 os.chown(self.name, os.getuid(), grp.getgrnam(self.group).gr_gid)
131 sk.listen(16)
132 self.listen(sk)
133 finally:
134 sk.close()
135 if ul:
136 os.unlink(self.name)
137
138class tcplistener(listener):
139 def __init__(self, port, bindaddr = "127.0.0.1"):
140 super(tcplistener, self).__init__()
141 self.port = port
142 self.bindaddr = bindaddr
143
144 def run(self):
145 sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
146 try:
147 sk.bind((self.bindaddr, self.port))
148 sk.listen(16)
149 self.listen(sk)
150 finally:
151 sk.close()