1###########################################################################
2#
3# This program is part of Zenoss Core, an open source monitoring platform.
4# Copyright (C) 2008-2010, Zenoss Inc.
5#
6# This program is free software; you can redistribute it and/or modify it
7# under the terms of the GNU General Public License version 2, or (at your
8# option) any later version, as published by the Free Software Foundation.
9#
10# For complete information please visit: http://www.zenoss.com/oss/
11#
12###########################################################################
13
14__doc__="python classes to integrate the twisted and Samba event loops"
15
16from pysamba.library import *
17from ctypes import *
18
19import os
20import sys
21import errno
22import sys
23import logging
24logging.basicConfig()
25log = logging.getLogger("zen.pysamba")
26
27EVENT_FD_READ = 1
28EVENT_FD_WRITE = 2
29
30class Timer(object):
31    callLater = None
32timer = Timer()
33
34class timeval(Structure):
35    _fields_ = [
36        ('tv_sec', c_long),
37        ('tv_usec', c_long),
38        ]
39
40#void zenoss_get_next_timeout(struct event_context* event_ctx, struct timeval* timeout)
41library.zenoss_get_next_timeout.restype = None
42library.zenoss_get_next_timeout.argtypes = [c_void_p, POINTER(timeval)]
43#logging will occur within this C function - avoid python logging for verbosity
44#library.zenoss_get_next_timeout = logFuncCall(library.zenoss_get_next_timeout)
45
46library.zenoss_read_ready.restype = None
47library.zenoss_read_ready.argtypes = [c_void_p, c_int]
48library.zenoss_read_ready = logFuncCall(library.zenoss_read_ready)
49
50library.zenoss_write_ready.restype = None
51library.zenoss_write_ready.argtypes = [c_void_p, c_int]
52library.zenoss_write_ready = logFuncCall(library.zenoss_write_ready)
53
54library.lp_do_parameter.restype = None
55library.lp_do_parameter.argtypes = [c_int, c_char_p, c_char_p]
56library.lp_do_parameter = logFuncCall(library.lp_do_parameter)
57
58from twisted.internet.selectreactor import SelectReactor
59from select import select
60
61class WmiReactorMixin:
62    def __init__(self):
63        self.readFdMap = {}
64        self.writeFdMap = {}
65
66    def callTimeouts(self, delay):
67        """
68        Perform a single iteration of the reactor. Here we make sure that
69        all of our file descriptors that we need to watch are and then delegate
70        the actual watching back to the twisted reactor.
71        """
72
73        #
74        # determine the next timeout interval based upon any queued events
75        #
76        timeout = timeval()
77        while True:
78            library.zenoss_get_next_timeout(eventContext, byref(timeout))
79            t = timeout.tv_sec + timeout.tv_usec / 1e6
80            if t > 0.0: break
81
82        if timer.callLater:
83            timer.callLater.cancel()
84            timer.callLater = None
85        timer.callLater = self.callLater(t, self.__checkTimeouts)
86
87    def removeFileDescriptor(self, fd):
88        self.watchFileDescriptor(fd, 0)
89
90    def watchFileDescriptor(self, fd, flags):
91        log.debug("Callback from c: watchFileDescriptor: fd: %s flags: %s" % (fd, flags))
92        if (flags & EVENT_FD_READ) != 0:
93            if fd not in self.readFdMap:
94                reader = ActivityHook('Reader', fd)
95                self.readFdMap[fd] = reader
96                self.addReader(self.readFdMap[fd])
97        elif fd in self.readFdMap:
98            self.removeReader(self.readFdMap[fd])
99            del self.readFdMap[fd]
100
101        if (flags & EVENT_FD_WRITE) != 0:
102            if fd not in self.writeFdMap:
103                writer = ActivityHook('Writer', fd)
104                self.writeFdMap[fd] = writer
105                self.addWriter(self.writeFdMap[fd])
106        elif fd in self.writeFdMap:
107            self.removeWriter(self.writeFdMap[fd])
108            del self.writeFdMap[fd]
109
110        return 0
111
112    def __checkTimeouts(self):
113        timer.callLater = None
114
115    def doOnce(self):
116        self.doIteration(0.1)
117        return 0
118
119class WmiReactor(WmiReactorMixin, SelectReactor):
120    def __init__(self):
121        SelectReactor.__init__(self)
122        WmiReactorMixin.__init__(self)
123
124    def _preenDescriptors(self):
125        for fdMap, lst in (
126            (self.readFdMap, self.readFdMap.keys()),
127            (self.writeFdMap.keys(), self.writeFdMap.keys())
128            ):
129            for fd in lst:
130                try:
131                    select(fd + 1, [fd], [fd], [fd], 0)
132                except:
133                    try:
134                        fdMap.pop(fd)
135                    except IndexError:
136                        pass
137        SelectReactor._preenDescriptors(self)
138
139    def doIteration(self, delay):
140        """
141        Perform a single iteration of the reactor. Here we make sure that
142        all of our file descriptors that we need to watch are and then delegate
143        the actual watching back to the twisted reactor.
144        """
145
146        self.callTimeouts(delay)
147        return SelectReactor.doIteration(self, delay)
148
149#
150# Install the WmiReactor instead of the default twisted one. See the following
151# ticket for the history of using an epoll vs. select reactor.
152#   http://dev.zenoss.org/trac/ticket/4640
153#
154if os.uname()[0] == 'Linux':
155    from twisted.internet.epollreactor import EPollReactor
156    class WmiEPollReactor(WmiReactorMixin, EPollReactor):
157        def __init__(self):
158            EPollReactor.__init__(self)
159            WmiReactorMixin.__init__(self)
160
161        def _remove(self, xer, primary, other, selectables, event, antievent):
162            try:
163                return EPollReactor._remove(self,
164                                            xer, primary, other, selectables,
165                                            event, antievent)
166            except IOError, err:
167                # huh, wha?  oh, we weren't listening for that fileno anyhow
168                if err.errno != errno.EBADF:
169                    raise
170
171        def doPoll(self, delay):
172            self.callTimeouts(delay)
173            return EPollReactor.doPoll(self, delay)
174
175        doIteration = doPoll
176
177    wmiReactor = WmiEPollReactor()
178
179else:
180    wmiReactor = WmiReactor()
181
182callback = reactor_functions()
183callback.fd_callback = library.watch_fd_callback(logFuncCall(wmiReactor.watchFileDescriptor))
184callback.loop_callback = library.loop_callback(logFuncCall(wmiReactor.doOnce))
185eventContext = library.async_create_context(byref(callback))
186
187from twisted.internet.main import installReactor
188try:
189    installReactor(wmiReactor)
190except Exception, e:
191    log.critical("Unable to install the WMI reactor. %s" % e)
192    sys.exit(1) # TODO: pick a better error code?
193
194class ActivityHook:
195    "Notify the Samba event loop of file descriptor activity"
196
197    def __init__(self, prefix, fd):
198        self.prefix = prefix
199        self.fd = fd
200
201    def logPrefix(self):
202        return self.prefix
203
204    def doRead(self):
205        library.zenoss_read_ready(eventContext, self.fd)
206
207    def doWrite(self):
208        library.zenoss_write_ready(eventContext, self.fd)
209
210    def fileno(self):
211        return self.fd
212
213    def connectionLost(self, why):
214        wmiReactor.removeFileDescriptor(self.fd)
215
216# allow users to write:
217#    from pysamba.twisted import reactor
218# instead of
219#    from twisted.internet import reactor
220from twisted.internet import reactor
221
222def setNTLMv2Authentication(enabled = False):
223    """
224    Enables or disables the NTLMv2 authentication feature.
225    @param enabled: True if NTLMv2 is supported
226    @type enabled: boolean
227    """
228    flag = "no"
229    if enabled:
230        flag = "yes"
231    library.lp_do_parameter(-1, "client ntlmv2 auth", flag)
232    log.debug("client ntlmv2 auth is now %s", flag)
233