[arch-projects] [netctl][PATCH 2/3] Distinguish between network{, -online}.target (FS#50476)

Jouke Witteveen j.witteveen at gmail.com
Wed Sep 28 10:06:42 UTC 2016


The netctl@ service now yields before obtaining an IP address. This
prevents holding up network.target unnecessarily. Waiting for profiles
to obtain an IP address is possible through

1) The netctl-wait-online service
This service waits until all enabled profiles have obtained an address.
It is ordered before network-online.target so that this target is now
correctly implemented in netctl.

2) The wait-online <PROFILE> subcommand to netctl
This command waits for a started profile to obtain an address.
---
 contrib/bash-completion             |  4 ++--
 contrib/zsh-completion              |  3 ++-
 docs/netctl.1.txt                   |  4 ++++
 docs/netctl.special.7.txt           | 16 +++++++++++---
 services/netctl-ifplugd at .service    |  1 +
 services/netctl-wait-online.service | 13 +++++++++++
 services/netctl at .service            |  3 ++-
 src/lib/connections/mobile_ppp      |  3 +++
 src/lib/connections/pppoe           |  1 +
 src/lib/globals                     |  6 ++++++
 src/lib/ip                          |  2 ++
 src/lib/network                     | 40 ++++++++++++++++++++++++++++------
 src/netctl.in                       | 43 ++++++++++++++++++++++++-------------
 13 files changed, 111 insertions(+), 28 deletions(-)
 create mode 100644 services/netctl-wait-online.service

diff --git a/contrib/bash-completion b/contrib/bash-completion
index 2ab6acb..5f78355 100644
--- a/contrib/bash-completion
+++ b/contrib/bash-completion
@@ -23,10 +23,10 @@ _netctl()
 
     case $COMP_CWORD in
       1)
-        COMPREPLY=( $(compgen -W "--help --version list store restore stop-all start stop restart switch-to is-active status enable disable reenable is-enabled edit" -- "$cur") )
+        COMPREPLY=( $(compgen -W "--help --version list store restore stop-all start stop restart switch-to is-active status enable disable reenable is-enabled edit wait-online" -- "$cur") )
       ;;
       2)
-        [[ ${COMP_WORDS[COMP_CWORD-1]} = @(start|stop|restart|switch-to|is-active|status|enable|disable|reenable|is-enabled|edit) ]] &&
+        [[ ${COMP_WORDS[COMP_CWORD-1]} = @(start|stop|restart|switch-to|is-active|status|enable|disable|reenable|is-enabled|edit|wait-online) ]] &&
           mapfile -t COMPREPLY < <(IFS=$'\n'; compgen -W "$(_netctl_profiles)" -- "$cur")
       ;;
     esac
diff --git a/contrib/zsh-completion b/contrib/zsh-completion
index f583526..00cef81 100644
--- a/contrib/zsh-completion
+++ b/contrib/zsh-completion
@@ -11,7 +11,7 @@ _wireless_interfaces() {
 
 (( $+function[_netctl_command] )) ||
 _netctl_command() {
-    [[ $words[1] = (start|stop|restart|switch-to|is-active|status|enable|disable|reenable|is-enabled|edit) ]] &&
+    [[ $words[1] = (start|stop|restart|switch-to|is-active|status|enable|disable|reenable|is-enabled|edit|wait-online) ]] &&
       compadd "${(f)$(find -L /etc/netctl -maxdepth 1 -type f -not -name '.*' -not -name '*~' -not -name '*.conf' -not -name '*.service' -printf "%f\n")}"
 }
 
@@ -34,6 +34,7 @@ _netctl_commands() {
         'reenable:Reenable the systemd unit for a profile'
         'is-enabled:Check whether a profile is enabled'
         'edit:Edit a profile'
+        'wait-online:Wait for a profile to finish connecting'
       )
     _describe "netctl commands" _commands
 }
