1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! Contains shared code between the broker & its children, specifically any IPC messages or common
//! bootstrapping code.

use std::fs::File;
use std::fs::OpenOptions;
use std::path::PathBuf;

use anyhow::Context;
use base::enable_high_res_timers;
use base::syslog;
use base::syslog::LogArgs;
use base::EnabledHighResTimer;
use base::FromRawDescriptor;
use base::IntoRawDescriptor;
use base::SafeDescriptor;
use base::SendTube;
#[cfg(feature = "process-invariants")]
pub use broker_ipc_product::init_broker_process_invariants;
use broker_ipc_product::init_child_crash_reporting;
use broker_ipc_product::product_child_setup;
#[cfg(feature = "process-invariants")]
pub use broker_ipc_product::EmulatorProcessInvariants;
use broker_ipc_product::ProductAttributes;
use serde::Deserialize;
use serde::Serialize;

/// Arguments that are common to all devices & helper processes.
#[derive(Serialize, Deserialize)]
pub struct CommonChildStartupArgs {
    log_args: LogArgs,
    syslog_file: Option<SafeDescriptor>,
    metrics_tube: Option<SendTube>,
    product_attrs: ProductAttributes,
}

impl CommonChildStartupArgs {
    #[allow(clippy::new_without_default)]
    pub fn new(
        log_args: &LogArgs,
        syslog_path: Option<PathBuf>,
        #[cfg(feature = "crash-report")] _crash_attrs: crash_report::CrashReportAttributes,
        #[cfg(feature = "process-invariants")] _process_invariants: EmulatorProcessInvariants,
        metrics_tube: Option<SendTube>,
    ) -> anyhow::Result<Self> {
        Ok(Self {
            log_args: log_args.clone(),
            product_attrs: ProductAttributes {},
            metrics_tube,
            syslog_file: log_file_from_path(syslog_path)?,
        })
    }
}

pub struct ChildLifecycleCleanup {
    _timer_resolution: Box<dyn EnabledHighResTimer>,
}

/// Initializes crash reporting, metrics, logging, and product specific features
/// for a process.
///
/// Returns a value that should be dropped when the process exits.
pub fn common_child_setup(args: CommonChildStartupArgs) -> anyhow::Result<ChildLifecycleCleanup> {
    // Logging must initialize first in case there are other startup errors.
    let mut cfg = syslog::LogConfig {
        log_args: args.log_args,
        ..Default::default()
    };
    if let Some(log_file_descriptor) = args.syslog_file {
        let log_file =
            // SAFETY:
            // Safe because we are taking ownership of a SafeDescriptor.
            unsafe { File::from_raw_descriptor(log_file_descriptor.into_raw_descriptor()) };
        cfg.pipe = Some(Box::new(log_file));
        cfg.log_args.stderr = false;
    } else {
        cfg.log_args.stderr = true;
    }
    syslog::init_with(cfg)?;

    // Crash reporting should start as early as possible, in case other startup tasks fail.
    init_child_crash_reporting(&args.product_attrs);

    // Initialize anything product specific.
    product_child_setup(&args.product_attrs)?;

    if let Some(metrics_tube) = args.metrics_tube {
        metrics::initialize(metrics_tube);
    }

    let timer_resolution = enable_high_res_timers().context("failed to enable high res timer")?;

    Ok(ChildLifecycleCleanup {
        _timer_resolution: timer_resolution,
    })
}

pub(crate) fn log_file_from_path(path: Option<PathBuf>) -> anyhow::Result<Option<SafeDescriptor>> {
    Ok(match path {
        Some(path) => Some(SafeDescriptor::from(
            OpenOptions::new()
                .append(true)
                .create(true)
                .open(path.as_path())
                .context(format!("failed to open log file {}", path.display()))?,
        )),
        None => None,
    })
}