/** * Convert task to string. * */ char* task2str(task_type* task, char* buftask) { char* strtime = NULL; char* strtask = NULL; if (task) { strtime = ctime(&task->when); if (strtime) { strtime[strlen(strtime)-1] = '\0'; } if (buftask) { (void)snprintf(buftask, ODS_SE_MAXLINE, "%s %s I will %s zone %s" "\n", task->flush?"Flush":"On", strtime?strtime:"(null)", task_what2str(task->what), task_who2str(task)); return buftask; } else { strtask = (char*) calloc(ODS_SE_MAXLINE, sizeof(char)); snprintf(strtask, ODS_SE_MAXLINE, "%s %s I will %s zone %s\n", task->flush?"Flush":"On", strtime?strtime:"(null)", task_what2str(task->what), task_who2str(task)); return strtask; } } return NULL; }
/** * Log task. * */ void task_log(task_type* task) { char* strtime = NULL; if (task) { strtime = ctime(&task->when); if (strtime) { strtime[strlen(strtime)-1] = '\0'; } ods_log_debug("[%s] %s %s I will %s zone %s", task_str, task->flush?"Flush":"On", strtime?strtime:"(null)", task_what2str(task->what), task_who2str(task)); } return; }
/** * Print task. * */ void task_print(FILE* out, task_type* task) { char* strtime = NULL; if (out && task) { strtime = ctime(&task->when); if (strtime) { strtime[strlen(strtime)-1] = '\0'; } fprintf(out, "%s %s I will %s zone %s\n", task->flush?"Flush":"On", strtime?strtime:"(null)", task_what2str(task->what), task_who2str(task)); } return; }
/** * Perform task. * */ static void worker_perform_task(worker_type* worker) { task_type* task = NULL; if (!worker || !worker->task || !worker->task->context || !worker->engine) { return; } ods_log_assert(worker); ods_log_assert(worker->task); ods_log_assert(worker->task->context); task = (task_type*) worker->task; ods_log_debug("[worker[%i]]: perform task [%s] for %s at %u", worker->thread_num, task_what2str(task->what), task_who2str(task->who), (uint32_t) worker->clock_in); worker->task = task_perform(task); }
/** * Perform task. * */ static void worker_perform_task(worker_type* worker) { task_type* task; if (!worker || !worker->task || !worker->task->context || !worker->engine) { return; } task = (task_type*) worker->task; ods_log_debug("[worker[%i]]: perform task [%s] for %s", worker->thread_num, task_what2str(task->what), task_who2str(task->who)); /* We temporarily assign the database connection to the task so * it is accessable from the task function */ task->dbconn = worker->dbconn; worker->task = task_perform(task); if (worker->task) task->dbconn = NULL; }
/** * Perform task. * */ static void worker_perform_task(worker_type* worker) { engine_type* engine = NULL; zone_type* zone = NULL; task_type* task = NULL; task_id what = TASK_NONE; time_t when = 0; time_t never = (3600*24*365); ods_status status = ODS_STATUS_OK; int backup = 0; time_t start = 0; time_t end = 0; if (!worker || !worker->task || !worker->task->zone || !worker->engine) { return; } engine = (engine_type*) worker->engine; task = (task_type*) worker->task; zone = (zone_type*) worker->task->zone; ods_log_debug("[%s[%i]] perform task %s for zone %s at %u", worker2str(worker->type), worker->thread_num, task_what2str(task->what), task_who2str(task), (uint32_t) worker->clock_in); /* do what you have been told to do */ switch (task->what) { case TASK_SIGNCONF: /* perform 'load signconf' task */ worker_working_with(worker, TASK_SIGNCONF, TASK_READ, "configure", task_who2str(task), &what, &when); status = tools_signconf(zone); if (status == ODS_STATUS_UNCHANGED) { if (!zone->signconf->last_modified) { ods_log_debug("[%s[%i]] no signconf.xml for zone %s yet", worker2str(worker->type), worker->thread_num, task_who2str(task)); status = ODS_STATUS_ERR; } } if (status == ODS_STATUS_UNCHANGED) { if (task->halted != TASK_NONE && task->halted != TASK_SIGNCONF) { goto task_perform_continue; } status = ODS_STATUS_OK; } else if (status == ODS_STATUS_OK) { task->interrupt = TASK_NONE; task->halted = TASK_NONE; } else { if (task->halted == TASK_NONE) { goto task_perform_fail; } goto task_perform_continue; } /* break; */ case TASK_READ: /* perform 'read input adapter' task */ worker_working_with(worker, TASK_READ, TASK_SIGN, "read", task_who2str(task), &what, &when); task->what = TASK_READ; if (!zone->signconf->last_modified) { ods_log_debug("[%s[%i]] no signconf.xml for zone %s yet", worker2str(worker->type), worker->thread_num, task_who2str(task)); status = ODS_STATUS_ERR; } else { lhsm_check_connection((void*)engine); status = tools_input(zone); } if (status == ODS_STATUS_UNCHANGED) { ods_log_verbose("[%s[%i]] zone %s unsigned data not changed, " "continue", worker2str(worker->type), worker->thread_num, task_who2str(task)); status = ODS_STATUS_OK; } if (status == ODS_STATUS_OK) { if (task->interrupt > TASK_SIGNCONF) { task->interrupt = TASK_NONE; task->halted = TASK_NONE; } } else { if (task->halted == TASK_NONE) { goto task_perform_fail; } goto task_perform_continue; } /* break; */ case TASK_SIGN: /* perform 'sign' task */ worker_working_with(worker, TASK_SIGN, TASK_WRITE, "sign", task_who2str(task), &what, &when); task->what = TASK_SIGN; status = zone_update_serial(zone); if (status == ODS_STATUS_OK) { if (task->interrupt > TASK_SIGNCONF) { task->interrupt = TASK_NONE; task->halted = TASK_NONE; } } else { ods_log_error("[%s[%i]] unable to sign zone %s: " "failed to increment serial", worker2str(worker->type), worker->thread_num, task_who2str(task)); if (task->halted == TASK_NONE) { goto task_perform_fail; } goto task_perform_continue; } /* start timer */ start = time(NULL); if (zone->stats) { lock_basic_lock(&zone->stats->stats_lock); if (!zone->stats->start_time) { zone->stats->start_time = start; } zone->stats->sig_count = 0; zone->stats->sig_soa_count = 0; zone->stats->sig_reuse = 0; zone->stats->sig_time = 0; lock_basic_unlock(&zone->stats->stats_lock); } /* check the HSM connection before queuing sign operations */ lhsm_check_connection((void*)engine); /* queue menial, hard signing work */ worker_queue_zone(worker, engine->signq, zone); ods_log_deeebug("[%s[%i]] wait until drudgers are finished " "signing zone %s", worker2str(worker->type), worker->thread_num, task_who2str(task)); /* sleep until work is done */ worker_sleep_unless(worker, 0); /* stop timer */ end = time(NULL); status = worker_check_jobs(worker, task); worker_clear_jobs(worker); if (status == ODS_STATUS_OK && zone->stats) { lock_basic_lock(&zone->stats->stats_lock); zone->stats->sig_time = (end-start); lock_basic_unlock(&zone->stats->stats_lock); } if (status != ODS_STATUS_OK) { if (task->halted == TASK_NONE) { goto task_perform_fail; } goto task_perform_continue; } else { if (task->interrupt > TASK_SIGNCONF) { task->interrupt = TASK_NONE; task->halted = TASK_NONE; } } /* break; */ case TASK_WRITE: /* perform 'write to output adapter' task */ worker_working_with(worker, TASK_WRITE, TASK_SIGN, "write", task_who2str(task), &what, &when); task->what = TASK_WRITE; status = tools_output(zone, engine); if (status == ODS_STATUS_OK) { if (task->interrupt > TASK_SIGNCONF) { task->interrupt = TASK_NONE; task->halted = TASK_NONE; } } else { /* clear signatures? */ if (task->halted == TASK_NONE) { goto task_perform_fail; } goto task_perform_continue; } zone->db->is_processed = 1; if (zone->signconf && duration2time(zone->signconf->sig_resign_interval)) { what = TASK_SIGN; when = worker->clock_in + duration2time(zone->signconf->sig_resign_interval); } else { ods_log_error("[%s[%i]] unable to retrieve resign interval " "for zone %s: duration2time() failed", worker2str(worker->type), worker->thread_num, task_who2str(task)); ods_log_info("[%s[%i]] defaulting to 1H resign interval for " "zone %s", worker2str(worker->type), worker->thread_num, task_who2str(task)); what = TASK_SIGN; when = worker->clock_in + 3600; } backup = 1; break; case TASK_NONE: worker->working_with = TASK_NONE; /* no task */ ods_log_warning("[%s[%i]] none task for zone %s", worker2str(worker->type), worker->thread_num, task_who2str(task)); when = time_now() + never; break; default: worker->working_with = TASK_NONE; /* unknown task */ ods_log_warning("[%s[%i]] unknown task, trying full sign zone %s", worker2str(worker->type), worker->thread_num, task_who2str(task)); what = TASK_SIGNCONF; when = time_now(); break; } /* no error */ task->backoff = 0; if (task->interrupt != TASK_NONE && task->interrupt != what) { ods_log_debug("[%s[%i]] interrupt task %s for zone %s", worker2str(worker->type), worker->thread_num, task_what2str(what), task_who2str(task)); task->halted = what; task->halted_when = when; task->what = task->interrupt; task->when = time_now(); } else { ods_log_debug("[%s[%i]] next task %s for zone %s", worker2str(worker->type), worker->thread_num, task_what2str(what), task_who2str(task)); task->what = what; task->when = when; task->interrupt = TASK_NONE; task->halted = TASK_NONE; task->halted_when = 0; } /* backup the last successful run */ if (backup) { status = zone_backup2(zone); if (status != ODS_STATUS_OK) { ods_log_warning("[%s[%i]] unable to backup zone %s: %s", worker2str(worker->type), worker->thread_num, task_who2str(task), ods_status2str(status)); /* just a warning */ status = ODS_STATUS_OK; } backup = 0; } return; task_perform_fail: if (status != ODS_STATUS_XFR_NOT_READY) { /* other statuses is critical, and we know it is not ODS_STATUS_OK */ ods_log_crit("[%s[%i]] CRITICAL: failed to sign zone %s: %s", worker2str(worker->type), worker->thread_num, task_who2str(task), ods_status2str(status)); } /* in case of failure, also mark zone processed (for single run usage) */ zone->db->is_processed = 1; if (task->backoff) { task->backoff *= 2; } else { task->backoff = 60; } if (task->backoff > ODS_SE_MAX_BACKOFF) { task->backoff = ODS_SE_MAX_BACKOFF; } ods_log_info("[%s[%i]] backoff task %s for zone %s with %u seconds", worker2str(worker->type), worker->thread_num, task_what2str(task->what), task_who2str(task), task->backoff); task->when = time_now() + task->backoff; return; task_perform_continue: ods_log_info("[%s[%i]] continue task %s for zone %s", worker2str(worker->type), worker->thread_num, task_what2str(task->halted), task_who2str(task)); task->what = task->halted; task->when = task->halted_when; task->interrupt = TASK_NONE; task->halted = TASK_NONE; task->halted_when = 0; return; }
ods_status schedule_task(schedule_type* schedule, task_type* task) { ldns_rbnode_t *node1, *node2; ods_status status; task_type* task2; if (!task) { ods_log_error("[%s] unable to schedule task: no task", schedule_str); return ODS_STATUS_ERR; } task->flush = 0; if (!schedule || !schedule->tasks) { ods_log_error("[%s] unable to schedule task: no schedule", schedule_str); return ODS_STATUS_ERR; } ods_log_debug("[%s] schedule task [%s] for %s", schedule_str, task_what2str(task->what), task_who2str(task->who)); pthread_mutex_lock(&schedule->schedule_lock); status = ODS_STATUS_ERR; if ((node1 = task2node(task))) { if (ldns_rbtree_insert(schedule->tasks_by_name, node1)) { if ((node2 = task2node(task))) { if(ldns_rbtree_insert(schedule->tasks, node2)) { /* success inserting in two trees */ set_alarm(schedule); status = ODS_STATUS_OK; } else { /* insert in tasks tree failed */ ods_log_error("[%s] unable to schedule task [%s] for %s: " " already present", schedule_str, task_what2str(task->what), task_who2str(task->who)); /* this will free node1 */ free(ldns_rbtree_delete(schedule->tasks_by_name, node1)); free(node2); } } else { /* could not alloc node2 */ /* this will free node1 */ free(ldns_rbtree_delete(schedule->tasks_by_name, node1)); } } else {/* insert in name tree failed */ free(node1); /** * Task is already in tasks_by_name queue, so we must * update it in tasks queue */ /* still in lock guaranteed to succeed. */ node1 = ldns_rbtree_search(schedule->tasks_by_name, task); /* This copy of 'task' is referenced by both trees */ task2 = (task_type*)node1->key; node1 = ldns_rbtree_delete(schedule->tasks, task2); if (task->when < task2->when) task2->when = task->when; if (task2->context && task2->clean_context) { task2->clean_context(task2); } task2->context = task->context; task2->clean_context = task->clean_context; task->context = NULL; task_cleanup(task); (void) ldns_rbtree_insert(schedule->tasks, node1); /* node1 now owned by tree */ node1 = NULL; set_alarm(schedule); status = ODS_STATUS_OK; } } /* else {failure) */ pthread_mutex_unlock(&schedule->schedule_lock); return status; }