/* * Purge records from database * * Purge Files (from) [Job|JobId|Client|Volume] * Purge Jobs (from) [Client|Volume] * * N.B. Not all above is implemented yet. */ int purgecmd(UAContext *ua, const char *cmd) { int i; CLIENTRES *client; MEDIA_DBR mr; JOB_DBR jr; static const char *keywords[] = { NT_("files"), NT_("jobs"), NT_("volume"), NT_("quota"), NULL }; static const char *files_keywords[] = { NT_("Job"), NT_("JobId"), NT_("Client"), NT_("Volume"), NULL }; static const char *quota_keywords[] = { NT_("Client"), NULL }; static const char *jobs_keywords[] = { NT_("Client"), NT_("Volume"), NULL }; ua->warning_msg(_( "\nThis command can be DANGEROUS!!!\n\n" "It purges (deletes) all Files from a Job,\n" "JobId, Client or Volume; or it purges (deletes)\n" "all Jobs from a Client or Volume without regard\n" "to retention periods. Normally you should use the\n" "PRUNE command, which respects retention periods.\n")); if (!open_db(ua)) { return 1; } switch (find_arg_keyword(ua, keywords)) { /* Files */ case 0: switch(find_arg_keyword(ua, files_keywords)) { case 0: /* Job */ case 1: /* JobId */ if (get_job_dbr(ua, &jr)) { char jobid[50]; edit_int64(jr.JobId, jobid); purge_files_from_jobs(ua, jobid); } return 1; case 2: /* client */ client = get_client_resource(ua); if (client) { purge_files_from_client(ua, client); } return 1; case 3: /* Volume */ if (select_media_dbr(ua, &mr)) { purge_files_from_volume(ua, &mr); } return 1; } /* Jobs */ case 1: switch(find_arg_keyword(ua, jobs_keywords)) { case 0: /* client */ client = get_client_resource(ua); if (client) { purge_jobs_from_client(ua, client); } return 1; case 1: /* Volume */ if (select_media_dbr(ua, &mr)) { purge_jobs_from_volume(ua, &mr, /*force*/true); } return 1; } /* Volume */ case 2: /* Perform ActionOnPurge (action=truncate) */ if (find_arg(ua, "action") >= 0) { return action_on_purge_cmd(ua, ua->cmd); } while ((i=find_arg(ua, NT_("volume"))) >= 0) { if (select_media_dbr(ua, &mr)) { purge_jobs_from_volume(ua, &mr, /*force*/true); } *ua->argk[i] = 0; /* zap keyword already seen */ ua->send_msg("\n"); } return 1; /* Quota */ case 3: switch(find_arg_keyword(ua, quota_keywords)) { case 0: /* client */ client = get_client_resource(ua); if (client) { purge_quota_from_client(ua, client); } return 1; } default: break; } switch (do_keyword_prompt(ua, _("Choose item to purge"), keywords)) { case 0: /* files */ client = get_client_resource(ua); if (client) { purge_files_from_client(ua, client); } break; case 1: /* jobs */ client = get_client_resource(ua); if (client) { purge_jobs_from_client(ua, client); } break; case 2: /* Volume */ if (select_media_dbr(ua, &mr)) { purge_jobs_from_volume(ua, &mr, /*force*/true); } break; case 3: client = get_client_resource(ua); /* Quota */ if (client) { purge_quota_from_client(ua, client); } } return 1; }
/* * Items needed: * mr.PoolId must be set * mr.StorageId should also be set * mr.ScratchPoolId could be set (used if create==true) * jcr->wstore * jcr->db * jcr->pool * MEDIA_DBR mr with PoolId set * create -- whether or not to create a new volume */ int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int index, bool create, bool prune) { int retry = 0; bool ok; bool InChanger; STORE *store = jcr->wstore; bstrncpy(mr->MediaType, store->media_type, sizeof(mr->MediaType)); Dmsg3(100, "find_next_vol_for_append: JobId=%u PoolId=%d, MediaType=%s\n", (uint32_t)jcr->JobId, (int)mr->PoolId, mr->MediaType); /* * If we are using an Autochanger, restrict Volume * search to the Autochanger on the first pass */ InChanger = store->autochanger; /* * Find the Next Volume for Append */ db_lock(jcr->db); for ( ;; ) { bstrncpy(mr->VolStatus, "Append", sizeof(mr->VolStatus)); /* want only appendable volumes */ /* * 1. Look for volume with "Append" status. */ ok = db_find_next_volume(jcr, jcr->db, index, InChanger, mr); if (!ok) { Dmsg4(150, "after find_next_vol ok=%d index=%d InChanger=%d Vstat=%s\n", ok, index, InChanger, mr->VolStatus); /* * 2. Try finding a recycled volume */ ok = find_recycled_volume(jcr, InChanger, mr); Dmsg2(150, "find_recycled_volume ok=%d FW=%d\n", ok, mr->FirstWritten); if (!ok) { /* * 3. Try recycling any purged volume */ ok = recycle_oldest_purged_volume(jcr, InChanger, mr); if (!ok) { /* * 4. Try pruning Volumes */ if (prune) { Dmsg0(150, "Call prune_volumes\n"); prune_volumes(jcr, InChanger, mr); } ok = recycle_oldest_purged_volume(jcr, InChanger, mr); if (!ok && create) { Dmsg4(150, "after prune volumes_vol ok=%d index=%d InChanger=%d Vstat=%s\n", ok, index, InChanger, mr->VolStatus); /* * 5. Try pulling a volume from the Scratch pool */ ok = get_scratch_volume(jcr, InChanger, mr); Dmsg4(150, "after get scratch volume ok=%d index=%d InChanger=%d Vstat=%s\n", ok, index, InChanger, mr->VolStatus); } /* * If we are using an Autochanger and have not found * a volume, retry looking for any volume. */ if (!ok && InChanger) { InChanger = false; continue; /* retry again accepting any volume */ } } } if (!ok && create) { /* * 6. Try "creating" a new Volume */ ok = newVolume(jcr, mr); } /* * Look at more drastic ways to find an Appendable Volume */ if (!ok && (jcr->pool->purge_oldest_volume || jcr->pool->recycle_oldest_volume)) { Dmsg2(200, "No next volume found. PurgeOldest=%d\n RecyleOldest=%d", jcr->pool->purge_oldest_volume, jcr->pool->recycle_oldest_volume); /* Find oldest volume to recycle */ ok = db_find_next_volume(jcr, jcr->db, -1, InChanger, mr); Dmsg1(200, "Find oldest=%d Volume\n", ok); if (ok && prune) { UAContext *ua; Dmsg0(200, "Try purge Volume.\n"); /* * 7. Try to purging oldest volume only if not UA calling us. */ ua = new_ua_context(jcr); if (jcr->pool->purge_oldest_volume && create) { Jmsg(jcr, M_INFO, 0, _("Purging oldest volume \"%s\"\n"), mr->VolumeName); ok = purge_jobs_from_volume(ua, mr); /* * 8. or try recycling the oldest volume */ } else if (jcr->pool->recycle_oldest_volume) { Jmsg(jcr, M_INFO, 0, _("Pruning oldest volume \"%s\"\n"), mr->VolumeName); ok = prune_volume(ua, mr); } free_ua_context(ua); if (ok) { ok = recycle_volume(jcr, mr); Dmsg1(400, "Recycle after purge oldest=%d\n", ok); } } } } Dmsg2(100, "VolJobs=%d FirstWritten=%d\n", mr->VolJobs, mr->FirstWritten); if (ok) { /* If we can use the volume, check if it is expired */ if (has_volume_expired(jcr, mr)) { if (retry++ < 200) { /* sanity check */ continue; /* try again from the top */ } else { Jmsg(jcr, M_ERROR, 0, _( "We seem to be looping trying to find the next volume. I give up.\n")); } } } break; } /* end for loop */ db_unlock(jcr->db); Dmsg1(150, "return ok=%d find_next_vol\n", ok); return ok; }