#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <dirent.h>
/* -------------------------------------------------------------------------
fusage -
- William Favorite, wfavorite@tablespace.net
- Released under GPL.
Version History:
0.2.0 - - Original creation.
0.3.0 - 6/1/4 - Additional command line parameters / features
0.4.0 - 6/1/4 - cmdline mode now displays the entirety of /proc/X/cmdline
0.5.0 - 9/28/4 - Included header info with the -h switch
0.6.0 - 1/19/6 - Minor cleanup prior to posting on web site.
*/
/* -------------------------------------------------------------------------
Set some defines
*/
/* Version string - self explanatory */
#define VERSION_STRING "0.6.0"
/* What type of summary information to display */
#define SUMMARY_NONE 0
#define SUMMARY_NORMAL 1
#define SUMMARY_ONLY 2
/* Should we truncate process names in normal (! -c) output. */
#define TRUNCATE_NAMES
/* Should we show the "sum" total of files. This will conflict with the number
of kernel files - as threads have the same file-handles open(?). Turn this
off to limit confusing elements of the display. */
#define SUM_TOTAL
/* 1 is on, 0 is off. Whatever this is, -h will toggle the value. */
#define DEFAULT_HEADER 1
/* -------------------------------------------------------------------------
Globals
*/
/* This is used by PrintCmdLine(). I declared it as a global so that it does
not need to be instantiated each time we call the function. */
char file[1024];
/* -------------------------------------------------------------------------
Prototypes, etc...
*/
/* Struct - proc fd count */
struct procfdc
{
unsigned long pid;
char name[256];
int fdcnt;
struct procfdc *next;
};
/* -------------------------------------------------------------------------
Checks to see if the string is comprised entirely of numbers.
*/
int isanumber(const char *cp)
{
int i = 0;
while(cp[i] != 0)
{
if((cp[i] < '0') || (cp[i] > '9'))
return(0);
i++;
}
return(1);
}
/* -------------------------------------------------------------------------
Print the data from /proc/sys/fs/file-nr
*/
void PrintSysFileStat(void);
/* -------------------------------------------------------------------------
Print the command line from /proc/<pid>/cmdline
*/
void PrintCmdLine(unsigned long pid);
/* ------------------------------------------------------------------------- */
int main(int argc, char *argv[])
{
/**** Declare Variables ****/
DIR *procd; /* Directory handle */
DIR *fdd;
struct dirent *proce; /* Return structure from readdir() */
struct dirent *fde;
char statfname[1024]; /* String to hold filename of /proc/nnn/stat */
char fddirname[1024];
FILE *statf; /* File (stat) handle */
char statbuf[1024]; /* Holder for stat contents */
int i, j; /* Counter for stepping through stat buffer */
int cnt;
int got;
unsigned long tmppid;
struct procfdc *basepde;
struct procfdc *thispde, *lastpde, *sortpde;
char tmpstr[256];
unsigned long totalcnt;
int linesout; /* How many lines to output */
int showheader; /* Show the header or not */
int showall; /* Boolean - sets linesout to ALL when true */
int cmdline; /* 0 = show command, 1 = show entire command line. */
int summary; /* What type of summary info to display. See the
SUMMARY_xxxxxx defines. */
/**** Set Variable Defaults ****/
summary = SUMMARY_NORMAL;
showheader = DEFAULT_HEADER;
basepde = NULL;
totalcnt = 0;
linesout = 10;
cmdline = 0;
showall = 1; /* The default line "weight" is 1. When we print a
line, we subtract this weight from the lines
we have to print. If we want to print them all
(fusage -a) then we will set this to 0. */
/**** Process Command Line Options ****/
if(argc > 1)
{
i = 1;
while(i < argc)
{
if(isanumber(argv[i]))
{
linesout = atoi(argv[i]);
if(linesout < 0)
linesout *= -1;
}
else
{
if(0 == strcmp(argv[i], "-v"))
{
printf("Version %s\n", VERSION_STRING);
return(0);
}
if(0 == strcmp(argv[i], "-a"))
{
showall = 0;
}
if(0 == strcmp(argv[i], "-c"))
{
cmdline = 1;
}
if(0 == strcmp(argv[i], "-d"))
{
if(showheader)
showheader = 0;
else
showheader = 1;
}
if(0 == strcmp(argv[i], "-s"))
{
summary = SUMMARY_ONLY;
}
if(0 == strcmp(argv[i], "-S"))
{
summary = SUMMARY_NONE;
}
if((0 == strcmp(argv[i], "-h")) |
(0 == strcmp(argv[i], "-H")) |
(0 == strcmp(argv[i], "--help")))
{
printf("fusage - An open file handle counter\n");
printf(" Version %s\n", VERSION_STRING);
printf(" William Favorite <wfavorite@tablespace.net>\n");
printf("\n");
printf(" options:\n");
printf(" -v Show version and exit.\n");
printf(" -h Show help and exit.\n");
printf(" -c Show entire command line on output.\n");
printf(" -s Only show summary info.\n");
printf(" -S Do not show summary info.\n");
if(DEFAULT_HEADER == 1)
printf(" -d Disable column descriptors.\n");
else
printf(" -d Show column descriptors.\n");
printf(" X (Where X is a positive integer) Show X lines of output. Default: 10\n");
printf(" -a Show all output, not just X lines.\n");
return(0);
}
} /* if(isanumber()) else */
i++;
} /* while(i < argc) */
}
/**** Start processing /proc info ****/
if(NULL == (procd = opendir("/proc")))
{
fprintf(stderr, "ERROR: Unable to open /proc.\n");
return(1);
}
while(NULL != (proce = readdir(procd)))
{
if(isanumber(proce->d_name))
{
got = 0;
sprintf(statfname, "/proc/%s/stat", proce->d_name);
sprintf(fddirname, "/proc/%s/fd", proce->d_name);
tmppid = 0;
tmppid = atol(proce->d_name);
if(tmppid > 0)
got++;
/*** Read the binary - process name ***/
if(NULL != (statf = fopen(statfname, "r")))
{
if(NULL != (fgets(statbuf, 1024, statf)))
{
i = 0;
j = 0;
/* Step through to the beginning of the binary name */
while(statbuf[i++] != '(');
/* Print out all the characters in the binary name */
while(statbuf[i] != ')')
tmpstr[j++] = statbuf[i++];
tmpstr[j++] = 0;
}
if(j > 1)
got++;
fclose(statf);
}
/*** Count the number of open files ***/
if(NULL != (fdd = opendir(fddirname)))
{
cnt = 0;
while(NULL != (fde = readdir(fdd)))
{
cnt++;
totalcnt++;
}
got++;
closedir(fdd);
}
if(got == 3)
{
thispde = (struct procfdc *)malloc(sizeof(struct procfdc));
thispde->pid = tmppid;
strncpy(thispde->name, tmpstr, 256);
thispde->fdcnt = cnt;
thispde->next = NULL;
if(basepde == NULL)
{
/* ONLY */
basepde = thispde;
}
else
{
if(thispde->fdcnt >= basepde->fdcnt)
{
/* FIRST */
thispde->next = basepde;
basepde = thispde;
}
else
{
sortpde = basepde;
lastpde = NULL;
while(thispde->fdcnt < sortpde->fdcnt)
{
if(sortpde->next == NULL)
break;
lastpde = sortpde;
sortpde = sortpde->next;
}
if(lastpde == NULL)
basepde = thispde;
else
lastpde->next = thispde;
thispde->next = sortpde;
//sortpde->next = thispde;
}
}
//thispde->next = basepde;
//basepde = thispde;
}
} /* if(isanumber()) */
} /* while(readdir()) */
closedir(procd);
/**** Display Information ****/
if(summary != SUMMARY_ONLY)
{
if((basepde) && (showheader))
{
printf("PID Process Name File Count\n");
}
thispde = basepde;
cnt = 0;
while((thispde != NULL)&(cnt < linesout))
{
#ifdef TRUNCATE_NAMES
thispde->name[13] = 0;
#endif
printf("%-8lu %-17s %-4d", thispde->pid, thispde->name, thispde->fdcnt);
if(cmdline)
PrintCmdLine(thispde->pid);
printf("\n");
thispde = thispde->next;
cnt += showall;
}
}
if(summary)
{
#ifdef SUM_TOTAL
printf("Total files open: %lu\n", totalcnt);
#endif
PrintSysFileStat();
}
return(0);
}
/* ======================================================================== */
void PrintSysFileStat(void)
{
FILE *filenr;
unsigned long sysopenf = 0;
unsigned long sysmaxf = 0;
unsigned long sysallocf = 0;
unsigned long sysfreef = 0;
if(NULL == (filenr = fopen("/proc/sys/fs/file-nr", "r")))
return;
if(3 == fscanf(filenr, "%lu %lu %lu", &sysallocf, &sysfreef, &sysmaxf))
{
fclose(filenr);
sysopenf = sysallocf - sysfreef;
printf("Kernel Open: %-8lu Max: %-8lu Alloc: %-8lu Free: %-8lu\n",
sysopenf,
sysmaxf,
sysallocf,
sysfreef);
}
}
/* ======================================================================== */
void PrintCmdLine(unsigned long pid)
{
FILE *f;
int i = 0;
sprintf(file, "/proc/%lu/cmdline", pid);
if(NULL == (f = fopen(file, "r")))
return;
#ifdef UNDEFINED
/* This tends to runcate output. */
if(fgets(file, 1024, f))
printf(" %s", file);
#else
/* This will not truncate output in the shell */
while(EOF != (i = fgetc(f)))
putchar(i);
#endif
fclose(f);
}