blob: 8dfb274f38197f2ff34c8504cbd99e1d839bc4d9 [file] [log] [blame] [edit]
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// All interactions with dlcservice is wrapped here. This includes both
// sending D-BUS methods and responding to signals.
use super::{signal, DEFAULT_DBUS_TIMEOUT};
use crate::common::*;
use crate::dbus_constants::dlc_service;
use crate::shader_cache_mount::ShaderCacheMountMapPtr;
use anyhow::{anyhow, Result};
use dbus::nonblock::SyncConnection;
use libchromeos::sys::{debug, info, warn};
use std::sync::Arc;
use system_api::{dlcservice::DlcState, shadercached::ShaderCacheMountStatus};
pub async fn handle_dlc_state_changed(
raw_bytes: Vec<u8>,
mount_map: ShaderCacheMountMapPtr,
conn: Arc<SyncConnection>,
) {
// If shader cache DLC was installed, mount the DLC to a MountPoint that
// wants this DLC.
let dlc_state: DlcState = protobuf::Message::parse_from_bytes(&raw_bytes).unwrap();
debug!(
"DLC state changed: {}, {}",
dlc_state.get_id(),
dlc_state.get_progress()
);
if dlc_state.get_state() == system_api::dlcservice::DlcState_State::INSTALLED
&& dlc_state.get_progress() == 1.0
{
if let Ok(id) = dlc_to_steam_app_id(dlc_state.get_id()) {
info!("DLC state changed for shader cache DLC");
debug!("ShaderCache DLC for {} installed, mounting if required", id);
if let Err(e) = mount_dlc(id, mount_map, conn).await {
warn!("Mount failed, {}", e);
}
}
}
}
async fn mount_dlc(
steam_app_id: SteamAppId,
mount_map: ShaderCacheMountMapPtr,
conn: Arc<SyncConnection>,
) -> Result<()> {
info!("Mounting DLC");
// Iterate through all mount points then attempt to mount shader cache if
// |target_steam_app_id| matches |steam_app_id_to_mount| (which was just
// installed)
let mut mount_map = mount_map.write().await;
let mut errors: Vec<String> = vec![];
for (vm_id, shader_cache_mount) in mount_map.iter_mut() {
if shader_cache_mount.is_pending_mount(&steam_app_id) {
debug!("Mounting for {:?}", vm_id);
let mount_result = shader_cache_mount
.setup_mount_destination(vm_id, steam_app_id, conn.clone())
.await
.and_then(|_| shader_cache_mount.bind_mount_dlc(steam_app_id))
.and_then(|_| shader_cache_mount.add_game_to_db_list(steam_app_id));
let mut mount_status = ShaderCacheMountStatus::new();
mount_status.set_mounted(mount_result.is_ok());
mount_status.set_vm_name(vm_id.vm_name.clone());
mount_status.set_vm_owner_id(vm_id.vm_owner_id.clone());
mount_status.set_steam_app_id(steam_app_id);
if let Err(e) = mount_result {
errors.push(format!("Failed to mount {:?}, {:?}", vm_id, e));
mount_status.set_error(e.to_string());
}
if let Err(e) = signal::signal_mount_status(vec![mount_status], &conn) {
errors.push(format!("Failed to send mount signal, {:?}\n", e));
}
}
}
if errors.is_empty() {
Ok(())
} else {
Err(anyhow!("{:?}", errors))
}
}
pub async fn unmount_dlc(
steam_app_id_to_unmount: SteamAppId,
mount_map: ShaderCacheMountMapPtr,
) -> Result<()> {
info!("Unmounting DLC");
// Iterate through all mount points then queue unmount for
// |steam_app_id_to_unmount|
{
// |mount_map| with write mutex needs to go out of scope after this
// loop so that background unmounter can take the mutex
let mut mount_map = mount_map.write().await;
for (vm_id, shader_cache_mount) in mount_map.iter_mut() {
debug!(
"Processing DLC {} unmount for VM {:?}",
steam_app_id_to_unmount, vm_id
);
shader_cache_mount.remove_game_from_db_list(steam_app_id_to_unmount)?;
}
}
Ok(())
}
pub async fn install_shader_cache_dlc(
steam_game_id: SteamAppId,
conn: Arc<SyncConnection>,
) -> Result<()> {
let dlcservice_proxy = dbus::nonblock::Proxy::new(
dlc_service::SERVICE_NAME,
dlc_service::PATH_NAME,
DEFAULT_DBUS_TIMEOUT,
conn,
);
let dlc_name = steam_app_id_to_dlc(steam_game_id);
debug!("Requesting to install dlc {}", dlc_name);
dlcservice_proxy
.method_call(
dlc_service::INTERFACE_NAME,
dlc_service::INSTALL_METHOD,
(dlc_name,),
)
.await?;
Ok(())
}
pub async fn uninstall_shader_cache_dlc(
steam_game_id: SteamAppId,
conn: Arc<SyncConnection>,
) -> Result<()> {
let dlcservice_proxy = dbus::nonblock::Proxy::new(
dlc_service::SERVICE_NAME,
dlc_service::PATH_NAME,
DEFAULT_DBUS_TIMEOUT,
conn,
);
let dlc_name = steam_app_id_to_dlc(steam_game_id);
debug!("Requesting to uninstall dlc {}", dlc_name);
dlcservice_proxy
.method_call(
dlc_service::INTERFACE_NAME,
dlc_service::UNINSTALL_METHOD,
(dlc_name,),
)
.await?;
Ok(())
}
pub async fn uninstall_all_shader_cache_dlcs(conn: Arc<SyncConnection>) -> Result<()> {
let dlcservice_proxy = dbus::nonblock::Proxy::new(
dlc_service::SERVICE_NAME,
dlc_service::PATH_NAME,
DEFAULT_DBUS_TIMEOUT,
conn.clone(),
);
let (installed_ids,): (Vec<String>,) = dlcservice_proxy
.method_call(
dlc_service::INTERFACE_NAME,
dlc_service::GET_INSTALLED_METHOD,
(),
)
.await?;
for dlc_id in installed_ids {
if let Ok(steam_game_id) = dlc_to_steam_app_id(&dlc_id) {
uninstall_shader_cache_dlc(steam_game_id, conn.clone()).await?;
}
}
Ok(())
}