summaryrefslogtreecommitdiffstats
path: root/lilo-config/common/lilo.cc
diff options
context:
space:
mode:
Diffstat (limited to 'lilo-config/common/lilo.cc')
-rw-r--r--lilo-config/common/lilo.cc585
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;
+}