OpenFilesScreen.c 5.8 KB
/*
htop - OpenFilesScreen.c
(C) 2005-2006 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/

#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <stdbool.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>

#include "OpenFilesScreen.h"
#include "ProcessList.h"
#include "Process.h"
#include "ListItem.h"
#include "Panel.h"
#include "FunctionBar.h"

/*{

typedef struct OpenFiles_ProcessData_ {
   char* data[256];
   struct OpenFiles_FileData_* files;
   int error;
} OpenFiles_ProcessData;

typedef struct OpenFiles_FileData_ {
   char* data[256];
   struct OpenFiles_FileData_* next;
} OpenFiles_FileData;

typedef struct OpenFilesScreen_ {
   Process* process;
   pid_t pid;
   Panel* display;
   FunctionBar* bar;
   bool tracing;
} OpenFilesScreen;

}*/

static const char* ofsFunctions[] = {"Refresh", "Done   ", NULL};

static const char* ofsKeys[] = {"F5", "Esc"};

static int ofsEvents[] = {KEY_F(5), 27};

OpenFilesScreen* OpenFilesScreen_new(Process* process) {
   OpenFilesScreen* this = (OpenFilesScreen*) malloc(sizeof(OpenFilesScreen));
   this->process = process;
   this->display = Panel_new(0, 1, COLS, LINES-3, LISTITEM_CLASS, true, ListItem_compare);
   this->bar = FunctionBar_new(ofsFunctions, ofsKeys, ofsEvents);
   this->tracing = true;
   if (Process_isThread(process))
      this->pid = process->tgid;
   else
      this->pid = process->pid;
   return this;
}

void OpenFilesScreen_delete(OpenFilesScreen* this) {
   Panel_delete((Object*)this->display);
   FunctionBar_delete((Object*)this->bar);
   free(this);
}

static void OpenFilesScreen_draw(OpenFilesScreen* this) {
   attrset(CRT_colors[METER_TEXT]);
   mvhline(0, 0, ' ', COLS);
   mvprintw(0, 0, "Files open in process %d - %s", this->pid, this->process->comm);
   attrset(CRT_colors[DEFAULT_COLOR]);
   Panel_draw(this->display, true);
   FunctionBar_draw(this->bar, NULL);
}

static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
   char command[1025];
   snprintf(command, 1024, "lsof -P -p %d -F 2> /dev/null", pid);
   FILE* fd = popen(command, "r");
   OpenFiles_ProcessData* process = calloc(sizeof(OpenFiles_ProcessData), 1);
   OpenFiles_FileData* file = NULL;
   OpenFiles_ProcessData* item = process;
   bool anyRead = false;
   if (!fd) {
      process->error = 127;
      return process;
   }
   while (!feof(fd)) {
      int cmd = fgetc(fd);
      if (cmd == EOF && !anyRead)
         break;
      anyRead = true;
      char* entry = malloc(1024);
      if (!fgets(entry, 1024, fd)) {
         free(entry);
         break;
      }
      char* newline = strrchr(entry, '\n');
      *newline = '\0';
      if (cmd == 'f') {
         OpenFiles_FileData* nextFile = calloc(sizeof(OpenFiles_ProcessData), 1);
         if (file == NULL) {
            process->files = nextFile;
         } else {
            file->next = nextFile;
         }
         file = nextFile;
         item = (OpenFiles_ProcessData*) file;
      }
      item->data[cmd] = entry;
   }
   process->error = pclose(fd);
   return process;
}

static void OpenFilesScreen_scan(OpenFilesScreen* this) {
   Panel* panel = this->display;
   int idx = MAX(Panel_getSelectedIndex(panel), 0);
   Panel_prune(panel);
   OpenFiles_ProcessData* process = OpenFilesScreen_getProcessData(this->pid);
   if (process->error == 127) {
      Panel_add(panel, (Object*) ListItem_new("Could not execute 'lsof'. Please make sure it is available in your $PATH.", 0));
   } else if (process->error == 1) {
      Panel_add(panel, (Object*) ListItem_new("Failed listing open files.", 0));
   } else {
      OpenFiles_FileData* file = process->files;
      while (file) {
         char entry[1024];
         sprintf(entry, "%5s %4s %10s %10s %10s %s",
            file->data['f'] ? file->data['f'] : "",
            file->data['t'] ? file->data['t'] : "",
            file->data['D'] ? file->data['D'] : "",
            file->data['s'] ? file->data['s'] : "",
            file->data['i'] ? file->data['i'] : "",
            file->data['n'] ? file->data['n'] : "");
         Panel_add(panel, (Object*) ListItem_new(entry, 0));
         for (int i = 0; i < 255; i++)
            if (file->data[i])
               free(file->data[i]);
         OpenFiles_FileData* old = file;
         file = file->next;
         free(old);
      }
      for (int i = 0; i < 255; i++)
         if (process->data[i])
            free(process->data[i]);
   }
   free(process);
   Vector_sort(panel->items);
   Panel_setSelected(panel, idx);
}

void OpenFilesScreen_run(OpenFilesScreen* this) {
   Panel* panel = this->display;
   Panel_setHeader(panel, "   FD TYPE     DEVICE       SIZE       NODE NAME");
   OpenFilesScreen_scan(this);
   OpenFilesScreen_draw(this);
   //CRT_disableDelay();
   
   bool looping = true;
   while (looping) {
      Panel_draw(panel, true);
      int ch = getch();
      if (ch == KEY_MOUSE) {
         MEVENT mevent;
         int ok = getmouse(&mevent);
         if (ok == OK)
            if (mevent.y >= panel->y && mevent.y < LINES - 1) {
               Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV);
               ch = 0;
            } if (mevent.y == LINES - 1)
               ch = FunctionBar_synthesizeEvent(this->bar, mevent.x);
      }
      switch(ch) {
      case ERR:
         continue;
      case KEY_F(5):
         clear();
         OpenFilesScreen_scan(this);
         OpenFilesScreen_draw(this);
         break;
      case '\014': // Ctrl+L
         clear();
         OpenFilesScreen_draw(this);
         break;
      case 'q':
      case 27:
      case KEY_F(10):
         looping = false;
         break;
      case KEY_RESIZE:
         Panel_resize(panel, COLS, LINES-2);
         OpenFilesScreen_draw(this);
         break;
      default:
         Panel_onKey(panel, ch);
      }
   }
   //CRT_enableDelay();
}