2 * Copyright (C) ARM Limited 2010-2013. All rights reserved.
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.
13 #include <sys/syscall.h>
14 #include <sys/prctl.h>
15 #include <sys/types.h>
17 #include <sys/mount.h>
21 #include <sys/resource.h>
22 #include <arpa/inet.h>
23 #include <sys/socket.h>
25 #include "SessionData.h"
26 #include "OlySocket.h"
28 #include "OlyUtility.h"
34 static int shutdownFilesystem();
35 static pthread_mutex_t numSessions_mutex;
36 static int numSessions = 0;
37 static OlySocket* sock = NULL;
38 static bool driverRunningAtStart = false;
39 static bool driverMountedAtStart = false;
46 #define DEFAULT_PORT 8080
49 if (shutdownFilesystem() == -1) {
50 logg->logMessage("Error shutting down gator filesystem");
57 // CTRL C Signal Handler
58 static void handler(int signum) {
59 logg->logMessage("Received signal %d, gator daemon exiting", signum);
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
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);
74 // Give time for the child to exit
77 if (numSessions > 0) {
78 // The second signal force kills the child
79 logg->logMessage("Force kill the child");
81 // Again, sleep for 1 second
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");
95 // Child exit Signal Handler
96 static void child_exit(int) {
98 int pid = wait(&status);
100 pthread_mutex_lock(&numSessions_mutex);
102 pthread_mutex_unlock(&numSessions_mutex);
103 logg->logMessage("Child process %d exited with status %d", pid, status);
107 static int udpPort(int port) {
109 struct sockaddr_in6 sockaddr;
111 int family = AF_INET6;
113 s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
116 s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
118 logg->logError(__FILE__, __LINE__, "socket failed");
124 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on)) != 0) {
125 logg->logError(__FILE__, __LINE__, "setsockopt failed");
129 memset((void*)&sockaddr, 0, sizeof(sockaddr));
130 sockaddr.sin6_family = family;
131 sockaddr.sin6_port = htons(port);
132 sockaddr.sin6_addr = in6addr_any;
133 if (bind(s, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) < 0) {
134 logg->logError(__FILE__, __LINE__, "socket failed");
141 #define UDP_ANS_PORT 30000
142 #define UDP_REQ_PORT 30001
147 uint8_t ethernetAddress[8];
148 uint32_t ethernetType;
152 uint32_t defaultGateway;
154 uint32_t activeConnections;
157 static const char DST_REQ[] = { 'D', 'S', 'T', '_', 'R', 'E', 'Q', ' ', 0, 0, 0, 0x64 };
159 static void* answerThread(void* pVoid) {
160 const struct cmdline_t * const cmdline = (struct cmdline_t *)pVoid;
161 RVIConfigureInfo dstAns;
162 int req = udpPort(UDP_REQ_PORT);
163 int ans = udpPort(UDP_ANS_PORT);
165 // Format the answer buffer
166 memset(&dstAns, 0, sizeof(dstAns));
167 memcpy(dstAns.rviHeader, "STR_ANS ", sizeof(dstAns.rviHeader));
168 if (gethostname(dstAns.dhcpName, sizeof(dstAns.dhcpName) - 1) != 0) {
169 logg->logError(__FILE__, __LINE__, "gethostname failed");
172 // Subvert the defaultGateway field for the port number
173 if (cmdline->port != DEFAULT_PORT) {
174 dstAns.defaultGateway = cmdline->port;
176 // Subvert the subnetMask field for the protocol version
177 dstAns.subnetMask = PROTOCOL_VERSION;
181 struct sockaddr_in6 sockaddr;
184 addrlen = sizeof(sockaddr);
185 read = recvfrom(req, &buf, sizeof(buf), 0, (struct sockaddr *)&sockaddr, &addrlen);
187 logg->logError(__FILE__, __LINE__, "recvfrom failed");
189 } else if ((read == 12) && (memcmp(buf, DST_REQ, sizeof(DST_REQ)) == 0)) {
190 if (sendto(ans, &dstAns, sizeof(dstAns), 0, (struct sockaddr *)&sockaddr, addrlen) != sizeof(dstAns)) {
191 logg->logError(__FILE__, __LINE__, "sendto failed");
198 // retval: -1 = failure; 0 = was already mounted; 1 = successfully mounted
199 static int mountGatorFS() {
200 // If already mounted,
201 if (access("/dev/gator/buffer", F_OK) == 0) {
205 // else, mount the filesystem
206 mkdir("/dev/gator", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
207 if (mount("nodev", "/dev/gator", "gatorfs", 0, NULL) != 0) {
214 static bool init_module (const char * const location) {
216 const int fd = open(location, O_RDONLY);
219 if (fstat(fd, &st) == 0) {
220 void * const p = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
221 if (p != MAP_FAILED) {
222 if (syscall(__NR_init_module, p, st.st_size, "") == 0) {
225 munmap(p, st.st_size);
234 static int setupFilesystem(char* module) {
237 // Verify root permissions
238 uid_t euid = geteuid();
240 logg->logError(__FILE__, __LINE__, "gatord must be launched with root privileges");
245 // unmount and rmmod if the module was specified on the commandline, i.e. ensure that the specified module is indeed running
246 shutdownFilesystem();
249 if (access("/dev/gator/buffer", F_OK) == 0) {
250 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");
255 retval = mountGatorFS();
257 logg->logMessage("Driver already running at startup");
258 driverRunningAtStart = true;
259 } else if (retval == 0) {
260 logg->logMessage("Driver already mounted at startup");
261 driverRunningAtStart = driverMountedAtStart = true;
263 char command[256]; // arbitrarily large amount
264 char location[256]; // arbitrarily large amount
267 strncpy(location, module, sizeof(location));
269 // Is the driver co-located in the same directory?
270 if (util->getApplicationFullPath(location, sizeof(location)) != 0) { // allow some buffer space
271 logg->logMessage("Unable to determine the full path of gatord, the cwd will be used");
273 strncat(location, "gator.ko", sizeof(location) - strlen(location) - 1);
276 if (access(location, F_OK) == -1) {
277 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");
282 bool success = init_module(location);
284 logg->logMessage("init_module failed, trying insmod");
285 snprintf(command, sizeof(command), "insmod %s >/dev/null 2>&1", location);
286 if (system(command) != 0) {
287 logg->logMessage("Unable to load gator.ko driver with command: %s", command);
288 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");
293 if (mountGatorFS() == -1) {
294 logg->logError(__FILE__, __LINE__, "Unable to mount the gator filesystem needed for profiling.");
302 static int shutdownFilesystem() {
303 if (driverMountedAtStart == false) {
304 umount("/dev/gator");
306 if (driverRunningAtStart == false) {
307 if (syscall(__NR_delete_module, "gator", O_NONBLOCK) != 0) {
308 logg->logMessage("delete_module failed, trying rmmod");
309 if (system("rmmod gator >/dev/null 2>&1") != 0) {
318 static struct cmdline_t parseCommandLine(int argc, char** argv) {
319 struct cmdline_t cmdline;
320 cmdline.port = DEFAULT_PORT;
321 cmdline.module = NULL;
322 char version_string[256]; // arbitrary length to hold the version information
325 // build the version string
326 if (PROTOCOL_VERSION < PROTOCOL_DEV) {
327 snprintf(version_string, sizeof(version_string), "Streamline gatord version %d (DS-5 v5.%d)", PROTOCOL_VERSION, PROTOCOL_VERSION);
329 snprintf(version_string, sizeof(version_string), "Streamline gatord development version %d", PROTOCOL_VERSION);
332 while ((c = getopt(argc, argv, "hvp:s:c:e:m:o:")) != -1) {
335 gSessionData->mConfigurationXMLPath = optarg;
338 gSessionData->mEventsXMLPath = optarg;
341 cmdline.module = optarg;
344 cmdline.port = strtol(optarg, NULL, 10);
347 gSessionData->mSessionXMLPath = optarg;
350 gSessionData->mTargetPath = optarg;
354 logg->logError(__FILE__, __LINE__,
355 "%s. All parameters are optional:\n"
356 "-c config_xml path and filename of the configuration.xml to use\n"
357 "-e events_xml path and filename of the events.xml to use\n"
358 "-h this help page\n"
359 "-m module path and filename of gator.ko\n"
360 "-p port_number port upon which the server listens; default is 8080\n"
361 "-s session_xml path and filename of a session xml used for local capture\n"
362 "-o apc_dir path and name of the output for a local capture\n"
363 "-v version information\n"
368 logg->logError(__FILE__, __LINE__, version_string);
375 if (cmdline.port != DEFAULT_PORT && gSessionData->mSessionXMLPath != NULL) {
376 logg->logError(__FILE__, __LINE__, "Only a port or a session xml can be specified, not both");
380 if (gSessionData->mTargetPath != NULL && gSessionData->mSessionXMLPath == NULL) {
381 logg->logError(__FILE__, __LINE__, "Missing -s command line option required for a local capture.");
386 logg->logError(__FILE__, __LINE__, "Unknown argument: %s. Use '-h' for help.", argv[optind]);
393 // Gator data flow: collector -> collector fifo -> sender
394 int main(int argc, char** argv) {
395 // Ensure proper signal handling by making gatord the process group leader
396 // e.g. it may not be the group leader when launched as 'sudo gatord'
399 logg = new Logging(DEBUG); // Set up global thread-safe logging
400 gSessionData = new SessionData(); // Global data class
401 util = new OlyUtility(); // Set up global utility class
403 // Initialize drivers
406 prctl(PR_SET_NAME, (unsigned long)&"gatord-main", 0, 0, 0);
407 pthread_mutex_init(&numSessions_mutex, NULL);
409 signal(SIGINT, handler);
410 signal(SIGTERM, handler);
411 signal(SIGABRT, handler);
413 // Set to high priority
414 if (setpriority(PRIO_PROCESS, syscall(__NR_gettid), -19) == -1) {
415 logg->logMessage("setpriority() failed");
418 // Parse the command line parameters
419 struct cmdline_t cmdline = parseCommandLine(argc, argv);
421 // Call before setting up the SIGCHLD handler, as system() spawns child processes
422 setupFilesystem(cmdline.module);
424 // Handle child exit codes
425 signal(SIGCHLD, child_exit);
427 // Ignore the SIGPIPE signal so that any send to a broken socket will return an error code instead of asserting a signal
428 // Handling the error at the send function call is much easier than trying to do anything intelligent in the sig handler
429 signal(SIGPIPE, SIG_IGN);
431 // If the command line argument is a session xml file, no need to open a socket
432 if (gSessionData->mSessionXMLPath) {
437 pthread_t answerThreadID;
438 if (pthread_create(&answerThreadID, NULL, answerThread, &cmdline)) {
439 logg->logError(__FILE__, __LINE__, "Failed to create answer thread");
442 sock = new OlySocket(cmdline.port, true);
443 // Forever loop, can be exited via a signal or exception
445 logg->logMessage("Waiting on connection...");
446 sock->acceptConnection();
451 logg->logError(__FILE__, __LINE__, "Fork process failed. Please power cycle the target device if this error persists.");
452 } else if (pid == 0) {
454 sock->closeServerSocket();
455 child = new Child(sock, numSessions + 1);
463 pthread_mutex_lock(&numSessions_mutex);
465 pthread_mutex_unlock(&numSessions_mutex);
467 // Maximum number of connections is 2
469 while (numSessions > 1) {
470 // Throttle until one of the children exits before continuing to accept another socket connection
471 logg->logMessage("%d sessions active!", numSessions);
472 if (wait++ >= 10) { // Wait no more than 10 seconds
473 // Kill last created child