Hallo Marc,
Welche Möglichkeit gibt es, in einem Shell-Skript zu ermitteln, ob alle notwendigen Apache-Instanzen *wirklich* komplett neu gestartet sind?
Für diesen Zweck ist es sinnvoll, einfach zu überprüfen, ob die Log-Dateien noch offen sind. Wenn dies der Fall ist, so ist der Apache noch nicht komplett neu gestartet - wenn alle Dateien bereits zu sind, schon.
Auf den SELF-Servern habe ich mir ein paar Python-Scripte für den Zweck programmiert. Hier die relevanten Ausschnitte:
lrutils.py: Hilfsfunktion, die überprüft, ob bestimmte Dateien noch offen sind:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Utility functions for rotating logfiles
import os
from stat import *
# Check whether any of the specified files is still in use.
# Will only work correctly if the files are only opened by programs run under
# the current user or when this script is run as root.
def filesInUse (files):
# Get Inode / Device number for all files to check and save them for
# later. Ignore non-existing files.
tocheck = []
for file in files:
try:
statinfo = os.stat (file)
tocheck.append ((statinfo[ST_INO], statinfo[ST_DEV]))
except OSError:
pass
# Nothing to check? No file in use
if not len (tocheck):
return False
# Search /proc
for pid in os.listdir ("/proc"):
# Only enter PID-type directories
if not pid.isdigit(): continue
try:
# Get all file descriptors for the process and compare
# the inode and device numbers with the inode and
# device numbers of the files we want to check. If at
# least one file is already open, we're done.
for fd in os.listdir ("/proc/%s/fd" % pid):
try:
statinfo = os.stat ("/proc/%s/fd/%s" % (pid, fd))
if tocheck.count ((statinfo[ST_INO], statinfo[ST_DEV])) > 0:
return True
except OSError:
# Ignore access errors
pass
except OSError:
# Ignore access errors
pass
# None of the files was found to be open
return False
rotatelogs.py (Teile davon zumindest): Rotieren von Logfiles
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Rotate logfiles
# Import needed modules
import lrutils, os, time, glob, sys
# Initial period to wait if not closed immediately
interval1 = 20
# Interval to wait subsequently
interval = 60
# The logfiles to copy
logfiles = ['/var/log/apache2/access_log', '/var/log/apache2/error_log', '/var/log/apache2/access_log.*', '/var/log/apache2/error_log.*']
# Get logfiles to rotate
logfiles = reduce (lambda x, y: x+y, [glob.glob(y) for y in logfiles])
rotated = [x + '.rotated' for x in logfiles]
# Rotate all logfiles
for logfile in logfiles:
os.rename (logfile, logfile + '.rotated')
# Reload apache
os.system ('/usr/sbin/apache2ctl graceful')
# Now definitely sleep at least 1 second - Apache will *never* be
# finished reloading *so* quickly
time.sleep (1);
# If logfiles are already closed: we're done
if not lrutils.filesInUse (rotated):
# Stelle etwas mit den rotierten Logfiles an
sys.exit (0)
# If not, wait first interval
time.sleep (interval1)
# While logfiles are still open: wait normal interval
while lrutils.filesInUse (rotated):
# Do another reload because sometimes apache does not close the logs
# for some reason.
os.system ('/usr/sbin/apache2ctl graceful')
time.sleep (interval)
# Stelle was mit den rotierten Logfiles an
Was macht der Code hier?
-
Die Logfiles werden von $logfile nach $logfile.rotated umbenannt. Der Apache schreibt dann natürlich in die .rotated-Dateien, da der Kernel sich nur um die Inode-Nummern kümmert.
-
Dem Apache wird ein graceful geschickt. Er macht dann auf jeden Fall die neuen Logfiles auf (d.h. im Verzeichnis existieren $logfile und $logfile.rotated).
-
In mehreren regelmäßigen Intervallen überprüft das Script, ob die $logfile.rotated noch in Gebrauch sind. Wenn dies nicht der Fall ist, hat der Apache alle Logs zugemacht und das Script kann etwas mit den Logs anstellen (das habe ich hier ausgelassen, weil das SELF-Server-spezifisch ist - wir verschieben die alle auf einen einzigen Server, lassen dort das Statistikprogramm drüberlaufen und löschen die dann allesamt).
Was noch auf meiner TODO-Liste hierfür steht (allerdings ziemlich weit unten, da nicht kritisch), wäre eine Möglichkeit, nach soundsolanger Zeit (ein paar Stunden z.B.) auch einen restart zu schicken, wenn die Logs dann immer noch nicht freigegeben wurden.
Wenn jemand was mit dem Code anfangen will, ich stelle ihn (sofern er überhaupt eine Schöpfungshöhe erreicht) unter die MIT-Lizenz.
Viele Grüße,
Christian