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 <fcntl.h>
35
#include <stdio.h>
36
#include <stdlib.h>
37
#include <string.h>
38
#include <unistd.h>
39
#include <sys/stat.h>
40
41
#include "mgt/mgt.h"
42
#include "mgt/mgt_vcl.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 "vtim.h"
51
52
struct vcc_priv {
53
        unsigned        magic;
54
#define VCC_PRIV_MAGIC  0x70080cb8
55
        const char      *vclsrc;
56
        const char      *vclsrcfile;
57
        struct vsb      *dir;
58
        struct vsb      *csrcfile;
59
        struct vsb      *libfile;
60
        struct vsb      *symfile;
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
#define VGC_SYM         "vgc.sym"
74
75
/*--------------------------------------------------------------------*/
76
77
void
78 1
mgt_DumpBuiltin(void)
79
{
80 1
        printf("%s\n", builtin_vcl);
81 1
}
82
83
/*--------------------------------------------------------------------
84
 * Invoke system VCC compiler in a sub-process
85
 */
86
87
static void v_matchproto_(vsub_func_f)
88 1159
run_vcc(void *priv)
89
{
90 1159
        struct vsb *sb = NULL;
91
        struct vclprog *vpg;
92
        struct vcc_priv *vp;
93
        struct vcc *vcc;
94
        struct stevedore *stv;
95
        int i;
96
97 1159
        VJ_subproc(JAIL_SUBPROC_VCC);
98 1159
        CAST_OBJ_NOTNULL(vp, priv, VCC_PRIV_MAGIC);
99
100 1159
        AZ(chdir(VSB_data(vp->dir)));
101
102 1159
        vcc = VCC_New();
103 1159
        AN(vcc);
104 1159
        VCC_Builtin_VCL(vcc, builtin_vcl);
105 1159
        VCC_VCL_path(vcc, mgt_vcl_path);
106 1159
        VCC_VMOD_path(vcc, mgt_vmod_path);
107 1159
        VCC_Err_Unref(vcc, mgt_vcc_err_unref);
108 1159
        VCC_Allow_InlineC(vcc, mgt_vcc_allow_inline_c);
109 1159
        VCC_Unsafe_Path(vcc, mgt_vcc_unsafe_path);
110 3485
        STV_Foreach(stv)
111 2326
                VCC_Predef(vcc, "VCL_STEVEDORE", stv->ident);
112 3104
        VTAILQ_FOREACH(vpg, &vclhead, list)
113 1985
                if (mcf_is_label(vpg))
114 40
                        VCC_Predef(vcc, "VCL_VCL", vpg->name);
115 1159
        i = VCC_Compile(vcc, &sb, vp->vclsrc, vp->vclsrcfile,
116
            VGC_SRC, VGC_SYM);
117 1159
        if (VSB_len(sb))
118 209
                printf("%s", VSB_data(sb));
119 1159
        VSB_destroy(&sb);
120 1159
        exit(i == 0 ? 0 : 2);
121 0
}
122
123
/*--------------------------------------------------------------------
124
 * Invoke system C compiler in a sub-process
125
 */
126
127
static void v_matchproto_(vsub_func_f)
128 963
run_cc(void *priv)
129
{
130
        struct vcc_priv *vp;
131
        struct vsb *sb;
132
        int pct;
133
        char *p;
134
135 963
        VJ_subproc(JAIL_SUBPROC_CC);
136 963
        CAST_OBJ_NOTNULL(vp, priv, VCC_PRIV_MAGIC);
137
138 963
        AZ(chdir(VSB_data(vp->dir)));
139
140 963
        sb = VSB_new_auto();
141 963
        AN(sb);
142 421794
        for (p = mgt_cc_cmd, pct = 0; *p; ++p) {
143 420831
                if (pct) {
144 1926
                        switch (*p) {
145
                        case 's':
146 963
                                VSB_cat(sb, VGC_SRC);
147 963
                                break;
148
                        case 'o':
149 963
                                VSB_cat(sb, VGC_LIB);
150 963
                                break;
151
                        case '%':
152 0
                                VSB_putc(sb, '%');
153 0
                                break;
154
                        default:
155 0
                                VSB_putc(sb, '%');
156 0
                                VSB_putc(sb, *p);
157 0
                                break;
158
                        }
159 1926
                        pct = 0;
160 420831
                } else if (*p == '%') {
161 1926
                        pct = 1;
162 1926
                } else {
163 416979
                        VSB_putc(sb, *p);
164
                }
165 420831
        }
166 963
        if (pct)
167 0
                VSB_putc(sb, '%');
168 963
        AZ(VSB_finish(sb));
169
170 963
        (void)umask(027);
171 963
        (void)execl("/bin/sh", "/bin/sh", "-c", VSB_data(sb), (char*)0);
172 0
        VSB_destroy(&sb);                               // For flexelint
173 0
}
174
175
/*--------------------------------------------------------------------
176
 * Attempt to open compiled VCL in a sub-process
177
 */
178
179
static void v_matchproto_(vsub_func_f)
180 949
run_dlopen(void *priv)
181
{
182
        struct vcc_priv *vp;
183
184 949
        VJ_subproc(JAIL_SUBPROC_VCLLOAD);
185 949
        CAST_OBJ_NOTNULL(vp, priv, VCC_PRIV_MAGIC);
186 949
        if (VCL_TestLoad(VSB_data(vp->libfile)))
187 1
                exit(1);
188 948
        exit(0);
189 0
}
190
191
/*--------------------------------------------------------------------
192
 * Touch a filename and make it available to privsep-privs
193
 */
194
195
static int
196 2318
mgt_vcc_touchfile(const char *fn, struct vsb *sb)
197
{
198
        int i;
199
200 2318
        i = open(fn, O_WRONLY|O_CREAT|O_TRUNC, 0640);
201 2318
        if (i < 0) {
202 0
                VSB_printf(sb, "Failed to create %s: %s", fn, vstrerror(errno));
203 0
                return (2);
204
        }
205 2318
        if (fchown(i, mgt_param.uid, mgt_param.gid) != 0)
206 2318
                if (geteuid() == 0)
207 0
                        VSB_printf(sb, "Failed to change owner on %s: %s\n",
208 0
                            fn, vstrerror(errno));
209 2318
        closefd(&i);
210 2318
        return (0);
211 2318
}
212
213
/*--------------------------------------------------------------------
214
 * Compile a VCL program, return shared object, errors in sb.
215
 */
216
217
static unsigned
218 1159
mgt_vcc_compile(struct vcc_priv *vp, struct vsb *sb, int C_flag)
219
{
220
        char *csrc;
221
        unsigned subs;
222
223 1159
        AN(sb);
224 1159
        VSB_clear(sb);
225 1159
        if (mgt_vcc_touchfile(VSB_data(vp->csrcfile), sb))
226 0
                return (2);
227 1159
        if (mgt_vcc_touchfile(VSB_data(vp->libfile), sb))
228 0
                return (2);
229
230 1159
        subs = VSUB_run(sb, run_vcc, vp, "VCC-compiler", -1);
231 1159
        if (subs)
232 196
                return (subs);
233
234 963
        if (C_flag) {
235 1
                csrc = VFIL_readfile(NULL, VSB_data(vp->csrcfile), NULL);
236 1
                AN(csrc);
237 1
                VSB_cat(sb, csrc);
238 1
                free(csrc);
239
240 1
                VSB_cat(sb, "/* EXTERNAL SYMBOL TABLE\n");
241 1
                csrc = VFIL_readfile(NULL, VSB_data(vp->symfile), NULL);
242 1
                AN(csrc);
243 1
                VSB_cat(sb, csrc);
244 1
                VSB_cat(sb, "*/\n");
245 1
                free(csrc);
246 1
        }
247
248 963
        subs = VSUB_run(sb, run_cc, vp, "C-compiler", 10);
249 963
        if (subs)
250 0
                return (subs);
251
252 963
        subs = VSUB_run(sb, run_dlopen, vp, "dlopen", 10);
253 963
        return (subs);
254 1159
}
255
256
/*--------------------------------------------------------------------*/
257
258
static void
259 1159
mgt_vcc_init_vp(struct vcc_priv *vp)
260
{
261 1159
        INIT_OBJ(vp, VCC_PRIV_MAGIC);
262 1159
        vp->csrcfile = VSB_new_auto();
263 1159
        AN(vp->csrcfile);
264 1159
        vp->libfile = VSB_new_auto();
265 1159
        AN(vp->libfile);
266 1159
        vp->symfile = VSB_new_auto();
267 1159
        AN(vp->symfile);
268 1159
        vp->dir = VSB_new_auto();
269 1159
        AN(vp->dir);
270 1159
}
271
272
static void
273 1159
mgt_vcc_fini_vp(struct vcc_priv *vp, int leave_lib)
274
{
275 1159
        if (!MGT_DO_DEBUG(DBG_VCL_KEEP)) {
276 1156
                (void)unlink(VSB_data(vp->csrcfile));
277 1156
                (void)unlink(VSB_data(vp->symfile));
278 1156
                if (!leave_lib)
279 197
                        (void)unlink(VSB_data(vp->libfile));
280 1156
        }
281 1159
        (void)rmdir(VSB_data(vp->dir));
282 1159
        VSB_destroy(&vp->csrcfile);
283 1159
        VSB_destroy(&vp->libfile);
284 1159
        VSB_destroy(&vp->symfile);
285 1159
        VSB_destroy(&vp->dir);
286 1159
}
287
288
char *
289 1159
mgt_VccCompile(struct cli *cli, struct vclprog *vcl, const char *vclname,
290
    const char *vclsrc, const char *vclsrcfile, int C_flag)
291
{
292
        struct vcc_priv vp[1];
293
        struct vsb *sb;
294
        unsigned status;
295
        char *p;
296
297 1159
        AN(cli);
298
299 1159
        sb = VSB_new_auto();
300 1159
        AN(sb);
301
302 1159
        mgt_vcc_init_vp(vp);
303 1159
        vp->vclsrc = vclsrc;
304 1159
        vp->vclsrcfile = vclsrcfile;
305
306
        /*
307
         * The subdirectory must have a unique name to 100% certain evade
308
         * the refcounting semantics of dlopen(3).
309
         *
310
         * Bad implementations of dlopen(3) think the shlib you are opening
311
         * is the same, if the filename is the same as one already opened.
312
         *
313
         * Sensible implementations do a stat(2) and requires st_ino and
314
         * st_dev to also match.
315
         *
316
         * A correct implementation would run on filesystems which tickle
317
         * st_gen, and also insist that be the identical, before declaring
318
         * a match.
319
         *
320
         * Since no correct implementations are known to exist, we are subject
321
         * to really interesting races if you do something like:
322
         *
323
         *      (running on 'boot' vcl)
324
         *      vcl.load foo /foo.vcl
325
         *      vcl.use foo
326
         *      few/slow requests
327
         *      vcl.use boot
328
         *      vcl.discard foo
329
         *      vcl.load foo /foo.vcl   // dlopen(3) says "same-same"
330
         *      vcl.use foo
331
         *
332
         * Because discard of the first 'foo' lingers on non-zero reference
333
         * count, and when it finally runs, it trashes the second 'foo' because
334
         * dlopen(3) decided they were really the same thing.
335
         *
336
         * The Best way to reproduce this is to have regexps in the VCL.
337
         */
338
339 1159
        VSB_printf(vp->dir, "vcl_%s.%.6f", vclname, VTIM_real());
340 1159
        AZ(VSB_finish(vp->dir));
341
342 1159
        VSB_printf(vp->csrcfile, "%s/%s", VSB_data(vp->dir), VGC_SRC);
343 1159
        AZ(VSB_finish(vp->csrcfile));
344
345 1159
        VSB_printf(vp->libfile, "%s/%s", VSB_data(vp->dir), VGC_LIB);
346 1159
        AZ(VSB_finish(vp->libfile));
347
348 1159
        VSB_printf(vp->symfile, "%s/%s", VSB_data(vp->dir), VGC_SYM);
349 1159
        AZ(VSB_finish(vp->symfile));
350
351 1159
        if (VJ_make_subdir(VSB_data(vp->dir), "VCL", cli->sb)) {
352 0
                mgt_vcc_fini_vp(vp, 0);
353 0
                VSB_destroy(&sb);
354 0
                VCLI_Out(cli, "VCL compilation failed");
355 0
                VCLI_SetResult(cli, CLIS_PARAM);
356 0
                return (NULL);
357
        }
358
359 1159
        status = mgt_vcc_compile(vp, sb, C_flag);
360 1159
        AZ(VSB_finish(sb));
361 1159
        if (VSB_len(sb) > 0)
362 225
                VCLI_Out(cli, "%s", VSB_data(sb));
363 1159
        VSB_destroy(&sb);
364
365 1159
        if (status || C_flag) {
366 198
                mgt_vcc_fini_vp(vp, 0);
367 198
                if (status) {
368 197
                        VCLI_Out(cli, "VCL compilation failed");
369 197
                        VCLI_SetResult(cli, CLIS_PARAM);
370 197
                }
371 198
                return (NULL);
372
        }
373
374 961
        p = VFIL_readfile(NULL, VSB_data(vp->symfile), NULL);
375 961
        AN(p);
376 961
        mgt_vcl_symtab(vcl, p);
377
378 961
        VCLI_Out(cli, "VCL compiled.\n");
379
380 961
        REPLACE(p, VSB_data(vp->libfile));
381 961
        mgt_vcc_fini_vp(vp, 1);
382 961
        return (p);
383 1159
}