varnish-cache/lib/libvarnish/vsmw.c
1
/*-
2
 * Copyright (c) 2010-2011 Varnish Software AS
3
 * All rights reserved.
4
 *
5
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
6
 *
7
 * Redistribution and use in source and binary forms, with or without
8
 * modification, are permitted provided that the following conditions
9
 * are met:
10
 * 1. Redistributions of source code must retain the above copyright
11
 *    notice, this list of conditions and the following disclaimer.
12
 * 2. Redistributions in binary form must reproduce the above copyright
13
 *    notice, this list of conditions and the following disclaimer in the
14
 *    documentation and/or other materials provided with the distribution.
15
 *
16
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
20
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26
 * SUCH DAMAGE.
27
 *
28
 * VSM stuff common to manager and child.
29
 *
30
 * Please see comments in <vsm_priv.h> for details of protocols and
31
 * data consistency.
32
 *
33
 */
34
35
#include "config.h"
36
37
#include <errno.h>
38
#include <fcntl.h>
39
#include <stdarg.h>
40
#include <stdio.h>
41
#include <stdint.h>
42
#include <stdlib.h>
43
#include <string.h>
44
#include <time.h>
45
#include <unistd.h>
46
#include <sys/mman.h>
47
#include <sys/stat.h>
48
49
#include "vdef.h"
50
#include "vas.h"
51
#include "vsb.h"
52
#include "miniobj.h"
53
#include "vsmw.h"
54
#include "vqueue.h"
55
56
#include "vfil.h"
57
#include "vrnd.h"
58
59
#ifndef MAP_HASSEMAPHORE
60
#  define MAP_HASSEMAPHORE 0 /* XXX Linux */
61
#endif
62
63
#ifndef MAP_NOSYNC
64
#  define MAP_NOSYNC 0 /* XXX Linux */
65
#endif
66
67
/*--------------------------------------------------------------------*/
68
69
struct vsmwseg {
70
        unsigned                        magic;
71
#define VSMWSEG_MAGIC                   0x7e4ccaea
72
        VTAILQ_ENTRY(vsmwseg)           list;
73
        char                            *fn;
74
75
        char                            *class;
76
        size_t                          len;
77
        char                            *id;
78
        void                            *ptr;
79
};
80
81
struct vsmw {
82
        unsigned                        magic;
83
#define VSMW_MAGIC                      0xc2ca2cd9
84
        int                             vdirfd;
85
        int                             mode;
86
        char                            *idx;
87
        VTAILQ_HEAD(, vsmwseg)          segs;
88
        struct vsb                      *vsb;
89
        pid_t                           pid;
90
        time_t                          birth;
91
};
92
93
/*--------------------------------------------------------------------*/
94
95
static void
96 1271
vsmw_idx_head(const struct vsmw *vsmw, int fd)
97
{
98
        char buf[64];
99
100 1271
        bprintf(buf, "# %jd %jd\n", (intmax_t)vsmw->pid, (intmax_t)vsmw->birth);
101 1271
        assert(write(fd, buf, strlen(buf)) == strlen(buf));
102 1271
}
103
104
static void
105 23348
vsmw_write_index(const struct vsmw *vsmw, int fd, const struct vsmwseg *seg)
106
{
107
        ssize_t s;
108
109 23348
        CHECK_OBJ_NOTNULL(vsmw, VSMW_MAGIC);
110 23348
        CHECK_OBJ_NOTNULL(seg, VSMWSEG_MAGIC);
111 23348
        VSB_clear(vsmw->vsb);
112 23348
        VSB_printf(vsmw->vsb, "%s %zu %s %s\n",
113
            seg->fn,
114
            seg->len,
115
            seg->class,
116
            seg->id);
117 23348
        AZ(VSB_finish(vsmw->vsb));
118 23348
        s = write(fd, VSB_data(vsmw->vsb), VSB_len(vsmw->vsb));
119 23348
        assert(s == VSB_len(vsmw->vsb));
120 23348
}
121
122
/*--------------------------------------------------------------------*/
123
124
static void
125 22372
vsmw_mkent(const struct vsmw *vsmw, const char *pfx)
126
{
127
        int fd;
128
129
        while (1) {
130 22372
                VSB_clear(vsmw->vsb);
131 22372
                VSB_printf(vsmw->vsb, "_.%s", pfx);
132 22372
                VSB_printf(vsmw->vsb, ".%08lx", VRND_RandomTestable());
133 22372
                VSB_printf(vsmw->vsb, "%08lx", VRND_RandomTestable());
134 22372
                AZ(VSB_finish(vsmw->vsb));
135 22372
                fd = openat(vsmw->vdirfd, VSB_data(vsmw->vsb), O_RDONLY);
136 22372
                if (fd < 0 && errno == ENOENT)
137 44744
                        return;
138 0
                if (fd >= 0)
139 0
                        AZ(close(fd));
140 0
        }
141
}
142
143
/*--------------------------------------------------------------------*/
144
145
void *
146 22339
VSMW_Allocv(struct vsmw *vsmw, const char *class, size_t len,
147
    const char *fmt, va_list va)
