Daemonize jenkins-log-pusher.

* modules/openstack_project/files/logstash/log-pusher.py: Semi properly
daemon the log pusher process by default. Close open file descriptors,
fork into background, detach from terminal, redirect std* to /dev/null,
change the umask to 0, change working dir to '/', and lock a PID file.

* modules/openstack_project/files/logstash/jenkins-log-pusher.init:
Update start-stop-daemon commands to use the presence of a PID file and
no longer background with start-stop-daemon.

Change-Id: I4dcdd48478fa7d27745a3075a6942838e9df20ee
Reviewed-on: https://review.openstack.org/28449
Reviewed-by: Jeremy Stanley <fungi@yuggoth.org>
Approved: James E. Blair <corvus@inaugust.com>
Reviewed-by: James E. Blair <corvus@inaugust.com>
Tested-by: Jenkins
This commit is contained in:
Clark Boylan 2013-05-07 11:19:03 -07:00 committed by Jenkins
parent 1491c3e6c8
commit a5846e0f02
2 changed files with 83 additions and 17 deletions

View File

@ -17,7 +17,7 @@ DESC="Jenkins Log Pusher"
NAME=jenkins-log-pusher NAME=jenkins-log-pusher
DAEMON=/usr/local/bin/log-pusher.py DAEMON=/usr/local/bin/log-pusher.py
DAEMON_ARGS='-c /etc/logstash/jenkins-log-pusher.yaml -d /var/log/logstash/pusher-debug.log' DAEMON_ARGS='-c /etc/logstash/jenkins-log-pusher.yaml -d /var/log/logstash/pusher-debug.log'
#PIDFILE=/var/run/$NAME/$NAME.pid PIDFILE=/var/run/$NAME/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME SCRIPTNAME=/etc/init.d/$NAME
USER=logstash USER=logstash
@ -46,10 +46,10 @@ do_start()
mkdir -p /var/run/$NAME mkdir -p /var/run/$NAME
chown $USER /var/run/$NAME chown $USER /var/run/$NAME
start-stop-daemon --start --quiet -c $USER --exec $DAEMON --test > /dev/null \ start-stop-daemon --start --quiet --pidfile $PIDFILE -c $USER --exec $DAEMON --test > /dev/null \
|| return 1 || return 1
# Note using --background as log-pusher.py cannot daemonize itself yet. # Note using --background as log-pusher.py cannot daemonize itself yet.
start-stop-daemon --start --quiet --background -c $USER --exec $DAEMON -- \ start-stop-daemon --start --quiet --pidfile $PIDFILE -c $USER --exec $DAEMON -- \
$DAEMON_ARGS \ $DAEMON_ARGS \
|| return 2 || return 2
# Add code here, if necessary, that waits for the process to be ready # Add code here, if necessary, that waits for the process to be ready
@ -67,7 +67,7 @@ do_stop()
# 1 if daemon was already stopped # 1 if daemon was already stopped
# 2 if daemon could not be stopped # 2 if daemon could not be stopped
# other if a failure occurred # other if a failure occurred
start-stop-daemon --stop --signal 9 --exec $DAEMON start-stop-daemon --stop --signal 9 --pidfile $PIDFILE
RETVAL="$?" RETVAL="$?"
[ "$RETVAL" = 2 ] && return 2 [ "$RETVAL" = 2 ] && return 2
rm -f /var/run/$NAME/* rm -f /var/run/$NAME/*

View File

@ -15,15 +15,18 @@
# under the License. # under the License.
import argparse import argparse
import fcntl
import gzip import gzip
import json import json
import logging import logging
import threading import os
import time
import queue import queue
import re import re
import resource
import socket import socket
import sys import sys
import threading
import time
import urllib.error import urllib.error
import urllib.request import urllib.request
import yaml import yaml
@ -324,6 +327,8 @@ class Server(object):
self.default_output_host, self.default_output_host,
self.default_output_port) self.default_output_port)
else: else:
# Note this processor will not work if the process is run as a
# daemon. You must use the --foreground option.
self.processor = StdOutLogProcessor(self.logqueue) self.processor = StdOutLogProcessor(self.logqueue)
def main(self): def main(self):
@ -347,21 +352,78 @@ class Server(object):
class DaemonContext(object): class DaemonContext(object):
def __init__(self): def __init__(self, pidfile_path):
# Set pidfile path. self.pidfile_path = pidfile_path
pass self.pidfile = None
self.pidlocked = False
def __enter__(self): def __enter__(self):
# change umask # Perform Sys V daemonization steps as defined by
# chdir # http://www.freedesktop.org/software/systemd/man/daemon.html
# double fork # Close all open file descriptors but std*
# redirect stdin, stdout, stderr to /dev/null _, max_fds = resource.getrlimit(resource.RLIMIT_NOFILE)
# lock pidfile if max_fds == resource.RLIM_INFINITY:
pass max_fds = 4096
for fd in range(3, max_fds):
try:
os.close(fd)
except OSError:
# TODO(clarkb) check e.errno.
# fd not open.
pass
# TODO(clarkb) reset all signal handlers to their default
# TODO(clarkb) reset signal mask
# TODO(clarkb) sanitize environment block
# Fork to create background process
# TODO(clarkb) pass in read end of pipe and have parent wait for
# bytes on the pipe before exiting.
self._fork_exit_parent()
# setsid() to detach from terminal and create independent session.
os.setsid()
# Fork again to prevent reaquisition of terminal
self._fork_exit_parent()
# Hook std* to /dev/null.
devnull = os.open(os.devnull, os.O_RDWR)
os.dup2(devnull, 0)
os.dup2(devnull, 1)
os.dup2(devnull, 2)
# Set umask to 0
os.umask(0)
# chdir to root of filesystem.
os.chdir(os.sep)
# Lock pidfile.
self.pidfile = open(self.pidfile_path, 'w')
try:
fcntl.lockf(self.pidfile, fcntl.LOCK_EX | fcntl.LOCK_NB)
self.pidlocked = True
except IOError:
# another instance is running
sys.exit(0)
# TODO(clarkb) write pid to pidfile
def __exit__(self, exc_type, exc_value, traceback): def __exit__(self, exc_type, exc_value, traceback):
# remove pidfile # remove pidfile
pass if self.pidlocked:
os.unlink(self.pidfile_path)
if self.pidfile:
self.pidfile.close()
# TODO(clarkb) write to then close parent signal pipe if not
# already done.
def _fork_exit_parent(self, read_pipe=None):
if os.fork():
# Parent
if read_pipe:
os.fdopen(read_pipe).read()
sys.exit()
else:
# Child
return
def main(): def main():
@ -373,6 +435,10 @@ def main():
"Specifies file to write log to.") "Specifies file to write log to.")
parser.add_argument("--foreground", action='store_true', parser.add_argument("--foreground", action='store_true',
help="Run in the foreground.") help="Run in the foreground.")
parser.add_argument("-p", "--pidfile",
default="/var/run/jenkins-log-pusher/"
"jenkins-log-pusher.pid",
help="PID file to lock during daemonization.")
args = parser.parse_args() args = parser.parse_args()
with open(args.config, 'r') as config_stream: with open(args.config, 'r') as config_stream:
@ -383,7 +449,7 @@ def main():
server.setup_logging() server.setup_logging()
server.main() server.main()
else: else:
with DaemonContext(): with DaemonContext(args.pidfile):
server.setup_logging() server.setup_logging()
server.main() server.main()