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.
tdeadmin/lilo-config/common/lilo.cpp

586 lines
16 KiB

/* lilo.cpp
**
** 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.disklist();
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;
}