import java.io.*;
import java.util.Vector;
import java.util.Date;


public class Process{

    static { System.loadLibrary("LinuxNative"); }
    /** All fields, in order, from /proc/[PID]/stat **/
    long pid = 0;          /** process ID **/
    String command = "";   /** simple name of executeble **/
    String state = "";     /** stae of process (only in 2.1 kernels)!!!!!! **/
    long ppid = 0;         /** parent process ID **/
    long pgrp = 0;         /** process group ID **/
    long session = 0;      /** session ID **/
    long tty = 0;          /** minor device number of tty **/
    long tty_pgrp = 0;     /** controlling tty process group ID **/
    long flags = 0;        /** flags as in long format F filed **/
    long min_flt = 0;      /** number of minor page faults **/
    long cmin_flt = 0;     /** cumulative number of minor page faults **/
    long maj_flt = 0;      /** number of major page faults **/
    long cmaj_flt = 0;     /** cumulative number of major page faults **/
    long utime = 0;        /** user time **/
    long stime = 0;        /** system time **/
    long cutime = 0;       /** cumulative user time **/
    long cstime = 0;       /** cumulative system time **/
    long priority = 0;     /** priority, time in HZ of process's possible timeslice **/
    long nice = 0;         /** nice value **/
    long timeout = 0;      /** timeout **/
    long it_real_value = 0;/** ? **/
    long start_time = 0;   /** time process was started **/
    long vsize = 0;        /** total virtual memory size **/
    long rss = 0;          /** resident set size, kb of program in memory **/
    long rlim = 0;         /** ? resident pages ? **/
    long start_code = 0;   /** ? starting code address in memory **/ 
    long end_code = 0;     /** ? ending code address in memory **/ 
    long start_stack = 0;  /** ? address of stack ? **/
    long esp = 0;          /** ? segment pointer  **/
    long eip = 0;          /** ? instruction pointer **/
    long signal = 0;       /** signal ? **/
    long blocked = 0;      /** blocked **/
    long sigignore = 0;    /** ignore signal set **/
    long sigcatch = 0;     /** catch signal set **/
    long wchan = 0;        /** kernel  function  where the process is sleeping (/etc/psdatabase) **/
    long nswap = 0;        /** Kilobytes on swap device **/
    long cnswap = 0;       /** cumulative Kilobytes on swap device **/
    long exit_signal = 0;  /** exit signal **/

    /** All fields, in order, from /proc/[PID]/statm has to do with pages **/
    long size = 0;         /** Kb in total memory **/
    long resident = 0;     /** Kb resident in memory **/
    long share = 0;        /** Kb in shared memory **/
    long trs = 0;          /** text resident size **/
    long lrs = 0;          /** library resident size **/
    long drs = 0;          /** stack resident size **/
    long dt = 0;           /** dirty **/

    /** misc fields, from /proc/[PID]/status **/
    String stateL = null;  /** long version of state **/
    long uid = 0;          /** user ID **/
    long gid = 0;          /** group ID **/
    long vmsize = 0;       /** total vm size **/
    long vmlck = 0;        /** lock size **/
    long vmrss = 0;        /** resident set size **/
    long vmdata = 0;       /** data size **/
    long vmstk = 0;        /** stack size **/
    long vmexe = 0;        /** exe size **/
    long vmlib = 0;        /** library size **/

    String[] stat = null; 
    String[] statm = null;
    String[] status = null;
    
    //------------------Vectors of open file descriptors-----------------
    Vector sockets = new Vector();
    Vector files = new Vector();
    Vector pipes = new Vector();
    Vector devices = new Vector();
    double uptime = 0;
    int HZ = 0;
    long MemTotal = 0;
    float MemUsage = 0;
    //-------------------------------------------------------------------

