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