Tuesday Feb 08, 2005

C++, name mangling and function signatures

One of the guys in the office was having problems compiling POVray on solaris - the last link step kept failing with an unknown symbol. It turns out that the POVray source had declared the symbol in the .h file using the pattern:
int function(const char \*, unsigned int);
but defined the function in the .cpp file using the pattern:
int function(const char \*, const unsigned int) {}
When it came to the link step, the compiler complained that other object files referencing the function could not link, because the function as declared was not in any of the linked object files. We can easily ascertain this fact using the nm utility. To more easily see the differences, you need to use the -C option, which performs the neat-oh C++ name demangling.
The example code is here. Which demonstrates this issue precisely.
First we compile, but do not link the .cpp files. To perform this step do: CC -c \*.cpp.
% nm -C \*.o


[Index]   Value      Size    Type  Bind  Other Shndx   Name

[2]     |         0|      25|FUNC |GLOB |0    |2      |int function(const char\*,const unsigned)
[1]     |         0|       0|FILE |LOCL |0    |ABS    |inter.cpp


[Index]   Value      Size    Type  Bind  Other Shndx   Name

[4]     |         0|       0|NOTY |GLOB |0    |ABS    |__fsr_init_value
[2]     |         0|       0|FUNC |GLOB |0    |UNDEF  |int function(const char\*,unsigned)
[1]     |         0|       0|FILE |LOCL |0    |ABS    |link.cpp
[3]     |         0|      41|FUNC |GLOB |0    |2      |main
without the demangling, you would see the function names in brackets, which are not easy to distinguish, but once you add in the demangling the difference is obvious. Of course even if you #included the .h file into the .cpp file to 'type enforce' the function signature, because to the compiler they are not the same function it simply presumes that the declaration in the .h file is for another function.
It is annoying, but you have to pay attention to these things when you're writing C++.
Contrast the patterns observed when we compile using the gnu compiler: g++ -c \*.cpp
% nm -C \*.o


[Index]   Value      Size    Type  Bind  Other Shndx   Name

[2]     |         0|       0|SECT |LOCL |0    |1      |
[3]     |         0|       0|SECT |LOCL |0    |2      |
[5]     |         0|       0|SECT |LOCL |0    |4      |
[4]     |         0|       0|SECT |LOCL |0    |3      |
[6]     |         0|      10|FUNC |GLOB |0    |1      |function(const char\*, unsigned)
[1]     |         0|       0|FILE |LOCL |0    |ABS    |inter.cpp


[Index]   Value      Size    Type  Bind  Other Shndx   Name

[5]     |         0|       0|SECT |LOCL |0    |5      |
[2]     |         0|       0|SECT |LOCL |0    |1      |
[3]     |         0|       0|SECT |LOCL |0    |3      |
[4]     |         0|       0|SECT |LOCL |0    |4      |
[6]     |         0|       0|SECT |LOCL |0    |7      |
[9]     |         0|       0|NOTY |GLOB |0    |UNDEF  |__gxx_personality_v0
[8]     |         0|       0|NOTY |GLOB |0    |UNDEF  |function(const char\*, unsigned)
[1]     |         0|       0|FILE |LOCL |0    |ABS    |link.cpp
[7]     |         0|      53|FUNC |GLOB |0    |1      |main
Who is correct at this step? I don't quite have the answer for that, as if you tried to define two functions that differed by that const, the compiler has no way to disambiguate them. If you're being really picky, you could say that dropping the const against standard pass-by-value variables is incorrect; after all it's a function taking a const, and it's declaration should respect that.

Monday Feb 07, 2005

The curse of make (svr)

