Logo Search packages:      
Sourcecode: qps version File versions

details.C

// details.C
//
// This program is free software. See the file COPYING for details.
// Author: Mattias Engdegård, 1997-1999

// TODO:
// 1. geometry mgmt: find reasonable size from the start, make changes in
//    pane size reflect back on window size
// 2. try tabs under the panes instead?

// This is ripped from <linux/tcp.h>. It was just too much of a hassle to
// make it compile with both libc5 and libc6 otherwise.

enum {
  TCP_ESTABLISHED = 1,
  TCP_SYN_SENT,
  TCP_SYN_RECV,
  TCP_FIN_WAIT1,
  TCP_FIN_WAIT2,
  TCP_TIME_WAIT,
  TCP_CLOSE,
  TCP_CLOSE_WAIT,
  TCP_LAST_ACK,
  TCP_LISTEN,
  TCP_CLOSING     /* now a valid state */
};

#include <netdb.h>
#include <netinet/in.h>

#include <qaccel.h>
#include "details.h"
#include "qps.h"
#include "svec.C"


#define TABSHAPE RoundedAbove
//#define TABSBELOW

// default implementation does nothing
void Pane::config_change()
{}

Details::Details(Procinfo *p, Qps *qps, Proc *proc)
    : QWidget(0),
      panes(4),
      pi(p),
      pr(proc)
{
    pi->details = this;
    QString cap;
    cap.sprintf("Process %d (", pi->pid).append(pi->comm);
    cap.append(") - details");
    setCaption(cap);

    tbar = new QTabBar(this);
    tbar->setShape(QTabBar::TABSHAPE);

#ifdef LINUX
    Procinfo::read_sockets();
    if(pi->read_fds())
      addPane(new Sockets(this), "&Sockets");
#endif
#ifdef SOLARIS
    pi->read_fds();
#endif

    if(pi->read_maps())
      addPane(new Maps(this), "&Memory Maps");

    if(pi->fd_files)
      addPane(new Files(this), "&Files");

    if(pi->read_environ())
      addPane(new Environ(this), "&Environment");

    addPane(new AllFields(this), "&All Fields");

    connect(tbar, SIGNAL(selected(int)), SLOT(tab_change(int)));

    // The usual accelerators should work here as well
    QAccel *acc = new QAccel(this);
    acc->connectItem(acc->insertItem(ALT + Key_W),
                 this, SLOT(dismiss()));
    acc->connectItem(acc->insertItem(ALT + Key_Q),
                 qps, SLOT(save_quit()));
    acc->connectItem(acc->insertItem(Key_Space),
                 qps, SLOT(forced_update()));
    acc->connectItem(acc->insertItem(Key_Return),
                 qps, SLOT(forced_update()));

    tbar->adjustSize();
    int minw = tbar->width(), minh = 0;
    for(int i = 0; i < panes.size(); i++) {
      QWidget *w = panes[i]->asQWidget();
#ifdef TABSBELOW
      w->move(0, 0);
#else
      w->move(0, tbar->height() + top_spc + mid_spc);
#endif
      QSize s = w->minimumSize();
      minw = QMAX(minw, s.width());
      minh = QMAX(minh, s.height());
    }
    setMinimumSize(minw, minh + tbar->height() + top_spc + mid_spc);
#ifdef TABSBELOW
    tbar->move(left_spc, height() - tbar->height() - top_spc);
#else
    tbar->move(left_spc, top_spc);
#endif
}

Details::~Details()
{
    if(pi)
      pi->details = 0;
}

void Details::addPane(Pane *pane, const char *label)
{
    QTab *t = new QTab;
    t->setText(label);
    t->setEnabled(TRUE);
    tbar->addTab(t);
    panes.add(pane);
}

void Details::refresh()
{
    int cur = tbar->currentTab();
    if(cur >= 0 && cur < panes.size())
      panes[cur]->refresh();
}

void Details::process_gone()
{
    pi = 0;
    // for now, we just close the window. Another possibility would be
    // to leave it with a "process terminated" note.
    dismiss();
}

// slot: close the window
void Details::dismiss()
{
    emit closed(this);
}

