]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/plugins/fd/test-deltaseq-fd.c
Backport from BEE
[bacula/bacula] / bacula / src / plugins / fd / test-deltaseq-fd.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2007-2014 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from many
7    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    Bacula® is a registered trademark of Kern Sibbald.
15 */
16 /*
17  * A simple delta plugin for the Bacula File Daemon
18  *
19  *
20  */
21 #include "bacula.h"
22 #include "fd_plugins.h"
23 #include "fd_common.h"
24
25 #undef malloc
26 #undef free
27 #undef strdup
28
29 #ifdef __cplusplus
30 extern "C" {
31 #endif
32
33 static const int dbglvl = 0;
34
35 #define PLUGIN_LICENSE      "Bacula AGPLv3"
36 #define PLUGIN_AUTHOR       "Eric Bollengier"
37 #define PLUGIN_DATE         "November 2010"
38 #define PLUGIN_VERSION      "1"
39 #define PLUGIN_DESCRIPTION  "Bacula Delta Test Plugin"
40
41 /* Forward referenced functions */
42 static bRC newPlugin(bpContext *ctx);
43 static bRC freePlugin(bpContext *ctx);
44 static bRC getPluginValue(bpContext *ctx, pVariable var, void *value);
45 static bRC setPluginValue(bpContext *ctx, pVariable var, void *value);
46 static bRC handlePluginEvent(bpContext *ctx, bEvent *event, void *value);
47 static bRC startBackupFile(bpContext *ctx, struct save_pkt *sp);
48 static bRC endBackupFile(bpContext *ctx);
49 static bRC pluginIO(bpContext *ctx, struct io_pkt *io);
50 static bRC startRestoreFile(bpContext *ctx, const char *cmd);
51 static bRC endRestoreFile(bpContext *ctx);
52 static bRC createFile(bpContext *ctx, struct restore_pkt *rp);
53 static bRC setFileAttributes(bpContext *ctx, struct restore_pkt *rp);
54
55 /* Pointers to Bacula functions */
56 static bFuncs *bfuncs = NULL;
57 static bInfo  *binfo = NULL;
58
59 /* Plugin Information block */
60 static pInfo pluginInfo = {
61    sizeof(pluginInfo),
62    FD_PLUGIN_INTERFACE_VERSION,
63    FD_PLUGIN_MAGIC,
64    PLUGIN_LICENSE,
65    PLUGIN_AUTHOR,
66    PLUGIN_DATE,
67    PLUGIN_VERSION,
68    PLUGIN_DESCRIPTION,
69 };
70
71 /* Plugin entry points for Bacula */
72 static pFuncs pluginFuncs = {
73    sizeof(pluginFuncs),
74    FD_PLUGIN_INTERFACE_VERSION,
75
76    /* Entry points into plugin */
77    newPlugin,                         /* new plugin instance */
78    freePlugin,                        /* free plugin instance */
79    getPluginValue,
80    setPluginValue,
81    handlePluginEvent,
82    startBackupFile,
83    endBackupFile,
84    startRestoreFile,
85    endRestoreFile,
86    pluginIO,
87    createFile,
88    setFileAttributes,
89    NULL                         /* no checkFile */
90 };
91
92 #define get_self(x) ((delta_test*)((x)->pContext))
93 #define FO_DELTA        (1<<28)       /* Do delta on file */
94 #define FO_OFFSETS      (1<<30)       /* Keep block offsets */
95
96 class delta_test
97 {
98 private:
99    bpContext *ctx;
100
101 public:
102    POOLMEM *fname;              /* Filename to save */
103    int32_t delta;
104    FILE *fd;
105    bool done;
106    int level;
107
108    delta_test(bpContext *bpc) {
109       fd = NULL;
110       ctx = bpc;
111       done = false;
112       level = 0;
113       delta = 0;
114       fname = get_pool_memory(PM_FNAME);
115    }
116    ~delta_test() {
117       free_and_null_pool_memory(fname);
118    }
119 };
120
121 /*
122  * loadPlugin() and unloadPlugin() are entry points that are
123  *  exported, so Bacula can directly call these two entry points
124  *  they are common to all Bacula plugins.
125  */
126 /*
127  * External entry point called by Bacula to "load the plugin
128  */
129 bRC loadPlugin(bInfo *lbinfo, bFuncs *lbfuncs, pInfo **pinfo, pFuncs **pfuncs)
130 {
131    bfuncs = lbfuncs;                  /* set Bacula funct pointers */
132    binfo  = lbinfo;
133    *pinfo  = &pluginInfo;             /* return pointer to our info */
134    *pfuncs = &pluginFuncs;            /* return pointer to our functions */
135
136    /* Activate this plugin only in developer mode */
137 #ifdef DEVELOPER
138    return bRC_OK;
139 #else
140    return bRC_Error;
141 #endif
142 }
143
144 /*
145  * External entry point to unload the plugin
146  */
147 bRC unloadPlugin()
148 {
149 // Dmsg(NULL, dbglvl, "delta-test-fd: Unloaded\n");
150    return bRC_OK;
151 }
152
153 /*
154  * The following entry points are accessed through the function
155  *   pointers we supplied to Bacula. Each plugin type (dir, fd, sd)
156  *   has its own set of entry points that the plugin must define.
157  */
158 /*
159  * Create a new instance of the plugin i.e. allocate our private storage
160  */
161 static bRC newPlugin(bpContext *ctx)
162 {
163    delta_test *self = new delta_test(ctx);
164    if (!self) {
165       return bRC_Error;
166    }
167    ctx->pContext = (void *)self;        /* set our context pointer */
168    return bRC_OK;
169 }
170
171 /*
172  * Free a plugin instance, i.e. release our private storage
173  */
174 static bRC freePlugin(bpContext *ctx)
175 {
176    delta_test *self = get_self(ctx);
177    if (!self) {
178       return bRC_Error;
179    }
180    delete self;
181    return bRC_OK;
182 }
183
184 /*
185  * Return some plugin value (none defined)
186  */
187 static bRC getPluginValue(bpContext *ctx, pVariable var, void *value)
188 {
189    return bRC_OK;
190 }
191
192 /*
193  * Set a plugin value (none defined)
194  */
195 static bRC setPluginValue(bpContext *ctx, pVariable var, void *value)
196 {
197    return bRC_OK;
198 }
199
200 /*
201  * Handle an event that was generated in Bacula
202  */
203 static bRC handlePluginEvent(bpContext *ctx, bEvent *event, void *value)
204 {
205    delta_test *self = get_self(ctx);
206    int accurate=0;
207
208    if (!self) {
209       return bRC_Error;
210    }
211
212 // char *name;
213
214    /*
215     * Most events don't interest us so we ignore them.
216     *   the printfs are so that plugin writers can enable them to see
217     *   what is really going on.
218     */
219    switch (event->eventType) {
220    case bEventPluginCommand:
221 //    Dmsg(ctx, dbglvl,
222 //         "delta-test-fd: PluginCommand=%s\n", (char *)value);
223       break;
224    case bEventJobStart:
225 //    Dmsg(ctx, dbglvl, "delta-test-fd: JobStart=%s\n", (char *)value);
226       break;
227    case bEventJobEnd:
228 //    Dmsg(ctx, dbglvl, "delta-test-fd: JobEnd\n");
229       break;
230    case bEventStartBackupJob:
231 //    Dmsg(ctx, dbglvl, "delta-test-fd: StartBackupJob\n");
232       break;
233    case bEventEndBackupJob:
234 //    Dmsg(ctx, dbglvl, "delta-test-fd: EndBackupJob\n");
235       break;
236    case bEventLevel:
237 //    Dmsg(ctx, dbglvl, "delta-test-fd: JobLevel=%c %d\n", (int)value, (int)value);
238       self->level = (int)(intptr_t)value;
239       break;
240    case bEventSince:
241 //    Dmsg(ctx, dbglvl, "delta-test-fd: since=%d\n", (int)value);
242       break;
243
244    case bEventStartRestoreJob:
245 //    Dmsg(ctx, dbglvl, "delta-test-fd: StartRestoreJob\n");
246       break;
247
248    case bEventEndRestoreJob:
249 //    Dmsg(ctx, dbglvl, "delta-test-fd: EndRestoreJob\n");
250       break;
251
252    /* Plugin command e.g. plugin = <plugin-name>:<name-space>:read command:write command */
253    case bEventRestoreCommand:
254 //    Dmsg(ctx, dbglvl, "delta-test-fd: EventRestoreCommand cmd=%s\n", (char *)value);
255       /* Fall-through wanted */
256       break;
257    case bEventBackupCommand:
258       Dmsg(ctx, dbglvl, "delta-test-fd: pluginEvent cmd=%s\n", (char *)value);
259       if (self->level == 'I' || self->level == 'D') {
260          bfuncs->getBaculaValue(ctx, bVarAccurate, (void *)&accurate);
261          if (!accurate) {       /* can be changed to FATAL */
262             Jmsg(ctx, M_FATAL,
263                  "Accurate mode should be turned on when using the "
264                  "delta-test plugin\n");
265             return bRC_Error;
266          }
267       }
268       break;
269
270    default:
271 //    Dmsg(ctx, dbglvl, "delta-test-fd: unknown event=%d\n", event->eventType);
272       break;
273    }
274    return bRC_OK;
275 }
276
277 static const char *files[] = {
278    "/etc/passwd",
279    "/etc/group",
280    "/etc/hosts",
281    "/etc/services"
282 };
283 static int nb_files = 4;
284
285 /*
286  * Start the backup of a specific file
287  */
288 static bRC startBackupFile(bpContext *ctx, struct save_pkt *sp)
289 {
290    delta_test *self = get_self(ctx);
291    if (!self) {
292       return bRC_Error;
293    }
294    time_t now = time(NULL);
295    sp->fname = (char *)"/delta.txt";
296    sp->type = FT_REG;
297    sp->statp.st_mode = 0700 | S_IFREG;
298    sp->statp.st_ctime = now;
299    sp->statp.st_mtime = now;
300    sp->statp.st_atime = now;
301    sp->statp.st_size = -1;
302    sp->statp.st_blksize = 4096;
303    sp->statp.st_blocks = 1;
304    if (self->level == 'I' || self->level == 'D') {
305       bRC state = bfuncs->checkChanges(ctx, sp);
306       /* Should always be bRC_OK */
307       sp->type = (state == bRC_Seen)? FT_NOCHG : FT_REG;
308       sp->flags |= (FO_DELTA|FO_OFFSETS);
309       self->delta = sp->delta_seq + 1;
310    }
311    pm_strcpy(self->fname, files[self->delta % nb_files]);
312    Dmsg(ctx, dbglvl, "delta-test-fd: delta_seq=%i delta=%i fname=%s\n",
313         sp->delta_seq, self->delta, self->fname);
314 // Dmsg(ctx, dbglvl, "delta-test-fd: startBackupFile\n");
315    return bRC_OK;
316 }
317
318 /*
319  * Done with backup of this file
320  */
321 static bRC endBackupFile(bpContext *ctx)
322 {
323    /*
324     * We would return bRC_More if we wanted startBackupFile to be
325     * called again to backup another file
326     */
327    return bRC_OK;
328 }
329
330
331 /*
332  * Bacula is calling us to do the actual I/O
333  */
334 static bRC pluginIO(bpContext *ctx, struct io_pkt *io)
335 {
336    delta_test *self = get_self(ctx);
337    struct stat statp;
338    if (!self) {
339       return bRC_Error;
340    }
341
342    io->status = 0;
343    io->io_errno = 0;
344    switch(io->func) {
345    case IO_OPEN:
346       Dmsg(ctx, dbglvl, "delta-test-fd: IO_OPEN\n");
347       if (io->flags & (O_CREAT | O_WRONLY)) {
348          /* TODO: if the file already exists, the result is undefined */
349          if (stat(io->fname, &statp) == 0) { /* file exists */
350             self->fd = fopen(io->fname, "r+");
351          } else {
352             self->fd = fopen(io->fname, "w"); /* file doesn't exist,create it */
353          }
354          if (!self->fd) {
355             io->io_errno = errno;
356             Jmsg(ctx, M_FATAL,
357                  "Open failed: ERR=%s\n", strerror(errno));
358             return bRC_Error;
359          }
360
361       } else {
362          self->fd = fopen(self->fname, "r");
363          if (!self->fd) {
364             io->io_errno = errno;
365             Jmsg(ctx, M_FATAL,
366                "Open failed: ERR=%s\n", strerror(errno));
367             return bRC_Error;
368          }
369       }
370       break;
371
372    case IO_READ:
373       if (!self->fd) {
374          Jmsg(ctx, M_FATAL, "Logic error: NULL read FD\n");
375          return bRC_Error;
376       }
377       if (self->done) {
378          io->status = 0;
379       } else {
380          /* first time, read 300, then replace 50-250 by other data */
381          if (self->delta == 0) {
382             io->status = fread(io->buf, 1, 400, self->fd);
383          } else {
384             io->offset = self->delta * 100 / 2; /* chunks are melted */
385             io->status = fread(io->buf, 1, 100, self->fd);
386          }
387          Dmsg(ctx, dbglvl, "delta-test-fd: READ offset=%lld\n", (int64_t)io->offset);
388          self->done = true;
389       }
390       if (io->status == 0 && ferror(self->fd)) {
391          Jmsg(ctx, M_FATAL,
392             "Pipe read error: ERR=%s\n", strerror(errno));
393          Dmsg(ctx, dbglvl,
394             "Pipe read error: ERR=%s\n", strerror(errno));
395          return bRC_Error;
396       }
397       Dmsg(ctx, dbglvl, "offset=%d\n", io->offset);
398       break;
399
400    case IO_WRITE:
401       if (!self->fd) {
402          Jmsg(ctx, M_FATAL, "Logic error: NULL write FD\n");
403          return bRC_Error;
404       }
405       Dmsg(ctx, dbglvl, "delta-test-fd: WRITE count=%lld\n", (int64_t)io->count);
406       io->status = fwrite(io->buf, 1, io->count, self->fd);
407       if (io->status == 0 && ferror(self->fd)) {
408          Jmsg(ctx, M_FATAL,
409             "Pipe write error\n");
410          Dmsg(ctx, dbglvl,
411             "Pipe read error: ERR=%s\n", strerror(errno));
412          return bRC_Error;
413       }
414       break;
415
416    case IO_CLOSE:
417       if (!self->fd) {
418          Jmsg(ctx, M_FATAL, "Logic error: NULL FD on delta close\n");
419          return bRC_Error;
420       }
421       io->status = fclose(self->fd);
422       break;
423
424    case IO_SEEK:
425       if (!self->fd) {
426          Jmsg(ctx, M_FATAL, "Logic error: NULL FD on delta close\n");
427          return bRC_Error;
428       }
429       Dmsg(ctx, dbglvl, "delta-test-fd: SEEK offset=%lld\n", (int64_t)io->offset);
430       io->status = fseek(self->fd, io->offset, io->whence);
431       Dmsg(ctx, dbglvl, "after SEEK=%lld\n", (int64_t)ftell(self->fd));
432       break;
433    }
434    return bRC_OK;
435 }
436
437 /*
438  * Bacula is notifying us that a plugin name string was found, and
439  *   passing us the plugin command, so we can prepare for a restore.
440  */
441 static bRC startRestoreFile(bpContext *ctx, const char *cmd)
442 {
443 // Dmsg(ctx, dbglvl, "delta-test-fd: startRestoreFile cmd=%s\n", cmd);
444    return bRC_OK;
445 }
446
447 /*
448  * Bacula is notifying us that the plugin data has terminated, so
449  *  the restore for this particular file is done.
450  */
451 static bRC endRestoreFile(bpContext *ctx)
452 {
453 // Dmsg(ctx, dbglvl, "delta-test-fd: endRestoreFile\n");
454    return bRC_OK;
455 }
456
457 /*
458  * This is called during restore to create the file (if necessary)
459  * We must return in rp->create_status:
460  *
461  *  CF_ERROR    -- error
462  *  CF_SKIP     -- skip processing this file
463  *  CF_EXTRACT  -- extract the file (i.e.call i/o routines)
464  *  CF_CREATED  -- created, but no content to extract (typically directories)
465  *
466  */
467 static bRC createFile(bpContext *ctx, struct restore_pkt *rp)
468 {
469    delta_test *self = get_self(ctx);
470    pm_strcpy(self->fname, rp->ofname);
471    rp->create_status = CF_EXTRACT;
472    return bRC_OK;
473 }
474
475 /*
476  * We will get here if the File is a directory after everything
477  * is written in the directory.
478  */
479 static bRC setFileAttributes(bpContext *ctx, struct restore_pkt *rp)
480 {
481 // Dmsg(ctx, dbglvl, "delta-test-fd: setFileAttributes\n");
482    return bRC_OK;
483 }
484
485 #ifdef __cplusplus
486 }
487 #endif