bool RUNSCRIPT::run(JCR *jcr, const char *name) { Dmsg1(100, "runscript: running a RUNSCRIPT object type=%d\n", cmd_type); POOLMEM *ecmd = get_pool_memory(PM_FNAME); int status; BPIPE *bpipe; POOL_MEM line(PM_NAME); ecmd = edit_job_codes(jcr, ecmd, this->command, "", this->job_code_callback); Dmsg1(100, "runscript: running '%s'...\n", ecmd); Jmsg(jcr, M_INFO, 0, _("%s: run %s \"%s\"\n"), cmd_type==SHELL_CMD?"shell command":"console command", name, ecmd); switch (cmd_type) { case SHELL_CMD: bpipe = open_bpipe(ecmd, 0, "r"); free_pool_memory(ecmd); if (bpipe == NULL) { berrno be; Jmsg(jcr, M_ERROR, 0, _("Runscript: %s could not execute. ERR=%s\n"), name, be.bstrerror()); goto bail_out; } while (fgets(line.c_str(), line.size(), bpipe->rfd)) { strip_trailing_junk(line.c_str()); Jmsg(jcr, M_INFO, 0, _("%s: %s\n"), name, line.c_str()); } status = close_bpipe(bpipe); if (status != 0) { berrno be; Jmsg(jcr, M_ERROR, 0, _("Runscript: %s returned non-zero status=%d. ERR=%s\n"), name, be.code(status), be.bstrerror(status)); goto bail_out; } Dmsg0(100, "runscript OK\n"); break; case CONSOLE_CMD: if (console_command) { /* can we run console command? */ if (!console_command(jcr, ecmd)) { /* yes, do so */ goto bail_out; } } break; } return true; bail_out: /* cancel running job properly */ if (fail_on_error) { jcr->setJobStatus(JS_ErrorTerminated); } Dmsg1(100, "runscript failed. fail_on_error=%d\n", fail_on_error); return false; }
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; }
/* * Run an external program. Optionally wait a specified number * of seconds. Program killed if wait exceeded. Optionally * return the output from the program (normally a single line). * * If the watchdog kills the program, fgets returns, and ferror is set * to 1 (=>SUCCESS), so we check if the watchdog killed the program. * * Contrary to my normal calling conventions, this program * * Returns: 0 on success * non-zero on error == berrno status */ int run_program(char *prog, int wait, POOLMEM *&results) { BPIPE *bpipe; int stat1, stat2; char *mode; mode = (char *)"r"; bpipe = open_bpipe(prog, wait, mode); if (!bpipe) { return ENOENT; } results[0] = 0; int len = sizeof_pool_memory(results) - 1; fgets(results, len, bpipe->rfd); results[len] = 0; if (feof(bpipe->rfd)) { stat1 = 0; } else { stat1 = ferror(bpipe->rfd); } if (stat1 < 0) { berrno be; Dmsg2(150, "Run program fgets stat=%d ERR=%s\n", stat1, be.bstrerror(errno)); } else if (stat1 != 0) { Dmsg1(150, "Run program fgets stat=%d\n", stat1); if (bpipe->timer_id) { Dmsg1(150, "Run program fgets killed=%d\n", bpipe->timer_id->killed); /* NB: I'm not sure it is really useful for run_program. Without the * following lines run_program would not detect if the program was killed * by the watchdog. */ if (bpipe->timer_id->killed) { stat1 = ETIME; pm_strcpy(results, _("Program killed by Bacula (timeout)\n")); } } } stat2 = close_bpipe(bpipe); stat1 = stat2 != 0 ? stat2 : stat1; Dmsg1(150, "Run program returning %d\n", stat1); return stat1; }
/* * This job is done, so release the device. From a Unix standpoint, * the device remains open. * * Note, if we were spooling, we may enter with the device blocked. * We unblock at the end, only if it was us who blocked the * device. * */ bool release_device(DCR *dcr) { utime_t now; JCR *jcr = dcr->jcr; DEVICE *dev = dcr->dev; bool ok = true; char tbuf[100]; int was_blocked = BST_NOT_BLOCKED; /* * Capture job statistics now that we are done using this device. */ now = (utime_t)time(NULL); update_job_statistics(jcr, now); dev->Lock(); if (!dev->is_blocked()) { block_device(dev, BST_RELEASING); } else { was_blocked = dev->blocked(); dev->set_blocked(BST_RELEASING); } lock_volumes(); Dmsg2(100, "release_device device %s is %s\n", dev->print_name(), dev->is_tape() ? "tape" : "disk"); /* * If device is reserved, job never started, so release the reserve here */ dcr->clear_reserved(); if (dev->can_read()) { VOLUME_CAT_INFO *vol = &dev->VolCatInfo; dev->clear_read(); /* clear read bit */ Dmsg2(150, "dir_update_vol_info. label=%d Vol=%s\n", dev->is_labeled(), vol->VolCatName); if (dev->is_labeled() && vol->VolCatName[0] != 0) { dcr->dir_update_volume_info(false, false); /* send Volume info to Director */ remove_read_volume(jcr, dcr->VolumeName); volume_unused(dcr); } } else if (dev->num_writers > 0) { /* * Note if WEOT is set, we are at the end of the tape and may not be positioned correctly, * so the job_media_record and update_vol_info have already been done, * which means we skip them here. */ dev->num_writers--; Dmsg1(100, "There are %d writers in release_device\n", dev->num_writers); if (dev->is_labeled()) { Dmsg2(200, "dir_create_jobmedia. Release vol=%s dev=%s\n", dev->getVolCatName(), dev->print_name()); if (!dev->at_weot() && !dcr->dir_create_jobmedia_record(false)) { Jmsg2(jcr, M_FATAL, 0, _("Could not create JobMedia record for Volume=\"%s\" Job=%s\n"), dcr->getVolCatName(), jcr->Job); } /* * If no more writers, and no errors, and wrote something, write an EOF */ if (!dev->num_writers && dev->can_write() && dev->block_num > 0) { dev->weof(1); write_ansi_ibm_labels(dcr, ANSI_EOF_LABEL, dev->VolHdr.VolumeName); } if (!dev->at_weot()) { dev->VolCatInfo.VolCatFiles = dev->file; /* set number of files */ /* * Note! do volume update before close, which zaps VolCatInfo */ dcr->dir_update_volume_info(false, false); /* send Volume info to Director */ Dmsg2(200, "dir_update_vol_info. Release vol=%s dev=%s\n", dev->getVolCatName(), dev->print_name()); } if (dev->num_writers == 0) { /* if not being used */ volume_unused(dcr); /* we obviously are not using the volume */ } } } else { /* * If we reach here, it is most likely because the job has failed, * since the device is not in read mode and there are no writers. * It was probably reserved. */ volume_unused(dcr); } Dmsg3(100, "%d writers, %d reserve, dev=%s\n", dev->num_writers, dev->num_reserved(), dev->print_name()); /* * If no writers, close if file or !CAP_ALWAYS_OPEN */ if (dev->num_writers == 0 && (!dev->is_tape() || !dev->has_cap(CAP_ALWAYSOPEN))) { dev->close(dcr); free_volume(dev); } unlock_volumes(); /* * Fire off Alert command and include any output */ if (!job_canceled(jcr)) { if (!dcr->device->drive_tapealert_enabled && dcr->device->alert_command) { int status = 1; POOLMEM *alert, *line; BPIPE *bpipe; alert = get_pool_memory(PM_FNAME); line = get_pool_memory(PM_FNAME); alert = edit_device_codes(dcr, alert, dcr->device->alert_command, ""); /* * Wait maximum 5 minutes */ bpipe = open_bpipe(alert, 60 * 5, "r"); if (bpipe) { while (bfgets(line, bpipe->rfd)) { Jmsg(jcr, M_ALERT, 0, _("Alert: %s"), line); } status = close_bpipe(bpipe); } else { status = errno; } if (status != 0) { berrno be; Jmsg(jcr, M_ALERT, 0, _("3997 Bad alert command: %s: ERR=%s.\n"), alert, be.bstrerror(status)); } Dmsg1(400, "alert status=%d\n", status); free_pool_memory(alert); free_pool_memory(line); } else { /* * If all reservations are cleared for this device raise an event that SD plugins can register to. */ if (dev->num_reserved() == 0) { generate_plugin_event(jcr, bsdEventDeviceReleased, dcr); } } } pthread_cond_broadcast(&dev->wait_next_vol); Dmsg2(100, "JobId=%u broadcast wait_device_release at %s\n", (uint32_t)jcr->JobId, bstrftimes(tbuf, sizeof(tbuf), (utime_t)time(NULL))); release_device_cond(); /* * If we are the thread that blocked the device, then unblock it */ if (pthread_equal(dev->no_wait_id, pthread_self())) { dev->dunblock(true); } else { /* * Otherwise, reset the prior block status and unlock */ dev->set_blocked(was_blocked); dev->Unlock(); } if (dcr->keep_dcr) { detach_dcr_from_dev(dcr); } else { free_dcr(dcr); } Dmsg2(100, "Device %s released by JobId=%u\n", dev->print_name(), (uint32_t)jcr->JobId); return ok; }
/* * Open a new configuration file. We push the * state of the current file (lf) so that we * can do includes. This is a bit of a hammer. * Instead of passing back the pointer to the * new packet, I simply replace the contents * of the caller's packet with the new packet, * and link the contents of the old packet into * the next field. */ LEX *lex_open_file(LEX *lf, const char *filename, LEX_ERROR_HANDLER *scan_error, LEX_WARNING_HANDLER *scan_warning) { FILE *fd; BPIPE *bpipe = NULL; char *bpipe_filename = NULL; if (filename[0] == '|') { bpipe_filename = bstrdup(filename); if ((bpipe = open_bpipe(bpipe_filename + 1, 0, "rb")) == NULL) { free(bpipe_filename); return NULL; } free(bpipe_filename); fd = bpipe->rfd; return lex_add(lf, filename, fd, bpipe, scan_error, scan_warning); } else { #ifdef HAVE_GLOB int globrc; glob_t fileglob; char *filename_expanded = NULL; /* * Flag GLOB_NOMAGIC is a GNU extension, therefore manually check if string is a wildcard string. */ /* * Clear fileglob at least required for mingw version of glob() */ memset(&fileglob, 0, sizeof(fileglob)); globrc = glob(filename, 0, NULL, &fileglob); if ((globrc == GLOB_NOMATCH) && (is_wildcard_string(filename))) { /* * fname is a wildcard string, but no matching files have been found. * Ignore this include statement and continue. */ return lf; } else if (globrc != 0) { /* * glob() error has occurred. Giving up. */ return NULL; } Dmsg2(100, "glob %s: %i files\n", filename, fileglob.gl_pathc); for (size_t i = 0; i < fileglob.gl_pathc; i++) { filename_expanded = fileglob.gl_pathv[i]; if ((fd = fopen(filename_expanded, "rb")) == NULL) { globfree(&fileglob); return NULL; } lf = lex_add(lf, filename_expanded, fd, bpipe, scan_error, scan_warning); } globfree(&fileglob); #else if ((fd = fopen(filename, "rb")) == NULL) { return NULL; } lf = lex_add(lf, filename, fd, bpipe, scan_error, scan_warning); #endif return lf; } }
void update_bootstrap_file(JCR *jcr) { /* Now update the bootstrap file if any */ if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes && jcr->job->WriteBootstrap) { FILE *fd; BPIPE *bpipe = NULL; int got_pipe = 0; POOLMEM *fname = get_pool_memory(PM_FNAME); fname = edit_job_codes(jcr, fname, jcr->job->WriteBootstrap, ""); VOL_PARAMS *VolParams = NULL; int VolCount; char edt[50], ed1[50], ed2[50]; if (*fname == '|') { got_pipe = 1; bpipe = open_bpipe(fname+1, 0, "w"); /* skip first char "|" */ fd = bpipe ? bpipe->wfd : NULL; } else { /* ***FIXME*** handle BASE */ fd = fopen(fname, jcr->get_JobLevel()==L_FULL?"w+b":"a+b"); } if (fd) { VolCount = db_get_job_volume_parameters(jcr, jcr->db, jcr->JobId, &VolParams); if (VolCount == 0) { Jmsg(jcr, M_ERROR, 0, _("Could not get Job Volume Parameters to " "update Bootstrap file. ERR=%s\n"), db_strerror(jcr->db)); if (jcr->SDJobFiles != 0) { set_jcr_job_status(jcr, JS_ErrorTerminated); } } /* Start output with when and who wrote it */ bstrftimes(edt, sizeof(edt), time(NULL)); fprintf(fd, "# %s - %s - %s%s\n", edt, jcr->jr.Job, level_to_str(jcr->get_JobLevel()), jcr->since); for (int i=0; i < VolCount; i++) { /* Write the record */ fprintf(fd, "Volume=\"%s\"\n", VolParams[i].VolumeName); fprintf(fd, "MediaType=\"%s\"\n", VolParams[i].MediaType); if (VolParams[i].Slot > 0) { fprintf(fd, "Slot=%d\n", VolParams[i].Slot); } fprintf(fd, "VolSessionId=%u\n", jcr->VolSessionId); fprintf(fd, "VolSessionTime=%u\n", jcr->VolSessionTime); fprintf(fd, "VolAddr=%s-%s\n", edit_uint64(VolParams[i].StartAddr, ed1), edit_uint64(VolParams[i].EndAddr, ed2)); fprintf(fd, "FileIndex=%d-%d\n", VolParams[i].FirstIndex, VolParams[i].LastIndex); } if (VolParams) { free(VolParams); } if (got_pipe) { close_bpipe(bpipe); } else { fclose(fd); } } else { berrno be; Jmsg(jcr, M_ERROR, 0, _("Could not open WriteBootstrap file:\n" "%s: ERR=%s\n"), fname, be.bstrerror()); set_jcr_job_status(jcr, JS_ErrorTerminated); } free_pool_memory(fname); } }
/* * Run an external program. Optionally wait a specified number * of seconds. Program killed if wait exceeded (it is done by the * watchdog, as fgets is a blocking function). * * If the watchdog kills the program, fgets returns, and ferror is set * to 1 (=>SUCCESS), so we check if the watchdog killed the program. * * Return the full output from the program (not only the first line). * * Contrary to my normal calling conventions, this program * * Returns: 0 on success * non-zero on error == berrno status * */ int run_program_full_output(char *prog, int wait, POOLMEM *&results) { BPIPE *bpipe; int stat1, stat2; char *mode; POOLMEM* tmp; char *buf; const int bufsize = 32000; Dsm_check(200); tmp = get_pool_memory(PM_MESSAGE); buf = (char *)malloc(bufsize+1); results[0] = 0; mode = (char *)"r"; bpipe = open_bpipe(prog, wait, mode); if (!bpipe) { stat1 = ENOENT; goto bail_out; } Dsm_check(200); tmp[0] = 0; while (1) { buf[0] = 0; fgets(buf, bufsize, bpipe->rfd); buf[bufsize] = 0; pm_strcat(tmp, buf); if (feof(bpipe->rfd)) { stat1 = 0; Dmsg1(900, "Run program fgets stat=%d\n", stat1); break; } else { stat1 = ferror(bpipe->rfd); } if (stat1 < 0) { berrno be; Dmsg2(200, "Run program fgets stat=%d ERR=%s\n", stat1, be.bstrerror()); break; } else if (stat1 != 0) { Dmsg1(900, "Run program fgets stat=%d\n", stat1); if (bpipe->timer_id && bpipe->timer_id->killed) { Dmsg1(250, "Run program saw fgets killed=%d\n", bpipe->timer_id->killed); break; } } } /* * We always check whether the timer killed the program. We would see * an eof even when it does so we just have to trust the killed flag * and set the timer values to avoid edge cases where the program ends * just as the timer kills it. */ if (bpipe->timer_id && bpipe->timer_id->killed) { Dmsg1(150, "Run program fgets killed=%d\n", bpipe->timer_id->killed); pm_strcpy(tmp, _("Program killed by Bacula (timeout)\n")); stat1 = ETIME; } pm_strcpy(results, tmp); Dmsg3(1900, "resadr=0x%x reslen=%d res=%s\n", results, strlen(results), results); stat2 = close_bpipe(bpipe); stat1 = stat2 != 0 ? stat2 : stat1; Dmsg1(900, "Run program returning %d\n", stat1); bail_out: free_pool_memory(tmp); free(buf); return stat1; }
/* * Bareos is calling us to do the actual I/O */ static bRC pluginIO(bpContext *ctx, struct io_pkt *io) { struct plugin_ctx *p_ctx = (struct plugin_ctx *)ctx->pContext; if (!p_ctx) { return bRC_Error; } io->status = 0; io->io_errno = 0; switch(io->func) { case IO_OPEN: Dmsg(ctx, dbglvl, "bpipe-fd: IO_OPEN\n"); if (io->flags & (O_CREAT | O_WRONLY)) { char *writer_codes = apply_rp_codes(ctx); p_ctx->pfd = open_bpipe(writer_codes, 0, "w"); Dmsg(ctx, dbglvl, "bpipe-fd: IO_OPEN fd=%p writer=%s\n", p_ctx->pfd, writer_codes); if (!p_ctx->pfd) { io->io_errno = errno; Jmsg(ctx, M_FATAL, "bpipe-fd: Open pipe writer=%s failed: ERR=%s\n", writer_codes, strerror(io->io_errno)); Dmsg(ctx, dbglvl, "bpipe-fd: Open pipe writer=%s failed: ERR=%s\n", writer_codes, strerror(io->io_errno)); if (writer_codes) { free(writer_codes); } return bRC_Error; } if (writer_codes) { free(writer_codes); } } else { p_ctx->pfd = open_bpipe(p_ctx->reader, 0, "r", false); Dmsg(ctx, dbglvl, "bpipe-fd: IO_OPEN fd=%p reader=%s\n", p_ctx->pfd, p_ctx->reader); if (!p_ctx->pfd) { io->io_errno = errno; Jmsg(ctx, M_FATAL, "bpipe-fd: Open pipe reader=%s failed: ERR=%s\n", p_ctx->reader, strerror(io->io_errno)); Dmsg(ctx, dbglvl, "bpipe-fd: Open pipe reader=%s failed: ERR=%s\n", p_ctx->reader, strerror(io->io_errno)); return bRC_Error; } } sleep(1); /* let pipe connect */ break; case IO_READ: if (!p_ctx->pfd) { Jmsg(ctx, M_FATAL, "bpipe-fd: Logic error: NULL read FD\n"); Dmsg(ctx, dbglvl, "bpipe-fd: Logic error: NULL read FD\n"); return bRC_Error; } io->status = fread(io->buf, 1, io->count, p_ctx->pfd->rfd); if (io->status == 0 && ferror(p_ctx->pfd->rfd)) { io->io_errno = errno; Jmsg(ctx, M_FATAL, "bpipe-fd: Pipe read error: ERR=%s\n", strerror(io->io_errno)); Dmsg(ctx, dbglvl, "bpipe-fd: Pipe read error: ERR=%s\n", strerror(io->io_errno)); return bRC_Error; } break; case IO_WRITE: if (!p_ctx->pfd) { Jmsg(ctx, M_FATAL, "bpipe-fd: Logic error: NULL write FD\n"); Dmsg(ctx, dbglvl, "bpipe-fd: Logic error: NULL write FD\n"); return bRC_Error; } io->status = fwrite(io->buf, 1, io->count, p_ctx->pfd->wfd); if (io->status == 0 && ferror(p_ctx->pfd->wfd)) { io->io_errno = errno; Jmsg(ctx, M_FATAL, "bpipe-fd: Pipe write error: ERR=%s\n", strerror(io->io_errno)); Dmsg(ctx, dbglvl, "bpipe-fd: Pipe write error: ERR=%s\n", strerror(io->io_errno)); return bRC_Error; } break; case IO_CLOSE: if (!p_ctx->pfd) { Jmsg(ctx, M_FATAL, "bpipe-fd: Logic error: NULL FD on bpipe close\n"); Dmsg(ctx, dbglvl, "bpipe-fd: Logic error: NULL FD on bpipe close\n"); return bRC_Error; } io->status = close_bpipe(p_ctx->pfd); if (io->status) { Jmsg(ctx, M_FATAL, "bpipe-fd: Error closing stream for pseudo file %s: %d\n", p_ctx->fname, io->status); Dmsg(ctx, dbglvl, "bpipe-fd: Error closing stream for pseudo file %s: %d\n", p_ctx->fname, io->status); } break; case IO_SEEK: io->offset = p_ctx->offset; break; } return bRC_OK; }