Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions Makefile-daemon.am
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,15 @@ systemdunit_service_in_files = \
$(NULL)

systemdunit_service_files = $(systemdunit_service_in_files:.service.in=.service)
systemdunit_timer_files = \
systemdunit_other_files = \
$(srcdir)/src/daemon/rpm-ostreed.socket \
$(srcdir)/src/daemon/rpm-ostreed-automatic.timer \
$(srcdir)/src/daemon/rpm-ostree-countme.timer \
$(NULL)

systemdunit_DATA = \
$(systemdunit_service_files) \
$(systemdunit_timer_files) \
$(systemdunit_other_files) \
$(NULL)

systemdunitdir = $(prefix)/lib/systemd/system/
Expand Down Expand Up @@ -110,7 +111,7 @@ EXTRA_DIST += \
$(sysconf_DATA) \
$(service_in_files) \
$(systemdunit_service_in_files) \
$(systemdunit_timer_files) \
$(systemdunit_other_files) \
$(NULL)

CLEANFILES += \
Expand Down
106 changes: 106 additions & 0 deletions rust/src/daemon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
// SPDX-License-Identifier: Apache-2.0 OR MIT

use crate::{cxxrsutil::*, variant_utils};
use anyhow::{anyhow, Context, Result};
use openat_ext::OpenatDirExt;
use std::io::prelude::*;
use std::os::unix::net::{UnixListener, UnixStream};
use std::os::unix::prelude::FromRawFd;
use std::pin::Pin;

/// Validate basic assumptions on daemon startup.
Expand All @@ -26,6 +30,108 @@ pub(crate) fn daemon_sanitycheck_environment(
Ok(())
}

/// Connect to the client socket and ensure the daemon is initialized;
/// this avoids DBus and ensures that we get any early startup errors
/// returned cleanly.
pub(crate) fn start_daemon_via_socket() -> CxxResult<()> {
let address = "/run/rpm-ostree/client.sock";
let s = UnixStream::connect(address)
.with_context(|| anyhow!("Failed to connect to {}", address))?;
let mut s = std::io::BufReader::new(s);
let mut r = String::new();
s.read_to_string(&mut r)
.context("Reading from client socket")?;
if r.is_empty() {
Ok(())
} else {
Err(anyhow!("{}", r).into())
}
}

fn send_init_result_to_client(client: &UnixStream, err: &Result<()>) {
let mut client = std::io::BufWriter::new(client);
match err {
Ok(_) => {
// On successwe close the stream without writing anything,
// which acknowledges successful startup to the client.
}
Err(e) => {
let msg = e.to_string();
match client
.write_all(msg.as_bytes())
.and_then(|_| client.flush())
{
Ok(_) => {}
Err(inner_err) => {
eprintln!(
"Failed to write error message to client socket (original error: {}): {}",
e, inner_err
);
}
}
}
}
}

fn process_clients(listener: UnixListener, res: &Result<()>) {
for stream in listener.incoming() {
match stream {
Ok(stream) => send_init_result_to_client(&stream, res),
Err(e) => {
// This shouldn't be fatal, we continue to start up.
eprintln!("Failed to listen for client stream: {}", e);
}
}
if res.is_err() {
break;
}
}
}

/// Perform initialization steps required by systemd service activation.
///
/// This ensures that the system is running under systemd, then receives the
/// socket-FD for main IPC logic, and notifies systemd about ready-state.
pub(crate) fn daemon_main(debug: bool) -> Result<()> {
if !systemd::daemon::booted()? {
return Err(anyhow!("not running as a systemd service"));
}

let init_res: Result<()> = crate::ffi::daemon_init_inner(debug).map_err(|e| e.into());

let mut fds = systemd::daemon::listen_fds(false)?.iter();
let listener = match fds.next() {
None => {
// If started directly via `systemctl start` or DBus activation, we
// directly propagate the error back to our exit code.
init_res?;
UnixListener::bind("/run/rpmostreed.socket")?
}
Some(fd) => {
if fds.next().is_some() {
return Err(anyhow!("Expected exactly 1 fd from systemd activation"));
}
let listener = unsafe { UnixListener::from_raw_fd(fd) };
match init_res {
Ok(_) => listener,
Err(e) => {
let err_copy = Err(anyhow!("{}", e));
process_clients(listener, &err_copy);
return Err(e);
}
}
}
};

// On success, we spawn a helper thread that just responds with
// sucess to clients that connect via the socket. In the future,
// perhaps we'll expose an API here.
std::thread::spawn(move || process_clients(listener, &Ok(())));

// And now, enter the main loop.
Ok(crate::ffi::daemon_main_inner()?)
}

