def __init__(self): """Whole project is run from this constructor """ # +++++++++++++++ Init +++++++++++++++ # self.keep_run = 1 # used in run for daemon loop, reset by SIGTERM self.ret_code = 0 # return code to command line (10 = shutdown) # +++++++++++++++ Objects +++++++++++++++ # # Each inner object will get reference to PyBlaster as self.main. # exceptions in child threads are put here self.ex_queue = queue.Queue() self.log = Log(self) self.settings = Settings(self) self.settings.parse() PB_GPIO.init_gpio(self) self.led = LED(self) self.buttons = Buttons(self) self.lirc = Lirc(self) self.dbhandle = DBHandle(self) self.cmd = Cmd(self) self.mpc = MPC(self) self.bt = RFCommServer(self) self.alsa = AlsaMixer(self) self.i2c = I2C(self) self.usb = UsbDrive(self) # +++++++++++++++ Init Objects +++++++++++++++ # # Make sure to run init functions in proper order! # Some might depend upon others ;) self.led.init_leds() self.dbhandle.dbconnect() self.mpc.connect() self.bt.start_server_thread() self.alsa.init_alsa() self.i2c.open_bus() self.usb.start_uploader_thread() # +++++++++++++++ Daemoninze +++++++++++++++ # self.check_pidfile() self.daemonize() self.create_pidfile() # +++++++++++++++ Daemon loop +++++++++++++++ # self.led.show_init_done() self.buttons.start() self.lirc.start() self.run() # +++++++++++++++ Finalize +++++++++++++++ # self.log.write(log.MESSAGE, "Joining threads...") # join remaining threads self.usb.join() self.bt.join() self.buttons.join() self.lirc.join() self.mpc.join() self.led.join() self.log.write(log.MESSAGE, "leaving...") # cleanup self.mpc.exit_client() self.delete_pidfile() PB_GPIO.cleanup(self)
def __init__(self): """Whole project is run from this constructor """ # +++++++++++++++ Init +++++++++++++++ # self.keep_run = 0 # used in run for daemon loop, reset by SIGTERM self.log = Log(self) self.settings = Settings(self) self.led = LED(self) self.dbhandle = DBHandle(self) self.mixer = AlsaMixer(self) self.usb = UsbManager(self) self.rfcomm = RFCommServer(self) self.cmd = EvalCmd(self) self.listmngr = PlayListManager(self) self.play = Play(self) self.lirc = LircThread(self) self.buttons = Buttons(self) self.keep_run = 1 self.ret_code = 0 # return code to command line (10 = shutdown) self.led.reset_leds() # invoke arg parser and parse config or create new config if not found self.settings.parse() # check if we can load database, create otherwise self.dbhandle.dbconnect() # restore alsa mixer settings self.mixer.restore_mixer() # load connected usb before bluetooth self.usb.check_new_usb() # initialize sound mixer self.play.init_mixer() # load latest playlist from database self.listmngr.load_active_playlist() # open cmd fifo to read commands self.cmd.open_fifo() # fire up bluetooth service self.rfcomm.start_server_thread() # start lirc thread self.lirc.start() # fire up one thread per each button self.buttons.start() # +++++++++++++++ Daemoninze +++++++++++++++ # self.check_pidfile() self.daemonize() self.create_pidfile() self.led.show_init_done() # +++++++++++++++ Daemon loop +++++++++++++++ # self.run() # +++++++++++++++ Finalize +++++++++++++++ # self.listmngr.save_active() self.led.cleanup() self.delete_pidfile()
class PyBlaster: """Daemon for PiBlaster project""" def __init__(self): """Whole project is run from this constructor """ # +++++++++++++++ Init +++++++++++++++ # self.keep_run = 1 # used in run for daemon loop, reset by SIGTERM self.ret_code = 0 # return code to command line (10 = shutdown) # +++++++++++++++ Objects +++++++++++++++ # # Each inner object will get reference to PyBlaster as self.main. # exceptions in child threads are put here self.ex_queue = queue.Queue() self.log = Log(self) self.settings = Settings(self) self.settings.parse() PB_GPIO.init_gpio(self) self.led = LED(self) self.buttons = Buttons(self) self.lirc = Lirc(self) self.dbhandle = DBHandle(self) self.cmd = Cmd(self) self.mpc = MPC(self) self.bt = RFCommServer(self) self.alsa = AlsaMixer(self) self.i2c = I2C(self) self.usb = UsbDrive(self) # +++++++++++++++ Init Objects +++++++++++++++ # # Make sure to run init functions in proper order! # Some might depend upon others ;) self.led.init_leds() self.dbhandle.dbconnect() self.mpc.connect() self.bt.start_server_thread() self.alsa.init_alsa() self.i2c.open_bus() self.usb.start_uploader_thread() # +++++++++++++++ Daemoninze +++++++++++++++ # self.check_pidfile() self.daemonize() self.create_pidfile() # +++++++++++++++ Daemon loop +++++++++++++++ # self.led.show_init_done() self.buttons.start() self.lirc.start() self.run() # +++++++++++++++ Finalize +++++++++++++++ # self.log.write(log.MESSAGE, "Joining threads...") # join remaining threads self.usb.join() self.bt.join() self.buttons.join() self.lirc.join() self.mpc.join() self.led.join() self.log.write(log.MESSAGE, "leaving...") # cleanup self.mpc.exit_client() self.delete_pidfile() PB_GPIO.cleanup(self) def run(self): """Daemon loop""" # Expensive operations like new usb drive check # should not be run every loop run. poll_count = 0 led_count = 1 # -e flag is set, run only init and exit directly. # self.keep_run = 0 if self.settings.exitafterinit else 1 # # # # # # DAEMON LOOP ENTRY # # # # # # self.log.write(log.MESSAGE, "Entering daemon loop...") while self.keep_run: poll_count += 1 time.sleep(50. / 1000.) # 50ms default in config self.buttons.read_buttons() self.mpc.process_idler_events() self.lirc.read_lirc() self.bt.check_incomming_commands() # TODO: play LEDs while playing -- if paused, do something else... if poll_count % 10 == 0: self.led.play_leds(led_count) led_count += 1 # try: # exc = self.ex_queue.get(block=False) # except queue.Empty: # pass # else: # exc_type, exc_obj, exc_trace = exc # print(exc_type, exc_obj) # print(exc_trace) # self.ret_code = 1 # self.keep_run = False # self.led.indicate_error() # end daemon loop # # # # # # # DAEMON LOOP EXIT # # # # # # def daemonize(self): """Fork process and disable print in log object""" signal.signal(signal.SIGTERM, self.term_handler) signal.signal(signal.SIGINT, self.term_handler) if not self.settings.daemonize: self.log.init_log() return self.log.write(log.DEBUG1, "daemonizing") try: pid = os.fork() except OSError: # self.log.write(log.EMERGENCY, "Failed to fork daemon") raise if pid == 0: os.setsid() try: pid = os.fork() except OSError: # self.log.write(log.EMERGENCY, "Failed to fork daemon") raise if pid == 0: os.chdir("/tmp") os.umask(0) else: exit(0) else: exit(0) self.settings.is_daemonized = True self.log.init_log() self.log.write(log.MESSAGE, "daemonized.") def term_handler(self, *args): """ Signal handler to stop daemon loop""" self.log.write(log.MESSAGE, "Got TERM or INT signal -- leaving!") self.keep_run = 0 def check_pidfile(self): """Check if daemon already running, throw if pid file found""" if os.path.exists(self.settings.pidfile): self.log.write(log.EMERGENCY, "Found pid file for pyblaster, " "another process running?") raise Exception("pid file found") def create_pidfile(self): """Write getpid() to file after daemonize()""" try: fpid = open(self.settings.pidfile, "w") except IOError: self.log.write(log.EMERGENCY, "failed to create pidfile %s" % self.settings.pidfile) raise fpid.write("%s\n" % os.getpid()) def delete_pidfile(self): """Try to remove pid file after daemon should exit""" if os.path.exists(self.settings.pidfile): try: os.remove(self.settings.pidfile) except OSError: self.log.write(log.EMERGENCY, "failed to remove pidfile %s" % self.settings.pidfile) raise def kill_other_pyblaster(self): """Check if pid found in pid file and try to kill this (old) process""" if not os.path.exists(self.settings.pidfile): return try: f = open(self.settings.pidfile, "r") except IOError: self.log.write(log.EMERGENCY, "failed to read pidfile %s" % self.settings.pidfile) raise pid = int(f.readline().strip()) print("Trying to kill old process with pid %s..." % pid) try: os.kill(pid, signal.SIGTERM) except OSError: self.log.write(log.EMERGENCY, "failed to kill process with pid %s" % pid) raise exit(0)
class PyBlaster: """Daemon for PiBlaster project""" def __init__(self): """Whole project is run from this constructor """ # +++++++++++++++ Init +++++++++++++++ # self.keep_run = 0 # used in run for daemon loop, reset by SIGTERM self.log = Log(self) self.settings = Settings(self) self.led = LED(self) self.dbhandle = DBHandle(self) self.mixer = AlsaMixer(self) self.usb = UsbManager(self) self.rfcomm = RFCommServer(self) self.cmd = EvalCmd(self) self.listmngr = PlayListManager(self) self.play = Play(self) self.lirc = LircThread(self) self.buttons = Buttons(self) self.keep_run = 1 self.ret_code = 0 # return code to command line (10 = shutdown) self.led.reset_leds() # invoke arg parser and parse config or create new config if not found self.settings.parse() # check if we can load database, create otherwise self.dbhandle.dbconnect() # restore alsa mixer settings self.mixer.restore_mixer() # load connected usb before bluetooth self.usb.check_new_usb() # initialize sound mixer self.play.init_mixer() # load latest playlist from database self.listmngr.load_active_playlist() # open cmd fifo to read commands self.cmd.open_fifo() # fire up bluetooth service self.rfcomm.start_server_thread() # start lirc thread self.lirc.start() # fire up one thread per each button self.buttons.start() # +++++++++++++++ Daemoninze +++++++++++++++ # self.check_pidfile() self.daemonize() self.create_pidfile() self.led.show_init_done() # +++++++++++++++ Daemon loop +++++++++++++++ # self.run() # +++++++++++++++ Finalize +++++++++++++++ # self.listmngr.save_active() self.led.cleanup() self.delete_pidfile() def run(self): """Daemon loop""" # Expensive operations like new usb drive check # should not be run every loop run. poll_count = 0 # -e flag is set, run only init and exit directly. self.keep_run = 0 if self.settings.exitafterinit else 1 reset_poll_count = self.settings.keep_alive_count * 30 * 4 # # # # # # DAEMON LOOP ENTRY # # # # # # while self.keep_run: poll_count += 1 # Check cmd fifo for new commands. if poll_count % 10 == 0: # each 300 ms is enough self.cmd.read_fifo() # Check button events if self.buttons.has_button_events(): self.buttons.read_buttons() # Check bluetooth channel for new messages/connections. self.rfcomm.check_incomming_commands() # Check if lirc thread has command in queue if self.lirc.queue_not_empty(): ircmd = self.lirc.read_command() if ircmd is not None: self.cmd.evalcmd(ircmd, "lirc") # Check if song has ended if poll_count % 4 == 0: # every 120 ms self.play.check_pygame_events() time.sleep(self.settings.polltime / 1000.) # 30ms default in # config if poll_count % self.settings.keep_alive_count == 0: self.led.set_led_green(1) if (poll_count - self.settings.flash_count) % \ self.settings.keep_alive_count == 0: self.led.set_led_green(0) # Check for new USB drives. if poll_count % 30 == 0: # If new usb device found, new usbdev instance will be created, # including dir and mp3 entries. # If usb device got lost, all its entries will be removed. # To check every ~900ms is enough self.usb.check_new_usb() # Multiple of all poll counts reached: # may reset poll count at reset_poll_count. if poll_count >= reset_poll_count: poll_count = 0 # end daemon loop # # # # # # # DAEMON LOOP EXIT # # # # # # # join remaining threads self.mixer.save_mixer() self.lirc.join() self.buttons.join() self.rfcomm.join() self.log.write(log.MESSAGE, "---- closed regularly ----") # end run() # def daemonize(self): """Fork process and disable print in log object""" signal.signal(signal.SIGTERM, self.term_handler) signal.signal(signal.SIGINT, self.term_handler) if not self.settings.daemonize: self.log.init_log() return self.log.write(log.DEBUG1, "daemonizing") try: pid = os.fork() except OSError: self.log.write(log.EMERGENCY, "Failed to fork daemon") raise if pid == 0: os.setsid() try: pid = os.fork() except OSError: self.log.write(log.EMERGENCY, "Failed to fork daemon") raise if pid == 0: os.chdir("/tmp") os.umask(0) else: os._exit(0) else: os._exit(0) self.settings.is_daemonized = True self.log.init_log() self.log.write(log.MESSAGE, "daemonized.") # end daemonize() # def term_handler(self, *args): """ Signal handler to stop daemon loop""" self.keep_run = 0 def check_pidfile(self): """Check if daemon already running, throw if pid file found""" if os.path.exists(self.settings.pidfile): self.log.write(log.EMERGENCY, "Found pid file for pyblaster, " "another process running?") raise Exception("pid file found") def create_pidfile(self): """Write getpid() to file after daemonize()""" try: fpid = open(self.settings.pidfile, "w") except IOError: self.log.write(log.EMERGENCY, "failed to create pidfile %s" % self.settings.pidfile) raise fpid.write("%s\n" % os.getpid()) def delete_pidfile(self): """Try to remove pid file after daemon should exit""" if os.path.exists(self.settings.pidfile): try: os.remove(self.settings.pidfile) except OSError: self.log.write(log.EMERGENCY, "failed to remove pidfile %s" % self.settings.pidfile) raise def kill_other_pyblaster(self): """Check if pid found in pid file and try to kill this (old) process""" if not os.path.exists(self.settings.pidfile): return try: f = open(self.settings.pidfile, "r") except IOError: self.log.write(log.EMERGENCY, "failed to read pidfile %s" % self.settings.pidfile) raise pid = int(f.readline().strip()) print("Trying to kill old process with pid %s..." % pid) try: os.kill(pid, signal.SIGTERM) except OSError: self.log.write(log.EMERGENCY, "failed to kill process with pid %s" % pid) raise exit(0)