// proc.h   
// emacs, this is written in -*-c++-*- (who?)
//
// This program is free software. See the file COPYING for details.
// Author: Mattias Engdeg?rd, 1997-1999
//   		fasthyun@magicn.com 

#ifndef PROC_H
#define PROC_H


#include "config.h"

#ifdef SOLARIS
#include <kstat.h>	// kstat_ctl_t 
#endif

#ifndef USING_PCH
#include <QHash>
#include <QVector>
#include <QString>
#include <QStringList>
#include <QDir>
#endif 

class Procinfo;
int 	read_file(char *name, char *buf, int max);
int 	read_file_addNULL(char *name, void *buf, int max);
void 	check_system_requirement();
int		get_kernel_version();

//#define F_PID 0x00000000
enum fields {
	F_PID=0, 
#ifdef LINUX
	F_TGID,
#endif
	F_PPID, F_PGID, F_SID, F_TTY,
#ifdef LINUX
	F_TPGID,
#endif
#ifdef MOSIX
	F_MIGR,
	F_LOCKED,
	F_NMIGS,
	F_NOMOVE,
	F_RPID,
#endif
	F_USER, F_GROUP,
	F_UID, F_EUID,
#ifdef LINUX
	F_SUID, F_FSUID,
#endif
	F_GID, F_EGID,
#ifdef LINUX
	F_SGID, F_FSGID,
#endif
	F_PRI, F_NICE,
	F_PLCY, F_RPRI,
#ifdef LINUX
	F_TMS,
	F_AFFCPU,
	F_SLPAVG,
#endif
	F_NLWP,
#ifdef SOLARIS
	F_ARCH,
#endif
	F_MAJFLT, F_MINFLT,
#ifdef LINUX
	F_TRS, F_DRS, F_STACK,
#endif
	F_SIZE,		// VSIZE
	F_SWAP,  // Linux not correct 
	F_MEM,
	F_RSS,
#ifdef LINUX
	F_SHARE, F_DT,
	F_IOW, F_IOR,
#endif
	F_STAT,
	F_FLAGS,
	F_WCHAN,
	F_WCPU, F_CPU, /* %CPU */
	F_PMEM,    // F_PMEM: %MEM 
	F_START, F_TIME,
	F_CPUNUM,
	F_CMD,
	F_CWD, 
	F_ROOT, //?
	F_CMDLINE,
	F_END = -1 };

class Details;

#ifdef LINUX

class Sockinfo
{
	public:
		enum proto_t { TCP, UDP };
		proto_t proto;
		unsigned char st;
		unsigned char tr;
		unsigned local_addr;
		unsigned rem_addr;
		unsigned short local_port;
		unsigned short rem_port;
		unsigned tx_queue;
		unsigned rx_queue;
		unsigned tm_when;
		unsigned rexmits;
		int uid;		//??
		int pid;
		int timeout;
		int inode;
};

class SockInode
{
	public:
		SockInode(int descr, unsigned long ino) : fd(descr), inode(ino) {};
		int fd;
		unsigned long inode;
};

class UnixSocket
{
	public:
		unsigned long inode;
		QString name;
		unsigned flags;
		unsigned char type;		// SOCK_STREAM or SOCK_DGRAM
		unsigned char state;	// SS_FREE, SS_UNCONNECTED, SS_CONNECTING,
		// SS_CONNECTED, SS_DISCONNECTING
};

#endif // LINUX

// COMMON
class Mapsinfo
{	
	public:
		unsigned long from, to;
		unsigned long offset;
		unsigned long inode;
		QString filename;		// null if name unknown
		char perm[4];		// "rwx[ps]"; last is private/shared flag
		unsigned minor, major;
};

#define OPEN_READ 1
#define OPEN_WRITE 2