// slot: changed displayed pane
void Details::tab_change(int id)
{
    for(int i = 0; i < panes.size(); i++)
      if(id == i)
          panes[i]->asQWidget()->show();
      else
          panes[i]->asQWidget()->hide();
}

// slot: react to changes in preferences
void Details::config_change()
{
    for(int i = 0; i < panes.size(); i++)
      panes[i]->config_change();
}

void Details::paintEvent(QPaintEvent *)
{
    QPainter p(this);
#ifdef TABSBELOW
    p.setPen(colorGroup().dark());
    int y = tbar->geometry().top();
#else
    p.setPen(white);
    int y = tbar->geometry().bottom();
#endif
    p.drawLine(0, y, width() - 1, y);     // will be partially overwritten
    p.end();
}

void Details::resizeEvent(QResizeEvent *)
{
    int w = width();
    int h = height() - tbar->height() - top_spc;
#ifdef TABSBELOW
    tbar->move(left_spc, h);
#endif
    for(int i = 0; i < panes.size(); i++)
      panes[i]->asQWidget()->resize(w, h - mid_spc);
}

// user closed the window (via window manager)
void Details::closeEvent(QCloseEvent *)
{
    dismiss();
}

SimpleTable::SimpleTable(QWidget *parent, int nfields, TableField *f,
                   int options)
    : HeadedTable(parent, HTBL_HEADING_TOOLTIPS | options),
      fields(f)
{
    setNumCols(nfields);
}

QString SimpleTable::title(int col)
{
    return fields[col].name;
}

int SimpleTable::colWidth(int col)
{
    return fields[col].width;
}

int SimpleTable::alignment(int col)
{
    return fields[col].align;
}

int SimpleTable::leftGap(int col)
{
    return fields[col].gap;
}

QString SimpleTable::tipText(int col)
{
    return fields[col].tooltip;
}

#ifdef LINUX

// declaration of static members
bool Sockets::have_services = FALSE;
QIntDict<char> Sockets::servdict(17);
Lookup *Sockets::lookup = 0;

TableField Sockets::fields[] = {
    {"Fd", 5, 8, AlignRight, "File descriptor"},
    {"Proto", 4, 8, AlignLeft, "Protocol (TCP or UDP)"},
    {"Recv-Q", 9, 8, AlignRight, "Bytes in receive queue"},
    {"Send-Q", 9, 8, AlignRight, "Bytes in send queue"},
    {"Local Addr", -1, 8, AlignLeft, "Local IP address"},
    {"Port", 6, 8, AlignLeft, "Local port"},
    {"Remote Addr", -1, 8, AlignLeft, "Remote IP address"},
    {"Port", 6, 8, AlignLeft, "Remote port"},
    {"State", 18, 8, AlignLeft, "Connection state"}
};

Sockets::Sockets(QWidget *parent)
         : SimpleTable(parent, SOCKFIELDS, fields), Pane(this)
{
    if(!lookup)
      lookup = new Lookup();
    connect(lookup, SIGNAL(resolved(unsigned)),
          SLOT(update_hostname(unsigned)));
    doing_lookup = Qps::hostname_lookup;
    refresh();
    // compute total width = window width
    int totw = 0;
    for(int i = 0; i < SOCKFIELDS; i++)
      totw += actualColWidth(i);
    resize(totw, 200);
}

Sockets::~Sockets()
{}

