Go to:
Gentoo Home
Documentation
Forums
Lists
Bugs
Planet
Store
Wiki
Get Gentoo!
Gentoo's Bugzilla – Attachment 101997 Details for
Bug 154670
sys-apps/baselayout starting ntp-client before network is ready
Home
|
New
–
[Ex]
|
Browse
|
Search
|
Privacy Policy
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
race illustration
test.html (text/html), 16.22 KB, created by
Daniel Drake (RETIRED)
on 2006-11-15 11:34:40 UTC
(
hide
)
Description:
race illustration
Filename:
MIME Type:
Creator:
Daniel Drake (RETIRED)
Created:
2006-11-15 11:34:40 UTC
Size:
16.22 KB
patch
obsolete
><font color=red>First process</font><br /> ><font color=blue>2nd process</font><br /><br /> > ><font color=red> >rc (or whatever) invokes net.ath0 start<br /> >we soon get to svc_start, and all of this code gets executed:<br /> > ><pre> >svc_start() { > local x= y= retval=0 startinactive= > > # Do not try to start if i have done so already on runlevel change > if is_runlevel_start && service_failed "${SVCNAME}" ; then > return 1 > elif service_started "${SVCNAME}" ; then > ewarn $"WARNING:" " ${SVCNAME}" $"has already been started." > return 0 > elif service_inactive "${SVCNAME}" ; then > if [[ ${IN_BACKGROUND} != "true" \ > && ${IN_BACKGROUND} != "1" ]] ; then > ewarn $"WARNING:" " ${SVCNAME}" $"has already been started." > return 0 > fi > elif [[ ${SOFTLEVEL} == "shutdown" || ${SOFTLEVEL} == "reboot" ]] ; then > ewarn $"WARNING: system shutting down, will not start" "${SVCNAME}" > return 1 > elif [[ ${SOFTLEVEL} == "single" ]] ; then > eerror $"ERROR: system is in single user mode, will not start" "${SVCNAME}" > return 1 > fi > > if ! <b>mark_service_starting "${SVCNAME}"</b> ; then > if service_stopping "${SVCNAME}" ; then > eerror $"ERROR:" " ${SVCNAME}" $"is already stopping." > else > eerror $"ERROR: "" ${SVCNAME}" $"is already starting." > fi > return 1 > fi > > if begin_service "${SVCNAME}" ; then > svcbegin=true > else > svcbegin=false > fi > > # Ensure that we clean up if we abort for any reason > trap "svc_quit" INT QUIT TSTP > > # Store our e* messages in a buffer so we pretty print when parallel > [[ ${RC_PARALLEL_STARTUP} == "yes" && ${RC_QUIET} != "yes" ]] \ > && ebuffer "${svcdir}/ebuffer/${SVCNAME}" > > veinfo $"Service" "${SVCNAME}" $"starting" > > if [[ -n $(rc-depend --notrace -broken "${SVCNAME}") ]] ; then > eerror $"ERROR: Some services needed are missing. Run" > eerror " ""'./${SVCNAME}" $"broken' for a list of those" > eerror " " $"services." "${SVCNAME}" $"was not started." > retval=1 > fi > > # Save the IN_BACKGROUND var as we need to clear it for starting depends > local ib_save="${IN_BACKGROUND}" > unset IN_BACKGROUND > > if [[ ${retval} == "0" && ${RC_NO_DEPS} != "yes" ]] ; then > # Start dependencies, if any. > local startsvc=$(rc-depend -ineed -iuse "${SVCNAME}") > if ! is_runlevel_start ; then > for x in ${startsvc} ; do > service_stopped "${x}" && start_service "${x}" > done > fi > > # Wait for dependencies to finish. > local ineed=$(rc-depend --notrace -ineed "${SVCNAME}") > for x in ${startsvc} $(rc-depend -iafter "${SVCNAME}") ; do > local timeout=3 > while [[ ${timeout} -gt 0 ]] ; do > service_started "${x}" && continue 2 > wait_service "${x}" > service_started "${x}" && continue 2 > if service_inactive "${x}" || service_scheduled "${x}" ; then > if [[ " ${startsvc} " == *" ${x} "* || > " ${ineed} " == *" $(rc-depend --notrace -iprovide "${x}") " ]] ; then > svc_schedule_start "${x}" "${SVCNAME}" > [[ -n ${startinactive} ]] && startinactive="${startinactive}, " > startinactive="${startinactive}${x}" > fi > > continue 2 > fi > service_stopped "${x}" && break > > ((timeout--)) > done > > [[ " ${ineed} " != *" ${x} "* ]] && continue > > eerror "ERROR:" $"cannot start" "${SVCNAME}" $"as" "${x}" $"could not start" > retval=1 > break > done > > if [[ -n ${startinactive} && ${retval} == "0" ]] ; then > # Change the last , to or for correct grammar. > x="${startinactive##*, }" > startinactive="${startinactive/%, ${x}/ or ${x}}" > ewarn "WARNING:" " ${SVCNAME}" $"is scheduled to start when" "${startinactive}" $"has started." > retval=1 > fi > fi > > if [[ ${retval} == "0" ]] ; then > IN_BACKGROUND="${ib_save}" > veindent > ( > [[ ${RC_QUIET} == "yes" ]] && RC_QUIET_STDOUT="yes" > exit() { > eerror $"DO NOT USE EXIT IN INIT.D SCRIPTS" > eerror $"This IS a bug, please fix your broken init.d" > unset -f exit > exit "$@" > } > > # Apply any ulimits if defined > [[ -n ${RC_ULIMIT} ]] && ulimit ${RC_ULIMIT} > > start > ) > retval="$?" ></pre> > >net.ath0 is now in state 'starting'.<br /><br /> > >The above "start" call eventually causes execution to get to wpa_supplicant_pre_start():<br /> > ><pre> >wpa_supplicant_pre_start() { > local iface="$1" opts= timeout= actfile= cfgfile= > > # We don't configure wireless if we're being called from > # the background unless we're not currently running > if ${IN_BACKGROUND} ; then > if service_started_daemon "net.${iface}" /sbin/wpa_supplicant ; then > if wpa_supplicant_exists "${iface}" ; then > ESSID=$(wpa_supplicant_get_essid "${iface}") > ESSIDVAR=$(bash_variable "${ESSID}") > save_options "ESSID" "${ESSID}" > metric=2000 > fi > return 0 > fi > fi > > save_options "ESSID" "" > > local ifvar=$(bash_variable "${iface}") > opts="wpa_supplicant_${ifvar}" > opts=" ${!opts} " > > # We only work on wirelesss interfaces unless a driver for wired > # has been defined > if [[ ${opts} != *" -Dwired "* && ${opts} != *" -D wired "* ]] ; then > if ! wpa_supplicant_exists "${iface}" ; then > veinfo $"wpa_supplicant only works on wireless" \ > "interfaces unless the -D wired option" \ > "is specified" > return 0 > fi > fi > > # If wireless-tools is installed, try and apply our user config > # This is needed for some drivers - such as hostap because they start > # the card in Master mode which causes problems with wpa_supplicant. > if is_function iwconfig_defaults ; then > if wpa_supplicant_exists "${iface}" ; then > iwconfig_defaults "${iface}" > iwconfig_user_config "${iface}" > fi > fi > > # Check for rf_kill - only ipw supports this at present, but other > # cards may in the future > if [[ -e "/sys/class/net/${iface}/device/rf_kill" ]] ; then > if [[ $( < "/sys/class/net/${iface}/device/rf_kill" ) != 0 ]] ; then > ewarn $"Wireless radio has been killed for interface" "${iface}" > local asc="associate_timeout_${ifvar}" > if [[ -n ${!asc} && ${!asc} -ge 0 ]] ; then > eerror $"As you have" "${asc}" $"set to 0 or" \ > $"greater we will abort as we cannot" \ > $"associate" > return 1 > fi > ewarn $"wpa_supplicant will launch, but not associate" \ > $"until wireles radio is re-enabled for" \ > $"interface" "${iface}" > fi > fi > > ebegin "Starting wpa_supplicant on" "${iface}" > > cfgfile="${opts##* -c}" > if [[ -n ${cfgfile} && ${cfgfile} != "${opts}" ]] ; then > [[ ${cfgfile:0:1} == " " ]] && cfgfile="${cfgfile# *}" > cfgfile="${cfgfile%% *}" > else > # Support new and old style locations > cfgfile="/etc/wpa_supplicant/wpa_supplicant-${iface}.conf" > [[ ! -e ${cfgfile} ]] \ > && cfgfile="/etc/wpa_supplicant/wpa_supplicant.conf" > [[ ! -e ${cfgfile} ]] \ > && cfgfile="/etc/wpa_supplicant.conf" > opts="${opts} -c${cfgfile}" > fi > > if [[ ! -f ${cfgfile} ]] ; then > eend 1 "/etc/wpa_supplicant/wpa_supplicant.conf" $"not found!" > return 1 > fi > > # Work out where the ctrl_interface dir is if it's not specified > local ctrl_dir=$(sed -n -e 's/[ \t]*#.*//g;s/[ \t]*$//g;s/^ctrl_interface=//p' "${cfgfile}") > if [[ -z ${ctrl_dir} ]] ; then > ctrl_dir="${opts##* -C}" > if [[ -n ${ctrl_dir} && ${ctrl_dir} != "${opts}" ]] ; then > [[ ${ctrl_dir:0:1} == " " ]] && ctrl_dir="${ctrl_dir# *}" > ctrl_dir="${ctrl_dir%% *}" > else > ctrl_dir="/var/run/wpa_supplicant" > opts="${opts} -C${ctrl_dir}" > fi > fi > save_options ctrl_dir "${ctrl_dir}" > > # Some drivers require the interface to be up > interface_up "${iface}" > > # wpa_supplicant 0.4.0 and greater supports wpa_cli actions > # This is very handy as if and when different association mechanisms are > # introduced to wpa_supplicant we don't have to recode for them as > # wpa_cli is now responsible for informing us of success/failure. > # The downside of this is that we don't see the interface being configured > # for DHCP/static. > actfile="/etc/wpa_supplicant/wpa_cli.sh" > opts="${opts} -W" > > eval start-stop-daemon --start --exec /sbin/wpa_supplicant \ > --pidfile "/var/run/wpa_supplicant-${iface}.pid" \ > -- "${opts}" -B -i"${iface}" \ > -P"/var/run/wpa_supplicant-${iface}.pid" > eend "$?" || return 1 > > # Starting wpa_supplication-0.4.0, we can get wpa_cli to > # start/stop our scripts from wpa_supplicant messages > <b>mark_service_inactive "net.${iface}"</b> > ebegin $"Starting wpa_cli on" "${iface}" > start-stop-daemon --start --exec /bin/wpa_cli \ > --pidfile "/var/run/wpa_cli-${iface}.pid" \ > -- -a"${actfile}" -p"${ctrl_dir}" -i"${iface}" \ > -P"/var/run/wpa_cli-${iface}.pid" -B > eend "$?" || return 1 ></pre> > >At this point, wpa_supplicant and wpa_cli are running and net.ath0 is now in state INACTIVE. > >When it's happy, wpa_supplicant starts scanning and associating. When it has associated, wpa_cli is notified, and wpa_cli launches /etc/wpa_supplicant/wpa_cli.sh<br /><br /> > ></font> ><font color=blue> >I'll call wpa_cli.sh the 2nd process. wpa_cli launches /etc/init.d/net.ath0 start and we eventually get back into svc_start():<br /><br /> > >[Note that the process 1 svc_start() hasnt finished yet, it's been preempted at the last mentioned point]<br /> > ><pre> >svc_start() { > local x= y= retval=0 startinactive= > > # Do not try to start if i have done so already on runlevel change > if is_runlevel_start && service_failed "${SVCNAME}" ; then > return 1 > elif service_started "${SVCNAME}" ; then > ewarn $"WARNING:" " ${SVCNAME}" $"has already been started." > return 0 > elif service_inactive "${SVCNAME}" ; then > if [[ ${IN_BACKGROUND} != "true" \ > && ${IN_BACKGROUND} != "1" ]] ; then > ewarn $"WARNING:" " ${SVCNAME}" $"has already been started." > return 0 > fi > elif [[ ${SOFTLEVEL} == "shutdown" || ${SOFTLEVEL} == "reboot" ]] ; then > ewarn $"WARNING: system shutting down, will not start" "${SVCNAME}" > return 1 > elif [[ ${SOFTLEVEL} == "single" ]] ; then > eerror $"ERROR: system is in single user mode, will not start" "${SVCNAME}" > return 1 > fi > > if ! <b>mark_service_starting "${SVCNAME}"</b> ; then > if service_stopping "${SVCNAME}" ; then > eerror $"ERROR:" " ${SVCNAME}" $"is already stopping." > else > eerror $"ERROR: "" ${SVCNAME}" $"is already starting." > fi > return 1 > fi ></pre> > >net.ath0 is now in state 'starting'<br /><br /> > ></font> > ><font color=red> >back to process 1. continuing where we left off, wpa_supplicant_assocaite gets called, which does nothing but "exit 0" and control is returned to svc_start():<br /> > ><pre> > # Don't trust init scripts to reset indentation properly > # Needed for ebuffer > eoutdent 99999 > > # If a service has been marked inactive, exit now as something > # may attempt to start it again later > if [[ ${retval} == "0" ]] && <b>service_inactive "${SVCNAME}" </b>; then > svcinactive=0 > ewarn $"WARNING:" " ${SVCNAME}" $"has started but is inactive" > if [[ ${RC_PARALLEL_STARTUP} == "yes" && ${RC_QUIET} != "yes" ]] ; then > eflush > ebuffer "" > fi > return 1 > fi > fi ></pre> > >the service_inactive check fails, so svc_start continues. > ><pre> > if [[ ${retval} != "0" ]] ; then > if [[ ${svcinactive} == "0" ]] ; then > mark_service_inactive "${SVCNAME}" > elif [[ -z ${startinactive} ]] ; then > mark_service_stopped "${SVCNAME}" > is_runlevel_start && mark_service_failed "${SVCNAME}" > eerror $"ERROR:" " ${SVCNAME}" $"failed to start" > fi > else > svcstarted=0 > <b>mark_service_started "${SVCNAME}"</b> > veinfo $"Service" "${SVCNAME}" $"started" > fi ></pre> > >net.ath0 is now in state STARTED even though we have no indication at all of the status of process 2 (this has not yet even thought about invoking dhcpcd).<br /><br /></font> > >to conclude I will highlight in red the inner part of critical section in which the race is significant. in this area, svc_start can run again in another process, and alter the state of the process, and therefore alter the behavior of the first process outside of the highlighted section.<br /><br /> > ><pre> >svc_start() { > local x= y= retval=0 startinactive= > > # Do not try to start if i have done so already on runlevel change > if is_runlevel_start && service_failed "${SVCNAME}" ; then > return 1 > elif service_started "${SVCNAME}" ; then > ewarn $"WARNING:" " ${SVCNAME}" $"has already been started." > return 0 > elif service_inactive "${SVCNAME}" ; then > if [[ ${IN_BACKGROUND} != "true" \ > && ${IN_BACKGROUND} != "1" ]] ; then > ewarn $"WARNING:" " ${SVCNAME}" $"has already been started." > return 0 > fi > elif [[ ${SOFTLEVEL} == "shutdown" || ${SOFTLEVEL} == "reboot" ]] ; then > ewarn $"WARNING: system shutting down, will not start" "${SVCNAME}" > return 1 > elif [[ ${SOFTLEVEL} == "single" ]] ; then > eerror $"ERROR: system is in single user mode, will not start" "${SVCNAME}" > return 1 > fi > > if ! mark_service_starting "${SVCNAME}" ; then > if service_stopping "${SVCNAME}" ; then > eerror $"ERROR:" " ${SVCNAME}" $"is already stopping." > else > eerror $"ERROR: "" ${SVCNAME}" $"is already starting." > fi > return 1 > fi > > if begin_service "${SVCNAME}" ; then > svcbegin=true > else > svcbegin=false > fi > > # Ensure that we clean up if we abort for any reason > trap "svc_quit" INT QUIT TSTP > > # Store our e* messages in a buffer so we pretty print when parallel > [[ ${RC_PARALLEL_STARTUP} == "yes" && ${RC_QUIET} != "yes" ]] \ > && ebuffer "${svcdir}/ebuffer/${SVCNAME}" > > veinfo $"Service" "${SVCNAME}" $"starting" > > if [[ -n $(rc-depend --notrace -broken "${SVCNAME}") ]] ; then > eerror $"ERROR: Some services needed are missing. Run" > eerror " ""'./${SVCNAME}" $"broken' for a list of those" > eerror " " $"services." "${SVCNAME}" $"was not started." > retval=1 > fi > > # Save the IN_BACKGROUND var as we need to clear it for starting depends > local ib_save="${IN_BACKGROUND}" > unset IN_BACKGROUND > > if [[ ${retval} == "0" && ${RC_NO_DEPS} != "yes" ]] ; then > # Start dependencies, if any. > local startsvc=$(rc-depend -ineed -iuse "${SVCNAME}") > if ! is_runlevel_start ; then > for x in ${startsvc} ; do > service_stopped "${x}" && start_service "${x}" > done > fi > > # Wait for dependencies to finish. > local ineed=$(rc-depend --notrace -ineed "${SVCNAME}") > for x in ${startsvc} $(rc-depend -iafter "${SVCNAME}") ; do > local timeout=3 > while [[ ${timeout} -gt 0 ]] ; do > service_started "${x}" && continue 2 > wait_service "${x}" > service_started "${x}" && continue 2 > if service_inactive "${x}" || service_scheduled "${x}" ; then > if [[ " ${startsvc} " == *" ${x} "* || > " ${ineed} " == *" $(rc-depend --notrace -iprovide "${x}") " ]] ; then > svc_schedule_start "${x}" "${SVCNAME}" > [[ -n ${startinactive} ]] && startinactive="${startinactive}, " > startinactive="${startinactive}${x}" > fi > > continue 2 > fi > service_stopped "${x}" && break > > ((timeout--)) > done > > [[ " ${ineed} " != *" ${x} "* ]] && continue > > eerror "ERROR:" $"cannot start" "${SVCNAME}" $"as" "${x}" $"could not start" > retval=1 > break > done > > if [[ -n ${startinactive} && ${retval} == "0" ]] ; then > # Change the last , to or for correct grammar. > x="${startinactive##*, }" > startinactive="${startinactive/%, ${x}/ or ${x}}" > ewarn "WARNING:" " ${SVCNAME}" $"is scheduled to start when" "${startinactive}" $"has started." > retval=1 > fi > fi > > if [[ ${retval} == "0" ]] ; then > IN_BACKGROUND="${ib_save}" > veindent > ( > [[ ${RC_QUIET} == "yes" ]] && RC_QUIET_STDOUT="yes" > exit() { > eerror $"DO NOT USE EXIT IN INIT.D SCRIPTS" > eerror $"This IS a bug, please fix your broken init.d" > unset -f exit > exit "$@" > } > > # Apply any ulimits if defined > [[ -n ${RC_ULIMIT} ]] && ulimit ${RC_ULIMIT} > > start > <font color=red>) > retval="$?" > > # Don't trust init scripts to reset indentation properly > # Needed for ebuffer > eoutdent 99999 > > # If a service has been marked inactive, exit now as something > # may attempt to start it again later > if [[ ${retval} == "0" ]] &&</font> service_inactive "${SVCNAME}" ; then > svcinactive=0 > ewarn $"WARNING:" " ${SVCNAME}" $"has started but is inactive" > if [[ ${RC_PARALLEL_STARTUP} == "yes" && ${RC_QUIET} != "yes" ]] ; then > eflush > ebuffer "" > fi > return 1 > fi > fi > > if [[ ${retval} != "0" ]] ; then > if [[ ${svcinactive} == "0" ]] ; then > mark_service_inactive "${SVCNAME}" > elif [[ -z ${startinactive} ]] ; then > mark_service_stopped "${SVCNAME}" > is_runlevel_start && mark_service_failed "${SVCNAME}" > eerror $"ERROR:" " ${SVCNAME}" $"failed to start" > fi > else > svcstarted=0 > mark_service_started "${SVCNAME}" > veinfo $"Service" "${SVCNAME}" $"started" > fi > > # Flush the ebuffer > if [[ ${RC_PARALLEL_STARTUP} == "yes" && ${RC_QUIET} != "yes" ]] ; then > eflush > ebuffer "" > fi > > if ${svcbegin} ; then > svcbegin=false > end_service "${SVCNAME}" > fi > > # Reset the trap > svc_trap > > return "${retval}" >} ></pre> >
<font color=red>First process</font><br /> <font color=blue>2nd process</font><br /><br /> <font color=red> rc (or whatever) invokes net.ath0 start<br /> we soon get to svc_start, and all of this code gets executed:<br /> <pre> svc_start() { local x= y= retval=0 startinactive= # Do not try to start if i have done so already on runlevel change if is_runlevel_start && service_failed "${SVCNAME}" ; then return 1 elif service_started "${SVCNAME}" ; then ewarn $"WARNING:" " ${SVCNAME}" $"has already been started." return 0 elif service_inactive "${SVCNAME}" ; then if [[ ${IN_BACKGROUND} != "true" \ && ${IN_BACKGROUND} != "1" ]] ; then ewarn $"WARNING:" " ${SVCNAME}" $"has already been started." return 0 fi elif [[ ${SOFTLEVEL} == "shutdown" || ${SOFTLEVEL} == "reboot" ]] ; then ewarn $"WARNING: system shutting down, will not start" "${SVCNAME}" return 1 elif [[ ${SOFTLEVEL} == "single" ]] ; then eerror $"ERROR: system is in single user mode, will not start" "${SVCNAME}" return 1 fi if ! <b>mark_service_starting "${SVCNAME}"</b> ; then if service_stopping "${SVCNAME}" ; then eerror $"ERROR:" " ${SVCNAME}" $"is already stopping." else eerror $"ERROR: "" ${SVCNAME}" $"is already starting." fi return 1 fi if begin_service "${SVCNAME}" ; then svcbegin=true else svcbegin=false fi # Ensure that we clean up if we abort for any reason trap "svc_quit" INT QUIT TSTP # Store our e* messages in a buffer so we pretty print when parallel [[ ${RC_PARALLEL_STARTUP} == "yes" && ${RC_QUIET} != "yes" ]] \ && ebuffer "${svcdir}/ebuffer/${SVCNAME}" veinfo $"Service" "${SVCNAME}" $"starting" if [[ -n $(rc-depend --notrace -broken "${SVCNAME}") ]] ; then eerror $"ERROR: Some services needed are missing. Run" eerror " ""'./${SVCNAME}" $"broken' for a list of those" eerror " " $"services." "${SVCNAME}" $"was not started." retval=1 fi # Save the IN_BACKGROUND var as we need to clear it for starting depends local ib_save="${IN_BACKGROUND}" unset IN_BACKGROUND if [[ ${retval} == "0" && ${RC_NO_DEPS} != "yes" ]] ; then # Start dependencies, if any. local startsvc=$(rc-depend -ineed -iuse "${SVCNAME}") if ! is_runlevel_start ; then for x in ${startsvc} ; do service_stopped "${x}" && start_service "${x}" done fi # Wait for dependencies to finish. local ineed=$(rc-depend --notrace -ineed "${SVCNAME}") for x in ${startsvc} $(rc-depend -iafter "${SVCNAME}") ; do local timeout=3 while [[ ${timeout} -gt 0 ]] ; do service_started "${x}" && continue 2 wait_service "${x}" service_started "${x}" && continue 2 if service_inactive "${x}" || service_scheduled "${x}" ; then if [[ " ${startsvc} " == *" ${x} "* || " ${ineed} " == *" $(rc-depend --notrace -iprovide "${x}") " ]] ; then svc_schedule_start "${x}" "${SVCNAME}" [[ -n ${startinactive} ]] && startinactive="${startinactive}, " startinactive="${startinactive}${x}" fi continue 2 fi service_stopped "${x}" && break ((timeout--)) done [[ " ${ineed} " != *" ${x} "* ]] && continue eerror "ERROR:" $"cannot start" "${SVCNAME}" $"as" "${x}" $"could not start" retval=1 break done if [[ -n ${startinactive} && ${retval} == "0" ]] ; then # Change the last , to or for correct grammar. x="${startinactive##*, }" startinactive="${startinactive/%, ${x}/ or ${x}}" ewarn "WARNING:" " ${SVCNAME}" $"is scheduled to start when" "${startinactive}" $"has started." retval=1 fi fi if [[ ${retval} == "0" ]] ; then IN_BACKGROUND="${ib_save}" veindent ( [[ ${RC_QUIET} == "yes" ]] && RC_QUIET_STDOUT="yes" exit() { eerror $"DO NOT USE EXIT IN INIT.D SCRIPTS" eerror $"This IS a bug, please fix your broken init.d" unset -f exit exit "$@" } # Apply any ulimits if defined [[ -n ${RC_ULIMIT} ]] && ulimit ${RC_ULIMIT} start ) retval="$?" </pre> net.ath0 is now in state 'starting'.<br /><br /> The above "start" call eventually causes execution to get to wpa_supplicant_pre_start():<br /> <pre> wpa_supplicant_pre_start() { local iface="$1" opts= timeout= actfile= cfgfile= # We don't configure wireless if we're being called from # the background unless we're not currently running if ${IN_BACKGROUND} ; then if service_started_daemon "net.${iface}" /sbin/wpa_supplicant ; then if wpa_supplicant_exists "${iface}" ; then ESSID=$(wpa_supplicant_get_essid "${iface}") ESSIDVAR=$(bash_variable "${ESSID}") save_options "ESSID" "${ESSID}" metric=2000 fi return 0 fi fi save_options "ESSID" "" local ifvar=$(bash_variable "${iface}") opts="wpa_supplicant_${ifvar}" opts=" ${!opts} " # We only work on wirelesss interfaces unless a driver for wired # has been defined if [[ ${opts} != *" -Dwired "* && ${opts} != *" -D wired "* ]] ; then if ! wpa_supplicant_exists "${iface}" ; then veinfo $"wpa_supplicant only works on wireless" \ "interfaces unless the -D wired option" \ "is specified" return 0 fi fi # If wireless-tools is installed, try and apply our user config # This is needed for some drivers - such as hostap because they start # the card in Master mode which causes problems with wpa_supplicant. if is_function iwconfig_defaults ; then if wpa_supplicant_exists "${iface}" ; then iwconfig_defaults "${iface}" iwconfig_user_config "${iface}" fi fi # Check for rf_kill - only ipw supports this at present, but other # cards may in the future if [[ -e "/sys/class/net/${iface}/device/rf_kill" ]] ; then if [[ $( < "/sys/class/net/${iface}/device/rf_kill" ) != 0 ]] ; then ewarn $"Wireless radio has been killed for interface" "${iface}" local asc="associate_timeout_${ifvar}" if [[ -n ${!asc} && ${!asc} -ge 0 ]] ; then eerror $"As you have" "${asc}" $"set to 0 or" \ $"greater we will abort as we cannot" \ $"associate" return 1 fi ewarn $"wpa_supplicant will launch, but not associate" \ $"until wireles radio is re-enabled for" \ $"interface" "${iface}" fi fi ebegin "Starting wpa_supplicant on" "${iface}" cfgfile="${opts##* -c}" if [[ -n ${cfgfile} && ${cfgfile} != "${opts}" ]] ; then [[ ${cfgfile:0:1} == " " ]] && cfgfile="${cfgfile# *}" cfgfile="${cfgfile%% *}" else # Support new and old style locations cfgfile="/etc/wpa_supplicant/wpa_supplicant-${iface}.conf" [[ ! -e ${cfgfile} ]] \ && cfgfile="/etc/wpa_supplicant/wpa_supplicant.conf" [[ ! -e ${cfgfile} ]] \ && cfgfile="/etc/wpa_supplicant.conf" opts="${opts} -c${cfgfile}" fi if [[ ! -f ${cfgfile} ]] ; then eend 1 "/etc/wpa_supplicant/wpa_supplicant.conf" $"not found!" return 1 fi # Work out where the ctrl_interface dir is if it's not specified local ctrl_dir=$(sed -n -e 's/[ \t]*#.*//g;s/[ \t]*$//g;s/^ctrl_interface=//p' "${cfgfile}") if [[ -z ${ctrl_dir} ]] ; then ctrl_dir="${opts##* -C}" if [[ -n ${ctrl_dir} && ${ctrl_dir} != "${opts}" ]] ; then [[ ${ctrl_dir:0:1} == " " ]] && ctrl_dir="${ctrl_dir# *}" ctrl_dir="${ctrl_dir%% *}" else ctrl_dir="/var/run/wpa_supplicant" opts="${opts} -C${ctrl_dir}" fi fi save_options ctrl_dir "${ctrl_dir}" # Some drivers require the interface to be up interface_up "${iface}" # wpa_supplicant 0.4.0 and greater supports wpa_cli actions # This is very handy as if and when different association mechanisms are # introduced to wpa_supplicant we don't have to recode for them as # wpa_cli is now responsible for informing us of success/failure. # The downside of this is that we don't see the interface being configured # for DHCP/static. actfile="/etc/wpa_supplicant/wpa_cli.sh" opts="${opts} -W" eval start-stop-daemon --start --exec /sbin/wpa_supplicant \ --pidfile "/var/run/wpa_supplicant-${iface}.pid" \ -- "${opts}" -B -i"${iface}" \ -P"/var/run/wpa_supplicant-${iface}.pid" eend "$?" || return 1 # Starting wpa_supplication-0.4.0, we can get wpa_cli to # start/stop our scripts from wpa_supplicant messages <b>mark_service_inactive "net.${iface}"</b> ebegin $"Starting wpa_cli on" "${iface}" start-stop-daemon --start --exec /bin/wpa_cli \ --pidfile "/var/run/wpa_cli-${iface}.pid" \ -- -a"${actfile}" -p"${ctrl_dir}" -i"${iface}" \ -P"/var/run/wpa_cli-${iface}.pid" -B eend "$?" || return 1 </pre> At this point, wpa_supplicant and wpa_cli are running and net.ath0 is now in state INACTIVE. When it's happy, wpa_supplicant starts scanning and associating. When it has associated, wpa_cli is notified, and wpa_cli launches /etc/wpa_supplicant/wpa_cli.sh<br /><br /> </font> <font color=blue> I'll call wpa_cli.sh the 2nd process. wpa_cli launches /etc/init.d/net.ath0 start and we eventually get back into svc_start():<br /><br /> [Note that the process 1 svc_start() hasnt finished yet, it's been preempted at the last mentioned point]<br /> <pre> svc_start() { local x= y= retval=0 startinactive= # Do not try to start if i have done so already on runlevel change if is_runlevel_start && service_failed "${SVCNAME}" ; then return 1 elif service_started "${SVCNAME}" ; then ewarn $"WARNING:" " ${SVCNAME}" $"has already been started." return 0 elif service_inactive "${SVCNAME}" ; then if [[ ${IN_BACKGROUND} != "true" \ && ${IN_BACKGROUND} != "1" ]] ; then ewarn $"WARNING:" " ${SVCNAME}" $"has already been started." return 0 fi elif [[ ${SOFTLEVEL} == "shutdown" || ${SOFTLEVEL} == "reboot" ]] ; then ewarn $"WARNING: system shutting down, will not start" "${SVCNAME}" return 1 elif [[ ${SOFTLEVEL} == "single" ]] ; then eerror $"ERROR: system is in single user mode, will not start" "${SVCNAME}" return 1 fi if ! <b>mark_service_starting "${SVCNAME}"</b> ; then if service_stopping "${SVCNAME}" ; then eerror $"ERROR:" " ${SVCNAME}" $"is already stopping." else eerror $"ERROR: "" ${SVCNAME}" $"is already starting." fi return 1 fi </pre> net.ath0 is now in state 'starting'<br /><br /> </font> <font color=red> back to process 1. continuing where we left off, wpa_supplicant_assocaite gets called, which does nothing but "exit 0" and control is returned to svc_start():<br /> <pre> # Don't trust init scripts to reset indentation properly # Needed for ebuffer eoutdent 99999 # If a service has been marked inactive, exit now as something # may attempt to start it again later if [[ ${retval} == "0" ]] && <b>service_inactive "${SVCNAME}" </b>; then svcinactive=0 ewarn $"WARNING:" " ${SVCNAME}" $"has started but is inactive" if [[ ${RC_PARALLEL_STARTUP} == "yes" && ${RC_QUIET} != "yes" ]] ; then eflush ebuffer "" fi return 1 fi fi </pre> the service_inactive check fails, so svc_start continues. <pre> if [[ ${retval} != "0" ]] ; then if [[ ${svcinactive} == "0" ]] ; then mark_service_inactive "${SVCNAME}" elif [[ -z ${startinactive} ]] ; then mark_service_stopped "${SVCNAME}" is_runlevel_start && mark_service_failed "${SVCNAME}" eerror $"ERROR:" " ${SVCNAME}" $"failed to start" fi else svcstarted=0 <b>mark_service_started "${SVCNAME}"</b> veinfo $"Service" "${SVCNAME}" $"started" fi </pre> net.ath0 is now in state STARTED even though we have no indication at all of the status of process 2 (this has not yet even thought about invoking dhcpcd).<br /><br /></font> to conclude I will highlight in red the inner part of critical section in which the race is significant. in this area, svc_start can run again in another process, and alter the state of the process, and therefore alter the behavior of the first process outside of the highlighted section.<br /><br /> <pre> svc_start() { local x= y= retval=0 startinactive= # Do not try to start if i have done so already on runlevel change if is_runlevel_start && service_failed "${SVCNAME}" ; then return 1 elif service_started "${SVCNAME}" ; then ewarn $"WARNING:" " ${SVCNAME}" $"has already been started." return 0 elif service_inactive "${SVCNAME}" ; then if [[ ${IN_BACKGROUND} != "true" \ && ${IN_BACKGROUND} != "1" ]] ; then ewarn $"WARNING:" " ${SVCNAME}" $"has already been started." return 0 fi elif [[ ${SOFTLEVEL} == "shutdown" || ${SOFTLEVEL} == "reboot" ]] ; then ewarn $"WARNING: system shutting down, will not start" "${SVCNAME}" return 1 elif [[ ${SOFTLEVEL} == "single" ]] ; then eerror $"ERROR: system is in single user mode, will not start" "${SVCNAME}" return 1 fi if ! mark_service_starting "${SVCNAME}" ; then if service_stopping "${SVCNAME}" ; then eerror $"ERROR:" " ${SVCNAME}" $"is already stopping." else eerror $"ERROR: "" ${SVCNAME}" $"is already starting." fi return 1 fi if begin_service "${SVCNAME}" ; then svcbegin=true else svcbegin=false fi # Ensure that we clean up if we abort for any reason trap "svc_quit" INT QUIT TSTP # Store our e* messages in a buffer so we pretty print when parallel [[ ${RC_PARALLEL_STARTUP} == "yes" && ${RC_QUIET} != "yes" ]] \ && ebuffer "${svcdir}/ebuffer/${SVCNAME}" veinfo $"Service" "${SVCNAME}" $"starting" if [[ -n $(rc-depend --notrace -broken "${SVCNAME}") ]] ; then eerror $"ERROR: Some services needed are missing. Run" eerror " ""'./${SVCNAME}" $"broken' for a list of those" eerror " " $"services." "${SVCNAME}" $"was not started." retval=1 fi # Save the IN_BACKGROUND var as we need to clear it for starting depends local ib_save="${IN_BACKGROUND}" unset IN_BACKGROUND if [[ ${retval} == "0" && ${RC_NO_DEPS} != "yes" ]] ; then # Start dependencies, if any. local startsvc=$(rc-depend -ineed -iuse "${SVCNAME}") if ! is_runlevel_start ; then for x in ${startsvc} ; do service_stopped "${x}" && start_service "${x}" done fi # Wait for dependencies to finish. local ineed=$(rc-depend --notrace -ineed "${SVCNAME}") for x in ${startsvc} $(rc-depend -iafter "${SVCNAME}") ; do local timeout=3 while [[ ${timeout} -gt 0 ]] ; do service_started "${x}" && continue 2 wait_service "${x}" service_started "${x}" && continue 2 if service_inactive "${x}" || service_scheduled "${x}" ; then if [[ " ${startsvc} " == *" ${x} "* || " ${ineed} " == *" $(rc-depend --notrace -iprovide "${x}") " ]] ; then svc_schedule_start "${x}" "${SVCNAME}" [[ -n ${startinactive} ]] && startinactive="${startinactive}, " startinactive="${startinactive}${x}" fi continue 2 fi service_stopped "${x}" && break ((timeout--)) done [[ " ${ineed} " != *" ${x} "* ]] && continue eerror "ERROR:" $"cannot start" "${SVCNAME}" $"as" "${x}" $"could not start" retval=1 break done if [[ -n ${startinactive} && ${retval} == "0" ]] ; then # Change the last , to or for correct grammar. x="${startinactive##*, }" startinactive="${startinactive/%, ${x}/ or ${x}}" ewarn "WARNING:" " ${SVCNAME}" $"is scheduled to start when" "${startinactive}" $"has started." retval=1 fi fi if [[ ${retval} == "0" ]] ; then IN_BACKGROUND="${ib_save}" veindent ( [[ ${RC_QUIET} == "yes" ]] && RC_QUIET_STDOUT="yes" exit() { eerror $"DO NOT USE EXIT IN INIT.D SCRIPTS" eerror $"This IS a bug, please fix your broken init.d" unset -f exit exit "$@" } # Apply any ulimits if defined [[ -n ${RC_ULIMIT} ]] && ulimit ${RC_ULIMIT} start <font color=red>) retval="$?" # Don't trust init scripts to reset indentation properly # Needed for ebuffer eoutdent 99999 # If a service has been marked inactive, exit now as something # may attempt to start it again later if [[ ${retval} == "0" ]] &&</font> service_inactive "${SVCNAME}" ; then svcinactive=0 ewarn $"WARNING:" " ${SVCNAME}" $"has started but is inactive" if [[ ${RC_PARALLEL_STARTUP} == "yes" && ${RC_QUIET} != "yes" ]] ; then eflush ebuffer "" fi return 1 fi fi if [[ ${retval} != "0" ]] ; then if [[ ${svcinactive} == "0" ]] ; then mark_service_inactive "${SVCNAME}" elif [[ -z ${startinactive} ]] ; then mark_service_stopped "${SVCNAME}" is_runlevel_start && mark_service_failed "${SVCNAME}" eerror $"ERROR:" " ${SVCNAME}" $"failed to start" fi else svcstarted=0 mark_service_started "${SVCNAME}" veinfo $"Service" "${SVCNAME}" $"started" fi # Flush the ebuffer if [[ ${RC_PARALLEL_STARTUP} == "yes" && ${RC_QUIET} != "yes" ]] ; then eflush ebuffer "" fi if ${svcbegin} ; then svcbegin=false end_service "${SVCNAME}" fi # Reset the trap svc_trap return "${retval}" } </pre>
View Attachment As Raw
Actions:
View
Attachments on
bug 154670
:
101886
| 101997