class Fileinfo
{
	public:
		Fileinfo(int descr, QString name, int open_mode = 0)
			: fd(descr), filename(name), mode(open_mode) {};
		int fd;
		QString filename;		// "major:minor inode" in Linux 2.0,
		// texual description in Solaris 2.6
		unsigned mode;		// bits from OPEN_* above (Linux only)
};

class NameValue
{
	public:
		NameValue(){};
		//NameValue(const NameValue &nv){ name=nv.name; value=nv.value; };
		NameValue(const char *n, const char *val) : name(n), value(val) {};
		QString name;
		QString value;
};

class Category
{
	public:
		Category(const char *heading, const char *explain)
			: name(heading), help(explain),reversed(false), flag_int_value(false) {};
		virtual ~Category();

		virtual int alignment() = 0;
		virtual QString string(Procinfo *p) = 0;  
		virtual int width() = 0;
		virtual int compare(Procinfo *a, Procinfo *b);

		const char *name;
		const char *help;
		int index;
		int id;
		bool reversed; 			// testing
		bool flag_int_value;	// testing: for total sum , cat_memory , cat_int
};

// COMMON 
class Cat_int : public Category
{
	public:
		Cat_int(const char *heading, const char *explain,
				int w, int Procinfo::*member);
		virtual int alignment() { return Qt::AlignRight; };
		virtual QString string(Procinfo *p);
		virtual int width() { return field_width; };
		virtual int compare(Procinfo *a, Procinfo *b);

	protected:
		int Procinfo::*int_member;
		int field_width;
};

// COMMON  for memory usage  
class Cat_memory : public Category
{
	public:
		Cat_memory(const char *heading, const char *explain,int w, unsigned long Procinfo::*member);
		virtual int alignment() { return Qt::AlignRight; };
		virtual QString string(Procinfo *p);
		virtual int width() { return field_width; };
		virtual int compare(Procinfo *a, Procinfo *b);

	protected:
		unsigned long Procinfo::*uintl_member;
		int field_width;
};


class Cat_uintl : public Category
{
	public:
		Cat_uintl(const char *heading, const char *explain, int w,
				unsigned long Procinfo::*member);
		virtual int alignment() { return Qt::AlignRight; };
		virtual QString string(Procinfo *p);
		virtual int width() { return field_width; };
		virtual int compare(Procinfo *a, Procinfo *b);

	protected:
		unsigned long Procinfo::*uintl_member;
		int field_width;
};

class Cat_hex : public Cat_uintl
{
	public:
		Cat_hex(const char *heading, const char *explain, int w,
				unsigned long Procinfo::*member);
		virtual QString string(Procinfo *p);
};

class Cat_swap : public Category
{
	public:
		Cat_swap(const char *heading, const char *explain);
		virtual int alignment() { return Qt::AlignRight; };
		virtual QString string(Procinfo *p);
		virtual int width() { return 8; };
		virtual int compare(Procinfo *a, Procinfo *b);
};

class Cat_string : public Category
{
	public:
		Cat_string(const char *heading, const char *explain,
				QString Procinfo::*member = 0);
		virtual int alignment() { return Qt::AlignLeft; };
		virtual QString string(Procinfo *p);
		virtual int width() { return -9; };
		virtual int gap() { return 8; };

	protected:
		QString Procinfo::*str_member;
};

class Cat_user : public Cat_string
{
	public:
		Cat_user(const char *heading, const char *explain);
		virtual QString string(Procinfo *p);
};

class Cat_group : public Cat_string
{
	public:
		Cat_group(const char *heading, const char *explain);
		virtual QString string(Procinfo *p);
};

class Cat_wchan : public Cat_string
{
	public:
		Cat_wchan(const char *heading, const char *explain);
		virtual QString string(Procinfo *p);
};

class Cat_dir : public Cat_string
{
	public:
		Cat_dir(const char *heading, const char *explain, const char *dirname,
				QString Procinfo::*member);
		virtual QString string(Procinfo *p);

	protected:
		const char *dir;
		QString Procinfo::*cache;
};