QString Sockets::text(int row, int col)
{
    Procinfo *p = procinfo();
    if(!p->sock_inodes)
      refresh_sockets();
    SockInode sock_ino = (*p->sock_inodes)[row];
    Sockinfo *si = Procinfo::socks[sock_ino.inode];
    if(!si)
      return "";        // process gone, return empty string

    QString s;
    switch(col) {
    case FD:
      s.setNum(sock_ino.fd);
      break;

    case PROTO:
        s = (si->proto == Sockinfo::TCP) ? "tcp" : "udp";
      break;

    case RECVQ:
      s.setNum(si->rx_queue);
      break;

    case SENDQ:
      s.setNum(si->tx_queue);
      break;

    case LOCALADDR:
      s = ipAddr(si->local_addr);
      break;

    case LOCALPORT:
      if(Qps::service_lookup) {
          const char *serv = servname(si->local_port);
          if(serv) {
            s = serv;
            break;
          }
      }
      s.setNum(si->local_port);
      break;

    case REMOTEADDR:
      s = ipAddr(si->rem_addr);
      break;

    case REMOTEPORT:
      if(Qps::service_lookup) {
          const char *serv = servname(si->rem_port);
          if(serv) {
            s = serv;
            break;
          }
      }
      s.setNum(si->rem_port);
      break;

    case STATE:
      switch(si->st) {
      case TCP_ESTABLISHED:
          s = "ESTABLISHED"; break;
      case TCP_SYN_SENT:
          s = "SYN_SENT"; break;
      case TCP_SYN_RECV:
          s = "SYN_RECV"; break;
      case TCP_FIN_WAIT1:
          s = "FIN_WAIT1"; break;
      case TCP_FIN_WAIT2:
          s = "FIN_WAIT2"; break;
      case TCP_TIME_WAIT:
          s = "TIME_WAIT"; break;
      case TCP_CLOSE:
          s = "CLOSE"; break;
      case TCP_CLOSE_WAIT:
          s = "CLOSE_WAIT"; break;
      case TCP_LAST_ACK:
          s = "LAST_ACK"; break;
      case TCP_LISTEN:
          s = "LISTEN"; break;
      case TCP_CLOSING:
          s = "CLOSING"; break;
      default:
          s = "UNKNOWN"; break;
      }
      break;
    }
    return s;
}

QString Sockets::ipAddr(unsigned addr)
{
    unsigned a = htonl(addr);
    QString s;
    if(doing_lookup) {
      s = lookup->hostname(addr);
      if(s.isNull()) {
          s.sprintf("(%d.%d.%d.%d)",
                  (a >> 24) & 0xff,
                  (a >> 16) & 0xff,
                  (a >> 8) & 0xff,
                  a & 0xff);
      }
    } else {
      if(a == 0)
          s = "*";
      else
          s.sprintf("%d.%d.%d.%d",
                  (a >> 24) & 0xff,
                  (a >> 16) & 0xff,
                  (a >> 8) & 0xff,
                  a & 0xff);
    }
    return s;
}

void Sockets::refresh_window()
{
    Procinfo *p = procinfo();
    if(!p) return;
    int rows = p->sock_inodes->size();
    invalidateCache();
    resetWidths();
    setNumRows(rows);
    setNumCols(SOCKFIELDS);
    repaintAll();
}

void Sockets::refresh()
{
    if(refresh_sockets())
      refresh_window();
}

// return true if sockets could be read successfully, false otherwise
bool Sockets::refresh_sockets()
{
    Procinfo::read_sockets();
    return procinfo()->read_fds();
}

// react to changes in preferences
void Sockets::config_change()
{
    if(doing_lookup != Qps::hostname_lookup) {
      doing_lookup = Qps::hostname_lookup;
      setNumCols(SOCKFIELDS);
      for(int col = 0; col < SOCKFIELDS; col++)
          widthChanged(col);
      updateTableSize();
      repaintAll();
    }
}

// slot: called when a host name has been looked up
void Sockets::update_hostname(unsigned addr)
{
    invalidateCache();
    if(widthChanged(REMOTEADDR) || widthChanged(LOCALADDR)) {
      updateTableSize();
      repaintAll();
    } else {
      // just repaint some rows
      Procinfo *p = procinfo();
      if(!p->sock_inodes) {
          Procinfo::read_sockets();
          p->read_fds();
      }
      int rows = p->sock_inodes->size();
      for(int i = 0; i < rows; i++) {
          int inode = (*p->sock_inodes)[i].inode;
          Sockinfo *si = Procinfo::socks[inode];
          if(si->local_addr == addr)
            updateCell(i, LOCALADDR);
          if(si->rem_addr == addr)
            updateCell(i, REMOTEADDR);
      }
    }
}

const char *Sockets::servname(unsigned port)
{
    if(!have_services) {
      have_services = TRUE;
      // fill servdict from /etc/services (just try once)
      setservent(1);
      struct servent *s;
      while((s = getservent()) != 0) {
          unsigned short hport = ntohs((unsigned short)s->s_port);
          if(!servdict[hport]) {
            servdict.insert(hport, strdup(s->s_name));
            if(servdict.count() > servdict.size() * 3)
                servdict.resize(servdict.count());
          }
      }
      endservent();
    }
    return servdict[port];
}

