ominfra 0.0.0.dev125__py3-none-any.whl → 0.0.0.dev127__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. ominfra/clouds/aws/auth.py +1 -1
  2. ominfra/deploy/_executor.py +1 -1
  3. ominfra/deploy/poly/_main.py +1 -1
  4. ominfra/pyremote/_runcommands.py +1 -1
  5. ominfra/scripts/journald2aws.py +2 -2
  6. ominfra/scripts/supervisor.py +1825 -1217
  7. ominfra/supervisor/collections.py +52 -0
  8. ominfra/supervisor/context.py +2 -336
  9. ominfra/supervisor/datatypes.py +1 -63
  10. ominfra/supervisor/dispatchers.py +22 -338
  11. ominfra/supervisor/dispatchersimpl.py +342 -0
  12. ominfra/supervisor/groups.py +33 -110
  13. ominfra/supervisor/groupsimpl.py +86 -0
  14. ominfra/supervisor/inject.py +45 -13
  15. ominfra/supervisor/main.py +1 -1
  16. ominfra/supervisor/pipes.py +83 -0
  17. ominfra/supervisor/poller.py +6 -3
  18. ominfra/supervisor/privileges.py +65 -0
  19. ominfra/supervisor/processes.py +18 -0
  20. ominfra/supervisor/{process.py → processesimpl.py} +99 -317
  21. ominfra/supervisor/setup.py +38 -0
  22. ominfra/supervisor/setupimpl.py +261 -0
  23. ominfra/supervisor/signals.py +24 -16
  24. ominfra/supervisor/spawning.py +31 -0
  25. ominfra/supervisor/spawningimpl.py +347 -0
  26. ominfra/supervisor/supervisor.py +54 -78
  27. ominfra/supervisor/types.py +122 -39
  28. ominfra/supervisor/users.py +64 -0
  29. {ominfra-0.0.0.dev125.dist-info → ominfra-0.0.0.dev127.dist-info}/METADATA +3 -3
  30. {ominfra-0.0.0.dev125.dist-info → ominfra-0.0.0.dev127.dist-info}/RECORD +34 -23
  31. {ominfra-0.0.0.dev125.dist-info → ominfra-0.0.0.dev127.dist-info}/LICENSE +0 -0
  32. {ominfra-0.0.0.dev125.dist-info → ominfra-0.0.0.dev127.dist-info}/WHEEL +0 -0
  33. {ominfra-0.0.0.dev125.dist-info → ominfra-0.0.0.dev127.dist-info}/entry_points.txt +0 -0
  34. {ominfra-0.0.0.dev125.dist-info → ominfra-0.0.0.dev127.dist-info}/top_level.txt +0 -0
@@ -1,7 +1,6 @@
1
1
  # ruff: noqa: UP006 UP007
2
2
  import errno
3
3
  import os.path
4
- import shlex
5
4
  import signal
6
5
  import time
7
6
  import traceback
@@ -9,40 +8,31 @@ import typing as ta
9
8
 
10
9
  from omlish.lite.check import check_isinstance
11
10
  from omlish.lite.logs import log
11
+ from omlish.lite.typing import Func1
12
12
 
13
13
  from .configs import ProcessConfig
14
- from .context import check_execv_args
15
- from .context import close_child_pipes
16
- from .context import close_parent_pipes
17
- from .context import drop_privileges
18
- from .context import make_pipes
19
14
  from .datatypes import RestartUnconditionally
20
- from .dispatchers import Dispatcher
21
- from .dispatchers import InputDispatcher
22
- from .dispatchers import OutputDispatcher
15
+ from .dispatchers import Dispatchers
23
16
  from .events import PROCESS_STATE_EVENT_MAP
24
17
  from .events import EventCallbacks
