/* * IF volume status is Append, Full, Used, or Error, mark it Purged * Purged volumes can then be recycled (if enabled). */ bool mark_media_purged(UAContext *ua, MEDIA_DBR *mr) { JCR *jcr = ua->jcr; bool status; status = bstrcmp(mr->VolStatus, "Append") || bstrcmp(mr->VolStatus, "Full") || bstrcmp(mr->VolStatus, "Used") || bstrcmp(mr->VolStatus, "Error"); if (status) { bstrncpy(mr->VolStatus, "Purged", sizeof(mr->VolStatus)); set_storageid_in_mr(NULL, mr); if (!db_update_media_record(jcr, ua->db, mr)) { return false; } pm_strcpy(jcr->VolumeName, mr->VolumeName); generate_plugin_event(jcr, bDirEventVolumePurged); /* * If the RecyclePool is defined, move the volume there */ if (mr->RecyclePoolId && mr->RecyclePoolId != mr->PoolId) { POOL_DBR oldpr, newpr; memset(&oldpr, 0, sizeof(POOL_DBR)); memset(&newpr, 0, sizeof(POOL_DBR)); newpr.PoolId = mr->RecyclePoolId; oldpr.PoolId = mr->PoolId; if ( db_get_pool_record(jcr, ua->db, &oldpr) && db_get_pool_record(jcr, ua->db, &newpr)) { /* check if destination pool size is ok */ if (newpr.MaxVols > 0 && newpr.NumVols >= newpr.MaxVols) { ua->error_msg(_("Unable move recycled Volume in full " "Pool \"%s\" MaxVols=%d\n"), newpr.Name, newpr.MaxVols); } else { /* move media */ update_vol_pool(ua, newpr.Name, mr, &oldpr); } } else { ua->error_msg("%s", db_strerror(ua->db)); } } /* Send message to Job report, if it is a *real* job */ if (jcr && jcr->JobId > 0) { Jmsg(jcr, M_INFO, 0, _("All records pruned from Volume \"%s\"; marking it \"Purged\"\n"), mr->VolumeName); } return true; } else { ua->error_msg(_("Cannot purge Volume with VolStatus=%s\n"), mr->VolStatus); } return bstrcmp(mr->VolStatus, "Purged"); }
static void update_all_vols(UAContext *ua) { int i, num_pools; uint32_t *ids; POOL_DBR pr; MEDIA_DBR mr; memset(&pr, 0, sizeof(pr)); memset(&mr, 0, sizeof(mr)); if (!db_get_pool_ids(ua->jcr, ua->db, &num_pools, &ids)) { ua->error_msg(_("Error obtaining pool ids. ERR=%s\n"), db_strerror(ua->db)); return; } for (i=0; i<num_pools; i++) { pr.PoolId = ids[i]; if (!db_get_pool_record(ua->jcr, ua->db, &pr)) { /* ***FIXME*** use acl? */ ua->warning_msg(_("Updating all pools, but skipped PoolId=%d. ERR=%s\n"), db_strerror(ua->db)); continue; } set_pool_dbr_defaults_in_media_dbr(&mr, &pr); mr.PoolId = pr.PoolId; if (!db_update_media_defaults(ua->jcr, ua->db, &mr)) { ua->error_msg(_("Error updating Volume records: ERR=%s"), db_strerror(ua->db)); } else { ua->info_msg(_("All Volume defaults updated from \"%s\" Pool record.\n"), pr.Name); } } free(ids); }
static void update_volslot(UAContext *ua, char *val, MEDIA_DBR *mr) { POOL_DBR pr; memset(&pr, 0, sizeof(POOL_DBR)); pr.PoolId = mr->PoolId; if (!db_get_pool_record(ua->jcr, ua->db, &pr)) { ua->error_msg("%s", db_strerror(ua->db)); return; } mr->Slot = atoi(val); if (pr.MaxVols > 0 && mr->Slot > (int)pr.MaxVols) { ua->error_msg(_("Invalid slot, it must be between 0 and MaxVols=%d\n"), pr.MaxVols); return; } /* * Make sure to use db_update... rather than doing this directly, * so that any Slot is handled correctly. */ if (!db_update_media_record(ua->jcr, ua->db, mr)) { ua->error_msg(_("Error updating media record Slot: ERR=%s"), db_strerror(ua->db)); } else { ua->info_msg(_("New Slot is: %d\n"), mr->Slot); } }
/* * Automatic Volume name creation using the LabelFormat * * The media record must have the PoolId filled in when * calling this routine. */ bool newVolume(JCR *jcr, MEDIA_DBR *mr) { POOL_DBR pr; memset(&pr, 0, sizeof(pr)); /* See if we can create a new Volume */ db_lock(jcr->db); pr.PoolId = mr->PoolId; if (!db_get_pool_record(jcr, jcr->db, &pr)) { goto bail_out; } if (pr.MaxVols == 0 || pr.NumVols < pr.MaxVols) { memset(mr, 0, sizeof(MEDIA_DBR)); set_pool_dbr_defaults_in_media_dbr(mr, &pr); jcr->VolumeName[0] = 0; bstrncpy(mr->MediaType, jcr->wstore->media_type, sizeof(mr->MediaType)); generate_job_event(jcr, "NewVolume"); /* return bool */ generate_plugin_event(jcr, bEventNewVolume); /* return void... */ if (jcr->VolumeName[0] && is_volume_name_legal(NULL, jcr->VolumeName)) { bstrncpy(mr->VolumeName, jcr->VolumeName, sizeof(mr->VolumeName)); /* Check for special characters */ } else if (pr.LabelFormat[0] && pr.LabelFormat[0] != '*') { if (is_volume_name_legal(NULL, pr.LabelFormat)) { /* No special characters, so apply simple algorithm */ if (!create_simple_name(jcr, mr, &pr)) { goto bail_out; } } else { /* try full substitution */ /* Found special characters, so try substitution */ if (!perform_full_name_substitution(jcr, mr, &pr)) { goto bail_out; } if (!is_volume_name_legal(NULL, mr->VolumeName)) { Jmsg(jcr, M_ERROR, 0, _("Illegal character in Volume name \"%s\"\n"), mr->VolumeName); goto bail_out; } } } else { goto bail_out; } pr.NumVols++; mr->Enabled = 1; if (db_create_media_record(jcr, jcr->db, mr) && db_update_pool_record(jcr, jcr->db, &pr)) { db_unlock(jcr->db); Jmsg(jcr, M_INFO, 0, _("Created new Volume \"%s\" in catalog.\n"), mr->VolumeName); Dmsg1(90, "Created new Volume=%s\n", mr->VolumeName); return true; } else { Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db)); } } bail_out: db_unlock(jcr->db); return false; }
/* * Fill in the remaining fields of the jcr as if it * is going to run the job. */ bool complete_jcr_for_job(JCR *jcr, JOB *job, POOL *pool) { POOL_DBR pr; memset(&pr, 0, sizeof(POOL_DBR)); set_jcr_defaults(jcr, job); if (pool) { jcr->pool = pool; /* override */ } if (jcr->db) { Dmsg0(100, "complete_jcr close db\n"); db_close_database(jcr, jcr->db); jcr->db = NULL; } Dmsg0(100, "complete_jcr open db\n"); jcr->db = db_init_database(jcr, jcr->catalog->db_driver, jcr->catalog->db_name, jcr->catalog->db_user, jcr->catalog->db_password, jcr->catalog->db_address, jcr->catalog->db_port, jcr->catalog->db_socket, jcr->catalog->mult_db_connections, jcr->catalog->disable_batch_insert); if (!jcr->db || !db_open_database(jcr, jcr->db)) { Jmsg(jcr, M_FATAL, 0, _("Could not open database \"%s\".\n"), jcr->catalog->db_name); if (jcr->db) { Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db)); db_close_database(jcr, jcr->db); jcr->db = NULL; } return false; } bstrncpy(pr.Name, jcr->pool->name(), sizeof(pr.Name)); while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */ /* Try to create the pool */ if (create_pool(jcr, jcr->db, jcr->pool, POOL_OP_CREATE) < 0) { Jmsg(jcr, M_FATAL, 0, _("Pool %s not in database. %s"), pr.Name, db_strerror(jcr->db)); if (jcr->db) { db_close_database(jcr, jcr->db); jcr->db = NULL; } return false; } else { Jmsg(jcr, M_INFO, 0, _("Pool %s created in database.\n"), pr.Name); } } jcr->jr.PoolId = pr.PoolId; return true; }
/* * Refresh the Volume information from the Pool record */ static void update_vol_from_pool(UAContext *ua, MEDIA_DBR *mr) { POOL_DBR pr; memset(&pr, 0, sizeof(pr)); pr.PoolId = mr->PoolId; if (!db_get_pool_record(ua->jcr, ua->db, &pr) || !acl_access_ok(ua, Pool_ACL, pr.Name)) { return; } set_pool_dbr_defaults_in_media_dbr(mr, &pr); if (!db_update_media_defaults(ua->jcr, ua->db, mr)) { ua->error_msg(_("Error updating Volume record: ERR=%s"), db_strerror(ua->db)); } else { ua->info_msg(_("Volume defaults updated from \"%s\" Pool record.\n"), pr.Name); } }
/* * Get or create a Pool record with the given name. * Returns: 0 on error * poolid if OK */ DBId_t get_or_create_pool_record(JCR *jcr, char *pool_name) { POOL_DBR pr; memset(&pr, 0, sizeof(pr)); bstrncpy(pr.Name, pool_name, sizeof(pr.Name)); Dmsg1(110, "get_or_create_pool=%s\n", pool_name); while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */ /* Try to create the pool */ if (create_pool(jcr, jcr->db, jcr->res.pool, POOL_OP_CREATE) < 0) { Jmsg(jcr, M_FATAL, 0, _("Pool \"%s\" not in database. ERR=%s"), pr.Name, db_strerror(jcr->db)); return 0; } else { Jmsg(jcr, M_INFO, 0, _("Created database record for Pool \"%s\".\n"), pr.Name); } } return pr.PoolId; }
void catalog_request(JCR *jcr, BSOCK *bs) { MEDIA_DBR mr, sdmr; JOBMEDIA_DBR jm; char Job[MAX_NAME_LENGTH]; char pool_name[MAX_NAME_LENGTH]; int index, ok, label, writing; POOLMEM *omsg; POOL_DBR pr; uint32_t Stripe, Copy; uint64_t MediaId; utime_t VolFirstWritten; utime_t VolLastWritten; memset(&sdmr, 0, sizeof(sdmr)); memset(&jm, 0, sizeof(jm)); Dsm_check(100); /* * Request to find next appendable Volume for this Job */ Dmsg1(100, "catreq %s", bs->msg); if (!jcr->db) { omsg = get_memory(bs->msglen+1); pm_strcpy(omsg, bs->msg); bs->fsend(_("1990 Invalid Catalog Request: %s"), omsg); Jmsg1(jcr, M_FATAL, 0, _("Invalid Catalog request; DB not open: %s"), omsg); free_memory(omsg); return; } /* * Find next appendable medium for SD */ if (sscanf(bs->msg, Find_media, &Job, &index, &pool_name, &mr.MediaType) == 4) { memset(&pr, 0, sizeof(pr)); bstrncpy(pr.Name, pool_name, sizeof(pr.Name)); unbash_spaces(pr.Name); ok = db_get_pool_record(jcr, jcr->db, &pr); if (ok) { mr.PoolId = pr.PoolId; set_storageid_in_mr(jcr->res.wstore, &mr); mr.ScratchPoolId = pr.ScratchPoolId; ok = find_next_volume_for_append(jcr, &mr, index, fnv_create_vol, fnv_prune); Dmsg3(050, "find_media ok=%d idx=%d vol=%s\n", ok, index, mr.VolumeName); } /* * Send Find Media response to Storage daemon */ if (ok) { send_volume_info_to_storage_daemon(jcr, bs, &mr); } else { bs->fsend(_("1901 No Media.\n")); Dmsg0(500, "1901 No Media.\n"); } /* * Request to find specific Volume information */ } else if (sscanf(bs->msg, Get_Vol_Info, &Job, &mr.VolumeName, &writing) == 3) { Dmsg1(100, "CatReq GetVolInfo Vol=%s\n", mr.VolumeName); /* * Find the Volume */ unbash_spaces(mr.VolumeName); if (db_get_media_record(jcr, jcr->db, &mr)) { const char *reason = NULL; /* detailed reason for rejection */ /* * If we are reading, accept any volume (reason == NULL) * If we are writing, check if the Volume is valid * for this job, and do a recycle if necessary */ if (writing) { /* * SD wants to write this Volume, so make * sure it is suitable for this job, i.e. * Pool matches, and it is either Append or Recycle * and Media Type matches and Pool allows any volume. */ if (mr.PoolId != jcr->jr.PoolId) { reason = _("not in Pool"); } else if (!bstrcmp(mr.MediaType, jcr->res.wstore->media_type)) { reason = _("not correct MediaType"); } else { /* * Now try recycling if necessary * reason set non-NULL if we cannot use it */ check_if_volume_valid_or_recyclable(jcr, &mr, &reason); } } if (!reason && mr.Enabled != 1) { reason = _("is not Enabled"); } if (reason == NULL) { /* * Send Find Media response to Storage daemon */ send_volume_info_to_storage_daemon(jcr, bs, &mr); } else { /* Not suitable volume */ bs->fsend(_("1998 Volume \"%s\" catalog status is %s, %s.\n"), mr.VolumeName, mr.VolStatus, reason); } } else { bs->fsend(_("1997 Volume \"%s\" not in catalog.\n"), mr.VolumeName); Dmsg1(100, "1997 Volume \"%s\" not in catalog.\n", mr.VolumeName); } /* * Request to update Media record. Comes typically at the end * of a Storage daemon Job Session, when labeling/relabeling a * Volume, or when an EOF mark is written. */ } else if (sscanf(bs->msg, Update_media, &Job, &sdmr.VolumeName, &sdmr.VolJobs, &sdmr.VolFiles, &sdmr.VolBlocks, &sdmr.VolBytes, &sdmr.VolMounts, &sdmr.VolErrors, &sdmr.VolWrites, &sdmr.MaxVolBytes, &VolLastWritten, &sdmr.VolStatus, &sdmr.Slot, &label, &sdmr.InChanger, &sdmr.VolReadTime, &sdmr.VolWriteTime, &VolFirstWritten) == 18) { db_lock(jcr->db); Dmsg3(400, "Update media %s oldStat=%s newStat=%s\n", sdmr.VolumeName, mr.VolStatus, sdmr.VolStatus); bstrncpy(mr.VolumeName, sdmr.VolumeName, sizeof(mr.VolumeName)); /* copy Volume name */ unbash_spaces(mr.VolumeName); if (!db_get_media_record(jcr, jcr->db, &mr)) { Jmsg(jcr, M_ERROR, 0, _("Unable to get Media record for Volume %s: ERR=%s\n"), mr.VolumeName, db_strerror(jcr->db)); bs->fsend(_("1991 Catalog Request for vol=%s failed: %s"), mr.VolumeName, db_strerror(jcr->db)); goto bail_out; } /* Set first written time if this is first job */ if (mr.FirstWritten == 0) { if (VolFirstWritten == 0) { mr.FirstWritten = jcr->start_time; /* use Job start time as first write */ } else { mr.FirstWritten = VolFirstWritten; } mr.set_first_written = true; } /* If we just labeled the tape set time */ if (label || mr.LabelDate == 0) { mr.LabelDate = jcr->start_time; mr.set_label_date = true; if (mr.InitialWrite == 0) { mr.InitialWrite = jcr->start_time; } Dmsg2(400, "label=%d labeldate=%d\n", label, mr.LabelDate); } else { /* * Insanity check for VolFiles get set to a smaller value */ if (sdmr.VolFiles < mr.VolFiles) { Jmsg(jcr, M_FATAL, 0, _("Volume Files at %u being set to %u" " for Volume \"%s\". This is incorrect.\n"), mr.VolFiles, sdmr.VolFiles, mr.VolumeName); bs->fsend(_("1992 Update Media error. VolFiles=%u, CatFiles=%u\n"), sdmr.VolFiles, mr.VolFiles); goto bail_out; } } Dmsg2(400, "Update media: BefVolJobs=%u After=%u\n", mr.VolJobs, sdmr.VolJobs); /* * Check if the volume has been written by the job, * and update the LastWritten field if needed. */ if (mr.VolBlocks != sdmr.VolBlocks && VolLastWritten != 0) { mr.LastWritten = VolLastWritten; } /* * Update to point to the last device used to write the Volume. * However, do so only if we are writing the tape, i.e. * the number of VolWrites has increased. */ if (jcr->res.wstore && sdmr.VolWrites > mr.VolWrites) { Dmsg2(050, "Update StorageId old=%d new=%d\n", mr.StorageId, jcr->res.wstore->StorageId); /* Update StorageId after write */ set_storageid_in_mr(jcr->res.wstore, &mr); } else { /* Nothing written, reset same StorageId */ set_storageid_in_mr(NULL, &mr); } /* Copy updated values to original media record */ mr.VolJobs = sdmr.VolJobs; mr.VolFiles = sdmr.VolFiles; mr.VolBlocks = sdmr.VolBlocks; mr.VolBytes = sdmr.VolBytes; mr.VolMounts = sdmr.VolMounts; mr.VolErrors = sdmr.VolErrors; mr.VolWrites = sdmr.VolWrites; mr.Slot = sdmr.Slot; mr.InChanger = sdmr.InChanger; bstrncpy(mr.VolStatus, sdmr.VolStatus, sizeof(mr.VolStatus)); mr.VolReadTime = sdmr.VolReadTime; mr.VolWriteTime = sdmr.VolWriteTime; Dmsg2(400, "db_update_media_record. Stat=%s Vol=%s\n", mr.VolStatus, mr.VolumeName); /* * Update the database, then before sending the response to the * SD, check if the Volume has expired. */ if (!db_update_media_record(jcr, jcr->db, &mr)) { Jmsg(jcr, M_FATAL, 0, _("Catalog error updating Media record. %s"), db_strerror(jcr->db)); bs->fsend(_("1993 Update Media error\n")); Dmsg0(400, "send error\n"); } else { (void)has_volume_expired(jcr, &mr); send_volume_info_to_storage_daemon(jcr, bs, &mr); } bail_out: db_unlock(jcr->db); Dmsg1(400, ">CatReq response: %s", bs->msg); Dmsg1(400, "Leave catreq jcr 0x%x\n", jcr); return; /* * Request to create a JobMedia record */ } else if (sscanf(bs->msg, Create_job_media, &Job, &jm.FirstIndex, &jm.LastIndex, &jm.StartFile, &jm.EndFile, &jm.StartBlock, &jm.EndBlock, &Copy, &Stripe, &MediaId) == 10) { if (jcr->mig_jcr) { jm.JobId = jcr->mig_jcr->JobId; } else { jm.JobId = jcr->JobId; } jm.MediaId = MediaId; Dmsg6(400, "create_jobmedia JobId=%d MediaId=%d SF=%d EF=%d FI=%d LI=%d\n", jm.JobId, jm.MediaId, jm.StartFile, jm.EndFile, jm.FirstIndex, jm.LastIndex); if (!db_create_jobmedia_record(jcr, jcr->db, &jm)) { Jmsg(jcr, M_FATAL, 0, _("Catalog error creating JobMedia record. %s"), db_strerror(jcr->db)); bs->fsend(_("1992 Create JobMedia error\n")); } else { Dmsg0(400, "JobMedia record created\n"); bs->fsend(OK_create); } } else { omsg = get_memory(bs->msglen+1); pm_strcpy(omsg, bs->msg); bs->fsend(_("1990 Invalid Catalog Request: %s"), omsg); Jmsg1(jcr, M_FATAL, 0, _("Invalid Catalog request: %s"), omsg); free_memory(omsg); } Dmsg1(400, ">CatReq response: %s", bs->msg); Dmsg1(400, "Leave catreq jcr 0x%x\n", jcr); return; }
/* * Update a media record -- allows you to change the * Volume status. E.g. if you want Bacula to stop * writing on the volume, set it to anything other * than Append. */ static int update_volume(UAContext *ua) { MEDIA_DBR mr; POOL *pool; POOL_DBR pr; POOLMEM *query; POOL_MEM ret; char buf[1000]; char ed1[130]; bool done = false; int i; const char *kw[] = { NT_("VolStatus"), /* 0 */ NT_("VolRetention"), /* 1 */ NT_("VolUse"), /* 2 */ NT_("MaxVolJobs"), /* 3 */ NT_("MaxVolFiles"), /* 4 */ NT_("MaxVolBytes"), /* 5 */ NT_("Recycle"), /* 6 */ NT_("InChanger"), /* 7 */ NT_("Slot"), /* 8 */ NT_("Pool"), /* 9 */ NT_("FromPool"), /* 10 */ NT_("AllFromPool"), /* 11 !!! see below !!! */ NT_("Enabled"), /* 12 */ NT_("RecyclePool"), /* 13 */ NT_("ActionOnPurge"), /* 14 */ NULL }; #define AllFromPool 11 /* keep this updated with above */ for (i=0; kw[i]; i++) { int j; POOL_DBR pr; if ((j=find_arg_with_value(ua, kw[i])) > 0) { /* If all from pool don't select a media record */ if (i != AllFromPool && !select_media_dbr(ua, &mr)) { return 0; } switch (i) { case 0: update_volstatus(ua, ua->argv[j], &mr); break; case 1: update_volretention(ua, ua->argv[j], &mr); break; case 2: update_voluseduration(ua, ua->argv[j], &mr); break; case 3: update_volmaxjobs(ua, ua->argv[j], &mr); break; case 4: update_volmaxfiles(ua, ua->argv[j], &mr); break; case 5: update_volmaxbytes(ua, ua->argv[j], &mr); break; case 6: update_volrecycle(ua, ua->argv[j], &mr); break; case 7: update_volinchanger(ua, ua->argv[j], &mr); break; case 8: update_volslot(ua, ua->argv[j], &mr); break; case 9: memset(&pr, 0, sizeof(POOL_DBR)); pr.PoolId = mr.PoolId; if (!db_get_pool_record(ua->jcr, ua->db, &pr)) { ua->error_msg("%s", db_strerror(ua->db)); break; } update_vol_pool(ua, ua->argv[j], &mr, &pr); break; case 10: update_vol_from_pool(ua, &mr); return 1; case 11: update_all_vols_from_pool(ua, ua->argv[j]); return 1; case 12: update_volenabled(ua, ua->argv[j], &mr); break; case 13: update_vol_recyclepool(ua, ua->argv[j], &mr); break; case 14: update_vol_actiononpurge(ua, ua->argv[j], &mr); break; } done = true; } } /* Allow user to simply update all volumes */ if (find_arg(ua, NT_("fromallpools")) > 0) { update_all_vols(ua); return 1; } for ( ; !done; ) { start_prompt(ua, _("Parameters to modify:\n")); add_prompt(ua, _("Volume Status")); /* 0 */ add_prompt(ua, _("Volume Retention Period")); /* 1 */ add_prompt(ua, _("Volume Use Duration")); /* 2 */ add_prompt(ua, _("Maximum Volume Jobs")); /* 3 */ add_prompt(ua, _("Maximum Volume Files")); /* 4 */ add_prompt(ua, _("Maximum Volume Bytes")); /* 5 */ add_prompt(ua, _("Recycle Flag")); /* 6 */ add_prompt(ua, _("Slot")); /* 7 */ add_prompt(ua, _("InChanger Flag")); /* 8 */ add_prompt(ua, _("Volume Files")); /* 9 */ add_prompt(ua, _("Pool")); /* 10 */ add_prompt(ua, _("Volume from Pool")); /* 11 */ add_prompt(ua, _("All Volumes from Pool")); /* 12 */ add_prompt(ua, _("All Volumes from all Pools")); /* 13 */ add_prompt(ua, _("Enabled")), /* 14 */ add_prompt(ua, _("RecyclePool")), /* 15 */ add_prompt(ua, _("Action On Purge")), /* 16 */ add_prompt(ua, _("Done")); /* 17 */ i = do_prompt(ua, "", _("Select parameter to modify"), NULL, 0); /* For All Volumes, All Volumes from Pool, and Done, we don't need * a Volume record */ if ( i != 12 && i != 13 && i != 17) { if (!select_media_dbr(ua, &mr)) { /* Get Volume record */ return 0; } ua->info_msg(_("Updating Volume \"%s\"\n"), mr.VolumeName); } switch (i) { case 0: /* Volume Status */ /* Modify Volume Status */ ua->info_msg(_("Current Volume status is: %s\n"), mr.VolStatus); start_prompt(ua, _("Possible Values are:\n")); add_prompt(ua, NT_("Append")); add_prompt(ua, NT_("Archive")); add_prompt(ua, NT_("Disabled")); add_prompt(ua, NT_("Full")); add_prompt(ua, NT_("Used")); add_prompt(ua, NT_("Cleaning")); if (strcmp(mr.VolStatus, NT_("Purged")) == 0) { add_prompt(ua, NT_("Recycle")); } add_prompt(ua, NT_("Read-Only")); if (do_prompt(ua, "", _("Choose new Volume Status"), ua->cmd, sizeof(mr.VolStatus)) < 0) { return 1; } update_volstatus(ua, ua->cmd, &mr); break; case 1: /* Retention */ ua->info_msg(_("Current retention period is: %s\n"), edit_utime(mr.VolRetention, ed1, sizeof(ed1))); if (!get_cmd(ua, _("Enter Volume Retention period: "))) { return 0; } update_volretention(ua, ua->cmd, &mr); break; case 2: /* Use Duration */ ua->info_msg(_("Current use duration is: %s\n"), edit_utime(mr.VolUseDuration, ed1, sizeof(ed1))); if (!get_cmd(ua, _("Enter Volume Use Duration: "))) { return 0; } update_voluseduration(ua, ua->cmd, &mr); break; case 3: /* Max Jobs */ ua->info_msg(_("Current max jobs is: %u\n"), mr.MaxVolJobs); if (!get_pint(ua, _("Enter new Maximum Jobs: "))) { return 0; } update_volmaxjobs(ua, ua->cmd, &mr); break; case 4: /* Max Files */ ua->info_msg(_("Current max files is: %u\n"), mr.MaxVolFiles); if (!get_pint(ua, _("Enter new Maximum Files: "))) { return 0; } update_volmaxfiles(ua, ua->cmd, &mr); break; case 5: /* Max Bytes */ ua->info_msg(_("Current value is: %s\n"), edit_uint64(mr.MaxVolBytes, ed1)); if (!get_cmd(ua, _("Enter new Maximum Bytes: "))) { return 0; } update_volmaxbytes(ua, ua->cmd, &mr); break; case 6: /* Recycle */ ua->info_msg(_("Current recycle flag is: %s\n"), mr.Recycle==1?_("yes"):_("no")); if (!get_yesno(ua, _("Enter new Recycle status: "))) { return 0; } update_volrecycle(ua, ua->cmd, &mr); break; case 7: /* Slot */ ua->info_msg(_("Current Slot is: %d\n"), mr.Slot); if (!get_pint(ua, _("Enter new Slot: "))) { return 0; } update_volslot(ua, ua->cmd, &mr); break; case 8: /* InChanger */ ua->info_msg(_("Current InChanger flag is: %d\n"), mr.InChanger); bsnprintf(buf, sizeof(buf), _("Set InChanger flag for Volume \"%s\": yes/no: "), mr.VolumeName); if (!get_yesno(ua, buf)) { return 0; } mr.InChanger = ua->pint32_val; /* * Make sure to use db_update... rather than doing this directly, * so that any Slot is handled correctly. */ if (!db_update_media_record(ua->jcr, ua->db, &mr)) { ua->error_msg(_("Error updating media record Slot: ERR=%s"), db_strerror(ua->db)); } else { ua->info_msg(_("New InChanger flag is: %d\n"), mr.InChanger); } break; case 9: /* Volume Files */ int32_t VolFiles; ua->warning_msg(_("Warning changing Volume Files can result\n" "in loss of data on your Volume\n\n")); ua->info_msg(_("Current Volume Files is: %u\n"), mr.VolFiles); if (!get_pint(ua, _("Enter new number of Files for Volume: "))) { return 0; } VolFiles = ua->pint32_val; if (VolFiles != (int)(mr.VolFiles + 1)) { ua->warning_msg(_("Normally, you should only increase Volume Files by one!\n")); if (!get_yesno(ua, _("Increase Volume Files? (yes/no): ")) || ua->pint32_val == 0) { break; } } query = get_pool_memory(PM_MESSAGE); Mmsg(query, "UPDATE Media SET VolFiles=%u WHERE MediaId=%s", VolFiles, edit_int64(mr.MediaId, ed1)); if (!db_sql_query(ua->db, query, NULL, NULL)) { ua->error_msg("%s", db_strerror(ua->db)); } else { ua->info_msg(_("New Volume Files is: %u\n"), VolFiles); } free_pool_memory(query); break; case 10: /* Volume's Pool */ memset(&pr, 0, sizeof(POOL_DBR)); pr.PoolId = mr.PoolId; if (!db_get_pool_record(ua->jcr, ua->db, &pr)) { ua->error_msg("%s", db_strerror(ua->db)); return 0; } ua->info_msg(_("Current Pool is: %s\n"), pr.Name); if (!get_cmd(ua, _("Enter new Pool name: "))) { return 0; } update_vol_pool(ua, ua->cmd, &mr, &pr); return 1; case 11: update_vol_from_pool(ua, &mr); return 1; case 12: pool = select_pool_resource(ua); if (pool) { update_all_vols_from_pool(ua, pool->name()); } return 1; case 13: update_all_vols(ua); return 1; case 14: ua->info_msg(_("Current Enabled is: %d\n"), mr.Enabled); if (!get_cmd(ua, _("Enter new Enabled: "))) { return 0; } if (strcasecmp(ua->cmd, "yes") == 0 || strcasecmp(ua->cmd, "true") == 0) { mr.Enabled = 1; } else if (strcasecmp(ua->cmd, "no") == 0 || strcasecmp(ua->cmd, "false") == 0) { mr.Enabled = 0; } else if (strcasecmp(ua->cmd, "archived") == 0) { mr.Enabled = 2; } else { mr.Enabled = atoi(ua->cmd); } update_volenabled(ua, ua->cmd, &mr); break; case 15: memset(&pr, 0, sizeof(POOL_DBR)); pr.PoolId = mr.RecyclePoolId; if (db_get_pool_record(ua->jcr, ua->db, &pr)) { ua->info_msg(_("Current RecyclePool is: %s\n"), pr.Name); } else { ua->info_msg(_("No current RecyclePool\n")); } if (!select_pool_dbr(ua, &pr, NT_("recyclepool"))) { return 0; } update_vol_recyclepool(ua, pr.Name, &mr); return 1; case 16: pm_strcpy(ret, ""); ua->info_msg(_("Current ActionOnPurge is: %s\n"), action_on_purge_to_string(mr.ActionOnPurge, ret)); if (!get_cmd(ua, _("Enter new ActionOnPurge (one of: Truncate, None): "))) { return 0; } update_vol_actiononpurge(ua, ua->cmd, &mr); break; default: /* Done or error */ ua->info_msg(_("Selection terminated.\n")); return 1; } } return 1; }
/* * Implement Bareos bconsole command purge action * purge action= pool= volume= storage= devicetype= */ static int action_on_purge_cmd(UAContext *ua, const char *cmd) { bool allpools = false; int drive = -1; int nb = 0; uint32_t *results = NULL; const char *action = "all"; STORERES *store = NULL; POOLRES *pool = NULL; MEDIA_DBR mr; POOL_DBR pr; BSOCK *sd = NULL; memset(&pr, 0, sizeof(pr)); /* Look at arguments */ for (int i=1; i<ua->argc; i++) { if (bstrcasecmp(ua->argk[i], NT_("allpools"))) { allpools = true; } else if (bstrcasecmp(ua->argk[i], NT_("volume")) && is_name_valid(ua->argv[i], NULL)) { bstrncpy(mr.VolumeName, ua->argv[i], sizeof(mr.VolumeName)); } else if (bstrcasecmp(ua->argk[i], NT_("devicetype")) && ua->argv[i]) { bstrncpy(mr.MediaType, ua->argv[i], sizeof(mr.MediaType)); } else if (bstrcasecmp(ua->argk[i], NT_("drive")) && ua->argv[i]) { drive = atoi(ua->argv[i]); } else if (bstrcasecmp(ua->argk[i], NT_("action")) && is_name_valid(ua->argv[i], NULL)) { action=ua->argv[i]; } } /* Choose storage */ ua->jcr->res.wstore = store = get_storage_resource(ua, false); if (!store) { goto bail_out; } switch (store->Protocol) { case APT_NDMPV2: case APT_NDMPV3: case APT_NDMPV4: ua->warning_msg(_("Storage has non-native protocol.\n")); goto bail_out; default: break; } if (!open_db(ua)) { Dmsg0(100, "Can't open db\n"); goto bail_out; } if (!allpools) { /* force pool selection */ pool = get_pool_resource(ua); if (!pool) { Dmsg0(100, "Can't get pool resource\n"); goto bail_out; } bstrncpy(pr.Name, pool->name(), sizeof(pr.Name)); if (!db_get_pool_record(ua->jcr, ua->db, &pr)) { Dmsg0(100, "Can't get pool record\n"); goto bail_out; } mr.PoolId = pr.PoolId; } /* * Look for all Purged volumes that can be recycled, are enabled and * have more the 10,000 bytes. */ mr.Recycle = 1; mr.Enabled = 1; mr.VolBytes = 10000; set_storageid_in_mr(store, &mr); bstrncpy(mr.VolStatus, "Purged", sizeof(mr.VolStatus)); if (!db_get_media_ids(ua->jcr, ua->db, &mr, &nb, &results)) { Dmsg0(100, "No results from db_get_media_ids\n"); goto bail_out; } if (!nb) { ua->send_msg(_("No Volumes found to perform %s action.\n"), action); goto bail_out; } if ((sd = open_sd_bsock(ua)) == NULL) { Dmsg0(100, "Can't open connection to sd\n"); goto bail_out; } /* * Loop over the candidate Volumes and actually truncate them */ for (int i=0; i < nb; i++) { mr.clear(); mr.MediaId = results[i]; if (db_get_media_record(ua->jcr, ua->db, &mr)) { /* TODO: ask for drive and change Pool */ if (bstrcasecmp("truncate", action) || bstrcasecmp("all", action)) { do_truncate_on_purge(ua, &mr, pr.Name, store->dev_name(), drive, sd); } } else { Dmsg1(0, "Can't find MediaId=%lld\n", (uint64_t) mr.MediaId); } } bail_out: close_db(ua); if (sd) { sd->signal(BNET_TERMINATE); sd->close(); ua->jcr->store_bsock = NULL; } ua->jcr->res.wstore = NULL; if (results) { free(results); } return 1; }
/* Returns: NULL if error * PyObject * return value if OK */ PyObject *job_getattr(PyObject *self, char *attrname) { JCR *jcr; bool found = false; int i; char buf[10]; char errmsg[200]; Dmsg0(100, "In job_getattr.\n"); jcr = get_jcr_from_PyObject(self); if (!jcr) { bstrncpy(errmsg, _("Job pointer not found."), sizeof(errmsg)); goto bail_out; } for (i=0; getvars[i].name; i++) { if (strcmp(getvars[i].name, attrname) == 0) { found = true; break; } } if (!found) { /* Try our methods */ return Py_FindMethod(JobMethods, self, attrname); } switch (i) { case 0: /* Job */ return Py_BuildValue((char *)getvars[i].fmt, jcr->job->hdr.name); case 1: /* level */ return Py_BuildValue((char *)getvars[i].fmt, job_level_to_str(jcr->getJobLevel())); case 2: /* type */ return Py_BuildValue((char *)getvars[i].fmt, job_type_to_str(jcr->getJobType())); case 3: /* JobId */ return Py_BuildValue((char *)getvars[i].fmt, jcr->JobId); case 4: /* Client */ return Py_BuildValue((char *)getvars[i].fmt, jcr->client->hdr.name); case 5: /* NumVols */ POOL_DBR pr; memset(&pr, 0, sizeof(pr)); bstrncpy(pr.Name, jcr->pool->hdr.name, sizeof(pr.Name)); if (db_get_pool_record(jcr, jcr->db, &pr)) { jcr->NumVols = pr.NumVols; return Py_BuildValue((char *)getvars[i].fmt, jcr->NumVols); } else { bsnprintf(errmsg, sizeof(errmsg), _("Pool record not found.")); goto bail_out; } case 6: /* Pool */ return Py_BuildValue((char *)getvars[i].fmt, jcr->pool->name()); case 7: /* Storage */ if (jcr->wstore) { return Py_BuildValue((char *)getvars[i].fmt, jcr->wstore->name()); } else if (jcr->rstore) { return Py_BuildValue((char *)getvars[i].fmt, jcr->rstore->name()); } else { goto bail_out; } case 8: return Py_BuildValue((char *)getvars[i].fmt, jcr->catalog->name()); case 9: /* MediaType */ if (jcr->wstore) { return Py_BuildValue((char *)getvars[i].fmt, jcr->wstore->media_type); } else if (jcr->rstore) { return Py_BuildValue((char *)getvars[i].fmt, jcr->rstore->media_type); } else { goto bail_out; } case 10: /* JobName */ return Py_BuildValue((char *)getvars[i].fmt, jcr->Job); case 11: /* JobStatus */ buf[1] = 0; buf[0] = jcr->JobStatus; return Py_BuildValue((char *)getvars[i].fmt, buf); case 12: /* Priority */ return Py_BuildValue((char *)getvars[i].fmt, jcr->JobPriority); case 13: return Py_BuildValue((char *)getvars[i].fmt, jcr->VolumeName); case 14: /* CatalogRes */ return Py_BuildValue((char *)getvars[i].fmt, jcr->catalog->db_name, jcr->catalog->db_address, jcr->catalog->db_user, jcr->catalog->db_password, jcr->catalog->db_socket, jcr->catalog->db_port, db_get_type(jcr->db)); case 15: /* JobErrors */ return Py_BuildValue((char *)getvars[i].fmt, jcr->JobErrors); case 16: /* JobFiles */ return Py_BuildValue((char *)getvars[i].fmt, jcr->JobFiles); case 17: /* SDJobFiles */ return Py_BuildValue((char *)getvars[i].fmt, jcr->SDJobFiles); case 18: /* SDErrors */ return Py_BuildValue((char *)getvars[i].fmt, jcr->SDErrors); case 19: /* FDJobStatus */ buf[1] = 0; buf[0] = jcr->FDJobStatus; return Py_BuildValue((char *)getvars[i].fmt, buf); case 20: /* SDJobStatus */ buf[1] = 0; buf[0] = jcr->SDJobStatus; return Py_BuildValue((char *)getvars[i].fmt, buf); } bsnprintf(errmsg, sizeof(errmsg), _("Attribute %s not found."), attrname); bail_out: PyErr_SetString(PyExc_AttributeError, errmsg); return NULL; }
static bool list_nextvol(UAContext *ua, int ndays) { JOBRES *job; JCR *jcr; USTORERES store; RUNRES *run; utime_t runtime; bool found = false; MEDIA_DBR mr; POOL_DBR pr; int i = find_arg_with_value(ua, "job"); if (i <= 0) { if ((job = select_job_resource(ua)) == NULL) { return false; } } else { job = (JOBRES *)GetResWithName(R_JOB, ua->argv[i]); if (!job) { Jmsg(ua->jcr, M_ERROR, 0, _("%s is not a job name.\n"), ua->argv[i]); if ((job = select_job_resource(ua)) == NULL) { return false; } } } jcr = new_jcr(sizeof(JCR), dird_free_jcr); for (run=NULL; (run = find_next_run(run, job, runtime, ndays)); ) { if (!complete_jcr_for_job(jcr, job, run->pool)) { found = false; goto get_out; } if (!jcr->jr.PoolId) { ua->error_msg(_("Could not find Pool for Job %s\n"), job->name()); continue; } memset(&pr, 0, sizeof(pr)); pr.PoolId = jcr->jr.PoolId; if (!db_get_pool_record(jcr, jcr->db, &pr)) { bstrncpy(pr.Name, "*UnknownPool*", sizeof(pr.Name)); } mr.PoolId = jcr->jr.PoolId; get_job_storage(&store, job, run); set_storageid_in_mr(store.store, &mr); /* no need to set ScratchPoolId, since we use fnv_no_create_vol */ if (!find_next_volume_for_append(jcr, &mr, 1, fnv_no_create_vol, fnv_prune)) { ua->error_msg(_("Could not find next Volume for Job %s (Pool=%s, Level=%s).\n"), job->name(), pr.Name, level_to_str(run->level)); } else { ua->send_msg( _("The next Volume to be used by Job \"%s\" (Pool=%s, Level=%s) will be %s\n"), job->name(), pr.Name, level_to_str(run->level), mr.VolumeName); found = true; } } get_out: if (jcr->db) { db_sql_close_pooled_connection(jcr, jcr->db); jcr->db = NULL; } free_jcr(jcr); if (!found) { ua->error_msg(_("Could not find next Volume for Job %s.\n"), job->hdr.name); return false; } return true; }
static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist) { POOLMEM *VolumeName; int jobid, n; int i, j; JOB_DBR jr; POOL_DBR pr; MEDIA_DBR mr; if (!open_client_db(ua)) return 1; memset(&jr, 0, sizeof(jr)); memset(&pr, 0, sizeof(pr)); Dmsg1(20, "list: %s\n", cmd); if (!ua->db) { ua->error_msg(_("Hey! DB is NULL\n")); } /* Apply any limit */ j = find_arg_with_value(ua, NT_("limit")); if (j >= 0) { jr.limit = atoi(ua->argv[j]); } /* Scan arguments looking for things to do */ for (i=1; i<ua->argc; i++) { /* List JOBS */ if (bstrcasecmp(ua->argk[i], NT_("jobs"))) { db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist); /* List JOBTOTALS */ } else if (bstrcasecmp(ua->argk[i], NT_("jobtotals"))) { db_list_job_totals(ua->jcr, ua->db, &jr, prtit, ua); /* List JOBID=nn */ } else if (bstrcasecmp(ua->argk[i], NT_("jobid"))) { if (ua->argv[i]) { jobid = str_to_int64(ua->argv[i]); if (jobid > 0) { jr.JobId = jobid; db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist); } } /* List JOB=xxx */ } else if ((bstrcasecmp(ua->argk[i], NT_("job")) || bstrcasecmp(ua->argk[i], NT_("jobname"))) && ua->argv[i]) { bstrncpy(jr.Name, ua->argv[i], MAX_NAME_LENGTH); jr.JobId = 0; db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist); /* List UJOBID=xxx */ } else if (bstrcasecmp(ua->argk[i], NT_("ujobid")) && ua->argv[i]) { bstrncpy(jr.Job, ua->argv[i], MAX_NAME_LENGTH); jr.JobId = 0; db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist); /* List Base files */ } else if (bstrcasecmp(ua->argk[i], NT_("basefiles"))) { /* TODO: cleanup this block */ for (j=i+1; j<ua->argc; j++) { if (bstrcasecmp(ua->argk[j], NT_("ujobid")) && ua->argv[j]) { bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH); jr.JobId = 0; db_get_job_record(ua->jcr, ua->db, &jr); jobid = jr.JobId; } else if (bstrcasecmp(ua->argk[j], NT_("jobid")) && ua->argv[j]) { jobid = str_to_int64(ua->argv[j]); } else { continue; } if (jobid > 0) { db_list_base_files_for_job(ua->jcr, ua->db, jobid, prtit, ua); } } /* List FILES */ } else if (bstrcasecmp(ua->argk[i], NT_("files"))) { for (j=i+1; j<ua->argc; j++) { if (bstrcasecmp(ua->argk[j], NT_("ujobid")) && ua->argv[j]) { bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH); jr.JobId = 0; db_get_job_record(ua->jcr, ua->db, &jr); jobid = jr.JobId; } else if (bstrcasecmp(ua->argk[j], NT_("jobid")) && ua->argv[j]) { jobid = str_to_int64(ua->argv[j]); } else { continue; } if (jobid > 0) { db_list_files_for_job(ua->jcr, ua->db, jobid, prtit, ua); } } /* List JOBMEDIA */ } else if (bstrcasecmp(ua->argk[i], NT_("jobmedia"))) { bool done = false; for (j=i+1; j<ua->argc; j++) { if (bstrcasecmp(ua->argk[j], NT_("ujobid")) && ua->argv[j]) { bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH); jr.JobId = 0; db_get_job_record(ua->jcr, ua->db, &jr); jobid = jr.JobId; } else if (bstrcasecmp(ua->argk[j], NT_("jobid")) && ua->argv[j]) { jobid = str_to_int64(ua->argv[j]); } else { continue; } db_list_jobmedia_records(ua->jcr, ua->db, jobid, prtit, ua, llist); done = true; } if (!done) { /* List for all jobs (jobid=0) */ db_list_jobmedia_records(ua->jcr, ua->db, 0, prtit, ua, llist); } /* List JOBLOG */ } else if (bstrcasecmp(ua->argk[i], NT_("joblog"))) { bool done = false; for (j=i+1; j<ua->argc; j++) { if (bstrcasecmp(ua->argk[j], NT_("ujobid")) && ua->argv[j]) { bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH); jr.JobId = 0; db_get_job_record(ua->jcr, ua->db, &jr); jobid = jr.JobId; } else if (bstrcasecmp(ua->argk[j], NT_("jobid")) && ua->argv[j]) { jobid = str_to_int64(ua->argv[j]); } else { continue; } db_list_joblog_records(ua->jcr, ua->db, jobid, prtit, ua, llist); done = true; } if (!done) { /* List for all jobs (jobid=0) */ db_list_joblog_records(ua->jcr, ua->db, 0, prtit, ua, llist); } /* List POOLS */ } else if (bstrcasecmp(ua->argk[i], NT_("pool")) || bstrcasecmp(ua->argk[i], NT_("pools"))) { POOL_DBR pr; memset(&pr, 0, sizeof(pr)); if (ua->argv[i]) { bstrncpy(pr.Name, ua->argv[i], sizeof(pr.Name)); } db_list_pool_records(ua->jcr, ua->db, &pr, prtit, ua, llist); } else if (bstrcasecmp(ua->argk[i], NT_("clients"))) { db_list_client_records(ua->jcr, ua->db, prtit, ua, llist); /* List MEDIA or VOLUMES */ } else if (bstrcasecmp(ua->argk[i], NT_("media")) || bstrcasecmp(ua->argk[i], NT_("volume")) || bstrcasecmp(ua->argk[i], NT_("volumes"))) { bool done = false; for (j=i+1; j<ua->argc; j++) { if (bstrcasecmp(ua->argk[j], NT_("ujobid")) && ua->argv[j]) { bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH); jr.JobId = 0; db_get_job_record(ua->jcr, ua->db, &jr); jobid = jr.JobId; } else if (bstrcasecmp(ua->argk[j], NT_("jobid")) && ua->argv[j]) { jobid = str_to_int64(ua->argv[j]); } else { continue; } VolumeName = get_pool_memory(PM_FNAME); n = db_get_job_volume_names(ua->jcr, ua->db, jobid, &VolumeName); ua->send_msg(_("Jobid %d used %d Volume(s): %s\n"), jobid, n, VolumeName); free_pool_memory(VolumeName); done = true; } /* if no job or jobid keyword found, then we list all media */ if (!done) { int num_pools; uint32_t *ids; /* List a specific volume? */ if (ua->argv[i]) { bstrncpy(mr.VolumeName, ua->argv[i], sizeof(mr.VolumeName)); db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist); return 1; } /* Is a specific pool wanted? */ for (i=1; i<ua->argc; i++) { if (bstrcasecmp(ua->argk[i], NT_("pool"))) { if (!get_pool_dbr(ua, &pr)) { ua->error_msg(_("No Pool specified.\n")); return 1; } mr.PoolId = pr.PoolId; db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist); return 1; } } /* List Volumes in all pools */ if (!db_get_pool_ids(ua->jcr, ua->db, &num_pools, &ids)) { ua->error_msg(_("Error obtaining pool ids. ERR=%s\n"), db_strerror(ua->db)); return 1; } if (num_pools <= 0) { return 1; } for (i=0; i < num_pools; i++) { pr.PoolId = ids[i]; if (db_get_pool_record(ua->jcr, ua->db, &pr)) { ua->send_msg(_("Pool: %s\n"), pr.Name); } mr.PoolId = ids[i]; db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist); } free(ids); return 1; } /* List next volume */ } else if (bstrcasecmp(ua->argk[i], NT_("nextvol")) || bstrcasecmp(ua->argk[i], NT_("nextvolume"))) { n = 1; j = find_arg_with_value(ua, NT_("days")); if (j >= 0) { n = atoi(ua->argv[j]); if ((n < 0) || (n > 50)) { ua->warning_msg(_("Ignoring invalid value for days. Max is 50.\n")); n = 1; } } list_nextvol(ua, n); } else if (bstrcasecmp(ua->argk[i], NT_("copies"))) { char *jobids = NULL; uint32_t limit=0; for (j=i+1; j<ua->argc; j++) { if (bstrcasecmp(ua->argk[j], NT_("jobid")) && ua->argv[j]) { if (is_a_number_list(ua->argv[j])) { jobids = ua->argv[j]; } } else if (bstrcasecmp(ua->argk[j], NT_("limit")) && ua->argv[j]) { limit = atoi(ua->argv[j]); } } db_list_copies_records(ua->jcr,ua->db,limit,jobids,prtit,ua,llist); } else if (bstrcasecmp(ua->argk[i], NT_("limit")) || bstrcasecmp(ua->argk[i], NT_("days"))) { /* Ignore it */ } else { ua->error_msg(_("Unknown list keyword: %s\n"), NPRT(ua->argk[i])); } } return 1; }
/* * Print slots from AutoChanger */ void status_slots(UAContext *ua, STORE *store_r) { USTORE store; POOL_DBR pr; vol_list_t *vl, *vol_list = NULL; MEDIA_DBR mr; char *slot_list; int max_slots; int drive; int i=1; /* output format */ const char *slot_api_empty_format="%i|||||\n"; const char *slot_api_full_format="%i|%i|%s|%s|%s|%s|\n"; const char *slot_hformat=" %4i%c| %16s | %9s | %20s | %18s |\n"; if (!open_client_db(ua)) { return; } store.store = store_r; pm_strcpy(store.store_source, _("command line")); set_wstorage(ua->jcr, &store); drive = get_storage_drive(ua, store.store); max_slots = get_num_slots_from_SD(ua); if (max_slots <= 0) { ua->warning_msg(_("No slots in changer to scan.\n")); return; } slot_list = (char *)malloc(max_slots+1); if (!get_user_slot_list(ua, slot_list, max_slots)) { free(slot_list); return; } vol_list = get_vol_list_from_SD(ua, true /* want to see all slots */); if (!vol_list) { ua->warning_msg(_("No Volumes found, or no barcodes.\n")); goto bail_out; } if (!ua->api) { ua->info_msg(_(" Slot | Volume Name | Status | Media Type | Pool |\n")); ua->info_msg(_("------+------------------+-----------+----------------------+--------------------|\n")); } /* Walk through the list getting the media records */ for (vl=vol_list; vl; vl=vl->next) { if (vl->Slot > max_slots) { ua->warning_msg(_("Slot %d greater than max %d ignored.\n"), vl->Slot, max_slots); continue; } /* Check if user wants us to look at this slot */ if (!slot_list[vl->Slot]) { Dmsg1(100, "Skipping slot=%d\n", vl->Slot); continue; } slot_list[vl->Slot] = 0; /* clear Slot */ if (!vl->VolName) { Dmsg1(100, "No VolName for Slot=%d.\n", vl->Slot); if (!ua->api) { ua->info_msg(slot_hformat, vl->Slot, '*', "?", "?", "?", "?"); } else { ua->info_msg(slot_api_empty_format, vl->Slot); } continue; } /* Hope that slots are ordered */ for (; i < vl->Slot; i++) { if (slot_list[i]) { if (!ua->api) { ua->info_msg(slot_hformat, i, ' ', "", "", "", ""); } else { ua->info_msg(slot_api_empty_format, i); } slot_list[i]=0; } } memset(&mr, 0, sizeof(mr)); bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName)); db_lock(ua->db); if (mr.VolumeName[0] && db_get_media_record(ua->jcr, ua->db, &mr)) { memset(&pr, 0, sizeof(POOL_DBR)); pr.PoolId = mr.PoolId; if (!db_get_pool_record(ua->jcr, ua->db, &pr)) { strcpy(pr.Name, "?"); } if (!ua->api) { /* Print information */ ua->info_msg(slot_hformat, vl->Slot, ((vl->Slot==mr.Slot)?' ':'*'), mr.VolumeName, mr.VolStatus, mr.MediaType, pr.Name); } else { ua->info_msg(slot_api_full_format, vl->Slot, mr.Slot, mr.VolumeName, mr.VolStatus, mr.MediaType, pr.Name); } db_unlock(ua->db); continue; } else { /* TODO: get information from catalog */ ua->info_msg(slot_hformat, vl->Slot, '*', mr.VolumeName, "?", "?", "?"); } db_unlock(ua->db); } /* Display the rest of the autochanger */ for (; i <= max_slots; i++) { if (slot_list[i]) { if (!ua->api) { ua->info_msg(slot_hformat, i, ' ', "", "", "", ""); } else { ua->info_msg(slot_api_empty_format, i); } slot_list[i]=0; } } bail_out: free_vol_list(vol_list); free(slot_list); close_sd_bsock(ua); return; }
/* * Prune at least one Volume in current Pool. This is called from * catreq.c => next_vol.c when the Storage daemon is asking for another * volume and no appendable volumes are available. * */ void prune_volumes(JCR *jcr, bool InChanger, MEDIA_DBR *mr, STORERES *store) { int i; int count; POOL_DBR spr; UAContext *ua; dbid_list ids; struct del_ctx prune_list; POOL_MEM query(PM_MESSAGE); char ed1[50], ed2[100], ed3[50]; Dmsg1(100, "Prune volumes PoolId=%d\n", jcr->jr.PoolId); if (!jcr->res.job->PruneVolumes && !jcr->res.pool->AutoPrune) { Dmsg0(100, "AutoPrune not set in Pool.\n"); return; } memset(&prune_list, 0, sizeof(prune_list)); prune_list.max_ids = 10000; prune_list.JobId = (JobId_t *)malloc(sizeof(JobId_t) * prune_list.max_ids); ua = new_ua_context(jcr); db_lock(jcr->db); /* Edit PoolId */ edit_int64(mr->PoolId, ed1); /* * Get Pool record for Scratch Pool */ memset(&spr, 0, sizeof(spr)); bstrncpy(spr.Name, "Scratch", sizeof(spr.Name)); if (db_get_pool_record(jcr, jcr->db, &spr)) { edit_int64(spr.PoolId, ed2); bstrncat(ed2, ",", sizeof(ed2)); } else { ed2[0] = 0; } if (mr->ScratchPoolId) { edit_int64(mr->ScratchPoolId, ed3); bstrncat(ed2, ed3, sizeof(ed2)); bstrncat(ed2, ",", sizeof(ed2)); } Dmsg1(100, "Scratch pool(s)=%s\n", ed2); /* * ed2 ends up with scratch poolid and current poolid or * just current poolid if there is no scratch pool */ bstrncat(ed2, ed1, sizeof(ed2)); /* * Get the List of all media ids in the current Pool or whose * RecyclePoolId is the current pool or the scratch pool */ const char *select = "SELECT DISTINCT MediaId,LastWritten FROM Media WHERE " "(PoolId=%s OR RecyclePoolId IN (%s)) AND MediaType='%s' %s" "ORDER BY LastWritten ASC,MediaId"; if (InChanger) { char changer[100]; /* Ensure it is in this autochanger */ bsnprintf(changer, sizeof(changer), "AND InChanger=1 AND StorageId=%s ", edit_int64(mr->StorageId, ed3)); Mmsg(query, select, ed1, ed2, mr->MediaType, changer); } else { Mmsg(query, select, ed1, ed2, mr->MediaType, ""); } Dmsg1(100, "query=%s\n", query.c_str()); if (!db_get_query_dbids(ua->jcr, ua->db, query, ids)) { Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db)); goto bail_out; } Dmsg1(100, "Volume prune num_ids=%d\n", ids.num_ids); /* Visit each Volume and Prune it until we find one that is purged */ for (i=0; i<ids.num_ids; i++) { MEDIA_DBR lmr; lmr.MediaId = ids.DBId[i]; Dmsg1(100, "Get record MediaId=%d\n", (int)lmr.MediaId); if (!db_get_media_record(jcr, jcr->db, &lmr)) { Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db)); continue; } Dmsg1(100, "Examine vol=%s\n", lmr.VolumeName); /* Don't prune archived volumes */ if (lmr.Enabled == 2) { Dmsg1(100, "Vol=%s disabled\n", lmr.VolumeName); continue; } /* Prune only Volumes with status "Full", or "Used" */ if (bstrcmp(lmr.VolStatus, "Full") || bstrcmp(lmr.VolStatus, "Used")) { Dmsg2(100, "Add prune list MediaId=%d Volume %s\n", (int)lmr.MediaId, lmr.VolumeName); count = get_prune_list_for_volume(ua, &lmr, &prune_list); Dmsg1(100, "Num pruned = %d\n", count); if (count != 0) { purge_job_list_from_catalog(ua, prune_list); prune_list.num_ids = 0; /* reset count */ } if (!is_volume_purged(ua, &lmr)) { Dmsg1(050, "Vol=%s not pruned\n", lmr.VolumeName); continue; } Dmsg1(050, "Vol=%s is purged\n", lmr.VolumeName); /* * Since we are also pruning the Scratch pool, continue * until and check if this volume is available (InChanger + StorageId) * If not, just skip this volume and try the next one */ if (InChanger) { if (!lmr.InChanger || (lmr.StorageId != mr->StorageId)) { Dmsg1(100, "Vol=%s not inchanger or correct StoreId\n", lmr.VolumeName); continue; /* skip this volume, ie not loadable */ } } if (!lmr.Recycle) { Dmsg1(100, "Vol=%s not recyclable\n", lmr.VolumeName); continue; } if (has_volume_expired(jcr, &lmr)) { Dmsg1(100, "Vol=%s has expired\n", lmr.VolumeName); continue; /* Volume not usable */ } /* * If purged and not moved to another Pool, * then we stop pruning and take this volume. */ if (lmr.PoolId == mr->PoolId) { Dmsg2(100, "Got Vol=%s MediaId=%d purged.\n", lmr.VolumeName, (int)lmr.MediaId); mr->copy(&lmr); set_storageid_in_mr(store, mr); break; /* got a volume */ } } } bail_out: Dmsg0(100, "Leave prune volumes\n"); db_unlock(jcr->db); free_ua_context(ua); if (prune_list.JobId) { free(prune_list.JobId); } return; }
bool get_scratch_volume(JCR *jcr, bool InChanger, MEDIA_DBR *mr) { MEDIA_DBR smr; /* for searching scratch pool */ POOL_DBR spr, pr; bool ok = false; bool found = false; /* Only one thread at a time can pull from the scratch pool */ P(mutex); /* * Get Pool record for Scratch Pool * choose between ScratchPoolId and Scratch * db_get_pool_record will first try ScratchPoolId, * and then try the pool named Scratch */ memset(&spr, 0, sizeof(spr)); bstrncpy(spr.Name, "Scratch", sizeof(spr.Name)); spr.PoolId = mr->ScratchPoolId; if (db_get_pool_record(jcr, jcr->db, &spr)) { memset(&smr, 0, sizeof(smr)); smr.PoolId = spr.PoolId; if (InChanger) { smr.StorageId = mr->StorageId; /* want only Scratch Volumes in changer */ } bstrncpy(smr.VolStatus, "Append", sizeof(smr.VolStatus)); /* want only appendable volumes */ bstrncpy(smr.MediaType, mr->MediaType, sizeof(smr.MediaType)); /* * If we do not find a valid Scratch volume, try * recycling any existing purged volumes, then * try to take the oldest volume. */ if (db_find_next_volume(jcr, jcr->db, 1, InChanger, &smr)) { found = true; } else if (find_recycled_volume(jcr, InChanger, &smr)) { found = true; } else if (recycle_oldest_purged_volume(jcr, InChanger, &smr)) { found = true; } if (found) { POOL_MEM query(PM_MESSAGE); /* * Get pool record where the Scratch Volume will go to ensure * that we can add a Volume. */ memset(&pr, 0, sizeof(pr)); bstrncpy(pr.Name, jcr->pool->name(), sizeof(pr.Name)); if (!db_get_pool_record(jcr, jcr->db, &pr)) { Jmsg(jcr, M_WARNING, 0, _("Unable to get Pool record: ERR=%s"), db_strerror(jcr->db)); goto bail_out; } /* Make sure there is room for another volume */ if (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) { Jmsg(jcr, M_WARNING, 0, _("Unable add Scratch Volume, Pool \"%s\" full MaxVols=%d\n"), jcr->pool->name(), pr.MaxVols); goto bail_out; } memcpy(mr, &smr, sizeof(MEDIA_DBR)); /* Set default parameters from current pool */ set_pool_dbr_defaults_in_media_dbr(mr, &pr); /* * set_pool_dbr_defaults_in_media_dbr set VolStatus to Append, * we could have Recycled media, also, we retain the old * RecyclePoolId. */ bstrncpy(mr->VolStatus, smr.VolStatus, sizeof(smr.VolStatus)); mr->RecyclePoolId = smr.RecyclePoolId; if (!db_update_media_record(jcr, jcr->db, mr)) { Jmsg(jcr, M_WARNING, 0, _("Failed to move Scratch Volume. ERR=%s\n"), db_strerror(jcr->db)); goto bail_out; } Jmsg(jcr, M_INFO, 0, _("Using Volume \"%s\" from 'Scratch' pool.\n"), mr->VolumeName); ok = true; } } bail_out: V(mutex); return ok; }