/* Author(s): Amnon Shiloh for Mosix */ /* Adapted to openMosix from Mosix and bugfixing by David Santo Orcero */ /* irbis@orcero.org http://www.orcero.org/irbis */ /* Mosix is (c) of prof. Amnon Barak http://www.mosix.org */ /* Original code is (c) of prof. Amnon Barak http://www.mosix.org */ /* openMosix is (c) of Moshe Bar http://www.openmosix.com */ /* Each respective trademark is of its own owner */ /* All rights reserved. */ /* This software is distributed under GPL 2 */ /* THIS SOFTWARE IS PROVIDED "AS IS". NO WARRANTY IS ASSUMED. */ /* NO LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING */ /* FROM THE USE OF THIS SOFTWARE WILL BE ACCEPTED. IT CAN BURN */ /* YOUR HARD DISK, ERASE ALL YOUR DATA AND BROKE DOWN YOUR */ /* MICROWAVE OVEN. YOU ARE ADVISED. */ /* THIS SOFTWARE IS PROVIDED IN ITS "AS IS" CONDITION, WITH NO WARRANTY */ /* WHATSOEVER. NO LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING */ /* FROM THE USE OF THIS SOFTWARE WILL BE ACCEPTED. */ /* ** Modified on 7 April 2004 by Moreno 'baro' Baricevic ** ** ** This version of mosmon is independent of kernel headers. ** mosmon guesses the size of struct mosix_info (usually correctly!). ** The user can specify an arbitrary kernel version using the (new) command line ** option "-F ". Where version is in the range (2416-2425). ** ** Other fixes/changes: ** - new usage() routine; ** - help fixed; ** - most of the interactive keys have their command line counterpart; ** - log-load display patch (by Tony Travis ) ** has been merged as command line option "-L" and interactive key "L"; ** - ... */ /* ** Last modified on December 2004 by Jozef Ivanecky and Moreno Baricevic ** - ** ** - main() splitted into several subroutines; ** - multirows display (-12345). */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define PROGNAME "mosmon" #define PROGVER "2.1" #include "utils.h" #include "mosix_info.h" #define USE_LOG_LOAD // enable log-load display (-L option, L key) #ifdef USE_LOG_LOAD # include int log_load = 0; #endif /* USE_LOG_LOAD */ /************************************************************** ** map some ncurses keys () */ #define KEY_DOWN 0402 /* Sent by terminal down arrow key */ #define KEY_UP 0403 /* Sent by terminal up arrow key */ #define KEY_LEFT 0404 /* Sent by terminal left arrow key */ #define KEY_RIGHT 0405 /* Sent by terminal right arrow key */ #define KEY_HOME 0406 /* Sent by home key. */ #define KEY_NPAGE 0522 /* Sent by next-page key */ #define KEY_PPAGE 0523 /* Sent by previous-page key */ #define KEY_ENTER 0527 /* Enter/send (unreliable) */ #define KEY_A1 0534 /* Upper left of keypad */ #define KEY_A3 0535 /* Upper right of keypad */ #define KEY_B2 0536 /* Center of keypad */ #define KEY_C1 0537 /* Lower left of keypad */ #define KEY_C3 0540 /* Lower right of keypad */ #define KEY_END 0550 /* end key */ #define KEY_DC 0512 /* delete-character key */ #define KEY_IC 0513 /* insert-character key */ /************************************************************** ** first help string (basic help message). ** Should fit on 80x24 screen (-2 lines for "\nPress any key..."). */ #define HELP_STR1 \ "openMosix Load Monitor Version " PROGVER ", Based on code (c) by MOSIX GROUP HUJI.\n" \ "\n" \ "A load unit represents 100% CPU utilisation of a standard processor. If the\n" \ "speed of a CPU is the same as the yardstick, one load unit is equivalent\n" \ "to a load average of 1.0 as displayed by \"top\", \"mtop\" and \"uptime\".\n" \ "However, nodes with faster CPU's and/or SMP nodes with multiple CPU's\n" \ "will show a lower load and nodes with slower CPU's will show a higher load.\n" \ "\n" \ "For larger systems (with 10 or more nodes), node-numbers can be shown either\n"\ "horizontally (better looking) or vertically (to see more at a time).\n" \ "\n" \ "Help for Interactive Keys:\n" \ "\n" \ "q or Q Quit mosmon\n" \ "h or H or ? bring up this help screen\n" \ "\n" \ "w/v/V/a\t horizontal (wide), vertical, super-vertical (tight) or\n" \ " automatic selection of numbering (default auto)\n" \ "s display processor-speeds (also number of SMP CPU's)\n" \ "m display logically-used vs. total memory\n" \ "r display raw-used (non-free) memory vs. total memory\n" \ "" /************************************************************** ** second help string (additional keys) ** Should fit on 80x24 screen (-2 lines for "\nPress any key..."). */ #define HELP_STR2 \ "Additional keys:\n" \ "\n" \ "l display loads (default, 1.0 = 100% CPU)\n" \ "L toggle display of load on log scale (2.0 = 100% CPU)\n" \ "d show dead nodes (configured but not-responding)\n" \ "t toggle display of total operational node count\n" \ " (not recommended on very large clusters - will be very slow)\n" \ "y display the yardstick (i.e. speed of a standard processor)\n" \ "1/2/3/4/5 split display into 2, 3, 4 or 5 rows (default 1)\n" \ "\n" \ "Enter redraw the screen\n" \ "Insert force update after openMosix is restarted\n" \ "\n" \ "When not all nodes fit on the screen, press:\n" \ "\n" \ "Left/Right-Arrow or -/+ \n" \ " move one node to the left/right\n" \ "p/n move one screen to the left/right (previous/next)\n" \ "\n" \ "\n" \ "Try \"" PROGNAME " -h\" to see command line options or " \ "\"man " PROGNAME "\" for details.\n" \ "" /************************************************************** ** new types */ typedef struct { // Display: int dead; // dead nodes int vert; // vertical numbering int wide; // wide (horizontal) numbering int tot; // total number of operational nodes } disp_flag_s; /************************************************************** ** functions declaration */ void usage( const char * errmsg , ... ); void set_mode ( int i ); void get_npe( void ); void find_npe( void ); void adjust_first( void ); void ch_alarm( void ); void not_yet_ch( int ch ); int readc_half_second( void ); int my_getch( void ); int is_input( void ); void help( void ); void yardstick( void ); void cb( const char * msg , int y , int x ); void cb_ctr( const char * msg ); void onio( void ); void sleep_or_input( unsigned int secs ); void onint( int i ); /* ex-main() */ void process_args( int argc , char * argv[] , disp_flag_s * flg , int * need_count ); void initialization( const char * file ); void get_screen_size( void ); void get_total_counts( int * first_read , int * nread , int * tot_display , int * cpus_display , unsigned int turns ); void get_counts( int * first_read , int * nread , int * tot , int dflg , int tflg ); void calculate_screen_width( int * wid , int * ver , int vflg , int wflg , int tot ); void display_header( void ); void display_item_text( void ); void display_axes( int wid ); void display_total_info( int tot_display , int cpus_display ); void collect_nodes_stats( int * dead , int first_read , int nread , int dflg , int tflg , float * max ); void check_max_value( float * max , float * curmax ); NCURSES_CONST char * get_item_format( float max ); void display_x_labels( int ver , int wid , int dflg ); void display_y_labels( int ver , float max ); void display_bars( int ver , int wid , int dflg , int rev , char space , float max ); void check_input( disp_flag_s * flg , int * need_count , float * curmax ); /* ! openMosix */ static void not_mosix( void ) { fprintf( stderr , PROGNAME ": This is NOT a openMosix system!\n" ); exit( 1 ); } /* not_mosix */ /************************************************************** ** some useful macros */ #define PROC_HPC_SSPEED "/proc/hpc/admin/sspeed" #define COL_FROM_0 7 #define MAX_SILENT ( -5 ) #define VERY_DEAD ( MAX_SILENT - 1 ) #define N_STRLEN( _x ) ( 1 + ( _x > 9 ) + ( _x > 99 ) + ( _x > 999 ) + ( _x > 9999 ) ) /************************************************************** ** global variables */ int screen_width; int screen_cols = 80 , screen_rows = 24 ; int base; /* baseline location */ int rows = 1; /* number of rows (multirow display) */ //#define ROW_OFFSET( _row ) ( ( _row - 1 ) * ( base / rows ) ) /* constant */ #define ROW_OFFSET( _row ) ( base * ( _row - 1 ) / rows ) /* arrays numbering starts at 1 */ mosix_info_s info[MOSIX_MAX+1]; float load[MOSIX_MAX+1], other[MOSIX_MAX+1]; char valid[MOSIX_MAX+1]; enum { D_GETLOAD, D_GETSPEED, D_GETUTIL, D_GETMEM, D_GETBMEM } item = D_GETLOAD; int ifd = -1; /* input file */ int npe = 0; /* number of records */ int first = 1; /* first node displayed */ /************************************************************** ** mosix_info_* structures handling stuff */ int ( * readstruct )( int , mosix_info_s * , size_t ); int vernum = _UNKNOWN; int recsz = 0; char mstruct[16]; /************ * MAIN * ************/ int main( int argc , char * argv[] ) { disp_flag_s flg = { 0 , 0 , 0 , 0 }; int ver = 0 ; /* vertical display */ int wid ; /* width of nodes numbers */ int dead ; /* Number of dead nodes ... */ int last ; /* last node displayed */ int first_read , nread ; /* nodes */ int tot = 0 , tot_display = 0 , /* number of available nodes */ cpus_display = 0 ; /* total number of available cpus */ float max , curmax = 0 ; /* max value displayed (load/mem/speed/...) */ int need_count = 1 ; unsigned int turns = 0 ; /* turns from startup */ char space ; /* character used as vertical bar */ int rev ; /* reverse highlight (solid bar) */ #ifdef DEBUG stdreopen(); #endif process_args( argc , argv , &flg , &need_count ); initialization( PROC_HPC_INFOS ); while ( 1 ) // start neverending loop { // if oM un/configured while we are running, recheck for recsz and npe. if ( vernum == _UNKNOWN ) vernum = guess_by_recsz( ifd ) , get_npe(); first_read = nread = 0; /* get the new (if changed) win size */ get_screen_size(); if ( screen_cols != COLS || screen_rows != LINES ) { if ( is_term_resized( screen_rows , screen_cols ) ) { DBGprint( "resizing window to %dx%d" , screen_cols , screen_rows ); resizeterm( screen_rows , screen_cols ); } initscr(); refresh(); need_count = 1; vernum = _UNKNOWN; // force reload (previous acquisition maybe interrupted by SIGWINCH) continue; } if ( flg.tot ) get_total_counts( &first_read , &nread , &tot_display , &cpus_display , turns ); if ( need_count && ! flg.vert && ! flg.wide ) get_counts( &first_read , &nread , &tot , flg.dead , flg.tot ) , need_count = 0; calculate_screen_width( &wid , &ver , flg.vert , flg.wide , tot ); display_item_text(); display_axes( wid ); if ( flg.tot ) display_total_info( tot_display , cpus_display ); max = 0; dead = 0; /* number of not responding machines */ turns++; collect_nodes_stats( &dead , first_read , nread , flg.dead , flg.tot , &max ); check_max_value( &max , &curmax ); display_y_labels( ver , max ); last = first + screen_width + dead - 1; if ( npe < last ) last = npe; if ( wid == 0 && 2 * ( last - first + 1 - dead ) <= screen_width ) wid = 1; if ( wid == 0 ) space = '|' , rev = 0; else space = ' ' , rev = 1; if ( rev ) standout(); if ( max ) display_bars( ver , wid , flg.dead , rev , space , max ); if ( rev ) standend(); display_x_labels( ver , wid , flg.dead ); /* Nodes numbers */ if ( turns % 60 == 0 ) get_npe(); /* ** screen's out, check for interactive keys */ sleep_or_input( 1 ); check_input( &flg , &need_count , &curmax ); } /* neverending loop */ } /* main */ /******************************************************************** *_____________________________ USAGE _____________________________* ********************************************************************/ /* ** ...does something useful ** should fit on 80x24 screen (error + help + prompt) */ #define BELL { if ( isatty( STDERR_FILENO ) ) putc( '\a' , stderr ); } void usage ( const char * errmsg , ... ) { if ( errmsg && *errmsg ) { va_list args; BELL; fprintf( stderr , "*** Usage error: " ); va_start( args , errmsg ); vfprintf( stderr , errmsg , args ); va_end( args ); putc( '\n' , stderr ); } fprintf ( stderr , "\n" "Usage: %s [options]\n" "\n" " -w horizontal (wide) numbering\n" " -v vertical numbering\n" " -V super-vertical (tight) numbering\n" " -a automatic selection of numbering (default)\n" "\n" " -s show CPU speed (10,000 = 400MHz Pentium-2)\n" " -m show memory (used out of total)\n" " -r show memory (raw used/free out of total)\n" " -u show utilizability (%%)\n" " -l show load (default, 1.0 = 100%% standard CPU)\n" #ifdef USE_LOG_LOAD " -L show load (log scale, 2.0 = 100%% standard CPU)\n" #endif /* USE_LOG_LOAD */ // "\n" " -d show dead nodes (configured but not-responding)\n" " -t show total number of operational nodes\n" // " (not recommended on very large clusters, will\n" // " be very slow)\n" " -[12345] split display into 2, 3, 4 or 5 rows (default 1)\n" // "\n" " +NODE_NUMBER begin the display at a particular node-number\n" "\n" " -F KVER force kernel version instead of guessing record size\n" // " (KVER = [2416|...|2419|2420|2421|2422|2423|...])\n" "\n" " -h|-H display this message\n" // "\n" // "Try 'man %s' for details.\n" "\n" , PROGNAME // , PROGNAME ); exit( errmsg ? 1 : 0 ); } /* usage */ /*************************************************************************** *_____________________________ PROCESS_ARGS _____________________________* ***************************************************************************/ /* ** command line options parser */ void process_args( int argc , char * argv[] , disp_flag_s * flg , int * need_count ) { register int j = 0 ; while ( argc > 1 && ( argv[1][0] == '-' || argv[1][0] == '+' ) ) { if ( argv[1][0] == '+' ) { if ( ! argv[1][1] ) usage( "+: NODE_NUMBER missing" ); first = strtoi( &argv[1][1] ); if ( errno == EINVAL ) usage( "+: invalid argument \"%s\"" , &argv[1][1] ); if ( errno == ERANGE ) usage( "+: argument out of range \"%s\"" , &argv[1][1] ); if ( first < 1 || first > MOSIX_MAX ) usage( "+: NODE_NUMBER out of range (1-%d) \"%s\"" , MOSIX_MAX , &argv[1][1] ); } else { for ( j = 1 ; argv[1][j] ; j++ ) { switch ( argv[1][j] ) { default: usage( "unrecognized option '%c' in argument \"%s\"" , argv[1][j] , argv[1] ); case 'h': case 'H': usage( NULL ); //--------------------------------- case 'v': flg->vert = 1; flg->wide = 0; break; case 'V': flg->vert = 2; flg->wide = 0; break; case 'w': flg->vert = 0; flg->wide = 1; break; case 'a': flg->wide = flg->vert = 0; *need_count = 1; break; //--------------------------------- case 's': item = D_GETSPEED; break; case 'm': item = D_GETMEM; break; case 'r': item = D_GETBMEM; break; case 'u': item = D_GETUTIL; break; #ifdef USE_LOG_LOAD case 'L': log_load = 1; // fall through #endif /* USE_LOG_LOAD */ case 'l': item = D_GETLOAD; break; //--------------------------------- case 't': flg->tot = 1; break; case 'd': flg->dead = 1; break; //--------------------------------- case '1': case '2': case '3': case '4': case '5': rows = argv[1][j] - '0' ; break; //--------------------------------- case 'F': if ( ! argv[2] || ! *argv[2] ) usage( "-%c: missing argument" , argv[1][j] ); { int struct_ver = strtoi( argv[2] ); if ( errno == EINVAL ) usage( "-%c: invalid argument \"%s\"" , argv[1][j] , argv[2] ); if ( errno == ERANGE ) usage( "-%c: argument out of range \"%s\"" , argv[1][j] , argv[2] ); vernum = force_version( struct_ver ); if ( vernum == _UNKNOWN ) // your fault, not mine usage( "-%c: unknown version \"%s\"" , argv[1][j] , argv[2] ); fprintf( stderr , "force using struct %s\n" , mstruct ); argc--; argv++; goto skip_arg; } } } } skip_arg: argc--; argv++; } if ( argc > 1 ) usage( "too many arguments [%s]" , argv[1] ); } /* process_args */ /***************************************************************************** *_____________________________ INITIALIZATION _____________________________* *****************************************************************************/ void initialization( const char * file ) { DBGprint( "using file \"%s\" ..." , file ); ifd = open( file , O_RDONLY ); if ( ifd == -1 ) not_mosix(); #ifdef DEBUG summary(); #endif /* DEBUG */ if ( vernum == _UNKNOWN ) vernum = guess_by_recsz( ifd ); DBGprint( "vernum = %d" , vernum ); DBGprint( "recsz = %d" , recsz ); /* // if ( vernum == _UNKNOWN ) // die( "*** unsupported record" ); ** No. Continue, openMosix can be configured later ** (DUMMY struct saves us from segfault) */ get_npe(); // get the number of records from PROC_HPC_INFOS #if 0 { register int i = 0; while ( i <= npe ) load[i++] = 0.0; } #else memset( load , 0 , ( 1 + npe ) * sizeof( float ) ); #endif /* ** ok, start ncurses ** trap some signals in order to clear the screen before exit. */ signal( SIGINT , (sig_t)onint ); signal( SIGQUIT , (sig_t)onint ); initscr(); noecho(); cbreak(); system( "stty cbreak -echo" ); /* ** trap also SIGWINCH since it could interrupt guess_by_recsz and confuse ** the user with a "openMosix not configured" message. ** Window change is handled by main() trough get_screen_size() and ** resizeterm(). */ signal( SIGWINCH , SIG_IGN ); } /* initialization */ /****************************************************************************** *_____________________________ GET_SCREEN_SIZE _____________________________* ******************************************************************************/ #ifdef DEBUG # define XY( msg ) \ do { \ DBGprint( "%10.10s %d x %d - %d x %d" \ , #msg \ , screen_cols , screen_rows \ , COLS , LINES \ ); \ } while ( 0 ); #else # define XY(_) #endif void get_screen_size( void ) { struct winsize ws ; /* window size structure (termios) */ if ( ioctl( STDOUT_FILENO , TIOCGWINSZ , &ws ) != -1 && ws.ws_col > 0 && ws.ws_row > 0 ) { screen_cols = ws.ws_col; screen_rows = ws.ws_row; XY( "ioctl" ); } #if 1 else // use ncurses size { screen_cols = COLS; screen_rows = LINES; XY( "curses" ); } #else //{ if ( screen_cols == 0 || screen_rows == 0 ) { screen_cols = tgetnum( "co" ); screen_rows = tgetnum( "li" ); XY( "tgetnum" ); } if ( screen_cols == 0 || screen_rows == 0 ) { char *columns , *lines ; /* Unix98 environment variables */ columns = getenv( "COLUMNS" ); if ( columns && *columns ) { screen_cols = strtoi( columns ); if ( errno == EINVAL || screen_cols == 0 ) screen_cols = 80; } lines = getenv( "LINES" ); if ( lines && *lines ) { screen_rows = strtoi( lines ); if ( errno == EINVAL || screen_rows == 0 ) screen_rows = 24; } XY( "getenv" ); } #endif //} if ( screen_cols == 0 || screen_rows == 0 ) fprintf( stderr , "No luck! Screen size %dx%d\n" , screen_cols , screen_rows ) , onint( 0 ); } /* get_screen_size */ #undef XY /******************************************************************************* *_____________________________ GET_TOTAL_COUNTS _____________________________* *******************************************************************************/ void get_total_counts( int * first_read , int * nread , int * tot_display , int * cpus_display , unsigned int turns ) { register int i , j ; memset( &info[1] , 0 , npe * RECSZ_STD ); // clear records buffer (void)lseek( ifd , 0L , SEEK_SET ); // goto start of file j = readstruct( ifd , &info[1] , npe ); // read the whole file content DBGprint( "j = %d" , j ); if ( j < npe ) { npe = j; adjust_first(); } *first_read = 1; *nread = npe; /* ** check for invalid records (dead nodes) */ for ( *tot_display = 0 , *cpus_display = 0 , i = 1 ; i <= npe ; i++ ) { if ( info[i].status & DS_MOSIX_UP ) { (*tot_display)++; (*cpus_display) += info[i].ncpus; valid[i] = 1; load[i] = -1; } else if ( ( info[i].status & DS_MOSIX_DEF ) && valid[i] > ( turns >= -MAX_SILENT ? MAX_SILENT : -(int64_t)turns ) ) { (*tot_display)++; (*cpus_display) += info[i].ncpus; } } } /* get_total_counts */ /************************************************************************* *_____________________________ GET_COUNTS _____________________________* *************************************************************************/ void get_counts( int * first_read , int * nread , int * tot , int dflg , int tflg ) { register int i , j , k ; int last ; get_npe(); k = ( screen_cols - COL_FROM_0 - 1 ) + 1; *tot = 0; /* we only need to answer whether there are at least k * nodes up ahead or not: once we have the answer, * stop reading. Of course, with "tflg" we have already * read everything */ *first_read = first; if ( ! tflg ) *nread = 0; for ( i = first ; i <= npe && *tot < k ; i += k ) { last = i + k - 1; if ( last > npe ) last = npe; if ( ! tflg ) { (void)lseek( ifd , i - 1 , SEEK_SET ); j = readstruct( ifd , &info[i] , ( last - i + 1 ) ); if ( j < ( last - i + 1 ) ) last = npe = i + j - 1; *nread = last + 1 - first; } for ( j = i ; j <= last && *tot < k ; j++ ) if ( info[j].status & ( dflg ? DS_MOSIX_DEF : DS_MOSIX_UP ) ) (*tot)++; } } /* get_counts */ /************************************************************************************* *_____________________________ CALCULATE_SCREEN_WIDTH _____________________________* *************************************************************************************/ void calculate_screen_width( int * wid , int * ver , int vflg , int wflg , int tot ) { int k ; /* calculate the screen_width */ *wid = N_STRLEN( npe ); k = ( screen_cols - COL_FROM_0 - 1 ) / ( *wid + 1 ); DBGprint( "-- %d -- %d -- %d -- %d --" , *wid , *ver , vflg , screen_width ); if ( vflg ) { vert: base = screen_rows - 2 - *wid; *ver = 1; if ( vflg == 2 ) { *wid = 0; screen_width = ( screen_cols - COL_FROM_0 - 1 ); } else { *wid = 1; screen_width = ( screen_cols - COL_FROM_0 - 1 ) / 2; } adjust_first(); } else if ( wflg || k >= tot || ( screen_cols - COL_FROM_0 - 1 ) < tot ) { *ver = 0; screen_width = k; base = screen_rows - 3; } else goto vert; DBGprint( "++ %d ++ %d ++ %d ++ %d ++" , *wid , *ver , vflg , screen_width ); } /* calculate_screen_width */ /***************************************************************************** *_____________________________ DISPLAY_HEADER _____________________________* *****************************************************************************/ #define LONG_HEADER "openMosix monitor" #define SHORT_HEADER "mosmon" void display_header ( void ) { // static const int lh = strlen( LONG_HEADER ); // static const int sh = strlen( SHORT_HEADER ); // sizeof() makes gcc happy (avoid "initializer element is not constant" warning) static const int lh = sizeof( LONG_HEADER ) - 1 ; static const int sh = sizeof( SHORT_HEADER ) - 1 ; int header = screen_cols - COL_FROM_0 - 1; if ( header > lh ) { move( 0 , COL_FROM_0 + 1 + ( header >> 1 ) - ( lh >> 1 ) ); addstr( LONG_HEADER ); } else if ( header > sh ) { move( 0 , COL_FROM_0 + 1 + ( header >> 1 ) - ( sh >> 1 ) ); addstr( SHORT_HEADER ); } } /* display_header */ #undef LONG_HEADER #undef SHORT_HEADER /******************************************************************************** *_____________________________ DISPLAY_ITEM_TEXT _____________________________* ********************************************************************************/ void display_item_text( void ) { int i , j ; const char * ylabel; move( 0 , 0 ); clrtobot(); display_header(); if ( item == D_GETMEM || item == D_GETBMEM ) { if ( item == D_GETBMEM ) { ylabel = "Raw Used Memory"; i = base / 2 - 8; if ( i < 1 ) i = 1; } else { ylabel = "Used Memory"; i = base / 2 - 6; j = 1; } for ( ; *ylabel ; i++ ) { move( i , 0 ); addch( (chtype)*ylabel++ ); } while ( i == base / 4 || i == base / 2 || i == base * 3 / 4 ) i++; if ( i < base ) { move( 0 , 2 ); addstr( "(MB)" ); } } else if ( item == D_GETLOAD ) { #ifdef USE_LOG_LOAD int n = ( log_load ) ? 7 : 3 ; ylabel = ( log_load ) ? "LOG LOAD" : "LOAD" ; #else int n = 3; ylabel = "LOAD"; #endif /* USE_LOG_LOAD */ for ( i = base / 2 - n ; *ylabel ; i += 2 ) { move( i , 0 ); addch( (chtype)*ylabel++ ); } } else if ( item == D_GETSPEED ) { ylabel = "SPEED"; for ( i = base / 2 - 4 ; *ylabel ; i += 2 ) { move( i , 0 ); addch( (chtype)*ylabel++ ); } } else { ylabel = "Utilizability"; for ( i = base / 2 - 7 ; *ylabel ; i++ ) { move( i , 0 ); addch( (chtype)*ylabel++ ); } } } /* display_item_text */ /*************************************************************************** *_____________________________ DISPLAY_AXES _____________________________* ***************************************************************************/ void display_axes( int wid ) { register int i; int r; for ( i = 0 ; i < base ; i++ ) { move( i , COL_FROM_0 ); addch( '|' ); } for ( r = 1 ; r <= rows ; r++ ) { move( base - ROW_OFFSET( r ) , COL_FROM_0 ); for ( i = screen_width * ( wid + 1 ) + 1 ; i > 0 ; i-- ) addch( '-' ); } move( screen_rows - 2 , 0 ); addstr( "Node #" ); } /* display_axes */ /********************************************************************************* *_____________________________ DISPLAY_TOTAL_INFO _____________________________* *********************************************************************************/ void display_total_info( int tot_display , int cpus_display ) { move( screen_rows - 1 , 0 ); if ( tot_display ) printw( "[Total %d] [CPUs %d]" , tot_display , cpus_display ); else printw( "[openMosix Not Configured]" ); } /* display_total_info */ /********************************************************************************** *_____________________________ COLLECT_NODES_STATS _____________________________* **********************************************************************************/ void collect_nodes_stats( int * dead , int first_read , int nread , int dflg , int tflg , float * max ) { register int i , j ; if ( ! tflg ) (void)lseek( ifd , first - 1 , SEEK_SET ); for ( i = first ; ( i < ( first + screen_width * rows + *dead ) ) && ( i <= npe ) ; i++ ) { /* if we receive 0 we don't remove the load of the process completely: only divide it by two */ if ( ! ( ( i >= first_read ) && ( i < first_read + nread ) ) ) { /* try to compute optimal read-ahead (not easy) */ if ( ! nread ) first_read = i; /* guess same # of dead as already found */ j = first + screen_width * rows + *dead + *dead - i; if ( j > npe - i + 1 ) j = npe - i + 1; j = readstruct( ifd , &info[i] , j ); if ( j <= 0 ) { npe = i - 1; break; } nread += j; } if ( info[i].status & DS_MOSIX_UP ) { valid[i] = 1; switch ( item ) { case D_GETLOAD: #ifdef USE_LOG_LOAD load[i] = ( log_load ) ? ( ( info[i].load > 0 ) ? log10( info[i].load ) : 0 ) : ( info[i].load / 100.0 ) ; #else load[i] = info[i].load / 100.0; #endif /* USE_LOG_LOAD */ other[i] = info[i].ncpus; break; case D_GETMEM: load[i] = info[i].tmem / 1048576.0; other[i] = load[i] - info[i].mem / 1048576.0; break; case D_GETBMEM: load[i] = info[i].tmem / 1048576.0; other[i] = load[i] - info[i].rmem / 1048576.0; break; case D_GETSPEED: load[i] = info[i].speed; other[i] = info[i].ncpus; break; case D_GETUTIL: load[i] = info[i].util / info[i].ncpus; break; } } else if ( ! ( info[i].status & DS_MOSIX_DEF ) ) load[i] = valid[i] = VERY_DEAD; else if ( valid[i] <= MAX_SILENT + 1 ) load[i] = valid[i] = MAX_SILENT; else { valid[i]--; if ( load[i] < 0 ) load[i] = 0; } if ( load[i] < 0 ) { load[i] = ( valid[i] < 0 ) ? valid[i] : -1; if ( load[i] <= ( dflg ? VERY_DEAD : MAX_SILENT ) ) (*dead)++; } #ifdef USE_LOG_LOAD # if 0 if ( load[i] > *max ) { if ( log_load && item == D_GETLOAD ) *max = 4; else *max = load[i]; } # else if ( log_load && item == D_GETLOAD ) *max = 4; // when log_load max must be always 4 else if ( load[i] > *max ) *max = load[i]; # endif #else if ( load[i] > *max ) *max = load[i]; #endif /* USE_LOG_LOAD */ } if ( tflg ) { for ( ; i <= npe ; i++ ) if ( ! ( info[i].status & DS_MOSIX_DEF ) ) valid[i] = VERY_DEAD; else if ( valid[i] > MAX_SILENT && ! ( info[i].status & DS_MOSIX_UP ) ) valid[i]--; for ( i = first - 1 ; i > 0 ; i-- ) if ( ! ( info[i].status & DS_MOSIX_DEF ) ) valid[i] = VERY_DEAD; else if ( valid[i] > MAX_SILENT && ! ( info[i].status & DS_MOSIX_UP ) ) valid[i]--; } } /* collect_nodes_stats */ /****************************************************************************** *_____________________________ CHECK_MAX_VALUE _____________________________* ******************************************************************************/ void check_max_value( float * max , float * curmax ) { static int cool; if ( *max < 1 ) { if ( *max == 0 && item == D_GETLOAD ) /* idle */ { standout(); move( base - 1 , 2 ); addstr( "IDLE" ); standend(); } *max = 1; } if ( *max >= *curmax ) { *curmax = *max; cool = 0; } else { if ( cool++ >= 3 ) *curmax = *max; *max = *curmax; } } /* check_max_value */ /****************************************************************************** *_____________________________ GET_ITEM_FORMAT _____________________________* ******************************************************************************/ NCURSES_CONST char * get_item_format( float max ) { switch ( item ) { case D_GETLOAD: return "%5.2f"; case D_GETSPEED: return "%5.0f"; case D_GETUTIL: return "%4.0f%%"; case D_GETMEM: // fall through case D_GETBMEM: return ( max >= 999.0 ) ? "%5.0f" : "%5.3g"; default: return "%f"; // WTF! // should never happen } } /* get_item_format */ /******************************************************************************* *_____________________________ DISPLAY_X_LABELS _____________________________* *******************************************************************************/ void display_x_labels( int ver , int wid , int dflg ) { register int i , j , k; int r ; int dead = 0 , prevdead = 0 ; for ( r = 1 ; r <= rows ; r++ ) { if ( ver ) { for ( j = 10 , k = screen_rows - 2 ; k > base ; k-- , j *= 10 ) { move( k - ROW_OFFSET( r ) , COL_FROM_0 + 1 ); for ( i = first + screen_width * ( r - 1 ) + prevdead , dead = 0 ; i < ( first + screen_width * r + dead + prevdead ) && i <= npe ; i++ ) { if ( load[i] > ( dflg ? VERY_DEAD : MAX_SILENT ) ) { if ( wid ) addch( ' ' ); addch( (chtype)( '0' + i % j / ( j / 10 ) ) ); } else dead++; } } prevdead += dead ; } else { move( base + 1 - ROW_OFFSET( r ) , COL_FROM_0 + 1 ); for ( i = first + screen_width * ( r - 1 ) + prevdead , dead = 0 ; i < ( first + screen_width * r + dead + prevdead ) && i <= npe ; i++ ) { if ( load[i] > ( dflg ? VERY_DEAD : MAX_SILENT ) ) { if ( i <= 9 && wid % 2 == 0 ) printw( "%*s%d%*s" , 1 + wid / 2 , "" , i , wid / 2 - 1 , "" ); else if ( i >= 100 && i <= 999 && wid % 2 == 0 ) printw( "%*s%d%*s" , wid / 2 , "" , i , wid / 2 - 2 , "" ); else { j = wid - N_STRLEN( i ); printw( "%*s%d%*s" , j / 2 + 1 , "" , i , ( j + 1 ) / 2 , "" ); } } else dead++; } prevdead += dead ; } } move( base , 79 ); refresh(); } /* display_x_labels */ /******************************************************************************* *_____________________________ DISPLAY_Y_LABELS _____________________________* *******************************************************************************/ void display_y_labels( int ver , float max ) { NCURSES_CONST char * fmt = get_item_format( max ); int j ; int ors ; /* One Row Size */ int rbase ; int vh; if ( rows == 1 ) vh = 1 ; /* 1 row, no 'vh' space for nodes numbers is needed ... */ else if ( ! ver ) vh = 2 ; else vh = 1 + N_STRLEN( npe ); ors = base / rows - vh ; /* We need to consider a space 'vh' for nodes numbers */ for ( j = 1 ; j <= rows ; j++ ) { rbase = base - ROW_OFFSET( j ); if ( max > 0 ) { // move( rbase - ors + 1 , 1 ); move( rbase - ors , 1 ); printw( fmt , max ); } if ( ors > 4 && max > 0 ) { move( rbase - ( ors >> 1 ) , 1 ); printw( fmt , max / 2 ); } if ( ors > 8 && max > 0 ) { move( rbase - ( ors >> 1 ) - ( ors >> 2 ) , 1 ); printw( fmt , max * 3 / 4 ); move( rbase - ( ors >> 1 ) + ( ors >> 2 ) , 1 ); printw( fmt , max / 4 ); } move( rbase , 5 ); addch( '0' ); } } /* display_y_labels */ /*************************************************************************** *_____________________________ DISPLAY_BARS _____________________________* ***************************************************************************/ void display_bars( int ver , int wid , int dflg , int rev , char space , float max ) { register int i , k , ri ; int col = 0 ; int l , r , dead , prevdead = 0; int rbase ; int vh; int disp_h; if ( rows == 1 ) vh = 1 ; /* 1 row, no 'vh' space for nodes numbers is needed ... */ else if ( ! ver ) vh = 2 ; else vh = 1 + N_STRLEN( npe ); disp_h = base / rows - vh ; for ( r = 1 ; r <= rows ; r++ ) { rbase = base - base * ( r - 1 ) / rows ; dead = 0; for ( i = first + screen_width * ( r - 1 ) + prevdead , ri = 0 ; i < ( first + screen_width * r + dead + prevdead ) && i <= npe ; i++ , ri++ ) { if ( load[i] > 0 ) { col = COL_FROM_0 + 1 + ( wid != 0 ) + wid / 2 + ( wid + 1 ) * ri ; l = rbase - ( load[i] * ( base / rows - vh ) ) / max + 0.5; if ( item == D_GETMEM || item == D_GETBMEM ) { k = rbase - ( other[i] * ( rbase / rows - vh ) ) / max + 0.5; if ( rev ) standend(); for ( ; l < k ; l++ ) { move( l , col ); addch( (chtype)( valid[i] > 0 ? '+' : '?' ) ); } if ( rev ) standout(); } if ( item == D_GETSPEED && other[i] > 1 ) space = '0' + other[i]; for ( ; l < rbase ; l++ ) { move( l , col ); addch( (chtype)( valid[i] > 0 ? space : '?' ) ); } } else if ( load[i] < 0 ) { if ( load[i] <= ( dflg ? VERY_DEAD : MAX_SILENT ) ) { dead++; ri--; continue; } col = COL_FROM_0 + 1 + ( wid != 0 ) + wid / 2 + ( wid + 1 ) * ri; if ( rev ) standend(); if ( disp_h > 3 ) { move( rbase - 4 , col ); addch( 'D' ); move( rbase - 3 , col ); addch( 'E' ); move( rbase - 2 , col ); addch( 'A' ); move( rbase - 1 , col ); addch( 'D' ); } else { move( rbase - 1 , col ); addch( 'D' ); } if ( rev ) standout(); } } prevdead += dead ; } } /* display_bars */ /************************************************************************** *_____________________________ CHECK_INPUT _____________________________* **************************************************************************/ void check_input( disp_flag_s * flg , int * need_count , float * curmax ) { if ( is_input() ) { int c = my_getch(); // must be int (KEY_* are greater than 255) switch ( c ) { //-- - -- - -- - -- - -- - -- - case '\2': case KEY_HOME: //#define __CB(m) cb(m,4,9) #define __CB(m) cb_ctr(m) __CB( "The openMosix Group" ); __CB( " presents... " ); __CB( PROGNAME " version " PROGVER "!" ); #undef __CB break; case 'q': case 'Q': onint( 0 ); case '\14': case '\r': case '\n': clear(); refresh(); break; case '?': case 'h': case 'H': help(); break; //-- - -- - -- - -- - -- - -- - case 'a': case 'A': flg->wide = flg->vert = 0; *need_count = 1; break; case 'v': flg->vert = 1; flg->wide = 0; break; case 'V': flg->vert = 2; flg->wide = 0; break; case 'w': case 'W': flg->vert = 0; flg->wide = 1; break; //-- - -- - -- - -- - -- - -- - case 't': flg->tot = !flg->tot; break; case 'd': case 'D': flg->dead = !flg->dead; break; case 'y': case 'Y': yardstick(); break; //-- - -- - -- - -- - -- - -- - case '1': case '2': case '3': case '4': case '5': rows = (char)c - '0' ; break; //-- - -- - -- - -- - -- - -- - case 's': case 'S': *curmax = 0 ; set_mode( D_GETSPEED ); break; case 'm': case 'M': *curmax = 0 ; set_mode( D_GETMEM ); break; #ifdef USE_LOG_LOAD case 'L': log_load = !log_load; // fall through #endif /* USE_LOG_LOAD */ case 'l': *curmax = 0 ; set_mode( D_GETLOAD ); break; case 'u': case 'U': *curmax = 0 ; set_mode( D_GETUTIL ); break; case 'r': case 'R': *curmax = 0 ; set_mode( D_GETBMEM ); break; //-- - -- - -- - -- - -- - -- - case '+': case KEY_RIGHT: if ( first + screen_width - 1 < npe ) first++; break; case '-': case KEY_LEFT: if ( first > 1 ) { *need_count = 1; first--; } break; case 'n': //next case 'N': first += screen_width; if ( first + screen_width - 1 > npe ) { first = npe + 1 - screen_width; if ( first < 1 ) first = 1; } break; case 'p': //previous case 'P': case 'b': //back? case 'B': if ( first ) *need_count = 1; if ( first > screen_width ) first -= screen_width; else first = 1; break; //-- - -- - -- - -- - -- - -- - case KEY_IC: DBGprint( "interactive key INSERT, force update" ); vernum = guess_by_recsz( ifd ) , get_npe(); break; //-- - -- - -- - -- - -- - -- - default: write( STDOUT_FILENO , "\a" , 1 ); } } /* is_input */ } /* check_input */ /*********************************************************************** *_____________________________ SET_MODE _____________________________* ***********************************************************************/ /* ** set item and clear old mode (reuse argument as counter) */ void set_mode ( int i ) { item = i; for ( i = 1 ; i <= npe ; i++ ) load[i] = MAX_SILENT - 1; } /* set_mode */ /********************************************************************** *_____________________________ GET_NPE _____________________________* **********************************************************************/ /* ** PROC_HPC_INFOS is a special proc file. ** On this file, lseek() jumps across records instead of bytes. ** ** Note that on "normal" files lseek() jumps across bytes, ** on proc files it usually does not work (EINVAL). ** ** By jumping directly to end of file, we get the total number of ** records stored inside the file. No more needs for find_npe(). ** ** npe may be 0 when oM unconfigured (empty file) */ void get_npe ( void ) { npe = (int)lseek( ifd , 0L , SEEK_END ); // goto EOF (void)lseek( ifd , 0L , SEEK_SET ); // goto SOF if ( npe == -1 ) npe = 0; DBGprint( "npe = %d" , npe ); if ( ! npe ) // give us a second chance, just in case... { find_npe(); DBGprint( "npe = %d" , npe ); } } /* get_npe */ /*********************************************************************** *_____________________________ FIND_NPE _____________________________* ***********************************************************************/ /* superseded by get_npe() */ /* ** Computes the number of records on /proc/hpc/info/infos. ** Note that this is a PROC_HPC_INFOS specific implementation, on normal ** file lseek() jumps across bytes, not records... */ void find_npe ( void ) { register int i ; void * dummy = xcmalloc( recsz ); // oM version specific, size may change int onpe = npe ; for ( npe = i = 1 ; npe <= MOSIX_MAX ; i = npe , npe += npe ) { lseek( ifd , npe - 1 , SEEK_SET ); if ( read( ifd , dummy , recsz ) != recsz ) break ; } while ( npe > i + 1 ) { lseek( ifd , ( i + npe ) / 2 - 1 , SEEK_SET ); if ( read( ifd , dummy , recsz ) != recsz ) npe = ( i + npe ) / 2 ; else i = ( i + npe ) / 2 ; } npe = i ; for ( i = onpe + 1 ; i <= npe ; i++ ) load[i] = valid[i] = 0 ; free( dummy ) ; DBGprint( "npe = %d" , npe ); adjust_first(); } /* find_npe */ #if 0 //{ /* ** old/original version of find_npe(), adapted above, superseded by get_npe() */ void find_npe() { register int i; struct mosix_info x; int onpe = npe; for(npe = i = 1 ; npe <= MOSIX_MAX ; i = npe , npe += npe) { lseek(ifd, npe-1, 0); if(read(ifd, &x, RECSZ) != RECSZ) break; } while(npe > i+1) { lseek(ifd, (i+npe)/2 - 1, 0); if(read(ifd, &x, RECSZ) != RECSZ) npe = (i+npe)/2; else i = (i+npe)/2; } npe = i; for(i = onpe+1 ; i <= npe ; i++) load[i] = valid[i] = 0; adjust_first(); } #endif //} /*************************************************************************** *_____________________________ ADJUST_FIRST _____________________________* ***************************************************************************/ /* ** which is the first node to display ? */ void adjust_first ( void ) { if ( first >= npe ) first = npe; if ( first < 1 ) first = 1; } /* adjust_first */ /* my_getch() - input stuff: */ char esc_no_wait; int un_char, las_char; /*********************************************************************** *_____________________________ CH_ALARM _____________________________* ***********************************************************************/ void ch_alarm ( void ) { signal( SIGALRM , (sig_t)ch_alarm ); } /* ch_alarm */ /************************************************************************* *_____________________________ NOT_YET_CH _____________________________* *************************************************************************/ void not_yet_ch ( int ch ) { if ( ch ) un_char = ch; else if ( las_char && las_char != ERR ) un_char = las_char; } /* not_yet_ch */ /******************************************************************************** *_____________________________ READC_HALF_SECOND _____________________________* ********************************************************************************/ int readc_half_second ( void ) { char r = ERR; int a; struct itimerval t, tt; if ( ioctl( STDIN_FILENO , FIONREAD , &a ) >= 0 && a > 0 ) read( STDIN_FILENO , &r , 1 ); else if ( esc_no_wait ) return( ERR ); else { signal( SIGALRM , (sig_t)ch_alarm ); t.it_interval.tv_sec = 0; t.it_interval.tv_usec = 500000; t.it_value = t.it_interval; setitimer( ITIMER_REAL , &t , &tt ); read( STDIN_FILENO , &r , 1 ); t.it_interval.tv_usec = t.it_interval.tv_sec = 0; t.it_value = t.it_interval; setitimer( ITIMER_REAL , &t , &tt ); } return( r ); } /* readc_half_second */ /*********************************************************************** *_____________________________ MY_GETCH _____________________________* ***********************************************************************/ int my_getch ( void ) { char r = ERR; if ( un_char && un_char != ERR ) { las_char = un_char; un_char = 0; return( las_char ); } read( STDIN_FILENO , &r , 1 ); if ( r == '\33' ) { r = readc_half_second(); if ( r == ERR ) return( las_char = '\33' ); if ( r == '[' || r == 'O' ) { /* ** special keys ESC[X, ESC[X~ */ switch ( r = readc_half_second() ) { case 'A': return( las_char = KEY_UP ); case 'B': return( las_char = KEY_DOWN ); case 'C': return( las_char = KEY_RIGHT ); case 'D': return( las_char = KEY_LEFT ); case 'M': return( las_char = KEY_ENTER ); case 'q': case 'F': return( las_char = KEY_C1 ); case 'r': return( las_char = KEY_DOWN ); case 's': case 'G': return( las_char = KEY_C3 ); case 't': return( las_char = KEY_LEFT ); case 'v': return( las_char = KEY_RIGHT ); case 'w': case 'x': return( las_char = KEY_UP ); case 'y': case 'I': return( las_char = KEY_A3 ); case 'H': return( las_char = KEY_HOME ); // return( las_char = KEY_A1 ); case '1': if ( readc_half_second() == '~' ) return( las_char = KEY_HOME ); break; case '2': if ( readc_half_second() == '~' ) return( las_char = KEY_IC ); // INSERT = ESC[2~ break; case '3': // otherwise trapped as key '3' (3 rows) if ( readc_half_second() == '~' ) return( las_char = KEY_DC ); // DELETE = ESC[3~ break; case '4': // otherwise trapped as key '4' (4 rows) return ERR; case '5': case '6': if ( readc_half_second() == '~' ) return( las_char = ( r == '5' ? KEY_A3 : KEY_C3 ) ); break; default: break; } return( las_char = r ); } else return( las_char = r ); } else return( las_char = r ); } /* my_getch */ /********************************************************************** *_____________________________ ISINPUT _____________________________* **********************************************************************/ int is_input ( void ) { int r; return( ( un_char && un_char != ERR ) || ( ioctl( STDIN_FILENO , FIONREAD , &r ) >= 0 && r > 0 ) ); } /* is_input */ /******************************************************************* *_____________________________ HELP _____________________________* *******************************************************************/ void help ( void ) { static WINDOW * w1 , * w2; int c; w1 = newwin( 0 , 0 , 0 , 0 ); if ( w1 == NULL ) printf( "error creating help. Exiting...\n" ) , onint( 1 ); waddstr( w1 , HELP_STR1 ); waddstr( w1 , "\nPress ESC to exit help or any other key to continue..." ); w2 = newwin( 0 , 0 , 0 , 0 ); if ( w2 == NULL ) printf( "error creating help. Exiting...\n" ) , onint( 1 ); waddstr( w2 , HELP_STR2 ); waddstr( w2 , "\nPress any key to continue..." ); /* run */ alarm( 0 ); clearok( w1 , TRUE ); wrefresh( w1 ); refresh(); c = wgetch( w1 ); if ( c == 'q' || c == 'Q' ) onint( 0 ); #define ESC '\033' if ( c != ESC ) { clearok( w2 , TRUE ); wrefresh( w2 ); refresh(); c = wgetch( w2 ); if ( c == 'q' || c == 'Q' ) onint( 0 ); } #undef ESC delwin( w1 ); delwin( w2 ); clearok( stdscr , TRUE ); refresh(); } /* help */ /************************************************************************ *_____________________________ YARDSTICK _____________________________* ************************************************************************/ void yardstick ( void ) { static WINDOW * w; char buf[1025]; FILE * yard = fopen( PROC_HPC_SSPEED , "r" ); int n; if ( ! yard || fscanf( yard , "%d" , &n ) != 1 ) sprintf( buf , "Sorry, Failed obtaining yardstick: [%d] %m\n" , errno ); else sprintf( buf , "Yardstick speed currently set to %d\n" , n ); if ( yard ) fclose( yard ); w = newwin( 0 , 0 ,0 , 0 ); if ( w == NULL ) printf( "error creating yardwindow.exiting..\n" ) , onint( 1 ); waddstr( w , buf ); waddstr( w , "\nPress Any Key to Continue..." ); /* run */ alarm( 0 ); clearok( w , TRUE ); wrefresh( w ); wgetch( w ); delwin( w ); clearok( stdscr , TRUE ); refresh(); } /* yardstick */ /***************************************************************** *_____________________________ CB _____________________________* *****************************************************************/ void cb ( const char * msg , int y , int x ) { static int mode = 0; static WINDOW *wn; if ( mode == 0 ) /* create */ { wn = newwin( screen_rows / 3 , screen_cols * 4 / 9 , screen_rows / 3 , screen_cols / 3 ); if ( wn == NULL ) printf( "very bad\n" ) , onint( 1 ); mode = 1; } else wclear( wn ); alarm( 0 ); clearok( wn , TRUE ); box( wn , '|' , '-' ); wmove( wn , y , x ); waddstr( wn , msg ); wrefresh( wn ); sleep( 1 ); clearok( stdscr , TRUE ); refresh(); } /* cb */ /********************************************************************* *_____________________________ CB_CTR _____________________________* *********************************************************************/ /* ** same of cb() but autocentered (min screen size 43x9 :) */ void cb_ctr ( const char * msg ) { static int mode = 0; static WINDOW *wn; int nlines = ( screen_rows / 3 ); int ncols = ( screen_cols * 4 / 9 ); int begin_y = ( screen_rows / 3 ); int begin_x = ( screen_cols / 3 ); if ( mode == 0 ) /* create */ { wn = newwin( nlines , ncols , begin_y , begin_x ); if ( wn == NULL ) printf( "very bad\n" ) , onint( 1 ); mode = 1; } else wclear( wn ); alarm( 0 ); clearok( wn , TRUE ); box( wn , '|' , '-' ); wmove( wn , ( nlines / 2 ) , ( ncols / 2 ) - ( strlen( msg ) / 2 ) ); waddstr( wn , msg ); wrefresh( wn ); sleep( 1 ); clearok( stdscr , TRUE ); refresh(); } /* cb_ctr */ /******************************************************************* *_____________________________ ONIO _____________________________* *******************************************************************/ struct sigaction act; void onio ( void ) { act.sa_handler = (sig_t)onio; sigaction( SIGALRM , &act , 0 ); alarm( 1 ); } /* onio */ /***************************************************************************** *_____________________________ SLEEP_OR_INPUT _____________________________* *****************************************************************************/ void sleep_or_input ( unsigned int secs ) { if ( is_input() ) return; act.sa_handler = (sig_t)onio; sigaction( SIGALRM , &act , 0 ); alarm( secs ); if ( my_getch() != ERR ) not_yet_ch( 0 ); alarm( 0 ); } /* sleep_or_input */ /******************************************************************** *_____________________________ ONINT _____________________________* ********************************************************************/ /* ** clear window before exit */ void onint ( int i ) { clear(); echo(); nocbreak(); system( "stty -cbreak echo" ); move( screen_rows - 1 , 0 ); refresh(); endwin(); ioctl( STDIN_FILENO , TCIOFLUSH , 0 ); exit( i ); } /* onint */ /*********************** E N D O F F I L E ************************/