#endif // LINUX

#ifdef SOLARIS

// Stupid code to make up for moc:s inability to grok preprocessor conditionals
void Sockets::refresh() {}
QString Sockets::text(int, int) { return 0; }
void Sockets::config_change() {}
Sockets::~Sockets() {}
void Sockets::update_hostname(unsigned int) {}

#endif

TableField Maps::fields[] = {
    {"Address Range", -1, 8, AlignLeft, "Mapped addresses (hex)"},
    {"Size", 8, 8, AlignRight, "Kbytes mapped (dec)"},
    {"Perm", 5, 8, AlignLeft, "Permission flags"},
    {"Offset", -1, 8, AlignRight, "File offset at start of mapping (hex)"},
    {"Device", 8, 8, AlignLeft, "Major,Minor device numbers (dec)"},
    {"Inode", 10, 8, AlignRight, "Inode number (dec)"},
    {"File", -1, 8, AlignLeft, "File name (if available)"}
};


Maps::Maps(QWidget *parent)
    : SimpleTable(parent, MAPSFIELDS, fields), Pane(this)
{
    // monospaced font looks best in the table body since it contains
    // hex numerals and flag fields. Pick Courier (why not)
    setBodyFont(QFont("Courier", font().pointSize()));
    
    refresh();
    // compute total width = window width
    int totw = 0;
    for(int i = 0; i < MAPSFIELDS; i++)
      totw += actualColWidth(i);
    resize(totw + 20, 200);
}

Maps::~Maps()
{}

QString Maps::text(int row, int col)
{
    Procinfo *p = procinfo();
    if(!p->maps) {
      refresh_maps();
      if(!p->maps)
          return "";
    }
    Mapsinfo *mi = (*p->maps)[row];

    QString s;
    switch(col) {
    case ADDRESS:
      s.sprintf((sizeof(void*) == 4) ? "%08lx-%08lx" : "%016lx-%016lx",
              mi->from, mi->to);
      break;
    case SIZE:
      s.setNum((mi->to - mi->from) >> 10);
      break;
    case PERM:
      s = "    ";
      for(int i = 0; i < 4; i++)
          s[i] = mi->perm[i];
      break;
    case OFFSET:
      s.sprintf((sizeof(void*) == 4) ? "%08lx" : "%016lx",
              mi->offset);
      break;
    case DEVICE:
      s.sprintf("%3u,%3u", mi->major, mi->minor);
      break;
    case INODE:
      s.setNum(mi->inode);
      break;
    case FILENAME:
      s = mi->filename;
      break;
    }
    return s;
}

void Maps::refresh_window()
{
    if(!procinfo()) return;
    int rows = procinfo()->maps->size();
    invalidateCache();
    resetWidths();
    setNumRows(rows);
    setNumCols(MAPSFIELDS);
    repaintAll();
}

void Maps::refresh()
{
    if(refresh_maps())
      refresh_window();
}

bool Maps::refresh_maps()
{
    return procinfo()->read_maps();
}


TableField Files::fields[] = {
    {"Fd", 5, 8, AlignRight, "File descriptor"},
#ifdef LINUX
    {"Mode", 3, 8, AlignLeft, "Open mode"},
#endif
    {"Name", -1, 8, AlignLeft, "File name (if available)"}
};

Files::Files(QWidget *parent)
    : SimpleTable(parent, FILEFIELDS, fields), Pane(this)
{
    refresh();
    // compute total width = window width
    int totw = 0;
    for(int i = 0; i < FILEFIELDS; i++)
      totw += actualColWidth(i);
    resize(totw, 200);
}

Files::~Files()
{}

void Files::refresh()
{
    if(refresh_fds())
      refresh_window();
}

// return true if fds could be read successfully, false otherwise
bool Files::refresh_fds()
{
    return procinfo()->read_fds();
}

void Files::refresh_window()
{
    Procinfo *p = procinfo();
    if(!p) return;
    int rows = p->fd_files->size();
    invalidateCache();
    resetWidths();
    setNumRows(rows);
    setNumCols(FILEFIELDS);
    repaintAll();
}

