diff options
author | Andrew Gildfind <ajag@sgi.com> | 2001-08-31 01:39:05 +0000 |
---|---|---|
committer | Andrew Gildfind <ajag@sgi.com> | 2001-08-31 01:39:05 +0000 |
commit | eaecf140a73d42680fcf85a4a38701b430e58f0b (patch) | |
tree | 9efbc531751774b32f7367294a04f9311051bba3 /common | |
parent | 55a4c7a45a08f55f8e3a96082a88c7f28a4290d4 (diff) | |
download | xfsdump-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.c | 2 | ||||
-rw-r--r-- | common/dlog.c | 2 | ||||
-rw-r--r-- | common/main.c | 95 | ||||
-rw-r--r-- | common/mlog.c | 349 | ||||
-rw-r--r-- | common/mlog.h | 7 | ||||
-rw-r--r-- | common/qlock.c | 14 | ||||
-rw-r--r-- | common/stream.c | 204 | ||||
-rw-r--r-- | common/stream.h | 33 | ||||
-rw-r--r-- | common/types.h | 17 |
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 ... |