class Cat_cmdline : public Cat_string
{
	public:
		Cat_cmdline(const char *heading, const char *explain);
		virtual QString string(Procinfo *p);
};

class Cat_state : public Category
{
	public:
		Cat_state(const char *heading, const char *explain);
		virtual int alignment() { return Qt::AlignLeft; };
		virtual QString string(Procinfo *p);
		virtual int width() { return 6; };
		virtual int gap() { return 8; };
};

class Cat_policy : public Category
{
	public:
		Cat_policy(const char *heading, const char *explain);
		virtual int alignment() { return Qt::AlignLeft; };
		virtual QString string(Procinfo *p);
		virtual int width() { return 3; };
		virtual int gap() { return 8; };
		virtual int compare(Procinfo *a, Procinfo *b);
};

class Cat_rtprio : public Category
{
	public:
		Cat_rtprio(const char *heading, const char *explain);
		virtual int alignment() { return Qt::AlignRight; };
		virtual QString string(Procinfo *p);
		virtual int width() { return 5; };
		virtual int compare(Procinfo *a, Procinfo *b);
};

#ifdef LINUX
class Cat_tms : public Category
{
public:
    Cat_tms(const char *heading, const char *explain);
    virtual int alignment() { return Qt::AlignRight; };
    virtual QString string(Procinfo *p);
    virtual int width() { return 5; };
    virtual int compare(Procinfo *a, Procinfo *b);
};

class Cat_affcpu : public Category
{
public:
    Cat_affcpu(const char *heading, const char *explain);
    virtual int alignment() { return Qt::AlignRight; };
    virtual QString string(Procinfo *p);
    virtual int width() { return 8; };
    // virtual int compare(Procinfo *a, Procinfo *b);
};
#endif

class Cat_time : public Category
{
	public:
		Cat_time(const char *heading, const char *explain);
		virtual int alignment() { return Qt::AlignRight; };
		virtual QString string(Procinfo *p);
		virtual int width() { return 7; };
		virtual int compare(Procinfo *a, Procinfo *b);
};

class Cat_start : public Category
{
	public:
		Cat_start(const char *heading, const char *explain);
		virtual int alignment() { return Qt::AlignRight; };
		virtual QString string(Procinfo *p);
		virtual int width() { return 8; };
		virtual int compare(Procinfo *a, Procinfo *b);
};

class Cat_percent : public Category
{
	public:
		Cat_percent(const char *heading, const char *explain,
				int w, float Procinfo::*member);
		virtual int alignment() { return Qt::AlignRight; };
		virtual QString string(Procinfo *p);
		virtual int width() { return field_width; };
		virtual int compare(Procinfo *a, Procinfo *b);

	protected:
		float Procinfo::*float_member;
		int field_width;
};

class Cat_tty : public Cat_string
{
	public:
		Cat_tty(const char *heading, const char *explain);
		virtual QString string(Procinfo *p);
};

#define CPU_TIMES(cpu, kind) cpu_times_vec[cpu * CPUTIMES + kind]
class Proc;

class Procinfo	// Process Infomation 
{
	public:
		Procinfo(Proc *system_proc,int pid,int thread_id=-1);
#ifdef SOLARIS 
	//	Procinfo(int pid);  //solaris !
	//	Procinfo(int pid, int thread);  //solaris !
		int readproc(int pid, int lwp);
#endif
		~Procinfo();
		Procinfo *ref() { refcnt++; return this; };
		void deref() { if(!--refcnt)  delete this; };
		Proc	*proc;
		
		inline void calculate_cpu();

		int readproc();

		bool isThread();
		void read_fd(int fdnum, char *path);
		bool read_fds();
		bool read_maps();
		bool read_environ();
#ifdef SOLARIS
		void read_pmap_maps();
#endif
#ifdef MOSIX
		static void check_for_mosix();
		static Svec<int> mosix_nodes();
#endif

