shill: Add code to wait for dhcpcd to exit before continuing.

In the past, we stop dhcpcd by sending it a SIGTERM without waiting for
it to exit.  This cause a race condition where the first instance of
dhcpcd is still exiting when we start the second instance.  The second
instance fails to acquire the lock that is still held by the first
instance so it exits.  Eventually, the first instance also exits and
we're left with no dhcpcd running.  This CL waits for dhcpcd to exit
after we send it a SIGTERM.

BUG=chromium-os:26580
TEST=Unit tests, run network_WiFiManager and examine the messages to
ensure that processing does not continue until after dhcpcd exits

Change-Id: I8b4c9ea21ec652eb36f69b37841424b72c0f6078
Reviewed-on: https://gerrit.chromium.org/gerrit/16323
Reviewed-by: mukesh agrawal <quiche@chromium.org>
Reviewed-by: Paul Stewart <pstew@chromium.org>
Commit-Ready: Thieu Le <thieule@chromium.org>
Tested-by: Thieu Le <thieule@chromium.org>
diff --git a/dhcp_config.cc b/dhcp_config.cc
index 3acefc0..7435432 100644
--- a/dhcp_config.cc
+++ b/dhcp_config.cc
@@ -1,10 +1,11 @@
-// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "shill/dhcp_config.h"
 
 #include <arpa/inet.h>
+#include <sys/wait.h>
 
 #include <base/file_util.h>
 #include <base/logging.h>
@@ -32,6 +33,8 @@
 const char DHCPConfig::kConfigurationKeyMTU[] = "InterfaceMTU";
 const char DHCPConfig::kConfigurationKeyRouters[] = "Routers";
 const char DHCPConfig::kConfigurationKeySubnetCIDR[] = "SubnetCIDR";
+const int DHCPConfig::kDHCPCDExitPollMilliseconds = 50;
+const int DHCPConfig::kDHCPCDExitWaitMilliseconds = 3000;
 const char DHCPConfig::kDHCPCDPath[] = "/sbin/dhcpcd";
 const char DHCPConfig::kDHCPCDPathFormatLease[] = "var/run/dhcpcd-%s.lease";
 const char DHCPConfig::kDHCPCDPathFormatPID[] = "var/run/dhcpcd-%s.pid";
@@ -180,7 +183,23 @@
 void DHCPConfig::Stop() {
   if (pid_) {
     VLOG(2) << "Terminating " << pid_;
-    PLOG_IF(ERROR, kill(pid_, SIGTERM) < 0);
+    if (kill(pid_, SIGTERM) < 0) {
+      PLOG(ERROR);
+      return;
+    }
+    pid_t ret;
+    int num_iterations =
+        kDHCPCDExitWaitMilliseconds / kDHCPCDExitPollMilliseconds;
+    for (int count = 0; count < num_iterations; ++count) {
+      ret = waitpid(pid_, NULL, WNOHANG);
+      if (ret == pid_ || ret == -1)
+        break;
+      usleep(kDHCPCDExitPollMilliseconds * 1000);
+      if (count == num_iterations / 2)  // Make one last attempt to kill dhcpcd.
+        kill(pid_, SIGKILL);
+    }
+    if (ret != pid_)
+      PLOG(ERROR);
   }
 }