dhcp-done: Add daemon

Used to communicate with cloud utilities monitoring the netboot status
of the machine. Started once the netboot has completed in order to
provide status.

Change-Id: I6eb8b5a0048e3a9d5d4f56adae42ef5aff2c2bab
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/.gitignore b/.gitignore
index d73d627..fbedebd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
 build*/
 subprojects/*
 !subprojects/acpi-power-state-daemon/
+!subprojects/dhcp-done/
 !subprojects/libcr51sign/
 !subprojects/metrics-ipmi-blobs/
 !subprojects/ncsid/
diff --git a/dhcp-done b/dhcp-done
new file mode 120000
index 0000000..02030d3
--- /dev/null
+++ b/dhcp-done
@@ -0,0 +1 @@
+subprojects/dhcp-done
\ No newline at end of file
diff --git a/subprojects/dhcp-done/dhcp-done.cpp b/subprojects/dhcp-done/dhcp-done.cpp
new file mode 100644
index 0000000..dae06db
--- /dev/null
+++ b/subprojects/dhcp-done/dhcp-done.cpp
@@ -0,0 +1,59 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <fmt/format.h>
+
+#include <sdeventplus/event.hpp>
+#include <sdeventplus/source/io.hpp>
+#include <stdplus/fd/create.hpp>
+#include <stdplus/fd/ops.hpp>
+
+// A privileged port that is reserved for querying BMC DHCP completion.
+// This is well known by the clients querying the status.
+constexpr uint16_t kListenPort = 23;
+
+stdplus::ManagedFd createListener()
+{
+    using namespace stdplus::fd;
+    auto sock =
+        socket(SocketDomain::INet6, SocketType::Stream, SocketProto::TCP);
+    setFileFlags(sock, getFileFlags(sock).set(stdplus::fd::FileFlag::NonBlock));
+    sockaddr_in6 addr = {};
+    addr.sin6_family = AF_INET6;
+    addr.sin6_port = htons(kListenPort);
+    bind(sock, addr);
+    listen(sock, 10);
+    return sock;
+}
+
+int main()
+{
+    try
+    {
+        auto listener = createListener();
+        auto event = sdeventplus::Event::get_default();
+        sdeventplus::source::IO do_accept(
+            event, listener.get(), EPOLLIN | EPOLLET,
+            [&](sdeventplus::source::IO&, int, uint32_t) {
+                while (stdplus::fd::accept(listener))
+                    ;
+            });
+        return event.loop();
+    }
+    catch (const std::exception& e)
+    {
+        fmt::print(stderr, "Failed: {}\n", e.what());
+        return 1;
+    }
+}
diff --git a/subprojects/dhcp-done/dhcp-done.service.in b/subprojects/dhcp-done/dhcp-done.service.in
new file mode 100644
index 0000000..9fa5e24
--- /dev/null
+++ b/subprojects/dhcp-done/dhcp-done.service.in
@@ -0,0 +1,6 @@
+[Unit]
+Description=gBMC DHCP Complete
+
+[Service]
+Restart=on-failure
+ExecStart=@@BIN@ dhcp-done
diff --git a/subprojects/dhcp-done/meson.build b/subprojects/dhcp-done/meson.build
new file mode 100644
index 0000000..a72690b
--- /dev/null
+++ b/subprojects/dhcp-done/meson.build
@@ -0,0 +1,50 @@
+# Copyright 2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+project(
+  'dhcp-done',
+  'cpp',
+  version: '0.1',
+  meson_version: '>=0.57.0',
+  default_options: [
+    'warning_level=3',
+    'werror=true',
+    'cpp_std=c++20',
+  ],
+)
+
+deps = [
+  dependency('sdeventplus'),
+  dependency('stdplus'),
+]
+
+libexecdir = get_option('prefix') / get_option('libexecdir')
+
+executable(
+  'dhcp-done',
+  'dhcp-done.cpp',
+  implicit_include_directories: false,
+  dependencies: deps,
+  install: true,
+  install_dir: libexecdir)
+
+systemd = dependency('systemd')
+systemunitdir = systemd.get_pkgconfig_variable('systemdsystemunitdir')
+
+configure_file(
+  configuration: {'BIN': libexecdir / 'dhcp-done'},
+  input: 'dhcp-done.service.in',
+  output: 'dhcp-done.service',
+  install_mode: 'rw-r--r--',
+  install_dir: systemunitdir)
diff --git a/subprojects/dhcp-done/subprojects b/subprojects/dhcp-done/subprojects
new file mode 120000
index 0000000..a96aa0e
--- /dev/null
+++ b/subprojects/dhcp-done/subprojects
@@ -0,0 +1 @@
+..
\ No newline at end of file
diff --git a/subprojects/sdeventplus.wrap b/subprojects/sdeventplus.wrap
new file mode 100644
index 0000000..a506404
--- /dev/null
+++ b/subprojects/sdeventplus.wrap
@@ -0,0 +1,6 @@
+[wrap-git]
+url = https://github.com/openbmc/sdeventplus
+revision = HEAD
+
+[provide]
+sdeventplus = sdeventplus_dep
diff --git a/subprojects/stdplus.wrap b/subprojects/stdplus.wrap
index 00dae65..765ba71 100644
--- a/subprojects/stdplus.wrap
+++ b/subprojects/stdplus.wrap
@@ -1,3 +1,6 @@
 [wrap-git]
 url = https://github.com/openbmc/stdplus
 revision = HEAD
+
+[provide]
+stdplus = stdplus_dep