Sounds like it should be a movie title. The curse of the black-hearted and arcane makefiles.
On one of our projects we're constantly adding subdirectories of tests that need to be picked up by the harness. Somebody started to place the names of the subdirectories in a variable in the Makefile, so it looked like:
SUBDIRS=acl mount umount
This is all very fine and well until we start having 50+ directories, and a policy which states that the subdirectories have to be kept in order and that you can't exceed 80 characters per line (queue lots and lots of backslashes and rearranging of lines every time you add a new subdirectory). This is sub-optimal, so I changed the SUBDIRS variable like so:
SUBDIRS :sh =  /bin/ls \*/[Mm]akefile | sed -e '@/[Mm]akefile@@g'
The :sh tells make that the SUBDIRS variable is set by the output from the ls command filtered through the sed command. Short and simple.
Welcome to the world of development. Sometimes we need to EXCLUDE certain subdirectories - the functionality isn't there (it happens), the command line options change (ui reviews cause this a lot).
Fixing this is a bit more difficult. The first try is normally:
This does not work!!!!
ALL_SUBDIRS :sh =  /bin/ls \*/[Mm]akefile | sed -e '@/[Mm]akefile@@g'

SUBDIRS :sh = \\
        for all in $(ALL_SUBDIRS); do \\
                for excl in $(EXCLUDE_SUBDIRS); do \\
                        if [ "$excl" = "$all" ]; then \\
                                all=""; \\
                        fi; \\
                done; \\
                echo $all; \\
