/** * @file command_template.c * @author Ambroz Bizjak * * @section LICENSE * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the author nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #define STATE_ADDING_LOCK 1 #define STATE_ADDING 2 #define STATE_ADDING_NEED_DELETE 3 #define STATE_DONE 4 #define STATE_DELETING_LOCK 5 #define STATE_DELETING 6 static void lock_handler (command_template_instance *o); static void process_handler (command_template_instance *o, int normally, uint8_t normally_exit_status); static void free_template (command_template_instance *o, int is_error); static void lock_handler (command_template_instance *o) { ASSERT(o->state == STATE_ADDING_LOCK || o->state == STATE_DELETING_LOCK) ASSERT(!(o->state == STATE_ADDING_LOCK) || o->do_exec) ASSERT(!(o->state == STATE_DELETING_LOCK) || o->undo_exec) if (o->state == STATE_ADDING_LOCK) { // start process if (!BProcess_Init(&o->process, o->i->params->iparams->manager, (BProcess_handler)process_handler, o, o->do_exec, CmdLine_Get(&o->do_cmdline), NULL)) { NCDModuleInst_Backend_Log(o->i, o->blog_channel, BLOG_ERROR, "BProcess_Init failed"); free_template(o, 1); return; } // set state o->state = STATE_ADDING; } else { // start process if (!BProcess_Init(&o->process, o->i->params->iparams->manager, (BProcess_handler)process_handler, o, o->undo_exec, CmdLine_Get(&o->undo_cmdline), NULL)) { NCDModuleInst_Backend_Log(o->i, o->blog_channel, BLOG_ERROR, "BProcess_Init failed"); free_template(o, 1); return; } // set state o->state = STATE_DELETING; } } static void process_handler (command_template_instance *o, int normally, uint8_t normally_exit_status) { ASSERT(o->state == STATE_ADDING || o->state == STATE_ADDING_NEED_DELETE || o->state == STATE_DELETING) // release lock BEventLockJob_Release(&o->elock_job); // free process BProcess_Free(&o->process); if (!normally || normally_exit_status != 0) { NCDModuleInst_Backend_Log(o->i, o->blog_channel, BLOG_ERROR, "command failed"); free_template(o, 1); return; } switch (o->state) { case STATE_ADDING: { // set state o->state = STATE_DONE; // signal up NCDModuleInst_Backend_Up(o->i); } break; case STATE_ADDING_NEED_DELETE: { if (o->undo_exec) { // wait for lock BEventLockJob_Wait(&o->elock_job); // set state o->state = STATE_DELETING_LOCK; } else { free_template(o, 0); return; } } break; case STATE_DELETING: { // finish free_template(o, 0); return; } break; } } void command_template_new (command_template_instance *o, NCDModuleInst *i, const struct NCDModuleInst_new_params *params, command_template_build_cmdline build_cmdline, command_template_free_func free_func, void *user, int blog_channel, BEventLock *elock) { // init arguments o->i = i; o->free_func = free_func; o->user = user; o->blog_channel = blog_channel; // build do command if (!build_cmdline(o->i, params->args, 0, &o->do_exec, &o->do_cmdline)) { NCDModuleInst_Backend_Log(o->i, o->blog_channel, BLOG_ERROR, "build_cmdline do callback failed"); goto fail0; } // build undo command if (!build_cmdline(o->i, params->args, 1, &o->undo_exec, &o->undo_cmdline)) { NCDModuleInst_Backend_Log(o->i, o->blog_channel, BLOG_ERROR, "build_cmdline undo callback failed"); goto fail1; } // init lock job BEventLockJob_Init(&o->elock_job, elock, (BEventLock_handler)lock_handler, o); if (o->do_exec) { // wait for lock BEventLockJob_Wait(&o->elock_job); // set state o->state = STATE_ADDING_LOCK; } else { // set state o->state = STATE_DONE; // signal up NCDModuleInst_Backend_Up(o->i); } return; fail1: if (o->do_exec) { free(o->do_exec); CmdLine_Free(&o->do_cmdline); } fail0: o->free_func(o->user, 1); } static void free_template (command_template_instance *o, int is_error) { // free lock job BEventLockJob_Free(&o->elock_job); // free undo command if (o->undo_exec) { free(o->undo_exec); CmdLine_Free(&o->undo_cmdline); } // free do command if (o->do_exec) { free(o->do_exec); CmdLine_Free(&o->do_cmdline); } // call free function o->free_func(o->user, is_error); } void command_template_die (command_template_instance *o) { ASSERT(o->state == STATE_ADDING_LOCK || o->state == STATE_ADDING || o->state == STATE_DONE) switch (o->state) { case STATE_ADDING_LOCK: { free_template(o, 0); return; } break; case STATE_ADDING: { // set state o->state = STATE_ADDING_NEED_DELETE; } break; case STATE_DONE: { if (o->undo_exec) { // wait for lock BEventLockJob_Wait(&o->elock_job); // set state o->state = STATE_DELETING_LOCK; } else { free_template(o, 0); return; } } break; } }