/// Get a currently unique (for this host) identifier for the
/// deployment; TODO - adding the deployment timestamp would make it
/// persistently unique, needs API in libostree.
Expand Down
8 changes: 8 additions & 0 deletions rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,8 @@ pub mod ffi {
// daemon.rs
extern "Rust" {
fn daemon_sanitycheck_environment(sysroot: Pin<&mut OstreeSysroot>) -> Result<()>;
fn daemon_main(debug: bool) -> Result<()>;
fn start_daemon_via_socket() -> Result<()>;
fn deployment_generate_id(deployment: Pin<&mut OstreeDeployment>) -> String;
fn deployment_populate_variant(
mut sysroot: Pin<&mut OstreeSysroot>,
Expand Down Expand Up @@ -474,6 +476,12 @@ pub mod ffi {
fn main_print_error(msg: &str);
}

unsafe extern "C++" {
include!("rpmostreed-daemon.h");
fn daemon_init_inner(debug: bool) -> Result<()>;
fn daemon_main_inner() -> Result<()>;
}

unsafe extern "C++" {
include!("rpmostree-clientlib.h");
fn client_require_root() -> Result<()>;
Expand Down
2 changes: 2 additions & 0 deletions src/app/libmain.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,8 @@ rpmostree_option_context_parse (GOptionContext *context,
}
}

rpmostreecxx::start_daemon_via_socket();

/* root never needs to auth */
if (getuid () != 0)
/* ignore errors; we print out a warning if we fail to spawn pkttyagent */
Expand Down
59 changes: 39 additions & 20 deletions src/app/rpmostree-builtin-start-daemon.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@

#include "rpmostree-builtins.h"
#include "rpmostree-util.h"
#include "rpmostreed-utils.h"
#include "rpmostreed-daemon.h"
#include "rpmostree-libbuiltin.h"

Expand All @@ -53,6 +54,7 @@ static GOptionEntry opt_entries[] =
{ NULL }
};

static GDBusConnection *bus = NULL;
static RpmostreedDaemon *rpm_ostree_daemon = NULL;

static void
Expand Down Expand Up @@ -225,20 +227,14 @@ on_log_handler (const gchar *log_domain,
sd_journal_print (priority, "%s", message);
}

gboolean
rpmostree_builtin_start_daemon (int argc,
char **argv,
RpmOstreeCommandInvocation *invocation,
GCancellable *cancellable,
GError **error)
namespace rpmostreecxx {
// This function is always called from the Rust side. Hopefully
// soon we'll move more of this code into daemon.rs.
void
daemon_init_inner (bool debug)
{
g_autoptr(GOptionContext) opt_context = g_option_context_new (" - start the daemon process");
g_option_context_add_main_entries (opt_context, opt_entries, NULL);

if (!g_option_context_parse (opt_context, &argc, &argv, error))
return FALSE;

if (opt_debug)
g_autoptr(GError) local_error = NULL;
if (debug)
{
g_autoptr(GIOChannel) channel = NULL;
g_log_set_handler (G_LOG_DOMAIN, (GLogLevelFlags)(G_LOG_LEVEL_DEBUG | G_LOG_LEVEL_INFO), on_log_debug, NULL);
Expand All @@ -258,18 +254,24 @@ rpmostree_builtin_start_daemon (int argc,
g_unix_signal_add (SIGTERM, on_sigint, NULL);

/* Get an explicit ref to the bus so we can use it later */
g_autoptr(GDBusConnection) bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error);
g_autoptr(GDBusConnection) bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &local_error);
if (!bus)
return FALSE;
if (!start_daemon (bus, error)) {
if (*error)
sd_notifyf (0, "STATUS=error: %s", (*error)->message);
return FALSE;
}
util::throw_gerror(local_error);
if (!start_daemon (bus, &local_error))
{
sd_notifyf (0, "STATUS=error: %s", local_error->message);
util::throw_gerror(local_error);
}
}

// Called from rust side to enter mainloop.
void
daemon_main_inner ()
{
state_transition (APPSTATE_RUNNING);

g_debug ("Entering main event loop");
g_assert (rpm_ostree_daemon);
rpmostreed_daemon_run_until_idle_exit (rpm_ostree_daemon);

if (bus)
Expand Down Expand Up @@ -303,6 +305,23 @@ rpmostree_builtin_start_daemon (int argc,
g_autoptr(GMainContext) mainctx = g_main_context_default ();
while (appstate == APPSTATE_FLUSHING)
g_main_context_iteration (mainctx, TRUE);
}
} /* namespace */

gboolean
rpmostree_builtin_start_daemon (int argc,
char **argv,
RpmOstreeCommandInvocation *invocation,
GCancellable *cancellable,
GError **error)
{
g_autoptr(GOptionContext) opt_context = g_option_context_new (" - start the daemon process");
g_option_context_add_main_entries (opt_context, opt_entries, NULL);

if (!g_option_context_parse (opt_context, &argc, &argv, error))
return FALSE;

rpmostreecxx::daemon_main (opt_debug);

return TRUE;
}
9 changes: 9 additions & 0 deletions src/daemon/rpm-ostreed.socket
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[Unit]
ConditionKernelCommandLine=ostree

[Socket]
ListenStream=/run/rpm-ostree/client.sock
SocketMode=0600

[Install]
WantedBy=sockets.target
6 changes: 6 additions & 0 deletions src/daemon/rpmostreed-daemon.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ G_BEGIN_DECLS
#define RPMOSTREE_DRIVER_SD_UNIT "driver-sd-unit"
#define RPMOSTREE_DRIVER_NAME "driver-name"

/* Note: Currently actually defined in rpmostree-builtin-start-daemon.cxx for historical reasons */
namespace rpmostreecxx {
void daemon_init_inner (bool debug);
void daemon_main_inner ();
}

GType rpmostreed_daemon_get_type (void) G_GNUC_CONST;
RpmostreedDaemon * rpmostreed_daemon_get (void);
GDBusConnection * rpmostreed_daemon_connection (void);
Expand Down