    public Process(String pid){
	this.pid = (Long.valueOf(pid)).longValue();           //set the PID value and covnet from string to long
	update();
	try {
	              //parse the /proc/[PID]/stat array which contains stats on a process
	    command = stat[1];           //set the command name
	    state = stat[2];           //set the state
	    ppid = (Long.valueOf(stat[3])).longValue();           //set parent id and covnet from string to long
	    pgrp = (Long.valueOf(stat[4])).longValue();           //set the process group and convert from string to long
	    session = (Long.valueOf(stat[5])).longValue();            //yada yada yada
	    tty =(Long.valueOf(stat[6])).longValue(); 
	    tty_pgrp =(Long.valueOf(stat[7])).longValue(); 
	    flags =(Long.valueOf(stat[8])).longValue(); 
	    min_flt =(Long.valueOf(stat[9])).longValue(); 
	    cmin_flt =(Long.valueOf(stat[10])).longValue(); 
	    maj_flt =(Long.valueOf(stat[11])).longValue(); 
	    cmaj_flt =(Long.valueOf(stat[12])).longValue(); 
	    utime =(Long.valueOf(stat[13])).longValue(); 
	    stime =(Long.valueOf(stat[14])).longValue(); 
	    cutime =(Long.valueOf(stat[15])).longValue(); 
	    cstime =(Long.valueOf(stat[16])).longValue(); 
	    priority =(Long.valueOf(stat[17])).longValue(); 
	    nice = (Long.valueOf(stat[18])).longValue(); 
	    timeout =(Long.valueOf(stat[19])).longValue(); 
	    it_real_value =(Long.valueOf(stat[20])).longValue(); 
	    start_time = (Long.valueOf(stat[21])).longValue(); 
	    vsize = (Long.valueOf(stat[22])).longValue(); 
	    rss = (Long.valueOf(stat[23])).longValue(); 
	    rlim = (Long.valueOf(stat[24])).longValue(); 
	    start_code = (Long.valueOf(stat[25])).longValue(); 
	    end_code =(Long.valueOf(stat[26])).longValue(); 
	    start_stack =(Long.valueOf(stat[27])).longValue(); 
	    esp = (Long.valueOf(stat[28])).longValue(); 
	    eip = (Long.valueOf(stat[29])).longValue(); 
	    signal = (Long.valueOf(stat[30])).longValue(); 
	    blocked = (Long.valueOf(stat[31])).longValue(); 
	    sigignore =(Long.valueOf(stat[32])).longValue(); 
	    sigcatch = (Long.valueOf(stat[33])).longValue(); 
	    wchan = (Long.valueOf(stat[34])).longValue(); 
	    nswap = (Long.valueOf(stat[35])).longValue(); 
	    cnswap =(Long.valueOf(stat[36])).longValue(); 
	    exit_signal =(Long.valueOf(stat[37])).longValue(); 
	    
	    //parse the statm array which contains process memory status
	    size = (Long.valueOf(statm[0])).longValue(); 
	    resident = (Long.valueOf(statm[1])).longValue(); 
	    share = (Long.valueOf(statm[2])).longValue(); 
	    trs = (Long.valueOf(statm[3])).longValue(); 
	    lrs = (Long.valueOf(statm[4])).longValue(); 
	    drs = (Long.valueOf(statm[5])).longValue(); 
	    dt =(Long.valueOf(statm[6])).longValue(); 
	    
	    //parse status array this is not really nessecary, but it's there. It is a newer human readable version of some of the stat and statm information
	    stateL = status[4];
	    uid = (Long.valueOf(status[10])).longValue();
	    gid = (Long.valueOf(status[15])).longValue();
	    //the follwoing don't always exist in the status file, if a processs is really just a kernel fork (klogd etc)
	    vmsize = (Long.valueOf(status[21])).longValue();
	    vmlck =(Long.valueOf(status[25])).longValue();
	    vmrss =(Long.valueOf(status[29])).longValue();
	    vmdata =(Long.valueOf(status[33])).longValue();
	    vmstk =(Long.valueOf(status[37])).longValue();
	    vmexe =(Long.valueOf(status[41])).longValue();
	    vmlib =(Long.valueOf(status[45])).longValue();
	   
	}
	/** if a process is really just a part of the kernel, the below information doesnot exist so it will throw an exception, which we dutifully ignore **/
	catch (NumberFormatException nfe){
	    //initialize all this to zero just for arguments sake
		vmsize = 0;
		vmlck =0;
		vmrss =0;
		vmdata =0;
		vmstk =0;
		vmexe =0;
		vmlib =0; 
	}
    }
    //return a vector of sockets associated with this process (tcp,udp,raw,stream/unix)
    public Vector getSockets(){
	return sockets;
    }
    //return a vector of file names associated with this process (mypaper.txt)
    public Vector getFiles(){
	return files;
    }
    //return a vector of pipes associated with this process (thread communication, or real pipes ala IPC)
    public Vector getPipes(){
	return pipes;
    }
    //return a vector of devices associated with this process (/dev/ttyS1)
    public Vector getDevices(){
	return devices;
    }