148
{
149
        struct vsmwseg *seg;
150
        int fd;
151
        size_t ps;
152
153 22339
        CHECK_OBJ_NOTNULL(vsmw, VSMW_MAGIC);
154
155 22339
        ALLOC_OBJ(seg, VSMWSEG_MAGIC);
156 22339
        AN(seg);
157 22339
        REPLACE(seg->class, class);
158 22339
        seg->len = len;
159
160 22339
        VSB_clear(vsmw->vsb);
161 22339
        VSB_vprintf(vsmw->vsb, fmt, va);
162 22339
        AZ(VSB_finish(vsmw->vsb));
163 22339
        REPLACE(seg->id, VSB_data(vsmw->vsb));
164
165 22339
        vsmw_mkent(vsmw, class);
166 22339
        REPLACE(seg->fn, VSB_data(vsmw->vsb));
167
168 22339
        ps = getpagesize();
169 22339
        len = RUP2(len, ps);
170
171 22339
        fd = openat(vsmw->vdirfd, seg->fn,
172
            O_RDWR | O_CREAT | O_EXCL, vsmw->mode);
173 22339
        assert(fd >= 0);
174
175 22339
        AZ(VFIL_allocate(fd, (off_t)len, 1));
176
177 22339
        seg->ptr = (void *)mmap(NULL, len,
178
            PROT_READ|PROT_WRITE,
179
            MAP_HASSEMAPHORE | MAP_NOSYNC | MAP_SHARED,
180
            fd, 0);
181
182 22339
        AZ(close(fd));
183 22339
        assert(seg->ptr != MAP_FAILED);
184 22339
        (void)mlock(seg->ptr, len);
185
186 22339
        VTAILQ_INSERT_TAIL(&vsmw->segs, seg, list);
187 22339
        fd = openat(vsmw->vdirfd, vsmw->idx, O_APPEND | O_WRONLY);
188 22339
        assert(fd >= 0);
189 22339
        vsmw_write_index(vsmw, fd, seg);
190 22339
        AZ(close(fd));
191
192 22339
        return (seg->ptr);
193
}
194
195
void *
196 3716
VSMW_Allocf(struct vsmw *vsmw, const char *class, size_t len,
197
    const char *fmt, ...)
