From: Radosław Korzeniewski Date: Wed, 23 Jun 2021 16:03:18 +0000 (+0200) Subject: metaplugin: Add a proper job cancel handling. X-Git-Tag: Release-11.3.2~447 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4e7bd08dd3cf54de7f8af22ca97d6c1516fd6c4d;p=thirdparty%2Fbacula.git metaplugin: Add a proper job cancel handling. With this patch you can receive information about a cancel event as a SIGUSR1 signal. Now the backend can intercept this signal to handle clean and finish procedures. After that for about 5 secs, which is the default but you can customize it using CUSTOMCANCELSLEEP compile-in variable, the backend receive a termination - bpipe communication will be closed and SIGTERM sent. It is assumed that after receiving SIGUSR1 no standard metaplugin protocol communication is expected and supported. In this case the metaplugin return bRC_Error on every plugin entrypoint except freePlugin(). --- diff --git a/bacula/src/plugins/fd/pluginlib/metaplugin.cpp b/bacula/src/plugins/fd/pluginlib/metaplugin.cpp index 01cf8a7e2..57903333b 100644 --- a/bacula/src/plugins/fd/pluginlib/metaplugin.cpp +++ b/bacula/src/plugins/fd/pluginlib/metaplugin.cpp @@ -27,6 +27,7 @@ */ #include "metaplugin.h" +#include "smartlock.h" #include #include #include @@ -112,6 +113,15 @@ static pInfo pluginInfo = { PLUGIN_DESCRIPTION, }; +#define _STR(x) __STR(x) +#define __STR(x) #x + +#ifdef VERSIONGIT + #define VERSIONGIT_STR _STR(VERSIONGIT) +#else + #define VERSIONGIT_STR "/unknown" +#endif + /* * Plugin called here when it is first loaded */ @@ -120,7 +130,7 @@ bRC DLL_IMP_EXP loadPlugin(bInfo *lbinfo, bFuncs *lbfuncs, pInfo ** pinfo, pFunc bfuncs = lbfuncs; /* set Bacula function pointers */ binfo = lbinfo; - Dmsg3(DINFO, "%s Plugin version %s %s (c) 2020 by Inteos\n", PLUGINNAME, PLUGIN_VERSION, PLUGIN_DATE); + Dmsg4(DINFO, "%s Plugin version %s%s %s (c) 2021 by Inteos\n", PLUGINNAME, PLUGIN_VERSION, VERSIONGIT_STR, PLUGIN_DATE); *pinfo = &pluginInfo; /* return pointer to our info */ *pfuncs = &pluginFuncs; /* return pointer to our functions */ @@ -140,68 +150,6 @@ bRC DLL_IMP_EXP unloadPlugin() } #endif -/* - * Main PLUGIN class constructor. - */ -METAPLUGIN::METAPLUGIN(bpContext *bpctx) : - backend_cmd(PM_FNAME), - ctx(NULL), - backend_available(false), - backend_error(PM_MESSAGE), - mode(NONE), - JobId(0), - JobName(NULL), - since(0), - where(NULL), - regexwhere(NULL), - replace(0), - robjsent(false), - estimate(false), - listing(None), - nodata(false), - nextfile(false), - openerror(false), - pluginobject(false), - pluginobjectsent(false), - readacl(false), - readxattr(false), - skipextract(false), - last_type(0), - fname(PM_FNAME), - lname(PM_FNAME), - robjbuf(NULL), - plugin_obj_cat(PM_FNAME), - plugin_obj_type(PM_FNAME), - plugin_obj_name(PM_FNAME), - plugin_obj_src(PM_FNAME), - plugin_obj_uuid(PM_FNAME), - plugin_obj_size(PM_FNAME), - acldatalen(0), - acldata(PM_MESSAGE), - xattrdatalen(0), - xattrdata(PM_MESSAGE), - metadatas_list(10, true), - prevjobname(NULL) -{ - /* TODO: we have a ctx variable stored internally, decide if we use it - * for every method or rip it off as not required in our code */ - ctx = bpctx; -} - -/* - * Main PLUGIN class destructor, handles variable freeing on delete. - * - * in: - * none - * out: - * freed internal variables and class allocated during job execution - */ -METAPLUGIN::~METAPLUGIN() -{ - /* free standard variables */ - free_and_null_pool_memory(robjbuf); -} - /* * Check if a parameter (param) exist in ConfigFile variables set by user. * The checking ignore case of the parameter. @@ -594,7 +542,7 @@ bRC send_endjob(bpContext *ctx, PTCOMM *ptcomm) */ bRC backendctx_jobend_func(PTCOMM *ptcomm, void *cp) { - bpContext * ctx = (bpContext*)cp; + bpContext *ctx = (bpContext *)cp; bRC status = bRC_OK; if (send_endjob(ctx, ptcomm) != bRC_OK){ @@ -624,6 +572,46 @@ bRC METAPLUGIN::terminate_all_backends(bpContext *ctx) return backend.foreach_command_status(backendctx_jobend_func, ctx); } +/** + * @brief Callback used for sending a `cancel event` to the selected backend + * + * @param ptcomm the backend communication object + * @param cp a bpContext - for Bacula debug and jobinfo messages + * @return bRC bRC_OK when success + */ +bRC backendctx_cancel_func(PTCOMM *ptcomm, void *cp) +{ + bpContext * ctx = (bpContext*)cp; + + // cancel procedure + // 1. get backend pid + // 2. send SIGUSR1 to backend pid + // 3. wait default 5 sec or defined in CUSTOMCANCELSLEEP + // 4. terminate the backend as usual + + pid_t pid = ptcomm->get_backend_pid(); + DMSG(ctx, DINFO, "Inform backend about Cancel at PID=%d ...\n", pid) + kill(pid, SIGUSR1); + int32_t waitsleep = (CUSTOMCANCELSLEEP == 0) * 5 + CUSTOMCANCELSLEEP; + bmicrosleep(waitsleep, 1); + DMSG(ctx, DINFO, "Terminate backend at PID=%d\n", pid); + ptcomm->terminate(ctx); + return bRC_OK; +} + +/** + * @brief Send `cancel event` to every backend and terminate it. + * + * @param ctx bpContext - for Bacula debug and jobinfo messages + * @return bRC bRC_OK when success, bRC_Error if not + */ +bRC METAPLUGIN::cancel_all_backends(bpContext *ctx) +{ + METAPLUGIN *pctx = (METAPLUGIN *)ctx->pContext; + // the cancel procedure: for all backends execute cancel func + return pctx->backend.foreach_command_status(backendctx_cancel_func, ctx); +} + /* * Send a "Job Info" protocol procedure parameters. * @@ -788,6 +776,7 @@ bRC METAPLUGIN::send_parameters(bpContext *ctx, char *command) "regress_error_backup_abort", "regress_metadata_support", "regress_standard_error_backup", + "regress_cancel_backup", NULL, }; #endif @@ -1172,7 +1161,15 @@ bRC METAPLUGIN::prepare_backend(bpContext *ctx, char type, char *command) bRC METAPLUGIN::handlePluginEvent(bpContext *ctx, bEvent *event, void *value) { bRC status; - POOL_MEM tmp; + + // extract original plugin context, basically it should be `this` + METAPLUGIN *pctx = (METAPLUGIN *)ctx->pContext; + // this ensures that handlePluginEvent is thread safe for extracted pContext + smart_lock lg(&pctx->mutex); + + if (job_cancelled) { + return bRC_Error; + } switch (event->eventType) { @@ -1186,10 +1183,8 @@ bRC METAPLUGIN::handlePluginEvent(bpContext *ctx, bEvent *event, void *value) break; case bEventJobEnd: - status = bRC_OK; DMSG(ctx, D3, "bEventJobEnd value=%s\n", NPRT((char *)value)); - status = terminate_all_backends(ctx); - return status; + return terminate_all_backends(ctx); case bEventLevel: char lvl; @@ -1297,20 +1292,12 @@ bRC METAPLUGIN::handlePluginEvent(bpContext *ctx, bEvent *event, void *value) return parse_plugin_restoreobj(ctx, (restore_object_pkt *) value); case bEventCancelCommand: - DMSG(ctx, D3, "bEventCancelCommand value=%s\n", NPRT((char *)value)); - /* - TODO: The code can set a flag (better if protected via a global mutex), and we - TODO: can send a kill USR1 or TERM signal to the backend if the variable is - TODO: protected with the global mutex and available easily. - TODO: PETITION: Our plugin (RHV WhiteBearSolutions) search the packet E CANCEL. - TODO: If you modify this behaviour, please you notify us. - */ - if (backend.ctx != NULL) { - // XXX: something is going different then designed here, as backend.ctx is NULL - bsscanf("CANCEL", "%s", tmp.c_str()); - backend.ctx->signal_error(ctx, tmp.c_str()); - } - break; + DMSG2(ctx, D3, "bEventCancelCommand self = %p pctx = %p\n", this, pctx); + // TODO: PETITION: Our plugin (RHV WhiteBearSolutions) search the packet E CANCEL. + // TODO: If you modify this behaviour, please you notify us. + // TODO: RPK[20210623]: The information about a new procedure was sent to Eric + pctx->job_cancelled = true; + return cancel_all_backends(ctx); default: // enabled only for Debug @@ -1346,7 +1333,7 @@ bRC METAPLUGIN::perform_read_data(bpContext *ctx, struct io_pkt *io) } io->status = rc; if (backend.ctx->is_eod()){ - /* TODO: we signal EOD as rc=0, so no need to explicity check for EOD, right? */ + // TODO: we signal EOD as rc=0, so no need to explicity check for EOD, right? io->status = 0; } return bRC_OK; @@ -1588,7 +1575,7 @@ bRC METAPLUGIN::perform_read_metadata_info(bpContext *ctx, metadata_type type, s * @param cmd a command string read from backend * @return metadata_type returned from map */ -metadata_type METAPLUGIN::scan_metadata_type(const POOL_MEM &cmd) +metadata_type METAPLUGIN::scan_metadata_type(bpContext *ctx, const POOL_MEM &cmd) { DMSG1(ctx, DDEBUG, "scan_metadata_type checking: %s\n", cmd.c_str()); for (int i = 0; plugin_metadata_map[i].command != NULL; i++) @@ -1877,6 +1864,14 @@ bRC METAPLUGIN::pluginIO(bpContext *ctx, struct io_pkt *io) { static int rw = 0; // this variable handles single debug message + { + // synchronie access to job_cancelled variable + smart_lock lg(&mutex); + if (job_cancelled) { + return bRC_Error; + } + } + /* assume no error from the very beginning */ io->status = 0; io->io_errno = 0; @@ -1988,19 +1983,21 @@ bRC METAPLUGIN::startBackupFile(bpContext *ctx, struct save_pkt *sp) int32_t nfi; int reqparams = 2; + { + // synchronie access to job_cancelled variable + smart_lock lg(&mutex); + if (job_cancelled) { + return bRC_Error; + } + } + /* The first file in Full backup, is the RestoreObject */ if (!estimate && mode == BACKUP_FULL && robjsent == false) { ConfigFile ini; - - /* robj for the first time, allocate the buffer */ - if (!robjbuf){ - robjbuf = get_pool_memory(PM_FNAME); - } - ini.register_items(plugin_items_dump, sizeof(struct ini_items)); sp->restore_obj.object_name = (char *)INI_RESTORE_OBJECT_NAME; - sp->restore_obj.object_len = ini.serialize(&robjbuf); - sp->restore_obj.object = robjbuf; + sp->restore_obj.object_len = ini.serialize(robjbuf.c_str()); + sp->restore_obj.object = robjbuf.c_str(); sp->type = FT_PLUGIN_CONFIG; DMSG0(ctx, DINFO, "Prepared RestoreObject sent.\n"); return bRC_OK; @@ -2115,7 +2112,7 @@ bRC METAPLUGIN::startBackupFile(bpContext *ctx, struct save_pkt *sp) } continue; } - metadata_type mtype = scan_metadata_type(cmd); + metadata_type mtype = scan_metadata_type(ctx, cmd); if (mtype != plugin_meta_invalid) { DMSG1(ctx, DDEBUG, "metaData handling: %d\n", mtype); if (perform_read_metadata_info(ctx, mtype, sp) != bRC_OK) { @@ -2179,6 +2176,14 @@ bRC METAPLUGIN::endBackupFile(bpContext *ctx) { POOL_MEM cmd(PM_FNAME); + { + // synchronie access to job_cancelled variable + smart_lock lg(&mutex); + if (job_cancelled) { + return bRC_Error; + } + } + if (!estimate){ /* The current file was the restore object, so just ask for the next file */ if (mode == BACKUP_FULL && robjsent == false) { @@ -2242,6 +2247,14 @@ bRC METAPLUGIN::createFile(bpContext *ctx, struct restore_pkt *rp) POOL_MEM cmd(PM_FNAME); char type; + { + // synchronie access to job_cancelled variable + smart_lock lg(&mutex); + if (job_cancelled) { + return bRC_Error; + } + } + skipextract = false; acldatalen = 0; xattrdatalen = 0; @@ -2364,6 +2377,14 @@ void METAPLUGIN::setup_backend_command(bpContext *ctx, POOL_MEM &exepath) */ bRC METAPLUGIN::handleXACLdata(bpContext *ctx, struct xacl_pkt *xacl) { + { + // synchronie access to job_cancelled variable + smart_lock lg(&mutex); + if (job_cancelled) { + return bRC_Error; + } + } + switch (xacl->func) { case BACL_BACKUP: @@ -2435,6 +2456,14 @@ bRC METAPLUGIN::queryParameter(bpContext *ctx, struct query_pkt *qp) return bRC_OK; } + { + // synchronie access to job_cancelled variable + smart_lock lg(&mutex); + if (job_cancelled) { + return bRC_Error; + } + } + if (listing == None) { listing = Query; Mmsg(cmd, "%s query=%s", qp->command, qp->parameter); @@ -2499,14 +2528,8 @@ bRC METAPLUGIN::queryParameter(bpContext *ctx, struct query_pkt *qp) if (values.size() > 1){ ow.end_list(); } - - /* allocate working buffer, we use robjbuf variable for that as it will be freed at dtor */ - if (!robjbuf){ - robjbuf = get_pool_memory(PM_MESSAGE); - } - - pm_strcpy(&robjbuf, ow.get_output(OT_END)); - qp->result = robjbuf; + pm_strcpy(robjbuf, ow.get_output(OT_END)); + qp->result = robjbuf.c_str(); } return ret; @@ -2521,6 +2544,14 @@ bRC METAPLUGIN::queryParameter(bpContext *ctx, struct query_pkt *qp) */ bRC METAPLUGIN::metadataRestore(bpContext *ctx, struct meta_pkt *mp) { + { + // synchronie access to job_cancelled variable + smart_lock lg(&mutex); + if (job_cancelled) { + return bRC_Error; + } + } + if (!skipextract){ POOL_MEM cmd(PM_FNAME); @@ -2576,8 +2607,12 @@ bRC METAPLUGIN::checkFile(bpContext * ctx, char *fname) { if ((!CUSTOMNAMESPACE && isourpluginfname(PLUGINPREFIX, fname)) || (CUSTOMNAMESPACE && isourpluginfname(PLUGINNAMESPACE, fname))) { - if (::checkFile != NULL){ - return ::checkFile(ctx, fname); + // synchronie access to job_cancelled variable + smart_lock lg(&mutex); + if (!job_cancelled) { + if (::checkFile != NULL) { + return ::checkFile(ctx, fname); + } } return bRC_Seen; } @@ -2596,13 +2631,15 @@ static bRC newPlugin(bpContext *ctx) { int JobId; char *exepath; - METAPLUGIN *self = New(METAPLUGIN(ctx)); + METAPLUGIN *self = New(METAPLUGIN); POOL_MEM exepath_clean(PM_FNAME); if (!self) return bRC_Error; ctx->pContext = (void*) self; + pthread_t mythid = pthread_self(); + DMSG2(ctx, DVDEBUG, "pContext = %p thid = %p\n", self, mythid); /* setup the backend command */ getBaculaVar(bVarExePath, (void *)&exepath); @@ -2669,9 +2706,9 @@ static bRC setPluginValue(bpContext *ctx, pVariable var, void *value) static bRC handlePluginEvent(bpContext *ctx, bEvent *event, void *value) { ASSERT_CTX; - - DMSG(ctx, D1, "handlePluginEvent (%i)\n", event->eventType); + pthread_t mythid = pthread_self(); METAPLUGIN *self = pluginclass(ctx); + DMSG3(ctx, D1, "handlePluginEvent (%i) pContext = %p thid = %p\n", event->eventType, self, mythid); return self->handlePluginEvent(ctx, event, value); } @@ -2685,8 +2722,9 @@ static bRC handlePluginEvent(bpContext *ctx, bEvent *event, void *value) static bRC startBackupFile(bpContext *ctx, struct save_pkt *sp) { ASSERT_CTX; - if (!sp) + if (!sp) { return bRC_Error; + } DMSG0(ctx, D1, "startBackupFile.\n"); METAPLUGIN *self = pluginclass(ctx); diff --git a/bacula/src/plugins/fd/pluginlib/metaplugin.h b/bacula/src/plugins/fd/pluginlib/metaplugin.h index d75e32b6c..842446b86 100644 --- a/bacula/src/plugins/fd/pluginlib/metaplugin.h +++ b/bacula/src/plugins/fd/pluginlib/metaplugin.h @@ -28,6 +28,7 @@ #include "pluginlib.h" #include "ptcomm.h" +#include "smartmutex.h" #include "lib/ini.h" #include "pluginlib/commctx.h" #include "pluginlib/smartalist.h" @@ -59,13 +60,14 @@ extern const char *PLUGIN_VERSION; extern const char *PLUGIN_DESCRIPTION; // Plugin linking time variables -extern const char *PLUGINPREFIX; /// is used for prefixing every Job and Debug messages generted by a plugin -extern const char *PLUGINNAME; /// should match the backend $pluginname$ used for Handshake procedure -extern const bool CUSTOMNAMESPACE; /// defines if metaplugin should send `Namespace=...` backend plugin parameter using PLUGINNAMESPACE variable -extern const bool CUSTOMPREVJOBNAME; /// defines if metaplugin should send `PrevJobName=...` backend plugin parameter from bacula variable -extern const char *PLUGINNAMESPACE; /// custom backend plugin namespace used as file name prefix -extern const char *PLUGINAPI; -extern const char *BACKEND_CMD; +extern const char *PLUGINPREFIX; /// is used for prefixing every Job and Debug messages generted by a plugin +extern const char *PLUGINNAME; /// should match the backend $pluginname$ used for Handshake procedure +extern const bool CUSTOMNAMESPACE; /// defines if metaplugin should send `Namespace=...` backend plugin parameter using PLUGINNAMESPACE variable +extern const bool CUSTOMPREVJOBNAME; /// defines if metaplugin should send `PrevJobName=...` backend plugin parameter from bacula variable +extern const char *PLUGINNAMESPACE; /// custom backend plugin namespace used as file name prefix +extern const char *PLUGINAPI; /// the plugin api string which should match backend expectations +extern const char *BACKEND_CMD; /// a backend execution command path +extern const int32_t CUSTOMCANCELSLEEP; /// custom wait time for backend between USR1 and terminate procedures /// defines if metaplugin should handle local filesystem restore with Bacula Core functions /// `false` means metaplugin will redirect local restore to backend @@ -123,13 +125,51 @@ public: bRC queryParameter(bpContext *ctx, struct query_pkt *qp); bRC metadataRestore(bpContext *ctx, struct meta_pkt *mp); void setup_backend_command(bpContext *ctx, POOL_MEM &exepath); - METAPLUGIN(bpContext *bpctx); + METAPLUGIN() : + backend_cmd(PM_FNAME), + job_cancelled(false), + backend_available(false), + backend_error(PM_MESSAGE), + mode(NONE), + JobId(0), + JobName(NULL), + since(0), + where(NULL), + regexwhere(NULL), + replace(0), + robjsent(false), + estimate(false), + listing(None), + nodata(false), + nextfile(false), + openerror(false), + pluginobject(false), + pluginobjectsent(false), + readacl(false), + readxattr(false), + skipextract(false), + last_type(0), + fname(PM_FNAME), + lname(PM_FNAME), + robjbuf(PM_MESSAGE), + plugin_obj_cat(PM_FNAME), + plugin_obj_type(PM_FNAME), + plugin_obj_name(PM_FNAME), + plugin_obj_src(PM_FNAME), + plugin_obj_uuid(PM_FNAME), + plugin_obj_size(PM_FNAME), + acldatalen(0), + acldata(PM_MESSAGE), + xattrdatalen(0), + xattrdata(PM_MESSAGE), + metadatas_list(10, true), + prevjobname(NULL) + {} #if __cplusplus > 201103L - METAPLUGIN() = delete; METAPLUGIN(METAPLUGIN&) = delete; METAPLUGIN(METAPLUGIN&&) = delete; #endif - ~METAPLUGIN(); + ~METAPLUGIN() {} private: enum LISTING @@ -139,8 +179,8 @@ private: Query, }; - // TODO: define a variable which will signal job cancel - bpContext *ctx; // Bacula Plugin Context + bool job_cancelled; // it signal the metaplugin that job was cancelled + smart_mutex mutex; // mutex to synchronize data access bool backend_available; // When `False` then backend program is unuseable or unavailable POOL_MEM backend_error; // Holds the error string when backend program is unavailable MODE mode; // Plugin mode of operation @@ -165,7 +205,7 @@ private: COMMCTX backend; // the backend context list for multiple backend execution for a single job POOL_MEM fname; // current file name to backup (grabbed from backend) POOL_MEM lname; // current LSTAT data if any - POOLMEM *robjbuf; // the buffer for restore object data + POOL_MEM robjbuf; // the buffer for restore object data POOL_MEM plugin_obj_cat; // Plugin object Category POOL_MEM plugin_obj_type; // Plugin object Type POOL_MEM plugin_obj_name; // Plugin object Name @@ -210,7 +250,7 @@ private: bRC perform_read_metadata_info(bpContext *ctx, metadata_type type, struct save_pkt *sp); bRC perform_file_index_query(bpContext *ctx); // bRC perform_write_metadata_info(bpContext *ctx, struct meta_pkt *mp); - metadata_type scan_metadata_type(const POOL_MEM &cmd); + metadata_type scan_metadata_type(bpContext *ctx, const POOL_MEM &cmd); const char *prepare_metadata_type(metadata_type type); int check_ini_param(char *param); bool check_plugin_param(const char *param, alist *params); @@ -219,6 +259,7 @@ private: bRC switch_or_run_backend(bpContext *ctx, char *command); bRC terminate_current_backend(bpContext *ctx); bRC terminate_all_backends(bpContext *ctx); + bRC cancel_all_backends(bpContext *ctx); bRC signal_finish_all_backends(bpContext *ctx); bRC render_param(bpContext *ctx, POOLMEM *param, INI_ITEM_HANDLER *handler, char *key, item_value val); }; diff --git a/bacula/src/plugins/fd/pluginlib/ptcomm.h b/bacula/src/plugins/fd/pluginlib/ptcomm.h index c869f46e7..2eb28175f 100644 --- a/bacula/src/plugins/fd/pluginlib/ptcomm.h +++ b/bacula/src/plugins/fd/pluginlib/ptcomm.h @@ -193,7 +193,7 @@ public: * * @return int backend PID - when backend available; -1 - when backend is unavailable */ - inline int get_backend_pid() { return bpipe != NULL ? bpipe->worker_pid : -1; } + inline pid_t get_backend_pid() { return bpipe != NULL ? bpipe->worker_pid : -1; } /** * @brief Sets a BPIPE object for our main communication channel. diff --git a/bacula/src/plugins/fd/pluginlib/test_metaplugin_backend.c b/bacula/src/plugins/fd/pluginlib/test_metaplugin_backend.c index 8894df3d6..616a542a8 100644 --- a/bacula/src/plugins/fd/pluginlib/test_metaplugin_backend.c +++ b/bacula/src/plugins/fd/pluginlib/test_metaplugin_backend.c @@ -34,12 +34,21 @@ #include #include #include +#include #ifndef LOGDIR #define LOGDIR "/tmp" #endif +#define EXIT_BACKEND_NOMEMORY 255 +#define EXIT_BACKEND_LOGFILE_ERROR 1 +#define EXIT_BACKEND_HEADER_TOOSHORT 2 +#define EXIT_BACKEND_MESSAGE_TOOLONG 3 +#define EXIT_BACKEND_DATA_COMMAND_REQ 4 +#define EXIT_BACKEND_SIGNAL_HANDLER_ERROR 5 +#define EXIT_BACKEND_CANCEL 6 + extern const char *PLUGINPREFIX; extern const char *PLUGINNAME; @@ -60,6 +69,7 @@ bool regress_error_restore_stderr = false; bool regress_backup_other_file = false; bool regress_metadata_support = false; bool regress_standard_error_backup = false; +bool regress_cancel_backup = false; #define BUFLEN 4096 @@ -108,7 +118,7 @@ int read_plugin(char * buf) if (len < 8){ LOG("#> Err: header too short"); close(logfd); - exit(2); + exit(EXIT_BACKEND_HEADER_TOOSHORT); } if (header[0] == 'F'){ LOG(">> EOD >>"); @@ -117,13 +127,13 @@ int read_plugin(char * buf) if (header[0] == 'T'){ LOG(">> TERM >>"); close(logfd); - exit(0); + exit(EXIT_SUCCESS); } size = atoi(header+1); if (size > BIGBUFLEN){ LOG("#> Err: message too long"); close(logfd); - exit(3); + exit(EXIT_BACKEND_MESSAGE_TOOLONG); } if (header[0] == 'C'){ @@ -235,6 +245,14 @@ void signal_term(){ LOG("<< TERM <<"); } +static bool jobcancelled = false; + +static void catch_function(int signo) +{ + LOG("#CANCELLED#"); + jobcancelled = true; +} + /** * @brief Perform test backup. */ @@ -254,6 +272,13 @@ void perform_backup() snprintf(fileindex_link, 256, "%s/bucket/%d/vm1.iso", PLUGINPREFIX, mypid); // Backup Loop + if (regress_error_backup_no_files) { + write_plugin('E', "No files found for pattern container1/otherobject\n"); + signal_eod(); + return; + } + + // first file snprintf(buf, BIGBUFLEN, "FNAME:%s\n", fileindex_link); // we use it here write_plugin('C', buf); write_plugin('C', "STAT:F 1048576 100 100 100640 2\n"); // this will be the first file hardlinked @@ -275,10 +300,13 @@ void perform_backup() write_plugin('I', "TEST5Acl"); signal_eod(); - if (regress_error_backup_no_files) { - write_plugin('E', "No files found for pattern container1/otherobject\n"); - signal_eod(); - return; + if (regress_cancel_backup) + { + LOG("#Cancel wait started..."); + while (!jobcancelled) + sleep(1); + LOG("#Cancel event received, EXIT"); + exit(EXIT_BACKEND_CANCEL); } // next file @@ -666,6 +694,9 @@ void perform_backup() signal_eod(); } +/** + * @brief Perform test estimate + */ void perform_estimate(){ /* Estimate Loop (5) */ snprintf(buf, BIGBUFLEN, "FNAME:%s/bucket/%d/vm1.iso\n", PLUGINPREFIX, mypid); @@ -924,7 +955,7 @@ void perform_restore() write_plugin('C', "OK\n"); } else { write_plugin('E', "Error DATA command required."); - exit (1); + exit(EXIT_BACKEND_DATA_COMMAND_REQ); } } @@ -943,26 +974,26 @@ int main(int argc, char** argv) { buf = (char*)malloc(BIGBUFLEN); if (buf == NULL){ - exit(255); + exit(EXIT_BACKEND_NOMEMORY); } buflog = (char*)malloc(BUFLEN); if (buflog == NULL){ - exit(255); + exit(EXIT_BACKEND_NOMEMORY); } listing = (char*)malloc(BUFLEN); if (listing == NULL){ - exit(255); + exit(EXIT_BACKEND_NOMEMORY); } query = (char*)malloc(BUFLEN); if (query == NULL){ - exit(255); + exit(EXIT_BACKEND_NOMEMORY); } mypid = getpid(); snprintf(buf, 4096, "%s/%s_backend_%d.log", LOGDIR, PLUGINNAME, mypid); logfd = open(buf, O_CREAT|O_TRUNC|O_WRONLY, 0640); if (logfd < 0){ - exit (1); + exit(EXIT_BACKEND_LOGFILE_ERROR); } //sleep(30); @@ -993,54 +1024,64 @@ int main(int argc, char** argv) { // "regress_backup_plugin_objects", // "regress_error_backup_abort", // "regress_standard_error_backup", - if (strcmp(buf, "regress_error_plugin_params=1\n") == 0){ + // "regress_cancel_backup", + + if (strcmp(buf, "regress_error_plugin_params=1\n") == 0) { regress_error_plugin_params = true; continue; } - if (strcmp(buf, "regress_error_start_job=1\n") == 0){ + if (strcmp(buf, "regress_error_start_job=1\n") == 0) { regress_error_start_job = true; continue; } - if (strcmp(buf, "regress_error_backup_no_files=1\n") == 0){ + if (strcmp(buf, "regress_error_backup_no_files=1\n") == 0) { regress_error_backup_no_files = true; continue; } - if (strcmp(buf, "regress_error_backup_stderr=1\n") == 0){ + if (strcmp(buf, "regress_error_backup_stderr=1\n") == 0) { regress_error_backup_stderr = true; continue; } - if (strcmp(buf, "regress_error_estimate_stderr=1\n") == 0){ + if (strcmp(buf, "regress_error_estimate_stderr=1\n") == 0) { regress_error_estimate_stderr = true; continue; } - if (strcmp(buf, "regress_error_listing_stderr=1\n") == 0){ + if (strcmp(buf, "regress_error_listing_stderr=1\n") == 0) { regress_error_listing_stderr = true; continue; } - if (strcmp(buf, "regress_error_restore_stderr=1\n") == 0){ + if (strcmp(buf, "regress_error_restore_stderr=1\n") == 0) { regress_error_restore_stderr = true; continue; } - if (strcmp(buf, "regress_backup_plugin_objects=1\n") == 0){ + if (strcmp(buf, "regress_backup_plugin_objects=1\n") == 0) { regress_backup_plugin_objects = true; continue; } - if (strcmp(buf, "regress_error_backup_abort=1\n") == 0){ + if (strcmp(buf, "regress_error_backup_abort=1\n") == 0) { regress_error_backup_abort = true; continue; } - if (strcmp(buf, "regress_backup_other_file=1\n") == 0){ + if (strcmp(buf, "regress_backup_other_file=1\n") == 0) { regress_backup_other_file = true; continue; } - if (strcmp(buf, "regress_metadata_support=1\n") == 0){ + if (strcmp(buf, "regress_metadata_support=1\n") == 0) { regress_metadata_support = true; continue; } - if (strcmp(buf, "regress_standard_error_backup=1\n") == 0){ + if (strcmp(buf, "regress_standard_error_backup=1\n") == 0) { regress_standard_error_backup = true; continue; } + if (strcmp(buf, "regress_cancel_backup=1\n") == 0) { + regress_cancel_backup = true; + if (signal(SIGUSR1, catch_function) == SIG_ERR){ + LOG("Cannot setup signal handler!"); + exit(EXIT_BACKEND_SIGNAL_HANDLER_ERROR); + } + continue; + } if (sscanf(buf, "listing=%s\n", buf) == 1){ strcpy(listing, buf); continue;