summaryrefslogtreecommitdiffstats
path: root/perl-install/install2.pm
blob: a2d52ea20a3ef506190e14145bad66bcbbd40e92 (plain)
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599

package install2; # $Id$

use diagnostics;
use strict;
use vars qw($o $version);

#-######################################################################################
#- misc imports
#-######################################################################################
use steps;
use common;
use install_any qw(:all);
use install_steps;
use lang;
use keyboard;
use mouse;
use fsedit;
use devices;
use partition_table qw(:types);
use modules;
use detect_devices;
use run_program;
use any;
use log;
use fs;
#-$::corporate=1;


#-#######################################################################################
#-$O
#-the big struct which contain, well everything (globals + the interactive methods ...)
#-if you want to do a kickstart file, you just have to add all the required fields (see for example
#-the variable $default)
#-#######################################################################################
$o = $::o = {
#    bootloader => { linear => 0, lba32 => 1, message => 1, timeout => 5, restricted => 0 },
    mkbootdisk => 0, #- no mkbootdisk if 0 or undef, find a floppy with 1, or fd1
#-    packages   => [ qw() ],
    partitioning => { clearall => 0, eraseBadPartitions => 0, auto_allocate => 0 }, #-, readonly => 0 },
    security => 2,
    authentication => { md5 => 1, shadow => 1 },
    lang         => 'en_US',
    isUpgrade    => 0,
    toRemove     => [],
    toSave       => [],
#-    simple_themes => 1,

    timezone => {
#-                   timezone => "Europe/Paris",
#-                   UTC      => 1,
                },
#-    superuser => { password => 'a', shell => '/bin/bash', realname => 'God' },
#-    user => { name => 'foo', password => 'bar', home => '/home/foo', shell => '/bin/bash', realname => 'really, it is foo' },

#-    keyboard => 'de',
#-    display => "192.168.1.19:1",
    steps        => \%steps::installSteps,
    orderedSteps => \@steps::orderedInstallSteps,

#- for the list of fields available for user and superuser, see @etc_pass_fields in install_steps.pm
#-    intf => { eth0 => { DEVICE => "eth0", IPADDR => '1.2.3.4', NETMASK => '255.255.255.128' } },

#-step : the current one
#-prefix
#-mouse
#-keyboard
#-netc
#-methods
#-packages compss
#-printer haveone entry(cf printer.pm)

};


sub installStepsCall {
    my ($o, $auto, $fun, @args) = @_;
    $fun = "install_steps::$fun" if $auto;
    $o->$fun(@args);
}

#-######################################################################################
#- Steps Functions
#- each step function are called with two arguments : clicked(because if you are a
#- beginner you can force the the step) and the entered number
#-######################################################################################

#------------------------------------------------------------------------------
sub selectLanguage {
    my ($clicked, $ent_number, $auto) = @_;

    installStepsCall($o, $auto, 'selectLanguage', $ent_number == 1);
}

#------------------------------------------------------------------------------
sub selectMouse {
    my ($clicked, $ent_number, $auto) = @_;

    require pkgs;
    my ($first_time) = $ent_number == 1;

    add2hash($o->{mouse} ||= {}, mouse::read($o->{prefix})) if $o->{isUpgrade} && $first_time;

    installStepsCall($o, $auto, 'selectMouse', !$first_time || $clicked);

    addToBeDone { mouse::write($o->{prefix}, $o->{mouse}) } 'installPackages';
}

#------------------------------------------------------------------------------
sub setupSCSI {
    my ($clicked, $ent_number, $auto) = @_;

    if (!$::live && !$::g_auto_install && !$o->{blank} && !$::testing) {
	-s modules::cz_file() or die _("Can't access kernel modules corresponding to your kernel (file %s is missing)", modules::cz_file());
    }

    installStepsCall($o, $auto, 'setupSCSI', $clicked);
}

#------------------------------------------------------------------------------
sub selectKeyboard {
    my ($clicked, $first_time, $auto) = ($_[0], $_[1] == 1, $_[2]);

    if ($o->{isUpgrade} && $first_time && $o->{keyboard_unsafe}) {
	my $keyboard = keyboard::read($o->{prefix});
	$keyboard and $o->{keyboard} = $keyboard;
    }
    installStepsCall($o, $auto, 'selectKeyboard', $clicked);
}

#------------------------------------------------------------------------------
sub selectInstallClass {
    my ($clicked, $ent_number, $auto) = @_;

    installStepsCall($o, $auto, 'selectInstallClass', $clicked);

    if ($o->{steps}{choosePackages}{entered} >= 1 && !$o->{steps}{installPackages}{done}) {
	installStepsCall($o, $auto, 'setPackages');
	installStepsCall($o, $auto, 'selectPackagesToUpgrade') if $o->{isUpgrade};
    }
    if ($o->{isUpgrade}) {
	@{$o->{orderedSteps}} = map { /setupSCSI/ ? ($_, "doPartitionDisks") : $_ }
	                        grep { !/doPartitionDisks/ } @{$o->{orderedSteps}};
	my $s; foreach (@{$o->{orderedSteps}}) {
	    $s->{next} = $_ if $s;
	    $s = $o->{steps}{$_};
	}
    }
}

#------------------------------------------------------------------------------
sub doPartitionDisks {
    my ($clicked, $ent_number, $auto) = @_;
    $o->{steps}{formatPartitions}{done} = 0;
    installStepsCall($o, $auto, 'doPartitionDisksBefore');
    installStepsCall($o, $auto, 'doPartitionDisks');
    installStepsCall($o, $auto, 'doPartitionDisksAfter');
}

sub formatPartitions {
    my ($clicked, $ent_number, $auto) = @_;

    $o->{steps}{choosePackages}{done} = 0;
    installStepsCall($o, $auto, 'choosePartitionsToFormat', $o->{fstab}) if !$o->{isUpgrade};
    installStepsCall($o, $auto, 'formatMountPartitions', $o->{fstab}) if !$::testing;

    mkdir "$o->{prefix}/$_", 0755 foreach 
      qw(dev etc etc/profile.d etc/rpm etc/sysconfig etc/sysconfig/console 
	etc/sysconfig/network-scripts etc/sysconfig/console/consolefonts 
	etc/sysconfig/console/consoletrans
	home mnt tmp var var/tmp var/lib var/lib/rpm var/lib/urpmi);
    mkdir "$o->{prefix}/$_", 0700 foreach qw(root root/tmp);

    any::rotate_logs($o->{prefix});

    require raid;
    raid::prepare_prefixed($o->{all_hds}{raids}, $o->{prefix});
}

