55import shlex
66import time
77import traceback
8+ import re
89
910from supervisor .compat import maxint
1011from supervisor .compat import as_bytes
2223from supervisor .options import decode_wait_status
2324from supervisor .options import signame
2425from supervisor .options import ProcessException , BadCommand
26+ from supervisor .options import readFile
2527
2628from supervisor .dispatchers import EventListenerStates
2729
@@ -197,79 +199,97 @@ def record_spawnerr(self, msg):
197199 self .spawnerr = msg
198200 self .config .options .logger .info ("spawnerr: %s" % msg )
199201
200- def spawn (self ):
202+ def queue_all_dependee_processes (self , supervisor ):
203+ if self .config .name not in supervisor .process_spawn_set :
204+ supervisor .process_spawn_queue .append (self )
205+ supervisor .process_spawn_set .add (self .config .name )
206+ # all dependees that are not in queue and not in STARTING need to be added to queue.
207+ if self .config .depends_on is not None :
208+ for dependee in self .config .depends_on .values ():
209+ if dependee .state is not ProcessStates .RUNNING :
210+ ready_to_be_spawned = False
211+ if dependee .state is not ProcessStates .STARTING :
212+ if dependee .config .name not in supervisor .process_spawn_set :
213+ supervisor .process_spawn_queue .append (dependee )
214+ supervisor .process_spawn_set .add (dependee .config .name )
215+ dependee .queue_all_dependee_processes (supervisor )
216+
217+
218+ def spawn (self , supervisor = None ):
201219 """Start the subprocess. It must not be running already.
202220
203221 Return the process id. If the fork() call fails, return None.
204222 """
223+ # spawn if all dependees are running - else add to queue if not in queue already
224+ ready_to_be_spawned = True
205225
206- options = self .config .options
207- processname = as_string (self .config .name )
226+ options = self .config .options
227+ processname = as_string (self .config .name )
208228
209- if self .pid :
210- msg = 'process \' %s\' already running' % processname
211- options .logger .warn (msg )
212- return
229+ if self .pid :
230+ msg = 'process \' %s\' already running' % processname
231+ options .logger .warn (msg )
232+ return
213233
214- self .killing = False
215- self .spawnerr = None
216- self .exitstatus = None
217- self .system_stop = False
218- self .administrative_stop = False
234+ self .killing = False
235+ self .spawnerr = None
236+ self .exitstatus = None
237+ self .system_stop = False
238+ self .administrative_stop = False
219239
220- self .laststart = time .time ()
240+ self .laststart = time .time ()
221241
222- self ._assertInState (ProcessStates .EXITED , ProcessStates .FATAL ,
223- ProcessStates .BACKOFF , ProcessStates .STOPPED )
242+ self ._assertInState (ProcessStates .EXITED , ProcessStates .FATAL ,
243+ ProcessStates .BACKOFF , ProcessStates .STOPPED )
224244
225- self .change_state (ProcessStates .STARTING )
245+ self .change_state (ProcessStates .STARTING )
226246
227- try :
228- filename , argv = self .get_execv_args ()
229- except ProcessException as what :
230- self .record_spawnerr (what .args [0 ])
231- self ._assertInState (ProcessStates .STARTING )
232- self .change_state (ProcessStates .BACKOFF )
233- return
247+ try :
248+ filename , argv = self .get_execv_args ()
249+ except ProcessException as what :
250+ self .record_spawnerr (what .args [0 ])
251+ self ._assertInState (ProcessStates .STARTING )
252+ self .change_state (ProcessStates .BACKOFF )
253+ return
234254
235- try :
236- self .dispatchers , self .pipes = self .config .make_dispatchers (self )
237- except (OSError , IOError ) as why :
238- code = why .args [0 ]
239- if code == errno .EMFILE :
240- # too many file descriptors open
241- msg = 'too many open files to spawn \' %s\' ' % processname
242- else :
243- msg = 'unknown error making dispatchers for \' %s\' : %s' % (
244- processname , errno .errorcode .get (code , code ))
245- self .record_spawnerr (msg )
246- self ._assertInState (ProcessStates .STARTING )
247- self .change_state (ProcessStates .BACKOFF )
248- return
255+ try :
256+ self .dispatchers , self .pipes = self .config .make_dispatchers (self )
257+ except (OSError , IOError ) as why :
258+ code = why .args [0 ]
259+ if code == errno .EMFILE :
260+ # too many file descriptors open
261+ msg = 'too many open files to spawn \' %s\' ' % processname
262+ else :
263+ msg = 'unknown error making dispatchers for \' %s\' : %s' % (
264+ processname , errno .errorcode .get (code , code ))
265+ self .record_spawnerr (msg )
266+ self ._assertInState (ProcessStates .STARTING )
267+ self .change_state (ProcessStates .BACKOFF )
268+ return
249269
250- try :
251- pid = options .fork ()
252- except OSError as why :
253- code = why .args [0 ]
254- if code == errno .EAGAIN :
255- # process table full
256- msg = ('Too many processes in process table to spawn \' %s\' ' %
257- processname )
258- else :
259- msg = 'unknown error during fork for \' %s\' : %s' % (
260- processname , errno .errorcode .get (code , code ))
261- self .record_spawnerr (msg )
262- self ._assertInState (ProcessStates .STARTING )
263- self .change_state (ProcessStates .BACKOFF )
264- options .close_parent_pipes (self .pipes )
265- options .close_child_pipes (self .pipes )
266- return
270+ try :
271+ pid = options .fork ()
272+ except OSError as why :
273+ code = why .args [0 ]
274+ if code == errno .EAGAIN :
275+ # process table full
276+ msg = ('Too many processes in process table to spawn \' %s\' ' %
277+ processname )
278+ else :
279+ msg = 'unknown error during fork for \' %s\' : %s' % (
280+ processname , errno .errorcode .get (code , code ))
281+ self .record_spawnerr (msg )
282+ self ._assertInState (ProcessStates .STARTING )
283+ self .change_state (ProcessStates .BACKOFF )
284+ options .close_parent_pipes (self .pipes )
285+ options .close_child_pipes (self .pipes )
286+ return
267287
268- if pid != 0 :
269- return self ._spawn_as_parent (pid )
288+ if pid != 0 :
289+ return self ._spawn_as_parent (pid )
270290
271- else :
272- return self ._spawn_as_child (filename , argv )
291+ else :
292+ return self ._spawn_as_child (filename , argv )
273293
274294 def _spawn_as_parent (self , pid ):
275295 # Parent
@@ -671,11 +691,11 @@ def transition(self, supervisord_instance=None):
671691 if self .config .autorestart :
672692 if self .config .autorestart is RestartUnconditionally :
673693 # EXITED -> STARTING
674- self .spawn ()
694+ self .spawn (supervisor )
675695 else : # autorestart is RestartWhenExitUnexpected
676696 if self .exitstatus not in self .config .exitcodes :
677697 # EXITED -> STARTING
678- self .spawn ()
698+ self .spawn (supervisor )
679699 elif state == ProcessStates .STOPPED and not self .laststart :
680700 if self .config .autostart :
681701 # STOPPED -> STARTING
@@ -691,11 +711,11 @@ def transition(self, supervisord_instance=None):
691711 if self .backoff <= self .config .startretries :
692712 if now > self .delay :
693713 # BACKOFF -> STARTING
694- self .spawn ()
714+ self .spawn (supervisor )
695715
696716 processname = as_string (self .config .name )
697717 if state == ProcessStates .STARTING :
698- if now - self .laststart > self .config .startsecs :
718+ if now - self .laststart > self .config .startsecs and not self . config . runningregex :
699719 # STARTING -> RUNNING if the proc has started
700720 # successfully and it has stayed up for at least
701721 # proc.config.startsecs,
@@ -708,6 +728,27 @@ def transition(self, supervisord_instance=None):
708728 '> than %s seconds (startsecs)' % self .config .startsecs )
709729 logger .info ('success: %s %s' % (processname , msg ))
710730
731+ if self .config .runningregex :
732+ logfile = getattr (self .config , 'stdout_logfile' )
733+ logfile_as_str = as_string (readFile (logfile , self .log_offset , 0 ))
734+
735+ # delete ascii escape sequence and newlines with regular expression
736+ ansi_escape = re .compile (r'(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]|\n' )
737+ logfile_as_str = ansi_escape .sub ('' , logfile_as_str )
738+
739+ # STARTING -> RUNNING if the process has started
740+ # successfully and the runningregex is met
741+ if self .config .runningregex .match (logfile_as_str ):
742+ self .delay = 0
743+ self .backoff = 0
744+ self ._assertInState (ProcessStates .STARTING )
745+ self .change_state (ProcessStates .RUNNING )
746+ msg = ('entered RUNNING state, found runningregex in stdout' )
747+ logger .info ('success: %s %s' % (processname , msg ))
748+
749+
750+
751+
711752 if state == ProcessStates .BACKOFF :
712753 if self .backoff > self .config .startretries :
713754 # BACKOFF -> FATAL if the proc has exceeded its number
0 commit comments