1 use anyhow
::{bail, format_err, Result}
;
2 use log
::{error, info, LevelFilter}
;
5 io
::{BufRead, BufReader, Write}
,
10 use proxmox_installer_common
::setup
::{
11 installer_setup
, read_json
, spawn_low_level_installer
, LocaleInfo
, RuntimeInfo
, SetupInfo
,
14 use proxmox_auto_installer
::{
18 utils
::{parse_answer, LowLevelMessage}
,
21 static LOGGER
: AutoInstLogger
= AutoInstLogger
;
23 pub fn init_log() -> Result
<()> {
24 AutoInstLogger
::init("/tmp/auto_installer.log")?
;
25 log
::set_logger(&LOGGER
)
26 .map(|()| log
::set_max_level(LevelFilter
::Info
))
27 .map_err(|err
| format_err
!(err
))
30 fn auto_installer_setup(in_test_mode
: bool
) -> Result
<(Answer
, UdevInfo
)> {
31 let base_path
= if in_test_mode { "./testdir" }
else { "/" }
;
32 let mut path
= PathBuf
::from(base_path
);
35 path
.push("proxmox-installer");
37 let udev_info
: UdevInfo
= {
38 let mut path
= path
.clone();
39 path
.push("run-env-udev.json");
42 .map_err(|err
| format_err
!("Failed to retrieve udev info details: {err}"))?
45 let mut buffer
= String
::new();
46 let lines
= std
::io
::stdin().lock().lines();
48 buffer
.push_str(&line
.unwrap());
53 toml
::from_str(&buffer
).map_err(|err
| format_err
!("Failed parsing answer file: {err}"))?
;
55 Ok((answer
, udev_info
))
58 fn main() -> ExitCode
{
59 if let Err(err
) = init_log() {
60 panic
!("could not initilize logging: {}", err
);
63 let in_test_mode
= match env
::args().nth(1).as_deref() {
65 // Always force the test directory in debug builds
66 _
=> cfg
!(debug_assertions
),
68 info
!("Starting auto installer");
70 let (setup_info
, locales
, runtime_info
) = match installer_setup(in_test_mode
) {
73 error
!("Installer setup error: {err}");
74 return ExitCode
::FAILURE
;
78 let (answer
, udevadm_info
) = match auto_installer_setup(in_test_mode
) {
81 error
!("Autoinstaller setup error: {err}");
82 return ExitCode
::FAILURE
;
86 match run_installation(&answer
, &locales
, &runtime_info
, &udevadm_info
, &setup_info
) {
87 Ok(_
) => info
!("Installation done."),
89 error
!("Installation failed: {err}");
90 return exit_failure(answer
.global
.reboot_on_error
);
94 // TODO: (optionally) do a HTTP post with basic system info, like host SSH public key(s) here
99 /// When we exit with a failure, the installer will not automatically reboot.
100 /// Default value for reboot_on_error is false
101 fn exit_failure(reboot_on_error
: bool
) -> ExitCode
{
111 locales
: &LocaleInfo
,
112 runtime_info
: &RuntimeInfo
,
113 udevadm_info
: &UdevInfo
,
114 setup_info
: &SetupInfo
,
116 let config
= parse_answer(answer
, udevadm_info
, runtime_info
, locales
, setup_info
)?
;
117 info
!("Calling low-level installer");
119 let mut child
= match spawn_low_level_installer(false) {
122 bail
!("Low level installer could not be started: {}", err
);
126 let mut inner
= || -> Result
<()> {
131 .ok_or(format_err
!("failed to get stdout reader"))?
;
132 let mut writer
= child
135 .ok_or(format_err
!("failed to get stdin writer"))?
;
137 serde_json
::to_writer(&mut writer
, &config
)
138 .map_err(|err
| format_err
!("failed to serialize install config: {err}"))?
;
139 writeln
!(writer
).map_err(|err
| format_err
!("failed to write install config: {err}"))?
;
141 for line
in reader
.lines() {
142 let line
= match line
{
146 let msg
= match serde_json
::from_str
::<LowLevelMessage
>(&line
) {
149 // Not a fatal error, so don't abort the installation by returning
155 LowLevelMessage
::Info { message }
=> info
!("{message}"),
156 LowLevelMessage
::Error { message }
=> error
!("{message}"),
157 LowLevelMessage
::Prompt { query }
=> {
158 bail
!("Got interactive prompt I cannot answer: {query}")
160 LowLevelMessage
::Progress { ratio, text }
=> {
161 let percentage
= ratio
* 100.;
162 info
!("progress {percentage:>5.1} % - {text}");
164 LowLevelMessage
::Finished { state, message }
=> {
168 info
!("Finished: '{state}' {message}");
174 inner().map_err(|err
| format_err
!("low level installer returned early: {err}"))