25
- from .events import ProcessCommunicationEvent
26
- from .events import ProcessCommunicationStderrEvent
27
- from .events import ProcessCommunicationStdoutEvent
28
- from .exceptions import BadCommandError
29
- from .exceptions import ProcessError
18
+ from .pipes import ProcessPipes
19
+ from .pipes import close_parent_pipes
20
+ from .processes import ProcessStateError
30
21
  from .signals import sig_name
22
+ from .spawning import ProcessSpawnError
23
+ from .spawning import ProcessSpawning
31
24
  from .states import ProcessState
32
25
  from .states import SupervisorState
26
+ from .types import InputDispatcher
33
27
  from .types import Process
34
28
  from .types import ProcessGroup
35
29
  from .types import ServerContext
36
- from .utils import as_bytes
37
30
  from .utils import as_string
38
- from .utils import close_fd
39
- from .utils import compact_traceback
40
31
  from .utils import decode_wait_status
41
- from .utils import get_path
42
- from .utils import real_exit
43
32
 
44
33
 
45
- InheritedFds = ta.NewType('InheritedFds', ta.FrozenSet[int])
34
+ class ProcessSpawningFactory(Func1[Process, ProcessSpawning]):
35
+ pass
46
36
 
47
37
 
48
38
  ##
@@ -58,19 +48,22 @@ class ProcessImpl(Process):
58
48
  *,
59
49
  context: ServerContext,
60
50
  event_callbacks: EventCallbacks,
61
-
62
- inherited_fds: ta.Optional[InheritedFds] = None,
51
+ process_spawning_factory: ProcessSpawningFactory,
63
52
  ) -> None:
64
53
  super().__init__()
65
54
 
66
55
  self._config = config
67
56
  self._group = group
57
+
68
58
  self._context = context
69
59
  self._event_callbacks = event_callbacks
70
- self._inherited_fds = InheritedFds(frozenset(inherited_fds or []))
71
60
 
72
- self._dispatchers: ta.Dict[int, Dispatcher] = {}
73
- self._pipes: ta.Dict[str, int] = {}
61
+ self._spawning = process_spawning_factory(self)
62
+
63
+ #
64
+
65
+ self._dispatchers = Dispatchers([])
66
+ self._pipes = ProcessPipes()
74
67
 
75
68
  self._state = ProcessState.STOPPED
76
69
  self._pid = 0 # 0 when not running
@@ -90,17 +83,30 @@ class ProcessImpl(Process):
90
83
  self._exitstatus: ta.Optional[int] = None # status attached to dead process by finish()
91
84
  self._spawn_err: ta.Optional[str] = None # error message attached by spawn() if any
92
85
 
86
+ #
87
+
88
+ def __repr__(self) -> str:
89
+ return f'<Subprocess at {id(self)} with name {self._config.name} in state {self.get_state().name}>'
90
+
91
+ #
92
+
93
93
  @property
94
- def pid(self) -> int:
95
- return self._pid
94
+ def name(self) -> str:
95
+ return self._config.name
96
+
97
+ @property
98
+ def config(self) -> ProcessConfig:
99
+ return self._config
96
100
 
97
101
  @property
98
102
  def group(self) -> ProcessGroup:
99
103
  return self._group
100
104
 
101
105
  @property
102
- def config(self) -> ProcessConfig:
103
- return self._config
106
+ def pid(self) -> int:
107
+ return self._pid
108
+
109
+ #
104
110
 
105
111
  @property
106
112
  def context(self) -> ServerContext:
@@ -114,33 +120,59 @@ class ProcessImpl(Process):
114
120
  def backoff(self) -> int:
115
121
  return self._backoff
116
122
 
117
- def get_dispatchers(self) -> ta.Mapping[int, Dispatcher]:
118
- return self._dispatchers
123
+ #
124
+
125
+ def spawn(self) -> ta.Optional[int]:
126
+ process_name = as_string(self._config.name)
127
+
128
+ if self.pid:
129
+ log.warning('process \'%s\' already running', process_name)
130
+ return None
119
131
 
