Main menu:

Site search

March 2010
S M T W T F S
« Dec    
 123456
78910111213
14151617181920
21222324252627
28293031  

Categories

Tags

Diskless Slackware Installation using NFS and PXE

Project: Run Slackware on a diskless machine using TFTP, NFS and PXE to boot over the network.

This project requires two computers. The first will be called SERVER and the second will be CLIENT. At the conclusion of the project SERVER will have an installation of Slackware that CLIENT boots over the network. The IP address for SERVER is 192.168.3.1 and CLIENT is 192.168.3.2

SERVER Configuration

The first step is to make a fresh install of Slackware in a new directory. Obtain the Slackware installation ISOs for the version you would like to install. The examples use the Slackware 12.0 CD sets but the instructions could easily be adapted to any version or the installation DVD.

Don’t bother burning the ISOs to disc. Create a directory in the same folder where the ISOs are and mount the first one using loopback.

root@SERVER:~/slackware-12.0# ls
slackware-12.0-install-d1.iso  slackware-12.0-install-d3.iso
slackware-12.0-install-d2.iso
root@SERVER:~/slackware-12.0# mkdir tmp
root@SERVER:~/slackware-12.0# mount -o loop \
> slackware-12.0-install-d1.iso tmp

Create the directory /nfsroot for the the new installation. This can easily be moved to a new or sub directory later if desired. Set the environment variable ROOT to this directory so when the packages are installed in later steps they will install to /nfsroot.

root@SERVER:~/slackware-12.0# mkdir /nfsroot
root@SERVER:~/slackware-12.0# export ROOT=/nfsroot

The next step is to perform the actual installation. For each disk set to be installed change to the directory containing the set and run the install-packages script. Make sure you have set the ROOT environment variable or you will be installing packages into your current installation.

root@SERVER:~/slackware-12.0# env | grep ROOT
ROOT=/nfsroot
root@SERVER:~/slackware-12.0#  cd tmp/slackware/a
root@SERVER:~/slackware-12.0# ./install-packages

Select the packages you want installed and Select OK. The packages will be installed to /nfsroot. Change to the other disk set directories to install and run the install-packages script to continue installation. I recommend at least A and L disk sets to get a usable system.

The only file that needs to be created in the new installation is fstab. The Slackware setup script creates an fstab during a normal install. This would be a good time to add entries for the CDROM and floppy on CLIENT. Edit the file and add the line to mount the NFS export on /.

192.168.3.1:/nfsroot  /                nfs      noauto             0   0
/dev/cdrom              /mnt/cdrom  auto    noauto,owner,ro 0   0
/dev/fd0                  /mnt/floppy  auto    noauto,owner    0   0
devpts                    /dev/pts      devpts gid=5,mode=620 0   0
proc                       /proc           proc    defaults            0   0
tmpfs                     /dev/shm      tmpfs   defaults            0   0

There are a few more configuration changes that need to be made on SERVER. It must be running tftpd, serving DHCP requests, and have the ability to export directories using NFS. Slackware provides all of these features, they just need to be configured and enabled.

