varnish-cache/bin/varnishd/mgt/mgt_vcc.c
1
/*-
2
 * Copyright (c) 2006 Verdens Gang AS
3
 * Copyright (c) 2006-2015 Varnish Software AS
4
 * All rights reserved.
5
 *
6
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 * 1. Redistributions of source code must retain the above copyright
12
 *    notice, this list of conditions and the following disclaimer.
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in the
15
 *    documentation and/or other materials provided with the distribution.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
 * SUCH DAMAGE.
28
 *
29
 * VCL compiler stuff
30
 */
31
32
#include "config.h"
33
34
#include <errno.h>
35
#include <fcntl.h>
36
#include <stdio.h>
37
#include <stdlib.h>
38
#include <string.h>
39
#include <unistd.h>
40
#include <sys/stat.h>
41
42
#include "mgt/mgt.h"
43
#include "common/heritage.h"
44
#include "storage/storage.h"
45
46
#include "libvcc.h"
47
#include "vcli_serve.h"
48
#include "vfil.h"
49
#include "vsub.h"
50
#include "vav.h"
51
#include "vtim.h"
52
53
struct vcc_priv {
54
        unsigned        magic;
55
#define VCC_PRIV_MAGIC  0x70080cb8
56
        char            *dir;
57
        const char      *vclsrc;
58
        const char      *vclsrcfile;
59
        char            *csrcfile;
60
        char            *libfile;
61
};
62
63
char *mgt_cc_cmd;
64
const char *mgt_vcl_path;
65
const char *mgt_vmod_path;
66
unsigned mgt_vcc_err_unref;
67
unsigned mgt_vcc_allow_inline_c;
68
unsigned mgt_vcc_unsafe_path;
69
70
71
#define VGC_SRC         "vgc.c"
72
#define VGC_LIB         "vgc.so"
73
74
/*--------------------------------------------------------------------*/
75
76
void
77 2
mgt_DumpBuiltin(void)
78
{
79 2
        printf("%s\n", builtin_vcl);
80 2
}
81
82
/*--------------------------------------------------------------------
83
 * Invoke system VCC compiler in a sub-process
84
 */
85
86
static void v_matchproto_(vsub_func_f)
87 906
run_vcc(void *priv)
88
{
89
        struct vsb *csrc;
90 906
        struct vsb *sb = NULL;
91
        struct vcc_priv *vp;
92
        int fd, i, l;
93
        struct vcc *vcc;
94
        struct stevedore *stv;
95
96 906
        VJ_subproc(JAIL_SUBPROC_VCC);
97 906
        CAST_OBJ_NOTNULL(vp, priv, VCC_PRIV_MAGIC);
98
99 906
        AZ(chdir(vp->dir));
100
101 906
        vcc = VCC_New();
102 906
        AN(vcc);
103 906
        VCC_Builtin_VCL(vcc, builtin_vcl);
104 906
        VCC_VCL_path(vcc, mgt_vcl_path);
105 906
        VCC_VMOD_path(vcc, mgt_vmod_path);
106 906
        VCC_Err_Unref(vcc, mgt_vcc_err_unref);
107 906
        VCC_Allow_InlineC(vcc, mgt_vcc_allow_inline_c);
108 906
        VCC_Unsafe_Path(vcc, mgt_vcc_unsafe_path);
109 3632
        STV_Foreach(stv)
110 1820
                VCC_Predef(vcc, "VCL_STEVEDORE", stv->ident);
111 906
        mgt_vcl_export_labels(vcc);
112 906
        csrc = VCC_Compile(vcc, &sb, vp->vclsrc, vp->vclsrcfile);
113 906
        AZ(VSB_finish(sb));
114 906
        if (VSB_len(sb))
115 155
                printf("%s", VSB_data(sb));
116 906
        VSB_destroy(&sb);
117 906
        if (csrc == NULL)
118 151
                exit(2);
119
120 755
        fd = open(VGC_SRC, O_WRONLY|O_TRUNC|O_CREAT, 0600);
121 755
        if (fd < 0) {
122 0
                fprintf(stderr, "VCC cannot open %s", vp->csrcfile);
123 0
                exit(2);
124
        }
125 755
        l = VSB_len(csrc);
126 755
        i = write(fd, VSB_data(csrc), l);
127 755
        if (i != l) {
128 0
                fprintf(stderr, "VCC cannot write %s", vp->csrcfile);
129 0
                exit(2);
130
        }
131 755
        closefd(&fd);
132 755
        VSB_destroy(&csrc);
133 755
        exit(0);
134
}
135
136
/*--------------------------------------------------------------------
137
 * Invoke system C compiler in a sub-process
138
 */
