1 /*
2   +----------------------------------------------------------------------+
3   | APC                                                                  |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 2006-2011 The PHP Group                                |
6   +----------------------------------------------------------------------+
7   | This source file is subject to version 3.01 of the PHP license,      |
8   | that is bundled with this package in the file LICENSE, and is        |
9   | available through the world-wide-web at the following url:           |
10   | http://www.php.net/license/3_01.txt                                  |
11   | If you did not receive a copy of the PHP license and are unable to   |
12   | obtain it through the world-wide-web, please send a note to          |
13   | license@php.net so we can mail you a copy immediately.               |
14   +----------------------------------------------------------------------+
15   | Authors: Rasmus Lerdorf <rasmus@php.net>                             |
16   +----------------------------------------------------------------------+
17 
18    This software was contributed to PHP by Community Connect Inc. in 2002
19    and revised in 2005 by Yahoo! Inc. to add support for PHP 5.1.
20    Future revisions and derivatives of this source code must acknowledge
21    Community Connect Inc. as the original contributor of this module by
22    leaving this note intact in the source code.
23 
24    All other licensing and usage conditions are those of the PHP Group.
25 
26  */
27 
28 #include "apc.h"
29 #include "apc_mmap.h"
30 #include "apc_lock.h"
31 
32 #if APC_MMAP
33 
34 #include <fcntl.h>
35 #include <sys/types.h>
36 #include <sys/mman.h>
37 
38 /*
39  * Some operating systems (like FreeBSD) have a MAP_NOSYNC flag that
40  * tells whatever update daemons might be running to not flush dirty
41  * vm pages to disk unless absolutely necessary.  My guess is that
42  * most systems that don't have this probably default to only synching
43  * to disk when absolutely necessary.
44  */
45 #ifndef MAP_NOSYNC
46 #define MAP_NOSYNC 0
47 #endif
48 
49 /* support for systems where MAP_ANONYMOUS is defined but not MAP_ANON, ie: HP-UX bug #14615 */
50 #if !defined(MAP_ANON) && defined(MAP_ANONYMOUS)
51 # define MAP_ANON MAP_ANONYMOUS
52 #endif
53 
apc_mmap(char * file_mask,size_t size)54 apc_segment_t apc_mmap(char *file_mask, size_t size)
55 {
56 	apc_segment_t segment;
57 
58 	int fd = -1;
59 	int flags = MAP_SHARED | MAP_NOSYNC;
60 #ifdef APC_MEMPROTECT
61 	int remap = 1;
62 #endif
63 
64 	/* If no filename was provided, do an anonymous mmap */
65 	if(!file_mask || (file_mask && !strlen(file_mask))) {
66 #if !defined(MAP_ANON)
67 		zend_error_noreturn(E_CORE_ERROR, "Anonymous mmap does not appear to be available on this system (MAP_ANON/MAP_ANONYMOUS).  Please see the apc.mmap_file_mask INI option.");
68 #else
69 		fd = -1;
70 		flags = MAP_SHARED | MAP_ANON;
71 #ifdef APC_MEMPROTECT
72 		remap = 0;
73 #endif
74 #endif
75 	} else if(!strcmp(file_mask,"/dev/zero")) {
76 		fd = open("/dev/zero", O_RDWR, S_IRUSR | S_IWUSR);
77 		if(fd == -1) {
78 			zend_error_noreturn(E_CORE_ERROR, "apc_mmap: open on /dev/zero failed");
79 		}
80 #ifdef APC_MEMPROTECT
81 		remap = 0; /* cannot remap */
82 #endif
83 	} else {
84 		/*
85 		 * Otherwise we do a normal filesystem mmap
86 		 */
87 		fd = mkstemp(file_mask);
88 		if(fd == -1) {
89 			zend_error_noreturn(E_CORE_ERROR, "apc_mmap: mkstemp on %s failed", file_mask);
90 		}
91 		if (ftruncate(fd, size) < 0) {
92 			close(fd);
93 			unlink(file_mask);
94 			zend_error_noreturn(E_CORE_ERROR, "apc_mmap: ftruncate failed");
95 		}
96 		unlink(file_mask);
97 	}
98 
99 	segment.shmaddr = (void *)mmap(NULL, size, PROT_READ | PROT_WRITE, flags, fd, 0);
100 	segment.size = size;
101 
102 #ifdef APC_MEMPROTECT
103 	if(remap) {
104 		segment.roaddr = (void *)mmap(NULL, size, PROT_READ, flags, fd, 0);
105 	} else {
106 		segment.roaddr = NULL;
107 	}
108 #endif
109 
110 	if ((long)segment.shmaddr == -1) {
111 		zend_error_noreturn(E_CORE_ERROR, "apc_mmap: Failed to mmap %zu bytes. Is your apc.shm_size too large?", size);
112 	}
113 
114 	if (fd != -1) close(fd);
115 
116 	return segment;
117 }
118 
apc_unmap(apc_segment_t * segment)119 void apc_unmap(apc_segment_t *segment)
120 {
121 	if (munmap(segment->shmaddr, segment->size) < 0) {
122 		apc_warning("apc_unmap: munmap failed");
123 	}
124 
125 #ifdef APC_MEMPROTECT
126 	if (segment->roaddr && munmap(segment->roaddr, segment->size) < 0) {
127 		apc_warning("apc_unmap: munmap failed");
128 	}
129 #endif
130 
131 }
132 
133 #endif
134 
135 /*
136  * Local variables:
137  * tab-width: 4
138  * c-basic-offset: 4
139  * End:
140  * vim>600: noexpandtab sw=4 ts=4 sts=4 fdm=marker
141  * vim<600: noexpandtab sw=4 ts=4 sts=4
142  */
143