		int get_policy();
		int get_rtprio();

#ifdef LINUX
		double get_tms();
		unsigned long get_affcpu();
		
		QVector<SockInode*> sock_inodes; // socket inodes or NULL if not read
#endif
		int pid;
		bool clone;

		bool 	first_run; 	// for optimization 
		char 	hashstr[128*3];	// cache
		int		hashlen;
		int 	hashcmp(char *str);

		QString command;	//COMMAND
		QString cmdline; 	//COMMAND_LINE
		QString username; 	//
		QString groupname; 	//
		QString cwd;		// null if not read
		QString root;		// null if not read

        bool accepted;      
		int test_stop;  	// for test
		int session;        //	???
		
		int uid, euid;
		int gid, egid;

		char state;
		int ppid;			// Parent's PID
		int pgrp;
		dev_t tty;			// tty major:minor device 
		int type; 		    // TESTING X,NETWORK,FILE_OPEN,TERMINAL(tty),THREAD,

		int nthreads;		// number of threads : LWP(Solaris), task(Linux)
		int tgid;			// thread leader's id

#ifdef LINUX
 	   	double tms;		// slice time
		int slpavg;
   		unsigned long affcpu;

		int suid, fsuid;
		int sgid, fsgid;		
		int tpgid;
		
		unsigned long cminflt;
		unsigned long cmajflt;
#endif
		
		unsigned long	io_read;		// byte, testing
		unsigned long	io_write;		// testing
		unsigned long	io_read_KBps;		// K byte/sec 
		unsigned long	io_write_KBps;		// K byte/sec
		unsigned long	io_read_prev,io_write_prev; 
		
		unsigned long flags;	//?
		unsigned long minflt;
		unsigned long majflt;
		
		long utime;
		long old_utime;		// initial value = -1 ;
		long cutime;
		int priority;
		int nice;
		unsigned long starttime;// start time since run in epoch? Linux : jiffies since boot , solaris 
		unsigned long wchan;
		QString wchan_str;

		// Memory 
		unsigned long mem;		// user Memory define  
		unsigned long size;		// SIZE: total memory (K)
		unsigned long resident;	// RSS: pages in resident set (non-swapped) (K) 
#ifdef LINUX
		unsigned long share;	// shared memory pages (mmaped) (K)
		unsigned long trs;      // text resident set size (K)
		unsigned long lrs;		// shared-lib resident set size (K)
		unsigned long drs;		// data resident set size (K)
		unsigned long dt;		// dirty pages (number of pages, not K), obsolute in Kernel 2.6
		unsigned long stack;	// stack size (K)
#endif

#ifdef SOLARIS
		int addr_bits;		// address bits (32 or 64)
		
		char policy_name[2];	// two first letters of scheduling class
#endif
		struct timeval tv;		// time when the snapshot was taken
		struct timeval old_tv;	// 

		// Posix.1b scheduling
		int policy;		// -1 = uninitialized
		int rtprio;		// 0-99, higher can pre-empt lower (-1 = uninitialized)


		// Linux: the cpu used most of the time of the process
		// Solaris: the cpu on which the process last ran
		int which_cpu;

		// computed %cpu and %mem since last update
		float wcpu, old_wcpu;		// %WCPUwheight cpu 
		float pcpu;					// %CPU: percent cpu after last update
		float pmem;					// %MEM
	

		QVector<Fileinfo*> fd_files;	// file names list
		QVector<Mapsinfo*> maps;		// maps list
		QVector<NameValue> environ;		// environment 
		char 	*envblock;				// malloc()ed environment data block


#ifdef SOLARIS
		unsigned long env_ofs;
#endif

		Details *detail;		// details window or NULL (backlink)
		
		unsigned int generation;	// timestamp
		
		bool selected:1;	// true if selected in current view
		bool hidekids:1;	// true if children are hidden in tree view
		bool lastchild:1;	// true if last (visible) child in tree view

		short level;					// distance from process root
		QVector<Procinfo *> children;	// real child processes
		static const int MAX_CMD_LEN = 512;

