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/ 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);

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 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

Liveupgrade - the script

I use the Script to upgrade my machine whenever a new build comes out of Solaris. It allows me to not have to remember to issue the necessary upgrade commands.
It illustrates the use of the live upgrade cli commands to automate the upgrading of a machine into a pre-configured alternate boot environment.

Tuesday Jul 13, 2004

ext2fs - readonly access for Solaris Express

While having lunch today I decided to port the read-only version of ext2fs for Solaris Express. Not a load of changes (in my book), and it seems to work correctly. I've posted the updated driver back to the original porter of the code, so it should be appearing on soon enough. I posted an internal copy for those that want it inside the wall too.

Monday Jul 12, 2004


Overlay mountpoints

Generally when you mount a file system at a certain point, it prohibits you from mounting other file systems to that point. This is normally not a problem, as you should normally never need to mount another resource over the previous mount point. However there can be times that this is your only option - for example replacing a device with a new one.
To Accomplish this we use the overlay (-O) option to mount. For example we have a mount point /opt/silly, containing a local device and it's corresponding data:
# df -h /opt/silly
Filesystem             size   used  avail capacity  Mounted on
/dev/dsk/c7t0d0s0       22G   9.8G    12G    47%    /opt/silly
if we try to mount another file system at this point:
# mount trex:/export/web /opt/silly
nfs mount: mount: /opt/silly: Device busy
but we can add the -O flag:
# mount -O trex:/export/web /opt/silly
and then:
# df -h /opt/silly
Filesystem             size   used  avail capacity  Mounted on
trex:/export/web       8.1G   6.9G   1.0G    87%    /opt/silly
But there are actually two file systems mounted there:
# grep opt/silly /etc/mnttab
/dev/dsk/c7t0d0s0       /opt/silly      ufs     rw,intr,largefiles,logging,xattr,onerror=panic,dev=8002d0       1089624975
trex:/export/web        /opt/silly      nfs     rw,xattr,dev=44000ae    1089625076



« July 2014