Oracle ODBC Setup on Ubuntu (32 on 64)

First of all, I am running a 64 bit Ubuntu 7.04 but I need a 32 Bit Oracle for the application I am using. I have a 32 bit chroot setup. The setup for this is beyond the scope of this article. This version of Ubuntu ships with unixodbc version 2.2.11-13 . There is a symbol, SQLGetPrivateProfileStringW, defined in later versions that the Oracle 11g driver requires. This symbol is not defined in unixodbc version 2.2.11-13. Thus, you have to use the 10.2 Oracle Drivers.

I Downloaded 3 files from The Oracle tech download page for 32bit Linux: the instantclient, SQL Plus, and ODBC packages. I unzipped these in my directory ~/apps/oracle32 Which now looks like this:

adyoung@adyoung-laptop$ pwd
/home/adyoung/apps/oracle32/instantclient_10_2
adyoung@adyoung-laptop$ ls
classes12.jar libocci.so.10.1 libsqora.so.10.1 ojdbc14.jar
genezi libociei.so ODBC_IC_Readme_Linux.html sqlplus
glogin.sql libocijdbc10.so ODBCRelnotesJA.htm
libclntsh.so.10.1 libsqlplusic.so ODBCRelnotesUS.htm
libnnz10.so libsqlplus.so odbc_update_ini.sh

I created an entry in /chroot/etc/odbcinst.ini:

[Oracle 10g ODBC driver]
Description = Oracle ODBC driver for Oracle 10g
Driver = /home/adyoung/apps/oracle32/instantclient_10_2/libsqora.so.10.1
Setup =
FileUsage =
CPTimeout =
CPReuse =

And another in /chroot/etc/odbc.ini

[EMO]
Application Attributes = T
Attributes = W
BatchAutocommitMode = IfAllSuccessful
BindAsFLOAT = F
CloseCursor = F
DisableDPM = F
DisableMTS = T
Driver = Oracle 10g ODBC driver
DSN = EMO
EXECSchemaOpt =
EXECSyntax = T
Failover = T
FailoverDelay = 10
FailoverRetryCount = 10
FetchBufferSize = 64000
ForceWCHAR = F
Lobs = T
Longs = T
MetadataIdDefault = F
QueryTimeout = T
ResultSets = T
ServerName = 10.10.15.15/DRORACLE
SQLGetData extensions = F
Translation DLL =
Translation Option = 0
DisableRULEHint = T
UserID = adyoung
StatementCache=F
CacheBufferSize=20

Once again, DSN and IP Address have been changed to protect the guilty. To test the datasource, run:

sudo dchroot -d LD_LIBRARY_PATH=/home/adyoung/apps/oracle32/instantclient_10_2 DataManagerII

To just test sqlplus connectivity, from inside the chroot, run:

./sqlplus adyoung/adyoung@10.10.15.15/DRORACLE

Note that using the instant client, no TNSNAMES.ORA file is required.

dpkg-others

dpkg-query -S `which $1` | cut -d: -f1 | xargs dpkg-query -L

This command finds other files in the same debian package as a given executable.  I put it into a file called dpkg-others.  Example run:

/home/adyoung/bin/dpkg-others ODBCConfig
/.
/usr
/usr/bin
/usr/bin/DataManager
/usr/bin/DataManagerII
/usr/bin/ODBCConfig
/usr/bin/odbctest
/usr/share
/usr/share/doc
/usr/share/doc/unixodbc-bin
/usr/share/doc/unixodbc-bin/copyright
/usr/share/doc/unixodbc-bin/changelog.gz
/usr/share/doc/unixodbc-bin/changelog.Debian.gz
/usr/share/menu
/usr/share/menu/unixodbc-bin
/usr/share/man
/usr/share/man/man1
/usr/share/man/man1/ODBCConfig.1.gz

High Availability and dealing with failures

Time to use my pubic forum to muddle through some design issues I’m struggling to lay straight.

