]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/plugins/fd/fd_common.h
Big backport from Enterprise
[bacula/bacula] / bacula / src / plugins / fd / fd_common.h
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2017 Kern Sibbald
5
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.
8
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.
13
14    This notice must be preserved when any source code is
15    conveyed and/or propagated.
16
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19
20 /* You can include this file to your plugin to have
21  * access to some common tools and utilities provided by Bacula
22  */
23
24 #ifndef PCOMMON_H
25 #define PCOMMON_H
26
27 #define JT_BACKUP                'B'  /* Backup Job */
28 #define JT_RESTORE               'R'  /* Restore Job */
29
30 #define L_FULL                   'F'  /* Full backup */
31 #define L_INCREMENTAL            'I'  /* since last backup */
32 #define L_DIFFERENTIAL           'D'  /* since last full backup */
33
34 #ifndef DLL_IMP_EXP
35 # if defined(BUILDING_DLL)
36 #   define DLL_IMP_EXP   __declspec(dllexport)
37 # elif defined(USING_DLL)
38 #   define DLL_IMP_EXP   __declspec(dllimport)
39 # else
40 #   define DLL_IMP_EXP
41 # endif
42 #endif
43
44 #ifdef SMARTALLOC
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);
49
50 #ifndef bmalloc
51 # define bmalloc(s)      sm_malloc(__FILE__, __LINE__, (s))
52 # define bfree(o)        sm_free(__FILE__, __LINE__, (o))
53 #endif
54
55 #define SM_CHECK sm_check(__FILE__, __LINE__, false)
56
57 #ifdef malloc
58 #undef malloc
59 #undef free
60 #endif
61
62 #define malloc(s)    sm_malloc(__FILE__, __LINE__, (s))
63 #define free(o)      sm_free(__FILE__, __LINE__, (o))
64
65 /* Looks to be broken on scientific linux */
66 #ifdef xxxx
67 inline void *operator new(size_t size, char const * file, int line)
68 {
69    void *pnew = sm_malloc(file,line, size);
70    memset((char *)pnew, 0, size);
71    return pnew;
72 }
73
74 inline void *operator new[](size_t size, char const * file, int line)
75 {
76    void *pnew = sm_malloc(file, line, size);
77    memset((char *)pnew, 0, size);
78    return pnew;
79 }
80
81 inline void *operator new(size_t size)
82 {
83    void *pnew = sm_malloc(__FILE__, __LINE__, size);
84    memset((char *)pnew, 0, size);
85    return pnew;
86 }
87
88 inline void *operator new[](size_t size)
89 {
90    void *pnew = sm_malloc(__FILE__, __LINE__, size);
91    memset((char *)pnew, 0, size);
92    return pnew;
93 }
94
95 #define new   new(__FILE__, __LINE__)
96
97 inline void operator delete(void *buf)
98 {
99    sm_free( __FILE__, __LINE__, buf);
100 }
101
102 inline void operator delete[] (void *buf)
103 {
104   sm_free(__FILE__, __LINE__, buf);
105 }
106
107 inline void operator delete[] (void *buf, char const * file, int line)
108 {
109   sm_free(file, line, buf);
110 }
111
112 inline void operator delete(void *buf, char const * file, int line)
113 {
114    sm_free(file, line, buf);
115 }
116
117 #endif
118 #endif  /* !SMARTALLOC */
119
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__ )
122
123
124 #ifdef USE_CMD_PARSER
125 #include "lib/cmd_parser.h"
126 #endif /* USE_CMD_PARSER */
127
128 #ifdef USE_ADD_DRIVE
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;
138          (*nCount)++;
139       }                                
140    }
141 }
142
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)) {
148          dest[last++] = *p;
149          dest[last] = 0;
150       }
151    }
152 }
153 #endif  /* USE_ADD_DRIVE */
154
155 #endif  /* ! PCOMMON_H */
156
157 /* Check if the bacula version is enterprise */
158 #ifndef check_beef
159 # define check_beef(ctx, ret) \
160   do {                       \
161      const char *v;          \
162      bfuncs->getBaculaValue(ctx, bVarVersion, (void *)&v);  \
163      if (v[0] == '6' || v[0] == '8') {                      \
164         *(ret) = true;       \
165      } else {                \
166         *(ret) = false;      \
167      }                       \
168   } while (0)
169
170 #endif  /* check_beef */
171
172 #ifdef USE_JOB_LIST
173
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. !!!
177  */
178
179 class joblist: public SMARTALLOC
180 {
181 private:
182    bpContext *ctx;
183
184 public:
185    char level;                  /* level of the job */
186
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 */
193
194    btime_t job_time;           /* job time */
195
196    void init() {
197       level = 0;
198       job_time = 0;
199       *key = *name = *prev = *root = *rootdiff = 0;
200       set_base("jobs.dat");
201       ctx = NULL;
202    }
203
204    void set_base(const char *b) {
205       strncpy(base, b, sizeof(base));
206    }
207
208    joblist(bpContext *actx) { init(); ctx = actx; }
209
210    joblist(bpContext *actx, 
211         const char *akey, 
212         const char *jobname, 
213         const char *prevjobname, 
214         char joblevel) 
215    {
216       init();
217       ctx = actx;
218       if (jobname) {
219          strncpy(name, jobname, MAX_NAME_LENGTH);
220       }
221
222       if (prevjobname) {
223          strncpy(prev, prevjobname, MAX_NAME_LENGTH);
224       }
225
226       level = joblevel;
227
228       if (akey) {
229          strncpy(key, akey, MAX_NAME_LENGTH);
230
231       } else {
232          get_key_from_name();
233       }
234    }
235
236    ~joblist() { }
237
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");
243
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
254          {
255             bstrncpy(key, name, start + 1);
256             Dmsg(ctx, dbglvl+100, "key is %s from jobname %s\n", key, name);
257             return true;
258          }
259       }
260       Dmsg(ctx, dbglvl+100, "Unable to get key from jobname %s\n", name);
261       return false;
262    }
263
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);
269 };
270
271 static pthread_mutex_t joblist_mutex = PTHREAD_MUTEX_INITIALIZER;
272
273 bool joblist::find_job(const char *name, POOLMEM **data)
274 {
275    BFILE fp;
276    FILE *f;
277    POOLMEM *tmp;
278    char buf[1024];
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];
284    char curlevel;
285    bool ok=false;
286
287    *root = 0;
288    job_time = 0;
289    *rootdiff = 0;
290
291    binit(&fp);
292    set_portable_backup(&fp);
293
294    tmp = get_pool_memory(PM_FNAME);
295    Mmsg(tmp, "%s/%s", working, base);
296
297    P(joblist_mutex);
298    if (bopen(&fp, tmp, O_RDONLY, 0) < 0) {
299       berrno be;
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));
303       goto bail_out;
304    }
305
306    f = fdopen(fp.fid, "r");
307    if (!f) {
308       berrno be;
309       Jmsg(ctx, M_ERROR, "Unable to open job database. ERR=%s\n",
310            be.bstrerror(errno));
311       goto bail_out;
312    }
313
314    while (!ok && fgets(buf, sizeof(buf), f) != NULL) {
315       *curkey = *curjobname = *rootjob = *prevjob = 0;
316
317       Dmsg(ctx, dbglvl+100, "line = [%s]\n", buf);
318
319       if (sscanf(buf, "time=%60s level=%c key=%127s name=%127s root=%127s prev=%127s", 
320                  t, &curlevel, curkey, curjobname, rootjob, prevjob) != 6) {
321
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);
325             continue;
326          }
327       }
328       
329       if (strcmp(name, curjobname) == 0 &&
330           strcmp(key, curkey) == 0)
331       {
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);
336          }
337
338          if (data) {
339             pm_strcpy(data, strstr(buf, " vol=") +  5);
340             strip_trailing_newline(*data);
341             unbash_spaces(*data);
342          }
343
344          ok = true;
345          Dmsg(ctx, dbglvl+100, "Found job root %s -> %s -> %s\n",
346               rootdiff, root, curjobname);
347       } 
348    }
349
350    fclose(f);
351
352 bail_out:
353    V(joblist_mutex);
354    free_pool_memory(tmp);
355    return ok;
356
357 }
358
359 /* Find the root job for the current job */
360 bool joblist::find_root_job()
361 {
362    BFILE fp;
363    FILE *f;
364    POOLMEM *tmp;
365    char buf[1024];
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];
371    char curlevel;
372    bool ok=false;
373
374    *root = 0;
375    job_time = 0;
376
377    if (level == 'F') {
378       bstrncpy(root, name, MAX_NAME_LENGTH);
379       return true;
380    }
381
382    binit(&fp);
383    set_portable_backup(&fp);
384
385    tmp = get_pool_memory(PM_FNAME);
386    Mmsg(tmp, "%s/%s", working, base);
387
388    P(joblist_mutex);
389    if (bopen(&fp, tmp, O_RDONLY, 0) < 0) {
390       berrno be;
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));
394       goto bail_out;
395    }
396
397    f = fdopen(fp.fid, "r");
398    if (!f) {
399       berrno be;
400       Jmsg(ctx, M_ERROR, "Unable to prune previous jobs. ERR=%s\n",
401            be.bstrerror(errno));
402       goto bail_out;
403    }
404
405    while (!ok && fgets(buf, sizeof(buf), f) != NULL) {
406       *curkey = *curjobname = *rootjob = *prevjob = 0;
407
408       Dmsg(ctx, dbglvl+100, "line = [%s]\n", buf);
409
410       if (sscanf(buf, "time=%60s level=%c key=%127s name=%127s root=%127s prev=%127s", 
411                  t, &curlevel, curkey, curjobname, rootjob, prevjob) != 6) {
412
413          if (sscanf(buf, "time=%60s level=F key=%127s name=%127s", 
414                         t, curkey, curjobname) == 3) {
415             bstrncpy(rootjob, curjobname, MAX_NAME_LENGTH);
416             *prevjob = 0;
417             curlevel = 'F';
418
419          } else {
420             Dmsg(ctx, dbglvl+100, "Bad line l=[%s]\n", buf);
421             continue;
422          }
423       }
424       
425       if (strcmp(key,  curkey)  == 0  &&
426           strcmp(prev, curjobname) == 0) 
427       {
428          bstrncpy(root, rootjob, MAX_NAME_LENGTH);
429
430          if (curlevel == 'D') {
431             bstrncpy(rootdiff, curjobname, MAX_NAME_LENGTH);
432          }
433          ok = true;
434          Dmsg(ctx, dbglvl+100, "Found job root %s -> %s -> %s\n",
435               rootdiff, root, curjobname);
436       } 
437    }
438
439    fclose(f);
440
441 bail_out:
442    V(joblist_mutex);
443    free_pool_memory(tmp);
444    return true;
445 }
446
447 /* Store the current job in the jobs.dat for a specific data list */
448 void joblist::store_job(char *data)
449 {
450    BFILE fp;
451    int l;
452    POOLMEM *tmp = NULL;
453    btime_t now;
454
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);
458       return;
459    }
460
461    find_root_job();
462
463    binit(&fp);
464    set_portable_backup(&fp);
465
466    P(joblist_mutex);
467
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) {
471       berrno be;
472       Jmsg(ctx, M_ERROR, "Unable to update the job history. ERR=%s\n",
473            be.bstrerror(errno));
474       goto bail_out;
475    }
476
477    now = time(NULL);
478    
479    bash_spaces(data);
480
481    if (level == 'F') {
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);
484
485    } else {
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);
488    }
489
490    if (bwrite(&fp, tmp, l) != l) {
491       berrno be;
492       Jmsg(ctx, M_ERROR, "Unable to update the job history. ERR=%s\n",
493            be.bstrerror(errno));
494    }
495
496    bclose(&fp);
497
498 bail_out:
499    V(joblist_mutex);
500    free_pool_memory(tmp);
501 }
502
503 /* Prune jobs at the end of the job, this function can generate commands
504  * in order to cleanup something
505  */
506 void joblist::prune_jobs(char *build_cmd(void *arg, const char *data, const char *job), 
507                          void *arg, alist *jobs)
508 {
509    BFILE fp, fpout;
510    FILE *f=NULL;
511    POOLMEM *tmp;
512    POOLMEM *tmpout;
513    POOLMEM *data;
514    POOLMEM *buf;
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];
520    uint32_t datalen;
521    char curlevel;
522    bool keep;
523    bool ok=false;
524    int count=0, len;
525
526    /* In Incremental, it means that the previous Full/Diff is well terminated */
527    if (level != 'I') {
528       return;
529    }
530
531    find_root_job();
532
533    binit(&fp);
534    set_portable_backup(&fp);
535
536    binit(&fpout);
537    set_portable_backup(&fpout);
538
539    tmp = get_pool_memory(PM_FNAME);
540    Mmsg(tmp, "%s/%s", working, base);
541
542    tmpout = get_pool_memory(PM_FNAME);
543    Mmsg(tmpout, "%s/%s.swap", working, base);
544
545    buf = get_pool_memory(PM_FNAME);
546    data = get_pool_memory(PM_FNAME);
547    *buf = *data = 0;
548
549    P(joblist_mutex);
550    if (bopen(&fp, tmp, O_RDONLY, 0) < 0) {
551       berrno be;
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));
555       goto bail_out;
556    }
557    if (bopen(&fpout, tmpout, O_CREAT|O_WRONLY, 0600) < 0) {
558       berrno be;
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));
562       goto bail_out;
563    }
564
565    f = fdopen(fp.fid, "r");     /* we use fgets from open() */
566    if (!f) {
567       berrno be;
568       Jmsg(ctx, M_ERROR, "Unable to prune previous jobs. ERR=%s\n",
569            be.bstrerror(errno));
570       goto bail_out;
571    }
572
573    while (fgets(buf, sizeof_pool_memory(buf), f) != NULL) {
574       *data = *curkey = *jobname = *rootjob = *prevjob = 0;
575       keep = false;
576       datalen = 0;
577       
578       len = strlen(buf);
579       if (len > 0 && buf[len -1] != '\n') {
580          /* The line is larger than the buffer, we need to capture the rest */
581          bool ok=false;
582          while (!ok) {
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) {
586                ok = true;
587             }
588             len = strlen(buf);
589             if (buf[len - 1] == '\n') {
590                ok = true;
591             }
592             if (len > 32000) {  /* sanity check */
593                ok = true;
594             }
595          }
596       }
597
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) {
601
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;
605             curlevel = 'F';
606
607          } else {
608             Dmsg(ctx, dbglvl+100, "Bad line l=[%s]\n", buf);
609             keep = true;
610          }
611       }
612       
613       if (!keep) {
614          pm_strcpy(data, strstr(buf, " vol=") +  5);
615          strip_trailing_newline(data);
616          unbash_spaces(data);
617
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);
621          }
622       }
623
624
625       if (!keep &&
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))
631       {
632          keep = true;
633       } 
634       
635       if (keep) {
636          if (bwrite(&fpout, buf, len) < 0) {
637             berrno be;
638             Jmsg(ctx, M_ERROR, "Unable to update the job history. ERR=%s\n",
639                  be.bstrerror(errno));
640             goto bail_out;
641          }
642
643       } else if (build_cmd) {
644          count++;
645          Dmsg(ctx, dbglvl+100, "Can prune jobname %s\n", jobname);
646
647          char *p2 = data;
648          for(char *p = data; *p; p++) {
649             if (*p == ',') {
650                *p = 0;
651                jobs->append(bstrdup(build_cmd(arg, p2, jobname)));
652                p2 = p + 1 ;
653             }
654          }
655          jobs->append(bstrdup(build_cmd(arg, p2, jobname)));
656
657       } else if (jobs) {
658          jobs->append(bstrdup(data));
659       }
660    }
661
662    ok = true;
663
664 bail_out:
665    if (f) {
666       fclose(f);
667    }
668    if (is_bopen(&fpout)) {
669       bclose(&fpout);
670    }
671
672    /* We can switch the file */
673    if (ok) {
674       unlink(tmp);
675       
676       if (rename(tmpout, tmp) < 0) {
677          berrno be;
678          Jmsg(ctx, M_ERROR, "Unable to update the job history. ERR=%s\n",
679               be.bstrerror(errno));
680       }
681    }
682
683    V(joblist_mutex);
684    free_pool_memory(tmp);
685    free_pool_memory(tmpout);
686    free_pool_memory(data);
687    free_pool_memory(buf);
688
689    Dmsg(ctx, dbglvl+100, "Pruning %d jobs\n", count);
690 }
691
692
693 #endif  /* ! USE_JOB_LIST */
694