]> err.no Git - sope/blob - Recycler/mod_objc/GSBundleModule.m
use %p for pointer formats
[sope] / Recycler / mod_objc / GSBundleModule.m
1 // $Id: GSBundleModule.m,v 1.1 2004/06/08 11:15:59 helge Exp $
2
3 #include "httpd.h"
4 #include "http_config.h"
5 #include "http_log.h"
6 #include "ap_config.h"
7
8 #include "ApacheCmdParms.h"
9 #include "ApacheServer.h"
10 #include "ApacheModule.h"
11 #include "ApacheResourcePool.h"
12 #import <Foundation/Foundation.h>
13
14 /*
15   Note:
16   
17   an Apache module gets *unloaded* during the config process !!!
18
19   This is why we have this helper shared object which is not unloaded :-)
20 */
21
22 @interface NSObject(ModuleClass)
23 + (void)setBundleHandler:(id)_handler;
24 - (module *)apacheModule;
25 @end
26
27 static void _ensureObjCEnvironment(void) {
28 #if LIB_FOUNDATION_LIBRARY
29   extern char **environ; /* man 5 environ */
30   static char *argv[2] = { "apache", NULL };
31   [NSProcessInfo initializeWithArguments:argv
32                  count:1
33                  environment:environ];
34 #endif
35 }
36
37 @interface ApBundleInfo : NSObject
38 {
39 @public
40   ApacheResourcePool *configPool;
41   NSString     *bundlePath;
42   NSString     *bundleName;
43   NSBundle     *bundle;
44   module       *apacheModule;
45   ApacheModule *bundleHandler;
46   BOOL         moduleAdded;
47   BOOL         bundleLoaded;
48 }
49
50 + (NSString *)makeBundlePathAbsolute:(NSString *)_relpath;
51 + (ApBundleInfo *)bundleInfoForPath:(NSString *)_relpath;
52
53 /* accessors */
54
55 - (NSBundle *)bundle;
56 - (NSString *)bundleModuleClassName;
57 - (Class)bundleModuleClass;
58
59 - (BOOL)isBundleLoaded;
60 - (BOOL)isApacheModuleInitialized;
61
62 /* operations */
63
64 - (NSString *)setUpWithArgs:(NSString *)_args
65   inPool:(ApacheResourcePool *)_pool;
66 - (void)tearDown;
67
68 - (void)configureForServer:(ApacheServer *)_server;
69
70 @end
71
72 @implementation ApBundleInfo
73
74 + (ApBundleInfo *)bundleInfoForPath:(NSString *)_relpath {
75   static NSMutableDictionary *pathToInfo = nil;
76   NSString     *p;
77   ApBundleInfo *bi;
78   
79   p = [self makeBundlePathAbsolute:_relpath];
80   if ([p length] == 0) return nil;
81   
82   if (pathToInfo == nil)
83     pathToInfo = [[NSMutableDictionary alloc] initWithCapacity:16];
84
85   if ((bi = [pathToInfo objectForKey:p]))
86     return bi;
87   
88   if ((bi = [[self alloc] initWithPath:p])) {
89     [pathToInfo setObject:bi forKey:p];
90     RELEASE(bi);
91   }
92   return bi;
93 }
94 + (NSString *)makeBundlePathAbsolute:(NSString *)_relpath {
95   NSString *s;
96   
97   if ([_relpath length] == 0) return nil;
98   
99   s = [[[NSProcessInfo processInfo]
100                        environment]
101                        objectForKey:@"GNUSTEP_SYSTEM_ROOT"];
102   s = [s stringByAppendingPathComponent:@"Library/Bundles"];
103   s = [s stringByAppendingPathComponent:_relpath];
104   
105   return s;
106 }
107
108 - (id)initWithPath:(NSString *)_path {
109   if ([_path length] == 0) {
110     RELEASE(self);
111     return nil;
112   }
113   if (![_path isAbsolutePath]) {
114     NSLog(@"bundle path '%@' is not absolute ...", _path);
115     RELEASE(self);
116     return nil;
117   }
118   
119   self->bundlePath = [_path copy];
120   self->bundleName =
121     [[[_path lastPathComponent] stringByDeletingPathExtension] copy];
122   self->bundle     = [[NSBundle bundleWithPath:self->bundlePath] retain];
123   
124   if (self->bundle == nil) {
125     NSLog(@"missing bundle at path %@", self->bundlePath);
126     RELEASE(self);
127     return nil;
128   }
129   
130   return self;
131 }
132
133 - (void)dealloc {
134   NSAutoreleasePool *pool;
135
136   pool = [[NSAutoreleasePool alloc] init];
137   RELEASE(self->configPool);
138   RELEASE(self->bundleHandler);
139   RELEASE(self->bundlePath);
140   RELEASE(self->bundleName);
141   RELEASE(self->bundle);
142   RELEASE(pool);
143   [super dealloc];
144 }
145
146 /* accessors */
147
148 - (BOOL)isBundleLoaded {
149   if (self->bundle == nil) return NO;
150   return self->bundleLoaded;
151 }
152 - (BOOL)isApacheModuleInitialized {
153   if (self->apacheModule == NULL) return NO;
154   return self->moduleAdded;
155 }
156
157 - (NSBundle *)bundle {
158   return self->bundle;
159 }
160
161 - (NSString *)bundleModuleClassName {
162   return [self->bundleName stringByAppendingString:@"Mod_Module_Class"];
163 }
164 - (Class)bundleModuleClass {
165   /* the class which manages the module structure (helper class) */
166   return NSClassFromString([self bundleModuleClassName]);
167 }
168
169 - (Class)bundleHandlerClass {
170   /* the class which represents the apache module itself */
171   Class c;
172   
173   if ((c = [self->bundle principalClass]) == Nil)
174     return nil;
175   if (![c isKindOfClass:[ApacheModule class]])
176     return nil;
177   
178   return c;
179 }
180
181 /* operations */
182
183 - (NSString *)setUpWithArgs:(NSString *)_args
184   inPool:(ApacheResourcePool *)_pool
185 {
186   NSAutoreleasePool *pool;
187   Class modClass;
188   
189   /* check pre-conditions */
190
191   if (self->bundle == nil) {
192     return [NSString stringWithFormat:@"%s: missing bundle info",
193                        __PRETTY_FUNCTION__];
194   }
195   if (self->configPool)
196     return @"config pool is already setup";
197   if (self->bundleHandler)
198     return @"bundle handler is already set up !!!";
199   
200   pool = [[NSAutoreleasePool alloc] init];
201
202 #if 0
203   printf("\nSETUP 0x%p (loaded=%s) ...\n", (unsigned int)self,
204          self->bundleLoaded ? "yes" : "no");
205   fflush(stdout);
206 #endif
207   
208   /* Step 0: setup resource pool wrapper */
209   
210   self->configPool = RETAIN(_pool);
211   
212   /* Step 1: Load bundle if not done already ... */
213   
214   if (!self->bundleLoaded) {
215     if (![self->bundle load]) {
216       return
217         [NSString stringWithFormat:@"couldn't load bundle %@", self->bundle];
218     }
219     self->bundleLoaded = YES;
220   }
221   
222   if ((modClass = [self bundleModuleClass]) == Nil) {
223     RELEASE(pool);
224     return [NSString stringWithFormat:
225                        @"did not find bundle module class (name=%@) ...",
226                        [self bundleModuleClassName]];
227   }
228   
229   /* Step 2: Initialize bundle handler object ... */
230   
231   self->bundleHandler = [[[self bundleHandlerClass] alloc] init];
232   if (self->bundleHandler == nil) {
233     RELEASE(pool);
234     return [NSString stringWithFormat:
235                        @"couldn't initialize bundle handler of class '%@'",
236                        [self bundleHandlerClass]];
237   }
238   
239   /* Step 3: Remember bundle handler in module-handler class ... */
240   
241   [modClass setBundleHandler:self->bundleHandler];
242   
243   /* Step 4: add Apache C module structure */
244   
245   if ((self->apacheModule = [modClass apacheModule]) == NULL) {
246     RELEASE(pool);
247     return @"did not find apache module structure of bundle";
248   }
249   if (self->apacheModule->magic != MODULE_MAGIC_COOKIE) {
250     RELEASE(pool);
251     return [NSString stringWithFormat:
252                        @"API module structure of bundle is broken !"];
253   }
254   
255   /* Step 5: register module with apache ... */
256   
257   ap_add_loaded_module(self->apacheModule);
258   self->moduleAdded = YES;
259   
260   /* release pool & done */
261   
262   RELEASE(pool);
263   
264   return nil;
265 }
266
267 - (void)tearDown {
268   NSAutoreleasePool *pool;
269
270   pool = [[NSAutoreleasePool alloc] init];
271
272 #if 0
273   printf("TEARDOWN 0x%p ...\n", (unsigned int)self);
274   fflush(stdout);
275 #endif
276
277   /* Reverse Step 5: unregister module with apache */
278   
279   if ((self->apacheModule != NULL) && self->moduleAdded)
280     ap_remove_loaded_module(self->apacheModule);
281   
282   self->moduleAdded = NO;
283   
284   /* Reverse Step 4: reset apache module structure */
285   self->apacheModule = NULL;
286   
287   /* Reverse Step 3: reset bundle handler in module-handler class ... */
288   [[self bundleModuleClass] setBundleHandler:nil];
289   
290   /* Reverse Step 2: release bundle handler object ... */
291   RELEASE(self->bundleHandler);
292   self->bundleHandler = nil;
293   
294   /* DO NOT reverse Step 1 ... */
295
296   /* Reverse Step 0: release config pool proxy */
297   RELEASE(self->configPool); self->configPool = nil;
298   
299   RELEASE(pool);
300 }
301
302 - (void)configureForServer:(ApacheServer *)_server {
303   if (self->apacheModule == NULL) {
304     NSLog(@"missing bundle module ...");
305     return;
306   }
307   if (self->configPool == NULL) {
308     NSLog(@"missing config pool ...");
309     return;
310   }
311
312 #if DEBUG && 0
313   printf("CONFIGURE 0x%p ...\n", (unsigned int)self);
314   fflush(stdout);
315 #endif
316   
317   ap_single_module_configure([self->configPool handle],
318                              [_server handle],
319                              self->apacheModule);
320 }
321
322 @end /* ApBundleInfo */
323
324 static void unloadModule(void *data) {
325   ApBundleInfo *binfo;
326
327   if ((binfo = (void *)data)) {
328     [binfo tearDown];
329   }
330 }
331
332 static int callCount = 0;
333
334 /* Called when the LoadBundle config directive is found */
335 const char *GSBundleModuleLoadBundleCommand
336 (module *module, cmd_parms *cmd, char *bundlePath)
337 {
338   const char         *result = NULL;
339   ApacheCmdParms     *paras;
340   NSAutoreleasePool  *opool;
341   NSString     *bp, *args;
342   ApBundleInfo *bi;
343   id tmp;
344   
345   _ensureObjCEnvironment();
346   callCount++;
347   
348 #if HEAVY_GSBUNDLE_DEBUG
349   printf("%s: #%i module=0x%p pid=%i "
350          "(cmd=0x%p,bp=0x%p) ...\n",
351          __PRETTY_FUNCTION__, callCount, (unsigned int)module, getpid(),
352          (unsigned int)cmd, (unsigned int)bundlePath);
353   fflush(stdout); fflush(stderr);
354 #endif
355   
356   opool = [[NSAutoreleasePool alloc] init];
357   paras = [[ApacheCmdParms alloc] initWithHandle:cmd];
358   
359   /* separate bundle path from bundle args */
360   
361   tmp = [NSString stringWithCString:bundlePath];
362 #if HEAVY_GSBUNDLE_DEBUG
363   printf("%s: %s\n", __PRETTY_FUNCTION__, [[s description] cString]);
364   printf("  paras: %s\n",  [[paras description] cString]);
365   printf("  server: %s\n", [[[paras server] description] cString]);
366   fflush(stdout); fflush(stderr);
367 #endif
368   
369   /* separate bundle path and load arguments ... */
370   {
371     unsigned idx;
372     
373     if ((idx = [tmp indexOfString:@" "]) == NSNotFound) {
374       bp   = tmp;
375       args = nil;
376     }
377     else {
378       bp   = [tmp substringToIndex:idx];
379       args = [tmp substringFromIndex:(idx + 1)];
380     }
381   }
382   
383   /* makeup absolute bundle path */
384
385   if ((bi = [ApBundleInfo bundleInfoForPath:bp]) == nil) {
386     result = ap_pstrcat(cmd->pool, "bundle at '", [bp cString], "' could "
387                         "not find info !");
388     goto done;
389   }
390
391   if ((tmp = [bi setUpWithArgs:args inPool:[paras pool]])) {
392     result = ap_pstrdup(cmd->pool, [tmp cString]);
393     goto done;
394   }
395
396   /* register cleanup */
397   ap_register_cleanup(cmd->pool, bi, 
398                       (void (*)(void*))unloadModule, ap_null_cleanup);
399   
400   /* run module configuration */
401   [bi configureForServer:[paras server]];
402   
403  done:
404   RELEASE(paras);
405   RELEASE(opool);
406   return result;
407 }