Refactor into Python daemon and simplified shell

This commit makes a major change to the organization and operation of the RPi BMC software. The 'shell' (`bmc.sh`) has been greatly simplified,
removing all instances of `wiringpi` GPIO code. The shell communicates with a new Python daemon via a pair of named pipes in `/run/bmcd`.

The Python daemon starts and immediately executes two threads:
1) The first manages reading from `/run/bmcd/bmcd.cmd`, which handles the passing of commands from `bmc.sh`, such as 'powersw_press' or 'locate_on'.
2) The second manages writing the current host state out to `/run/bmcd/bmcd.state`, to be read from `bmc.sh` when needed. It does this every second
   to avoid blocking.

An additional thread with an Event handler manages the flashing power LED for the `locate` functions.
This commit is contained in:
Joshua Boniface 2017-03-31 00:03:52 -04:00
parent a52f8b071f
commit e56002baf8
2 changed files with 122 additions and 74 deletions

79
bmc.sh Normal file → Executable file
View File

@ -4,25 +4,18 @@ stty eof undef
stty intr undef stty intr undef
hostsystem="$( cat /etc/bmchost )" hostsystem="$( cat /etc/bmchost )"
packages=( screen wiringpi ) bmcd_cmdpipe="/run/bmcd/bmcd.cmd"
pkgfail="" bmcd_statepipe="/run/bmcd/bmcd.state"
for package in ${packages[@]}; do
dpkg -l | grep "^ii ${package}" &>/dev/null || pkgfail="true"
done
if test -n "$pkgfail"; then
echo -n "Installing required packages... "
sudo apt update &>/dev/null
sudo apt install -y ${packages[@]} &>/dev/null
echo "done."
fi
help() { help() {
echo -e "Available commands:" echo -e "Available commands:"
echo -e " \e[1mstate\e[0m - Show the system power state" echo -e " \e[1mstate\e[0m - Show the system power state"
echo -e " \e[1mconsole\e[0m - Connect to host via serial console; ^A+D to disconnect" echo -e " \e[1mconsole\e[0m - Connect to host via serial console; ^A+D to disconnect"
echo -e " \e[1mpowersw\e[0m - Press power switch on host" echo -e " \e[1mpower\e[0m - Press power switch on host"
echo -e " \e[1mresetsw\e[0m - Press reset switch on host" echo -e " \e[1mreset\e[0m - Press reset switch on host"
echo -e " \e[1mkill\e[0m - Forcibly power off host" echo -e " \e[1mkill\e[0m - Forcibly power off host"
echo -e " \e[1mlocate\e[0m - Enable locator (flash power LED)"
echo -e " \e[1munlocate\e[0m - Disable locator"
echo -e " \e[1mhelp\e[0m - This help menu" echo -e " \e[1mhelp\e[0m - This help menu"
echo -e " \e[1mbmc\e[0m - Show BMC information" echo -e " \e[1mbmc\e[0m - Show BMC information"
echo -e " \e[1mhostname\e[0m - Set BMC hostname" echo -e " \e[1mhostname\e[0m - Set BMC hostname"
@ -60,33 +53,30 @@ setpassword() {
echo "Passwords to not match!" echo "Passwords to not match!"
fi fi
} }
resetsw() { resetsw_press() {
echo -n "Pressing reset switch... " echo "Pressing reset switch."
gpio mode 0 out echo "resetsw_press" > ${bmcd_cmdpipe}
gpio write 0 1
sleep 1
gpio write 0 0
sleep 1
echo "done."
} }
powersw() { powersw_press() {
if [ "$1" == "hard" ]; then echo "Holding power switch."
delay='sleep 10' echo "powersw_hold" > ${bmcd_cmdpipe}
echo -n "Holding power switch... "
else
delay='sleep 1'
echo -n "Pressing power switch... "
fi
gpio mode 1 out
gpio write 1 1
$delay
gpio write 1 0
sleep 2 sleep 2
echo "done." }
powersw_hold() {
echo "Pressing power switch."
echo "powersw_press" > ${bmcd_cmdpipe}
sleep 2
}
locate_on() {
echo "Enabling locator - host power LED will flash."
echo "locate_on" > ${bmcd_cmdpipe}
}
locate_off() {
echo "Disabling locator."
echo "locate_off" > ${bmcd_cmdpipe}
} }
readpower() { readpower() {
gpio mode 2 in powerstate_raw=$(cat ${bmcd_statepipe})
powerstate_raw=$(gpio read 2)
if [ "${powerstate_raw}" -eq 1 ]; then if [ "${powerstate_raw}" -eq 1 ]; then
powerstate="\e[32mOn\e[0m" powerstate="\e[32mOn\e[0m"
else else
@ -118,26 +108,37 @@ case $input in
;; ;;
'console') 'console')
echo "Starting console..." echo "Starting console..."
# Connect to screen, or start it
sudo screen -r serialconsole &>/dev/null || sudo screen -S serialconsole /dev/ttyUSB0 115200 sudo screen -r serialconsole &>/dev/null || sudo screen -S serialconsole /dev/ttyUSB0 115200
# If the user killed screen, restart it - just in case
pgrep screen &>/dev/null || sudo screen -S serialconsole /dev/ttyUSB0 115200
echo echo
;; ;;
'powersw') 'powersw')
powersw soft powersw_press
readpower readpower
echo -e "Host state: $powerstate" echo -e "Host state: $powerstate"
echo echo
;; ;;
'resetsw') 'resetsw')
resetsw resetsw_press
readpower readpower
echo -e "Host state: $powerstate" echo -e "Host state: $powerstate"
echo echo
;; ;;
'kill') 'kill')
powersw hard powersw_hold
readpower readpower
echo -e "Host state: $powerstate" echo -e "Host state: $powerstate"
echo echo
;;
'locate')
locate_on
echo
;;
'unlocate')
locate_off
echo
;; ;;
'help') 'help')
help help