		char refcnt;
	
		//virtual child for Table_Tree
		QVector<Procinfo *> table_children;
		int	table_child_seq;				
		int	clear_gen;
		int child_seq_prev;
		int parent_row;		// virtual parent for tree table

#ifdef MOSIX
		bool isremote;
		int from;
		int where;
		int remotepid;
		QString migr; 	// String explaining migration "node>" or ">node" 
		int nmigs;
		int locked;
		QString cantmove;
		static bool mosix_running;	// true if MOSIX is running
#endif
};


typedef QHash<int,Procinfo*> Proclist;

class SysHistory {
	public:
	int idx;
	time_t time; 	// saved time, epoch...
	int	current_gen; 
	float load_cpu;		// %CPU total ; green
	float load_mem;		// %mem 	; yellow?
	float load_io;		// %SYS_IO 	; BLUE
	float load_net;		//	;blue?	orange?
	Proclist procs;
	~SysHistory();
};


// for A System      
// cf. Procinfo is for a Process
//
class Proc
{
	public:
		Proc();
		~Proc();
		void commonPostInit();	// COMMON
		void read_proc_all(); // test
		void refresh();

		static void init_static(); 	
		int read_system();
		int countCpu();
		void read_loadavg();	
		
		int  read_pid_tasks(int pid);

		Category *cat_by_name(const char *s);
		int  field_id_by_name(const char *s);

#ifdef LINUX
		/* 	from /proc/net/{tcp,udp,unix}  */
		QHash<int,Sockinfo*> socks;	// tcp/udp sockets
		QHash<int,UnixSocket*> usocks;	// unix domain sockets
		bool socks_current;		// true if the socks list is current
		bool usocks_current;		// true if the usocks list is current

		bool read_socket_list(Sockinfo::proto_t proto, const char *pseudofile);
		void read_sockets();
		bool read_usocket_list();
		void read_usockets();
		void invalidate_sockets();

#endif

#ifdef SOLARIS
		static kstat_ctl_t *kc;		// NULL if kstat not opened
#endif
		QHash<int,Category *> categories;
		
		Proclist 		procs;	// processes indexed by pid
		
		// TESTING
		QString 		supasswd; 		// test
		int	 					syshistoryMAX;	
		Proclist				*hprocs; // temp_hprocs list 
		Proclist				*mprocs; //  
		QList<SysHistory *> 	history;

		void 	setHistory(int tick);
		Proclist getHistory(int pos);
		
		
		int 	 qps_pid;		//test
		float loadQps;	 		//TEST
		static int update_msec;


		// class 
		int 	num_cpus;			// current number of CPUs
		int		old_num_cpus; 		// previous number of CPUs
	
		long 	num_network_process; 	//  number of network(socket) process
		long 	num_opened_files; 		//  number of opened normal(not socket) files  
		int 	num_process;			//  number of process
		
		long 	dt_total;				//  
		long 	dt_used;				//  cpu used time in clktick 
		
		long 	read_byte;				//  test
		long	write_byte;				// test 
		long	io_byte; 	//test file_io

		float 	load_cpu;				// %CPU total 
		float 	loadavg[3];				// 1,5,15 minutes load avgs  

		unsigned int clk_tick;		// the  number  of  clock ticks per second.  
		unsigned int boot_time;		// boot time in seconds since the Epoch 

		int mem_total, mem_free;		// (Kb)
		int swap_total, swap_free;		// in kB
	
		int mem_shared, mem_buffers, mem_cached; // Linux

		// the following are pointers to matrices indexed by kind (above) and cpu
		unsigned *cpu_times_vec;
		unsigned *old_cpu_times_vec;
		
