1 /*
2  * process.cpp
3  *
4  * Copyright (c) 2004,2005,2008,2011 Arnout Engelen
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
19  *USA.
20  *
21  */
22 
23 #include <iostream>
24 #include <strings.h>
25 #include <string>
26 #include <ncurses.h>
27 #if !defined(__APPLE__) && !defined(__FreeBSD__)
28 #include <asm/types.h>
29 #endif
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <unistd.h>
33 #include <sys/socket.h>
34 #include <stdlib.h>
35 #include <pwd.h>
36 #include <map>
37 
38 #include "process.h"
39 #include "nethogs.h"
40 #include "inode2prog.h"
41 #include "conninode.h"
42 
43 extern timeval curtime;
44 
45 /*
46  * connection-inode table. takes information from /proc/net/tcp.
47  * key contains source ip, source port, destination ip, destination
48  * port in format: '1.2.3.4:5-1.2.3.4:5'
49  */
50 extern std::map<std::string, unsigned long> conninode;
51 
52 /* this file includes:
53  * - calls to inodeproc to get the pid that belongs to that inode
54  */
55 
56 /*
57  * Initialise the global process-list with some special processes:
58  * * unknown TCP traffic
59  * * UDP traffic
60  * * unknown IP traffic
61  * We must take care these never get removed from the list.
62  */
63 Process *unknowntcp;
64 Process *unknownudp;
65 Process *unknownip;
66 ProcList *processes;
67 
tomb(u_int32_t bytes)68 float tomb(u_int32_t bytes) { return ((double)bytes) / 1024 / 1024; }
tokb(u_int32_t bytes)69 float tokb(u_int32_t bytes) { return ((double)bytes) / 1024; }
70 
tokbps(u_int32_t bytes)71 float tokbps(u_int32_t bytes) { return (((double)bytes) / PERIOD) / 1024; }
72 
process_init()73 void process_init() {
74   unknowntcp = new Process(0, "", "unknown TCP");
75   // unknownudp = new Process (0, "", "unknown UDP");
76   // unknownip = new Process (0, "", "unknown IP");
77   processes = new ProcList(unknowntcp, NULL);
78   // processes = new ProcList (unknownudp, processes);
79   // processes = new ProcList (unknownip, processes);
80 }
81 
getLastPacket()82 int Process::getLastPacket() {
83   int lastpacket = 0;
84   ConnList *curconn = connections;
85   while (curconn != NULL) {
86     assert(curconn != NULL);
87     assert(curconn->getVal() != NULL);
88     if (curconn->getVal()->getLastPacket() > lastpacket)
89       lastpacket = curconn->getVal()->getLastPacket();
90     curconn = curconn->getNext();
91   }
92   return lastpacket;
93 }
94 
95 /** Get the kb/s values for this process */
getkbps(float * recvd,float * sent)96 void Process::getkbps(float *recvd, float *sent) {
97   u_int32_t sum_sent = 0, sum_recv = 0;
98 
99   /* walk though all this process's connections, and sum
100    * them up */
101   ConnList *curconn = this->connections;
102   ConnList *previous = NULL;
103   while (curconn != NULL) {
104     if (curconn->getVal()->getLastPacket() <= curtime.tv_sec - CONNTIMEOUT) {
105       /* stalled connection, remove. */
106       ConnList *todelete = curconn;
107       Connection *conn_todelete = curconn->getVal();
108       curconn = curconn->getNext();
109       if (todelete == this->connections)
110         this->connections = curconn;
111       if (previous != NULL)
112         previous->setNext(curconn);
113       delete (todelete);
114       delete (conn_todelete);
115     } else {
116       u_int32_t sent = 0, recv = 0;
117       curconn->getVal()->sumanddel(curtime, &recv, &sent);
118       sum_sent += sent;
119       sum_recv += recv;
120       previous = curconn;
121       curconn = curconn->getNext();
122     }
123   }
124   *recvd = tokbps(sum_recv);
125   *sent = tokbps(sum_sent);
126 }
127 
128 /** get total values for this process */
gettotal(u_int32_t * recvd,u_int32_t * sent)129 void Process::gettotal(u_int32_t *recvd, u_int32_t *sent) {
130   u_int32_t sum_sent = 0, sum_recv = 0;
131   ConnList *curconn = this->connections;
132   while (curconn != NULL) {
133     Connection *conn = curconn->getVal();
134     sum_sent += conn->sumSent;
135     sum_recv += conn->sumRecv;
136     curconn = curconn->getNext();
137   }
138   // std::cout << "Sum sent: " << sum_sent << std::endl;
139   // std::cout << "Sum recv: " << sum_recv << std::endl;
140   *recvd = sum_recv;
141   *sent = sum_sent;
142 }
143 
gettotalmb(float * recvd,float * sent)144 void Process::gettotalmb(float *recvd, float *sent) {
145   u_int32_t sum_sent = 0, sum_recv = 0;
146   gettotal(&sum_recv, &sum_sent);
147   *recvd = tomb(sum_recv);
148   *sent = tomb(sum_sent);
149 }
150 
151 /** get total values for this process */
gettotalkb(float * recvd,float * sent)152 void Process::gettotalkb(float *recvd, float *sent) {
153   u_int32_t sum_sent = 0, sum_recv = 0;
154   gettotal(&sum_recv, &sum_sent);
155   *recvd = tokb(sum_recv);
156   *sent = tokb(sum_sent);
157 }
158 
gettotalb(float * recvd,float * sent)159 void Process::gettotalb(float *recvd, float *sent) {
160   u_int32_t sum_sent = 0, sum_recv = 0;
161   gettotal(&sum_recv, &sum_sent);
162   // std::cout << "Total sent: " << sum_sent << std::endl;
163   *sent = sum_sent;
164   *recvd = sum_recv;
165 }
166 
findProcess(struct prg_node * node)167 Process *findProcess(struct prg_node *node) {
168   ProcList *current = processes;
169   while (current != NULL) {
170     Process *currentproc = current->getVal();
171     assert(currentproc != NULL);
172 
173     if (node->pid == currentproc->pid)
174       return current->getVal();
175     current = current->next;
176   }
177   return NULL;
178 }
179 
180 /* finds process based on inode, if any */
181 /* should be done quickly after arrival of the packet,
182  * otherwise findPID will be outdated */
findProcess(unsigned long inode)183 Process *findProcess(unsigned long inode) {
184   struct prg_node *node = findPID(inode);
185 
186   if (node == NULL)
187     return NULL;
188 
189   return findProcess(node);
190 }
191 
size()192 int ProcList::size() {
193   int i = 1;
194 
195   if (next != NULL)
196     i += next->size();
197 
198   return i;
199 }
200 
check_all_procs()201 void check_all_procs() {
202   ProcList *curproc = processes;
203   while (curproc != NULL) {
204     curproc->getVal()->check();
205     curproc = curproc->getNext();
206   }
207 }
208 
209 /*
210  * returns the process from proclist with matching pid
211  * if the inode is not associated with any PID, return NULL
212  * if the process is not yet in the proclist, add it
213  */
getProcess(unsigned long inode,const char * devicename)214 Process *getProcess(unsigned long inode, const char *devicename) {
215   struct prg_node *node = findPID(inode);
216 
217   if (node == NULL) {
218     if (DEBUG || bughuntmode)
219       std::cout << "No PID information for inode " << inode << std::endl;
220     return NULL;
221   }
222 
223   Process *proc = findProcess(node);
224 
225   if (proc != NULL)
226     return proc;
227 
228   Process *newproc = new Process(inode, devicename, node->name.c_str());
229   newproc->pid = node->pid;
230 
231   char procdir[100];
232   sprintf(procdir, "/proc/%d", node->pid);
233   struct stat stats;
234   int retval = stat(procdir, &stats);
235 
236   /* 0 seems a proper default.
237    * used in case the PID disappeared while nethogs was running
238    * TODO we can store node->uid this while info on the inodes,
239    * right? */
240   /*
241   if (!ROBUST && (retval != 0))
242   {
243           std::cerr << "Couldn't stat " << procdir << std::endl;
244           assert (false);
245   }
246   */
247 
248   if (retval != 0)
249     newproc->setUid(0);
250   else
251     newproc->setUid(stats.st_uid);
252 
253   /*if (getpwuid(stats.st_uid) == NULL) {
254           std::stderr << "uid for inode
255           if (!ROBUST)
256                   assert(false);
257   }*/
258   processes = new ProcList(newproc, processes);
259   return newproc;
260 }
261 
262 /*
263  * Used when a new connection is encountered. Finds corresponding
264  * process and adds the connection. If the connection  doesn't belong
265  * to any known process, the process list is updated and a new process
266  * is made. If no process can be found even then, it's added to the
267  * 'unknown' process.
268  */
getProcess(Connection * connection,const char * devicename)269 Process *getProcess(Connection *connection, const char *devicename) {
270   unsigned long inode = conninode[connection->refpacket->gethashstring()];
271 
272   if (inode == 0) {
273     // no? refresh and check conn/inode table
274     if (bughuntmode) {
275       std::cout << "?  new connection not in connection-to-inode table before "
276                    "refresh, hash " << connection->refpacket->gethashstring()
277                 << std::endl;
278     }
279 // refresh the inode->pid table first. Presumably processing the renewed
280 // connection->inode table
281 // is slow, making this worthwhile.
282 // We take the fact for granted that we might already know the inode->pid
283 // (unlikely anyway if we
284 // haven't seen the connection->inode yet though).
285 #ifndef __APPLE__
286     reread_mapping();
287 #endif
288     refreshconninode();
289     inode = conninode[connection->refpacket->gethashstring()];
290     if (bughuntmode) {
291       if (inode == 0) {
292         std::cout << ":( inode for connection not found after refresh.\n";
293       } else {
294         std::cout << ":) inode for connection found after refresh.\n";
295       }
296     }
297 #if REVERSEHACK
298     if (inode == 0) {
299       /* HACK: the following is a hack for cases where the
300        * 'local' addresses aren't properly recognised, as is
301        * currently the case for IPv6 */
302 
303       /* we reverse the direction of the stream if
304        * successful. */
305       Packet *reversepacket = connection->refpacket->newInverted();
306       inode = conninode[reversepacket->gethashstring()];
307 
308       if (inode == 0) {
309         delete reversepacket;
310         if (bughuntmode || DEBUG)
311           std::cout << "LOC: " << connection->refpacket->gethashstring()
312                     << " STILL not in connection-to-inode table - adding to "
313                        "the unknown process\n";
314         unknowntcp->connections =
315             new ConnList(connection, unknowntcp->connections);
316         return unknowntcp;
317       }
318 
319       delete connection->refpacket;
320       connection->refpacket = reversepacket;
321     }
322 #endif
323   } else if (bughuntmode) {
324     std::cout
325         << ";) new connection in connection-to-inode table before refresh.\n";
326   }
327 
328   if (bughuntmode) {
329     std::cout << "   inode # " << inode << std::endl;
330   }
331 
332   Process *proc = NULL;
333   if (inode != 0)
334     proc = getProcess(inode, devicename);
335 
336   if (proc == NULL) {
337     proc = new Process(inode, "", connection->refpacket->gethashstring());
338     processes = new ProcList(proc, processes);
339   }
340 
341   proc->connections = new ConnList(connection, proc->connections);
342   return proc;
343 }
344 
procclean()345 void procclean() {
346   // delete conninode;
347   prg_cache_clear();
348 }
349 
remove_timed_out_processes()350 void remove_timed_out_processes() {
351   ProcList *previousproc = NULL;
352 
353   for (ProcList *curproc = processes; curproc != NULL; curproc = curproc->next) {
354     if ((curproc->getVal()->getLastPacket() + PROCESSTIMEOUT <=
355          curtime.tv_sec) &&
356         (curproc->getVal() != unknowntcp) &&
357         (curproc->getVal() != unknownudp) && (curproc->getVal() != unknownip)) {
358       if (DEBUG)
359         std::cout << "PROC: Deleting process\n";
360       ProcList *todelete = curproc;
361       Process *p_todelete = curproc->getVal();
362       if (previousproc) {
363         previousproc->next = curproc->next;
364         curproc = curproc->next;
365       } else {
366         processes = curproc->getNext();
367         curproc = processes;
368       }
369       delete todelete;
370       delete p_todelete;
371     }
372     previousproc = curproc;
373   }
374 }
375