1 |
#!/usr/bin/perl |
2 |
|
3 |
# Must be done as early as possible to avoid issues when displaying translated |
4 |
# strings |
5 |
BEGIN { |
6 |
push @::textdomains, 'draklive-install'; |
7 |
} |
8 |
|
9 |
use lib qw(/usr/lib/libDrakX); |
10 |
use standalone; |
11 |
use interactive; |
12 |
use fs; |
13 |
use fs::any; |
14 |
use fs::type; |
15 |
use fs::partitioning; |
16 |
use fs::partitioning_wizard; |
17 |
use partition_table; |
18 |
use MDK::Common; |
19 |
use common; |
20 |
use feature qw(state); |
21 |
|
22 |
($::real_windowwidth, $::real_windowheight) = (600, 400); |
23 |
|
24 |
{ |
25 |
use diskdrake::interactive; |
26 |
package diskdrake::interactive; |
27 |
my $old = \&hd_possible_actions_base; |
28 |
undef *hd_possible_actions_base; |
29 |
*hd_possible_actions_base = sub { |
30 |
#- for the partition wizard to show the auto-allocate option |
31 |
local $::isInstall = 1; |
32 |
&$old; |
33 |
}; |
34 |
undef *Done; |
35 |
#- skip the fstab/reboot checks |
36 |
*Done = \&diskdrake_interactive_Done; |
37 |
#- don't ask whether to Move/Hide old files |
38 |
undef *need_migration; |
39 |
*need_migration = sub { 'hide' }; |
40 |
} |
41 |
|
42 |
install_live(); |
43 |
|
44 |
sub install_live() { |
45 |
my $in = 'interactive'->vnew('su'); |
46 |
$in->{pop_wait_messages} = 0; |
47 |
|
48 |
$::isWizard = 1; |
49 |
$::Wizard_no_previous = 1; |
50 |
$::Wizard_pix_up = "draklive-install"; |
51 |
any::set_wm_hints_if_needed($in); |
52 |
|
53 |
my $all_hds = {}; |
54 |
my $fstab = []; |
55 |
$::prefix = '/mnt/install'; |
56 |
|
57 |
my $system_file = '/etc/sysconfig/draklive-install'; |
58 |
my %settings = getVarsFromSh($system_file); |
59 |
|
60 |
my $copy_source = $settings{SOURCE} || '/'; |
61 |
my $live_media = '/live/media'; |
62 |
|
63 |
display_start_message(); |
64 |
init_hds($in, $all_hds, $fstab, $live_media); |
65 |
ask_partitions_loop($in, $all_hds, $fstab, $copy_source); |
66 |
remove_unused_packages($in, $copy_source); |
67 |
prepare_root($in, $all_hds); |
68 |
copy_root($in, $copy_source); |
69 |
complete_install($in, $all_hds); |
70 |
setup_bootloader($in, $all_hds, $fstab); |
71 |
finish_installation($fstab); |
72 |
display_end_message($in); |
73 |
$in->exit(0); |
74 |
} |
75 |
|
76 |
sub umount_all { |
77 |
my ($fstab) = @_; |
78 |
#- make sure nothing is mounted in the new root |
79 |
foreach (sort { $b cmp $a } grep { /^$::prefix/ } map { (split)[1] } cat_('/proc/mounts')) { |
80 |
system('umount', $_); |
81 |
} |
82 |
#- make sure selected devices aren't mounted, and swap isn't used |
83 |
foreach (grep { isSwap($_) } @$fstab) { |
84 |
eval { fs::mount::swapoff($_->{device}) }; |
85 |
} |
86 |
foreach (map { $_->{mntpoint} && !isSwap($_) ? "/dev/$_->{device}" : () } @$fstab) { |
87 |
system('umount', $_); |
88 |
} |
89 |
} |
90 |
|
91 |
sub on_reboot_needed { |
92 |
my ($in) = @_; |
93 |
fs::partitioning_wizard::warn_reboot_needed($in); |
94 |
$in->exit(0); |
95 |
} |
96 |
|
97 |
sub display_start_message() { |
98 |
require any; |
99 |
my $has_running_wm = to_bool(any::running_window_manager()); |
100 |
local $::isStandalone = $has_running_wm; # center me if run in xsetup.d script |
101 |
my $w = ugtk2->new(N("Mageia Live")); |
102 |
ugtk2::gtkadd($w->{window}, |
103 |
ugtk2::gtkcreate_img("MageiaLive-install"), |
104 |
ugtk2::gtknew('Label', height => 5), |
105 |
N("This wizard will help you to install the live distribution."), |
106 |
ugtk2::create_okcancel($w)); |
107 |
$w->{ok}->grab_focus; |
108 |
$w->main; |
109 |
} |
110 |
|
111 |
sub umount_first_pass() { |
112 |
local $::prefix = undef; |
113 |
my $all_hds = fsedit::get_hds(); |
114 |
fs::get_raw_hds('', $all_hds); |
115 |
fs::get_info_from_fstab($all_hds); |
116 |
my $fstab = [ fs::get::fstab($all_hds) ]; |
117 |
fs::merge_info_from_mtab($fstab); |
118 |
|
119 |
#- inlined from fs::mount::umount_all to go on when one umount fail |
120 |
#- (maybe the sort function could be shared) |
121 |
log::l("unmounting all filesystems"); |
122 |
foreach (sort { $b->{mntpoint} cmp $a->{mntpoint} } |
123 |
grep { $_->{mntpoint} && !$_->{real_mntpoint} } @$fstab) { |
124 |
eval { fs::mount::umount_part($_) }; |
125 |
log::l("error unmounting $_->{mntpoint}: $@") if $@; |
126 |
} |
127 |
} |
128 |
|
129 |
sub init_hds { |
130 |
my ($in, $all_hds, $fstab, $live_media) = @_; |
131 |
my $wait = $in->wait_message('', N("Please wait")); |
132 |
umount_first_pass(); |
133 |
eval { fs::any::get_hds($all_hds, $fstab, [], {}, 'skip_mtab', $in) }; |
134 |
|
135 |
#- fs::any::get_hds does not return mounts that are not in fstab |
136 |
my @mounted = fs::read_fstab('', '/proc/mounts'); |
137 |
my $live_part = find { $_->{mntpoint} eq $live_media } @mounted; |
138 |
my $live_device = $live_part && $live_part->{device}; |
139 |
#- remove live device from the detected hds, so that bootloader is not installed on it: |
140 |
#- bootloader installation uses first device from detect_devices::get, which tries to list devices |
141 |
#- by booting order, and our live system is likely to be here in first position |
142 |
#- it can be either a partition (USB) or the full disk (Hybrid on USB) |
143 |
@{$all_hds->{hds}} = grep { |
144 |
$_->{device} ne $live_device && |
145 |
!member($live_device, map { $_->{device} } partition_table::get_normal_parts_and_holes($_)); |
146 |
} @{$all_hds->{hds}} if $live_device; |
147 |
|
148 |
my $err = $@; |
149 |
umount_all($fstab); |
150 |
if ($err) { |
151 |
undef $wait; |
152 |
$in->ask_warn(N("Error"), [ formatError($err) ]); |
153 |
$in->exit(1); |
154 |
} |
155 |
} |
156 |
|
157 |
sub ask_partitions_loop { |
158 |
my ($in, $all_hds, $fstab, $copy_source) = @_; |
159 |
|
160 |
while (1) { |
161 |
eval { ask_partitions($in, $all_hds, $fstab, $copy_source) }; |
162 |
my $err = $@ or last; |
163 |
$in->exit(1) if $err =~ /wizcancel/ || |
164 |
!$in->ask_warn(N("Error"), [ N("An error occurred"), formatError($err) ]); |
165 |
} |
166 |
} |
167 |
|
168 |
sub ask_partitions { |
169 |
my ($in, $all_hds, $fstab, $copy_source) = @_; |
170 |
fs::partitioning_wizard::main($in, $all_hds, $fstab, [], undef, {}, 'skip_mtab'); |
171 |
|
172 |
mkdir_p($::prefix) or die "unable to create $::prefix"; |
173 |
|
174 |
fs::any::write_hds($all_hds, $fstab, undef, sub { on_reboot_needed($in) }, {}); |
175 |
fs::any::check_hds_boot_and_root($all_hds, $fstab); |
176 |
fs::partitioning::choose_partitions_to_format($in, $fstab); |
177 |
|
178 |
my $total = get_total_size($in, $copy_source); |
179 |
my $available = fs::any::getAvailableSpace($fstab, 'skip_mounted'); |
180 |
die N("Not enough space available (%s available while %s are needed)", |
181 |
formatXiB($available), formatXiB($total)) . "\n" |
182 |
if $total > $available; |
183 |
|
184 |
umount_all($fstab); |
185 |
fs::partitioning::format_mount_partitions($in, $all_hds, $fstab); |
186 |
} |
187 |
|
188 |
sub remove_unused_packages { |
189 |
my ($in, $o_prefix) = @_; |
190 |
require pkgs; |
191 |
#in remove_unused_packages, we want to get the locale from the currently |
192 |
#running system, but we want to remove unused packages from the |
193 |
#system based in $o_prefix, that's why we use an extra arg instead of |
194 |
#directly using $::prefix |
195 |
local $::prefix; |
196 |
pkgs::remove_unused_packages($in, $in->do_pkgs, $o_prefix); |
197 |
} |
198 |
|
199 |
sub prepare_root { |
200 |
my ($in, $all_hds) = @_; |
201 |
#- create required directories and devices (early to have a consistent root before calling other programs) |
202 |
my $_wait = $in->wait_message('', N("Please wait")); |
203 |
fs::any::prepare_minimal_root(); |
204 |
} |
205 |
|
206 |
sub build_copy_command { |
207 |
my ($source, $dest) = @_; |
208 |
join(' ', |
209 |
'tar', 'c', '--one-file-system', '-C', $source, '.', |
210 |
'|', |
211 |
'tar', 'xvv', '-C', $dest, |
212 |
); |
213 |
} |
214 |
|
215 |
sub get_total_size { |
216 |
my ($in, $source) = @_; |
217 |
state %total; |
218 |
return $total{$source} if $total{$source}; |
219 |
my $_wait = $in->wait_message('', N("Computing total size")); |
220 |
$total{$source} = first(split(/\s+/, `du -sbx $source 2>/dev/null`)); |
221 |
} |
222 |
|
223 |
sub sync_logs() { |
224 |
cp_af('/var/log', $::prefix . '/var'); |
225 |
} |
226 |
|
227 |
sub copy_root { |
228 |
my ($in, $copy_source) = @_; |
229 |
my $total = get_total_size($in, $copy_source); |
230 |
|
231 |
my ($wait, $update_progress) = copying_message_with_progress_bar($in, N("Copying in progress")); |
232 |
open(my $OUTPUT, '-|', build_copy_command($copy_source, $::prefix)); |
233 |
{ |
234 |
local $_; |
235 |
my $current = my $previous = 0; |
236 |
while (<$OUTPUT>) { |
237 |
(undef, undef, my $size) = split; |
238 |
$current += $size; |
239 |
if ($current <= $total && $current/$total > $previous/$total + 0.001) { |
240 |
$update_progress->('', $current, $total); |
241 |
$previous = $current; |
242 |
} |
243 |
} |
244 |
} |
245 |
if (!close($OUTPUT)) { |
246 |
undef $wait; |
247 |
undef $update_progress; |
248 |
$in->ask_warn(N("Error"), N("Unable to copy files to new root")); |
249 |
$in->exit(1); |
250 |
} |
251 |
sync_logs(); |
252 |
} |
253 |
|
254 |
sub clean_harddrake_hds { |
255 |
my ($prefix) = @_; |
256 |
#- remove harddisks from harddrake's config file, so that hardddisks |
257 |
#- are automatically rediscovered at first boot |
258 |
require Storable; |
259 |
my $harddrake_file = $prefix . "/etc/sysconfig/harddrake2/previous_hw"; |
260 |
my $harddrake_conf = eval { Storable::retrieve($harddrake_file) }; |
261 |
if ($harddrake_conf) { |
262 |
delete $harddrake_conf->{HARDDISK}; |
263 |
Storable::store($harddrake_conf, $harddrake_file); |
264 |
} |
265 |
} |
266 |
|
267 |
|
268 |
sub complete_install { |
269 |
my ($in, $all_hds) = @_; |
270 |
my $_wait = $in->wait_message('', N("Please wait")); |
271 |
|
272 |
my $real_rpm_dir = "/tmp/rpm/real"; |
273 |
cp_f(glob($real_rpm_dir . "/*"), $::prefix . "/var/lib/rpm") if -d $real_rpm_dir; |
274 |
|
275 |
#- FIXME: maybe factorize with draklive, using draklive --clean-chroot ? |
276 |
#- remove unwanted files and packages |
277 |
my $live_user = chomp_(cat_('/etc/draklive-install.d/user')); |
278 |
my $live_user_desktop = $live_user && chomp_(run_program::rooted_get_stdout($::prefix, "su - $live_user -c 'xdg-user-dir DESKTOP'")); |
279 |
unlink(map { $::prefix . $_ } '/.autofsck', |
280 |
chomp_(cat_(glob('/etc/draklive-install.d/remove.d/*'))), |
281 |
if_($live_user_desktop, |
282 |
$live_user_desktop . '/draklive-copy-wizard.desktop', |
283 |
$live_user_desktop . '/draklive-install.desktop'), |
284 |
); |
285 |
{ |
286 |
#- do not allow update-menus to create home directory with invalid perms |
287 |
local $ENV{HOME} = '/root'; |
288 |
system('chroot', $::prefix, 'rpm', '-e', 'draklive-install'); |
289 |
} |
290 |
|
291 |
foreach (glob('/etc/draklive-install.d/run.d/*')) { |
292 |
run_program::rooted($::prefix, $_); |
293 |
} |
294 |
|
295 |
#- copy sysconfig files for first boot |
296 |
cp_f(glob('/etc/draklive-install.d/sysconfig/*'), $::prefix . '/etc/sysconfig'); |
297 |
|
298 |
#- unselect live user in kdm |
299 |
my $kdm_cfg = common::read_alternative('kdm4-config'); |
300 |
update_gnomekderc($::prefix . $kdm_cfg, |
301 |
'X-:0-Greeter' => (PreselectUser => 'None', DefaultUser => '')) if -f $kdm_cfg; |
302 |
my $autologin = any::get_autologin(); |
303 |
delete $autologin->{user}; |
304 |
any::set_autologin($in->do_pkgs, $autologin); |
305 |
|
306 |
#- allow to install doc in disk install |
307 |
substInFile { undef $_ if /^\%_excludedocs/ } $::prefix . '/etc/rpm/macros'; |
308 |
|
309 |
fs::write_fstab($all_hds, $::prefix); |
310 |
|
311 |
clean_harddrake_hds($::prefix); |
312 |
|
313 |
# enable back some disabled services |
314 |
require services; |
315 |
services::start_service_on_boot($_) foreach chomp_(cat_('/etc/draklive-install.d/services')); |
316 |
|
317 |
sync_logs(); |
318 |
} |
319 |
|
320 |
sub setup_bootloader { |
321 |
my ($in, $all_hds, $fstab) = @_; |
322 |
use bootloader; |
323 |
my $bootloader = {}; |
324 |
any::setupBootloaderBeforeStandalone($in->do_pkgs, $bootloader, $all_hds, $fstab); |
325 |
local $::Wizard_no_previous = 0; |
326 |
any::setupBootloaderUntilInstalled($in, $bootloader, $all_hds, $fstab, $ENV{SECURE_LEVEL}); |
327 |
sync_logs(); |
328 |
} |
329 |
|
330 |
sub clean_live_system_hds() { |
331 |
#- clean fstab and harddrake config in the live system |
332 |
#- since partitions UUIDs of the installed system have been modified |
333 |
#- (useful for persistent live systems) |
334 |
local $::prefix = undef; |
335 |
clean_harddrake_hds($::prefix); |
336 |
my $all_hds = fs::get::empty_all_hds(); #- skip real harddisks |
337 |
fs::get_raw_hds('', $all_hds); |
338 |
fs::get_info_from_fstab($all_hds); |
339 |
fs::write_fstab($all_hds, $::prefix); |
340 |
} |
341 |
|
342 |
sub finish_installation { |
343 |
my ($fstab) = @_; |
344 |
sync_logs(); |
345 |
#- cleanly umount here, it will avoid fs journals to be corrupted after a hackish reboot |
346 |
umount_all($fstab); |
347 |
clean_live_system_hds(); |
348 |
} |
349 |
|
350 |
sub display_end_message { |
351 |
my ($in) = @_; |
352 |
$::Wizard_finished = 1; |
353 |
$in->ask_okcancel(N("Congratulations"), N("Please halt your computer, remove your live system, and restart your computer.")); |
354 |
} |
355 |
|
356 |
### |
357 |
### duplicate code |
358 |
### |
359 |
|
360 |
#- from disdrake::interactive |
361 |
{ |
362 |
package diskdrake::interactive; |
363 |
sub diskdrake_interactive_Done { |
364 |
my ($in, $all_hds) = @_; |
365 |
eval { raid::verify($all_hds->{raids}) }; |
366 |
if (my $err = $@) { |
367 |
$::expert or die; |
368 |
$in->ask_okcancel('', [ formatError($err), N("Continue anyway?") ]) or return; |
369 |
} |
370 |
foreach (@{$all_hds->{hds}}) { |
371 |
if (!write_partitions($in, $_, 'skip_check_rebootNeeded')) { |
372 |
return if !$::isStandalone; |
373 |
$in->ask_yesorno(N("Quit without saving"), N("Quit without writing the partition table?"), 1) or return; |
374 |
} |
375 |
} |
376 |
#- skip that fstab/reboot steps |
377 |
if (!$::isInstall && 0) { |
378 |
my $new = fs::fstab_to_string($all_hds); |
379 |
if ($new ne $all_hds->{current_fstab} && $in->ask_yesorno('', N("Do you want to save /etc/fstab modifications"), 1)) { |
380 |
$all_hds->{current_fstab} = $new; |
381 |
fs::write_fstab($all_hds); |
382 |
} |
383 |
update_bootloader_for_renumbered_partitions($in, $all_hds); |
384 |
|
385 |
if (any { $_->{rebootNeeded} } @{$all_hds->{hds}}) { |
386 |
$in->ask_warn('', N("You need to reboot for the partition table modifications to take place")); |
387 |
tell_wm_and_reboot(); |
388 |
} |
389 |
} |
390 |
if (my $part = find { $_->{mntpoint} && !maybeFormatted($_) } fs::get::fstab($all_hds)) { |
391 |
$in->ask_okcancel('', N("You should format partition %s. |
392 |
Otherwise no entry for mount point %s will be written in fstab. |
393 |
Quit anyway?", $part->{device}, $part->{mntpoint})) or return if $::isStandalone && 0; #- no, please |
394 |
} |
395 |
1; |
396 |
} |
397 |
} |
398 |
|
399 |
# forked from interactive::wait_message |
400 |
sub copying_message { |
401 |
my ($o, $title, $message, $b_temp) = @_; |
402 |
|
403 |
my $w = $o->wait_messageW($title, N("Copying in progress"), ugtk2::gtknew('VBox', padding => 5, children_tight => [ |
404 |
ugtk2::gtkcreate_img("MageiaLive-advert"), |
405 |
$message, |
406 |
])); |
407 |
push @tempory::objects, $w if $b_temp; |
408 |
my $b = before_leaving { $o->wait_message_endW($w) }; |
409 |
|
410 |
#- enable access through set |
411 |
MDK::Common::Func::add_f4before_leaving(sub { $o->wait_message_nextW($_[1], $w) }, $b, 'set'); |
412 |
$b; |
413 |
} |
414 |
|
415 |
# forked from interactive::gtk::wait_message_with_progress_bar |
416 |
sub copying_message_with_progress_bar { |
417 |
my ($in, $o_title) = @_; |
418 |
|
419 |
my $progress = Gtk2::ProgressBar->new; |
420 |
my $w = copying_message($in, $o_title, $progress); |
421 |
my $displayed; |
422 |
$progress->signal_connect(expose_event => sub { $displayed = 1; 0 }); |
423 |
$w, sub { |
424 |
my ($msg, $current, $total) = @_; |
425 |
if ($msg) { |
426 |
$w->set($msg); |
427 |
} |
428 |
|
429 |
if ($total) { |
430 |
$progress or internal_error('You must first give some text to display'); |
431 |
$progress->set_fraction($current / $total); |
432 |
$progress->show; |
433 |
$displayed = 0; |
434 |
mygtk2::flush() while !$displayed; |
435 |
} else { |
436 |
$progress->hide if !$total; |
437 |
} |
438 |
}; |
439 |
} |