/* * Delete jobs (all records) from the catalog in groups of 1000 * at a time. */ void purge_job_list_from_catalog(UAContext *ua, del_ctx &del) { POOL_MEM jobids(PM_MESSAGE); char ed1[50]; for (int i=0; del.num_ids; ) { Dmsg1(150, "num_ids=%d\n", del.num_ids); pm_strcat(jobids, ""); for (int j=0; j<1000 && del.num_ids>0; j++) { del.num_ids--; if (del.JobId[i] == 0 || ua->jcr->JobId == del.JobId[i]) { Dmsg2(150, "skip JobId[%d]=%d\n", i, (int)del.JobId[i]); i++; continue; } if (*jobids.c_str() != 0) { pm_strcat(jobids, ","); } pm_strcat(jobids, edit_int64(del.JobId[i++], ed1)); Dmsg1(150, "Add id=%s\n", ed1); del.num_del++; } Dmsg1(150, "num_ids=%d\n", del.num_ids); purge_jobs_from_catalog(ua, jobids.c_str()); } }
static void tree_getpath_item(TREE_NODE *node, POOLMEM **path) { if (!node) { return; } tree_getpath_item(node->parent, path); /* * Fixup for Win32. If we have a Win32 directory and * there is only a / in the buffer, remove it since * win32 names don't generally start with / */ if (node->type == TN_DIR_NLS && IsPathSeparator((*path[0])) && (*path)[1] == '\0') { pm_strcpy(path, ""); } pm_strcat(path, node->fname); /* * Add a slash for all directories unless we are at the root, * also add a slash to a soft linked file if it has children * i.e. it is linked to a directory. */ if ((node->type != TN_FILE && !(IsPathSeparator((*path)[0]) && (*path)[1] == '\0')) || (node->soft_link && tree_node_has_child(node))) { pm_strcat(path, "/"); } }
/* * Delete files from a list of jobs in groups of 1000 * at a time. */ void purge_files_from_job_list(UAContext *ua, del_ctx &del) { POOL_MEM jobids(PM_MESSAGE); char ed1[50]; /* * OK, now we have the list of JobId's to be pruned, send them * off to be deleted batched 1000 at a time. */ for (int i=0; del.num_ids; ) { pm_strcat(jobids, ""); for (int j=0; j<1000 && del.num_ids>0; j++) { del.num_ids--; if (del.JobId[i] == 0 || ua->jcr->JobId == del.JobId[i]) { Dmsg2(150, "skip JobId[%d]=%d\n", i, (int)del.JobId[i]); i++; continue; } if (*jobids.c_str() != 0) { pm_strcat(jobids, ","); } pm_strcat(jobids, edit_int64(del.JobId[i++], ed1)); Dmsg1(150, "Add id=%s\n", ed1); del.num_del++; } purge_files_from_jobs(ua, jobids.c_str()); } }
/* * Use pool and client specified by user to select jobs to prune * returns add_from string to add in FROM clause * add_where string to add in WHERE clause */ bool prune_set_filter(UAContext *ua, CLIENTRES *client, POOLRES *pool, utime_t period, POOL_MEM *add_from, POOL_MEM *add_where) { utime_t now; char ed1[50], ed2[MAX_ESCAPE_NAME_LENGTH]; POOL_MEM tmp(PM_MESSAGE); now = (utime_t)time(NULL); edit_int64(now - period, ed1); Dmsg3(150, "now=%lld period=%lld JobTDate=%s\n", now, period, ed1); Mmsg(tmp, " AND JobTDate < %s ", ed1); pm_strcat(*add_where, tmp.c_str()); db_lock(ua->db); if (client) { db_escape_string(ua->jcr, ua->db, ed2, client->name(), strlen(client->name())); Mmsg(tmp, " AND Client.Name = '%s' ", ed2); pm_strcat(*add_where, tmp.c_str()); pm_strcat(*add_from, " JOIN Client USING (ClientId) "); } if (pool) { db_escape_string(ua->jcr, ua->db, ed2, pool->name(), strlen(pool->name())); Mmsg(tmp, " AND Pool.Name = '%s' ", ed2); pm_strcat(*add_where, tmp.c_str()); /* Use ON() instead of USING for some old SQLite */ pm_strcat(*add_from, " JOIN Pool ON (Job.PoolId = Pool.PoolId) "); } Dmsg2(150, "f=%s w=%s\n", add_from->c_str(), add_where->c_str()); db_unlock(ua->db); return true; }
/* * Create a connection string for connecting to the master database. */ static void set_ado_connect_string(bpContext *ctx) { POOL_MEM ado_connect_string(PM_NAME); plugin_ctx *p_ctx = (plugin_ctx *)ctx->pContext; if (bstrcasecmp(p_ctx->instance, DEFAULT_INSTANCE)) { pm_strcpy(ado_connect_string, "Provider=SQLOLEDB.1;Data Source=localhost;Initial Catalog=master"); } else { Mmsg(ado_connect_string, "Provider=SQLOLEDB.1;Data Source=localhost\\%s;Initial Catalog=master", p_ctx->instance); } /* * See if we need to use a username/password or a trusted connection. */ if (p_ctx->username && p_ctx->password) { POOL_MEM temp(PM_NAME); Mmsg(temp, ";User Id=%s;Password=%s;", p_ctx->username, p_ctx->password); pm_strcat(ado_connect_string, temp.c_str()); } else { pm_strcat(ado_connect_string, ";Integrated Security=SSPI;"); } Dmsg(ctx, dbglvl, "set_ado_connect_string: ADO Connect String '%s'\n", ado_connect_string.c_str()); if (p_ctx->ado_connect_string) { free(p_ctx->ado_connect_string); } p_ctx->ado_connect_string = bstrdup(ado_connect_string.c_str()); }
/* Dump the item table content to a text file (used by director) */ int ConfigFile::dump_results(POOLMEM **buf) { int len; POOLMEM *tmp; if (!items) { **buf = 0; return 0; } len = Mmsg(buf, "# Plugin configuration file\n# Version %d\n", version); tmp = get_pool_memory(PM_MESSAGE); for (int i = 0; items[i].name; i++) { if (items[i].found) { items[i].handler(NULL, this, &items[i]); if (items[i].comment && *items[i].comment) { Mmsg(tmp, "# %s\n", items[i].comment); pm_strcat(buf, tmp); } Mmsg(tmp, "%s=%s\n\n", items[i].name, this->edit); len = pm_strcat(buf, tmp); } } free_pool_memory(tmp); return len ; }
/* * Open a device. */ void DEVICE::open_device(DCR *dcr, int omode) { POOL_MEM archive_name(PM_FNAME); get_autochanger_loaded_slot(dcr); /* * Handle opening of File Archive (not a tape) */ pm_strcpy(archive_name, dev_name); /* * If this is a virtual autochanger (i.e. changer_res != NULL) we simply use * the device name, assuming it has been appropriately setup by the "autochanger". */ if (!device->changer_res || device->changer_command[0] == 0) { if (VolCatInfo.VolCatName[0] == 0) { Mmsg(errmsg, _("Could not open file device %s. No Volume name given.\n"), print_name()); clear_opened(); return; } if (!IsPathSeparator(archive_name.c_str()[strlen(archive_name.c_str())-1])) { pm_strcat(archive_name, "/"); } pm_strcat(archive_name, getVolCatName()); } mount(dcr, 1); /* do mount if required */ open_mode = omode; set_mode(omode); /* * If creating file, give 0640 permissions */ Dmsg3(100, "open disk: mode=%s open(%s, 0x%x, 0640)\n", mode_to_str(omode), archive_name.c_str(), oflags); if ((m_fd = d_open(archive_name.c_str(), oflags, 0640)) < 0) { berrno be; dev_errno = errno; Mmsg2(errmsg, _("Could not open: %s, ERR=%s\n"), archive_name.c_str(), be.bstrerror()); Dmsg1(100, "open failed: %s", errmsg); } if (m_fd >= 0) { dev_errno = 0; file = 0; file_addr = 0; } Dmsg1(100, "open dev: disk fd=%d opened\n", m_fd); }
/* * Dump the item table content to a text file (used by director) */ int ConfigFile::dump_results(POOL_MEM *buf) { int len; POOL_MEM tmp(PM_MESSAGE); if (!items) { char *p; p = buf->c_str(); p[0] = '\0'; return 0; } len = Mmsg(buf, "# Plugin configuration file\n# Version %d\n", version); for (int i = 0; items[i].name; i++) { if (items[i].found) { switch (items[i].type) { case INI_CFG_TYPE_INT32: ini_store_int32(NULL, this, &items[i]); break; case INI_CFG_TYPE_PINT32: ini_store_pint32(NULL, this, &items[i]); break; case INI_CFG_TYPE_INT64: ini_store_int64(NULL, this, &items[i]); break; case INI_CFG_TYPE_PINT64: ini_store_pint64(NULL, this, &items[i]); break; case INI_CFG_TYPE_NAME: ini_store_name(NULL, this, &items[i]); break; case INI_CFG_TYPE_STR: ini_store_str(NULL, this, &items[i]); break; case INI_CFG_TYPE_BOOL: ini_store_bool(NULL, this, &items[i]); break; case INI_CFG_TYPE_ALIST_STR: ini_store_alist_str(NULL, this, &items[i]); break; default: break; } if (items[i].comment && *items[i].comment) { Mmsg(tmp, "# %s\n", items[i].comment); pm_strcat(buf, tmp.c_str()); } Mmsg(tmp, "%s=%s\n\n", items[i].name, this->edit); len = pm_strcat(buf, tmp.c_str()); } } return len ; }
bool print_config_schema_json(POOL_MEM &buffer) { RES_TABLE *resources = my_config->m_resources; initialize_json(); json_t *json = json_object(); json_object_set_new(json, "format-version", json_integer(2)); json_object_set_new(json, "component", json_string("bconsole")); json_object_set_new(json, "version", json_string(VERSION)); /* * Resources */ json_t *resource = json_object(); json_object_set(json, "resource", resource); json_t *bconsole = json_object(); json_object_set(resource, "bconsole", bconsole); for (int r = 0; resources[r].name; r++) { RES_TABLE resource = my_config->m_resources[r]; json_object_set(bconsole, resource.name, json_items(resource.items)); } pm_strcat(buffer, json_dumps(json, JSON_INDENT(2))); json_decref(json); return true; }
static int do_a_command(FILE *input, BSOCK *UA_sock) { unsigned int i; int status; int found; int len; char *cmd; found = 0; status = 1; Dmsg1(120, "Command: %s\n", UA_sock->msg); if (argc == 0) { return 1; } cmd = argk[0]+1; if (*cmd == '#') { /* comment */ return 1; } len = strlen(cmd); for (i=0; i<comsize; i++) { /* search for command */ if (bstrncasecmp(cmd, _(commands[i].key), len)) { status = (*commands[i].func)(input, UA_sock); /* go execute command */ found = 1; break; } } if (!found) { pm_strcat(&UA_sock->msg, _(": is an invalid command\n")); UA_sock->msglen = strlen(UA_sock->msg); sendit(UA_sock->msg); } return status; }
int do_shell_expansion(char *name, int name_len) { static char meta[] = "~\\$[]*?`'<>\""; bool found = false; int len, i, status; POOLMEM *cmd; BPIPE *bpipe; char line[MAXSTRING]; const char *shellcmd; /* Check if any meta characters are present */ len = strlen(meta); for (i = 0; i < len; i++) { if (strchr(name, meta[i])) { found = true; break; } } if (found) { cmd = get_pool_memory(PM_FNAME); /* look for shell */ if ((shellcmd = getenv("SHELL")) == NULL) { shellcmd = "/bin/sh"; } pm_strcpy(&cmd, shellcmd); pm_strcat(&cmd, " -c \"echo "); pm_strcat(&cmd, name); pm_strcat(&cmd, "\""); Dmsg1(400, "Send: %s\n", cmd); if ((bpipe = open_bpipe(cmd, 0, "r"))) { *line = 0; fgets(line, sizeof(line), bpipe->rfd); strip_trailing_junk(line); status = close_bpipe(bpipe); Dmsg2(400, "status=%d got: %s\n", status, line); } else { status = 1; /* error */ } free_pool_memory(cmd); if (status == 0) { bstrncpy(name, line, name_len); } } return 1; }
static void add_file_and_part_name(DEVICE *dev, POOL_MEM &archive_name) { char partnumber[20]; if (!IsPathSeparator(archive_name.c_str()[strlen(archive_name.c_str())-1])) { pm_strcat(archive_name, "/"); } pm_strcat(archive_name, dev->getVolCatName()); /* if part > 1, append .# to the filename (where # is the part number) */ if (dev->part > 1) { pm_strcat(archive_name, "."); bsnprintf(partnumber, sizeof(partnumber), "%d", dev->part); pm_strcat(archive_name, partnumber); } Dmsg2(400, "Exit add_file_part_name: arch=%s, part=%d\n", archive_name.c_str(), dev->part); }
/* * Edit codes into (Un)MountCommand, Write(First)PartCommand * %% = % * %a = archive device name * %e = erase (set if cannot mount and first part) * %n = part number * %m = mount point * %v = last part name * * omsg = edited output message * imsg = input string containing edit codes (%x) * */ void DEVICE::edit_mount_codes(POOL_MEM &omsg, const char *imsg) { const char *p; const char *str; char add[20]; POOL_MEM archive_name(PM_FNAME); omsg.c_str()[0] = 0; Dmsg1(800, "edit_mount_codes: %s\n", imsg); for (p=imsg; *p; p++) { if (*p == '%') { switch (*++p) { case '%': str = "%"; break; case 'a': str = dev_name; break; case 'e': if (num_dvd_parts == 0) { if (truncating || blank_dvd) { str = "2"; } else { str = "1"; } } else { str = "0"; } break; case 'n': bsnprintf(add, sizeof(add), "%d", part); str = add; break; case 'm': str = device->mount_point; break; default: add[0] = '%'; add[1] = *p; add[2] = 0; str = add; break; } } else { add[0] = *p; add[1] = 0; str = add; } Dmsg1(1900, "add_str %s\n", str); pm_strcat(omsg, (char *)str); Dmsg1(1800, "omsg=%s\n", omsg.c_str()); } }
/* * Open the resource fork of a file. */ int bopen_rsrc(BFILE *bfd, const char *fname, int flags, mode_t mode) { POOLMEM *rsrc_fname; rsrc_fname = get_pool_memory(PM_FNAME); pm_strcpy(rsrc_fname, fname); pm_strcat(rsrc_fname, _PATH_RSRCFORKSPEC); bopen(bfd, rsrc_fname, flags, mode, 0); free_pool_memory(rsrc_fname); return bfd->fid; }
/* * Get general SQL query for Catalog */ bool sqlquery_cmd(UAContext *ua, const char *cmd) { POOL_MEM query(PM_MESSAGE); int len; const char *msg; if (!open_client_db(ua, true)) { return true; } *query.c_str() = 0; ua->send_msg(_("Entering SQL query mode.\n" "Terminate each query with a semicolon.\n" "Terminate query mode with a blank line.\n")); msg = _("Enter SQL query: "); while (get_cmd(ua, msg)) { len = strlen(ua->cmd); Dmsg2(400, "len=%d cmd=%s:\n", len, ua->cmd); if (len == 0) { break; } if (*query.c_str() != 0) { pm_strcat(query, " "); } pm_strcat(query, ua->cmd); if (ua->cmd[len-1] == ';') { ua->cmd[len-1] = 0; /* zap ; */ /* Submit query */ db_list_sql_query(ua->jcr, ua->db, query.c_str(), ua->send, HORZ_LIST, true); *query.c_str() = 0; /* start new query */ msg = _("Enter SQL query: "); } else { msg = _("Add to SQL query: "); } } ua->send_msg(_("End query mode.\n")); return true; }
/* Dump the item table format to a text file (used by plugin) */ int ConfigFile::serialize(POOLMEM **buf) { int len; POOLMEM *tmp; if (!items) { **buf = 0; return 0; } len = Mmsg(buf, "# Plugin configuration file\n# Version %d\n", version); tmp = get_pool_memory(PM_MESSAGE); for (int i = 0; items[i].name; i++) { if (items[i].comment) { Mmsg(tmp, "OptPrompt=%s\n", items[i].comment); pm_strcat(buf, tmp); } if (items[i].default_value) { Mmsg(tmp, "OptDefault=%s\n", items[i].default_value); pm_strcat(buf, tmp); } if (items[i].required) { Mmsg(tmp, "OptRequired=yes\n"); pm_strcat(buf, tmp); } /* variable = @INT64@ */ Mmsg(tmp, "%s=%s\n\n", items[i].name, ini_get_store_code(items[i].handler)); len = pm_strcat(buf, tmp); } free_pool_memory(tmp); return len ; }
/* * Dump the item table format to a text file (used by plugin) */ int ConfigFile::serialize(POOL_MEM *buf) { int len; POOL_MEM tmp(PM_MESSAGE); if (!items) { char *p; p = buf->c_str(); p[0] = '\0'; return 0; } len = Mmsg(buf, "# Plugin configuration file\n# Version %d\n", version); for (int i = 0; items[i].name; i++) { if (items[i].comment) { Mmsg(tmp, "OptPrompt=%s\n", items[i].comment); pm_strcat(buf, tmp.c_str()); } if (items[i].default_value) { Mmsg(tmp, "OptDefault=%s\n", items[i].default_value); pm_strcat(buf, tmp.c_str()); } if (items[i].required) { Mmsg(tmp, "OptRequired=yes\n"); pm_strcat(buf, tmp.c_str()); } /* variable = @INT64@ */ Mmsg(tmp, "%s=%s\n\n", items[i].name, ini_get_store_code(items[i].type)); len = pm_strcat(buf, tmp.c_str()); } return len ; }
/* * Edit codes into (Un)MountCommand * %% = % * %a = archive device name * %m = mount point * * omsg = edited output message * imsg = input string containing edit codes (%x) * */ void DEVICE::edit_mount_codes(POOL_MEM &omsg, const char *imsg) { const char *p; const char *str; char add[20]; POOL_MEM archive_name(PM_FNAME); omsg.c_str()[0] = 0; Dmsg1(800, "edit_mount_codes: %s\n", imsg); for (p=imsg; *p; p++) { if (*p == '%') { switch (*++p) { case '%': str = "%"; break; case 'a': str = dev_name; break; case 'm': str = device->mount_point; break; default: add[0] = '%'; add[1] = *p; add[2] = 0; str = add; break; } } else { add[0] = *p; add[1] = 0; str = add; } Dmsg1(1900, "add_str %s\n", str); pm_strcat(omsg, (char *)str); Dmsg1(1800, "omsg=%s\n", omsg.c_str()); } }
/* * Verify attributes of the requested files on the Volume * */ void do_verify_volume(JCR *jcr) { BSOCK *sd, *dir; POOLMEM *fname; /* original file name */ POOLMEM *lname; /* link name */ int32_t stream; uint32_t size; uint32_t VolSessionId, VolSessionTime, file_index; uint32_t record_file_index; char digest[BASE64_SIZE(CRYPTO_DIGEST_MAX_SIZE)]; int type, stat; sd = jcr->store_bsock; if (!sd) { Jmsg(jcr, M_FATAL, 0, _("Storage command not issued before Verify.\n")); jcr->setJobStatus(JS_FatalError); return; } dir = jcr->dir_bsock; jcr->setJobStatus(JS_Running); LockRes(); CLIENT *client = (CLIENT *)GetNextRes(R_CLIENT, NULL); UnlockRes(); uint32_t buf_size; if (client) { buf_size = client->max_network_buffer_size; } else { buf_size = 0; /* use default */ } if (!sd->set_buffer_size(buf_size, BNET_SETBUF_WRITE)) { jcr->setJobStatus(JS_FatalError); return; } jcr->buf_size = sd->msglen; fname = get_pool_memory(PM_FNAME); lname = get_pool_memory(PM_FNAME); /* * Get a record from the Storage daemon */ while (bget_msg(sd) >= 0 && !job_canceled(jcr)) { /* * First we expect a Stream Record Header */ if (sscanf(sd->msg, rec_header, &VolSessionId, &VolSessionTime, &file_index, &stream, &size) != 5) { Jmsg1(jcr, M_FATAL, 0, _("Record header scan error: %s\n"), sd->msg); goto bail_out; } Dmsg3(30, "Got hdr: FilInx=%d Stream=%d size=%d.\n", file_index, stream, size); /* * Now we expect the Stream Data */ if (bget_msg(sd) < 0) { Jmsg1(jcr, M_FATAL, 0, _("Data record error. ERR=%s\n"), sd->bstrerror()); goto bail_out; } if (size != ((uint32_t)sd->msglen)) { Jmsg2(jcr, M_FATAL, 0, _("Actual data size %d not same as header %d\n"), sd->msglen, size); goto bail_out; } Dmsg2(30, "Got stream data %s, len=%d\n", stream_to_ascii(stream), sd->msglen); /* File Attributes stream */ switch (stream) { case STREAM_UNIX_ATTRIBUTES: case STREAM_UNIX_ATTRIBUTES_EX: char *ap, *lp, *fp; Dmsg0(400, "Stream=Unix Attributes.\n"); if ((int)sizeof_pool_memory(fname) < sd->msglen) { fname = realloc_pool_memory(fname, sd->msglen + 1); } if ((int)sizeof_pool_memory(lname) < sd->msglen) { lname = realloc_pool_memory(lname, sd->msglen + 1); } *fname = 0; *lname = 0; /* * An Attributes record consists of: * File_index * Type (FT_types) * Filename * Attributes * Link name (if file linked i.e. FT_LNK) * Extended Attributes (if Win32) */ if (sscanf(sd->msg, "%d %d", &record_file_index, &type) != 2) { Jmsg(jcr, M_FATAL, 0, _("Error scanning record header: %s\n"), sd->msg); Dmsg0(0, "\nError scanning header\n"); goto bail_out; } Dmsg2(30, "Got Attr: FilInx=%d type=%d\n", record_file_index, type); ap = sd->msg; while (*ap++ != ' ') /* skip record file index */ ; while (*ap++ != ' ') /* skip type */ ; /* Save filename and position to attributes */ fp = fname; while (*ap != 0) { *fp++ = *ap++; /* copy filename to fname */ } *fp = *ap++; /* terminate filename & point to attribs */ Dmsg2(100, "File=%s Attr=%s\n", fname, ap); /* Skip to Link name */ if (type == FT_LNK || type == FT_LNKSAVED) { lp = ap; while (*lp++ != 0) { ; } pm_strcat(lname, lp); /* "save" link name */ } else { *lname = 0; } jcr->lock(); jcr->JobFiles++; jcr->num_files_examined++; pm_strcpy(jcr->last_fname, fname); /* last file examined */ jcr->unlock(); /* * Send file attributes to Director * File_index * Stream * Verify Options * Filename (full path) * Encoded attributes * Link name (if type==FT_LNK) * For a directory, link is the same as fname, but with trailing * slash. For a linked file, link is the link. */ /* Send file attributes to Director */ Dmsg2(200, "send ATTR inx=%d fname=%s\n", jcr->JobFiles, fname); if (type == FT_LNK || type == FT_LNKSAVED) { stat = dir->fsend("%d %d %s %s%c%s%c%s%c", jcr->JobFiles, STREAM_UNIX_ATTRIBUTES, "pinsug5", fname, 0, ap, 0, lname, 0); /* for a deleted record, we set fileindex=0 */ } else if (type == FT_DELETED) { stat = dir->fsend("%d %d %s %s%c%s%c%c", 0, STREAM_UNIX_ATTRIBUTES, "pinsug5", fname, 0, ap, 0, 0); } else { stat = dir->fsend("%d %d %s %s%c%s%c%c", jcr->JobFiles, STREAM_UNIX_ATTRIBUTES, "pinsug5", fname, 0, ap, 0, 0); } Dmsg2(200, "bfiled>bdird: attribs len=%d: msg=%s\n", dir->msglen, dir->msg); if (!stat) { Jmsg(jcr, M_FATAL, 0, _("Network error in send to Director: ERR=%s\n"), dir->bstrerror()); goto bail_out; } break; case STREAM_MD5_DIGEST: bin_to_base64(digest, sizeof(digest), (char *)sd->msg, CRYPTO_DIGEST_MD5_SIZE, true); Dmsg2(400, "send inx=%d MD5=%s\n", jcr->JobFiles, digest); dir->fsend("%d %d %s *MD5-%d*", jcr->JobFiles, STREAM_MD5_DIGEST, digest, jcr->JobFiles); Dmsg2(20, "bfiled>bdird: MD5 len=%d: msg=%s\n", dir->msglen, dir->msg); break; case STREAM_SHA1_DIGEST: bin_to_base64(digest, sizeof(digest), (char *)sd->msg, CRYPTO_DIGEST_SHA1_SIZE, true); Dmsg2(400, "send inx=%d SHA1=%s\n", jcr->JobFiles, digest); dir->fsend("%d %d %s *SHA1-%d*", jcr->JobFiles, STREAM_SHA1_DIGEST, digest, jcr->JobFiles); Dmsg2(20, "bfiled>bdird: SHA1 len=%d: msg=%s\n", dir->msglen, dir->msg); break; case STREAM_SHA256_DIGEST: bin_to_base64(digest, sizeof(digest), (char *)sd->msg, CRYPTO_DIGEST_SHA256_SIZE, true); Dmsg2(400, "send inx=%d SHA256=%s\n", jcr->JobFiles, digest); dir->fsend("%d %d %s *SHA256-%d*", jcr->JobFiles, STREAM_SHA256_DIGEST, digest, jcr->JobFiles); Dmsg2(20, "bfiled>bdird: SHA256 len=%d: msg=%s\n", dir->msglen, dir->msg); break; case STREAM_SHA512_DIGEST: bin_to_base64(digest, sizeof(digest), (char *)sd->msg, CRYPTO_DIGEST_SHA512_SIZE, true); Dmsg2(400, "send inx=%d SHA512=%s\n", jcr->JobFiles, digest); dir->fsend("%d %d %s *SHA512-%d*", jcr->JobFiles, STREAM_SHA512_DIGEST, digest, jcr->JobFiles); Dmsg2(20, "bfiled>bdird: SHA512 len=%d: msg=%s\n", dir->msglen, dir->msg); break; /* * Restore stream object is counted, but not restored here */ case STREAM_RESTORE_OBJECT: jcr->lock(); jcr->JobFiles++; jcr->num_files_examined++; jcr->unlock(); break; /* Ignore everything else */ default: break; } /* end switch */ } /* end while bnet_get */ jcr->setJobStatus(JS_Terminated); goto ok_out; bail_out: jcr->setJobStatus(JS_ErrorTerminated); ok_out: if (jcr->compress_buf) { free(jcr->compress_buf); jcr->compress_buf = NULL; } free_pool_memory(fname); free_pool_memory(lname); Dmsg2(050, "End Verify-Vol. Files=%d Bytes=%" lld "\n", jcr->JobFiles, jcr->JobBytes); }
/* * Fill the NDMP restore environment table with the data for the data agent to act on. */ static inline bool fill_restore_environment(JCR *jcr, int32_t current_fi, struct ndm_job_param *job) { int i; char *bp; ndmp9_pval pv; FILESETRES *fileset; char *restore_pathname, *ndmp_filesystem, *restore_prefix, *level; POOL_MEM tape_device; POOL_MEM destination_path; ndmp_backup_format_option *nbf_options; /* * See if we know this backup format and get it options. */ nbf_options = ndmp_lookup_backup_format_options(job->bu_type); /* * Lookup the current fileindex and map it to an actual pathname. */ restore_pathname = lookup_fileindex(jcr, current_fi); if (!restore_pathname) { return false; } else { /* * Skip over the /@NDMP prefix. */ ndmp_filesystem = restore_pathname + 6; } /* * See if there is a level embedded in the pathname. */ bp = strrchr(ndmp_filesystem, '%'); if (bp) { *bp++ = '\0'; level = bp; } else { level = NULL; } /* * Lookup the environment stack saved during the backup so we can restore it. */ if (!db_get_ndmp_environment_string(jcr, jcr->db, &jcr->jr, ndmp_env_handler, &job->env_tab)) { /* * Fallback code try to build a environment stack that is good enough to * restore this NDMP backup. This is used when the data is not available in * the database when its either expired or when an old NDMP backup is restored * where the whole environment was not saved. */ if (!nbf_options || nbf_options->uses_file_history) { /* * We asked during the NDMP backup to receive file history info. */ pv.name = ndmp_env_keywords[NDMP_ENV_KW_HIST]; pv.value = ndmp_env_values[NDMP_ENV_VALUE_YES]; ndma_store_env_list(&job->env_tab, &pv); } /* * Tell the data agent what type of restore stream to expect. */ pv.name = ndmp_env_keywords[NDMP_ENV_KW_TYPE]; pv.value = job->bu_type; ndma_store_env_list(&job->env_tab, &pv); /* * Tell the data agent that this is a NDMP backup which uses a level indicator. */ if (level) { pv.name = ndmp_env_keywords[NDMP_ENV_KW_LEVEL]; pv.value = level; ndma_store_env_list(&job->env_tab, &pv); } /* * Tell the data engine what was backuped. */ pv.name = ndmp_env_keywords[NDMP_ENV_KW_FILESYSTEM]; pv.value = ndmp_filesystem; ndma_store_env_list(&job->env_tab, &pv); } /* * Lookup any meta tags that need to be added. */ fileset = jcr->res.fileset; for (i = 0; i < fileset->num_includes; i++) { int j; char *item; INCEXE *ie = fileset->include_items[i]; /* * Loop over each file = entry of the fileset. */ for (j = 0; j < ie->name_list.size(); j++) { item = (char *)ie->name_list.get(j); /* * See if the original path matches. */ if (bstrcasecmp(item, ndmp_filesystem)) { int k, l; FOPTS *fo; for (k = 0; k < ie->num_opts; k++) { fo = ie->opts_list[i]; /* * Parse all specific META tags for this option block. */ for (l = 0; l < fo->meta.size(); l++) { ndmp_parse_meta_tag(&job->env_tab, (char *)fo->meta.get(l)); } } } } } /* * See where to restore the data. */ restore_prefix = NULL; if (jcr->where) { restore_prefix = jcr->where; } else { restore_prefix = jcr->res.job->RestoreWhere; } if (!restore_prefix) { return false; } /* * Tell the data engine where to restore. */ if (nbf_options && nbf_options->restore_prefix_relative) { switch (*restore_prefix) { case '^': /* * Use the restore_prefix as an absolute restore prefix. * We skip the leading ^ that is the trigger for absolute restores. */ pm_strcpy(destination_path, restore_prefix + 1); break; default: /* * Use the restore_prefix as an relative restore prefix. */ if (strlen(restore_prefix) == 1 && *restore_prefix == '/') { pm_strcpy(destination_path, ndmp_filesystem); } else { pm_strcpy(destination_path, ndmp_filesystem); pm_strcat(destination_path, restore_prefix); } } } else { if (strlen(restore_prefix) == 1 && *restore_prefix == '/') { /* * Use the original pathname as restore prefix. */ pm_strcpy(destination_path, ndmp_filesystem); } else { /* * Use the restore_prefix as an absolute restore prefix. */ pm_strcpy(destination_path, restore_prefix); } } pv.name = ndmp_env_keywords[NDMP_ENV_KW_PREFIX]; pv.value = ndmp_filesystem; ndma_store_env_list(&job->env_tab, &pv); if (!nbf_options || nbf_options->needs_namelist) { if (set_files_to_restore(jcr, job, current_fi, destination_path.c_str(), ndmp_filesystem) == 0) { /* * There is no specific filename selected so restore everything. */ add_to_namelist(job, (char *)"", destination_path.c_str(), (char *)"", (char *)"", NDMP_INVALID_U_QUAD); } } /* * If we have a paired storage definition we put the storage daemon * auth key and the filesystem into the tape device name of the * NDMP session. This way the storage daemon can link the NDMP * data and the normal save session together. */ if (jcr->store_bsock) { Mmsg(tape_device, "%s@%s", jcr->sd_auth_key, restore_pathname + 6); job->tape_device = bstrdup(tape_device.c_str()); } free(restore_pathname); return true; }
/* * Restore files */ bool restore_cmd(UAContext *ua, const char *cmd) { RESTORE_CTX rx; /* restore context */ POOL_MEM buf; JOBRES *job; int i; JCR *jcr = ua->jcr; char *escaped_bsr_name = NULL; char *escaped_where_name = NULL; char *strip_prefix, *add_prefix, *add_suffix, *regexp; strip_prefix = add_prefix = add_suffix = regexp = NULL; memset(&rx, 0, sizeof(rx)); rx.path = get_pool_memory(PM_FNAME); rx.fname = get_pool_memory(PM_FNAME); rx.JobIds = get_pool_memory(PM_FNAME); rx.JobIds[0] = 0; rx.BaseJobIds = get_pool_memory(PM_FNAME); rx.query = get_pool_memory(PM_FNAME); rx.bsr = new_bsr(); i = find_arg_with_value(ua, "comment"); if (i >= 0) { rx.comment = ua->argv[i]; if (!is_comment_legal(ua, rx.comment)) { goto bail_out; } } i = find_arg_with_value(ua, "backupformat"); if (i >= 0) { rx.backup_format = ua->argv[i]; } i = find_arg_with_value(ua, "where"); if (i >= 0) { rx.where = ua->argv[i]; } i = find_arg_with_value(ua, "replace"); if (i >= 0) { rx.replace = ua->argv[i]; } i = find_arg_with_value(ua, "pluginoptions"); if (i >= 0) { rx.plugin_options = ua->argv[i]; } i = find_arg_with_value(ua, "strip_prefix"); if (i >= 0) { strip_prefix = ua->argv[i]; } i = find_arg_with_value(ua, "add_prefix"); if (i >= 0) { add_prefix = ua->argv[i]; } i = find_arg_with_value(ua, "add_suffix"); if (i >= 0) { add_suffix = ua->argv[i]; } i = find_arg_with_value(ua, "regexwhere"); if (i >= 0) { rx.RegexWhere = ua->argv[i]; } if (strip_prefix || add_suffix || add_prefix) { int len = bregexp_get_build_where_size(strip_prefix, add_prefix, add_suffix); regexp = (char *)bmalloc(len * sizeof(char)); bregexp_build_where(regexp, len, strip_prefix, add_prefix, add_suffix); rx.RegexWhere = regexp; } /* TODO: add acl for regexwhere ? */ if (rx.RegexWhere) { if (!acl_access_ok(ua, Where_ACL, rx.RegexWhere, true)) { ua->error_msg(_("\"RegexWhere\" specification not authorized.\n")); goto bail_out; } } if (rx.where) { if (!acl_access_ok(ua, Where_ACL, rx.where, true)) { ua->error_msg(_("\"where\" specification not authorized.\n")); goto bail_out; } } if (!open_client_db(ua, true)) { goto bail_out; } /* Ensure there is at least one Restore Job */ LockRes(); foreach_res(job, R_JOB) { if (job->JobType == JT_RESTORE) { if (!rx.restore_job) { rx.restore_job = job; } rx.restore_jobs++; } } UnlockRes(); if (!rx.restore_jobs) { ua->error_msg(_( "No Restore Job Resource found in bareos-dir.conf.\n" "You must create at least one before running this command.\n")); goto bail_out; } /* * Request user to select JobIds or files by various different methods * last 20 jobs, where File saved, most recent backup, ... * In the end, a list of files are pumped into * add_findex() */ switch (user_select_jobids_or_files(ua, &rx)) { case 0: /* error */ goto bail_out; case 1: /* selected by jobid */ get_and_display_basejobs(ua, &rx); if (!build_directory_tree(ua, &rx)) { ua->send_msg(_("Restore not done.\n")); goto bail_out; } break; case 2: /* selected by filename, no tree needed */ break; } if (rx.bsr->JobId) { char ed1[50]; if (!complete_bsr(ua, rx.bsr)) { /* find Vol, SessId, SessTime from JobIds */ ua->error_msg(_("Unable to construct a valid BSR. Cannot continue.\n")); goto bail_out; } if (!(rx.selected_files = write_bsr_file(ua, rx))) { ua->warning_msg(_("No files selected to be restored.\n")); goto bail_out; } display_bsr_info(ua, rx); /* display vols needed, etc */ if (rx.selected_files==1) { ua->info_msg(_("\n1 file selected to be restored.\n\n")); } else { ua->info_msg(_("\n%s files selected to be restored.\n\n"), edit_uint64_with_commas(rx.selected_files, ed1)); } } else { ua->warning_msg(_("No files selected to be restored.\n")); goto bail_out; } if (rx.restore_jobs == 1) { job = rx.restore_job; } else { job = get_restore_job(ua); } if (!job) { goto bail_out; } if (!get_client_name(ua, &rx)) { goto bail_out; } if (!rx.ClientName) { ua->error_msg(_("No Client resource found!\n")); goto bail_out; } if (!get_restore_client_name(ua, rx)) { goto bail_out; } escaped_bsr_name = escape_filename(jcr->RestoreBootstrap); Mmsg(ua->cmd, "run job=\"%s\" client=\"%s\" restoreclient=\"%s\" storage=\"%s\"" " bootstrap=\"%s\" files=%u catalog=\"%s\"", job->name(), rx.ClientName, rx.RestoreClientName, rx.store?rx.store->name():"", escaped_bsr_name ? escaped_bsr_name : jcr->RestoreBootstrap, rx.selected_files, ua->catalog->name()); /* * Build run command */ if (rx.backup_format) { Mmsg(buf, " backupformat=%s", rx.backup_format); pm_strcat(ua->cmd, buf); } pm_strcpy(buf, ""); if (rx.RegexWhere) { escaped_where_name = escape_filename(rx.RegexWhere); Mmsg(buf, " regexwhere=\"%s\"", escaped_where_name ? escaped_where_name : rx.RegexWhere); } else if (rx.where) { escaped_where_name = escape_filename(rx.where); Mmsg(buf," where=\"%s\"", escaped_where_name ? escaped_where_name : rx.where); } pm_strcat(ua->cmd, buf); if (rx.replace) { Mmsg(buf, " replace=%s", rx.replace); pm_strcat(ua->cmd, buf); } if (rx.plugin_options) { Mmsg(buf, " pluginoptions=%s", rx.plugin_options); pm_strcat(ua->cmd, buf); } if (rx.comment) { Mmsg(buf, " comment=\"%s\"", rx.comment); pm_strcat(ua->cmd, buf); } if (escaped_bsr_name != NULL) { bfree(escaped_bsr_name); } if (escaped_where_name != NULL) { bfree(escaped_where_name); } if (regexp) { bfree(regexp); } if (find_arg(ua, NT_("yes")) > 0) { pm_strcat(ua->cmd, " yes"); /* pass it on to the run command */ } Dmsg1(200, "Submitting: %s\n", ua->cmd); /* * Transfer jobids to jcr to for picking up restore objects */ jcr->JobIds = rx.JobIds; rx.JobIds = NULL; parse_ua_args(ua); run_cmd(ua, ua->cmd); free_rx(&rx); garbage_collect_memory(); /* release unused memory */ return true; bail_out: if (escaped_bsr_name != NULL) { bfree(escaped_bsr_name); } if (escaped_where_name != NULL) { bfree(escaped_where_name); } if (regexp) { bfree(regexp); } free_rx(&rx); garbage_collect_memory(); /* release unused memory */ return false; }
/* * The first step in the restore process is for the user to * select a list of JobIds from which he will subsequently * select which files are to be restored. * * Returns: 2 if filename list made * 1 if jobid list made * 0 on error */ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx) { char *p; char date[MAX_TIME_LENGTH]; bool have_date = false; /* Include current second if using current time */ utime_t now = time(NULL) + 1; JobId_t JobId; JOB_DBR jr = { (JobId_t)-1 }; bool done = false; int i, j; const char *list[] = { _("List last 20 Jobs run"), _("List Jobs where a given File is saved"), _("Enter list of comma separated JobIds to select"), _("Enter SQL list command"), _("Select the most recent backup for a client"), _("Select backup for a client before a specified time"), _("Enter a list of files to restore"), _("Enter a list of files to restore before a specified time"), _("Find the JobIds of the most recent backup for a client"), _("Find the JobIds for a backup for a client before a specified time"), _("Enter a list of directories to restore for found JobIds"), _("Select full restore to a specified Job date"), _("Cancel"), NULL }; const char *kw[] = { /* * These keywords are handled in a for loop */ "jobid", /* 0 */ "current", /* 1 */ "before", /* 2 */ "file", /* 3 */ "directory", /* 4 */ "select", /* 5 */ "pool", /* 6 */ "all", /* 7 */ /* * The keyword below are handled by individual arg lookups */ "client", /* 8 */ "storage", /* 9 */ "fileset", /* 10 */ "where", /* 11 */ "yes", /* 12 */ "bootstrap", /* 13 */ "done", /* 14 */ "strip_prefix", /* 15 */ "add_prefix", /* 16 */ "add_suffix", /* 17 */ "regexwhere", /* 18 */ "restoreclient", /* 19 */ "copies", /* 20 */ "comment", /* 21 */ "restorejob", /* 22 */ "replace", /* 23 */ "pluginoptions", /* 24 */ NULL }; rx->JobIds[0] = 0; for (i = 1; i<ua->argc; i++) { /* loop through arguments */ bool found_kw = false; for (j = 0; kw[j]; j++) { /* loop through keywords */ if (bstrcasecmp(kw[j], ua->argk[i])) { found_kw = true; break; } } if (!found_kw) { ua->error_msg(_("Unknown keyword: %s\n"), ua->argk[i]); return 0; } /* Found keyword in kw[] list, process it */ switch (j) { case 0: /* jobid */ if (!has_value(ua, i)) { return 0; } if (*rx->JobIds != 0) { pm_strcat(rx->JobIds, ","); } pm_strcat(rx->JobIds, ua->argv[i]); done = true; break; case 1: /* current */ /* * Note, we add one second here just to include any job * that may have finished within the current second, * which happens a lot in scripting small jobs. */ bstrutime(date, sizeof(date), now); have_date = true; break; case 2: /* before */ if (have_date || !has_value(ua, i)) { return 0; } if (str_to_utime(ua->argv[i]) == 0) { ua->error_msg(_("Improper date format: %s\n"), ua->argv[i]); return 0; } bstrncpy(date, ua->argv[i], sizeof(date)); have_date = true; break; case 3: /* file */ case 4: /* dir */ if (!has_value(ua, i)) { return 0; } if (!have_date) { bstrutime(date, sizeof(date), now); } if (!get_client_name(ua, rx)) { return 0; } pm_strcpy(ua->cmd, ua->argv[i]); insert_one_file_or_dir(ua, rx, date, j==4); return 2; case 5: /* select */ if (!have_date) { bstrutime(date, sizeof(date), now); } if (!select_backups_before_date(ua, rx, date)) { return 0; } done = true; break; case 6: /* pool specified */ if (!has_value(ua, i)) { return 0; } rx->pool = (POOLRES *)GetResWithName(R_POOL, ua->argv[i]); if (!rx->pool) { ua->error_msg(_("Error: Pool resource \"%s\" does not exist.\n"), ua->argv[i]); return 0; } if (!acl_access_ok(ua, Pool_ACL, ua->argv[i], true)) { rx->pool = NULL; ua->error_msg(_("Error: Pool resource \"%s\" access not allowed.\n"), ua->argv[i]); return 0; } break; case 7: /* all specified */ rx->all = true; break; default: /* * All keywords 7 or greater are ignored or handled by a select prompt */ break; } } if (!done) { ua->send_msg(_("\nFirst you select one or more JobIds that contain files\n" "to be restored. You will be presented several methods\n" "of specifying the JobIds. Then you will be allowed to\n" "select which files from those JobIds are to be restored.\n\n")); } /* If choice not already made above, prompt */ for ( ; !done; ) { char *fname; int len; bool gui_save; db_list_ctx jobids; start_prompt(ua, _("To select the JobIds, you have the following choices:\n")); for (int i=0; list[i]; i++) { add_prompt(ua, list[i]); } done = true; switch (do_prompt(ua, "", _("Select item: "), NULL, 0)) { case -1: /* error or cancel */ return 0; case 0: /* list last 20 Jobs run */ if (!acl_access_ok(ua, Command_ACL, NT_("sqlquery"), true)) { ua->error_msg(_("SQL query not authorized.\n")); return 0; } gui_save = ua->jcr->gui; ua->jcr->gui = true; db_list_sql_query(ua->jcr, ua->db, uar_list_jobs, ua->send, HORZ_LIST, true); ua->jcr->gui = gui_save; done = false; break; case 1: /* list where a file is saved */ if (!get_client_name(ua, rx)) { return 0; } if (!get_cmd(ua, _("Enter Filename (no path):"))) { return 0; } len = strlen(ua->cmd); fname = (char *)malloc(len * 2 + 1); db_escape_string(ua->jcr, ua->db, fname, ua->cmd, len); Mmsg(rx->query, uar_file[db_get_type_index(ua->db)], rx->ClientName, fname); free(fname); gui_save = ua->jcr->gui; ua->jcr->gui = true; db_list_sql_query(ua->jcr, ua->db, rx->query, ua->send, HORZ_LIST, true); ua->jcr->gui = gui_save; done = false; break; case 2: /* enter a list of JobIds */ if (!get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) { return 0; } pm_strcpy(rx->JobIds, ua->cmd); break; case 3: /* Enter an SQL list command */ if (!acl_access_ok(ua, Command_ACL, NT_("sqlquery"), true)) { ua->error_msg(_("SQL query not authorized.\n")); return 0; } if (!get_cmd(ua, _("Enter SQL list command: "))) { return 0; } gui_save = ua->jcr->gui; ua->jcr->gui = true; db_list_sql_query(ua->jcr, ua->db, ua->cmd, ua->send, HORZ_LIST, true); ua->jcr->gui = gui_save; done = false; break; case 4: /* Select the most recent backups */ if (!have_date) { bstrutime(date, sizeof(date), now); } if (!select_backups_before_date(ua, rx, date)) { return 0; } break; case 5: /* select backup at specified time */ if (!have_date) { if (!get_date(ua, date, sizeof(date))) { return 0; } } if (!select_backups_before_date(ua, rx, date)) { return 0; } break; case 6: /* Enter files */ if (!have_date) { bstrutime(date, sizeof(date), now); } if (!get_client_name(ua, rx)) { return 0; } ua->send_msg(_("Enter file names with paths, or < to enter a filename\n" "containing a list of file names with paths, and terminate\n" "them with a blank line.\n")); for ( ;; ) { if (!get_cmd(ua, _("Enter full filename: "))) { return 0; } len = strlen(ua->cmd); if (len == 0) { break; } insert_one_file_or_dir(ua, rx, date, false); } return 2; case 7: /* enter files backed up before specified time */ if (!have_date) { if (!get_date(ua, date, sizeof(date))) { return 0; } } if (!get_client_name(ua, rx)) { return 0; } ua->send_msg(_("Enter file names with paths, or < to enter a filename\n" "containing a list of file names with paths, and terminate\n" "them with a blank line.\n")); for ( ;; ) { if (!get_cmd(ua, _("Enter full filename: "))) { return 0; } len = strlen(ua->cmd); if (len == 0) { break; } insert_one_file_or_dir(ua, rx, date, false); } return 2; case 8: /* Find JobIds for current backup */ if (!have_date) { bstrutime(date, sizeof(date), now); } if (!select_backups_before_date(ua, rx, date)) { return 0; } done = false; break; case 9: /* Find JobIds for give date */ if (!have_date) { if (!get_date(ua, date, sizeof(date))) { return 0; } } if (!select_backups_before_date(ua, rx, date)) { return 0; } done = false; break; case 10: /* Enter directories */ if (*rx->JobIds != 0) { ua->send_msg(_("You have already selected the following JobIds: %s\n"), rx->JobIds); } else if (get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) { if (*rx->JobIds != 0 && *ua->cmd) { pm_strcat(rx->JobIds, ","); } pm_strcat(rx->JobIds, ua->cmd); } if (*rx->JobIds == 0 || *rx->JobIds == '.') { *rx->JobIds = 0; return 0; /* nothing entered, return */ } if (!have_date) { bstrutime(date, sizeof(date), now); } if (!get_client_name(ua, rx)) { return 0; } ua->send_msg(_("Enter full directory names or start the name\n" "with a < to indicate it is a filename containing a list\n" "of directories and terminate them with a blank line.\n")); for ( ;; ) { if (!get_cmd(ua, _("Enter directory name: "))) { return 0; } len = strlen(ua->cmd); if (len == 0) { break; } /* Add trailing slash to end of directory names */ if (ua->cmd[0] != '<' && !IsPathSeparator(ua->cmd[len-1])) { strcat(ua->cmd, "/"); } insert_one_file_or_dir(ua, rx, date, true); } return 2; case 11: /* Choose a jobid and select jobs */ if (!get_cmd(ua, _("Enter JobId to get the state to restore: ")) || !is_an_integer(ua->cmd)) { return 0; } memset(&jr, 0, sizeof(jr)); jr.JobId = str_to_int64(ua->cmd); if (!db_get_job_record(ua->jcr, ua->db, &jr)) { ua->error_msg(_("Unable to get Job record for JobId=%s: ERR=%s\n"), ua->cmd, db_strerror(ua->db)); return 0; } ua->send_msg(_("Selecting jobs to build the Full state at %s\n"), jr.cStartTime); jr.JobLevel = L_INCREMENTAL; /* Take Full+Diff+Incr */ if (!db_accurate_get_jobids(ua->jcr, ua->db, &jr, &jobids)) { return 0; } pm_strcpy(rx->JobIds, jobids.list); Dmsg1(30, "Item 12: jobids = %s\n", rx->JobIds); break; case 12: /* Cancel or quit */ return 0; } } memset(&jr, 0, sizeof(jr)); POOLMEM *JobIds = get_pool_memory(PM_FNAME); *JobIds = 0; rx->TotalFiles = 0; /* * Find total number of files to be restored, and filter the JobId * list to contain only ones permitted by the ACL conditions. */ for (p=rx->JobIds; ; ) { char ed1[50]; int status = get_next_jobid_from_list(&p, &JobId); if (status < 0) { ua->error_msg(_("Invalid JobId in list.\n")); free_pool_memory(JobIds); return 0; } if (status == 0) { break; } if (jr.JobId == JobId) { continue; /* duplicate of last JobId */ } memset(&jr, 0, sizeof(jr)); jr.JobId = JobId; if (!db_get_job_record(ua->jcr, ua->db, &jr)) { ua->error_msg(_("Unable to get Job record for JobId=%s: ERR=%s\n"), edit_int64(JobId, ed1), db_strerror(ua->db)); free_pool_memory(JobIds); return 0; } if (!acl_access_ok(ua, Job_ACL, jr.Name, true)) { ua->error_msg(_("Access to JobId=%s (Job \"%s\") not authorized. Not selected.\n"), edit_int64(JobId, ed1), jr.Name); continue; } if (*JobIds != 0) { pm_strcat(JobIds, ","); } pm_strcat(JobIds, edit_int64(JobId, ed1)); rx->TotalFiles += jr.JobFiles; } free_pool_memory(rx->JobIds); rx->JobIds = JobIds; /* Set ACL filtered list */ if (*rx->JobIds == 0) { ua->warning_msg(_("No Jobs selected.\n")); return 0; } if (strchr(rx->JobIds,',')) { ua->info_msg(_("You have selected the following JobIds: %s\n"), rx->JobIds); } else { ua->info_msg(_("You have selected the following JobId: %s\n"), rx->JobIds); } return true; }
/* * List Job record(s) that match JOB_DBR */ void db_list_job_records(JCR *jcr, B_DB *mdb, JOB_DBR *jr, const char *range, const char* clientname, int jobstatus, const char* volumename, utime_t since_time, int last, int count, OUTPUT_FORMATTER *sendit, e_list_type type) { char ed1[50]; char esc[MAX_ESCAPE_NAME_LENGTH]; POOL_MEM temp(PM_MESSAGE), selection(PM_MESSAGE), criteria(PM_MESSAGE); POOL_MEM selection_last(PM_MESSAGE); char dt[MAX_TIME_LENGTH]; if (jr->JobId > 0) { temp.bsprintf("AND Job.JobId=%s", edit_int64(jr->JobId, ed1)); pm_strcat(selection, temp.c_str()); } if (jr->Name[0] != 0) { mdb->db_escape_string(jcr, esc, jr->Name, strlen(jr->Name)); temp.bsprintf( "AND Job.Name = '%s' ", esc); pm_strcat(selection, temp.c_str()); } if (clientname) { temp.bsprintf("AND Client.Name = '%s' ", clientname); pm_strcat(selection, temp.c_str()); } if (jobstatus) { temp.bsprintf("AND Job.JobStatus = '%c' ", jobstatus); pm_strcat(selection, temp.c_str()); } if (volumename) { temp.bsprintf("AND Media.Volumename = '%s' ", volumename); pm_strcat(selection, temp.c_str()); } if (since_time) { bstrutime(dt, sizeof(dt), since_time); temp.bsprintf("AND Job.SchedTime > '%s' ", dt); pm_strcat(selection, temp.c_str()); } if (last > 0) { /* * Show only the last run of a job (Job.Name). * Do a subquery to get a list of matching JobIds * to be used in the main query later. * * range: while it might be more efficient, * to apply the range to the subquery, * at least mariadb 10 does not support this. * Therefore range is handled in the main query. */ temp.bsprintf("AND Job.JobId IN (%s) ", list_jobs_last); selection_last.bsprintf(temp.c_str(), selection.c_str(), ""); /* * As the existing selection is handled in the subquery, * overwrite the main query selection * by the newly created selection_last. */ pm_strcpy(selection, selection_last.c_str()); } db_lock(mdb); if (count > 0) { Mmsg(mdb->cmd, list_jobs_count, selection.c_str(), range); } else { if (type == VERT_LIST) { Mmsg(mdb->cmd, list_jobs_long, selection.c_str(), range); } else { Mmsg(mdb->cmd, list_jobs, selection.c_str(), range); } } if (!QUERY_DB(jcr, mdb, mdb->cmd)) { goto bail_out; } sendit->array_start("jobs"); list_result(jcr, mdb, sendit, type); sendit->array_end("jobs"); sql_free_result(mdb); bail_out: db_unlock(mdb); }
/* * Edit codes into ChangerCommand * %% = % * %a = Archive device name * %c = Changer device name * %D = Diagnostic device name * %d = Changer drive index * %f = Client's name * %j = Job name * %o = Command * %s = Slot base 0 * %S = Slot base 1 * %v = Volume name * * * omsg = edited output message * imsg = input string containing edit codes (%x) * cmd = command string (load, unload, ...) */ char *edit_device_codes(DCR *dcr, POOLMEM *&omsg, const char *imsg, const char *cmd) { const char *p; const char *str; char ed1[50]; *omsg = 0; Dmsg1(1800, "edit_device_codes: %s\n", imsg); for (p=imsg; *p; p++) { if (*p == '%') { switch (*++p) { case '%': str = "%"; break; case 'a': str = dcr->dev->archive_name(); break; case 'c': str = NPRT(dcr->device->changer_name); break; case 'D': str = NPRT(dcr->device->diag_device_name); break; case 'd': str = edit_int64(dcr->dev->drive_index, ed1); break; case 'o': str = NPRT(cmd); break; case 's': str = edit_int64(dcr->VolCatInfo.Slot - 1, ed1); break; case 'S': str = edit_int64(dcr->VolCatInfo.Slot, ed1); break; case 'j': /* Job name */ str = dcr->jcr->Job; break; case 'v': if (dcr->VolCatInfo.VolCatName[0]) { str = dcr->VolCatInfo.VolCatName; } else if (dcr->VolumeName[0]) { str = dcr->VolumeName; } else if (dcr->dev->vol && dcr->dev->vol->vol_name) { str = dcr->dev->vol->vol_name; } else { str = dcr->dev->VolHdr.VolumeName; } break; case 'f': str = NPRT(dcr->jcr->client_name); break; default: ed1[0] = '%'; ed1[1] = *p; ed1[2] = 0; str = ed1; break; } } else { ed1[0] = *p; ed1[1] = 0; str = ed1; } Dmsg1(1900, "add_str %s\n", str); pm_strcat(omsg, (char *)str); Dmsg1(1800, "omsg=%s\n", omsg); } Dmsg1(800, "omsg=%s\n", omsg); return omsg; }
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 (strcmp(result->d_name, ".") == 0 || strcmp(result->d_name, "..") == 0) { 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 (!dir_get_volume_info(dcr, 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; }
/* Store Messages Destination information */ void store_msgs(LEX *lc, RES_ITEM *item, int index, int pass) { int token; char *cmd; POOLMEM *dest; int dest_len; Dmsg2(900, "store_msgs pass=%d code=%d\n", pass, item->code); if (pass == 1) { switch (item->code) { case MD_STDOUT: case MD_STDERR: case MD_SYSLOG: /* syslog */ case MD_CONSOLE: case MD_CATALOG: scan_types(lc, (MSGS *)(item->value), item->code, NULL, NULL); break; case MD_OPERATOR: /* send to operator */ case MD_DIRECTOR: /* send to Director */ case MD_MAIL: /* mail */ case MD_MAIL_ON_ERROR: /* mail if Job errors */ case MD_MAIL_ON_SUCCESS: /* mail if Job succeeds */ if (item->code == MD_OPERATOR) { cmd = res_all.res_msgs.operator_cmd; } else { cmd = res_all.res_msgs.mail_cmd; } dest = get_pool_memory(PM_MESSAGE); dest[0] = 0; dest_len = 0; /* Pick up comma separated list of destinations */ for ( ;; ) { token = lex_get_token(lc, T_NAME); /* scan destination */ dest = check_pool_memory_size(dest, dest_len + lc->str_len + 2); if (dest[0] != 0) { pm_strcat(dest, " "); /* separate multiple destinations with space */ dest_len++; } pm_strcat(dest, lc->str); dest_len += lc->str_len; Dmsg2(900, "store_msgs newdest=%s: dest=%s:\n", lc->str, NPRT(dest)); token = lex_get_token(lc, T_SKIP_EOL); if (token == T_COMMA) { continue; /* get another destination */ } if (token != T_EQUALS) { scan_err1(lc, _("expected an =, got: %s"), lc->str); return; } break; } Dmsg1(900, "mail_cmd=%s\n", NPRT(cmd)); scan_types(lc, (MSGS *)(item->value), item->code, dest, cmd); free_pool_memory(dest); Dmsg0(900, "done with dest codes\n"); break; case MD_FILE: /* file */ case MD_APPEND: /* append */ dest = get_pool_memory(PM_MESSAGE); /* Pick up a single destination */ token = lex_get_token(lc, T_NAME); /* scan destination */ pm_strcpy(dest, lc->str); dest_len = lc->str_len; token = lex_get_token(lc, T_SKIP_EOL); Dmsg1(900, "store_msgs dest=%s:\n", NPRT(dest)); if (token != T_EQUALS) { scan_err1(lc, _("expected an =, got: %s"), lc->str); return; } scan_types(lc, (MSGS *)(item->value), item->code, dest, NULL); free_pool_memory(dest); Dmsg0(900, "done with dest codes\n"); break; default: scan_err1(lc, _("Unknown item code: %d\n"), item->code); return; } } scan_to_eol(lc); set_bit(index, res_all.hdr.item_present); Dmsg0(900, "Done store_msgs\n"); }
bool print_config_schema_json(POOL_MEM &buffer) { pm_strcat(buffer, "{ \"success\": false, \"message\": \"not available\" }"); return false; }
/* * Parse the plugin definition passed in. * * The definition is in this form: * * python:module_path=<path>:module_name=<python_module_name>:... */ static bRC parse_plugin_definition(bpContext *ctx, void *value, POOL_MEM &plugin_options) { bool found; int i, cnt; POOL_MEM plugin_definition(PM_FNAME); char *bp, *argument, *argument_value; plugin_ctx *p_ctx = (plugin_ctx *)ctx->pContext; if (!value) { return bRC_Error; } /* * Parse the plugin definition. * Make a private copy of the whole string. */ pm_strcpy(plugin_definition, (char *)value); bp = strchr(plugin_definition.c_str(), ':'); if (!bp) { Jmsg(ctx, M_FATAL, "Illegal plugin definition %s\n", plugin_definition.c_str()); Dmsg(ctx, dbglvl, "Illegal plugin definition %s\n", plugin_definition.c_str()); goto bail_out; } /* * Skip the first ':' */ bp++; cnt = 0; while (bp) { if (strlen(bp) == 0) { break; } /* * Each argument is in the form: * <argument> = <argument_value> * * So we setup the right pointers here, argument to the beginning * of the argument, argument_value to the beginning of the argument_value. */ argument = bp; argument_value = strchr(bp, '='); if (!argument_value) { Jmsg(ctx, M_FATAL, "Illegal argument %s without value\n", argument); Dmsg(ctx, dbglvl, "Illegal argument %s without value\n", argument); goto bail_out; } *argument_value++ = '\0'; /* * See if there are more arguments and setup for the next run. */ bp = argument_value; do { bp = strchr(bp, ':'); if (bp) { if (*(bp - 1) != '\\') { *bp++ = '\0'; break; } else { bp++; } } } while (bp); found = false; for (i = 0; plugin_arguments[i].name; i++) { if (bstrcasecmp(argument, plugin_arguments[i].name)) { int64_t *int_destination = NULL; char **str_destination = NULL; bool *bool_destination = NULL; switch (plugin_arguments[i].type) { case argument_instance: int_destination = &p_ctx->instance; break; case argument_module_path: str_destination = &p_ctx->module_path; break; case argument_module_name: str_destination = &p_ctx->module_name; break; default: break; } if (int_destination) { *int_destination = parse_integer(argument_value); } if (str_destination) { set_string(str_destination, argument_value); } if (bool_destination) { *bool_destination = parse_boolean(argument_value); } /* * When we have a match break the loop. */ found = true; break; } } /* * If we didn't consume this parameter we add it to the plugin_options list. */ if (!found) { POOL_MEM option(PM_FNAME); if (cnt) { Mmsg(option, ":%s=%s", argument, argument_value); pm_strcat(plugin_options, option.c_str()); } else { Mmsg(option, "%s=%s", argument, argument_value); pm_strcat(plugin_options, option.c_str()); } cnt++; } } if (cnt > 0) { pm_strcat(plugin_options, ":"); } return bRC_OK; bail_out: return bRC_Error; }
/* * Remove old .spool files written by me from the working directory. */ static void cleanup_old_files() { DIR* dp; struct dirent *entry, *result; int rc, name_max; int my_name_len = strlen(my_name); int len = strlen(me->working_directory); POOLMEM *cleanup = get_pool_memory(PM_MESSAGE); POOLMEM *basename = get_pool_memory(PM_MESSAGE); regex_t preg1; char prbuf[500]; const int nmatch = 30; regmatch_t pmatch[nmatch]; berrno be; /* Look for .spool files but don't allow spaces */ const char *pat1 = "^[^ ]+\\.spool$"; /* Setup working directory prefix */ pm_strcpy(basename, me->working_directory); if (len > 0 && !IsPathSeparator(me->working_directory[len-1])) { pm_strcat(basename, "/"); } /* Compile regex expressions */ rc = regcomp(&preg1, pat1, REG_EXTENDED); if (rc != 0) { regerror(rc, &preg1, prbuf, sizeof(prbuf)); Pmsg2(000, _("Could not compile regex pattern \"%s\" ERR=%s\n"), pat1, prbuf); goto get_out2; } name_max = pathconf(".", _PC_NAME_MAX); if (name_max < 1024) { name_max = 1024; } if (!(dp = opendir(me->working_directory))) { berrno be; Pmsg2(000, "Failed to open working dir %s for cleanup: ERR=%s\n", me->working_directory, be.bstrerror()); goto get_out1; } entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 1000); while (1) { if ((readdir_r(dp, entry, &result) != 0) || (result == NULL)) { break; } /* Exclude any name with ., .., not my_name or containing a space */ if (strcmp(result->d_name, ".") == 0 || strcmp(result->d_name, "..") == 0 || strncmp(result->d_name, my_name, my_name_len) != 0) { Dmsg1(500, "Skipped: %s\n", result->d_name); continue; } /* Unlink files that match regex */ if (regexec(&preg1, result->d_name, nmatch, pmatch, 0) == 0) { pm_strcpy(cleanup, basename); pm_strcat(cleanup, result->d_name); Dmsg1(500, "Unlink: %s\n", cleanup); unlink(cleanup); } } free(entry); closedir(dp); get_out1: regfree(&preg1); get_out2: free_pool_memory(cleanup); free_pool_memory(basename); }
/* * Build attr->ofname from attr->fname and * attr->olname from attr->olname */ void build_attr_output_fnames(JCR *jcr, ATTR *attr) { /* * Prepend the where directory so that the * files are put where the user wants. * * We do a little jig here to handle Win32 files with * a drive letter -- we simply change the drive * from, for example, c: to c/ for * every filename if a prefix is supplied. * */ if (jcr->where_bregexp) { char *ret; apply_bregexps(attr->fname, jcr->where_bregexp, &ret); pm_strcpy(attr->ofname, ret); if (attr->type == FT_LNKSAVED || attr->type == FT_LNK) { /* Always add prefix to hard links (FT_LNKSAVED) and * on user request to soft links */ if ((attr->type == FT_LNKSAVED || jcr->prefix_links)) { apply_bregexps(attr->lname, jcr->where_bregexp, &ret); pm_strcpy(attr->olname, ret); } else { pm_strcpy(attr->olname, attr->lname); } } } else if (jcr->where[0] == 0) { pm_strcpy(attr->ofname, attr->fname); pm_strcpy(attr->olname, attr->lname); } else { const char *fn; int wherelen = strlen(jcr->where); pm_strcpy(attr->ofname, jcr->where); /* copy prefix */ #if defined(HAVE_WIN32) if (attr->fname[1] == ':') { attr->fname[1] = '/'; /* convert : to / */ } #endif fn = attr->fname; /* take whole name */ /* Ensure where is terminated with a slash */ if (!IsPathSeparator(jcr->where[wherelen-1]) && !IsPathSeparator(fn[0])) { pm_strcat(attr->ofname, "/"); } pm_strcat(attr->ofname, fn); /* copy rest of name */ /* * Fixup link name -- if it is an absolute path */ if (attr->type == FT_LNKSAVED || attr->type == FT_LNK) { bool add_link; /* Always add prefix to hard links (FT_LNKSAVED) and * on user request to soft links */ if (IsPathSeparator(attr->lname[0]) && (attr->type == FT_LNKSAVED || jcr->prefix_links)) { pm_strcpy(attr->olname, jcr->where); add_link = true; } else { attr->olname[0] = 0; add_link = false; } #if defined(HAVE_WIN32) if (attr->lname[1] == ':') { attr->lname[1] = '/'; /* turn : into / */ } #endif fn = attr->lname; /* take whole name */ /* Ensure where is terminated with a slash */ if (add_link && !IsPathSeparator(jcr->where[wherelen-1]) && !IsPathSeparator(fn[0])) { pm_strcat(attr->olname, "/"); } pm_strcat(attr->olname, fn); /* copy rest of link */ } } #if defined(HAVE_WIN32) strip_double_slashes(attr->ofname); strip_double_slashes(attr->olname); #endif }