/* * Create a Job Control Record and link it into JCR chain * Returns newly allocated JCR * Note, since each daemon has a different JCR, he passes * us the size. */ JCR *new_jcr(int size, JCR_free_HANDLER *daemon_free_jcr) { JCR *jcr; MQUEUE_ITEM *item = NULL; struct sigaction sigtimer; int status; Dmsg0(dbglvl, "Enter new_jcr\n"); status = pthread_once(&key_once, create_jcr_key); if (status != 0) { berrno be; Jmsg1(NULL, M_ABORT, 0, _("pthread_once failed. ERR=%s\n"), be.bstrerror(status)); } jcr = (JCR *)malloc(size); memset(jcr, 0, size); jcr->my_thread_id = pthread_self(); jcr->msg_queue = New(dlist(item, &item->link)); jcr->job_end_push.init(1, false); jcr->sched_time = time(NULL); jcr->daemon_free_jcr = daemon_free_jcr; /* plug daemon free routine */ jcr->init_mutex(); jcr->inc_use_count(); jcr->VolumeName = get_pool_memory(PM_FNAME); jcr->VolumeName[0] = 0; jcr->errmsg = get_pool_memory(PM_MESSAGE); jcr->errmsg[0] = 0; /* Setup some dummy values */ bstrncpy(jcr->Job, "*System*", sizeof(jcr->Job)); jcr->JobId = 0; jcr->set_JobType(JT_SYSTEM); /* internal job until defined */ jcr->set_JobLevel(L_NONE); set_jcr_job_status(jcr, JS_Created); /* ready to run */ set_jcr_in_tsd(jcr); sigtimer.sa_flags = 0; sigtimer.sa_handler = timeout_handler; sigfillset(&sigtimer.sa_mask); sigaction(TIMEOUT_SIGNAL, &sigtimer, NULL); /* * Locking jobs is a global lock that is needed * so that the Director can stop new jobs from being * added to the jcr chain while it processes a new * conf file and does the job_end_push(). */ lock_jobs(); lock_jcr_chain(); if (!jcrs) { jcrs = New(dlist(jcr, &jcr->link)); } jcrs->append(jcr); unlock_jcr_chain(); unlock_jobs(); return jcr; }
/* * Return number of Jobs */ int job_count() { JCR *jcr; int count = 0; lock_jcr_chain(); for (jcr = (JCR *)jcrs->first(); (jcr = (JCR *)jcrs->next(jcr)); ) { if (jcr->JobId > 0) { count++; } } unlock_jcr_chain(); return count; }
/* * Start walk of jcr chain * The proper way to walk the jcr chain is: * JCR *jcr; * foreach_jcr(jcr) { * ... * } * endeach_jcr(jcr); * * It is possible to leave out the endeach_jcr(jcr), but * in that case, the last jcr referenced must be explicitly * released with: * * free_jcr(jcr); */ JCR *jcr_walk_start() { JCR *jcr; lock_jcr_chain(); jcr = (JCR *)jcrs->first(); if (jcr) { jcr->inc_use_count(); if (jcr->JobId > 0) { Dmsg3(dbglvl, "Inc walk_start jid=%u use_count=%d Job=%s\n", jcr->JobId, jcr->use_count(), jcr->Job); } } unlock_jcr_chain(); return jcr; }
/* * Get next jcr from chain, and release current one */ JCR *jcr_walk_next(JCR *prev_jcr) { JCR *jcr; lock_jcr_chain(); jcr = (JCR *)jcrs->next(prev_jcr); if (jcr) { jcr->inc_use_count(); if (jcr->JobId > 0) { Dmsg3(dbglvl, "Inc walk_next jid=%u use_count=%d Job=%s\n", jcr->JobId, jcr->use_count(), jcr->Job); } } unlock_jcr_chain(); if (prev_jcr) { free_jcr(prev_jcr); } return jcr; }
void b_free_jcr(const char *file, int line, JCR *jcr) { struct s_last_job *je; Dmsg3(dbglvl, "Enter free_jcr jid=%u from %s:%d\n", jcr->JobId, file, line); #else void free_jcr(JCR *jcr) { struct s_last_job *je; Dmsg3(dbglvl, "Enter free_jcr jid=%u use_count=%d Job=%s\n", jcr->JobId, jcr->use_count(), jcr->Job); #endif lock_jcr_chain(); jcr->dec_use_count(); /* decrement use count */ if (jcr->use_count() < 0) { Jmsg2(jcr, M_ERROR, 0, _("JCR use_count=%d JobId=%d\n"), jcr->use_count(), jcr->JobId); } if (jcr->JobId > 0) { Dmsg3(dbglvl, "Dec free_jcr jid=%u use_count=%d Job=%s\n", jcr->JobId, jcr->use_count(), jcr->Job); } if (jcr->use_count() > 0) { /* if in use */ unlock_jcr_chain(); return; } if (jcr->JobId > 0) { Dmsg3(dbglvl, "remove jcr jid=%u use_count=%d Job=%s\n", jcr->JobId, jcr->use_count(), jcr->Job); } remove_jcr(jcr); /* remove Jcr from chain */ unlock_jcr_chain(); dequeue_messages(jcr); job_end_pop(jcr); /* pop and call hooked routines */ Dmsg1(dbglvl, "End job=%d\n", jcr->JobId); /* * Keep some statistics */ switch (jcr->getJobType()) { case JT_BACKUP: case JT_VERIFY: case JT_RESTORE: case JT_MIGRATE: case JT_COPY: case JT_ADMIN: /* * Keep list of last jobs, but not Console where JobId==0 */ if (jcr->JobId > 0) { lock_last_jobs_list(); num_jobs_run++; je = (struct s_last_job *)malloc(sizeof(struct s_last_job)); memset(je, 0, sizeof(struct s_last_job)); /* zero in case unset fields */ je->Errors = jcr->JobErrors; je->JobType = jcr->getJobType(); je->JobId = jcr->JobId; je->VolSessionId = jcr->VolSessionId; je->VolSessionTime = jcr->VolSessionTime; bstrncpy(je->Job, jcr->Job, sizeof(je->Job)); je->JobFiles = jcr->JobFiles; je->JobBytes = jcr->JobBytes; je->JobStatus = jcr->JobStatus; je->JobLevel = jcr->getJobLevel(); je->start_time = jcr->start_time; je->end_time = time(NULL); if (!last_jobs) { init_last_jobs_list(); } last_jobs->append(je); if (last_jobs->size() > max_last_jobs) { je = (struct s_last_job *)last_jobs->first(); last_jobs->remove(je); free(je); } unlock_last_jobs_list(); } break; default: break; } close_msg(jcr); /* close messages for this job */ if (jcr->daemon_free_jcr) { jcr->daemon_free_jcr(jcr); /* call daemon free routine */ } free_common_jcr(jcr); close_msg(NULL); /* flush any daemon messages */ Dmsg0(dbglvl, "Exit free_jcr\n"); } /* * Remove jcr from thread specific data, but * but make sure it is us who are attached. */ void remove_jcr_from_tsd(JCR *jcr) { JCR *tjcr = get_jcr_from_tsd(); if (tjcr == jcr) { set_jcr_in_tsd(INVALID_JCR); } }