A Data center is made up of several objects: Servers ( computers, usually horizontal), racks(hold the computers), switches(a network device that connects two or more computers together), power sources, and cables (both electric and network cables, to include fiber optic for storage devices). A server in the data center can serve on or more roles: storage host, computation host, administration, or user interface. If an application is hosted in a data cetner, it is usually important enough that it requires some guarantee of availability. This application will resided on a computation host, and require access to other types of hosts. An email server stores the received messages in a storage host, probably connected to the computation host via fiber optics. It receives and sends messages via a network connection that goes to the outside world. It may also talk to a User Interface machine that runs a web server and an application that allows web access to email. If the computation host loses connectivity with either the public network or the storage host, it cannot process mail. If the web server loses connectivity to the mail server, certain users cannot access their mail.

There are many reasons that connectivity can fail. The major links in the chain are: OS failure, Network interface card (NIC) failure, bad cable, disconnected cable, bad switch, unplugged switch, switch turned off. Once you pass the switch, the same set of potential problems exist on to the other host. To increase reliability, a network will often have two switches, and each server will have two NICs, one plugged into each switch. The same set up goes for storage, although different technologies are used. As a general rule, you don’t want to have things running in hot standby mode. It is a waste of resources, and it doesn’t get tested until an emergency hits. Thus, the double network connectivity usually gets set up also as a way to double bandwidth. Now if one of the cables breaks, that server merely operates in a degraded mode. The second cable has been passing network traffic already, now it just gets all of it.

A typical data center has many machines. Typical server loads are very low, sometimes in the 1-3% range of overall capacity. Thus, if a machine fails, a data center often has plenty of servers that could absorb the load from the failed server. Figuring out how to cross load services in a data center has been a major topic in the IT world over the past decade. This goes by many names, one of which is grid computing. I’ll use that term myself here. There are several problems any grid system has to solve, but most can be clumped under the term application provisioning. This means getting all of the resources together that a given application requires so that they available on the computation host. These resources include the network and storage connections described above, as well as the executables, data files, licenses, and security permissions required to run the application.

When a host fails, some remote monitoring system needs to act. First, it needs to know that the host has failed. This is typically performed through a heartbeat sensor. This is a simple network communication sent by the computation host saying “I’m still alive.” Cue Mike McCready. When a heartbeat fails, the monitor needs to make sure that the application is up online somewhere as soon as possible. Now, the reason the heartbeat failed might have been because of a problem on the heartbeat network, and the application is actually up and running just fine. An advanced solution is to test the system through some alternative method. In the case of the email server, it may be to connect to the email port and send a sample message. This delays the restart of the application, but may minimize downtime.

Sometimes, two copies of the applications can’t run at the same time. In this case, you have to be sure that the original one is gone. To achieve this, you shut off the original server. This is called “Shoot the other node in the head.” or STONITH. Sometimes the word node is replaced with guy and you get STOGITH. If you do this incorrectly, you may take yourself down, a situation referred to as SMITH. Or you take all servers down, and this is called SEITH. But I digest…

Here’s the part that I am currently trying to decide. If an application depends on a resource, and that resource fails, you can bring the application up on a different server. It will take a non-trivial amount of time (estimate it a minute) to shut down the old instance and bring up the new instance. If, on the other hand, the disconnect is temporary, we can have minimal down time by just waiting for the network to come back up. If someone disconnects a cable by accident, that person can just plug the cable back in. If the network is redundant, removing one cable may result in degraded performance, but it may not.
If the failure is due to the switch being down, just selecting another host connected to the same switch will result in downtime and a situation that is no better than the original. If the problem is the storage host being down, there probably is nothing you can do to recover outside of human or divine intervention.

If a switch goes down but there is another set of hosts on a different switch, you can migrate applications to the new host. But you may end up overloading the new switch. This is referred to as the stampeding herd effect. If the lost switch is degrading performance for all applications dependent on that switch, you best strategy is to migrate a subset of applications to balance the load. After each application is moved, recheck the network traffic to determine if you’ve done enough. Or done too much.

A malfunctioning NIC or switch might manifest in intermittent connectivity.  In this case, you want to get the application off of the original server and on to a new server.  The problem is in distinguishing this from the case where the cable just got unplugged once, and then plugged back in.  From the server’s perspective, the network is gone, and then it is back.  This leads to a a lot of questions. What interval, and how many iterations do you let go by before you decide to bail from that server?  If you have redundancy, does the failing connection impact the user’s experience, or does proper routing ensure that they have seamless connectivity?

