You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

332 lines
9.0 KiB

modemmanager: increase initial timeout for event reporting When the ModemManager daemon is started by the init script, we're explicitly calling mm_report_events_from_cache() so that all the hotplug events that happened before that moment are properly notified to the newly launched daemon. This initial reporting of events does a wait for the ModemManager process to be available in DBus, and if the daemon isn't registered in the bus in a given time, the process is considered failed: Sun Sep 6 16:20:02 2020 ModemManager: hotplug: checking if ModemManager is available... Sun Sep 6 16:20:02 2020 ModemManager: hotplug: ModemManager not yet available Sun Sep 6 16:20:03 2020 [2180]: <info> ModemManager (version 1.14.6) starting in system bus... Sun Sep 6 16:20:03 2020 ModemManager: hotplug: checking if ModemManager is available... Sun Sep 6 16:20:04 2020 ModemManager: hotplug: ModemManager not yet available Sun Sep 6 16:20:05 2020 ModemManager: hotplug: checking if ModemManager is available... Sun Sep 6 16:20:05 2020 ModemManager: hotplug: ModemManager not yet available Sun Sep 6 16:20:06 2020 ModemManager: hotplug: checking if ModemManager is available... Sun Sep 6 16:20:06 2020 ModemManager: hotplug: ModemManager not yet available Sun Sep 6 16:20:07 2020 ModemManager: hotplug: checking if ModemManager is available... Sun Sep 6 16:20:07 2020 ModemManager: hotplug: ModemManager not yet available Sun Sep 6 16:20:08 2020 ModemManager: hotplug: checking if ModemManager is available... Sun Sep 6 16:20:08 2020 ModemManager: hotplug: ModemManager not yet available Sun Sep 6 16:20:09 2020 ModemManager: hotplug: checking if ModemManager is available... Sun Sep 6 16:20:09 2020 ModemManager: hotplug: ModemManager not yet available Sun Sep 6 16:20:10 2020 ModemManager: hotplug: checking if ModemManager is available... Sun Sep 6 16:20:10 2020 ModemManager: hotplug: ModemManager not yet available Sun Sep 6 16:20:11 2020 ModemManager: hotplug: checking if ModemManager is available... Sun Sep 6 16:20:11 2020 ModemManager: hotplug: ModemManager not yet available Sun Sep 6 16:20:12 2020 ModemManager: hotplug: checking if ModemManager is available... Sun Sep 6 16:20:12 2020 ModemManager: hotplug: ModemManager not yet available Sun Sep 6 16:20:12 2020 ModemManager: hotplug: error: couldn't report initial kernel events: ModemManager not running Update the default wait time for this initial event notification from 10s to 60s, because there are cases where the daemon is slower to boot, e.g. during the first boot after a sysupgrade. Signed-off-by: Aleksander Morgado <aleksander@aleksander.es>
4 years ago
  1. #!/bin/sh
  2. # Copyright (C) 2016 Velocloud Inc
  3. # Copyright (C) 2016 Aleksander Morgado <aleksander@aleksander.es>
  4. ################################################################################
  5. . /lib/functions.sh
  6. . /lib/netifd/netifd-proto.sh
  7. ################################################################################
  8. # Runtime state
  9. MODEMMANAGER_RUNDIR="/var/run/modemmanager"
  10. MODEMMANAGER_PID_FILE="${MODEMMANAGER_RUNDIR}/modemmanager.pid"
  11. MODEMMANAGER_CDCWDM_CACHE="${MODEMMANAGER_RUNDIR}/cdcwdm.cache"
  12. MODEMMANAGER_SYSFS_CACHE="${MODEMMANAGER_RUNDIR}/sysfs.cache"
  13. MODEMMANAGER_EVENTS_CACHE="${MODEMMANAGER_RUNDIR}/events.cache"
  14. ################################################################################
  15. # Common logging
  16. mm_log() {
  17. logger -t "ModemManager" "hotplug: $*"
  18. }
  19. ################################################################################
  20. # Receives as input argument the full sysfs path of the device
  21. # Returns the physical device sysfs path
  22. #
  23. # NOTE: this method only works when the device exists, i.e. it cannot be used
  24. # on removal hotplug events
  25. mm_find_physdev_sysfs_path() {
  26. local tmp_path="$1"
  27. while true; do
  28. tmp_path=$(dirname "${tmp_path}")
  29. # avoid infinite loops iterating
  30. [ -z "${tmp_path}" ] || [ "${tmp_path}" = "/" ] && return
  31. # the physical device will be that with a idVendor and idProduct pair of files
  32. [ -f "${tmp_path}"/idVendor ] && [ -f "${tmp_path}"/idProduct ] && {
  33. tmp_path=$(readlink -f "$tmp_path")
  34. echo "${tmp_path}"
  35. return
  36. }
  37. done
  38. }
  39. ################################################################################
  40. # Returns the cdc-wdm name retrieved from sysfs
  41. mm_track_cdcwdm() {
  42. local wwan="$1"
  43. local cdcwdm
  44. cdcwdm=$(ls "/sys/class/net/${wwan}/device/usbmisc/")
  45. [ -n "${cdcwdm}" ] || return
  46. # We have to cache it for later, as we won't be able to get the
  47. # associated cdc-wdm device on a remove event
  48. echo "${wwan} ${cdcwdm}" >> "${MODEMMANAGER_CDCWDM_CACHE}"
  49. echo "${cdcwdm}"
  50. }
  51. # Returns the cdc-wdm name retrieved from the cache
  52. mm_untrack_cdcwdm() {
  53. local wwan="$1"
  54. local cdcwdm
  55. # Look for the cached associated cdc-wdm device
  56. [ -f "${MODEMMANAGER_CDCWDM_CACHE}" ] || return
  57. cdcwdm=$(awk -v wwan="${wwan}" '!/^#/ && $0 ~ wwan { print $2 }' "${MODEMMANAGER_CDCWDM_CACHE}")
  58. [ -n "${cdcwdm}" ] || return
  59. # Remove from cache
  60. sed -i "/${wwan} ${cdcwdm}/d" "${MODEMMANAGER_CDCWDM_CACHE}"
  61. echo "${cdcwdm}"
  62. }
  63. ################################################################################
  64. # ModemManager needs some time from the ports being added until a modem object
  65. # is exposed in DBus. With the logic here we do an explicit wait of N seconds
  66. # for ModemManager to expose the new modem object, making sure that the wait is
  67. # unique per device (i.e. per physical device sysfs path).
  68. # Gets the modem wait status as retrieved from the cache
  69. mm_get_modem_wait_status() {
  70. local sysfspath="$1"
  71. # If no sysfs cache file, we're done
  72. [ -f "${MODEMMANAGER_SYSFS_CACHE}" ] || return
  73. # Get status of the sysfs path
  74. awk -v sysfspath="${sysfspath}" '!/^#/ && $0 ~ sysfspath { print $2 }' "${MODEMMANAGER_SYSFS_CACHE}"
  75. }
  76. # Clear the modem wait status from the cache, if any
  77. mm_clear_modem_wait_status() {
  78. local sysfspath="$1"
  79. local escaped_sysfspath
  80. [ -f "${MODEMMANAGER_SYSFS_CACHE}" ] && {
  81. # escape '/', '\' and '&' for sed...
  82. escaped_sysfspath=$(echo "$sysfspath" | sed -e 's/[\/&]/\\&/g')
  83. sed -i "/${escaped_sysfspath}/d" "${MODEMMANAGER_SYSFS_CACHE}"
  84. }
  85. }
  86. # Sets the modem wait status in the cache
  87. mm_set_modem_wait_status() {
  88. local sysfspath="$1"
  89. local status="$2"
  90. # Remove sysfs line before adding the new one with the new state
  91. mm_clear_modem_wait_status "${sysfspath}"
  92. # Add the new status
  93. echo "${sysfspath} ${status}" >> "${MODEMMANAGER_SYSFS_CACHE}"
  94. }
  95. # Callback for config_foreach()
  96. mm_get_modem_config_foreach_cb() {
  97. local cfg="$1"
  98. local sysfspath="$2"
  99. local proto
  100. config_get proto "${cfg}" proto
  101. [ "${proto}" = modemmanager ] || return 0
  102. local dev
  103. dev=$(uci_get network "${cfg}" device)
  104. [ "${dev}" = "${sysfspath}" ] || return 0
  105. echo "${cfg}"
  106. }
  107. # Returns the name of the interface configured for this device
  108. mm_get_modem_config() {
  109. local sysfspath="$1"
  110. # Look for configuration for the given sysfs path
  111. config_load network
  112. config_foreach mm_get_modem_config_foreach_cb interface "${sysfspath}"
  113. }
  114. # Wait for a modem in the specified sysfspath
  115. mm_wait_for_modem() {
  116. local cfg="$1"
  117. local sysfspath="$2"
  118. # TODO: config max wait
  119. local n=45
  120. local step=5
  121. while [ $n -ge 0 ]; do
  122. [ -d "${sysfspath}" ] || {
  123. mm_log "error: ignoring modem detection request: no device at ${sysfspath}"
  124. proto_set_available "${cfg}" 0
  125. return 1
  126. }
  127. # Check if the modem exists at the given sysfs path
  128. if ! mmcli -m "${sysfspath}" > /dev/null 2>&1
  129. then
  130. mm_log "error: modem not detected at sysfs path"
  131. else
  132. mm_log "modem exported successfully at ${sysfspath}"
  133. mm_log "setting interface '${cfg}' as available"
  134. proto_set_available "${cfg}" 1
  135. return 0
  136. fi
  137. sleep $step
  138. n=$((n-step))
  139. done
  140. mm_log "error: timed out waiting for the modem to get exported at ${sysfspath}"
  141. proto_set_available "${cfg}" 0
  142. return 2
  143. }
  144. mm_report_modem_wait() {
  145. local sysfspath=$1
  146. local parent_sysfspath status
  147. parent_sysfspath=$(mm_find_physdev_sysfs_path "$sysfspath")
  148. [ -n "${parent_sysfspath}" ] || {
  149. mm_log "error: parent device sysfspath not found"
  150. return
  151. }
  152. status=$(mm_get_modem_wait_status "${parent_sysfspath}")
  153. case "${status}" in
  154. "")
  155. local cfg
  156. cfg=$(mm_get_modem_config "${parent_sysfspath}")
  157. if [ -n "${cfg}" ]; then
  158. mm_log "interface '${cfg}' is set to configure device '${parent_sysfspath}'"
  159. mm_log "now waiting for modem at sysfs path ${parent_sysfspath}"
  160. mm_set_modem_wait_status "${parent_sysfspath}" "processed"
  161. # Launch subshell for the explicit wait
  162. ( mm_wait_for_modem "${cfg}" "${parent_sysfspath}" ) > /dev/null 2>&1 &
  163. else
  164. mm_log "no need to wait for modem at sysfs path ${parent_sysfspath}"
  165. mm_set_modem_wait_status "${parent_sysfspath}" "ignored"
  166. fi
  167. ;;
  168. "processed")
  169. mm_log "already waiting for modem at sysfs path ${parent_sysfspath}"
  170. ;;
  171. "ignored")
  172. ;;
  173. *)
  174. mm_log "error: unknown status read for device at sysfs path ${parent_sysfspath}"
  175. ;;
  176. esac
  177. }
  178. ################################################################################
  179. # Cleanup interfaces
  180. mm_cleanup_interface_cb() {
  181. local cfg="$1"
  182. local proto
  183. config_get proto "${cfg}" proto
  184. [ "${proto}" = modemmanager ] || return 0
  185. proto_set_available "${cfg}" 0
  186. }
  187. mm_cleanup_interfaces() {
  188. config_load network
  189. config_foreach mm_cleanup_interface_cb interface
  190. }
  191. mm_cleanup_interface_by_sysfspath() {
  192. local dev="$1"
  193. local cfg
  194. cfg=$(mm_get_modem_config "$dev")
  195. [ -n "${cfg}" ] || return
  196. mm_log "setting interface '$cfg' as unavailable"
  197. proto_set_available "${cfg}" 0
  198. }
  199. ################################################################################
  200. # Event reporting
  201. # Receives as input the action, the device name and the subsystem
  202. mm_report_event() {
  203. local action="$1"
  204. local name="$2"
  205. local subsystem="$3"
  206. local sysfspath="$4"
  207. # Track/untrack events in cache
  208. case "${action}" in
  209. "add")
  210. # On add events, store event details in cache (if not exists yet)
  211. grep -qs "${name},${subsystem}" "${MODEMMANAGER_EVENTS_CACHE}" || \
  212. echo "${action},${name},${subsystem},${sysfspath}" >> "${MODEMMANAGER_EVENTS_CACHE}"
  213. ;;
  214. "remove")
  215. # On remove events, remove old events from cache (match by subsystem+name)
  216. sed -i "/${name},${subsystem}/d" "${MODEMMANAGER_EVENTS_CACHE}"
  217. ;;
  218. esac
  219. # Report the event
  220. mm_log "event reported: action=${action}, name=${name}, subsystem=${subsystem}"
  221. mmcli --report-kernel-event="action=${action},name=${name},subsystem=${subsystem}" 1>/dev/null 2>&1 &
  222. # Wait for added modem if a sysfspath is given
  223. [ -n "${sysfspath}" ] && [ "$action" = "add" ] && mm_report_modem_wait "${sysfspath}"
  224. }
  225. mm_report_event_from_cache_line() {
  226. local event_line="$1"
  227. local action name subsystem sysfspath
  228. action=$(echo "${event_line}" | awk -F ',' '{ print $1 }')
  229. name=$(echo "${event_line}" | awk -F ',' '{ print $2 }')
  230. subsystem=$(echo "${event_line}" | awk -F ',' '{ print $3 }')
  231. sysfspath=$(echo "${event_line}" | awk -F ',' '{ print $4 }')
  232. mm_log "cached event found: action=${action}, name=${name}, subsystem=${subsystem}, sysfspath=${sysfspath}"
  233. mm_report_event "${action}" "${name}" "${subsystem}" "${sysfspath}"
  234. }
  235. mm_report_events_from_cache() {
  236. # Remove the sysfs cache
  237. rm -f "${MODEMMANAGER_SYSFS_CACHE}"
  238. local n=60
  239. local step=1
  240. local mmrunning=0
  241. # Wait for ModemManager to be available in the bus
  242. while [ $n -ge 0 ]; do
  243. sleep $step
  244. mm_log "checking if ModemManager is available..."
  245. if ! mmcli -L >/dev/null 2>&1
  246. then
  247. mm_log "ModemManager not yet available"
  248. else
  249. mmrunning=1
  250. break
  251. fi
  252. n=$((n-step))
  253. done
  254. [ ${mmrunning} -eq 1 ] || {
  255. mm_log "error: couldn't report initial kernel events: ModemManager not running"
  256. return
  257. }
  258. # Report cached kernel events
  259. while IFS= read -r event_line; do
  260. mm_report_event_from_cache_line "${event_line}"
  261. done < ${MODEMMANAGER_EVENTS_CACHE}
  262. }