# rpm-ostree install Command Flow Analysis ## Overview This document provides a detailed, step-by-step analysis of what happens when a user runs `rpm-ostree install htop`. The analysis is based on examination of the rpm-ostree source code and reveals the complex orchestration between client-side CLI parsing, D-Bus communication, daemon-side processing, and the underlying OSTree and DNF (libdnf) systems. ## Command Entry Point ### 1. CLI Parsing and Validation **File**: `src/app/rpmostree-pkg-builtins.cxx` **Function**: `rpmostree_builtin_install()` ```cpp gboolean rpmostree_builtin_install (int argc, char **argv, RpmOstreeCommandInvocation *invocation, GCancellable *cancellable, GError **error) ``` **Steps**: 1. **Option Parsing**: Parse command-line options using GLib's `GOptionContext` - `--reboot`, `--dry-run`, `--apply-live`, `--idempotent`, etc. - Package names are extracted from positional arguments 2. **Argument Validation**: Ensure at least one package is specified ```cpp if (argc < 2) { rpmostree_usage_error (context, "At least one PACKAGE must be specified", error); return FALSE; } ``` 3. **Interactive Confirmation**: Handle `--apply-live` without `--assumeyes` - If running interactively, perform a dry-run first and prompt for confirmation - If non-interactive (script), auto-infer `--assumeyes` with a warning 4. **Container Detection**: Check if running in an OSTree container - If in container, use different code path for container rebuilds - Otherwise, proceed with normal system installation ### 2. Core Package Change Logic **Function**: `pkg_change()` **Steps**: 1. **Package Classification**: Determine package types ```cpp gboolean met_local_pkg = FALSE; for (const char *const *it = packages_to_add; it && *it; it++) met_local_pkg = met_local_pkg || g_str_has_suffix (*it, ".rpm") || g_str_has_prefix (*it, "file://"); ``` 2. **Option Dictionary Creation**: Build GVariant dictionary with all options ```cpp GVariantDict dict; g_variant_dict_init (&dict, NULL); g_variant_dict_insert (&dict, "reboot", "b", opt_reboot); g_variant_dict_insert (&dict, "cache-only", "b", opt_cache_only); g_variant_dict_insert (&dict, "download-only", "b", opt_download_only); // ... more options ``` 3. **API Selection**: Choose between legacy and new D-Bus APIs - **Legacy API**: `rpmostree_os_call_pkg_change_sync()` for simple cases - **New API**: `rpmostree_update_deployment()` for complex cases (local packages, apply-live, etc.) ## D-Bus Communication Layer ### 3. Client-Side D-Bus Setup **File**: `src/app/rpmostree-clientlib.cxx` **Steps**: 1. **Daemon Startup**: Ensure rpm-ostreed daemon is running ```cpp ROSCXX_TRY (client_start_daemon (), error); ``` 2. **System Bus Connection**: Connect to system D-Bus ```cpp g_autoptr (GDBusConnection) connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, cancellable, error); ``` 3. **Client Registration**: Register as a client with the daemon ```cpp g_dbus_connection_call_sync (connection, bus_name, sysroot_objpath, "org.projectatomic.rpmostree1.Sysroot", "RegisterClient", ...); ``` 4. **OS Proxy Creation**: Get proxy to the OS interface ```cpp glnx_unref_object RPMOSTreeOS *os_proxy = NULL; if (!rpmostree_load_os_proxy (sysroot_proxy, opt_osname, cancellable, &os_proxy, error)) return FALSE; ``` ### 4. D-Bus Method Invocation **Legacy API Path** (`rpmostree_os_call_pkg_change_sync`): ```cpp if (!rpmostree_os_call_pkg_change_sync (os_proxy, options, install_pkgs, uninstall_pkgs, NULL, &transaction_address, NULL, cancellable, error)) return FALSE; ``` **New API Path** (`rpmostree_update_deployment`): ```cpp if (!rpmostree_update_deployment (os_proxy, NULL, /* refspec */ NULL, /* revision */ install_pkgs, install_fileoverride_pkgs, uninstall_pkgs, NULL, /* override replace */ NULL, /* override remove */ NULL, /* override reset */ NULL, /* local_repo_remote */ NULL, /* treefile */ options, &transaction_address, cancellable, error)) return FALSE; ``` ## Daemon-Side Processing ### 5. D-Bus Method Handler **File**: `src/daemon/rpmostreed-os.cxx` **Steps**: 1. **Authorization Check**: Verify user permissions via Polkit ```cpp else if (g_strcmp0 (method_name, "PkgChange") == 0) { g_ptr_array_add (actions, (void *)"org.projectatomic.rpmostree1.install-uninstall-packages"); } ``` 2. **Transaction Management**: Check for existing transactions or create new one ```cpp if (!rpmostreed_sysroot_prep_for_txn (rsysroot, invocation, &transaction, &local_error)) return os_throw_dbus_invocation_error (invocation, &local_error); ``` 3. **Transaction Creation**: Create appropriate transaction type - For package changes: `rpmostreed_transaction_new_pkg_change()` - For deployment updates: `rpmostreed_transaction_new_update_deployment()` 4. **Transaction Address Return**: Return D-Bus object path for transaction monitoring ```cpp const char *client_address = rpmostreed_transaction_get_client_address (transaction); rpmostree_os_complete_pkg_change (interface, invocation, client_address); ``` ### 6. Transaction Processing **File**: `src/daemon/rpmostreed-transaction.cxx` **Steps**: 1. **Transaction State Management**: Track transaction progress and state 2. **Dependency Resolution**: Use libdnf to resolve package dependencies 3. **Package Download**: Download required RPM packages from repositories 4. **OSTree Integration**: Prepare for OSTree commit creation ## Core Package Management ### 7. DNF Integration (libdnf) **Steps**: 1. **Repository Setup**: Initialize DNF context with configured repositories ```cpp g_autoptr (DnfContext) dnfctx = dnf_context_new (); ``` 2. **Package Resolution**: Resolve package names to specific RPM packages ```cpp hy_autoquery HyQuery query = hy_query_create (dnf_context_get_sack (dnfctx)); hy_query_filter (query, HY_PKG_NAME, HY_EQ, package_name); ``` 3. **Dependency Resolution**: Calculate full dependency tree - Resolve all required dependencies - Handle conflicts and alternatives - Determine download order 4. **Package Download**: Download RPM files to local cache - Use DNF's download infrastructure - Verify package integrity - Handle network failures and retries ### 8. OSTree Integration **Steps**: 1. **Base Tree Preparation**: Get current OSTree deployment as base ```cpp OstreeDeployment *booted = ostree_sysroot_get_booted_deployment (sysroot); ``` 2. **Package Import**: Import downloaded RPMs into OSTree repository - Extract RPM contents - Apply to filesystem tree - Handle file conflicts and replacements 3. **Commit Creation**: Create new OSTree commit with layered packages ```cpp // Create new commit with package changes ostree_repo_prepare_transaction (repo, NULL, NULL, error); // ... package application logic ... ostree_repo_commit_transaction (repo, NULL, error); ``` 4. **Metadata Generation**: Generate commit metadata - Package lists and versions - Dependency information - Origin and timestamp data ## Filesystem and Deployment ### 9. Filesystem Assembly **Steps**: 1. **Base Tree Checkout**: Checkout base OSTree commit to temporary location 2. **Package Application**: Apply RPM contents to filesystem - Extract files from RPMs - Handle file permissions and ownership - Apply package scripts (preinst, postinst) 3. **Layer Management**: Create layered filesystem structure - `/usr` - Base system files (immutable) - `/var` - Variable data (mutable) - `/etc` - Configuration (merged) - Package overlays in `/usr/lib/rpm-ostree/` 4. **Script Execution**: Run package installation scripts - Pre-installation scripts - Post-installation scripts - Handle script failures and rollback ### 10. Deployment Management **Steps**: 1. **Deployment Creation**: Create new OSTree deployment ```cpp ostree_sysroot_deploy_tree (sysroot, osname, new_commit, ...); ``` 2. **Boot Configuration**: Update bootloader configuration - Generate new initramfs if needed - Update kernel arguments - Configure boot entries 3. **State Persistence**: Save deployment state - Update deployment index - Save package metadata - Update rollback information ## Transaction Monitoring ### 11. Progress Reporting **Steps**: 1. **Signal Emission**: Emit D-Bus signals for progress ```cpp // Progress signals rpmostree_transaction_emit_percent_progress (transaction, "Downloading packages", 50); rpmostree_transaction_emit_message (transaction, "Installing htop-2.2.0-1.fc33.x86_64"); ``` 2. **Client Monitoring**: Client receives and displays progress ```cpp // Client connects to transaction and monitors signals rpmostree_transaction_client_run (invocation, sysroot_proxy, os_proxy, options, ...); ``` 3. **Error Handling**: Handle failures and provide rollback - Network failures during download - Package conflicts - Script execution failures - OSTree commit failures ### 12. Transaction Completion **Steps**: 1. **Success Path**: - Mark transaction as successful - Emit completion signals - Clean up temporary files - Update system state 2. **Failure Path**: - Rollback to previous state - Clean up partial changes - Emit error signals - Provide error details 3. **Reboot Handling**: If `--reboot` specified ```cpp if (opt_reboot) { // Schedule reboot after transaction completion rpmostree_sysroot_deploy_tree (sysroot, osname, new_commit, OSTREE_SYSROOT_DEPLOY_FLAGS_REBOOT, ...); } ``` ## Final Steps ### 13. System State Update **Steps**: 1. **Deployment Activation**: Activate new deployment - Update bootloader entries - Set new deployment as default - Preserve rollback deployment 2. **Cache Updates**: Update package metadata cache - Refresh repository metadata - Update package lists - Cache dependency information 3. **Cleanup**: Remove temporary files and caches - Clean downloaded RPMs - Remove temporary directories - Update system journal ### 14. User Feedback **Steps**: 1. **Status Display**: Show final status ```bash # Example output Checking out packages...done Running pre scripts...done Running post scripts...done Writing rpmdb...done Writing OSTree commit...done ``` 2. **Deployment Information**: Display deployment details - New deployment checksum - Package changes summary - Reboot requirement (if applicable) 3. **Next Steps**: Provide guidance for user - Reboot instructions if needed - Package verification commands - Rollback instructions ## Key Architectural Insights ### 1. **Client-Daemon Architecture** - CLI client handles user interaction and D-Bus communication - Daemon (rpm-ostreed) handles all privileged operations - Clear separation of concerns and security boundaries ### 2. **Transaction-Based Design** - All operations wrapped in transactions - Atomic operations with rollback capability - Progress monitoring and cancellation support ### 3. **Layered Filesystem Model** - Base OSTree commit remains immutable - Package changes applied as overlays - Clear separation between system and user packages ### 4. **Dependency Management** - Full dependency resolution via libdnf - Handles complex dependency graphs - Conflict resolution and alternatives ### 5. **Error Handling and Recovery** - Comprehensive error handling at each stage - Automatic rollback on failures - Detailed error reporting and logging This analysis reveals the sophisticated architecture of rpm-ostree, which combines the atomic deployment model of OSTree with the powerful package management capabilities of DNF, all orchestrated through a robust client-daemon architecture with comprehensive transaction management.