2 * Copyright (C) ARM Limited 2010-2014. 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/mount.h>
14 #include <sys/prctl.h>
15 #include <sys/resource.h>
16 #include <sys/socket.h>
18 #include <sys/syscall.h>
22 #include "CCNDriver.h"
24 #include "EventsXML.h"
27 #include "OlySocket.h"
28 #include "OlyUtility.h"
29 #include "SessionData.h"
33 static int shutdownFilesystem();
34 static pthread_mutex_t numSessions_mutex;
35 static OlyServerSocket* sock = NULL;
36 static Monitor monitor;
37 static int numSessions = 0;
38 static bool driverRunningAtStart = false;
39 static bool driverMountedAtStart = false;
47 #define DEFAULT_PORT 8080
50 if (shutdownFilesystem() == -1) {
51 logg->logMessage("Error shutting down gator filesystem");
58 // CTRL C Signal Handler
59 static void handler(int signum) {
60 logg->logMessage("Received signal %d, gator daemon exiting", signum);
62 // Case 1: both child and parent receive the signal
63 if (numSessions > 0) {
64 // Arbitrary sleep of 1 second to give time for the child to exit;
65 // if something bad happens, continue the shutdown process regardless
69 // Case 2: only the parent received the signal
70 if (numSessions > 0) {
71 // Kill child threads - the first signal exits gracefully
72 logg->logMessage("Killing process group as %d child was running when signal was received", numSessions);
75 // Give time for the child to exit
78 if (numSessions > 0) {
79 // The second signal force kills the child
80 logg->logMessage("Force kill the child");
82 // Again, sleep for 1 second
85 if (numSessions > 0) {
86 // Something bad has really happened; the child is not exiting and therefore may hold the /dev/gator resource open
87 printf("Unable to kill the gatord child process, thus gator.ko may still be loaded.\n");
96 // Child exit Signal Handler
97 static void child_exit(int) {
99 int pid = wait(&status);
101 pthread_mutex_lock(&numSessions_mutex);
103 pthread_mutex_unlock(&numSessions_mutex);
104 logg->logMessage("Child process %d exited with status %d", pid, status);
108 static const int UDP_REQ_PORT = 30001;
113 uint8_t ethernetAddress[8];
114 uint32_t ethernetType;
118 uint32_t defaultGateway;
120 uint32_t activeConnections;
123 static const char DST_REQ[] = { 'D', 'S', 'T', '_', 'R', 'E', 'Q', ' ', 0, 0, 0, 0x64 };
127 UdpListener() : mDstAns(), mReq(-1) {}
129 void setup(int port) {
130 mReq = udpPort(UDP_REQ_PORT);
132 // Format the answer buffer
133 memset(&mDstAns, 0, sizeof(mDstAns));
134 memcpy(mDstAns.rviHeader, "STR_ANS ", sizeof(mDstAns.rviHeader));
135 if (gethostname(mDstAns.dhcpName, sizeof(mDstAns.dhcpName) - 1) != 0) {
136 logg->logError(__FILE__, __LINE__, "gethostname failed");
139 // Subvert the defaultGateway field for the port number
140 if (port != DEFAULT_PORT) {
141 mDstAns.defaultGateway = port;
143 // Subvert the subnetMask field for the protocol version
144 mDstAns.subnetMask = PROTOCOL_VERSION;
153 struct sockaddr_in6 sockaddr;
156 addrlen = sizeof(sockaddr);
157 read = recvfrom(mReq, &buf, sizeof(buf), 0, (struct sockaddr *)&sockaddr, &addrlen);
159 logg->logError(__FILE__, __LINE__, "recvfrom failed");
161 } else if ((read == 12) && (memcmp(buf, DST_REQ, sizeof(DST_REQ)) == 0)) {
162 // Don't care if sendto fails - gatord shouldn't exit because of it and Streamline will retry
163 sendto(mReq, &mDstAns, sizeof(mDstAns), 0, (struct sockaddr *)&sockaddr, addrlen);
172 int udpPort(int port) {
174 struct sockaddr_in6 sockaddr;
176 int family = AF_INET6;
178 s = socket_cloexec(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
181 s = socket_cloexec(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
183 logg->logError(__FILE__, __LINE__, "socket failed");
189 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on)) != 0) {
190 logg->logError(__FILE__, __LINE__, "setsockopt failed");
194 memset((void*)&sockaddr, 0, sizeof(sockaddr));
195 sockaddr.sin6_family = family;
196 sockaddr.sin6_port = htons(port);
197 sockaddr.sin6_addr = in6addr_any;
198 if (bind(s, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) < 0) {
199 logg->logError(__FILE__, __LINE__, "socket failed");
206 RVIConfigureInfo mDstAns;
210 static UdpListener udpListener;
212 // retval: -1 = failure; 0 = was already mounted; 1 = successfully mounted
213 static int mountGatorFS() {
214 // If already mounted,
215 if (access("/dev/gator/buffer", F_OK) == 0) {
219 // else, mount the filesystem
220 mkdir("/dev/gator", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
221 if (mount("nodev", "/dev/gator", "gatorfs", 0, NULL) != 0) {
228 static bool init_module (const char * const location) {
230 const int fd = open(location, O_RDONLY | O_CLOEXEC);
233 if (fstat(fd, &st) == 0) {
234 void * const p = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
235 if (p != MAP_FAILED) {
236 if (syscall(__NR_init_module, p, (unsigned long)st.st_size, "") == 0) {
239 munmap(p, st.st_size);
248 static bool setupFilesystem(char* module) {
250 // unmount and rmmod if the module was specified on the commandline, i.e. ensure that the specified module is indeed running
251 shutdownFilesystem();
254 if (access("/dev/gator/buffer", F_OK) == 0) {
255 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");
260 const int retval = mountGatorFS();
262 logg->logMessage("Driver already running at startup");
263 driverRunningAtStart = true;
264 } else if (retval == 0) {
265 logg->logMessage("Driver already mounted at startup");
266 driverRunningAtStart = driverMountedAtStart = true;
268 char command[256]; // arbitrarily large amount
269 char location[256]; // arbitrarily large amount
272 strncpy(location, module, sizeof(location));
274 // Is the driver co-located in the same directory?
275 if (util->getApplicationFullPath(location, sizeof(location)) != 0) { // allow some buffer space
276 logg->logMessage("Unable to determine the full path of gatord, the cwd will be used");
278 strncat(location, "gator.ko", sizeof(location) - strlen(location) - 1);
281 if (access(location, F_OK) == -1) {
282 if (module == NULL) {
283 // The gator kernel is not already loaded and unable to locate gator.ko in the default location
286 // gator location specified on the command line but it was not found
287 logg->logError(__FILE__, __LINE__, "gator module not found at %s", location);
293 bool success = init_module(location);
295 logg->logMessage("init_module failed, trying insmod");
296 snprintf(command, sizeof(command), "insmod %s >/dev/null 2>&1", location);
297 if (system(command) != 0) {
298 logg->logMessage("Unable to load gator.ko driver with command: %s", command);
299 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");
304 if (mountGatorFS() == -1) {
305 logg->logError(__FILE__, __LINE__, "Unable to mount the gator filesystem needed for profiling.");
313 static int shutdownFilesystem() {
314 if (driverMountedAtStart == false) {
315 umount("/dev/gator");
317 if (driverRunningAtStart == false) {
318 if (syscall(__NR_delete_module, "gator", O_NONBLOCK) != 0) {
319 logg->logMessage("delete_module failed, trying rmmod");
320 if (system("rmmod gator >/dev/null 2>&1") != 0) {
329 static const char OPTSTRING[] = "hvudap:s:c:e:m:o:";
331 static bool hasDebugFlag(int argc, char** argv) {
335 while ((c = getopt(argc, argv, OPTSTRING)) != -1) {
344 static struct cmdline_t parseCommandLine(int argc, char** argv) {
345 struct cmdline_t cmdline;
346 cmdline.port = DEFAULT_PORT;
347 cmdline.module = NULL;
348 cmdline.update = false;
349 char version_string[256]; // arbitrary length to hold the version information
352 // build the version string
353 if (PROTOCOL_VERSION < PROTOCOL_DEV) {
354 snprintf(version_string, sizeof(version_string), "Streamline gatord version %d (DS-5 v5.%d)", PROTOCOL_VERSION, PROTOCOL_VERSION);
356 snprintf(version_string, sizeof(version_string), "Streamline gatord development version %d", PROTOCOL_VERSION);
360 while ((c = getopt(argc, argv, OPTSTRING)) != -1) {
363 gSessionData->mConfigurationXMLPath = optarg;
369 gSessionData->mEventsXMLPath = optarg;
372 cmdline.module = optarg;
375 cmdline.port = strtol(optarg, NULL, 10);
378 gSessionData->mSessionXMLPath = optarg;
381 gSessionData->mTargetPath = optarg;
384 cmdline.update = true;
387 gSessionData->mAllowCommands = true;
391 logg->logError(__FILE__, __LINE__,
392 "%s. All parameters are optional:\n"
393 "-c config_xml path and filename of the configuration.xml to use\n"
394 "-e events_xml path and filename of the events.xml to use\n"
395 "-h this help page\n"
396 "-m module path and filename of gator.ko\n"
397 "-p port_number port upon which the server listens; default is 8080\n"
398 "-s session_xml path and filename of a session.xml used for local capture\n"
399 "-o apc_dir path and name of the output for a local capture\n"
400 "-v version information\n"
401 "-d enable debug messages\n"
402 "-a allow the user user to provide a command to run at the start of a capture"
407 logg->logError(__FILE__, __LINE__, version_string);
414 if (cmdline.port != DEFAULT_PORT && gSessionData->mSessionXMLPath != NULL) {
415 logg->logError(__FILE__, __LINE__, "Only a port or a session xml can be specified, not both");
419 if (gSessionData->mTargetPath != NULL && gSessionData->mSessionXMLPath == NULL) {
420 logg->logError(__FILE__, __LINE__, "Missing -s command line option required for a local capture.");
425 logg->logError(__FILE__, __LINE__, "Unknown argument: %s. Use '-h' for help.", argv[optind]);
432 static void handleClient() {
433 OlySocket client(sock->acceptConnection());
438 logg->logError(__FILE__, __LINE__, "Fork process failed. Please power cycle the target device if this error persists.");
439 } else if (pid == 0) {
441 sock->closeServerSocket();
444 child = new Child(&client, numSessions + 1);
450 client.closeSocket();
452 pthread_mutex_lock(&numSessions_mutex);
454 pthread_mutex_unlock(&numSessions_mutex);
456 // Maximum number of connections is 2
458 while (numSessions > 1) {
459 // Throttle until one of the children exits before continuing to accept another socket connection
460 logg->logMessage("%d sessions active!", numSessions);
461 if (wait++ >= 10) { // Wait no more than 10 seconds
462 // Kill last created child
471 // Gator data flow: collector -> collector fifo -> sender
472 int main(int argc, char** argv) {
473 // Ensure proper signal handling by making gatord the process group leader
474 // e.g. it may not be the group leader when launched as 'sudo gatord'
477 // Set up global thread-safe logging
478 logg = new Logging(hasDebugFlag(argc, argv));
480 gSessionData = new SessionData();
481 // Set up global utility class
482 util = new OlyUtility();
484 // Initialize drivers
487 prctl(PR_SET_NAME, (unsigned long)&"gatord-main", 0, 0, 0);
488 pthread_mutex_init(&numSessions_mutex, NULL);
490 signal(SIGINT, handler);
491 signal(SIGTERM, handler);
492 signal(SIGABRT, handler);
494 // Set to high priority
495 if (setpriority(PRIO_PROCESS, syscall(__NR_gettid), -19) == -1) {
496 logg->logMessage("setpriority() failed");
499 // Parse the command line parameters
500 struct cmdline_t cmdline = parseCommandLine(argc, argv);
502 if (cmdline.update) {
503 return update(argv[0]);
506 // Verify root permissions
507 uid_t euid = geteuid();
509 logg->logError(__FILE__, __LINE__, "gatord must be launched with root privileges");
513 // Call before setting up the SIGCHLD handler, as system() spawns child processes
514 if (!setupFilesystem(cmdline.module)) {
515 logg->logMessage("Unable to setup gatorfs, trying perf");
516 if (!gSessionData->perf.setup()) {
517 logg->logError(__FILE__, __LINE__,
518 "Unable to locate gator.ko driver:\n"
519 " >>> gator.ko should be co-located with gatord in the same directory\n"
520 " >>> OR insmod gator.ko prior to launching gatord\n"
521 " >>> OR specify the location of gator.ko on the command line\n"
522 " >>> OR run Linux 3.4 or later with perf (CONFIG_PERF_EVENTS and CONFIG_HW_PERF_EVENTS) and tracing (CONFIG_TRACING and CONFIG_CONTEXT_SWITCH_TRACER) support to collect data via userspace only");
529 mxml_node_t *xml = eventsXML.getTree();
530 // Initialize all drivers
531 for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) {
532 driver->readEvents(xml);
537 // Handle child exit codes
538 signal(SIGCHLD, child_exit);
540 // Ignore the SIGPIPE signal so that any send to a broken socket will return an error code instead of asserting a signal
541 // Handling the error at the send function call is much easier than trying to do anything intelligent in the sig handler
542 signal(SIGPIPE, SIG_IGN);
544 // If the command line argument is a session xml file, no need to open a socket
545 if (gSessionData->mSessionXMLPath) {
550 gSessionData->annotateListener.setup();
551 sock = new OlyServerSocket(cmdline.port);
552 udpListener.setup(cmdline.port);
553 if (!monitor.init() ||
554 !monitor.add(sock->getFd()) ||
555 !monitor.add(udpListener.getReq()) ||
556 !monitor.add(gSessionData->annotateListener.getFd()) ||
558 logg->logError(__FILE__, __LINE__, "Monitor setup failed");
561 // Forever loop, can be exited via a signal or exception
563 struct epoll_event events[2];
564 logg->logMessage("Waiting on connection...");
565 int ready = monitor.wait(events, ARRAY_LENGTH(events), -1);
567 logg->logError(__FILE__, __LINE__, "Monitor::wait failed");
570 for (int i = 0; i < ready; ++i) {
571 if (events[i].data.fd == sock->getFd()) {
573 } else if (events[i].data.fd == udpListener.getReq()) {
574 udpListener.handle();
575 } else if (events[i].data.fd == gSessionData->annotateListener.getFd()) {
576 gSessionData->annotateListener.handle();