#!/usr/bin/perl # http://www.phys.ethz.ch/~franklin/Projects/dphys2/debconf-preload # - preload debconf database, so that base-config does not ask questions # copyright ETH Zuerich Physics Departement, # use under either BSD or GPL license # author Neil Franklin, last modification 2004.09.24 # this script is run at the end of first installation stage, before reboot # and so lets us preload debconf, with fake entries, to silence base-config # this script is run by the command /usr/sbin/chroot /target /debconf-preload # so all paths should be relative to the targets / directory # --- setup some variables for better code legibility $debconfdb = "/var/cache/debconf/config.dat"; # --- save the old database, for referrence, and as base for modifying rename($debconfdb, $debconfdb . ".orig"); # and generate new database, reading old, modifying it, write out # we handle our own filenames, as they are constant, no need for stdin/stout # this keeps all details here in script, not some out in dbootstrap open(DBORIG, "<$debconfdb" . ".orig"); open(DBMOD, ">$debconfdb"); # we want to read entie blank line separated blocks $/ = ""; while () { # blocks have Name: something as their first line /^Name: ([^\n]*)\n/; # --- process to set up, for each of the /usr/lib/base-config/* scripts # these sections are labled after their /usr/lib/base-config/* scripts # - 00dbootstrap_settings - harmless, needed # just set debconf system to only ask critical stuff, minimize questions #if ($1 eq "debconf/priority") { # s@Value: medium@Value: critical@; } # or better, tell it to shut up all together # and then we "just" need to fudge/patch up the data base for our settings # ** this does not work, it asks questions anyway, even "medium" level ones #if ($1 eq "debconf/frontend") { # s@Value: Dialog@Value: Noninteractive@; } # - 10intro - annoying text output, totally useless # is "medium", will be killed anyway by "critical", but be consistent if ($1 eq "base-config/intro") { s@Owners:@Value: \nOwners:@; s@\n\n@\nFlags: seen\n\n@; } # - 12console-tools - harmless, needed # no questions, definitely no changes # bugfix: when upgrading with woody-proposed-updates, this question appears if ($1 eq "console-data/keymap/policy") { if ("%CONF_USE_PROPOSED%" eq "yes") { s@\n\n@\nFlags: seen\n\n@; } } # - 15tzconfig - asks questions, but we need to give time zone info if ($1 eq "tzconfig/gmt") { # insert "Value:" before "Owners:" s@Owners:@Value: true\nOwners:@; # tag these onto the end s@\n\n@\nFlags: seen\n\n@; # use fields 1-6 (not 1-5) here, because day may be 1 digit, 2 spaces # after year there are 2 spaces, so this works, tzsetup does it so also $timestring = `/sbin/hwclock --show | /usr/bin/cut -f 1-6 -d " "`; s@\n\n@\nVariables:\n hwtime = $timestring\n\n@; # /usr/sbin/tzsetup tries everything it can do, to get its dialog up # even faking "seen false" debconf entries, them bastards!!! :-) # so we patch the script to not do this, and then restore itsself # thanks to Debian we need to fix this multiple places, so sub it &delete_script_line("/usr/sbin/tzsetup", "seen false"); } if ($1 eq "tzconfig/change_timezone") { s@\n\n@\nVariables:\n timezone = UTC\n\n@; } if ($1 eq "tzconfig/geographic_area") { s@Owners:@Value: %CONF_TIMEZONE_AREA%\nOwners:@; s@\n\n@\nFlags: seen\n\n@; } if ($1 eq "tzconfig/select_zone") { # add tzconfig/select_zone/%CONF_TIMEZONE_AREA% to our DB # dump the actual tzconfig/select_zone record # as the normal print will do tzconfig/select_zone/%CONF_TIMEZONE_AREA% print DBMOD; # add the entire new block tzconfig/select_zone/%CONF_TIMEZONE_AREA% # will be added to output by the normal print $_ = "Name: tzconfig/select_zone/%CONF_TIMEZONE_AREA%\n" . "Template: tzconfig/select_zone\n" . "Value: %CONF_TIMEZONE_PLACE%\n" . "Owners: \n" . "Flags: seen\n" . "Variables:\n" . " choices = %CONF_TIMEZONE_PLACE%\n\n"; } # - 20passwd - asks questions, but we need to set root password, no make user # there are no pre-existing entries for passwd/* in the debconf DB (!) # so look for last entry before passwd/* (netkit-inetd/inetd-dos-services) if ($1 eq "netkit-inetd/inetd-dos-services") { # force it out and then output all passwd/* until the last one # dump the netkit-inetd/inetd-dos-services before $_ overwrite print DBMOD; # attempts to do this by just fudging data into debconf have failled # even putting data in, and setting "Flags: seen", # and setting priority to critical, and setting noninteractive # the script just keeps on asking for these # unfortunately the settings are ignored somewhere inside debconf # seems that either debconf is broken, or dpkg-reconfigure sabotaging # so in addition to setting we also kill the call to dpkg-reconfigure # and do the desired work it did do ourselves # copying the desited stuff from /var/lib/dpkg/info/passwd.config # fix up system so that dpkg-reconfigure is not run from 20passwd &delete_script_line("/usr/lib/base-config/20passwd", "dpkg-reconfigure"); # set up our passwd/* records # we do not install an user account, so don't do anything $_ = "Name: passwd/make-user\n" . "Template: passwd/make-user\n" . "Value: false\n" . "Owners: passwd\n" . "Flags: seen\n\n"; print DBMOD; # enable MD5 passwords, if wanted $_ = "Name: passwd/md5\n" . "Template: passwd/md5\n" . "Value: %CONF_PASSWORD_MD5%\n" . "Owners: passwd\n" . "Flags: seen\n\n"; print DBMOD; # this seems to be all that actually is done to the system if ("%CONF_PASSWORD_MD5%" eq "yes") { extend_file_line("/etc/pam.d/passwd", "^password", " md5"); extend_file_line("/etc/pam.d/login", "^password", " md5"); } # set our desired root password, debconf with empty Value: lines $_ = "Name: passwd/password-empty\n" . "Template: passwd/password-empty\n" . "Owners: passwd\n\n"; print DBMOD; $_ = "Name: passwd/password-mismatch\n" . "Template: passwd/password-mismatch\n" . "Owners: passwd\n\n"; print DBMOD; $_ = "Name: passwd/root-password\n" . "Template: passwd/root-password\n" . "Value: \n" . "Owners: passwd\n" . "Flags: seen\n\n"; print DBMOD; $_ = "Name: passwd/root-password-again\n" . "Template: passwd/root-password-again\n" . "Value: \n" . "Owners: passwd\n" . "Flags: seen\n\n"; print DBMOD; # and then crypt the actual wanted password into /etc/passwd if ("%CONF_PASSWORD_MD5%" eq "yes") { # user wants MD5 password, so we need to encryt the password ourself # we use the salt algorithm from /var/lib/dpkg/info/passwd.config $salt = "\$1\$"; @base64 = split(//, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz./"); open (URANDOM, " /dev/null"); } # we do not install an user account, so we miss these also $_ = "Name: passwd/user-fullname\n" . "Template: passwd/user-fullname\n" . "Owners: passwd\n\n"; print DBMOD; $_ = "Name: passwd/user-password\n" . "Template: passwd/user-password\n" . "Owners: passwd\n\n"; print DBMOD; $_ = "Name: passwd/user-password-again\n" . "Template: passwd/user-password-again\n" . "Owners: passwd\n\n"; print DBMOD; $_ = "Name: passwd/username\n" . "Template: passwd/username\n" . "Owners: passwd\n\n"; print DBMOD; $_ = "Name: passwd/username-bad\n" . "Template: passwd/username-bad\n" . "Owners: passwd\n\n"; # last will be printed by the normal print at the bottom, no print DBMOD; ; } # - 25pcmcia - asks questions, but we need to give "delete" into the DB if ($1 eq "base-config/remove-pcmcia") { s@Owners:@Value: %CONF_PCMCIA_REMOVE%\nOwners:@; s@\n\n@\nFlags: seen\n\n@; } # - 30pon - asks stuff, is PPP and we are on Ethernet, so useless # is "critical", so even priority can not kill these, needs DB modification if ($1 eq "base-config/use-ppp") { s@Owners:@Value: false\nOwners:@; s@\n\n@\nFlags: seen\n\n@; # /usr/lib/base-config/30pon is annother DB faking "seen false" assh* # repeat the same "patch the script" method as in tzsetup &delete_script_line("/usr/lib/base-config/30pon", "seen false"); } # - 40apt-setup - asks questions, but we need to give details into DB # we want explicit woody, not stable, so no surprise upgrades later on if ($1 eq "apt-setup/distribution") { s@Owners:@Value: woody\nOwners:@; s@\n\n@\nFlags: seen\n\n@; } # setup where everything comes from # we fetch everything by HTTP, as it is universally available if ($1 eq "apt-setup/uri_type") { s@Owners:@Value: http\nOwners:@; s@\n\n@\nFlags: seen\n\n@; s@\n\n@\nVariables:\n note = \n\n@; } # this is most likely useless, but set same as test install if ($1 eq "apt-setup/country") { s@Owners:@Value: enter information manually\nOwners:@; s@\n\n@\nFlags: seen\n\n@; } # our server if ($1 eq "apt-setup/hostname") { my $method, $path; ($method, $path) = split /:/, "%CONF_DEBSERVER%", 2; my $dummy0, $dummy1, $host, $dir; ($dummy0, $dummy1, $host, $dir) = split '/', $path, 4; $host="debian.ethz.ch"; s@Owners:@Value: $host\nOwners:@; s@\n\n@\nFlags: seen\n\n@; } # path where it is on our server if ($1 eq "apt-setup/directory") { my $method, $path; ($method, $path) = split /:/, "%CONF_DEBSERVER%", 2; my $dummy0, $dummy1, $host, $dir; ($dummy0, $dummy1, $host, $dir) = split '/', $path, 4; $dir="mirror/debian"; s@Owners:@Value: /$dir\nOwners:@; s@\n\n@\nFlags: seen\n\n@; } # no proxy as we are on the same network if ($1 eq "apt-setup/http_proxy") { s@Owners:@Value: \nOwners:@; s@\n\n@\nFlags: seen\n\n@; } # setup what our package policies are # do we use contrib stuff here if ($1 eq "apt-setup/contrib") { s@Owners:@Value: %CONF_USE_CONTRIB%\nOwners:@; s@\n\n@\nFlags: seen\n\n@; } # do we use non-free stuff here if ($1 eq "apt-setup/non-free") { s@Owners:@Value: %CONF_USE_NON_FREE%\nOwners:@; s@\n\n@\nFlags: seen\n\n@; } # setup what servers we want to use, apart from the main one # are we outside the United States of Idiotic Politicians? if ($1 eq "apt-setup/non-us") { if ("%CONF_DEBSERVER_NON_US%" ne "" and "%CONF_DEBSERVER_NON_US%" ne "") { s@Owners:@Value: true\nOwners:@; } else { s@Owners:@Value: false\nOwners:@; } s@\n\n@\nFlags: seen\n\n@; } # do we want it secure? if ($1 eq "apt-setup/security-updates") { if ("%CONF_DEBSERVER_SECURITY%" ne "" and "%CONF_DEBSERVER_SECURITY%" ne "") { s@Owners:@Value: true\nOwners:@; } else { s@Owners:@Value: false\nOwners:@; } s@\n\n@\nFlags: seen\n\n@; } # no more apt servers, thats it folks! if ($1 eq "apt-setup/another") { s@Owners:@Value: false\nOwners:@; s@\n\n@\nFlags: seen\n\n@; # and now get the system to actually do what we want # don't run all the dialogs, this is the easiest way, no action &delete_script_line("/usr/lib/base-config/40apt-setup", "apt-setup probe"); # instead generate our desired /etc/apt/sources.list file "manually" # what types of packages we want to have $type = "main"; if ("%CONF_USE_CONTRIB%" eq "yes") { $type .= " contrib"; } if ("%CONF_USE_NON_FREE%" eq "yes") { $type .= " non-free"; } # generate the actual entries open(EASL, "> /etc/apt/sources.list"); # we allways use our install server we got the base install from print EASL "deb %CONF_DEBSERVER% woody $type\n"; print EASL "deb-src %CONF_DEBSERVER% woody $type\n"; # if we want new packages being added to woody, for next dot release if ("%CONF_USE_PROPOSED%" eq "yes") { print EASL "deb %CONF_DEBSERVER% woody-proposed-updates $type\n"; print EASL "deb-src %CONF_DEBSERVER% woody-proposed-updates $type\n"; } # and then add non-US if wanted if ("%CONF_DEBSERVER_NON_US%" ne "") { print EASL "deb %CONF_DEBSERVER_NON_US% woody/non-US $type\n"; print EASL "deb-src %CONF_DEBSERVER_NON_US% woody/non-US $type\n"; # non-us also can have proposed packages if ("%CONF_USE_PROPOSED%" eq "yes") { print EASL "deb %CONF_DEBSERVER_NON_US%" . " woody-proposed-updates/non-US $type\n"; print EASL "deb-src %CONF_DEBSERVER_NON_US%" . " woody-proposed-updates/non-US $type\n"; } } # and then add security if wanted, has no proposed if ("%CONF_DEBSERVER_SECURITY%" ne "") { print EASL "deb %CONF_DEBSERVER_SECURITY% woody/updates $type\n"; print EASL "deb-src %CONF_DEBSERVER_SECURITY% woody/updates $type\n"; } close(EASL); # our just generated host lines get commented out # by the "comment out the shipped sources.list" code in 40apt-setup # so undo this, as we are using our "shipped" sources.list open(ULBC40AS, ">>/usr/lib/base-config/40apt-setup"); print ULBC40AS "/bin/mv /etc/apt/sources.list /\n"; # add an comment about how this /etc/apt/sources.list was made print ULBC40AS "/bin/echo # /etc/apt/sources.list was auto-generated " . "by dphys2 > /etc/apt/sources.list\n"; print ULBC40AS "/bin/echo # this is not an standard Debian woody 3.0 " . "file >> /etc/apt/sources.list\n"; # this strips first char of # by taking all after first # print ULBC40AS "/usr/bin/cut -f 2- -d '#' /sources.list " . ">> /etc/apt/sources.list\n"; print ULBC40AS "/bin/rm /sources.list\n"; # and while this is open, add the lost apt-get update from apt-setup # the || true is needed, so that apt-get with non-accessibe http: # does not end up returning 100 to base-config, endless looping by init # an simple exit 0 after the apt-get fails because script has set -e print ULBC40AS "/usr/bin/apt-get update || /bin/true\n"; # repeat this stuff, as /etc/apt/sources.list was still all commented out # so when they ran, nothing was done, now corrected, so redo # this was Milestone 1 (from 2002.12.18): Known bug #1 print ULBC40AS '$APTCACHE dumpavail > $CACHEDIR/available' . "\n"; print ULBC40AS '$DPKG "$DPKG_OPTS" --update-avail $CACHEDIR/available' . "\n"; print ULBC40AS '/bin/rm -f $CACHEDIR/available' . "\n"; close(ULBC40AS); } # - 50tasksel and 60dselect - interactive installers # we don't use them, we apt-get install to unstall all the stuff we want # anyone else can also call these from the command line if they want them # are both medium, but little work to knock them out anyway if ($1 eq "base-config/run-tasksel") { s@Owners:@Value: false\nOwners:@; s@\n\n@\nFlags: seen\n\n@; # is yet annother DB "seen false" faker, again "patch the script" &delete_script_line("/usr/lib/base-config/50tasksel", "seen false"); } if ($1 eq "base-config/run-dselect") { s@Owners:@Value: false\nOwners:@; s@\n\n@\nFlags: seen\n\n@; # is yet annother DB "seen false" faker, again "patch the script" &delete_script_line("/usr/lib/base-config/60dselect", "seen false"); } # - 75apt-get - asks questions, but we just want the to be ignored # asks 3 non-dialog questions, these are hard coded, not debconf at all(!) # there are no pre-existing or new entries entries in the debconf DB # so look for last entry in apt-setup (apt-setup/uri_type) if ($1 eq "apt-setup/uri_type") { # we do all this inline, as we need an special "dual line replace" edit local($inst, $instline); $inst = "/usr/lib/dpkg/methods/apt/install"; # save old install for reactivating later rename($inst, $inst . ".orig"); open(INSTORIG, "<$inst" . ".orig"); open(INSTMOD, ">$inst"); # replace lines with $reppattern, only lines and not blocks, so re-set $/ $/ = "\n"; while ($instline = ) { # force erasing of .debs, don't bother asking if ($instline =~ /Do you want to erase any previously downloaded/) { $instline = "if [ /bin/true ]; then\n"; } # don't ask stupid "enter" confirmation, as no answer is taken anyway # as this exits, put in command to reactivate unchanged install here if ($instline =~ /Press enter to continue/) { $instline = "/bin/mv " . $inst . ".orig " . $inst . " && exit 0;\n"; } # stop apt-get dselect-upgrade asking for confirmations # for this add the -y option to all apt-get calls #if ($instline =~ /^OPTS/) { # $instline = 'OPTS="-f -y"'; } # but above does not work, it answers the "purge pcmcia-cs" with no # this because pcmcia-cs is marked Priority: extra # so instead pipe yes into apt-get dselect-upgrade # I *hate* too "intelligent" programs, that screw up like this argh!!! if ($instline =~ /dselect-upgrade/) { # run yes with nice, else uses up all CPU und apt-gat gets really slow $instline = "/usr/bin/nice /usr/bin/yes '' | " . $instline; } print INSTMOD $instline; } $/ = ""; close(INSTORIG); close(INSTMOD); # and make our replacement install executable chmod(0755, $inst); } # get rid of new man-db question, from updated (security or proposed-updates) # old man.db ist fom base install, first stage, no questions asked # now with update its postinst asks, if not "medium" set by endfirstrun if ($1 eq "man-db/install-setuid") { s@Owners:@Value: true\nOwners:@; s@\n\n@\nFlags: seen\n\n@; } # - 77exim - unused mailer, we later install our preferred mailer # does not use debconf, will need its own personal invitation to shut up # best is to make /usr/sbin/eximconfig non executable, 77exim jumps it then # was bad idea, apt-get dselect-upgrade with new package overwrites this # so better fake an /etc/exim/exim.conf file, suggesting job done # needs to be non empty, else it gets removed and config runs again # as exim has no debconf database records, trigger it off of an unrelated one # I have chosen one (install-problem) that is somehow appropriate :-) if ($1 eq "base-config/install-problem") { open(EXIMCONF, ">/etc/exim/exim.conf"); print EXIMCONF "exim.conf ignored, we use different mailer\n"; close(EXIMCONF); } # - 80poff - asks stuff, tidy up from 30pon stuff # this seems to not get asked anyway, if 30pon was negative # - 90final-messages - annoying text output, totally useless # is "medium", will be killed anyway by "critical", but be consistent if ($1 eq "base-config/login-with-tty") { s@Owners:@Value: \nOwners:@; s@\n\n@\nFlags: seen\n\n@; } # - 98s390 - harmless, but actually irrelevant # no questions, definitely no changes # - 99inittab - harmless, needed # no questions, definitely no changes # - and output the block, modified or not print DBMOD; } # and tidy up files close(DBORIG); close(DBMOD); # patch an shell script, deleting any line containing $delpattern sub delete_script_line { local($script, $delpattern) = @_; local($scriptline); # save old script for reactivating later rename($script, $script . ".orig"); # ensure that these are not runnable, else /usr/sbin/base-config screws us up chmod(0644, $script . ".orig"); open(SCRIPTORIG, "<$script" . ".orig"); open(SCRIPTMOD, ">$script"); # kill lines with $delpattern, only lines and not blocks, so re-set $/ $/ = "\n"; while ($scriptline = ) { unless ($scriptline =~ /$delpattern/) { print SCRIPTMOD $scriptline; } } $/ = ""; # add tail command to reactivate unchanged script after running changed one print SCRIPTMOD "/bin/mv " . $script . ".orig " . $script . "\n"; # and don't forget to make the reactivated script runnable again print SCRIPTMOD "/bin/chmod 755 " . $script . "\n"; close(SCRIPTORIG); close(SCRIPTMOD); # and make our replacement script executable chmod(0755, $script); } # extend line in an config file, modifying any line containing $modpattern sub extend_file_line { local($file, $modpattern, $extension) = @_; local($fileline); # save old file for archival rename($file, $file . ".orig"); open(FILEORIG, "<$file" . ".orig"); open(FILEMOD, ">$file"); # extend lines with $modpattern, only lines and not blocks, so re-set $/ $/ = "\n"; while ($fileline = ) { $fileline =~ s/^(.*$modpattern.*)$/$1$extension/; print FILEMOD $fileline; } $/ = ""; close(FILEORIG); close(FILEMOD); }