aboutsummaryrefslogtreecommitdiffstats
path: root/src/AppLauncherImpl.cc
diff options
context:
space:
mode:
authorScott Murray <scott.murray@konsulko.com>2022-09-10 12:15:20 -0400
committerScott Murray <scott.murray@konsulko.com>2022-09-10 12:30:27 -0400
commitc52afaf8b5c96136fe20e5d5a1332121d3b09ee9 (patch)
tree4f749a779cdf92abb7e49fee7398b8e0e2ef852a /src/AppLauncherImpl.cc
parentc675bafdf15cc19276bd8276c34f56404a5ecb62 (diff)
Add gRPC API implementation
Changes: - Rename the existing daemon to applaunchd-dbus, and add a new version that exposes a gRPC based implementation of the API based on the protobuf definition added in a top-level protos directory. Having both the D-Bus and gRPC APIs exposed by a single daemon was posing difficulties around startup dependencies stemming from D-Bus activation of the daemon. Since the end goal is dropping the D-Bus API entirely, it is easier to just add a second daemon for gRPC that will eventually be the only one present. - To facilitate building the two implementations, a significant amount of code refactoring has been done to move things from the D-Bus API implementing app_launcher.[ch] to systemd_manager.[ch] so the code can be reused. - All use of the systemd D-Bus library except for the path encoding helper function has been replaced with GDBus. The systemd interface wrapper code was generated with gdbus-codegen from XML files captured via introspection on a running system. The motivation for this change was to avoid multithreading issues with sd_bus exposed when calling into it from the gRPC threads. - The copyright headers in the source files have been tweaked to remove the Apache license boilerplate in favour of a SPDX license tag. Notes: - The gRPC API differs slightly from the D-Bus one in that it has a single status streaming RPC method as opposed to the separate signals for application started or terminated that the D-Bus API has. - The gRPC API is currently unauthenticated, the aim is to circle back and implement authentication once a consensus can be reached on what mechanism should be used (fixed JWT configuration, OAuth, etc.). Bug-AGL: SPEC-4559 Signed-off-by: Scott Murray <scott.murray@konsulko.com> Change-Id: I828f38a58b60e9959162b98054982124d4fa4380
Diffstat (limited to 'src/AppLauncherImpl.cc')
-rw-r--r--src/AppLauncherImpl.cc137
1 files changed, 137 insertions, 0 deletions
diff --git a/src/AppLauncherImpl.cc b/src/AppLauncherImpl.cc
new file mode 100644
index 0000000..d691120
--- /dev/null
+++ b/src/AppLauncherImpl.cc
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: Apache-2.0
+/*
+ * Copyright (C) 2022 Konsulko Group
+ */
+
+#include <AppLauncherImpl.h>
+#include <systemd_manager.h>
+
+using grpc::StatusCode;
+using automotivegradelinux::AppStatus;
+
+
+AppLauncherImpl::AppLauncherImpl(SystemdManager *manager) :
+ m_manager(manager)
+{
+ systemd_manager_connect_callbacks(m_manager,
+ G_CALLBACK(started_cb),
+ G_CALLBACK(terminated_cb),
+ this);
+}
+
+Status AppLauncherImpl::StartApplication(ServerContext* context,
+ const StartRequest* request,
+ StartResponse* response)
+{
+ if (!m_manager)
+ return Status(StatusCode::INTERNAL, "Initialization failed");
+
+ // Search the apps list for the given app-id
+ std::string app_id = request->id();
+ auto dbus_launcher_info = systemd_manager_get_app_info(m_manager, app_id.c_str());
+ if (!dbus_launcher_info) {
+ std::string error("Unknown application '");
+ error += app_id;
+ error += "'";
+ return Status(StatusCode::INVALID_ARGUMENT, error);
+ }
+
+ gboolean status = systemd_manager_start_app(m_manager, dbus_launcher_info);
+ response->set_status(status);
+ if (!status) {
+ // Maybe just return StatusCode::NOT_FOUND instead?
+ std::string error("Failed to start application '");
+ error += app_id;
+ error += "'";
+ response->set_message(error);
+ }
+
+ return Status::OK;
+}
+
+Status AppLauncherImpl::ListApplications(ServerContext* context,
+ const ListRequest* request,
+ ListResponse* response)
+{
+ if (!m_manager)
+ return Status(StatusCode::INTERNAL, "Initialization failed");
+
+ GList *apps = systemd_manager_get_app_list(m_manager);
+ if (!apps)
+ return Status::OK; // Perhaps return failure here?
+
+ guint len = g_list_length(apps);
+ for (guint i = 0; i < len; i++) {
+ struct _AppInfo *app_info = (struct _AppInfo*) g_list_nth_data(apps, i);
+ auto info = response->add_apps();
+ info->set_id(app_info_get_app_id(app_info));
+ info->set_name(app_info_get_name(app_info));
+ info->set_icon_path(app_info_get_icon_path(app_info));
+ }
+
+ return Status::OK;
+}
+
+Status AppLauncherImpl::GetStatusEvents(ServerContext* context,
+ const StatusRequest* request,
+ ServerWriter<StatusResponse>* writer)
+{
+
+ // Save client information
+ m_clients_mutex.lock();
+ m_clients.push_back(std::pair(context, writer));
+ m_clients_mutex.unlock();
+
+ // For now block until client disconnect / server shutdown
+ // A switch to the async or callback server APIs might be more elegant than
+ // holding the thread like this, and may be worth investigating at some point.
+ std::unique_lock lock(m_done_mutex);
+ m_done_cv.wait(lock, [context, this]{ return (context->IsCancelled() || m_done); });
+
+ return Status::OK;
+}
+
+
+void AppLauncherImpl::SendStatus(std::string id, std::string status)
+{
+ const std::lock_guard<std::mutex> lock(m_clients_mutex);
+
+ if (m_clients.empty())
+ return;
+
+ StatusResponse response;
+ auto app_status = response.mutable_app();
+ app_status->set_id(id);
+ app_status->set_status(status);
+
+ auto it = m_clients.begin();
+ while (it != m_clients.end()) {
+ if (it->first->IsCancelled()) {
+ // Client has gone away, remove from list
+ std::cout << "Removing cancelled RPC client!" << std::endl;
+ it = m_clients.erase(it);
+
+ // We're not exiting, but wake up blocked client RPC handlers so
+ // the canceled one will clean exit.
+ // Note that in practice this means the client RPC handler thread
+ // sticks around until the next status event is sent.
+ m_done_cv.notify_all();
+
+ continue;
+ } else {
+ it->second->Write(response);
+ ++it;
+ }
+ }
+}
+
+void AppLauncherImpl::HandleAppStarted(std::string id)
+{
+ SendStatus(id, "started");
+}
+
+void AppLauncherImpl::HandleAppTerminated(std::string id)
+{
+ SendStatus(id, "terminated");
+}
+