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.

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