A ready but empty daemon for linux/unix - written in python with logging, configfile management, leveldb, init.d start/stop scripts, installer, uninstaller, ... just add your business logic!
Sometimes I need to run a python application as a daemon with logging, configfile management and a leveldb. So I created this package, where I can start from. There are only a few changes necessary, which are very easy and done in less than 5 minutes. Then you just have to add your business logic! I guess you can daemonize every script that runs in a loop with this easily.
This project is currently tested with
- python 3.5, 3.6
- linux mint 18
Please share your experience and help to make it more compatible.
But this will get installed via the
./install script, so you dont have to do it now!
The most of the infos and ideas I used are from https://dpbl.wordpress.com/2017/02/12/a-tutorial-on-python-daemon/ and the links you find there for example https://pagure.io/python-daemon/blob/master/f/daemon/runner.py#_82
There should only run one instance of a daemon per system, so the paths we have to use are pretty clear:
a) `/etc/appname/conf.d` for config files b) `/etc/init.d/appname` for the start script c) `/opt/appname` for the program code d) `/var/log/appname/appname.log` for the logfile e) `/var/run/appname/appname.pid` for the pidfile
Since this is clear, all you have to modify is the name of the application, the rest will be done by the
OK, here we go:
Be careful, use everywhere the exactly same appname!
Follow this step by step guide!
Download the latest release or download the current state via
git clone firstname.lastname@example.org:bithon/python-daemon-set-sunrise.gitto your home or download directory.
If you use the release version, then
unzipit and rename the directory name to your appname, then
cdinto the directory.
To change the name of the app, here is a list how to do it:
a) edit the content in `./lib/res/appname` and change "sunrise" to the new appname b) in `./lib/res/init.d/service.sh` you have to change "sunrise" 3 times, also in the comments! DO NOT CHANGE ANYTHING ELSE IN THE HEAD! THE COMMENTS ARE USED BY THE SERVICE INSTALLER! c) set appname, author and more in the head of `./app.py` d) check config files in `./lib/res/conf.d` e) delete `./sunrise -> app.py` symlink
./installas root and confirm every step with "yes", this will install dependencies, copy files to
/opt/appname, create start/stop scripts, copy config files from
/etc/appname/conf.d, set file permissions, install the service for autostart, create symlinks, ... But there is one thing you have to keep in mind! You can run the daemon as root or as a non privileged user. You can create with the install script a user and a group for the daemon, but you have to decide, if you set permissions for root or the non privileged service account. What ever you choose, write your choice also to the
global.cfgfile to the parameter
Now it should be fine to start the app, just type
sudo appname -dand enter. It should even work without "./", since we made the appname global available with a symlink to
/usr/local/bin/appnamewith the install script. Start as root
tail -f /var/log/appname/appname.logto see, what the daemon is doing.
Add your code to
./lib/classes/app/mainand 3rd party classes to
./lib/classes, so its easy to move your classes to an updated versions of this project in the future.
./lib/classes/app/main/runmain.py, initiate and call your class.
That's it, have fun!
You can use the
./install script for fixing permissions, to "deploy" your update or to repair the existing installation.
What, if its not running?
Please open an Issue and report your error messages.
$ sunrise --help usage: sunrise [-h] [-d] [-r] [-o] [-s] [-g] [-c] [-cp CONFIG_PATH] [-lf LOG_FILE] [-ll LOG_LEVEL] [-sd] [-rm] [-pr] Sunrise 0.3.1 - Oliver Zehentleitner description: A ready but empty daemon for linux/unix, just add your business logic! optional arguments: -h, --help show this help message and exit -d, --daemonize start as daemon -r, --restart restart daemon -o, --output redirect output to the console during daemon mode (foreground), use with -d or -r -s, --stop stop daemon -g, --graceful-shutdown let the deamon shutdown itself -c, --check check daemon status -cp CONFIG_PATH, --config-path CONFIG_PATH add a path to a directory with *.cfg files -lf LOG_FILE, --log-file LOG_FILE log to an alternate log file -ll LOG_LEVEL, --log-level LOG_LEVEL options: NOTSET,DEBUG,INFO,WARNING,ERROR or CRITICAL -sd, --start-delay the program waits a little bit before it starts to initialize itself. if the main process dies, then the systemmanager thread is using this to have time to free up its locked ressources! -rm, --revival-mode if the main thread crashes, the systemmanager thread will restart the deamon - look in global.cfg to 'heartbeat_main_lifetime' -pr, --plain-restart if the systemmanager restarts the process, demonize must not be used, becouse the process is already a deamon and would lead to an error! GitHub: https://github.com/bithon/python-daemon-set-sunrise/
Important: If you start the app without any parameter, it runs as a normal process with console output (foreground) and will be closed with your terminal session.
Examples for daemon use:
sunrise -d starts the daemon. When the process turns into a daemon, the output to the console will stop!
sunrise -d -o starts the daemon with output to the console, this is very useful for development!
sunrise -r restarts the server.
sunrise -s kills it, so its better to use
sunrise -g for a graceful shutdown. In this case, the daemon waits that all jobs are finished and then shutdown it self.
sunrise -c will show you the current status of the daemon.
sunrise -d -rm starts the daemon in revival mode. this means, if the main or the systemmanager thread dies, the other thread will restart the daemon. set the heartbeat life time for the main thread in global.cfg, the heartbeat life time for systemmanager is set 300 seconds and can not be changed.
sunrise -cp /path/to/other/configfiles/ is very useful, if you have to test behaviour between different configurations.
sunrise -lf ./blah.log to log to an alternate file, this overrules the config file setting
sunrise -ll DEBUG sets the loglevel, this overrules the config file setting
/etc/init.d/sunrise start ->
/etc/init.d/sunrise stop ->
/etc/init.d/sunrise stopgracefully ->
/etc/init.d/sunrise restart ->
/etc/init.d/sunrise status ->
/etc/init.d/sunrise install enable autostart
/etc/init.d/sunrise uninstall disable autostart
Some useful hints
In the variable
self.app you find all resources of this app like:
Enable the exitflag for the systemmanager thread to close itself
value = self.app['app_config'].get('SECTION', 'key')
Log a message:
self.app['logging'].debug(debug_msg) self.app['logging'].info(info_msg) self.app['logging'].warning(warning_msg) self.app['logging'].error(error_msg) self.app['logging'].critical(critical_msg)
The handler for the systemmanager thread from ./lib/classes/app/core/systemmanager.py:
Write and read to leveldb_app database
self.app['leveldb_app'].put(b'key-word', b'the_value') self.app['leveldb_app'].get(b'key-word') key = bytes("app_heartbeat_timestamp_" + threadname, 'utf-8') timestamp = self.int_to_bytes(int(self.get_timestamp())) self.app['leveldb_app'].put(key, timestamp)
Add a new variable to self.app
self.app = self.app['globaltools'].add_app_entry_with_log(self.app, "key", value)
How to uninstall?
I think is not a good idea to automatically remove any file with a script, so the only thing that
./uninstall does, is to remove the autostart entry, the symlink in
/usr/local/bin/appname and to tell you, which files you have to delete manually. Be also careful with uninstalling dependencies, maybe they are also used by an other application! You can easily undo the actions of
./uninstall with executing