120
- def remove_logs(self) -> None:
121
- for dispatcher in self._dispatchers.values():
122
- if hasattr(dispatcher, 'remove_logs'):
123
- dispatcher.remove_logs()
124
-
125
- def reopen_logs(self) -> None:
126
- for dispatcher in self._dispatchers.values():
127
- if hasattr(dispatcher, 'reopen_logs'):
128
- dispatcher.reopen_logs()
129
-
130
- def drain(self) -> None:
131
- for dispatcher in self._dispatchers.values():
132
- # note that we *must* call readable() for every dispatcher, as it may have side effects for a given
133
- # dispatcher (eg. call handle_listener_state_change for event listener processes)
134
- if dispatcher.readable():
135
- dispatcher.handle_read_event()
136
- if dispatcher.writable():
137
- dispatcher.handle_write_event()
132
+ self.check_in_state(
133
+ ProcessState.EXITED,
134
+ ProcessState.FATAL,
135
+ ProcessState.BACKOFF,
136
+ ProcessState.STOPPED,
137
+ )
138
+
139
+ self._killing = False
140
+ self._spawn_err = None
141
+ self._exitstatus = None
142
+ self._system_stop = False
143
+ self._administrative_stop = False
144
+
145
+ self._last_start = time.time()
146
+
147
+ self.change_state(ProcessState.STARTING)
148
+
149
+ try:
150
+ sp = self._spawning.spawn()
151
+ except ProcessSpawnError as err:
152
+ log.exception('Spawn error')
153
+ self._spawn_err = err.args[0]
154
+ self.check_in_state(ProcessState.STARTING)
155
+ self.change_state(ProcessState.BACKOFF)
156
+ return None
157
+
158
+ log.info("Spawned: '%s' with pid %s", self.name, sp.pid)
159
+
160
+ self._pid = sp.pid
161
+ self._pipes = sp.pipes
162
+ self._dispatchers = sp.dispatchers
163
+
164
+ self._delay = time.time() + self.config.startsecs
165
+
166
+ return sp.pid
167
+
168
+ def get_dispatchers(self) -> Dispatchers:
169
+ return self._dispatchers
138
170
 
139
171
  def write(self, chars: ta.Union[bytes, str]) -> None:
140
172
  if not self.pid or self._killing:
141
173
  raise OSError(errno.EPIPE, 'Process already closed')
142
174
 
143
- stdin_fd = self._pipes['stdin']
175
+ stdin_fd = self._pipes.stdin
144
176
  if stdin_fd is None:
145
177
  raise OSError(errno.EPIPE, 'Process has no stdin channel')
146
178
 
@@ -151,51 +183,7 @@ class ProcessImpl(Process):
151
183
  dispatcher.write(chars)
152
184
  dispatcher.flush() # this must raise EPIPE if the pipe is closed
153
185
 
154
- def _get_execv_args(self) -> ta.Tuple[str, ta.Sequence[str]]:
155
- """
156
- Internal: turn a program name into a file name, using $PATH, make sure it exists / is executable, raising a
157
- ProcessError if not
158
- """
159
-
160
- try:
161
- commandargs = shlex.split(self._config.command)
162
- except ValueError as e:
163
- raise BadCommandError(f"can't parse command {self._config.command!r}: {e}") # noqa
164
-
165
- if commandargs:
166
- program = commandargs[0]
167
- else:
168
- raise BadCommandError('command is empty')
169
-
170
- if '/' in program:
171
- filename = program
172
- try:
173
- st = os.stat(filename)
174
- except OSError:
175
- st = None
176
-
177
- else:
178
- path = get_path()
179
- found = None
180
- st = None
181
- for dir in path: # noqa
182
- found = os.path.join(dir, program)
183
- try:
184
- st = os.stat(found)
185
- except OSError:
186
- pass
187
- else:
188
- break
189
- if st is None:
190
- filename = program
191
- else:
192
- filename = found # type: ignore
193
-
194
- # check_execv_args will raise a ProcessError if the execv args are bogus, we break it out into a separate
195
- # options method call here only to service unit tests
196
- check_execv_args(filename, commandargs, st)
197
-
198
- return filename, commandargs
186
+ #
199
187
 
