cli/command/
cache.rs

1// SPDX-License-Identifier: GPL-3-0-or-later
2// Copyright (c) 2025 Opinsys Oy
3// Copyright (c) 2024-2025 Jarkko Sakkinen
4
5use crate::{
6    cli::SubCommand,
7    command::{print_table, CommandError, Tabled},
8    device::with_device,
9    job::Job,
10    vtpm::{RefreshAction, VtpmSession},
11};
12use clap::Args;
13
14struct CacheRow {
15    handle: String,
16    class: String,
17    details: String,
18}
19
20impl Tabled for CacheRow {
21    fn headers() -> Vec<String> {
22        vec![
23            "HANDLE".to_string(),
24            "TYPE".to_string(),
25            "DETAILS".to_string(),
26        ]
27    }
28
29    fn row(&self) -> Vec<String> {
30        vec![
31            self.handle.clone(),
32            self.class.clone(),
33            self.details.clone(),
34        ]
35    }
36}
37
38/// Lists cached TPM objects.
39#[derive(Args, Debug)]
40#[command(about = "Lists cached TPM objects.")]
41pub struct Cache {}
42
43impl Cache {
44    fn refresh_cache(job: &mut Job) -> Result<(), CommandError> {
45        with_device(job.device.clone(), |dev| {
46            let vhandles: Vec<u32> = job.cache.contexts.keys().copied().collect();
47            let mut handles_to_remove = Vec::new();
48            let mut encountered_error: Option<CommandError> = None;
49
50            for vhandle in vhandles {
51                if let Some(context) = job.cache.contexts.get_mut(&vhandle) {
52                    match context.refresh(dev) {
53                        Ok(RefreshAction::Keep) => {}
54                        Ok(RefreshAction::Stale) => {
55                            log::debug!("vtpm:{vhandle:08x} is stale");
56                            handles_to_remove.push(vhandle);
57                        }
58                        Ok(RefreshAction::Updated(context)) => {
59                            if let Some(session) = job
60                                .cache
61                                .contexts
62                                .get_mut(&vhandle)
63                                .and_then(|ctx| ctx.as_any_mut().downcast_mut::<VtpmSession>())
64                            {
65                                session.context = *context;
66                                job.cache.mark_dirty(vhandle);
67                            } else {
68                                log::error!("vtpm:{vhandle:08x}: context type mismatch");
69                                handles_to_remove.push(vhandle);
70                            }
71                        }
72                        Err(e) => {
73                            log::warn!("vtpm:{vhandle:08x}: {e}");
74                            handles_to_remove.push(vhandle);
75                            if encountered_error.is_none() {
76                                encountered_error = Some(CommandError::from(e));
77                            }
78                        }
79                    }
80                }
81            }
82
83            for vhandle in handles_to_remove {
84                if let Err(e) = job.cache.remove(dev, vhandle) {
85                    log::error!("vtpm:{vhandle:08x}: {e}");
86                    if encountered_error.is_none() {
87                        encountered_error = Some(CommandError::from(e));
88                    }
89                }
90            }
91
92            if let Some(err) = encountered_error {
93                Err(err)
94            } else {
95                Ok(())
96            }
97        })
98    }
99}
100
101impl SubCommand for Cache {
102    fn run(&self, job: &mut Job) -> Result<(), CommandError> {
103        Self::refresh_cache(job)?;
104
105        let mut rows: Vec<CacheRow> = job
106            .cache
107            .contexts
108            .values()
109            .map(|ctx| CacheRow {
110                handle: format!("{:08x}", ctx.handle()),
111                class: ctx.class().to_string(),
112                details: ctx.details(),
113            })
114            .collect();
115        rows.sort_unstable_by(|a, b| a.handle.cmp(&b.handle));
116
117        print_table(&mut job.writer, &rows)?;
118        Ok(())
119    }
120}