/* * Wake watchdog timer thread so that it walks the * queue and adjusts its wait time (or exits). */ static void ping_watchdog() { P(timer_mutex); pthread_cond_signal(&timer); V(timer_mutex); bmicrosleep(0, 100); }
/* Respond to challenge from other end */ bool cram_md5_respond(BSOCK *bs, const char *password, int *tls_remote_need, bool *compatible) { char chal[MAXSTRING]; uint8_t hmac[20]; *compatible = false; if (bs->recv() <= 0) { bmicrosleep(5, 0); return false; } if (bs->msglen >= MAXSTRING) { Dmsg1(dbglvl, "Msg too long wanted auth cram... Got: %s", bs->msg); bmicrosleep(5, 0); return false; } Dmsg1(100, "cram-get received: %s", bs->msg); if (sscanf(bs->msg, "auth cram-md5c %s ssl=%d", chal, tls_remote_need) == 2) { *compatible = true; } else if (sscanf(bs->msg, "auth cram-md5 %s ssl=%d", chal, tls_remote_need) != 2) { if (sscanf(bs->msg, "auth cram-md5 %s\n", chal) != 1) { Dmsg1(dbglvl, "Cannot scan challenge: %s", bs->msg); bs->fsend(_("1999 Authorization failed.\n")); bmicrosleep(5, 0); return false; } } hmac_md5((uint8_t *)chal, strlen(chal), (uint8_t *)password, strlen(password), hmac); bs->msglen = bin_to_base64(bs->msg, 50, (char *)hmac, 16, *compatible) + 1; // Dmsg3(100, "get_auth: chal=%s pw=%s hmac=%s\n", chal, password, bs->msg); if (!bs->send()) { Dmsg1(dbglvl, "Send challenge failed. ERR=%s\n", bs->bstrerror()); return false; } Dmsg1(99, "sending resp to challenge: %s\n", bs->msg); if (bs->wait_data(180) <= 0 || bs->recv() <= 0) { Dmsg1(dbglvl, "Receive chanllenge response failed. ERR=%s\n", bs->bstrerror()); bmicrosleep(5, 0); return false; } if (bstrcmp(bs->msg, "1000 OK auth\n")) { return true; } Dmsg1(dbglvl, "Received bad response: %s\n", bs->msg); bmicrosleep(5, 0); return false; }
/* * Connection request. We accept connections either from the * Director, Storage Daemon or a Client (File daemon). * * Note, we are running as a seperate thread of the Storage daemon. * * Basic tasks done here: * - If it was a connection from the FD, call handle_filed_connection() * - If it was a connection from another SD, call handle_stored_connection() * - Otherwise it was a connection from the DIR, call handle_director_connection() */ static void *handle_connection_request(void *arg) { BSOCK *bs = (BSOCK *)arg; char name[MAX_NAME_LENGTH]; char tbuf[MAX_TIME_LENGTH]; if (bs->recv() <= 0) { Emsg1(M_ERROR, 0, _("Connection request from %s failed.\n"), bs->who()); bmicrosleep(5, 0); /* make user wait 5 seconds */ bs->close(); return NULL; } /* * Do a sanity check on the message received */ if (bs->msglen < MIN_MSG_LEN || bs->msglen > MAX_MSG_LEN) { Dmsg1(000, "<filed: %s", bs->msg); Emsg2(M_ERROR, 0, _("Invalid connection from %s. Len=%d\n"), bs->who(), bs->msglen); bmicrosleep(5, 0); /* make user wait 5 seconds */ bs->close(); return NULL; } Dmsg1(110, "Conn: %s", bs->msg); /* * See if this is a File daemon connection. If so call FD handler. */ if (sscanf(bs->msg, "Hello Start Job %127s", name) == 1) { Dmsg1(110, "Got a FD connection at %s\n", bstrftimes(tbuf, sizeof(tbuf), (utime_t)time(NULL))); return handle_filed_connection(bs, name); } /* * See if this is a Storage daemon connection. If so call SD handler. */ if (sscanf(bs->msg, "Hello Start Storage Job %127s", name) == 1) { Dmsg1(110, "Got a SD connection at %s\n", bstrftimes(tbuf, sizeof(tbuf), (utime_t)time(NULL))); return handle_stored_connection(bs, name); } Dmsg1(110, "Got a DIR connection at %s\n", bstrftimes(tbuf, sizeof(tbuf), (utime_t)time(NULL))); return handle_director_connection(bs); }
int32_t read_nbytes(BSOCK * bsock, char *ptr, int32_t nbytes) { int32_t nleft, nread; #ifdef HAVE_TLS if (bsock->tls) { /* TLS enabled */ return (tls_bsock_readn(bsock, ptr, nbytes)); } #endif /* HAVE_TLS */ nleft = nbytes; while (nleft > 0) { errno = 0; nread = socketRead(bsock->m_fd, ptr, nleft); if (bsock->is_timed_out() || bsock->is_terminated()) { return -1; } #ifdef HAVE_WIN32 /* * For Windows, we must simulate Unix errno on a socket * error in order to handle errors correctly. */ if (nread == SOCKET_ERROR) { DWORD err = WSAGetLastError(); nread = -1; if (err == WSAEINTR) { errno = EINTR; } else if (err == WSAEWOULDBLOCK) { errno = EAGAIN; } else { errno = EIO; /* some other error */ } } #endif if (nread == -1) { if (errno == EINTR) { continue; } if (errno == EAGAIN) { bmicrosleep(0, 20000); /* try again in 20ms */ continue; } } if (nread <= 0) { return -1; /* error, or EOF */ } nleft -= nread; ptr += nread; if (bsock->use_bwlimit()) { bsock->control_bwlimit(nread); } } return nbytes - nleft; /* return >= 0 */ }
/* * Inititiate the message channel with the Director. * He has made a connection to our server. * * Basic tasks done here: * Assume the Hello message is already in the input * buffer. We then authenticate him. * Get device, media, and pool information from Director * * This is the channel across which we will send error * messages and job status information. */ int authenticate_director(JCR *jcr) { BSOCK *dir = jcr->dir_bsock; if (!authenticate(R_DIRECTOR, dir, jcr)) { dir->fsend("%s", Dir_sorry); Dmsg1(dbglvl, "Unable to authenticate Director at %s.\n", dir->who()); Jmsg1(jcr, M_ERROR, 0, _("Unable to authenticate Director at %s.\n"), dir->who()); bmicrosleep(5, 0); return 0; } return dir->fsend("%s", OK_hello); }
/* * Try to connect to host for max_retry_time at retry_time intervals. * Note, you must have called the constructor prior to calling * this routine. */ bool BSOCK::connect(JCR * jcr, int retry_interval, utime_t max_retry_time, utime_t heart_beat, const char *name, char *host, char *service, int port, int verbose) { bool ok = false; int i; int fatal = 0; time_t begin_time = time(NULL); time_t now; btimer_t *tid = NULL; /* Try to trap out of OS call when time expires */ if (max_retry_time) { tid = start_thread_timer(jcr, pthread_self(), (uint32_t)max_retry_time); } for (i = 0; !open(jcr, name, host, service, port, heart_beat, &fatal); i -= retry_interval) { berrno be; if (fatal || (jcr && job_canceled(jcr))) { goto bail_out; } Dmsg4(100, "Unable to connect to %s on %s:%d. ERR=%s\n", name, host, port, be.bstrerror()); if (i < 0) { i = 60 * 5; /* complain again in 5 minutes */ if (verbose) Qmsg4(jcr, M_WARNING, 0, _( "Could not connect to %s on %s:%d. ERR=%s\n" "Retrying ...\n"), name, host, port, be.bstrerror()); } bmicrosleep(retry_interval, 0); now = time(NULL); if (begin_time + max_retry_time <= now) { Qmsg4(jcr, M_FATAL, 0, _("Unable to connect to %s on %s:%d. ERR=%s\n"), name, host, port, be.bstrerror()); goto bail_out; } } ok = true; bail_out: if (tid) { stop_thread_timer(tid); } return ok; }
/* * Takes base_name and appends (unique) current * date and time to form unique job name. * * Note, the seconds are actually a sequence number. This * permits us to start a maximum fo 59 unique jobs a second, which * should be sufficient. * * Returns: unique job name in jcr->Job * date/time in jcr->start_time */ void create_unique_job_name(JCR *jcr, const char *base_name) { /* Job start mutex */ static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; static time_t last_start_time = 0; static int seq = 0; time_t now = time(NULL); struct tm tm; char dt[MAX_TIME_LENGTH]; char name[MAX_NAME_LENGTH]; char *p; int len; /* Guarantee unique start time -- maximum one per second, and * thus unique Job Name */ P(mutex); /* lock creation of jobs */ seq++; if (seq > 59) { /* wrap as if it is seconds */ seq = 0; while (now == last_start_time) { bmicrosleep(0, 500000); now = time(NULL); } } last_start_time = now; V(mutex); /* allow creation of jobs */ jcr->start_time = now; /* Form Unique JobName */ (void)localtime_r(&now, &tm); /* Use only characters that are permitted in Windows filenames */ strftime(dt, sizeof(dt), "%Y-%m-%d_%H.%M.%S", &tm); len = strlen(dt) + 5; /* dt + .%02d EOS */ bstrncpy(name, base_name, sizeof(name)); name[sizeof(name)-len] = 0; /* truncate if too long */ bsnprintf(jcr->Job, sizeof(jcr->Job), "%s.%s_%02d", name, dt, seq); /* add date & time */ /* Convert spaces into underscores */ for (p=jcr->Job; *p; p++) { if (*p == ' ') { *p = '_'; } } Dmsg2(100, "JobId=%u created Job=%s\n", jcr->JobId, jcr->Job); }
/********************************************************************* * * Main Bareos Scheduler * */ JCR *wait_for_next_job(char *one_shot_job_to_run) { JCR *jcr; JOBRES *job; RUNRES *run; time_t now, prev; static bool first = true; job_item *next_job = NULL; Dmsg0(dbglvl, "Enter wait_for_next_job\n"); if (first) { first = false; /* Create scheduled jobs list */ jobs_to_run = New(dlist(next_job, &next_job->link)); if (one_shot_job_to_run) { /* one shot */ job = (JOBRES *)GetResWithName(R_JOB, one_shot_job_to_run); if (!job) { Emsg1(M_ABORT, 0, _("Job %s not found\n"), one_shot_job_to_run); } Dmsg1(5, "Found one_shot_job_to_run %s\n", one_shot_job_to_run); jcr = new_jcr(sizeof(JCR), dird_free_jcr); set_jcr_defaults(jcr, job); return jcr; } } /* Wait until we have something in the * next hour or so. */ again: while (jobs_to_run->empty()) { find_runs(); if (!jobs_to_run->empty()) { break; } bmicrosleep(next_check_secs, 0); /* recheck once per minute */ } #ifdef list_chain job_item *je; foreach_dlist(je, jobs_to_run) { dump_job(je, _("Walk queue")); }
/* Cleanup and then exit */ void terminate_dird(int sig) { static bool already_here = false; if (already_here) { /* avoid recursive temination problems */ bmicrosleep(2, 0); /* yield */ exit(1); } already_here = true; debug_level = 0; /* turn off debug */ stop_watchdog(); db_sql_pool_destroy(); db_flush_backends(); unload_dir_plugins(); write_state_file(me->working_directory, "bareos-dir", get_first_port_host_order(me->DIRaddrs)); delete_pid_file(me->pid_directory, "bareos-dir", get_first_port_host_order(me->DIRaddrs)); term_scheduler(); term_job_server(); if (runjob) { free(runjob); } if (configfile != NULL) { free(configfile); } if (debug_level > 5) { print_memory_pool_stats(); } if (my_config) { my_config->free_resources(); free(my_config); my_config = NULL; } stop_UA_server(); term_msg(); /* terminate message handler */ cleanup_crypto(); close_memory_pool(); /* release free memory in pool */ lmgr_cleanup_main(); sm_dump(false); exit(sig); }
/* * First prove our identity to the Storage daemon, then * make him prove his identity. */ int authenticate_storagedaemon(JCR *jcr) { BSOCK *sd = jcr->store_bsock; int tls_local_need = BNET_TLS_NONE; int tls_remote_need = BNET_TLS_NONE; int compatible = true; bool auth_success = false; btimer_t *tid = start_bsock_timer(sd, AUTH_TIMEOUT); /* TLS Requirement */ if (have_tls && me->tls_enable) { if (me->tls_require) { tls_local_need = BNET_TLS_REQUIRED; } else { tls_local_need = BNET_TLS_OK; } } if (me->tls_authenticate) { tls_local_need = BNET_TLS_REQUIRED; } if (job_canceled(jcr)) { auth_success = false; /* force quick exit */ goto auth_fatal; } /* Respond to SD challenge */ auth_success = cram_md5_respond(sd, jcr->sd_auth_key, &tls_remote_need, &compatible); if (job_canceled(jcr)) { auth_success = false; /* force quick exit */ goto auth_fatal; } if (!auth_success) { Dmsg1(dbglvl, "cram_respond failed for %s\n", sd->who()); } else { /* Now challenge him */ auth_success = cram_md5_challenge(sd, jcr->sd_auth_key, tls_local_need, compatible); if (!auth_success) { Dmsg1(dbglvl, "cram_challenge failed for %s\n", sd->who()); } } if (!auth_success) { Jmsg(jcr, M_FATAL, 0, _("Authorization key rejected by Storage daemon.\n" "Please see " MANUAL_AUTH_URL " for help.\n")); goto auth_fatal; } /* Verify that the remote host is willing to meet our TLS requirements */ if (tls_remote_need < tls_local_need && tls_local_need != BNET_TLS_OK && tls_remote_need != BNET_TLS_OK) { Jmsg(jcr, M_FATAL, 0, _("Authorization problem: Remote server did not" " advertize required TLS support.\n")); Dmsg2(dbglvl, "remote_need=%d local_need=%d\n", tls_remote_need, tls_local_need); auth_success = false; goto auth_fatal; } /* Verify that we are willing to meet the remote host's requirements */ if (tls_remote_need > tls_local_need && tls_local_need != BNET_TLS_OK && tls_remote_need != BNET_TLS_OK) { Jmsg(jcr, M_FATAL, 0, _("Authorization problem: Remote server requires TLS.\n")); Dmsg2(dbglvl, "remote_need=%d local_need=%d\n", tls_remote_need, tls_local_need); auth_success = false; goto auth_fatal; } if (tls_local_need >= BNET_TLS_OK && tls_remote_need >= BNET_TLS_OK) { /* Engage TLS! Full Speed Ahead! */ if (!bnet_tls_client(me->tls_ctx, sd, NULL)) { Jmsg(jcr, M_FATAL, 0, _("TLS negotiation failed.\n")); auth_success = false; goto auth_fatal; } if (me->tls_authenticate) { /* tls authentication only? */ sd->free_tls(); /* yes, shutdown tls */ } } auth_fatal: /* Destroy session key */ memset(jcr->sd_auth_key, 0, strlen(jcr->sd_auth_key)); stop_bsock_timer(tid); /* Single thread all failures to avoid DOS */ if (!auth_success) { P(mutex); bmicrosleep(6, 0); V(mutex); } return auth_success; }
/* Update the free space on the device */ bool DEVICE::update_freespace() { POOL_MEM ocmd(PM_FNAME); POOLMEM* results; char* icmd; int timeout; uint64_t free; char ed1[50]; bool ok = false; int status; if (!is_dvd() || is_freespace_ok()) { return true; } /* The device must be mounted in order to dvd-freespace to work */ mount(1); Dsm_check(400); icmd = device->free_space_command; if (!icmd) { free_space = 0; free_space_errno = 0; clear_freespace_ok(); /* No valid freespace */ clear_media(); Dmsg2(29, "ERROR: update_free_space_dev: free_space=%s, free_space_errno=%d (!icmd)\n", edit_uint64(free_space, ed1), free_space_errno); Mmsg(errmsg, _("No FreeSpace command defined.\n")); return false; } edit_mount_codes(ocmd, icmd); Dmsg1(29, "update_freespace: cmd=%s\n", ocmd.c_str()); results = get_pool_memory(PM_MESSAGE); /* Try at most 3 times to get the free space on the device. This should perhaps be configurable. */ timeout = 3; while (1) { berrno be; Dmsg1(20, "Run freespace prog=%s\n", ocmd.c_str()); status = run_program_full_output(ocmd.c_str(), max_open_wait/2, results); Dmsg2(500, "Freespace status=%d result=%s\n", status, results); if (status == 0) { free = str_to_int64(results); Dmsg1(400, "Free space program run: Freespace=%s\n", results); if (free >= 0) { free_space = free; free_space_errno = 0; set_freespace_ok(); /* have valid freespace */ set_media(); Mmsg(errmsg, ""); ok = true; break; } } free_space = 0; free_space_errno = EPIPE; clear_freespace_ok(); /* no valid freespace */ Mmsg2(errmsg, _("Cannot run free space command. Results=%s ERR=%s\n"), results, be.bstrerror(status)); if (--timeout > 0) { Dmsg4(40, "Cannot get free space on device %s. free_space=%s, " "free_space_errno=%d ERR=%s\n", print_name(), edit_uint64(free_space, ed1), free_space_errno, errmsg); bmicrosleep(1, 0); continue; } dev_errno = free_space_errno; Dmsg4(40, "Cannot get free space on device %s. free_space=%s, " "free_space_errno=%d ERR=%s\n", print_name(), edit_uint64(free_space, ed1), free_space_errno, errmsg); break; } free_pool_memory(results); Dmsg4(29, "leave update_freespace: free_space=%s freespace_ok=%d free_space_errno=%d have_media=%d\n", edit_uint64(free_space, ed1), !!is_freespace_ok(), free_space_errno, !!have_media()); Dsm_check(400); return ok; }
/* * Close both pipes and free resources * * Returns: 0 on success * berrno on failure */ int close_bpipe(BPIPE *bpipe) { int chldstatus = 0; int stat = 0; int wait_option; int remaining_wait; pid_t wpid = 0; /* Close pipes */ if (bpipe->rfd) { fclose(bpipe->rfd); bpipe->rfd = NULL; } if (bpipe->wfd) { fclose(bpipe->wfd); bpipe->wfd = NULL; } if (bpipe->wait == 0) { wait_option = 0; /* wait indefinitely */ } else { wait_option = WNOHANG; /* don't hang */ } remaining_wait = bpipe->wait; /* wait for worker child to exit */ for ( ;; ) { Dmsg2(800, "Wait for %d opt=%d\n", bpipe->worker_pid, wait_option); do { wpid = waitpid(bpipe->worker_pid, &chldstatus, wait_option); } while (wpid == -1 && (errno == EINTR || errno == EAGAIN)); if (wpid == bpipe->worker_pid || wpid == -1) { berrno be; stat = errno; Dmsg3(800, "Got break wpid=%d status=%d ERR=%s\n", wpid, chldstatus, wpid==-1?be.bstrerror():"none"); break; } Dmsg3(800, "Got wpid=%d status=%d ERR=%s\n", wpid, chldstatus, wpid==-1?strerror(errno):"none"); if (remaining_wait > 0) { bmicrosleep(1, 0); /* wait one second */ remaining_wait--; } else { stat = ETIME; /* set error status */ wpid = -1; break; /* don't wait any longer */ } } if (wpid > 0) { if (WIFEXITED(chldstatus)) { /* process exit()ed */ stat = WEXITSTATUS(chldstatus); if (stat != 0) { Dmsg1(800, "Non-zero status %d returned from child.\n", stat); stat |= b_errno_exit; /* exit status returned */ } Dmsg1(800, "child status=%d\n", stat & ~b_errno_exit); } else if (WIFSIGNALED(chldstatus)) { /* process died */ #ifndef HAVE_WIN32 stat = WTERMSIG(chldstatus); #else stat = 1; /* fake child status */ #endif Dmsg1(800, "Child died from signal %d\n", stat); stat |= b_errno_signal; /* exit signal returned */ } } if (bpipe->timer_id) { stop_child_timer(bpipe->timer_id); } free(bpipe); Dmsg2(800, "returning stat=%d,%d\n", stat & ~(b_errno_exit|b_errno_signal), stat); return stat; }
/* * 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: bfuncs->DebugMessage(ctx, fi, li, dbglvl, "bpipe-fd: IO_OPEN\n"); if (io->flags & (O_CREAT | O_WRONLY)) { char *writer_codes = apply_rp_codes(p_ctx); p_ctx->fd = popen(writer_codes, "w"); bfuncs->DebugMessage(ctx, fi, li, dbglvl, "bpipe-fd: IO_OPEN fd=%d writer=%s\n", p_ctx->fd, writer_codes); if (!p_ctx->fd) { io->io_errno = errno; bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0, "Open pipe writer=%s failed: ERR=%s\n", writer_codes, strerror(errno)); if (writer_codes) { free(writer_codes); } return bRC_Error; } if (writer_codes) { free(writer_codes); } } else { p_ctx->fd = popen(p_ctx->reader, "r"); bfuncs->DebugMessage(ctx, fi, li, dbglvl, "bpipe-fd: IO_OPEN fd=%p reader=%s\n", p_ctx->fd, p_ctx->reader); if (!p_ctx->fd) { io->io_errno = errno; bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0, "Open pipe reader=%s failed: ERR=%s\n", p_ctx->reader, strerror(errno)); return bRC_Error; } } bmicrosleep(1,0); /* let pipe connect */ break; case IO_READ: if (!p_ctx->fd) { bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0, "Logic error: NULL read FD\n"); return bRC_Error; } io->status = fread(io->buf, 1, io->count, p_ctx->fd); // bfuncs->DebugMessage(ctx, fi, li, dbglvl, "bpipe-fd: IO_READ buf=%p len=%d\n", io->buf, io->status); if (io->status == 0 && ferror(p_ctx->fd)) { bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0, "Pipe read error: ERR=%s\n", strerror(errno)); bfuncs->DebugMessage(ctx, fi, li, dbglvl, "Pipe read error: ERR=%s\n", strerror(errno)); return bRC_Error; } break; case IO_WRITE: if (!p_ctx->fd) { bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0, "Logic error: NULL write FD\n"); return bRC_Error; } // printf("bpipe-fd: IO_WRITE fd=%p buf=%p len=%d\n", p_ctx->fd, io->buf, io->count); io->status = fwrite(io->buf, 1, io->count, p_ctx->fd); // printf("bpipe-fd: IO_WRITE buf=%p len=%d\n", io->buf, io->status); if (io->status == 0 && ferror(p_ctx->fd)) { bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0, "Pipe write error\n"); bfuncs->DebugMessage(ctx, fi, li, dbglvl, "Pipe read error: ERR=%s\n", strerror(errno)); return bRC_Error; } break; case IO_CLOSE: if (!p_ctx->fd) { bfuncs->JobMessage(ctx, fi, li, M_FATAL, 0, "Logic error: NULL FD on bpipe close\n"); return bRC_Error; } io->status = pclose(p_ctx->fd); break; case IO_SEEK: io->offset = p_ctx->offset; break; } return bRC_OK; }
void *handle_stored_connection(BSOCK *sd) { JCR *jcr; char job_name[MAX_NAME_LENGTH]; /* * Do a sanity check on the message received */ if (sd->msglen < 25 || sd->msglen > 256) { Dmsg1(000, "<filed: %s", sd->msg); Emsg2(M_ERROR, 0, _("Invalid connection from %s. Len=%d\n"), sd->who(), sd->msglen); bmicrosleep(5, 0); /* make user wait 5 seconds */ sd->close(); return NULL; } if (sscanf(sd->msg, "Hello Storage calling Start Job %127s", job_name) != 1) { char addr[64]; char *who = bnet_get_peer(sd, addr, sizeof(addr)) ? sd->who() : addr; sd->msg[100] = 0; Dmsg2(dbglvl, "Bad Hello command from Director at %s: %s\n", sd->who(), sd->msg); Jmsg2(NULL, M_FATAL, 0, _("Bad Hello command from Director at %s: %s\n"), who, sd->msg); sd->close(); return NULL; } if (!(jcr = get_jcr_by_full_name(job_name))) { Jmsg1(NULL, M_FATAL, 0, _("SD connect failed: Job name not found: %s\n"), job_name); Dmsg1(3, "**** Job \"%s\" not found.\n", job_name); sd->close(); return NULL; } Dmsg1(50, "Found Job %s\n", job_name); jcr->store_bsock = sd; jcr->store_bsock->set_jcr(jcr); /* * Authenticate the Storage Daemon. */ if (!authenticate_storagedaemon(jcr)) { Dmsg1(50, "Authentication failed Job %s\n", jcr->Job); Jmsg(jcr, M_FATAL, 0, _("Unable to authenticate File daemon\n")); jcr->setJobStatus(JS_ErrorTerminated); } else { Dmsg2(50, "OK Authentication jid=%u Job %s\n", (uint32_t)jcr->JobId, jcr->Job); } if (!jcr->max_bandwidth) { if (jcr->director->max_bandwidth_per_job) { jcr->max_bandwidth = jcr->director->max_bandwidth_per_job; } else if (me->max_bandwidth_per_job) { jcr->max_bandwidth = me->max_bandwidth_per_job; } } sd->set_bwlimit(jcr->max_bandwidth); if (me->allow_bw_bursting) { sd->set_bwlimit_bursting(); } free_jcr(jcr); return NULL; }
/* * Rewind the device. * * Returns: true on success * false on failure */ bool generic_tape_device::rewind(DCR *dcr) { struct mtop mt_com; unsigned int i; bool first = true; Dmsg3(400, "rewind res=%d fd=%d %s\n", num_reserved(), m_fd, prt_name); state &= ~(ST_EOT | ST_EOF | ST_WEOT); /* Remove EOF/EOT flags */ block_num = file = 0; file_size = 0; file_addr = 0; if (m_fd < 0) { return false; } mt_com.mt_op = MTREW; mt_com.mt_count = 1; /* * If we get an I/O error on rewind, it is probably because * the drive is actually busy. We loop for (about 5 minutes) * retrying every 5 seconds. */ for (i = max_rewind_wait; ; i -= 5) { if (d_ioctl(m_fd, MTIOCTOP, (char *)&mt_com) < 0) { berrno be; clrerror(mt_com.mt_op); if (i == max_rewind_wait) { Dmsg1(200, "Rewind error, %s. retrying ...\n", be.bstrerror()); } /* * This is a gross hack, because if the user has the * device mounted (i.e. open), then uses mtx to load * a tape, the current open file descriptor is invalid. * So, we close the drive and re-open it. */ if (first && dcr) { int oo_mode = open_mode; d_close(m_fd); clear_opened(); open(dcr, oo_mode); if (m_fd < 0) { return false; } first = false; continue; } #ifdef HAVE_SUN_OS if (dev_errno == EIO) { Mmsg1(errmsg, _("No tape loaded or drive offline on %s.\n"), prt_name); return false; } #else if (dev_errno == EIO && i > 0) { Dmsg0(200, "Sleeping 5 seconds.\n"); bmicrosleep(5, 0); continue; } #endif Mmsg2(errmsg, _("Rewind error on %s. ERR=%s.\n"), prt_name, be.bstrerror()); return false; } break; } return true; }
bool run_cmd(JCR *jcr) { struct timeval tv; struct timezone tz; struct timespec timeout; int errstat = 0; BSOCK *cl; int fd_version = 0; int sd_version = 0; char job_name[500]; int i; int stat; Dsm_check(200); Dmsg1(200, "Run_cmd: %s\n", jcr->dir_bsock->msg); /* If we do not need the FD, we are doing a virtual backup. */ if (jcr->no_client_used()) { do_vbackup(jcr); return false; } jcr->sendJobStatus(JS_WaitFD); /* wait for FD to connect */ Dmsg2(050, "sd_calls_client=%d sd_client=%d\n", jcr->sd_calls_client, jcr->sd_client); if (jcr->sd_calls_client) { /* We connected to Client, so finish work */ cl = jcr->file_bsock; if (!cl) { Jmsg0(jcr, M_FATAL, 0, _("Client socket not open. Could not connect to Client.\n")); Dmsg0(050, "Client socket not open. Could not connect to Client.\n"); return false; } /* Get response to Hello command sent earlier */ Dmsg0(050, "Read Hello command from Client\n"); for (i=0; i<60; i++) { stat = cl->recv(); if (stat <= 0) { bmicrosleep(1, 0); } else { break; } } if (stat <= 0) { berrno be; Jmsg1(jcr, M_FATAL, 0, _("Recv request to Client failed. ERR=%s\n"), be.bstrerror()); Dmsg1(050, _("Recv request to Client failed. ERR=%s\n"), be.bstrerror()); return false; } Dmsg1(050, "Got from FD: %s\n", cl->msg); if (sscanf(cl->msg, "Hello Bacula SD: Start Job %127s %d %d", job_name, &fd_version, &sd_version) != 3) { Jmsg1(jcr, M_FATAL, 0, _("Bad Hello from Client: %s.\n"), cl->msg); Dmsg1(050, _("Bad Hello from Client: %s.\n"), cl->msg); return false; } unbash_spaces(job_name); jcr->FDVersion = fd_version; jcr->SDVersion = sd_version; Dmsg1(050, "FDVersion=%d\n", fd_version); /* * Authenticate the File daemon */ Dmsg0(050, "=== Authenticate FD\n"); if (jcr->authenticated || !authenticate_filed(jcr)) { Dmsg1(050, "Authentication failed Job %s\n", jcr->Job); Jmsg(jcr, M_FATAL, 0, _("Unable to authenticate File daemon\n")); } else { jcr->authenticated = true; } } else if (!jcr->sd_client) { /* We wait to receive connection from Client */ gettimeofday(&tv, &tz); timeout.tv_nsec = tv.tv_usec * 1000; timeout.tv_sec = tv.tv_sec + me->client_wait; Dmsg3(050, "%s waiting %d sec for FD to contact SD key=%s\n", jcr->Job, (int)(timeout.tv_sec-time(NULL)), jcr->sd_auth_key); Dmsg3(800, "=== Block Job=%s jid=%d %p\n", jcr->Job, jcr->JobId, jcr); /* * Wait for the File daemon to contact us to start the Job, * when he does, we will be released, unless the 30 minutes * expires. */ P(mutex); while ( !jcr->authenticated && !job_canceled(jcr) ) { errstat = pthread_cond_timedwait(&jcr->job_start_wait, &mutex, &timeout); if (errstat == ETIMEDOUT || errstat == EINVAL || errstat == EPERM) { break; } Dmsg1(800, "=== Auth cond errstat=%d\n", errstat); } Dmsg4(050, "=== Auth=%d jid=%d canceled=%d errstat=%d\n", jcr->JobId, jcr->authenticated, job_canceled(jcr), errstat); V(mutex); Dmsg2(800, "Auth fail or cancel for jid=%d %p\n", jcr->JobId, jcr); } memset(jcr->sd_auth_key, 0, strlen(jcr->sd_auth_key)); if (jcr->authenticated && !job_canceled(jcr)) { Dmsg2(800, "Running jid=%d %p\n", jcr->JobId, jcr); run_job(jcr); /* Run the job */ } Dmsg2(800, "Done jid=%d %p\n", jcr->JobId, jcr); return false; }
/* * See who is connecting and lookup the authentication information. * First make him prove his identity and then prove our identity to the Remote daemon. */ static inline bool two_way_authenticate(int rcode, BSOCK *bs, JCR* jcr) { POOLMEM *dirname = get_pool_memory(PM_MESSAGE); DIRRES *director = NULL; int tls_local_need = BNET_TLS_NONE; int tls_remote_need = BNET_TLS_NONE; bool compatible = true; /* Want md5 compatible DIR */ bool auth_success = false; alist *verify_list = NULL; btimer_t *tid = NULL; if (rcode != R_DIRECTOR) { Dmsg1(dbglvl, "I only authenticate directors, not %d\n", rcode); Jmsg1(jcr, M_FATAL, 0, _("I only authenticate directors, not %d\n"), rcode); goto auth_fatal; } if (bs->msglen < 25 || bs->msglen > 500) { Dmsg2(dbglvl, "Bad Hello command from Director at %s. Len=%d.\n", bs->who(), bs->msglen); char addr[64]; char *who = bnet_get_peer(bs, addr, sizeof(addr)) ? bs->who() : addr; Jmsg2(jcr, M_FATAL, 0, _("Bad Hello command from Director at %s. Len=%d.\n"), who, bs->msglen); goto auth_fatal; } dirname = check_pool_memory_size(dirname, bs->msglen); if (sscanf(bs->msg, "Hello Director %s calling", dirname) != 1) { char addr[64]; char *who = bnet_get_peer(bs, addr, sizeof(addr)) ? bs->who() : addr; bs->msg[100] = 0; Dmsg2(dbglvl, "Bad Hello command from Director at %s: %s\n", bs->who(), bs->msg); Jmsg2(jcr, M_FATAL, 0, _("Bad Hello command from Director at %s: %s\n"), who, bs->msg); goto auth_fatal; } unbash_spaces(dirname); foreach_res(director, R_DIRECTOR) { if (bstrcmp(director->hdr.name, dirname)) break; } if (!director) { char addr[64]; char *who = bnet_get_peer(bs, addr, sizeof(addr)) ? bs->who() : addr; Jmsg2(jcr, M_FATAL, 0, _("Connection from unknown Director %s at %s rejected.\n"), dirname, who); goto auth_fatal; } if (have_tls) { /* * TLS Requirement */ if (director->tls_enable) { if (director->tls_require) { tls_local_need = BNET_TLS_REQUIRED; } else { tls_local_need = BNET_TLS_OK; } } if (director->tls_authenticate) { tls_local_need = BNET_TLS_REQUIRED; } if (director->tls_verify_peer) { verify_list = director->tls_allowed_cns; } } /* * Timeout Hello after 10 min */ tid = start_bsock_timer(bs, AUTH_TIMEOUT); /* * Sanity check. */ ASSERT(director->password.encoding == p_encoding_md5); /* * Challenge the director */ auth_success = cram_md5_challenge(bs, director->password.value, tls_local_need, compatible); if (job_canceled(jcr)) { auth_success = false; goto auth_fatal; /* quick exit */ } if (auth_success) { auth_success = cram_md5_respond(bs, director->password.value, &tls_remote_need, &compatible); if (!auth_success) { char addr[64]; char *who = bnet_get_peer(bs, addr, sizeof(addr)) ? bs->who() : addr; Dmsg1(dbglvl, "cram_get_auth failed for %s\n", who); } } else { char addr[64]; char *who = bnet_get_peer(bs, addr, sizeof(addr)) ? bs->who() : addr; Dmsg1(dbglvl, "cram_auth failed for %s\n", who); } if (!auth_success) { Emsg1(M_FATAL, 0, _("Incorrect password given by Director at %s.\n"), bs->who()); goto auth_fatal; } /* * Verify that the remote host is willing to meet our TLS requirements */ if (tls_remote_need < tls_local_need && tls_local_need != BNET_TLS_OK && tls_remote_need != BNET_TLS_OK) { Jmsg0(jcr, M_FATAL, 0, _("Authorization problem: Remote server did not" " advertize required TLS support.\n")); Dmsg2(dbglvl, "remote_need=%d local_need=%d\n", tls_remote_need, tls_local_need); auth_success = false; goto auth_fatal; } /* * Verify that we are willing to meet the remote host's requirements */ if (tls_remote_need > tls_local_need && tls_local_need != BNET_TLS_OK && tls_remote_need != BNET_TLS_OK) { Jmsg0(jcr, M_FATAL, 0, _("Authorization problem: Remote server requires TLS.\n")); Dmsg2(dbglvl, "remote_need=%d local_need=%d\n", tls_remote_need, tls_local_need); auth_success = false; goto auth_fatal; } if (tls_local_need >= BNET_TLS_OK && tls_remote_need >= BNET_TLS_OK) { /* * Engage TLS! Full Speed Ahead! */ if (!bnet_tls_server(director->tls_ctx, bs, verify_list)) { Jmsg0(jcr, M_FATAL, 0, _("TLS negotiation failed.\n")); auth_success = false; goto auth_fatal; } if (director->tls_authenticate) { /* authentication only? */ bs->free_tls(); /* shutodown tls */ } } auth_fatal: if (tid) { stop_bsock_timer(tid); tid = NULL; } free_pool_memory(dirname); jcr->director = director; /* * Single thread all failures to avoid DOS */ if (!auth_success) { P(mutex); bmicrosleep(6, 0); V(mutex); } return auth_success; }
/* * Open a fifo device */ void win32_fifo_device::open_device(DCR *dcr, int omode) { file_size = 0; int timeout = max_open_wait; utime_t start_time = time(NULL); mount(dcr, 1); /* do mount if required */ Dmsg0(100, "Open dev: device is fifo\n"); get_autochanger_loaded_slot(dcr); open_mode = omode; set_mode(omode); if (timeout < 1) { timeout = 1; } errno = 0; if (timeout) { /* * Set open timer */ tid = start_thread_timer(dcr->jcr, pthread_self(), timeout); } Dmsg2(100, "Try open %s mode=%s\n", prt_name, mode_to_str(omode)); /* * If busy retry each second for max_open_wait seconds */ for ( ;; ) { /* * Try non-blocking open */ m_fd = d_open(dev_name, oflags | O_NONBLOCK, 0); if (m_fd < 0) { berrno be; dev_errno = errno; Dmsg5(100, "Open error on %s omode=%d oflags=%x errno=%d: ERR=%s\n", prt_name, omode, oflags, errno, be.bstrerror()); } else { d_close(m_fd); m_fd = d_open(dev_name, oflags, 0); /* open normally */ if (m_fd < 0) { berrno be; dev_errno = errno; Dmsg5(100, "Open error on %s omode=%d oflags=%x errno=%d: ERR=%s\n", prt_name, omode, oflags, errno, be.bstrerror()); break; } dev_errno = 0; lock_door(); break; /* Successfully opened and rewound */ } bmicrosleep(5, 0); /* * Exceed wait time ? */ if (time(NULL) - start_time >= max_open_wait) { break; /* yes, get out */ } } if (!is_open()) { berrno be; Mmsg2(errmsg, _("Unable to open device %s: ERR=%s\n"), prt_name, be.bstrerror(dev_errno)); Dmsg1(100, "%s", errmsg); } /* * Stop any open() timer we started */ if (tid) { stop_thread_timer(tid); tid = 0; } Dmsg1(100, "open dev: fifo %d opened\n", m_fd); }
/* * 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; }
/* * Handle signals here */ extern "C" void signal_handler(int sig) { static int already_dead = 0; int chld_status=-1; utime_t now; /* If we come back more than once, get out fast! */ if (already_dead) { exit(1); } Dmsg2(900, "sig=%d %s\n", sig, sig_names[sig]); /* Ignore certain signals -- SIGUSR2 used to interrupt threads */ if (sig == SIGCHLD || sig == SIGUSR2) { return; } /* FreeBSD seems to generate a signal of 0, which is of course undefined */ if (sig == 0) { return; } already_dead++; /* Don't use Emsg here as it may lock and thus block us */ if (sig == SIGTERM || sig == SIGINT) { syslog(LOG_DAEMON|LOG_ERR, "Shutting down Bacula service: %s ...\n", my_name); } else { fprintf(stderr, _("Bacula interrupted by signal %d: %s\n"), sig, get_signal_name(sig)); syslog(LOG_DAEMON|LOG_ERR, _("Bacula interrupted by signal %d: %s\n"), sig, get_signal_name(sig)); /* Edit current time for showing in the dump */ now = time(NULL); bstrftimes(fail_time, 30, now); } #ifdef TRACEBACK if (sig != SIGTERM && sig != SIGINT) { struct sigaction sigdefault; static char *argv[5]; static char pid_buf[20]; static char btpath[400]; char buf[400]; pid_t pid; int exelen = strlen(exepath); fprintf(stderr, _("Kaboom! %s, %s got signal %d - %s at %s. Attempting traceback.\n"), exename, my_name, sig, get_signal_name(sig), fail_time); fprintf(stderr, _("Kaboom! exepath=%s\n"), exepath); if (exelen + 12 > (int)sizeof(btpath)) { bstrncpy(btpath, "btraceback", sizeof(btpath)); } else { bstrncpy(btpath, exepath, sizeof(btpath)); if (IsPathSeparator(btpath[exelen-1])) { btpath[exelen-1] = 0; } bstrncat(btpath, "/btraceback", sizeof(btpath)); } if (!IsPathSeparator(exepath[exelen - 1])) { strcat(exepath, "/"); } strcat(exepath, exename); if (!working_directory) { working_directory = buf; *buf = 0; } if (*working_directory == 0) { strcpy((char *)working_directory, "/tmp/"); } if (chdir(working_directory) != 0) { /* dump in working directory */ berrno be; Pmsg2(000, "chdir to %s failed. ERR=%s\n", working_directory, be.bstrerror()); strcpy((char *)working_directory, "/tmp/"); } unlink("./core"); /* get rid of any old core file */ #ifdef DEVELOPER /* When DEVELOPER not set, this is done below */ /* print information about the current state into working/<file>.lockdump */ dbg_print_bacula(); #endif sprintf(pid_buf, "%d", (int)main_pid); Dmsg1(300, "Working=%s\n", working_directory); Dmsg1(300, "btpath=%s\n", btpath); Dmsg1(300, "exepath=%s\n", exepath); switch (pid = fork()) { case -1: /* error */ fprintf(stderr, _("Fork error: ERR=%s\n"), strerror(errno)); break; case 0: /* child */ argv[0] = btpath; /* path to btraceback */ argv[1] = exepath; /* path to exe */ argv[2] = pid_buf; argv[3] = (char *)working_directory; argv[4] = (char *)NULL; fprintf(stderr, _("Calling: %s %s %s %s\n"), btpath, exepath, pid_buf, working_directory); if (execv(btpath, argv) != 0) { berrno be; printf(_("execv: %s failed: ERR=%s\n"), btpath, be.bstrerror()); } exit(-1); default: /* parent */ break; } /* Parent continue here, waiting for child */ sigdefault.sa_flags = 0; sigdefault.sa_handler = SIG_DFL; sigfillset(&sigdefault.sa_mask); sigaction(sig, &sigdefault, NULL); if (pid > 0) { Dmsg0(500, "Doing waitpid\n"); waitpid(pid, &chld_status, 0); /* wait for child to produce dump */ Dmsg0(500, "Done waitpid\n"); } else { Dmsg0(500, "Doing sleep\n"); bmicrosleep(30, 0); } if (WEXITSTATUS(chld_status) == 0) { fprintf(stderr, _("It looks like the traceback worked...\n")); } else { fprintf(stderr, _("The btraceback call returned %d\n"), WEXITSTATUS(chld_status)); } /* If we want it printed, do so */ #ifdef direct_print if (prt_kaboom) { FILE *fd; snprintf(buf, sizeof(buf), "%s/%s.%s.traceback", working_directory, my_name, pid_buf); fd = fopen(buf, "r"); if (fd != NULL) { printf("\n\n ==== Traceback output ====\n\n"); while (fgets(buf, (int)sizeof(buf), fd) != NULL) { printf("%s", buf); } fclose(fd); printf(" ==== End traceback output ====\n\n"); } } #else if (prt_kaboom) { snprintf(buf, sizeof(buf), "/bin/cat %s/%s.%s.traceback", working_directory, my_name, pid_buf); fprintf(stderr, "\n\n ==== Traceback output ====\n\n"); system(buf); fprintf(stderr, " ==== End traceback output ====\n\n"); } #endif #ifndef DEVELOPER /* When DEVELOPER set, this is done above */ /* print information about the current state into working/<file>.lockdump */ dbg_print_bacula(); #endif } #endif exit_handler(sig); Dmsg0(500, "Done exit_handler\n"); }
/* * Open a tape device */ void generic_tape_device::open_device(DCR *dcr, int omode) { file_size = 0; int timeout = max_open_wait; #if !defined(HAVE_WIN32) struct mtop mt_com; utime_t start_time = time(NULL); #endif mount(dcr, 1); /* do mount if required */ Dmsg0(100, "Open dev: device is tape\n"); get_autochanger_loaded_slot(dcr); open_mode = omode; set_mode(omode); if (timeout < 1) { timeout = 1; } errno = 0; Dmsg2(100, "Try open %s mode=%s\n", prt_name, mode_to_str(omode)); #if defined(HAVE_WIN32) /* * Windows Code */ if ((m_fd = d_open(dev_name, oflags, 0)) < 0) { dev_errno = errno; } #else /* * UNIX Code * * If busy retry each second for max_open_wait seconds */ for ( ;; ) { /* * Try non-blocking open */ m_fd = d_open(dev_name, oflags | O_NONBLOCK, 0); if (m_fd < 0) { berrno be; dev_errno = errno; Dmsg5(100, "Open error on %s omode=%d oflags=%x errno=%d: ERR=%s\n", prt_name, omode, oflags, errno, be.bstrerror()); } else { /* * Tape open, now rewind it */ Dmsg0(100, "Rewind after open\n"); mt_com.mt_op = MTREW; mt_com.mt_count = 1; /* * Rewind only if dev is a tape */ if (d_ioctl(m_fd, MTIOCTOP, (char *)&mt_com) < 0) { berrno be; dev_errno = errno; /* set error status from rewind */ d_close(m_fd); clear_opened(); Dmsg2(100, "Rewind error on %s close: ERR=%s\n", prt_name, be.bstrerror(dev_errno)); /* * If we get busy, device is probably rewinding, try again */ if (dev_errno != EBUSY) { break; /* error -- no medium */ } } else { /* * Got fd and rewind worked, so we must have medium in drive */ d_close(m_fd); m_fd = d_open(dev_name, oflags, 0); /* open normally */ if (m_fd < 0) { berrno be; dev_errno = errno; Dmsg5(100, "Open error on %s omode=%d oflags=%x errno=%d: ERR=%s\n", prt_name, omode, oflags, errno, be.bstrerror()); break; } dev_errno = 0; lock_door(); set_os_device_parameters(dcr); /* do system dependent stuff */ break; /* Successfully opened and rewound */ } } bmicrosleep(5, 0); /* * Exceed wait time ? */ if (time(NULL) - start_time >= max_open_wait) { break; /* yes, get out */ } } #endif if (!is_open()) { berrno be; Mmsg2(errmsg, _("Unable to open device %s: ERR=%s\n"), prt_name, be.bstrerror(dev_errno)); Dmsg1(100, "%s", errmsg); } Dmsg1(100, "open dev: tape %d opened\n", m_fd); }
/* Authorize other end * Codes that tls_local_need and tls_remote_need can take: * * BNET_TLS_NONE I cannot do tls * BNET_TLS_OK I can do tls, but it is not required on my end * BNET_TLS_REQUIRED tls is required on my end * * Returns: false if authentication failed * true if OK */ bool cram_md5_challenge(BSOCK *bs, const char *password, int tls_local_need, bool compatible) { struct timeval t1; struct timeval t2; struct timezone tz; int i; bool ok; char chal[MAXSTRING]; char host[MAXSTRING]; uint8_t hmac[20]; gettimeofday(&t1, &tz); for (i=0; i<4; i++) { gettimeofday(&t2, &tz); } srandom((t1.tv_sec&0xffff) * (t2.tv_usec&0xff)); if (!gethostname(host, sizeof(host))) { bstrncpy(host, my_name, sizeof(host)); } /* Send challenge -- no hashing yet */ bsnprintf(chal, sizeof(chal), "<%u.%u@%s>", (uint32_t)random(), (uint32_t)time(NULL), host); if (compatible) { Dmsg2(dbglvl, "send: auth cram-md5 %s ssl=%d\n", chal, tls_local_need); if (!bs->fsend("auth cram-md5 %s ssl=%d\n", chal, tls_local_need)) { Dmsg1(dbglvl, "Bnet send challenge comm error. ERR=%s\n", bs->bstrerror()); return false; } } else { /* Old non-compatible system */ Dmsg2(dbglvl, "send: auth cram-md5 %s ssl=%d\n", chal, tls_local_need); if (!bs->fsend("auth cram-md5 %s ssl=%d\n", chal, tls_local_need)) { Dmsg1(dbglvl, "Bnet send challenge comm error. ERR=%s\n", bs->bstrerror()); return false; } } /* Read hashed response to challenge */ if (bs->wait_data(180) <= 0 || bs->recv() <= 0) { Dmsg1(dbglvl, "Bnet receive challenge response comm error. ERR=%s\n", bs->bstrerror()); bmicrosleep(5, 0); return false; } /* Attempt to duplicate hash with our password */ hmac_md5((uint8_t *)chal, strlen(chal), (uint8_t *)password, strlen(password), hmac); bin_to_base64(host, sizeof(host), (char *)hmac, 16, compatible); ok = bstrcmp(bs->msg, host); if (ok) { Dmsg1(dbglvl, "Authenticate OK %s\n", host); } else { bin_to_base64(host, sizeof(host), (char *)hmac, 16, false); ok = bstrcmp(bs->msg, host); if (!ok) { Dmsg2(dbglvl, "Authenticate NOT OK: wanted %s, got %s\n", host, bs->msg); } } if (ok) { bs->fsend("1000 OK auth\n"); } else { bs->fsend(_("1999 Authorization failed.\n")); bmicrosleep(5, 0); } return ok; }
/* * (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; }
/* * First prove our identity to the Remote daemon and then make him prove his identity. */ static inline bool two_way_authenticate(BSOCK *bs, JCR *jcr, bool initiate, const char *what) { int tls_local_need = BNET_TLS_NONE; int tls_remote_need = BNET_TLS_NONE; bool compatible = true; bool auth_success = false; alist *verify_list = NULL; btimer_t *tid = NULL; /* * TLS Requirement */ if (have_tls && me->tls_enable) { if (me->tls_require) { tls_local_need = BNET_TLS_REQUIRED; } else { tls_local_need = BNET_TLS_OK; } } if (me->tls_authenticate) { tls_local_need = BNET_TLS_REQUIRED; } if (job_canceled(jcr)) { auth_success = false; /* force quick exit */ goto auth_fatal; } /* * Timeout Hello after 10 min */ tid = start_bsock_timer(bs, AUTH_TIMEOUT); /* * See if we initiate the challenge or respond to a challenge. */ if (initiate) { /* * Challenge SD */ auth_success = cram_md5_challenge(bs, jcr->sd_auth_key, tls_local_need, compatible); if (auth_success) { /* * Respond to his challenge */ auth_success = cram_md5_respond(bs, jcr->sd_auth_key, &tls_remote_need, &compatible); if (!auth_success) { Dmsg1(dbglvl, "Respond cram-get-auth failed with %s\n", bs->who()); } } else { Dmsg1(dbglvl, "Challenge cram-auth failed with %s\n", bs->who()); } } else { /* * Respond to challenge */ auth_success = cram_md5_respond(bs, jcr->sd_auth_key, &tls_remote_need, &compatible); if (job_canceled(jcr)) { auth_success = false; /* force quick exit */ goto auth_fatal; } if (!auth_success) { Dmsg1(dbglvl, "cram_respond failed for %s\n", bs->who()); } else { /* * Challenge SD. */ auth_success = cram_md5_challenge(bs, jcr->sd_auth_key, tls_local_need, compatible); if (!auth_success) { Dmsg1(dbglvl, "cram_challenge failed for %s\n", bs->who()); } } } if (!auth_success) { Jmsg(jcr, M_FATAL, 0, _("Authorization key rejected by %s daemon.\n" "Please see %s for help.\n"), what, MANUAL_AUTH_URL); goto auth_fatal; } /* * Verify that the remote host is willing to meet our TLS requirements */ if (tls_remote_need < tls_local_need && tls_local_need != BNET_TLS_OK && tls_remote_need != BNET_TLS_OK) { Jmsg(jcr, M_FATAL, 0, _("Authorization problem: Remote server did not" " advertize required TLS support.\n")); Dmsg2(dbglvl, "remote_need=%d local_need=%d\n", tls_remote_need, tls_local_need); auth_success = false; goto auth_fatal; } /* * Verify that we are willing to meet the remote host's requirements */ if (tls_remote_need > tls_local_need && tls_local_need != BNET_TLS_OK && tls_remote_need != BNET_TLS_OK) { Jmsg(jcr, M_FATAL, 0, _("Authorization problem: Remote server requires TLS.\n")); Dmsg2(dbglvl, "remote_need=%d local_need=%d\n", tls_remote_need, tls_local_need); auth_success = false; goto auth_fatal; } if (tls_local_need >= BNET_TLS_OK && tls_remote_need >= BNET_TLS_OK) { /* * See if we are handshaking a passive client connection. */ if (initiate) { verify_list = me->tls_allowed_cns; } /* * Engage TLS! Full Speed Ahead! */ if (!bnet_tls_client(me->tls_ctx, bs, verify_list)) { Jmsg(jcr, M_FATAL, 0, _("TLS negotiation failed.\n")); auth_success = false; goto auth_fatal; } if (me->tls_authenticate) { /* tls authentication only? */ bs->free_tls(); /* yes, shutdown tls */ } } auth_fatal: /* * Destroy session key */ memset(jcr->sd_auth_key, 0, strlen(jcr->sd_auth_key)); stop_bsock_timer(tid); /* * Single thread all failures to avoid DOS */ if (!auth_success) { P(mutex); bmicrosleep(6, 0); V(mutex); } return auth_success; }
int main (int argc, char *argv[]) { int ch; bool disable_batch = false; char *restore_list=NULL; setlocale(LC_ALL, ""); bindtextdomain("bareos", LOCALEDIR); textdomain("bareos"); init_stack_dump(); lmgr_init_thread(); char **files = (char **) malloc (10 * sizeof(char *)); int i; my_name_is(argc, argv, "bbatch"); init_msg(NULL, NULL); OSDependentInit(); while ((ch = getopt(argc, argv, "bBh:c:d:D:n:P:Su:vf:w:r:?")) != -1) { switch (ch) { case 'r': restore_list=bstrdup(optarg); break; case 'B': disable_batch = true; break; case 'b': disable_batch = false; 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 'D': db_driver = 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 'f': if (nb < 10 ) { files[nb++] = optarg; } break; case '?': default: usage(); } } argc -= optind; argv += optind; if (argc != 0) { Pmsg0(0, _("Wrong number of arguments: \n")); usage(); } if (restore_list) { uint64_t nb_file=0; btime_t start, end; /* To use the -r option, the catalog should already contains records */ if ((db = db_init_database(NULL, db_driver, db_name, db_user, db_password, db_host, 0, NULL, false, disable_batch)) == NULL) { Emsg0(M_ERROR_TERM, 0, _("Could not init Bareos database\n")); } if (!db_open_database(NULL, db)) { Emsg0(M_ERROR_TERM, 0, db_strerror(db)); } start = get_current_btime(); db_get_file_list(NULL, db, restore_list, false, false, list_handler, &nb_file); end = get_current_btime(); Pmsg3(0, _("Computing file list for jobid=%s files=%lld secs=%d\n"), restore_list, nb_file, (uint32_t)btime_to_unix(end-start)); free(restore_list); return 0; } if (disable_batch) { printf("Without new Batch mode\n"); } else { printf("With new Batch mode\n"); } i = nb; while (--i >= 0) { pthread_t thid; JCR *bjcr = new_jcr(sizeof(JCR), NULL); bjcr->bsr = NULL; bjcr->VolSessionId = 1; bjcr->VolSessionTime = (uint32_t)time(NULL); bjcr->NumReadVolumes = 0; bjcr->NumWriteVolumes = 0; bjcr->JobId = getpid(); bjcr->setJobType(JT_CONSOLE); bjcr->setJobLevel(L_FULL); bjcr->JobStatus = JS_Running; bjcr->where = bstrdup(files[i]); bjcr->job_name = get_pool_memory(PM_FNAME); pm_strcpy(bjcr->job_name, "Dummy.Job.Name"); bjcr->client_name = get_pool_memory(PM_FNAME); pm_strcpy(bjcr->client_name, "Dummy.Client.Name"); bstrncpy(bjcr->Job, "bbatch", sizeof(bjcr->Job)); bjcr->fileset_name = get_pool_memory(PM_FNAME); pm_strcpy(bjcr->fileset_name, "Dummy.fileset.name"); bjcr->fileset_md5 = get_pool_memory(PM_FNAME); pm_strcpy(bjcr->fileset_md5, "Dummy.fileset.md5"); if ((db = db_init_database(NULL, db_driver, db_name, db_user, db_password, db_host, 0, NULL, false, false)) == NULL) { Emsg0(M_ERROR_TERM, 0, _("Could not init Bareos database\n")); } 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; pthread_create(&thid, NULL, do_batch, bjcr); } while (nb > 0) { bmicrosleep(1,0); } return 0; }
/* * Become Threaded Network Server * * This function is able to handle multiple server ips in * ipv4 and ipv6 style. The Addresse are give in a comma * seperated string in bind_addr * * At the moment it is impossible to bind to different ports. */ void bnet_thread_server(dlist *addr_list, int max_clients, alist *sockfds, workq_t *client_wq, void *handle_client_request(void *bsock)) { int newsockfd, status; socklen_t clilen; struct sockaddr cli_addr; /* client's address */ int tlog, tmax; int turnon = 1; #ifdef HAVE_LIBWRAP struct request_info request; #endif IPADDR *ipaddr, *next; s_sockfd *fd_ptr = NULL; char buf[128]; #ifdef HAVE_POLL nfds_t nfds; struct pollfd *pfds; #endif char allbuf[256 * 10]; /* * Remove any duplicate addresses. */ for (ipaddr = (IPADDR *)addr_list->first(); ipaddr; ipaddr = (IPADDR *)addr_list->next(ipaddr)) { for (next = (IPADDR *)addr_list->next(ipaddr); next; next = (IPADDR *)addr_list->next(next)) { if (ipaddr->get_sockaddr_len() == next->get_sockaddr_len() && memcmp(ipaddr->get_sockaddr(), next->get_sockaddr(), ipaddr->get_sockaddr_len()) == 0) { addr_list->remove(next); } } } Dmsg1(100, "Addresses %s\n", build_addresses_str(addr_list, allbuf, sizeof(allbuf))); #ifdef HAVE_POLL nfds = 0; #endif foreach_dlist(ipaddr, addr_list) { /* * Allocate on stack from -- no need to free */ fd_ptr = (s_sockfd *)alloca(sizeof(s_sockfd)); fd_ptr->port = ipaddr->get_port_net_order(); /* * Open a TCP socket */ for (tlog= 60; (fd_ptr->fd=socket(ipaddr->get_family(), SOCK_STREAM, 0)) < 0; tlog -= 10) { if (tlog <= 0) { berrno be; char curbuf[256]; Emsg3(M_ABORT, 0, _("Cannot open stream socket. ERR=%s. Current %s All %s\n"), be.bstrerror(), ipaddr->build_address_str(curbuf, sizeof(curbuf)), build_addresses_str(addr_list, allbuf, sizeof(allbuf))); } bmicrosleep(10, 0); } /* * Reuse old sockets */ if (setsockopt(fd_ptr->fd, SOL_SOCKET, SO_REUSEADDR, (sockopt_val_t)&turnon, sizeof(turnon)) < 0) { berrno be; Emsg1(M_WARNING, 0, _("Cannot set SO_REUSEADDR on socket: %s\n"), be.bstrerror()); } tmax = 30 * (60 / 5); /* wait 30 minutes max */ for (tlog = 0; bind(fd_ptr->fd, ipaddr->get_sockaddr(), ipaddr->get_sockaddr_len()) < 0; tlog -= 5) { berrno be; if (tlog <= 0) { tlog = 2 * 60; /* Complain every 2 minutes */ Emsg2(M_WARNING, 0, _("Cannot bind port %d: ERR=%s: Retrying ...\n"), ntohs(fd_ptr->port), be.bstrerror()); } bmicrosleep(5, 0); if (--tmax <= 0) { Emsg2(M_ABORT, 0, _("Cannot bind port %d: ERR=%s.\n"), ntohs(fd_ptr->port), be.bstrerror()); } } listen(fd_ptr->fd, 50); /* tell system we are ready */ sockfds->append(fd_ptr); #ifdef HAVE_POLL nfds++; #endif }
/* * Try to limit the bandwidth of a network connection */ void BSOCK::control_bwlimit(int bytes) { btime_t now, temp; int64_t usec_sleep; /* * If nothing written or read nothing todo. */ if (bytes == 0) { return; } /* * See if this is the first time we enter here. */ now = get_current_btime(); if (m_last_tick == 0) { m_nb_bytes = bytes; m_last_tick = now; return; } /* * Calculate the number of microseconds since the last check. */ temp = now - m_last_tick; /* * Less than 0.1ms since the last call, see the next time */ if (temp < 100) { m_nb_bytes += bytes; return; } /* * Keep track of how many bytes are written in this timeslice. */ m_nb_bytes += bytes; m_last_tick = now; if (debug_level >= 400) { Dmsg3(400, "control_bwlimit: now = %lld, since = %lld, nb_bytes = %d\n", now, temp, m_nb_bytes); } /* * Take care of clock problems (>10s) */ if (temp > 10000000) { return; } /* * Remove what was authorised to be written in temp usecs. */ m_nb_bytes -= (int64_t)(temp * ((double)m_bwlimit / 1000000.0)); if (m_nb_bytes < 0) { /* * If more was authorized then used but bursting is not enabled * reset the counter as these bytes cannot be used later on when * we are exceeding our bandwidth. */ if (!m_use_bursting) { m_nb_bytes = 0; } return; } /* * What exceed should be converted in sleep time */ usec_sleep = (int64_t)(m_nb_bytes /((double)m_bwlimit / 1000000.0)); if (usec_sleep > 100) { if (debug_level >= 400) { Dmsg1(400, "control_bwlimit: sleeping for %lld usecs\n", usec_sleep); } /* * Sleep the right number of usecs. */ while (1) { bmicrosleep(0, usec_sleep); now = get_current_btime(); /* * See if we slept enough or that bmicrosleep() returned early. */ if ((now - m_last_tick) < usec_sleep) { usec_sleep -= (now - m_last_tick); continue; } else { m_last_tick = now; break; } } /* * Subtract the number of bytes we could have sent during the sleep * time given the bandwidth limit set. We only do this when we are * allowed to burst e.g. use unused bytes from previous timeslices * to get an overall bandwidth limiting which may sometimes be below * the bandwidth and sometimes above it but the average will be near * the set bandwidth. */ if (m_use_bursting) { m_nb_bytes -= (int64_t)(usec_sleep * ((double)m_bwlimit / 1000000.0)); } else { m_nb_bytes = 0; } } }