chiark / gitweb /
Initial import.
[mLib-python] / select.pyx
1 # -*-pyrex-*-
2 #
3 # $Id$
4 #
5 # Selectery
6 #
7 # (c) 2005 Straylight/Edgeware
8 #
9
10 #----- Licensing notice -----------------------------------------------------
11 #
12 # This file is part of the Python interface to mLib.
13 #
14 # mLib/Python is free software; you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation; either version 2 of the License, or
17 # (at your option) any later version.
18
19 # mLib/Python is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 # GNU General Public License for more details.
23
24 # You should have received a copy of the GNU General Public License
25 # along with mLib/Python; if not, write to the Free Software Foundation,
26 # Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27
28 #----- External dependencies ------------------------------------------------
29
30 cdef extern from 'stddef.h':
31   ctypedef int size_t
32 cdef extern from 'string.h':
33   void memcpy(void *p, void *q, size_t n)
34 cdef extern from 'errno.h':
35   int errno
36   enum:
37     EINTR
38     EAGAIN
39 cdef extern from 'math.h':
40   double modf(double x, double *i)
41 cdef extern from 'string.h':
42   char *strerror(int err)
43 cdef extern from 'sys/time.h':
44   struct timeval:
45     int tv_sec
46     int tv_usec
47 cdef extern from 'sys/types.h':
48   pass
49 cdef extern from 'sys/socket.h':
50   struct sockaddr:
51     int sa_family
52   enum:
53     AF_INET
54   int getsockname(int fd, sockaddr *pa, size_t *psz)
55   int getpeername(int fd, sockaddr *pa, size_t *psz)
56 cdef extern from 'arpa/inet.h':
57   struct in_addr:
58     int s_addr
59   int inet_aton(char *rp, in_addr *ia)
60   char *inet_ntoa(in_addr ia)
61 cdef extern from 'netinet/in.h':
62   struct sockaddr_in:
63     int sin_family
64     in_addr sin_addr
65     int sin_port
66   int htons(int x)
67   int htonl(int x)
68   int ntohs(int x)
69   int ntohl(int x)
70 cdef extern from 'netdb.h':
71   struct hostent:
72     char *h_name
73     char **h_aliases
74     int h_addrtype
75     int h_length
76     char **h_addr_list
77     char *h_addr
78   int h_errno
79
80 cdef extern from 'mLib/sel.h':
81   ctypedef struct sel_state:
82     pass
83   ctypedef struct sel_file:
84     int fd
85   ctypedef struct sel_timer:
86     pass
87   enum:
88     SEL_READ
89     SEL_WRITE
90     SEL_EXC
91   void sel_init(sel_state *s)
92   void sel_initfile(sel_state *s, sel_file *f, int fd, unsigned mode,
93                     void (*func)(int fd, unsigned mode, void *arg),
94                     void *arg)
95   void sel_force(sel_file *f)
96   void sel_addfile(sel_file *f)
97   void sel_rmfile(sel_file *f)
98   void sel_addtimer(sel_state *s, sel_timer *t, timeval *tv,
99                     void (*func)(timeval *tv, void *arg),
100                     void *arg)
101   void sel_rmtimer(sel_timer *t)
102   int sel_select(sel_state *s) except *
103
104 cdef extern from 'mLib/conn.h':
105   ctypedef struct conn:
106     pass
107   int conn_fd(conn *c, sel_state *s, int fd,
108               void (*func)(int fd, void *arg), void *arg)
109   void conn_kill(conn *c)
110
111 cdef extern from 'mLib/bres.h':
112   ctypedef struct bres_client:
113     pass
114   void bres_byname(bres_client *r, char *name,
115                    void (*func)(hostent *h, void *arg), void *arg)
116   void bres_byaddr(bres_client *r, in_addr addr,
117                    void (*func)(hostent *h, void *arg), void *arg)
118   void bres_abort(bres_client *r)
119   void bres_exec(char *null)
120   void bres_init(sel_state *s)
121
122 cdef extern from 'mLib/sig.h':
123   ctypedef struct sig:
124     pass
125   void sig_add(sig *s, int n, void (*func)(int n, void *arg), void *arg)
126   void sig_remove(sig *s)
127   void sig_init(sel_state *s)
128
129 cdef extern from 'mLib/lbuf.h':
130   cdef struct lbuf:
131     int f
132     int delim
133     size_t sz
134   enum:
135     LBUF_ENABLE
136     LBUF_CRLF
137     LBUF_STRICTCRLF
138
139 cdef extern from 'mLib/selbuf.h':
140   ctypedef struct selbuf:
141     sel_file reader
142     lbuf b
143   void selbuf_enable(selbuf *b)
144   void selbuf_disable(selbuf *b)
145   void selbuf_setsize(selbuf *b, size_t sz)
146   void selbuf_init(selbuf *b, sel_state *s, int fd,
147                    void (*func)(char *s, size_t len, void *arg), void *arg)
148   void selbuf_destroy(selbuf *b)
149
150 cdef extern from 'mLib/pkbuf.h':
151   ctypedef struct pkbuf:
152     int f
153     int want
154   enum:
155     PKBUF_ENABLE
156
157 cdef extern from 'mLib/selpk.h':
158   ctypedef struct selpk:
159     sel_file reader
160     pkbuf pk
161   void selpk_enable(selpk *b)
162   void selpk_disable(selpk *b)
163   void selpk_want(selpk *b, size_t sz)
164   void selpk_init(selpk *b, sel_state *s, int fd,
165                   void (*func)(unsigned char *p, size_t n,
166                                pkbuf *pk, size_t *keep, void *arg),
167                   void *arg)
168   void selpk_destroy(selpk *b)
169
170 cdef extern from 'mLib/ident.h':
171   ctypedef struct ident_request:
172     pass
173   enum:
174     IDENT_USERID
175     IDENT_ERROR
176     IDENT_BAD
177   struct ident_userid:
178     char *os
179     char *user
180   union ident_u:
181     ident_userid userid
182     char *error
183   ctypedef struct ident_reply:
184     int sport
185     int dport
186     int type
187     ident_u u
188   void ident(ident_request *rq, sel_state *s,
189              sockaddr_in *local, sockaddr_in *remote,
190              void (*func)(ident_reply *r, void *arg),
191              void *arg)
192   void ident_abort(ident_request *rq)
193
194 cdef extern from 'Python.h':
195   object PyString_FromStringAndSize(char *p, int len)
196   int PyString_AsStringAndSize(obj, char **p, int *len) except -1
197   int PyObject_AsReadBuffer(obj, void **buf, int *len) except -1
198   int PyObject_TypeCheck(obj, ty)
199
200 cdef extern from 'grim.h':
201   int PSIZEOF(void *x)
202
203 import socket
204 import signal
205
206 #----- Utility functions ----------------------------------------------------
207
208 cdef _oserror():
209   raise OSError, (errno, strerror(errno))
210
211 cdef object _tobool(int i):
212   if i:
213     return True
214   else:
215     return False
216
217 cdef int _getfd(object fdobj):
218   try:
219     return fdobj
220   except TypeError:
221     return fdobj.fileno()
222
223 #----- The global select state ----------------------------------------------
224
225 cdef sel_state sel
226
227 sel_init(&sel)
228 bres_init(&sel)
229 bres_exec(NULL)
230 sig_init(&sel)
231
232 def select():
233   if sel_select(&sel) and errno != EINTR and errno != EAGAIN:
234     _oserror()
235
236 #----- File selectors -------------------------------------------------------
237
238 READ = SEL_READ
239 WRITE = SEL_WRITE
240 EXCEPT = SEL_EXC
241
242 cdef class File:
243   cdef sel_file f
244   cdef int _activep
245   cdef readonly unsigned mode
246   def __new__(me, fd, int mode = SEL_READ):
247     if (mode != SEL_READ and
248         mode != SEL_WRITE and
249         mode != SEL_EXC):
250       raise ValueError, 'bad select mode'
251     sel_initfile(&sel, &me.f, _getfd(fd), mode, _filefunc, <void *>me)
252     me._activep = 0
253     me.mode = mode
254   def __dealloc__(me):
255     if me._activep:
256       sel_rmfile(&me.f)
257   property fd:
258     def __get__(me):
259       return me.f.fd
260   property activep:
261     def __get__(me):
262       return _tobool(me._activep)
263   def enable(me):
264     if me._activep:
265       raise ValueError, 'already enabled'
266     sel_addfile(&me.f)
267     me._activep = 1
268     return me
269   def disable(me):
270     if not me._activep:
271       raise ValueError, 'already disabled'
272     sel_rmfile(&me.f)
273     me._activep = 0
274     return me
275   def force(me):
276     sel_force(&me.f)
277     return me
278   def ready(me):
279     pass
280
281 cdef void _filefunc(int fd, unsigned mode, void *arg):
282   cdef File sf
283   sf = <File>arg
284   sf.ready()
285
286 #----- Timer selectors ------------------------------------------------------
287
288 cdef double _tvtofloat(timeval *tv):
289   return tv.tv_sec + (tv.tv_usec / 1000000)
290 cdef void _floattotv(timeval *tv, double t):
291   cdef double s, us
292   us = modf(t, &s)
293   tv.tv_sec = s
294   tv.tv_usec = us * 1000000
295
296 cdef class Timer:
297   cdef sel_timer t
298   cdef int _activep
299   cdef readonly double time
300   def __new__(me, double when):
301     cdef timeval tv
302     _floattotv(&tv, when)
303     sel_addtimer(&sel, &me.t, &tv, _timerfunc, <void *>me)
304     me._activep = 1
305     me.time = when
306   def __dealloc__(me):
307     if me._activep:
308       sel_rmtimer(&me.t)
309   property activep:
310     def __get__(me):
311       return _tobool(me._activep)
312   def kill(me):
313     if not me._activep:
314       raise ValueError, 'already dead'
315     sel_rmtimer(&me.t)
316     me._activep = 0
317     return me
318   def timer(me, now):
319     pass
320
321 cdef void _timerfunc(timeval *now, void *arg):
322   cdef Timer st
323   st = <Timer>arg
324   st._activep = 0
325   st.timer(_tvtofloat(now))
326
327 #----- Connections ----------------------------------------------------------
328
329 cdef class Connect:
330   cdef conn c
331   cdef int _activep
332   cdef readonly object socket
333   def __new__(me, sk):
334     conn_fd(&me.c, &sel, sk.fileno(), _connfunc, <void *>me)
335     me._activep = 1
336     me.socket = sk
337   def __dealloc__(me):
338     if me._activep:
339       conn_kill(&me.c)
340   property activep:
341     def __get__(me):
342       return _tobool(me._activep)
343   def kill(me):
344     if not me._activep:
345       raise ValueError, 'already dead'
346     conn_kill(&me.c);
347     me._activep = 0
348     return me
349   def connected(me):
350     pass
351   def error(me, errno, strerror):
352     pass
353
354 cdef void _connfunc(int fd, void *arg):
355   cdef Connect c
356   c = <Connect>arg
357   c._activep = 0
358   if fd == -1:
359     c.socket = None
360     c.error(errno, strerror(errno))
361   else:
362     c.connected()
363
364 #----- Background name resolution -------------------------------------------
365
366 cdef class Resolve:
367   cdef bres_client r
368   cdef int _activep
369   def __init__(me, *hunoz, **hukairz):
370     raise TypeError, 'abstract class'
371   property activep:
372     def __get__(me):
373       return _tobool(me._activep)
374   def kill(me):
375     if not me._activep:
376       raise ValueError, 'already dead'
377     bres_abort(&me.r)
378     return me
379   def resolved(me, h):
380     pass
381   def failed(me):
382     pass
383
384 cdef class ResolveByName (Resolve):
385   def __new__(me, char *name):
386     bres_byname(&me.r, name, _resfunc, <void *>me)
387     me._activep = 1
388   def __init__(me, name):
389     pass
390
391 cdef class ResolveByAddr (Resolve):
392   def __new__(me, char *addr):
393     cdef in_addr ia
394     if not inet_aton(addr, &ia):
395       raise TypeError, 'bad IP address'
396     bres_byaddr(&me.r, ia, _resfunc, <void *>me)
397     me._activep = 1
398   def __init__(me, addr):
399     pass
400
401 cdef void _resfunc(hostent *h, void *arg):
402   cdef Resolve r
403   cdef int i
404   r = <Resolve>arg
405   r._activep = 0
406   if h is NULL:
407     r.failed(r)
408   else:
409     alias = []
410     addr = []
411     i = 0
412     while h.h_aliases[i]:
413       alias.append(h.h_aliases[i])
414       i = i + 1
415     i = 0
416     while h.h_addr_list[i]:
417       addr.append(inet_ntoa((<in_addr *>h.h_addr_list[i])[0]))
418       i = i + 1
419     r.resolved(h.h_name, alias, addr)
420
421 #----- Signal handling ------------------------------------------------------
422
423 cdef class Signal:
424   cdef sig s
425   cdef int _activep
426   cdef readonly int signal
427   def __new__(me, int sig):
428     if sig < 0 or sig >= signal.NSIG:
429       raise ValueError, 'signal number out of range'
430     me.signal = sig
431     me._activep = 0
432   def __dealloc__(me):
433     if me._activep:
434       sig_remove(&me.s)
435   def enable(me):
436     if me._activep:
437       raise ValueError, 'already enabled'
438     sig_add(&me.s, me.signal, _sigfunc, <void *>me)
439     me._activep = 1
440     return me
441   def disable(me):
442     if not me._activep:
443       raise ValueError, 'already disabled'
444     sig_remove(&me.s)
445     me._activep = 0
446     return me
447   def signalled(me):
448     pass
449
450 cdef void _sigfunc(int sig, void *arg):
451   cdef Signal s
452   s = <Signal>arg
453   s.signalled()
454
455 #----- Line buffers ---------------------------------------------------------
456
457 CRLF = LBUF_CRLF
458 STRICTCRLF = LBUF_STRICTCRLF
459
460 cdef class LineBuffer:
461   cdef selbuf b
462   def __new__(me, fd):
463     selbuf_init(&me.b, &sel, _getfd(fd), _lbfunc, <void *>me)
464     selbuf_disable(&me.b)
465   def __dealloc__(me):
466     selbuf_destroy(&me.b)
467   property activep:
468     def __get__(me):
469       return _tobool(me.b.b.f & LFBUF_ENABLE)
470   property delim:
471     def __get__(me):
472       if me.b.b.delim == LBUF_CRLF or me.b.b.delim == LBUF_STRICTCRLF:
473         return me.b.b.delim
474       else:
475         return chr(me.b.b.delim)
476     def __set__(me, d):
477       if d == LBUF_CRLF or d == LBUF_STRICTCRLF:
478         me.b.b.delim = d
479       else:
480         me.b.b.delim = ord(d)
481   property size:
482     def __get__(me):
483       return me.b.b.sz
484     def __set__(me, sz):
485       selbuf_setsize(&me.b, sz)
486   def enable(me):
487     if me.b.b.f & LBUF_ENABLE:
488       raise ValueError, 'already enabled'
489     selbuf_enable(&me.b)
490     return me
491   def disable(me):
492     if not (me.b.b.f & LBUF_ENABLE):
493       raise ValueError, 'already disabled'
494     selbuf_disable(&me.b)
495     return me
496   def line(me, line):
497     pass
498   def eof(me):
499     pass
500
501 cdef void _lbfunc(char *s, size_t n, void *arg):
502   cdef LineBuffer sb
503   sb = <LineBuffer>arg
504   if s is NULL:
505     sb.eof()
506   else:
507     sb.line(PyString_FromStringAndSize(s, n))
508
509 #----- Packet buffers -------------------------------------------------------
510
511 cdef class PacketBuffer:
512   cdef selpk p
513   def __new__(me, fd):
514     selpk_init(&me.p, &sel, _getfd(fd), _pkfunc, <void *>me)
515     selpk_disable(&me.p)
516   def __dealloc__(me):
517     selpk_destroy(&me.p)
518   property activep:
519     def __get__(me):
520       return _to_bool(me.p.pk.f & PKBUF_ENABLE)
521   property want:
522     def __get__(me):
523       return me.p.pk.want
524     def __set__(me, n):
525       selpk_want(&me.p, n)
526   def enable(me):
527     if me.p.pk.f & PKBUF_ENABLE:
528       raise ValueError, 'already enabled'
529     selpk_enable(&me.p)
530     return me
531   def disable(me):
532     if not (me.p.pk.f & PKBUF_ENABLE):
533       raise ValueError, 'already disabled'
534     selpk_disable(&me.p)
535     return me
536   def packet(me, pk):
537     return None
538   def eof(me):
539     pass
540
541 cdef void _pkfunc(unsigned char *p, size_t n, pkbuf *pk,
542                   size_t *keep, void *arg):
543   cdef PacketBuffer pb
544   cdef void *rp
545   cdef int rn
546   pb = <PacketBuffer>arg
547   if p is NULL:
548     pb.eof()
549   else:
550     r = pb.packet(PyString_FromStringAndSize(<char *>p, n))
551     if r is not None:
552       PyObject_AsReadBuffer(r, &rp, &rn)
553       if rn > n:
554         raise ValueError, 'remaining buffer too large'
555       if rn:
556         memcpy(p + n - rn, rp, rn)
557         keep[0] = rn
558
559 #----- Ident client ---------------------------------------------------------
560
561 cdef _inaddr_frompy(sockaddr_in *sin, addr):
562   cdef int port
563   if len(addr) != 2:
564     raise TypeError, 'want address/port pair'
565   a = addr[0]
566   if not inet_aton(a, &sin.sin_addr):
567     raise TypeError, 'bad IP address'
568   port = addr[1]
569   if not (0 <= port < 65536):
570     raise TypeError, 'port number out of range'
571   sin.sin_port = htons(port)
572
573 cdef _inaddr_topy(sockaddr_in *sin):
574   return inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)
575
576 cdef class Identify:
577   cdef ident_request irq
578   cdef int _activep
579   cdef readonly localaddr
580   cdef readonly remoteaddr
581   def __new__(me, sk):
582     cdef sockaddr_in s_in, s_out
583     cdef size_t sz_in, sz_out
584     cdef int fd
585     if PyObject_TypeCheck(sk, socket.SocketType):
586       fd = sk.fileno()
587       sz_in = PSIZEOF(&s_in)
588       sz_out = PSIZEOF(&s_out)
589       if getsockname(fd, <sockaddr *>&s_in, &sz_in) or \
590          getpeername(fd, <sockaddr *>&s_out, &sz_out):
591         _oserror()
592       if s_in.sin_family != AF_INET or s_out.sin_family != AF_INET:
593         raise TypeError, 'must be internet socket'
594     elif len(sk) != 2:
595       raise TypeError, 'want pair of addresses'
596     else:
597       _inaddr_frompy(&s_in, sk[0])
598       _inaddr_frompy(&s_out, sk[1])
599     ident(&me.irq, &sel, &s_in, &s_out, _identfunc, <void *>me)
600     me.localaddr = _inaddr_topy(&s_in)
601     me.remoteaddr = _inaddr_topy(&s_out)
602     me._activep = 1
603   def __dealloc__(me):
604     if me._activep:
605       ident_abort(&me.irq)
606   property activep:
607     def __get__(me):
608       return _tobool(me._activep)
609   def kill(me):
610     if not me._activep:
611       raise ValueError, 'already disabled'
612     ident_abort(&me.irq)
613     me._activep = 0
614   def user(me, os, user):
615     pass
616   def bad(me):
617     pass
618   def error(me, error):
619     pass
620   def failed(me, errno, strerror):
621     pass
622
623 cdef void _identfunc(ident_reply *i, void *arg):
624   cdef Identify id
625   id = <Identify>arg
626   id._activep = 0
627   if i.type == IDENT_BAD:
628     ii.bad()
629   elif i.type == IDENT_ERROR:
630     ii.error(i.u.error)
631   elif i.type == IDENT_USER:
632     ii.user(i.u.userid.os, i.u.userid.user)
633
634 #----- That's all, folks ----------------------------------------------------