    /** cpu usage howmany cycles has a process used since it's been running **/
    public float getCpuUsage(){
	getHZ();           //this set the number of ticks per second for the process
	double seconds = (double)uptime - ((double)start_time/HZ);          //determine number of seconds process has been running
	double total_time = (double)(utime + stime)/HZ;          //determine number of seconds system has been running since process started
	double pcpu = (total_time/seconds);          //create a percentage 
	float cpuUsage = (float)(pcpu * 100);
	cpuUsage = (float)Math.floor(cpuUsage * 10);          //clear off some of the every thing after the 1 decimal place 
	cpuUsage = cpuUsage / 10;           //do a decimal shift left round it, then shift it back to the right
	return cpuUsage;           //retun it
    }

    /** this tells us howmany ticks per second, we take the number of calls to the timer interupt and devide it by uptime **/
    private int getHZ(){
	if (this.HZ == 0){           //save time by only doing this once, i doubt a system will have 0 hz
	    try {          //watch for any file problems
		StringBuffer buffer = new StringBuffer();          //make a temporary string buffer
		FileReader timer = new FileReader("/proc/interrupts");          //open the interupts file
		StreamTokenizer timerStream = new StreamTokenizer(timer);          //chop the file up into tokens and head for the one that contains the number of interupts
		String stop = "XT-PIC";          //which happens to be right before this
		timerStream.nextToken();
		while (!(stop.equals(timerStream.sval))){          //go till we find our stop value
		    timerStream.nextToken();          //moving forward one token at a time
		}
		timerStream.pushBack();          //when we find it got back one toek and read in value
		this.HZ = (int)Math.round((double)timerStream.nval/getUptime());          //devide the number of ticks by the time the system has been running to get HZ
	                                                                              //on my system it's 100HZ and I think on all systems, but better safe than sorry
		timer.close();          //close the interupts file
	    }
	    catch (FileNotFoundException fnfe){          //catch anything that we don't want to crash the program
	    }
	    catch (NullPointerException npe){
	    }
	    catch (IOException ioe){
	    }
	}          //skip if we already have a hz value
	return this.HZ;          //return the number of HZ
    }
    /** this tells us howmany seconds the system has been running **/
    private double getUptime(){
	char c = 0;
	String uptimeS = null;
	StringBuffer buffer = new StringBuffer();
	try {
	    FileReader uptime = new FileReader("/proc/uptime");          //open the uptime file which hols the number od seconds the system has been running
	    while (c != ' '){          //read them in till we get a blank and stick the values on the string buffer
		c = (char)uptime.read();
		buffer.append(c);
	    }
	    uptime.close();          //close the file
	    uptimeS = new String(buffer);          //convert the string buffer into a string
	    this.uptime = new Double(uptimeS).doubleValue();          //convert the string into a double (this is a java thing, can't convert a string buffer).
	}
	catch (FileNotFoundException fnfe){
	}
	catch (NumberFormatException nfe){
	}
	catch (IOException ioe){
	}
	return this.uptime;          //return the value. I like to set these globally just incase
    }

