2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2017 Kern Sibbald
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
20 /* You can include this file to your plugin to have
21 * access to some common tools and utilities provided by Bacula
27 #define JT_BACKUP 'B' /* Backup Job */
28 #define JT_RESTORE 'R' /* Restore Job */
30 #define L_FULL 'F' /* Full backup */
31 #define L_INCREMENTAL 'I' /* since last backup */
32 #define L_DIFFERENTIAL 'D' /* since last full backup */
35 # if defined(BUILDING_DLL)
36 # define DLL_IMP_EXP __declspec(dllexport)
37 # elif defined(USING_DLL)
38 # define DLL_IMP_EXP __declspec(dllimport)
45 DLL_IMP_EXP void *sm_malloc(const char *fname, int lineno, unsigned int nbytes);
46 DLL_IMP_EXP void sm_free(const char *file, int line, void *fp);
47 DLL_IMP_EXP void *reallymalloc(const char *fname, int lineno, unsigned int nbytes);
48 DLL_IMP_EXP void reallyfree(const char *file, int line, void *fp);
51 # define bmalloc(s) sm_malloc(__FILE__, __LINE__, (s))
52 # define bfree(o) sm_free(__FILE__, __LINE__, (o))
55 #define SM_CHECK sm_check(__FILE__, __LINE__, false)
62 #define malloc(s) sm_malloc(__FILE__, __LINE__, (s))
63 #define free(o) sm_free(__FILE__, __LINE__, (o))
65 /* Looks to be broken on scientific linux */
67 inline void *operator new(size_t size, char const * file, int line)
69 void *pnew = sm_malloc(file,line, size);
70 memset((char *)pnew, 0, size);
74 inline void *operator new[](size_t size, char const * file, int line)
76 void *pnew = sm_malloc(file, line, size);
77 memset((char *)pnew, 0, size);
81 inline void *operator new(size_t size)
83 void *pnew = sm_malloc(__FILE__, __LINE__, size);
84 memset((char *)pnew, 0, size);
88 inline void *operator new[](size_t size)
90 void *pnew = sm_malloc(__FILE__, __LINE__, size);
91 memset((char *)pnew, 0, size);
95 #define new new(__FILE__, __LINE__)
97 inline void operator delete(void *buf)
99 sm_free( __FILE__, __LINE__, buf);
102 inline void operator delete[] (void *buf)
104 sm_free(__FILE__, __LINE__, buf);
107 inline void operator delete[] (void *buf, char const * file, int line)
109 sm_free(file, line, buf);
112 inline void operator delete(void *buf, char const * file, int line)
114 sm_free(file, line, buf);
118 #endif /* !SMARTALLOC */
120 #define Dmsg(context, level, ...) bfuncs->DebugMessage(context, __FILE__, __LINE__, level, __VA_ARGS__ )
121 #define Jmsg(context, type, ...) bfuncs->JobMessage(context, __FILE__, __LINE__, type, 0, __VA_ARGS__ )
124 #ifdef USE_CMD_PARSER
125 #include "lib/cmd_parser.h"
126 #endif /* USE_CMD_PARSER */
129 /* Keep drive letters for windows vss snapshot */
130 static void add_drive(char *drives, int *nCount, char *fname) {
131 if (strlen(fname) >= 2 && B_ISALPHA(fname[0]) && fname[1] == ':') {
132 /* always add in uppercase */
133 char ch = toupper(fname[0]);
134 /* if not found in string, add drive letter */
135 if (!strchr(drives,ch)) {
136 drives[*nCount] = ch;
137 drives[*nCount+1] = 0;
143 /* Copy our drive list to Bacula core list */
144 static void copy_drives(char *drives, char *dest) {
145 int last = strlen(dest); /* dest is 27 bytes long */
146 for (char *p = drives; *p && last < 26; p++) {
147 if (!strchr(dest, *p)) {
153 #endif /* USE_ADD_DRIVE */
155 #endif /* ! PCOMMON_H */
157 /* Check if the bacula version is enterprise */
159 # define check_beef(ctx, ret) \
162 bfuncs->getBaculaValue(ctx, bVarVersion, (void *)&v); \
163 if (v[0] == '6' || v[0] == '8') { \
170 #endif /* check_beef */
174 /* This class is used to store locally the job history, you can attach data
175 * to it such as snapshot names
176 * !!! Don't forget that this file may be deleted by the user. !!!
179 class joblist: public SMARTALLOC
185 char level; /* level of the job */
187 char base[MAX_NAME_LENGTH]; /* base name */
188 char key[MAX_NAME_LENGTH]; /* group of backup */
189 char name[MAX_NAME_LENGTH]; /* job name */
190 char prev[MAX_NAME_LENGTH]; /* based on jobname */
191 char root[MAX_NAME_LENGTH]; /* root of this branch */
192 char rootdiff[MAX_NAME_LENGTH]; /* root of diff if any */
194 btime_t job_time; /* job time */
199 *key = *name = *prev = *root = *rootdiff = 0;
200 set_base("jobs.dat");
204 void set_base(const char *b) {
205 strncpy(base, b, sizeof(base));
208 joblist(bpContext *actx) { init(); ctx = actx; }
210 joblist(bpContext *actx,
213 const char *prevjobname,
219 strncpy(name, jobname, MAX_NAME_LENGTH);
223 strncpy(prev, prevjobname, MAX_NAME_LENGTH);
229 strncpy(key, akey, MAX_NAME_LENGTH);
238 /* Will extract the name from the full job name */
239 bool get_key_from_name() {
240 // pluginTest.2012-07-19_16.59.21_11
241 int l = strlen(name);
242 int dlen = 23; // strlen(".2012-07-19_16.59.21_11");
244 if (l > dlen) { /* we probably have a key */
245 int start = l - dlen;
246 if (name[start] == '.' &&
247 B_ISDIGIT(name[start + 1]) && // 2
248 B_ISDIGIT(name[start + 2]) && // 0
249 B_ISDIGIT(name[start + 3]) && // 1
250 B_ISDIGIT(name[start + 4]) && // 2
251 name[start + 5] == '-' && // -
252 B_ISDIGIT(name[start + 6]) && // 0
253 B_ISDIGIT(name[start + 7])) // 7
255 bstrncpy(key, name, start + 1);
256 Dmsg(ctx, dbglvl+100, "key is %s from jobname %s\n", key, name);
260 Dmsg(ctx, dbglvl+100, "Unable to get key from jobname %s\n", name);
264 bool find_job(const char *name, POOLMEM **data=NULL); /* set root, job_time */
265 bool find_root_job();
266 void store_job(char *data);
267 void prune_jobs(char *build_cmd(void *arg, const char *data, const char *job),
268 void *arg, alist *jobs);
271 static pthread_mutex_t joblist_mutex = PTHREAD_MUTEX_INITIALIZER;
273 bool joblist::find_job(const char *name, POOLMEM **data)
279 char curkey[MAX_NAME_LENGTH]; /* key */
280 char curjobname[MAX_NAME_LENGTH]; /* jobname */
281 char prevjob[MAX_NAME_LENGTH]; /* last jobname */
282 char rootjob[MAX_NAME_LENGTH]; /* root jobname */
283 char t[MAX_NAME_LENGTH];
292 set_portable_backup(&fp);
294 tmp = get_pool_memory(PM_FNAME);
295 Mmsg(tmp, "%s/%s", working, base);
298 if (bopen(&fp, tmp, O_RDONLY, 0) < 0) {
300 Jmsg(ctx, M_ERROR, "Unable to open job database. "
301 "Can't open %s for reading ERR=%s\n",
302 tmp, be.bstrerror(errno));
306 f = fdopen(fp.fid, "r");
309 Jmsg(ctx, M_ERROR, "Unable to open job database. ERR=%s\n",
310 be.bstrerror(errno));
314 while (!ok && fgets(buf, sizeof(buf), f) != NULL) {
315 *curkey = *curjobname = *rootjob = *prevjob = 0;
317 Dmsg(ctx, dbglvl+100, "line = [%s]\n", buf);
319 if (sscanf(buf, "time=%60s level=%c key=%127s name=%127s root=%127s prev=%127s",
320 t, &curlevel, curkey, curjobname, rootjob, prevjob) != 6) {
322 if (sscanf(buf, "time=%60s level=F key=%127s name=%127s",
323 t, curkey, curjobname) != 3) {
324 Dmsg(ctx, dbglvl+100, "Bad line l=[%s]\n", buf);
329 if (strcmp(name, curjobname) == 0 &&
330 strcmp(key, curkey) == 0)
332 job_time = str_to_uint64(t);
333 bstrncpy(root, rootjob, MAX_NAME_LENGTH);
334 if (curlevel == 'D') {
335 bstrncpy(rootdiff, curjobname, MAX_NAME_LENGTH);
339 pm_strcpy(data, strstr(buf, " vol=") + 5);
340 strip_trailing_newline(*data);
341 unbash_spaces(*data);
345 Dmsg(ctx, dbglvl+100, "Found job root %s -> %s -> %s\n",
346 rootdiff, root, curjobname);
354 free_pool_memory(tmp);
359 /* Find the root job for the current job */
360 bool joblist::find_root_job()
366 char curkey[MAX_NAME_LENGTH]; /* key */
367 char curjobname[MAX_NAME_LENGTH]; /* jobname */
368 char prevjob[MAX_NAME_LENGTH]; /* last jobname */
369 char rootjob[MAX_NAME_LENGTH]; /* root jobname */
370 char t[MAX_NAME_LENGTH];
378 bstrncpy(root, name, MAX_NAME_LENGTH);
383 set_portable_backup(&fp);
385 tmp = get_pool_memory(PM_FNAME);
386 Mmsg(tmp, "%s/%s", working, base);
389 if (bopen(&fp, tmp, O_RDONLY, 0) < 0) {
391 Jmsg(ctx, M_ERROR, "Unable to prune previous jobs. "
392 "Can't open %s for reading ERR=%s\n",
393 tmp, be.bstrerror(errno));
397 f = fdopen(fp.fid, "r");
400 Jmsg(ctx, M_ERROR, "Unable to prune previous jobs. ERR=%s\n",
401 be.bstrerror(errno));
405 while (!ok && fgets(buf, sizeof(buf), f) != NULL) {
406 *curkey = *curjobname = *rootjob = *prevjob = 0;
408 Dmsg(ctx, dbglvl+100, "line = [%s]\n", buf);
410 if (sscanf(buf, "time=%60s level=%c key=%127s name=%127s root=%127s prev=%127s",
411 t, &curlevel, curkey, curjobname, rootjob, prevjob) != 6) {
413 if (sscanf(buf, "time=%60s level=F key=%127s name=%127s",
414 t, curkey, curjobname) == 3) {
415 bstrncpy(rootjob, curjobname, MAX_NAME_LENGTH);
420 Dmsg(ctx, dbglvl+100, "Bad line l=[%s]\n", buf);
425 if (strcmp(key, curkey) == 0 &&
426 strcmp(prev, curjobname) == 0)
428 bstrncpy(root, rootjob, MAX_NAME_LENGTH);
430 if (curlevel == 'D') {
431 bstrncpy(rootdiff, curjobname, MAX_NAME_LENGTH);
434 Dmsg(ctx, dbglvl+100, "Found job root %s -> %s -> %s\n",
435 rootdiff, root, curjobname);
443 free_pool_memory(tmp);
447 /* Store the current job in the jobs.dat for a specific data list */
448 void joblist::store_job(char *data)
455 /* Not initialized, no need to store jobs */
456 if (*name == 0 || !level) {
457 Dmsg(ctx, dbglvl+100, "store_job fail name=%s level=%d\n", name, level);
464 set_portable_backup(&fp);
468 tmp = get_pool_memory(PM_FNAME);
469 Mmsg(tmp, "%s/%s", working, base);
470 if (bopen(&fp, tmp, O_WRONLY|O_CREAT|O_APPEND, 0600) < 0) {
472 Jmsg(ctx, M_ERROR, "Unable to update the job history. ERR=%s\n",
473 be.bstrerror(errno));
482 l = Mmsg(tmp, "time=%lld level=%c key=%s name=%s vollen=%d vol=%s\n",
483 now, level, key, name, strlen(data), data);
486 l = Mmsg(tmp, "time=%lld level=%c key=%s name=%s root=%s prev=%s vollen=%d vol=%s\n",
487 now, level, key, name, root, prev, strlen(data), data);
490 if (bwrite(&fp, tmp, l) != l) {
492 Jmsg(ctx, M_ERROR, "Unable to update the job history. ERR=%s\n",
493 be.bstrerror(errno));
500 free_pool_memory(tmp);
503 /* Prune jobs at the end of the job, this function can generate commands
504 * in order to cleanup something
506 void joblist::prune_jobs(char *build_cmd(void *arg, const char *data, const char *job),
507 void *arg, alist *jobs)
515 char curkey[MAX_NAME_LENGTH]; /* key */
516 char jobname[MAX_NAME_LENGTH]; /* jobname */
517 char prevjob[MAX_NAME_LENGTH]; /* last jobname */
518 char rootjob[MAX_NAME_LENGTH]; /* root jobname */
519 char t[MAX_NAME_LENGTH];
526 /* In Incremental, it means that the previous Full/Diff is well terminated */
534 set_portable_backup(&fp);
537 set_portable_backup(&fpout);
539 tmp = get_pool_memory(PM_FNAME);
540 Mmsg(tmp, "%s/%s", working, base);
542 tmpout = get_pool_memory(PM_FNAME);
543 Mmsg(tmpout, "%s/%s.swap", working, base);
545 buf = get_pool_memory(PM_FNAME);
546 data = get_pool_memory(PM_FNAME);
550 if (bopen(&fp, tmp, O_RDONLY, 0) < 0) {
552 Jmsg(ctx, M_ERROR, "Unable to prune previous jobs. "
553 "Can't open %s for reading ERR=%s\n",
554 tmp, be.bstrerror(errno));
557 if (bopen(&fpout, tmpout, O_CREAT|O_WRONLY, 0600) < 0) {
559 Jmsg(ctx, M_ERROR, "Unable to prune previous jobs. "
560 "Can't open %s for writing ERR=%s\n",
561 tmpout, be.bstrerror(errno));
565 f = fdopen(fp.fid, "r"); /* we use fgets from open() */
568 Jmsg(ctx, M_ERROR, "Unable to prune previous jobs. ERR=%s\n",
569 be.bstrerror(errno));
573 while (fgets(buf, sizeof_pool_memory(buf), f) != NULL) {
574 *data = *curkey = *jobname = *rootjob = *prevjob = 0;
579 if (len > 0 && buf[len -1] != '\n') {
580 /* The line is larger than the buffer, we need to capture the rest */
583 Dmsg(ctx, dbglvl+100, "Reading extra 1024 bytes, len=%d\n", len);
584 buf = check_pool_memory_size(buf, sizeof_pool_memory(buf) + 1024);
585 if (fgets(buf + len, 1023, f) == NULL) {
589 if (buf[len - 1] == '\n') {
592 if (len > 32000) { /* sanity check */
598 /* We don't capture the vol list, because our sscanf is limited to 1000 bytes */
599 if (sscanf(buf, "time=%60s level=%c key=%127s name=%127s root=%127s prev=%127s vollen=%d vol=",
600 t, &curlevel, curkey, jobname, rootjob, prevjob, &datalen) != 7) {
602 if (sscanf(buf, "time=%60s level=F key=%127s name=%127s vollen=%d vol=",
603 t, curkey, jobname, &datalen) == 4) {
604 *rootdiff = *rootjob = *prevjob = 0;
608 Dmsg(ctx, dbglvl+100, "Bad line l=[%s]\n", buf);
614 pm_strcpy(data, strstr(buf, " vol=") + 5);
615 strip_trailing_newline(data);
618 if (datalen != strlen(data)) {
619 Dmsg(ctx, dbglvl+100, "Bad data line datalen != strlen(data) %d != %d\n", datalen, strlen(data));
620 Dmsg(ctx, dbglvl+100, "v=[%s]\n", data);
626 (strcmp(key, curkey) != 0 ||
627 strcmp(name, jobname) == 0 ||
628 strcmp(prev, jobname) == 0 ||
629 strcmp(root, jobname) == 0 ||
630 strcmp(rootdiff, jobname) == 0))
636 if (bwrite(&fpout, buf, len) < 0) {
638 Jmsg(ctx, M_ERROR, "Unable to update the job history. ERR=%s\n",
639 be.bstrerror(errno));
643 } else if (build_cmd) {
645 Dmsg(ctx, dbglvl+100, "Can prune jobname %s\n", jobname);
648 for(char *p = data; *p; p++) {
651 jobs->append(bstrdup(build_cmd(arg, p2, jobname)));
655 jobs->append(bstrdup(build_cmd(arg, p2, jobname)));
658 jobs->append(bstrdup(data));
668 if (is_bopen(&fpout)) {
672 /* We can switch the file */
676 if (rename(tmpout, tmp) < 0) {
678 Jmsg(ctx, M_ERROR, "Unable to update the job history. ERR=%s\n",
679 be.bstrerror(errno));
684 free_pool_memory(tmp);
685 free_pool_memory(tmpout);
686 free_pool_memory(data);
687 free_pool_memory(buf);
689 Dmsg(ctx, dbglvl+100, "Pruning %d jobs\n", count);
693 #endif /* ! USE_JOB_LIST */