////////////////////////////////////////////////////////////////////////// // sysinfo.cpp // // // // Copyright (C) 2005 Lukas Tinkl // // // // This program is free software; you can redistribute it and/or // // modify it under the terms of the GNU General Public License // // as published by the Free Software Foundation; either version 2 // // of the License, or (at your option) any later version. // // // // This program is distributed in the hope that it will be useful, // // but WITHOUT ANY WARRANTY; without even the implied warranty of // // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // // GNU General Public License for more details. // // // // You should have received a copy of the GNU General Public License // // along with this program; if not, write to the Free Software // // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA // // 02111-1307, USA. // ////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sysinfo.h" using namespace KIO; #define BR "
" static QString formattedUnit( Q_UINT64 value, int post=1 ) { if (value >= (1024 * 1024)) if (value >= (1024 * 1024 * 1024)) return i18n("%1 GB").arg(KGlobal::locale()->formatNumber(value / (1024 * 1024 * 1024.0), post)); else return i18n("%1 MB").arg(KGlobal::locale()->formatNumber(value / (1024 * 1024.0), post)); else return i18n("%1 KB").arg(KGlobal::locale()->formatNumber(value / 1024.0, post)); } static QString htmlQuote(const QString& _s) { QString s(_s); return s.replace("&", "&").replace("<", "<").replace(">", ">"); } static QString readFromFile( const QString & filename, const QString & info = QString::null, const char * sep = 0, bool returnlast = false ) { QFile file( filename ); if ( !file.exists() || !file.open( IO_ReadOnly ) ) return QString::null; QTextStream stream( &file ); QString line, result; while ( !stream.atEnd() ) { line = stream.readLine(); if ( !line.isEmpty() ) { if ( !sep ) result = line; else if ( line.startsWith( info ) ) result = line.section( sep, 1, 1 ); if (!result.isEmpty() && !returnlast) return result; } } return result; } kio_sysinfoProtocol::kio_sysinfoProtocol( const QCString & pool_socket, const QCString & app_socket ) : SlaveBase( "kio_sysinfo", pool_socket, app_socket ), m_dcopClient( new DCOPClient() ) { if ( !m_dcopClient->isAttached() ) m_dcopClient->attach(); } kio_sysinfoProtocol::~kio_sysinfoProtocol() { m_dcopClient->detach(); delete m_dcopClient; } void kio_sysinfoProtocol::get( const KURL & /*url*/ ) { mimeType( "application/x-sysinfo" ); // CPU info infoMessage( i18n( "Looking for CPU information..." ) ); cpuInfo(); // header QString location = locate( "data", "sysinfo/about/my-computer.html" ); QFile f( location ); f.open( IO_ReadOnly ); QTextStream t( &f ); QString content = t.read(); content = content.arg( i18n( "My Computer" ), htmlQuote("file:" + locate( "data", "sysinfo/about/style.css" )), i18n( "My Computer"), i18n( "Folders, Harddisks, Removable Devices, System Information and more..." )); QString sysInfo = "
"; // table with 2 cols QString dummy; // disk info infoMessage( i18n( "Looking for disk information..." ) ); sysInfo += "

" + i18n( "Disk Information" ) + "

"; sysInfo += diskInfo(); osInfo(); sysInfo += "

" +i18n( "OS Information" ) + "

"; sysInfo += ""; sysInfo += ""; sysInfo += ""; sysInfo += ""; sysInfo += ""; sysInfo += "
" + i18n( "OS:" ) + "" + htmlQuote(m_info[OS_SYSNAME]) + " " + htmlQuote(m_info[OS_RELEASE]) + " " + htmlQuote(m_info[OS_MACHINE]) + "
" + i18n( "Current user:" ) + "" + htmlQuote(m_info[OS_USER]) + "@" + htmlQuote(m_info[OS_HOSTNAME]) + "
" + i18n( "System:" ) + "" + htmlQuote(m_info[OS_SYSTEM]) + "
" + i18n( "KDE:" ) + "" + KDE::versionString() + "
"; // OpenGL info if ( glInfo() ) { sysInfo += "

