scripts/mycroft/lock/__init__.py
Go to the documentation of this file.
1 # Copyright 2017 Mycroft AI Inc.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 #
15 from signal import getsignal, signal, SIGKILL, SIGINT, SIGTERM, \
16  SIG_DFL, default_int_handler, SIG_IGN # signals
17 
18 import os # Operating System functions
19 
20 
21 #
22 # Wrapper around chain of handler functions for a specific system level signal.
23 # Often used to trap Ctrl-C for specific application purposes.
24 from mycroft.util import LOG
25 
26 
27 class Signal: # python 3+ class Signal
28 
29  """
30  Capture and replace a signal handler with a user supplied function.
31  The user supplied function is always called first then the previous
32  handler, if it exists, will be called. It is possible to chain several
33  signal handlers together by creating multiply instances of objects of
34  this class, providing a different user functions for each instance. All
35  provided user functions will be called in LIFO order.
36  """
37 
38  #
39  # Constructor
40  # Get the previous handler function then set the passed function
41  # as the new handler function for this signal
42 
43  def __init__(self, sig_value, func):
44  """
45  Create an instance of the signal handler class.
46 
47  sig_value: The ID value of the signal to be captured.
48  func: User supplied function that will act as the new signal handler.
49  """
50  super(Signal, self).__init__() # python 3+ 'super().__init__()
51  self.__sig_value = sig_value
52  self.__user_func = func # store user passed function
53  self.__previous_func = signal(sig_value, self)
54  self.__previous_func = { # Convert signal codes to functions
55  SIG_DFL: default_int_handler,
56  SIG_IGN: lambda a, b: None
57  }.get(self.__previous_func, self.__previous_func)
58 
59  #
60  # Called to handle the passed signal
61  def __call__(self, signame, sf):
62  """
63  Allows the instance of this class to be called as a function.
64  When called it runs the user supplied signal handler than
65  checks to see if there is a previously defined handler. If
66  there is a previously defined handler call it.
67  """
68  self.__user_func()
69  self.__previous_func(signame, sf)
70 
71  #
72  # reset the signal handler
73  def __del__(self):
74  """
75  Class destructor. Called during garbage collection.
76  Resets the signal handler to the previous function.
77  """
78  signal(self.__sig_value, self.__previous_func)
79 
80  # End class Signal
81 
82 
83 # ----------------------------------------------------------------------------
84 # ----------------------------------------------------------------------------
85 
86 
87 #
88 # Create, delete and manipulate a PID file for this service
89 # ------------------------------------------------------------------------------
90 class Lock: # python 3+ 'class Lock'
91 
92  """
93  Create and maintains the PID lock file for this application process.
94  The PID lock file is located in /tmp/mycroft/*.pid. If another process
95  of the same type is started, this class will 'attempt' to stop the
96  previously running process and then change the process ID in the lock file.
97  """
98 
99  #
100  # Class constants
101  DIRECTORY = '/tmp/mycroft'
102  FILE = '/{}.pid'
103 
104  #
105  # Constructor
106  def __init__(self, service):
107  """
108  Builds the instance of this object. Holds the lock until the
109  object is garbage collected.
110 
111  service: Text string. The name of the service application
112  to be locked (ie: skills, voice)
113  """
114  super(Lock, self).__init__() # python 3+ 'super().__init__()'
115  self.__pid = os.getpid() # PID of this application
116  self.path = Lock.DIRECTORY + Lock.FILE.format(service)
117  self.set_handlers() # set signal handlers
118  self.create()
119 
120  #
121  # Reset the signal handlers to the 'delete' function
122  def set_handlers(self):
123  """
124  Trap both SIGINT and SIGTERM to gracefully clean up PID files
125  """
126  self.__handlers = {SIGINT: Signal(SIGINT, self.delete),
127  SIGTERM: Signal(SIGTERM, self.delete)}
128 
129  #
130  # Check to see if the PID already exists
131  # If it does exits perform several things:
132  # Stop the current process
133  # Delete the exiting file
134  def exists(self):
135  """
136  Check to see if the PID lock file currently exists. If it does
137  than send a SIGTERM signal to the process defined by the value
138  in the lock file. Catch the keyboard interrupt exception to
139  prevent propagation if stopped by use of Ctrl-C.
140  """
141  if not os.path.isfile(self.path):
142  return
143  with open(self.path, 'r') as L:
144  try:
145  os.kill(int(L.read()), SIGKILL)
146  except Exception as E:
147  pass
148 
149  #
150  # Create a lock file for this server process
151  def touch(self):
152  """
153  If needed, create the '/tmp/mycroft' directory than open the
154  lock file for writting and store the current process ID (PID)
155  as text.
156  """
157  if not os.path.exists(Lock.DIRECTORY):
158  os.makedirs(Lock.DIRECTORY)
159  with open(self.path, 'w') as L:
160  L.write('{}'.format(self.__pid))
161 
162  #
163  # Create the PID file
164  def create(self):
165  """
166  Checks to see if a lock file for this service already exists,
167  if so have it killed. In either case write the process ID of
168  the current service process to to the existing or newly created
169  lock file in /tmp/mycroft/
170  """
171  self.exists() # check for current running process
172  self.touch()
173 
174  #
175  # Delete the PID file - but only if it has not been overwritten
176  # by a duplicate service application
177  def delete(self, *args):
178  """
179  If the PID lock file contains the PID of this process delete it.
180 
181  *args: Ignored. Required as this fuction is called as a signel
182  handler.
183  """
184  try:
185  with open(self.path, 'r') as L:
186  pid = int(L.read())
187  if self.__pid == pid:
188  os.unlink(self.path)
189  except IOError:
190  pass
191  # End class Lock
def __call__(self, signame, sf)
def __init__(self, sig_value, func)
def get(phrase, lang=None, context=None)


mycroft_ros
Author(s):
autogenerated on Mon Apr 26 2021 02:35:40