QString Files::text(int row, int col)
{
    Procinfo *p = procinfo();
    if(!p->fd_files) {
      refresh_fds();
      if(!p->fd_files)
          return "";
    }
    if(row >= p->fd_files->size())
      return "";

    Fileinfo *fi = (*p->fd_files)[row];
    QString s;
    switch(col) {
    case FILEDESC:
      s.setNum(fi->fd);
      break;

#ifdef LINUX
    case FILEMODE:
      if(fi->mode & OPEN_READ) s.append("R");
      if(fi->mode & OPEN_WRITE) s.append("W");
      break;
#endif

    case FILENAME:
      s = fi->filename;
      break;
    }
    return s;
}

TableField Environ::fields[] = {
    {"Variable", -1, 8, AlignLeft, "Variable name"},
    {"Value", -1, 8, AlignLeft, "Variable value"}
};

Environ *Environ::static_env = 0;

Environ::Environ(QWidget *parent)
    : SimpleTable(parent, ENVFIELDS, fields, HTBL_HEADING_CLICK), Pane(this),
      rev(FALSE)
{
    refresh();
    // compute total width = window width
    int totw = 0;
    for(int i = 0; i < ENVFIELDS; i++)
      totw += actualColWidth(i);
    resize(totw + 20, 200);
    connect(this, SIGNAL(titleClicked(int)), SLOT(sort_change(int)));
}

Environ::~Environ()
{}

QString Environ::text(int row, int col)
{
    Procinfo *p = procinfo();
    if(!p->environ) {
      refresh_environ();
      if(!p->environ)
          return "";
      sort();
    }
    NameValue nv = (*p->environ)[row];

    return (col == ENVNAME) ? nv.name : nv.value;
}

void Environ::refresh_window()
{
    if(!procinfo()) return;
    int rows = procinfo()->environ->size();
    invalidateCache();
    resetWidths();
    setNumRows(rows);
    setNumCols(ENVFIELDS);
    repaintAll();
}

void Environ::refresh()
{
    if(refresh_environ()) {
      sort();
      refresh_window();
    }
}

bool Environ::refresh_environ()
{
     return procinfo()->read_environ();
}

void Environ::sort_change(int col)
{
    Procinfo *p = procinfo();
    if(!p->environ) {
      refresh_environ();
      if(!p->environ)
          return;
    }
    rev = (col == sortedCol()) ? !rev : FALSE;
    setSortedCol(col);
    sort();
    refresh_window();
}

// sort table according to current settings
void Environ::sort()
{
    if(sortedCol() >= 0) {
      static_env = this;
      procinfo()->environ->sort(compare);
    }
}

int Environ::compare(const NameValue *a, const NameValue *b)
{
    Environ *e = Environ::static_env;
    int r;
    if(e->sortedCol() == ENVNAME)
      r = strcmp(a->name, b->name);
    else
      r = strcmp(a->value, b->value);
    return e->rev ? -r : r;
}


TableField AllFields::fields[] = {
    {"Field", -1, 8, AlignLeft, "Field name"},
    {"Description", -1, 8, AlignLeft, "Field description"},
    {"Value", -1, 8, AlignLeft, "Field value"}
};

AllFields::AllFields(QWidget *parent)
    : SimpleTable(parent, FIELDSFIELDS, fields), Pane(this)
{
    refresh();
    // compute total width = window width
    int totw = 0;
    for(int i = 0; i < FIELDSFIELDS; i++)
      totw += actualColWidth(i);
    resize(totw + 20, 200);
}

AllFields::~AllFields()
{}

QString AllFields::text(int row, int col)
{
    Category *cat = ((Details *)parent())->proc()->allcats[row];
    QString s;
    switch(col) {
    case FIELDNAME:
      s = cat->name;
      break;
    case FIELDDESC:
      s = cat->help;
      break;
    case FIELDVALUE:
      s = cat->string(procinfo());
      break;
    }
    return s;
}

void AllFields::refresh_window()
{
    if(!procinfo()) return;
    invalidateCache();
    resetWidths();
    setNumRows(((Details *)parent())->proc()->allcats.size());
    setNumCols(FIELDSFIELDS);
    repaintAll();
}

void AllFields::refresh()
{
    refresh_window();
}


Generated by  Doxygen 1.6.0   Back to index