1 /* $Id$ */
2
3 /*
4 * Copyright (c) 2006 Nicholas Marriott <nicholas.marriott@gmail.com>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <sys/types.h>
20 #include <sys/mman.h>
21
22 #include <fcntl.h>
23 #include <string.h>
24 #include <unistd.h>
25
26 #include "fdm.h"
27
28 /*
29 * This implements shared memory using mmap'd files in TMPDIR.
30 */
31
32 int shm_expand(struct shm *, size_t);
33
34 char shm_block[BUFSIZ];
35
36 #ifdef MAP_NOSYNC
37 #define SHM_FLAGS MAP_SHARED|MAP_NOSYNC
38 #else
39 #define SHM_FLAGS MAP_SHARED
40 #endif
41 #define SHM_PROT PROT_READ|PROT_WRITE
42
43 /* Work out shm path. */
44 char *
shm_path(struct shm * shm)45 shm_path(struct shm *shm)
46 {
47 static char path[MAXPATHLEN];
48
49 if (ppath(path, sizeof path, "%s/%s", conf.tmp_dir, shm->name) != 0)
50 return (NULL);
51 return (path);
52 }
53
54 /* Expand or reduce shm file to size. */
55 int
shm_expand(struct shm * shm,size_t size)56 shm_expand(struct shm *shm, size_t size)
57 {
58 ssize_t n;
59
60 if (size == shm->size)
61 return (0);
62
63 if (size < shm->size)
64 return (ftruncate(shm->fd, size) != 0);
65
66 if (lseek(shm->fd, shm->size, SEEK_SET) == -1)
67 return (-1);
68
69 /*
70 * Fill the file using write(2) to avoid fragmentation problems on
71 * FreeBSD and also to detect disk full.
72 */
73 while (size > sizeof shm_block) {
74 if ((n = write(shm->fd, shm_block, sizeof shm_block)) == -1)
75 return (-1);
76 if (n != sizeof shm_block) {
77 errno = EIO;
78 return (-1);
79 }
80 size -= sizeof shm_block;
81 }
82 if (size > 0) {
83 if ((n = write(shm->fd, shm_block, size)) == -1)
84 return (-1);
85 if ((size_t) n != size) {
86 errno = EIO;
87 return (-1);
88 }
89 }
90
91 /*
92 * Sync the fd, should hopefully fail if disk full.
93 */
94 if (fsync(shm->fd) != 0)
95 return (-1);
96
97 return (0);
98 }
99
100 /* Create an shm file and map it. */
101 void *
shm_create(struct shm * shm,size_t size)102 shm_create(struct shm *shm, size_t size)
103 {
104 int saved_errno;
105 char *path;
106
107 if (size == 0)
108 fatalx("zero size");
109
110 if (ppath(
111 shm->name, sizeof shm->name, "%s.XXXXXXXXXX", __progname) != 0)
112 return (NULL);
113 if ((path = shm_path(shm)) == NULL)
114 return (NULL);
115 if ((shm->fd = mkstemp(path)) == -1)
116 return (NULL);
117 strlcpy(shm->name, xbasename(path), sizeof shm->name);
118
119 if (shm_expand(shm, size) != 0)
120 goto error;
121
122 shm->data = mmap(NULL, size, SHM_PROT, SHM_FLAGS, shm->fd, 0);
123 if (shm->data == MAP_FAILED)
124 goto error;
125 madvise(shm->data, size, MADV_SEQUENTIAL);
126
127 shm->size = size;
128 return (shm->data);
129
130 error:
131 saved_errno = errno;
132 unlink(path);
133 errno = saved_errno;
134 return (NULL);
135 }
136
137 /* Destroy shm file. */
138 void
shm_destroy(struct shm * shm)139 shm_destroy(struct shm *shm)
140 {
141 char *path;
142
143 if (*shm->name == '\0')
144 return;
145
146 shm_close(shm);
147
148 if ((path = shm_path(shm)) == NULL)
149 fatal("unlink failed");
150 if (unlink(path) != 0)
151 fatal("unlink failed");
152
153 *shm->name = '\0';
154 }
155
156 /* Close and unmap shm without destroying file. */
157 void
shm_close(struct shm * shm)158 shm_close(struct shm *shm)
159 {
160 if (shm->fd == -1)
161 return;
162
163 if (munmap(shm->data, shm->size) != 0)
164 fatal("munmap failed");
165 shm->data = NULL;
166
167 close(shm->fd);
168 shm->fd = -1;
169 }
170
171 /* Reopen and map shm file. */
172 void *
shm_reopen(struct shm * shm)173 shm_reopen(struct shm *shm)
174 {
175 char *path;
176
177 if ((path = shm_path(shm)) == NULL)
178 return (NULL);
179 if ((shm->fd = open(path, O_RDWR, 0)) == -1)
180 return (NULL);
181
182 shm->data = mmap(NULL, shm->size, SHM_PROT, SHM_FLAGS, shm->fd, 0);
183 if (shm->data == MAP_FAILED)
184 return (NULL);
185 madvise(shm->data, shm->size, MADV_SEQUENTIAL);
186
187 return (shm->data);
188 }
189
190 /* Set ownership of shm file. */
191 int
shm_owner(struct shm * shm,uid_t uid,gid_t gid)192 shm_owner(struct shm *shm, uid_t uid, gid_t gid)
193 {
194 if (fchown(shm->fd, uid, gid) != 0)
195 return (-1);
196
197 return (0);
198 }
199
200 /* Resize an shm file. */
201 void *
shm_resize(struct shm * shm,size_t nmemb,size_t size)202 shm_resize(struct shm *shm, size_t nmemb, size_t size)
203 {
204 size_t newsize = nmemb * size;
205
206 if (size == 0)
207 fatalx("zero size");
208 if (SIZE_MAX / nmemb < size)
209 fatalx("nmemb * size > SIZE_MAX");
210
211 #ifndef HAVE_MREMAP
212 if (munmap(shm->data, shm->size) != 0)
213 fatal("munmap failed");
214 shm->data = NULL;
215 #endif
216
217 if (shm_expand(shm, newsize) != 0)
218 return (NULL);
219
220 #ifdef HAVE_MREMAP
221 shm->data = mremap(shm->data, shm->size, newsize, MREMAP_MAYMOVE);
222 #else
223 shm->data = mmap(NULL, newsize, SHM_PROT, SHM_FLAGS, shm->fd, 0);
224 #endif
225 if (shm->data == MAP_FAILED)
226 return (NULL);
227 madvise(shm->data, newsize, MADV_SEQUENTIAL);
228
229 shm->size = newsize;
230 return (shm->data);
231 }
232