Add Connect() method to console DBUS object

Added new poller and consumer for the console DBUS data.

Note: We initially developed SocketName attribute but it is now
      deprecated/removed.

The tree of default object:
$ busctl tree xyz.openbmc_project.Console.default
`-/xyz
  `-/xyz/openbmc_project
    `-/xyz/openbmc_project/console
      `-/xyz/openbmc_project/console/default

The introspect of default console:
$ busctl introspect xyz.openbmc_project.Console.default /xyz/openbmc_project/console/default
NAME                                TYPE      SIGNATURE RESULT/VALUE FLAGS
org.freedesktop.DBus.Introspectable interface -         -            -
.Introspect                         method    -         s            -
org.freedesktop.DBus.Peer           interface -         -            -
.GetMachineId                       method    -         s            -
.Ping                               method    -         -            -
org.freedesktop.DBus.Properties     interface -         -            -
.Get                                method    ss        v            -
.GetAll                             method    s         a{sv}        -
.Set                                method    ssv       -            -
.PropertiesChanged                  signal    sa{sv}as  -            -
xyz.openbmc_project.Console.Access  interface -         -            -
.Connect                            method    -         h            -
xyz.openbmc_project.console         interface -         -            -
.setBaudRate                        method    u         x            -
.baudrate                           property  u         0            -

Tested:
  Performed integration testing with bmcweb.

Change-Id: I2444b1083cf26536f43c6f6b4b0857a2921c4f78
Signed-off-by: Ninad Palsule <ninadpalsule@us.ibm.com>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 18d095b..7926339 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -21,6 +21,7 @@
 
 1. console-server: Add PTY support for testing purposes
 2. console-server: Add --console-id option
+3. console-server: Add DBUS interface to find console unix socket FD.
 
 ### Changed
 
diff --git a/console-dbus.c b/console-dbus.c
index d1dd674..d4477c1 100644
--- a/console-dbus.c
+++ b/console-dbus.c
@@ -17,6 +17,8 @@
 #include <assert.h>
 #include <errno.h>
 #include <err.h>
+#include <string.h>
+#include <sys/socket.h>
 
 #include "console-server.h"
 
@@ -105,20 +107,35 @@
 	return r;
 }
 
-static int get_socket_name(sd_bus *bus __attribute__((unused)),
-			   const char *path __attribute__((unused)),
-			   const char *interface __attribute__((unused)),
-			   const char *property __attribute__((unused)),
-			   sd_bus_message *reply, void *userdata,
-			   sd_bus_error *error __attribute__((unused)))
+static int method_connect(sd_bus_message *msg, void *userdata,
+			  sd_bus_error *err)
 {
 	struct console *console = userdata;
+	int rc;
+	int socket_fd = -1;
 
-	/* The abstract socket name starts with null character hence we need to
-	 * send it as a byte stream instead of regular string.
-	 */
-	return sd_bus_message_append_array(reply, 'y', console->socket_name,
-					   console->socket_name_len);
+	if (!console) {
+		warnx("Internal error: Console pointer is null");
+		sd_bus_error_set_const(err, DBUS_ERR, "Internal error");
+		return sd_bus_reply_method_error(msg, err);
+	}
+
+	/* Register the consumer. */
+	socket_fd = dbus_create_socket_consumer(console);
+	if (socket_fd < 0) {
+		rc = socket_fd;
+		warnx("Failed to create socket consumer: %s", strerror(rc));
+		sd_bus_error_set_const(err, DBUS_ERR,
+				       "Failed to create socket consumer");
+		return sd_bus_reply_method_error(msg, err);
+	}
+
+	rc = sd_bus_reply_method_return(msg, "h", socket_fd);
+
+	/* Close the our end */
+	close(socket_fd);
+
+	return rc;
 }
 
 static const sd_bus_vtable console_tty_vtable[] = {
@@ -131,7 +148,8 @@
 
 static const sd_bus_vtable console_access_vtable[] = {
 	SD_BUS_VTABLE_START(0),
-	SD_BUS_PROPERTY("SocketName", "ay", get_socket_name, 0, 0),
+	SD_BUS_METHOD("Connect", SD_BUS_NO_ARGS, "h", method_connect,
+		      SD_BUS_VTABLE_UNPRIVILEGED),
 	SD_BUS_VTABLE_END,
 };
 
diff --git a/console-server.h b/console-server.h
index 5343af8..47302dc 100644
--- a/console-server.h
+++ b/console-server.h
@@ -230,6 +230,9 @@
 void dbus_init(struct console *console,
 	       struct config *config __attribute__((unused)));
 
+/* socket-handler API */
+int dbus_create_socket_consumer(struct console *console);
+
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
 
 #ifndef offsetof
diff --git a/socket-handler.c b/socket-handler.c
index a0ec53e..5f9d383 100644
--- a/socket-handler.c
+++ b/socket-handler.c
@@ -341,6 +341,81 @@
 	return POLLER_OK;
 }
 
+/* Create socket pair and register one end as poller/consumer and return
+ * the other end to the caller.
+ * Return file descriptor on success and negative value on error.
+ */
+int dbus_create_socket_consumer(struct console *console)
+{
+	struct socket_handler *sh = NULL;
+	struct client *client;
+	int fds[2];
+	int i;
+	int rc = -1;
+	int n;
+
+	for (i = 0; i < console->n_handlers; i++) {
+		if (strcmp(console->handlers[i]->name, "socket") == 0) {
+			sh = to_socket_handler(console->handlers[i]);
+			break;
+		}
+	}
+
+	if (!sh) {
+		return -ENOSYS;
+	}
+
+	/* Create a socketpair */
+	rc = socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
+	if (rc < 0) {
+		warn("Failed to create socket pair");
+		return -errno;
+	}
+
+	client = malloc(sizeof(*client));
+	if (client == NULL) {
+		warnx("Failed to allocate client structure.");
+		rc = -ENOMEM;
+		goto close_fds;
+	}
+	memset(client, 0, sizeof(*client));
+
+	client->sh = sh;
+	client->fd = fds[0];
+	client->poller = console_poller_register(sh->console, &sh->handler,
+						 client_poll, client_timeout,
+						 client->fd, POLLIN, client);
+	client->rbc = console_ringbuffer_consumer_register(
+		sh->console, client_ringbuffer_poll, client);
+	if (client->rbc == NULL) {
+		warnx("Failed to register a consumer.\n");
+		rc = -ENOMEM;
+		goto free_client;
+	}
+
+	n = sh->n_clients++;
+
+	/*
+	 * We're managing an array of pointers to aggregates, so don't warn about
+	 * sizeof() on a pointer type.
+	 */
+	/* NOLINTBEGIN(bugprone-sizeof-expression) */
+	sh->clients =
+		reallocarray(sh->clients, sh->n_clients, sizeof(*sh->clients));
+	/* NOLINTEND(bugprone-sizeof-expression) */
+	sh->clients[n] = client;
+
+	/* Return the second FD to caller. */
+	return fds[1];
+
+free_client:
+	free(client);
+close_fds:
+	close(fds[0]);
+	close(fds[1]);
+	return rc;
+}
+
 static int socket_init(struct handler *handler, struct console *console,
 		       struct config *config __attribute__((unused)))
 {