#------------------------------------------------------------------------------
sub choosePackages {
    my ($clicked, $ent_number, $auto) = @_;
    require pkgs;

    #- always setPackages as it may have to copy hdlist files and depslist file.
    installStepsCall($o, $auto, 'setPackages');
    installStepsCall($o, $auto, 'selectPackagesToUpgrade') if $o->{isUpgrade} && $ent_number == 1;

    installStepsCall($o, $auto, 'choosePackages', $o->{packages}, $o->{compssUsers}, $ent_number == 1);
    log::l("compssUsersChoice's: ", join(" ", grep { $o->{compssUsersChoice}{$_} } keys %{$o->{compssUsersChoice}}));

    #- check pre-condition where base backage has to be selected.
    pkgs::packageFlagSelected(pkgs::packageByName($o->{packages}, 'basesystem')) or die "basesystem package not selected";

    #- check if there are package that need installation.
    $o->{steps}{installPackages}{done} = 0 if $o->{steps}{installPackages}{done} && pkgs::packagesToInstall($o->{packages}) > 0;
}

#------------------------------------------------------------------------------
sub installPackages {
    my ($clicked, $ent_number, $auto) = @_;

    installStepsCall($o, $auto, 'readBootloaderConfigBeforeInstall') if $ent_number == 1;

    installStepsCall($o, $auto, 'beforeInstallPackages');
    installStepsCall($o, $auto, 'installPackages');
    installStepsCall($o, $auto, 'afterInstallPackages');
}
#------------------------------------------------------------------------------
sub miscellaneous {
    my ($clicked, $ent_number, $auto) = @_;

    installStepsCall($o, $auto, 'miscellaneousBefore', $clicked);
    installStepsCall($o, $auto, 'miscellaneous', $clicked);

    addToBeDone {
	setVarsInSh("$o->{prefix}/etc/sysconfig/system", { 
            CLEAN_TMP => $o->{miscellaneous}{CLEAN_TMP},
            CLASS => $::expert && 'expert' || 'beginner',
            SECURITY => $o->{security},
	    META_CLASS => $o->{meta_class} || 'PowerPack',
        });
	substInFile { s/KEYBOARD_AT_BOOT=.*/KEYBOARD_AT_BOOT=yes/ } "$o->{prefix}/etc/sysconfig/usb" if detect_devices::usbKeyboards();

    } 'installPackages';
}

#------------------------------------------------------------------------------
sub summary {
    my ($clicked, $ent_number, $auto) = @_;
    installStepsCall($o, $auto, 'summary', $ent_number == 1);
}
#------------------------------------------------------------------------------
sub configureNetwork {
    my ($clicked, $ent_number, $auto) = @_;
    #- get current configuration of network device.
    require network;
    eval { network::read_all_conf($o->{prefix}, $o->{netc} ||= {}, $o->{intf} ||= {}) };
    installStepsCall($o, $auto, 'configureNetwork', $ent_number == 1, $clicked);
}
#------------------------------------------------------------------------------
sub installCrypto {
    my ($clicked, $ent_number, $auto) = @_;
    installStepsCall($o, $auto, 'installCrypto');
}
#------------------------------------------------------------------------------
sub configureServices {
    my ($clicked, $ent_number, $auto) = @_;
    installStepsCall($o, $auto, 'configureServices', $clicked);
}
#------------------------------------------------------------------------------
sub setRootPassword {
    my ($clicked, $ent_number, $auto) = @_;
    return if $o->{isUpgrade};

    installStepsCall($o, $auto, 'setRootPassword', $clicked);
    addToBeDone { install_any::setAuthentication($o) } 'installPackages';
}
#------------------------------------------------------------------------------
sub addUser {
    my ($clicked, $ent_number, $auto) = @_;
    return if $o->{isUpgrade} && !$clicked;

    installStepsCall($o, $auto, 'addUser', $clicked);
}

#------------------------------------------------------------------------------
sub createBootdisk {
    my ($clicked, $ent_number, $auto) = @_;
    modules::write_conf($o->{prefix});
    installStepsCall($o, $auto, 'createBootdisk', $ent_number == 1, $clicked);
}

#------------------------------------------------------------------------------
sub setupBootloader {
    my ($clicked, $ent_number, $auto) = @_;
    return if $::g_auto_install;

    modules::write_conf($o->{prefix});

    installStepsCall($o, $auto, 'setupBootloaderBefore') if $ent_number == 1;
    installStepsCall($o, $auto, 'setupBootloader', $ent_number-1 + $clicked*2); #- gore :-(

    local $ENV{DRAKX_PASSWORD} = $o->{bootloader}{password};
    local $ENV{DURING_INSTALL} = 1;
    run_program::rooted($o->{prefix}, "/usr/sbin/msec", $o->{security});
}
#------------------------------------------------------------------------------
sub configureX {
    my ($clicked, $ent_number, $auto) = @_;

    #- done here and also at the end of install2.pm, just in case...
    install_any::write_fstab($o);
    modules::write_conf($o->{prefix});

    require pkgs;
    installStepsCall($o, $auto, 'configureX', $clicked) if pkgs::packageFlagInstalled(pkgs::packageByName($o->{packages}, 'XFree86')) && !$o->{X}{disabled} || $clicked || $::testing;
}
#------------------------------------------------------------------------------
sub exitInstall {
    my ($clicked, $ent_number, $auto) = @_;
    installStepsCall($o, $auto, 'exitInstall', getNextStep() eq 'exitInstall');
}