117
bmcd
View File

@ -1,9 +1,9 @@
#!/usr/bin/python #!/usr/bin/env python
import socket, os, time, struct import socket, os, time, struct
from threading import Thread from threading import Thread, Event
from daemon import runner from daemon import runner
#import RPi.GPIO as GPIO import RPi.GPIO as GPIO
gpio_state = '11' gpio_state = '11'
gpio_psw = '12' gpio_psw = '12'
@ -14,21 +14,73 @@ bmcd_state = '/run/bmcd/bmcd.state'
bmcd_cmd = '/run/bmcd/bmcd.cmd' bmcd_cmd = '/run/bmcd/bmcd.cmd'
pidfile = '/run/bmcd/bmcd.pid' pidfile = '/run/bmcd/bmcd.pid'
class readcmd(Thread): is_pled_flashing = Event()
def run(self):
while True:
print "Hi"
line = fcmd.readline()
print line
line = ""
time.sleep(1)
class writestate(Thread): def powerled_on():
def run(self): GPIO.input(gpip_pled, 1)
while True:
print "Derp" def powerled_off():
fstate.write("Writing state\n") # Write str length and str GPIO.input(gpip_pled, 0)
time.sleep(1)
def powerled_flash(is_pled_flashing):
while is_pled_flashing.isSet():
GPIO.input(gpip_pled, 1)
time.sleep(1)
GPIO.input(gpip_pled, 0)
time.sleep(1)
is_pled_flashing.clear()
return
def powersw_press():
GPIO.input(gpip_psw, 1)
time.sleep(0.5)
GPIO.input(gpio_psw, 0)
def powersw_hold():
GPIO.input(gpip_psw, 1)
time.sleep(8)
GPIO.input(gpio_psw, 0)
def resetsw_press():
GPIO.input(gpip_rsw, 1)
time.sleep(0.5)
GPIO.input(gpio_rsw, 0)
def locate_on():
is_pled_flashing.set()
t = Thread(name='non-block', target=powerled_flash, args=(is_pled_flashing,))
t.start()
def locate_off():
is_pled_flashing.clear()
def readcmd():
fcmd = open(bmcd_cmd, 'r+b', 0)
while True:
line = fcmd.readline()
try:
globals()[line.rstrip()]()
except:
pass
def writestate(is_pled_flashing):
fstate = open(bmcd_state, 'w+b', 0)
state_prev = 1
while True:
state_now = GPIO.input(gpio_state)
if state_now != state_prev:
fstate.write(str(state_now))
state_prev = state_now
else:
pass
if not is_pled_flashing.isSet():
if state_now == 1:
powerled_on()
else:
powerled_off()
time.sleep(1)
class App(): class App():
@ -39,30 +91,25 @@ class App():
self.pidfile_path = pidfile self.pidfile_path = pidfile
self.pidfile_timeout = 5 self.pidfile_timeout = 5
def run(self): def run(self):
print "Starting daemon."
if not os.path.exists(bmcd_state): if not os.path.exists(bmcd_state):
os.mkfifo(bmcd_state) os.mkfifo(bmcd_state)
if not os.path.exists(bmcd_cmd): if not os.path.exists(bmcd_cmd):
os.mkfifo(bmcd_cmd) os.mkfifo(bmcd_cmd)
fstate = open(bmcd_state, 'w+b', 0) GPIO.setmode(GPIO.BOARD)
fcmd = open(bmcd_cmd, 'r+b', 0) GPIO.setup(gpio_state, GPIO.OUT)
GPIO.setup(gpio_psw, GPIO.OUT)
# thread.start_new_thread(readcmd(fcmd)) GPIO.setup(gpio_rsw, GPIO.IN)
# thread.start_new_thread(writestate(fstate)) GPIO.setup(gpio_pled, GPIO.OUT)
wthread = Thread(target=writestate(fstate))
rthread = Thread(target=readcmd(fcmd))
wthread.daemon = True
rthread.daemon = True
wthread.start()
rthread.start()
# GPIO.setmode(GPIO.BOARD)
# GPIO.setup(gpio_state, GPIO.OUT)
# GPIO.setup(gpio_psw, GPIO.OUT)
# GPIO.setup(gpio_rsw, GPIO.IN)
# GPIO.setup(gpio_pled, GPIO.OUT)
t1 = Thread(target=readcmd)
t2 = Thread(target=writestate, args=(is_pled_flashing,))
t1.setDaemon(True)
t2.setDaemon(True)
t1.start()
t2.start()
while True:
pass
app = App() app = App()
daemon_runner = runner.DaemonRunner(app) daemon_runner = runner.DaemonRunner(app)