1.35 Items to do:
- Add bscan to four-concurrent-jobs regression.
- Do tape alerts -- see tapealert.txt
-- Fix errno handling in win32 compat routines.
-- Add better error codes to run_program (10000+)
-- Revisit and revise Disaster Recovery (fix SCSI and RAID
- disk detection)
- Document a get out of jail procedure if everything breaks if
you lost/broke the Catalog -- do the same for "I know my
file is there how do I get it back?".
- Fix restore ++++ that get intermingled with "Building directory tree"
- Solve the termcap.h problem on Solaris configure.
- Fix ./configure to handle installed SQLite
+- Test Win32 errno handling.
Documentation to do: (any release a little bit at a time)
- Document query file format.
Building directory tree for JobId 856 ...
Building directory tree for JobId 797 ...3
Jobs inserted into the tree.
+- Fix errno handling in win32 compat routines.
+- Add better error codes to run_program (10000+)
+- Revisit and revise Disaster Recovery (fix SCSI and RAID
+ disk detection)
+
fclose(fd);
}
} else {
+ berrno be;
Jmsg(jcr, M_ERROR, 0, _("Could not open WriteBootstrap file:\n"
- "%s: ERR=%s\n"), fname, strerror(errno));
+ "%s: ERR=%s\n"), fname, be.strerror());
set_jcr_job_status(jcr, JS_ErrorTerminated);
}
}
fd->msg = edit_job_codes(jcr, fd->msg, p, "");
bpipe = open_bpipe(fd->msg, 0, "r");
if (!bpipe) {
+ berrno be;
Jmsg(jcr, M_FATAL, 0, _("Cannot run program: %s. ERR=%s\n"),
- p, strerror(errno));
+ p, be.strerror());
goto bail_out;
}
/* Copy File options */
}
}
if ((stat=close_bpipe(bpipe)) != 0) {
- Jmsg(jcr, M_FATAL, 0, _("Error running program: %s. RtnStat=%d ERR=%s\n"),
- p, stat, strerror(errno));
+ berrno be;
+ be.set_errno(stat);
+ Jmsg(jcr, M_FATAL, 0, _("Error running program %p: ERR=%s\n"),
+ p, be.strerror());
goto bail_out;
}
break;
}
}
if ((stat=close_bpipe(bpipe)) != 0) {
- Jmsg(jcr, M_FATAL, 0, _("Error running program: %s. RtnStat=%d ERR=%s\n"),
- p, stat, strerror(errno));
+ berrno be;
+ be.set_errno(stat);
+ Jmsg(jcr, M_FATAL, 0, _("Error running program: %s. ERR=%s\n"),
+ p, be.strerror());
goto bail_out;
}
break;
watchdog_t *wd;
if ((stat = jobq_init(&job_queue, max_workers, job_thread)) != 0) {
- Emsg1(M_ABORT, 0, _("Could not init job queue: ERR=%s\n"), strerror(stat));
+ berrno be;
+ be.set_errno(stat);
+ Emsg1(M_ABORT, 0, _("Could not init job queue: ERR=%s\n"), be.strerror());
}
if ((wd = new_watchdog()) == NULL) {
Emsg0(M_ABORT, 0, _("Could not init job monitor watchdogs\n"));
/* Initialize termination condition variable */
if ((errstat = pthread_cond_init(&jcr->term_wait, NULL)) != 0) {
- Jmsg1(jcr, M_FATAL, 0, _("Unable to init job cond variable: ERR=%s\n"), strerror(errstat));
+ berrno be;
+ be.set_errno(errstat);
+ Jmsg1(jcr, M_FATAL, 0, _("Unable to init job cond variable: ERR=%s\n"), be.strerror());
goto bail_out;
}
jcr->term_wait_inited = true;
/* Queue the job to be run */
if ((stat = jobq_add(&job_queue, jcr)) != 0) {
- Jmsg(jcr, M_FATAL, 0, _("Could not add job queue: ERR=%s\n"), strerror(stat));
+ berrno be;
+ be.set_errno(stat);
+ Jmsg(jcr, M_FATAL, 0, _("Could not add job queue: ERR=%s\n"), be.strerror());
JobId = 0;
goto bail_out;
}
}
status = close_bpipe(bpipe);
if (status != 0) {
- Jmsg(jcr, M_FATAL, 0, _("RunBeforeJob returned non-zero status=%d\n"),
- status);
+ berrno be;
+ be.set_errno(status);
+ Jmsg(jcr, M_FATAL, 0, _("RunBeforeJob error: ERR=%s\n"), be.strerror());
set_jcr_job_status(jcr, JS_FatalError);
update_job_end_record(jcr);
goto bail_out;
* job in error, simply report the error condition.
*/
if (status != 0) {
+ berrno be;
+ be.set_errno(status);
if (jcr->JobStatus == JS_Terminated) {
- Jmsg(jcr, M_WARNING, 0, _("RunAfterJob returned non-zero status=%d\n"),
- status);
+ Jmsg(jcr, M_WARNING, 0, _("RunAfterJob error: ERR=%s\n"), be.strerror());
} else {
- Jmsg(jcr, M_FATAL, 0, _("RunAfterFailedJob returned non-zero status=%d\n"),
- status);
+ Jmsg(jcr, M_FATAL, 0, _("RunAfterFailedJob error: ERR=%s\n"), be.strerror());
}
}
}
#include "bacula.h"
+#ifndef HAVE_WIN32
+extern const char *get_signal_name(int sig);
+extern int num_execvp_errors;
+extern int execvp_errors[];
+#endif
+
const char *berrno::strerror()
{
+ int stat = 0;
#ifdef HAVE_WIN32
LPVOID msg;
- if (errnum && b_errno_win32) {
+ if (berrno_ && b_errno_win32) {
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- (LPTSTR)&msg;
+ (LPTSTR)&msg,
0,
NULL);
- pm_strcpy(&buf_, msg);
+ pm_strcpy(&buf_, (const char *)msg);
LocalFree(msg);
return (const char *)buf_;
}
+#else
+ if (berrno_ & b_errno_exit) {
+ stat = (berrno_ & ~b_errno_exit); /* remove bit */
+ if (stat == 0) {
+ return "Child exited normally."; /* this really shouldn't happen */
+ } else {
+ /* Maybe an execvp failure */
+ if (stat >= 200) {
+ if (stat < 200 + num_execvp_errors) {
+ berrno_ = execvp_errors[stat - 200];
+ } else {
+ return "Unknown error during program execvp";
+ }
+ } else {
+ Mmsg(&buf_, "Child exited with code %d", stat);
+ return buf_;
+ }
+ /* If we drop out here, berrno_ is set to an execvp errno */
+ }
+ }
+ if (berrno_ & b_errno_signal) {
+ stat = (berrno_ & ~b_errno_signal); /* remove bit */
+ Mmsg(&buf_, "Child died from signal %d: %s", stat, get_signal_name(stat));
+ return buf_;
+ }
#endif
+ /* Normal errno */
if (bstrerror(berrno_, buf_, 1024) < 0) {
return "Invalid errno. No error message possible.";
}
- return (const char *)buf_;
+ return buf_;
}
#ifdef TEST_PROGRAM
-
-struct FILESET {
- alist mylist;
-};
-
int main()
{
- FILESET *fileset;
- char buf[30];
- alist *mlist;
-
- fileset = (FILESET *)malloc(sizeof(FILESET));
- memset(fileset, 0, sizeof(FILESET));
- fileset->mylist.init();
-
- printf("Manual allocation/destruction of list:\n");
-
- for (int i=0; i<20; i++) {
- sprintf(buf, "This is item %d", i);
- fileset->mylist.append(bstrdup(buf));
- }
- for (int i=0; i< fileset->mylist.size(); i++) {
- printf("Item %d = %s\n", i, (char *)fileset->mylist[i]);
- }
- fileset->mylist.destroy();
- free(fileset);
-
- printf("Allocation/destruction using new delete\n");
- mlist = new alist(10);
-
- for (int i=0; i<20; i++) {
- sprintf(buf, "This is item %d", i);
- mlist->append(bstrdup(buf));
- }
- for (int i=0; i< mlist->size(); i++) {
- printf("Item %d = %s\n", i, (char *)mlist->get(i));
- }
-
- delete mlist;
-
-
- sm_dump(false);
-
}
#endif
*/
#ifdef HAVE_WIN32
-#define b_errno_win32 (1<<29) /* user reserved bit */
+#define b_errno_win32 (1<<29) /* user reserved bit */
#endif
+#define b_errno_exit (1<<28) /* child exited, exit code returned */
+#define b_errno_signal (1<<27) /* child died, signal code returned */
/*
* A more generalized way of handling errno that works with Unix, Windows,
#include "bacula.h"
#include "jcr.h"
+int execvp_errors[] = {EACCES, ENOEXEC, EFAULT, EINTR, E2BIG,
+ ENAMETOOLONG, ENOMEM, ETXTBSY, ENOENT};
+int num_execvp_errors = (int)(sizeof(execvp_errors)/sizeof(int));
+
#define MAX_ARGV 100
static void build_argc_argv(char *cmd, int *bargc, char *bargv[], int max_arg);
-
-
/*
* Run an external program. Optionally wait a specified number
* of seconds. Program killed if wait exceeded. We open
POOLMEM *tprog;
int mode_read, mode_write;
BPIPE *bpipe;
+ int save_errno;
bpipe = (BPIPE *)malloc(sizeof(BPIPE));
memset(bpipe, 0, sizeof(BPIPE));
/* Each pipe is one way, write one end, read the other, so we need two */
if (mode_write && pipe(writep) == -1) {
+ save_errno = errno;
free(bpipe);
+ errno = save_errno;
return NULL;
}
if (mode_read && pipe(readp) == -1) {
+ save_errno = errno;
if (mode_write) {
close(writep[0]);
close(writep[1]);
}
free(bpipe);
+ errno = save_errno;
return NULL;
}
/* Start worker process */
switch (bpipe->worker_pid = fork()) {
case -1: /* error */
+ save_errno = errno;
if (mode_write) {
close(writep[0]);
close(writep[1]);
close(readp[1]);
}
free(bpipe);
+ errno = save_errno;
return NULL;
case 0: /* child */
close(i);
}
execvp(bargv[0], bargv); /* call the program */
- exit(errno); /* shouldn't get here */
+ /* Convert errno into an exit code for later analysis */
+ for (i=0; i< num_execvp_errors; i++) {
+ if (execvp_errors[i] == errno) {
+ exit(200 + i); /* exit code => errno */
+ }
+ }
+ exit(255); /* unknown errno */
+
+
default: /* parent */
break;
* Close both pipes and free resources
*
* Returns: 0 on success
- * errno on failure
+ * berrno on failure
*/
int close_bpipe(BPIPE *bpipe)
{
wpid = waitpid(bpipe->worker_pid, &chldstatus, wait_option);
} while (wpid == -1 && (errno == EINTR || errno == EAGAIN));
if (wpid == bpipe->worker_pid || wpid == -1) {
+ stat = errno;
Dmsg3(200, "Got break wpid=%d status=%d ERR=%s\n", wpid, chldstatus,
wpid==-1?strerror(errno):"none");
break;
Dmsg3(200, "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 */
+ bmicrosleep(1, 0); /* wait one second */
remaining_wait--;
} else {
stat = ETIME; /* set error status */
}
}
if (wpid > 0) {
- if (WIFEXITED(chldstatus)) { /* process exit()ed */
+ if (WIFEXITED(chldstatus)) { /* process exit()ed */
stat = WEXITSTATUS(chldstatus);
if (stat != 0) {
Dmsg1(100, "Non-zero status %s returned from child.\n", stat);
- stat = ECHILD;
+ stat |= b_errno_exit; /* exit status returned */
}
- Dmsg1(200, "child status=%d\n", stat);
+ Dmsg1(200, "child status=%d\n", stat & ~b_errno_exit);
} else if (WIFSIGNALED(chldstatus)) { /* process died */
- stat = ECHILD;
- Dmsg0(200, "Signaled\n");
+ stat = WTERMSIG(chldstatus);
+ Dmsg1(200, "Child died from signale %d\n", stat);
+ stat |= b_errno_signal; /* exit signal returned */
}
- }
+ }
if (bpipe->timer_id) {
stop_child_timer(bpipe->timer_id);
}
* Contrary to my normal calling conventions, this program
*
* Returns: 0 on success
- * non-zero on error == errno
+ * non-zero on error == berrno status
*/
int run_program(char *prog, int wait, POOLMEM *results)
{
stat = close_bpipe(bpipe);
if (stat != 0 && msgs != daemon_msgs) {
+ berrno be;
+ be.set_errno(stat);
Dmsg1(150, "Calling emsg. CMD=%s\n", cmd);
- Jmsg3(jcr, M_ERROR, 0, _("Mail program terminated in error. stat=%d\n"
+ Jmsg2(jcr, M_ERROR, 0, _("Mail program terminated in error.\n"
"CMD=%s\n"
- "ERR=%s\n"), stat, cmd, strerror(stat));
+ "ERR=%s\n"), cmd, be.strerror());
}
free_memory(line);
rem_temp_file:
/* Messages to the operator go one at a time */
stat = close_bpipe(bpipe);
if (stat != 0) {
+ berrno be;
+ be.set_errno(stat);
Jmsg2(jcr, M_ERROR, 0, _("Operator mail program terminated in error.\n"
"CMD=%s\n"
- "ERR=%s\n"), mcmd, strerror(stat));
+ "ERR=%s\n"), mcmd, be.strerror());
}
}
free_pool_memory(mcmd);
/* main process id */
static pid_t main_pid = 0;
+const char *get_signal_name(int sig)
+{
+ if (sig < 0 || sig > BA_NSIG || !sig_names[sig]) {
+ return "Invalid signal number";
+ } else {
+ return sig_names[sig];
+ }
+}
+
/*
* Handle signals here
*/
changer = edit_device_codes(jcr, changer,
jcr->device->changer_command, "unload");
status = run_program(changer, timeout, NULL);
+ if (status != 0) {
+ berrno be;
+ be.set_errno(status);
+ Jmsg(jcr, M_INFO, 0, _("3992 Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n"),
+ slot, drive, be.strerror());
+ }
+
Dmsg1(400, "unload status=%d\n", status);
}
/*
Jmsg(jcr, M_INFO, 0, _("3305 Autochanger \"load slot %d, drive %d\", status is OK.\n"),
slot, drive);
} else {
- Jmsg(jcr, M_INFO, 0, _("3992 Bad autochanger \"load slot %d, drive %d\", status=%d.\n"),
- slot, drive, status);
+ berrno be;
+ be.set_errno(status);
+ Jmsg(jcr, M_INFO, 0, _("3992 Bad autochanger \"load slot %d, drive %d\": ERR=%s.\n"),
+ slot, drive, be.strerror());
}
Dmsg2(400, "load slot %d status=%d\n", slot, status);
} else {
drive);
}
} else {
- Jmsg(jcr, M_INFO, 0, _("3991 Bad autochanger \"loaded drive %d\" command, status=%d.\n"),
- drive, status);
+ berrno be;
+ be.set_errno(status);
+ Jmsg(jcr, M_INFO, 0, _("3991 Bad autochanger \"loaded drive %d\" command: ERR=%s.\n"),
+ drive, be.strerror());
loaded = -1; /* force unload */
}
free_pool_memory(changer);
slot = jcr->VolCatInfo.Slot;
jcr->VolCatInfo.Slot = loaded;
changer = edit_device_codes(jcr, changer, jcr->device->changer_command, "unload");
- run_program(changer, timeout, NULL);
+ int stat = run_program(changer, timeout, NULL);
+ if (stat != 0) {
+ berrno be;
+ be.set_errno(stat);
+ Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d\" command: ERR=%s.\n"),
+ loaded, be.strerror());
+ }
jcr->VolCatInfo.Slot = slot;
}
dir->msglen = strlen(dir->msg);
bnet_send(dir);
}
+ int stat = close_bpipe(bpipe);
+ if (stat != 0) {
+ berrno be;
+ be.set_errno(stat);
+ bnet_fsend(dir, "Autochanger error: ERR=%s\n", be.strerror());
+ }
bnet_sig(dir, BNET_EOD);
- close_bpipe(bpipe);
free_pool_memory(changer);
return true;
changer = edit_device_codes(jcr, changer, jcr->device->changer_command,
"loaded");
status = run_program(changer, timeout, results);
- Dmsg3(100, "run_prog: %s stat=%d result=%s\n", changer, status, results);
+ Dmsg3(100, "run_prog: %s stat=%d result=\"%s\"\n", changer, status, results);
if (status == 0) {
loaded = atoi(results);
} else {
+ berrno be;
+ be.set_errno(status);
Pmsg1(-1, _("3991 Bad autochanger command: %s\n"), changer);
- Pmsg2(-1, _("3991 status=%d result=%s\n"), status, results);
+ Pmsg2(-1, _("3991 result=\"%s\": ERR=%s\n"), results, be.strerror());
goto bail_out;
}
if (loaded) {
status = run_program(changer, timeout, results);
Pmsg2(-1, "unload status=%s %d\n", status==0?"OK":"Bad", status);
if (status != 0) {
+ berrno be;
+ be.set_errno(status);
Pmsg1(-1, _("3992 Bad autochanger command: %s\n"), changer);
- Pmsg2(-1, _("3992 status=%d result=%s\n"), status, results);
+ Pmsg2(-1, _("3992 result=\"%s\": ERR=%s\n"), results, be.strerror());
}
}
Pmsg2(-1, _("3303 Autochanger \"load slot %d %d\" status is OK.\n"),
slot, dev->drive_index);
} else {
+ berrno be;
+ be.set_errno(status);
Pmsg1(-1, _("3993 Bad autochanger command: %s\n"), changer);
- Pmsg2(-1, _("3993 status=%d result=%s\n"), status, results);
+ Pmsg2(-1, _("3993 result=\"%s\": ERR=%s\n"), results, be.strerror());
goto bail_out;
}