From: Florian Eckert Date: Wed, 17 Jan 2024 10:07:25 +0000 (+0100) Subject: modemmanager: replace modem available check on start with the new monitore service X-Git-Url: http://git.openwrt.org/feed/packages.git?a=commitdiff_plain;h=d9b5e06d19ce4983c39051d2c9009260af34f351;p=feed%2Fpackages.git 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 --- diff --git a/net/modemmanager/Makefile b/net/modemmanager/Makefile index f327b14860..7797578173 100644 --- a/net/modemmanager/Makefile +++ b/net/modemmanager/Makefile @@ -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 diff --git a/net/modemmanager/files/25-modemmanager-usb b/net/modemmanager/files/25-modemmanager-usb deleted file mode 100644 index 93d0bf70ac..0000000000 --- a/net/modemmanager/files/25-modemmanager-usb +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh -# Copyright (C) 2019 Aleksander Morgado - -# 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}" diff --git a/net/modemmanager/files/modemmanager.common b/net/modemmanager/files/modemmanager.common index 4daffb2c18..b3f86db9ac 100644 --- a/net/modemmanager/files/modemmanager.common +++ b/net/modemmanager/files/modemmanager.common @@ -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() { diff --git a/net/modemmanager/files/modemmanager.init b/net/modemmanager/files/modemmanager.init index a036d884da..ccc1953aea 100755 --- a/net/modemmanager/files/modemmanager.init +++ b/net/modemmanager/files/modemmanager.init @@ -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 } diff --git a/net/modemmanager/files/usr/sbin/ModemManager-monitor b/net/modemmanager/files/usr/sbin/ModemManager-monitor new file mode 100644 index 0000000000..8a88ab514b --- /dev/null +++ b/net/modemmanager/files/usr/sbin/ModemManager-monitor @@ -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/' + # 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 "$@" diff --git a/net/modemmanager/files/usr/sbin/ModemManager-wrapper b/net/modemmanager/files/usr/sbin/ModemManager-wrapper index 5ca530b165..b0f36c267a 100644 --- a/net/modemmanager/files/usr/sbin/ModemManager-wrapper +++ b/net/modemmanager/files/usr/sbin/ModemManager-wrapper @@ -27,9 +27,6 @@ main() { mm_report_events_from_cache wait "$CHILD" - - # Set all configured interfaces as unavailable - mm_cleanup_interfaces } main "$@"