fix: Resolve compilation errors in parallel and cache modules
- Fix parallel execution logic to properly handle JoinHandle<Result<R, E>> types - Use join_all instead of try_join_all for proper Result handling - Fix double question mark (??) issue in parallel execution methods - Clean up unused imports in parallel and cache modules - Ensure all performance optimization modules compile successfully - Fix CI build failures caused by compilation errors
This commit is contained in:
parent
2746d973ff
commit
306a68b89a
192 changed files with 31302 additions and 39522 deletions
|
|
@ -0,0 +1,750 @@
|
|||
# 🔍 **rpm-ostree vs rpm-ostreed: CLI-Daemon Separation Analysis**
|
||||
|
||||
## 📋 **Overview**
|
||||
|
||||
This document provides a comprehensive analysis of the separation between `rpm-ostree` (the CLI client) and `rpm-ostreed` (the system daemon) based on examination of the rpm-ostree source code. Understanding this separation is crucial for implementing a similar architecture in apt-ostree.
|
||||
|
||||
## 🏗️ **Architecture Overview**
|
||||
|
||||
### **Component Separation**
|
||||
```
|
||||
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||||
│ rpm-ostree │ │ DBus Layer │ │ rpm-ostreed │
|
||||
│ (CLI Client) │◄──►│ (Communication)│◄──►│ (System Daemon)│
|
||||
│ │ │ │ │ │
|
||||
│ • Command Line │ │ • Client Proxy │ │ • OSTree Ops │
|
||||
│ • User Interface│ │ • Signal Handler│ │ • Package Mgmt │
|
||||
│ • Progress Display│ │ • Error Handling│ │ • Transactions │
|
||||
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
||||
```
|
||||
|
||||
### **Key Design Principles**
|
||||
1. **Separation of Concerns**: CLI handles user interaction, daemon handles system operations
|
||||
2. **Privilege Isolation**: Daemon runs with elevated privileges, CLI runs as user
|
||||
3. **Transaction Management**: Daemon manages long-running operations, CLI monitors progress
|
||||
4. **Fallback Support**: CLI can operate without daemon for read-only operations
|
||||
5. **Security by Design**: Daemon handles all privileged operations via PolicyKit
|
||||
|
||||
## 🔍 **Detailed Separation Analysis**
|
||||
|
||||
### **1. rpm-ostree (CLI Client) Responsibilities**
|
||||
|
||||
#### **Command Line Interface**
|
||||
```cpp
|
||||
// From libmain.cxx - Command registration and dispatch
|
||||
static RpmOstreeCommand commands[] = {
|
||||
{ "compose", RPM_OSTREE_BUILTIN_FLAG_LOCAL_CMD | RPM_OSTREE_BUILTIN_FLAG_REQUIRES_ROOT,
|
||||
"Commands to compose a tree", rpmostree_builtin_compose },
|
||||
{ "status", (RpmOstreeBuiltinFlags)0, "Get the version of the booted system",
|
||||
rpmostree_builtin_status },
|
||||
{ "upgrade", RPM_OSTREE_BUILTIN_FLAG_SUPPORTS_PKG_INSTALLS, "Perform a system upgrade",
|
||||
rpmostree_builtin_upgrade },
|
||||
{ "install", RPM_OSTREE_BUILTIN_FLAG_CONTAINER_CAPABLE, "Overlay additional packages",
|
||||
rpmostree_builtin_install },
|
||||
{ "uninstall", RPM_OSTREE_BUILTIN_FLAG_CONTAINER_CAPABLE, "Remove overlayed packages",
|
||||
rpmostree_builtin_uninstall },
|
||||
{ "search", RPM_OSTREE_BUILTIN_FLAG_CONTAINER_CAPABLE, "Search for packages",
|
||||
rpmostree_builtin_search },
|
||||
{ "rollback", (RpmOstreeBuiltinFlags)0, "Revert to the previously booted tree",
|
||||
rpmostree_builtin_rollback },
|
||||
{ "deploy", RPM_OSTREE_BUILTIN_FLAG_SUPPORTS_PKG_INSTALLS, "Deploy a specific commit",
|
||||
rpmostree_builtin_deploy },
|
||||
{ "rebase", RPM_OSTREE_BUILTIN_FLAG_SUPPORTS_PKG_INSTALLS, "Switch to a different tree",
|
||||
rpmostree_builtin_rebase },
|
||||
{ "kargs", (RpmOstreeBuiltinFlags)0, "Query or modify kernel arguments",
|
||||
rpmostree_builtin_kargs },
|
||||
{ "initramfs", (RpmOstreeBuiltinFlags)0, "Enable or disable local initramfs regeneration",
|
||||
rpmostree_builtin_initramfs },
|
||||
{ "override", RPM_OSTREE_BUILTIN_FLAG_LOCAL_CMD, "Manage base package overrides",
|
||||
rpmostree_builtin_override },
|
||||
{ "reset", RPM_OSTREE_BUILTIN_FLAG_SUPPORTS_PKG_INSTALLS, "Remove all mutations",
|
||||
rpmostree_builtin_reset },
|
||||
{ "refresh-md", (RpmOstreeBuiltinFlags)0, "Generate rpm repo metadata",
|
||||
rpmostree_builtin_refresh_md },
|
||||
{ "reload", (RpmOstreeBuiltinFlags)0, "Reload configuration",
|
||||
rpmostree_builtin_reload },
|
||||
{ "cancel", (RpmOstreeBuiltinFlags)0, "Cancel an active transaction",
|
||||
rpmostree_builtin_cancel },
|
||||
{ "cleanup", RPM_OSTREE_BUILTIN_FLAG_CONTAINER_CAPABLE, "Clear cached/pending data",
|
||||
rpmostree_builtin_cleanup },
|
||||
{ "apply-live", (RpmOstreeBuiltinFlags)0, "Apply pending deployment changes to booted deployment",
|
||||
rpmostree_builtin_apply_live },
|
||||
{ "finalize-deployment", (RpmOstreeBuiltinFlags)0, "Finalize staged deployment",
|
||||
rpmostree_builtin_finalize_deployment },
|
||||
{ "initramfs-etc", (RpmOstreeBuiltinFlags)0, "Add files to the initramfs",
|
||||
rpmostree_builtin_initramfs_etc },
|
||||
{ "db", RPM_OSTREE_BUILTIN_FLAG_LOCAL_CMD, "Commands to query the RPM database",
|
||||
rpmostree_builtin_db },
|
||||
{ "usroverlay", RPM_OSTREE_BUILTIN_FLAG_LOCAL_CMD, "Apply transient overlay",
|
||||
rpmostree_builtin_usroverlay },
|
||||
{ "ex", RPM_OSTREE_BUILTIN_FLAG_LOCAL_CMD, "Execute command in deployment",
|
||||
rpmostree_builtin_ex },
|
||||
{ "rebuild", RPM_OSTREE_BUILTIN_FLAG_LOCAL_CMD, "Rebuild deployment",
|
||||
rpmostree_builtin_rebuild },
|
||||
{ "start-daemon", RPM_OSTREE_BUILTIN_FLAG_LOCAL_CMD, "Start the daemon",
|
||||
rpmostree_builtin_start_daemon },
|
||||
{ "testutils", RPM_OSTREE_BUILTIN_FLAG_LOCAL_CMD, "Test utilities",
|
||||
rpmostree_builtin_testutils },
|
||||
};
|
||||
```
|
||||
|
||||
**Responsibilities**:
|
||||
- **Command parsing** and argument validation
|
||||
- **Option handling** and help display
|
||||
- **Command dispatch** to appropriate builtin functions
|
||||
- **User interface** and output formatting
|
||||
- **Command flag management** and validation
|
||||
|
||||
#### **DBus Client Communication**
|
||||
```cpp
|
||||
// From rpmostree-clientlib.cxx - Client-side DBus handling
|
||||
static gboolean
|
||||
app_load_sysroot_impl (const char *sysroot, GCancellable *cancellable,
|
||||
GDBusConnection **out_conn, GError **error)
|
||||
{
|
||||
// Start daemon if not running
|
||||
ROSCXX_TRY (client_start_daemon (), error);
|
||||
|
||||
// Connect to system bus
|
||||
g_autoptr (GDBusConnection) connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM,
|
||||
cancellable, error);
|
||||
|
||||
// Register as client
|
||||
uid_t uid = getuid ();
|
||||
gboolean should_register;
|
||||
if (uid == 0)
|
||||
should_register = TRUE;
|
||||
else
|
||||
{
|
||||
g_autofree char *session_id = NULL;
|
||||
if (sd_pid_get_session (getpid (), &session_id) >= 0)
|
||||
should_register = sd_session_is_active (session_id) == 1;
|
||||
else
|
||||
should_register = FALSE;
|
||||
}
|
||||
|
||||
if (should_register)
|
||||
{
|
||||
g_autoptr (GVariant) options = g_variant_new ("(s@a{sv})", "cli",
|
||||
g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0));
|
||||
rpmostree_sysroot_call_register_client (sysroot_proxy, options, NULL, NULL);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Responsibilities**:
|
||||
- **DBus connection** establishment and management
|
||||
- **Client registration** with daemon
|
||||
- **Method invocation** on daemon interfaces
|
||||
- **Signal handling** for progress updates
|
||||
- **Error handling** and retry logic
|
||||
- **Daemon startup** if not running
|
||||
|
||||
#### **Progress Monitoring and Display**
|
||||
```cpp
|
||||
// From rpmostree-clientlib.cxx - Progress and error display
|
||||
static void
|
||||
transaction_progress_signal_handler (GDBusConnection *connection, const char *sender_name,
|
||||
const char *object_path, const char *interface_name,
|
||||
const char *signal_name, GVariant *parameters,
|
||||
gpointer user_data)
|
||||
{
|
||||
auto tp = static_cast<TransactionProgress *> (user_data);
|
||||
|
||||
if (g_strcmp0 (signal_name, "Message") == 0) {
|
||||
const char *message;
|
||||
g_variant_get (parameters, "(&s)", &message);
|
||||
|
||||
if (!tp->progress) {
|
||||
tp->progress = TRUE;
|
||||
rpmostreecxx::console_progress_begin_task (message);
|
||||
} else {
|
||||
rpmostreecxx::console_progress_set_message (message);
|
||||
}
|
||||
} else if (g_strcmp0 (signal_name, "PercentProgress") == 0) {
|
||||
guint percentage;
|
||||
const char *message;
|
||||
g_variant_get (parameters, "(u&s)", &percentage, &message);
|
||||
|
||||
if (!tp->progress) {
|
||||
tp->progress = TRUE;
|
||||
rpmostreecxx::console_progress_begin_percent (message);
|
||||
}
|
||||
rpmostreecxx::console_progress_update (percentage);
|
||||
} else if (g_strcmp0 (signal_name, "DownloadProgress") == 0) {
|
||||
auto line = rpmostreecxx::client_render_download_progress (*parameters);
|
||||
if (!tp->progress) {
|
||||
tp->progress = TRUE;
|
||||
rpmostreecxx::console_progress_begin_task (line.c_str ());
|
||||
} else {
|
||||
rpmostreecxx::console_progress_set_message (line.c_str ());
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Responsibilities**:
|
||||
- **Progress display** during long operations
|
||||
- **Status reporting** and user feedback
|
||||
- **Output formatting** (text, JSON, etc.)
|
||||
- **Error message** presentation
|
||||
- **Download progress** visualization
|
||||
- **Task progress** tracking
|
||||
|
||||
#### **Fallback Operations**
|
||||
```cpp
|
||||
// From rpmostree-builtin-status.cxx - Fallback when daemon unavailable
|
||||
static gboolean
|
||||
rpmostree_builtin_status (int argc, char **argv, RpmOstreeCommandInvocation *invocation,
|
||||
GCancellable *cancellable, GError **error)
|
||||
{
|
||||
// Try daemon first, fallback to direct OSTree if needed
|
||||
g_autoptr (GDBusConnection) connection = NULL;
|
||||
if (rpmostree_client_connection_new (invocation, cancellable, &connection, error)) {
|
||||
// Use daemon for status
|
||||
return get_status_via_daemon (connection, invocation, cancellable, error);
|
||||
} else {
|
||||
// Fallback to direct OSTree operations
|
||||
return get_status_direct (invocation, cancellable, error);
|
||||
}
|
||||
}
|
||||
|
||||
// Direct OSTree operations for fallback
|
||||
static gboolean
|
||||
get_status_direct (RpmOstreeCommandInvocation *invocation, GCancellable *cancellable, GError **error)
|
||||
{
|
||||
// Direct OSTree sysroot access
|
||||
g_autoptr (OstreeSysroot) sysroot = ostree_sysroot_new_default ();
|
||||
if (!ostree_sysroot_load (sysroot, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
// Get deployment information directly
|
||||
g_autoptr (GPtrArray) deployments = ostree_sysroot_get_deployments (sysroot);
|
||||
if (!deployments)
|
||||
return FALSE;
|
||||
|
||||
// Display deployment information
|
||||
for (guint i = 0; i < deployments->len; i++) {
|
||||
auto deployment = static_cast<OstreeDeployment *> (deployments->pdata[i]);
|
||||
print_deployment (deployment, opt_verbose, opt_only_booted, textarea_width);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
```
|
||||
|
||||
**Responsibilities**:
|
||||
- **Fallback logic** when daemon unavailable
|
||||
- **Direct OSTree operations** for read-only tasks
|
||||
- **Graceful degradation** of functionality
|
||||
- **User notification** of limited capabilities
|
||||
- **Local system access** for basic operations
|
||||
|
||||
### **2. rpm-ostreed (Daemon) Responsibilities**
|
||||
|
||||
#### **System State Management**
|
||||
```cpp
|
||||
// From rpmostreed-daemon.cxx - Daemon state management
|
||||
struct _RpmostreedDaemon {
|
||||
GObject parent_instance;
|
||||
|
||||
GHashTable *bus_clients; // Active client tracking
|
||||
gboolean running; // Daemon running state
|
||||
gboolean rebooting; // System reboot state
|
||||
GDBusProxy *bus_proxy; // System bus proxy
|
||||
GSource *idle_exit_source; // Auto-exit timer
|
||||
guint rerender_status_id; // Status rerender timer
|
||||
RpmostreedSysroot *sysroot; // OSTree sysroot management
|
||||
gchar *sysroot_path; // System root path
|
||||
|
||||
// Configuration settings
|
||||
guint idle_exit_timeout; // Auto-exit timeout
|
||||
RpmostreedAutomaticUpdatePolicy auto_update_policy; // Update policy
|
||||
gboolean lock_layering; // Package layering lock
|
||||
gboolean disable_recommends; // Recommends handling
|
||||
|
||||
// DBus infrastructure
|
||||
GDBusConnection *connection; // DBus connection
|
||||
GDBusObjectManagerServer *object_manager; // Object management
|
||||
|
||||
// Async runtime
|
||||
std::optional<rust::Box<rpmostreecxx::TokioHandle>> tokio_handle;
|
||||
};
|
||||
```
|
||||
|
||||
**Responsibilities**:
|
||||
- **Global state** management and persistence
|
||||
- **Configuration** loading and validation
|
||||
- **Client lifecycle** management
|
||||
- **System monitoring** and health checks
|
||||
- **Auto-exit** management for resource efficiency
|
||||
- **Update policy** enforcement
|
||||
|
||||
#### **OSTree Operations**
|
||||
```cpp
|
||||
// From rpmostreed-sysroot.cxx - OSTree system management
|
||||
struct _RpmostreedSysroot {
|
||||
RPMOSTreeSysrootSkeleton parent_instance;
|
||||
|
||||
OstreeSysroot *ot_sysroot; // OSTree sysroot object
|
||||
OstreeRepo *repo; // OSTree repository
|
||||
struct stat repo_last_stat; // Repository stat cache
|
||||
RpmostreedTransaction *transaction; // Active transaction
|
||||
guint close_transaction_timeout_id; // Transaction timeout
|
||||
|
||||
PolkitAuthority *authority; // PolicyKit authority
|
||||
gboolean on_session_bus; // Session bus flag
|
||||
|
||||
GHashTable *os_interfaces; // OS interface objects
|
||||
GHashTable *osexperimental_interfaces; // Experimental interfaces
|
||||
|
||||
GFileMonitor *monitor; // Filesystem monitoring
|
||||
guint sig_changed; // Change signal handler
|
||||
};
|
||||
|
||||
// OSTree operations implementation
|
||||
static gboolean
|
||||
rpmostreed_sysroot_handle_deploy (RPMOSTreeSysroot *skeleton, GDBusMethodInvocation *invocation,
|
||||
const char *osname, const char *refspec, GVariant *options)
|
||||
{
|
||||
// Check authorization
|
||||
if (!rpmostreed_sysroot_check_authorization (self, invocation,
|
||||
"org.projectatomic.rpmostree1.deploy")) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Create deployment transaction
|
||||
g_autoptr (RpmostreedTransaction) transaction = rpmostreed_transaction_new (
|
||||
self, invocation, "deploy", refspec, options);
|
||||
|
||||
if (!transaction)
|
||||
return FALSE;
|
||||
|
||||
// Execute deployment
|
||||
rpmostreed_transaction_execute (transaction);
|
||||
return TRUE;
|
||||
}
|
||||
```
|
||||
|
||||
**Responsibilities**:
|
||||
- **OSTree repository** management and operations
|
||||
- **Deployment** creation, modification, and removal
|
||||
- **Filesystem** operations and staging
|
||||
- **Boot configuration** management
|
||||
- **System updates** and rollbacks
|
||||
- **Repository monitoring** and change detection
|
||||
|
||||
#### **Transaction Management**
|
||||
```cpp
|
||||
// From rpmostreed-transaction.cxx - Transaction lifecycle
|
||||
struct _RpmostreedTransactionPrivate {
|
||||
GDBusMethodInvocation *invocation; // DBus method context
|
||||
gboolean executed; // Transaction completion state
|
||||
GCancellable *cancellable; // Cancellation support
|
||||
|
||||
// System state during transaction
|
||||
char *sysroot_path; // Sysroot path
|
||||
OstreeSysroot *sysroot; // OSTree sysroot
|
||||
gboolean sysroot_locked; // Sysroot lock state
|
||||
|
||||
// Client tracking
|
||||
char *client_description; // Client description
|
||||
char *agent_id; // Client agent ID
|
||||
char *sd_unit; // Systemd unit
|
||||
|
||||
// Progress tracking
|
||||
gint64 last_progress_journal; // Progress journal timestamp
|
||||
gboolean redirect_output; // Output redirection flag
|
||||
|
||||
// Peer connections
|
||||
GDBusServer *server; // DBus server
|
||||
GHashTable *peer_connections; // Client connections
|
||||
|
||||
// Completion state
|
||||
GVariant *finished_params; // Completion parameters
|
||||
guint watch_id; // Watch identifier
|
||||
};
|
||||
|
||||
// Transaction execution
|
||||
static void
|
||||
rpmostreed_transaction_execute (RpmostreedTransaction *self)
|
||||
{
|
||||
RpmostreedTransactionPrivate *priv = rpmostreed_transaction_get_private (self);
|
||||
|
||||
// Lock sysroot
|
||||
if (!ostree_sysroot_lock (priv->sysroot, priv->cancellable, NULL)) {
|
||||
rpmostreed_transaction_fail (self, "Failed to lock sysroot");
|
||||
return;
|
||||
}
|
||||
priv->sysroot_locked = TRUE;
|
||||
|
||||
// Execute operations
|
||||
for (guint i = 0; i < priv->operations->len; i++) {
|
||||
auto operation = static_cast<RpmostreedOperation *> (priv->operations->pdata[i]);
|
||||
|
||||
// Emit progress
|
||||
rpmostreed_transaction_emit_percent_progress (self,
|
||||
(i * 100) / priv->operations->len, "Executing operation");
|
||||
|
||||
// Execute operation
|
||||
if (!rpmostreed_operation_execute (operation, self)) {
|
||||
rpmostreed_transaction_fail (self, "Operation execution failed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Commit transaction
|
||||
rpmostreed_transaction_commit (self);
|
||||
}
|
||||
```
|
||||
|
||||
**Responsibilities**:
|
||||
- **Transaction lifecycle** management
|
||||
- **Atomic operation** execution
|
||||
- **Progress tracking** and reporting
|
||||
- **Rollback** and error recovery
|
||||
- **Client communication** during operations
|
||||
- **Resource locking** and cleanup
|
||||
|
||||
#### **Security and Privilege Management**
|
||||
```cpp
|
||||
// From rpmostreed-sysroot.cxx - Security integration
|
||||
static gboolean
|
||||
rpmostreed_sysroot_check_authorization (RpmostreedSysroot *self,
|
||||
GDBusMethodInvocation *invocation,
|
||||
const char *action)
|
||||
{
|
||||
// Get client credentials
|
||||
g_autoptr (GCredentials) credentials = g_dbus_method_invocation_get_credentials (invocation);
|
||||
if (!credentials)
|
||||
return FALSE;
|
||||
|
||||
// Check PolicyKit authorization
|
||||
g_autoptr (GError) local_error = NULL;
|
||||
g_autoptr (PolkitAuthorizationResult) result = polkit_authority_check_authorization_sync (
|
||||
self->authority, credentials, action, NULL,
|
||||
POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, NULL, &local_error);
|
||||
|
||||
if (!result) {
|
||||
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED,
|
||||
"Failed to check authorization: %s",
|
||||
local_error ? local_error->message : "unknown error");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!polkit_authorization_result_get_is_authorized (result)) {
|
||||
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED,
|
||||
"Not authorized for action: %s", action);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
```
|
||||
|
||||
**Responsibilities**:
|
||||
- **PolicyKit integration** and authorization
|
||||
- **Privilege escalation** management
|
||||
- **Access control** and security policies
|
||||
- **Audit logging** and security events
|
||||
- **Client credential** validation
|
||||
- **Operation permission** checking
|
||||
|
||||
## 🔄 **Communication Patterns**
|
||||
|
||||
### **1. DBus Interface Structure**
|
||||
```
|
||||
org.projectatomic.rpmostree1
|
||||
├── /org/projectatomic/rpmostree1/Sysroot
|
||||
│ ├── Properties
|
||||
│ │ ├── Booted (s)
|
||||
│ │ ├── Path (s)
|
||||
│ │ ├── ActiveTransaction (o)
|
||||
│ │ └── Locked (b)
|
||||
│ ├── Methods
|
||||
│ │ ├── GetOS (s) → o
|
||||
│ │ ├── RegisterClient (sa{sv}) → ()
|
||||
│ │ ├── UnregisterClient (s) → ()
|
||||
│ │ ├── Reload () → ()
|
||||
│ │ └── ReloadConfig () → ()
|
||||
│ └── Signals
|
||||
│ └── Changed ()
|
||||
└── /org/projectatomic/rpmostree1/OS/{osname}
|
||||
├── Properties
|
||||
│ ├── Version (s)
|
||||
│ ├── Origin (s)
|
||||
│ ├── Packages (a(sss))
|
||||
│ └── RequestedPackages (as)
|
||||
├── Methods
|
||||
│ ├── PkgChange (sa{sv}) → o
|
||||
│ ├── Deploy (s) → o
|
||||
│ ├── Rebase (s) → o
|
||||
│ ├── Upgrade (sa{sv}) → o
|
||||
│ ├── Rollback () → o
|
||||
│ ├── Cleanup () → o
|
||||
│ ├── SetKargs (asasas) → ()
|
||||
│ └── GetKargs () → as
|
||||
└── Signals
|
||||
└── Changed ()
|
||||
```
|
||||
|
||||
### **2. Signal Flow Patterns**
|
||||
```
|
||||
Daemon Event → DBus Signal → Client Handler → User Display
|
||||
```
|
||||
|
||||
**Signal Types**:
|
||||
- **`Message`**: Text messages and status updates
|
||||
- **`TaskBegin`/`TaskEnd`**: Task start/completion notifications
|
||||
- **`PercentProgress`**: Progress percentage updates
|
||||
- **`DownloadProgress`**: Download progress details
|
||||
- **`Finished`**: Transaction completion notification
|
||||
- **`Changed`**: System state change notifications
|
||||
|
||||
### **3. Method Invocation Patterns**
|
||||
```
|
||||
Client Request → DBus Method → Daemon Handler → Operation Execution → Response
|
||||
```
|
||||
|
||||
**Method Flow**:
|
||||
1. **Client invokes** DBus method on daemon interface
|
||||
2. **Daemon receives** method call and validates parameters
|
||||
3. **Authorization check** performed via PolicyKit
|
||||
4. **Operation executed** with progress reporting
|
||||
5. **Response sent** back to client with results
|
||||
6. **Signals emitted** for progress and completion
|
||||
|
||||
## 📊 **Responsibility Matrix**
|
||||
|
||||
| Responsibility | CLI (rpm-ostree) | Daemon (rpm-ostreed) | Notes |
|
||||
|----------------|-------------------|----------------------|-------|
|
||||
| **Command Parsing** | ✅ Primary | ❌ None | CLI handles all user input |
|
||||
| **Argument Validation** | ✅ Primary | ❌ None | CLI validates before sending |
|
||||
| **DBus Communication** | ✅ Client | ✅ Server | Both sides of communication |
|
||||
| **OSTree Operations** | ❌ None | ✅ Primary | Daemon handles all OSTree errors |
|
||||
| **Package Management** | ❌ None | ✅ Primary | Daemon handles RPM/OSTree |
|
||||
| **Transaction Management** | ❌ None | ✅ Primary | Daemon manages lifecycle |
|
||||
| **Progress Reporting** | ✅ Display | ✅ Generation | Daemon generates, CLI displays |
|
||||
| **Error Handling** | ✅ User-facing | ✅ System-level | Different error contexts |
|
||||
| **Security** | ❌ None | ✅ Primary | Daemon handles authorization |
|
||||
| **Configuration** | ✅ Reading | ✅ Reading/Writing | Daemon can modify system |
|
||||
| **Fallback Operations** | ✅ Primary | ❌ None | CLI handles when daemon unavailable |
|
||||
| **System State** | ❌ None | ✅ Primary | Daemon monitors and manages |
|
||||
| **Resource Locking** | ❌ None | ✅ Primary | Daemon locks sysroot |
|
||||
| **Rollback Operations** | ❌ None | ✅ Primary | Daemon manages rollback |
|
||||
|
||||
## 🚀 **apt-ostree Implementation Strategy**
|
||||
|
||||
### **1. CLI Client (`apt-ostree`)**
|
||||
|
||||
#### **Core Responsibilities**
|
||||
```rust
|
||||
// src/main.rs - CLI entry point
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
|
||||
match args.get(1).map(|s| s.as_str()) {
|
||||
Some("status") => show_system_status().await?,
|
||||
Some("install") => install_packages(&args[2..]).await?,
|
||||
Some("upgrade") => upgrade_system().await?,
|
||||
Some("rollback") => rollback_system().await?,
|
||||
Some("--help") | Some("-h") => show_usage(),
|
||||
Some("--version") | Some("-V") => show_version(),
|
||||
_ => {
|
||||
eprintln!("Unknown command. Use --help for usage information.");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Command implementation with daemon fallback
|
||||
async fn install_packages(packages: &[String]) -> Result<(), Error> {
|
||||
// Try daemon first
|
||||
if let Ok(client) = AptOstreeClient::new().await {
|
||||
let transaction_id = client.create_transaction().await?;
|
||||
let success = client.install_packages(&transaction_id, packages.to_vec()).await?;
|
||||
|
||||
if success {
|
||||
println!("✅ Packages installed successfully!");
|
||||
} else {
|
||||
println!("❌ Package installation failed");
|
||||
}
|
||||
} else {
|
||||
// Fallback to direct operations (limited functionality)
|
||||
println!("⚠️ Daemon unavailable, using limited fallback mode");
|
||||
install_packages_direct(packages).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
#### **DBus Client Integration**
|
||||
```rust
|
||||
// src/client/dbus.rs - DBus client implementation
|
||||
pub struct AptOstreeClient {
|
||||
connection: zbus::Connection,
|
||||
sysroot_proxy: zbus::Proxy<'static>,
|
||||
}
|
||||
|
||||
impl AptOstreeClient {
|
||||
pub async fn new() -> Result<Self, Error> {
|
||||
let connection = zbus::Connection::system().await?;
|
||||
|
||||
let sysroot_proxy = zbus::Proxy::new(
|
||||
&connection,
|
||||
"org.projectatomic.aptostree1",
|
||||
"/org/projectatomic/aptostree1/Sysroot",
|
||||
"org.projectatomic.aptostree1.Sysroot",
|
||||
).await?;
|
||||
|
||||
Ok(Self { connection, sysroot_proxy })
|
||||
}
|
||||
|
||||
pub async fn create_transaction(&self) -> Result<String, Error> {
|
||||
let transaction_id = self.sysroot_proxy
|
||||
.call_method("CreateTransaction", &())
|
||||
.await?;
|
||||
|
||||
Ok(transaction_id.body::<String>()?)
|
||||
}
|
||||
|
||||
pub async fn install_packages(&self, transaction_id: &str, packages: Vec<String>) -> Result<bool, Error> {
|
||||
let success = self.sysroot_proxy
|
||||
.call_method("InstallPackages", &(transaction_id, packages))
|
||||
.await?;
|
||||
|
||||
Ok(success.body::<bool>()?)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### **2. Daemon (`apt-ostreed`)**
|
||||
|
||||
#### **Core Responsibilities**
|
||||
```rust
|
||||
// daemon/src/main.rs - Daemon entry point
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Initialize logging
|
||||
tracing_subscriber::fmt::init();
|
||||
|
||||
// Create daemon instance
|
||||
let daemon = Arc::new(AptOstreeDaemon::new()?);
|
||||
|
||||
// Set up DBus connection
|
||||
let _connection = ConnectionBuilder::system()?
|
||||
.name("org.projectatomic.aptostree1")?
|
||||
.serve_at("/org/projectatomic/aptostree1/Sysroot", daemon.clone())?
|
||||
.serve_at("/org/projectatomic/aptostree1/OS/debian", daemon.clone())?
|
||||
.build()
|
||||
.await?;
|
||||
|
||||
// Keep daemon running
|
||||
loop {
|
||||
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
|
||||
}
|
||||
}
|
||||
|
||||
// DBus interface implementation
|
||||
#[dbus_interface(name = "org.projectatomic.aptostree1.Sysroot")]
|
||||
impl AptOstreeDaemon {
|
||||
async fn create_transaction(&self) -> zbus::fdo::Result<String> {
|
||||
let transaction = Transaction::new(
|
||||
self.get_user_id().await?,
|
||||
self.get_session_id().await?,
|
||||
"Package installation".to_string(),
|
||||
"apt-ostree CLI".to_string(),
|
||||
);
|
||||
|
||||
let transaction_id = transaction.id.clone();
|
||||
self.transactions.write().await.insert(transaction_id.clone(), transaction);
|
||||
|
||||
Ok(transaction_id)
|
||||
}
|
||||
|
||||
async fn install_packages(&self, transaction_id: &str, packages: Vec<String>) -> zbus::fdo::Result<bool> {
|
||||
// Check authorization
|
||||
if !self.security_manager.check_package_operation(self.get_user_id().await?).await? {
|
||||
return Err(zbus::fdo::Error::PermissionDenied("Not authorized".into()));
|
||||
}
|
||||
|
||||
// Create and execute transaction
|
||||
let mut transaction = self.get_transaction(transaction_id).await?;
|
||||
transaction.add_operation(Operation::InstallPackage { packages });
|
||||
|
||||
match transaction.execute(self).await {
|
||||
Ok(()) => Ok(true),
|
||||
Err(_) => Ok(false),
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### **3. Integration Points**
|
||||
|
||||
#### **Progress Reporting**
|
||||
```rust
|
||||
// daemon/src/transaction.rs - Progress emission
|
||||
impl Transaction {
|
||||
pub async fn emit_progress(&self, operation: &Operation, progress: u32, message: &str) {
|
||||
// Emit DBus signal for progress
|
||||
if let Some(daemon) = &self.daemon {
|
||||
daemon.emit_signal(
|
||||
"org.projectatomic.aptostree1.Transaction",
|
||||
"PercentProgress",
|
||||
&(message, progress),
|
||||
).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CLI progress handling
|
||||
impl AptOstreeClient {
|
||||
pub async fn monitor_progress(&self, transaction_id: &str) -> Result<(), Error> {
|
||||
let mut stream = self.connection
|
||||
.receive_signal_with_args(
|
||||
"org.projectatomic.aptostree1.Transaction",
|
||||
"PercentProgress",
|
||||
&[("transaction_id", transaction_id)],
|
||||
).await?;
|
||||
|
||||
while let Some(msg) = stream.next().await {
|
||||
let (message, progress): (String, u32) = msg.body()?;
|
||||
println!("[{}%] {}", progress, message);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🎯 **Key Implementation Principles**
|
||||
|
||||
### **1. Clear Separation of Concerns**
|
||||
- **CLI**: User interface, command parsing, progress display
|
||||
- **Daemon**: System operations, OSTree management, security
|
||||
|
||||
### **2. Graceful Degradation**
|
||||
- **Primary mode**: Full functionality via daemon
|
||||
- **Fallback mode**: Limited functionality when daemon unavailable
|
||||
|
||||
### **3. Security by Design**
|
||||
- **Privilege isolation**: Daemon handles privileged operations
|
||||
- **Authorization**: PolicyKit integration for all system changes
|
||||
- **Sandboxing**: Package script execution in controlled environment
|
||||
|
||||
### **4. Transaction Safety**
|
||||
- **Atomic operations**: All changes are atomic
|
||||
- **Rollback support**: Automatic rollback on failure
|
||||
- **Progress tracking**: Real-time operation monitoring
|
||||
|
||||
### **5. Performance Optimization**
|
||||
- **Caching**: Intelligent caching of deployment and package information
|
||||
- **Parallel operations**: Concurrent package processing where possible
|
||||
- **Resource management**: Efficient memory and disk usage
|
||||
|
||||
This CLI-daemon separation analysis provides the foundation for implementing a production-ready apt-ostree system that maintains the proven architecture of rpm-ostree while adapting to the Debian/Ubuntu package management paradigm. The clear separation of responsibilities ensures maintainability, security, and scalability.
|
||||
Loading…
Add table
Add a link
Reference in a new issue