root/branch/umitweb-ng/umitCore/BGProcess.py @ 4770

Revision 4770, 6.3 kB (checked in by rcarvalho, 4 years ago)

Merged revisions 4208-4214 via svnmerge from
http://svn.umitproject.org/svnroot/umit/trunk

........

r4208 | gpolo | 2009-02-27 15:54:02 -0300 (Sex, 27 Fev 2009) | 1 line


Fixed ticket #213. HOME under Windows is now set to the user's local appdata.

........

r4209 | gpolo | 2009-02-27 15:55:22 -0300 (Sex, 27 Fev 2009) | 1 line


Adjusted to work with win32com.shell imports.

........

r4210 | gpolo | 2009-02-27 15:58:49 -0300 (Sex, 27 Fev 2009) | 1 line


Fixed ticket #199: "Umit may hang while staring".

........

r4211 | gpolo | 2009-02-27 20:09:20 -0300 (Sex, 27 Fev 2009) | 1 line


Generate a service class string from the service path if given. This helps installing the service inside Umit in case umit_scheduler.py moves to somewhere else (like to bin/).

........

r4212 | gpolo | 2009-02-27 20:21:42 -0300 (Sex, 27 Fev 2009) | 1 line


Setting service path

........

r4213 | gpolo | 2009-02-28 18:17:47 -0300 (Sáb, 28 Fev 2009) | 1 line


Relayout

........

r4214 | gpolo | 2009-02-28 18:45:29 -0300 (Sáb, 28 Fev 2009) | 3 lines


Moved umitDB/sql to share/umit/sql, updated installers. This also fixes
ticket #220.

........

  • Property svn:eol-style set to native
