2005-12-27 12:30

Teaching and money

I have been teaching for almost my whole adult life.

My first real job was teaching assistant. I worked in a university for 10 years.

Then I started teaching Linux trainning courses for diverse companies (think LPI kinda stuff).

Then I stopped. Why? Because it makes no sense economically to teach in most cases.

The following is written in pesos, but the idea is probably about the same for other countries.

There are no money signs beause cheetah templates hate them ;-)

A trainer is paid about 35 per hour. If he's pretty good, he gets 50.

A course is about 24 hours, so he gets about 1200 (I am going for the best case scenario here).

Usually he has between 6 and 12 students, which are charged about 900 + taxes, so the gross is average 5500.

Of that, half (or a little less) goes to the classroom rent, leaving about 3000 for the training company.

Pay the teacher, and you have a rather pathetic amount for the training company that pays overhead, salespeople, taxes and whatever.

So, who makes any money out of this? The classroom renters :-P

So, if youa re going to work on training, please don't rent classrooms, it makes no sense.

Now suppose you have a consulting firm and you can do onsite training (at your clients), and just pocket the money.

If you have just three students, you need no facilities, you can charge slightly higher, because its onsite.

It's the same effort for the trainer, because you just change where you commute to (training center vs client).

Since you can make more money with smaller classes, you can still charge a little higher (more personalized attention).

Since the students are all from one company they have more in common and you can structure the teaching better, which makes it way less boring.

So, if you want to make a living teaching, here's how.

  • Market directly to companies, for onsite training.
  • Have your own materials.
  • Be flexible.
  • Buy your own classroom if you have to.

And most of all, don't worry. It's pretty much impossible to lose money training, unless you open a exclusively training company.

2005-12-21 10:45

Kong at dawn

Last saturday I went to see King Kong with Rosario, and something happened I never saw before.

For whatever reason, we went at the 1:25AM session. I don't think I had ever been to one so late.

And then it lasted 3 hours. And it was almost the longest day of the year.

So when we left, at 4:30 AM, walking through a ghostly mall, it was dawn.

It's a small thing, but it was quite shocking :-)

The movie... she didn't like it and insists Naomi Watts is wearing, in one of the scenes, a Lurex dress, which couldn't possibly be the case in the 30s.

Me, I liked it quite a bit, perhaps my suspension of disbelief is not so easily taxed by textile issues, except for some serious moral trouble I got a day or so later.

You see, killing Kong on the Empire State building was right.

That damn beast had just stomped, thrown, smashed, chewn and swatted about 2500 people.

He was going all 9/11 on Manhattan, and just because he didn't feel like crushing one specific blonde (although he sure killed all her predecessors on the sacrificial girl job), we are supposed to feel sorry for him?

Cry me a river of giant alligator tears, I am not. I say we should bazooka the evil man-eating monkey, and put Denham in jail for reckless endangerment, along with all his accomplices.

AND he should lose his shirt (along with his theater bankrollers) in a civil suit to the family of the maori guy whose head got chewed by Kong, or Lumpy, the cook eaten by gigantic man-eating maggots.

At least in the original movie, the girl has the good sense to be scared senseless by the sight of a giant gorilla with romantic leanings.

Here? She laughs while they ice skate, I assume ignoring the blood stains all over the monkey's fur.

2005-12-20 12:25

Forgotten Language: Jorf could have been Python (or Ruby)

A long time ago, there was no Internet.

Ok, there was an internet, but I lived outside of it. It was 1992 or 1993, and I only saw my first webpage and send my first email in 1995. And I was perhaps the third person to have an account on a permanently internet-connected box in a 150 km radius.

But that didn't mean I had no access to internet stuff! What I did was buy CDs containing mirrors of repositories like Simtel.net (it was a single CD, too!) and in there you could find hundreds of programs.

Most of them shareware, most of them crap, but every once in a while, there was something cool, like DJGPP (a whole gcc suite for DOS! A real C compiler! For Free!)

At the time, I had a side job writing data entry software for statistics students. They were simple programs that showed a form, where data was loaded, then it did some simple manipulations of the data.

The natural language for that was something like Clipper or DBase, but I didn't have access to them (or a way to learn it. Remember, no Internet).