139
140
static void v_matchproto_(vsub_func_f)
141 755
run_cc(void *priv)
142
{
143
        struct vcc_priv *vp;
144
        struct vsb *sb;
145
        int pct;
146
        char *p;
147
148 755
        VJ_subproc(JAIL_SUBPROC_CC);
149 755
        CAST_OBJ_NOTNULL(vp, priv, VCC_PRIV_MAGIC);
150
151 755
        AZ(chdir(vp->dir));
152
153 755
        sb = VSB_new_auto();
154 755
        AN(sb);
155 388070
        for (p = mgt_cc_cmd, pct = 0; *p; ++p) {
156 387315
                if (pct) {
157 1510
                        switch (*p) {
158
                        case 's':
159 755
                                VSB_cat(sb, VGC_SRC);
160 755
                                break;
161
                        case 'o':
162 755
                                VSB_cat(sb, VGC_LIB);
163 755
                                break;
164
                        case '%':
165 0
                                VSB_putc(sb, '%');
166 0
                                break;
167
                        default:
168 0
                                VSB_putc(sb, '%');
169 0
                                VSB_putc(sb, *p);
170 0
                                break;
171
                        }
172 1510
                        pct = 0;
173 385805
                } else if (*p == '%') {
174 1510
                        pct = 1;
175
                } else {
176 384295
                        VSB_putc(sb, *p);
177
                }
178
        }
179 755
        if (pct)
180 0
                VSB_putc(sb, '%');
181 755
        AZ(VSB_finish(sb));
182
183 755
        (void)umask(027);
184 755
        (void)execl("/bin/sh", "/bin/sh", "-c", VSB_data(sb), (char*)0);
185 755
        VSB_destroy(&sb);                               // For flexelint
186 0
}
187
188
/*--------------------------------------------------------------------
189
 * Attempt to open compiled VCL in a sub-process
190
 */
191
192
static void v_matchproto_(vsub_func_f)
193 755
run_dlopen(void *priv)
194
{
195
        struct vcc_priv *vp;
196
197 755
        VJ_subproc(JAIL_SUBPROC_VCLLOAD);
198 755
        CAST_OBJ_NOTNULL(vp, priv, VCC_PRIV_MAGIC);
199 755
        if (VCL_TestLoad(vp->libfile))
200 0
                exit(1);
201 755
        exit(0);
202
}
203
204
/*--------------------------------------------------------------------
205
 * Touch a filename and make it available to privsep-privs
206
 */
207
208
static int
209 1812
mgt_vcc_touchfile(const char *fn, struct vsb *sb)
210
{
211
        int i;
212
213 1812
        i = open(fn, O_WRONLY|O_CREAT|O_TRUNC, 0640);
214 1812
        if (i < 0) {
215 0
                VSB_printf(sb, "Failed to create %s: %s", fn, strerror(errno));
216 0
                return (2);
217
        }
218 1812
        if (fchown(i, mgt_param.uid, mgt_param.gid) != 0)
219 1812
                if (geteuid() == 0)
220 0
                        VSB_printf(sb, "Failed to change owner on %s: %s\n",
221 0
                            fn, strerror(errno));
222 1812
        closefd(&i);
223 1812
        return (0);
224
}
225
226
/*--------------------------------------------------------------------
227
 * Compile a VCL program, return shared object, errors in sb.
228
 */
229
230
static unsigned
231 906
mgt_vcc_compile(struct vcc_priv *vp, struct vsb *sb, int C_flag)
232
{
233
        char *csrc;
234
        unsigned subs;
235
236 906
        if (mgt_vcc_touchfile(vp->csrcfile, sb))
237 0
                return (2);
238 906
        if (mgt_vcc_touchfile(vp->libfile, sb))
239 0
                return (2);
240
241 906
        subs = VSUB_run(sb, run_vcc, vp, "VCC-compiler", -1);
242 906
        if (subs)
243 151
                return (subs);
244
245 755
        if (C_flag) {
246 2
                csrc = VFIL_readfile(NULL, vp->csrcfile, NULL);
247 2
                AN(csrc);
248 2
                VSB_cat(sb, csrc);
249 2
                free(csrc);
250
        }
251
252 755
        subs = VSUB_run(sb, run_cc, vp, "C-compiler", 10);
253 755
        if (subs)
254 0
                return (subs);
255
256 755
        subs = VSUB_run(sb, run_dlopen, vp, "dlopen", 10);
257 755
        return (subs);
258
}
259
260
/*--------------------------------------------------------------------*/
261
262
char *
263 906
mgt_VccCompile(struct cli *cli, struct vclprog *vcl, const char *vclname,
264
    const char *vclsrc, const char *vclsrcfile, int C_flag)