diff --git a/docs/netctl.1.txt b/docs/netctl.1.txt
index 4fe87ca..0f63de4 100644
--- a/docs/netctl.1.txt
+++ b/docs/netctl.1.txt
@@ -84,6 +84,10 @@ The following commands are understood:
     Open the file of the specified profile in an editor. This does not
     reenable or restart any profiles.
 
+*wait-online [+PROFILE+]*::
+    Wait until the interface of the profile has a routable IP address of
+    some kind.
+
 
 EXIT STATUS
 -----------
diff --git a/docs/netctl.special.7.txt b/docs/netctl.special.7.txt
index a4e0911..da1c2ec 100644
--- a/docs/netctl.special.7.txt
+++ b/docs/netctl.special.7.txt
@@ -8,7 +8,8 @@ netctl.special - Special netctl systemd units
 
 SYNOPSIS
 --------
-netctl.service, netctl-auto.service, netctl-ifplugd.service
+netctl.service, netctl-auto.service, netctl-ifplugd.service,
+netctl-wait-online.service
 
 
 DESCRIPTION
@@ -23,7 +24,7 @@ SPECIAL UNITS
 netctl.service::
     When started, this unit tries to start the profiles that were
     running when the unit was last stopped. In some cases, the interface
-    a profile binds to might not be available yet, when netctl.service
+    a profile binds to might not be available yet, when 'netctl.service'
     tries to bring a profile up. A simple, hackish, solution is to do:
 --------------------------------------------------------------------
 echo "[[ -t 0 ]] || sleep 3" > /etc/netctl/interfaces/<interface>
@@ -56,7 +57,16 @@ netctl-ifplugd@<interface>.service::
     This unit starts ifplugd on the interface it is used for. It will
     try to start a netctl profile whenever a cable is plugged into the
     interface and stop the profile when the cable is unplugged. Note
-    that this unit does not provide network.target.
+    that this unit does not provide 'network.target'.
+
+netctl-wait-online.service::
+    When activated, this unit waits for all enabled netctl profiles to
+    come online. Enabling this unit causes 'network-online.target' to
+    only be reached once all enabled netctl profiles are fully
+    connected. The maximum time, in seconds, to wait for profiles can be
+    passed to this unit via 'TIMEOUT_ONLINE='. The default value is
+    +120+, but the service itself may time out sooner. If a timeout
+    occurs, the service enters a failed state.
 
 
 SEE ALSO
diff --git a/services/netctl-ifplugd at .service b/services/netctl-ifplugd at .service
index 2cea97b..76993b9 100644
--- a/services/netctl-ifplugd at .service
+++ b/services/netctl-ifplugd at .service
@@ -5,6 +5,7 @@ BindsTo=sys-subsystem-net-devices-%i.device
 After=sys-subsystem-net-devices-%i.device network-pre.target
 
 [Service]
+NotifyAccess=all
 ExecStart=/usr/bin/ifplugd -i %I -r /etc/ifplugd/netctl.action -bfIns
 
 [Install]
diff --git a/services/netctl-wait-online.service b/services/netctl-wait-online.service
new file mode 100644
index 0000000..64ca06d
--- /dev/null
+++ b/services/netctl-wait-online.service
@@ -0,0 +1,13 @@
+[Unit]
+Description=Wait for the enabled netctl profiles to come online
+Documentation=man:netctl.special(7)
+After=network.target
+Before=network-online.target
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart=/usr/lib/network/network wait-online
+
+[Install]
+WantedBy=network-online.target
diff --git a/services/netctl at .service b/services/netctl at .service
index b8a19c2..d9c637d 100644
--- a/services/netctl at .service
+++ b/services/netctl at .service
@@ -6,7 +6,8 @@ Before=network.target netctl.service
 Wants=network.target
 
 [Service]
-Type=oneshot
+Type=notify
+NotifyAccess=all
 RemainAfterExit=yes
 ExecStart=/usr/lib/network/network start %I
 ExecStop=/usr/lib/network/network stop %I