tftp Server
The tftp server can be run from inetd or as a daemon. I prefer to use inetd so open /etc/inetd.conf and find the line for tftp. Remove the comment indicator (#) from the beginning of the line. Note the directory /tftpboot from which the Linux kernel and PXE configuration files will be served. Restart inetd after making the changes.

tftp  dgram   udp     wait    root    /usr/sbin/in.tftpd  in.tftpd -s /tftpboot -r blksize
root@SERVER:~# /etc/rc.d/rc.inetd restart

NFS
Now the /nfsroot directory needs to be exported via NFS. Edit the /etc/exports file and add a new line for the export. After exports has been modified run exportfs.

/nfsroot 192.168.3.2{rw,no_root_squash,no_all_squash,no_subtree_check}

root@SERVER:~# exportfs -av
exporting 192.168.3.2:/nfsroot

DHCP
The dhcp server must be updated to tell CLIENT about the tftp server. A number of lines need to be added to /etc/dhcpd.conf. They are the DNS server IP, network gateway IP and the specific options for CLIENT. If you don’t know the MAC address for CLIENT you can get it from a later step. Come back and modify dhcpd.conf then restart dhcpd.

# dhcpd.conf
#
# Configuration file for ISC dhcpd (see 'man dhcpd.conf')
#
option domain-name "example.com";
option domain-name-servers 192.168.3.1;
option routers 192.168.3.1;

subnet 192.168.3.0 netmask 255.255.255.0 {
        server-name "SERVER";

        host CLIENT {
# hardware ethernet is the MAC address of CLIENT's NIC
                hardware ethernet 00:40:63:C0:AB:XX;
                fixed-address 192.168.3.2;
                next-server 192.168.3.1;
                filename "pxelinux.0";
                option root-path "/nfsroot";
        }
}

Restart dhcpd

root@SERVER:~# /etc/rc.d/rc.dhcpd restart

PXE
The last step in configuring SERVER is setting up the PXE environment. Create the /tftpboot and /tftpboot/pxelinux.cfg directories

root@SERVER:~# mkdir -p /tftpboot/pxelinux.cfg


Copy the contents of the boot directory of the new Slackware install. Create a softlink from the kernel you want to use to bzImage.

root@SERVER:~# cp -a /nfsroot/boot/* /tftpboot
root@SERVER:~# cd /tftpboot
root@SERVER:/tftpboot# ln -s vmlinuz-huge-2.6.21.5 bzImage

Copy pxelinux.0 from the current file system to /tftpboot

root@SERVER:~# cp /usr/lib/syslinux/pxelinux.0 /tftpboot

Create a PXE config file in the pxelinux.cfg directory for CLIENT. The name of the config file is the IP of CLIENT in hexadecimal format. You can use gethostip to calculate the value but it will be C0A80302 for 192.168.3.2.

root@SERVER:/tftpboot/pxelinux.cfg# gethostip 192.168.3.2
192.168.3.2 192.168.3.2 C0A80302
root@SERVER:/tftpboot/pxelinux.cfg# touch C0A80302

Edit the config file. (The APPEND line includes ip=…, it shouldn’t be on a separate line it just looks like it here)

# C0A80302
DEFAULT linux
LABEL linux
SAY Now booting the kernel from SYSLINUX ... Good Luck! ...
KERNEL bzImage
APPEND ro root=/dev/nfs nfsroot=192.168.3.1:/nfsroot ip=192.168.3.2:192.168.3.1:192.168.3.1:255.255.255.0

CLIENT configuration
There isn’t much to do on CLIENT. Boot it up and go into the BIOS configuration. Find the option to boot from the network and move it to the top of the list. Save the BIOS settings (F10) and reboot. When CLIENT is booting it should display the MAC address. If you didn’t know it when configuring dhcpd it’s time to go back and update /etc/dhcpd.conf.

After booting CLIENT run the scripts that are usually installed at the end of a normal Slackware setup. Run pkgtool and select Setup. Select the scripts to run and select OK. Do not select liloconfig since PXE is the bootloader, not lilo.

There are a lot of ways to optimize this setup. This is merely an example to get you started. If you are running the same version of Slackware on SERVER and CLIENT then exporting some directories such as /bin and /lib over NFS and then mounting them on CLIENT could help save some disk space.

MSBuild Logger in Delphi .NET

Project: Create a custom XML logger dll for use with MSBuild using Delphi.

Starting with Delphi 2007 Codegear uses MSBuild as the build engine. There is a split in the Delphi community over this being a good thing, but I think it is. MSBuild is Microsoft’s scripting tool for the build process and is very comparable to make. The ‘dproj’ file that is created with a project in Delphi is actually an XML file that can be used with MSBuild.

I think this is a good thing because I can use the exact same tool as the IDE to automates builds. Delphi WAnt worked for years but you have to manually create the build script and keep it in sync with the project. Now Delphi keeps the MSBuild script in sync since it is the same file it uses when you compile in the IDE.

While making the switch from WAnt to MSBuild I realized I no longer had to redirect stdout and parse the output for errors during the build process. I could just tell MSBuild to use a custom logger and I could write the log as easy to parse XML. It was finally time for me to learn a little .NET. All that is needed to create a custom logger is create a class that supports the ILogger interface.

During the following steps I will create a basic logger using Delphi .NET that should be easy to extend to any format you might need. The first thing is to start a new project.

New .NET DLL

Select ‘New | Other … ‘ and then select Library under ‘Delphi for .NET Projects.

 

 

Save the new library project as “DCBuildLogger.dproj” and add the references to the .NET assemblies Microsoft.Build.Framework and Microsoft.Build.Utilities.

Add Reference

Right click References in the Project manager and select Add Reference.

 

 

MSfk_MSut

Select Microsoft.Build.Frameworkand Microsoft.Build.Utilities and click Add References

 

 

 

NewUnit

Select ‘File | New | Other’ and select Unit under Delphi for .NET projects

 

 

 

Save the new unit as BuildLogger.pas. Now that everything is setup we can get to the interesting bits. First add a uses clause and specify the proper units.

uses
  SysUtils, Classes, System.Xml, Microsoft.Build.Utilities,
  Microsoft.Build.Framework;

 

Next define a class that will implement the ILogger interface called TXMLBuildLogger. We are actually extending the Logger class which already supports the interface. Include a couple of basic functions to make the first version of the logger somewhat useful.

type
  TXMLBuildLogger = class(Logger)
  private
    FXMLWriter: XmlTextWriter;
    procedure eventSource_ProjectStarted(sender: TObject;
      e: ProjectStartedEventArgs);
    procedure eventSource_ProjectFinished(sender: TObject;
      e: ProjectFinishedEventArgs);
  public
    procedure Initialize(eventSource: Microsoft.Build.Framework.IEventSource); override;
    procedure Shutdown; override;
  end;

 

The methods eventSource_ProjectStarted and eventSource_ProjectFinished will be called by MSBuild when the a project has been started or finished. The Initialize procedure is where the XMLTextWriter is setup and Shutdown is where all the cleanup occurs. Press CTRL+C to create the stubs.

procedure TXMLBuildLogger.Initialize(
  eventSource: Microsoft.Build.Framework.IEventSource);
begin
  FXMLWriter := XmlTextWriter.Create('c:\BuildLog.xml', nil);
  FXMLWriter.Formatting := Formatting.Indented;
  FXMLWriter.WriteStartDocument;
  FXMLWriter.WriteStartElement('Build');

  Include(eventSource.ProjectStarted, eventSource_ProjectStarted);
  Include(eventSource.ProjectFinished, eventSource_ProjectFinished);
end;

 

The first step in Initialize is creating the XMLTextWriter. This example uses a hardcoded filename for the output of c:\BuildLog.xml. Setting the indented property is completely optional but makes the XML output easier to check while debugging. WriteStartDocument must be called to start ouput and WriteStartElement is called to create the XML node that contains the output from the logger. The two Include lines set the method to use for the specified event.

procedure TXMLBuildLogger.Shutdown;
begin
  FXMLWriter.WriteEndElement;
  FXMLWriter.Close;
  FXMLWriter := nil;
end;

 

In the Shutdown method cleanup after the XMLTextWriter. Write the closing tag for the Build node created in Initialize and Close the document.

procedure TXMLBuildLogger.eventSource_ProjectStarted(sender: TObject; e:
  ProjectStartedEventArgs);
begin
  FXMLWriter.WriteStartElement('Project');
  FXMLWriter.WriteAttributeString('File', e.ProjectFile);
end;

 

When MSBuild calls eventSource_ProjectStarted create a new element call Project. Write the project file as an attribute of the Project node called File.

procedure TXMLBuildLogger.eventSource_ProjectFinished(sender: TObject;
  e:ProjectFinishedEventArgs);
begin
  FXMLWriter.WriteEndElement;
end;

 

Close the Project node when eventSource_ProjectFinished is called.

ChangeDir

We could build the library using the IDE but let’s use MSBuild. Open a command prompt and navigate to the directory where DCBuildLogger.dproj was saved. Build the project one time to generate DCBuildLogger.dll.

 

 

Now that the dll has been generated we can use it in a test build. The class name for the logger and the full path to the library must be specified. Rerun msbuild but specify the /logger switch and add the needed parameters. Include the /noconsole switch to turn off the output to the command window.

msbuild /noconsole
  /logger:TXMLLogger,c:\Develop\DCBuildLogger\DCBuildLogger.dll

 

TestRun

 

 

This should have created a file named BuildLog.xml in c:\. Take a look at the output from the logger.

<?xml version="1.0"?>
<Build>
<Project File="C:\Develop\DCBuildLogger\DCBuildLogger.dproj" />
</Build>

 

While it isn’t exactly useful it is a decent proof of concept. Check back for updates on adding more features and handling other events such as TargetStarted, TargetFinished, ErrorEventRaised, WarningEventRaised, and the generic StatusEventRaised that reports every status change.

Upgrading Embedded IDM to FileNet P8

Project: Upgrade a multi-tier Delphi application to include P8 functionality.

The existing application embedded the IDM viewer to display documents in the object store and also contained some forms for managing and working queues. The FileNet system was being upgraded to P8 and some code needed to be changed for the new API. Since the IDM viewer was no longer available the design called for an embedded Internet Explorer control to load the ViewOne viewer written in Java.

One particular problem is the P8 API is written in Java and so is ViewOne. Creating the java classes through the COM bridge (JiGlue) worked fine and displaying ViewOne worked fine, but Java raised errors if both happened at the same time. As it turns out only one instance of the JVM can be loaded in a single process. The JiGlue bridge is an in process COM object so it is loaded into the client process and starts a Java Virtual Machine (JVM). The IE control is also an in-proc dll and was trying to create it’s own JVM to launch ViewOne. The second attempt at starting a JVM would throw the previously mentioned errors.

The solution was to move one of the JVMs out of the client process. The IE control had to remain embedded in the client so the obvious choice was the calls to the P8 API. There was already a lot of code scattered through the client using OleVariants for calling methods on the API classes. Instead of moving all the API code into the out of process COM server and potentially adding bugs with the deadline approaching, the COM server contained two simple methods.

type
  TFNWrapper = class(TAutoObject, IFileNetWrapper)
  private
    FJiGlue: JiGlueUtil;
  protected
    function GetSession: OleVariant; safecall;
    function GetAttachment: OleVariant; safecall;
  public
    procedure Initialize; override;
    destructor Destroy; override;
  end;
...
function TFNWrapper.GetAttachment: OleVariant;
begin
  Result := FJiglue.NewInstance('filenet.vw.api.VWAttachment');
end;

function TFNWrapper.GetSession: OleVariant;
begin
  Result := FJiglue.NewInstance('filenet.vw.api.VWSession');
end;

The implementation of GetSession and GetAttachment called the COM bridge the same as in the client. Since it now runs as a separate process it can load it’s own JVM. The client application calls these two methods and stores the returned dispatch interfaces in the existing OleVariants as before and the rest of the client functions normally.

Introduction

I have taken a lot from the programming community over the years and decided it was time to try and give back a little more than some posts on random message boards. My intention is to detail programming problems that I have run into and explain my solutions. Hopefully this site will also have the side effect of improving my writing skills.

My programming experience is pretty random and covers a few languages and a lot of different ‘technologies’. Delphi is my preferred language and I have professionally used 1, 3, 5, 6, 7, 2006, and 2007. That is what I will be using in most of the examples but it is very readable so they should be adaptable to any language… provided I post something useful.