From: Kern Sibbald Date: Wed, 28 Jul 2004 18:53:15 +0000 (+0000) Subject: Implement berrno for bpipes and run_program X-Git-Tag: Release-7.0.0~9277 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=976bb75e86bee77eb79b97a20ff152454badbe9f;p=bacula%2Fbacula Implement berrno for bpipes and run_program git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/trunk@1496 91ce42f0-d328-0410-95d8-f526ca767f89 --- diff --git a/bacula/kernstodo b/bacula/kernstodo index 377e7e83ac..3f47599c7e 100644 --- a/bacula/kernstodo +++ b/bacula/kernstodo @@ -4,10 +4,6 @@ 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?". @@ -27,6 +23,7 @@ - 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. @@ -1115,3 +1112,8 @@ Block Position: 0 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) + diff --git a/bacula/src/dird/backup.c b/bacula/src/dird/backup.c index 9d0726f0dd..a10f733180 100644 --- a/bacula/src/dird/backup.c +++ b/bacula/src/dird/backup.c @@ -362,8 +362,9 @@ static void backup_cleanup(JCR *jcr, int TermCode, char *since, FILESET_DBR *fsr 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); } } diff --git a/bacula/src/dird/fd_cmds.c b/bacula/src/dird/fd_cmds.c index c37db4900a..fdc8617cf4 100644 --- a/bacula/src/dird/fd_cmds.c +++ b/bacula/src/dird/fd_cmds.c @@ -254,8 +254,9 @@ static int send_list(JCR *jcr, int list) 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 */ @@ -276,8 +277,10 @@ static int send_list(JCR *jcr, int list) } } 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; @@ -415,8 +418,10 @@ static int send_fileset(JCR *jcr) } } 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; diff --git a/bacula/src/dird/job.c b/bacula/src/dird/job.c index bbbefa8997..e387dd9255 100644 --- a/bacula/src/dird/job.c +++ b/bacula/src/dird/job.c @@ -57,7 +57,9 @@ void init_job_server(int max_workers) 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")); @@ -89,7 +91,9 @@ JobId_t run_job(JCR *jcr) /* 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; @@ -129,7 +133,9 @@ JobId_t run_job(JCR *jcr) /* 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; } @@ -189,8 +195,9 @@ static void *job_thread(void *arg) } 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; @@ -248,12 +255,12 @@ static void *job_thread(void *arg) * 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()); } } } diff --git a/bacula/src/lib/berrno.c b/bacula/src/lib/berrno.c index 9ec5fbd6bb..9354bb8ca9 100644 --- a/bacula/src/lib/berrno.c +++ b/bacula/src/lib/berrno.c @@ -33,77 +33,69 @@ #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 diff --git a/bacula/src/lib/berrno.h b/bacula/src/lib/berrno.h index 88492e1772..7efdeba61b 100644 --- a/bacula/src/lib/berrno.h +++ b/bacula/src/lib/berrno.h @@ -25,8 +25,10 @@ */ #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, diff --git a/bacula/src/lib/bpipe.c b/bacula/src/lib/bpipe.c index 6955aa650e..20cf5bee82 100644 --- a/bacula/src/lib/bpipe.c +++ b/bacula/src/lib/bpipe.c @@ -29,12 +29,14 @@ #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 @@ -49,6 +51,7 @@ BPIPE *open_bpipe(char *prog, int wait, const char *mode) POOLMEM *tprog; int mode_read, mode_write; BPIPE *bpipe; + int save_errno; bpipe = (BPIPE *)malloc(sizeof(BPIPE)); memset(bpipe, 0, sizeof(BPIPE)); @@ -68,20 +71,25 @@ BPIPE *open_bpipe(char *prog, int wait, const char *mode) /* 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]); @@ -91,6 +99,7 @@ BPIPE *open_bpipe(char *prog, int wait, const char *mode) close(readp[1]); } free(bpipe); + errno = save_errno; return NULL; case 0: /* child */ @@ -108,7 +117,15 @@ BPIPE *open_bpipe(char *prog, int wait, const char *mode) 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; @@ -148,7 +165,7 @@ int close_wpipe(BPIPE *bpipe) * Close both pipes and free resources * * Returns: 0 on success - * errno on failure + * berrno on failure */ int close_bpipe(BPIPE *bpipe) { @@ -183,6 +200,7 @@ 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; @@ -190,7 +208,7 @@ int close_bpipe(BPIPE *bpipe) 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 */ @@ -199,18 +217,19 @@ int close_bpipe(BPIPE *bpipe) } } 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); } @@ -228,7 +247,7 @@ int close_bpipe(BPIPE *bpipe) * 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) { diff --git a/bacula/src/lib/message.c b/bacula/src/lib/message.c index 93e82570a4..f33380aa67 100755 --- a/bacula/src/lib/message.c +++ b/bacula/src/lib/message.c @@ -473,10 +473,12 @@ void close_msg(JCR *jcr) 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: @@ -645,9 +647,11 @@ void dispatch_message(JCR *jcr, int type, int level, char *msg) /* 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); diff --git a/bacula/src/lib/signal.c b/bacula/src/lib/signal.c index 74091e75c1..b6ae5eafa1 100644 --- a/bacula/src/lib/signal.c +++ b/bacula/src/lib/signal.c @@ -55,6 +55,15 @@ static SIG_HANDLER *exit_handler; /* 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 */ diff --git a/bacula/src/stored/autochanger.c b/bacula/src/stored/autochanger.c index d73c564092..342619ad30 100644 --- a/bacula/src/stored/autochanger.c +++ b/bacula/src/stored/autochanger.c @@ -95,6 +95,13 @@ int autoload_device(DCR *dcr, int writing, BSOCK *dir) 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); } /* @@ -112,8 +119,10 @@ int autoload_device(DCR *dcr, int writing, BSOCK *dir) 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 { @@ -157,8 +166,10 @@ static int get_autochanger_loaded_slot(JCR *jcr) 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); @@ -216,7 +227,13 @@ bool autochanger_list(DCR *dcr, BSOCK *dir) 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; } @@ -234,8 +251,13 @@ bool autochanger_list(DCR *dcr, BSOCK *dir) 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; diff --git a/bacula/src/stored/btape.c b/bacula/src/stored/btape.c index 5ac4c6a46a..e9a1dc548b 100644 --- a/bacula/src/stored/btape.c +++ b/bacula/src/stored/btape.c @@ -1102,12 +1102,14 @@ try_again: 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) { @@ -1128,8 +1130,10 @@ try_again: 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()); } } @@ -1149,8 +1153,10 @@ try_again: 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; }