Apache HTTPD
shm.c
Go to the documentation of this file.
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#include "apr_arch_file_io.h"
19
20#include "apr_general.h"
21#include "apr_errno.h"
22#include "apr_user.h"
23#include "apr_strings.h"
24#include "apr_hash.h"
25
26#if APR_USE_SHMEM_MMAP_SHM
27/*
28 * For portable use, a shared memory object should be identified by a name of
29 * the form /somename; that is, a null-terminated string of up to NAME_MAX
30 * (i.e., 255) characters consisting of an initial slash, followed by one or
31 * more characters, none of which are slashes.
32 */
33#ifndef NAME_MAX
34#define NAME_MAX 255
35#endif
36
37/* See proc_mutex.c and sem_open for the reason for all this! */
38static unsigned int rshash (const char *p) {
39 /* hash function from Robert Sedgwicks 'Algorithms in C' book */
40 unsigned int b = 378551;
41 unsigned int a = 63689;
42 unsigned int retval = 0;
43
44 for( ; *p; p++) {
45 retval = retval * a + (*p);
46 a *= b;
47 }
48
49 return retval;
50}
51
52static const char *make_shm_open_safe_name(const char *filename,
54{
56 unsigned int h1, h2;
57
58 if (filename == NULL) {
59 return NULL;
60 }
61
62 flen = strlen(filename);
63 h1 = (apr_hashfunc_default(filename, &flen) & 0xffffffff);
64 h2 = (rshash(filename) & 0xffffffff);
65 return apr_psprintf(pool, "/ShM.%xH%x", h1, h2);
66
67}
68#endif
69
70#if APR_USE_SHMEM_SHMGET
71static key_t our_ftok(const char *filename)
72{
73 /* to help avoid collisions while still using
74 * an easily recreated proj_id */
75 apr_ssize_t slen = strlen(filename);
76 return ftok(filename,
78}
79#endif
80
82{
83 apr_shm_t *m = (apr_shm_t *)m_;
84
85 /* anonymous shared memory */
86 if (m->filename == NULL) {
87#if APR_USE_SHMEM_MMAP_ZERO || APR_USE_SHMEM_MMAP_ANON
88 if (munmap(m->base, m->realsize) == -1) {
89 return errno;
90 }
91 return APR_SUCCESS;
92#elif APR_USE_SHMEM_SHMGET_ANON
93 if (shmdt(m->base) == -1) {
94 return errno;
95 }
96 /* This segment will automatically remove itself after all
97 * references have detached. */
98 return APR_SUCCESS;
99#endif
100 }
101
102 /* name-based shared memory */
103 else {
104#if APR_USE_SHMEM_MMAP_TMP
105 if (munmap(m->base, m->realsize) == -1) {
106 return errno;
107 }
108 if (access(m->filename, F_OK)) {
109 return APR_SUCCESS;
110 }
111 else {
112 return apr_file_remove(m->filename, m->pool);
113 }
114#elif APR_USE_SHMEM_MMAP_SHM
115 if (munmap(m->base, m->realsize) == -1) {
116 return errno;
117 }
118 if (shm_unlink(make_shm_open_safe_name(m->filename, m->pool)) == -1 && errno != ENOENT) {
119 return errno;
120 }
121 return APR_SUCCESS;
122#elif APR_USE_SHMEM_SHMGET
123 /* Indicate that the segment is to be destroyed as soon
124 * as all processes have detached. This also disallows any
125 * new attachments to the segment. */
126 if (shmctl(m->shmid, IPC_RMID, NULL) == -1 && errno != EINVAL) {
127 return errno;
128 }
129 if (shmdt(m->base) == -1) {
130 return errno;
131 }
132 if (access(m->filename, F_OK)) {
133 return APR_SUCCESS;
134 }
135 else {
136 return apr_file_remove(m->filename, m->pool);
137 }
138#else
139 return APR_ENOTIMPL;
140#endif
141 }
142}
143
146 const char *filename,
148{
151#if APR_USE_SHMEM_SHMGET || APR_USE_SHMEM_SHMGET_ANON
152 struct shmid_ds shmbuf;
155#endif
156#if APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM || \
157 APR_USE_SHMEM_MMAP_ZERO
158 int tmpfd;
159#endif
160#if APR_USE_SHMEM_SHMGET
162#endif
163#if APR_USE_SHMEM_MMAP_ZERO || APR_USE_SHMEM_SHMGET || \
164 APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM
165 apr_file_t *file; /* file where metadata is stored */
166#endif
167
168 /* Check if they want anonymous or name-based shared memory */
169 if (filename == NULL) {
170#if APR_USE_SHMEM_MMAP_ZERO || APR_USE_SHMEM_MMAP_ANON
171 new_m = apr_palloc(pool, sizeof(apr_shm_t));
172 new_m->pool = pool;
173 new_m->reqsize = reqsize;
174 new_m->realsize = reqsize +
175 APR_ALIGN_DEFAULT(sizeof(apr_size_t)); /* room for metadata */
176 new_m->filename = NULL;
177
178#if APR_USE_SHMEM_MMAP_ZERO
179 status = apr_file_open(&file, "/dev/zero", APR_READ | APR_WRITE,
181 if (status != APR_SUCCESS) {
182 return status;
183 }
185 if (status != APR_SUCCESS) {
186 return status;
187 }
188
189 new_m->base = mmap(NULL, new_m->realsize, PROT_READ|PROT_WRITE,
190 MAP_SHARED, tmpfd, 0);
191 if (new_m->base == (void *)MAP_FAILED) {
192 return errno;
193 }
194
196 if (status != APR_SUCCESS) {
197 return status;
198 }
199
200 /* store the real size in the metadata */
201 *(apr_size_t*)(new_m->base) = new_m->realsize;
202 /* metadata isn't usable */
203 new_m->usable = (char *)new_m->base + APR_ALIGN_DEFAULT(sizeof(apr_size_t));
204
207 *m = new_m;
208 return APR_SUCCESS;
209
210#elif APR_USE_SHMEM_MMAP_ANON
211 new_m->base = mmap(NULL, new_m->realsize, PROT_READ|PROT_WRITE,
212 MAP_ANON|MAP_SHARED, -1, 0);
213 if (new_m->base == (void *)MAP_FAILED) {
214 return errno;
215 }
216
217 /* store the real size in the metadata */
218 *(apr_size_t*)(new_m->base) = new_m->realsize;
219 /* metadata isn't usable */
220 new_m->usable = (char *)new_m->base + APR_ALIGN_DEFAULT(sizeof(apr_size_t));
221
224 *m = new_m;
225 return APR_SUCCESS;
226
227#endif /* APR_USE_SHMEM_MMAP_ZERO */
228#elif APR_USE_SHMEM_SHMGET_ANON
229 new_m = apr_palloc(pool, sizeof(apr_shm_t));
230 new_m->pool = pool;
231 new_m->reqsize = reqsize;
232 new_m->realsize = reqsize;
233 new_m->filename = NULL;
234 new_m->shmkey = IPC_PRIVATE;
235 if ((new_m->shmid = shmget(new_m->shmkey, new_m->realsize,
236 SHM_R | SHM_W | IPC_CREAT)) < 0) {
237 return errno;
238 }
239
240 if ((new_m->base = shmat(new_m->shmid, NULL, 0)) == (void *)-1) {
241 return errno;
242 }
243 new_m->usable = new_m->base;
244
245 if (shmctl(new_m->shmid, IPC_STAT, &shmbuf) == -1) {
246 return errno;
247 }
249 shmbuf.shm_perm.uid = uid;
250 shmbuf.shm_perm.gid = gid;
251 if (shmctl(new_m->shmid, IPC_SET, &shmbuf) == -1) {
252 return errno;
253 }
254
255 /* Remove the segment once use count hits zero.
256 * We will not attach to this segment again, since it is
257 * anonymous memory, so it is ok to mark it for deletion.
258 */
259 if (shmctl(new_m->shmid, IPC_RMID, NULL) == -1) {
260 return errno;
261 }
262
265 *m = new_m;
266 return APR_SUCCESS;
267#else
268 /* It is an error if they want anonymous memory but we don't have it. */
269 return APR_ENOTIMPL; /* requested anonymous but we don't have it */
270#endif
271 }
272
273 /* Name-based shared memory */
274 else {
275 new_m = apr_palloc(pool, sizeof(apr_shm_t));
276 new_m->pool = pool;
277 new_m->reqsize = reqsize;
278 new_m->filename = apr_pstrdup(pool, filename);
279#if APR_USE_SHMEM_MMAP_SHM
281#endif
282#if APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM
283 new_m->realsize = reqsize +
284 APR_ALIGN_DEFAULT(sizeof(apr_size_t)); /* room for metadata */
285 /* FIXME: Ignore error for now. *
286 * status = apr_file_remove(file, pool);*/
288
289#if APR_USE_SHMEM_MMAP_TMP
293 if (status != APR_SUCCESS) {
294 return status;
295 }
296
298 if (status != APR_SUCCESS) {
299 apr_file_close(file); /* ignore errors, we're failing */
300 apr_file_remove(new_m->filename, new_m->pool);
301 return status;
302 }
303
304 status = apr_file_trunc(file, new_m->realsize);
305 if (status != APR_SUCCESS && status != APR_ESPIPE) {
306 apr_file_close(file); /* ignore errors, we're failing */
307 apr_file_remove(new_m->filename, new_m->pool);
308 return status;
309 }
310
311 new_m->base = mmap(NULL, new_m->realsize, PROT_READ | PROT_WRITE,
312 MAP_SHARED, tmpfd, 0);
313 /* FIXME: check for errors */
314
316 if (status != APR_SUCCESS) {
317 return status;
318 }
319#endif /* APR_USE_SHMEM_MMAP_TMP */
320#if APR_USE_SHMEM_MMAP_SHM
322 if (tmpfd == -1) {
323 return errno;
324 }
325
328 pool);
329 if (status != APR_SUCCESS) {
330 return status;
331 }
332
333 status = apr_file_trunc(file, new_m->realsize);
334 if (status != APR_SUCCESS && status != APR_ESPIPE) {
335 shm_unlink(shm_name); /* we're failing, remove the object */
336 return status;
337 }
338 new_m->base = mmap(NULL, new_m->realsize, PROT_READ | PROT_WRITE,
339 MAP_SHARED, tmpfd, 0);
340
341 /* FIXME: check for errors */
342
344 if (status != APR_SUCCESS) {
345 return status;
346 }
347#endif /* APR_USE_SHMEM_MMAP_SHM */
348
349 /* store the real size in the metadata */
350 *(apr_size_t*)(new_m->base) = new_m->realsize;
351 /* metadata isn't usable */
352 new_m->usable = (char *)new_m->base + APR_ALIGN_DEFAULT(sizeof(apr_size_t));
353
356 *m = new_m;
357 return APR_SUCCESS;
358
359#elif APR_USE_SHMEM_SHMGET
360 new_m->realsize = reqsize;
361
365 if (status != APR_SUCCESS) {
366 return status;
367 }
368
369 /* ftok() (on solaris at least) requires that the file actually
370 * exist before calling ftok(). */
371 new_m->shmkey = our_ftok(filename);
372 if (new_m->shmkey == (key_t)-1) {
374 return errno;
375 }
376
377 if ((new_m->shmid = shmget(new_m->shmkey, new_m->realsize,
378 SHM_R | SHM_W | IPC_CREAT | IPC_EXCL)) < 0) {
380 return errno;
381 }
382
383 if ((new_m->base = shmat(new_m->shmid, NULL, 0)) == (void *)-1) {
385 return errno;
386 }
387 new_m->usable = new_m->base;
388
389 if (shmctl(new_m->shmid, IPC_STAT, &shmbuf) == -1) {
391 return errno;
392 }
394 shmbuf.shm_perm.uid = uid;
395 shmbuf.shm_perm.gid = gid;
396 if (shmctl(new_m->shmid, IPC_SET, &shmbuf) == -1) {
398 return errno;
399 }
400
401 nbytes = sizeof(reqsize);
402 status = apr_file_write(file, (const void *)&reqsize,
403 &nbytes);
404 if (status != APR_SUCCESS) {
406 return status;
407 }
409 if (status != APR_SUCCESS) {
410 return status;
411 }
412
415 *m = new_m;
416 return APR_SUCCESS;
417
418#else
419 return APR_ENOTIMPL;
420#endif
421 }
422}
423
426 const char *filename,
427 apr_pool_t *p,
429{
430 return apr_shm_create(m, reqsize, filename, p);
431}
432
435{
436#if APR_USE_SHMEM_SHMGET
440 int shmid;
441#endif
442
443#if APR_USE_SHMEM_MMAP_TMP
445#elif APR_USE_SHMEM_MMAP_SHM
447 if (shm_unlink(shm_name) == -1) {
448 return errno;
449 }
450 return APR_SUCCESS;
451#elif APR_USE_SHMEM_SHMGET
452 /* Presume that the file already exists; just open for writing */
455 if (status) {
456 return status;
457 }
458
459 /* ftok() (on solaris at least) requires that the file actually
460 * exist before calling ftok(). */
462 if (shmkey == (key_t)-1) {
464 }
465
467
468 if ((shmid = shmget(shmkey, 0, SHM_R | SHM_W)) < 0) {
470 }
471
472 /* Indicate that the segment is to be destroyed as soon
473 * as all processes have detached. This also disallows any
474 * new attachments to the segment. */
475 if (shmctl(shmid, IPC_RMID, NULL) == -1) {
477 }
479
481 status = errno;
482 /* ensure the file has been removed anyway. */
484 return status;
485#else
486
487 /* No support for anonymous shm */
488 return APR_ENOTIMPL;
489#endif
490}
491
493{
494 if (m->filename) {
495 return apr_shm_remove(m->filename, m->pool);
496 }
497 else {
498 return APR_ENOTIMPL;
499 }
500}
501
503{
505}
506
508{
509 apr_shm_t *m = (apr_shm_t *)m_;
510
511 if (m->filename == NULL) {
512 /* It doesn't make sense to detach from an anonymous memory segment. */
513 return APR_EINVAL;
514 }
515 else {
516#if APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM
517 if (munmap(m->base, m->realsize) == -1) {
518 return errno;
519 }
520 return APR_SUCCESS;
521#elif APR_USE_SHMEM_SHMGET
522 if (shmdt(m->base) == -1) {
523 return errno;
524 }
525 return APR_SUCCESS;
526#else
527 return APR_ENOTIMPL;
528#endif
529 }
530}
531
533 const char *filename,
535{
536 if (filename == NULL) {
537 /* It doesn't make sense to attach to a segment if you don't know
538 * the filename. */
539 return APR_EINVAL;
540 }
541 else {
542#if APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM
545 int tmpfd;
546 apr_file_t *file; /* file where metadata is stored */
548
549 new_m = apr_palloc(pool, sizeof(apr_shm_t));
550 new_m->pool = pool;
551 new_m->filename = apr_pstrdup(pool, filename);
552#if APR_USE_SHMEM_MMAP_SHM
554
555 tmpfd = shm_open(shm_name, O_RDWR, 0600);
556 if (tmpfd == -1) {
557 return errno;
558 }
559
562 pool);
563 if (status != APR_SUCCESS) {
564 return status;
565 }
566
567#elif APR_USE_SHMEM_MMAP_TMP
571 if (status != APR_SUCCESS) {
572 return status;
573 }
575 if (status != APR_SUCCESS) {
576 return status;
577 }
578#else
579 return APR_ENOTIMPL;
580#endif
581
582 nbytes = sizeof(new_m->realsize);
583 status = apr_file_read(file, (void *)&(new_m->realsize),
584 &nbytes);
585 if (status != APR_SUCCESS) {
586 return status;
587 }
588
590 if (status != APR_SUCCESS) {
591 apr_file_close(file); /* ignore errors, we're failing */
592 apr_file_remove(new_m->filename, new_m->pool);
593 return status;
594 }
595
596 new_m->reqsize = new_m->realsize - sizeof(apr_size_t);
597
598 new_m->base = mmap(NULL, new_m->realsize, PROT_READ | PROT_WRITE,
599 MAP_SHARED, tmpfd, 0);
600 /* FIXME: check for errors */
601
603 if (status != APR_SUCCESS) {
604 return status;
605 }
606
607 /* metadata isn't part of the usable segment */
608 new_m->usable = (char *)new_m->base + APR_ALIGN_DEFAULT(sizeof(apr_size_t));
609
612 *m = new_m;
613 return APR_SUCCESS;
614
615#elif APR_USE_SHMEM_SHMGET
618 apr_file_t *file; /* file where metadata is stored */
620
621 new_m = apr_palloc(pool, sizeof(apr_shm_t));
622
625 if (status != APR_SUCCESS) {
626 return status;
627 }
628
629 nbytes = sizeof(new_m->reqsize);
630 status = apr_file_read(file, (void *)&(new_m->reqsize),
631 &nbytes);
632 if (status != APR_SUCCESS) {
633 return status;
634 }
636 if (status != APR_SUCCESS) {
637 return status;
638 }
639
640 new_m->filename = apr_pstrdup(pool, filename);
641 new_m->pool = pool;
642 new_m->shmkey = our_ftok(filename);
643 if (new_m->shmkey == (key_t)-1) {
644 return errno;
645 }
646 if ((new_m->shmid = shmget(new_m->shmkey, 0, SHM_R | SHM_W)) == -1) {
647 return errno;
648 }
649 if ((new_m->base = shmat(new_m->shmid, NULL, 0)) == (void *)-1) {
650 return errno;
651 }
652 new_m->usable = new_m->base;
653 new_m->realsize = new_m->reqsize;
654
657 *m = new_m;
658 return APR_SUCCESS;
659
660#else
661 return APR_ENOTIMPL;
662#endif
663 }
664}
665
667 const char *filename,
670{
671 return apr_shm_attach(m, filename, pool);
672}
673
675{
678 return rv;
679}
680
682{
683 return m->usable;
684}
685
687{
688 return m->reqsize;
689}
690
692{
693#if APR_USE_SHMEM_SHMGET || APR_USE_SHMEM_SHMGET_ANON
694 struct shmid_ds shmbuf;
695 int shmid;
697
698 if ((shmid = shmget(m->shmkey, 0, SHM_R | SHM_W)) == -1) {
699 return errno;
700 }
701 shmbuf.shm_perm.uid = uid;
702 shmbuf.shm_perm.gid = gid;
703 shmbuf.shm_perm.mode = apr_unix_perms2mode(perms);
704 if (shmctl(shmid, IPC_SET, &shmbuf) == -1) {
705 return errno;
706 }
707 return APR_SUCCESS;
708#elif APR_USE_SHMEM_MMAP_SHM && !defined(DARWIN)
709 /* ### This hangs or fails on MacOS, so skipping this for the
710 * ENOTIMPL case there - unclear why or if that's fixable. */
712 const char *shm_name;
713 int fd;
714 apr_status_t rv;
715
716 if (!shm->filename)
717 return APR_ENOTIMPL;
718
720
721 fd = shm_open(shm_name, O_RDWR, 0);
722 if (fd == -1)
723 return errno;
724
725 if (fchown(fd, uid, gid)) {
726 rv = errno;
727 close(fd);
728 return rv;
729 }
730
732 rv = errno;
733 close(fd);
734 return rv;
735 }
736 close(fd);
737 return APR_SUCCESS;
738#elif APR_USE_SHMEM_MMAP_TMP
740
741 if (!shm->filename)
742 return APR_ENOTIMPL;
743
744 if (chown(shm->filename, uid, gid))
745 return errno;
746
748 return errno;
749
750 return APR_SUCCESS;
751#else
752 return APR_ENOTIMPL;
753#endif
754}
755
757
759 apr_shm_t *shm)
760{
761 return APR_ENOTIMPL;
762}
763
767{
768 return APR_ENOTIMPL;
769}
770
#define SHM_R
#define MAP_FAILED
#define SHM_W
APR Error Codes.
APR Miscellaneous library routines.
APR Hash Tables.
APR Strings library.
APR User ID Services.
#define APR_ESPIPE
Definition apr_errno.h:718
#define APR_ENOTIMPL
Definition apr_errno.h:476
#define APR_EINVAL
Definition apr_errno.h:711
apr_fileperms_t apr_uid_t uid
apr_fileperms_t apr_uid_t apr_gid_t gid
apr_bucket apr_bucket_brigade * a
apr_file_t * fd
const char apr_ssize_t int flags
Definition apr_encode.h:168
const char apr_ssize_t slen
Definition apr_encode.h:168
const void apr_status_t(*) apr_status_t(* APR_DECLARE)(void) apr_pool_pre_cleanup_register(apr_pool_t *p
Definition apr_pools.h:646
apr_size_t size
const char int apr_pool_t * pool
Definition apr_cstr.h:84
#define APR_SUCCESS
Definition apr_errno.h:225
int apr_status_t
Definition apr_errno.h:44
void apr_size_t * nbytes
const char apr_fileperms_t perms
const char apr_file_t * file
#define APR_READ
Definition apr_file_io.h:93
#define APR_EXCL
Definition apr_file_io.h:99
#define APR_WRITE
Definition apr_file_io.h:94
#define APR_FOPEN_EXCL
Definition apr_file_io.h:63
#define APR_CREATE
Definition apr_file_io.h:95
#define APR_FOPEN_WRITE
Definition apr_file_io.h:55
#define APR_FOPEN_READ
Definition apr_file_io.h:54
#define APR_FOPEN_CREATE
Definition apr_file_io.h:56
#define APR_FPROT_UWRITE
#define APR_OS_DEFAULT
#define APR_FPROT_UREAD
#define APR_ALIGN_DEFAULT(size)
const apr_hash_t * h1
Definition apr_hash.h:232
const apr_hash_t const apr_hash_t * h2
Definition apr_hash.h:233
#define APR_PERMS_SET_IMPLEMENT(type)
apr_pool_t * b
Definition apr_pools.h:529
#define APR_POOL_IMPLEMENT_ACCESSOR(type)
Definition apr_pools.h:91
void * apr_os_shm_t
apr_os_shm_t * osshm
apr_shm_t * shm
apr_size_t const char * filename
Definition apr_shm.h:72
apr_size_t reqsize
Definition apr_shm.h:71
const void * m
int int status
gid_t apr_gid_t
Definition apr_user.h:54
uid_t apr_uid_t
Definition apr_user.h:45
apr_pool_t * p
Definition md_event.c:32
return NULL
Definition mod_so.c:359
apr_pool_t * pool
const char * filename
mode_t apr_unix_perms2mode(apr_fileperms_t perms)
Definition fileacc.c:35
apr_status_t apr_file_trunc(apr_file_t *fp, apr_off_t offset)
Definition seek.c:99
static apr_status_t shm_cleanup_owner(void *m_)
Definition shm.c:81
static apr_status_t shm_cleanup_attach(void *m_)
Definition shm.c:507