Skip to content
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!
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
ctrl
lib
.gitignore
LICENSE.ASF-2.md
LICENSE.GPL-3.md
README.md
app.py
install
requirements.txt
sunrise
uninstall

README.md

python-daemon-set-sunrise

codesize release

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!

Description

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.

Compatibility

This project is currently tested with

  • python 3.5, 3.6
  • debian
  • ubuntu
  • linux mint 18

Please share your experience and help to make it more compatible.

Dependencies

This app needs to run: plyvel, python-daemon which is an implementation of the well-behaved daemon specification PEP 3143 “Standard daemon process library”, cpython configparser, lockfile

But this will get installed via the ./install script, so you dont have to do it now!

Sources

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

Philosophy

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 ./install script!

OK, here we go:

Be careful, use everywhere the exactly same appname!

Follow this step by step guide!

  1. Download the latest release or download the current state via git clone git@github.com:bithon/python-daemon-set-sunrise.git to your home or download directory.

  2. If you use the release version, then unzip it and rename the directory name to your appname, then cd into the directory.

  3. 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
  1. Run ./install as root and confirm every step with "yes", this will install dependencies, copy files to /opt/appname, create start/stop scripts, copy config files from ./lib/res/conf.d to /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.cfg file to the parameter user and group.

  2. Now it should be fine to start the app, just type sudo appname -d and enter. It should even work without "./", since we made the appname global available with a symlink to /usr/local/bin/appname with the install script. Start as root tail -f /var/log/appname/appname.log to see, what the daemon is doing.

  3. Add your code to ./lib/classes/app/main and 3rd party classes to ./lib/classes, so its easy to move your classes to an updated versions of this project in the future.

  4. Open ./lib/classes/app/main/runmain.py, initiate and call your class.

  5. 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.

Commandline parameter

$ 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 -> sunrise -d

/etc/init.d/sunrise stop -> sunrise -s

/etc/init.d/sunrise stopgracefully -> sunrise -g

/etc/init.d/sunrise restart -> sunrise -r

/etc/init.d/sunrise status -> sunrise -c

/etc/init.d/sunrise install enable autostart

/etc/init.d/sunrise uninstall disable autostart

screenshot

Some useful hints

In the variable self.app you find all resources of this app like:

Shutdown:

self.app['globaltools'].app_destructor(self.app)

Enable the exitflag for the systemmanager thread to close itself

self.app['app_thread_systemmanager'].set_exitflag()

This returns the settings from the config files from /etc/appname/conf.d/ parsed with the cpython/configparser:

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:

self.app['app_thread_systemmanager']

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 ./install.

You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.