/* * info_solaris.cpp * * Torsten Kasch <tk@Genetik.Uni-Bielefeld.DE> */ #include <stdio.h> #include <stdlib.h> #include <sys/mnttab.h> #include <kstat.h> #include <sys/types.h> #include <sys/statvfs.h> #include <time.h> #ifdef HAVE_LIBDEVINFO_H #include <ctype.h> #include <string.h> #include <unistd.h> #include <sys/mkdev.h> #include <sys/stat.h> #include <devid.h> #include <libdevinfo.h> #endif /* HAVE_LIBDEVINFO_H */ #define INFO_CPU_AVAILABLE #define INFO_IRQ_AVAILABLE #define INFO_DMA_AVAILABLE #define INFO_PCI_AVAILABLE #define INFO_IOPORTS_AVAILABLE #define INFO_SOUND_AVAILABLE #define INFO_DEVICES_AVAILABLE #define INFO_SCSI_AVAILABLE #define INFO_PARTITIONS_AVAILABLE #define INFO_XSERVER_AVAILABLE bool GetInfo_CPU( TQListView *lBox ) { kstat_ctl_t *kctl; kstat_t *ksp; kstat_named_t *kdata; char cputype[16], fputype[16]; char *timetxt; char *ptr; uint32_t i, ncpus; unsigned long state_begin; QString state; QString mhz; QString inst; /* * get a kstat handle first and update the user's kstat chain */ if( (kctl = kstat_open()) == NULL ) { return false; } while( kstat_chain_update( kctl ) != 0 ) ; /* * get the # of CPUs */ if( (ksp = kstat_lookup( kctl, "unix", 0, "system_misc" )) == NULL ) { return false; } if( kstat_read( kctl, ksp, NULL ) == -1 ) { return false; } kdata = (kstat_named_t *) kstat_data_lookup( ksp, "ncpus" ); if( kdata != NULL ) { ncpus = kdata->value.ui32; } else { ncpus = 0; } lBox->addColumn( i18n( "Instance" )); lBox->addColumn( i18n( "CPU Type" )); lBox->addColumn( i18n( "FPU Type" )); lBox->addColumn( i18n( "MHz" )); lBox->addColumn( i18n( "State" )); /* * get the per-processor info */ for( i = 0; i < ncpus; i++ ) { if( (ksp = kstat_lookup( kctl, "cpu_info", i, NULL )) == NULL ){ return false; } if( kstat_read( kctl, ksp, NULL ) == -1 ) { return false; } inst.setNum( i ); kdata = (kstat_named_t *) kstat_data_lookup( ksp, "cpu_type" ); if( kdata != NULL ) { strcpy( cputype, kdata->value.c ); } else { sprintf( cputype, "???" ); } kdata = (kstat_named_t *) kstat_data_lookup( ksp, "fpu_type" ); if( kdata != NULL ) { strcpy( fputype, kdata->value.c ); } else { sprintf( fputype, "???" ); } kdata = (kstat_named_t *) kstat_data_lookup( ksp, "clock_MHz" ); if( kdata != NULL ) { mhz.setNum( kdata->value.ul ); } else { mhz.setNum( 0 ); } kdata = (kstat_named_t *) kstat_data_lookup( ksp, "state" ); if( kdata != NULL ) { state = TQString( kdata->value.c ); } else { state = "???"; } kdata = (kstat_named_t *) kstat_data_lookup( ksp, "state_begin" ); if( kdata != NULL ) { state_begin = kdata->value.i32; if( (timetxt = ctime( (time_t *) &state_begin )) != NULL ) { ptr = strrchr( timetxt, '\n' ); *ptr = '\0'; state += " since " + TQString( timetxt ); } } new TQListViewItem( lBox, inst, cputype, fputype, mhz, state ); } // sorting_allowed = true; lBox->setSorting( 0 ); return true; } bool GetInfo_IRQ( TQListView * ) { return false; } bool GetInfo_DMA( TQListView * ) { return false; } bool GetInfo_PCI( TQListView * ) { return false; } bool GetInfo_IO_Ports( TQListView * ) { return false; } bool GetInfo_Sound( TQListView * ) { return false; } bool GetInfo_SCSI( TQListView * ) { return false; } bool GetInfo_Partitions( TQListView *lBox ) { FILE *mnttab; struct mnttab mnt; struct statvfs statbuf; fsblkcnt_t tmp; QString total; QString avail; time_t mnttime; char *timetxt; char *ptr; if( (mnttab = fopen( MNTTAB, "r" )) == NULL ) { return false; } /* * set up column headers */ lBox->addColumn( i18n( "Device" )); lBox->addColumn( i18n( "Mount Point" )); lBox->addColumn( i18n( "FS Type" )); lBox->addColumn( i18n( "Total Size" )); // XXX: FIXME: how do I set column alignment correctly? lBox->setColumnAlignment( 3, 2 ); lBox->addColumn( i18n( "Free Size" )); // XXX: FIXME: how do I set column alignment correctly? lBox->setColumnAlignment( 4, 2 ); lBox->addColumn( i18n( "Mount Time" )); lBox->addColumn( i18n( "Mount Options" )); /* * get info about mounted file systems */ rewind( mnttab ); while( getmntent( mnttab, &mnt ) == 0 ) { /* * skip fstype "nfs" and "autofs" for two reasons: * o if the mountpoint is visible, the fs is not * necessarily available (autofs option "-nobrowse") * and we don't want to mount every remote fs just * to get its size, do we? * o the name "Partitions" for this statistics implies * "local file systems only" */ if( (strcmp( mnt.mnt_fstype, "nfs" ) == 0) || (strcmp( mnt.mnt_fstype, "autofs" ) == 0) ) continue; if( statvfs( mnt.mnt_mountp, &statbuf ) == 0 ) { if( statbuf.f_blocks > 0 ) { /* * produce output in KB, MB, or GB for * readability -- unfortunately, this * breaks sorting for these columns... */ tmp = statbuf.f_blocks * (statbuf.f_frsize / 1024); if( tmp > 9999 ) { tmp /= 1024; if( tmp > 9999 ) { tmp /= 1024; total.setNum( tmp ); total += " G"; } else { total.setNum( tmp ); total += " M"; } } else { total.setNum( tmp ); total += " K"; } // avail.setNum( statbuf.f_bavail ); // avail += " K"; tmp = statbuf.f_bavail * (statbuf.f_frsize / 1024); if( tmp > 9999 ) { tmp /= 1024; if( tmp > 9999 ) { tmp /= 1024; avail.setNum( tmp ); avail += " G"; } else { avail.setNum( tmp ); avail += " M"; } } else { avail.setNum( tmp ); avail += " K"; } } else { total = "-"; avail = "-"; } } else { total = "???"; avail = "???"; } /* * ctime() adds a '\n' which we have to remove * so that we get a one-line output for the QListViewItem */ mnttime = (time_t) atol( mnt.mnt_time ); if( (timetxt = ctime( &mnttime )) != NULL ) { ptr = strrchr( timetxt, '\n' ); *ptr = '\0'; } new TQListViewItem( lBox, mnt.mnt_special, mnt.mnt_mountp, mnt.mnt_fstype, total, avail, TQString( timetxt ), mnt.mnt_mntopts ); } fclose( mnttab ); lBox->setSorting( 0 ); // sorting_allowed = true; return true; } bool GetInfo_XServer_and_Video( TQListView *lBox ) { return GetInfo_XServer_Generic( lBox ); } #ifdef HAVE_LIBDEVINFO_H /* * get Solaris' device configuration data through libdevinfo(3) * and display it in a prtconf(1M) style tree * * NOTE: though the devinfo library seems to be present on earlier * Solaris releases, this interface is documented to be available * since Solaris 7 (libdevinfo.h is missing on pre-Solaris 7 systems) * * documentation for libdevinfo(3) including code samples on which * this implementation is based on is available at * http://soldc.sun.com/developer/support/driver/wps/libdevinfo/ */ /* * we start with various helper routines for GetInfo_Devices() */ /* * mktree() -- break up the device path and place its components * into the tree widget */ TQListViewItem *mktree( TQListViewItem *top, const char *path ) { QListViewItem *parent, *previous, *result; char *str = strdup( path ), *token; /* * start at "/" */ parent = top; result = (*top).firstChild(); previous = (*top).firstChild(); token = strtok( str, "/" ); while( token != NULL ) { /* * find insert pos: * try to match the node at the current level * * NOTE: this implementation assumes that there are * no two nodes with identical names at the * same level of the device tree */ while( result != NULL ) { if( strcmp( token, (*result).text( 0 ).latin1()) == 0 ) break; previous = result; result = (*result).nextSibling(); } if( result == NULL ) { /* * we haven't found the node, create a new one */ result = new TQListViewItem( parent, previous, token ); } else { /* * we've found the node */ parent = result; previous = NULL; if( (*result).firstChild() == NULL ) { /* * create new node during next iteration */ result->setExpandable( true ); result->setOpen( false ); } else { /* * follow the child path */ result = (*result).firstChild(); } } token = strtok( NULL, "/" ); } free( str ); return( result ); } /* * prop_type_str() -- return the property type as a string */ char *prop_type_str( di_prop_t prop ) { switch( di_prop_type( prop )) { case DI_PROP_TYPE_UNDEF_IT: return( "undefined" ); case DI_PROP_TYPE_BOOLEAN: return( "BOOL" ); case DI_PROP_TYPE_INT: return( "INT" ); case DI_PROP_TYPE_STRING: return( "STRING" ); case DI_PROP_TYPE_BYTE: return( "BYTE" ); default: return( "unknown" ); } } /* * prop_type_guess() -- guess the property type */ int prop_type_guess( uchar_t *data, int len ) { int slen; int guess; int i, c; if( len < 0 ) return( -1 ); else if( len == 0 ) return( DI_PROP_TYPE_BOOLEAN ); slen = 0; guess = DI_PROP_TYPE_STRING; for( i = 0; i < len; i++ ) { c = (int) data[i]; switch( c ) { case 0: if( i == (len - 1 )) break; if( slen == 0 ) guess = DI_PROP_TYPE_BYTE; else guess = slen = 0; break; default: if( ! isprint( c )) guess = DI_PROP_TYPE_BYTE; else slen++; } if( guess != DI_PROP_TYPE_STRING ) break; } // if( (guess == DI_PROP_TYPE_BYTE) && (len % sizeof( int ) == 0 )) // guess = DI_PROP_TYPE_INT; return( guess ); } /* * dump_minor_node() -- examine a device minor node * this routine gets passed to di_walk_node() */ int dump_minor_node( di_node_t node, di_minor_t minor, void *arg ) { QListViewItem *item; QString majmin; char *type; dev_t dev; item = new TQListViewItem( (TQListViewItem *) arg, di_minor_name( minor )); item->setExpandable( true ); item->setOpen( false ); new TQListViewItem( item, i18n( "Spectype:" ), (di_minor_spectype( minor ) == S_IFCHR) ? i18n( "character special" ) : i18n( "block special" )); type = di_minor_nodetype( minor ); new TQListViewItem( item, i18n( "Nodetype:" ), (type == NULL) ? "NULL" : type ); if( (dev = di_minor_devt( minor )) != DDI_DEV_T_NONE ) { majmin.sprintf( "%ld/%ld", major( dev ), minor( dev )); new TQListViewItem( item, i18n( "Major/Minor:" ), majmin ); } if( di_minor_next( node, minor ) == DI_MINOR_NIL ) return( DI_WALK_TERMINATE ); else return( DI_WALK_CONTINUE ); } /* * propvalue() -- return the property value */ TQString propvalue( di_prop_t prop ) { int type; int i, n; char *strp; int *intp; uchar_t *bytep; QString result; /* * Since a lot of printable strings seem to be tagged as 'byte', * we're going to guess, if the property is not STRING or INT * The actual type is shown in the info tree, though. */ type = di_prop_type( prop ); if( (type != DI_PROP_TYPE_STRING) && (type != DI_PROP_TYPE_INT) ) { n = di_prop_bytes( prop, &bytep ); type = prop_type_guess( bytep, n ); } result = ""; switch( type ) { case DI_PROP_TYPE_STRING: if( (n = di_prop_strings( prop, &strp )) < 0 ) { result = "(error)"; } else { for( i = 0; i < n; i++ ) { result += "\""; result += strp; result += "\" "; strp += strlen( strp ) + 1; } } break; case DI_PROP_TYPE_INT: if( (n = di_prop_ints( prop, &intp )) < 0 ) { result = "(error)"; } else { for( i = 0; i < n; i++ ) { TQString tmp; tmp.setNum( intp[i] ); result += tmp; result += " "; } } break; case DI_PROP_TYPE_BOOLEAN: /* * hmm, Sun's sample code handles the existence * of a boolean property as "true", whereas * prtconf(1M) obviously does not (Sol8, at least) * -- we're doing the same and handle "bool" as "byte" */ case DI_PROP_TYPE_BYTE: if( (n = di_prop_bytes( prop, &bytep )) < 0 ) { result = "(error)"; } else { if( n == 0 ) { result = i18n( "(no value)" ); break; } result = "0x"; for( i = 0; i < n; i++ ) { TQString tmp; unsigned byte = (unsigned) bytep[i]; tmp.sprintf( "%2.2x", byte ); result += tmp; } } break; default: result = "???"; } return( result ); } /* * dump_node() -- examine a device node and its children * this routine gets passed to di_walk_node() */ int dump_node( di_node_t node, void *arg ) { QListViewItem *top = (TQListViewItem *) arg, *parent, *previous; char *path; char *drivername; char *names; QString compatnames; int i, n; di_prop_t prop; path = di_devfs_path( node ); /* * if this is the root node ("/"), initialize the tree */ if( strlen( path ) == 1 ) { top->setText( 0, TQString( di_binding_name( node ))); top->setPixmap( 0, SmallIcon( "kcmdevices" )); top->setOpen( true ); top->setSelectable( false ); top->setExpandable( false ); } /* * place the node in the tree */ parent = mktree( top, path ); /* * we have to handle the root node differently... */ if( strlen( path ) > 1 ) { parent->setExpandable( true ); parent->setOpen( false ); } else { previous = parent; parent = top; } /* * node name and physical device path */ drivername = di_driver_name( node ); previous = new TQListViewItem( parent, i18n( "Driver Name:" ), (drivername == NULL) ? i18n( "(driver not attached)" ) : drivername ); previous = new TQListViewItem( parent, previous, i18n( "Binding Name:" ), di_binding_name( node )); n = di_compatible_names( node, &names ); if( n < 1 ) { compatnames = i18n( "(none)" ); } else { for( i = 0; i < n; i++ ) { compatnames += names; compatnames += " "; names += strlen( names ) + 1; } } previous = new TQListViewItem( parent, previous, i18n( "Compatible Names:" ), compatnames ); previous = new TQListViewItem( parent, previous, i18n( "Physical Path:" ), TQString( path )); /* * dump the node's property list (if any) */ if( (prop = di_prop_next( node, DI_PROP_NIL )) != DI_PROP_NIL ) { previous = new TQListViewItem( parent, previous, i18n( "Properties" )); previous->setExpandable( true ); previous->setOpen( false ); do { /* * property type & value */ QListViewItem *tmp, *prev; tmp = new TQListViewItem( previous, di_prop_name( prop )); tmp->setExpandable( true ); tmp->setOpen( false ); prev = new TQListViewItem( tmp, i18n( "Type:" ), prop_type_str( prop )); new TQListViewItem( tmp, prev, i18n( "Value:" ), propvalue( prop )); } while( (prop = di_prop_next( node, prop )) != DI_PROP_NIL ); } /* * if there are minor nodes, expand the tree appropriately */ if( di_minor_next( node, DI_MINOR_NIL ) != DI_MINOR_NIL ) { previous = new TQListViewItem( parent, previous, i18n( "Minor Nodes" )); previous->setExpandable( true ); previous->setOpen( false ); di_walk_minor( node, NULL, 0, previous, dump_minor_node ); } return( DI_WALK_CONTINUE ); } bool GetInfo_Devices( TQListView *lBox ) { QListViewItem *top; di_node_t root_node; /* * create a snapshot of the device tree */ if( (root_node = di_init( "/", DINFOCPYALL )) == DI_NODE_NIL ) { return( false ); } // XXX: might try to di_prom_init() here as well (if we're setgid sys) /* * prepare the tree widget */ lBox->addColumn( i18n( "Device Information" )); lBox->addColumn( i18n( "Value" )); top = new TQListViewItem( lBox ); /* * traverse the device tree */ di_walk_node( root_node, DI_WALK_CLDFIRST, top, dump_node ); di_fini( root_node ); sorting_allowed = false; return true; } #else /* ! HAVE_LIBDEVINFO_H */ bool GetInfo_Devices( TQListView * ) { return false; } #endif /* ! HAVE_LIBDEVINFO_H */