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 * SPDX-License-Identifier: BSD-2-Clause
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 *
31 */
32
33 #include "config.h"
34
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sys/stat.h>
38
39 #include "vcc_compile.h"
40
41 const char *
vcc_default_probe(struct vcc * tl)42 vcc_default_probe(struct vcc *tl)
43 {
44
45 if (tl->default_probe != NULL)
46 return (tl->default_probe);
47 VSB_cat(tl->sb, "No default probe defined\n");
48 vcc_ErrToken(tl, tl->t);
49 VSB_cat(tl->sb, " at\n");
50 vcc_ErrWhere(tl, tl->t);
51 return ("");
52 }
53
54 /*--------------------------------------------------------------------
55 * Struct sockaddr is not really designed to be a compile time
56 * initialized data structure, so we encode it as a byte-string
57 * and put it in an official sockaddr when we load the VCL.
58 */
59
60 static void
Emit_Sockaddr(struct vcc * tl,struct vsb * vsb1,const struct token * t_host,const struct token * t_port)61 Emit_Sockaddr(struct vcc *tl, struct vsb *vsb1, const struct token *t_host,
62 const struct token *t_port)
63 {
64 const char *ipv4, *ipv4a, *ipv6, *ipv6a, *pa;
65 char buf[BUFSIZ];
66
67 AN(t_host->dec);
68
69 if (t_port != NULL)
70 bprintf(buf, "%s %s", t_host->dec, t_port->dec);
71 else
72 bprintf(buf, "%s", t_host->dec);
73 Resolve_Sockaddr(tl, buf, "80",
74 &ipv4, &ipv4a, &ipv6, &ipv6a, &pa, 2, t_host, "Backend host");
75 ERRCHK(tl);
76 if (ipv4 != NULL) {
77 VSB_printf(vsb1,
78 "\t.ipv4 = (const struct suckaddr *)%s,\n",
79 ipv4);
80 }
81 if (ipv6 != NULL) {
82 VSB_printf(vsb1,
83 "\t.ipv6 = (const struct suckaddr *)%s,\n",
84 ipv6);
85 }
86 VSB_cat(vsb1, "\t.uds_path = (void *) 0,\n");
87 }
88
89 /*
90 * For UDS, we do not create a VSA. Check if it's an absolute path, can
91 * be accessed, and is a socket. If so, just emit the path field and set
92 * the IP suckaddrs to NULL.
93 */
94 static void
Emit_UDS_Path(struct vcc * tl,struct vsb * vsb1,const struct token * t_path,const char * errid)95 Emit_UDS_Path(struct vcc *tl, struct vsb *vsb1,
96 const struct token *t_path, const char *errid)
97 {
98 struct stat st;
99
100 AN(t_path);
101 AN(t_path->dec);
102
103 if (*t_path->dec != '/') {
104 VSB_printf(tl->sb,
105 "%s: Must be an absolute path:\n", errid);
106 vcc_ErrWhere(tl, t_path);
107 return;
108 }
109 errno = 0;
110 if (stat(t_path->dec, &st) != 0) {
111 int err = errno;
112 VSB_printf(tl->sb, "%s: Cannot stat: %s\n", errid,
113 strerror(errno));
114 vcc_ErrWhere(tl, t_path);
115 if (err == ENOENT || err == EACCES)
116 vcc_Warn(tl);
117 else
118 return;
119 } else if (!S_ISSOCK(st.st_mode)) {
120 VSB_printf(tl->sb, "%s: Not a socket:\n", errid);
121 vcc_ErrWhere(tl, t_path);
122 return;
123 }
124 VSB_printf(vsb1, "\t.uds_path = \"%s\",\n", t_path->dec);
125 VSB_cat(vsb1, "\t.ipv4 = (void *) 0,\n");
126 VSB_cat(vsb1, "\t.ipv6 = (void *) 0,\n");
127 }
128
129 /*--------------------------------------------------------------------
130 * Disallow mutually exclusive field definitions
131 */
132
133 static void
vcc_Redef(struct vcc * tl,const char * redef,const struct token ** t_did,const struct token * t_field)134 vcc_Redef(struct vcc *tl, const char *redef, const struct token **t_did,
135 const struct token *t_field)
136 {
137 if (*t_did != NULL) {
138 VSB_printf(tl->sb, "%s redefinition at:\n", redef);
139 vcc_ErrWhere(tl, t_field);
140 VSB_cat(tl->sb, "Previous definition:\n");
141 vcc_ErrWhere(tl, *t_did);
142 return;
143 }
144 *t_did = t_field;
145 }
146
147 /*--------------------------------------------------------------------
148 * Parse a backend probe specification
149 */
150
151 static void
vcc_ParseProbeSpec(struct vcc * tl,const struct symbol * sym,char ** namep)152 vcc_ParseProbeSpec(struct vcc *tl, const struct symbol *sym, char **namep)
153 {
154 struct fld_spec *fs;
155 const struct token *t_field;
156 const struct token *t_did = NULL, *t_window = NULL, *t_threshold = NULL;
157 struct token *t_initial = NULL;
158 unsigned window, threshold, initial, status;
159 char buf[32];
160 const char *name;
161 double t;
162
163 fs = vcc_FldSpec(tl,
164 "?url",
165 "?request",
166 "?expected_response",
167 "?timeout",
168 "?interval",
169 "?window",
170 "?threshold",
171 "?initial",
172 NULL);
173
174 SkipToken(tl, '{');
175
176 if (sym != NULL) {
177 name = sym->rname;
178 } else {
179 bprintf(buf, "vgc_probe__%d", tl->nprobe++);
180 name = buf;
181 }
182 Fh(tl, 0, "static const struct vrt_backend_probe %s[] = {{\n", name);
183 Fh(tl, 0, "\t.magic = VRT_BACKEND_PROBE_MAGIC,\n");
184 if (namep != NULL)
185 *namep = TlDup(tl, name);
186
187 window = 0;
188 threshold = 0;
189 initial = 0;
190 status = 0;
191 while (tl->t->tok != '}') {
192
193 vcc_IsField(tl, &t_field, fs);
194 ERRCHK(tl);
195 if (vcc_IdIs(t_field, "url")) {
196 vcc_Redef(tl, "Probe request", &t_did, t_field);
197 ERRCHK(tl);
198 ExpectErr(tl, CSTR);
199 Fh(tl, 0, "\t.url = ");
200 EncToken(tl->fh, tl->t);
201 Fh(tl, 0, ",\n");
202 vcc_NextToken(tl);
203 } else if (vcc_IdIs(t_field, "request")) {
204 vcc_Redef(tl, "Probe request", &t_did, t_field);
205 ERRCHK(tl);
206 ExpectErr(tl, CSTR);
207 Fh(tl, 0, "\t.request =\n");
208 while (tl->t->tok == CSTR) {
209 Fh(tl, 0, "\t\t");
210 EncToken(tl->fh, tl->t);
211 Fh(tl, 0, " \"\\r\\n\"\n");
212 vcc_NextToken(tl);
213 }
214 Fh(tl, 0, "\t\t\"\\r\\n\",\n");
215 } else if (vcc_IdIs(t_field, "timeout")) {
216 Fh(tl, 0, "\t.timeout = ");
217 vcc_Duration(tl, &t);
218 ERRCHK(tl);
219 Fh(tl, 0, "%g,\n", t);
220 } else if (vcc_IdIs(t_field, "interval")) {
221 Fh(tl, 0, "\t.interval = ");
222 vcc_Duration(tl, &t);
223 ERRCHK(tl);
224 Fh(tl, 0, "%g,\n", t);
225 } else if (vcc_IdIs(t_field, "window")) {
226 t_window = tl->t;
227 window = vcc_UintVal(tl);
228 ERRCHK(tl);
229 } else if (vcc_IdIs(t_field, "initial")) {
230 t_initial = tl->t;
231 initial = vcc_UintVal(tl);
232 ERRCHK(tl);
233 } else if (vcc_IdIs(t_field, "expected_response")) {
234 status = vcc_UintVal(tl);
235 if (status < 100 || status > 999) {
236 VSB_cat(tl->sb,
237 "Must specify .expected_response with "
238 "exactly three digits "
239 "(100 <= x <= 999)\n");
240 vcc_ErrWhere(tl, tl->t);
241 return;
242 }
243 ERRCHK(tl);
244 } else if (vcc_IdIs(t_field, "threshold")) {
245 t_threshold = tl->t;
246 threshold = vcc_UintVal(tl);
247 ERRCHK(tl);
248 } else {
249 vcc_ErrToken(tl, t_field);
250 vcc_ErrWhere(tl, t_field);
251 ErrInternal(tl);
252 return;
253 }
254
255 SkipToken(tl, ';');
256 }
257 free(fs);
258
259 if (t_threshold != NULL || t_window != NULL) {
260 if (t_threshold == NULL && t_window != NULL) {
261 VSB_cat(tl->sb,
262 "Must specify .threshold with .window\n");
263 vcc_ErrWhere(tl, t_window);
264 return;
265 } else if (t_threshold != NULL && t_window == NULL) {
266 if (threshold > 64) {
267 VSB_cat(tl->sb,
268 "Threshold must be 64 or less.\n");
269 vcc_ErrWhere(tl, t_threshold);
270 return;
271 }
272 window = threshold + 1;
273 } else if (window > 64) {
274 AN(t_window);
275 VSB_cat(tl->sb, "Window must be 64 or less.\n");
276 vcc_ErrWhere(tl, t_window);
277 return;
278 }
279 if (threshold > window ) {
280 VSB_cat(tl->sb,
281 "Threshold can not be greater than window.\n");
282 AN(t_threshold);
283 vcc_ErrWhere(tl, t_threshold);
284 AN(t_window);
285 vcc_ErrWhere(tl, t_window);
286 }
287 Fh(tl, 0, "\t.window = %u,\n", window);
288 Fh(tl, 0, "\t.threshold = %u,\n", threshold);
289 }
290 if (t_initial != NULL)
291 Fh(tl, 0, "\t.initial = %u,\n", initial);
292 else
293 Fh(tl, 0, "\t.initial = ~0U,\n");
294 if (status > 0)
295 Fh(tl, 0, "\t.exp_status = %u,\n", status);
296 Fh(tl, 0, "}};\n");
297 SkipToken(tl, '}');
298 }
299
300 /*--------------------------------------------------------------------
301 * Parse and emit a probe definition
302 */
303
304 void
vcc_ParseProbe(struct vcc * tl)305 vcc_ParseProbe(struct vcc *tl)
306 {
307 struct symbol *sym;
308 char *p;
309
310 vcc_NextToken(tl); /* ID: probe */
311
312 vcc_ExpectVid(tl, "backend probe"); /* ID: name */
313 ERRCHK(tl);
314 if (vcc_IdIs(tl->t, "default")) {
315 vcc_NextToken(tl);
316 vcc_ParseProbeSpec(tl, NULL, &p);
317 tl->default_probe = p;
318 } else {
319 sym = VCC_HandleSymbol(tl, PROBE, "vgc_probe");
320 ERRCHK(tl);
321 AN(sym);
322 vcc_ParseProbeSpec(tl, sym, NULL);
323 }
324 }
325
326 /*--------------------------------------------------------------------
327 * Parse and emit a backend host definition
328 *
329 * The struct vrt_backend is emitted to Fh().
330 */
331
332 static void
vcc_ParseHostDef(struct vcc * tl,const struct token * t_be,const char * vgcname)333 vcc_ParseHostDef(struct vcc *tl, const struct token *t_be, const char *vgcname)
334 {
335 const struct token *t_field;
336 const struct token *t_val;
337 const struct token *t_host = NULL;
338 const struct token *t_port = NULL;
339 const struct token *t_path = NULL;
340 const struct token *t_hosthdr = NULL;
341 const struct token *t_did = NULL;
342 const struct token *t_preamble = NULL;
343 struct symbol *pb;
344 struct fld_spec *fs;
345 struct inifin *ifp;
346 struct vsb *vsb1;
347 char *p;
348 unsigned u;
349 double t;
350
351 if (tl->t->tok == ID &&
352 (vcc_IdIs(tl->t, "none") || vcc_IdIs(tl->t, "None"))) {
353 vcc_NextToken(tl);
354 SkipToken(tl, ';');
355 ifp = New_IniFin(tl);
356 VSB_printf(ifp->ini, "\t(void)%s;\n", vgcname);
357 VSB_printf(ifp->fin, "\t\t(void)%s;\n", vgcname);
358 return;
359 }
360
361 SkipToken(tl, '{');
362
363 /* Check for old syntax */
364 if (tl->t->tok == ID && vcc_IdIs(tl->t, "set")) {
365 VSB_cat(tl->sb,
366 "NB: Backend Syntax has changed:\n"
367 "Remove \"set\" and \"backend\" in front"
368 " of backend fields.\n" );
369 vcc_ErrToken(tl, tl->t);
370 VSB_cat(tl->sb, " at ");
371 vcc_ErrWhere(tl, tl->t);
372 return;
373 }
374
375 fs = vcc_FldSpec(tl,
376 "?host",
377 "?port",
378 "?path",
379 "?host_header",
380 "?connect_timeout",
381 "?first_byte_timeout",
382 "?between_bytes_timeout",
383 "?probe",
384 "?max_connections",
385 "?proxy_header",
386 "?preamble",
387 NULL);
388
389 tl->fb = VSB_new_auto();
390 AN(tl->fb);
391
392 Fb(tl, 0, "\nstatic const struct vrt_backend vgc_dir_priv_%s = {\n",
393 vgcname);
394
395 Fb(tl, 0, "\t.magic = VRT_BACKEND_MAGIC,\n");
396 Fb(tl, 0, "\t.endpoint = &vgc_dir_ep_%s,\n", vgcname);
397 Fb(tl, 0, "\t.vcl_name = \"%.*s", PF(t_be));
398 Fb(tl, 0, "\",\n");
399
400 while (tl->t->tok != '}') {
401
402 vcc_IsField(tl, &t_field, fs);
403 ERRCHK(tl);
404 if (vcc_IdIs(t_field, "host")) {
405 vcc_Redef(tl, "Address", &t_did, t_field);
406 ERRCHK(tl);
407 ExpectErr(tl, CSTR);
408 assert(tl->t->dec != NULL);
409 t_host = tl->t;
410 vcc_NextToken(tl);
411 SkipToken(tl, ';');
412 } else if (vcc_IdIs(t_field, "port")) {
413 ExpectErr(tl, CSTR);
414 assert(tl->t->dec != NULL);
415 t_port = tl->t;
416 vcc_NextToken(tl);
417 SkipToken(tl, ';');
418 } else if (vcc_IdIs(t_field, "path")) {
419 if (tl->syntax < VCL_41) {
420 VSB_cat(tl->sb,
421 "Unix socket backends only supported"
422 " in VCL4.1 and higher.\n");
423 vcc_ErrToken(tl, tl->t);
424 VSB_cat(tl->sb, " at ");
425 vcc_ErrWhere(tl, tl->t);
426 VSB_destroy(&tl->fb);
427 return;
428 }
429 vcc_Redef(tl, "Address", &t_did, t_field);
430 ERRCHK(tl);
431 ExpectErr(tl, CSTR);
432 assert(tl->t->dec != NULL);
433 t_path = tl->t;
434 vcc_NextToken(tl);
435 SkipToken(tl, ';');
436 } else if (vcc_IdIs(t_field, "host_header")) {
437 ExpectErr(tl, CSTR);
438 assert(tl->t->dec != NULL);
439 t_hosthdr = tl->t;
440 vcc_NextToken(tl);
441 SkipToken(tl, ';');
442 } else if (vcc_IdIs(t_field, "connect_timeout")) {
443 Fb(tl, 0, "\t.connect_timeout = ");
444 vcc_Duration(tl, &t);
445 ERRCHK(tl);
446 Fb(tl, 0, "%g,\n", t);
447 SkipToken(tl, ';');
448 } else if (vcc_IdIs(t_field, "first_byte_timeout")) {
449 Fb(tl, 0, "\t.first_byte_timeout = ");
450 vcc_Duration(tl, &t);
451 ERRCHK(tl);
452 Fb(tl, 0, "%g,\n", t);
453 SkipToken(tl, ';');
454 } else if (vcc_IdIs(t_field, "between_bytes_timeout")) {
455 Fb(tl, 0, "\t.between_bytes_timeout = ");
456 vcc_Duration(tl, &t);
457 ERRCHK(tl);
458 Fb(tl, 0, "%g,\n", t);
459 SkipToken(tl, ';');
460 } else if (vcc_IdIs(t_field, "max_connections")) {
461 u = vcc_UintVal(tl);
462 ERRCHK(tl);
463 SkipToken(tl, ';');
464 Fb(tl, 0, "\t.max_connections = %u,\n", u);
465 } else if (vcc_IdIs(t_field, "proxy_header")) {
466 t_val = tl->t;
467 u = vcc_UintVal(tl);
468 ERRCHK(tl);
469 if (u != 1 && u != 2) {
470 VSB_cat(tl->sb,
471 ".proxy_header must be 1 or 2\n");
472 vcc_ErrWhere(tl, t_val);
473 VSB_destroy(&tl->fb);
474 return;
475 }
476 SkipToken(tl, ';');
477 Fb(tl, 0, "\t.proxy_header = %u,\n", u);
478 } else if (vcc_IdIs(t_field, "probe") && tl->t->tok == '{') {
479 vcc_ParseProbeSpec(tl, NULL, &p);
480 Fb(tl, 0, "\t.probe = %s,\n", p);
481 free(p);
482 ERRCHK(tl);
483 } else if (vcc_IdIs(t_field, "probe") && tl->t->tok == ID) {
484 if (vcc_IdIs(tl->t, "default")) {
485 vcc_NextToken(tl);
486 (void)vcc_default_probe(tl);
487 } else {
488 pb = VCC_SymbolGet(tl, SYM_MAIN, SYM_PROBE,
489 SYMTAB_EXISTING, XREF_REF);
490 ERRCHK(tl);
491 AN(pb);
492 Fb(tl, 0, "\t.probe = %s,\n", pb->rname);
493 }
494 SkipToken(tl, ';');
495 } else if (vcc_IdIs(t_field, "probe")) {
496 VSB_cat(tl->sb, "Expected '{' or name of probe, got ");
497 vcc_ErrToken(tl, tl->t);
498 VSB_cat(tl->sb, " at\n");
499 vcc_ErrWhere(tl, tl->t);
500 VSB_destroy(&tl->fb);
501 return;
502 } else if (vcc_IdIs(t_field, "preamble")) {
503 ExpectErr(tl, CBLOB);
504 t_preamble = tl->t;
505 vcc_NextToken(tl);
506 SkipToken(tl, ';');
507 } else {
508 ErrInternal(tl);
509 VSB_destroy(&tl->fb);
510 return;
511 }
512
513 }
514
515 vcc_FieldsOk(tl, fs);
516 free(fs);
517 ERRCHK(tl);
518
519 ExpectErr(tl, '}');
520
521 if (t_host == NULL && t_path == NULL) {
522 VSB_cat(tl->sb, "Expected .host or .path.\n");
523 vcc_ErrWhere(tl, t_be);
524 VSB_destroy(&tl->fb);
525 return;
526 }
527
528 vsb1 = VSB_new_auto();
529 AN(vsb1);
530 VSB_printf(vsb1,
531 "\nstatic const struct vrt_endpoint vgc_dir_ep_%s = {\n",
532 vgcname);
533 VSB_cat(vsb1, "\t.magic = VRT_ENDPOINT_MAGIC,\n");
534
535 assert(t_host != NULL || t_path != NULL);
536 if (t_host != NULL)
537 /* Check that the hostname makes sense */
538 Emit_Sockaddr(tl, vsb1, t_host, t_port);
539 else
540 /* Check that the path can be a legal UDS */
541 Emit_UDS_Path(tl, vsb1, t_path, "Backend path");
542 ERRCHK(tl);
543
544 if (t_preamble != NULL)
545 VSB_printf(vsb1, "\t.preamble = %s,\n", t_preamble->dec);
546
547 VSB_cat(vsb1, "};\n");
548 AZ(VSB_finish(vsb1));
549 Fh(tl, 0, "%s", VSB_data(vsb1));
550 VSB_destroy(&vsb1);
551
552 /* Emit the hosthdr field, fall back to .host if not specified */
553 /* If .path is specified, set "0.0.0.0". */
554 Fb(tl, 0, "\t.hosthdr = ");
555 if (t_hosthdr != NULL)
556 EncToken(tl->fb, t_hosthdr);
557 else if (t_host != NULL)
558 EncToken(tl->fb, t_host);
559 else
560 Fb(tl, 0, "\"0.0.0.0\"");
561 Fb(tl, 0, ",\n");
562
563 /* Close the struct */
564 Fb(tl, 0, "};\n");
565
566 vcc_NextToken(tl);
567
568 AZ(VSB_finish(tl->fb));
569 Fh(tl, 0, "%s", VSB_data(tl->fb));
570 VSB_destroy(&tl->fb);
571
572 ifp = New_IniFin(tl);
573 VSB_printf(ifp->ini,
574 "\t%s =\n\t VRT_new_backend_clustered(ctx, vsc_cluster,\n"
575 "\t\t&vgc_dir_priv_%s);",
576 vgcname, vgcname);
577 VSB_printf(ifp->fin, "\t\tVRT_delete_backend(ctx, &%s);", vgcname);
578 }
579
580 /*--------------------------------------------------------------------
581 * Parse directors and backends
582 */
583
584 void
vcc_ParseBackend(struct vcc * tl)585 vcc_ParseBackend(struct vcc *tl)
586 {
587 struct token *t_first, *t_be;
588 struct symbol *sym;
589 const char *dn;
590
591 tl->ndirector++;
592 t_first = tl->t;
593 SkipToken(tl, ID); /* ID: backend */
594
595 vcc_ExpectVid(tl, "backend"); /* ID: name */
596 ERRCHK(tl);
597
598 t_be = tl->t;
599 if (vcc_IdIs(tl->t, "default")) {
600 if (tl->first_director != NULL) {
601 tl->first_director->noref = 0;
602 tl->first_director = NULL;
603 tl->default_director = NULL;
604 }
605 if (tl->default_director != NULL) {
606 VSB_cat(tl->sb,
607 "Only one default director possible.\n");
608 vcc_ErrWhere(tl, t_first);
609 return;
610 }
611 vcc_NextToken(tl);
612 dn = "vgc_backend_default";
613 tl->default_director = dn;
614 } else {
615 sym = VCC_HandleSymbol(tl, BACKEND, "vgc_backend");
616 ERRCHK(tl);
617 AN(sym);
618 dn = sym->rname;
619 if (tl->default_director == NULL) {
620 tl->first_director = sym;
621 tl->default_director = dn;
622 sym->noref = 1;
623 }
624 }
625 Fh(tl, 0, "\nstatic VCL_BACKEND %s;\n", dn);
626 vcc_ParseHostDef(tl, t_be, dn);
627 if (tl->err) {
628 VSB_printf(tl->sb,
629 "\nIn %.*s specification starting at:\n", PF(t_first));
630 vcc_ErrWhere(tl, t_first);
631 return;
632 }
633 }
634
635 void
vcc_Backend_Init(struct vcc * tl)636 vcc_Backend_Init(struct vcc *tl)
637 {
638 struct inifin *ifp;
639
640 Fh(tl, 0, "\nstatic struct vsmw_cluster *vsc_cluster;\n");
641 ifp = New_IniFin(tl);
642 VSB_cat(ifp->ini, "\tvsc_cluster = VRT_VSM_Cluster_New(ctx,\n"
643 "\t ndirector * VRT_backend_vsm_need(ctx));\n");
644 VSB_cat(ifp->ini, "\tif (vsc_cluster == 0)\n\t\treturn(1);");
645 VSB_cat(ifp->fin, "\t\tVRT_VSM_Cluster_Destroy(ctx, &vsc_cluster);");
646 }
647