diff --git a/src/lib/connections/mobile_ppp b/src/lib/connections/mobile_ppp
index 78851f3..0890373 100644
--- a/src/lib/connections/mobile_ppp
+++ b/src/lib/connections/mobile_ppp
@@ -12,6 +12,9 @@ quote_word() {
 
 mobile_ppp_up() {
     local options_dir="$STATE_DIR/mobile_ppp.$Interface.$Profile"
+
+    network_ready
+
     mkdir -p "$options_dir"
     if [[ -z $ChatScript ]]; then
         ChatScript="$options_dir/modem.chat"
diff --git a/src/lib/connections/pppoe b/src/lib/connections/pppoe
index 36746a8..15e4db5 100644
--- a/src/lib/connections/pppoe
+++ b/src/lib/connections/pppoe
@@ -18,6 +18,7 @@ pppoe_up() {
         report_error "Failed to bring interface '$Interface' up"
         return 1
     fi
+    network_ready
 
     mkdir -p "$(dirname "$options")"
     cat >> "$options" << EOF
diff --git a/src/lib/globals b/src/lib/globals
index a9e9751..f4e64a3 100644
--- a/src/lib/globals
+++ b/src/lib/globals
@@ -141,6 +141,12 @@ sd_call() {
     systemctl $command $(systemd-escape --template=netctl at .service "$@")
 }
 
+## Retrieves the status string from the unit for a profile
+# $1: profile name
+sd_status_text() {
+    sd_call status "$1" | sed -n 's/^ *Status: "\(.*\)"$/\1/p'
+}
+
 
 # Set a restrictive umask
 do_readable :
diff --git a/src/lib/ip b/src/lib/ip
index 2dc67fc..71c7fa2 100644
--- a/src/lib/ip
+++ b/src/lib/ip
@@ -38,6 +38,8 @@ resolvconf_add() {
 ip_set() {
     local addr line route interface_sysctl=${Interface/.//}
 
+    network_ready
+
     if [[ -z $IP && -z $IP6 ]]; then
         report_error "Neither IP, nor IP6 was specified"
         return 1
diff --git a/src/lib/network b/src/lib/network
index adea2a2..8a43ce2 100755
--- a/src/lib/network
+++ b/src/lib/network
@@ -56,17 +56,33 @@ bring_interface_down() {
     timeout_wait "${TimeoutUp:-5}" '! interface_is_up "$interface"'
 }
 
+## Indicate that the network stack for the profile is up
+network_ready() {
+    if ! is_yes "${NETWORK_READY:-no}"; then
+        do_debug systemd-notify --pid --ready
+        NETWORK_READY=yes
+    fi
+}
+
+## Describe the status of the service for the profile
+# $1: status string, should be "online" when the profile gets connected
+network_status() {
+    do_debug systemd-notify --pid --status="$1"
+}
+
 
-if [[ $# -ne 2 || $1 != @(start|stop) ]]; then
-    exit_error "Usage: $0 {start|stop} <profile>"
-fi
 ensure_root netctl
 # Ensure we are not in a transient directory
 cd /
 
-# Expose the profile name
-Profile=$2
-load_profile "$Profile"
+if [[ $# -eq 2 && $1 == @(start|stop) ]]; then
+    # Expose the profile name
+    Profile=$2
+    load_profile "$Profile"
+elif [[ $# -ne 1 || $1 != "wait-online" ]]; then
+    exit_error "Usage: $0 {start|stop|wait-online} [profile]"
+fi
+
 case $1 in
   start)
     report_notice "Starting network profile '$Profile'..."
@@ -79,6 +95,7 @@ case $1 in
         report_error "Failed to bring the network up for profile '$Profile'"
         exit 1
     fi
+    network_ready
     # Sandbox the eval
     if ! ( eval $ExecUpPost ); then
         report_error "ExecUpPost failed for network profile '$Profile'"
@@ -86,6 +103,7 @@ case $1 in
         "${Connection}_down"
         exit 1
     fi
+    network_status "online"
     report_notice "Started network profile '$Profile'"
   ;;
   stop)
@@ -100,6 +118,7 @@ case $1 in
         report_error "Failed to bring the network down for profile '$Profile'"
         exit 1
     fi
+    network_status ""
     if is_interface "$Interface" && interface_is_up "$Interface" && \
        ! is_yes "${ForceConnect:-no}"; then
         report_error "The interface of network profile '$Profile' did not go down"
@@ -107,6 +126,15 @@ case $1 in
     fi
     report_notice "Stopped network profile '$Profile'"
   ;;
+  wait-online)
+    mapfile -t Profiles < <(list_profiles)
+    i=0
+    ## Wait for all enabled profiles to come online within a single timeout
+    timeout_wait "${TIMEOUT_ONLINE:-120}" \
+                 '! until [[ $(sd_call is-enabled "${Profiles[i]}") == "enabled" &&
+                             $(sd_status_text "${Profiles[i]}") != "online" ]]; do
+                      (( ++i < ${#Profiles[@]} )) || return 0; done'
+  ;;
 esac
 
 
diff --git a/src/netctl.in b/src/netctl.in
index 0df3759..fcb40f4 100644
--- a/src/netctl.in
+++ b/src/netctl.in
@@ -9,21 +9,22 @@ Usage: netctl {COMMAND} [PROFILE]
               [--help|--version]
 
 Commands:
-  list                  List available profiles
-  store                 Save which profiles are active
-  restore               Load saved profiles
-  stop-all              Stops all profiles
-  start [PROFILE]       Start a profile
-  stop [PROFILE]        Stop a profile
-  restart [PROFILE]     Restart a profile
-  switch-to [PROFILE]   Switch to a profile
-  is-active [PROFILE]   Check whether a profile is active
-  status [PROFILE]      Show runtime status of a profile
-  enable [PROFILE]      Enable the systemd unit for a profile
-  disable [PROFILE]     Disable the systemd unit for a profile
-  reenable [PROFILE]    Reenable the systemd unit for a profile
-  is-enabled [PROFILE]  Check whether a profile is enabled
-  edit [PROFILE]        Edit a profile
+  list                   List available profiles
+  store                  Save which profiles are active
+  restore                Load saved profiles
+  stop-all               Stops all profiles
+  start [PROFILE]        Start a profile
+  stop [PROFILE]         Stop a profile
+  restart [PROFILE]      Restart a profile
+  switch-to [PROFILE]    Switch to a profile
+  is-active [PROFILE]    Check whether a profile is active
+  status [PROFILE]       Show runtime status of a profile
+  enable [PROFILE]       Enable the systemd unit for a profile
+  disable [PROFILE]      Disable the systemd unit for a profile
+  reenable [PROFILE]     Reenable the systemd unit for a profile
+  is-enabled [PROFILE]   Check whether a profile is enabled
+  edit [PROFILE]         Edit a profile
+  wait-online [PROFILE]  Wait for a profile to finish connecting
 END
 }
 
@@ -122,6 +123,16 @@ unit_disable() {
     do_debug rm "$unit"
 }
 
+wait_online() {
+    local profile="$1"
+    if sd_call "is-active --quiet" "$profile"; then
+        timeout_wait "${TIMEOUT_ONLINE:-120}" \
+                     '[[ $(sd_status_text "$profile") == "online" ]]'
+    else
+        return 1
+    fi
+}
+
 
 case $# in
   1)
@@ -162,6 +173,8 @@ case $# in
         fi;;
       edit)
         exec ${EDITOR:-nano} "$PROFILE_DIR/$2";;
+      wait-online)
+        wait_online "$2";;
       *)
         exit_error "$(usage)";;
     esac;;
-- 
2.10.0


More information about the arch-projects mailing list