" + i18n( "Display Info" ) + "

"; sysInfo += ""; sysInfo += ""; sysInfo += ""; if (!m_info[GFX_DRIVER].isNull()) sysInfo += ""; sysInfo += "
" + i18n( "Vendor:" ) + "" + htmlQuote(m_info[GFX_VENDOR]) + "
" + i18n( "Model:" ) + "" + htmlQuote(m_info[GFX_MODEL]) + "
" + i18n( "Driver:" ) + "" + htmlQuote(m_info[GFX_DRIVER]) + "
"; } sysInfo += "
"; // second column // OS info infoMessage( i18n( "Getting OS information...." ) ); // common folders sysInfo += "

" + i18n( "Common Folders" ) + "

"; sysInfo += ""; // net info infoMessage( i18n( "Looking up network status..." ) ); int state = netInfo(); if (state > 1) { // assume no network manager / networkstatus sysInfo += "

" + i18n( "Network Status" ) + "

"; sysInfo += "
    "; sysInfo += "
  • " + netStatus( state ) + "
  • "; sysInfo += "
"; } if ( !m_info[CPU_MODEL].isNull() ) { sysInfo += "

" + i18n( "CPU Information" ) + "

"; sysInfo += "
    "; sysInfo += "
  • " + i18n( "Processor (CPU):" ) + "" + htmlQuote(m_info[CPU_MODEL]) + "
  • "; sysInfo += "
  • " + i18n( "Speed:" ) + " " + i18n( "%1 MHz" ).arg( KGlobal::locale()->formatNumber( m_info[CPU_SPEED].toFloat(), 2 ) ) + "
  • "; int core_num = m_info[CPU_CORES].toUInt() + 1; if (core_num > 1) sysInfo += "
  • " + i18n("Cores:") + QString(" %1
  • ").arg(core_num); if (!m_info[CPU_TEMP].isEmpty()) { sysInfo += "
  • " + i18n("Temperature:") + QString(" %1
  • ").arg(m_info[CPU_TEMP]); } sysInfo += "
"; } // memory info infoMessage( i18n( "Looking for memory information..." ) ); memoryInfo(); sysInfo += "

" + i18n( "Memory Information" ) + "