On one of those Simtel CDs I found Jorf. (Josephine's Recipe Filer). It was a OO language, with an interpreter for DOS or Windows, and it supported stuff that was really advanced for the time, and it made my coding a lot simpler.

Out of nostalgy, I downloaded a copy (yes, it is still there), and ran it in DosBOX (yes, it still works), to check if it was as good as I remembered.

You know what? It is.

In fact, if it had come out 2 or three years later, and as free software instead of shareware... I think it would have been big.

Here are some highlights og the language:

  • OOP
  • Has integrated windowing toolkit (for DOS and Windows)
  • It had an interactive hypertext/windowing tutorial written in itself. In 1993.
  • It looks like a cousin of Python. A freaky cousing, though.
    1. Comments start with |
    2. Strings limited with single or double quotes
    3. Automatic type conversions
    4. Intentation controls flow :-)
    5. No declared data types
    6. Integrated editor and debugger

Sample Hello World:

  Msg:Add ("Quick Demonstration","Ok")
    Sure you can say "Hello World" in one line of
    C code. But how many punctuation characters
    are required to display a dialog box like this?
  Return (Ok)

That piece of code showed a window with the message in it, and a Ok button.

The funky thing is: in the tutorial, you saw the integrated editor open, and the text of the example start to appear, and then it ran.

That looked like magic at the time :-)

The toolkit supported radio buttons, checkboxes, text entries, all the basics, and it was a thousand times easier than what Turbo Pascal or Turbo C guys battled with at the time.

The author was Wayland Bruns. He lived, in 1993, in Colton, Oregon.

He later seems to have become CTO of a company that designs sync software for Goldmine, Lotus and other such things.

So, he became a suit ;-). However, he was once a guy that wrote, in his software's manual, things like:

JORF Company is just me, Wayland Bruns. I have been working on JORF for six years, and ran out of money three years ago.


JORF(R) is a new computer language. JORF was created by a Grunt-programmer frustrated by low level math based computer languages that are inappropiate for business data processing.

And you know what? It was the right idea. If he started Jorf in 1987, that means he started it around the same time Perl 1.0, (and the syntax is much nicer ;-). He started it around the same time Guido started Python.

Here's a toast to JORF, which could have been Perl, or Python, or Ruby. But was not.

2005-12-19 23:31

Rant: The problem with expensive linux

I always liked SuSE's Linux distros. They even used to mail me a box every now and then when there was code of mine in it. It always seemed nicely done, and well integrated. Of course I only used it as a workstation.

Recently, I had the displeasure of installing a SLES9-based server for a client.

I say displeasure, because it was by far the worse experience I had with any Linux distribution ever. It was worse than the time I had to install openldap on a P2 with gentoo that was connected to the internet over a 56k dialup and had only 100MB of free disk space.

And that one was a screamer!

What were the problems?

Well, for starters, it was a punishing throwback to the times of proprietary software installation.

I install it. Ok.

I try to update it. Not Ok.

The problem? You need to authenticate to the servers in order to get the updates. And there is no user/password anywhere in the box. You have a serial number (in a word file inside a CD, not on a sticker), and no explanation on how to go from one to the other.

It turns out you have to call Support to get the auth data, and that depending on how you do it you get data that lets you access to the novell servers or the suse servers (and not the other).

Then, after we got that (48 hours on tech support), I start installing the software I need.

The mission of this server is simple. It's a mail forwarding server. It handles outgoing mail, and it stores incoming mail for a few minutes until a CRM software (in windows) grabs it via POP3.

I can do such a server on CentOS, Debian, (hell, yes, even gentoo) in about 2 hours without counting install+updates, including migration of old data.

