3 * Bacula Director -- expand.c -- does variable expansion
4 * in particular for the LabelFormat specification.
6 * Kern Sibbald, June MMIII
11 Bacula® - The Network Backup Solution
13 Copyright (C) 2003-2006 Free Software Foundation Europe e.V.
15 The main author of Bacula is Kern Sibbald, with contributions from
16 many others, a complete list can be found in the file AUTHORS.
17 This program is Free Software; you can redistribute it and/or
18 modify it under the terms of version two of the GNU General Public
19 License as published by the Free Software Foundation and included
22 This program is distributed in the hope that it will be useful, but
23 WITHOUT ANY WARRANTY; without even the implied warranty of
24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
25 General Public License for more details.
27 You should have received a copy of the GNU General Public License
28 along with this program; if not, write to the Free Software
29 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
32 Bacula® is a registered trademark of Kern Sibbald.
33 The licensor of Bacula is the Free Software Foundation Europe
34 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
35 Switzerland, email:ftf@fsfeurope.org.
43 static int date_item(JCR *jcr, int code,
44 const char **val_ptr, int *val_len, int *val_size)
47 time_t now = time(NULL);
48 (void)localtime_r(&now, &tm);
54 val = tm.tm_year + 1900;
71 case 7: /* Week day */
75 bsnprintf(buf, sizeof(buf), "%d", val);
76 *val_ptr = bstrdup(buf);
77 *val_len = strlen(buf);
78 *val_size = *val_len + 1;
82 static int job_item(JCR *jcr, int code,
83 const char **val_ptr, int *val_len, int *val_size)
85 const char *str = " ";
90 str = jcr->job->name();
92 case 2: /* Director's name */
96 str = job_level_to_str(jcr->JobLevel);
99 str = job_type_to_str(jcr->JobType);
102 bsnprintf(buf, sizeof(buf), "%d", jcr->JobId);
106 str = jcr->client->name();
111 case 7: /* NumVols */
112 bsnprintf(buf, sizeof(buf), "%d", jcr->NumVols);
116 str = jcr->pool->name();
118 case 9: /* Storage */
120 str = jcr->wstore->name();
122 str = jcr->rstore->name();
125 case 10: /* Catalog */
126 str = jcr->catalog->name();
128 case 11: /* MediaType */
130 str = jcr->wstore->media_type;
132 str = jcr->rstore->media_type;
135 case 12: /* JobName */
139 *val_ptr = bstrdup(str);
140 *val_len = strlen(str);
141 *val_size = *val_len + 1;
145 struct s_built_in_vars {const char *var_name; int code; int (*func)(JCR *jcr, int code,
146 const char **val_ptr, int *val_len, int *val_size);};
149 * Table of build in variables
151 static struct s_built_in_vars built_in_vars[] = {
152 { NT_("Year"), 1, date_item},
153 { NT_("Month"), 2, date_item},
154 { NT_("Day"), 3, date_item},
155 { NT_("Hour"), 4, date_item},
156 { NT_("Minute"), 5, date_item},
157 { NT_("Second"), 6, date_item},
158 { NT_("WeekDay"), 7, date_item},
160 { NT_("Job"), 1, job_item},
161 { NT_("Dir"), 2, job_item},
162 { NT_("Level"), 3, job_item},
163 { NT_("Type"), 4, job_item},
164 { NT_("JobId"), 5, job_item},
165 { NT_("Client"), 6, job_item},
166 { NT_("NumVols"), 7, job_item},
167 { NT_("Pool"), 8, job_item},
168 { NT_("Storage"), 9, job_item},
169 { NT_("Catalog"), 10, job_item},
170 { NT_("MediaType"), 11, job_item},
171 { NT_("JobName"), 12, job_item},
178 * Search the table of built-in variables, and if found,
179 * call the appropriate subroutine to do the work.
181 static var_rc_t lookup_built_in_var(var_t *ctx, void *my_ctx,
182 const char *var_ptr, int var_len, int var_index,
183 const char **val_ptr, int *val_len, int *val_size)
185 JCR *jcr = (JCR *)my_ctx;
188 for (int i=0; _(built_in_vars[i].var_name); i++) {
189 if (strncmp(_(built_in_vars[i].var_name), var_ptr, var_len) == 0) {
190 stat = (*built_in_vars[i].func)(jcr, built_in_vars[i].code,
191 val_ptr, val_len, val_size);
198 return VAR_ERR_UNDEFINED_VARIABLE;
203 * Search counter variables
205 static var_rc_t lookup_counter_var(var_t *ctx, void *my_ctx,
206 const char *var_ptr, int var_len, int var_inc, int var_index,
207 const char **val_ptr, int *val_len, int *val_size)
210 var_rc_t stat = VAR_ERR_UNDEFINED_VARIABLE;
212 if (var_len > (int)sizeof(buf) - 1) {
213 return VAR_ERR_OUT_OF_MEMORY;
215 memcpy(buf, var_ptr, var_len);
218 for (COUNTER *counter=NULL; (counter = (COUNTER *)GetNextRes(R_COUNTER, (RES *)counter)); ) {
219 if (strcmp(counter->name(), buf) == 0) {
220 Dmsg2(100, "Counter=%s val=%d\n", buf, counter->CurrentValue);
221 /* -1 => return size of array */
222 if (var_index == -1) {
223 bsnprintf(buf, sizeof(buf), "%d", counter->CurrentValue);
224 *val_len = bsnprintf(buf, sizeof(buf), "%d", strlen(buf));
226 *val_size = 0; /* don't try to free val_ptr */
229 bsnprintf(buf, sizeof(buf), "%d", counter->CurrentValue);
230 *val_ptr = bstrdup(buf);
231 *val_len = strlen(buf);
232 *val_size = *val_len + 1;
234 if (var_inc) { /* increment the variable? */
235 if (counter->CurrentValue == counter->MaxValue) {
236 counter->CurrentValue = counter->MinValue;
238 counter->CurrentValue++;
240 if (counter->Catalog) { /* update catalog if need be */
242 JCR *jcr = (JCR *)my_ctx;
243 memset(&cr, 0, sizeof(cr));
244 bstrncpy(cr.Counter, counter->name(), sizeof(cr.Counter));
245 cr.MinValue = counter->MinValue;
246 cr.MaxValue = counter->MaxValue;
247 cr.CurrentValue = counter->CurrentValue;
248 Dmsg1(100, "New value=%d\n", cr.CurrentValue);
249 if (counter->WrapCounter) {
250 bstrncpy(cr.WrapCounter, counter->WrapCounter->name(), sizeof(cr.WrapCounter));
252 cr.WrapCounter[0] = 0;
254 if (!db_update_counter_record(jcr, jcr->db, &cr)) {
255 Jmsg(jcr, M_ERROR, 0, _("Count not update counter %s: ERR=%s\n"),
256 counter->name(), db_strerror(jcr->db));
270 * Called here from "core" expand code to look up a variable
272 static var_rc_t lookup_var(var_t *ctx, void *my_ctx,
273 const char *var_ptr, int var_len, int var_inc, int var_index,
274 const char **val_ptr, int *val_len, int *val_size)
276 char buf[MAXSTRING], *val, *p, *v;
280 /* Note, if val_size > 0 and val_ptr!=NULL, the core code will free() it */
281 if ((stat = lookup_built_in_var(ctx, my_ctx, var_ptr, var_len, var_index,
282 val_ptr, val_len, val_size)) == VAR_OK) {
286 if ((stat = lookup_counter_var(ctx, my_ctx, var_ptr, var_len, var_inc, var_index,
287 val_ptr, val_len, val_size)) == VAR_OK) {
291 /* Look in environment */
292 if (var_len > (int)sizeof(buf) - 1) {
293 return VAR_ERR_OUT_OF_MEMORY;
295 memcpy(buf, var_ptr, var_len + 1);
297 Dmsg1(100, "Var=%s\n", buf);
299 if ((val = getenv(buf)) == NULL) {
300 return VAR_ERR_UNDEFINED_VARIABLE;
302 /* He wants to index the "array" */
304 /* Find the size of the "array"
305 * each element is separated by a |
307 for (p = val; *p; p++) {
312 Dmsg3(100, "For %s, reqest index=%d have=%d\n",
313 buf, var_index, count);
315 /* -1 => return size of array */
316 if (var_index == -1) {
318 if (count == 1) { /* if not array */
319 len = strlen(val); /* return length of string */
321 len = count; /* else return # array items */
323 *val_len = bsnprintf(buf, sizeof(buf), "%d", len);
325 *val_size = 0; /* don't try to free val_ptr */
330 if (var_index < -1 || var_index > --count) {
331 // return VAR_ERR_SUBMATCH_OUT_OF_RANGE;
332 return VAR_ERR_UNDEFINED_VARIABLE;
334 /* Now find the particular item (var_index) he wants */
338 if (count < var_index) {
347 if (p-val > (int)sizeof(buf) - 1) {
348 return VAR_ERR_OUT_OF_MEMORY;
350 Dmsg2(100, "val=%s len=%d\n", val, p-val);
351 /* Make a copy of item, and pass it back */
352 v = (char *)malloc(p-val+1);
353 memcpy(v, val, p-val);
358 Dmsg1(100, "v=%s\n", v);
363 * Called here to do a special operation on a variable
364 * op_ptr points to the special operation code (not EOS terminated)
365 * arg_ptr points to argument to special op code
366 * val_ptr points to the value string
367 * out_ptr points to string to be returned
369 static var_rc_t operate_var(var_t *var, void *my_ctx,
370 const char *op_ptr, int op_len,
371 const char *arg_ptr, int arg_len,
372 const char *val_ptr, int val_len,
373 char **out_ptr, int *out_len, int *out_size)
375 var_rc_t stat = VAR_ERR_UNDEFINED_OPERATION;
376 Dmsg0(100, "Enter operate_var\n");
381 if (op_len == 3 && strncmp(op_ptr, "inc", 3) == 0) {
383 if (val_len > (int)sizeof(buf) - 1) {
384 return VAR_ERR_OUT_OF_MEMORY;
386 memcpy(buf, arg_ptr, arg_len);
388 Dmsg1(100, "Arg=%s\n", buf);
389 memcpy(buf, val_ptr, val_len);
391 Dmsg1(100, "Val=%s\n", buf);
393 for (COUNTER *counter=NULL; (counter = (COUNTER *)GetNextRes(R_COUNTER, (RES *)counter)); ) {
394 if (strcmp(counter->name(), buf) == 0) {
395 Dmsg2(100, "counter=%s val=%s\n", counter->name(), buf);
408 * Expand an input line and return it.
410 * Returns: 0 on failure
411 * 1 on success and exp has expanded input
413 int variable_expansion(JCR *jcr, char *inp, POOLMEM **exp)
421 in_len = strlen(inp);
426 if ((stat = var_create(&var_ctx)) != VAR_OK) {
427 Jmsg(jcr, M_ERROR, 0, _("Cannot create var context: ERR=%s\n"), var_strerror(var_ctx, stat));
430 /* define callback */
431 if ((stat = var_config(var_ctx, VAR_CONFIG_CB_VALUE, lookup_var, (void *)jcr)) != VAR_OK) {
432 Jmsg(jcr, M_ERROR, 0, _("Cannot set var callback: ERR=%s\n"), var_strerror(var_ctx, stat));
436 /* define special operations */
437 if ((stat = var_config(var_ctx, VAR_CONFIG_CB_OPERATION, operate_var, (void *)jcr)) != VAR_OK) {
438 Jmsg(jcr, M_ERROR, 0, _("Cannot set var operate: ERR=%s\n"), var_strerror(var_ctx, stat));
442 /* unescape in place */
443 if ((stat = var_unescape(var_ctx, inp, in_len, inp, in_len+1, 0)) != VAR_OK) {
444 Jmsg(jcr, M_ERROR, 0, _("Cannot unescape string: ERR=%s\n"), var_strerror(var_ctx, stat));
448 in_len = strlen(inp);
450 /* expand variables */
451 if ((stat = var_expand(var_ctx, inp, in_len, &outp, &out_len, 0)) != VAR_OK) {
452 Jmsg(jcr, M_ERROR, 0, _("Cannot expand expression \"%s\": ERR=%s\n"),
453 inp, var_strerror(var_ctx, stat));
457 /* unescape once more in place */
458 if ((stat = var_unescape(var_ctx, outp, out_len, outp, out_len+1, 1)) != VAR_OK) {
459 Jmsg(jcr, M_ERROR, 0, _("Cannot unescape string: ERR=%s\n"), var_strerror(var_ctx, stat));
463 pm_strcpy(exp, outp);
468 /* destroy expansion context */
469 if ((stat = var_destroy(var_ctx)) != VAR_OK) {
470 Jmsg(jcr, M_ERROR, 0, _("Cannot destroy var context: ERR=%s\n"), var_strerror(var_ctx, stat));