]> granicus.if.org Git - apache/blob - server/util_mutex.c
* With APR >= 2.0 there is no longer an APR-UTIL only an APR.
[apache] / server / util_mutex.c
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /*
18  * util_mutex.c: Useful functions for determining allowable
19  *               mutexes and mutex settings
20  */
21
22
23 #include "apr.h"
24 #include "apr_hash.h"
25 #include "apr_strings.h"
26 #include "apr_lib.h"
27
28 #define APR_WANT_STRFUNC
29 #include "apr_want.h"
30
31 #include "ap_config.h"
32 #include "httpd.h"
33 #include "http_main.h"
34 #include "http_config.h"
35 #include "http_log.h"
36 #include "util_mutex.h"
37 #if AP_NEED_SET_MUTEX_PERMS
38 #include "unixd.h"
39 #endif
40 #ifdef HAVE_UNISTD_H
41 #include <unistd.h> /* getpid() */
42 #endif
43
44 AP_DECLARE(apr_status_t) ap_parse_mutex(const char *arg, apr_pool_t *pool,
45                                         apr_lockmech_e *mutexmech,
46                                         const char **mutexfile)
47 {
48     /* Split arg into meth and file */
49     char *meth = apr_pstrdup(pool, arg);
50     char *file = strchr(meth, ':');
51     if (file) {
52         *(file++) = '\0';
53         if (!*file) {
54             file = NULL;
55         }
56     }
57
58     /* APR determines temporary filename unless overridden below,
59      * we presume file indicates an mutexfile is a file path
60      * unless the method sets mutexfile=file and NULLs file
61      */
62     *mutexfile = NULL;
63
64     if (!strcasecmp(meth, "none") || !strcasecmp(meth, "no")) {
65         return APR_ENOLOCK;
66     }
67
68     /* NOTE: previously, 'yes' implied 'sem' */
69     if (!strcasecmp(meth, "default") || !strcasecmp(meth, "yes")) {
70         *mutexmech = APR_LOCK_DEFAULT;
71     }
72 #if APR_HAS_FCNTL_SERIALIZE
73     else if (!strcasecmp(meth, "fcntl") || !strcasecmp(meth, "file")) {
74         *mutexmech = APR_LOCK_FCNTL;
75     }
76 #endif
77 #if APR_HAS_FLOCK_SERIALIZE
78     else if (!strcasecmp(meth, "flock") || !strcasecmp(meth, "file")) {
79         *mutexmech = APR_LOCK_FLOCK;
80     }
81 #endif
82 #if APR_HAS_POSIXSEM_SERIALIZE
83     else if (!strcasecmp(meth, "posixsem") || !strcasecmp(meth, "sem")) {
84         *mutexmech = APR_LOCK_POSIXSEM;
85         /* Posix/SysV semaphores aren't file based, use the literal name
86          * if provided and fall back on APR's default if not.  Today, APR
87          * will ignore it, but once supported it has an absurdly short limit.
88          */
89         if (file) {
90             *mutexfile = apr_pstrdup(pool, file);
91
92             file = NULL;
93         }
94     }
95 #endif
96 #if APR_HAS_SYSVSEM_SERIALIZE
97     else if (!strcasecmp(meth, "sysvsem") || !strcasecmp(meth, "sem")) {
98         *mutexmech = APR_LOCK_SYSVSEM;
99     }
100 #endif
101 #if APR_HAS_PROC_PTHREAD_SERIALIZE
102     else if (!strcasecmp(meth, "pthread")) {
103         *mutexmech = APR_LOCK_PROC_PTHREAD;
104     }
105 #endif
106     else {
107         return APR_ENOTIMPL;
108     }
109
110     /* Unless the method above assumed responsibility for setting up
111      * mutexfile and NULLing out file, presume it is a file we
112      * are looking to use
113      */
114     if (file) {
115         *mutexfile = ap_server_root_relative(pool, file);
116         if (!*mutexfile) {
117             return APR_BADARG;
118         }
119     }
120
121     return APR_SUCCESS;
122 }
123
124 typedef struct {
125     apr_int32_t options;
126     int set;
127     int none;
128     int omit_pid;
129     apr_lockmech_e mech;
130     const char *dir;
131 } mutex_cfg_t;
132
133 /* hash is created the first time a module calls ap_mutex_register(),
134  * rather than attempting to be the REALLY_REALLY_FIRST pre-config
135  * hook; it is cleaned up when the associated pool goes away; assume
136  * pconf is the pool passed to ap_mutex_register()
137  */
138 static apr_hash_t *mxcfg_by_type;
139
140 static apr_status_t cleanup_mx_hash(void *dummy)
141 {
142     mxcfg_by_type = NULL;
143     return APR_SUCCESS;
144 }
145
146 static void mx_hash_init(apr_pool_t *p)
147 {
148     mutex_cfg_t *def;
149
150     if (mxcfg_by_type) {
151         return;
152     }
153
154     mxcfg_by_type = apr_hash_make(p);
155     apr_pool_cleanup_register(p, NULL, cleanup_mx_hash, apr_pool_cleanup_null);
156
157     /* initialize default mutex configuration */
158     def = apr_pcalloc(p, sizeof *def);
159     def->mech = APR_LOCK_DEFAULT;
160 #ifdef DEFAULT_REL_RUNTIMEDIR
161     def->dir = DEFAULT_REL_RUNTIMEDIR;
162 #else
163     def->dir = "logs";
164 #endif
165     apr_hash_set(mxcfg_by_type, "default", APR_HASH_KEY_STRING, def);
166 }
167
168 AP_DECLARE_NONSTD(const char *)ap_set_mutex(cmd_parms *cmd, void *dummy,
169                                             const char *arg)
170 {
171     apr_pool_t *p = cmd->pool;
172     const char **elt;
173     const char *mechdir;
174     int no_mutex = 0, omit_pid = 0;
175     apr_array_header_t *type_list;
176     apr_lockmech_e mech;
177     apr_status_t rv;
178     const char *mutexdir;
179     mutex_cfg_t *mxcfg;
180     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
181
182     if (err != NULL) {
183         return err;
184     }
185
186     mechdir = ap_getword_conf(cmd->pool, &arg);
187     if (*mechdir == '\0') {
188         return "Mutex requires at least a mechanism argument (" 
189                AP_ALL_AVAILABLE_MUTEXES_STRING ")";
190     }
191
192     rv = ap_parse_mutex(mechdir, p, &mech, &mutexdir);
193     if (rv == APR_ENOTIMPL) {
194         return apr_pstrcat(p, "Invalid Mutex argument ", mechdir,
195                            " (" AP_ALL_AVAILABLE_MUTEXES_STRING ")", NULL);
196     }
197     else if (rv == APR_BADARG
198              || (mutexdir && !ap_is_directory(p, mutexdir))) {
199         return apr_pstrcat(p, "Invalid Mutex directory in argument ",
200                            mechdir, NULL);
201     }
202     else if (rv == APR_ENOLOCK) { /* "none" */
203         no_mutex = 1;
204     }
205
206     /* "OmitPID" can appear at the end of the list, so build a list of
207      * mutex type names while looking for "OmitPID" (anywhere) or the end
208      */
209     type_list = apr_array_make(cmd->pool, 4, sizeof(char *));
210     while (*arg) {
211         char *s = ap_getword_conf(cmd->pool, &arg);
212
213         if (!strcasecmp(s, "omitpid")) {
214             omit_pid = 1;
215         }
216         else {
217             char **new_type = (char **)apr_array_push(type_list);
218             *new_type = s;
219         }
220     }
221
222     if (apr_is_empty_array(type_list)) { /* no mutex type?  assume "default" */
223         char **new_type = (char **)apr_array_push(type_list);
224         *new_type = "default";
225     }
226
227     while ((elt = (const char **)apr_array_pop(type_list)) != NULL) {
228         const char *type = *elt;
229         mxcfg = apr_hash_get(mxcfg_by_type, type, APR_HASH_KEY_STRING);
230         if (!mxcfg) {
231             return apr_psprintf(p, "Mutex type %s is not valid", type);
232         }
233
234         mxcfg->none = 0; /* in case that was the default */
235         mxcfg->omit_pid = omit_pid;
236
237         mxcfg->set = 1;
238         if (no_mutex) {
239             if (!(mxcfg->options & AP_MUTEX_ALLOW_NONE)) {
240                 return apr_psprintf(p,
241                                     "None is not allowed for mutex type %s",
242                                     type);
243             }
244             mxcfg->none = 1;
245         }
246         else {
247             mxcfg->mech = mech;
248             if (mutexdir) { /* retain mutex default if not configured */
249                 mxcfg->dir = mutexdir;
250             }
251         }
252     }
253
254     return NULL;
255 }
256
257 AP_DECLARE(apr_status_t) ap_mutex_register(apr_pool_t *pconf,
258                                            const char *type,
259                                            const char *default_dir,
260                                            apr_lockmech_e default_mech,
261                                            apr_int32_t options)
262 {
263     mutex_cfg_t *mxcfg = apr_pcalloc(pconf, sizeof *mxcfg);
264
265     if ((options & ~(AP_MUTEX_ALLOW_NONE | AP_MUTEX_DEFAULT_NONE))) {
266         return APR_EINVAL;
267     }
268
269     mx_hash_init(pconf);
270
271     mxcfg->options = options;
272     if (options & AP_MUTEX_DEFAULT_NONE) {
273         mxcfg->none = 1;
274     }
275     mxcfg->dir = default_dir; /* usually NULL */
276     mxcfg->mech = default_mech; /* usually APR_LOCK_DEFAULT */
277     apr_hash_set(mxcfg_by_type, type, APR_HASH_KEY_STRING, mxcfg);
278
279     return APR_SUCCESS;
280 }
281
282 static int mutex_needs_file(apr_lockmech_e mech)
283 {
284     if (mech != APR_LOCK_FLOCK
285         && mech != APR_LOCK_FCNTL
286 #if APR_USE_FLOCK_SERIALIZE || APR_USE_FCNTL_SERIALIZE
287         && mech != APR_LOCK_DEFAULT
288 #endif
289         ) {
290         return 0;
291     }
292     return 1;
293 }
294
295 static const char *get_mutex_filename(apr_pool_t *p, mutex_cfg_t *mxcfg,
296                                       const char *type,
297                                       const char *instance_id)
298 {
299     const char *pid_suffix = "";
300
301     if (!mutex_needs_file(mxcfg->mech)) {
302         return NULL;
303     }
304
305 #if HAVE_UNISTD_H
306     if (!mxcfg->omit_pid) {
307         pid_suffix = apr_psprintf(p, ".%" APR_PID_T_FMT, getpid());
308     }
309 #endif
310
311     return ap_server_root_relative(p,
312                                    apr_pstrcat(p,
313                                                mxcfg->dir,
314                                                "/",
315                                                type,
316                                                instance_id ? "-" : "",
317                                                instance_id ? instance_id : "",
318                                                pid_suffix,
319                                                NULL));
320 }
321
322 static mutex_cfg_t *mxcfg_lookup(apr_pool_t *p, const char *type)
323 {
324     mutex_cfg_t *defcfg, *mxcfg, *newcfg;
325
326     defcfg = apr_hash_get(mxcfg_by_type, "default", APR_HASH_KEY_STRING);
327
328     /* MUST exist in table, or wasn't registered */
329     mxcfg = apr_hash_get(mxcfg_by_type, type, APR_HASH_KEY_STRING);
330     if (!mxcfg) {
331         return NULL;
332     }
333
334     /* order of precedence:
335      * 1. Mutex directive for this mutex
336      * 2. Mutex directive for "default"
337      * 3. Defaults for this mutex from ap_mutex_register()
338      * 4. Global defaults
339      */
340
341     if (mxcfg->set) {
342         newcfg = mxcfg;
343     }
344     else if (defcfg->set) {
345         newcfg = defcfg;
346     }
347     else if (mxcfg->none || mxcfg->mech != APR_LOCK_DEFAULT) {
348         newcfg = mxcfg;
349     }
350     else {
351         newcfg = defcfg;
352     }
353
354     if (!newcfg->none && mutex_needs_file(newcfg->mech) && !newcfg->dir) {
355         /* a file-based mutex mechanism was configured, but
356          * without a mutex file directory; go back through
357          * the chain to find the directory, store in new
358          * mutex cfg structure
359          */
360         newcfg = apr_pmemdup(p, newcfg, sizeof *newcfg);
361
362         /* !true if dir not already set: mxcfg->set && defcfg->dir */
363         if (defcfg->set && defcfg->dir) {
364             newcfg->dir = defcfg->dir;
365         }
366         else if (mxcfg->dir) {
367             newcfg->dir = mxcfg->dir;
368         }
369         else {
370             newcfg->dir = defcfg->dir;
371         }
372     }
373
374     return newcfg;
375 }
376
377 static void log_bad_create_options(server_rec *s, const char *type)
378 {
379     ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s,
380                  "Invalid options were specified when creating the %s mutex",
381                  type);
382 }
383
384 static void log_unknown_type(server_rec *s, const char *type)
385 {
386     ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s,
387                  "Can't create mutex of unknown type %s", type);
388 }
389
390 static void log_create_failure(apr_status_t rv, server_rec *s, const char *type,
391                                const char *fname)
392 {
393     ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s,
394                  "Couldn't create the %s mutex %s%s%s", type,
395                  fname ? "(file " : "",
396                  fname ? fname : "",
397                  fname ? ")" : "");
398 }
399
400 static void log_perms_failure(apr_status_t rv, server_rec *s, const char *type)
401 {
402     ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s,
403                  "Couldn't set permissions on the %s mutex; "
404                  "check User and Group directives",
405                  type);
406 }
407
408 AP_DECLARE(apr_status_t) ap_global_mutex_create(apr_global_mutex_t **mutex,
409                                                 const char *type,
410                                                 const char *instance_id,
411                                                 server_rec *s, apr_pool_t *p,
412                                                 apr_int32_t options)
413 {
414     apr_status_t rv;
415     const char *fname;
416     mutex_cfg_t *mxcfg = mxcfg_lookup(p, type);
417
418     if (options) {
419         log_bad_create_options(s, type);
420         return APR_EINVAL;
421     }
422
423     if (!mxcfg) {
424         log_unknown_type(s, type);
425         return APR_EINVAL;
426     }
427
428     if (mxcfg->none) {
429         *mutex = NULL;
430         return APR_SUCCESS;
431     }
432
433     fname = get_mutex_filename(p, mxcfg, type, instance_id);
434
435     rv = apr_global_mutex_create(mutex, fname, mxcfg->mech, p);
436     if (rv != APR_SUCCESS) {
437         log_create_failure(rv, s, type, fname);
438         return rv;
439     }
440
441 #ifdef AP_NEED_SET_MUTEX_PERMS
442     rv = ap_unixd_set_global_mutex_perms(*mutex);
443     if (rv != APR_SUCCESS) {
444         log_perms_failure(rv, s, type);
445         return rv;
446     }
447 #endif
448
449     return APR_SUCCESS;
450 }
451
452 AP_DECLARE(apr_status_t) ap_proc_mutex_create(apr_proc_mutex_t **mutex,
453                                               const char *type,
454                                               const char *instance_id,
455                                               server_rec *s, apr_pool_t *p,
456                                               apr_int32_t options)
457 {
458     apr_status_t rv;
459     const char *fname;
460     mutex_cfg_t *mxcfg = mxcfg_lookup(p, type);
461
462     if (options) {
463         log_bad_create_options(s, type);
464         return APR_EINVAL;
465     }
466
467     if (!mxcfg) {
468         log_unknown_type(s, type);
469         return APR_EINVAL;
470     }
471
472     if (mxcfg->none) {
473         *mutex = NULL;
474         return APR_SUCCESS;
475     }
476
477     fname = get_mutex_filename(p, mxcfg, type, instance_id);
478
479     rv = apr_proc_mutex_create(mutex, fname, mxcfg->mech, p);
480     if (rv != APR_SUCCESS) {
481         log_create_failure(rv, s, type, fname);
482         return rv;
483     }
484
485 #ifdef AP_NEED_SET_MUTEX_PERMS
486     rv = ap_unixd_set_proc_mutex_perms(*mutex);
487     if (rv != APR_SUCCESS) {
488         log_perms_failure(rv, s, type);
489         return rv;
490     }
491 #endif
492
493     return APR_SUCCESS;
494 }