rentzsch.com: tales from the red shed

mod_throttle on Mac OS X

Mac OS X
Ole Eichhorn wrote an article about "slashdot sunscreen" based on when his "tyranny of email" was slashdotted. I read with particular interest his mention of mod_throttle, an apache module which allows control over web server activity.

There are no binaries for Mac OS X, so I pulled down the source. Uh-oh, source-level tweaking is required. I first have to choose between USE_POSIX_SERIALIZATION, USE_FCNTL_SERIALIZATION, USE_FLOCK_SERIALIZATION and USE_SYSTEM_V_SERIALIZATION. Next, I have to choose between USE_POSIX_SHARED_MEMORY and USE_SYSTEM_V_SHARED_MEMORY. There's also a #define in there named HAVE_GETPWENT. Fortunately man getpwent spits out a page acting like the functions are availible, so I can at least leave the latter alone, set to 1.

First I try USE_POSIX_SERIALIZATION/USE_POSIX_SHARED_MEMORY. While this compiled cleanly, when starting apache I got the following error:

[emerg] (22)Invalid argument: Failed to map the shared object.

Next up is USE_POSIX_SERIALIZATION/USE_SYSTEM_V_SHARED_MEMORY, which also compiled but yielded the following error:

critical_create(): cannot initialise unnamed semaphore: Function not implemented

OK, so POSIX's out of the question. POSIX is more modern than System V APIs so it was worth a shot. I thought Mac OS X was POSIX-studly, guess I was wrong. Let's try the default System V settings: USE_SYSTEM_V_SERIALIZATION/USE_SYSTEM_V_SHARED_MEMORY. Rats, compiler error:

mod_throttle.c:726: redefinition of `union semun'

Looking at the code referenced by the compiler error yielded this insight:

#if (defined(__GNU_LIBRARY__) && (!defined(_SEM_SEMUN_UNDEFINED))) || defined(__FreeBSD__) || defined(__NetBSD__)
/* union semun is defined by including <sys/sem.h> */
#else
/* X/OPEN says we have to define it ourselves (twits). */
union semun {
  int val;      /* value for SETVAL */
  struct semid_ds *buf;    /* buffer for IPC_STAT, IPC_SET */
  unsigned short int *array;  /* array for GETALL, SETALL */
  struct seminfo *__buf;    /* buffer for IPC_INFO */
};
#endif

Ah yes, twits at work again. But Mac OS X ain't no X/OPEN, so another special-case is needed:

#if (defined(__GNU_LIBRARY__) && (!defined(_SEM_SEMUN_UNDEFINED))) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE_CC__)
/* union semun is defined by including <sys/sem.h> */
#else
/* X/OPEN says we have to define it ourselves (twits). */
union semun {
  int val;      /* value for SETVAL */
  struct semid_ds *buf;    /* buffer for IPC_STAT, IPC_SET */
  unsigned short int *array;  /* array for GETALL, SETALL */
  struct seminfo *__buf;    /* buffer for IPC_INFO */
};
#endif

Bingo, clean compile, install, and apache happily starts. I added the following lines to my httpd.conf file:

# Put this AFTER the preexisting LoadModules series.
LoadModule throttle_module libexec/httpd/mod_throttle.so

# Put this AFTER the preexisting AddModules series.
AddModule mod_throttle.c

# Put this any place after LoadModule/AddModule.
<IfModule mod_throttle.c>
  ThrottlePolicy request 15 5s

  <Location /throttle-status>
    SetHandler throttle-status
  </Location>
</IfModule>

It's important that those LoadModule/AddModule lines come at the end of the series -- it didn't work if they were first. To save you the pain of trying to compile mod_throttle yourself, I'm posting my binary.

Update: Chris Ridd tells me I should check for __MACH__ being defined, not __APPLE_CC__. The latter simply tests for Apple's compiler, not Mach itself (which underlies Mac OS X). Thanks, Chris. By the way, cute kid, love the name.

Update: David La Monaca writes, informing me that if you want to deploy mod_throttle across multiple virtual hosts, you need to add a ThrottlePolicy directive per each <VirtualHost>.

Tuesday, April 01, 2003
12:00 AM