5 from .common import ST_ACTIVE, ST_TRANSITION, OnoffDevice
6 from .gobject import spawn_child, ScheduledFunction
8 logger = logging.getLogger("onoff.process")
10 class OnoffProcess(OnoffDevice):
11 """A device that in activated by starting a process and deactivated by
14 @type pid: int or None
15 @ivar pid: is either None if there is no process or the pid of the
17 @ivar starting: is either None or a ScheduledFunction representing the
18 callback signalling the end of the activation transition.
20 @ivar killed: indicates whether the termination signal has been sent
21 to the spawned process.
23 def __init__(self, command, start_wait=0, termsig=signal.SIGTERM):
26 @param command: an argument vector to be executed. The first element
27 is used as executable and looked up in $PATH.
28 @param start_wait: duration of the transition period from inactive to
30 @param termsig: termination signal to be sent to the process to
31 deactivate it. The process must exit in response to this
34 OnoffDevice.__init__(self)
35 self.command = command
36 self.start_wait = start_wait
37 self.termsig = termsig
38 self.desired_state = 0 # bit mask of just ST_ACTIVE
40 self.starting = None # timeout event during start
45 if self.starting or self.killed:
46 return self.desired_state | ST_TRANSITION
47 return self.desired_state
49 def start_process(self):
50 assert self.pid is None
51 assert self.starting is None
52 logger.info("starting command %s", " ".join(self.command))
53 self.pid = spawn_child(self.command, self.process_died)
54 logger.debug("started as pid %d", self.pid)
55 self.starting = ScheduledFunction(self.start_wait, self.process_started)
56 self.changestate(self.state)
58 def cancel_start_wait(self):
59 if self.starting is None:
61 logger.debug("cancelling start notification")
62 self.starting.cancel()
65 def process_died(self, pid, condition):
66 assert self.pid == pid
69 logger.info("process %d died", pid)
70 self.cancel_start_wait()
71 if self.desired_state == ST_ACTIVE:
74 self.changestate(self.state)
76 def process_started(self):
77 assert self.desired_state == ST_ACTIVE
78 assert self.starting is not None
80 logger.debug("process started")
81 self.changestate(self.state)
83 def stop_process(self):
84 assert self.pid is not None
85 self.cancel_start_wait()
86 logger.info("killing process %d", self.pid)
87 os.kill(self.pid, self.termsig)
89 self.changestate(self.state)
92 if self.desired_state != ST_ACTIVE:
93 self.desired_state = ST_ACTIVE
97 logger.debug("already activated. nothing to do")
100 if self.desired_state != 0:
101 self.desired_state = 0
102 if self.pid is not None and not self.killed:
105 logger.debug("already deactivated. nothing to do.")