diff options
Diffstat (limited to 'lilo-config/common/lilo.cc')
-rw-r--r-- | lilo-config/common/lilo.cc | 585 |
1 files changed, 585 insertions, 0 deletions
diff --git a/lilo-config/common/lilo.cc b/lilo-config/common/lilo.cc new file mode 100644 index 0000000..6625f0d --- /dev/null +++ b/lilo-config/common/lilo.cc @@ -0,0 +1,585 @@ +/* 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.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: // QNX + 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; +} |