Add to git.
[rws.git] / exec_so.c
1 /* Shared object scripts.
2  * - by Richard W.M. Jones <rich@annexia.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the Free
16  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  *
18  * $Id: exec_so.c,v 1.10 2003/01/31 14:36:22 rich Exp $
19  */
20
21 #include "config.h"
22
23 #include <stdio.h>
24 #include <stdlib.h>
25
26 #ifdef HAVE_DLFCN_H
27 #include <dlfcn.h>
28 #endif
29
30 #ifdef HAVE_FCNTL_H
31 #include <fcntl.h>
32 #endif
33
34 #include <pool.h>
35 #include <hash.h>
36
37 #include <pthr_pseudothread.h>
38 #include <pthr_iolib.h>
39 #include <pthr_http.h>
40 #include <pthr_cgi.h>
41
42 #include "rws_request.h"
43 #include "process_rq.h"
44 #include "errors.h"
45 #include "cfg.h"
46 #include "exec_so.h"
47
48 /* XXX make+ configure should figure this out. */
49 #ifndef __OpenBSD__
50 #define HANDLE_REQUEST_SYM "handle_request"
51 #else
52 #define HANDLE_REQUEST_SYM "_handle_request"
53 #endif
54
55 static shash cache = 0;
56 struct shared_object
57 {
58   void *dl_handle;              /* Handle returned by dlopen(3) */
59                                 /* Pointer to 'handle_request' fn. */
60   int (*handle_request) (rws_request rq);
61   time_t mtime;                 /* Modification time of this file at load. */
62   int use_count;                /* Number of current users. */
63 };
64
65 /* This structure is used when jumping into the handle_request function,
66  * so we can catch errors and return values from this function.
67  */
68 struct fn_result
69 {
70   struct shared_object *so;     /* Parameter to the call. */
71   rws_request rq;               /* Parameter to the call. */
72   int close;                    /* Return value from the call. */
73 };
74
75 static void call_handle_request (void *data);
76 static int do_error (process_rq p, const char *msg);
77
78 void
79 exec_so_init ()
80 {
81   cache = new_shash (global_pool, struct shared_object *);
82 }
83
84 int
85 exec_so_file (process_rq p)
86 {
87   struct shared_object *so;
88   const char *error;
89   rws_request rq;
90   struct fn_result fn_result;
91
92   /* Check our cache of currently loaded .so files to see if this one
93    * has already been loaded.
94    */
95   if (!shash_get (cache, p->file_path, so))
96     {
97       /* No: Need to dlopen this file. */
98       so = pmalloc (global_pool, sizeof *so);
99
100     reload:
101       so->dl_handle = dlopen (p->file_path,
102 #ifndef __OpenBSD__
103                               RTLD_NOW
104 #else
105                               O_RDWR
106 #endif
107                              );
108       if (so->dl_handle == 0)
109         {
110           fprintf (stderr, "%s\n", dlerror ());
111           return bad_request_error (p,
112                                     "failed to load shared object file");
113         }
114
115       /* Check it contains the 'handle_request' function. */
116       so->handle_request = dlsym (so->dl_handle, HANDLE_REQUEST_SYM);
117       if ((error = dlerror ()) != 0)
118         {
119           fprintf (stderr, "%s\n", error);
120           dlclose (so->dl_handle);
121           return bad_request_error (p,
122                                     "shared object file does not contain "
123                                     "handle_request function");
124         }
125
126       so->mtime = p->statbuf.st_mtime;
127       so->use_count = 0;
128
129       /* Add it to the cache. */
130       shash_insert (cache, p->file_path, so);
131     }
132
133   /* Check the modification time. We may need to reload this script if it's
134    * changed on disk. But if there are other current users, then we can't
135    * safely unload the library, so don't try (a later request will reload
136    * it when it's quiet anyway).
137    */
138   if (p->statbuf.st_mtime > so->mtime && so->use_count == 0)
139     {
140       shash_erase (cache, p->file_path);
141       dlclose (so->dl_handle);
142       goto reload;
143     }
144
145   /* OK, we're now about to use this file. */
146   so->use_count++;
147
148   /* Generate the rws_request object. */
149   rq = new_rws_request (p->pool,
150                         p->http_request,
151                         p->io,
152                         p->host_header,
153                         p->canonical_path,
154                         p->file_path,
155                         p->host,
156                         p->alias,
157                         cfg_get_string,
158                         cfg_get_int,
159                         cfg_get_bool);
160
161   /* Call the 'handle_request' function.
162    * XXX We could pass environment parameters here, but this requires
163    * a change to pthrlib to allow environment variables to be handled
164    * across context switches.
165    */
166   fn_result.so = so;
167   fn_result.rq = rq;
168   error = pth_catch (call_handle_request, &fn_result);
169
170   /* Finished using the file. */
171   so->use_count--;
172
173   if (error)
174     return do_error (p, error);
175
176   return fn_result.close;
177 }
178
179 static void
180 call_handle_request (void *data)
181 {
182   struct fn_result *fn_result = (struct fn_result *) data;
183
184   fn_result->close = fn_result->so->handle_request (fn_result->rq);
185 }
186
187 static int
188 do_error (process_rq p, const char *msg)
189 {
190   /* XXX In the future, we'd like to extend this function so that
191    * other non-500 errors can be displayed (particularly for 404
192    * Page Not Found errors).
193    */
194   return bad_request_error (p, msg);
195 }