    /** this gets the total system memory, w/o swap added in but will only parse once**/
    private long getMemTotal(){
	if (MemTotal == 0){          //check to see if we already have this valuse if so don't waste time
	    try {          //watch for file problems
		StringBuffer buffer = new StringBuffer();
		FileReader meminfo = new FileReader("/proc/meminfo");          //open the file
		StreamTokenizer memStream = new StreamTokenizer(meminfo);          //chop it up
		String stop = "Mem";          //set the stop value
		memStream.nextToken();          //get an early start so there's no garbage in the token
		while (!(stop.equals(memStream.sval))){          //read till we hit the stop value
		    memStream.nextToken();
		}
		memStream.nextToken();
		memStream.nextToken();          //move to tokens ahead
		this.MemTotal = (long)memStream.nval;          //set the system value I reget this since this may be run against multiple systems simulatnousely
		meminfo.close();          //close the file
		
	    }
	    catch (FileNotFoundException fnfe){
	    }
	    catch (NullPointerException npe){
	    }
	    catch (IOException ioe){
	    }
	    
	}
	return this.MemTotal;          //retuen the value
    }

    /** this matches what ps.c had for determineing memory usage. **/
    public float getMemUsage(){
	double memUsage = (double)(((double)rss * 1024) /(double)getMemTotal() *(getpagesize()/1024));
	memUsage = Math.round(memUsage * 100 * 10);
	memUsage = memUsage / 10;
	this.MemUsage = (float)memUsage;
	return (float)this.MemUsage;
    }
    /** this get called by System.out.println(process) makes debuging eaiser. **/
    public String toString(){
	String s = new String(pid+"\t "+command +"   \t "+getCpuUsage()+"\t"+getMemUsage()+"\t sockets:"+sockets.size()+"\t pipes:"+pipes.size()+"\t devices:"+devices.size()+"\t files:"+files.size());
	return s;
    }
    /** this is a method that will update a processes information **/
    public void update(){

	FileReader stat = null;           //create some file reades for each file
	FileReader statm = null;
	FileReader status = null;

	int pos = 0;          //make some varibles so we can move around
	char c = 0;
	char cp = 0;
	int d,dp = 0;

	StringBuffer buffer = null;          //make something we can append text to

	String[] statString = new String[38];          //make some string arrays to enter values into
	String[] statmString = new String[7];
	String[] statusString = new String[47];
	
	
	
	buffer = new StringBuffer();          //init the string buffer
	try {
	    stat = new FileReader("/proc/"+pid+"/stat");          //open the file	   
	    pos = 0;          //set our array position
	    d = 0;          //set or current char to zero
	    while (d != -1 & d != '\n'){          //loop while d isnt eual to and EOL or EOF
		try {
		    d = stat.read();          //read a char
		    c = (char)d;          //convert it to a letter
		    if (c == ' '){          //if it's a space move to the next member of our array
			statString[pos] = new String(buffer);          //enter current stringbuffer into array
			pos++;          //increment our position
			buffer = new StringBuffer();          //init the string buffer
		    }
		    else if (d != -1 & d != '\n'){          //if d isn't equal to EOL or EOF add the char to our buffer
			buffer.append(c);
		    }
		    else if (d == '\n'){
			statString[pos] = new String(buffer);          //if d equals EOL add the final string and move on to the next file
		    }
		}
		catch (IOException ioe){
		}
	    }
	              //finished while loop create new Process sts info with statString   
	}
	catch (FileNotFoundException fnfe){
	}
	try {
	    stat.close();
	}
	catch (IOException ioe){
	}
	this.stat = statString;
	          //*******************  finished with /proc/[pid]/stat ********************************
	          //this is the same idea as above...might want to change this into a big loop
	buffer = new StringBuffer();
	try {
	    statm = new FileReader("/proc/"+pid+"/statm");          //read out of the statm file
	    pos = 0;
	    d = 0;
	    c = 0;
	    while (d != -1 & d != '\n'){
		try {
		    d = statm.read();
		    c = (char)d;
		    if (c == ' '){
			statmString[pos] = new String(buffer);
			pos++;
			buffer = new StringBuffer();
		    }
		    else if (d != -1 & d != '\n'){
			buffer.append(c);
		    }
		    else if (d == '\n'){
			statmString[pos] = new String(buffer);
		    }
		}
		catch (IOException ioe){
		}
	    }
	}
	catch (FileNotFoundException fnfe){
	}
	try {
	    statm.close();
	}
	catch (IOException ioe){
	}
	this.statm = statmString;
	          //*************** finished with /proc/[pid]/statm *************************
	
	          //this works a little differently, each element is on a seperate lines preceeded by a lable 
	buffer = new StringBuffer();
	try {
	    status = new FileReader("/proc/"+pid+"/status");          //read out of the status file
	    pos = 0;
	    d = 0;
	    c = 0;
	    cp = 0;
	    while (d != -1 & pos < 47){          //read until EOF and not more than the array can hold
		try {
		    d = status.read();          //read a char
		    cp = c;          //keep the prevoius char in cp
		    c = (char)d;          //convert d to c
		    if ((c == ' ' & cp != ' ') || c == '\t' || c == '\n'){          //if we are at the end of a word, tab or line append our current vale
			statusString[pos] = new String(buffer);
			pos++;          //move our position in the array up one
			buffer = new StringBuffer();          //init the buffer
		    }
		    else if (d != -1 & c != '\n' & c != ' '){          //if we are a letter append us
			if (c != '\n' ){
			    buffer.append(c);
			}
		    }
		    else if (d == '\n'){          //if we are a new line push the last element onto the array
			statusString[pos] = new String(buffer);
		    }
		}
		catch (IOException ioe){
		}
	    }
	}
	catch (FileNotFoundException fnfe){
	}
	try {
	    status.close();          //close the file
	    this.status = statusString;          //set he string
	}
	catch (IOException ioe){          //make sure we don't have any file probelms
	}
	//**************** begin parsing /proc/[pid]/fd/[filediscriptors] **********************
	try {
	    File dir = new File("/proc/"+pid+"/fd");          //open the filedescriptor diectory of a process
	    String[] fds = dir.list();          //get the list of files
	    String link = null;
	    File temp = null;
	    FileInputStream fis = null;
	    int start = 0;
	    int end = 0;
	    for (int i = 0; i < fds.length;i++){
		link = readlink("/proc/"+pid+"/fd/"+fds[i]);          //use the native readlink method to evaluate the link
		if (link.startsWith("socket")){          //if it starts with socket add it to the socket vector
		    start = link.indexOf(":[");          //can't just use trim since there is extra arbage on the end of the string
		    end = link.indexOf("]");
		    sockets.addElement(link.substring(start+2,end));
		}
		else if (link.startsWith("pipe")){          //do the same as above for pipes
		    start = link.indexOf(":[");
		    end = link.indexOf("]");
		    pipes.addElement(link.substring(start+2,end));
		}
		else if (link.startsWith("/dev")){          //devices
		    devices.addElement(link);
		}
		else
		    files.addElement(link);          //if it isn't any of the above then it's a file
	    }
	}
	catch (NullPointerException npe){
	}

	          //this insures fresh values for the statistics algoritms
	getMemUsage();
	getCpuUsage();
	for (int i = 0 ; i < sockets.size();i++){          //this makes new LinuxSockets out of any sockets we found
	    new LinuxSocket(new Integer(((String)sockets.elementAt(i)).trim()).intValue());
	}
    }
    
    /** both of these are posix complient methods **/
    /** method to get accurate symbolic links **/
    private native String readlink(String filename);
    /** method to get system page size **/
    private native int getpagesize();
}