265
{
266
        struct vcc_priv vp;
267
        struct vsb *sb;
268
        unsigned status;
269
        char buf[1024];
270
        FILE *fcs;
271
        char **av;
272
        int ac;
273
274 906
        AN(cli);
275
276 906
        sb = VSB_new_auto();
277 906
        XXXAN(sb);
278
279 906
        INIT_OBJ(&vp, VCC_PRIV_MAGIC);
280 906
        vp.vclsrc = vclsrc;
281 906
        vp.vclsrcfile = vclsrcfile;
282
283
        /*
284
         * The subdirectory must have a unique name to 100% certain evade
285
         * the refcounting semantics of dlopen(3).
286
         *
287
         * Bad implementations of dlopen(3) think the shlib you are opening
288
         * is the same, if the filename is the same as one already opened.
289
         *
290
         * Sensible implementations do a stat(2) and requires st_ino and
291
         * st_dev to also match.
292
         *
293
         * A correct implementation would run on filesystems which tickle
294
         * st_gen, and also insist that be the identical, before declaring
295
         * a match.
296
         *
297
         * Since no correct implementations are known to exist, we are subject
298
         * to really interesting races if you do something like:
299
         *
300
         *      (running on 'boot' vcl)
301
         *      vcl.load foo /foo.vcl
302
         *      vcl.use foo
303
         *      few/slow requests
304
         *      vcl.use boot
305
         *      vcl.discard foo
306
         *      vcl.load foo /foo.vcl   // dlopen(3) says "same-same"
307
         *      vcl.use foo
308
         *
309
         * Because discard of the first 'foo' lingers on non-zero reference
310
         * count, and when it finally runs, it trashes the second 'foo' because
311
         * dlopen(3) decided they were really the same thing.
312
         *
313
         * The Best way to reproduce this is to have regexps in the VCL.
314
         */
315 906
        VSB_printf(sb, "vcl_%s.%.9f", vclname, VTIM_real());
316 906
        AZ(VSB_finish(sb));
317 906
        vp.dir = strdup(VSB_data(sb));
318 906
        AN(vp.dir);
319
320 906
        if (VJ_make_vcldir(vp.dir)) {
321 0
                free(vp.dir);
322 0
                VSB_destroy(&sb);
323 0
                VCLI_Out(cli, "VCL compilation failed");
324 0
                VCLI_SetResult(cli, CLIS_PARAM);
325 0
                return (NULL);
326
        }
327
328 906
        VSB_clear(sb);
329 906
        VSB_printf(sb, "%s/%s", vp.dir, VGC_SRC);
330 906
        AZ(VSB_finish(sb));
331 906
        vp.csrcfile = strdup(VSB_data(sb));
332 906
        AN(vp.csrcfile);
333 906
        VSB_clear(sb);
334
335 906
        VSB_printf(sb, "%s/%s", vp.dir, VGC_LIB);
336 906
        AZ(VSB_finish(sb));
337 906
        vp.libfile = strdup(VSB_data(sb));
338 906
        AN(vp.csrcfile);
339 906
        VSB_clear(sb);
340
341 906
        status = mgt_vcc_compile(&vp, sb, C_flag);
342
343 906
        AZ(VSB_finish(sb));
344 906
        if (VSB_len(sb) > 0)
345 169
                VCLI_Out(cli, "%s", VSB_data(sb));
346 906
        VSB_destroy(&sb);
347
348 906
        if (status || C_flag) {
349 153
                (void)unlink(vp.csrcfile);
350 153
                free(vp.csrcfile);
351 153
                (void)unlink(vp.libfile);
352 153
                free(vp.libfile);
353 153
                (void)rmdir(vp.dir);
354 153
                free(vp.dir);
355 153
                if (status) {
356 151
                        VCLI_Out(cli, "VCL compilation failed");
357 151
                        VCLI_SetResult(cli, CLIS_PARAM);
358
                }
359 153
                return (NULL);
360
        }
361
362 753
        fcs = fopen(vp.csrcfile, "r");
363 753
        AN(fcs);
364
        while (1) {
365 997
                AN(fgets(buf, sizeof buf, fcs));
366 997
                if (memcmp(buf, VCC_INFO_PREFIX, strlen(VCC_INFO_PREFIX)))
367 753
                        break;
368 244
                av = VAV_Parse(buf, &ac, 0);
369 244
                AN(av);
370 244
                AZ(av[0]);
371 244
                AZ(strcmp(av[1], "/*"));
372 244
                AZ(strcmp(av[ac-1], "*/"));
373 244
                if (!strcmp(av[3], "VCL"))
374 16
                        mgt_vcl_depends(vcl, av[4]);
375 228
                else if (!strcmp(av[3], "VMOD"))
376 228
                        mgt_vcl_vmod(vcl, av[4], av[5]);
377
                else
378 0
                        WRONG("Wrong VCCINFO");
379 244
                VAV_Free(av);
380 244
        }
381 753
        AZ(fclose(fcs));
382
383 753
        (void)unlink(vp.csrcfile);
384 753
        free(vp.csrcfile);
385
386 753
        free(vp.dir);
387
388 753
        VCLI_Out(cli, "VCL compiled.\n");
389
390 753
        return (vp.libfile);
391
}