JIRA: BOTTLENECKS-29
[bottlenecks.git] / vstf / vstf / common / daemon.py
1 ##############################################################################
2 # Copyright (c) 2015 Huawei Technologies Co.,Ltd and others.
3 #
4 # All rights reserved. This program and the accompanying materials
5 # are made available under the terms of the Apache License, Version 2.0
6 # which accompanies this distribution, and is available at
7 # http://www.apache.org/licenses/LICENSE-2.0
8 ##############################################################################
9
10 import sys, os, time, atexit
11 import logging
12 from signal import SIGTERM
13
14 LOG = logging.getLogger(__name__)
15
16
17 class Daemon(object):
18     """
19     A generic daemon class.
20     
21     Usage: subclass the Daemon class and override the run() method
22     """
23     def __init__(self, pidfile, stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):
24         super(Daemon, self).__init__()
25         self.stdin = stdin
26         self.stdout = stdout
27         self.stderr = stderr
28         self.pidfile = pidfile
29     
30     def daemonize(self):
31         """
32         do the UNIX double-fork magic, see Stevens' "Advanced 
33         Programming in the UNIX Environment" for details (ISBN 0201563177)
34         http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16
35         """
36         try: 
37             pid = os.fork() 
38             if pid > 0:
39                 sys.exit(0) 
40         except OSError, e: 
41             LOG.error("fork #1 failed: %(errno)s, %(strerror)s",
42                       {'errno':e.errno, 'strerror': e.strerror})
43             sys.exit(1)
44     
45         # decouple from parent environment
46         os.chdir("/") 
47         os.setsid() 
48         os.umask(0) 
49     
50         # do second fork
51         try: 
52             pid = os.fork() 
53             if pid > 0:
54                 # exit from second parent
55                 sys.exit(0) 
56         except OSError, e: 
57             LOG.error("fork #1 failed: %(errno)s, %(strerror)s",
58                       {'errno':e.errno, 'strerror': e.strerror})
59             sys.exit(1) 
60     
61         # redirect standard file descriptors
62         sys.stdout.flush()
63         sys.stderr.flush()
64         si = file(self.stdin, 'r')
65         so = file(self.stdout, 'a+')
66         se = file(self.stderr, 'a+', 0)
67         os.dup2(si.fileno(), sys.stdin.fileno())
68         os.dup2(so.fileno(), sys.stdout.fileno())
69         os.dup2(se.fileno(), sys.stderr.fileno())
70     
71         # write pidfile
72         atexit.register(self.delpid)
73         pid = str(os.getpid())
74         file(self.pidfile,'w+').write("%s\n" % pid)
75     
76     def delpid(self):
77         os.remove(self.pidfile)
78
79     def start(self):
80         """
81         Start the daemon
82         """
83         
84         # Check for a pidfile to see if the daemon already runs
85         try:
86             pf = file(self.pidfile,'r')
87             pid = int(pf.read().strip())
88             pf.close()
89         except IOError:
90             pid = None
91     
92         if pid:
93             message = "pidfile %s already exist. Daemon already running?\n"
94             sys.stderr.write(message % self.pidfile)
95             sys.exit(1)
96         LOG.info("daemon start to run daemonize")
97         # Start the daemon
98         self.daemonize()
99         self.run()
100
101     def stop(self):
102         """
103         Stop the daemon
104         """
105         # Get the pid from the pidfile
106         try:
107             pf = file(self.pidfile,'r')
108             pid = int(pf.read().strip())
109             pf.close()
110         except IOError:
111             pid = None
112     
113         if not pid:
114             message = "pidfile %s does not exist. Daemon not running?\n"
115             sys.stderr.write(message % self.pidfile)
116             return # not an error in a restart
117
118         # Try killing the daemon process    
119         try:
120             while 1:
121                 os.kill(pid, SIGTERM)
122                 time.sleep(0.1)
123         except OSError, err:
124             err = str(err)
125             if err.find("No such process") > 0:
126                 if os.path.exists(self.pidfile):
127                     os.remove(self.pidfile)
128             else:
129                 print str(err)
130                 sys.exit(1)
131
132     def restart(self):
133         """
134         Restart the daemon
135         """
136         self.stop()
137         self.start()
138
139     def run(self):
140         """
141         You should override this method when you subclass Daemon. 
142         It will be called after the process has been
143         daemonized by start() or restart().
144         
145         """
146         pass
147         
148     def daemon_die(self):
149         """You should this method when you shutdown daemon
150         this func will be call by stop() before kill the process
151         
152         """
153         pass