diff --git a/openapi/sled-agent.json b/openapi/sled-agent.json index 90eea0504b..fe000f75ca 100644 --- a/openapi/sled-agent.json +++ b/openapi/sled-agent.json @@ -754,6 +754,33 @@ } } }, + "/support/pfiles-info": { + "get": { + "operationId": "support_pfiles_info", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "title": "Array_of_SledDiagnosticsQueryOutput", + "type": "array", + "items": { + "$ref": "#/components/schemas/SledDiagnosticsQueryOutput" + } + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, "/support/pstack-info": { "get": { "operationId": "support_pstack_info", diff --git a/sled-agent/api/src/lib.rs b/sled-agent/api/src/lib.rs index 18f397a51c..d03ddc0b52 100644 --- a/sled-agent/api/src/lib.rs +++ b/sled-agent/api/src/lib.rs @@ -664,6 +664,14 @@ pub trait SledAgentApi { async fn support_pstack_info( request_context: RequestContext, ) -> Result>, HttpError>; + + #[endpoint { + method = GET, + path = "/support/pfiles-info", + }] + async fn support_pfiles_info( + request_context: RequestContext, + ) -> Result>, HttpError>; } #[derive(Clone, Debug, Deserialize, JsonSchema, Serialize)] diff --git a/sled-agent/src/http_entrypoints.rs b/sled-agent/src/http_entrypoints.rs index 0c7706a459..b0deec1f4a 100644 --- a/sled-agent/src/http_entrypoints.rs +++ b/sled-agent/src/http_entrypoints.rs @@ -1027,4 +1027,18 @@ impl SledAgentApi for SledAgentImpl { .collect::>(), )) } + + async fn support_pfiles_info( + request_context: RequestContext, + ) -> Result>, HttpError> + { + let sa = request_context.context(); + Ok(HttpResponseOk( + sa.support_pfiles_info() + .await + .into_iter() + .map(|cmd| cmd.get_output()) + .collect::>(), + )) + } } diff --git a/sled-agent/src/sim/http_entrypoints.rs b/sled-agent/src/sim/http_entrypoints.rs index a0634d132a..f6dc4a8cd4 100644 --- a/sled-agent/src/sim/http_entrypoints.rs +++ b/sled-agent/src/sim/http_entrypoints.rs @@ -754,6 +754,13 @@ impl SledAgentApi for SledAgentSimImpl { { method_unimplemented() } + + async fn support_pfiles_info( + _request_context: RequestContext, + ) -> Result>, HttpError> + { + method_unimplemented() + } } fn method_unimplemented() -> Result { diff --git a/sled-agent/src/sled_agent.rs b/sled-agent/src/sled_agent.rs index c626957097..3338665a55 100644 --- a/sled-agent/src/sled_agent.rs +++ b/sled-agent/src/sled_agent.rs @@ -1400,6 +1400,12 @@ impl SledAgent { ) -> Vec> { sled_diagnostics::pstack_oxide_processes(&self.log).await } + + pub(crate) async fn support_pfiles_info( + &self, + ) -> Vec> { + sled_diagnostics::pfiles_oxide_processes(&self.log).await + } } #[derive(From, thiserror::Error, Debug)] diff --git a/sled-diagnostics/src/lib.rs b/sled-diagnostics/src/lib.rs index fb596977f1..dba055b85f 100644 --- a/sled-diagnostics/src/lib.rs +++ b/sled-diagnostics/src/lib.rs @@ -102,3 +102,24 @@ pub async fn pstack_oxide_processes( .collect::>>() .await } + +pub async fn pfiles_oxide_processes( + log: &Logger, +) -> Vec> { + // In a diagnostics context we care about looping over every pid we find, + // but on failure we should just return a single error in a vec that + // represents the entire failed operation. + let pids = match contract::find_oxide_pids(log) { + Ok(pids) => pids, + Err(e) => return vec![Err(e.into())], + }; + + pids.iter() + .map(|pid| pfiles_process(*pid)) + .map(|c| async move { + execute_command_with_timeout(c, DEFAULT_TIMEOUT).await + }) + .collect::>() + .collect::>>() + .await +} diff --git a/sled-diagnostics/src/queries.rs b/sled-diagnostics/src/queries.rs index 2e8cf55993..418005e34b 100644 --- a/sled-diagnostics/src/queries.rs +++ b/sled-diagnostics/src/queries.rs @@ -23,6 +23,7 @@ use crate::contract_stub::ContractError; const DLADM: &str = "/usr/sbin/dladm"; const IPADM: &str = "/usr/sbin/ipadm"; const PFEXEC: &str = "/usr/bin/pfexec"; +const PFILES: &str = "/usr/bin/pfiles"; const PSTACK: &str = "/usr/bin/pstack"; const PARGS: &str = "/usr/bin/pargs"; const ZONEADM: &str = "/usr/sbin/zoneadm"; @@ -247,6 +248,12 @@ pub fn pstack_process(pid: i32) -> Command { cmd } +pub fn pfiles_process(pid: i32) -> Command { + let mut cmd = std::process::Command::new(PFEXEC); + cmd.env_clear().arg(PFILES).arg(pid.to_string()); + cmd +} + #[cfg(test)] mod test { use super::*;