		// accessors for (old_)cpu_times_vec
		unsigned &cpu_times(int cpu, int kind)
		{ return cpu_times_vec[cpu * CPUTIMES + kind]; }
		unsigned &old_cpu_times(int cpu, int kind)
		{ return old_cpu_times_vec[cpu * CPUTIMES + kind]; }


#ifdef LINUX
		// from /proc/stat
		unsigned long *per_cpu_times; // vector of num_cpus times
		unsigned long *old_per_cpu_times; // vector of num_cpus times
#endif
		
		// Solaris <sys/sysinfo.h> #defines CPU_xxx so we must avoid them
		enum { CPUTIME_USER,
#ifdef LINUX
			CPUTIME_NICE,
#endif
			CPUTIME_SYSTEM,
#ifdef SOLARIS
			CPUTIME_WAIT,
#endif
			CPUTIME_IDLE,
			CPUTIMES };

		//#define CPU_TIMES(cpu, kind) cpu_times_vec[cpu * CPUTIMES + kind]
		

		unsigned int 	current_gen;
		int		maxSizeHistory;
};


class Procview : public Proc
{
	public:
		Procview();
		QString filterstr;

		static bool flag_show_file_path;
		static bool flag_pcpu_single;
		static bool flag_cumulative;

		static int compare(Procinfo *const *a, Procinfo *const *b);
		static int compare_backwards(Procinfo *const *a, Procinfo *const *b);
		void refresh();
		bool accept_proc(Procinfo *p); // COMMON
		void linearize_tree(QVector<Procinfo *> *ps, int level, int prow, bool flag_hide=false);
		void build_tree(Proclist &);
		void rebuild();

		void set_fields();
		void set_fields_list(int fields[]);
		void addField(char *name);	// interface 
		void addField(int FIELD_ID,int where=-1);	// base interface 
		void removeField(int FIELD_ID);
		int	 findCol(int FIELD_ID);
		void moveColumn(int col, int place); 
		void deduce_fields();
   		void fieldArrange();  
		void update_customfield();
		//
		void setSortColumn(int col,bool r=false); 
		void setTreeMode(bool b);
		void saveCOMMANDFIELD();
		
		#ifndef GTK
		Procinfo* getProcinfoByPID(int pid){ return procs.value(pid,NULL);};
		#endif

		QVector<Procinfo *> linear_procs;  // this is linear_proc_list for viewer
		
		#ifdef LINUX
		QVector<Sockinfo *> linear_socks;  // Linux Testing
		#endif

		//QList<> tags_kernel;
		
		// root_procs contains processes without parent; normally only init, but
		// we cannot rely on this (Solaris has several parentless processes).
		// Also, if the view is restricted, all processes whose parent isn't in
		// the table.
		QVector<Procinfo *> root_procs; // table_root_procs; for viewer
		QVector<Category *> cats;		// for table
		
		Category *sortcat;
		Category *sortcat_linear;		// testing
		int  sort_column;	// index of 
		bool reversed;		// true if sorted backwards
		static bool treeview;		// true if viewed in tree form
		bool enable;		// tmp

		enum procstates {ALL, OWNED, NROOT, RUNNING, HIDDEN, NETWORK} ;
		enum fieldstates {USER = HIDDEN + 1, JOBS, MEM, SCHED,CUSTOM};
		int viewproc;
		int viewfields;
		
	
		int idxF_CMD; ////Test
		QStringList customfields;
		static int custom_fields[64];
		// lists of fields to be used for different views, terminated by -1:
		static int mini_fields[]; 	//for mobile 
		static int basic_fields[];
		static int jobs_fields[];
		static int mem_fields[];
#ifdef LINUX
		static int sched_fields[];
#endif


#ifdef MOSIX
		static int user_fields_mosix[];
		static int jobs_fields_mosix[];
		static int mem_fields_mosix[];
#endif
		static float avg_factor;		// exponential factor for averaging
		static const int cpu_avg_time = 30 * 1000;	// averaging time for WCPU (ms)
	
	private:
		static Category *static_sortcat;	// kludge: to be used by compare
};

#endif	// PROC_H
