torfsen/service

Daemon does not start

YuriyLisovskiy opened this issue · 3 comments

I've written some code to test the service class:

import time

from service import Service


class TestService(Service):

	def run(self):
		i = 0
		while i < 5 and not self.got_sigterm():
			i += 1
			with open('./file.txt', 'a') as the_file:
				the_file.write('Service info: {}\n'.format(i))
			time.sleep(1)

if __name__ == '__main__':
	service = TestService('test_service', '/tmp')
	service.start()
	time.sleep(1)
	print(service.get_pid())

The program printed out None and file was not created.

After I've tried to modify the module, so this:

try:
setproctitle.setproctitle(self.name)
self._debug('Process title has been set')
files_preserve = (self.files_preserve +
self._get_logger_file_handles())
with DaemonContext(
detach_process=False,
signal_map={
signal.SIGTTIN: None,
signal.SIGTTOU: None,
signal.SIGTSTP: None,
signal.SIGTERM: on_sigterm,
},
files_preserve=files_preserve):
self._debug('Daemon context has been established')
# Python's signal handling mechanism only forwards signals to
# the main thread and only when that thread is doing something
# (e.g. not when it's waiting for a lock, etc.). If we use the
# main thread for the ``run`` method this means that we cannot
# use the synchronization devices from ``threading`` for
# communicating the reception of SIGTERM to ``run``. Hence we
# use a separate thread for ``run`` and make sure that the
# main loop receives signals. See
# https://bugs.python.org/issue1167930
thread = threading.Thread(target=runner)
thread.start()
while thread.is_alive():
time.sleep(1)
except Exception as e:
self.logger.exception(e)

I have changed to this:

try: 
     setproctitle.setproctitle(self.name) 
     self._debug('Process title has been set') 
     files_preserve = (self.files_preserve + 
                       self._get_logger_file_handles()) 
     DaemonContext( 
         detach_process=False, 
         signal_map={ 
             signal.SIGTTIN: None, 
             signal.SIGTTOU: None, 
             signal.SIGTSTP: None, 
             signal.SIGTERM: on_sigterm, 
         }, 
         files_preserve=files_preserve)
     self._debug('Daemon context has been established') 
  
     # Python's signal handling mechanism only forwards signals to 
     # the main thread and only when that thread is doing something 
     # (e.g. not when it's waiting for a lock, etc.). If we use the 
     # main thread for the ``run`` method this means that we cannot 
     # use the synchronization devices from ``threading`` for 
     # communicating the reception of SIGTERM to ``run``. Hence we 
     # use  a separate thread for ``run`` and make sure that the 
     # main loop receives signals. See 
     # https://bugs.python.org/issue1167930 
     thread = threading.Thread(target=runner) 
     thread.start() 
     while thread.is_alive(): 
         time.sleep(1) 
 except Exception as e: 
     self.logger.exception(e)

i.e. removed with near the DaemonContext, line 425. And now service is running fine.

Why it is not working without this modification?
Is my test service is written incorrectly?

I am getting the same error. +1 to follow the issue.

@YuriyLisovskiy There are two issues with your code:

The first thing is that in the service process, the working directory is set to / (the root directory). Unless your service is running as the root user, you won't have the permission to create a file there. You can see that if you enable logging:

import logging
from logging.handlers import SysLogHandler
import os
import time

from service import find_syslog, Service

class TestService(Service):

    def __init__(self, *args, **kwargs):
        super(TestService, self).__init__(*args, **kwargs)
        self.logger.addHandler(SysLogHandler(address=find_syslog(),
                               facility=SysLogHandler.LOG_DAEMON))
        self.logger.setLevel(logging.INFO)

    def run(self):
        self.logger.info('cwd = {}'.format(os.getcwd()))
        i = 0
        while i < 5 and not self.got_sigterm():            
            i += 1
            with open('./file.txt', 'a') as the_file:
                the_file.write('Service info: {}\n'.format(i))
            time.sleep(1)

With this code, I find the following in my /var/log/syslog (that location might be different for you depending on your system's configuration):

Apr 27 14:55:24 leibniz cwd = /
Apr 27 14:55:24 leibniz [Errno 13] Permission denied: './file.txt'#012Traceback (most recent call last):#012  File "/home/torf/projects/service/src/service/__init__.py", line 407, in runner#012    self.run()#012  File "./issue11.py", line 26, in run#012    with open('./file.txt', 'a') as the_file:#012IOError: [Errno 13] Permission denied: './file.txt'

If you simply want to write into the current working directory of your script, then store that path in a variable (script_cwd = os.getcwd()) and reference it from within run -- or pass the directory to your service as a constructor argument.

The second issue is about service.get_pid() returning None. This could have two reasons: either your service isn't fully running when you call service.get_pid() (it usually takes some seconds) or it has already exited because of the permission exception described above. An easy way to resolve the former problem is to pass block=True to start. That way, start will block until the service is really running:

test_service = TestService('test_service', '/tmp')
test_service.start(block=True)
print(test_service.get_pid())

Regarding the fix you proposed: your changes completely disable the forking of the service into a separate process. That fixes the permission problem, but for the wrong reason 😉

I think this will fix your issues, so I'm closing this ticket. Feel free to reopen it if required.

@torfsen Thanks much =)