/* * Update a media record -- allows you to change the * Volume status. E.g. if you want BAREOS to stop * writing on the volume, set it to anything other * than Append. */ static int update_volume(UAContext *ua) { POOLRES *pool; 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; MEDIA_DBR mr; memset(&pr, 0, sizeof(pr)); memset(&mr, 0, sizeof(mr)); 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: 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; ) { POOL_DBR pr; MEDIA_DBR mr; memset(&pr, 0, sizeof(pr)); memset(&mr, 0, sizeof(mr)); 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 (bstrcmp(mr.VolStatus, NT_("Purged"))) { 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. */ set_storageid_in_mr(NULL, &mr); 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)) { 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 */ 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 (bstrcasecmp(ua->cmd, "yes") || bstrcasecmp(ua->cmd, "true")) { mr.Enabled = 1; } else if (bstrcasecmp(ua->cmd, "no") || bstrcasecmp(ua->cmd, "false")) { mr.Enabled = 0; } else if (bstrcasecmp(ua->cmd, "archived")) { mr.Enabled = 2; } else { mr.Enabled = atoi(ua->cmd); } update_volenabled(ua, ua->cmd, &mr); break; case 15: 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; }
static inline struct bstr * parse_object(struct bstr *args, struct bstr *arg, struct tcf_meta_val *obj, unsigned long *dst, struct tcf_meta_val *left) { struct meta_entry *entry; unsigned long num; struct bstr *a; if (arg->quoted) { obj->kind = TCF_META_TYPE_VAR << 12; obj->kind |= TCF_META_ID_VALUE; *dst = (unsigned long) arg; return bstr_next(arg); } num = bstrtoul(arg); if (num != ULONG_MAX) { obj->kind = TCF_META_TYPE_INT << 12; obj->kind |= TCF_META_ID_VALUE; *dst = (unsigned long) num; return bstr_next(arg); } entry = lookup_meta_entry(arg); if (entry == NULL) { PARSE_ERR(arg, "meta: unknown meta id\n"); return PARSE_FAILURE; } obj->kind = entry->id | (map_type(entry->mask[0]) << 12); if (left) { struct tcf_meta_val *right = obj; if (TCF_META_TYPE(right->kind) == TCF_META_TYPE(left->kind)) goto compatible; if (can_adopt(left) && !can_adopt(right)) { if (is_compatible(left, right)) left->kind = overwrite_type(left, right); else goto not_compatible; } else if (can_adopt(right) && !can_adopt(left)) { if (is_compatible(right, left)) right->kind = overwrite_type(right, left); else goto not_compatible; } else if (can_adopt(left) && can_adopt(right)) { if (is_compatible(left, right)) left->kind = overwrite_type(left, right); else if (is_compatible(right, left)) right->kind = overwrite_type(right, left); else goto not_compatible; } else goto not_compatible; } compatible: a = bstr_next(arg); while(a) { if (!bstrcmp(a, "shift")) { unsigned long shift; if (a->next == NULL) { PARSE_ERR(a, "meta: missing argument"); return PARSE_FAILURE; } a = bstr_next(a); shift = bstrtoul(a); if (shift == ULONG_MAX) { PARSE_ERR(a, "meta: invalid shift, must " \ "be numeric"); return PARSE_FAILURE; } obj->shift = (__u8) shift; a = bstr_next(a); } else if (!bstrcmp(a, "mask")) { unsigned long mask; if (a->next == NULL) { PARSE_ERR(a, "meta: missing argument"); return PARSE_FAILURE; } a = bstr_next(a); mask = bstrtoul(a); if (mask == ULONG_MAX) { PARSE_ERR(a, "meta: invalid mask, must be " \ "numeric"); return PARSE_FAILURE; } *dst = (unsigned long) mask; a = bstr_next(a); } else break; } return a; not_compatible: PARSE_ERR(arg, "lvalue and rvalue are not compatible."); return PARSE_FAILURE; }
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)); if (sdmr.VolReadTime >= 0) { mr.VolReadTime = sdmr.VolReadTime; } if (sdmr.VolWriteTime >= 0) { 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; }
static int cpustr_to_cpulist_physical(bstring bcpustr, int* cpulist, int length) { topology_init(); CpuTopology_t cpuid_topology = get_cpuTopology(); affinity_init(); AffinityDomains_t affinity = get_affinityDomains(); bstring bdomain; bstring blist; int domainidx = -1; if (bstrchrp(bcpustr, ':', 0) != BSTR_ERR) { struct bstrList* strlist = bstrListCreate(); strlist = bsplit(bcpustr, ':'); bdomain = bstrcpy(strlist->entry[0]); blist = bstrcpy(strlist->entry[1]); bstrListDestroy(strlist); } else { bdomain = bformat("N"); blist = bstrcpy(bcpustr); } for (int i=0; i<affinity->numberOfAffinityDomains; i++) { if (bstrcmp(bdomain, affinity->domains[i].tag) == 0) { domainidx = i; break; } } if (domainidx < 0) { fprintf(stderr, "Cannot find domain %s\n", bdata(bdomain)); bdestroy(bdomain); bdestroy(blist); return 0; } struct bstrList* strlist = bstrListCreate(); strlist = bsplit(blist, ','); int insert = 0; for (int i=0;i< strlist->qty; i++) { if (bstrchrp(strlist->entry[i], '-', 0) != BSTR_ERR) { struct bstrList* indexlist = bstrListCreate(); indexlist = bsplit(strlist->entry[i], '-'); if (atoi(bdata(indexlist->entry[0])) <= atoi(bdata(indexlist->entry[1]))) { for (int j=atoi(bdata(indexlist->entry[0])); j<=atoi(bdata(indexlist->entry[1]));j++) { if (cpu_in_domain(domainidx, j)) { cpulist[insert] = j; insert++; if (insert == length) { bstrListDestroy(indexlist); goto physical_done; } } else { fprintf(stderr, "CPU %d not in domain %s\n", j, bdata(affinity->domains[domainidx].tag)); } } } else { for (int j=atoi(bdata(indexlist->entry[0])); j>=atoi(bdata(indexlist->entry[1]));j--) { if (cpu_in_domain(domainidx, j)) { cpulist[insert] = j; insert++; if (insert == length) { bstrListDestroy(indexlist); goto physical_done; } } else { fprintf(stderr, "CPU %d not in domain %s\n", j, bdata(affinity->domains[domainidx].tag)); } } } bstrListDestroy(indexlist); } else { int cpu = atoi(bdata(strlist->entry[i])); if (cpu_in_domain(domainidx, cpu)) { cpulist[insert] = cpu; insert++; if (insert == length) { goto physical_done; } } else { fprintf(stderr, "CPU %d not in domain %s\n", cpu, bdata(affinity->domains[domainidx].tag)); } } } physical_done: bstrListDestroy(strlist); bdestroy(bdomain); bdestroy(blist); return insert; }
int main (int argc, char *argv[]) { int ch; char *jobids = (char *)"1"; char *path=NULL, *client=NULL; uint64_t limit=0; bool clean=false; setlocale(LC_ALL, ""); bindtextdomain("bareos", LOCALEDIR); textdomain("bareos"); init_stack_dump(); Dmsg0(0, "Starting bvfs_test tool\n"); my_name_is(argc, argv, "bvfs_test"); init_msg(NULL, NULL); OSDependentInit(); while ((ch = getopt(argc, argv, "h:c:l:d:D:n:P:Su:vf:w:?j:p:f:T")) != -1) { switch (ch) { case 'd': /* debug level */ if (*optarg == 't') { dbg_timestamp = true; } else { debug_level = atoi(optarg); if (debug_level <= 0) { debug_level = 1; } } break; case 'D': db_driver = optarg; break; case 'l': limit = str_to_int64(optarg); break; case 'c': client = optarg; break; case 'h': db_host = optarg; break; case 'n': db_name = optarg; break; case 'w': working_directory = optarg; break; case 'u': db_user = optarg; break; case 'P': db_password = optarg; break; case 'v': verbose++; break; case 'p': path = optarg; break; case 'f': file = optarg; break; case 'j': jobids = optarg; break; case 'T': clean = true; break; case '?': default: usage(); } } argc -= optind; argv += optind; if (argc != 0) { Pmsg0(0, _("Wrong number of arguments: \n")); usage(); } JCR *bjcr = new_jcr(sizeof(JCR), NULL); bjcr->JobId = getpid(); bjcr->setJobType(JT_CONSOLE); bjcr->setJobLevel(L_FULL); bjcr->JobStatus = JS_Running; bjcr->client_name = get_pool_memory(PM_FNAME); pm_strcpy(bjcr->client_name, "Dummy.Client.Name"); bstrncpy(bjcr->Job, "bvfs_test", sizeof(bjcr->Job)); if ((db = db_init_database(NULL, NULL, db_name, db_user, db_password, db_host, 0, NULL)) == NULL) { Emsg0(M_ERROR_TERM, 0, _("Could not init Bareos database\n")); } Dmsg1(0, "db_type=%s\n", db_get_type(db)); if (!db_open_database(NULL, db)) { Emsg0(M_ERROR_TERM, 0, db_strerror(db)); } Dmsg0(200, "Database opened\n"); if (verbose) { Pmsg2(000, _("Using Database: %s, User: %s\n"), db_name, db_user); } bjcr->db = db; if (clean) { Pmsg0(0, "Clean old table\n"); db_sql_query(db, "DELETE FROM PathHierarchy", NULL, NULL); db_sql_query(db, "UPDATE Job SET HasCache=0", NULL, NULL); db_sql_query(db, "DELETE FROM PathVisibility", NULL, NULL); bvfs_update_cache(bjcr, db); } Bvfs fs(bjcr, db); fs.set_handler(result_handler, &fs); fs.set_jobids(jobids); fs.update_cache(); if (limit) fs.set_limit(limit); if (path) { fs.ch_dir(path); fs.ls_special_dirs(); fs.ls_dirs(); while (fs.ls_files()) { fs.next_offset(); } if (fnid && client) { Pmsg0(0, "---------------------------------------------\n"); Pmsg1(0, "Getting file version for %s\n", file); fs.get_all_file_versions(fs.get_pwd(), fnid, client); } exit (0); } Pmsg0(0, "list /\n"); fs.ch_dir("/"); fs.ls_special_dirs(); fs.ls_dirs(); fs.ls_files(); Pmsg0(0, "list /tmp/\n"); fs.ch_dir("/tmp/"); fs.ls_special_dirs(); fs.ls_dirs(); fs.ls_files(); Pmsg0(0, "list /tmp/regress/\n"); fs.ch_dir("/tmp/regress/"); fs.ls_special_dirs(); fs.ls_files(); fs.ls_dirs(); Pmsg0(0, "list /tmp/regress/build/\n"); fs.ch_dir("/tmp/regress/build/"); fs.ls_special_dirs(); fs.ls_dirs(); fs.ls_files(); fs.get_all_file_versions(1, 347, "zog4-fd"); char p[200]; strcpy(p, "/tmp/toto/rep/"); bvfs_parent_dir(p); if(!bstrcmp(p, "/tmp/toto/")) { Pmsg0(000, "Error in bvfs_parent_dir\n"); } bvfs_parent_dir(p); if(!bstrcmp(p, "/tmp/")) { Pmsg0(000, "Error in bvfs_parent_dir\n"); } bvfs_parent_dir(p); if(!bstrcmp(p, "/")) { Pmsg0(000, "Error in bvfs_parent_dir\n"); } bvfs_parent_dir(p); if(!bstrcmp(p, "")) { Pmsg0(000, "Error in bvfs_parent_dir\n"); } bvfs_parent_dir(p); if(!bstrcmp(p, "")) { Pmsg0(000, "Error in bvfs_parent_dir\n"); } return 0; }
/* * Search counter variables */ static var_rc_t lookup_counter_var(var_t *ctx, void *my_ctx, const char *var_ptr, int var_len, int var_inc, int var_index, const char **val_ptr, int *val_len, int *val_size) { COUNTERRES *counter; POOL_MEM buf(PM_NAME); var_rc_t status = VAR_ERR_UNDEFINED_VARIABLE; buf.check_size(var_len + 1); pm_memcpy(buf, var_ptr, var_len); (buf.c_str())[var_len] = 0; LockRes(); for (counter = NULL; (counter = (COUNTERRES *)GetNextRes(R_COUNTER, (RES *)counter)); ) { if (bstrcmp(counter->name(), buf.c_str())) { Dmsg2(100, "Counter=%s val=%d\n", buf.c_str(), counter->CurrentValue); /* * -1 => return size of array */ if (var_index == -1) { Mmsg(buf, "%d", counter->CurrentValue); *val_len = Mmsg(buf, "%d", strlen(buf.c_str())); *val_ptr = bstrdup(buf.c_str()); *val_size = 0; /* don't try to free val_ptr */ return VAR_OK; } else { Mmsg(buf, "%d", counter->CurrentValue); *val_ptr = bstrdup(buf.c_str()); *val_len = strlen(buf.c_str()); *val_size = *val_len + 1; } if (var_inc) { /* increment the variable? */ if (counter->CurrentValue == counter->MaxValue) { counter->CurrentValue = counter->MinValue; } else { counter->CurrentValue++; } if (counter->Catalog) { /* update catalog if need be */ COUNTER_DBR cr; JCR *jcr = (JCR *)my_ctx; memset(&cr, 0, sizeof(cr)); bstrncpy(cr.Counter, counter->name(), sizeof(cr.Counter)); cr.MinValue = counter->MinValue; cr.MaxValue = counter->MaxValue; cr.CurrentValue = counter->CurrentValue; Dmsg1(100, "New value=%d\n", cr.CurrentValue); if (counter->WrapCounter) { bstrncpy(cr.WrapCounter, counter->WrapCounter->name(), sizeof(cr.WrapCounter)); } else { cr.WrapCounter[0] = 0; } if (!db_update_counter_record(jcr, jcr->db, &cr)) { Jmsg(jcr, M_ERROR, 0, _("Count not update counter %s: ERR=%s\n"), counter->name(), db_strerror(jcr->db)); } } } status = VAR_OK; break; } } UnlockRes(); return status; }
static int cpustr_to_cpulist_expression(bstring bcpustr, int* cpulist, int length) { topology_init(); CpuTopology_t cpuid_topology = get_cpuTopology(); affinity_init(); AffinityDomains_t affinity = get_affinityDomains(); bstring bdomain; int domainidx = -1; int count = 0; int stride = 0; int chunk = 0; if (bstrchrp(bcpustr, 'E', 0) != 0) { fprintf(stderr, "Not a valid CPU expression\n"); return 0; } struct bstrList* strlist = bstrListCreate(); strlist = bsplit(bcpustr, ':'); if (strlist->qty == 3) { bdomain = bstrcpy(strlist->entry[1]); count = atoi(bdata(strlist->entry[2])); stride = 1; chunk = 1; } else if (strlist->qty == 5) { bdomain = bstrcpy(strlist->entry[1]); count = atoi(bdata(strlist->entry[2])); chunk = atoi(bdata(strlist->entry[3])); stride = atoi(bdata(strlist->entry[4])); } for (int i=0; i<affinity->numberOfAffinityDomains; i++) { if (bstrcmp(bdomain, affinity->domains[i].tag) == 0) { domainidx = i; break; } } if (domainidx < 0) { fprintf(stderr, "Cannot find domain %s\n", bdata(bdomain)); bstrListDestroy(strlist); return 0; } int offset = 0; int insert = 0; for (int i=0;i<count;i++) { for (int j=0;j<chunk && offset+j<affinity->domains[domainidx].numberOfProcessors;j++) { cpulist[insert] = affinity->domains[domainidx].processorList[offset + j]; insert++; if (insert == length) goto expression_done; } offset += stride; if (offset >= affinity->domains[domainidx].numberOfProcessors) { offset = 0; } if (insert >= count) goto expression_done; } bstrListDestroy(strlist); return 0; expression_done: bstrListDestroy(strlist); return insert; }
/* * Convert a volume name into a slot selection. */ static inline bool get_slot_list_using_volname(UAContext *ua, const char *volumename, dlist *vol_list, char *wanted_slot_list, char *selected_slot_list, int max_slots) { vol_list_t *vl1, *vl2; bool found = false; if (is_name_valid(volumename)) { foreach_dlist(vl1, vol_list) { /* * We only select normal and import/export slots. */ switch (vl1->Type) { case slot_type_normal: case slot_type_import: /* * When the source slot list is limited we check to * see if this slot should be taken into consideration. */ if (wanted_slot_list && !bit_is_set(vl1->Slot - 1, wanted_slot_list)) { continue; } switch (vl1->Content) { case slot_content_full: /* * See if the wanted volume is loaded in this slot. */ Dmsg3(100, "Checking for volume name in slot %d, wanted %s, found %s\n", vl1->Slot, volumename, (vl1->VolName) ? vl1->VolName : "NULL"); if (vl1->VolName && bstrcmp(vl1->VolName, volumename)) { found = true; } break; case slot_content_empty: /* * See if this slot is loaded in drive and drive contains wanted volume */ vl2 = is_loaded_in_drive(vol_list, vl1->Slot); if (vl2 != NULL) { Dmsg3(100, "Checking for volume name in drive %d, wanted %s, found %s\n", vl2->Slot, volumename, (vl2->VolName) ? vl2->VolName : "NULL"); if (vl2->VolName && bstrcmp(vl2->VolName, volumename)) { found = true; } } else { Dmsg1(100, "Skipping empty slot %d\n", vl1->Slot); } break; default: break; } break; default: break; } /* * If we found a match break the loop. */ if (found) { break; } } /* * See if we found the wanted volumename in the list * of available slots in the autochanger and mark the * slot in the slot_list or give a warning when the * volumename was not found. */ if (found) { set_bit(vl1->Slot - 1, selected_slot_list); } else { Dmsg1(100, "No volume named %s in changer or in selected source slots.\n", volumename); ua->warning_msg(_("No volume named %s in changer or in selected source slots.\n"), volumename); } } else {
static bRC getXattr(bpContext *ctx, xattr_pkt *xp) { char *bp; bool skip_xattr; int status, current_size; int32_t xattr_value_length; POOL_MEM xattr_value(PM_MESSAGE); plugin_ctx *p_ctx = (plugin_ctx *)ctx->pContext; if (!p_ctx) { return bRC_Error; } /* * See if we need to retrieve the xattr list. */ if (!p_ctx->processing_xattr) { while (1) { current_size = sizeof_pool_memory(p_ctx->xattr_list); status = ceph_llistxattr(p_ctx->cmount, xp->fname, p_ctx->xattr_list, current_size); if (status < 0) { berrno be; switch (status) { #if defined(ENOTSUP) || defined(EOPNOTSUPP) #if defined(ENOTSUP) case ENOTSUP: #endif #if defined(EOPNOTSUPP) && EOPNOTSUPP != ENOTSUP case EOPNOTSUPP: #endif return bRC_OK; #endif case -ERANGE: /* * Not enough room in buffer double its size and retry. */ p_ctx->xattr_list = check_pool_memory_size(p_ctx->xattr_list, current_size * 2); continue; default: Jmsg(ctx, M_ERROR, "ceph_llistxattr(%s) failed: %s\n", xp->fname, be.bstrerror(-status)); return bRC_Error; } } /* * Retrieved the xattr list so break the loop. */ break; } p_ctx->next_xattr_name = p_ctx->xattr_list; p_ctx->processing_xattr = true; } while (1) { /* * On some OSes you also get the acls in the extented attribute list. * So we check if we are already backing up acls and if we do we * don't store the extended attribute with the same info. */ skip_xattr = false; if (bit_is_set(FO_ACL, p_ctx->flags)) { for (int cnt = 0; xattr_acl_skiplist[cnt] != NULL; cnt++) { if (bstrcmp(p_ctx->next_xattr_name, xattr_acl_skiplist[cnt])) { skip_xattr = true; break; } } } if (!skip_xattr) { current_size = xattr_value.max_size(); xattr_value_length = ceph_lgetxattr(p_ctx->cmount, xp->fname, p_ctx->next_xattr_name, xattr_value.c_str(), current_size); if (xattr_value_length < 0) { berrno be; switch (xattr_value_length) { #if defined(ENOATTR) || defined(ENODATA) #if defined(ENOATTR) case ENOATTR: #endif #if defined(ENODATA) && ENOATTR != ENODATA case ENODATA: #endif skip_xattr = true; break; #endif #if defined(ENOTSUP) || defined(EOPNOTSUPP) #if defined(ENOTSUP) case ENOTSUP: #endif #if defined(EOPNOTSUPP) && EOPNOTSUPP != ENOTSUP case EOPNOTSUPP: #endif return bRC_OK; #endif case -ERANGE: /* * Not enough room in buffer double its size and retry. */ xattr_value.check_size(current_size * 2); continue; default: Jmsg(ctx, M_ERROR, "ceph_lgetxattr(%s) failed: %s\n", xp->fname, be.bstrerror(-xattr_value_length)); return bRC_Error; } } /* * Retrieved the xattr so break the loop. */ break; } else { /* * No data to retrieve so break the loop. */ break; } } if (!skip_xattr) { xp->name = bstrdup(p_ctx->next_xattr_name); xp->name_length = strlen(xp->name) + 1; xp->value = (char *)malloc(xattr_value_length); memcpy(xp->value, xattr_value.c_str(), xattr_value_length); xp->value_length = xattr_value_length; } /* * See if there are more xattr to process. */ bp = strchr(p_ctx->next_xattr_name, '\0'); if (++bp != '\0') { p_ctx->next_xattr_name = bp; return bRC_More; } else { /* * No more reset processing_xattr flag. */ p_ctx->processing_xattr = false; return bRC_OK; } }
/* Save the new resource by chaining it into the head list for * the resource. If this is pass 2, we update any resource * pointers (currently only in the Job resource). */ void save_resource(int type, RES_ITEM *items, int pass) { URES *res; int rindex = type - r_first; int i, size; int error = 0; /* * Ensure that all required items are present */ for (i=0; items[i].name; i++) { if (items[i].flags & ITEM_REQUIRED) { if (!bit_is_set(i, res_all.res_dir.hdr.item_present)) { Emsg2(M_ABORT, 0, _("%s item is required in %s resource, but not found.\n"), items[i].name, resources[rindex]); } } } /* During pass 2, we looked up pointers to all the resources * referrenced in the current resource, , now we * must copy their address from the static record to the allocated * record. */ if (pass == 2) { switch (type) { /* Resources not containing a resource */ case R_CONSOLE: case R_DIRECTOR: break; default: Emsg1(M_ERROR, 0, _("Unknown resource type %d\n"), type); error = 1; break; } /* Note, the resoure name was already saved during pass 1, * so here, we can just release it. */ if (res_all.res_dir.hdr.name) { free(res_all.res_dir.hdr.name); res_all.res_dir.hdr.name = NULL; } if (res_all.res_dir.hdr.desc) { free(res_all.res_dir.hdr.desc); res_all.res_dir.hdr.desc = NULL; } return; } /* The following code is only executed during pass 1 */ switch (type) { case R_CONSOLE: size = sizeof(CONRES); break; case R_DIRECTOR: size = sizeof(DIRRES); break; default: printf(_("Unknown resource type %d\n"), type); error = 1; size = 1; break; } /* Common */ if (!error) { res = (URES *)malloc(size); memcpy(res, &res_all, size); if (!res_head[rindex]) { res_head[rindex] = (RES *)res; /* store first entry */ } else { RES *next, *last; for (last=next=res_head[rindex]; next; next=next->next) { last = next; if (bstrcmp(next->name, res->res_dir.hdr.name)) { Emsg2(M_ERROR_TERM, 0, _("Attempt to define second %s resource named \"%s\" is not permitted.\n"), resources[rindex].name, res->res_dir.hdr.name); } } last->next = (RES *)res; Dmsg2(90, "Inserting %s res: %s\n", res_to_str(type), res->res_dir.hdr.name); } } }
int reverse_compare(void *a, void *b) { return bstrcmp((bstring)b, (bstring)a); }
/* * This routine is called only during a Verify */ void get_attributes_and_compare_to_catalog(JCR *jcr, JobId_t JobId) { BSOCK *fd; int n, len; FILE_DBR fdbr; struct stat statf; /* file stat */ struct stat statc; /* catalog stat */ POOL_MEM buf(PM_MESSAGE); POOLMEM *fname = get_pool_memory(PM_FNAME); int do_Digest = CRYPTO_DIGEST_NONE; int32_t file_index = 0; memset(&fdbr, 0, sizeof(fdbr)); fd = jcr->file_bsock; fdbr.JobId = JobId; jcr->FileIndex = 0; Dmsg0(20, "dir: waiting to receive file attributes\n"); /* * Get Attributes and Signature from File daemon * We expect: * FileIndex * Stream * Options or Digest (MD5/SHA1) * Filename * Attributes * Link name ??? */ while ((n=bget_dirmsg(fd)) >= 0 && !job_canceled(jcr)) { int stream; char *attr, *p, *fn; POOL_MEM Opts_Digest(PM_MESSAGE); /* Verify Opts or MD5/SHA1 digest */ if (job_canceled(jcr)) { goto bail_out; } fname = check_pool_memory_size(fname, fd->msglen); jcr->fname = check_pool_memory_size(jcr->fname, fd->msglen); Dmsg1(200, "Atts+Digest=%s\n", fd->msg); if ((len = sscanf(fd->msg, "%ld %d %100s", &file_index, &stream, fname)) != 3) { Jmsg3(jcr, M_FATAL, 0, _("dird<filed: bad attributes, expected 3 fields got %d\n" " mslen=%d msg=%s\n"), len, fd->msglen, fd->msg); goto bail_out; } /* * We read the Options or Signature into fname * to prevent overrun, now copy it to proper location. */ pm_strcpy(Opts_Digest, fname); p = fd->msg; skip_nonspaces(&p); /* skip FileIndex */ skip_spaces(&p); skip_nonspaces(&p); /* skip Stream */ skip_spaces(&p); skip_nonspaces(&p); /* skip Opts_Digest */ p++; /* skip space */ fn = fname; while (*p != 0) { *fn++ = *p++; /* copy filename */ } *fn = *p++; /* term filename and point to attribs */ attr = p; /* * Got attributes stream, decode it */ switch (stream) { case STREAM_UNIX_ATTRIBUTES: case STREAM_UNIX_ATTRIBUTES_EX: int32_t LinkFIf, LinkFIc; Dmsg2(400, "file_index=%d attr=%s\n", file_index, attr); jcr->JobFiles++; jcr->FileIndex = file_index; /* remember attribute file_index */ jcr->previous_jr.FileIndex = file_index; decode_stat(attr, &statf, sizeof(statf), &LinkFIf); /* decode file stat packet */ do_Digest = CRYPTO_DIGEST_NONE; jcr->fn_printed = false; pm_strcpy(jcr->fname, fname); /* move filename into JCR */ Dmsg2(040, "dird<filed: stream=%d %s\n", stream, jcr->fname); Dmsg1(020, "dird<filed: attr=%s\n", attr); /* * Find equivalent record in the database */ fdbr.FileId = 0; if (!db_get_file_attributes_record(jcr, jcr->db, jcr->fname, &jcr->previous_jr, &fdbr)) { Jmsg(jcr, M_INFO, 0, _("New file: %s\n"), jcr->fname); Dmsg1(020, _("File not in catalog: %s\n"), jcr->fname); jcr->setJobStatus(JS_Differences); continue; } else { /* * mark file record as visited by stuffing the * current JobId, which is unique, into the MarkId field. */ db_mark_file_record(jcr, jcr->db, fdbr.FileId, jcr->JobId); } Dmsg3(400, "Found %s in catalog. inx=%d Opts=%s\n", jcr->fname, file_index, Opts_Digest.c_str()); decode_stat(fdbr.LStat, &statc, sizeof(statc), &LinkFIc); /* decode catalog stat */ /* * Loop over options supplied by user and verify the * fields he requests. */ for (p = Opts_Digest.c_str(); *p; p++) { char ed1[30], ed2[30]; switch (*p) { case 'i': /* compare INODEs */ if (statc.st_ino != statf.st_ino) { prt_fname(jcr); Jmsg(jcr, M_INFO, 0, _(" st_ino differ. Cat: %s File: %s\n"), edit_uint64((uint64_t)statc.st_ino, ed1), edit_uint64((uint64_t)statf.st_ino, ed2)); jcr->setJobStatus(JS_Differences); } break; case 'p': /* permissions bits */ if (statc.st_mode != statf.st_mode) { prt_fname(jcr); Jmsg(jcr, M_INFO, 0, _(" st_mode differ. Cat: %x File: %x\n"), (uint32_t)statc.st_mode, (uint32_t)statf.st_mode); jcr->setJobStatus(JS_Differences); } break; case 'n': /* number of links */ if (statc.st_nlink != statf.st_nlink) { prt_fname(jcr); Jmsg(jcr, M_INFO, 0, _(" st_nlink differ. Cat: %d File: %d\n"), (uint32_t)statc.st_nlink, (uint32_t)statf.st_nlink); jcr->setJobStatus(JS_Differences); } break; case 'u': /* user id */ if (statc.st_uid != statf.st_uid) { prt_fname(jcr); Jmsg(jcr, M_INFO, 0, _(" st_uid differ. Cat: %u File: %u\n"), (uint32_t)statc.st_uid, (uint32_t)statf.st_uid); jcr->setJobStatus(JS_Differences); } break; case 'g': /* group id */ if (statc.st_gid != statf.st_gid) { prt_fname(jcr); Jmsg(jcr, M_INFO, 0, _(" st_gid differ. Cat: %u File: %u\n"), (uint32_t)statc.st_gid, (uint32_t)statf.st_gid); jcr->setJobStatus(JS_Differences); } break; case 's': /* size */ if (statc.st_size != statf.st_size) { prt_fname(jcr); Jmsg(jcr, M_INFO, 0, _(" st_size differ. Cat: %s File: %s\n"), edit_uint64((uint64_t)statc.st_size, ed1), edit_uint64((uint64_t)statf.st_size, ed2)); jcr->setJobStatus(JS_Differences); } break; case 'a': /* access time */ if (statc.st_atime != statf.st_atime) { prt_fname(jcr); Jmsg(jcr, M_INFO, 0, _(" st_atime differs\n")); jcr->setJobStatus(JS_Differences); } break; case 'm': if (statc.st_mtime != statf.st_mtime) { prt_fname(jcr); Jmsg(jcr, M_INFO, 0, _(" st_mtime differs\n")); jcr->setJobStatus(JS_Differences); } break; case 'c': /* ctime */ if (statc.st_ctime != statf.st_ctime) { prt_fname(jcr); Jmsg(jcr, M_INFO, 0, _(" st_ctime differs\n")); jcr->setJobStatus(JS_Differences); } break; case 'd': /* file size decrease */ if (statc.st_size > statf.st_size) { prt_fname(jcr); Jmsg(jcr, M_INFO, 0, _(" st_size decrease. Cat: %s File: %s\n"), edit_uint64((uint64_t)statc.st_size, ed1), edit_uint64((uint64_t)statf.st_size, ed2)); jcr->setJobStatus(JS_Differences); } break; case '5': /* compare MD5 */ Dmsg1(500, "set Do_MD5 for %s\n", jcr->fname); do_Digest = CRYPTO_DIGEST_MD5; break; case '1': /* compare SHA1 */ do_Digest = CRYPTO_DIGEST_SHA1; break; case ':': case 'V': default: break; } } break; case STREAM_RESTORE_OBJECT: Dmsg1(400, "RESTORE_OBJECT %s\n", jcr->fname); break; default: /* * Got Digest Signature from Storage daemon * It came across in the Opts_Digest field. */ if (crypto_digest_stream_type(stream) != CRYPTO_DIGEST_NONE) { Dmsg2(400, "stream=Digest inx=%d Digest=%s\n", file_index, Opts_Digest.c_str()); /* * When ever we get a digest it MUST have been * preceded by an attributes record, which sets attr_file_index */ if (jcr->FileIndex != (uint32_t)file_index) { Jmsg2(jcr, M_FATAL, 0, _("MD5/SHA1 index %d not same as attributes %d\n"), file_index, jcr->FileIndex); goto bail_out; } if (do_Digest != CRYPTO_DIGEST_NONE) { db_escape_string(jcr, jcr->db, buf.c_str(), Opts_Digest.c_str(), strlen(Opts_Digest.c_str())); if (!bstrcmp(buf.c_str(), fdbr.Digest)) { prt_fname(jcr); Jmsg(jcr, M_INFO, 0, _(" %s differs. File=%s Cat=%s\n"), stream_to_ascii(stream), buf.c_str(), fdbr.Digest); jcr->setJobStatus(JS_Differences); } do_Digest = CRYPTO_DIGEST_NONE; } } break; } jcr->JobFiles = file_index; } if (is_bnet_error(fd)) { berrno be; Jmsg2(jcr, M_FATAL, 0, _("dir<filed: bad attributes from filed n=%d : %s\n"), n, be.bstrerror()); goto bail_out; } /* Now find all the files that are missing -- i.e. all files in * the database where the MarkId != current JobId */ jcr->fn_printed = false; Mmsg(buf, "SELECT Path.Path,Filename.Name FROM File,Path,Filename " "WHERE File.JobId=%d AND File.FileIndex > 0 " "AND File.MarkId!=%d AND File.PathId=Path.PathId " "AND File.FilenameId=Filename.FilenameId", JobId, jcr->JobId); /* missing_handler is called for each file found */ db_sql_query(jcr->db, buf.c_str(), missing_handler, (void *)jcr); if (jcr->fn_printed) { jcr->setJobStatus(JS_Differences); } bail_out: free_pool_memory(fname); }
static int u32_parse_eopt(struct nlmsghdr *n, struct tcf_ematch_hdr *hdr, struct bstr *args) { struct bstr *a; int align, nh_len; unsigned long key, mask, offmask = 0, offset; struct tc_u32_key u_key; memset(&u_key, 0, sizeof(u_key)); #define PARSE_ERR(CARG, FMT, ARGS...) \ em_parse_error(EINVAL, args, CARG, &u32_ematch_util, FMT ,##ARGS) if (args == NULL) return PARSE_ERR(args, "u32: missing arguments"); if (!bstrcmp(args, "u8")) align = 1; else if (!bstrcmp(args, "u16")) align = 2; else if (!bstrcmp(args, "u32")) align = 4; else return PARSE_ERR(args, "u32: invalid alignment"); a = bstr_next(args); if (a == NULL) return PARSE_ERR(a, "u32: missing key"); key = bstrtoul(a); if (key == ULONG_MAX) return PARSE_ERR(a, "u32: invalid key, must be numeric"); a = bstr_next(a); if (a == NULL) return PARSE_ERR(a, "u32: missing mask"); mask = bstrtoul(a); if (mask == ULONG_MAX) return PARSE_ERR(a, "u32: invalid mask, must be numeric"); a = bstr_next(a); if (a == NULL || bstrcmp(a, "at") != 0) return PARSE_ERR(a, "u32: missing \"at\""); a = bstr_next(a); if (a == NULL) return PARSE_ERR(a, "u32: missing offset"); nh_len = strlen("nexthdr+"); if (a->len > nh_len && !memcmp(a->data, "nexthdr+", nh_len)) { char buf[a->len - nh_len + 1]; offmask = -1; memcpy(buf, a->data + nh_len, a->len - nh_len); offset = strtoul(buf, NULL, 0); } else if (!bstrcmp(a, "nexthdr+")) { a = bstr_next(a); if (a == NULL) return PARSE_ERR(a, "u32: missing offset"); offset = bstrtoul(a); } else offset = bstrtoul(a); if (offset == ULONG_MAX) return PARSE_ERR(a, "u32: invalid offset"); if (a->next) return PARSE_ERR(a->next, "u32: unexpected trailer"); switch (align) { case 1: if (key > 0xFF) return PARSE_ERR(a, "Illegal key (>0xFF)"); if (mask > 0xFF) return PARSE_ERR(a, "Illegal mask (>0xFF)"); key <<= 24 - ((offset & 3) * 8); mask <<= 24 - ((offset & 3) * 8); offset &= ~3; break; case 2: if (key > 0xFFFF) return PARSE_ERR(a, "Illegal key (>0xFFFF)"); if (mask > 0xFFFF) return PARSE_ERR(a, "Illegal mask (>0xFFFF)"); if ((offset & 3) == 0) { key <<= 16; mask <<= 16; } offset &= ~3; break; } key = htonl(key); mask = htonl(mask); if (offset % 4) return PARSE_ERR(a, "u32: invalid offset alignment, " \ "must be aligned to 4."); key &= mask; u_key.mask = mask; u_key.val = key; u_key.off = offset; u_key.offmask = offmask; addraw_l(n, MAX_MSG, hdr, sizeof(*hdr)); addraw_l(n, MAX_MSG, &u_key, sizeof(u_key)); #undef PARSE_ERR return 0; }
/* * Execute a command from the UA */ bool do_a_dot_command(UAContext *ua) { int i; int len; bool ok = false; bool found = false; BSOCK *user = ua->UA_sock; Dmsg1(1400, "Dot command: %s\n", user->msg); if (ua->argc == 0) { return false; } len = strlen(ua->argk[0]); if (len == 1) { if (ua->api) { user->signal(BNET_CMD_BEGIN); } if (ua->api) { user->signal(BNET_CMD_OK); } return true; /* no op */ } for (i = 0; i < comsize; i++) { /* search for command */ if (bstrncasecmp(ua->argk[0], _(commands[i].key), len)) { bool gui = ua->gui; /* * Check if this command is authorized in RunScript */ if (ua->runscript && !commands[i].use_in_rs) { ua->error_msg(_("Can't use %s command in a runscript"), ua->argk[0]); break; } /* * If we need to audit this event do it now. */ if (audit_event_wanted(ua, commands[i].audit_event)) { log_audit_event_cmdline(ua); } /* * Check if command permitted, but "quit" is always OK */ if (!bstrcmp(ua->argk[0], NT_(".quit")) && !acl_access_ok(ua, Command_ACL, ua->argk[0], len, true)) { break; } Dmsg1(100, "Cmd: %s\n", ua->cmd); ua->gui = true; if (ua->api) { user->signal(BNET_CMD_BEGIN); } ok = (*commands[i].func)(ua, ua->cmd); /* go execute command */ if (ua->api) { user->signal(ok?BNET_CMD_OK:BNET_CMD_FAILED); } ua->gui = gui; found = true; break; } } if (!found) { ua->error_msg("%s%s", ua->argk[0], _(": is an invalid command.\n")); ok = false; } return ok; }
bool DEVICE::scan_dir_for_volume(DCR *dcr) { DIR* dp; struct dirent *entry, *result; int name_max; char *mount_point; VOLUME_CAT_INFO dcrVolCatInfo, devVolCatInfo; char VolumeName[MAX_NAME_LENGTH]; struct stat statp; bool found = false; POOL_MEM fname(PM_FNAME); bool need_slash = false; int len; dcrVolCatInfo = dcr->VolCatInfo; /* structure assignment */ devVolCatInfo = VolCatInfo; /* structure assignment */ bstrncpy(VolumeName, dcr->VolumeName, sizeof(VolumeName)); name_max = pathconf(".", _PC_NAME_MAX); if (name_max < 1024) { name_max = 1024; } if (device->mount_point) { mount_point = device->mount_point; } else { mount_point = device->device_name; } if (!(dp = opendir(mount_point))) { berrno be; dev_errno = errno; Dmsg3(29, "scan_dir_for_vol: failed to open dir %s (dev=%s), ERR=%s\n", mount_point, print_name(), be.bstrerror()); goto get_out; } len = strlen(mount_point); if (len > 0) { need_slash = !IsPathSeparator(mount_point[len - 1]); } entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 1000); for ( ;; ) { if ((readdir_r(dp, entry, &result) != 0) || (result == NULL)) { dev_errno = EIO; Dmsg2(129, "scan_dir_for_vol: failed to find suitable file in dir %s (dev=%s)\n", mount_point, print_name()); break; } if (bstrcmp(result->d_name, ".") || bstrcmp(result->d_name, "..")) { continue; } if (!is_volume_name_legal(result->d_name)) { continue; } pm_strcpy(fname, mount_point); if (need_slash) { pm_strcat(fname, "/"); } pm_strcat(fname, result->d_name); if (lstat(fname.c_str(), &statp) != 0 || !S_ISREG(statp.st_mode)) { continue; /* ignore directories & special files */ } /* * OK, we got a different volume mounted. First save the * requested Volume info (dcr) structure, then query if * this volume is really OK. If not, put back the desired * volume name, mark it not in changer and continue. */ /* Check if this is a valid Volume in the pool */ bstrncpy(dcr->VolumeName, result->d_name, sizeof(dcr->VolumeName)); if (!dcr->dir_get_volume_info(GET_VOL_INFO_FOR_WRITE)) { continue; } /* This was not the volume we expected, but it is OK with * the Director, so use it. */ VolCatInfo = dcr->VolCatInfo; /* structure assignment */ found = true; break; /* got a Volume */ } free(entry); closedir(dp); get_out: if (!found) { /* Restore VolumeName we really wanted */ bstrncpy(dcr->VolumeName, VolumeName, sizeof(dcr->VolumeName)); dcr->VolCatInfo = dcrVolCatInfo; /* structure assignment */ VolCatInfo = devVolCatInfo; /* structure assignment */ } Dsm_check(100); return found; }
static struct bstr strip_ext(struct bstr str) { int dotpos = bstrrchr(str, '.'); if (dotpos < 0) return str; return (struct bstr){str.start, dotpos}; } static struct bstr get_ext(struct bstr s) { int dotpos = bstrrchr(s, '.'); if (dotpos < 0) return (struct bstr){NULL, 0}; return bstr_splice(s, dotpos + 1, s.len); } bool mp_might_be_subtitle_file(const char *filename) { return is_sub_ext(get_ext(bstr0(filename))); } static int compare_sub_filename(const void *a, const void *b) { const struct subfn *s1 = a; const struct subfn *s2 = b; return strcoll(s1->fname, s2->fname); } static int compare_sub_priority(const void *a, const void *b) { const struct subfn *s1 = a; const struct subfn *s2 = b; if (s1->priority > s2->priority) return -1; if (s1->priority < s2->priority) return 1; return strcoll(s1->fname, s2->fname); } static struct bstr guess_lang_from_filename(struct bstr name) { if (name.len < 2) return (struct bstr){NULL, 0}; int n = 0; int i = name.len - 1; if (name.start[i] == ')' || name.start[i] == ']') i--; while (i >= 0 && isalpha(name.start[i])) { n++; if (n > 3) return (struct bstr){NULL, 0}; i--; } if (n < 2) return (struct bstr){NULL, 0}; return (struct bstr){name.start + i + 1, n}; } /** * @brief Append all the subtitles in the given path matching fname * @param opts MPlayer options * @param slist pointer to the subtitles list tallocated * @param nsub pointer to the number of subtitles * @param path Look for subtitles in this directory * @param fname Subtitle filename (pattern) * @param limit_fuzziness Ignore flag when sub_fuziness == 2 */ static void append_dir_subtitles(struct mpv_global *global, struct subfn **slist, int *nsub, struct bstr path, const char *fname, int limit_fuzziness) { void *tmpmem = talloc_new(NULL); struct MPOpts *opts = global->opts; struct mp_log *log = mp_log_new(tmpmem, global->log, "find_subfiles"); if (mp_is_url(bstr0(fname))) goto out; struct bstr f_fname = bstr0(mp_basename(fname)); struct bstr f_fname_noext = bstrdup(tmpmem, strip_ext(f_fname)); bstr_lower(f_fname_noext); struct bstr f_fname_trim = bstr_strip(f_fname_noext); // 0 = nothing // 1 = any subtitle file // 2 = any sub file containing movie name // 3 = sub file containing movie name and the lang extension char *path0 = bstrdup0(tmpmem, path); DIR *d = opendir(path0); if (!d) goto out; mp_verbose(log, "Load subtitles in %.*s\n", BSTR_P(path)); struct dirent *de; while ((de = readdir(d))) { struct bstr dename = bstr0(de->d_name); void *tmpmem2 = talloc_new(tmpmem); // retrieve various parts of the filename struct bstr tmp_fname_noext = bstrdup(tmpmem2, strip_ext(dename)); bstr_lower(tmp_fname_noext); struct bstr tmp_fname_ext = get_ext(dename); struct bstr tmp_fname_trim = bstr_strip(tmp_fname_noext); // does it end with a subtitle extension? if (!is_sub_ext(tmp_fname_ext)) goto next_sub; // we have a (likely) subtitle file int prio = 0; char *found_lang = NULL; if (opts->sub_lang) { if (bstr_startswith(tmp_fname_trim, f_fname_trim)) { struct bstr lang = guess_lang_from_filename(tmp_fname_trim); if (lang.len) { for (int n = 0; opts->sub_lang[n]; n++) { if (bstr_startswith0(lang, opts->sub_lang[n])) { prio = 4; // matches the movie name + lang extension found_lang = opts->sub_lang[n]; break; } } } } } if (!prio && bstrcmp(tmp_fname_trim, f_fname_trim) == 0) prio = 3; // matches the movie name if (!prio && bstr_find(tmp_fname_trim, f_fname_trim) >= 0 && opts->sub_match_fuzziness >= 1) prio = 2; // contains the movie name if (!prio) { // doesn't contain the movie name // don't try in the mplayer subtitle directory if (!limit_fuzziness && opts->sub_match_fuzziness >= 2) { prio = 1; } } mp_dbg(log, "Potential sub file: \"%s\" Priority: %d\n", de->d_name, prio); if (prio) { prio += prio; char *subpath = mp_path_join(*slist, path, dename); if (mp_path_exists(subpath)) { MP_GROW_ARRAY(*slist, *nsub); struct subfn *sub = *slist + (*nsub)++; // annoying and redundant if (strncmp(subpath, "./", 2) == 0) subpath += 2; sub->priority = prio; sub->fname = subpath; sub->lang = found_lang; } else talloc_free(subpath); } next_sub: talloc_free(tmpmem2); } closedir(d); out: talloc_free(tmpmem); } static bool case_endswith(const char *s, const char *end) { size_t len = strlen(s); size_t elen = strlen(end); return len >= elen && strcasecmp(s + len - elen, end) == 0; } // Drop .sub file if .idx file exists. // Assumes slist is sorted by compare_sub_filename. static void filter_subidx(struct subfn **slist, int *nsub) { const char *prev = NULL; for (int n = 0; n < *nsub; n++) { const char *fname = (*slist)[n].fname; if (case_endswith(fname, ".idx")) { prev = fname; } else if (case_endswith(fname, ".sub")) { if (prev && strncmp(prev, fname, strlen(fname) - 4) == 0) (*slist)[n].priority = -1; } } for (int n = *nsub - 1; n >= 0; n--) { if ((*slist)[n].priority < 0) MP_TARRAY_REMOVE_AT(*slist, *nsub, n); } } // Return a list of subtitles found, sorted by priority. // Last element is terminated with a fname==NULL entry. struct subfn *find_text_subtitles(struct mpv_global *global, const char *fname) { struct MPOpts *opts = global->opts; struct subfn *slist = talloc_array_ptrtype(NULL, slist, 1); int n = 0; // Load subtitles from current media directory append_dir_subtitles(global, &slist, &n, mp_dirname(fname), fname, 0); // Load subtitles in dirs specified by sub-paths option if (opts->sub_paths) { for (int i = 0; opts->sub_paths[i]; i++) { char *path = mp_path_join(slist, mp_dirname(fname), bstr0(opts->sub_paths[i])); append_dir_subtitles(global, &slist, &n, bstr0(path), fname, 0); } } // Load subtitles in ~/.mpv/sub limiting sub fuzziness char *mp_subdir = mp_find_user_config_file(NULL, global, "sub/"); if (mp_subdir) append_dir_subtitles(global, &slist, &n, bstr0(mp_subdir), fname, 1); talloc_free(mp_subdir); // Sort by name for filter_subidx() qsort(slist, n, sizeof(*slist), compare_sub_filename); filter_subidx(&slist, &n); // Sort subs by priority and append them qsort(slist, n, sizeof(*slist), compare_sub_priority); struct subfn z = {0}; MP_TARRAY_APPEND(NULL, slist, n, z); return slist; }
int Route_children_compare(Heap *heap, void *x, void *y) { return bstrcmp(((Route *)x)->name, ((Route *)y)->name); }
static int cmp_parse_eopt(struct nlmsghdr *n, struct tcf_ematch_hdr *hdr, struct bstr *args) { struct bstr *a; int align, opnd = 0; unsigned long offset = 0, layer = TCF_LAYER_NETWORK, mask = 0, value = 0; int offset_present = 0, value_present = 0; struct tcf_em_cmp cmp; memset(&cmp, 0, sizeof(cmp)); #define PARSE_ERR(CARG, FMT, ARGS...) \ em_parse_error(EINVAL, args, CARG, &cmp_ematch_util, FMT ,##ARGS) if (args == NULL) return PARSE_ERR(args, "cmp: missing arguments"); if (!bstrcmp(args, "u8")) align = TCF_EM_ALIGN_U8; else if (!bstrcmp(args, "u16")) align = TCF_EM_ALIGN_U16; else if (!bstrcmp(args, "u32")) align = TCF_EM_ALIGN_U32; else return PARSE_ERR(args, "cmp: invalid alignment"); for (a = bstr_next(args); a; a = bstr_next(a)) { if (!bstrcmp(a, "at")) { if (a->next == NULL) return PARSE_ERR(a, "cmp: missing argument"); a = bstr_next(a); offset = bstrtoul(a); if (offset == ULONG_MAX) return PARSE_ERR(a, "cmp: invalid offset, " \ "must be numeric"); offset_present = 1; } else if (!bstrcmp(a, "layer")) { if (a->next == NULL) return PARSE_ERR(a, "cmp: missing argument"); a = bstr_next(a); layer = parse_layer(a); if (layer == INT_MAX) { layer = bstrtoul(a); if (layer == ULONG_MAX) return PARSE_ERR(a, "cmp: invalid " \ "layer"); } if (layer > TCF_LAYER_MAX) return PARSE_ERR(a, "cmp: illegal layer, " \ "must be in 0..%d", TCF_LAYER_MAX); } else if (!bstrcmp(a, "mask")) { if (a->next == NULL) return PARSE_ERR(a, "cmp: missing argument"); a = bstr_next(a); mask = bstrtoul(a); if (mask == ULONG_MAX) return PARSE_ERR(a, "cmp: invalid mask"); } else if (!bstrcmp(a, "trans")) { cmp.flags |= TCF_EM_CMP_TRANS; } else if (!bstrcmp(a, "eq") || !bstrcmp(a, "gt") || !bstrcmp(a, "lt")) { if (!bstrcmp(a, "eq")) opnd = TCF_EM_OPND_EQ; else if (!bstrcmp(a, "gt")) opnd = TCF_EM_OPND_GT; else if (!bstrcmp(a, "lt")) opnd = TCF_EM_OPND_LT; if (a->next == NULL) return PARSE_ERR(a, "cmp: missing argument"); a = bstr_next(a); value = bstrtoul(a); if (value == ULONG_MAX) return PARSE_ERR(a, "cmp: invalid value"); value_present = 1; } else return PARSE_ERR(a, "nbyte: unknown parameter"); } if (offset_present == 0 || value_present == 0) return PARSE_ERR(a, "cmp: offset and value required"); cmp.val = (__u32) value; cmp.mask = (__u32) mask; cmp.off = (__u16) offset; cmp.align = (__u8) align; cmp.layer = (__u8) layer; cmp.opnd = (__u8) opnd; addraw_l(n, MAX_MSG, hdr, sizeof(*hdr)); addraw_l(n, MAX_MSG, &cmp, sizeof(cmp)); #undef PARSE_ERR return 0; }
/* * Check for duplicate jobs. * Returns: true if current job should continue * false if current job should terminate */ bool allow_duplicate_job(JCR *jcr) { JCR *djcr; /* possible duplicate job */ JOBRES *job = jcr->res.job; bool cancel_dup = false; bool cancel_me = false; /* * See if AllowDuplicateJobs is set or * if duplicate checking is disabled for this job. */ if (job->AllowDuplicateJobs || jcr->IgnoreDuplicateJobChecking) { return true; } Dmsg0(800, "Enter allow_duplicate_job\n"); /* * After this point, we do not want to allow any duplicate * job to run. */ foreach_jcr(djcr) { if (jcr == djcr || djcr->JobId == 0) { continue; /* do not cancel this job or consoles */ } /* * See if this Job has the IgnoreDuplicateJobChecking flag set, ignore it * for any checking against other jobs. */ if (djcr->IgnoreDuplicateJobChecking) { continue; } if (bstrcmp(job->name(), djcr->res.job->name())) { if (job->DuplicateJobProximity > 0) { utime_t now = (utime_t)time(NULL); if ((now - djcr->start_time) > job->DuplicateJobProximity) { continue; /* not really a duplicate */ } } if (job->CancelLowerLevelDuplicates && djcr->getJobType() == 'B' && jcr->getJobType() == 'B') { switch (jcr->getJobLevel()) { case L_FULL: if (djcr->getJobLevel() == L_DIFFERENTIAL || djcr->getJobLevel() == L_INCREMENTAL) { cancel_dup = true; } break; case L_DIFFERENTIAL: if (djcr->getJobLevel() == L_INCREMENTAL) { cancel_dup = true; } if (djcr->getJobLevel() == L_FULL) { cancel_me = true; } break; case L_INCREMENTAL: if (djcr->getJobLevel() == L_FULL || djcr->getJobLevel() == L_DIFFERENTIAL) { cancel_me = true; } } /* * cancel_dup will be done below */ if (cancel_me) { /* Zap current job */ jcr->setJobStatus(JS_Canceled); Jmsg(jcr, M_FATAL, 0, _("JobId %d already running. Duplicate job not allowed.\n"), djcr->JobId); break; /* get out of foreach_jcr */ } } /* * Cancel one of the two jobs (me or dup) * If CancelQueuedDuplicates is set do so only if job is queued. */ if (job->CancelQueuedDuplicates) { switch (djcr->JobStatus) { case JS_Created: case JS_WaitJobRes: case JS_WaitClientRes: case JS_WaitStoreRes: case JS_WaitPriority: case JS_WaitMaxJobs: case JS_WaitStartTime: cancel_dup = true; /* cancel queued duplicate */ break; default: break; } } if (cancel_dup || job->CancelRunningDuplicates) { /* * Zap the duplicated job djcr */ UAContext *ua = new_ua_context(jcr); Jmsg(jcr, M_INFO, 0, _("Cancelling duplicate JobId=%d.\n"), djcr->JobId); cancel_job(ua, djcr); bmicrosleep(0, 500000); djcr->setJobStatus(JS_Canceled); cancel_job(ua, djcr); free_ua_context(ua); Dmsg2(800, "Cancel dup %p JobId=%d\n", djcr, djcr->JobId); } else { /* * Zap current job */ jcr->setJobStatus(JS_Canceled); Jmsg(jcr, M_FATAL, 0, _("JobId %d already running. Duplicate job not allowed.\n"), djcr->JobId); Dmsg2(800, "Cancel me %p JobId=%d\n", jcr, jcr->JobId); } Dmsg4(800, "curJobId=%d use_cnt=%d dupJobId=%d use_cnt=%d\n", jcr->JobId, jcr->use_count(), djcr->JobId, djcr->use_count()); break; /* did our work, get out of foreach loop */ } } endeach_jcr(djcr); return true; }
int main (int argc, char *argv[]) { int i, ch; FILE *fd; char line[1000]; char *VolumeName = NULL; char *bsrName = NULL; char *DirectorName = NULL; bool ignore_label_errors = false; DIRRES *director = NULL; setlocale(LC_ALL, ""); bindtextdomain("bareos", LOCALEDIR); textdomain("bareos"); init_stack_dump(); lmgr_init_thread(); working_directory = "/tmp"; my_name_is(argc, argv, "bls"); init_msg(NULL, NULL); /* initialize message handler */ OSDependentInit(); ff = init_find_files(); while ((ch = getopt(argc, argv, "b:c:D:d:e:i:jkLpvV:?")) != -1) { switch (ch) { case 'b': bsrName = optarg; break; case 'c': /* specify config file */ if (configfile != NULL) { free(configfile); } configfile = bstrdup(optarg); break; case 'D': /* specify director name */ if (DirectorName != NULL) { free(DirectorName); } DirectorName = bstrdup(optarg); break; case 'd': /* debug level */ if (*optarg == 't') { dbg_timestamp = true; } else { debug_level = atoi(optarg); if (debug_level <= 0) { debug_level = 1; } } break; case 'e': /* exclude list */ if ((fd = fopen(optarg, "rb")) == NULL) { berrno be; Pmsg2(0, _("Could not open exclude file: %s, ERR=%s\n"), optarg, be.bstrerror()); exit(1); } while (fgets(line, sizeof(line), fd) != NULL) { strip_trailing_junk(line); Dmsg1(100, "add_exclude %s\n", line); add_fname_to_exclude_list(ff, line); } fclose(fd); break; case 'i': /* include list */ if ((fd = fopen(optarg, "rb")) == NULL) { berrno be; Pmsg2(0, _("Could not open include file: %s, ERR=%s\n"), optarg, be.bstrerror()); exit(1); } while (fgets(line, sizeof(line), fd) != NULL) { strip_trailing_junk(line); Dmsg1(100, "add_include %s\n", line); add_fname_to_include_list(ff, 0, line); } fclose(fd); break; case 'j': list_jobs = true; break; case 'k': list_blocks = true; break; case 'L': dump_label = true; break; case 'p': ignore_label_errors = true; forge_on = true; break; case 'v': verbose++; break; case 'V': /* Volume name */ VolumeName = optarg; break; case '?': default: usage(); } /* end switch */ } /* end while */ argc -= optind; argv += optind; if (!argc) { Pmsg0(0, _("No archive name specified\n")); usage(); } if (configfile == NULL) { configfile = bstrdup(CONFIG_FILE); } my_config = new_config_parser(); parse_sd_config(my_config, configfile, M_ERROR_TERM); if (DirectorName) { foreach_res(director, R_DIRECTOR) { if (bstrcmp(director->name(), DirectorName)) { break; } } if (!director) { Emsg2(M_ERROR_TERM, 0, _("No Director resource named %s defined in %s. Cannot continue.\n"), DirectorName, configfile); } } load_sd_plugins(me->plugin_directory, me->plugin_names); read_crypto_cache(me->working_directory, "bareos-sd", get_first_port_host_order(me->SDaddrs)); if (ff->included_files_list == NULL) { add_fname_to_include_list(ff, 0, "/"); } for (i=0; i < argc; i++) { if (bsrName) { bsr = parse_bsr(NULL, bsrName); } dcr = New(DCR); jcr = setup_jcr("bls", argv[i], bsr, director, dcr, VolumeName, true); /* read device */ if (!jcr) { exit(1); } jcr->ignore_label_errors = ignore_label_errors; dev = jcr->dcr->dev; if (!dev) { exit(1); } dcr = jcr->dcr; rec = new_record(); attr = new_attr(jcr); /* * Assume that we have already read the volume label. * If on second or subsequent volume, adjust buffer pointer */ if (dev->VolHdr.PrevVolumeName[0] != 0) { /* second volume */ Pmsg1(0, _("\n" "Warning, this Volume is a continuation of Volume %s\n"), dev->VolHdr.PrevVolumeName); } if (list_blocks) { do_blocks(argv[i]); } else if (list_jobs) { do_jobs(argv[i]); } else { do_ls(argv[i]); } do_close(jcr); } if (bsr) { free_bsr(bsr); } term_include_exclude_files(ff); term_find_files(ff); return 0; }
static int cpustr_to_cpulist_logical(bstring bcpustr, int* cpulist, int length) { topology_init(); CpuTopology_t cpuid_topology = get_cpuTopology(); affinity_init(); AffinityDomains_t affinity = get_affinityDomains(); int domainidx = -1; bstring bdomain; bstring blist; if (bstrchrp(bcpustr, 'L', 0) != 0) { fprintf(stderr, "Not a valid CPU expression\n"); return 0; } struct bstrList* strlist = bstrListCreate(); strlist = bsplit(bcpustr, ':'); if (strlist->qty != 3) { fprintf(stderr, "ERROR: Invalid expression, should look like L:<domain>:<indexlist> or be in a cpuset\n"); bstrListDestroy(strlist); return 0; } bdomain = bstrcpy(strlist->entry[1]); blist = bstrcpy(strlist->entry[2]); bstrListDestroy(strlist); for (int i=0; i<affinity->numberOfAffinityDomains; i++) { if (bstrcmp(bdomain, affinity->domains[i].tag) == 0) { domainidx = i; break; } } if (domainidx < 0) { fprintf(stderr, "Cannot find domain %s\n", bdata(bdomain)); return 0; } int *inlist = malloc(affinity->domains[domainidx].numberOfProcessors * sizeof(int)); if (inlist == NULL) { return -ENOMEM; } int ret = cpulist_sort(affinity->domains[domainidx].processorList, inlist, affinity->domains[domainidx].numberOfProcessors); strlist = bstrListCreate(); strlist = bsplit(blist, ','); int insert = 0; for (int i=0; i< strlist->qty; i++) { if (bstrchrp(strlist->entry[i], '-', 0) != BSTR_ERR) { struct bstrList* indexlist = bstrListCreate(); indexlist = bsplit(strlist->entry[i], '-'); if (atoi(bdata(indexlist->entry[0])) <= atoi(bdata(indexlist->entry[1]))) { for (int j=atoi(bdata(indexlist->entry[0])); j<=atoi(bdata(indexlist->entry[1]));j++) { cpulist[insert] = inlist[j]; insert++; if (insert == length) { bstrListDestroy(indexlist); goto logical_done; } } } else { for (int j=atoi(bdata(indexlist->entry[0])); j>=atoi(bdata(indexlist->entry[1]));j--) { cpulist[insert] = inlist[j]; insert++; if (insert == length) { bstrListDestroy(indexlist); goto logical_done; } } } bstrListDestroy(indexlist); } else { cpulist[insert] = inlist[atoi(bdata(strlist->entry[i]))]; insert++; if (insert == length) { goto logical_done; } } } logical_done: free(inlist); bstrListDestroy(strlist); return insert; }
/* * Read the volume label * * If dcr->VolumeName == NULL, we accept any Bareos Volume * If dcr->VolumeName[0] == 0, we accept any Bareos Volume * otherwise dcr->VolumeName must match the Volume. * * If VolName given, ensure that it matches * * Returns VOL_ code as defined in record.h * VOL_NOT_READ * VOL_OK good label found * VOL_NO_LABEL volume not labeled * VOL_IO_ERROR I/O error reading tape * VOL_NAME_ERROR label has wrong name * VOL_CREATE_ERROR Error creating label * VOL_VERSION_ERROR label has wrong version * VOL_LABEL_ERROR bad label type * VOL_NO_MEDIA no media in drive * * The dcr block is emptied on return, and the Volume is * rewound. */ int read_dev_volume_label(DCR *dcr) { JCR *jcr = dcr->jcr; DEVICE * volatile dev = dcr->dev; char *VolName = dcr->VolumeName; DEV_RECORD *record; bool ok = false; DEV_BLOCK *block = dcr->block; int status; bool want_ansi_label; bool have_ansi_label = false; Dmsg4(100, "Enter read_volume_label res=%d device=%s vol=%s dev_Vol=%s\n", dev->num_reserved(), dev->print_name(), VolName, dev->VolHdr.VolumeName[0]?dev->VolHdr.VolumeName:"*NULL*"); if (!dev->is_open()) { if (!dev->open(dcr, OPEN_READ_ONLY)) { return VOL_IO_ERROR; } } dev->clear_labeled(); dev->clear_append(); dev->clear_read(); dev->label_type = B_BAREOS_LABEL; if (!dev->rewind(dcr)) { Mmsg(jcr->errmsg, _("Couldn't rewind device %s: ERR=%s\n"), dev->print_name(), dev->print_errmsg()); Dmsg1(130, "return VOL_NO_MEDIA: %s", jcr->errmsg); return VOL_NO_MEDIA; } bstrncpy(dev->VolHdr.Id, "**error**", sizeof(dev->VolHdr.Id)); /* * The stored plugin handling the bsdEventLabelRead event can abort * the reading of the label by returning a non bRC_OK. */ if (generate_plugin_event(jcr, bsdEventLabelRead, dcr) != bRC_OK) { Dmsg0(200, "Error from bsdEventLabelRead plugin event.\n"); return VOL_NO_MEDIA; } /* * Read ANSI/IBM label if so requested */ want_ansi_label = dcr->VolCatInfo.LabelType != B_BAREOS_LABEL || dcr->device->label_type != B_BAREOS_LABEL; if (want_ansi_label || dev->has_cap(CAP_CHECKLABELS)) { status = read_ansi_ibm_label(dcr); /* * If we want a label and didn't find it, return error */ if (want_ansi_label && status != VOL_OK) { goto bail_out; } if (status == VOL_NAME_ERROR || status == VOL_LABEL_ERROR) { Mmsg(jcr->errmsg, _("Wrong Volume mounted on device %s: Wanted %s have %s\n"), dev->print_name(), VolName, dev->VolHdr.VolumeName); if (!dev->poll && jcr->label_errors++ > 100) { Jmsg(jcr, M_FATAL, 0, _("Too many tries: %s"), jcr->errmsg); } goto bail_out; } if (status != VOL_OK) { /* Not an ANSI/IBM label, so re-read */ dev->rewind(dcr); } else { have_ansi_label = true; } } /* * Read the Bareos Volume label block */ record = new_record(); empty_block(block); Dmsg0(130, "Big if statement in read_volume_label\n"); if (!dcr->read_block_from_dev(NO_BLOCK_NUMBER_CHECK)) { Mmsg(jcr->errmsg, _("Requested Volume \"%s\" on %s is not a Bareos " "labeled Volume, because: ERR=%s"), NPRT(VolName), dev->print_name(), dev->print_errmsg()); Dmsg1(130, "%s", jcr->errmsg); } else if (!read_record_from_block(dcr, record)) { Mmsg(jcr->errmsg, _("Could not read Volume label from block.\n")); Dmsg1(130, "%s", jcr->errmsg); } else if (!unser_volume_label(dev, record)) { Mmsg(jcr->errmsg, _("Could not unserialize Volume label: ERR=%s\n"), dev->print_errmsg()); Dmsg1(130, "%s", jcr->errmsg); } else if (!bstrcmp(dev->VolHdr.Id, BareosId) && !bstrcmp(dev->VolHdr.Id, OldBaculaId) && !bstrcmp(dev->VolHdr.Id, OlderBaculaId)) { Mmsg(jcr->errmsg, _("Volume Header Id bad: %s\n"), dev->VolHdr.Id); Dmsg1(130, "%s", jcr->errmsg); } else { ok = true; } free_record(record); /* finished reading Volume record */ if (!dev->is_volume_to_unload()) { dev->clear_unload(); } if (!ok) { if (forge_on || jcr->ignore_label_errors) { dev->set_labeled(); /* set has Bareos label */ Jmsg(jcr, M_ERROR, 0, "%s", jcr->errmsg); goto ok_out; } Dmsg0(100, "No volume label - bailing out\n"); status = VOL_NO_LABEL; goto bail_out; } /* * At this point, we have read the first Bareos block, and * then read the Bareos Volume label. Now we need to * make sure we have the right Volume. */ if (dev->VolHdr.VerNum != BareosTapeVersion && dev->VolHdr.VerNum != OldCompatibleBareosTapeVersion1 && dev->VolHdr.VerNum != OldCompatibleBareosTapeVersion2 && dev->VolHdr.VerNum != OldCompatibleBareosTapeVersion3) { Mmsg(jcr->errmsg, _("Volume on %s has wrong Bareos version. Wanted %d got %d\n"), dev->print_name(), BareosTapeVersion, dev->VolHdr.VerNum); Dmsg1(130, "VOL_VERSION_ERROR: %s", jcr->errmsg); status = VOL_VERSION_ERROR; goto bail_out; } /* * We are looking for either an unused Bareos tape (PRE_LABEL) or * a Bareos volume label (VOL_LABEL) */ if (dev->VolHdr.LabelType != PRE_LABEL && dev->VolHdr.LabelType != VOL_LABEL) { Mmsg(jcr->errmsg, _("Volume on %s has bad Bareos label type: %x\n"), dev->print_name(), dev->VolHdr.LabelType); Dmsg1(130, "%s", jcr->errmsg); if (!dev->poll && jcr->label_errors++ > 100) { Jmsg(jcr, M_FATAL, 0, _("Too many tries: %s"), jcr->errmsg); } Dmsg0(150, "return VOL_LABEL_ERROR\n"); status = VOL_LABEL_ERROR; goto bail_out; } dev->set_labeled(); /* set has Bareos label */ /* Compare Volume Names */ Dmsg2(130, "Compare Vol names: VolName=%s hdr=%s\n", VolName?VolName:"*", dev->VolHdr.VolumeName); if (VolName && *VolName && *VolName != '*' && !bstrcmp(dev->VolHdr.VolumeName, VolName)) { Mmsg(jcr->errmsg, _("Wrong Volume mounted on device %s: Wanted %s have %s\n"), dev->print_name(), VolName, dev->VolHdr.VolumeName); Dmsg1(130, "%s", jcr->errmsg); /* * Cancel Job if too many label errors * => we are in a loop */ if (!dev->poll && jcr->label_errors++ > 100) { Jmsg(jcr, M_FATAL, 0, "Too many tries: %s", jcr->errmsg); } Dmsg0(150, "return VOL_NAME_ERROR\n"); status = VOL_NAME_ERROR; goto bail_out; } if (debug_level >= 200) { dump_volume_label(dev); } Dmsg0(130, "Leave read_volume_label() VOL_OK\n"); /* * If we are a streaming device, we only get one chance to read */ if (!dev->has_cap(CAP_STREAM)) { dev->rewind(dcr); if (have_ansi_label) { status = read_ansi_ibm_label(dcr); /* * If we want a label and didn't find it, return error */ if (status != VOL_OK) { goto bail_out; } } } Dmsg1(100, "Call reserve_volume=%s\n", dev->VolHdr.VolumeName); if (reserve_volume(dcr, dev->VolHdr.VolumeName) == NULL) { Mmsg2(jcr->errmsg, _("Could not reserve volume %s on %s\n"), dev->VolHdr.VolumeName, dev->print_name()); Dmsg2(150, "Could not reserve volume %s on %s\n", dev->VolHdr.VolumeName, dev->print_name()); status = VOL_NAME_ERROR; goto bail_out; } ok_out: /* * The stored plugin handling the bsdEventLabelVerified event can override * the return value e.g. although we think the volume label is ok the plugin * has reasons to override that. So when the plugin returns something else * then bRC_OK it want to tell us the volume is not OK to use and as * such we return VOL_NAME_ERROR as error although it might not be te * best error it should be sufficient. */ if (generate_plugin_event(jcr, bsdEventLabelVerified, dcr) != bRC_OK) { Dmsg0(200, "Error from bsdEventLabelVerified plugin event.\n"); status = VOL_NAME_ERROR; goto bail_out; } empty_block(block); return VOL_OK; bail_out: empty_block(block); dev->rewind(dcr); Dmsg1(150, "return %d\n", status); return status; }
/* * (Un)mount the device (For a FILE device) */ static bool do_mount(DCR *dcr, bool mount, int dotimeout) { DEVRES *device = dcr->dev->device; POOL_MEM ocmd(PM_FNAME); POOLMEM *results; DIR* dp; char *icmd; struct dirent *entry, *result; int status, tries, name_max, count; berrno be; Dsm_check(200); if (mount) { icmd = device->mount_command; } else { icmd = device->unmount_command; } dcr->dev->edit_mount_codes(ocmd, icmd); Dmsg2(100, "do_mount: cmd=%s mounted=%d\n", ocmd.c_str(), dcr->dev->is_mounted()); if (dotimeout) { /* Try at most 10 times to (un)mount the device. This should perhaps be configurable. */ tries = 10; } else { tries = 1; } results = get_memory(4000); /* If busy retry each second */ Dmsg1(100, "do_mount run_prog=%s\n", ocmd.c_str()); while ((status = run_program_full_output(ocmd.c_str(), dcr->dev->max_open_wait / 2, results)) != 0) { /* Doesn't work with internationalization (This is not a problem) */ if (mount && fnmatch("*is already mounted on*", results, 0) == 0) { break; } if (!mount && fnmatch("* not mounted*", results, 0) == 0) { break; } if (tries-- > 0) { /* Sometimes the device cannot be mounted because it is already mounted. * Try to unmount it, then remount it */ if (mount) { Dmsg1(400, "Trying to unmount the device %s...\n", dcr->dev->print_name()); do_mount(dcr, 0, 0); } bmicrosleep(1, 0); continue; } Dmsg5(100, "Device %s cannot be %smounted. status=%d result=%s ERR=%s\n", dcr->dev->print_name(), (mount ? "" : "un"), status, results, be.bstrerror(status)); Mmsg(dcr->dev->errmsg, _("Device %s cannot be %smounted. ERR=%s\n"), dcr->dev->print_name(), (mount ? "" : "un"), be.bstrerror(status)); /* * Now, just to be sure it is not mounted, try to read the filesystem. */ name_max = pathconf(".", _PC_NAME_MAX); if (name_max < 1024) { name_max = 1024; } if (!(dp = opendir(device->mount_point))) { berrno be; dcr->dev->dev_errno = errno; Dmsg3(100, "do_mount: failed to open dir %s (dev=%s), ERR=%s\n", device->mount_point, dcr->dev->print_name(), be.bstrerror()); goto get_out; } entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 1000); count = 0; while (1) { if ((readdir_r(dp, entry, &result) != 0) || (result == NULL)) { dcr->dev->dev_errno = EIO; Dmsg2(129, "do_mount: failed to find suitable file in dir %s (dev=%s)\n", device->mount_point, dcr->dev->print_name()); break; } if (!bstrcmp(result->d_name, ".") && !bstrcmp(result->d_name, "..") && !bstrcmp(result->d_name, ".keep")) { count++; /* result->d_name != ., .. or .keep (Gentoo-specific) */ break; } else { Dmsg2(129, "do_mount: ignoring %s in %s\n", result->d_name, device->mount_point); } } free(entry); closedir(dp); Dmsg1(100, "do_mount: got %d files in the mount point (not counting ., .. and .keep)\n", count); if (count > 0) { /* If we got more than ., .. and .keep */ /* there must be something mounted */ if (mount) { Dmsg1(100, "Did Mount by count=%d\n", count); break; } else { /* An unmount request. We failed to unmount - report an error */ free_pool_memory(results); Dmsg0(200, "== error mount=1 wanted unmount\n"); return false; } } get_out: free_pool_memory(results); Dmsg0(200, "============ mount=0\n"); Dsm_check(200); return false; } free_pool_memory(results); Dmsg1(200, "============ mount=%d\n", mount); return true; }
/* * Store Schedule Run information * * Parse Run statement: * * Run <keyword=value ...> [on] 2 january at 23:45 * * Default Run time is daily at 0:0 * * There can be multiple run statements, they are simply chained * together. * */ void store_run(LEX *lc, RES_ITEM *item, int index, int pass) { char *p; int i, j; int options = lc->options; int token, state, state2 = 0, code = 0, code2 = 0; bool found; utime_t utime; RES *res; RUNRES **run = (RUNRES **)(item->value); URES *res_all = (URES *)my_config->m_res_all; lc->options |= LOPT_NO_IDENT; /* Want only "strings" */ /* * Clear local copy of run record */ memset(&lrun, 0, sizeof(RUNRES)); /* * Scan for Job level "full", "incremental", ... */ for (found = true; found; ) { found = false; token = lex_get_token(lc, T_NAME); for (i = 0; !found && RunFields[i].name; i++) { if (bstrcasecmp(lc->str, RunFields[i].name)) { found = true; if (lex_get_token(lc, T_ALL) != T_EQUALS) { scan_err1(lc, _("Expected an equals, got: %s"), lc->str); /* NOT REACHED */ } switch (RunFields[i].token) { case 's': /* Data spooling */ token = lex_get_token(lc, T_NAME); if (bstrcasecmp(lc->str, "yes") || bstrcasecmp(lc->str, "true")) { lrun.spool_data = true; lrun.spool_data_set = true; } else if (bstrcasecmp(lc->str, "no") || bstrcasecmp(lc->str, "false")) { lrun.spool_data = false; lrun.spool_data_set = true; } else { scan_err1(lc, _("Expect a YES or NO, got: %s"), lc->str); } break; case 'L': /* Level */ token = lex_get_token(lc, T_NAME); for (j = 0; joblevels[j].level_name; j++) { if (bstrcasecmp(lc->str, joblevels[j].level_name)) { lrun.level = joblevels[j].level; lrun.job_type = joblevels[j].job_type; j = 0; break; } } if (j != 0) { scan_err1(lc, _("Job level field: %s not found in run record"), lc->str); /* NOT REACHED */ } break; case 'p': /* Priority */ token = lex_get_token(lc, T_PINT32); if (pass == 2) { lrun.Priority = lc->pint32_val; } break; case 'P': /* Pool */ case 'f': /* FullPool */ case 'i': /* IncPool */ case 'd': /* DiffPool */ case 'n': /* NextPool */ token = lex_get_token(lc, T_NAME); if (pass == 2) { res = GetResWithName(R_POOL, lc->str); if (res == NULL) { scan_err1(lc, _("Could not find specified Pool Resource: %s"), lc->str); /* NOT REACHED */ } switch(RunFields[i].token) { case 'P': lrun.pool = (POOLRES *)res; break; case 'f': lrun.full_pool = (POOLRES *)res; break; case 'i': lrun.inc_pool = (POOLRES *)res; break; case 'd': lrun.diff_pool = (POOLRES *)res; break; case 'n': lrun.next_pool = (POOLRES *)res; break; } } break; case 'S': /* Storage */ token = lex_get_token(lc, T_NAME); if (pass == 2) { res = GetResWithName(R_STORAGE, lc->str); if (res == NULL) { scan_err1(lc, _("Could not find specified Storage Resource: %s"), lc->str); /* NOT REACHED */ } lrun.storage = (STORERES *)res; } break; case 'M': /* Messages */ token = lex_get_token(lc, T_NAME); if (pass == 2) { res = GetResWithName(R_MSGS, lc->str); if (res == NULL) { scan_err1(lc, _("Could not find specified Messages Resource: %s"), lc->str); /* NOT REACHED */ } lrun.msgs = (MSGSRES *)res; } break; case 'm': /* Max run sched time */ token = lex_get_token(lc, T_QUOTED_STRING); if (!duration_to_utime(lc->str, &utime)) { scan_err1(lc, _("expected a time period, got: %s"), lc->str); return; } lrun.MaxRunSchedTime = utime; lrun.MaxRunSchedTime_set = true; break; case 'a': /* Accurate */ token = lex_get_token(lc, T_NAME); if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) { lrun.accurate = true; lrun.accurate_set = true; } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) { lrun.accurate = false; lrun.accurate_set = true; } else { scan_err1(lc, _("Expect a YES or NO, got: %s"), lc->str); } break; default: scan_err1(lc, _("Expected a keyword name, got: %s"), lc->str); /* NOT REACHED */ break; } /* end switch */ } /* end if bstrcasecmp */ } /* end for RunFields */ /* * At this point, it is not a keyword. Check for old syle * Job Levels without keyword. This form is depreciated!!! */ if (!found) { for (j = 0; joblevels[j].level_name; j++) { if (bstrcasecmp(lc->str, joblevels[j].level_name)) { lrun.level = joblevels[j].level; lrun.job_type = joblevels[j].job_type; found = true; break; } } } } /* end for found */ /* * Scan schedule times. * Default is: daily at 0:0 */ state = s_none; set_defaults(); for (; token != T_EOL; (token = lex_get_token(lc, T_ALL))) { int len; bool pm = false; bool am = false; switch (token) { case T_NUMBER: state = s_mday; code = atoi(lc->str) - 1; if (code < 0 || code > 30) { scan_err0(lc, _("Day number out of range (1-31)")); } break; case T_NAME: /* This handles drop through from keyword */ case T_UNQUOTED_STRING: if (strchr(lc->str, (int)'-')) { state = s_range; break; } if (strchr(lc->str, (int)':')) { state = s_time; break; } if (strchr(lc->str, (int)'/')) { state = s_modulo; break; } if (lc->str_len == 3 && (lc->str[0] == 'w' || lc->str[0] == 'W') && is_an_integer(lc->str+1)) { code = atoi(lc->str+1); if (code < 0 || code > 53) { scan_err0(lc, _("Week number out of range (0-53)")); /* NOT REACHED */ } state = s_woy; /* Week of year */ break; } /* * Everything else must be a keyword */ for (i = 0; keyw[i].name; i++) { if (bstrcasecmp(lc->str, keyw[i].name)) { state = keyw[i].state; code = keyw[i].code; i = 0; break; } } if (i != 0) { scan_err1(lc, _("Job type field: %s in run record not found"), lc->str); /* NOT REACHED */ } break; case T_COMMA: continue; default: scan_err2(lc, _("Unexpected token: %d:%s"), token, lc->str); /* NOT REACHED */ break; } switch (state) { case s_none: continue; case s_mday: /* Day of month */ if (!have_mday) { clear_bits(0, 30, lrun.mday); have_mday = true; } set_bit(code, lrun.mday); break; case s_month: /* Month of year */ if (!have_month) { clear_bits(0, 11, lrun.month); have_month = true; } set_bit(code, lrun.month); break; case s_wday: /* Week day */ if (!have_wday) { clear_bits(0, 6, lrun.wday); have_wday = true; } set_bit(code, lrun.wday); break; case s_wom: /* Week of month 1st, ... */ if (!have_wom) { clear_bits(0, 4, lrun.wom); have_wom = true; } set_bit(code, lrun.wom); break; case s_woy: if (!have_woy) { clear_bits(0, 53, lrun.woy); have_woy = true; } set_bit(code, lrun.woy); break; case s_time: /* Time */ if (!have_at) { scan_err0(lc, _("Time must be preceded by keyword AT.")); /* NOT REACHED */ } if (!have_hour) { clear_bits(0, 23, lrun.hour); } // Dmsg1(000, "s_time=%s\n", lc->str); p = strchr(lc->str, ':'); if (!p) { scan_err0(lc, _("Time logic error.\n")); /* NOT REACHED */ } *p++ = 0; /* Separate two halves */ code = atoi(lc->str); /* Pick up hour */ code2 = atoi(p); /* Pick up minutes */ len = strlen(p); if (len >= 2) { p += 2; } if (bstrcasecmp(p, "pm")) { pm = true; } else if (bstrcasecmp(p, "am")) { am = true; } else if (len != 2) { scan_err0(lc, _("Bad time specification.")); /* NOT REACHED */ } /* * Note, according to NIST, 12am and 12pm are ambiguous and * can be defined to anything. However, 12:01am is the same * as 00:01 and 12:01pm is the same as 12:01, so we define * 12am as 00:00 and 12pm as 12:00. */ if (pm) { /* * Convert to 24 hour time */ if (code != 12) { code += 12; } } else if (am && code == 12) { /* * AM */ code -= 12; } if (code < 0 || code > 23 || code2 < 0 || code2 > 59) { scan_err0(lc, _("Bad time specification.")); /* NOT REACHED */ } set_bit(code, lrun.hour); lrun.minute = code2; have_hour = true; break; case s_at: have_at = true; break; case s_last: lrun.last_set = true; if (!have_wom) { clear_bits(0, 4, lrun.wom); have_wom = true; } break; case s_modulo: p = strchr(lc->str, '/'); if (!p) { scan_err0(lc, _("Modulo logic error.\n")); } *p++ = 0; /* Separate two halves */ if (is_an_integer(lc->str) && is_an_integer(p)) { /* * Check for day modulo specification. */ code = atoi(lc->str) - 1; code2 = atoi(p); if (code < 0 || code > 30 || code2 < 0 || code2 > 30) { scan_err0(lc, _("Bad day specification in modulo.")); } if (code > code2) { scan_err0(lc, _("Bad day specification, offset must always be <= than modulo.")); } if (!have_mday) { clear_bits(0, 30, lrun.mday); have_mday = true; } /* * Set the bits according to the modulo specification. */ for (i = 0; i < 31; i++) { if (i % code2 == 0) { set_bit(i + code, lrun.mday); } } } else if (strlen(lc->str) == 3 && strlen(p) == 3 && (lc->str[0] == 'w' || lc->str[0] == 'W') && (p[0] == 'w' || p[0] == 'W') && is_an_integer(lc->str + 1) && is_an_integer(p + 1)) { /* * Check for week modulo specification. */ code = atoi(lc->str + 1); code2 = atoi(p + 1); if (code < 0 || code > 53 || code2 < 0 || code2 > 53) { scan_err0(lc, _("Week number out of range (0-53) in modulo")); } if (code > code2) { scan_err0(lc, _("Bad week number specification in modulo, offset must always be <= than modulo.")); } if (!have_woy) { clear_bits(0, 53, lrun.woy); have_woy = true; } /* * Set the bits according to the modulo specification. */ for (i = 0; i < 54; i++) { if (i % code2 == 0) { set_bit(i + code - 1, lrun.woy); } } } else { scan_err0(lc, _("Bad modulo time specification. Format for weekdays is '01/02', for yearweeks is 'w01/w02'.")); } break; case s_range: p = strchr(lc->str, '-'); if (!p) { scan_err0(lc, _("Range logic error.\n")); } *p++ = 0; /* Separate two halves */ if (is_an_integer(lc->str) && is_an_integer(p)) { /* * Check for day range. */ code = atoi(lc->str) - 1; code2 = atoi(p) - 1; if (code < 0 || code > 30 || code2 < 0 || code2 > 30) { scan_err0(lc, _("Bad day range specification.")); } if (!have_mday) { clear_bits(0, 30, lrun.mday); have_mday = true; } if (code < code2) { set_bits(code, code2, lrun.mday); } else { set_bits(code, 30, lrun.mday); set_bits(0, code2, lrun.mday); } } else if (strlen(lc->str) == 3 && strlen(p) == 3 && (lc->str[0] == 'w' || lc->str[0] == 'W') && (p[0] == 'w' || p[0] == 'W') && is_an_integer(lc->str + 1) && is_an_integer(p + 1)) { /* * Check for week of year range. */ code = atoi(lc->str + 1); code2 = atoi(p + 1); if (code < 0 || code > 53 || code2 < 0 || code2 > 53) { scan_err0(lc, _("Week number out of range (0-53)")); } if (!have_woy) { clear_bits(0, 53, lrun.woy); have_woy = true; } if (code < code2) { set_bits(code, code2, lrun.woy); } else { set_bits(code, 53, lrun.woy); set_bits(0, code2, lrun.woy); } } else { /* * lookup first half of keyword range (week days or months). */ lcase(lc->str); for (i = 0; keyw[i].name; i++) { if (bstrcmp(lc->str, keyw[i].name)) { state = keyw[i].state; code = keyw[i].code; i = 0; break; } } if (i != 0 || (state != s_month && state != s_wday && state != s_wom)) { scan_err0(lc, _("Invalid month, week or position day range")); /* NOT REACHED */ } /* * Lookup end of range. */ lcase(p); for (i = 0; keyw[i].name; i++) { if (bstrcmp(p, keyw[i].name)) { state2 = keyw[i].state; code2 = keyw[i].code; i = 0; break; } } if (i != 0 || state != state2 || code == code2) { scan_err0(lc, _("Invalid month, weekday or position range")); /* NOT REACHED */ } if (state == s_wday) { if (!have_wday) { clear_bits(0, 6, lrun.wday); have_wday = true; } if (code < code2) { set_bits(code, code2, lrun.wday); } else { set_bits(code, 6, lrun.wday); set_bits(0, code2, lrun.wday); } } else if (state == s_month) { if (!have_month) { clear_bits(0, 11, lrun.month); have_month = true; } if (code < code2) { set_bits(code, code2, lrun.month); } else { /* * This is a bit odd, but we accept it anyway */ set_bits(code, 11, lrun.month); set_bits(0, code2, lrun.month); } } else { /* * Must be position */ if (!have_wom) { clear_bits(0, 4, lrun.wom); have_wom = true; } if (code < code2) { set_bits(code, code2, lrun.wom); } else { set_bits(code, 4, lrun.wom); set_bits(0, code2, lrun.wom); } } } break; case s_hourly: have_hour = true; set_bits(0, 23, lrun.hour); break; case s_weekly: have_mday = have_wom = have_woy = true; set_bits(0, 30, lrun.mday); set_bits(0, 4, lrun.wom); set_bits(0, 53, lrun.woy); break; case s_daily: have_mday = true; set_bits(0, 6, lrun.wday); break; case s_monthly: have_month = true; set_bits(0, 11, lrun.month); break; default: scan_err0(lc, _("Unexpected run state\n")); /* NOT REACHED */ break; } } /* Allocate run record, copy new stuff into it, * and append it to the list of run records * in the schedule resource. */ if (pass == 2) { RUNRES *tail; /* Create new run record */ RUNRES *nrun = (RUNRES *)malloc(sizeof(RUNRES)); memcpy(nrun, &lrun, sizeof(RUNRES)); nrun ->next = NULL; if (!*run) { /* If empty list */ *run = nrun; /* Add new record */ } else { for (tail = *run; tail->next; tail=tail->next) { } tail->next = nrun; } } lc->options = options; /* Restore scanner options */ set_bit(index, res_all->res_sch.hdr.item_present); }
/* * Insert a node in the tree. This is the main subroutine called when building a tree. */ TREE_NODE *insert_tree_node(char *path, char *fname, int type, TREE_ROOT *root, TREE_NODE *parent) { char *p, *q; int path_len = strlen(path); TREE_NODE *node; Dmsg1(100, "insert_tree_node: %s\n", path); /* * If trailing slash on path, strip it */ if (path_len > 0) { q = path + path_len - 1; if (IsPathSeparator(*q)) { *q = 0; /* strip trailing slash */ } else { q = NULL; /* no trailing slash */ } } else { q = NULL; /* no trailing slash */ } /* * If no filename, strip last component of path as "filename" */ if (*fname == 0) { p = (char *)last_path_separator(path); /* separate path and filename */ if (p) { fname = p + 1; /* set new filename */ *p = '\0'; /* terminate new path */ } } else { p = NULL; } if (*fname) { if (!parent) { /* if no parent, we need to make one */ Dmsg1(100, "make_tree_path for %s\n", path); path_len = strlen(path); /* get new length */ if (path_len == root->cached_path_len && bstrcmp(path, root->cached_path)) { parent = root->cached_parent; } else { root->cached_path_len = path_len; pm_strcpy(&root->cached_path, path); parent = make_tree_path(path, root); root->cached_parent = parent; } Dmsg1(100, "parent=%s\n", parent->fname); } } else { fname = path; if (!parent) { parent = (TREE_NODE *)root; type = TN_DIR_NLS; } Dmsg1(100, "No / found: %s\n", path); } node = search_and_insert_tree_node(fname, 0, root, parent); if (q) { /* if trailing slash on entry */ *q = '/'; /* restore it */ } if (p) { /* if slash in path trashed */ *p = '/'; /* restore full path */ } return node; }
int run_scripts(JCR *jcr, alist *runscripts, const char *label, alist *allowed_script_dirs) { RUNSCRIPT *script; bool runit; int when; Dmsg2(200, "runscript: running all RUNSCRIPT object (%s) JobStatus=%c\n", label, jcr->JobStatus); if (strstr(label, NT_("Before"))) { when = SCRIPT_Before; } else if (bstrcmp(label, NT_("ClientAfterVSS"))) { when = SCRIPT_AfterVSS; } else { when = SCRIPT_After; } if (runscripts == NULL) { Dmsg0(100, "runscript: WARNING RUNSCRIPTS list is NULL\n"); return 0; } foreach_alist(script, runscripts) { Dmsg2(200, "runscript: try to run %s:%s\n", NPRT(script->target), NPRT(script->command)); runit = false; if ((script->when & SCRIPT_Before) && (when & SCRIPT_Before)) { if ((script->on_success && (jcr->JobStatus == JS_Running || jcr->JobStatus == JS_Created)) || (script->on_failure && (job_canceled(jcr) || jcr->JobStatus == JS_Differences))) { Dmsg4(200, "runscript: Run it because SCRIPT_Before (%s,%i,%i,%c)\n", script->command, script->on_success, script->on_failure, jcr->JobStatus ); runit = true; } } if ((script->when & SCRIPT_AfterVSS) && (when & SCRIPT_AfterVSS)) { if ((script->on_success && (jcr->JobStatus == JS_Blocked)) || (script->on_failure && job_canceled(jcr))) { Dmsg4(200, "runscript: Run it because SCRIPT_AfterVSS (%s,%i,%i,%c)\n", script->command, script->on_success, script->on_failure, jcr->JobStatus ); runit = true; } } if ((script->when & SCRIPT_After) && (when & SCRIPT_After)) { if ((script->on_success && (jcr->JobStatus == JS_Terminated || jcr->JobStatus == JS_Warnings)) || (script->on_failure && (job_canceled(jcr) || jcr->JobStatus == JS_Differences))) { Dmsg4(200, "runscript: Run it because SCRIPT_After (%s,%i,%i,%c)\n", script->command, script->on_success, script->on_failure, jcr->JobStatus ); runit = true; } } if (!script->is_local()) { runit = false; } /* * We execute it */ if (runit) { if (!script_dir_allowed(jcr, script, allowed_script_dirs)) { Dmsg1(200, "runscript: Not running script %s because its not in one of the allowed scripts dirs\n", script->command); Jmsg(jcr, M_ERROR, 0, _("Runscript: run %s \"%s\" could not execute, " "not in one of the allowed scripts dirs\n"), label, script->command); jcr->setJobStatus(JS_ErrorTerminated); goto bail_out; } script->run(jcr, label); } }
static int default_compare(void *a, void *b) { return bstrcmp((bstring) a, (bstring) b); }
static int filerecord_cache_lookup(void *data, void *key) { bstring request_path = (bstring) key; FileRecord *fr = (FileRecord *) data; return !bstrcmp(fr->request_path, request_path); }
/* * Common routine for both label and relabel */ static int do_label(UAContext *ua, const char *cmd, bool relabel) { USTORERES store; BSOCK *sd; char dev_name[MAX_NAME_LENGTH]; MEDIA_DBR mr, omr; POOL_DBR pr; bool print_reminder = true; bool label_barcodes = false; bool label_encrypt = false; int ok = FALSE; int i, j; int drive; bool media_record_exists = false; static const char *barcode_keywords[] = { "barcode", "barcodes", NULL }; if (!open_client_db(ua)) { return 1; } memset(&pr, 0, sizeof(pr)); memset(&mr, 0, sizeof(mr)); memset(&omr, 0, sizeof(omr)); /* * Look for one of the barcode keywords */ if (!relabel && (i = find_arg_keyword(ua, barcode_keywords)) >= 0) { /* * Now find the keyword in the list */ if ((j = find_arg(ua, barcode_keywords[i])) > 0) { *ua->argk[j] = 0; /* zap barcode keyword */ } label_barcodes = true; } /* * Look for the encrypt keyword */ if ((i = find_arg(ua, "encrypt")) > 0) { *ua->argk[i] = 0; /* zap encrypt keyword */ label_encrypt = true; } store.store = get_storage_resource(ua, true, label_barcodes); if (!store.store) { return 1; } switch (store.store->Protocol) { case APT_NDMPV2: case APT_NDMPV3: case APT_NDMPV4: /* * See if the user selected a NDMP storage device but its * handled by a native Bareos storage daemon e.g. we have * a paired_storage pointer. */ if (store.store->paired_storage) { store.store = store.store->paired_storage; } else { ua->warning_msg(_("Storage has non-native protocol.\n")); return 1; } break; default: break; } pm_strcpy(store.store_source, _("command line")); set_wstorage(ua->jcr, &store); drive = get_storage_drive(ua, store.store); if (label_barcodes) { label_from_barcodes(ua, drive, label_encrypt); return 1; } /* * If relabel get name of Volume to relabel */ if (relabel) { /* * Check for oldvolume=name */ i = find_arg_with_value(ua, "oldvolume"); if (i >= 0) { bstrncpy(omr.VolumeName, ua->argv[i], sizeof(omr.VolumeName)); if (db_get_media_record(ua->jcr, ua->db, &omr)) { goto checkVol; } ua->error_msg("%s", db_strerror(ua->db)); } /* * No keyword or Vol not found, ask user to select */ if (!select_media_dbr(ua, &omr)) { return 1; } /* * Require Volume to be Purged or Recycled */ checkVol: if (!bstrcmp(omr.VolStatus, "Purged") && !bstrcmp(omr.VolStatus, "Recycle")) { ua->error_msg(_("Volume \"%s\" has VolStatus %s. It must be Purged or Recycled before relabeling.\n"), omr.VolumeName, omr.VolStatus); return 1; } } /* * Check for volume=NewVolume */ i = find_arg_with_value(ua, "volume"); if (i >= 0) { pm_strcpy(ua->cmd, ua->argv[i]); goto checkName; } /* * Get a new Volume name */ for ( ;; ) { media_record_exists = false; if (!get_cmd(ua, _("Enter new Volume name: "))) { return 1; } checkName: if (!is_volume_name_legal(ua, ua->cmd)) { continue; } /* * Search by Media name so set VolumeName and clear MediaId. */ mr.MediaId = 0; bstrncpy(mr.VolumeName, ua->cmd, sizeof(mr.VolumeName)); /* * If VolBytes are zero the Volume is not labeled */ if (db_get_media_record(ua->jcr, ua->db, &mr)) { if (mr.VolBytes != 0) { ua->error_msg(_("Media record for new Volume \"%s\" already exists.\n"), mr.VolumeName); continue; } media_record_exists = true; } break; /* Got it */ } /* * If autochanger, request slot */ i = find_arg_with_value(ua, "slot"); if (i >= 0) { mr.Slot = atoi(ua->argv[i]); if (mr.Slot < 0) { mr.Slot = 0; } mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */ } else if (store.store->autochanger) { if (!get_pint(ua, _("Enter slot (0 or Enter for none): "))) { return 1; } mr.Slot = ua->pint32_val; if (mr.Slot < 0) { mr.Slot = 0; } mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */ } set_storageid_in_mr(store.store, &mr); bstrncpy(mr.MediaType, store.store->media_type, sizeof(mr.MediaType)); /* * Must select Pool if not already done */ if (pr.PoolId == 0) { memset(&pr, 0, sizeof(pr)); if (!select_pool_dbr(ua, &pr)) { return 1; } } /* * See if we need to generate a new passphrase for hardware encryption. */ if (label_encrypt) { ua->info_msg(_("Generating new hardware encryption key\n")); if (!generate_new_encryption_key(ua, &mr)) { return 1; } } ok = send_label_request(ua, &mr, &omr, &pr, relabel, media_record_exists, drive); if (ok) { sd = ua->jcr->store_bsock; if (relabel) { /* * Delete the old media record */ if (!db_delete_media_record(ua->jcr, ua->db, &omr)) { ua->error_msg(_("Delete of Volume \"%s\" failed. ERR=%s"), omr.VolumeName, db_strerror(ua->db)); } else { ua->info_msg(_("Old volume \"%s\" deleted from catalog.\n"), omr.VolumeName); /* * Update the number of Volumes in the pool */ pr.NumVols--; if (!db_update_pool_record(ua->jcr, ua->db, &pr)) { ua->error_msg("%s", db_strerror(ua->db)); } } } if (ua->automount) { bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name)); ua->info_msg(_("Requesting to mount %s ...\n"), dev_name); bash_spaces(dev_name); sd->fsend("mount %s drive=%d", dev_name, drive); unbash_spaces(dev_name); /* * We use bget_dirmsg here and not bnet_recv because as part of * the mount request the stored can request catalog information for * any plugin who listens to the bsdEventLabelVerified event. * As we don't want to loose any non protocol data e.g. errors * without a 3xxx prefix we set the allow_any_message of * bget_dirmsg to true and as such is behaves like a normal * bnet_recv for any non protocol messages. */ while (bget_dirmsg(sd, true) >= 0) { ua->send_msg("%s", sd->msg); /* * Here we can get * 3001 OK mount. Device=xxx or * 3001 Mounted Volume vvvv * 3002 Device "DVD-Writer" (/dev/hdc) is mounted. * 3906 is cannot mount non-tape * So for those, no need to print a reminder */ if (bstrncmp(sd->msg, "3001 ", 5) || bstrncmp(sd->msg, "3002 ", 5) || bstrncmp(sd->msg, "3906 ", 5)) { print_reminder = false; } } } } if (print_reminder) { ua->info_msg(_("Do not forget to mount the drive!!!\n")); } close_sd_bsock(ua); return 1; }
int main (int argc, char *argv[]) { int ch; bool ok; char *iVolumeName = NULL; char *oVolumeName = NULL; char *DirectorName = NULL; DIRRES *director = NULL; bool ignore_label_errors = false; DCR *in_dcr, *out_dcr; setlocale(LC_ALL, ""); bindtextdomain("bareos", LOCALEDIR); textdomain("bareos"); init_stack_dump(); my_name_is(argc, argv, "bcopy"); lmgr_init_thread(); init_msg(NULL, NULL); while ((ch = getopt(argc, argv, "b:c:D:d:i:o:pvw:?")) != -1) { switch (ch) { case 'b': bsr = parse_bsr(NULL, optarg); break; case 'c': /* specify config file */ if (configfile != NULL) { free(configfile); } configfile = bstrdup(optarg); break; case 'D': /* specify director name */ if (DirectorName != NULL) { free(DirectorName); } DirectorName = bstrdup(optarg); break; case 'd': /* debug level */ if (*optarg == 't') { dbg_timestamp = true; } else { debug_level = atoi(optarg); if (debug_level <= 0) { debug_level = 1; } } break; case 'i': /* input Volume name */ iVolumeName = optarg; break; case 'o': /* output Volume name */ oVolumeName = optarg; break; case 'p': ignore_label_errors = true; forge_on = true; break; case 'v': verbose++; break; case 'w': wd = optarg; break; case '?': default: usage(); } } argc -= optind; argv += optind; if (argc != 2) { Pmsg0(0, _("Wrong number of arguments: \n")); usage(); } OSDependentInit(); working_directory = wd; if (configfile == NULL) { configfile = bstrdup(CONFIG_FILE); } my_config = new_config_parser(); parse_sd_config(my_config, configfile, M_ERROR_TERM); if (DirectorName) { foreach_res(director, R_DIRECTOR) { if (bstrcmp(director->hdr.name, DirectorName)) { break; } } if (!director) { Emsg2(M_ERROR_TERM, 0, _("No Director resource named %s defined in %s. Cannot continue.\n"), DirectorName, configfile); } } load_sd_plugins(me->plugin_directory, me->plugin_names); read_crypto_cache(me->working_directory, "bareos-sd", get_first_port_host_order(me->SDaddrs)); /* * Setup and acquire input device for reading */ Dmsg0(100, "About to setup input jcr\n"); in_dcr = New(DCR); in_jcr = setup_jcr("bcopy", argv[0], bsr, director, in_dcr, iVolumeName, true); /* read device */ if (!in_jcr) { exit(1); } in_jcr->ignore_label_errors = ignore_label_errors; in_dev = in_jcr->dcr->dev; if (!in_dev) { exit(1); } /* * Setup output device for writing */ Dmsg0(100, "About to setup output jcr\n"); out_dcr = New(DCR); out_jcr = setup_jcr("bcopy", argv[1], bsr, director, out_dcr, oVolumeName, false); /* write device */ if (!out_jcr) { exit(1); } out_dev = out_jcr->dcr->dev; if (!out_dev) { exit(1); } Dmsg0(100, "About to acquire device for writing\n"); /* * For we must now acquire the device for writing */ out_dev->rLock(false); if (!out_dev->open(out_jcr->dcr, OPEN_READ_WRITE)) { Emsg1(M_FATAL, 0, _("dev open failed: %s\n"), out_dev->errmsg); out_dev->Unlock(); exit(1); } out_dev->Unlock(); if (!acquire_device_for_append(out_jcr->dcr)) { free_jcr(in_jcr); exit(1); } out_block = out_jcr->dcr->block; ok = read_records(in_jcr->dcr, record_cb, mount_next_read_volume); if (ok || out_dev->can_write()) { if (!out_jcr->dcr->write_block_to_device()) { Pmsg0(000, _("Write of last block failed.\n")); } } Pmsg2(000, _("%u Jobs copied. %u records copied.\n"), jobs, records); in_dev->term(); out_dev->term(); free_jcr(in_jcr); free_jcr(out_jcr); return 0; }