#-######################################################################################
#- MAIN
#-######################################################################################
sub main {
    $SIG{__DIE__} = sub { chomp(my $err = $_[0]); log::l("warning: $err") };
    $SIG{SEGV} = sub { 
	my $msg = "segmentation fault: seems like memory is missing as the install crashes"; print "$msg\n"; log::l($msg);
	$o->ask_warn('', $msg);
	setVirtual(1);
	require install_steps_auto_install;
	install_steps_auto_install_non_interactive::errorInStep ();
    };
    $ENV{PERL_BADLANG} = 1;
    umask 022;

    $::isInstall = 1;
    $::expert = $::g_auto_install = 0;

#-    c::unlimit_core() unless $::testing;

    my ($cfg, $patch, @auto);
    my %cmdline; map { 
	my ($n, $v) = split '=';
	$cmdline{$n} = $v || 1;
    } split ' ', cat_("/proc/cmdline");

    my $opt; foreach (@_) {
	if (/^--?(.*)/) {
	    $cmdline{$opt} = 1 if $opt;
	    $opt = $1;
	} else {
	    $cmdline{$opt} = $_ if $opt;
	    $opt = '';
	}
    } $cmdline{$opt} = 1 if $opt;
    
    map_each {
	my ($n, $v) = @_;
	my $f = ${{
	    oem       => sub { $::oem = $v },
	    lang      => sub { $o->{lang} = $v },
	    flang     => sub { $o->{lang} = $v ; push @auto, 'selectLanguage' },
	    method    => sub { $o->{method} = $v },
	    pcmcia    => sub { $o->{pcmcia} = $v },
	    vga16     => sub { $o->{vga16} = $v },
	    vga       => sub { $o->{vga} = $v },
	    step      => sub { $o->{steps}{first} = $v },
	    expert    => sub { $::expert = $v },
	    fbeginner => sub { $::expert = 0; push @auto, 'selectInstallClass' },
	    fexpert   => sub { $::expert = 1; push @auto, 'selectInstallClass' },
	    desktop   => sub { $o->{meta_class} = 'desktop' },
	    firewall  => sub { $o->{meta_class} = 'firewall'; push @auto, 'selectInstallClass'},
	    lnx4win   => sub { $o->{lnx4win} = 1 },
	    readonly  => sub { $o->{partitioning}{readonly} = $v ne "0" },
	    display   => sub { $o->{display} = $v },
	    security  => sub { $o->{security} = $v },
	    live      => sub { $::live = 1 },
	    noauto    => sub { $::noauto = 1 },
	    test      => sub { $::testing = 1 },
	    patch     => sub { $patch = 1 },
	    defcfg    => sub { $cfg = $v },
	    newt      => sub { $o->{interactive} = "newt" },
	    text      => sub { $o->{interactive} = "newt" },
	    stdio     => sub { $o->{interactive} = "stdio"},
	    corporate => sub { $::corporate = 1 },
	    kickstart => sub { $::auto_install = $v },
	    auto_install => sub { $::auto_install = $v },
	    simple_themes => sub { $o->{simple_themes} = 1 },
	    useless_thing_accepted => sub { $o->{useless_thing_accepted} = 1 },
	    alawindows => sub { $o->{security} = 0; $o->{partitioning}{clearall} = 1; $o->{bootloader}{crushMbr} = 1 },
	    fdisk => sub { $o->{partitioning}{fdisk} = 1 },
	    g_auto_install => sub { $::testing = $::g_auto_install = 1; $o->{partitioning}{auto_allocate} = 1 },
	    nomouseprobe => sub { $o->{nomouseprobe} = $v },
	    blank         => sub { $o->{blank} = 1},
	    updatemodules => sub { $o->{updatemodules} = 1},
	}}{lc $n}; &$f if $f;
    } %cmdline;

    if ($::testing) {
	$ENV{SHARE_PATH} ||= "/export/Mandrake/mdkinst/usr/share";
	$ENV{SHARE_PATH} = "/usr/share" if !-e $ENV{SHARE_PATH};
    } else {
	$ENV{SHARE_PATH} ||= "/usr/share";
    }

    undef $::auto_install if $cfg;
    if ($::g_auto_install) {
	(my $root = `/bin/pwd`) =~ s|(/[^/]*){5}$||;
	symlinkf $root, "/tmp/image" or die "unable to create link /tmp/image";
	$o->{method} ||= "cdrom";
	$o->{mkbootdisk} = 0;
    }
    unless ($::testing || $::live) {
	symlink "rhimage", "/tmp/image"; #- for compatibility with old stage1
	unlink $_ foreach "/modules/modules.mar", "/sbin/stage1";
    }

    print STDERR "in second stage install\n";
    log::openLog(($::testing || $o->{localInstall}) && 'debug.log');
    log::l("second stage install running (", any::drakx_version(), ")");

    $o->{prefix} = $::testing ? "/tmp/test-perl-install" : $::live ? "" : "/mnt";
    $o->{root}   = $::testing ? "/tmp/root-perl-install" : "/";
    $o->{isUpgrade} = 1 if $::live;
    mkdir $o->{prefix}, 0755;
    mkdir $o->{root}, 0755;
    devices::make("/dev/zero"); #- needed by ddcxinfos

    #-  make sure we don't pick up any gunk from the outside world
    my $remote_path = "$o->{prefix}/sbin:$o->{prefix}/bin:$o->{prefix}/usr/sbin:$o->{prefix}/usr/bin:$o->{prefix}/usr/X11R6/bin";
    $ENV{PATH} = "/usr/bin:/bin:/sbin:/usr/sbin:/usr/X11R6/bin:$remote_path" unless $::g_auto_install;

    eval { spawnShell() };

    $o->{prefix} = $::testing ? "/tmp/test-perl-install" : $::live ? "" : "/mnt";
    mkdir $o->{prefix}, 0755;

    modules::load_deps(($::testing ? ".." : "") . "/modules/modules.dep");
    modules::read_stage1_conf($_) foreach "/tmp/conf.modules", "/etc/modules.conf";
    modules::read_already_loaded();

    #- done after module dependencies are loaded for "vfat depends on fat"
    if ($::auto_install) {
	require install_steps_auto_install;
	eval { $o = $::o = install_any::loadO($o, $::auto_install) };
	if ($@) {
	    if ($o->{useless_thing_accepted}) { #- Pixel's hack to be able to fail through
		log::l("error using auto_install, continuing");
		undef $::auto_install;
	    } else {
		print "Error using auto_install\n$@\n";
		install_steps_auto_install_non_interactive::errorInStep ();
	    }
	} else {
	    log::l("auto install config file loaded successfully");
	}
    }
    $o->{interactive} ||= 'gtk' if !$::auto_install;
 
    if ($o->{interactive} eq "gtk" && availableMemory < 22 * 1024) {
 	log::l("switching to newt install cuz not enough memory");
 	$o->{interactive} = "newt";
    }
    require"install_steps_$o->{interactive}.pm" if $o->{interactive}; #- no space to skip perl2fcalls

 
    eval { $o = $::o = install_any::loadO($o, "patch") } if $patch;
    eval { $o = $::o = install_any::loadO($o, $cfg) } if $cfg;

    eval { modules::load("af_packet") };

    map_index {
	modules::add_alias("sound-slot-$::i", $_->{driver});
    } modules::get_that_type('sound');

    #- needed very early for install_steps_gtk
    $::noauto or modules::load_thiskind("usb"); 
    eval { ($o->{mouse}, @{$o->{wacom} = []}) = mouse::detect() } unless $o->{nomouseprobe} || $o->{mouse};

    $o->{lang} = lang::set($o->{lang}); #- mainly for defcfg

    $o->{allowFB} = listlength(cat_("/proc/fb"));

    my $VERSION = cat__(install_any::getFile("VERSION")) or do { print "VERSION file missing\n"; sleep 5 };
    $o->{lnx4win} = 1 if $VERSION =~ /lnx4win/i;
    $o->{meta_class} = 'desktop' if $VERSION =~ /desktop/i;
    $o->{meta_class} = 'firewall' if $VERSION =~ /firewall/i;
    $o->{meta_class} = 'server' if $VERSION =~ /server/i;
    if ($::oem) {
	$o->{partitioning}{use_existing_root} = 1;
	$o->{partitioning}{auto_allocate} = 1;
	$o->{compssListLevel} = 4;
	push @auto, 'selectInstallClass', 'doPartitionDisks', 'choosePackages', 'configureTimezone', 'exitInstall';
    }

    foreach (@auto) {
	my $s = $o->{steps}{/::(.*)/ ? $1 : $_} or next;
	$s->{auto} = $s->{hidden} = 1;
    }

    my $o_;
    while (1) {
    	$o_ = $::auto_install ?
    	  install_steps_auto_install->new($o) :
    	    $o->{interactive} eq "stdio" ?
    	  install_steps_stdio->new($o) :
    	    $o->{interactive} eq "newt" ?
    	  install_steps_newt->new($o) :
    	    $o->{interactive} eq "gtk" ?
    	  install_steps_gtk->new($o) :
    	    die "unknown install type";
	$o_ and last;

	$o->{interactive} = "newt";
	require install_steps_newt;
    }
    $::o = $o = $o_;

    if (-e '/tmp/network') {
	require network;
	#- get stage1 network configuration if any.
	log::l('found /tmp/network');
	$o->{netc} ||= network::read_conf('/tmp/network');
	if (my ($file) = glob_('/tmp/ifcfg-*')) {
	    log::l("found network config file $file");
	    my $l = network::read_interface_conf($file);
	    $o->{intf} ||= { $l->{DEVICE} => $l };
	}
	if (-e '/etc/resolv.conf') {
	    my $file ='/etc/resolv.conf';
	    log::l("found network config file $file");
	    add2hash($o->{netc}, network::read_resolv_conf($file));
	}
    }
    install_any::remove_unused() if common::usingRamdisk();

    #-the main cycle
    my $clicked = 0;
    MAIN: for ($o->{step} = $o->{steps}{first};; $o->{step} = getNextStep()) {
	$o->{steps}{$o->{step}}{entered}++;
	$o->enteringStep($o->{step});
	if ($o->{steps}{$o->{step}}{icon}) { $o->{icon} = $o->{steps}{$o->{step}}{icon} } else { undef $o->{icon} }
	eval {
	    &{$install2::{$o->{step}}}($clicked || $o->{steps}{$o->{step}}{noauto},
				       $o->{steps}{$o->{step}}{entered},
				       $clicked ? 0 : $o->{steps}{$o->{step}}{auto});
	};
	my $err = $@;
	$o->kill_action;
	$clicked = 0;
	if ($err) {
	    local $_ = $err;
	    $o->kill_action;
	    if (/^setstep (.*)/) {
		$o->{step} = $1;
		$o->{steps}{$1}{done} = 0;
		$clicked = 1;
		redo MAIN;
	    }
	    /^theme_changed$/ and redo MAIN;
	    unless (/^already displayed/) {
		eval { $o->errorInStep($_) };
		$o->{steps}{$o->{step}}{auto} = 0;
		$err = $@;
		$err and next;
	    }
	    $o->{step} = $o->{steps}{$o->{step}}{onError};
	    next MAIN unless $o->{steps}{$o->{step}}{reachable}; #- sanity check: avoid a step not reachable on error.
	    redo MAIN;
	}
	$o->{steps}{$o->{step}}{done} = 1;
	$o->leavingStep($o->{step});

	last if $o->{step} eq 'exitInstall';
    }
    install_any::clean_postinstall_rpms();
    install_any::log_sizes($o);
    install_any::ejectCdrom();
    install_any::remove_advertising($o);

    install_any::write_fstab($o);
    modules::write_conf($o->{prefix});

    #- to ensure linuxconf doesn't cry against those files being in the future
    foreach ('/etc/modules.conf', '/etc/crontab', '/etc/sysconfig/mouse', '/etc/sysconfig/network', '/etc/X11/fs/config') {
	my $now = time - 24 * 60 * 60;
	utime $now, $now, "$o->{prefix}/$_";
    }
    $::live or install_any::killCardServices();

    #- make sure failed upgrade will not hurt too much.
    install_steps::cleanIfFailedUpgrade($o);

    -e "$o->{prefix}/usr/sbin/urpmi.update" or eval { rm_rf("$o->{prefix}/var/lib/urpmi") };

    #- mainly for auto_install's
    run_program::run("bash", "-c", $o->{postInstallNonRooted}) if $o->{postInstallNonRooted};
    run_program::rooted($o->{prefix}, "sh", "-c", $o->{postInstall}) if $o->{postInstall};

    #- have the really bleeding edge ddebug.log
    eval { cp_af("/tmp/ddebug.log", "$o->{prefix}/root") };

    #- ala pixel? :-) [fpons]
    common::sync(); common::sync();

    log::l("installation complete, leaving");
    log::l("files still open by install2: ", readlink($_)) foreach glob_("/proc/self/fd/*");
    print "\n" x 80;
}

