/* 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. */ /* ** Last 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"; ** - ... */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define PROGNAME "mosmon" #define PROGVER "2.0" #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_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" \ "\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" \ "\n" /************************************************************** ** 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 ); /* ! openMosix */ static void not_mosix( void ) { fprintf( stderr , PROGNAME ": This is NOT a openMosix system!\n" ); exit( 1 ); } /* not_mosix */ /************************************************************** ** some useful macros */ #define COL_FROM_0 7 #define MAX_SILENT ( -5 ) #define VERY_DEAD ( MAX_SILENT - 1 ) #define PROC_HPC_SSPEED "/proc/hpc/admin/sspeed" /************************************************************** ** global variables (arrays numbering starts at 1) */ int screen_width; 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 record */ 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[] ) { int l , col ; register int i = 0 , j = 0 , k = 0 ; float max , curmax = 0 ; int cool = 0 ; int dead ; struct winsize ws ; int vflg = 0 , wflg = 0 , tflg = 0 ; int dflg = 0 ; int last ; int base ; /* baseline location */ int ver = 0 ; int wid ; /* width of processor numbers */ char need_count = 1 ; int tot = 0 , tot_display = 0 , cpus_display = 0 ; NCURSES_CONST char * fmt ; unsigned int turns = 0 ; char space , ospace ; int rev ; int first_read , nread ; const char * file = PROC_HPC_INFOS ; #ifdef DEBUG stdreopen(); #endif /* ** command line options parser */ 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': vflg = 1; wflg = 0; break; case 'V': vflg = 2; wflg = 0; break; case 'w': vflg = 0; wflg = 1; break; case 'a': wflg = vflg = 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': tflg = 1; break; case 'd': dflg = 1; 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] ); 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 /* ** 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" ); for ( i = 0 ; i <= npe ; i++ ) load[i] = 0; 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 */ if ( ioctl( STDIN_FILENO , TIOCGWINSZ , (char *)&ws ) == -1 ) fprintf( stderr , "ioctl() failed: [%d] %m\n" , errno ) , onint( 1 ); if ( ( COLS != ws.ws_col ) || ( LINES != ws.ws_row ) ) { initscr(); need_count = 1; } if ( tflg ) { 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; } } } if ( need_count && ! vflg && ! wflg ) { get_npe(); k = ( 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++; } need_count = 0; } /* calculate the screen_width */ wid = 1 + ( npe > 9 ) + ( npe > 99 ) + ( npe > 999 ) + ( npe > 9999 ); k = ( COLS - COL_FROM_0 - 1 ) / ( wid + 1 ); if ( vflg ) { vert: base = LINES - 2 - wid; ver = 1; if ( vflg == 2 ) { wid = 0; screen_width = ( COLS - COL_FROM_0 - 1 ); } else { wid = 1; screen_width = ( COLS - COL_FROM_0 - 1 ) / 2; } adjust_first(); } else if ( wflg || k >= tot || ( COLS - COL_FROM_0 - 1 ) < tot ) { ver = 0; screen_width = k; base = LINES - 3; } else goto vert; move( 0 , 0 ); clrtobot(); if ( item == D_GETMEM || item == D_GETBMEM ) { if ( item == D_GETBMEM ) { fmt = "Raw Used Memory"; i = base / 2 - 8; if ( i < 1 ) i = 1; } else { fmt = "Used Memory"; i = base / 2 - 6; j = 1; } for ( ; *fmt ; i++ ) { move( i , 0 ); addch( (chtype)*fmt++ ); } while ( i == base / 4 || i == base / 2 || i == base * 3 / 4 ) i++; if ( i < base ) { move( i , 2 ); addstr( "(MB)" ); } } else if ( item == D_GETLOAD ) { #ifdef USE_LOG_LOAD int n = ( log_load ) ? 7 : 3 ; fmt = ( log_load ) ? "LOG LOAD" : "LOAD" ; #else int n = 3; fmt = "LOAD"; #endif /* USE_LOG_LOAD */ for ( i = base / 2 - n ; *fmt ; i += 2 ) { move( i , 0 ); addch( (chtype)*fmt++ ); } } else if ( item == D_GETSPEED ) { fmt = "SPEED"; for ( i = base / 2 - 4 ; *fmt ; i += 2 ) { move( i , 0 ); addch( (chtype)*fmt++ ); } } else { fmt = "Utilizability"; for ( i = base / 2 - 7 ; *fmt ; i++ ) { move( i , 0 ); addch( (chtype)*fmt++ ); } } for ( i = 0 ; i < base ; i++ ) { move( i , COL_FROM_0 ); addch( '|' ); } move( base , COL_FROM_0 ); for ( i = screen_width * ( wid + 1 ) + 1 ; i > 0 ; i-- ) addch( '-' ); j = ( COLS - COL_FROM_0 - 8 ) / 2; if ( j < COL_FROM_0 ) j = COL_FROM_0; else if ( j > 27 ) j = 27; move( LINES - 2 , 0 ); addstr( "Node #" ); if ( tflg ) { move( LINES - 1 , 0 ); if ( tot_display ) printw( "[Total %d] [CPUs %d]" , tot_display , cpus_display ); else printw( "[openMosix Not Configured]" ); } max = 0; dead = 0; turns++; if ( ! tflg ) (void)lseek( ifd , first - 1 , SEEK_SET ); for ( i = first ; ( i < ( first + screen_width + 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 + 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]--; } 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; } if ( item == D_GETMEM ) { /* typical values are very close to 1MB multiples, * but not quite, causing distortions: */ } switch ( item ) { case D_GETLOAD: fmt = "%5.2f"; break; case D_GETSPEED: fmt = "%5.0f"; break; case D_GETUTIL: fmt = "%4.0f%%"; break; case D_GETMEM: case D_GETBMEM: fmt = ( max >= 999.0 ) ? "%5.0f" : "%5.3g" ; break; } if ( max > 0 ) { move( 0 , 1 ); printw( fmt , max ); } /*if ( max >= 3 )*/ { move( base / 4 , 1 ); printw( fmt , max * 3 / 4 ); move( base / 2 , 1 ); printw( fmt , max / 2 ); move( base * 3 / 4 , 1 ); printw( fmt , max / 4 ); } move( base , 5 ); addch( '0' ); 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(); dead = 0; /* number of not responding machines */ if ( max ) { for ( i = first ; i <= last ; i++ ) { if ( load[i] > 0 ) { col = COL_FROM_0 + 1 + ( wid != 0 ) + wid / 2 + ( wid + 1 ) * ( i - first - dead ); l = base - ( load[i] * base ) / max + 0.5; if ( item == D_GETMEM || item == D_GETBMEM ) { k = base - ( other[i] * base ) / max + 0.5; if ( rev ) standend(); for ( ; l < k ; l++ ) { move( l , col ); addch( (chtype)( valid[i] > 0 ? '+' : '?' ) ); } if ( rev ) standout(); } ospace = space; if ( item == D_GETSPEED && other[i] > 1 ) space = '0' + other[i]; for ( ; l < base ; l++ ) { move( l , col ); addch( (chtype)( valid[i] > 0 ? space : '?' ) ); } space = ospace; } else if ( load[i] < 0 ) { if ( load[i] <= ( dflg ? VERY_DEAD : MAX_SILENT ) ) { dead++; continue; } col = COL_FROM_0 + 1 + ( wid != 0 ) + wid / 2 + ( wid + 1 ) * ( i - first - dead ); if ( rev ) standend(); move( base - 4 , col ); addch( 'D' ); move( base - 3 , col ); addch( 'E' ); move( base - 2 , col ); addch( 'A' ); move( base - 1 , col ); addch( 'D' ); if ( rev ) standout(); } } } if ( rev ) standend(); if ( ver ) { for ( j = 10 , k = LINES - 2 ; k > base ; k-- , j *= 10 ) { move( k , COL_FROM_0 + 1 ); for ( i = first ; i < ( first + screen_width + dead ) && i <= npe ; i++ ) { if ( load[i] > ( dflg ? VERY_DEAD : MAX_SILENT ) ) { if ( wid ) addch( ' ' ); addch( (chtype)( '0' + i % j / ( j / 10 ) ) ); } } } } else { move( base + 1 ,COL_FROM_0 + 1 ); for ( i = first ; i < ( first + screen_width + dead ) && 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 - ( 1 + ( i > 9 ) + ( i > 99 ) + ( i > 999 ) + ( i > 9999 ) ); printw( "%*s%d%*s" , j / 2 + 1 , "" , i , ( j + 1 ) / 2 , "" ); } } } } move( base , 79 ); refresh(); if ( turns % 60 == 0 ) get_npe(); /* ** screen's out, check for interactive keys */ sleep_or_input( 1 ); if ( is_input() ) { switch ( my_getch() ) { //-- - -- - -- - -- - -- - -- - 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': wflg = vflg = 0; need_count = 1; break; case 'v': vflg = 1; wflg = 0; break; case 'V': vflg = 2; wflg = 0; break; case 'w': case 'W': vflg = 0; wflg = 1; break; //-- - -- - -- - -- - -- - -- - case 't': tflg = !tflg; break; case 'd': case 'D': dflg = !dflg; break; case 'y': case 'Y': yardstick(); 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 */ } /* 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" // "\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 */ /*********************************************************************** *_____________________________ 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 '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( LINES / 3 , COLS * 4 / 9 , LINES / 3 , 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 = ( LINES / 3 ); int ncols = ( COLS * 4 / 9 ); int begin_y = ( LINES / 3 ); int begin_x = ( 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( LINES - 1 , 0 ); refresh(); endwin(); ioctl( STDIN_FILENO , TCIOFLUSH , 0 ); exit( i ); } /* onint */ /*********************** E N D O F F I L E ************************/