1 use anyhow
::{anyhow, bail, Error, 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
::{
19 utils
::{parse_answer, LowLevelMessage}
,
22 static LOGGER
: AutoInstLogger
= AutoInstLogger
;
24 pub fn init_log() -> Result
<()> {
25 AutoInstLogger
::init("/tmp/auto_installer.log")?
;
26 log
::set_logger(&LOGGER
)
27 .map(|()| log
::set_max_level(LevelFilter
::Info
))
28 .map_err(|err
| anyhow
!(err
))
31 fn auto_installer_setup(in_test_mode
: bool
) -> Result
<(Answer
, UdevInfo
)> {
32 let base_path
= if in_test_mode { "./testdir" }
else { "/" }
;
33 let mut path
= PathBuf
::from(base_path
);
36 path
.push("proxmox-installer");
38 let udev_info
: UdevInfo
= {
39 let mut path
= path
.clone();
40 path
.push("run-env-udev.json");
42 read_json(&path
).map_err(|err
| anyhow
!("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
| anyhow
!("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 utils
::run_cmds("Pre", &answer
.global
.pre_commands
) {
89 error
!("Error when running Pre-Commands: {}", err
);
90 return exit_failure(answer
.global
.reboot_on_error
);
93 match run_installation(&answer
, &locales
, &runtime_info
, &udevadm_info
, &setup_info
) {
94 Ok(_
) => info
!("Installation done."),
96 error
!("Installation failed: {err}");
97 return exit_failure(answer
.global
.reboot_on_error
);
100 match utils
::run_cmds("Post", &answer
.global
.post_commands
) {
103 error
!("Error when running Post-Commands: {}", err
);
104 return exit_failure(answer
.global
.reboot_on_error
);
110 /// When we exit with a failure, the installer will not automatically reboot.
111 /// Default value for reboot_on_error is false
112 fn exit_failure(reboot_on_error
: bool
) -> ExitCode
{
122 locales
: &LocaleInfo
,
123 runtime_info
: &RuntimeInfo
,
124 udevadm_info
: &UdevInfo
,
125 setup_info
: &SetupInfo
,
127 let config
= parse_answer(answer
, udevadm_info
, runtime_info
, locales
, setup_info
)?
;
128 info
!("Calling low-level installer");
130 let mut child
= match spawn_low_level_installer(false) {
133 bail
!("Low level installer could not be started: {}", err
);
137 let mut cur_counter
= 111;
138 let mut inner
= || -> Result
<()> {
143 .ok_or(anyhow
!("failed to get stdout reader"))?
;
144 let mut writer
= child
147 .ok_or(anyhow
!("failed to get stdin writer"))?
;
149 serde_json
::to_writer(&mut writer
, &config
)
150 .map_err(|err
| anyhow
!("failed to serialize install config: {err}"))?
;
151 writeln
!(writer
).map_err(|err
| anyhow
!("failed to write install config: {err}"))?
;
153 for line
in reader
.lines() {
154 let line
= match line
{
158 let msg
= match serde_json
::from_str
::<LowLevelMessage
>(&line
) {
161 // Not a fatal error, so don't abort the installation by returning
167 LowLevelMessage
::Info { message }
=> info
!("{message}"),
168 LowLevelMessage
::Error { message }
=> error
!("{message}"),
169 LowLevelMessage
::Prompt { query }
=> {
170 bail
!("Got interactive prompt I cannot answer: {query}")
172 LowLevelMessage
::Progress { ratio, text: _ }
=> {
173 let counter
= (ratio
* 100.).floor() as usize;
174 if counter
!= cur_counter
{
175 cur_counter
= counter
;
176 info
!("Progress: {counter:>3}%");
179 LowLevelMessage
::Finished { state, message }
=> {
183 info
!("Finished: '{state}' {message}");
190 Err(err
) => Err(Error
::msg(format
!(
191 "low level installer returned early: {err}"