198
{
199
        va_list ap;
200
        void *p;
201
202 3716
        va_start(ap, fmt);
203 3716
        p = VSMW_Allocv(vsmw, class, len, fmt, ap);
204 3716
        va_end(ap);
205 3716
        return (p);
206
}
207
208
/*--------------------------------------------------------------------*/
209
static void
210 3759
vsmw_delseg(struct vsmw *vsmw, struct vsmwseg *seg, int fixidx)
211
{
212 3759
        char *t = NULL;
213
        int fd;
214
        size_t len;
215
216 3759
        CHECK_OBJ_NOTNULL(vsmw, VSMW_MAGIC);
217 3759
        CHECK_OBJ_NOTNULL(seg, VSMWSEG_MAGIC);
218
219 3759
        len = getpagesize();
220 3759
        len = RUP2(seg->len, len);
221 3759
        AZ(munmap(seg->ptr, len));
222
223 3759
        VTAILQ_REMOVE(&vsmw->segs, seg, list);
224 3759
        if (unlinkat(vsmw->vdirfd, seg->fn, 0))
225 0
                assert (errno == ENOENT);
226 3759
        REPLACE(seg->fn, NULL);
227 3759
        REPLACE(seg->class, NULL);
228 3759
        REPLACE(seg->id, NULL);
229 3759
        FREE_OBJ(seg);
230
231 3759
        if (fixidx) {
232 33
                vsmw_mkent(vsmw, vsmw->idx);
233 33
                REPLACE(t, VSB_data(vsmw->vsb));
234 33
                AN(t);
235 33
                fd = openat(vsmw->vdirfd, t, O_WRONLY|O_CREAT|O_EXCL, vsmw->mode);
236 33
                assert(fd >= 0);
237 33
                vsmw_idx_head(vsmw, fd);
238 1042
                VTAILQ_FOREACH(seg, &vsmw->segs, list)
239 1009
                        vsmw_write_index(vsmw, fd, seg);
240 33
                AZ(close(fd));
241 33
                AZ(renameat(vsmw->vdirfd, t, vsmw->vdirfd, vsmw->idx));
242 33
                REPLACE(t, NULL);
243
        }
244 3759
}
245
246
/*--------------------------------------------------------------------*/
247
248
void
249 33
VSMW_Free(struct vsmw *vsmw, void **pp)
250
{
251
        struct vsmwseg *seg;
252
        void *p;
253
254 33
        CHECK_OBJ_NOTNULL(vsmw, VSMW_MAGIC);
255 33
        AN(pp);
256 33
        p = *pp;
257 33
        AN(p);
258 33
        *pp = NULL;
259 975
        VTAILQ_FOREACH(seg, &vsmw->segs, list)
260 975
                if (seg->ptr == p)
261 33
                        break;
262 33
        AN(seg);
263 33
        vsmw_delseg(vsmw, seg, 1);
264 33
}
265
266
/*--------------------------------------------------------------------*/
267
268
struct vsmw *
269 1238
VSMW_New(int vdirfd, int mode, const char *idxname)
270
{
271
        struct vsmw *vsmw;
272
        int fd;
273
274 1238
        assert(vdirfd > 0);
275 1238
        assert(mode > 0);
276 1238
        AN(idxname);
277
278 1238
        ALLOC_OBJ(vsmw, VSMW_MAGIC);
279 1238
        AN(vsmw);
280
281 1238
        VTAILQ_INIT(&vsmw->segs);
282 1238
        vsmw->vsb = VSB_new_auto();
283 1238
        AN(vsmw->vsb);
284 1238
        REPLACE(vsmw->idx, idxname);
285 1238
        vsmw->mode = mode;
286 1238
        vsmw->vdirfd = vdirfd;
287 1238
        vsmw->pid = getpid();
288 1238
        vsmw->birth = time(NULL);
289
290 1238
        if (unlinkat(vdirfd, vsmw->idx, 0))
291 1238
                assert (errno == ENOENT);
292 2476
        fd = openat(vdirfd,
293 1238
            vsmw->idx, O_APPEND | O_WRONLY | O_CREAT, vsmw->mode);
294 1238
        assert(fd >= 0);
295 1238
        vsmw_idx_head(vsmw, fd);
296 1238
        AZ(close(fd));
297
298 1238
        return (vsmw);
299
}
300
301
void
302 624
VSMW_Destroy(struct vsmw **pp)
303
{
304
        struct vsmw *vsmw;
305
        struct vsmwseg *seg, *s2;
306
307 624
        TAKE_OBJ_NOTNULL(vsmw, pp, VSMW_MAGIC);
308 624
        if (unlinkat(vsmw->vdirfd, vsmw->idx, 0))
309 0
                assert (errno == ENOENT);
310 624
        REPLACE(vsmw->idx, NULL);
311 4350
        VTAILQ_FOREACH_SAFE(seg, &vsmw->segs, list, s2)
312 3726
                vsmw_delseg(vsmw, seg, 0);
313 624
        VSB_destroy(&vsmw->vsb);
314 624
        AZ(close(vsmw->vdirfd));
315 624
        FREE_OBJ(vsmw);
316 624
}