JIRA: BOTTLENECKS-41 define the documents of vstf in B release
[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
24     def __init__(self, pidfile, stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):
25         super(Daemon, self).__init__()
26         self.stdin = stdin
27         self.stdout = stdout
28         self.stderr = stderr
29         self.pidfile = pidfile
30
31     def daemonize(self):
32         """
33         do the UNIX double-fork magic, see Stevens' "Advanced 
34         Programming in the UNIX Environment" for details (ISBN 0201563177)
35         http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16
36         """
37         try:
38             pid = os.fork()
39             if pid > 0:
40                 sys.exit(0)
41         except OSError, e:
42             LOG.error("fork #1 failed: %(errno)s, %(strerror)s",
43                       {'errno': e.errno, 'strerror': e.strerror})
44             sys.exit(1)
45
46         # decouple from parent environment
47         os.chdir("/")
48         os.setsid()
49         os.umask(0)
50
51         # do second fork
52         try:
53             pid = os.fork()
54             if pid > 0:
55                 # exit from second parent
56                 sys.exit(0)
57         except OSError, e:
58             LOG.error("fork #1 failed: %(errno)s, %(strerror)s",
59                       {'errno': e.errno, 'strerror': e.strerror})
60             sys.exit(1)
61
62             # redirect standard file descriptors
63         sys.stdout.flush()
64         sys.stderr.flush()
65         si = file(self.stdin, 'r')
66         so = file(self.stdout, 'a+')
67         se = file(self.stderr, 'a+', 0)
68         os.dup2(si.fileno(), sys.stdin.fileno())
69         os.dup2(so.fileno(), sys.stdout.fileno())
70         os.dup2(se.fileno(), sys.stderr.fileno())
71
72         # write pidfile
73         atexit.register(self.delpid)
74         pid = str(os.getpid())
75         file(self.pidfile, 'w+').write("%s\n" % pid)
76
77     def delpid(self):
78         os.remove(self.pidfile)
79
80     def start(self):
81         """
82         Start the daemon
83         """
84
85         # Check for a pidfile to see if the daemon already runs
86         try:
87             pf = file(self.pidfile, 'r')
88             pid = int(pf.read().strip())
89             pf.close()
90         except IOError:
91             pid = None
92
93         if pid:
94             message = "pidfile %s already exist. Daemon already running?\n"
95             sys.stderr.write(message % self.pidfile)
96             sys.exit(1)
97         LOG.info("daemon start to run daemonize")
98         # Start the daemon
99         self.daemonize()
100         self.run()
101
102     def stop(self):
103         """
104         Stop the daemon
105         """
106         # Get the pid from the pidfile
107         try:
108             pf = file(self.pidfile, 'r')
109             pid = int(pf.read().strip())
110             pf.close()
111         except IOError:
112             pid = None
113
114         if not pid:
115             message = "pidfile %s does not exist. Daemon not running?\n"
116             sys.stderr.write(message % self.pidfile)
117             return  # not an error in a restart
118
119         # Try killing the daemon process    
120         try:
121             while 1:
122                 os.kill(pid, SIGTERM)
123                 time.sleep(0.1)
124         except OSError, err:
125             err = str(err)
126             if err.find("No such process") > 0:
127                 if os.path.exists(self.pidfile):
128                     os.remove(self.pidfile)
129             else:
130                 print str(err)
131                 sys.exit(1)
132
133     def restart(self):
134         """
135         Restart the daemon
136         """
137         self.stop()
138         self.start()
139
140     def run(self):
141         """
142         You should override this method when you subclass Daemon. 
143         It will be called after the process has been
144         daemonized by start() or restart().
145         
146         """
147         pass
148
149     def daemon_die(self):
150         """You should override this method when you shutdown daemon
151         this func will be call by stop() before kill the process
152         
153         """
154         pass