I install postfix and imapd (I think it's wu-imapd, which sucks, BTW, but the alternative was cyrus, which was gross overkill).

It seems to work. But the CRM can't fetch the mail. Outlook can, though. What the hell?

Well, SuSE decided to disable plain logins for POP and IMAP over non-SSL connections. And there is no way to enable it.

Since that's the only kind of connection the CRM will do, it will not work.

Mind you, in this case, it is absolutely no security risk whatsowever, since the mail server and the CRM are segregated from the user's network...

Ok, I will install courier-imap, which is better anyway. But it's not on SLES9 CDs, and the RPMs on the web are for every other SuSE and not SLES9. So I had to build it myself.

That is, of course, because there is no free repositories for SLES9. You have what comes with it, or what you can build yourself. Anything else, you are SOL.

The same thing happened for almost everything I wanted to install. Either it was not there, or it was for some other SuSE, so it was time to compile a RPM again.

It was like Gentoo, only without the automatical dependencies, and with no hope for future security updates unless I build them myself.

At that point I was already telling the customer that maybe I could just install OpenSUSE, which was free and would not have these problems (hell, I would even get apt4suse and avoid the damn novell servers).

Of course that means they would be a few hundred dollars poorer for no good reason.

But anyway, it took me roughly 3 extra days to set this up, which made me actually lose money on the gig. ANd I lost the time in the most pathetic way, sitting in a customer's office waiting for tech support, watching my money go away.

That had never happened to me before. I must say I am pretty disappointed.

But what was the root of the problems here?

  • There is no free SLES clone like CentOS

If there were, then there would be 3rd party repos. The customer would still have bought SLES9 because they are support groupies, but my life would have been easier.

Of course, it would probably cut into SLES sales, but hey, that is not my problem, is it?

  • Novell Argentina tech support sucks for Linux.

The guy on the phone literally had no idea what I talked about when I asked about how to get into YOU to update the box.

But don't worry Novell, I heard Red Hat Argentina's is quite bad too.

If you really want SuSE, buy a regular one, the ones with public FTP repositories, and avoid trouble. Or get OpenSUSE.

Me, I'm pretty bummed :-(

2005-12-17 23:20

Unknown jewels of Unix: NoSQL

I have known about Carlo Strozzi's NoSQL for about 6 years. I don't think many others do (it's google score is 39500, compared with 81200000 for MySQL) , and I think it's a shame, because it's a much more interesting idea.

What is it? It's a relational database.

Sure, I can almost hear you think [1] , "like Mysql!" or "like Postgres!" and you are wrong because it's very different.

NoSQL is a relational database implemented in AWK and operated via the unix shell. Yes, NoSQL is the most unixy RDBMS in the world. So all of you so-called unix lovers, shell freaks, you are gonna love this.

Installing it is perhaps a bit harder than it should (who knew there are no MAWK RPMs anymore!), but it seems to be packed for Debian at least ;-)

You can learn to use NoSQL with a nice [2] tutorial that was published on Linux Journal.

The tables are plain text files, and later you can do things like this (from the docs):

% column Item Cost Value < inventory |
row 'Cost > 50' | mean -l Value
Item   Cost  Value
----   ----  -----
3      80    400
6      147   13083
7      175   875
----   ----  -----

Isn't that cool? What it does is take the inventory table, select over columns Item, Cost, Value the rows with Cost > 50, then calculate the mean of the Value column :-)

It even supports reports, joins, a bazillion things.

Honestly, I am not sure I can find a practical use for it [3], but it is a great piece of software, written by a dedicated guy, it is extremely original, and it does work. I'd even say it's pretty powerful, and you must accept, it's unixy as all hell.

So, Carlo Strozzi, here's a toast for you and NoSQL. You are cool.

[1] So good is my hearing

[2] Sadly it's quite old

[3] I did once, but that was when I was a real programmer ;-)

2005-12-16 15:44


And no, I am not trying to take Aaron's spot as most frequent poster ;-)

2005-12-16 14:27

Bound by Smoke I

This is what I understood of Smoke so far. I may be way off, since it is C++ sorcery of a higher level than I'm used to, but I really think I am getting the hang of it (and a bunch of thanks to Richard Dale and Ashley Winters who are the ones that made me understand so far. Any mistakes a re my fault, any good thing is theirs ;-).

This piece is only half of the story, though. Maybe one third.


Since Smoke's goal is to help you write bindings for languages other than C++, it provides a way to access Qt's API. The original thing about Smoke is that it does so by providing you with a smaller, more dynamic API that maps onto Qt's.

You could write a Qt C++ program using Smoke as the API instead of using Qt's. In fact, you can see it here written by Ashley Winters.

I had to rename it hello.cpp to make it work because that looks like C but may not really be C ;-)

As you can see, the Smoke version is quite a bit more complex than the Qt one. But that's ok, remember that the goal is a binding, which means that what you need to make your life simpler is less API... which is what makes the program more verbose.

Let's examine the Smoke hello.cpp in detail.

One key point is callMethod:

