X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=bacula%2Fsrc%2Fplugins%2Ffd%2Fbpipe-fd.c;h=d6091fce93863fbe248b59391cd03c1bff967f74;hb=7c3df9d20a1d06c21b33c6f03a583aeac0585cf6;hp=98f93c450e192d18b2070ff6d89d813ada4d95aa;hpb=1948e0dd4f8296598c64cc79b471624c766ae5c3;p=bacula%2Fbacula diff --git a/bacula/src/plugins/fd/bpipe-fd.c b/bacula/src/plugins/fd/bpipe-fd.c index 98f93c450e..d6091fce93 100644 --- a/bacula/src/plugins/fd/bpipe-fd.c +++ b/bacula/src/plugins/fd/bpipe-fd.c @@ -1,7 +1,7 @@ /* Bacula® - The Network Backup Solution - Copyright (C) 2007-2008 Free Software Foundation Europe e.V. + Copyright (C) 2007-2010 Free Software Foundation Europe e.V. The main author of Bacula is Kern Sibbald, with contributions from many others, a complete list can be found in the file AUTHORS. @@ -20,32 +20,38 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - Bacula® is a registered trademark of John Walker. + Bacula® is a registered trademark of Kern Sibbald. The licensor of Bacula is the Free Software Foundation Europe (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich, Switzerland, email:ftf@fsfeurope.org. */ /* - * A simple pipe plugin for Bacula + * A simple pipe plugin for the Bacula File Daemon * * Kern Sibbald, October 2007 * */ -#include "fd-plugins.h" +#include "bacula.h" +#include "fd_plugins.h" #undef malloc #undef free #undef strdup +#define fi __FILE__ +#define li __LINE__ + #ifdef __cplusplus extern "C" { #endif -#define PLUGIN_LICENSE "GPL" +static const int dbglvl = 150; + +#define PLUGIN_LICENSE "Bacula GPLv2" #define PLUGIN_AUTHOR "Kern Sibbald" #define PLUGIN_DATE "January 2008" #define PLUGIN_VERSION "1" -#define PLUGIN_DESCRIPTION "Test File Daemon Plugin" +#define PLUGIN_DESCRIPTION "Bacula Pipe File Daemon Plugin" /* Forward referenced functions */ static bRC newPlugin(bpContext *ctx); @@ -61,15 +67,17 @@ static bRC endRestoreFile(bpContext *ctx); static bRC createFile(bpContext *ctx, struct restore_pkt *rp); static bRC setFileAttributes(bpContext *ctx, struct restore_pkt *rp); +static char *apply_rp_codes(struct plugin_ctx * p_ctx); /* Pointers to Bacula functions */ static bFuncs *bfuncs = NULL; static bInfo *binfo = NULL; +/* Plugin Information block */ static pInfo pluginInfo = { sizeof(pluginInfo), - PLUGIN_INTERFACE_VERSION, - PLUGIN_MAGIC, + FD_PLUGIN_INTERFACE_VERSION, + FD_PLUGIN_MAGIC, PLUGIN_LICENSE, PLUGIN_AUTHOR, PLUGIN_DATE, @@ -77,9 +85,10 @@ static pInfo pluginInfo = { PLUGIN_DESCRIPTION, }; +/* Plugin entry points for Bacula */ static pFuncs pluginFuncs = { sizeof(pluginFuncs), - PLUGIN_INTERFACE_VERSION, + FD_PLUGIN_INTERFACE_VERSION, /* Entry points into plugin */ newPlugin, /* new plugin instance */ @@ -96,79 +105,134 @@ static pFuncs pluginFuncs = { setFileAttributes }; +/* + * Plugin private context + */ struct plugin_ctx { boffset_t offset; - FILE *fd; - bool backup; - char *cmd; - char *fname; - char *reader; - char *writer; + FILE *fd; /* pipe file descriptor */ + bool backup; /* set for backup (not needed) */ + char *cmd; /* plugin command line */ + char *fname; /* filename to "backup/restore" */ + char *reader; /* reader program for backup */ + char *writer; /* writer program for backup */ + + char where[512]; + int replace; }; +/* + * loadPlugin() and unloadPlugin() are entry points that are + * exported, so Bacula can directly call these two entry points + * they are common to all Bacula plugins. + */ +/* + * External entry point called by Bacula to "load the plugin + */ bRC loadPlugin(bInfo *lbinfo, bFuncs *lbfuncs, pInfo **pinfo, pFuncs **pfuncs) { bfuncs = lbfuncs; /* set Bacula funct pointers */ binfo = lbinfo; -// printf("bpipe-fd: Loaded: size=%d version=%d\n", bfuncs->size, bfuncs->version); - *pinfo = &pluginInfo; /* return pointer to our info */ *pfuncs = &pluginFuncs; /* return pointer to our functions */ return bRC_OK; } +/* + * External entry point to unload the plugin + */ bRC unloadPlugin() { // printf("bpipe-fd: Unloaded\n"); return bRC_OK; } +/* + * The following entry points are accessed through the function + * pointers we supplied to Bacula. Each plugin type (dir, fd, sd) + * has its own set of entry points that the plugin must define. + */ +/* + * Create a new instance of the plugin i.e. allocate our private storage + */ static bRC newPlugin(bpContext *ctx) { struct plugin_ctx *p_ctx = (struct plugin_ctx *)malloc(sizeof(struct plugin_ctx)); + if (!p_ctx) { + return bRC_Error; + } memset(p_ctx, 0, sizeof(struct plugin_ctx)); ctx->pContext = (void *)p_ctx; /* set our context pointer */ return bRC_OK; } +/* + * Free a plugin instance, i.e. release our private storage + */ static bRC freePlugin(bpContext *ctx) { struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext; + if (!p_ctx) { + return bRC_Error; + } if (p_ctx->cmd) { - free(p_ctx->cmd); + free(p_ctx->cmd); /* free any allocated command string */ } - free(p_ctx); + free(p_ctx); /* free our private context */ + p_ctx = NULL; return bRC_OK; } +/* + * Return some plugin value (none defined) + */ static bRC getPluginValue(bpContext *ctx, pVariable var, void *value) { return bRC_OK; } +/* + * Set a plugin value (none defined) + */ static bRC setPluginValue(bpContext *ctx, pVariable var, void *value) { return bRC_OK; } +/* + * Handle an event that was generated in Bacula + */ static bRC handlePluginEvent(bpContext *ctx, bEvent *event, void *value) { struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext; + if (!p_ctx) { + return bRC_Error; + } + // char *name; + /* + * Most events don't interest us so we ignore them. + * the printfs are so that plugin writers can enable them to see + * what is really going on. + */ switch (event->eventType) { + case bEventPluginCommand: + bfuncs->DebugMessage(ctx, fi, li, dbglvl, + "bpipe-fd: PluginCommand=%s\n", (char *)value); + break; case bEventJobStart: -// printf("bpipe-fd: JobStart=%s\n", (char *)value); + bfuncs->DebugMessage(ctx, fi, li, dbglvl, "bpipe-fd: JobStart=%s\n", (char *)value); break; case bEventJobEnd: // printf("bpipe-fd: JobEnd\n"); break; case bEventStartBackupJob: -// printf("bpipe-fd: BackupStart\n"); +// printf("bpipe-fd: StartBackupJob\n"); break; case bEventEndBackupJob: -// printf("bpipe-fd: BackupEnd\n"); +// printf("bpipe-fd: EndBackupJob\n"); break; case bEventLevel: // printf("bpipe-fd: JobLevel=%c %d\n", (int)value, (int)value); @@ -178,60 +242,65 @@ static bRC handlePluginEvent(bpContext *ctx, bEvent *event, void *value) break; case bEventStartRestoreJob: - printf("bpipe-fd: StartRestoreJob\n"); +// printf("bpipe-fd: StartRestoreJob\n"); break; case bEventEndRestoreJob: - printf("bpipe-fd: EndRestoreJob\n"); +// printf("bpipe-fd: EndRestoreJob\n"); break; /* Plugin command e.g. plugin = ::read command:write command */ case bEventRestoreCommand: - printf("bpipe-fd: EventRestoreCommand cmd=%s\n", (char *)value); +// printf("bpipe-fd: EventRestoreCommand cmd=%s\n", (char *)value); + /* Fall-through wanted */ case bEventBackupCommand: char *p; - printf("bpipe-fd: pluginEvent cmd=%s\n", (char *)value); + bfuncs->DebugMessage(ctx, fi, li, dbglvl, "bpipe-fd: pluginEvent cmd=%s\n", (char *)value); p_ctx->cmd = strdup((char *)value); p = strchr(p_ctx->cmd, ':'); if (!p) { - bfuncs->JobMessage(ctx, __FILE__, __LINE__, M_FATAL, 0, "Plugin terminator not found: %s\n", (char *)value); + bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0, "Plugin terminator not found: %s\n", (char *)value); return bRC_Error; } *p++ = 0; /* terminate plugin */ p_ctx->fname = p; p = strchr(p, ':'); if (!p) { - bfuncs->JobMessage(ctx, __FILE__, __LINE__, M_FATAL, 0, "File terminator not found: %s\n", (char *)value); + bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0, "File terminator not found: %s\n", (char *)value); return bRC_Error; } *p++ = 0; /* terminate file */ p_ctx->reader = p; p = strchr(p, ':'); if (!p) { - bfuncs->JobMessage(ctx, __FILE__, __LINE__, M_FATAL, 0, "Reader terminator not found: %s\n", (char *)value); + bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0, "Reader terminator not found: %s\n", (char *)value); return bRC_Error; } *p++ = 0; /* terminate reader string */ p_ctx->writer = p; - printf("bpipe-fd: plugin=%s fname=%s reader=%s writer=%s\n", - p_ctx->cmd, p_ctx->fname, p_ctx->reader, p_ctx->writer); +// printf("bpipe-fd: plugin=%s fname=%s reader=%s writer=%s\n", +// p_ctx->cmd, p_ctx->fname, p_ctx->reader, p_ctx->writer); break; default: - printf("bpipe-fd: unknown event=%d\n", event->eventType); +// printf("bpipe-fd: unknown event=%d\n", event->eventType); + break; } -// bfuncs->getBaculaValue(ctx, bVarFDName, (void *)&name); -// printf("FD Name=%s\n", name); -// bfuncs->JobMessage(ctx, __FILE__, __LINE__, 1, 0, "JobMesssage message"); -// bfuncs->DebugMessage(ctx, __FILE__, __LINE__, 1, "DebugMesssage message"); return bRC_OK; } +/* + * Start the backup of a specific file + */ static bRC startBackupFile(bpContext *ctx, struct save_pkt *sp) { struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext; + if (!p_ctx) { + return bRC_Error; + } time_t now = time(NULL); sp->fname = p_ctx->fname; + sp->type = FT_REG; sp->statp.st_mode = 0700 | S_IFREG; sp->statp.st_ctime = now; sp->statp.st_mtime = now; @@ -244,44 +313,60 @@ static bRC startBackupFile(bpContext *ctx, struct save_pkt *sp) return bRC_OK; } +/* + * Done with backup of this file + */ static bRC endBackupFile(bpContext *ctx) { /* * We would return bRC_More if we wanted startBackupFile to be * called again to backup another file */ -// printf("bpipe-fd: endBackupFile\n"); return bRC_OK; } + /* - * Do actual I/O + * Bacula is calling us to do the actual I/O */ static bRC pluginIO(bpContext *ctx, struct io_pkt *io) { struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext; + if (!p_ctx) { + return bRC_Error; + } io->status = 0; io->io_errno = 0; switch(io->func) { case IO_OPEN: - printf("bpipe-fd: IO_OPEN\n"); + bfuncs->DebugMessage(ctx, fi, li, dbglvl, "bpipe-fd: IO_OPEN\n"); if (io->flags & (O_CREAT | O_WRONLY)) { - p_ctx->fd = popen(p_ctx->writer, "w"); - printf("bpipe-fd: IO_OPEN writer=%s\n", p_ctx->writer); + char *writer_codes = apply_rp_codes(p_ctx); + + p_ctx->fd = popen(writer_codes, "w"); + bfuncs->DebugMessage(ctx, fi, li, dbglvl, "bpipe-fd: IO_OPEN fd=%d writer=%s\n", + p_ctx->fd, writer_codes); if (!p_ctx->fd) { io->io_errno = errno; - bfuncs->JobMessage(ctx, __FILE__, __LINE__, M_FATAL, 0, - "bpipe-fd: writer=%s failed: ERR=%d\n", p_ctx->writer, errno); + bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0, + "Open pipe writer=%s failed: ERR=%s\n", writer_codes, strerror(errno)); + if (writer_codes) { + free(writer_codes); + } return bRC_Error; } + if (writer_codes) { + free(writer_codes); + } } else { p_ctx->fd = popen(p_ctx->reader, "r"); - printf("bpipe-fd: IO_OPEN reader=%s\n", p_ctx->reader); + bfuncs->DebugMessage(ctx, fi, li, dbglvl, "bpipe-fd: IO_OPEN fd=%p reader=%s\n", + p_ctx->fd, p_ctx->reader); if (!p_ctx->fd) { io->io_errno = errno; - bfuncs->JobMessage(ctx, __FILE__, __LINE__, M_FATAL, 0, - "bpipe-fd: reader=%s failed: ERR=%d\n", p_ctx->reader, errno); + bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0, + "Open pipe reader=%s failed: ERR=%s\n", p_ctx->reader, strerror(errno)); return bRC_Error; } } @@ -290,42 +375,43 @@ static bRC pluginIO(bpContext *ctx, struct io_pkt *io) case IO_READ: if (!p_ctx->fd) { - bfuncs->JobMessage(ctx, __FILE__, __LINE__, M_FATAL, 0, "NULL read FD\n"); + bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0, "Logic error: NULL read FD\n"); return bRC_Error; } io->status = fread(io->buf, 1, io->count, p_ctx->fd); - printf("bpipe-fd: IO_READ buf=%p len=%d\n", io->buf, io->status); +// bfuncs->DebugMessage(ctx, fi, li, dbglvl, "bpipe-fd: IO_READ buf=%p len=%d\n", io->buf, io->status); if (io->status == 0 && ferror(p_ctx->fd)) { - bfuncs->JobMessage(ctx, __FILE__, __LINE__, M_FATAL, 0, "Pipe read error\n"); - printf("Error reading pipe\n"); + bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0, + "Pipe read error: ERR=%s\n", strerror(errno)); + bfuncs->DebugMessage(ctx, fi, li, dbglvl, + "Pipe read error: ERR=%s\n", strerror(errno)); return bRC_Error; } -// printf("status=%d\n", io->status); break; case IO_WRITE: if (!p_ctx->fd) { - bfuncs->JobMessage(ctx, __FILE__, __LINE__, M_FATAL, 0, "NULL write FD\n"); + bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0, "Logic error: NULL write FD\n"); return bRC_Error; } - printf("bpipe-fd: IO_WRITE fd=%p buf=%p len=%d\n", p_ctx->fd, io->buf, io->count); +// printf("bpipe-fd: IO_WRITE fd=%p buf=%p len=%d\n", p_ctx->fd, io->buf, io->count); io->status = fwrite(io->buf, 1, io->count, p_ctx->fd); - printf("bpipe-fd: IO_WRITE buf=%p len=%d\n", io->buf, io->status); +// printf("bpipe-fd: IO_WRITE buf=%p len=%d\n", io->buf, io->status); if (io->status == 0 && ferror(p_ctx->fd)) { - bfuncs->JobMessage(ctx, __FILE__, __LINE__, M_FATAL, 0, "Pipe write error\n"); - printf("Error writing pipe\n"); + bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0, + "Pipe write error\n"); + bfuncs->DebugMessage(ctx, fi, li, dbglvl, + "Pipe read error: ERR=%s\n", strerror(errno)); return bRC_Error; } -// printf("status=%d\n", io->status); break; case IO_CLOSE: if (!p_ctx->fd) { - bfuncs->JobMessage(ctx, __FILE__, __LINE__, M_FATAL, 0, "NULL FD\n"); + bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0, "Logic error: NULL FD on bpipe close\n"); return bRC_Error; } io->status = pclose(p_ctx->fd); - printf("bpipe-fd: IO_CLOSE\n"); break; case IO_SEEK: @@ -335,30 +421,146 @@ static bRC pluginIO(bpContext *ctx, struct io_pkt *io) return bRC_OK; } +/* + * Bacula is notifying us that a plugin name string was found, and + * passing us the plugin command, so we can prepare for a restore. + */ static bRC startRestoreFile(bpContext *ctx, const char *cmd) { - printf("bpipe-fd: startRestoreFile cmd=%s\n", cmd); +// printf("bpipe-fd: startRestoreFile cmd=%s\n", cmd); return bRC_OK; } +/* + * Bacula is notifying us that the plugin data has terminated, so + * the restore for this particular file is done. + */ static bRC endRestoreFile(bpContext *ctx) { - printf("bpipe-fd: endRestoreFile\n"); +// printf("bpipe-fd: endRestoreFile\n"); return bRC_OK; } +/* + * This is called during restore to create the file (if necessary) + * We must return in rp->create_status: + * + * CF_ERROR -- error + * CF_SKIP -- skip processing this file + * CF_EXTRACT -- extract the file (i.e.call i/o routines) + * CF_CREATED -- created, but no content to extract (typically directories) + * + */ static bRC createFile(bpContext *ctx, struct restore_pkt *rp) { - printf("bpipe-fd: createFile\n"); +// printf("bpipe-fd: createFile\n"); + if (strlen(rp->where) > 512) { + printf("Restore target dir too long. Restricting to first 512 bytes.\n"); + } + strncpy(((struct plugin_ctx *)ctx->pContext)->where, rp->where, 513); + ((struct plugin_ctx *)ctx->pContext)->replace = rp->replace; + rp->create_status = CF_EXTRACT; return bRC_OK; } +/* + * We will get here if the File is a directory after everything + * is written in the directory. + */ static bRC setFileAttributes(bpContext *ctx, struct restore_pkt *rp) { - printf("bpipe-fd: setFileAttributes\n"); +// printf("bpipe-fd: setFileAttributes\n"); return bRC_OK; } +/************************************************************************* + * Apply codes in writer command: + * %w -> "where" + * %r -> "replace" + * + * Replace: + * 'always' => 'a', chr(97) + * 'ifnewer' => 'w', chr(119) + * 'ifolder' => 'o', chr(111) + * 'never' => 'n', chr(110) + * + * This function will allocate the required amount of memory with malloc. + * Need to be free()d manually. + * Inspired by edit_job_codes in lib/util.c + */ + +static char *apply_rp_codes(struct plugin_ctx * p_ctx) +{ + char *p, *q; + const char *str; + char add[10]; + int w_count = 0, r_count = 0; + char *omsg; + + char *imsg = p_ctx->writer; + + if (!imsg) { + return NULL; + } + + if ((p = imsg)) { + while ((q = strstr(p, "%w"))) { + w_count++; + p=q+1; + } + + p = imsg; + while ((q = strstr(p, "%r"))) { + r_count++; + p=q+1; + } + } + + /* Required mem: + * len(imsg) + * + number of "where" codes * (len(where)-2) + * - number of "replace" codes + */ + omsg = (char*)malloc(strlen(imsg) + (w_count * (strlen(p_ctx->where)-2)) - r_count + 1); + if (!omsg) { + fprintf(stderr, "Out of memory."); + return NULL; + } + + *omsg = 0; + //printf("apply_rp_codes: %s\n", imsg); + for (p=imsg; *p; p++) { + if (*p == '%') { + switch (*++p) { + case '%': + str = "%"; + break; + case 'w': + str = p_ctx->where; + break; + case 'r': + snprintf(add, 2, "%c", p_ctx->replace); + str = add; + break; + default: + add[0] = '%'; + add[1] = *p; + add[2] = 0; + str = add; + break; + } + } else { + add[0] = *p; + add[1] = 0; + str = add; + } + //printf("add_str %s\n", str); + strcat(omsg, str); + //printf("omsg=%s\n", omsg); + } + return omsg; +} + #ifdef __cplusplus }