Naming in code

int i;

How many times have we seen this? The reason we process this quickly is because we have seen it in code examples in K & R , THinking in C++/Java, or whatever your intro to structured programming was.

Even before that, we saw i used in sequences and series in Algebra. Assuming you took algebra before learning to code, no longer a safe assumption. Actually, I learned Basic before I took Algebra. Anyways, i as an index is very common. If we need multiple we go to j, and then to k.

char c;

Is pretty common too.

float f;

double d;

Probably the defaults, but people here tend to use more explicit names:

float r ; //rate

double a; //accelleration

All these come from the world of math done on a chalk board. If you’ve done any electricity you know:

v= i* (r*r); //twinkle twinkle little start, power equals i squared r.

e=m*(c*c);

a=(b*h)/2;

But it still takes a second to translate:

a=pi*(r*r);

into the more familiar.

a=Пr²

The fact that we can read these so quickly comes from their familiarity. But since we can’t use the Greek letter pi as a variable (at least not without knowing significantly more key bindings in vi than I have at the tips of my finger. I am doing this up in HTML, so I have to go and lookup not only the pi character, but the superscript 2 character as well.

This basic introduction to variable naming should illustrate the point that short names are a benefit if you can quickly translate them to their original intentions. It should not be too hard to show that the contrapositive is also true: if you cannot quickly translate them back to their original meaning, they are not a benefit. I would go so far as to state that they are a hindrance.

How many people can quickly translate the following acronyms:

CINCGARS

CINCPACFLT

RPM

TANSTAAFL

TARFU

RPG

If you have worked in the Army,you know the CINCGARS as the encrypted radio system, although you probably couldn’t state what it meants. If you were in the the Navy, or drove around Pearl Harbor, you would recognize CINCPACFLT as the Commander in CHeif, Pacific Fleet. RPM is rotations per minute, to most people, or the Redhat Package Manager to Linux people. To people who studiedeconomics, TANSTAAFL is short for “There ain;t no such thing as a free lunch.” TARFU is an old WWII slang expression saying that things are two levelsabove SNAFU (Situation normal, all …), having surpassed FUMTU (… more than usual) to “Things are really …” Which is one step below our well loved FUBAR. In all these cases, the acronym is helpful to people already familiar with the term, and a barrier to understanding to people on the outside.

I put RPG last because it illustrates the real problem with abbreviation. We can accept that RedHat chose RPM because it was already popular as an acronym due to the geek cache of rotational dynamics. But RPG is common enough that at least three different realms have used it as an acronym. In the Army, it means rocket propelled grenade. In the mainframe world, it means Report Generator (ver COBOLesque). In the Geek civilian world it means Role Playing Game. These three circles can intersect, cauuing a need to disabiguate the acronym. If you were designing a military role playing game system, you might accept that RPG means the game, and you use the full term for the weapon. If you were building a mainframe program to track the concepts in various Role Playing Game, you might need to use RPG to list the RPGs that refer to RPGs. While you can probably parse the previous sentence, you have to expand each of the acronyms to clarify. Each of these terms resides inside a namespace. Once you know which namespace you are using, you can revert back to the TLA (Three Letter Acronym).

A common pattern of software application development is to have a three tiered design. Say you are writing a program to design and control data centers. Your domain model will contain such objects as Hosts (physical computers), Hostnames, Racks, Ports, Operating Systems, install images, and so forth. Adding a new host to the system may be a multi stage process: specify the physical make up, figure out where to put it, give it a name, put an OS on it, etc. Now you might have differentviews of a host depending on whether you are in the User Interface, business logic, or Data tier of your application. I would tend to create a namespace around each. For instance, in C++:

Web::Host, Business::Host, Data:Host.

All three of these would live in the namespace of the application:

Datacenter::Web::Host, Datacenter::Business::Host, Datacenter::Data:Host.

Note that I chose the specific UI type to name it. Say we alter want a Qt based Application, or a command line interface, the namespaces could reflect that:

Datacenter::Qt::Host Datacenter::CLI::Host

But suppose there is some commonoality between these two. Some might go for a generic term like “Common” But I would caution against that: you want to go as specific as possible. Say the code for validating an IPv6 address is common to all of the UI layers. Put it into a validation package:

Datacenter::Validator::IPv6Address

Note that I move between the terms package and namespace. From a programmatic perspective, they provide the same functionality, they are just different mechanisms depending on the language used (C++ vs Java).

Note that now the Validator package can be used by any stage of the application. You can validate a file load in the Data layer using the same mechanism as the UI layer.

Here’s a rule of thumb:  Make a name as simple as you can.  Use a namespace to avoid name clashes.

Note that the rule states simple, not short. Acronyms require an added levle of translation.  While that may make it easier to type, it does not make it easier to understand.

Here’s another rule of thumb.  The primary focus of the developer should be maintainability.

Not performance.,   Not getting features out of the door as fast as possible, not robustness, not correctness, but maintainability.  Why?  Because it is a crosscutting concern that will support all of those other goals.  The easier it is to maintain your code, the easier it will be to change it for any purpose.   The lead programmer on your team will get hit by a bus, or promoted, or hired away to work at a cool new startup, or decide that he wants a career change, or go on maternity leave, or…and leave someone newly hired to your organization to take over.  Or newly transfered to this group that has never looked at the code before.  You might even be lucky enough to have a change over before the newbie gets the full brunt of the change requests, but it doesn’t matter, because it won’t sink in until the person who wrote is gone…see ya and I wouldn’t want to be ya.

How do you understand a code base?  What tools do you have? One powerful one is refactoring.  Make a copy of the working tree and go crazy renaming methods, extrating blocks from long functions, move things around, etc.  Make the code as pretty as you like.  After a long painful night of it, you will understand the code, in both its pre-and post refactored form.  Then be prepared to throw out the refactored version and work with the code.  But now you can refactor a little at a time.

One of my favorite lines of code from early bproc had a fragment something like this:

req->req.req

THe First req awas a pointer to a messsage that had come in.  The second was the message header, and the third was the message type.  THe current code reads more like this:

message->header.type

Aside from being almost  twice as many characters, the second version is also instantaneously understandable.

Rethink Web Apps in the light of Generics

The following is some musings on Templates and generics. I am not advocating programming this way, just trying to think through these issues in a framework I’ve used before.

The Struts framework dominated web application development in the Java world until the introduction of Java Server Faces. Since Java 1.4 and earlier provide great introspection capabilities and no generics, Struts did heavy runtime introspection to initialize the application and interpret URLS into the paths of the Java classes that should process them. The heart of form processing in Struts is the Action and ActionForm classes. Bascially, struts uses property names to map HTML parameters to bean ‘set’ methods. These can be nested. For instance

orderForm.billingAddress.phoneNumber.areaCode=415

would map to

OrderForm form = session.getBean(“order”);

form.getAddress().getBillingAddress().getPhoneNumber().setAreaCode(“415”)

albeit executed via reflection. Once the form object is populated it is passed to an Action Object. The Action object is an instance of the Flyweight Design Pattern. A Single Action instance can process all of the forms submitted at a time. Note that an Action object is not a singleton: Two instances of the same subclass of Action can be instantiated and have state assigned from the configuration file. They are only “Stateless” with regard to the HttpRequest, HttpResponse, and HttpSessions. Here is the API:

public class ExampleAction extends Action
{

public ActionForward execute(
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response) throws Exception{}

}

The ActionMapping allows you to get specifics about how the action was configured. Mostly it is used to figure out where to go next. The Action form is either down-cast to the correct type (throwing an exception if it is the wrong type) or ignored if there is no submitted data. The mapping class contains a list of forwards. These forwards are kept in a map. The action is going to return a forward configured like this:

return mapping.findForward("success");

We have here several instances of Runtime processing about decisions that could be made at compile time. The code that makes these decisions could be optimized by the compiler. Let’s start with the mapping. A map lookup based on a string is fairly expensive: Either has the string and do a hash-table lookup, or do a series of string compares. Really, what we want is code like:

template <typename Mapping, typename Form>

ActionForward execute(Form form, Mapping map, HttpRequest req, HttResponse resp) {

return map.forwardSuccess;

}

The java Bean API is based on a Java d design decision that everything class type variable a reference. To ensure that you don’t get a Null pointer, you have to fetch something through a ‘get’ method that often acts as a Lazy accessor. This is not necessary for most uses. C++ has the alternative of objects owning the objects they create. Thus the map object, even if created on the stack, can own the forward object. To transform the above in C++, it would be more like:

ActionForward& execute(Form& form, Mapping& map, HttpRequest& req, HttResponse& resp) {

return map.forwardSuccess;

}

The ‘&’ ensures that there is no copying of the objects through the function call. Because the map and returned action forward object both live outside the scope of this function, you can return a reference to the forward. This is true even if the map was originally created on the stack. We’ll come back to that in a little bit.

One task that actions often have to perform is validate a form an return to the previous screen if the form is invalid. In our above code, the logic would be:

ActionForward& execute(Form& form, Mapping& map, HttpRequest& req, HttResponse& resp) {

if (form.isValid() ){

return map.forwardSuccess;

} else {

return map.forwardPrevious;

}

Note that with templates, you could go crazy and have the forward and previous actions, as well as the isValid function name all passed in as template parameters. This would be the ulitmate in flexibility. The question here is the middle ground of settling on an acceptable implied interface.

Let’s take the case of the bean setters being called.

orderForm.billingAddress.phoneNumber.areaCode=415

Code like this is usually generated from a struts tag embedded inside a JSP. Here, code generation (JSP compilation) is generating late bound code. Even without the Generics capabilities of Java 1.5 and later, this can be bypassed, and in fact “pure” jsp processing does that by doinbg the bean API translation in line. However, struts does not have access to the JSP grammer and could not define rules for translation at that point. It only had the weaker rules of the JSP tag API, and thus had to be content with run time binding.

When the servlet API calls the struts servlet onPost method, the struts servel iterates through all of the parameters performs a binding on each one. The cope of the Form is defined by the action mapping that thje JSP refers to in the <FORM> tag. The code in the JSP is something like this:

<form action=”myurl.com/struts/thisform/sumbit.do”>

<input type=”text” name=”billingAddress.phoneNumber.areaCode”>

The URL will be translated to the mapping in the struts config file (minus the .do). The form object is a Bean, usually Session scoped, defined in the same file. Once that is resolved, the rest of the lookup is done using a util function for properties. To convert this to Template style, the first lookup would have to remain the same. URLS can be either absolute or relative. However, we can use functors for the second part: Validating and setting property values. We know what values will be used on a given page. A functor can handle the validation and the setting.
Map <string, string> properties;

Map <string, string>:: data_type data;

Map <string, string>:: iterator_type iterator;

for(iterator = properties.begin(); iterator != properties.end() iterator++){

data = *iterator;

propName,propValue in request

try{

mapping.propertyMap.get(data.first).set(form,data.second));

}catch(ActionException& ae){

actionErrors.add(ae);

}

}

Some people question the efficiency of using Exceptions for handling code of this type. I’ve addressed that in an earlier blog post. Basically, entering a try block generated by g++ is free. The only cost is when the exception is thrown: A new object has to be created and the stack unwound to the point where that exception is handled. The cheapest way to write this code would be to return an error code from action errors, but that propertyMap.get(“”).set(form,””). But then the setter has no way to customize the error message. We could pass back a string or null to signify error or success, but now we are already allocating objects, and adding in an additional test done for every success condition.

If we were to peek beneath the covers of struts into how the code was written for the ActionMapping we would see something like:

getAction().execute(request, response, form,this);

This is where the template parameter comes into play:

template<typename Form, typename Action> ActionMapping : public Mapping{

Mapping(Action& action){

}

process(HttpRequest req HttpResponse resp){

action.execute(request, response, getForm(req),this);

}

}

We still need to descend from a common Mapping baseclass so that the Action and Mapping classes don’t have a circular dependency. But now we have type safety in the call into the Action. Also, the construction of the mapping depends on a specific class. Initializing this code now has to be done in C++. Both the Mapping and the Action objects should be scoped global to the application. The Form can either be request or session scoped. Thus the construction of this complex set of relationships will require a lot of thought.

This article could go on and on. I’m going to add one more related concept and then declare victory and post it.

The difference between a Form object and the domain model is that the form object shuld be able to work with half formed, invalid data. Quite often, the form will contain builders for objects in the domain model. A Trivial example is a social security number. This is of the format 000-11-2222. Often, the UI will put each of these fields into a separate text box. The builder will then accept each field, validate their length and that it only has didgits, and produce a SocialSecurityNumber.

One way that this could be made generic is to provide a Template class that represents a validator for a regular expression. The SocialSecurity Number would be one instance, but each of the sub fields would be instances as well. Someething like this:

typedef ValidatedString<“\d\d\d-\d\d-\d\d\d\d> SocialSecurityNumber;

This is not valid C++, as the string cannot be passed to a type. It might be OK in Java where Regular Expressions are processed at run time.

Doing Unspeakable things with Templates

The C++ Template language is Turing complete.  That means it should easily be able to build an Acceptor for a Regular expression.  Yes, this is headed where you think it is. Get out while you still can.

Still with me?  Too bad for you:

#include <string>
using std::string;
namespace acceptor{
template <typename Next> class Expression{
    public:
    static bool accept(string& s){
        return Next::accept(s, s.begin());
    }
};
class EndType {
    public:
    static bool accept(string& s, string::iterator itr){
       return(itr == s.end());
    }
};
class NoOp {
    public:
    static bool accept(string& s, string::iterator itr){
        return true;
    }
};
template <char valid,typename Next = NoOp > class Char{
    public:
    static bool accept(string& s, string::iterator  itr){
        return (valid == *itr) && Next::accept(s,++itr);
    }
};
template <char first,char last,typename Next = NoOp >
class RangeAcceptor{
    public:
    static bool accept(string& s, string::iterator itr){
        return ((*itr >= first)
            && (*itr <= last)
            && Next::accept(s,++itr));
    }
};
template <int count, typename Acceptor ,typename Next = NoOp>
class Count{
    public:
    static bool accept(string& s, string::iterator itr){
        int i;
        for ( i = 0; i < count ; ++i){
            if (Acceptor::accept(s,itr)){
                ++itr;
            }else{
                return false;
            }
        }
        return Next::accept(s, itr);
    }
};
template <typename First, typename Second >
class Or {
    public:
    static bool accept(string& s, string::iterator itr){
        if ( First::accept(s,itr)) {
            return true;
        } else {
            return Second::accept(s,itr);
        }
    }
};
template <typename First, typename Second >
class And {
public:
    static bool accept(string& s, string::iterator itr){
        return (First::accept(s,itr) && Second::accept(s,itr));
    }
};
template <typename Next = NoOp >
class Digit: public RangeAcceptor<'0','9',Next>{};
template <typename Next>
class LowerLetter : public RangeAcceptor<'a','z',Next>{};
template <typename Next>
class UpperLetter : public RangeAcceptor<'A','Z',Next>{};
template <typename Next = NoOp >
class Letter : public Or < LowerLetter<NoOp> , UpperLetter<NoOp> > {};
};

How would you use this?

void testDigit(){
Expression<
Digit <
Digit <
Digit <
Char  < '-' ,
Digit <
Digit <
Char  < '-' ,
Digit <
Digit <
Digit <
Digit <
EndType
> > > > > > > > > > > >ssn;
string s("000-22-9999");
assertTrue(s, ssn.accept(s));
s = "abcdefghij";
assertFalse(s, ssn.accept(s));
};

Not shown:  Implementation of the Kleene Star, as that would require a writing a greedy algorithm, something I have no desire to do right now.

Linux init process

The BProc project supports an older protocol called RARP to assign an IP address for a compute node. While this made sense when BProc was written, it has been made obsolete by DHCP. Since I really don’t want to write a DHCP server, I’ve decided to try to use the DHCP and TFTP servers that come with CentOS to boot the compute nodes. Here’s what I’ve (re)learned:

The initrd image that the Linux kernel builds has a file in it’s / directory called init. This is a shell script that executes in the lash interpreter. It does a modprobe for a set of modules, greats /dev a file for and mounts the root file system, and performs a switchroot.

Aside: Anyone on a linux system can find this out by running:

zcat /boot/initrd<version>.img | cpio -di

I would suggest doing this in an empty directory.

My thinking is that I should hack this script to do a tftp fetch before creating the /dev file. What I plan on fetching is a file that contains an ext2 file system that can be mounted as a ram disk. This ramdisk can be created by creating a (large) file, then running mke2fs. This file will not dynamically resize, so I need to make it large enough to fit all my files needed for booting, but not so large that it is going to eat up a significant portion of ram on the compute node. I know I am going to need the bproc kernel modules (bproc.ko, vmadump.ko), bpmaster, some process to act as init (I’ll use bash to start) and the support libraries:

  • /lib/libncurses.so.5 374024
  • /lib/libdl.so.2 14624
  • /lib/libc.so.6 1367432
  • /lib64/ld-linux-x86-64.so.2 119536
  • bproc.ko 1929345
  • vmadump.ko 285821
  • /bin/bash 797208
  • bpmaster 112920

Turning to my old friend the binary calculator:

echo “( 374024 + 14624 + 1367432 + 119536 + 1929345 + 285821 +112920 + 797208 ) / ( 1024 * 1024 )” | bc

4

So roughly 4 MB. I’ll make it an odd 5 to start.

To create the file:

$ dd if=/dev/zero of=/tmp/ramdisk bs=1024 count=51105110+0 records in
5110+0 records out
5232640 bytes (5.2 MB) copied, 0.024132 seconds, 217 MB/s

I’ll take the defaults for ext2 for now. Notice that I have to type ‘Y when asked to proceed.

$ mke2fs /tmp/ramdisk
mke2fs 1.40-WIP (14-Nov-2006)
/tmp/ramdisk is not a block special device.
Proceed anyway? (y,n) y
Filesystem label=
OS type: Linux
Block size=1024 (log=0)
Fragment size=1024 (log=0)
1280 inodes, 5108 blocks
255 blocks (4.99%) reserved for the super user
First data block=1
Maximum filesystem blocks=5242880
1 block group
8192 blocks per group, 8192 fragments per group
1280 inodes per group

Writing inode tables: done
Writing superblocks and filesystem accounting information: done

This filesystem will be automatically checked every 38 mounts or
180 days, whichever comes first. Use tune2fs -c or -i to override.

Now That I have a ramdisk, I can copy to it

$ sudo mkdir /mnt/ramdisk
Password:
$ sudo mount -o loop /tmp/ramdisk /mnt/ramdisk/
$ ls /mnt/ramdisk/
lost+found

And we have a file system.

Update 1: The initrd layout seems to be distribution specific. On my debian box, there is no lash, and instead there is a busybox executable with, amongst other things, a tftp client built in. This may be a worthy approach: having tftp available as part of the init rd will allow fetching a rootfs to be done more cleanly. Also, there are hooks to put scripts in, and command line options to allow building initrd’s for nfs root or local root. If only I had targeted Debian instead of RHEL 4 to start.

Update2: The Redhat initrd does not have a tftp client in it. I added one in by hand, added all of the libraries it needed (ldd bin/tftp) and kicked off another PXE boot. Network unreachable. Interesting that it is supposed to be able to NFS mount root, but it seems unable to do a tftp fetch.

Back to BProc

The last check in to the BProc CVS repository on sourceforge happened 16 Months ago. I recently checked out the top of tree and found I was unable to build. Looks like what is there is a mix of 2.6.10 and something in the vicinity of 2.6.20 Linux code bases. I am starting again, this time with the code in the tarball. I’ve built this before and know it compiles. Here is my general plan forward:

1. Get a 2.6.9 Kernel with the BPRoc patch applied to boot on a RHEL4 System.

2. Build the BPRoc and VMADump Kernel modules and load them into the kernel.

3. Build the BPMaster and BPSlave binaries. Make sure BPMaster runs.

4. Build the beoboot code.

This is where it gets tricky. At Penguin we had our own PXE Server (Beoserv) that handled provisioning a compute node. Part of the Beoboot package there was creating the root file system and bring up the slave node binary. So here is a tentative plan instead.

1. Deploy the standard redhat PXE and DHCP servers on my head node. Ensure that the DHCP server only responds to requests from the subnet where the compute node resides. Probably best to unplug from the company network when I do this.

2. Set the PXE server to support the booting of a stripped down RHEL4 system. Really, all I want is to get as far as running init.

3. Replace the init in the PXE IMage with the beoboot binary. Have it bring up BPSlave and see if it can talk to BPMaster on the head node.

If I can get this far, I will consider it a great success.

Update 1: I built a 2.6.9 Linux Kernel with the bproc patch applied. makeoldconfig, selected BProc but none of the other options. Upon BootingI got a panic when it could not find device mapper. Looks like device-mapper got added in the 2.6.10 kernel. Since I have already built that kernel, I guess I’ll start by trying the tarball kernel module code against the 2.6.19 patch.

Update 2: Um, nope. TASK_ZOMBIE and mmlist_nr are showing up as undefined symbols. mmlist_nr seems to be acount of the number of memory managers out there. I suspect that this is something that changed between 2.6.9 and 2.6.10. Probably some better way to keep the ref counts was introduced. I Vaguly remember something about the TASK_ZOMBIE.

Update 3: This was bogus and I removed it.
Update 4: Replaced TASK_ZOMBIE with EXIT_ZOMBIE. Commented out the decrement as it seems like it has just been removed.

Update 5: Error accessing rlim in task_strcut. This is now in the signal struct:

– unsigned long gap = current->rlim[RLIMIT_STACK].rlim_cur;
+ unsigned long gap = current->signal->rlim[RLIMIT_STACK].rlim_cur;

Update 6: OK, back to the point I found before. THe hook for kill_pg_info is now kill_pgrp info, and the hook for kill_proc_info is now kill_pid info. This is a change in the patch, so I have to get the module code in line with the new function call parameters. Looks like the header has been changed, but the old function call names are using in kernel/signal.c. Changing, rebuilding, and redeploying kernel.

Update 7:  Success through building and running bpmaster.    I had to create a config directory, but other than that, nothing was too far out of the ordinary.

Build systems

For the non programmers, the word ‘make’ in the following paragraphs refers to a tool used to control the compilation of a software product.

At work we are in transition to using scons as our build driver. I guess enough people were frustrated with Make that they were able to change things over. Here is the problem.

You can never get away from make.

Any build system that uses code from some where else is, eventually going to have to deal with make. Your standard GNU package is a tarball that you unpack and then run:

./configure

make

make install

Or some variation on this. At work, the build team wanted to standardize on the version of autotools that they use. Makes sense, you should not check generated files into a repository if it makes more sense to regenerate them…but does it? Well, you need to be able to modify the Makefile.in and Configure.in files, so yes, every developer needs to run Automake/Libtoolize/Autoconf/etc with the same options and the same version of the tools. I’m not certain that autotools really make sense for a Linux only project, which was why we didn’t use them at Penguin and why we have to use them for the packages we have here.

People don’t like make because they either don’t like rules based programming or they don’t like debugging make files. Fair enough…but deal with it. Instead of trying to rewrite the tool, extend it. I am not talking about your average developers, just the ones that decided they can do it better using X, where X is some other language or some other library.

Personally, I’ve never found a replacement of make that didn’t end up sporting some of make’s warts. If your goal is to do incremental compilation, then the Ant approach, depending on the compiler to identify which file to build, won’t work for most languages. This is a case where you need to recognize in a Makefile that you don’t want to have a .SUFFIXES rule (.java.class) and instead, if you depend on the Java code, you let javac build everything every time. See the difference? One depends on a tool support for something, and fails when it isn’t there. The other allows you to take advantage of the tool support if it is there.

One reason that people don’t like make is that it is sensitive to white space. SCons is built in Python, also sensitive to white space. I guess those people will keep looking for something else.