// call obj->method(args)
void callMethod(Smoke *smoke, void *obj, Smoke::Index method, Smoke::Stack args) {
        Smoke::Method *m = smoke->methods + method;
        Smoke::ClassFn fn = smoke->classes[m->classId].classFn;
        fn(m->method, obj, args);

If you have programmed in Python or Ruby or a number of other dynamic languages you may guess what this does already.

It takes as arguments a number of things which still have to be explained but the main gist is there is an object, there is a method, there are args, and it ends calling obj->method(args).

The first argument is a Smoke pointer , which is the big object in the Smoke library, created in our program by the init_smoke function.

A Smoke object is a strange beast. It contains a description (in this case, because we got qt_smoke) for the whole Qt API.

You can find classes in it indexed by their names, and methods for each class indexed by their names and types of arguments.

That is why you can have a generic callMethod that will work for any kind of object and for any method in the class, because all of them are somewhere in the Smoke object.

The second argument is void *obj which is the object itself we are manipulating. So, if you are trying to call QLabel::setText, it will have to be a Qlabel* casted as void*.

In the hello.cpp example, we even create these objects using Smoke's API (see later).

The third argument is a Smoke::Index which is what Smoke uses to find the requested method in its method table. This Index we get using the getMethod function, which is the second key piece of code:

// given class-name and mangled function-name, return an unambiguous method ID
Smoke::Index getMethod(Smoke *smoke, const char* c, const char* m) {
        Smoke::Index method = smoke->findMethod(c, m);
        Smoke::Index i = smoke->methodMaps[method].method;
        if(i <= 0) {
                // ambiguous method have i < 0; it's possible to resolve them, see the other bindings
                fprintf(stderr, "%s method %s::%s\n",
                i ? "Ambiguous" : "Unknown", c, m);
        return i;

Here is an example of a getMethod call, where we are getting QApplication::setMainWidget

Smoke::Index method = getMethod(smoke, "QApplication", "setMainWidget#");

As you can see, we search for the method using strings of the class name and method name. Excapt for that pesky # at the end of setMainWidget#.

That is a basic argument-type-mangling scheme, since there can be more than one QApplication::setMainWidget on the Qt side of the fence, we are saying we want the one that has an object as the first and only argument. Here is the key to the mangling taken from smoke.h:

* The munging works this way:
* $ is a plain scalar
* # is an object
* ? is a non-scalar (reference to array or hash, undef)
* e.g. QApplication(int &, char **) becomes QApplication$?

I am not yet completely clear on how this is enough to do all the work (for example, what happens if you have two methods that take different objects as only argument?) but it's what I saw :-)

The last argument, args is of Smoke::Stack type, and it's the tricky one, at least for me.

Here's how it's used in the previous example, QApplication::setMainWidget

// qapp->setMainWidget(l)
Smoke::Index method = getMethod(smoke, "QApplication", "setMainWidget#");
Smoke::StackItem args[2];
smokeCast(smoke, method, args, 1, l, "QLabel");
smokeCastThis(smoke, method, args, qapp, "QApplication");
callMethod(smoke, args[0].s_class, method, args);

A Smoke::Stack is a way to pass the arguments to be used with the method getMethod gave us.

We first create an array of 2 StackItems:

Smoke::StackItem args[2];

Then we assign a value to the second of them:

smokeCast(smoke, method, args, 1, l, "QLabel");

Here l is a pointer to a QLabel. ( Ok, it is really declared as a void* because, remember, we are not using the Qt API, so we have no clue what a QLabel is ;-) and what we are doing is storing in args[1] a casted version of l.

The exact details of why you have to pass smoke and method are not that important, and they seem pretty involved, so I won't try to go there, at least not yet. This has to be done for each argument for the method.

Then we have a similar, yet different line:

smokeCastThis(smoke, method, args, qapp, "QApplication");

This puts the qapp void * in args[0], casted to QApplication. There are tricky C++ reasons why this is done slightly different here than on smokeCast, which I am not 100% sure I get right, so I will keep quiet ;-)

This special case is only for the object to which the method belongs (the this object).

Here is the code for smokeCast and smokeCastThis

// cast argument pointer to the correct type for the specified method argument
// args[i].s_class = (void*)(typeof(args[i]))(className*)obj
void smokeCast(Smoke *smoke, Smoke::Index method, Smoke::Stack args, Smoke::Index i, void *obj, const char *className) {
        // cast obj from className to the desired type of args[i]
        Smoke::Index arg = smoke->argumentList[
                smoke->methods[method].args + i - 1
        // cast(obj, from_type, to_type)
        args[i].s_class = smoke->cast(obj, smoke->idClass(className), smoke->types[arg].classId);

// cast obj to the required type of this, which, dur to multiple-inheritance, could change the pointer-address
// from the one returned by new. Puts the pointer in args[0].s_class, even though smoke doesn't do it that way
void smokeCastThis(Smoke *smoke, Smoke::Index method, Smoke::Stack args, void *obj, const char *className) {
        args[0].s_class = smoke->cast(obj, smoke->idClass(className), smoke->methods[method].classId);

But where did we get l or qapp? You can use these same mechanisms to create an object:

void *qapp;
    // new QApplication(argc, argv)
    Smoke::Index method = getMethod(smoke, "QApplication", "QApplication$?");
    Smoke::StackItem args[3];
    args[1].s_voidp = (void*)&argc;
    args[2].s_voidp = (void*)argv;
    callMethod(smoke, 0, method, args);

    qapp = args[0].s_class;

You get QApplication::QApplication(scalar,undef) which should hopefully map to QApplication::QApplication(argc,argv). You create a Smoke::Stack of 3 items. The first is unset because this is a constructor, so it has no this yet, and the other two are argc and argv.

Then you call it through callMethod, and you get the resulting object via args[0].s_class.

Later you repeat this sort of operation for every method call you want, and you got yourself an application.

The binding side of things

So, how do you use this to bind your language to Qt?

Well, you will have to create an object in your language called, for example, QApplication, and map the "missing method" mechanism (your scripting language probably has one) which is used when a method is undefined so that somehow it finds the right Smoke method (mangling the name correctly should be the hard part), then create the Smoke::Stack with the passed arguments, call, get the result, wrap it in some language construct you can use of the side of your language, and pass it back.

It looks involved, and I am sure it's tricky, but at least it only has to be done once unlike on traditional bindings, where you had to do it for every class and for every method.

The traditional solution was to automatically generate the code for such wrapping (like SWIG does). I think Smoke is less error prone.

If I keep on understanding things, there may be a second part of this article, explaining the SmokeBinding class, and perhaps a short example of how to bind Qt into a language (Io is a strong candidate).

The regularity of Io's syntax is probably going to make the binding simpler than most.

2005-12-16 12:38

Bacula is good, backups are hard

Just finished implementing a network backup solution for a customer using Bacula and reached these comclusions:

  • Bacula is hard. But that is mostly because the problem is hard. You need to backup 30 different computers over a net, in a secure manner, with different data sets for each... well, it is going to involve defining 30 datasets and so on.
  • Bacula is very good. It supports VSS, which alleviates the classic windows "that file is open you can not read it" problem.
  • Backing up Windows sucks.
    1. It is pretty hard to know what to backup exactly to catch all configs and user data. Not so much on XP, but on older versions... yikes.
    2. You pretty much can't ever be sure you are allowed to back up everything. I have a fileserver with files the local administrator can not read. So, it seems a user can create unbackupable files!
  • I still need a decent system backup for Linux. I have used Mondo for a long time, but it's a pain in the butt, and it's getting more painful as time passes. I want something that I call and I get a nice DVD with the whole system in it. If you have a suggestion, please drop a comment ;-)
  • A system that automates the backup policy as much as Bacula does is great. Except that it makes it quite hard to guess the exact amount of storage (I am doing disk-based backups) you will need.
  • For simpler stuff, you should use flexbackup. Their slogan is amanda is "too much", and tarring things up by hand isn't nearly enough and it pretty much is what it does.
  • For personal stuff, rdiff-backup is awesome. I really should write a graphical tool for it one of these years :-)

2005-12-12 19:47

Ok, so, I am a lazy guy

I just realized I have not learned a whole new real language in almost 5 years.

I have learned a few dialects, some minor stuff to work around or through an obstacle in some project, but I have not written a whole program in anything but python since 2001.

So, since I don't want my brain to turn into muesli, I will now proceed to learn Io .

Nice OO language, prototype-based, which is something I haven't run into outside of JS, I think, a sort of LISPy flavor, with a dash of smalltalk for piquancy.

Maybe I will get really bored with it, maybe not :-)

And yes, the previous post was about having the idea of hacking a Qt binding for it, but it's probably not doable right now, since extending the language is pretty much undocumented, and extending it in C++ is unheard of.

To top that, it uses continuations, which probably cause all kinds of hell on Qt's memory management :-(

2005-12-12 12:30

PlanetKDE, point me in some direction, please!

I have been trying to figure out how to use libsmoke from kdebindings for a few hours.

So far, I have been unable to find any kind of documentation whatsoever.

If anyone knows of something (short of reading the ruby bindings, for instance ;-) I would be thrilled.

Addendum: I just found on google that there is a whole language with Qt bindings I had never heard of!


Contents © 2000-2019 Roberto Alsina