Line 
1import os
2import sys
3import errno
4import signal
5
6NT = os.name == 'nt'
7
8try:
9    DEVNULL = os.devnull
10except AttributeError:
11    DEVNULL = '/dev/null'
12
13try:
14    import pywintypes
15    import win32event
16    import win32service
17    import win32serviceutil
18except ImportError:
19    if NT:
20        # If we are on Windows then the above modules are totally required
21        raise
22    else:
23        # Otherwise we make this dummy thing so we can define the
24        # WindowsService class elsewhere.
25        class win32serviceutil:
26            class ServiceFramework:
27                pass
28        win32serviceutil = win32serviceutil()
29
30
31class UNIXDaemon(object):
32
33    def __init__(self, pidfilename, post_init=None):
34        self._pidfilename = pidfilename
35        self._post_init = post_init
36
37    def running(self):
38        """
39        Return True if the daemon is still running, False otherwise.
40        """
41        pid = self._get_pid()
42        if isinstance(pid, int):
43            return not bool(self._stopped(pid))
44        else:
45            return False
46
47    def stop(self):
48        if self.running():
49            self.cleanup()
50
51    def cleanup(self):
52        pid = self._get_pid()
53        if not isinstance(pid, int):
54            # Assuming that some error related to the pidfile not being
55            # present was returned, meaning there is no cleanup to perform.
56            return
57        err = self._finish(pid)
58        if err:
59            return err
60
61        return self._remove_pidfile()
62
63    def start(self):
64        # Perform cleanup before starting a new daemon, if necessary
65        if self._get_pid():
66            # Finish the daemon before continuing
67            self.stop()
68
69        self._daemon_init()
70
71        pid = os.getpid()
72        f_pid = open(self._pidfilename, 'w')
73        f_pid.write("%d" % pid)
74        f_pid.close()
75
76        if self._post_init:
77            self._post_init()
78
79    def _stopped(self, pid):
80        try:
81            os.kill(pid, 0)
82        except OSError, err:
83            if err.errno == errno.ESRCH: # OS Error 3: No such process
84                return 1
85            # Could be OS Error 1: Operation not permitted
86            return -1, err
87
88    def _finish(self, pid):
89        if not self._stopped(pid):
90            # Try finishing it now
91            try:
92                os.kill(pid, signal.SIGTERM)
93            except OSError, err:
94                return err
95
96    def _get_pid(self):
97        try:
98            return int(open(self._pidfilename, 'r').read())
99        except IOError, err:
100            return err
101
102    def _remove_pidfile(self):
103        try:
104            os.remove(self._pidfilename)
105        except OSError, err:
106            # maybe control file just got deleted
107            return err
108
109    def _daemon_init(self, new_cwd='/', umask=0):
110        pid = os.fork()
111        if pid:
112            # parent terminates
113            sys.exit(0)
114
115        # child 1 continues
116        os.setsid() # Become session leader
117
118        signal.signal(signal.SIGHUP, signal.SIG_IGN)
119        pid = os.fork()
120        if pid:
121            # child 1 terminates
122            sys.exit(0)
123
124        # child 2 continues
125        os.umask(0)
126
127        if new_cwd is not None:
128            os.chdir(new_cwd)
129
130        # Redirect stdin, stdout, and stderr to DEVNULL
131        for fobj in (sys.stdin, sys.stdout, sys.stderr):
132            fobj.close()
133        sys.stdin = open(DEVNULL, 'r')
134        sys.stdout = open(DEVNULL, 'a+')
135        sys.stderr = open(DEVNULL, 'a+')
136
137        # Success!
138
139class WindowsService(win32serviceutil.ServiceFramework):
140    def __init__(self, name):
141        win32serviceutil.ServiceFramework.__init__(self, name)
142        self.hndl_waitstop = win32event.CreateEvent(None, False, False, None)
143
144    def SvcStop(self):
145        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
146        win32event.SetEvent(self.hndl_waitstop)
147
148    def SvcDoRun(self):
149        self.run()
150
151class WindowsServiceController(object):
152    def __init__(self, service_class, service_path=None):
153        self.service_class = service_class
154        self.service_path = service_path
155
156    def running(self):
157        """Return True if the service is still running, False otherwise."""
158        try:
159            status = win32serviceutil.QueryServiceStatus(
160                    self.service_class._svc_name_)
161        except pywintypes.error:
162            # Service is not installed, assuming it is not running
163            return False
164        # status is a _SERVICE_STATUS struct (check msdn for that),
165        # status[1] contains the current state for the given service.
166        if status[1] == win32service.SERVICE_RUNNING:
167            return True
168        return False
169
170    def remove(self):
171        if self.running():
172            self.stop()
173        return win32serviceutil.HandleCommandLine(self.service_class,
174                argv=('', 'remove'))
175
176    def install(self):
177        # Always use the complete path to the module containing
178        # service_class so this doesn't fail when starting from Umit.
179        # This should be equivalent to the service class string that would
180        # be generated if we were running umit_scheduler.py from the command
181        # line.
182        if self.service_path:
183            path = os.path.abspath(self.service_path)
184            svc_class_string = "%s.%s" % (
185                    os.path.splitext(path)[0], self.service_class.__name__)
186        else:
187            svc_class_string = None
188        return win32serviceutil.HandleCommandLine(self.service_class,
189                svc_class_string, argv=('', 'install'))
190
191    def stop(self):
192        return win32serviceutil.HandleCommandLine(self.service_class,
193                argv=('', 'stop'))
194
195    def start(self):
196        # Perform cleanup before starting the service, if necessary
197        if self.running():
198            # Stop the service before continuing
199            self.stop()
200
201        cls = self.service_class
202
203        try:
204            win32serviceutil.QueryServiceStatus(cls._svc_name_)
205        except pywintypes.error:
206            # Service not installed yet, will install it now
207            err = self.install()
208            if err:
209                return err
210
211        return win32serviceutil.HandleCommandLine(cls, argv=('', 'start'))
212
213    def cleanup(self):
214        pass
215
216
217if NT:
218    BaseControl = WindowsServiceController
219else:
220    BaseControl = UNIXDaemon
221
222class BGRunner(BaseControl):
223    def __init__(self, *args, **kwargs):
224        BaseControl.__init__(self, *args, **kwargs)
Note: See TracBrowser for help on using the browser.