modemmanager: replace modem available check on start with the new monitore service
Before this change, the status of the sysfs paths from the kernel events was cached with a cache file. This is necessary to mark configured modems as available for the netifd. Using the new monitor service via the mmcli command 'mmcli -M' simplifies the whole process. There is no need to start sub shells in the background anymore that monitors whether the modem has already been added to the ModemManager. For this purpose, a new service was added that reacts on add and remove events for modems in the ModemManager and, if necessary, marks the logical netifd interface as available. Signed-off-by: Florian Eckert <fe@dev.tdt.de>
This commit is contained in:
parent
ebc9038721
commit
d9b5e06d19
6 changed files with 163 additions and 174 deletions
|
@ -92,6 +92,7 @@ define Package/modemmanager/install
|
|||
$(INSTALL_DIR) $(1)/usr/sbin
|
||||
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/ModemManager $(1)/usr/sbin
|
||||
$(INSTALL_BIN) ./files/usr/sbin/ModemManager-wrapper $(1)/usr/sbin
|
||||
$(INSTALL_BIN) ./files/usr/sbin/ModemManager-monitor $(1)/usr/sbin
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/bin
|
||||
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/mmcli $(1)/usr/bin
|
||||
|
@ -118,9 +119,6 @@ define Package/modemmanager/install
|
|||
$(INSTALL_DIR) $(1)/etc/init.d
|
||||
$(INSTALL_BIN) ./files/modemmanager.init $(1)/etc/init.d/modemmanager
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/hotplug.d/usb
|
||||
$(INSTALL_DATA) ./files/25-modemmanager-usb $(1)/etc/hotplug.d/usb
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/hotplug.d/net
|
||||
$(INSTALL_DATA) ./files/25-modemmanager-net $(1)/etc/hotplug.d/net
|
||||
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
#!/bin/sh
|
||||
# Copyright (C) 2019 Aleksander Morgado <aleksander@aleksander.es>
|
||||
|
||||
# We need to process only full USB device removal events, we don't
|
||||
# want to process specific interface removal events.
|
||||
[ "$ACTION" = remove ] || exit
|
||||
[ -z "${INTERFACE}" ] || exit
|
||||
|
||||
# Load common utilities
|
||||
. /usr/share/ModemManager/modemmanager.common
|
||||
|
||||
mm_clear_modem_wait_status "/sys${DEVPATH}"
|
||||
mm_cleanup_interface_by_sysfspath "/sys${DEVPATH}"
|
|
@ -13,7 +13,7 @@
|
|||
MODEMMANAGER_RUNDIR="/var/run/modemmanager"
|
||||
MODEMMANAGER_PID_FILE="${MODEMMANAGER_RUNDIR}/modemmanager.pid"
|
||||
MODEMMANAGER_CDCWDM_CACHE="${MODEMMANAGER_RUNDIR}/cdcwdm.cache"
|
||||
MODEMMANAGER_SYSFS_CACHE="${MODEMMANAGER_RUNDIR}/sysfs.cache"
|
||||
MODEMMANAGER_MONITOR_CACHE="${MODEMMANAGER_RUNDIR}/monitor.cache"
|
||||
MODEMMANAGER_EVENTS_CACHE="${MODEMMANAGER_RUNDIR}/events.cache"
|
||||
|
||||
################################################################################
|
||||
|
@ -92,48 +92,6 @@ mm_untrack_cdcwdm() {
|
|||
echo "${cdcwdm}"
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# ModemManager needs some time from the ports being added until a modem object
|
||||
# is exposed in DBus. With the logic here we do an explicit wait of N seconds
|
||||
# for ModemManager to expose the new modem object, making sure that the wait is
|
||||
# unique per device (i.e. per physical device sysfs path).
|
||||
|
||||
# Gets the modem wait status as retrieved from the cache
|
||||
mm_get_modem_wait_status() {
|
||||
local sysfspath="$1"
|
||||
|
||||
# If no sysfs cache file, we're done
|
||||
[ -f "${MODEMMANAGER_SYSFS_CACHE}" ] || return
|
||||
|
||||
# Get status of the sysfs path
|
||||
awk -v sysfspath="${sysfspath}" '!/^#/ && $0 ~ sysfspath { print $2 }' "${MODEMMANAGER_SYSFS_CACHE}"
|
||||
}
|
||||
|
||||
# Clear the modem wait status from the cache, if any
|
||||
mm_clear_modem_wait_status() {
|
||||
local sysfspath="$1"
|
||||
|
||||
local escaped_sysfspath
|
||||
|
||||
[ -f "${MODEMMANAGER_SYSFS_CACHE}" ] && {
|
||||
# escape '/', '\' and '&' for sed...
|
||||
escaped_sysfspath=$(echo "$sysfspath" | sed -e 's/[\/&]/\\&/g')
|
||||
sed -i "/${escaped_sysfspath}/d" "${MODEMMANAGER_SYSFS_CACHE}"
|
||||
}
|
||||
}
|
||||
|
||||
# Sets the modem wait status in the cache
|
||||
mm_set_modem_wait_status() {
|
||||
local sysfspath="$1"
|
||||
local status="$2"
|
||||
|
||||
# Remove sysfs line before adding the new one with the new state
|
||||
mm_clear_modem_wait_status "${sysfspath}"
|
||||
|
||||
# Add the new status
|
||||
echo "${sysfspath} ${status}" >> "${MODEMMANAGER_SYSFS_CACHE}"
|
||||
}
|
||||
|
||||
# Callback for config_foreach()
|
||||
mm_get_modem_config_foreach_cb() {
|
||||
local cfg="$1"
|
||||
|
@ -155,112 +113,6 @@ mm_get_modem_config() {
|
|||
config_foreach mm_get_modem_config_foreach_cb interface "${sysfspath}"
|
||||
}
|
||||
|
||||
# Wait for a modem in the specified sysfspath
|
||||
mm_wait_for_modem() {
|
||||
local cfg="$1"
|
||||
local sysfspath="$2"
|
||||
|
||||
# TODO: config max wait
|
||||
local n=45
|
||||
local step=5
|
||||
|
||||
while [ $n -ge 0 ]; do
|
||||
[ -d "${sysfspath}" ] || {
|
||||
mm_log "error" "ignoring modem detection request: no device at ${sysfspath}"
|
||||
proto_set_available "${cfg}" 0
|
||||
return 1
|
||||
}
|
||||
|
||||
# Check if the modem exists at the given sysfs path
|
||||
if ! mmcli -m "${sysfspath}" > /dev/null 2>&1
|
||||
then
|
||||
mm_log "error" "modem not detected at sysfs path"
|
||||
else
|
||||
mm_log "info" "modem exported successfully at ${sysfspath}"
|
||||
mm_log "info" "setting interface '${cfg}' as available"
|
||||
proto_set_available "${cfg}" 1
|
||||
return 0
|
||||
fi
|
||||
|
||||
sleep $step
|
||||
n=$((n-step))
|
||||
done
|
||||
|
||||
mm_log "error" "timed out waiting for the modem to get exported at ${sysfspath}"
|
||||
proto_set_available "${cfg}" 0
|
||||
return 2
|
||||
}
|
||||
|
||||
mm_report_modem_wait() {
|
||||
local sysfspath=$1
|
||||
|
||||
local parent_sysfspath status
|
||||
|
||||
parent_sysfspath=$(mm_find_physdev_sysfs_path "$sysfspath")
|
||||
[ -n "${parent_sysfspath}" ] || {
|
||||
mm_log "error" "parent device sysfspath not found"
|
||||
return
|
||||
}
|
||||
|
||||
status=$(mm_get_modem_wait_status "${parent_sysfspath}")
|
||||
case "${status}" in
|
||||
"")
|
||||
local cfg
|
||||
|
||||
cfg=$(mm_get_modem_config "${parent_sysfspath}")
|
||||
if [ -n "${cfg}" ]; then
|
||||
mm_log "info" "interface '${cfg}' is set to configure device '${parent_sysfspath}'"
|
||||
mm_log "info" "now waiting for modem at sysfs path ${parent_sysfspath}"
|
||||
mm_set_modem_wait_status "${parent_sysfspath}" "processed"
|
||||
# Launch subshell for the explicit wait
|
||||
( mm_wait_for_modem "${cfg}" "${parent_sysfspath}" ) > /dev/null 2>&1 &
|
||||
else
|
||||
mm_log "info" "no need to wait for modem at sysfs path ${parent_sysfspath}"
|
||||
mm_set_modem_wait_status "${parent_sysfspath}" "ignored"
|
||||
fi
|
||||
;;
|
||||
"processed")
|
||||
mm_log "info" "already waiting for modem at sysfs path ${parent_sysfspath}"
|
||||
;;
|
||||
"ignored")
|
||||
;;
|
||||
*)
|
||||
mm_log "error" "unknown status read for device at sysfs path ${parent_sysfspath}"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Cleanup interfaces
|
||||
|
||||
mm_cleanup_interfaces() {
|
||||
local sysfs_path status
|
||||
|
||||
# Do nothing if there is no sysfs cache
|
||||
[ -f "${MODEMMANAGER_SYSFS_CACHE}" ] || return
|
||||
|
||||
while IFS= read -r sysfs_cache_line; do
|
||||
sysfs_path=$(echo "${sysfs_cache_line}" | awk '{print $1}')
|
||||
status=$(echo "${sysfs_cache_line}" | awk '{print $2}')
|
||||
|
||||
if [ "${status}" = "processed" ]; then
|
||||
mm_log "debug" "call cleanup for: ${sysfs_path}"
|
||||
mm_cleanup_interface_by_sysfspath "${sysfs_path}"
|
||||
fi
|
||||
done < ${MODEMMANAGER_SYSFS_CACHE}
|
||||
}
|
||||
|
||||
mm_cleanup_interface_by_sysfspath() {
|
||||
local dev="$1"
|
||||
|
||||
local cfg
|
||||
cfg=$(mm_get_modem_config "$dev")
|
||||
[ -n "${cfg}" ] || return
|
||||
|
||||
mm_log "info" "setting interface '$cfg' as unavailable"
|
||||
proto_set_available "${cfg}" 0
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Event reporting
|
||||
|
||||
|
@ -295,13 +147,9 @@ mm_report_event() {
|
|||
# Report the event
|
||||
mm_log "debug" "Report event: action=${action}, name=${name}, subsystem=${subsystem}"
|
||||
result=$(mmcli --report-kernel-event="action=${action},name=${name},subsystem=${subsystem}" 2>&1)
|
||||
if [ "$?" -eq "0" ]; then
|
||||
# Wait for added modem if a sysfspath is given
|
||||
[ -n "${sysfspath}" ] && [ "$action" = "add" ] && mm_report_modem_wait "${sysfspath}"
|
||||
else
|
||||
if [ "$?" -ne "0" ]; then
|
||||
mm_log "error" "Couldn't report kernel event: ${result}"
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
mm_report_event_from_cache_line() {
|
||||
|
|
|
@ -21,11 +21,15 @@ start_service() {
|
|||
# wrapper script called '/usr/sbin/ModemManager-wrapper'.
|
||||
#
|
||||
. /usr/share/ModemManager/modemmanager.common
|
||||
procd_open_instance
|
||||
procd_open_instance "service"
|
||||
procd_set_param command /usr/sbin/ModemManager-wrapper
|
||||
procd_append_param command --log-level="$LOG_LEVEL"
|
||||
[ "$LOG_LEVEL" = "DEBUG" ] && procd_append_param command --debug
|
||||
procd_set_param respawn "${respawn_threshold:-3600}" "${respawn_timeout:-5}" "${respawn_retry:-5}"
|
||||
procd_set_param pidfile "${MODEMMANAGER_PID_FILE}"
|
||||
procd_close_instance
|
||||
procd_open_instance "monitor"
|
||||
procd_set_param command /usr/sbin/ModemManager-monitor
|
||||
procd_set_param respawn "${respawn_threshold:-3600}" "${respawn_timeout:-5}" "${respawn_retry:-5}"
|
||||
procd_close_instance
|
||||
}
|
||||
|
|
155
net/modemmanager/files/usr/sbin/ModemManager-monitor
Normal file
155
net/modemmanager/files/usr/sbin/ModemManager-monitor
Normal file
|
@ -0,0 +1,155 @@
|
|||
#!/bin/sh
|
||||
|
||||
. /lib/functions.sh
|
||||
. /lib/netifd/netifd-proto.sh
|
||||
. /usr/share/ModemManager/modemmanager.common
|
||||
|
||||
trap_with_arg() {
|
||||
func="$1" ; shift
|
||||
for sig ; do
|
||||
# shellcheck disable=SC2064
|
||||
trap "$func $sig" "$sig"
|
||||
done
|
||||
}
|
||||
|
||||
func_trap() {
|
||||
local monitor_cache_line object
|
||||
|
||||
logger "ModemManager-monitor[$$]" "Sending signal ${1} ..."
|
||||
|
||||
# Set all configured logical interfaces to unavailable
|
||||
while IFS= read -r monitor_cache_line; do
|
||||
object=$(echo "${monitor_cache_line}" | awk '{print $1}')
|
||||
mm_monitor_cache_remove "$object"
|
||||
done < ${MODEMMANAGER_MONITOR_CACHE}
|
||||
|
||||
kill "-${1}" "$CHILD" 2>/dev/null
|
||||
}
|
||||
|
||||
mm_monitor_get_sysfspath() {
|
||||
local object="$1"
|
||||
|
||||
# If no monitor cache file, we're done
|
||||
[ -f "${MODEMMANAGER_MONITOR_CACHE}" ] || return
|
||||
|
||||
awk -v object="${object}" '!/^#/ && $0 ~ object { print $2 }' "${MODEMMANAGER_MONITOR_CACHE}"
|
||||
}
|
||||
|
||||
mm_monitor_cache_remove() {
|
||||
local object="$1"
|
||||
|
||||
local device cfg
|
||||
|
||||
device=$(mm_monitor_get_sysfspath "${object}")
|
||||
|
||||
cfg=$(mm_get_modem_config "${device}")
|
||||
if [ -n "${cfg}" ]; then
|
||||
mm_log "debug" "interface '${cfg}' set '${device}' state unavailable"
|
||||
proto_set_available "${cfg}" 0
|
||||
fi
|
||||
|
||||
mm_log "debug" "delete object '$object' from monitore cache"
|
||||
|
||||
# On monitor remove event, remove old events from cache
|
||||
# Also substitute object path '/org/freedesktop/ModemManager1/Modem/<number>'
|
||||
# all '/' with '\/' to make sed happy with shell expansion
|
||||
sed -i "/${object//\//\\/}/d" "${MODEMMANAGER_MONITOR_CACHE}"
|
||||
}
|
||||
|
||||
mm_monitor_cache_add() {
|
||||
local object="$1"
|
||||
local modemstatus device sysfspath cfg
|
||||
|
||||
modemstatus="$(mmcli --modem="${object}" --output-keyvalue)"
|
||||
|
||||
device=$(modemmanager_get_field "${modemstatus}" "modem.generic.device")
|
||||
[ -n "${device}" ] || {
|
||||
mm_log "err" "No 'device' for object '$object' not found..."
|
||||
return 1
|
||||
}
|
||||
|
||||
sysfspath=$(modemmanager_get_field "${modemstatus}" "modem.generic.physdev")
|
||||
[ -n "${sysfspath}" ] || {
|
||||
mm_log "err" "No 'sysfspath' for object '$object' not found..."
|
||||
return 2
|
||||
}
|
||||
|
||||
mm_log "debug" "add object '$object' to monitore cache (device=${device},sysfspath=${sysfspath})"
|
||||
|
||||
# On monitor add event, store event details in cache (if not exists yet)
|
||||
grep -qs "${sysfspath}" "${MODEMMANAGER_MONITOR_CACHE}" || \
|
||||
echo "${object} ${device} ${sysfspath}" >> "${MODEMMANAGER_MONITOR_CACHE}"
|
||||
|
||||
cfg=$(mm_get_modem_config "${device}")
|
||||
if [ -n "${cfg}" ]; then
|
||||
mm_log "info" "interface '${cfg}' set '${device}' state available"
|
||||
proto_set_available "${cfg}" 1
|
||||
fi
|
||||
}
|
||||
|
||||
mm_monitor_cache_del() {
|
||||
local object="$1"
|
||||
|
||||
mm_monitor_cache_remove "$object"
|
||||
}
|
||||
|
||||
mm_monitor_cache() {
|
||||
local line="$1"
|
||||
local event object modemstatus device pyhsdev
|
||||
|
||||
event="$(echo "$line" | cut -d " " -f 1)"
|
||||
object="$(echo "$line" | cut -d " " -f 2)"
|
||||
|
||||
case "$event" in
|
||||
"(+)")
|
||||
mm_monitor_cache_add "$object"
|
||||
;;
|
||||
"(-)")
|
||||
mm_monitor_cache_del "$object"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
main() {
|
||||
|
||||
local n=60
|
||||
local step=1
|
||||
local mmrunning=0
|
||||
|
||||
trap_with_arg func_trap INT TERM KILL
|
||||
|
||||
mkdir -p "${MODEMMANAGER_RUNDIR}"
|
||||
chmod 0755 "${MODEMMANAGER_RUNDIR}"
|
||||
|
||||
# Wait for ModemManager to be available in the bus
|
||||
while [ $n -ge 0 ]; do
|
||||
sleep $step
|
||||
mm_log "info" "Checking if ModemManager is available..."
|
||||
|
||||
if ! /usr/bin/mmcli -L >/dev/null 2>&1; then
|
||||
mm_log "info" "ModemManager not yet available"
|
||||
else
|
||||
mmrunning=1
|
||||
break
|
||||
fi
|
||||
n=$((n-step))
|
||||
done
|
||||
|
||||
[ ${mmrunning} -eq 1 ] || {
|
||||
mm_log "error" "couldn't report initial kernel events: ModemManager not running"
|
||||
return
|
||||
}
|
||||
|
||||
/usr/bin/mmcli -M | {
|
||||
local line
|
||||
while read -r line; do
|
||||
mm_log "debug" "Monitor cache line: ${line}"
|
||||
mm_monitor_cache "$line"
|
||||
done
|
||||
} &
|
||||
CHILD="$!"
|
||||
|
||||
wait $CHILD
|
||||
}
|
||||
|
||||
main "$@"
|
|
@ -27,9 +27,6 @@ main() {
|
|||
mm_report_events_from_cache
|
||||
|
||||
wait "$CHILD"
|
||||
|
||||
# Set all configured interfaces as unavailable
|
||||
mm_cleanup_interfaces
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
|
Loading…
Reference in a new issue