"; sysInfo += ""; sysInfo += ""; sysInfo += ""; dummy = i18n( "Used Memory" ); dummy += ""; sysInfo += ""; sysInfo += "
" + i18n( "Total memory (RAM):" ) + "" + m_info[MEM_TOTALRAM] + "
" + i18n( "Free memory:" ) + "" + m_info[MEM_FREERAM] + "
" + i18n( "Total swap:" ) + "" + m_info[MEM_TOTALSWAP] + "
" + i18n( "Free swap:" ) + "" + m_info[MEM_FREESWAP] + "
"; sysInfo += "
"; // Send the data content = content.arg( sysInfo ); // put the sysinfo text into the main box data( QCString( content.utf8() ) ); data( QByteArray() ); // empty array means we're done sending the data finished(); } void kio_sysinfoProtocol::mimetype( const KURL & /*url*/ ) { mimeType( "application/x-sysinfo" ); finished(); } static unsigned long int scan_one( const char* buff, const char *key ) { char *b = strstr( buff, key ); if ( !b ) return 0; unsigned long int val = 0; if ( sscanf( b + strlen( key ), ": %lu", &val ) != 1 ) return 0; kdDebug(1242) << "scan_one " << key << " " << val << endl; return val; } static Q_UINT64 calculateFreeRam() { FILE *fd = fopen( "/proc/meminfo", "rt" ); if ( !fd ) return 0; QTextIStream is( fd ); QString MemInfoBuf = is.read(); Q_UINT64 MemFree = scan_one( MemInfoBuf.latin1(), "MemFree" ); Q_UINT64 Buffers = scan_one( MemInfoBuf.latin1(), "Buffers" ); Q_UINT64 Cached = scan_one( MemInfoBuf.latin1(), "Cached" ); Q_UINT64 Slab = scan_one( MemInfoBuf.latin1(), "Slab" ); fclose( fd ); MemFree += Cached + Buffers + Slab; if ( MemFree > 50 * 1024 ) MemFree -= 50 * 1024; return MemFree; } void kio_sysinfoProtocol::memoryInfo() { struct sysinfo info; int retval = sysinfo( &info ); if ( retval !=-1 ) { Q_UINT64 mem_unit = info.mem_unit; m_info[MEM_TOTALRAM] = formattedUnit( Q_UINT64(info.totalram) * mem_unit ); Q_UINT64 totalFree = calculateFreeRam() * 1024; kdDebug(1242) << "total " << totalFree << " free " << info.freeram << " unit " << mem_unit << endl; if ( totalFree > info.freeram * info.mem_unit || true ) m_info[MEM_FREERAM] = i18n("%1 (+ %2 Caches)").arg(formattedUnit( Q_UINT64(info.freeram) * mem_unit )) .arg( formattedUnit( totalFree - info.freeram * mem_unit )); else m_info[MEM_FREERAM] = formattedUnit( Q_UINT64(info.freeram) * mem_unit ); m_info[MEM_TOTALSWAP] = formattedUnit( Q_UINT64(info.totalswap) * mem_unit ); m_info[MEM_FREESWAP] = formattedUnit( Q_UINT64(info.freeswap) * mem_unit ); m_info[SYSTEM_UPTIME] = convertSeconds( info.uptime ); } } void kio_sysinfoProtocol::cpuInfo() { QString speed = readFromFile( "/proc/cpuinfo", "cpu MHz", ":" ); if ( speed.isNull() ) // PPC? speed = readFromFile( "/proc/cpuinfo", "clock", ":" ); if ( speed.endsWith( "MHz", false ) ) speed = speed.left( speed.length() - 3 ); m_info[CPU_SPEED] = speed; m_info[CPU_CORES] = readFromFile("/proc/cpuinfo", "processor", ":", true); const char* const names[] = { "THM0", "THRM", "THM" }; for (unsigned i = 0; i < sizeof(names)/sizeof(*names); ++i) { m_info[CPU_TEMP] = readFromFile(QString("/proc/acpi/thermal_zone/%1/temperature").arg(names[i]), "temperature", ":"); m_info[CPU_TEMP] = m_info[CPU_TEMP].stripWhiteSpace(); m_info[CPU_TEMP].replace(" C",QString::fromUtf8("°C")); if (!m_info[CPU_TEMP].isEmpty()) break; } m_info[CPU_MODEL] = readFromFile( "/proc/cpuinfo", "model name", ":" ); if ( m_info[CPU_MODEL].isNull() ) // PPC? m_info[CPU_MODEL] = readFromFile( "/proc/cpuinfo", "cpu", ":" ); } QString kio_sysinfoProtocol::diskInfo() { QString result = ""; if ( fillMediaDevices() ) { for ( QValueList::ConstIterator it = m_devices.constBegin(); it != m_devices.constEnd(); ++it ) { QString tooltip = i18n("Press the right mouse button for more options like Mount or Eject"); DiskInfo di = ( *it ); unsigned int percent = 0; Q_UINT64 usage = di.total - di.avail; if (di.total) percent = usage / ( di.total / 100); KURL media; media.setProtocol("media"); media.setPath("/" + di.name); result += QString( "\n" ). arg( icon( di.iconName ) ).arg( media.htmlURL() ).arg( htmlQuote(di.label) ).arg( di.fsType ). arg( di.total ? formattedUnit( di.total) : QString::null).arg(di.mounted ? formattedUnit (di.avail ) : QString::null).arg( htmlQuote(tooltip) ); if (di.mounted) { QColor c; c.setHsv(100-percent, 180, 230); result += QString("\n") .arg(percent).arg(formattedUnit(usage).replace(" ", " ")).arg(c.name()); } } } else // fall back to fstab parsing { FILE * fp; if ( !( fp = setmntent( "/etc/fstab", "r" ) ) ) { kdDebug(1242) << k_funcinfo << "Failed to open /etc/fstab" << endl; return QString::null; } #define FS_NAME mnt_ent->mnt_fsname // device-name #define FS_FILE mnt_ent->mnt_dir // mount-point #define FS_TYPE mnt_ent->mnt_type // fs-type struct statfs sfs; struct mntent *mnt_ent; unsigned long long total, avail; while ( ( mnt_ent = getmntent( fp ) ) != NULL ) { total = avail = 0; if ( statfs( FS_FILE, &sfs ) == 0 ) { total = ( unsigned long long )sfs.f_blocks * sfs.f_bsize; if ( total > 0 && strcmp( FS_TYPE, "subdomainfs" ) != 0 ) // discard empty or otherwise strange devices { avail = ( unsigned long long )( getuid() ? sfs.f_bavail : sfs.f_bfree ) * sfs.f_bsize; int lastSlash = QString( FS_NAME ).findRev( '/' ); QString deviceName = lastSlash != -1 ? QString( FS_NAME ).mid( lastSlash + 1 ) : QString( FS_NAME ); //kdDebug(1242) << k_funcinfo << "Device name: " << deviceName << endl; QString model = deviceName.left( deviceName.length() - 1 ); model = readFromFile( "/proc/ide/" + model + "/model" ); QString devIcon = icon( iconForDevice( deviceName ), KIcon::SizeSmall ); result += QString( "" ) .arg( devIcon ).arg( FS_FILE ).arg( FS_FILE ).arg( FS_TYPE ).arg( formattedUnit( total ) ) .arg( formattedUnit( avail ) ).arg( model ); } } } endmntent( fp ); } result += "
" + i18n( "Device" ) + "" + i18n( "Filesystem" ) + "" + i18n( "Total space" ) + "" + i18n( "Available space" ) + "
%1 %3%4%5%6
" "
" "%2
%1 %3%4%5%6
"; return result; } int kio_sysinfoProtocol::netInfo() const { // query kded.networkstatus.status(QString host) DCOPRef nsd( "kded", "networkstatus" ); nsd.setDCOPClient( m_dcopClient ); DCOPReply reply = nsd.call( "status", QString( "www.novell.com" ) ); if ( reply.isValid() ) return reply; kdDebug(1242) << k_funcinfo << "Reply is invalid" << endl; return 0; } #define HAVE_GLXCHOOSEVISUAL #ifdef HAVE_GLXCHOOSEVISUAL #include #endif //------------------------------------- bool hasDirectRendering ( QString &renderer ) { renderer = QString::null; Display *dpy = QApplication::desktop()->x11Display(); #ifdef HAVE_GLXCHOOSEVISUAL int attribSingle[] = { GLX_RGBA, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, None }; int attribDouble[] = { GLX_RGBA, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, GLX_DOUBLEBUFFER, None }; XVisualInfo* visinfo = glXChooseVisual ( dpy, QApplication::desktop()->primaryScreen(), attribSingle ); if (visinfo) { GLXContext ctx = glXCreateContext ( dpy, visinfo, NULL, True ); if (glXIsDirect(dpy, ctx)) { glXDestroyContext (dpy,ctx); return true; } XSetWindowAttributes attr; unsigned long mask; Window root; XVisualInfo *visinfo; int width = 100, height = 100; int scrnum = QApplication::desktop()->primaryScreen(); root = RootWindow(dpy, scrnum); visinfo = glXChooseVisual(dpy, scrnum, attribSingle); if (!visinfo) { visinfo = glXChooseVisual(dpy, scrnum, attribDouble); if (!visinfo) { fprintf(stderr, "Error: couldn't find RGB GLX visual\n"); return false; } } attr.background_pixel = 0; attr.border_pixel = 0; attr.colormap = XCreateColormap(dpy, root, visinfo->visual, AllocNone); attr.event_mask = StructureNotifyMask | ExposureMask; mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask; Window win = XCreateWindow(dpy, root, 0, 0, width, height, 0, visinfo->depth, InputOutput, visinfo->visual, mask, &attr); if ( glXMakeCurrent(dpy, win, ctx)) renderer = (const char *) glGetString(GL_RENDERER); XDestroyWindow(dpy, win); glXDestroyContext (dpy,ctx); return false; } else { return false; } #else #error no GL? return false; #endif } bool kio_sysinfoProtocol::glInfo() { static hd_data_t hd_data; static bool inited_hd = false; if ( !inited_hd ) { memset(&hd_data, 0, sizeof(hd_data)); inited_hd = true; } if (!hd_list(&hd_data, hw_display, 1, NULL)) return false; hd_t* hd = hd_get_device_by_idx(&hd_data, hd_display_adapter(&hd_data)); if (!hd) return false; driver_info_t *di = hd->driver_info; QString renderer; bool dri = hasDirectRendering( renderer ); QString driver; for(di = di; di; di = di->next) { QString loadline; if (di->any.type == di_x11) driver = di->x11.server; else if (di->any.type == di_module && di->module.names) driver = di->module.names->str; loadline = QString("(II) LoadModule: \"%1\"").arg( driver ); QFile file( "/var/log/Xorg.0.log" ); if ( !file.exists() || !file.open( IO_ReadOnly ) ) { di = 0; break; } QTextStream stream( &file ); QString line; bool found_line = false; while ( !stream.atEnd() ) { line = stream.readLine(); if (line == loadline) { found_line = true; break; } } kdDebug(1242) << "found_line " << found_line << endl; if (found_line) break; else driver = QString::null; } m_info[GFX_VENDOR] = hd->vendor.name; m_info[GFX_MODEL] = hd->device.name; if (!driver.isNull()) { if (dri) m_info[GFX_DRIVER] = i18n("%1 (3D Support)").arg(driver); else { if ( renderer.contains( "Mesa GLX Indirect" ) ) m_info[GFX_DRIVER] = i18n("%1 (No 3D Support)").arg(driver); else m_info[GFX_DRIVER] = driver; } } else m_info[GFX_DRIVER] = i18n( "Unknown" ); return true; } QString kio_sysinfoProtocol::netStatus( int code ) const { if ( code == 1 || code == 2 ) return i18n( "Network is unreachable" ); else if ( code == 3 || code == 4 || code == 6 ) return i18n( "You are offline" ); else if ( code == 5 ) return i18n( "Network is shutting down" ); else if ( code == 7 ) return i18n( "Establishing connection to the network" ); else if ( code == 8 ) return i18n( "You are online" ); return i18n( "Unknown network status" ); } QString kio_sysinfoProtocol::icon( const QString & name, int size ) const { QString path = KGlobal::iconLoader()->iconPath( name, -size ); return QString( "" ).arg( htmlQuote(path) ).arg( size ).arg( size ); } QString kio_sysinfoProtocol::iconForDevice( const QString & name ) const { DCOPRef nsd( "kded", "mediamanager" ); nsd.setDCOPClient( m_dcopClient ); QStringList result = nsd.call( "properties", name ); if ( result.isEmpty() ) return QString::null; KMimeType::Ptr mime = KMimeType::mimeType( result[10] ); return mime->icon( QString::null, false ); } void kio_sysinfoProtocol::osInfo() { struct utsname uts; uname( &uts ); m_info[ OS_SYSNAME ] = uts.sysname; m_info[ OS_RELEASE ] = uts.release; m_info[ OS_VERSION ] = uts.version; m_info[ OS_MACHINE ] = uts.machine; m_info[ OS_HOSTNAME ] = uts.nodename; m_info[ OS_USER ] = KUser().loginName(); m_info[ OS_SYSTEM ] = readFromFile( "/etc/SuSE-release" ); m_info[ OS_SYSTEM ].replace("X86-64","x86_64"); } static const KCmdLineOptions options[] = { { "+protocol", "Protocol name", 0 }, { "+pool", "Socket name", 0 }, { "+app", "Socket name", 0 }, KCmdLineLastOption }; extern "C" { int kdemain(int argc, char **argv) { // we need KApp to check the display capabilities putenv(strdup("SESSION_MANAGER=")); KCmdLineArgs::init(argc, argv, "kio_sysinfo", 0, 0, 0, 0); KCmdLineArgs::addCmdLineOptions( options ); KApplication app( false, true ); kdDebug(1242) << "*** Starting kio_sysinfo " << endl; if (argc != 4) { kdDebug(1242) << "Usage: kio_sysinfo protocol domain-socket1 domain-socket2" << endl; exit(-1); } KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); kio_sysinfoProtocol slave( args->arg(1), args->arg(2)); slave.dispatchLoop(); kdDebug(1242) << "*** kio_sysinfo Done" << endl; return 0; } } bool kio_sysinfoProtocol::fillMediaDevices() { DCOPRef nsd( "kded", "mediamanager" ); nsd.setDCOPClient( m_dcopClient ); QStringList devices = nsd.call( "fullList" ); if ( devices.isEmpty() ) return false; kdDebug(1242) << devices << endl; m_devices.clear(); LibHalContext *m_halContext = libhal_ctx_new(); if (!m_halContext) { kdDebug(1242) << "Failed to initialize HAL!" << endl; return false; } DBusError error; dbus_error_init(&error); DBusConnection *dbus_connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error); if (dbus_error_is_set(&error)) { dbus_error_free(&error); libhal_ctx_free(m_halContext); m_halContext = 0; } if (m_halContext) { libhal_ctx_set_dbus_connection(m_halContext, dbus_connection); dbus_error_init(&error); if (!libhal_ctx_init(m_halContext, &error)) { printf("error %s %s\n", error.name, error.message); if (dbus_error_is_set(&error)) dbus_error_free(&error); libhal_ctx_free(m_halContext); m_halContext = 0; } } for ( QStringList::ConstIterator it = devices.constBegin(); it != devices.constEnd(); ++it ) { DiskInfo di; di.id = ( *it ); di.name = *++it; di.label = *++it; di.userLabel = ( *++it ); di.mountable = ( *++it == "true" ); // bool di.deviceNode = ( *++it ); di.mountPoint = ( *++it ); di.fsType = ( *++it ); di.mounted = ( *++it == "true" ); // bool di.baseURL = ( *++it ); di.mimeType = ( *++it ); di.iconName = ( *++it ); if ( di.iconName.isEmpty() ) // no user icon, query the MIME type { KMimeType::Ptr mime = KMimeType::mimeType( di.mimeType ); di.iconName = mime->icon( QString::null, false ); } di.total = di.avail = 0; // calc the free/total space struct statfs sfs; if ( di.mounted && statfs( QFile::encodeName( di.mountPoint ), &sfs ) == 0 ) { di.total = ( unsigned long long )sfs.f_blocks * sfs.f_bsize; di.avail = ( unsigned long long )( getuid() ? sfs.f_bavail : sfs.f_bfree ) * sfs.f_bsize; } else if (m_halContext && di.id.startsWith("/org/freedesktop/Hal/" ) ) { dbus_error_init(&error); di.total = libhal_device_get_property_uint64(m_halContext, di.id.latin1(), "volume.size", &error); if (dbus_error_is_set(&error)) di.total = 0; } // guess the model QString ideName = di.name; ideName.truncate( 3 ); di.model = readFromFile( "/proc/ide/" + ideName + "/model" ); ++it; // skip separator m_devices.append( di ); } libhal_ctx_free(m_halContext); return true; }