upload http
[bottlenecks.git] / rubbos / app / httpd-2.0.64 / srclib / apr / shmem / unix / shm.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 #include "apr_arch_shm.h"
18
19 #include "apr_general.h"
20 #include "apr_errno.h"
21 #include "apr_user.h"
22 #include "apr_strings.h"
23
24 static apr_status_t shm_cleanup_owner(void *m_)
25 {
26     apr_shm_t *m = (apr_shm_t *)m_;
27
28     /* anonymous shared memory */
29     if (m->filename == NULL) {
30 #if APR_USE_SHMEM_MMAP_ZERO || APR_USE_SHMEM_MMAP_ANON
31         if (munmap(m->base, m->realsize) == -1) {
32             return errno;
33         }
34         return APR_SUCCESS;
35 #endif
36 #if APR_USE_SHMEM_SHMGET_ANON
37         if (shmdt(m->base) == -1) {
38             return errno;
39         }
40         /* This segment will automatically remove itself after all
41          * references have detached. */
42         return APR_SUCCESS;
43 #endif
44     }
45
46     /* name-based shared memory */
47     else {
48 #if APR_USE_SHMEM_MMAP_TMP
49         apr_status_t rv;
50
51         if (munmap(m->base, m->realsize) == -1) {
52             return errno;
53         }
54         rv = apr_file_remove(m->filename, m->pool);
55         if (rv != APR_SUCCESS) {
56             return rv;
57         }
58         return APR_SUCCESS;
59 #endif
60 #if APR_USE_SHMEM_MMAP_SHM
61         if (munmap(m->base, m->realsize) == -1) {
62             return errno;
63         }
64         if (shm_unlink(m->filename) == -1) {
65             return errno;
66         }
67         return APR_SUCCESS;
68 #endif
69 #if APR_USE_SHMEM_SHMGET
70         apr_status_t rv;
71
72         /* Indicate that the segment is to be destroyed as soon
73          * as all processes have detached. This also disallows any
74          * new attachments to the segment. */
75         if (shmctl(m->shmid, IPC_RMID, NULL) == -1) {
76             return errno;
77         }
78         if (shmdt(m->base) == -1) {
79             return errno;
80         }
81         rv = apr_file_remove(m->filename, m->pool);
82         if (rv != APR_SUCCESS) {
83             return rv;
84         }
85         return APR_SUCCESS;
86 #endif
87     }
88
89     return APR_ENOTIMPL;
90 }
91
92 APR_DECLARE(apr_status_t) apr_shm_create(apr_shm_t **m,
93                                          apr_size_t reqsize, 
94                                          const char *filename,
95                                          apr_pool_t *pool)
96 {
97     apr_shm_t *new_m;
98     apr_status_t status;
99 #if APR_USE_SHMEM_SHMGET || APR_USE_SHMEM_SHMGET_ANON
100     struct shmid_ds shmbuf;
101     apr_uid_t uid;
102     apr_gid_t gid;
103 #endif
104 #if APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM || \
105     APR_USE_SHMEM_MMAP_ZERO
106     int tmpfd;
107 #endif
108 #if APR_USE_SHMEM_SHMGET
109     apr_size_t nbytes;
110     key_t shmkey;
111 #endif
112 #if APR_USE_SHMEM_MMAP_ZERO || APR_USE_SHMEM_SHMGET || \
113     APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM
114     apr_file_t *file;   /* file where metadata is stored */
115 #endif
116
117     /* Check if they want anonymous or name-based shared memory */
118     if (filename == NULL) {
119 #if APR_USE_SHMEM_MMAP_ZERO || APR_USE_SHMEM_MMAP_ANON
120         new_m = apr_palloc(pool, sizeof(apr_shm_t));
121         if (!new_m) {
122             return APR_ENOMEM;
123         }
124         new_m->pool = pool;
125         new_m->reqsize = reqsize;
126         new_m->realsize = reqsize + 
127             APR_ALIGN_DEFAULT(sizeof(apr_size_t)); /* room for metadata */
128         new_m->filename = NULL;
129     
130 #if APR_USE_SHMEM_MMAP_ZERO
131         status = apr_file_open(&file, "/dev/zero", APR_READ | APR_WRITE, 
132                                APR_OS_DEFAULT, pool);
133         if (status != APR_SUCCESS) {
134             return status;
135         }
136         status = apr_os_file_get(&tmpfd, file);
137         if (status != APR_SUCCESS) {
138             return status;
139         }
140
141         new_m->base = mmap(NULL, new_m->realsize, PROT_READ|PROT_WRITE,
142                            MAP_SHARED, tmpfd, 0);
143         if (new_m->base == (void *)MAP_FAILED) {
144             return errno;
145         }
146
147         status = apr_file_close(file);
148         if (status != APR_SUCCESS) {
149             return status;
150         }
151
152         /* store the real size in the metadata */
153         *(apr_size_t*)(new_m->base) = new_m->realsize;
154         /* metadata isn't usable */
155         new_m->usable = (char *)new_m->base + APR_ALIGN_DEFAULT(sizeof(apr_size_t));
156
157         apr_pool_cleanup_register(new_m->pool, new_m, shm_cleanup_owner,
158                                   apr_pool_cleanup_null);
159         *m = new_m;
160         return APR_SUCCESS;
161
162 #elif APR_USE_SHMEM_MMAP_ANON
163         new_m->base = mmap(NULL, new_m->realsize, PROT_READ|PROT_WRITE,
164                            MAP_ANON|MAP_SHARED, -1, 0);
165         if (new_m->base == (void *)MAP_FAILED) {
166             return errno;
167         }
168
169         /* store the real size in the metadata */
170         *(apr_size_t*)(new_m->base) = new_m->realsize;
171         /* metadata isn't usable */
172         new_m->usable = (char *)new_m->base + APR_ALIGN_DEFAULT(sizeof(apr_size_t));
173
174         apr_pool_cleanup_register(new_m->pool, new_m, shm_cleanup_owner,
175                                   apr_pool_cleanup_null);
176         *m = new_m;
177         return APR_SUCCESS;
178
179 #endif /* APR_USE_SHMEM_MMAP_ZERO */
180 #endif /* APR_USE_SHMEM_MMAP_ZERO || APR_USE_SHMEM_MMAP_ANON */
181 #if APR_USE_SHMEM_SHMGET_ANON
182
183         new_m = apr_palloc(pool, sizeof(apr_shm_t));
184         if (!new_m) {
185             return APR_ENOMEM;
186         }
187         new_m->pool = pool;
188         new_m->reqsize = reqsize;
189         new_m->realsize = reqsize;
190         new_m->filename = NULL;
191
192         if ((new_m->shmid = shmget(IPC_PRIVATE, new_m->realsize,
193                                    SHM_R | SHM_W | IPC_CREAT)) < 0) {
194             return errno;
195         }
196
197         if ((new_m->base = shmat(new_m->shmid, NULL, 0)) == (void *)-1) {
198             return errno;
199         }
200         new_m->usable = new_m->base;
201
202         if (shmctl(new_m->shmid, IPC_STAT, &shmbuf) == -1) {
203             return errno;
204         }
205         apr_uid_current(&uid, &gid, pool);
206         shmbuf.shm_perm.uid = uid;
207         shmbuf.shm_perm.gid = gid;
208         if (shmctl(new_m->shmid, IPC_SET, &shmbuf) == -1) {
209             return errno;
210         }
211
212         /* Remove the segment once use count hits zero.
213          * We will not attach to this segment again, since it is
214          * anonymous memory, so it is ok to mark it for deletion.
215          */
216         if (shmctl(new_m->shmid, IPC_RMID, NULL) == -1) {
217             return errno;
218         }
219
220         apr_pool_cleanup_register(new_m->pool, new_m, shm_cleanup_owner,
221                                   apr_pool_cleanup_null);
222         *m = new_m;
223         return APR_SUCCESS;
224 #endif /* APR_USE_SHMEM_SHMGET_ANON */
225         /* It is an error if they want anonymous memory but we don't have it. */
226         return APR_ENOTIMPL; /* requested anonymous but we don't have it */
227     }
228
229     /* Name-based shared memory */
230     else {
231         new_m = apr_palloc(pool, sizeof(apr_shm_t));
232         if (!new_m) {
233             return APR_ENOMEM;
234         }
235         new_m->pool = pool;
236         new_m->reqsize = reqsize;
237         new_m->filename = apr_pstrdup(pool, filename);
238
239 #if APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM
240         new_m->realsize = reqsize + 
241             APR_ALIGN_DEFAULT(sizeof(apr_size_t)); /* room for metadata */
242         /* FIXME: Ignore error for now. *
243          * status = apr_file_remove(file, pool);*/
244         status = APR_SUCCESS;
245     
246 #if APR_USE_SHMEM_MMAP_TMP
247         /* FIXME: Is APR_OS_DEFAULT sufficient? */
248         status = apr_file_open(&file, filename, 
249                                APR_READ | APR_WRITE | APR_CREATE | APR_EXCL,
250                                APR_OS_DEFAULT, pool);
251         if (status != APR_SUCCESS) {
252             return status;
253         }
254
255         status = apr_os_file_get(&tmpfd, file);
256         if (status != APR_SUCCESS) {
257             apr_file_close(file); /* ignore errors, we're failing */
258             apr_file_remove(new_m->filename, new_m->pool);
259             return status;
260         }
261
262         status = apr_file_trunc(file, new_m->realsize);
263         if (status != APR_SUCCESS) {
264             apr_file_close(file); /* ignore errors, we're failing */
265             apr_file_remove(new_m->filename, new_m->pool);
266             return status;
267         }
268
269         new_m->base = mmap(NULL, new_m->realsize, PROT_READ | PROT_WRITE,
270                            MAP_SHARED, tmpfd, 0);
271         /* FIXME: check for errors */
272
273         status = apr_file_close(file);
274         if (status != APR_SUCCESS) {
275             return status;
276         }
277 #endif /* APR_USE_SHMEM_MMAP_TMP */
278 #if APR_USE_SHMEM_MMAP_SHM
279         /* FIXME: Is APR_OS_DEFAULT sufficient? */
280         tmpfd = shm_open(filename, O_RDWR | O_CREAT | O_EXCL, APR_OS_DEFAULT);
281         if (tmpfd == -1) {
282             return errno;
283         }
284
285         status = apr_os_file_put(&file, &tmpfd,
286                                  APR_READ | APR_WRITE | APR_CREATE | APR_EXCL,
287                                  pool); 
288         if (status != APR_SUCCESS) {
289             return status;
290         }
291
292         status = apr_file_trunc(file, new_m->realsize);
293         if (status != APR_SUCCESS) {
294             shm_unlink(filename); /* we're failing, remove the object */
295             return status;
296         }
297         new_m->base = mmap(NULL, reqsize, PROT_READ | PROT_WRITE,
298                            MAP_SHARED, tmpfd, 0);
299
300         /* FIXME: check for errors */
301
302         /* FIXME: Is it ok to close this file when using shm_open?? */
303         status = apr_file_close(file);
304         if (status != APR_SUCCESS) {
305             return status;
306         }
307 #endif /* APR_USE_SHMEM_MMAP_SHM */
308
309         /* store the real size in the metadata */
310         *(apr_size_t*)(new_m->base) = new_m->realsize;
311         /* metadata isn't usable */
312         new_m->usable = (char *)new_m->base + APR_ALIGN_DEFAULT(sizeof(apr_size_t));
313
314         apr_pool_cleanup_register(new_m->pool, new_m, shm_cleanup_owner,
315                                   apr_pool_cleanup_null);
316         *m = new_m;
317         return APR_SUCCESS;
318
319 #endif /* APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM */
320
321 #if APR_USE_SHMEM_SHMGET
322         new_m->realsize = reqsize;
323
324         /* FIXME: APR_OS_DEFAULT is too permissive, switch to 600 I think. */
325         status = apr_file_open(&file, filename, 
326                                APR_WRITE | APR_CREATE | APR_EXCL,
327                                APR_OS_DEFAULT, pool);
328         if (status != APR_SUCCESS) {
329             return status;
330         }
331
332         /* ftok() (on solaris at least) requires that the file actually
333          * exist before calling ftok(). */
334         shmkey = ftok(filename, 1);
335         if (shmkey == (key_t)-1) {
336             return errno;
337         }
338
339         if ((new_m->shmid = shmget(shmkey, new_m->realsize,
340                                    SHM_R | SHM_W | IPC_CREAT | IPC_EXCL)) < 0) {
341             return errno;
342         }
343
344         if ((new_m->base = shmat(new_m->shmid, NULL, 0)) == (void *)-1) {
345             return errno;
346         }
347         new_m->usable = new_m->base;
348
349         if (shmctl(new_m->shmid, IPC_STAT, &shmbuf) == -1) {
350             return errno;
351         }
352         apr_uid_current(&uid, &gid, pool);
353         shmbuf.shm_perm.uid = uid;
354         shmbuf.shm_perm.gid = gid;
355         if (shmctl(new_m->shmid, IPC_SET, &shmbuf) == -1) {
356             return errno;
357         }
358
359         nbytes = sizeof(reqsize);
360         status = apr_file_write(file, (const void *)&reqsize,
361                                 &nbytes);
362         if (status != APR_SUCCESS) {
363             return status;
364         }
365         status = apr_file_close(file);
366         if (status != APR_SUCCESS) {
367             return status;
368         }
369
370         apr_pool_cleanup_register(new_m->pool, new_m, shm_cleanup_owner,
371                                   apr_pool_cleanup_null);
372         *m = new_m; 
373         return APR_SUCCESS;
374
375 #endif /* APR_USE_SHMEM_SHMGET */
376     }
377
378     return APR_ENOTIMPL;
379 }
380
381 APR_DECLARE(apr_status_t) apr_shm_destroy(apr_shm_t *m)
382 {
383     return apr_pool_cleanup_run(m->pool, m, shm_cleanup_owner);
384 }
385
386 static apr_status_t shm_cleanup_attach(void *m_)
387 {
388     apr_shm_t *m = (apr_shm_t *)m_;
389
390     if (m->filename == NULL) {
391         /* It doesn't make sense to detach from an anonymous memory segment. */
392         return APR_EINVAL;
393     }
394     else {
395 #if APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM
396         if (munmap(m->base, m->realsize) == -1) {
397             return errno;
398         }
399         return APR_SUCCESS;
400 #endif /* APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM */
401 #if APR_USE_SHMEM_SHMGET
402         if (shmdt(m->base) == -1) {
403             return errno;
404         }
405         return APR_SUCCESS;
406 #endif
407     }
408
409     return APR_ENOTIMPL;
410 }
411
412 APR_DECLARE(apr_status_t) apr_shm_attach(apr_shm_t **m,
413                                          const char *filename,
414                                          apr_pool_t *pool)
415 {
416     if (filename == NULL) {
417         /* It doesn't make sense to attach to a segment if you don't know
418          * the filename. */
419         return APR_EINVAL;
420     }
421     else {
422 #if APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM
423         apr_shm_t *new_m;
424         apr_status_t status;
425         int tmpfd;
426         apr_file_t *file;   /* file where metadata is stored */
427         apr_size_t nbytes;
428
429         new_m = apr_palloc(pool, sizeof(apr_shm_t));
430         if (!new_m) {
431             return APR_ENOMEM;
432         }
433         new_m->pool = pool;
434         new_m->filename = apr_pstrdup(pool, filename);
435
436         status = apr_file_open(&file, filename, 
437                                APR_READ | APR_WRITE,
438                                APR_OS_DEFAULT, pool);
439         if (status != APR_SUCCESS) {
440             return status;
441         }
442         status = apr_os_file_get(&tmpfd, file);
443         if (status != APR_SUCCESS) {
444             return status;
445         }
446
447         nbytes = sizeof(new_m->realsize);
448         status = apr_file_read(file, (void *)&(new_m->realsize),
449                                &nbytes);
450         if (status != APR_SUCCESS) {
451             return status;
452         }
453
454         status = apr_os_file_get(&tmpfd, file);
455         if (status != APR_SUCCESS) {
456             apr_file_close(file); /* ignore errors, we're failing */
457             apr_file_remove(new_m->filename, new_m->pool);
458             return status;
459         }
460
461         new_m->reqsize = new_m->realsize - sizeof(apr_size_t);
462
463         new_m->base = mmap(NULL, new_m->realsize, PROT_READ | PROT_WRITE,
464                            MAP_SHARED, tmpfd, 0);
465         /* FIXME: check for errors */
466         
467         status = apr_file_close(file);
468         if (status != APR_SUCCESS) {
469             return status;
470         }
471
472         /* metadata isn't part of the usable segment */
473         new_m->usable = (char *)new_m->base + APR_ALIGN_DEFAULT(sizeof(apr_size_t));
474
475         apr_pool_cleanup_register(new_m->pool, new_m, shm_cleanup_attach,
476                                   apr_pool_cleanup_null);
477         *m = new_m;
478         return APR_SUCCESS;
479
480 #endif /* APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM */
481 #if APR_USE_SHMEM_SHMGET
482         apr_shm_t *new_m;
483         apr_status_t status;
484         apr_file_t *file;   /* file where metadata is stored */
485         apr_size_t nbytes;
486         key_t shmkey;
487
488         new_m = apr_palloc(pool, sizeof(apr_shm_t));
489         if (!new_m) {
490             return APR_ENOMEM;
491         }
492
493         /* FIXME: does APR_OS_DEFAULT matter for reading? */
494         status = apr_file_open(&file, filename, 
495                                APR_READ, APR_OS_DEFAULT, pool);
496         if (status != APR_SUCCESS) {
497             return status;
498         }
499
500         nbytes = sizeof(new_m->reqsize);
501         status = apr_file_read(file, (void *)&(new_m->reqsize),
502                                &nbytes);
503         if (status != APR_SUCCESS) {
504             return status;
505         }
506         status = apr_file_close(file);
507         if (status != APR_SUCCESS) {
508             return status;
509         }
510
511         new_m->filename = apr_pstrdup(pool, filename);
512         new_m->pool = pool;
513         shmkey = ftok(filename, 1);
514         if (shmkey == (key_t)-1) {
515             return errno;
516         }
517         if ((new_m->shmid = shmget(shmkey, 0, SHM_R | SHM_W)) == -1) {
518             return errno;
519         }
520         if ((new_m->base = shmat(new_m->shmid, NULL, 0)) == (void *)-1) {
521             return errno;
522         }
523         new_m->usable = new_m->base;
524         new_m->realsize = new_m->reqsize;
525
526         apr_pool_cleanup_register(new_m->pool, new_m, shm_cleanup_attach,
527                                   apr_pool_cleanup_null);
528         *m = new_m;
529         return APR_SUCCESS;
530
531 #endif /* APR_USE_SHMEM_SHMGET */
532     }
533
534     return APR_ENOTIMPL;
535 }
536
537 APR_DECLARE(apr_status_t) apr_shm_detach(apr_shm_t *m)
538 {
539     apr_status_t rv = shm_cleanup_attach(m);
540     apr_pool_cleanup_kill(m->pool, m, shm_cleanup_attach);
541     return rv;
542 }
543
544 APR_DECLARE(void *) apr_shm_baseaddr_get(const apr_shm_t *m)
545 {
546     return m->usable;
547 }
548
549 APR_DECLARE(apr_size_t) apr_shm_size_get(const apr_shm_t *m)
550 {
551     return m->reqsize;
552 }
553
554 APR_POOL_IMPLEMENT_ACCESSOR(shm)
555
556 APR_DECLARE(apr_status_t) apr_os_shm_get(apr_os_shm_t *osshm,
557                                          apr_shm_t *shm)
558 {
559     return APR_ENOTIMPL;
560 }
561
562 APR_DECLARE(apr_status_t) apr_os_shm_put(apr_shm_t **m,
563                                          apr_os_shm_t *osshm,
564                                          apr_pool_t *pool)
565 {
566     return APR_ENOTIMPL;
567 }    
568