5 from .common import ST_ACTIVE, ST_TRANSITION, OnoffDevice
6 from .gobject import spawn_child
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, 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 termsig: termination signal to be sent to the process to
29 deactivate it. The process must exit in response to this
32 OnoffDevice.__init__(self)
33 self.command = command
34 self.termsig = termsig
35 self.desired_state = 0 # bit mask of just ST_ACTIVE
42 return self.desired_state | ST_TRANSITION
43 return self.desired_state
45 def start_process(self):
46 assert self.pid is None
47 logger.info("starting command %s", " ".join(self.command))
48 self.pid = spawn_child(self.command, self.process_died)
49 logger.debug("started as pid %d", self.pid)
50 assert self.desired_state == ST_ACTIVE
51 logger.debug("process started")
52 self.changestate(self.state)
54 def process_died(self, pid, condition):
55 assert self.pid == pid
58 logger.info("process %d died", pid)
59 if self.desired_state == ST_ACTIVE:
62 self.changestate(self.state)
65 if self.desired_state != ST_ACTIVE:
66 self.desired_state = ST_ACTIVE
70 logger.debug("already activated. nothing to do")
73 if self.desired_state != 0:
74 self.desired_state = 0
75 if self.pid is not None and not self.killed:
76 logger.info("killing process %d", self.pid)
77 os.kill(self.pid, self.termsig)
79 self.changestate(self.state)
81 logger.debug("already deactivated. nothing to do.")