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