tracexec_core/
printer.rs

1use std::{
2  cell::RefCell,
3  collections::BTreeMap,
4  fmt::{Debug, Display},
5  io::{self, Write},
6  sync::Arc,
7};
8
9use crate::{
10  cli::{
11    args::{LogModeArgs, ModifierArgs},
12    theme::THEME,
13  },
14  event::{FriendlyError, OutputMsg},
15  proc::{BaselineInfo, FileDescriptorInfo, FileDescriptorInfoCollection, Interpreter, diff_env},
16  timestamp::TimestampFormat,
17  tracer::ExecData,
18};
19
20use crate::cache::ArcStr;
21use itertools::chain;
22use nix::{fcntl::OFlag, libc::ENOENT, unistd::Pid};
23use owo_colors::{OwoColorize, Style};
24
25macro_rules! escape_str_for_bash {
26  ($x:expr) => {{
27    let result: String = shell_quote::QuoteRefExt::quoted($x, shell_quote::Bash);
28    result
29  }};
30}
31
32#[derive(Debug, Clone, Copy)]
33pub enum EnvPrintFormat {
34  Diff,
35  Raw,
36  None,
37}
38
39#[derive(Debug, Clone, Copy)]
40pub enum FdPrintFormat {
41  Diff,
42  Raw,
43  None,
44}
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd)]
47pub enum ColorLevel {
48  Less,
49  Normal,
50  More,
51}
52
53#[derive(Debug, Clone)]
54pub struct PrinterArgs {
55  pub trace_comm: bool,
56  pub trace_argv: bool,
57  pub trace_env: EnvPrintFormat,
58  pub trace_fd: FdPrintFormat,
59  pub trace_cwd: bool,
60  pub print_cmdline: bool,
61  pub successful_only: bool,
62  pub trace_interpreter: bool,
63  pub trace_filename: bool,
64  pub decode_errno: bool,
65  pub color: ColorLevel,
66  pub stdio_in_cmdline: bool,
67  pub fd_in_cmdline: bool,
68  pub hide_cloexec_fds: bool,
69  pub inline_timestamp_format: Option<TimestampFormat>,
70}
71
72impl PrinterArgs {
73  pub fn from_cli(tracing_args: &LogModeArgs, modifier_args: &ModifierArgs) -> Self {
74    Self {
75      trace_comm: !tracing_args.no_show_comm,
76      trace_argv: !tracing_args.no_show_argv && !tracing_args.show_cmdline,
77      trace_env: match (
78        tracing_args.show_cmdline,
79        tracing_args.diff_env,
80        tracing_args.no_diff_env,
81        tracing_args.show_env,
82        tracing_args.no_show_env,
83      ) {
84        (true, ..) | (.., true) => EnvPrintFormat::None,
85        (false, .., true, _) | (false, _, true, ..) => EnvPrintFormat::Raw,
86        _ => EnvPrintFormat::Diff, // diff_env is enabled by default
87      },
88      trace_fd: match (
89        tracing_args.diff_fd,
90        tracing_args.no_diff_fd,
91        tracing_args.show_fd,
92        tracing_args.no_show_fd,
93      ) {
94        (false, _, true, false) => FdPrintFormat::Raw,
95        (_, true, _, _) => FdPrintFormat::None,
96        (true, _, _, _) => FdPrintFormat::Diff,
97        _ => {
98          // The default is diff fd,
99          // but if fd_in_cmdline or stdio_in_cmdline is enabled, we disable diff fd by default
100          if modifier_args.fd_in_cmdline || modifier_args.stdio_in_cmdline {
101            FdPrintFormat::None
102          } else {
103            FdPrintFormat::Diff
104          }
105        }
106      },
107      trace_cwd: tracing_args.show_cwd,
108      print_cmdline: tracing_args.show_cmdline,
109      successful_only: modifier_args.successful_only,
110      trace_interpreter: tracing_args.show_interpreter,
111      trace_filename: match (tracing_args.show_filename, tracing_args.no_show_filename) {
112        (_, true) => false,
113        (true, _) => true,
114        // default
115        _ => true,
116      },
117      decode_errno: !tracing_args.no_decode_errno,
118      color: match (tracing_args.more_colors, tracing_args.less_colors) {
119        (false, false) => ColorLevel::Normal,
120        (true, false) => ColorLevel::More,
121        (false, true) => ColorLevel::Less,
122        _ => unreachable!(),
123      },
124      stdio_in_cmdline: modifier_args.stdio_in_cmdline,
125      fd_in_cmdline: modifier_args.fd_in_cmdline,
126      hide_cloexec_fds: modifier_args.hide_cloexec_fds,
127      inline_timestamp_format: modifier_args.timestamp.then(||
128        // We ensure a default is set in modifier_args
129        modifier_args.inline_timestamp_format.clone().unwrap()),
130    }
131  }
132}
133
134pub type PrinterOut = dyn Write + Send + Sync + 'static;
135
136enum DeferredWarningKind {
137  NoArgv0,
138  FailedReadingArgv(FriendlyError),
139  FailedReadingFilename(FriendlyError),
140  FailedReadingEnvp(FriendlyError),
141}
142
143struct DeferredWarnings {
144  warning: DeferredWarningKind,
145  pid: Pid,
146}
147
148impl Drop for DeferredWarnings {
149  fn drop(&mut self) {
150    Printer::OUT.with_borrow_mut(|out| {
151      if let Some(out) = out {
152        write!(out, "{}", self.pid.bright_red()).unwrap();
153        write!(out, "[{}]: ", "warning".bright_yellow()).unwrap();
154        match self.warning {
155          DeferredWarningKind::NoArgv0 => {
156            write!(
157              out,
158              "No argv[0] provided! The printed commandline might be incorrect!"
159            )
160            .unwrap();
161          }
162          DeferredWarningKind::FailedReadingArgv(e) => {
163            write!(out, "Failed to read argv: {e}").unwrap();
164          }
165          DeferredWarningKind::FailedReadingFilename(e) => {
166            write!(out, "Failed to read filename: {e}").unwrap();
167          }
168          DeferredWarningKind::FailedReadingEnvp(e) => {
169            write!(out, "Failed to read envp: {e}").unwrap();
170          }
171        };
172        writeln!(out).unwrap();
173      };
174    })
175  }
176}
177
178pub struct ListPrinter {
179  style: owo_colors::Style,
180}
181
182impl ListPrinter {
183  pub fn new(color: ColorLevel) -> Self {
184    if color > ColorLevel::Normal {
185      Self {
186        style: Style::new().bright_white().bold(),
187      }
188    } else {
189      Self {
190        style: Style::new(),
191      }
192    }
193  }
194
195  pub fn begin(&self, out: &mut dyn Write) -> io::Result<()> {
196    write!(out, "{}", "[".style(self.style))
197  }
198
199  pub fn end(&self, out: &mut dyn Write) -> io::Result<()> {
200    write!(out, "{}", "]".style(self.style))
201  }
202
203  pub fn comma(&self, out: &mut dyn Write) -> io::Result<()> {
204    write!(out, "{}", ", ".style(self.style))
205  }
206
207  pub fn print_string_list(&self, out: &mut dyn Write, list: &[impl Display]) -> io::Result<()> {
208    self.begin(out)?;
209    if let Some((last, rest)) = list.split_last() {
210      if rest.is_empty() {
211        write!(out, "{last}")?;
212      } else {
213        for s in rest {
214          write!(out, "{s}")?;
215          self.comma(out)?;
216        }
217        write!(out, "{last}")?;
218      }
219    }
220    self.end(out)
221  }
222
223  pub fn print_env(
224    &self,
225    out: &mut dyn Write,
226    env: &BTreeMap<OutputMsg, OutputMsg>,
227  ) -> io::Result<()> {
228    self.begin(out)?;
229    let mut first_item_written = false;
230    let mut write_separator = |out: &mut dyn Write| -> io::Result<()> {
231      if first_item_written {
232        self.comma(out)?;
233      } else {
234        first_item_written = true;
235      }
236      Ok(())
237    };
238    for (k, v) in env.iter() {
239      write_separator(out)?;
240      write!(out, "{k}={v}")?;
241    }
242    self.end(out)
243  }
244}
245
246pub struct Printer {
247  pub args: PrinterArgs,
248  baseline: Arc<BaselineInfo>,
249}
250
251impl Printer {
252  pub fn new(args: PrinterArgs, baseline: Arc<BaselineInfo>) -> Self {
253    Self { args, baseline }
254  }
255
256  thread_local! {
257    pub static OUT: RefCell<Option<Box<PrinterOut>>> = RefCell::new(None);
258  }
259
260  pub fn init_thread_local(&self, output: Option<Box<PrinterOut>>) {
261    Self::OUT.with(|out| {
262      *out.borrow_mut() = output;
263    });
264  }
265
266  pub fn print_new_child(&self, parent: Pid, comm: &str, child: Pid) -> color_eyre::Result<()> {
267    Self::OUT.with_borrow_mut(|out| {
268      let Some(out) = out else {
269        return Ok(());
270      };
271      write!(out, "{}", parent.bright_green())?;
272      if self.args.trace_comm {
273        write!(out, "<{}>", comm.cyan())?;
274      }
275      writeln!(out, ": {}: {}", "new child".purple(), child.bright_green())?;
276      out.flush()?;
277      Ok(())
278    })
279  }
280
281  fn print_stdio_fd(
282    &self,
283    out: &mut dyn Write,
284    fd: i32,
285    orig_fd: &FileDescriptorInfo,
286    curr_fd: Option<&FileDescriptorInfo>,
287    list_printer: &ListPrinter,
288  ) -> io::Result<()> {
289    let desc = match fd {
290      0 => "stdin",
291      1 => "stdout",
292      2 => "stderr",
293      _ => unreachable!(),
294    };
295    if let Some(fdinfo) = curr_fd {
296      if fdinfo.flags.contains(OFlag::O_CLOEXEC) {
297        if !self.args.hide_cloexec_fds {
298          write!(
299            out,
300            "{}{}",
301            "cloexec: ".bright_red().bold(),
302            desc.bright_red().bold()
303          )?;
304          list_printer.comma(out)?;
305        } else {
306          write!(
307            out,
308            "{}{}",
309            "closed: ".bright_red().bold(),
310            desc.bright_red().bold()
311          )?;
312          list_printer.comma(out)?;
313        }
314      } else if fdinfo.not_same_file_as(orig_fd) {
315        write!(out, "{}", desc.bright_yellow().bold())?;
316        write!(out, "={}", fdinfo.path.bright_yellow())?;
317        list_printer.comma(out)?;
318      }
319    } else {
320      write!(
321        out,
322        "{}{}",
323        "closed: ".bright_red().bold(),
324        desc.bright_red().bold()
325      )?;
326      list_printer.comma(out)?;
327    }
328    Ok(())
329  }
330
331  pub fn print_fd(
332    &self,
333    out: &mut dyn Write,
334    fds: &FileDescriptorInfoCollection,
335  ) -> io::Result<()> {
336    match self.args.trace_fd {
337      FdPrintFormat::Diff => {
338        write!(out, " {} ", "fd".purple())?;
339        let list_printer = ListPrinter::new(self.args.color);
340        list_printer.begin(out)?;
341        // Stdio
342        for fd in 0..=2 {
343          let fdinfo_orig = self.baseline.fdinfo.get(fd).unwrap();
344          self.print_stdio_fd(out, fd, fdinfo_orig, fds.fdinfo.get(&fd), &list_printer)?;
345        }
346        for (&fd, fdinfo) in fds.fdinfo.iter() {
347          if fd < 3 {
348            continue;
349          }
350          if fdinfo.flags.contains(OFlag::O_CLOEXEC) {
351            if !self.args.hide_cloexec_fds {
352              write!(
353                out,
354                "{} {}",
355                "cloexec:".bright_red().bold(),
356                fd.bright_green().bold()
357              )?;
358              write!(out, "={}", fdinfo.path.bright_red())?;
359              list_printer.comma(out)?;
360            }
361          } else {
362            write!(out, "{}", fd.bright_green().bold())?;
363            write!(out, "={}", fdinfo.path.bright_green())?;
364            list_printer.comma(out)?;
365          }
366        }
367        list_printer.end(out)?;
368      }
369      FdPrintFormat::Raw => {
370        write!(out, " {} ", "fd".purple())?;
371        let list_printer = ListPrinter::new(self.args.color);
372        list_printer.begin(out)?;
373        let last = fds.fdinfo.len() - 1;
374        for (idx, (fd, fdinfo)) in fds.fdinfo.iter().enumerate() {
375          if fdinfo.flags.contains(OFlag::O_CLOEXEC) {
376            if self.args.hide_cloexec_fds {
377              continue;
378            }
379            write!(out, "{}", fd.bright_red().bold())?;
380          } else {
381            write!(out, "{}", fd.bright_cyan().bold())?;
382          }
383          write!(out, "={}", fdinfo.path)?;
384          if idx != last {
385            list_printer.comma(out)?;
386          }
387        }
388        list_printer.end(out)?;
389      }
390      FdPrintFormat::None => {}
391    }
392    Ok(())
393  }
394
395  pub fn print_exec_trace(
396    &self,
397    pid: Pid,
398    comm: ArcStr,
399    result: i64,
400    exec_data: &ExecData,
401    env: &BTreeMap<OutputMsg, OutputMsg>,
402    cwd: &OutputMsg,
403  ) -> color_eyre::Result<()> {
404    // Preconditions:
405    // 1. execve syscall exit, which leads to 2
406    // 2. state.exec_data is Some
407
408    // Defer the warnings so that they are printed after the main message
409    #[allow(clippy::collection_is_never_read)]
410    let mut _deferred_warnings = vec![];
411
412    Self::OUT.with_borrow_mut(|out| {
413      let Some(out) = out else {
414        return Ok(());
415      };
416      let list_printer = ListPrinter::new(self.args.color);
417      if let Some(f) = self.args.inline_timestamp_format.as_deref() {
418        write!(out, "{} ", exec_data.timestamp.format(f).bright_cyan())?;
419      }
420      if result == 0 {
421        write!(out, "{}", pid.bright_green())?;
422      } else if result == -ENOENT as i64 {
423        write!(out, "{}", pid.bright_yellow())?;
424      } else {
425        write!(out, "{}", pid.bright_red())?;
426      }
427      if self.args.trace_comm {
428        write!(out, "<{}>", comm.cyan())?;
429      }
430      write!(out, ":")?;
431
432      if self.args.trace_filename {
433        write!(
434          out,
435          " {}",
436          exec_data.filename.cli_escaped_styled(THEME.filename)
437        )?;
438      }
439      if let OutputMsg::Err(e) = exec_data.filename {
440        _deferred_warnings.push(DeferredWarnings {
441          warning: DeferredWarningKind::FailedReadingFilename(e),
442          pid,
443        });
444      }
445
446      match exec_data.argv.as_ref() {
447        Err(e) => {
448          _deferred_warnings.push(DeferredWarnings {
449            warning: DeferredWarningKind::FailedReadingArgv(FriendlyError::InspectError(*e)),
450            pid,
451          });
452        }
453        Ok(argv) => {
454          if self.args.trace_argv {
455            write!(out, " ")?;
456            list_printer.print_string_list(out, argv)?;
457          }
458        }
459      }
460
461      // CWD
462
463      if self.args.trace_cwd {
464        write!(
465          out,
466          " {} {}",
467          "at".purple(),
468          exec_data
469            .cwd
470            .cli_escaped_styled(if self.args.color >= ColorLevel::Normal {
471              THEME.cwd
472            } else {
473              THEME.plain
474            })
475        )?;
476      }
477
478      // Interpreter
479
480      if self.args.trace_interpreter
481        && result == 0
482        && let Some(interpreters) = exec_data.interpreters.as_ref()
483      {
484        // FIXME: show interpreter for errnos other than ENOENT
485        write!(out, " {} ", "interpreter".purple(),)?;
486        match interpreters.len() {
487          0 => {
488            write!(out, "{}", Interpreter::None)?;
489          }
490          1 => {
491            write!(out, "{}", interpreters[0])?;
492          }
493          _ => {
494            list_printer.begin(out)?;
495            for (idx, interpreter) in interpreters.iter().enumerate() {
496              if idx != 0 {
497                list_printer.comma(out)?;
498              }
499              write!(out, "{interpreter}")?;
500            }
501            list_printer.end(out)?;
502          }
503        }
504      }
505
506      // File descriptors
507
508      self.print_fd(out, &exec_data.fdinfo)?;
509
510      // Environment
511
512      match exec_data.envp.as_ref() {
513        Ok(envp) => {
514          match self.args.trace_env {
515            EnvPrintFormat::Diff => {
516              write!(out, " {} ", "with".purple())?;
517              list_printer.begin(out)?;
518              let env = env.clone();
519              let mut first_item_written = false;
520              let mut write_separator = |out: &mut dyn Write| -> io::Result<()> {
521                if first_item_written {
522                  list_printer.comma(out)?;
523                } else {
524                  first_item_written = true;
525                }
526                Ok(())
527              };
528
529              let diff = diff_env(&env, envp);
530              for (k, v) in diff.added.into_iter() {
531                write_separator(out)?;
532                write!(
533                  out,
534                  "{}{}{}{}",
535                  "+".bright_green().bold(),
536                  k.cli_escaped_styled(THEME.added_env_var),
537                  "=".bright_green().bold(),
538                  v.cli_escaped_styled(THEME.added_env_var)
539                )?;
540              }
541              for (k, v) in diff.modified.into_iter() {
542                write_separator(out)?;
543                write!(
544                  out,
545                  "{}{}{}{}",
546                  "M".bright_yellow().bold(),
547                  k.cli_escaped_styled(THEME.modified_env_key),
548                  "=".bright_yellow().bold(),
549                  v.cli_escaped_styled(THEME.modified_env_val)
550                )?;
551              }
552              // Now we have the tracee removed entries in env
553              for k in diff.removed.into_iter() {
554                write_separator(out)?;
555                write!(
556                  out,
557                  "{}{}{}{}",
558                  "-".bright_red().bold(),
559                  k.cli_escaped_styled(THEME.removed_env_var),
560                  "=".bright_red().strikethrough(),
561                  env
562                    .get(&k)
563                    .unwrap()
564                    .cli_escaped_styled(THEME.removed_env_var)
565                )?;
566              }
567              list_printer.end(out)?;
568              // Avoid trailing color
569              // https://unix.stackexchange.com/questions/212933/background-color-whitespace-when-end-of-the-terminal-reached
570              if owo_colors::control::should_colorize() {
571                write!(out, "\x1B[49m\x1B[K")?;
572              }
573            }
574            EnvPrintFormat::Raw => {
575              write!(out, " {} ", "with".purple())?;
576              list_printer.print_env(out, envp)?;
577            }
578            EnvPrintFormat::None => (),
579          }
580        }
581        Err(e) => {
582          match self.args.trace_env {
583            EnvPrintFormat::Diff | EnvPrintFormat::Raw => {
584              write!(
585                out,
586                " {} {}",
587                "with".purple(),
588                format!("[Failed to read envp: {e}]")
589                  .bright_red()
590                  .blink()
591                  .bold()
592              )?;
593            }
594            EnvPrintFormat::None => {}
595          }
596          _deferred_warnings.push(DeferredWarnings {
597            warning: DeferredWarningKind::FailedReadingEnvp(FriendlyError::InspectError(*e)),
598            pid,
599          });
600        }
601      }
602
603      // Command line
604
605      if self.args.print_cmdline {
606        write!(out, " {}", "cmdline".purple())?;
607        write!(out, " env")?;
608
609        if self.args.stdio_in_cmdline {
610          let fdinfo_orig = self.baseline.fdinfo.stdin().unwrap();
611          if let Some(fdinfo) = exec_data.fdinfo.stdin() {
612            if fdinfo.flags.contains(OFlag::O_CLOEXEC) {
613              // stdin will be closed
614              write!(out, " {}", "0>&-".bright_red().bold().italic())?;
615            } else if fdinfo.not_same_file_as(fdinfo_orig) {
616              write!(
617                out,
618                " {}{}",
619                "<".bright_yellow().bold(),
620                fdinfo.path.cli_bash_escaped_with_style(THEME.modified_fd)
621              )?;
622            }
623          } else {
624            // stdin is closed
625            write!(out, " {}", "0>&-".bright_red().bold())?;
626          }
627          let fdinfo_orig = self.baseline.fdinfo.stdout().unwrap();
628          if let Some(fdinfo) = exec_data.fdinfo.stdout() {
629            if fdinfo.flags.contains(OFlag::O_CLOEXEC) {
630              // stdout will be closed
631              write!(out, " {}", "1>&-".bright_red().bold().italic())?;
632            } else if fdinfo.not_same_file_as(fdinfo_orig) {
633              write!(
634                out,
635                " {}{}",
636                ">".bright_yellow().bold(),
637                fdinfo.path.cli_bash_escaped_with_style(THEME.modified_fd)
638              )?;
639            }
640          } else {
641            // stdout is closed
642            write!(out, " {}", "1>&-".bright_red().bold())?;
643          }
644          let fdinfo_orig = self.baseline.fdinfo.stderr().unwrap();
645          if let Some(fdinfo) = exec_data.fdinfo.stderr() {
646            if fdinfo.flags.contains(OFlag::O_CLOEXEC) {
647              // stderr will be closed
648              write!(out, " {}", "2>&-".bright_red().bold().italic())?;
649            } else if fdinfo.not_same_file_as(fdinfo_orig) {
650              write!(
651                out,
652                " {}{}",
653                "2>".bright_yellow().bold(),
654                fdinfo.path.cli_bash_escaped_with_style(THEME.modified_fd)
655              )?;
656            }
657          } else {
658            // stderr is closed
659            write!(out, " {}", "2>&-".bright_red().bold())?;
660          }
661        }
662
663        if self.args.fd_in_cmdline {
664          for (&fd, fdinfo) in exec_data.fdinfo.fdinfo.iter() {
665            if fd < 3 {
666              continue;
667            }
668            if fdinfo.flags.contains(OFlag::O_CLOEXEC) {
669              // Don't show fds that will be closed upon exec
670              continue;
671            }
672            write!(
673              out,
674              " {}{}{}",
675              fd.bright_green().bold(),
676              "<>".bright_green().bold(),
677              fdinfo.path.cli_bash_escaped_with_style(THEME.added_fd)
678            )?;
679          }
680        }
681
682        match exec_data.argv.as_ref() {
683          Ok(argv) => {
684            if let Some(arg0) = argv.first() {
685              // filename warning is already handled
686              if &exec_data.filename != arg0 {
687                write!(
688                  out,
689                  " {} {}",
690                  "-a".bright_white().italic(),
691                  escape_str_for_bash!(arg0.as_ref()).bright_white().italic()
692                )?;
693              }
694            } else {
695              _deferred_warnings.push(DeferredWarnings {
696                warning: DeferredWarningKind::NoArgv0,
697                pid,
698              });
699            }
700            if cwd != &exec_data.cwd {
701              if self.args.color >= ColorLevel::Normal {
702                write!(
703                  out,
704                  " -C {}",
705                  &exec_data.cwd.cli_bash_escaped_with_style(THEME.cwd)
706                )?;
707              } else {
708                write!(out, " -C {}", exec_data.cwd.bash_escaped())?;
709              }
710            }
711            // envp warning is already handled
712            if let Ok(envp) = exec_data.envp.as_ref() {
713              let diff = diff_env(env, envp);
714              let need_env_argument_separator = diff.need_env_argument_separator();
715              // Now we have the tracee removed entries in env
716              for k in diff.removed.into_iter() {
717                if self.args.color >= ColorLevel::Normal {
718                  write!(
719                    out,
720                    " {}{}",
721                    "-u ".bright_red(),
722                    k.cli_bash_escaped_with_style(THEME.removed_env_key)
723                  )?;
724                } else {
725                  write!(out, " -u {}", k.bash_escaped())?;
726                }
727              }
728              if need_env_argument_separator {
729                write!(out, " --")?;
730              }
731              if self.args.color >= ColorLevel::Normal {
732                for (k, v) in diff.added.into_iter() {
733                  write!(
734                    out,
735                    " {}{}{}",
736                    k.cli_bash_escaped_with_style(THEME.added_env_var),
737                    "=".green().bold(),
738                    v.cli_bash_escaped_with_style(THEME.added_env_var)
739                  )?;
740                }
741                for (k, v) in diff.modified.into_iter() {
742                  write!(
743                    out,
744                    " {}{}{}",
745                    k.bash_escaped(),
746                    "=".bright_yellow().bold(),
747                    v.cli_bash_escaped_with_style(THEME.modified_env_val)
748                  )?;
749                }
750              } else {
751                for (k, v) in chain!(diff.added.into_iter(), diff.modified.into_iter()) {
752                  write!(out, " {}={}", k.bash_escaped(), v.bash_escaped())?;
753                }
754              }
755            }
756            write!(out, " {}", exec_data.filename.bash_escaped())?;
757            for arg in argv.iter().skip(1) {
758              write!(out, " {}", arg.bash_escaped())?;
759            }
760          }
761          Err(e) => {
762            _deferred_warnings.push(DeferredWarnings {
763              warning: DeferredWarningKind::FailedReadingArgv(FriendlyError::InspectError(*e)),
764              pid,
765            });
766          }
767        }
768      }
769
770      // Result
771
772      if result == 0 {
773        writeln!(out)?;
774      } else {
775        write!(out, " {} ", "=".purple())?;
776        if self.args.decode_errno {
777          writeln!(
778            out,
779            "{} ({})",
780            result.bright_red().bold(),
781            nix::errno::Errno::from_raw(-result as i32).red()
782          )?;
783        } else {
784          writeln!(out, "{}", result.bright_red().bold())?;
785        }
786      }
787      // It is critical to call [flush] before BufWriter<W> is dropped.
788      // Though dropping will attempt to flush the contents of the buffer, any errors that happen in the process of dropping will be ignored.
789      // Calling [flush] ensures that the buffer is empty and thus dropping will not even attempt file operations.
790      out.flush()?;
791      Ok(())
792    })
793  }
794}