The reason why this does not work is that at the first pass, the variables ALL_SUBIRS and EXCLUDE_SUBDIRS are not set (it's a make thing), so what we need to do is add a level of indirection to the solution.
ALL_SUBDIRS :sh =  /bin/ls \*/[Mm]akefile | sed -e '@/[Mm]akefile@@g'

FOO = \\
        for all in $(ALL_SUBDIRS); do \\
                for excl in $(EXCLUDE_SUBDIRS); do \\
                        if [ "$excl" = "$all" ]; then \\
                                all=""; \\
                        fi; \\
                done; \\
                echo $all; \\

This works, but it contains a subtle bug! If you don't have any entries in EXCLUDE_SUBDIRS, then the expansion fails with a syntax error: sh: syntax error at line 1: `;' unexpected . This error is caused as the code: for var in <>; do requires a token, so we have to change it once more:
ALL_SUBDIRS :sh =  /bin/ls \*/[Mm]akefile | sed -e '@/[Mm]akefile@@g'

FOO = \\
        for all in $(ALL_SUBDIRS); do \\
                for excl in $(EXCLUDE_SUBDIRS) WILLNEVERMATCH; do \\
                        if [ "$excl" = "$all" ]; then \\
                                all=""; \\
                        fi; \\
                done; \\
                echo $all; \\

but this means that we need to go through the loop even if EXCLUDE_SUBDIRS is never set, so more changes:
ALL_SUBDIRS :sh =  /bin/ls \*/[Mm]akefile | sed -e '@/[Mm]akefile@@g'

FOO = \\
        if [ -n "$EXCLUDE_SUBDIRS" ]; then \\
                for all in $(ALL_SUBDIRS); do \\
                        for excl in $(EXCLUDE_SUBDIRS) WILLNEVERMATCH; do \\
                                if [ "$excl" = "$all" ]; then \\
                                        all=""; \\
                                fi; \\
                        done; \\
                        echo $all; \\
                done; \\
                echo $(ALL_SUBDIRS); \\

This has two lessons associated with it.
  • Make is not easy
  • remember to test all the variants of a solution

Thursday Feb 03, 2005

What file is playing under xmms

Here's a simple program that determines what file is playing under xmms. Short, sweet and to the point. You need the xmms headers and libintl, but apart form those it should compile just dandily (big ifs of course).
#include <sys/types.h>
#include <xmmsctrl.h>
#include <glib.h>
#include <stdio.h>

main(int argc, char \*\*argv)
        gint session = 0;
        gchar \*file;
        gint pos;

        if (xmms_remote_get_version(session) == 0) {
                return (0);

        pos = xmms_remote_get_playlist_pos(session);

        file = xmms_remote_get_playlist_file(session, pos);
        if (file != NULL) {
                printf("%s\\n", file);

Wednesday Feb 02, 2005

Symbolic annoyance, preprocessor hints and tips

One of the guys in the office was having a compilation problem (hey tim!). Neither gcc nor sun cc were being (too) helpful in their error messages.
The piece of code in particular was a function declaration like:
void foo(food_t \*);
When it was compiled both sun cc said:"foo.c", line 4: syntax error before or at: \*, and gcc said: foo.c:4: error: syntax error before '\*' token which is not a particularly useful error message. Even when the code is compiled with full warnings (-v for sun cc, -Wall for gcc) we get the same useless error message.
Experience tells us that this means that the data type food_t has not been declared, as a result the food_t \* parses as a syntax error.
This was part of a (moderately) complicated piece of code, we knew the data type was being declared in one header file, but didn't know if that file was being included in this particular piece of code. In cases like this the preprocessor is a boon. We knew that a header file foo.h was needed so send in the preprocessor. Using the -E option to gcc (or sun cc), it only preprocesses the file, producing a stream of output.
Interpreting the output is a quite easy. In this case we were looking for a typedef unsigned int food_t;, which would tell us that the variable datatype had been defined. In this case it had not, meaning, in our case that the header file foo.h had not been included as part of the foo.c file. We #included the file, and lo and behold we made it to the next step.
I've grossly simplified the example to illustrate a point, but invariably when you encounter syntax errors in your c files, and are stumped for the reason why, it's only one of the many tools in your arsenal for finding out the reason why. After all what kind of ill effects are being induced in your code as a result of using complicated #define macros.

Thursday Jan 27, 2005

playing around with mice and windows

Updated: 2005/02/03 - it didn't build so well on Linux.
This is a bit of code for Solaris which allows you to:
  • Move your mouse around the screen
  • Resize the window under the mouse
  • Move the window under the mouse
Build instructions
SolarisLinux (mandrake 10 tested)
cc -v -I/usr/openwin/include -L/usr/openwin/lib -R/usr/openwin/lib -o movemouse movemouse.c -lX11 -lXmu gcc -DLINUX -W -I/usr/X11R6/include -L/usr/X11R6/lib -o movemouse movemouse.c -lX11 -lXmu

The meaning of the -R/usr/openwin/lib for solaris is to set the run-time linker lookup to try the /usr/openwin/lib path for libraries, as part of the symbol resolution step. Otherwise it relies on either the LD_LIBRARY_PATH (It is evil), or the crle path (man crle - it is neat).

For Linux you normally have the /etc/ld.so.conf file, which typically has /usr/X11R6/lib in there (and if you've got qt, probably has /usr/lib/qt3/lib as well). If you want to find out more abou tthis then look at the info/man page for ldconfig.

I have this code for window managers that don't have mouse/window movement shaping hotkeys of their own (e.g. fvwm). It makes the gnome window managers almost useful. It should be portable to other X based OS's.
It comes with the usual preamble - use it at your own risk, if it eats your cat then it's not mine (or Sun's) fault.

#include <alloca.h>
#include <libgen.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#if defined(LINUX)
#include <string.h>
#include <stdint.h>
#include <sys/types.h>
#include <unistd.h>
#include <X11/X.h>
#include <X11/Xlib.h>

static char \*appname;

struct systeminfo {
        int rootx;
        int rooty;
        int width;
        int height;
        int winx;
        int winy;
        Display \*display;
        Window rootwin;
        Window undermouse;
        XWindowAttributes childattr;

static char \*actionhelp();

static void
usage(int exitcode)
        (void) printf("usage: %s {<action> [+-]<valx>[%%] [+-]<valy>[%%] }+ \\n",
        (void) printf("\\tif you use +- it is taken as a delta, otherwise it is"
                      " absolute\\n");
        (void) printf("Where action is one of %s\\n", actionhelp());
        (void) printf("\\tFor example %s mm 0 0 would move the mouse to the "
                      "\\n\\ttop left of the screen\\n", appname);

static int
XError(Display \*disp, XErrorEvent \*code)
        char buffer[2000];
        XGetErrorText(disp, code->error_code, buffer, 1999);
        (void) printf("Error: %s\\n", buffer);
        return (0);

static int isdelta(const char \*value) {
        return ((\*value == '-') || (\*value == '+'));

static int isperc(const char \*value) {
        return ((strrchr(value, '%') - value) == (strlen(value) - 1));

static int transform(const char \*val, int start, int maxMetric) {
        int av = atoi(val);
        /\*(void) printf("%s %d %d %d\\n", val, av, start, maxMetric);\*/
        if (isdelta(val) && isperc(val))
                return (start + (int)(maxMetric \* av / 100));
        if (isdelta(val))
                return (start + av);
        if (isperc(val))
                return ((int)(maxMetric \* av / 100));
        return (av);

static void getxy(struct systeminfo \*info, const char \*xarg,
        const char \*yarg, int \*xval, int \*yval, uintptr_t offx,
        uintptr_t offy) {
        \*xval = transform(xarg, \*((int \*)((char \*)info + offx)),
                isperc(xarg) && isdelta(xarg) ? info->width :
        \*yval = transform(yarg, \*((int \*)((char \*)info + offy)),
                isperc(yarg) && isdelta(yarg) ? info->height :
        /\* (void) printf("%d %d\\n", \*xval, \*yval); \*/

static void MoveMouse(struct systeminfo \*info, int xarg, int yarg) {
        XWarpPointer(info->display, None, info->rootwin, 0, 0, 0, 0,
                xarg, yarg);

static void ResizeWindow(struct systeminfo \*info, int xarg, int yarg) {
        XResizeWindow(info->display, info->undermouse, xarg, yarg);

static void MoveWindow(struct systeminfo \*info, int xarg, int yarg) {
        XMoveWindow(info->display, info->undermouse, xarg, yarg);

#define funtostring(FV) #FV, FV

static struct activity {
        char \*action;
        char \*helpstring;
        void (\*function)(struct systeminfo \*, int, int);
        uintptr_t offsetx;
        uintptr_t offsety;
} activitylist[] = {
        { "mm", funtostring(MoveMouse),
                offsetof(struct systeminfo, rootx),
                offsetof(struct systeminfo, rooty) },
        { "rw", funtostring(ResizeWindow),
                offsetof(struct systeminfo, width),
                offsetof(struct systeminfo, height) },
        { "mw", funtostring(MoveWindow),
                offsetof(struct systeminfo, winx),
                offsetof(struct systeminfo, winy) },
        { NULL, NULL, NULL }

static char \*actionhelp(void) {
        static char \*buffer = NULL;
        char \*stribo;
        struct activity \*ack = activitylist;

        stribo = alloca(1024);

        if (buffer != NULL)
                return (buffer);
        buffer = calloc(1, getpagesize());
        while (ack->action != NULL) {
                (void) sprintf(stribo, "%s(%s) ", ack->action, ack->helpstring);
                (void) strcat(buffer, stribo);
        return (buffer);

extern Window XmuClientWindow(Display \*, Window);

static void InitializeWinfoid(struct systeminfo \*winfoid) {
        unsigned int mask;
        winfoid->rootwin = XDefaultRootWindow(winfoid->display);
        XQueryPointer(winfoid->display, winfoid->rootwin,
                &winfoid->rootwin, &winfoid->undermouse,
                &winfoid->rootx, &winfoid->rooty,
                &winfoid->winx, &winfoid->winy, &mask);
        XGetWindowAttributes(winfoid->display, winfoid->undermouse,
        winfoid->winx = winfoid->childattr.x;
        winfoid->winy = winfoid->childattr.y;
        winfoid->undermouse = XmuClientWindow(winfoid->display,
        XGetWindowAttributes(winfoid->display, winfoid->undermouse,
        winfoid->width = winfoid->childattr.width;
        winfoid->height = winfoid->childattr.height;

main(int argc, char \*\*argv)
        struct systeminfo winfoid;
        int atarg;
        int thisx, thisy;

        char \*disp;

        appname = basename(strdup(argv[0]));


        if ((argc < 3) || ((argc - 1) % 3)) usage(1);

        disp = XDisplayName(NULL);
        if (disp == NULL) disp = ":0.0";
        winfoid.display = XOpenDisplay(disp);
        if (winfoid.display == NULL) {
                (void) fprintf(stderr, "Can't open display %s\\n", disp);
        /\* handle arguments \*/
        for (atarg = 1; atarg < argc; atarg += 3) {
                struct activity \*atac = activitylist;
                while (atac->action != NULL) {
                        if (strncmp(atac->action, argv[atarg], 2) == 0)
                if (atac->action != NULL) {
                        getxy(&winfoid, argv[atarg+1], argv[atarg+2],
                                &thisx, &thisy, atac->offsetx, atac->offsety);
                        atac->function(&winfoid, thisx, thisy);

        return (0);

fuser trickiness

I encountered this one a bit over a year ago while having a conversation with Ed in MPK (or is it a year and a half?). The fuser command is used to list the processes using a file system - fuser /home/bubba would list all the users of /home/bubba; and you can kill them too, using the -k option. So far so good.
What happens, though when you want to do something with the output?
haiiro[64i]~% fuser /home/bubba
/home/bubba:     6780c    6760c    6758c    6756c    6502c    6422c    6404c   6368c    6332c
6331c    6330c    6322c    6308c    6301c    6206c    6081c    5951c    5879c    5756c    5251c
5247c    5201c    5092c    5004c    4992c    4987c    4985c    4982c    4980c    4968c    4952c
4920c    4672c    4080c    4079c    3990c    3989c    3987c    3985c    3983c    3981c    3979c
3975c    3973c    3969c    3963c    3943c    3939c    3937c    3935c    3898c    3896c    3893c
3891c    3882c    3880c    3836c    3834c    3831c    3769c    3726c
it's a bit hard to parse? not so! What you can do is redirect stderr to /dev/null and magic happens
haiiro[64i]~% fuser /home/bubba 2>/dev/null
6781    6760    6758    6756    6502    6422    6404    6368    6332    6331    6330
6322    6308    6301    6206    6081    5951    5879    5756    5251    5247    5201
5092    5004    4992    4987    4985    4982    4980    4968    4952    4920    4672
4080    4079    3990    3989    3987    3985    3983    3981    3979    3975    3973
3969    3963    3943    3939    3937    3935    3898    3896    3893    3891    3882
3880    3836    3834    3831    3769
No parse issues there. Then you can get full process information on each of the processes using a small bit of shell:
haiiro[64i]~% ps -o pid,args -p "$(fuser /home/bubba 2>/dev/null)"
 6206 /usr/openwin/bin/xterm -geom 80x25 -e /bin/zsh
 3943 /usr/lib/evolution/1.4/evolution-alarm-notify --sm-config-prefix /evolution-ala
 3939 nautilus --sm-config-prefix /nautilus-udaGgf/ --sm-client-id 11819cee2200010909
 3935 metacity --sm-save-file 1106677632-2610-3195804027.ms
 3880 /bin/ksh /usr/dt/config/Xsession2.jds
 3834 /usr/dt/bin/sdt_shell -c      unset DT;     DISPLAY=:0;       /usr/dt/bin/dt
Just what the doctor ordered for a sysadmin.

Wednesday Jan 26, 2005

Exit status from commands in a pipeline (ksh)

It's already answered in the comp.unix.shell FAQ, so stop asking me!
For the most part it's the slightly more compatible shells that require some work. As the ksh that ships in Solaris needs to be compatible with practically everything it's a bit tough to insist that people convert their scripts to languages that may not be present on all their machines.
The one I learned (moons ago) was to execute each of the elements in the background, and then wait on the exit status of each of the processes one by one.
mkfifo /tmp/fifo1
tar cf /tmp/fifo1 /blob&
gzip -c /tmp/fifo1 >compressed.tar.gz&
wait $pid1
if (( tarstatus != 0 )); then
    echo "tar command exited with: $tarstatus"
wait $pid2
if (( gzipstatus != 0 )); then
    echo "gzip command exited with: $gzipstatus"
I'm never using this ever again!

Friday Nov 05, 2004

Solaris on laptops and summer/winter time

Well this one really caught me by surprise. I use Solaris on my laptop and since the the timezone changed I've noticed that the clock was out by an hour when I rebooted from Solaris to another operating system. It turns out that Solaris uses a configuration file - /etc/rtc_config to determine the number of seconds offset that your bios clock is offset relative to GMT. Solaris uses GMT on the inside, all dates and times presented to the user are manipulated via the TZ variable (so you can screw around with date displays using TZ=<foo> date).
There is a cron job for root, which runs at 2:01 every morning which executed /usr/sbin/rtc -c, which reconfigures the zone_lag entry in the /etc/rtc_config file. For some strange and unusual reason (call it sanity) my laptop is not powered on at this late time of night (think beer) so I was regularly an hour out of sync with the real world. xntp would kick in and the clock would be resynced with the real world until I rebooted into another OS where it would be incorrect. The fix is of course to execute the /usr/sbin/rtc -c program from the command line and when ntp next kicks off all is right with the world. It's a gotcha.

Wednesday Oct 27, 2004

Copy on Write

Well I suppose it had to happen. Someone in the office asked me what was Copy on Write(aka COW) and what was the point of it.
To put it succinctly, Copy on Write is where you share a page until some destructive activity is performed on it, whereupon the writing process is given a new private copy (well duh!) of the page which is no longer set to copy-on-write, all the other processes that are sharing the page keep the shared, unchanged page themselves.
Implementations of copy on write typically mark a page as un-writeable, and when the page is written to it causes an access fault. Because the page is marked as a COW page the OS knows to deal with it differently than an ordinary access fault. It creates a new page that contains the content of the old page, removes the COW bit from this page and marks it read/writeable then returns control to the process at the same address that caused the access fault. This causes the code to execute this instruction again, writing the data to this new private copy. If it didn't do this then you would not have the correct data in this location in only the new copy.
Why do we do this? It's quite simple really - forking. Every time you fork it needs to duplicate the address space of the parent process in the child. If you made a complete copy of all the data pages every time you forked this would amount to a large amount of memory that has to be kept around just to deal with memory that may or may not be used in the sub-process. If you consider that a whole truck load of forks are simply just to perform an exec of a new process the creation and teardown of the data is an horrible waste of processor time and resources so we don't do this.
This leads to an interesting problem. If you have, say 50 processes, each with a 1M COW segment you need to ensure that you have 50M of available swap just in case. Ever wonder what the difference between the allocated and reserved swap was? These COW pages are just another part of this equation.

Wednesday Sep 08, 2004

dtrace - ticks!

I have a lot of fun when I see Bryan making his dtrace demos. They're all cool, he says type this in: dtrace -i 'syscall:::entry { @[execname] = count() }', press enter, wait a few seconds and then hit control-c. You then get a result of the aggregation - the number of system calls per process over the time in question.
I'm a neat freak, A couple of seconds plus or minus a half a second is useless to me, I crave precision. That's why the tick provider is so cool.
Say I want a trace to run for 5 seconds, I add 'tick-5sec { exit(0) }' to the end of my script or command and after 5 seconds all the gathered data is output.
you can simply profile what a system is doing by 'tick-97hz { @[stack()] = count() }', which shows what happens in line with the clock.
That's not all, you can output data points every 'tick' intervals, so for example if you are gathering the system call information, and want to output the call count every 5 seconds you can do: 'syscall:::entry { @[probefunc] = count() } tick-5sec { printa(@); cleara(@); }', which gives you the system call count every 5 seconds - just like iostat would give you the io count every 5 seconds or mpstat would give you the processor information every 5 seconds.... Easily accomplished in a few lines.
Dtrace... it slices, it dices, it makes coffee in the morning (and it will need to for me as it's 2am).

Preparation is the key

I have a shiney laptop, and I was planning on showing off a 64bit operating system running on it for my last dtrace presentation, so making sure I had the latest and greatest AMD64 bits, I started to prepare a few scripts that would allow me to show off what dtrace can do. I fired up one of my most complicated examples and bang my laptop rebooted.
It's just as well this was in my pre-flight test mode and not in front of everyone, because heaven knows, you don't need to have it all go horribly wrong in front of everyone.
As a preparation freak, I of course have the latest clean build of Solaris on an alternate boot environment on the laptop, so I quickly swapped unstable for stable to ensure that the demo went without a hitch.
Live upgrade, yet another reason why Solaris rules!

Friday Sep 03, 2004

AMD64 - compilation flag differences from cc to gcc

Well I've been asked more than once for this, so I decided that a publicly accessible version would help people.
Purposesun ccgnu cc
Compile in 64bit mode-xarch=<sparcv9|amd64> (-xarch=generic64 soon)-m64 (gcc really wins here!)
Compile a kernel module-D_KERNEL-D_KERNEL -mcmodel=kernel
Multi-Threaded-D_REENTRANT (stop using -mt in S10)-D_REENTRANT
Warnings-v-W or -Wall (mini lint)
Warnings are errors-errwarn=%all-Werror
Build a shared object (.so)-G-shared
Accept C++ style comments-xCCNone (does it automatically)
Position independent code-K PIC (32bit addresses - longer sparc instructions) -K pic (not 32bit addresses)-fpic
There are a few coding gotchas - string constants in gcc get into the .text segment of the application, not the data segment, thus you are not permitted to alter them. Occasionally I find that the final link step of gcc fails with relocation errors - the code segment ends up addressing > 32bit values within itself, by adding -fpic to the compilation flags it works just fine (it's been the occasional test with char foo[MAX_LONG>>1]; in the global segment causing this).

Thursday Sep 02, 2004

The wonderful world of AMD64

I already have the laptop capable of doing that (an Acer Ferrari 3200), so part of my work consists of getting the latest and greatest development bits onto it, and running. I get a tingle from executing isainfo -b, and get a result of 64.
Some people may think that this is lame (yes, Fintan, I mean you), but it means that I can test and verify all kinds of things in my time travelling into and out of the office by train. For example, this morning I verified that the read-only code for ext2 works just fine on 64bits. Mind you for fine read it works for a find /mntpt -type f -exec od {} \\| head 2 \\;, which for me is about as much testing as I can afford to spend on it. It needed a couple of compile flag changes, to support 64bit mode (-m64 | -xarch=amd64) but the code was just fine - a testament to the open source folks.
Another verification was a sound driver - I checked out the tools.de via82xx driver, which seemed to be the most appropriate one for the laptop, made some minor changes to the Makefiles (it didn't try to make 64bit drivers) and again I was treated with the witty remarks of Eddie Izzard; but this time it was running over a 64bit kernel.
Now if only my work was this easy :)

Tuesday Aug 03, 2004

automount gotchas

Solaris's automount is great, but it's a tad confusing.
for instance the default /etc/auto_master line has an entry reading:
which translates to 'look up the other choices here'.
If your /etc/nsswitch.conf entry for automount reads:
automount: files nis
This means that it will look up nis at this point before continuing. If this entry is at the start of the auto_master file this means that NIS entries win over files entries, so if we have a nis map which specifies different options to mounting, such as -nobrowse, then this option is used instead of the option in the file.
In SunOS 4.x days all the automount maps were dot-delimited, so we had entries like auto.home and auto.master, but when NIS+ was introduced this became a problem, as this was a map called auto in a nis branch called home, so the default maps were relabelled to use underscores instead of periods.
Of course we don't want to break backwards compatibility with SunOS 4.x nis servers, so when you request an underscore delimited map under NIS if it doesn't find the map, it will try to find a period delimited map of the same form.
For example /etc/auto_master reads:
/net            -hosts          -nosuid,nobrowse
/home           auto_home       -nobrowse
Then lookups will search the ypmap auto_master (and if that map is missing then auto.master), so if you have a line in NIS stating: /home auto.home, it will override the entry in /etc/auto_master that says: /home auto_home, so if you have an /etc/auto_home file, it will not be considered when searching for entries in /home.

Wednesday Jul 21, 2004

Korn shell arrays

Korn shell supports arrays. You can have about 4000 elements in a korn shell array, and the performance is not great, but they're quite handy for walking through sets of variables (like paths).
The syntax is:
set -A <array> [values...]
This will set the array named to the values passed, clearing the variable before setting. This is fine, but what if we're simply appending to an array? Lots of folks want to do that. In this case what you use is:
set +A <array> [values...]
All very fine and well, but how do we reference the values in the array? You use the variable[<index>] syntax.
What about getting all the values from the array? simply use either @ or \* as the index.
Then there's dereferencing it. Dereferencing the entry involves using ${array[index]}, otherwise the $ would evaluate up to the left bracket, and not evaluate the content of the array, such is the grammar of ksh.
Putting this all together into something useful (like a gcc wrapper for gcc specific options)
set -A arguments -- "$@"
typeset -i index=0
while [[ -n ${arguments[$index]} ]]; do
	case ${arguments[$index]} in
	index=$((index + 1))

cc -xCC "${arguments[@]}"



« April 2014