dbus-c++: add acquire_name method to Connection class

Our daemons would like to acquire a D-Bus name, or exit if the
name is not available. This will guarantee that at most one instance
of a daemon is running at any given time. (More precisely: the
guarantee will be that no more than one process of each daemon has
continued past its D-Bus setup code.)

Unfortunately, the existing request_name method doesn't tell
us whether or not our request succeeded. Add a new method that
does.

If our request_name attempt fails, the new method also pings
the current owner of the name. This updates dbus-daemon's
knowledge of whether or not that owner is alive. After this,
we retry request_name once.

The ping and retry should avoid a potential race, where a
name owner has terminated abruptly, and our name request
is processed by dbus-daemon before it realizes the name
owner has exited.

BUG=chromium:356874
TEST=network_TwoShills, manual

Manual test
-----------
- build shill with this patch and CL:195012
- install the new shill and dbus-c++

// when the old shill answers the d-bus ping, the new shill
// aborts.
dut# pid=$(pgrep shill)
dut# (kill -STOP $pid; sleep 10; kill -CONT $pid) & (time /usr/bin/shill --foreground; /usr/local/lib/flimflam/test/list-active-service | head -1)
[...]
[0416/151422:FATAL:dbus_control.cc(94)] Failed to acquire D-Bus name org.chromium.flimflam. Is another shill running?
[...]
real    0m10.041s
user    0m0.008s
sys     0m0.013s
[ /service/1 ]

// if the old shill times out the d-bus ping, the new shill
// still aborts. (the old shill may be hung, but we should
// deal with that some other way.)
dut# kill -STOP $pid & (time /usr/bin/shill --foreground; kill -CONT $pid; /usr/local/lib/flimflam/test/list-active-service | head -1)
[...]
[0416/151554:FATAL:dbus_control.cc(94)] Failed to acquire D-Bus name org.chromium.flimflam. Is another shill running?
real    0m25.086s
user    0m0.015s
sys     0m0.008s
[ /service/1 ]

// if the old shill dies in the middle of the ping, the new
// shill takes over.
Wed Apr 16 16:13:42 PDT 2014
2014-04-16T16:13:42.100291-07:00 localhost shill: [0416/161342:INFO:manager.cc(197)] Manager started.
[ /service/1 ]

Change-Id: I95b10747750bd0c983ca91b9f4fbaf2c8ed3b8cf
Reviewed-on: https://chromium-review.googlesource.com/195002
Tested-by: mukesh agrawal <[email protected]>
Reviewed-by: Ben Chan <[email protected]>
Commit-Queue: mukesh agrawal <[email protected]>
diff --git a/include/dbus-c++/connection.h b/include/dbus-c++/connection.h
index 7c19ea1..7db166f 100644
--- a/include/dbus-c++/connection.h
+++ b/include/dbus-c++/connection.h
@@ -416,8 +416,30 @@
 	 */
 	PendingCall *send_async(Message& msg, int timeout = -1);
 
+        /*!
+         *\brief Request a name from DBus.
+         *
+         * Request ownership of a name. The name and flags are passed
+         * on to the lower-layer.
+         *
+         * Unless you are doing something unusual, you should use
+         * acquire_name instead of this method.
+         *
+         * \param name The name to request ownership of.
+         * \param flags Flags to be passed to dbus_bus_request_name.
+         */
 	void request_name( const char* name, int flags = 0 );
-    
+
+        /*!
+         *\brief Acquire a name from DBus.
+         *
+         * Request ownership of a name. Return true if the name was
+         * acquired, and false otherwise.
+         *
+         * \return True if the name was acquired, and false otherwise.
+         */
+        bool acquire_name( const char* name );
+
 	unsigned long sender_unix_uid(const char *sender);
 
 	/*!
@@ -462,6 +484,7 @@
 private:
 
 	DXXAPILOCAL void init();
+	DXXAPILOCAL int _request_name( const char* name, int flags );
 
 private:
 
diff --git a/src/connection.cpp b/src/connection.cpp
index 9b5bc59..10d96bd 100644
--- a/src/connection.cpp
+++ b/src/connection.cpp
@@ -388,17 +388,12 @@
 	return new PendingCall(new PendingCall::Private(pending));
 }
 
-void Connection::request_name(const char *name, int flags)
+int Connection::_request_name(const char *name, int flags)
 {
 	InternalError e;
 
 	debug_log("%s: registering bus name %s", unique_name(), name);
 
-        /*
-         * TODO:
-         * Think about giving back the 'ret' value. Some people on the list
-         * requested about this...
-         */
 	int ret = dbus_bus_request_name(_pvt->conn, name, flags, e);
 
 	if (ret == -1)
@@ -414,6 +409,75 @@
 		std::string match = "destination='" + _pvt->names.back() + "'";
 		add_match(match.c_str());
 	}
+
+	return ret;
+}
+
+void Connection::request_name(const char *name, int flags)
+{
+	/*
+	 * TODO:
+	 * Think about giving back the 'ret' value. Some people on the list
+	 * requested about this...
+	 */
+	_request_name(name, flags);
+}
+
+bool Connection::acquire_name(const char *name)
+{
+	/*
+	 * Request the desired name, allowing the request to be queued.
+	 */
+	int ret = _request_name(name, 0);
+	switch (ret) {
+		case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER:
+		case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER:
+			return true;
+		case DBUS_REQUEST_NAME_REPLY_IN_QUEUE:
+			break;
+		case DBUS_REQUEST_NAME_REPLY_EXISTS:
+		default:
+			// Unexpected, but try to keep going.
+			debug_log("%s: unexpected reply %d for RequestName",
+				  dbus_bus_get_unique_name(_pvt->conn), ret);
+			break;
+	}
+
+	/*
+	 * We didn't get the name. Perhaps the name owner terminated
+	 * abruptly, and dbus-daemon doesn't know that yet.  Ask
+	 * dbus-daemon to check if the name owner is still up.
+	 */
+	CallMessage ping_request(name, "/", "org.freedesktop.DBus.Peer", "Ping");
+	try {
+		send_blocking(ping_request, DBUS_TIMEOUT_USE_DEFAULT);
+	} catch (const DBus::Error &) {
+		/*
+		 * We don't care if the ping request times out or generates
+                 * some other error. We only care about its side-effect.
+		 */
+	}
+
+	/*
+	 * Now that we've nudged dbus-daemon, try requesting the name
+	 * one more time. Note that we may already be the owner at
+	 * this point, because our first attempt allowed queuing the
+	 * request.
+	 */
+	ret = _request_name(name, DBUS_NAME_FLAG_DO_NOT_QUEUE);
+	switch (ret) {
+		case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER:
+		case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER:
+			return true;
+		case DBUS_REQUEST_NAME_REPLY_EXISTS:
+			return false;
+		case DBUS_REQUEST_NAME_REPLY_IN_QUEUE:
+		default:
+			// Log unexpected response.
+			debug_log("%s: unexpected reply %d for RequestName",
+				  dbus_bus_get_unique_name(_pvt->conn), ret);
+			return false;
+	}
 }
 
 unsigned long Connection::sender_unix_uid(const char *sender)