Skip to content

Commit

Permalink
[snmp]: Fix a race between snmpd-config-updater and snmpd (#1628)
Browse files Browse the repository at this point in the history
There is a small window in which snmpd might not have registered a
callback for SIGHUP and which will result in its death if
snmpd-config-updater send this signal meant for a config reload.
  • Loading branch information
Staphylo authored and lguohan committed Apr 28, 2018
1 parent ae14846 commit de3e8cc
Showing 1 changed file with 61 additions and 2 deletions.
63 changes: 61 additions & 2 deletions dockers/docker-snmp-sv2/snmpd-config-updater
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

import os
import re
import signal
import subprocess
import sys
import syslog
Expand All @@ -24,6 +25,62 @@ SYSLOG_IDENTIFIER = "snmpd-config-updater"

# ============================== Classes ==============================

class Process(object):
def __init__(self, pid):
self.pid = pid
self.path = '/proc/%d/status' % pid
self.status = None

def read_proc_status(self):
data = {}
with open(self.path) as f:
for line in f.readlines():
key, value = line.split(':', 1)
data[ key ] = value.strip()
self.status = data

def get_proc_signals(self):
assert self.status
sigBlk = int(self.status[ 'SigBlk' ], 16)
sigIgn = int(self.status[ 'SigIgn' ], 16)
sigCgt = int(self.status[ 'SigCgt' ], 16)
return (sigBlk, sigIgn, sigCgt)

def handle_signal(self, sig):
sigBlk, sigIgn, sigCgt = self.get_proc_signals()
mask = 1 << ( sig - 1 )
if mask & sigBlk:
return True
if mask & sigIgn:
return True
if mask & sigCgt:
return True
return False

def send_signal(self, sig):
log_info('Sending signal %s to %d' % (sig, self.pid))
os.kill(self.pid, sig)

def safe_send_signal(self, sig):
self.read_proc_status()
if not self.handle_signal(sig):
return False
self.send_signal(sig)
return True

def wait_send_signal(self, sig, interval=0.1):
while not self.safe_send_signal(sig):
log_info('Process %s has not yet registered %s' % (self.pid, sig))
time.sleep(interval)

@staticmethod
def by_name(name):
try:
pid = subprocess.check_output([ 'pidof', '-s', name ])
except subprocess.CalledProcessError:
return None
return Process(int(pid.rstrip()))

class ConfigUpdater(object):
SERVICE = "snmpd"
CONFIG_FILE_PATH = "/etc/snmp"
Expand Down Expand Up @@ -125,8 +182,10 @@ class ConfigUpdater(object):

os.rename(filename_tmp, filename)

# Force snmpd to reload its configuration
os.system("kill -HUP $(pgrep snmpd) > /dev/null 2> /dev/null || :")
# Force snmpd process to reload its configuration if it is running
proc = Process.by_name(self.SERVICE)
if proc:
proc.wait_send_signal(signal.SIGHUP)

def notification_handler(self, key, data):
log_info("ACL configuration changed. Updating {} config accordingly...".format(self.SERVICE))
Expand Down

0 comments on commit de3e8cc

Please sign in to comment.