#-######################################################################################
#- Wonderful perl :(
#-######################################################################################
1;
s="hl kwc">or next; $otherOnly or packageSetFlagSelected($provided, 0); $otherOnly and $otherOnly->{packageName($provided)} = 1; } foreach (map { split '\|' } packageDepsId($provided)) { my $dep = packageById($packages, $_) or next; packageFlagBase($dep) and next; packageFlagSelected($dep) or next; for (packageFlagSelected($dep)) { $_ == 1 and do { $otherOnly and $otherOnly->{packageName($dep)} ||= 0; }; $_ > 1 and do { $otherOnly or packageSetFlagSelected($dep, $_-1); }; last; } } } 1; } sub togglePackageSelection($$;$) { my ($packages, $pkg, $otherOnly) = @_; packageFlagSelected($pkg) ? unselectPackage($packages, $pkg, $otherOnly) : selectPackage($packages, $pkg, 0, $otherOnly); } sub setPackageSelection($$$) { my ($packages, $pkg, $value) = @_; $value ? selectPackage($packages, $pkg) : unselectPackage($packages, $pkg); } sub unselectAllPackages($) { my ($packages) = @_; foreach (values %{$packages->{names}}) { unless (packageFlagBase($_) || packageFlagUpgrade($_)) { packageSetFlagSelected($_, 0); } } } sub unselectAllPackagesIncludingUpgradable($) { my ($packages, $removeUpgradeFlag) = @_; foreach (values %{$packages->{names}}) { unless (packageFlagBase($_)) { packageSetFlagSelected($_, 0); packageSetFlagUpgrade($_, 0); } } } sub psUpdateHdlistsDeps { my ($prefix, $method) = @_; my $listf = install_any::getFile('Mandrake/base/hdlists') or die "no hdlists found"; #- WARNING: this function should be kept in sync with functions #- psUsingHdlists and psUsingHdlist. #- it purpose it to update hdlist files on system to install. #- parse hdlist.list file. my $medium = 1; foreach (<$listf>) { chomp; s/\s*#.*$//; /^\s*$/ and next; m/^\s*(hdlist\S*\.cz2?)\s+(\S+)\s*(.*)$/ or die "invalid hdlist description \"$_\" in hdlists file"; my ($hdlist, $rpmsdir, $descr) = ($1, $2, $3); #- copy hdlist file directly to $prefix/var/lib/urpmi, this will be used #- for getting header of package during installation or after by urpmi. my $fakemedium = "$descr ($method$medium)"; my $newf = "$prefix/var/lib/urpmi/hdlist.$fakemedium.cz" . ($hdlist =~ /\.cz2/ && "2"); -e $newf and do { unlink $newf or die "cannot remove $newf: $!"; }; install_any::getAndSaveFile("Mandrake/base/$hdlist", $newf) or die "no $hdlist found"; symlinkf $newf, "/tmp/$hdlist"; ++$medium; } #- this is necessary for urpmi. install_any::getAndSaveFile("Mandrake/base/$_", "$prefix/var/lib/urpmi/$_") foreach qw(depslist.ordered provides rpmsrate); } sub psUsingHdlists { my ($prefix, $method) = @_; my $listf = install_any::getFile('Mandrake/base/hdlists') or die "no hdlists found"; my %packages = ( names => {}, count => 0, depslist => [], mediums => {}); #- parse hdlists file. my $medium = 1; foreach (<$listf>) { chomp; s/\s*#.*$//; /^\s*$/ and next; m/^\s*(hdlist\S*\.cz2?)\s+(\S+)\s*(.*)$/ or die "invalid hdlist description \"$_\" in hdlists file"; #- make sure the first medium is always selected! #- by default select all image. psUsingHdlist($prefix, $method, \%packages, $1, $medium, $2, $3, 1); ++$medium; } log::l("psUsingHdlists read " . scalar keys(%{$packages{names}}) . " headers on " . scalar keys(%{$packages{mediums}}) . " hdlists"); \%packages; } sub psUsingHdlist { my ($prefix, $method, $packages, $hdlist, $medium, $rpmsdir, $descr, $selected, $fhdlist) = @_; my $fakemedium = "$descr ($method$medium)"; log::l("trying to read $hdlist for medium $medium"); #- if the medium already exist, use it. $packages->{mediums}{$medium} and return; my $m = $packages->{mediums}{$medium} = { hdlist => $hdlist, method => $method, medium => $medium, rpmsdir => $rpmsdir, #- where is RPMS directory. descr => $descr, fakemedium => $fakemedium, min => $packages->{count}, max => -1, #- will be updated after reading current hdlist. selected => $selected, #- default value is only CD1, it is really the minimal. }; #- copy hdlist file directly to $prefix/var/lib/urpmi, this will be used #- for getting header of package during installation or after by urpmi. my $newf = "$prefix/var/lib/urpmi/hdlist.$fakemedium.cz" . ($hdlist =~ /\.cz2/ && "2"); -e $newf and do { unlink $newf or die "cannot remove $newf: $!"; }; install_any::getAndSaveFile($fhdlist || "Mandrake/base/$hdlist", $newf) or die "no $hdlist found"; symlinkf $newf, "/tmp/$hdlist"; #- avoid using more than one medium if Cd is not ejectable. #- but keep all medium here so that urpmi has the whole set. $method eq 'cdrom' && $medium > 1 && !common::usingRamdisk() and return; #- extract filename from archive, this take advantage of verifying #- the archive too. eval { require packdrake; my $packer = new packdrake($newf, quiet => 1); foreach (@{$packer->{files}}) { $packer->{data}{$_}[0] eq 'f' or next; ++$packages->{count}; #- take care of this one, so that desplist will be clean with index of package. my $pkg = [ (undef) x 8 ]; $pkg->[$FILE] = $_; $pkg->[$MEDIUM] = $medium; my $specific_arch = packageArch($pkg); if (!$specific_arch || MDK::Common::System::compat_arch($specific_arch)) { my $old_pkg = $packages->{names}{packageName($pkg)}; if ($old_pkg) { if (packageVersion($pkg) eq packageVersion($old_pkg) && packageRelease($pkg) eq packageRelease($old_pkg)) { if (MDK::Common::System::better_arch($specific_arch, packageArch($old_pkg))) { log::l("replacing old package with package $_ with better arch: $specific_arch"); $packages->{names}{packageName($pkg)} = $pkg; } else { log::l("keeping old package against package $_ with worse arch"); } } else { log::l("ignoring package $_ already present in distribution with different version or release"); } } else { $packages->{names}{packageName($pkg)} = $pkg; } } else { log::l("ignoring package $_ with incompatible arch: $specific_arch"); } } }; #- update maximal index. $m->{max} = $packages->{count} - 1; $m->{max} >= $m->{min} or die "nothing found while parsing $newf"; log::l("read " . ($m->{max} - $m->{min} + 1) . " headers in $hdlist"); 1; } sub getOtherDeps($$) { my ($packages, $f) = @_; #- this version of getDeps is customized for handling errors more easily and #- convert reference by name to deps id including closure computation. local $_; while (<$f>) { my ($name, $version, $release, $size, $deps) = /^(\S*)-([^-\s]+)-([^-\s]+)\s+(\d+)\s+(.*)/; my $pkg = $packages->{names}{$name}; $pkg or log::l("ignoring package $name-$version-$release in depslist is not in hdlist"), next; $version eq packageVersion($pkg) and $release eq packageRelease($pkg) or log::l("warning package $name-$version-$release in depslist mismatch version or release in hdlist ($version ne ", packageVersion($pkg), " or $release ne ", packageRelease($pkg), ")"), next; my $index = scalar @{$packages->{depslist}}; $index >= packageMedium($packages, $pkg)->{min} && $index <= packageMedium($packages, $pkg)->{max} or log::l("ignoring package $name-$version-$release in depslist outside of hdlist indexation"); #- here we have to translate referenced deps by name to id. #- this include a closure on deps too. my %closuredeps; @closuredeps{map { packageId($packages, $_), packageDepsId($_) } grep { $_ } map { packageByName($packages, $_) or do { log::l("unknown package $_ in depslist for closure"); undef } } split /\s+/, $deps} = (); $pkg->[$SIZE_DEPS] = join " ", $size, keys %closuredeps; push @{$packages->{depslist}}, $pkg; } #- check for same number of package in depslist and hdlists, avoid being to hard. scalar(keys %{$packages->{names}}) == scalar(@{$packages->{depslist}}) or log::l("other depslist has not same package as hdlist file"); } sub getDeps { my ($prefix, $packages) = @_; #- this is necessary for urpmi. install_any::getAndSaveFile('Mandrake/base/depslist.ordered', "$prefix/var/lib/urpmi/depslist.ordered"); install_any::getAndSaveFile('Mandrake/base/provides', "$prefix/var/lib/urpmi/provides"); #- beware of heavily mismatching depslist.ordered file against hdlist files. my $mismatch = 0; #- count the number of packages in deplist that are also in hdlist my $nb_deplist = 0; #- update dependencies list, provides attributes are updated later #- cross reference to be resolved on id (think of loop requires) #- provides should be updated after base flag has been set to save #- memory. local *F; open F, "$prefix/var/lib/urpmi/depslist.ordered" or die "can't find dependancies list"; local $_; while (<F>) { my ($name, $version, $release, $arch, $epoch, $sizeDeps) = /^([^:\s]*)-([^:\-\s]+)-([^:\-\s]+)\.([^:\.\-\s]*)(?::(\d+)\S*)?\s+(.*)/; my $pkg = $packages->{names}{$name}; #- these verification are necessary in case of error, but are no more fatal as #- in case of only one medium taken into account during install, there should be #- silent warning for package which are unknown at this point. $pkg or log::l("ignoring $name-$version-$release.$arch in depslist is not in hdlist"); $pkg && $version ne packageVersion($pkg) and log::l("ignoring $name-$version-$release.$arch in depslist mismatch version in hdlist"), $pkg = undef; $pkg && $release ne packageRelease($pkg) and log::l("ignoring $name-$version-$release.$arch in depslist mismatch release in hdlist"), $pkg = undef; $pkg && $arch ne packageArch($pkg) and log::l("ignoring $name-$version-$release.$arch in depslist mismatch arch in hdlist"), $pkg = undef; if ($pkg) { $nb_deplist++; $epoch && $epoch > 0 and $pkg->[$EPOCH] = $epoch; #- only 5% of the distribution use epoch (serial). $pkg->[$SIZE_DEPS] = $sizeDeps; #- check position of package in depslist according to precomputed #- limit by hdlist, very strict :-) #- above warning have chance to raise an exception here, but may help #- for debugging. my $i = scalar @{$packages->{depslist}}; $i >= packageMedium($packages, $pkg)->{min} && $i <= packageMedium($packages, $pkg)->{max} or log::l("inconsistency in position for $name-$version-$release.$arch in depslist and hdlist"), $mismatch = 1; } #- package are already sorted in depslist to enable small transaction and multiple medium. push @{$packages->{depslist}}, $pkg; } #- check for mismatching package, it should break with above die unless depslist has too many errors! $mismatch and die "depslist.ordered mismatch against hdlist files"; #- check for same number of package in depslist and hdlists. my $nb_hdlist = keys %{$packages->{names}}; $nb_hdlist == $nb_deplist or die "depslist.ordered has not same package as hdlist files ($nb_deplist != $nb_hdlist)"; } sub getProvides($) { my ($packages) = @_; #- update provides according to dependencies, here are stored #- reference to package directly and choice are included, this #- assume only 1 of the choice is selected, else on unselection #- the provided package will be deleted where other package still #- need it. #- base package are not updated because they cannot be unselected, #- this save certainly a lot of memory since most of them may be #- needed by a large number of package. #- now using a packed of signed short, this means no more than 32768 #- packages can be managed by DrakX (currently about 2000). my $i = 0; foreach my $pkg (@{$packages->{depslist}}) { $pkg or next; unless (packageFlagBase($pkg)) { foreach (map { split '\|' } grep { !/^NOTFOUND_/ } packageDepsId($pkg)) { my $provided = packageById($packages, $_) or next; packageFlagBase($provided) or $provided->[$PROVIDES] = pack "s*", (unpack "s*", $provided->[$PROVIDES]), $i; } } ++$i; } } sub read_rpmsrate { my ($packages, $f) = @_; my $line_nb = 0; my $fatal_error; my (@l); while (<$f>) { $line_nb++; /\t/ and die "tabulations not allowed at line $line_nb\n"; s/#.*//; # comments my ($indent, $data) = /(\s*)(.*)/; next if !$data; # skip empty lines @l = grep { $_->[0] < length $indent } @l; my @m = @l ? @{$l[$#l][1]} : (); my ($t, $flag, @l2); while ($data =~ /^(( [1-5] | (?: (?: !\s*)? [0-9A-Z_]+(?:".*?")?) (?: \s*\|\|\s* (?: !\s*)? [0-9A-Z_]+(?:".*?")?)* ) (?:\s+|$) )(.*)/x) { #@")) { ($t, $flag, $data) = ($1,$2,$3); while ($flag =~ s,^\s*(("[^"]*"|[^"\s]*)*)\s+,$1,) {} my $ok = 0; $flag = join('||', grep { if (my ($inv, $p) = /^(!)?HW"(.*)"/) { ($inv xor detect_devices::matching_desc($p)) and $ok = 1; 0; } else { 1; } } split '\|\|', $flag); push @m, $ok ? 'TRUE' : $flag || 'FALSE'; push @l2, [ length $indent, [ @m ] ]; $indent .= $t; } if ($data) { # has packages on same line my ($rate) = grep { /^\d$/ } @m or die sprintf qq(missing rate for "%s" at line %d (flags are %s)\n), $data, $line_nb, join('&&', @m); foreach (split ' ', $data) { if ($packages) { my $p = packageByName($packages, $_) or next; my @m2 = map { if_($_ && packageName($_) =~ /locales-(.*)/, qq(LOCALES"$1")) } map { packageById($packages, $_) } packageDepsId($p); my @m3 = ((grep { !/^\d$/ } @m), @m2); if (packageRate($p)) { next if @m3 == 1 && $m3[0] eq 'INSTALL'; my ($rate2, @m4) = packageRateRFlags($p); if (@m3 > 1 || @m4 > 1) { log::l("can't handle complicate flags for packages appearing twice ($_)"); $fatal_error++; } log::l("package $_ appearing twice with different rates ($rate != $rate2)") if $rate != $rate2; packageSetRateRFlags($p, $rate, "$m3[0]||$m4[0]"); } else { packageSetRateRFlags($p, $rate, @m3); } } else { print "$_ = ", join(" && ", @m), "\n"; } } push @l, @l2; } else { push @l, [ $l2[0][0], $l2[$#l2][1] ]; } } $fatal_error and die "$fatal_error fatal errors in rpmsrate"; } sub readCompssUsers { my ($meta_class) = @_; my (%compssUsers, @sorted, $l); my $file = 'Mandrake/base/compssUsers'; my $f = $meta_class && install_any::getFile("$file.$meta_class") || install_any::getFile($file) or die "can't find $file"; local $_; while (<$f>) { /^\s*$/ || /^#/ and next; s/#.*//; if (/^(\S.*)/) { my $verbatim = $_; my ($icon, $descr, $path); /^(.*?)\s*\[path=(.*?)\](.*)/ and $_ = "$1$3", $path = $2; /^(.*?)\s*\[icon=(.*?)\](.*)/ and $_ = "$1$3", $icon = $2; /^(.*?)\s*\[descr=(.*?)\](.*)/ and $_ = "$1$3", $descr = $2; $compssUsers{"$path|$_"} = { label => $_, verbatim => $verbatim, path => $path, icons => $icon, descr => $descr, flags => $l=[] }; push @sorted, "$path|$_"; } elsif (/^\s+(.*?)\s*$/) { push @$l, $1; } } \%compssUsers, \@sorted; } sub saveCompssUsers { my ($prefix, $packages, $compssUsers, $sorted) = @_; my $flat; foreach (@$sorted) { my @fl = @{$compssUsers->{$_}{flags}}; my %fl; $fl{$_} = 1 foreach @fl; $flat .= $compssUsers->{$_}{verbatim}; foreach my $p (values %{$packages->{names}}) { my ($rate, @flags) = packageRateRFlags($p); if ($rate && grep { grep { !/^!/ && $fl{$_} } split('\|\|') } @flags) { $flat .= sprintf "\t%d %s\n", $rate, packageName($p); } } } output "$prefix/var/lib/urpmi/compssUsers.flat", $flat; } sub setSelectedFromCompssList { my ($packages, $compssUsersChoice, $min_level, $max_size) = @_; $compssUsersChoice->{TRUE} = 1; #- ensure TRUE is set my $nb = selectedSize($packages); foreach my $p (sort { packageRate($b) <=> packageRate($a) } values %{$packages->{names}}) { my ($rate, @flags) = packageRateRFlags($p); next if !$rate || $rate < $min_level || grep { !grep { /^!(.*)/ ? !$compssUsersChoice->{$1} : $compssUsersChoice->{$_} } split('\|\|') } @flags; #- determine the packages that will be selected when #- selecting $p. the packages are not selected. my %newSelection; selectPackage($packages, $p, 0, \%newSelection); #- this enable an incremental total size. my $old_nb = $nb; foreach (grep { $newSelection{$_} } keys %newSelection) { $nb += packageSize($packages->{names}{$_}); } if ($max_size && $nb > $max_size) { $nb = $old_nb; $min_level = packageRate($p); last; } #- at this point the package can safely be selected. selectPackage($packages, $p); } log::l("setSelectedFromCompssList: reached size ", formatXiB($nb), ", up to indice $min_level (less than ", formatXiB($max_size), ")"); log::l("setSelectedFromCompssList: ", join(" ", sort map { packageName($_) } grep { packageFlagSelected($_) } @{$packages->{depslist}})); $min_level; } #- usefull to know the size it would take for a given min_level/max_size #- just saves the selected packages, call setSelectedFromCompssList and restores the selected packages sub saveSelected { my ($packages) = @_; my @l = values %{$packages->{names}}; my @flags = map { packageFlagSelected($_) } @l; [ $packages, \@l, \@flags ]; } sub restoreSelected { my ($packages, $l, $flags) = @{$_[0]}; mapn { packageSetFlagSelected(@_) } $l, $flags; } sub computeGroupSize { my ($packages, $min_level) = @_; sub inside { my ($l1, $l2) = @_; my $i = 0; return if @$l1 > @$l2; foreach (@$l1) { my $c; while ($c = $l2->[$i++] cmp $_ ) { return if $c == 1 || $i > @$l2; } } 1; } sub or_ify { my ($first, @other) = @_; my @l = split('\|\|', $first); foreach (@other) { @l = map { my $n = $_; map { "$_&&$n" } @l; } split('\|\|'); } #- HACK, remove LOCALES & CHARSET, too costly grep { !/LOCALES|CHARSET/ } @l; } sub or_clean { my (@l) = map { [ sort split('&&') ] } @_ or return ''; my @r; B: while (@l) { my $e = shift @l; foreach (@r, @l) { inside($e, $_) and next B; } push @r, $e; } join("\t", map { join('&&', @$_) } @r); } my (%group, %memo); foreach my $p (values %{$packages->{names}}) { my ($rate, @flags) = packageRateRFlags($p); next if !$rate || $rate < $min_level; my $flags = join("\t", @flags = or_ify(@flags)); $group{packageName($p)} = ($memo{$flags} ||= or_clean(@flags)); #- determine the packages that will be selected when selecting $p. the packages are not selected. my %newSelection; selectPackage($packages, $p, 0, \%newSelection); foreach (grep { $newSelection{$_} } keys %newSelection) { my $s = $group{$_} || do { $packages->{names}{$_}[$VALUES] =~ /\t(.*)/; join("\t", or_ify(split("\t", $1))); }; next if length($s) > 80; # HACK, truncated too complicated expressions, too costly my $m = "$flags\t$s"; $group{$_} = ($memo{$m} ||= or_clean(@flags, split("\t", $s))); } } my (%sizes, %pkgs); while (my ($k, $v) = each %group) { push @{$pkgs{$v}}, $k; $sizes{$v} += packageSize($packages->{names}{$k}); } log::l(sprintf "%s %dMB %s", $_, $sizes{$_} / sqr(1024), join(',', @{$pkgs{$_}})) foreach keys %sizes; \%sizes, \%pkgs; } sub init_db { my ($prefix) = @_; my $f = "$prefix/root/install.log"; open(LOG, ">> $f") ? log::l("opened $f") : log::l("Failed to open $f. No install log will be kept."); *LOG or *LOG = log::F() or *LOG = *STDERR; CORE::select((CORE::select(LOG), $| = 1)[0]); c::rpmErrorSetCallback(fileno LOG); #- c::rpmSetVeryVerbose(); log::l("reading /usr/lib/rpm/rpmrc"); c::rpmReadConfigFiles() or die "can't read rpm config files"; log::l("\tdone"); } sub rebuild_db_open_for_traversal { my ($packages, $prefix) = @_; log::l("reading /usr/lib/rpm/rpmrc"); c::rpmReadConfigFiles() or die "can't read rpm config files"; log::l("\tdone"); unless (exists $packages->{rebuild_db}) { if (my $pid = fork()) { waitpid $pid, 0; ($? & 0xff00) and die "rebuilding of rpm database failed"; } else { log::l("rebuilding rpm database"); my $rebuilddb_dir = "$prefix/var/lib/rpmrebuilddb.$$"; -d $rebuilddb_dir and log::l("removing stale directory $rebuilddb_dir"), rm_rf($rebuilddb_dir); c::rpmdbRebuild($prefix) or log::l("rebuilding of rpm database failed: ". c::rpmErrorString()), c::_exit(2); c::_exit(0); } $packages->{rebuild_db} = undef; } my $db = c::rpmdbOpenForTraversal($prefix) or die "unable to open $prefix/var/lib/rpm/Packages"; log::l("opened rpm database for examining existing packages"); $db; } sub clean_old_rpm_db { my ($prefix) = @_; my $failed; foreach (qw(Basenames Conflictname Group Name Packages Providename Requirename Triggername)) { -s "$prefix/var/lib/rpm/$_" or $failed = 'failed'; } #- rebuilding has been successfull, so remove old rpm database if any. #- once we have checked the rpm4 db file are present and not null, in case #- of doubt, avoid removing them... unless ($failed) { log::l("rebuilding rpm database completed successfully"); foreach (qw(conflictsindex.rpm fileindex.rpm groupindex.rpm nameindex.rpm packages.rpm providesindex.rpm requiredby.rpm triggerindex.rpm)) { -e "$prefix/var/lib/rpm/$_" or next; log::l("removing old rpm file $_"); rm_rf("$prefix/var/lib/rpm/$_"); } } } sub done_db { log::l("closing install.log file"); close LOG; } sub versionCompare($$) { my ($a, $b) = @_; local $_; while ($a || $b) { my ($sb, $sa) = map { $1 if $a =~ /^\W*\d/ ? s/^\W*0*(\d+)// : s/^\W*(\D*)// } ($b, $a); $_ = ($sa =~ /^\d/ || $sb =~ /^\d/) && length($sa) <=> length($sb) || $sa cmp $sb and return $_ || 0; $sa eq '' && $sb eq '' and return $a cmp $b || 0; } } sub selectPackagesAlreadyInstalled { my ($packages, $prefix) = @_; #- avoid rebuilding the database if such case. $packages->{rebuild_db} = "oem does not need rebuilding the rpm db"; my $db = rebuild_db_open_for_traversal($packages, $prefix); #- this method has only one objectif, check the presence of packages #- already installed and avoid installing them again. this is to be used #- with oem installation, if the database exists, preselect the packages #- installed WHATEVER their version/release (log if a problem is perceived #- is enough). c::rpmdbTraverse($db, sub { my ($header) = @_; my $p = $packages->{names}{c::headerGetEntry($header, 'name')}; if ($p) { my $epoch_cmp = c::headerGetEntry($header, 'epoch') <=> packageEpoch($p); my $version_cmp = $epoch_cmp == 0 && versionCompare(c::headerGetEntry($header, 'version'), packageVersion($p)); my $version_rel_test = $epoch_cmp > 0 || $epoch_cmp == 0 && ($version_cmp > 0 || $version_cmp == 0 && versionCompare(c::headerGetEntry($header, 'release'), packageRelease($p)) >= 0); $version_rel_test or log::l("keeping an older package, avoiding selecting $p->[$FILE]"); packageSetFlagInstalled($p, 1); } }); #- close db, job finished ! c::rpmdbClose($db); log::l("done selecting packages to upgrade"); } sub selectPackagesToUpgrade($$$;$$) { my ($packages, $prefix, $base, $toRemove, $toSave) = @_; local $_; #- else perl complains on the map { ... } grep { ... } @...; local (*UPGRADE_INPUT, *UPGRADE_OUTPUT); pipe UPGRADE_INPUT, UPGRADE_OUTPUT; if (my $pid = fork()) { @{$toRemove || []} = (); #- reset this one. close UPGRADE_OUTPUT; while (<UPGRADE_INPUT>) { chomp; my ($action, $name) = /^([\w\d]*):(.*)/; for ($action) { /remove/ and do { push @$toRemove, $name; next }; /keepfiles/ and do { push @$toSave, $name; next }; my $p = $packages->{names}{$name} or die "unable to find package ($name)"; /^\d*$/ and do { $p->[$INSTALLED_CUMUL_SIZE] = $action; next }; /installed/ and do { packageSetFlagInstalled($p, 1); next }; /select/ and do { selectPackage($packages, $p); next }; die "unknown action ($action)"; } } close UPGRADE_INPUT; waitpid $pid, 0; } else { close UPGRADE_INPUT; my $db = rebuild_db_open_for_traversal($packages, $prefix); #- used for package that are not correctly updated. #- should only be used when nothing else can be done correctly. my %upgradeNeedRemove = ( 'libstdc++' => 1, 'compat-glibc' => 1, 'compat-libs' => 1, ); #- generel purpose for forcing upgrade of package whatever version is. my %packageNeedUpgrade = ( #'lilo' => 1, #- this package has been misnamed in 7.0. ); #- help removing package which may have different release numbering my %toRemove; map { $toRemove{$_} = 1 } @{$toRemove || []}; #- help searching package to upgrade in regard to already installed files. my %installedFilesForUpgrade; #- help keeping memory by this set of package that have been obsoleted. my %obsoletedPackages; #- make a subprocess here for reading filelist, this is important #- not to waste a lot of memory for the main program which will fork #- latter for each transaction. local (*INPUT, *OUTPUT_CHILD); pipe INPUT, OUTPUT_CHILD; local (*INPUT_CHILD, *OUTPUT); pipe INPUT_CHILD, OUTPUT; if (my $pid = fork()) { close INPUT_CHILD; close OUTPUT_CHILD; select((select(OUTPUT), $| = 1)[0]); #- internal reading from interactive mode of parsehdlist. #- takes a code to call with the line read, this avoid allocating #- memory for that. my $ask_child = sub { my ($name, $tag, $code) = @_; $code or die "no callback code for parsehdlist output"; print OUTPUT "$name:$tag\n"; local $_; while (<INPUT>) { chomp; /^\s*$/ and last; $code->($_); } }; #- select packages which obseletes other package, obselete package are not removed, #- should we remove them ? this could be dangerous ! foreach my $p (values %{$packages->{names}}) { $ask_child->(packageName($p), "obsoletes", sub { #- take care of flags and version and release if present if ($_[0] =~ /^(\S*)\s*(\S*)\s*([^\s-]*)-?(\S*)/ && c::rpmdbNameTraverse($db, $1) > 0) { $3 and eval(versionCompare(packageVersion($p), $3) . $2 . 0) or next; $4 and eval(versionCompare(packageRelease($p), $4) . $2 . 0) or next; log::l("selecting " . packageName($p) . " by selection on obsoletes"); $obsoletedPackages{$1} = undef; selectPackage($packages, $p); } }); } #- mark all files which are not in /etc/rc.d/ for packages which are already installed but which #- are not in the packages list to upgrade. #- the 'installed' property will make a package unable to be selected, look at select. c::rpmdbTraverse($db, sub { my ($header) = @_; my $otherPackage = (c::headerGetEntry($header, 'release') !~ /mdk\w*$/ && (c::headerGetEntry($header, 'name'). '-' . c::headerGetEntry($header, 'version'). '-' . c::headerGetEntry($header, 'release'))); my $p = $packages->{names}{c::headerGetEntry($header, 'name')}; if ($p) { my $epoch_cmp = c::headerGetEntry($header, 'epoch') <=> packageEpoch($p); my $version_cmp = $epoch_cmp == 0 && versionCompare(c::headerGetEntry($header, 'version'),