aboutsummaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
authorAndrew Gildfind <ajag@sgi.com>2001-08-31 01:39:05 +0000
committerAndrew Gildfind <ajag@sgi.com>2001-08-31 01:39:05 +0000
commiteaecf140a73d42680fcf85a4a38701b430e58f0b (patch)
tree9efbc531751774b32f7367294a04f9311051bba3 /common
parent55a4c7a45a08f55f8e3a96082a88c7f28a4290d4 (diff)
downloadxfsdump-dev-eaecf140a73d42680fcf85a4a38701b430e58f0b.tar.gz
merge of irix6.5f-melb:eoe:06291a, irix6.5f-melb:eoe:06307a,
and irix6.5f-melb:eoe:06315a, see PV #784355 - Replace stream_unregister() with stream_dead()
Diffstat (limited to 'common')
-rw-r--r--common/cldmgr.c2
-rw-r--r--common/dlog.c2
-rw-r--r--common/main.c95
-rw-r--r--common/mlog.c349
-rw-r--r--common/mlog.h7
-rw-r--r--common/qlock.c14
-rw-r--r--common/stream.c204
-rw-r--r--common/stream.h33
-rw-r--r--common/types.h17
9 files changed, 637 insertions, 86 deletions
diff --git a/common/cldmgr.c b/common/cldmgr.c
index fcba379d..81b7ed0a 100644
--- a/common/cldmgr.c
+++ b/common/cldmgr.c
@@ -171,7 +171,7 @@ cldmgr_died( pid_t pid )
}
cldp->c_busy = BOOL_FALSE;
if ( ( intgen_t )( cldp->c_streamix ) >= 0 ) {
- stream_unregister( pid );
+ stream_dead( pid );
}
}
diff --git a/common/dlog.c b/common/dlog.c
index 3174916c..145178bb 100644
--- a/common/dlog.c
+++ b/common/dlog.c
@@ -493,6 +493,7 @@ promptinput( char *buf,
} else if ( dlog_signo_received == SIGINT ) {
mlog( MLOG_NORMAL | MLOG_NOLOCK | MLOG_BARE,
"keyboard interrupt\n" );
+ mlog_exit_hint(RV_KBD_INTR);
*exceptionixp = sigintix;
} else if ( dlog_signo_received == SIGHUP ) {
mlog( MLOG_NORMAL | MLOG_NOLOCK | MLOG_BARE,
@@ -505,6 +506,7 @@ promptinput( char *buf,
} else if ( dlog_signo_received == SIGQUIT ) {
mlog( MLOG_NORMAL | MLOG_NOLOCK | MLOG_BARE,
"keyboard quit\n" );
+ mlog_exit_hint(RV_KBD_INTR);
*exceptionixp = sigquitix;
} else {
mlog( MLOG_NORMAL | MLOG_NOLOCK | MLOG_BARE,
diff --git a/common/main.c b/common/main.c
index 9ed12f49..f5c9b76f 100644
--- a/common/main.c
+++ b/common/main.c
@@ -201,6 +201,7 @@ main( int argc, char *argv[] )
intgen_t exitcode;
rlim64_t tmpstacksz;
bool_t ok;
+ int rval;
/* sanity checks
*/
@@ -217,19 +218,26 @@ main( int argc, char *argv[] )
*/
progname = argv[ 0 ];
+ /* Get the parent's pid. will be used in signal handling
+ * to differentiate parent from children.
+ */
+ parentpid = getpid( );
+ rval = atexit(mlog_exit_flush);
+ assert(rval == 0);
+
/* pre-scan the command line for the option file option.
* if found, create a new argv.
*/
ok = loadoptfile( &argc, &argv );
if ( ! ok ) {
- return EXIT_ERROR;
+ return mlog_exit(EXIT_ERROR, RV_OPT);
}
/* initialize message logging (stage 1)
*/
ok = mlog_init1( argc, argv );
if ( ! ok ) {
- return EXIT_ERROR;
+ return mlog_exit(EXIT_ERROR, RV_INIT);
}
/* scan the command line for the miniroot, info, progress
* report options, and stacksz.
@@ -253,7 +261,7 @@ main( int argc, char *argv[] )
"-%c argument missing\n",
optopt );
usage( );
- return EXIT_ERROR;
+ return mlog_exit(EXIT_ERROR, RV_OPT);
}
errno = 0;
tmpstacksz = strtoull( optarg, 0, 0 );
@@ -265,7 +273,7 @@ main( int argc, char *argv[] )
optopt,
optarg );
usage( );
- return EXIT_ERROR;
+ return mlog_exit(EXIT_ERROR, RV_OPT);
}
minstacksz = tmpstacksz;
break;
@@ -275,7 +283,7 @@ main( int argc, char *argv[] )
"-%c argument missing\n",
optopt );
usage( );
- return EXIT_ERROR;
+ return mlog_exit(EXIT_ERROR, RV_OPT);
}
errno = 0;
tmpstacksz = strtoull( optarg, 0, 0 );
@@ -287,7 +295,7 @@ main( int argc, char *argv[] )
optopt,
optarg );
usage( );
- return EXIT_ERROR;
+ return mlog_exit(EXIT_ERROR, RV_OPT);
}
maxstacksz = tmpstacksz;
break;
@@ -296,6 +304,7 @@ main( int argc, char *argv[] )
break;
case GETOPT_HELP:
infoonly = BOOL_TRUE;
+ mlog_exit_hint(RV_USAGE);
break;
case GETOPT_PROGRESS:
if ( ! optarg || optarg[ 0 ] == '-' ) {
@@ -303,7 +312,7 @@ main( int argc, char *argv[] )
"-%c argument missing\n",
optopt );
usage( );
- return EXIT_ERROR;
+ return mlog_exit(EXIT_ERROR, RV_OPT);
}
progrpt_interval = ( time_t )atoi( optarg );
if ( progrpt_interval > 0 ) {
@@ -329,7 +338,7 @@ main( int argc, char *argv[] )
"min is 0x%llx, max is 0x%llx\n",
minstacksz,
maxstacksz );
- return EXIT_ERROR;
+ return mlog_exit(EXIT_ERROR, RV_INIT);
}
if ( argc == 1 ) {
@@ -361,7 +370,7 @@ main( int argc, char *argv[] )
ok = set_rlimits( &vmsz );
#endif /* RESTORE */
if ( ! ok ) {
- return EXIT_ERROR;
+ return mlog_exit(EXIT_ERROR, RV_INIT);
}
/* perform an experiment to determine if we are in the miniroot.
@@ -375,16 +384,19 @@ main( int argc, char *argv[] )
*/
ok = qlock_init( miniroot );
if ( ! ok ) {
- return EXIT_ERROR;
+ return mlog_exit(EXIT_ERROR, RV_INIT);
}
- /* initialize message logging (stage 2)
+ /* initialize message logging (stage 2) - allocate the message lock
*/
ok = mlog_init2( );
if ( ! ok ) {
- return EXIT_ERROR;
+ return mlog_exit(EXIT_ERROR, RV_INIT);
}
+ /* initialize the critical region lock
+ */
+ lock_init( );
rmt_turnonmsgs(1); /* turn on WARNING msgs for librmt */
mlog( MLOG_NITTY + 1, "INTGENMAX == %ld (0x%lx)\n", INTGENMAX, INTGENMAX );
@@ -413,14 +425,8 @@ main( int argc, char *argv[] )
ASSERT( ( intgen_t )pgsz > 0 );
pgmask = pgsz - 1;
- /* initialize the critical region lock
- */
- lock_init( );
-
- /* Get the parent's pid. will be used in signal handling
- * to differentiate parent from children.
- */
- parentpid = getpid( );
+ /* report parent pid
+ */
mlog( MLOG_DEBUG | MLOG_PROC,
"parent pid is %d\n",
parentpid );
@@ -433,7 +439,7 @@ main( int argc, char *argv[] )
mlog( MLOG_NORMAL | MLOG_ERROR,
"unable to determine current directory: %s\n",
strerror( errno ));
- return EXIT_ERROR;
+ return mlog_exit(EXIT_ERROR, RV_INIT);
}
/* if just looking for info, oblige
@@ -444,13 +450,13 @@ main( int argc, char *argv[] )
version,
subversion );
usage( );
- return EXIT_NORMAL; /* normal termination */
+ return mlog_exit(EXIT_NORMAL, RV_OK); /* normal termination */
}
/* if an inventory display is requested, do it and exit
*/
if ( ! inv_DEBUG_print( argc, argv )) {
- return EXIT_NORMAL; /* normal termination */
+ return mlog_exit(EXIT_NORMAL, RV_OK); /* normal termination */
}
#ifdef DUMP
@@ -465,7 +471,7 @@ main( int argc, char *argv[] )
if ( euid != 0 ) {
mlog( MLOG_NORMAL,
"effective user ID must be root\n" );
- return EXIT_ERROR;
+ return mlog_exit(EXIT_ERROR, RV_PERM);
}
#endif /* DUMP */
@@ -473,7 +479,7 @@ main( int argc, char *argv[] )
*/
ok = dlog_init( argc, argv );
if ( ! ok ) {
- return EXIT_ERROR;
+ return mlog_exit(EXIT_ERROR, RV_INIT);
}
/* initialize the stack checking abstraction
@@ -485,7 +491,7 @@ main( int argc, char *argv[] )
*/
ok = cldmgr_init( );
if ( ! ok ) {
- return EXIT_ERROR;
+ return mlog_exit(EXIT_ERROR, RV_INIT);
}
/* select and instantiate a drive manager for each stream. this
@@ -496,7 +502,7 @@ main( int argc, char *argv[] )
ok = drive_init1( argc, argv, miniroot );
if ( ! ok ) {
cldmgr_killall( );
- return EXIT_ERROR;
+ return mlog_exit(EXIT_ERROR, RV_INIT);
}
/* check the drives to see if we're in a pipeline.
@@ -546,7 +552,7 @@ main( int argc, char *argv[] )
*/
gwhdrtemplatep = global_hdr_alloc( argc, argv );
if ( ! gwhdrtemplatep ) {
- return EXIT_ERROR;
+ return mlog_exit(EXIT_ERROR, RV_INIT);
}
#endif /* DUMP */
@@ -598,7 +604,7 @@ main( int argc, char *argv[] )
#endif /* RESTORE */
if ( ! ok ) {
cldmgr_killall( );
- return EXIT_ERROR;
+ return mlog_exit(EXIT_ERROR, RV_INIT);
}
/* if miniroot or a pipeline, go single-threaded
@@ -621,11 +627,11 @@ main( int argc, char *argv[] )
( global_hdr_t * )0 );
#endif /* RESTORE */
if ( ! ok ) {
- return EXIT_ERROR;
+ return mlog_exit(EXIT_ERROR, RV_INIT);
}
ok = drive_init3( );
if ( ! ok ) {
- return EXIT_ERROR;
+ return mlog_exit(EXIT_ERROR, RV_INIT);
}
#ifdef DUMP
exitcode = content_stream_dump( 0 );
@@ -636,11 +642,11 @@ main( int argc, char *argv[] )
if ( exitcode != EXIT_NORMAL ) {
( void )content_complete( );
/* for cleanup side-effect */
- return exitcode;
+ return mlog_exit(exitcode, RV_UNKNOWN);
} else if ( content_complete( )) {
- return EXIT_NORMAL;
+ return mlog_exit(EXIT_NORMAL, RV_OK);
} else {
- return EXIT_INTERRUPT;
+ return mlog_exit(EXIT_INTERRUPT, RV_UNKNOWN);
}
}
@@ -851,7 +857,10 @@ main( int argc, char *argv[] )
*/
if ( stop_requested && ! stop_in_progress ) {
mlog( MLOG_NORMAL,
- "initiating session interrupt\n" );
+ "initiating session interrupt (timeout in %d second%s)\n",
+ stop_timeout,
+ stop_timeout == 1 ? "" : "s" );
+ mlog_exit_hint(RV_INTR);
stop_in_progress = BOOL_TRUE;
cldmgr_stop( );
ASSERT( stop_timeout >= 0 );
@@ -940,9 +949,15 @@ main( int argc, char *argv[] )
( void )content_complete( );
exitcode = EXIT_ERROR;
} else {
- exitcode = content_complete( ) ? EXIT_NORMAL : EXIT_INTERRUPT;
+ if ( content_complete( ) )
+ exitcode = EXIT_NORMAL;
+ else {
+ exitcode = EXIT_INTERRUPT;
+ if ( mlog_get_hint() == RV_NONE )
+ mlog_exit_hint(RV_INCOMPLETE);
+ }
}
- return exitcode;
+ return mlog_exit(exitcode, RV_UNKNOWN);
}
#define ULO( f, o ) fprintf( stderr, \
@@ -1091,6 +1106,12 @@ usage( void )
ULN( "- (stdin)" );
ULN( "<destination>" );
#endif /* RESTORE */
+
+ /* anywhere usage is called we will exit shortly after...
+ * catch all of those cases below
+ */
+
+ (void) mlog_exit(EXIT_ERROR, RV_OPT);
}
/* returns TRUE if preemption
diff --git a/common/mlog.c b/common/mlog.c
index 931d8673..e62fad33 100644
--- a/common/mlog.c
+++ b/common/mlog.c
@@ -38,6 +38,7 @@
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>
+#include <assert.h>
#include <time.h>
#include <getopt.h>
@@ -47,9 +48,14 @@
#include "mlog.h"
#include "cldmgr.h"
#include "getopt.h"
+#include "exit.h"
+#include "util.h"
+#include "global.h"
+#include "drive.h"
extern char *progname;
extern void usage( void );
+extern pid_t parentpid;
#ifdef DUMP
static FILE *mlog_fp = NULL; /* stderr */;
@@ -82,6 +88,14 @@ static size_t mlog_streamcnt;
static char mlog_levelstr[ 3 ];
#define MLOG_SS_NAME_MAX 10
+#ifdef DUMP
+#define PROGSTR "dump"
+#define PROGSTR_CAPS "Dump"
+#else
+#define PROGSTR "restore"
+#define PROGSTR_CAPS "Restore"
+#endif /* DUMP */
+#define N(a) (sizeof((a)) / sizeof((a)[0]))
static char mlog_ssstr[ MLOG_SS_NAME_MAX + 2 ];
@@ -123,6 +137,9 @@ static mlog_sym_t mlog_sym[ ] = {
};
static qlockh_t mlog_qlockh;
+static int mlog_main_exit_code = -1;
+static rv_t mlog_main_exit_return = RV_NONE;
+static rv_t mlog_main_exit_hint = RV_NONE;
bool_t
mlog_init1( intgen_t argc, char *argv[ ] )
@@ -392,14 +409,7 @@ mlog_va( intgen_t levelarg, char *fmt, va_list args )
}
if ( streamix != -1 && mlog_streamcnt > 1 ) {
fprintf( mlog_fp,
- "%s%s%s%s: "
-#ifdef DUMP
- "drive "
-#endif /* DUMP */
-#ifdef RESTORE
- "drive "
-#endif /* RESTORE */
- "%d: ",
+ "%s%s%s%s: drive %d: ",
progname,
mlog_tsstr,
mlog_ssstr,
@@ -435,6 +445,329 @@ mlog_va( intgen_t levelarg, char *fmt, va_list args )
}
}
+
+static const char *exit_strings[] =
+ { "SUCCESS", "ERROR", "INTERRUPT", "", "FAULT" };
+
+
+/*
+ * Map RV codes to actual error messages.
+ */
+
+struct rv_map {
+ int rv;
+ const char * rv_string;
+ const char * rv_desc;
+};
+
+static struct rv_map
+rvs[_RV_NUM] = {
+ /* Return Code Displayed Code Explanation */
+ { RV_OK, "OK", "success" },
+ { RV_NOTOK, "ERASE_FAILED", "media erase request denied" },
+ { RV_NOMORE, "NOMORE", "no more work to do" },
+ { RV_EOD, "EOD", "ran out of data" },
+ { RV_EOF, "EOF", "hit end of media file" },
+ { RV_EOM, "EOM", "hit end of media" },
+ { RV_ERROR, "ERROR", "operator error or resource exhaustion" },
+ { RV_DONE, "ALREADY_DONE", "another stream completed the operation" },
+ { RV_INTR, "INTERRUPT", PROGSTR " interrupted" },
+ { RV_CORRUPT, "CORRUPTION", "corrupt data encountered" },
+ { RV_QUIT, "QUIT", "media is no longer usable" },
+ { RV_DRIVE, "DRIVE_ERROR", "drive error" },
+ { RV_TIMEOUT, "TIMEOUT", "operation timed out" },
+ { RV_MEDIA, "NO_MEDIA", "no media in drive" },
+ { RV_PROTECTED, "WRITE_PROTECTED","object write protected" },
+ { RV_CORE, "CORE", "fatal error - core dumped" },
+ { RV_OPT, "OPT_ERROR", "bad command line option" },
+ { RV_INIT, "INIT_ERROR", "could not initialise subsystem" },
+ { RV_PERM, "NO_PERMISSION","insufficient privilege" },
+ { RV_COMPAT, "INCOMPATIBLE", "cannot apply - dump incompatible" },
+ { RV_INCOMPLETE,"INCOMPLETE", "the " PROGSTR " is incomplete" },
+ { RV_KBD_INTR, "KEYBOARD_INTERRUPT", "keyboard interrupt" },
+ { RV_INV, "INVENTORY", "error updating session inventory" },
+ { RV_NONE, "NONE", "no error code specified" },
+ { RV_UNKNOWN, "UNKNOWN", "unknown error" },
+};
+
+static struct rv_map
+rv_unknown = {
+ _RV_NUM, "???", "unknown error code"
+};
+
+static const struct rv_map *
+rv_getdesc(rv_t rv)
+{
+ int rvidx;
+
+ if (rv < 0 || rv >= _RV_NUM) {
+ return &rv_unknown;
+ }
+
+ for (rvidx = 0; rvidx < _RV_NUM; rvidx++)
+ if (rv == rvs[rvidx].rv)
+ return &rvs[rvidx];
+
+ return &rv_unknown;
+}
+
+
+/*
+ * mlog_exit takes two arguments an exit code (EXIT_*) and the internal
+ * return code (RV_*) that was signalled prior to the exit. mlog_exit
+ * stores these values in a per-stream structure managed by the stream_*
+ * functions.
+ *
+ * mlog_exit is called for: all returns from the content threads
+ * (content_stream_dump and content_stream_restore); for returns from
+ * the main process; and also from a couple of other locations where an
+ * error is known to directly lead to the termination of the program.
+ *
+ * For example, in the places mentioned above "return EXIT_ERROR;" has
+ * now been replaced with a call like
+ * "return mlog_exit(EXIT_ERROR, RV_DRIVE);" that specifies both the exit
+ * code, and the reason why the program is terminating.
+ *
+ * mlog_exit_flush uses the per-stream exit information recorded using
+ * mlog_exit to print a detailed status report, showing both the exit
+ * status of each stream, and the overall exit status of the
+ * program. This additional log information allows the user to detect
+ * failures that cannot be distinguished by looking at the exit code
+ * alone. In particular, the exit code does not currently allow the
+ * user to distinguish EOM conditions from user interruptions, and to
+ * detect an incomplete dump (caused, for example, by drive errors or
+ * corruption).
+ *
+ * Note, that to maintain backwards compatibility the exit codes
+ * returned by dump/restore have _not_ been changed. For more
+ * information see PV #784355.
+ *
+ * While mlog_exit provides considerably more information about the
+ * reasons for a dump terminating, there are a number of cases where
+ * dump maps return codes that have specific values such as RV_DRIVE,
+ * to return codes with less specific values such as RV_INTR, and in
+ * doing so throws away information that would have been useful in
+ * diagnosing the reasons for a failure. To alleviate this, an
+ * additional function mlog_exit_hint is provided that allows a "hint"
+ * to be made about the real reason a stream is terminating. A call to
+ * mlog_exit_hint should be made anywhere in the code a change in
+ * program state has occured that might lead to the termination of the
+ * dump. The mlog_exit_flush routine uses this additional information
+ * help work out what really happened.
+ */
+
+int
+_mlog_exit( const char *file, int line, int exit_code, rv_t rv )
+{
+ pid_t pid;
+ const struct rv_map *rvp;
+
+ pid = getpid();
+ rvp = rv_getdesc(rv);
+
+
+ mlog( MLOG_DEBUG | MLOG_NOLOCK | MLOG_BARE,
+ "%s: %d: mlog_exit called: "
+ "exit_code: %s return: %s (%s)\n",
+ file, line,
+ exit_strings[exit_code],
+ rvp->rv_string, rvp->rv_desc);
+
+ if (rv < 0 || rv >= _RV_NUM) {
+ mlog( MLOG_DEBUG | MLOG_ERROR | MLOG_NOLOCK | MLOG_BARE,
+ "mlog_exit(): bad return code");
+ return exit_code;
+ }
+
+ /*
+ * NOTE: we record the first call only. Exit codes can be mapped from
+ * more specific values to less specific values as we return up the
+ * call chain. We assume therefore that the first call contains the
+ * most accurate information about the termination condition.
+ */
+
+ if (pid == parentpid) {
+ if (mlog_main_exit_code == -1) {
+ mlog_main_exit_code = exit_code;
+ mlog_main_exit_return = rv;
+ }
+ }
+ else {
+ stream_state_t states[] = { S_RUNNING };
+ stream_state_t state;
+ intgen_t streamix;
+ int exit_code;
+ rv_t exit_return, exit_hint;
+
+ if (stream_get_exit_status(pid,
+ states,
+ N(states),
+ &state,
+ &streamix,
+ &exit_code,
+ &exit_return,
+ &exit_hint))
+ {
+ if (exit_code == -1) {
+ stream_set_code(pid, exit_code);
+ stream_set_return(pid, rv);
+ }
+ }
+ }
+
+ return exit_code;
+}
+
+void
+_mlog_exit_hint( const char *file, int line, rv_t rv )
+{
+ pid_t pid;
+ const struct rv_map *rvp;
+
+ pid = getpid();
+ rvp = rv_getdesc(rv);
+
+ mlog( MLOG_DEBUG | MLOG_NOLOCK | MLOG_BARE,
+ "%s: %d: mlog_exit_hint called: "
+ "hint: %s (%s)\n",
+ file, line,
+ rvp->rv_string, rvp->rv_desc);
+
+ if (rv < 0 || rv >= _RV_NUM) {
+ mlog( MLOG_DEBUG | MLOG_ERROR | MLOG_NOLOCK | MLOG_BARE,
+ "mlog_exit_hint(): bad return code");
+ return;
+ }
+
+ /*
+ * NOTE: we use the last hint before exit. Unlike exit codes we've added
+ * calls to mlog_exit_hint to improve error reporting. In general the
+ * call closest to the final exit point will provide the most accurate
+ * information about the termination condition.
+ */
+
+ if (pid == parentpid)
+ mlog_main_exit_hint = rv;
+ else
+ stream_set_hint( pid, rv );
+
+}
+
+rv_t
+mlog_get_hint( void )
+{
+ stream_state_t states[] = { S_RUNNING };
+ bool_t ok;
+ rv_t hint;
+
+ ok = stream_get_exit_status(getpid(), states, N(states),
+ NULL, NULL, NULL, NULL, &hint);
+ assert(ok);
+ return hint;
+}
+
+bool_t
+is_incomplete(rv_t rv)
+{
+ int j;
+ rv_t errors[] = { RV_CORRUPT, RV_INCOMPLETE, RV_EOD, RV_EOF, RV_EOM };
+
+ for (j = 0; j < N(errors); j++)
+ if (rv == errors[j])
+ return BOOL_TRUE;
+
+ return BOOL_FALSE;
+}
+
+void
+mlog_exit_flush(void)
+{
+ pid_t pids[STREAM_MAX];
+ int i, npids;
+ const struct rv_map *rvp;
+ stream_state_t states[] = { S_RUNNING, S_ZOMBIE };
+ bool_t incomplete = BOOL_FALSE;
+ bool_t quit = BOOL_FALSE;
+ const char *status_str;
+ rv_t rv;
+
+ if (mlog_level_ss[MLOG_SS_GEN] == MLOG_SILENT)
+ return;
+
+ if (mlog_main_exit_hint == RV_USAGE)
+ return;
+
+ npids = stream_find_all(states, N(states), pids, STREAM_MAX);
+ if (npids > 0) {
+
+ /* print the state of all the streams */
+ fprintf(mlog_fp, "%s: " PROGSTR_CAPS " Summary:\n", progname);
+
+ for (i = 0; i < npids; i++) {
+ stream_state_t state;
+ intgen_t streamix;
+ int exit_code;
+ rv_t exit_return, exit_hint;
+ bool_t ok;
+
+ ok = stream_get_exit_status(pids[i],
+ states,
+ N(states),
+ &state,
+ &streamix,
+ &exit_code,
+ &exit_return,
+ &exit_hint);
+ assert(ok);
+
+ /* hint takes priority over return */
+ rv = (exit_hint != RV_NONE) ? exit_hint : exit_return;
+ if (rv != RV_NONE) {
+ /* then an mlog_*() call was made otherwise we
+ * don't know anything about it...
+ */
+ rvp = rv_getdesc(rv);
+ fprintf(mlog_fp,
+ "%s: stream %d (pid %d) %s "
+ "%s (%s)\n",
+ progname,
+ streamix,
+ pids[i],
+ drivepp[streamix]->d_pathname,
+ rvp->rv_string,
+ rvp->rv_desc);
+ }
+
+ /* If the following conditions are true for any stream
+ * then they are true for the entire dump/restore.
+ */
+ if (! incomplete && is_incomplete(rv))
+ incomplete = BOOL_TRUE;
+
+ if (! quit && rv == RV_QUIT)
+ quit = BOOL_TRUE;
+ }
+ }
+
+ /* Check return codes for the main process
+ */
+ rv = (mlog_main_exit_hint != RV_NONE)
+ ? mlog_main_exit_hint : mlog_main_exit_return;
+
+ if (! incomplete && is_incomplete(rv))
+ incomplete = BOOL_TRUE;
+
+ if (! quit && rv == RV_QUIT)
+ quit = BOOL_TRUE;
+
+ /* now print the overall state of the dump/restore */
+ if (incomplete) status_str = "INCOMPLETE";
+ else if (quit) status_str = "QUIT";
+ else status_str = exit_strings[mlog_main_exit_code];
+ fprintf(mlog_fp, "%s: " PROGSTR_CAPS " Status: %s\n", progname, status_str);
+ fflush(mlog_fp);
+}
+
static intgen_t
mlog_sym_lookup( char *sym )
{
diff --git a/common/mlog.h b/common/mlog.h
index 74ce496f..d942948d 100644
--- a/common/mlog.h
+++ b/common/mlog.h
@@ -35,6 +35,7 @@
/* mlog.[hc] - message logging abstraction
*/
#include <stdarg.h>
+#include "types.h"
/* defined log levels - msg will be logged only if cmdline -v arg
* is greater than or equal to its level.
@@ -117,6 +118,12 @@ extern void mlog_tell_streamcnt( size_t streamcnt );
*/
extern void mlog( intgen_t level, char *fmt, ... );
extern void mlog_va( intgen_t levelarg, char *fmt, va_list args );
+#define mlog_exit( e, r ) _mlog_exit( __FILE__, __LINE__, (e), (r) )
+extern int _mlog_exit( const char *file, int line, int exit_code, rv_t return_code );
+#define mlog_exit_hint( r ) _mlog_exit_hint( __FILE__, __LINE__, (r) )
+extern void _mlog_exit_hint( const char *file, int line, rv_t rv );
+extern rv_t mlog_get_hint( void );
+extern void mlog_exit_flush( void );
/* the following calls are exported ONLY to dlog.c!
*/
diff --git a/common/qlock.c b/common/qlock.c
index 97fcbb80..2e7ed020 100644
--- a/common/qlock.c
+++ b/common/qlock.c
@@ -179,7 +179,7 @@ qlock_init( bool_t miniroot )
*/
rval = usconfig( CONF_ARENATYPE, ( u_intgen_t )US_SHAREDONLY );
if ( rval ) {
- mlog( MLOG_NORMAL | MLOG_ERROR | MLOG_NOLOCK,
+ mlog( MLOG_NORMAL | MLOG_ERROR | MLOG_NOLOCK | MLOG_BARE,
"unable to configure shared arena for auto unlink: %s\n",
strerror( errno ));
return BOOL_FALSE;
@@ -189,7 +189,7 @@ qlock_init( bool_t miniroot )
*/
qlock_usp = usinit( arenaname );
if ( ! qlock_usp ) {
- mlog( MLOG_NORMAL | MLOG_ERROR | MLOG_NOLOCK,
+ mlog( MLOG_NORMAL | MLOG_ERROR | MLOG_NOLOCK | MLOG_BARE,
"unable to allocate shared arena for thread locks: %s\n",
strerror( errno ));
return BOOL_FALSE;
@@ -225,7 +225,7 @@ qlock_thrdinit( void )
*/
rval = usadd( qlock_usp );
if ( rval ) {
- mlog( MLOG_NORMAL | MLOG_ERROR | MLOG_NOLOCK,
+ mlog( MLOG_NORMAL | MLOG_ERROR | MLOG_NOLOCK | MLOG_BARE,
"unable to add thread to shared arena: %s\n",
strerror( errno ));
return BOOL_FALSE;
@@ -311,7 +311,7 @@ qlock_lock( qlockh_t qlockh )
/* assert that this lock not already held
*/
if ( QLOCK_ORDMAP_GET( *ordmapp, qlockp->ql_ord )) {
- mlog( MLOG_NORMAL | MLOG_WARNING | MLOG_NOLOCK,
+ mlog( MLOG_NORMAL | MLOG_WARNING | MLOG_NOLOCK | MLOG_BARE,
"lock already held: thrd %d pid %d ord %d map %x\n",
thrdix,
pid,
@@ -323,7 +323,7 @@ qlock_lock( qlockh_t qlockh )
/* assert that no locks with a lesser ordinal are held by this thread
*/
if ( QLOCK_ORDMAP_CHK( *ordmapp, qlockp->ql_ord )) {
- mlog( MLOG_NORMAL | MLOG_WARNING | MLOG_NOLOCK,
+ mlog( MLOG_NORMAL | MLOG_WARNING | MLOG_NOLOCK | MLOG_BARE,
"lock ordinal violation: thrd %d pid %d ord %d map %x\n",
thrdix,
pid,
@@ -456,7 +456,7 @@ qsemP( qsemh_t qsemh )
*/
rval = uspsema( usemap );
if ( rval != 1 ) {
- mlog( MLOG_NORMAL | MLOG_ERROR | MLOG_NOLOCK,
+ mlog( MLOG_NORMAL | MLOG_ERROR | MLOG_NOLOCK | MLOG_BARE,
"unable to \"P\" semaphore: "
"rval == %d, errno == %d (%s)\n",
rval,
@@ -483,7 +483,7 @@ qsemV( qsemh_t qsemh )
*/
rval = usvsema( usemap );
if ( rval != 0 ) {
- mlog( MLOG_NORMAL | MLOG_ERROR | MLOG_NOLOCK,
+ mlog( MLOG_NORMAL | MLOG_ERROR | MLOG_NOLOCK | MLOG_BARE,
"unable to \"V\" semaphore: "
"rval == %d, errno == %d (%s)\n",
rval,
diff --git a/common/stream.c b/common/stream.c
index 8aff050d..bb80dbe7 100644
--- a/common/stream.c
+++ b/common/stream.c
@@ -34,19 +34,25 @@
#include <jdm.h>
#include "types.h"
+#include "exit.h"
#include "stream.h"
#include "lock.h"
+#include "mlog.h"
#define PROCMAX ( STREAM_SIMMAX * 2 + 1 )
+#define N(a) (sizeof((a)) / sizeof((a)[0]))
struct spm {
- bool_t s_busy;
- pid_t s_pid;
- intgen_t s_ix;
+ stream_state_t s_state;
+ pid_t s_pid;
+ intgen_t s_ix;
+ int s_exit_code;
+ rv_t s_exit_return;
+ rv_t s_exit_hint;
};
typedef struct spm spm_t;
-
+extern pid_t parentpid;
static spm_t spm[ STREAM_SIMMAX * 3 ];
void
@@ -64,79 +70,227 @@ stream_init( void )
}
+/*
+ * Note that the stream list structure (updated via the stream_* functions)
+ * is indexed by pid. Multiple processes can be registered against the same
+ * stream index, typically: the primary content process that does the work;
+ * and the drive slave process, which just processes stuff off the ring buffer.
+ * In general having multiple pids registered per stream is not an issue for
+ * termination status reporting, as the mlog_exit* logging functions only
+ * ever get called out of the primary content process.
+ */
+
void
stream_register( pid_t pid, intgen_t streamix )
{
spm_t *p = spm;
- spm_t *ep = spm + sizeof( spm ) / sizeof( spm[ 0 ] );
+ spm_t *ep = spm + N(spm);
ASSERT( streamix < STREAM_SIMMAX );
- lock( );
+ lock();
for ( ; p < ep ; p++ ) {
- if ( ! p->s_busy ) {
- p->s_busy = BOOL_TRUE;
+ if ( p->s_state == S_FREE ) {
+ p->s_state = S_RUNNING;
break;
}
}
unlock();
-
ASSERT( p < ep );
+
if ( p >= ep ) return;
p->s_pid = pid;
p->s_ix = streamix;
+ p->s_exit_code = -1;
+ p->s_exit_return = RV_NONE;
+ p->s_exit_hint = RV_NONE;
}
void
-stream_unregister( pid_t pid )
+stream_dead( pid_t pid )
{
spm_t *p = spm;
- spm_t *ep = spm + sizeof( spm ) / sizeof( spm[ 0 ] );
+ spm_t *ep = spm + N(spm);
- lock( );
- for ( ; p < ep ; p++ ) {
+ lock();
+ for ( ; p < ep ; p++ )
if ( p->s_pid == pid ) {
- p->s_pid = 0;
- p->s_ix = -1;
- p->s_busy = BOOL_FALSE;
+ p->s_state = S_ZOMBIE;
break;
}
- }
unlock();
-
ASSERT( p < ep );
}
-intgen_t
-stream_getix( pid_t pid )
+void
+stream_free( pid_t pid )
{
spm_t *p = spm;
- spm_t *ep = spm + sizeof( spm ) / sizeof( spm[ 0 ] );
+ spm_t *ep = spm + N(spm);
+ lock();
for ( ; p < ep ; p++ ) {
if ( p->s_pid == pid ) {
+ (void) memset( (void *) p, 0, sizeof(spm_t) );
+ p->s_state = S_FREE;
break;
}
}
+ unlock();
+ ASSERT( p < ep );
+}
+
+int
+stream_find_all( stream_state_t states[], int nstates,
+ pid_t pids[], int npids )
+{
+ int i, count = 0;
+ spm_t *p = spm;
+ spm_t *ep = spm + N(spm);
+ ASSERT(nstates > 0 && npids > 0);
+
+ /* lock - make sure we get a consistent snapshot of the stream status */
+ lock();
+ for ( ; p < ep && count < npids; p++ )
+ for (i = 0; i < nstates; i++)
+ if (p->s_state == states[i]) {
+ pids[count++] = p->s_pid;
+ break;
+ }
+ unlock();
+ return count;
+}
+
+static spm_t *
+stream_find( pid_t pid, stream_state_t s[], int nstates )
+{
+ int i;
+ spm_t *p = spm;
+ spm_t *ep = spm + N(spm);
+ static const char *state_strings[] = { "S_FREE", "S_RUNNING", "S_ZOMBIE" };
+
+ ASSERT(nstates > 0);
+
+ /* note we don't lock the stream array in this function */
+ for ( ; p < ep ; p++ )
+ if ( p->s_pid == pid ) {
+ /* check state */
+ for (i = 0; i < nstates; i++)
+ if (p->s_state == s[i])
+ return p;
+ }
+
+ mlog( MLOG_DEBUG | MLOG_ERROR | MLOG_NOLOCK | MLOG_BARE,
+ "stream_find(): no stream with pid: %d and state%s:",
+ pid, nstates == 1 ? "" : "s" );
+ for (i = 0; i < nstates; i++)
+ mlog( MLOG_DEBUG | MLOG_ERROR | MLOG_NOLOCK | MLOG_BARE,
+ " %s", state_strings[s[i]]);
+ mlog( MLOG_DEBUG | MLOG_ERROR | MLOG_NOLOCK | MLOG_BARE, "\n");
- return ( p >= ep ) ? -1 : p->s_ix;
+ return (spm_t *) NULL;
+}
+
+intgen_t
+stream_getix( pid_t pid )
+{
+ stream_state_t states[] = { S_RUNNING };
+ spm_t *p;
+ intgen_t ix;
+ lock();
+ p = stream_find( pid, states, N(states) );
+ ix = p ? p->s_ix : -1;
+ unlock();
+ return ix;
+}
+
+
+/*
+ * We don't currently export spm_t and don't really want callers
+ * keeping pointers into the stream array, so use explicit access
+ * functions. Note we only expect these to be called from running/owner
+ * streams.
+ */
+
+#define stream_set(field_name, pid, value) \
+ stream_state_t states[] = { S_RUNNING }; \
+ spm_t *p; \
+ pid_t mypid = getpid(); \
+ \
+ if (mypid != (pid)) { \
+ mlog( MLOG_DEBUG | MLOG_ERROR | MLOG_NOLOCK | MLOG_BARE,\
+ "stream_set_" #field_name "(): " \
+ "foreign stream (pid %d) " \
+ "not permitted to update this stream (pid %d)\n", \
+ mypid, (pid)); \
+ return; \
+ } \
+ \
+ lock(); \
+ p = stream_find( (pid), states, N(states) ); \
+ if (p) p->s_exit_ ## field_name = (value); \
+ unlock();
+
+void stream_set_code( pid_t pid, int exit_code )
+{
+ stream_set( code, pid, exit_code );
+}
+
+void stream_set_return( pid_t pid, rv_t rv )
+{
+ stream_set( return, pid, rv );
+}
+
+void stream_set_hint( pid_t pid, rv_t rv )
+{
+ stream_set( hint, pid, rv );
+}
+
+
+bool_t
+stream_get_exit_status( pid_t pid,
+ stream_state_t states[],
+ int nstates,
+ stream_state_t *state,
+ intgen_t *ix,
+ int *exit_code,
+ rv_t *exit_return,
+ rv_t *exit_hint)
+{
+ bool_t sts = BOOL_FALSE;
+ spm_t *p;
+
+ lock();
+ p = stream_find( pid, states, nstates );
+ if (! p) goto unlock;
+
+ if (state) *state = p->s_state;
+ if (ix) *ix = p->s_ix;
+ if (exit_code) *exit_code = p->s_exit_code;
+ if (exit_return) *exit_return = p->s_exit_return;
+ if (exit_hint) *exit_hint = p->s_exit_hint;
+ sts = BOOL_TRUE;
+
+ unlock:
+ unlock();
+ return sts;
}
size_t
stream_cnt( void )
{
spm_t *p = spm;
- spm_t *ep = spm + sizeof( spm ) / sizeof( spm[ 0 ] );
+ spm_t *ep = spm + N(spm);
size_t ixmap = 0;
size_t ixcnt;
size_t bitix;
ASSERT( sizeof( ixmap ) * NBBY >= STREAM_SIMMAX );
- lock( );
+ lock();
for ( ; p < ep ; p++ ) {
- if ( p->s_busy ) {
+ if ( p->s_state == S_RUNNING ) {
ixmap |= ( size_t )1 << p->s_ix;
}
}
diff --git a/common/stream.h b/common/stream.h
index 650c61e3..9b8b3b0d 100644
--- a/common/stream.h
+++ b/common/stream.h
@@ -45,15 +45,38 @@
#define STREAM_EXIT_STOP 1 /* thread requests a stop */
#define STREAM_EXIT_ABORT 2 /* thread requests an abort */
#define STREAM_EXIT_CORE 3 /* thread requests a core dump */
-
-extern void stream_init( void );
-extern void stream_register( pid_t pid, intgen_t streamix );
-extern void stream_unregister( pid_t pid );
+/* S_FREE: stream entry is available for use
+ * S_RUNNING: stream is actively dumping/restoring
+ * S_ZOMBIE: stream is dead but we're keeping it around because we want to
+ * know something about its termination condition.
+ */
-extern intgen_t stream_getix( pid_t pid );
+typedef enum { S_FREE, S_RUNNING, S_ZOMBIE } stream_state_t;
+extern void stream_init( void );
+extern void stream_register( pid_t pid, intgen_t streamix );
+extern void stream_dead( pid_t pid );
+extern void stream_free( pid_t pid );
+extern int stream_find_all( stream_state_t states[],
+ int nstates,
+ pid_t pids[],
+ int npids );
+extern intgen_t stream_getix( pid_t pid );
+extern void stream_set_code( pid_t pid, int code );
+extern void stream_set_return( pid_t pid, rv_t rv );
+extern void stream_set_hint( pid_t pid, rv_t rv );
+extern bool_t stream_exists( pid_t pid );
+extern bool_t stream_get_exit_status( pid_t pid,
+ stream_state_t states[],
+ int nstates,
+ stream_state_t *state,
+ intgen_t *ix,
+ int *exit_code,
+ rv_t *exit_return,
+ rv_t *exit_hint);
extern size_t stream_cnt( void );
+
#endif /* STREAM_H */
diff --git a/common/types.h b/common/types.h
index ec7eefab..9807dd74 100644
--- a/common/types.h
+++ b/common/types.h
@@ -104,7 +104,7 @@ typedef int bool_t;
/* useful return code scheme
*/
typedef enum { RV_OK, /* mission accomplished */
- RV_NOTOK, /* request denied */
+ RV_NOTOK, /* media erase request denied */
RV_NOMORE, /* no more work to do */
RV_EOD, /* ran out of data */
RV_EOF, /* hit end of media file */
@@ -117,8 +117,19 @@ typedef enum { RV_OK, /* mission accomplished */
RV_DRIVE, /* drive quit working */
RV_TIMEOUT, /* operation timed out */
RV_MEDIA, /* no media object in drive */
- RV_WRITEPOTECTED,/* want to write but write-protected */
- RV_CORE /* really bad - dump core! */
+ RV_PROTECTED, /* want to write but write-protected */
+ RV_CORE, /* really bad - dump core! */
+ RV_OPT, /* bad command line option */
+ RV_INIT, /* could not initialise subsystem */
+ RV_PERM, /* insufficient privilege */
+ RV_COMPAT, /* dump incompatible */
+ RV_INCOMPLETE, /* some part of the dump is incomplete */
+ RV_KBD_INTR, /* keyboard interrupt */
+ RV_INV, /* session inventory error */
+ RV_USAGE, /* print command usage only */
+ RV_NONE, /* no error code available */
+ RV_UNKNOWN, /* there was an error but we don't know which one */
+ _RV_NUM /* number of return codes */
} rv_t;
/* typedefs I'd like to see ...