blob: d1b0913aa78f6c863bfda98c7f81ee54d595582f [file] [log] [blame]
Jon Medhurstaaf37a32013-06-11 12:10:56 +01001/**
2 * Copyright (C) ARM Limited 2010-2013. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8
9#include <stdlib.h>
10#include <signal.h>
11#include <sys/wait.h>
12#include <unistd.h>
13#include <sys/syscall.h>
14#include <sys/prctl.h>
15#include <sys/types.h>
16#include <sys/stat.h>
17#include <sys/mount.h>
18#include <fcntl.h>
19#include <sys/mman.h>
20#include <sys/time.h>
21#include <sys/resource.h>
Jon Medhurstd3698592013-10-10 16:48:56 +010022#include <arpa/inet.h>
23#include <sys/socket.h>
Jon Medhurstaaf37a32013-06-11 12:10:56 +010024#include "Child.h"
25#include "SessionData.h"
26#include "OlySocket.h"
27#include "Logging.h"
28#include "OlyUtility.h"
29#include "KMod.h"
30
31#define DEBUG false
32
33extern Child* child;
34static int shutdownFilesystem();
35static pthread_mutex_t numSessions_mutex;
36static int numSessions = 0;
Jon Medhurstd3698592013-10-10 16:48:56 +010037static OlySocket* sock = NULL;
Jon Medhurstaaf37a32013-06-11 12:10:56 +010038static bool driverRunningAtStart = false;
39static bool driverMountedAtStart = false;
40
41struct cmdline_t {
42 int port;
43 char* module;
44};
45
Jon Medhurstd3698592013-10-10 16:48:56 +010046#define DEFAULT_PORT 8080
47
Jon Medhurstaaf37a32013-06-11 12:10:56 +010048void cleanUp() {
49 if (shutdownFilesystem() == -1) {
50 logg->logMessage("Error shutting down gator filesystem");
51 }
Jon Medhurstd3698592013-10-10 16:48:56 +010052 delete sock;
Jon Medhurstaaf37a32013-06-11 12:10:56 +010053 delete util;
54 delete logg;
55}
56
57// CTRL C Signal Handler
58static void handler(int signum) {
59 logg->logMessage("Received signal %d, gator daemon exiting", signum);
60
61 // Case 1: both child and parent receive the signal
62 if (numSessions > 0) {
63 // Arbitrary sleep of 1 second to give time for the child to exit;
64 // if something bad happens, continue the shutdown process regardless
65 sleep(1);
66 }
67
68 // Case 2: only the parent received the signal
69 if (numSessions > 0) {
70 // Kill child threads - the first signal exits gracefully
71 logg->logMessage("Killing process group as %d child was running when signal was received", numSessions);
72 kill(0, SIGINT);
73
74 // Give time for the child to exit
75 sleep(1);
76
77 if (numSessions > 0) {
78 // The second signal force kills the child
79 logg->logMessage("Force kill the child");
80 kill(0, SIGINT);
81 // Again, sleep for 1 second
82 sleep(1);
83
84 if (numSessions > 0) {
85 // Something bad has really happened; the child is not exiting and therefore may hold the /dev/gator resource open
86 printf("Unable to kill the gatord child process, thus gator.ko may still be loaded.\n");
87 }
88 }
89 }
90
91 cleanUp();
92 exit(0);
93}
94
95// Child exit Signal Handler
96static void child_exit(int signum) {
97 int status;
98 int pid = wait(&status);
99 if (pid != -1) {
100 pthread_mutex_lock(&numSessions_mutex);
101 numSessions--;
102 pthread_mutex_unlock(&numSessions_mutex);
103 logg->logMessage("Child process %d exited with status %d", pid, status);
104 }
105}
106
Jon Medhurstd3698592013-10-10 16:48:56 +0100107static int udpPort(int port) {
108 int s;
109 struct sockaddr_in sockaddr;
110 int on;
111
112 s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
113 if (s == -1) {
114 logg->logError(__FILE__, __LINE__, "socket failed");
115 handleException();
116 }
117
118 on = 1;
119 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on)) != 0) {
120 logg->logError(__FILE__, __LINE__, "setsockopt failed");
121 handleException();
122 }
123
124 memset((void*)&sockaddr, 0, sizeof(sockaddr));
125 sockaddr.sin_family = AF_INET;
126 sockaddr.sin_port = htons(port);
127 sockaddr.sin_addr.s_addr = INADDR_ANY;
128 if (bind(s, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) < 0) {
129 logg->logError(__FILE__, __LINE__, "socket failed");
130 handleException();
131 }
132
133 return s;
134}
135
136#define UDP_ANS_PORT 30000
137#define UDP_REQ_PORT 30001
138
139typedef struct {
140 char rviHeader[8];
141 uint32_t messageID;
142 uint8_t ethernetAddress[8];
143 uint32_t ethernetType;
144 uint32_t dhcp;
145 char dhcpName[40];
146 uint32_t ipAddress;
147 uint32_t defaultGateway;
148 uint32_t subnetMask;
149 uint32_t activeConnections;
150} RVIConfigureInfo;
151
152static const char DST_REQ[] = { 'D', 'S', 'T', '_', 'R', 'E', 'Q', ' ', 0, 0, 0, 0x64 };
153
154static void* answerThread(void* pVoid) {
155 const struct cmdline_t * const cmdline = (struct cmdline_t *)pVoid;
156 RVIConfigureInfo dstAns;
157 int req = udpPort(UDP_REQ_PORT);
158 int ans = udpPort(UDP_ANS_PORT);
159
160 // Format the answer buffer
161 memset(&dstAns, 0, sizeof(dstAns));
162 memcpy(dstAns.rviHeader, "STR_ANS ", sizeof(dstAns.rviHeader));
163 if (gethostname(dstAns.dhcpName, sizeof(dstAns.dhcpName) - 1) != 0) {
164 logg->logError(__FILE__, __LINE__, "gethostname failed");
165 handleException();
166 }
167 // Subvert the defaultGateway field for the port number
168 if (cmdline->port != DEFAULT_PORT) {
169 dstAns.defaultGateway = cmdline->port;
170 }
171 // Subvert the subnetMask field for the protocol version
172 dstAns.subnetMask = PROTOCOL_VERSION;
173
174 for (;;) {
175 char buf[128];
176 struct sockaddr_in sockaddr;
177 socklen_t addrlen;
178 int read;
179 addrlen = sizeof(sockaddr);
180 read = recvfrom(req, &buf, sizeof(buf), 0, (struct sockaddr *)&sockaddr, &addrlen);
181 if (read < 0) {
182 logg->logError(__FILE__, __LINE__, "recvfrom failed");
183 handleException();
184 } else if ((read == 12) && (memcmp(buf, DST_REQ, sizeof(DST_REQ)) == 0)) {
185 if (sendto(ans, &dstAns, sizeof(dstAns), 0, (struct sockaddr *)&sockaddr, addrlen) != sizeof(dstAns)) {
186 logg->logError(__FILE__, __LINE__, "sendto failed");
187 handleException();
188 }
189 }
190 }
191}
192
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100193// retval: -1 = failure; 0 = was already mounted; 1 = successfully mounted
194static int mountGatorFS() {
195 // If already mounted,
196 if (access("/dev/gator/buffer", F_OK) == 0) {
197 return 0;
198 }
199
200 // else, mount the filesystem
201 mkdir("/dev/gator", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
202 if (mount("nodev", "/dev/gator", "gatorfs", 0, NULL) != 0) {
203 return -1;
204 } else {
205 return 1;
206 }
207}
208
209static bool init_module (const char * const location) {
210 bool ret(false);
211 const int fd = open(location, O_RDONLY);
212 if (fd >= 0) {
213 struct stat st;
214 if (fstat(fd, &st) == 0) {
215 void * const p = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
216 if (p != MAP_FAILED) {
217 if (syscall(__NR_init_module, p, st.st_size, "") == 0) {
218 ret = true;
219 }
220 munmap(p, st.st_size);
221 }
222 }
223 close(fd);
224 }
225
226 return ret;
227}
228
229static int setupFilesystem(char* module) {
230 int retval;
231
232 // Verify root permissions
233 uid_t euid = geteuid();
234 if (euid) {
235 logg->logError(__FILE__, __LINE__, "gatord must be launched with root privileges");
236 handleException();
237 }
238
239 if (module) {
240 // unmount and rmmod if the module was specified on the commandline, i.e. ensure that the specified module is indeed running
241 shutdownFilesystem();
242
243 // if still mounted
244 if (access("/dev/gator/buffer", F_OK) == 0) {
245 logg->logError(__FILE__, __LINE__, "Unable to remove the running gator.ko. Manually remove the module or use the running module by not specifying one on the commandline");
246 handleException();
247 }
248 }
249
250 retval = mountGatorFS();
251 if (retval == 1) {
252 logg->logMessage("Driver already running at startup");
253 driverRunningAtStart = true;
254 } else if (retval == 0) {
255 logg->logMessage("Driver already mounted at startup");
256 driverRunningAtStart = driverMountedAtStart = true;
257 } else {
258 char command[256]; // arbitrarily large amount
259 char location[256]; // arbitrarily large amount
260
261 if (module) {
262 strncpy(location, module, sizeof(location));
263 } else {
264 // Is the driver co-located in the same directory?
265 if (util->getApplicationFullPath(location, sizeof(location)) != 0) { // allow some buffer space
266 logg->logMessage("Unable to determine the full path of gatord, the cwd will be used");
267 }
268 strncat(location, "gator.ko", sizeof(location) - strlen(location) - 1);
269 }
270
271 if (access(location, F_OK) == -1) {
272 logg->logError(__FILE__, __LINE__, "Unable to locate gator.ko driver:\n >>> gator.ko should be co-located with gatord in the same directory\n >>> OR insmod gator.ko prior to launching gatord\n >>> OR specify the location of gator.ko on the command line");
273 handleException();
274 }
275
276 // Load driver
277 bool success = init_module(location);
278 if (!success) {
279 logg->logMessage("init_module failed, trying insmod");
280 snprintf(command, sizeof(command), "insmod %s >/dev/null 2>&1", location);
281 if (system(command) != 0) {
282 logg->logMessage("Unable to load gator.ko driver with command: %s", command);
283 logg->logError(__FILE__, __LINE__, "Unable to load (insmod) gator.ko driver:\n >>> gator.ko must be built against the current kernel version & configuration\n >>> See dmesg for more details");
284 handleException();
285 }
286 }
287
288 if (mountGatorFS() == -1) {
289 logg->logError(__FILE__, __LINE__, "Unable to mount the gator filesystem needed for profiling.");
290 handleException();
291 }
292 }
293
294 return 0;
295}
296
297static int shutdownFilesystem() {
298 if (driverMountedAtStart == false) {
299 umount("/dev/gator");
300 }
301 if (driverRunningAtStart == false) {
302 if (syscall(__NR_delete_module, "gator", O_NONBLOCK) != 0) {
303 logg->logMessage("delete_module failed, trying rmmod");
304 if (system("rmmod gator >/dev/null 2>&1") != 0) {
305 return -1;
306 }
307 }
308 }
309
310 return 0; // success
311}
312
313static struct cmdline_t parseCommandLine(int argc, char** argv) {
314 struct cmdline_t cmdline;
Jon Medhurstd3698592013-10-10 16:48:56 +0100315 cmdline.port = DEFAULT_PORT;
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100316 cmdline.module = NULL;
317 char version_string[256]; // arbitrary length to hold the version information
318 int c;
319
320 // build the version string
321 if (PROTOCOL_VERSION < PROTOCOL_DEV) {
Jon Medhurstd3698592013-10-10 16:48:56 +0100322 snprintf(version_string, sizeof(version_string), "Streamline gatord version %d (DS-5 v5.%d)", PROTOCOL_VERSION, PROTOCOL_VERSION);
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100323 } else {
324 snprintf(version_string, sizeof(version_string), "Streamline gatord development version %d", PROTOCOL_VERSION);
325 }
326
327 while ((c = getopt(argc, argv, "hvp:s:c:e:m:o:")) != -1) {
328 switch(c) {
329 case 'c':
330 gSessionData->mConfigurationXMLPath = optarg;
331 break;
332 case 'e':
333 gSessionData->mEventsXMLPath = optarg;
334 break;
335 case 'm':
336 cmdline.module = optarg;
337 break;
338 case 'p':
339 cmdline.port = strtol(optarg, NULL, 10);
340 break;
341 case 's':
342 gSessionData->mSessionXMLPath = optarg;
343 break;
344 case 'o':
345 gSessionData->mTargetPath = optarg;
346 break;
347 case 'h':
348 case '?':
349 logg->logError(__FILE__, __LINE__,
350 "%s. All parameters are optional:\n"
351 "-c config_xml path and filename of the configuration.xml to use\n"
352 "-e events_xml path and filename of the events.xml to use\n"
353 "-h this help page\n"
354 "-m module path and filename of gator.ko\n"
355 "-p port_number port upon which the server listens; default is 8080\n"
356 "-s session_xml path and filename of a session xml used for local capture\n"
357 "-o apc_dir path and name of the output for a local capture\n"
358 "-v version information\n"
359 , version_string);
360 handleException();
361 break;
362 case 'v':
363 logg->logError(__FILE__, __LINE__, version_string);
364 handleException();
365 break;
366 }
367 }
368
369 // Error checking
Jon Medhurstd3698592013-10-10 16:48:56 +0100370 if (cmdline.port != DEFAULT_PORT && gSessionData->mSessionXMLPath != NULL) {
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100371 logg->logError(__FILE__, __LINE__, "Only a port or a session xml can be specified, not both");
372 handleException();
373 }
374
375 if (gSessionData->mTargetPath != NULL && gSessionData->mSessionXMLPath == NULL) {
376 logg->logError(__FILE__, __LINE__, "Missing -s command line option required for a local capture.");
377 handleException();
378 }
379
380 if (optind < argc) {
381 logg->logError(__FILE__, __LINE__, "Unknown argument: %s. Use '-h' for help.", argv[optind]);
382 handleException();
383 }
384
385 return cmdline;
386}
387
388// Gator data flow: collector -> collector fifo -> sender
389int main(int argc, char** argv, char* envp[]) {
390 // Ensure proper signal handling by making gatord the process group leader
391 // e.g. it may not be the group leader when launched as 'sudo gatord'
392 setsid();
393
394 logg = new Logging(DEBUG); // Set up global thread-safe logging
395 gSessionData = new SessionData(); // Global data class
396 util = new OlyUtility(); // Set up global utility class
397
398 // Initialize drivers
399 new KMod();
400
401 prctl(PR_SET_NAME, (unsigned long)&"gatord-main", 0, 0, 0);
402 pthread_mutex_init(&numSessions_mutex, NULL);
403
404 signal(SIGINT, handler);
405 signal(SIGTERM, handler);
406 signal(SIGABRT, handler);
407
408 // Set to high priority
409 if (setpriority(PRIO_PROCESS, syscall(__NR_gettid), -19) == -1) {
410 logg->logMessage("setpriority() failed");
411 }
412
413 // Parse the command line parameters
414 struct cmdline_t cmdline = parseCommandLine(argc, argv);
415
416 // Call before setting up the SIGCHLD handler, as system() spawns child processes
417 setupFilesystem(cmdline.module);
418
419 // Handle child exit codes
420 signal(SIGCHLD, child_exit);
421
422 // Ignore the SIGPIPE signal so that any send to a broken socket will return an error code instead of asserting a signal
423 // Handling the error at the send function call is much easier than trying to do anything intelligent in the sig handler
424 signal(SIGPIPE, SIG_IGN);
425
426 // If the command line argument is a session xml file, no need to open a socket
427 if (gSessionData->mSessionXMLPath) {
428 child = new Child();
429 child->run();
430 delete child;
431 } else {
Jon Medhurstd3698592013-10-10 16:48:56 +0100432 pthread_t answerThreadID;
433 if (pthread_create(&answerThreadID, NULL, answerThread, &cmdline)) {
434 logg->logError(__FILE__, __LINE__, "Failed to create answer thread");
435 handleException();
436 }
437 sock = new OlySocket(cmdline.port, true);
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100438 // Forever loop, can be exited via a signal or exception
439 while (1) {
440 logg->logMessage("Waiting on connection...");
Jon Medhurstd3698592013-10-10 16:48:56 +0100441 sock->acceptConnection();
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100442
443 int pid = fork();
444 if (pid < 0) {
445 // Error
446 logg->logError(__FILE__, __LINE__, "Fork process failed. Please power cycle the target device if this error persists.");
447 } else if (pid == 0) {
448 // Child
Jon Medhurstd3698592013-10-10 16:48:56 +0100449 sock->closeServerSocket();
450 child = new Child(sock, numSessions + 1);
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100451 child->run();
452 delete child;
453 exit(0);
454 } else {
455 // Parent
Jon Medhurstd3698592013-10-10 16:48:56 +0100456 sock->closeSocket();
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100457
458 pthread_mutex_lock(&numSessions_mutex);
459 numSessions++;
460 pthread_mutex_unlock(&numSessions_mutex);
461
462 // Maximum number of connections is 2
463 int wait = 0;
464 while (numSessions > 1) {
465 // Throttle until one of the children exits before continuing to accept another socket connection
466 logg->logMessage("%d sessions active!", numSessions);
467 if (wait++ >= 10) { // Wait no more than 10 seconds
468 // Kill last created child
469 kill(pid, SIGALRM);
470 break;
471 }
472 sleep(1);
473 }
474 }
475 }
476 }
477
478 cleanUp();
479 return 0;
480}