You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
586 lines
16 KiB
586 lines
16 KiB
/* lilo.cc
|
|
**
|
|
** Copyright (C) 2000,2001 by Bernhard Rosenkraenzer
|
|
**
|
|
** Contributions by A. Seigo and W. Bastian.
|
|
**
|
|
*/
|
|
|
|
/*
|
|
** This program is free software; you can redistribute it and/or modify
|
|
** it under the terms of the GNU General Public License as published by
|
|
** the Free Software Foundation; either version 2 of the License, or
|
|
** (at your option) any later version.
|
|
**
|
|
** This program is distributed in the hope that it will be useful,
|
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
** GNU General Public License for more details.
|
|
**
|
|
** You should have received a copy of the GNU General Public License
|
|
** along with this program in a file called COPYING; if not, write to
|
|
** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
** MA 02110-1301, USA.
|
|
*/
|
|
|
|
/*
|
|
** Bug reports and questions can be sent to kde-devel@kde.org
|
|
*/
|
|
#include "lilo.h"
|
|
#include "Disks.h"
|
|
#include "Files.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <sys/mount.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <fstream>
|
|
#include <string.h>
|
|
|
|
using namespace std;
|
|
|
|
bool liloimage::isLinux() const {
|
|
const_iterator it=begin();
|
|
if((*it).contains("image"))
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
liloimage *liloimages::find(String const &s) const
|
|
{
|
|
String t = String::escapeForRegExp(s);
|
|
|
|
String regex="^[ \t]*label[ \t]*=[ \t]*\"?" + t + "\"?[ \t]*";
|
|
for(const_iterator it=begin(); it!=end(); it++) {
|
|
if(!(*it).grep(regex).empty())
|
|
return (liloimage*)&(*it);
|
|
}
|
|
return 0;
|
|
}
|
|
void liloimages::remove(String const &s)
|
|
{
|
|
liloimage *i=find(s);
|
|
for(iterator it=begin(); it!=end(); it++)
|
|
if(*it==*i) {
|
|
erase(it);
|
|
break;
|
|
}
|
|
}
|
|
|
|
liloconf::liloconf(String const &filename)
|
|
{
|
|
checked=false;
|
|
defaults.clear();
|
|
images.clear();
|
|
if(filename.empty()) {
|
|
probe();
|
|
} else {
|
|
StringList s;
|
|
if(s.readfile(filename))
|
|
set(s);
|
|
else
|
|
probe();
|
|
}
|
|
}
|
|
void liloconf::set(StringList const &s)
|
|
{
|
|
defaults.clear();
|
|
images.clear();
|
|
checked=false;
|
|
bool inHeader=true;
|
|
bool start;
|
|
liloimage *image=0;
|
|
for(StringList::const_iterator it=s.begin(); it!=s.end(); it++) {
|
|
String s=*it;
|
|
start=false;
|
|
s=s.simplifyWhiteSpace();
|
|
if(s.empty())
|
|
continue;
|
|
if(s.left(5)==(String)"other" && (s.mid(6, 1)==' ' || s.mid(6, 1)=='=')) {
|
|
inHeader=false;
|
|
start=true;
|
|
}
|
|
if(s.left(5)==(String)"image" && (s.mid(6, 1)==' ' || s.mid(6, 1)=='=')) {
|
|
inHeader=false;
|
|
start=true;
|
|
}
|
|
if(inHeader) {
|
|
defaults+=*it;
|
|
} else if(start) {
|
|
if(image)
|
|
images.insert(images.end(), *image);
|
|
image=new liloimage;
|
|
*image += *it;
|
|
} else {
|
|
*image += *it;
|
|
}
|
|
}
|
|
if(image)
|
|
images.insert(images.end(), *image);
|
|
}
|
|
void liloconf::set(String const &s)
|
|
{
|
|
set((StringList)s);
|
|
}
|
|
void liloconf::writeFile(String const &filename)
|
|
{
|
|
ofstream f;
|
|
f.open(filename, ios::out);
|
|
f << *this << endl;
|
|
f.close();
|
|
chmod(filename, 0600);
|
|
}
|
|
bool liloconf::install(bool probeonly)
|
|
{
|
|
char *lilotmp=strdup("/tmp/liloXXXXXX");
|
|
String command;
|
|
int fd=mkstemp(lilotmp);
|
|
// Unfortunately, gcc 3.1 and higher don't have ofstream::attach
|
|
// anymore. Pity, used to be immensely useful.
|
|
// f.attach(fd);
|
|
close(fd);
|
|
ofstream f(lilotmp, ios::out);
|
|
f << defaults << endl;
|
|
for(liloimages::iterator it=images.begin(); it!=images.end(); it++) {
|
|
f << *it << endl;
|
|
}
|
|
f.close();
|
|
|
|
if(probeonly)
|
|
command.sprintf("/sbin/lilo -v -t -C %s 2>&1", (char const * const)lilotmp);
|
|
else
|
|
command.sprintf("/sbin/lilo -v -C %s 2>&1", (char const * const)lilotmp);
|
|
output="";
|
|
FILE *lilo=popen(command, "r");
|
|
char *buf=(char *) malloc(1024);
|
|
while(fgets(buf, 1024, lilo))
|
|
output += buf;
|
|
free(buf);
|
|
ok=(pclose(lilo)==0);
|
|
unlink(lilotmp);
|
|
free(lilotmp);
|
|
checked=true;
|
|
return ok;
|
|
}
|
|
bool const liloconf::isOk()
|
|
{
|
|
if(!checked)
|
|
check();
|
|
return ok;
|
|
}
|
|
String const liloconf::liloOut()
|
|
{
|
|
if(!checked)
|
|
check();
|
|
return output;
|
|
}
|
|
bool liloconf::probe()
|
|
{
|
|
ptable p;
|
|
StringList drives=p.distdelist();
|
|
String const root=p.device("/", true);
|
|
checked=false;
|
|
defaults.clear();
|
|
images.clear();
|
|
/* Set some reasonable defaults... */
|
|
// Try to figure out the boot device first...
|
|
if(drives.contains("/dev/hda")) {
|
|
defaults += "boot=/dev/hda"; // 1st IDE/ATAPI harddisk
|
|
defaults += "lba32"; // otherwise it is assumed
|
|
}
|
|
else if(drives.contains("/dev/sda")) {
|
|
defaults += "boot=/dev/sda"; // 1st SCSI harddisk
|
|
defaults += "linear"; // some SCSI disks need this
|
|
} else if(drives.contains("/dev/i2o/hda"))
|
|
defaults += "boot=/dev/i2o/hda"; // 1st I2O harddisk
|
|
else if(drives.contains("/dev/eda"))
|
|
defaults += "boot=/dev/eda"; // 1st MCA ESDI harddisk
|
|
else if(drives.contains("/dev/pda"))
|
|
defaults += "boot=/dev/pda"; // 1st Parallel port IDE disk
|
|
else
|
|
defaults += "boot=Insert_your_boot_device_here"; // shouldn't happen
|
|
defaults += "prompt";
|
|
defaults += "timeout=50";
|
|
if(!access("/boot/message", F_OK))
|
|
defaults += "message=/boot/message";
|
|
defaults += "root=" + root;
|
|
|
|
/* Scan for available operating systems...
|
|
* The list of what to do for each partition type is based on my
|
|
* best guess. I don't have anything but Linux (Red Hat Linux 7.0),
|
|
* FreeBSD (5.0-CURRENT), OpenBSD (2.6), FreeDOS (some CVS snapshot)
|
|
* and DR-DOS (7.03), so anything else might be wrong.
|
|
* If you have any additions or corrections, please send them to
|
|
* bero@redhat.com.
|
|
*/
|
|
// Scan for Linux kernels in the currently running system
|
|
// The following may or may not be specific to Red Hat Linux and
|
|
// similar distributions... If you're using a distribution that does
|
|
// things differently, tell me how it should be done there.
|
|
StringList files=Files::glob("/boot/*", Files::File);
|
|
for(StringList::iterator it=files.begin(); it!=files.end(); it++) {
|
|
struct stat s;
|
|
if(lstat(*it, &s)) // If we can't stat it, it can't be a kernel
|
|
continue;
|
|
if(s.st_size<131072) // if you managed to compile a kernel at less than 128k, you're cheating. ;)
|
|
continue;
|
|
if((*it).contains("System.map") || (*it).contains("initrd")) // definitely not a kernel
|
|
continue;
|
|
if((*it).contains("vmlinux")) {
|
|
// If the kernel exists both in compressed and in
|
|
// uncompressed form, ignore the uncompressed one.
|
|
String compr=(*it).replace("vmlinux", "vmlinuz");
|
|
if(!access(compr, F_OK))
|
|
continue;
|
|
}
|
|
String version=(*it).regex("(test-?|pre-?)?([0-9]\\.[0-9]\\.[0-9]+)(-?[0-9A-Za-z]+)*");
|
|
String label=version;
|
|
if(version.empty()) // not a recognized kernel
|
|
version="linux";
|
|
|
|
if (label.empty())
|
|
{
|
|
label = (*it);
|
|
if (label.find('/') != string::npos)
|
|
label = label.right(label.length()-1-label.rfind('/'));
|
|
}
|
|
// Check if we have an initial ramdisk (initrd) for this kernel...
|
|
String initrd1; // first guess
|
|
String initrd2; // second guess
|
|
if((*it).contains("vmlinuz")) {
|
|
initrd1=(*it).replace("vmlinux", "initrd")+".img";
|
|
initrd2=(*it).replace("vmlinuz", "initrd.img");
|
|
}
|
|
else if((*it).contains("vmlinux")) {
|
|
initrd1=(*it).replace("vmlinux", "initrd")+".img";
|
|
initrd2=(*it).replace("vmlinuz", "initrd.img");
|
|
}
|
|
else if((*it).contains("kernel")) {
|
|
initrd1=(*it).replace("kernel", "initrd")+".img";
|
|
initrd2=(*it).replace("vmlinuz", "initrd.img");
|
|
}
|
|
else if((*it).contains("linux")) {
|
|
initrd1=(*it).replace("linux", "initrd")+".img";
|
|
initrd2=(*it).replace("vmlinuz", "initrd.img");
|
|
}
|
|
else {
|
|
initrd1="/boot/initrd-"+version+".img";
|
|
initrd2="/boot/initrd.img-"+version;
|
|
}
|
|
|
|
String initrd = "";
|
|
if(!access(initrd1, F_OK))
|
|
initrd = initrd1;
|
|
else if(!access(initrd2, F_OK))
|
|
initrd = initrd2;
|
|
|
|
if(label.size()>15) // LILO can't handle this
|
|
if(label.contains("enterprise"))
|
|
label=label.replace("enterprise", "E");
|
|
if(label.size()>15)
|
|
label=label.left(15);
|
|
|
|
// label, kernel, root, initrd, optional, append, vga, readonly,
|
|
// literal, ramdisk
|
|
addLinux(label, *it, root, initrd);
|
|
}
|
|
addLinux("Linux_Compiled", "/usr/src/linux/arch/i386/boot/bzImage", root, "", true); // User-compiled kernel that wasn't moved...
|
|
|
|
// Scan for other OSes... and Linux kernels on other partitions.
|
|
for(StringList::iterator it=p.partition.begin(); it!=p.partition.end(); it++) {
|
|
switch(p.id[*it]) {
|
|
case 0x01: // FAT12... Might be some really really old DOS.
|
|
case 0x04: // FAT16 < 32 M... Probably another old DOS.
|
|
case 0x06: // FAT16
|
|
case 0x0b: // FAT32
|
|
case 0x0c: // FAT32 (LBA)
|
|
case 0x0e: // FAT16 (LBA)
|
|
case 0x14: // Hidden FAT16 < 32 M...
|
|
case 0x16: // Hidden FAT16
|
|
case 0x1b: // Hidden FAT32
|
|
case 0x1c: // Hidden FAT32 (LBA)
|
|
case 0x1e: // Hidden FAT16 (LBA)
|
|
case 0x24: // NEC DOS
|
|
case 0x55: // EZ-Drive... I think this was some DOS tool
|
|
// to see "large" disks ages ago, so it may be
|
|
// a DOS partition... Not sure about this one.
|
|
case 0xc1: // DRDOS/sec
|
|
case 0xc4: // DRDOS/sec
|
|
case 0xc6: // DRDOS/sec
|
|
{
|
|
// Try to determine which type of DOS we're using
|
|
String mp=p.mountpt[*it];
|
|
String lbl="DOS";
|
|
if(mp.empty()) {
|
|
char *tmp=tmpnam(NULL);
|
|
tmp=tmpnam(NULL);
|
|
mkdir(tmp, 0700);
|
|
if(!mount(*it, tmp, "msdos", MS_MGC_VAL|MS_RDONLY|MS_NOSUID|MS_NODEV|MS_NOEXEC, NULL))
|
|
mp=tmp;
|
|
else if(!mount(*it, mp, "vfat", MS_MGC_VAL|MS_RDONLY|MS_NOSUID|MS_NODEV|MS_NOEXEC, NULL))
|
|
mp=tmp;
|
|
}
|
|
if(!mp.empty()) {
|
|
struct stat s;
|
|
if(stat(mp+"/ibmbio.com", &s) && stat(mp+"/io.sys", &s) && stat(mp+"/ntldr", &s)) // Doesn't look like a bootable DOS partition, ignore it
|
|
break;
|
|
if(!stat(mp+"/drdos.386", &s))
|
|
lbl="DR-DOS";
|
|
else if(!stat(mp+"/ntldr", &s))
|
|
lbl="NT";
|
|
else if(!stat(mp+"/msdos.sys", &s)) {
|
|
if(s.st_size < 4096)
|
|
/* msdos.sys is actual code in DOS, it's a config file in SuckOS */
|
|
if(!stat(mp+"/windows/system/sfc.exe", &s)) /* This is (supposed to be?) a component of Suck98 but not Suck95 */
|
|
lbl="Windows98";
|
|
else
|
|
lbl="Windows95";
|
|
}
|
|
if(p.mountpt[*it].empty()) {
|
|
umount(mp);
|
|
sync();
|
|
rmdir(mp);
|
|
}
|
|
}
|
|
addOther(lbl, *it);
|
|
break;
|
|
}
|
|
case 0x02: // Xenix root... Does Xenix actually boot this way?
|
|
addOther("Xenix", *it);
|
|
break;
|
|
case 0x07: // HPFS or NTFS... Is there any way to check which?
|
|
// (without trying to mount them... many kernels
|
|
// support neither)
|
|
case 0x17: // Hidden HPFS or NTFS
|
|
addOther("NT", *it);
|
|
break;
|
|
case 0x09: // AIX
|
|
addOther("AIX", *it);
|
|
break;
|
|
case 0x83: // My favorite :)
|
|
case 0xfd: // Linux RAID
|
|
// TODO: scan other FSes for kernels...
|
|
break;
|
|
case 0x84:
|
|
{
|
|
// CLASH: SystemSoft MobilePRO BIOS and
|
|
// various Dell BIOSes use the same
|
|
// ID (0x84) for its hibernation partitions
|
|
// as OS/2 does for hidden C: drives.
|
|
// Fortunately, hibernation partitions are easy to
|
|
// recognize...
|
|
int fd=open(*it, O_RDONLY);
|
|
char *header=(char *) malloc(20);
|
|
read(fd, header, 20);
|
|
close(fd);
|
|
if(strncmp(header, "SystemSoft", 10)==0) // It's a hibernation partition
|
|
break;
|
|
else if(strncmp(header+3, "Dell Suspend", 12)==0) { // It's a Dell hibernation partition
|
|
// Dell BIOSes are ultimately buggy: They don't do resume-from-disk
|
|
// properly.
|
|
// Hibernation partitions are bootable and need to be loaded by the
|
|
// boot manager instead.
|
|
addOther("SuspendToDisk", *it);
|
|
break;
|
|
}
|
|
addOther("OS2", *it, false, "/boot/os2_d.b");
|
|
}
|
|
case 0x0a: // OS/2 Boot Manager
|
|
addOther("OS2", *it, false, "/boot/os2_d.b");
|
|
break;
|
|
case 0x10: // OPUS... (what is that?)
|
|
addOther("OPUS", *it);
|
|
break;
|
|
case 0x3c: // Partition Magic
|
|
addOther("PartitionMagic", *it);
|
|
break;
|
|
case 0x40: // Venix 80286
|
|
addOther("Venix", *it);
|
|
break;
|
|
case 0x4d: // TQNX
|
|
addOther("QNX", *it);
|
|
break;
|
|
case 0x52: // CP/M (does anyone dual-boot between CP/M and
|
|
// Linux? Would be interesting to see. ;) )
|
|
case 0xdb: // CP/M/CTOS
|
|
addOther("CPM", *it);
|
|
break;
|
|
case 0x63: // GNU/Hurd
|
|
addOther("GNU_Hurd", *it);
|
|
break;
|
|
case 0x64: // Novell Netware
|
|
case 0x65: // Novell Netware
|
|
addOther("Netware", *it);
|
|
break;
|
|
case 0x75: // PC/IX (what is that?)
|
|
addOther("PCIX", *it);
|
|
break;
|
|
case 0x80: // Old Minix
|
|
case 0x81: // Minix and some VERY old Linux kernels
|
|
addOther("Minix", *it);
|
|
break;
|
|
case 0x9f: // BSD/OS
|
|
case 0xb7: // BSDI
|
|
addOther("BSD_OS", *it);
|
|
break;
|
|
case 0xa5: // Some BSDs... Is there any way to determine which
|
|
// one?
|
|
addOther("BSD", *it);
|
|
break;
|
|
case 0xa6: // OpenBSD
|
|
addOther("OpenBSD", *it);
|
|
break;
|
|
case 0xa7: // NeXTSTEP
|
|
addOther("NeXT", *it);
|
|
break;
|
|
case 0xc7: // Syrinx (what is that?)
|
|
addOther("Syrinx", *it);
|
|
break;
|
|
case 0xeb: // BeOS
|
|
addOther("BeOS", *it);
|
|
break;
|
|
case 0xfe: // LANstep (what is that?)
|
|
addOther("LANstep", *it);
|
|
break;
|
|
case 0xff: // BBT (what is that?)
|
|
addOther("BBT", *it);
|
|
break;
|
|
}
|
|
}
|
|
// addOther("floppy", "/dev/fd0", true);
|
|
//Would be nice, but LILO can't handle an optional
|
|
//other=nonexistantdevice entry ATM.
|
|
return true;
|
|
}
|
|
void liloconf::addLinux(String const &label, String const &kernel, String const &root, String const &initrd, bool optional, String const &append, String const &vga, bool readonly, String const &literal, String const &ramdisk)
|
|
{
|
|
liloimage *lx_image=new liloimage;
|
|
*lx_image += "image="+kernel;
|
|
*lx_image += "\tlabel=\""+label+"\"";
|
|
if(!root.empty())
|
|
*lx_image += "\troot="+root;
|
|
if(readonly)
|
|
*lx_image += "\tread-only";
|
|
else
|
|
*lx_image += "\tread-write";
|
|
if(!initrd.empty())
|
|
*lx_image += "\tinitrd=\""+initrd+"\"";
|
|
if(!append.empty())
|
|
*lx_image += "\tappend=\""+append+"\"";
|
|
if(!vga.empty())
|
|
*lx_image += "\tvga=\""+vga+"\"";
|
|
if(!literal.empty())
|
|
*lx_image += "\tliteral=\""+literal+"\"";
|
|
if(!ramdisk.empty())
|
|
*lx_image += "\tramdisk=\""+ramdisk+"\"";
|
|
if(optional)
|
|
*lx_image += "\toptional";
|
|
images.insert(images.end(), *lx_image);
|
|
}
|
|
void liloconf::addOther(String const &name, String const &partition, bool const &optional, String const &chain)
|
|
{
|
|
liloimage *other=new liloimage;
|
|
*other += "other="+partition;
|
|
*other += "\tlabel=\""+name+"\"";
|
|
if(optional)
|
|
*other += "\toptional";
|
|
if(!chain.empty())
|
|
*other += "\tloader="+chain+"\"";
|
|
images.insert(images.end(), *other);
|
|
}
|
|
void liloconf::remove(String const &label)
|
|
{
|
|
String t = String::escapeForRegExp(label);
|
|
|
|
String regex="[ \t]*label[ \t]*=[ \t]*\"?"+t+"\"?[ \t]*";
|
|
for(liloimages::iterator it=images.begin(); it!=images.end(); it++) {
|
|
if(!(*it).grep(regex).empty()) {
|
|
images.erase(it);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
void liloconf::removeKernel(String const &kernel)
|
|
{
|
|
String t = String::escapeForRegExp(kernel);
|
|
|
|
String regex="[ \t]*(image|other)[ \t]*=[ \t]*\"?"+t+"\"?[ \t]*";
|
|
for(liloimages::iterator it=images.begin(); it!=images.end(); it++) {
|
|
if(!(*it).grep(regex).empty()) {
|
|
images.erase(it);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
StringList const &liloconf::entries() const
|
|
{
|
|
StringList *s=new StringList;
|
|
for(liloimages::const_iterator it=images.begin(); it!=images.end(); it++) {
|
|
String lbl=(*it).grep("[ \t]*label[ \t]*=.*");
|
|
lbl=lbl.mid(lbl.locate("label")+6);
|
|
while(isspace(lbl.chr(0)) || lbl.chr(0)=='=' || lbl.chr(0)=='\"')
|
|
lbl=lbl.mid(2);
|
|
while(isspace(lbl.right().chr(0)) || lbl.right()==(String)"\"")
|
|
lbl=lbl.left(lbl.size()-1);
|
|
s->add(lbl);
|
|
}
|
|
return *s;
|
|
}
|
|
String const liloconf::dflt() const
|
|
{
|
|
String dflt="";
|
|
for(StringList::const_iterator it=defaults.begin(); it!=defaults.end() && dflt.empty(); it++)
|
|
if(!((*it).regex("^[ \t]*default[ \t]*=")).empty())
|
|
dflt=(*it).simplifyWhiteSpace();
|
|
if(dflt.empty()) {
|
|
liloimages::const_iterator it=images.begin();
|
|
if (it != images.end())
|
|
dflt=(*it).grep("^[ \t]*label[ \t]*=").simplifyWhiteSpace();
|
|
}
|
|
if(!dflt.empty()) {
|
|
dflt=dflt.mid(dflt.locate("=")+2).simplifyWhiteSpace();
|
|
if(dflt.left()==(String)"\"")
|
|
dflt=dflt.mid(2).simplifyWhiteSpace();
|
|
if(dflt.right()==(String)"\"")
|
|
dflt=dflt.left(dflt.size()-1).simplifyWhiteSpace();
|
|
}
|
|
return dflt;
|
|
}
|
|
void liloconf::setDefault(String const &dflt)
|
|
{
|
|
bool ready=false;
|
|
for(StringList::const_iterator it=defaults.begin(); !ready && it!=defaults.end(); it++)
|
|
if(!((*it).regex("^[ \t]*default[ \t]*=")).empty()) {
|
|
defaults.remove(*it);
|
|
ready=true;
|
|
}
|
|
defaults+="default=" + dflt;
|
|
}
|
|
liloconf::operator String() const
|
|
{
|
|
String s=defaults;
|
|
s+="\n";
|
|
for(liloimages::const_iterator it=images.begin(); it!=images.end(); it++) {
|
|
s+=*it;
|
|
s+="\n";
|
|
}
|
|
return s;
|
|
}
|
|
|
|
ostream &operator <<(ostream &os, liloconf const &l)
|
|
{
|
|
os << l.defaults << endl;
|
|
for(liloimages::const_iterator it=l.images.begin(); it!=l.images.end(); it++)
|
|
os << *it << endl;
|
|
return os;
|
|
}
|
|
ostream &operator <<(ostream &os, liloconf const *l)
|
|
{
|
|
os << l->defaults << endl;
|
|
for(liloimages::const_iterator it=l->images.begin(); it!=l->images.end(); it++)
|
|
os << *it << endl;
|
|
return os;
|
|
}
|