200
188
  def change_state(self, new_state: ProcessState, expected: bool = True) -> bool:
201
189
  old_state = self._state
@@ -215,209 +203,14 @@ class ProcessImpl(Process):
215
203
 
216
204
  return True
217
205
 
218
- def _check_in_state(self, *states: ProcessState) -> None:
206
+ def check_in_state(self, *states: ProcessState) -> None:
219
207
  if self._state not in states:
220
- current_state = self._state.name
221
- allowable_states = ' '.join(s.name for s in states)
222
- process_name = as_string(self._config.name)
223
- raise RuntimeError('Assertion failed for %s: %s not in %s' % (process_name, current_state, allowable_states)) # noqa
224
-
225
- def _record_spawn_err(self, msg: str) -> None:
226
- self._spawn_err = msg
227
- log.info('_spawn_err: %s', msg)
228
-
229
- def spawn(self) -> ta.Optional[int]:
230
- process_name = as_string(self._config.name)
231
-
232
- if self.pid:
233
- log.warning('process \'%s\' already running', process_name)
234
- return None
235
-
236
- self._killing = False
237
- self._spawn_err = None
238
- self._exitstatus = None
239
- self._system_stop = False
240
- self._administrative_stop = False
241
-
242
- self._last_start = time.time()
243
-
244
- self._check_in_state(
245
- ProcessState.EXITED,
246
- ProcessState.FATAL,
247
- ProcessState.BACKOFF,
248
- ProcessState.STOPPED,
249
- )
250
-
251
- self.change_state(ProcessState.STARTING)
252
-
253
- try:
254
- filename, argv = self._get_execv_args()
255
- except ProcessError as what:
256
- self._record_spawn_err(what.args[0])
257
- self._check_in_state(ProcessState.STARTING)
258
- self.change_state(ProcessState.BACKOFF)
259
- return None
260
-
261
- try:
262
- self._dispatchers, self._pipes = self._make_dispatchers() # type: ignore
263
- except OSError as why:
264
- code = why.args[0]
265
- if code == errno.EMFILE:
266
- # too many file descriptors open
267
- msg = f"too many open files to spawn '{process_name}'"
268
- else:
269
- msg = f"unknown error making dispatchers for '{process_name}': {errno.errorcode.get(code, code)}"
270
- self._record_spawn_err(msg)
271
- self._check_in_state(ProcessState.STARTING)
272
- self.change_state(ProcessState.BACKOFF)
273
- return None
274
-
275
- try:
276
- pid = os.fork()
277
- except OSError as why:
278
- code = why.args[0]
279
- if code == errno.EAGAIN:
280
- # process table full
281
- msg = f'Too many processes in process table to spawn \'{process_name}\''
282
- else:
283
- msg = f'unknown error during fork for \'{process_name}\': {errno.errorcode.get(code, code)}'
284
- self._record_spawn_err(msg)
285
- self._check_in_state(ProcessState.STARTING)
286
- self.change_state(ProcessState.BACKOFF)
287
- close_parent_pipes(self._pipes)
288
- close_child_pipes(self._pipes)
289
- return None
290
-
291
- if pid != 0:
292
- return self._spawn_as_parent(pid)
293
-
294
- else:
295
- self._spawn_as_child(filename, argv)
296
- return None
297
-
298
- def _make_dispatchers(self) -> ta.Tuple[ta.Mapping[int, Dispatcher], ta.Mapping[str, int]]:
299
- use_stderr = not self._config.redirect_stderr
300
-
301
- p = make_pipes(use_stderr)
302
- stdout_fd, stderr_fd, stdin_fd = p['stdout'], p['stderr'], p['stdin']
303
-
304
- dispatchers: ta.Dict[int, Dispatcher] = {}
305
-
306
- dispatcher_kw = dict(
307
- event_callbacks=self._event_callbacks,
308
- )
309
-
310
- etype: ta.Type[ProcessCommunicationEvent]
311
- if stdout_fd is not None:
312
- etype = ProcessCommunicationStdoutEvent
313
- dispatchers[stdout_fd] = OutputDispatcher(
314
- self,
315
- etype,
316
- stdout_fd,
317
- **dispatcher_kw,
208
+ raise ProcessStateError(
209
+ f'Check failed for {self._config.name}: '
210
+ f'{self._state.name} not in {" ".join(s.name for s in states)}',
318
211
  )
319
212
 
320
- if stderr_fd is not None:
321
- etype = ProcessCommunicationStderrEvent
322
- dispatchers[stderr_fd] = OutputDispatcher(
323
- self,
324
- etype,
325
- stderr_fd,
326
- **dispatcher_kw,
327
- )
328
-
329
- if stdin_fd is not None:
330
- dispatchers[stdin_fd] = InputDispatcher(
331
- self,
332
- 'stdin',
333
- stdin_fd,
334
- **dispatcher_kw,
335
- )
336
-
337
- return dispatchers, p
338
-
339
- def _spawn_as_parent(self, pid: int) -> int:
340
- # Parent
341
- self._pid = pid
342
- close_child_pipes(self._pipes)
343
- log.info('spawned: \'%s\' with pid %s', as_string(self._config.name), pid)
344
- self._spawn_err = None
345
- self._delay = time.time() + self._config.startsecs
346
- self.context.pid_history[pid] = self
347
- return pid
348
-
349
- def _prepare_child_fds(self) -> None:
350
- os.dup2(self._pipes['child_stdin'], 0)
351
- os.dup2(self._pipes['child_stdout'], 1)
352
- if self._config.redirect_stderr:
353
- os.dup2(self._pipes['child_stdout'], 2)
354
- else:
355
- os.dup2(self._pipes['child_stderr'], 2)
356
-
357
- for i in range(3, self.context.config.minfds):
358
- if i in self._inherited_fds:
359
- continue
360
- close_fd(i)
361
-
362
- def _spawn_as_child(self, filename: str, argv: ta.Sequence[str]) -> None:
363
- try:
364
- # prevent child from receiving signals sent to the parent by calling os.setpgrp to create a new process
365
- # group for the child; this prevents, for instance, the case of child processes being sent a SIGINT when
366
- # running supervisor in foreground mode and Ctrl-C in the terminal window running supervisord is pressed.
367
- # Presumably it also prevents HUP, etc received by supervisord from being sent to children.
368
- os.setpgrp()
369
-
370
- self._prepare_child_fds()
371
- # sending to fd 2 will put this output in the stderr log
372
-
373
- # set user
374
- setuid_msg = self.set_uid()
375
- if setuid_msg:
376
- uid = self._config.uid
377
- msg = f"couldn't setuid to {uid}: {setuid_msg}\n"
378
- os.write(2, as_bytes('supervisor: ' + msg))
379
- return # finally clause will exit the child process
380
-
381
- # set environment
382
- env = os.environ.copy()
383
- env['SUPERVISOR_ENABLED'] = '1'
384
- env['SUPERVISOR_PROCESS_NAME'] = self._config.name
385
- if self._group:
386
- env['SUPERVISOR_GROUP_NAME'] = self._group.config.name
387
- if self._config.environment is not None:
388
- env.update(self._config.environment)
389
-
390
- # change directory
391
- cwd = self._config.directory
392
- try:
393
- if cwd is not None:
394
- os.chdir(os.path.expanduser(cwd))
395
- except OSError as why:
396
- code = errno.errorcode.get(why.args[0], why.args[0])
397
- msg = f"couldn't chdir to {cwd}: {code}\n"
398
- os.write(2, as_bytes('supervisor: ' + msg))
399
- return # finally clause will exit the child process
400
-
401
- # set umask, then execve
402
- try:
403
- if self._config.umask is not None:
404
- os.umask(self._config.umask)
405
- os.execve(filename, list(argv), env)
406
- except OSError as why:
407
- code = errno.errorcode.get(why.args[0], why.args[0])
408
- msg = f"couldn't exec {argv[0]}: {code}\n"
409
- os.write(2, as_bytes('supervisor: ' + msg))
410
- except Exception: # noqa
411
- (file, fun, line), t, v, tbinfo = compact_traceback()
412
- error = f'{t}, {v}: file: {file} line: {line}'
413
- msg = f"couldn't exec {filename}: {error}\n"
414
- os.write(2, as_bytes('supervisor: ' + msg))
415
-
416
- # this point should only be reached if execve failed. the finally clause will exit the child process.
417
-
418
- finally:
419
- os.write(2, as_bytes('supervisor: child process was not spawned\n'))
420
- real_exit(127) # exit process with code for spawn failure
213
+ #
421
214
 
422
215
  def _check_and_adjust_for_system_clock_rollback(self, test_time):
423
216
  """
@@ -463,7 +256,7 @@ class ProcessImpl(Process):
463
256
  self._delay = 0
464
257
  self._backoff = 0
465
258
  self._system_stop = True
466
- self._check_in_state(ProcessState.BACKOFF)
259
+ self.check_in_state(ProcessState.BACKOFF)
467
260
  self.change_state(ProcessState.FATAL)
468
261
 
469
262
  def kill(self, sig: int) -> ta.Optional[str]:
@@ -507,7 +300,7 @@ class ProcessImpl(Process):
507
300
  self._killing = True
508
301
  self._delay = now + self._config.stopwaitsecs
509
302
  # we will already be in the STOPPING state if we're doing a SIGKILL as a result of overrunning stopwaitsecs
510
- self._check_in_state(ProcessState.RUNNING, ProcessState.STARTING, ProcessState.STOPPING)
303
+ self.check_in_state(ProcessState.RUNNING, ProcessState.STARTING, ProcessState.STOPPING)
511
304
  self.change_state(ProcessState.STOPPING)
512
305
 
513
306
  pid = self.pid
@@ -552,7 +345,7 @@ class ProcessImpl(Process):
552
345
 
553
346
  log.debug('sending %s (pid %s) sig %s', process_name, self.pid, sig_name(sig))
554
347
 
555
- self._check_in_state(ProcessState.RUNNING, ProcessState.STARTING, ProcessState.STOPPING)
348
+ self.check_in_state(ProcessState.RUNNING, ProcessState.STARTING, ProcessState.STOPPING)
556
349
 
557
350
  try:
558
351
  try:
@@ -581,7 +374,7 @@ class ProcessImpl(Process):
581
374
  def finish(self, sts: int) -> None:
582
375
  """The process was reaped and we need to report and manage its state."""
583
376
 
584
- self.drain()
377
+ self._dispatchers.drain()
585
378
 
586
379
  es, msg = decode_wait_status(sts)
587
380
 
@@ -612,7 +405,7 @@ class ProcessImpl(Process):
612
405
  self._exitstatus = es
613
406
 
614
407
  fmt, args = 'stopped: %s (%s)', (process_name, msg)
615
- self._check_in_state(ProcessState.STOPPING)
408
+ self.check_in_state(ProcessState.STOPPING)
616
409
  self.change_state(ProcessState.STOPPED)
617
410
  if exit_expected:
618
411
  log.info(fmt, *args)
@@ -623,7 +416,7 @@ class ProcessImpl(Process):
623
416
  # the program did not stay up long enough to make it to RUNNING implies STARTING -> BACKOFF
624
417
  self._exitstatus = None
625
418
  self._spawn_err = 'Exited too quickly (process log may have details)'
626
- self._check_in_state(ProcessState.STARTING)
419
+ self.check_in_state(ProcessState.STARTING)
627
420
  self.change_state(ProcessState.BACKOFF)
628
421
  log.warning('exited: %s (%s)', process_name, msg + '; not expected')
629
422
 
@@ -639,7 +432,7 @@ class ProcessImpl(Process):
639
432
  if self._state == ProcessState.STARTING:
640
433
  self.change_state(ProcessState.RUNNING)
641
434
 
642
- self._check_in_state(ProcessState.RUNNING)
435
+ self.check_in_state(ProcessState.RUNNING)
643
436
 
644
437
  if exit_expected:
645
438
  # expected exit code
@@ -653,19 +446,8 @@ class ProcessImpl(Process):
653
446
 
654
447
  self._pid = 0
655
448
  close_parent_pipes(self._pipes)
656
- self._pipes = {}
657
- self._dispatchers = {}
658
-
659
- def set_uid(self) -> ta.Optional[str]:
660
- if self._config.uid is None:
661
- return None
662
- msg = drop_privileges(self._config.uid)
663
- return msg
664
-
665
- def __repr__(self) -> str:
666
- # repr can't return anything other than a native string, but the name might be unicode - a problem on Python 2.
667
- name = self._config.name
668
- return f'<Subprocess at {id(self)} with name {name} in state {self.get_state().name}>'
449
+ self._pipes = ProcessPipes()
450
+ self._dispatchers = Dispatchers([])
669
451
 
670
452
  def get_state(self) -> ProcessState:
671
453
  return self._state
@@ -707,7 +489,7 @@ class ProcessImpl(Process):
707
489
  # proc.config.startsecs,
708
490
  self._delay = 0
709
491
  self._backoff = 0
710
- self._check_in_state(ProcessState.STARTING)
492
+ self.check_in_state(ProcessState.STARTING)
711
493
  self.change_state(ProcessState.RUNNING)
712
494
  msg = ('entered RUNNING state, process has stayed up for > than %s seconds (startsecs)' % self._config.startsecs) # noqa
713
495
  logger.info('success: %s %s', process_name, msg)
@@ -727,7 +509,7 @@ class ProcessImpl(Process):
727
509
  log.warning('killing \'%s\' (%s) with SIGKILL', process_name, self.pid)
728
510
  self.kill(signal.SIGKILL)
729
511
 
730
- def create_auto_child_logs(self) -> None:
512
+ def after_setuid(self) -> None:
731
513
  # temporary logfiles which are erased at start time
732
514
  # get_autoname = self.context.get_auto_child_log_name # noqa
733
515
  # sid = self.context.config.identifier # noqa
@@ -0,0 +1,38 @@
1
+ # ruff: noqa: UP006 UP007
2
+ import abc
3
+ import typing as ta
4
+
5
+ from .users import User
6
+
7
+
8
+ ##
9
+
10
+
11
+ SupervisorUser = ta.NewType('SupervisorUser', User)
12
+
13
+
14
+ ##
15
+
16
+
17
+ class DaemonizeListener(abc.ABC): # noqa
18
+ def before_daemonize(self) -> None: # noqa
19
+ pass
20
+
21
+ def after_daemonize(self) -> None: # noqa
22
+ pass
23
+
24
+
25
+ DaemonizeListeners = ta.NewType('DaemonizeListeners', ta.Sequence[DaemonizeListener])
26
+
27
+
28
+ ##
29
+
30
+
31
+ class SupervisorSetup(abc.ABC):
32
+ @abc.abstractmethod
33
+ def setup(self) -> None:
34
+ raise NotImplementedError
35
+
36
+ @abc.abstractmethod
37
+ def cleanup(self) -> None:
38
+ raise NotImplementedError