Merge branch develop-3.10 into develop-3.10-next
[firefly-linux-kernel-4.4.55.git] / tools / gator / daemon / main.cpp
1 /**
2  * Copyright (C) ARM Limited 2010-2014. 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 <arpa/inet.h>
10 #include <fcntl.h>
11 #include <pthread.h>
12 #include <sys/mman.h>
13 #include <sys/mount.h>
14 #include <sys/prctl.h>
15 #include <sys/resource.h>
16 #include <sys/socket.h>
17 #include <sys/stat.h>
18 #include <sys/syscall.h>
19 #include <sys/wait.h>
20 #include <unistd.h>
21
22 #include "CCNDriver.h"
23 #include "Child.h"
24 #include "EventsXML.h"
25 #include "Logging.h"
26 #include "Monitor.h"
27 #include "OlySocket.h"
28 #include "OlyUtility.h"
29 #include "SessionData.h"
30 #include "Setup.h"
31
32 extern Child* child;
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;
40
41 struct cmdline_t {
42         char *module;
43         int port;
44         bool update;
45 };
46
47 #define DEFAULT_PORT 8080
48
49 void cleanUp() {
50         if (shutdownFilesystem() == -1) {
51                 logg->logMessage("Error shutting down gator filesystem");
52         }
53         delete sock;
54         delete util;
55         delete logg;
56 }
57
58 // CTRL C Signal Handler
59 static void handler(int signum) {
60         logg->logMessage("Received signal %d, gator daemon exiting", signum);
61
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
66                 sleep(1);
67         }
68
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);
73                 kill(0, SIGINT);
74
75                 // Give time for the child to exit
76                 sleep(1);
77
78                 if (numSessions > 0) {
79                         // The second signal force kills the child
80                         logg->logMessage("Force kill the child");
81                         kill(0, SIGINT);
82                         // Again, sleep for 1 second
83                         sleep(1);
84
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");
88                         }
89                 }
90         }
91
92         cleanUp();
93         exit(0);
94 }
95
96 // Child exit Signal Handler
97 static void child_exit(int) {
98         int status;
99         int pid = wait(&status);
100         if (pid != -1) {
101                 pthread_mutex_lock(&numSessions_mutex);
102                 numSessions--;
103                 pthread_mutex_unlock(&numSessions_mutex);
104                 logg->logMessage("Child process %d exited with status %d", pid, status);
105         }
106 }
107
108 static const int UDP_REQ_PORT = 30001;
109
110 typedef struct {
111         char rviHeader[8];
112         uint32_t messageID;
113         uint8_t ethernetAddress[8];
114         uint32_t ethernetType;
115         uint32_t dhcp;
116         char dhcpName[40];
117         uint32_t ipAddress;
118         uint32_t defaultGateway;
119         uint32_t subnetMask;
120         uint32_t activeConnections;
121 } RVIConfigureInfo;
122
123 static const char DST_REQ[] = { 'D', 'S', 'T', '_', 'R', 'E', 'Q', ' ', 0, 0, 0, 0x64 };
124
125 class UdpListener {
126 public:
127         UdpListener() : mDstAns(), mReq(-1) {}
128
129         void setup(int port) {
130                 mReq = udpPort(UDP_REQ_PORT);
131
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");
137                         handleException();
138                 }
139                 // Subvert the defaultGateway field for the port number
140                 if (port != DEFAULT_PORT) {
141                         mDstAns.defaultGateway = port;
142                 }
143                 // Subvert the subnetMask field for the protocol version
144                 mDstAns.subnetMask = PROTOCOL_VERSION;
145         }
146
147         int getReq() const {
148                 return mReq;
149         }
150
151         void handle() {
152                 char buf[128];
153                 struct sockaddr_in6 sockaddr;
154                 socklen_t addrlen;
155                 int read;
156                 addrlen = sizeof(sockaddr);
157                 read = recvfrom(mReq, &buf, sizeof(buf), 0, (struct sockaddr *)&sockaddr, &addrlen);
158                 if (read < 0) {
159                         logg->logError(__FILE__, __LINE__, "recvfrom failed");
160                         handleException();
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);
164                 }
165         }
166
167         void close() {
168                 ::close(mReq);
169         }
170
171 private:
172         int udpPort(int port) {
173                 int s;
174                 struct sockaddr_in6 sockaddr;
175                 int on;
176                 int family = AF_INET6;
177
178                 s = socket_cloexec(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
179                 if (s == -1) {
180                         family = AF_INET;
181                         s = socket_cloexec(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
182                         if (s == -1) {
183                                 logg->logError(__FILE__, __LINE__, "socket failed");
184                                 handleException();
185                         }
186                 }
187
188                 on = 1;
189                 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on)) != 0) {
190                         logg->logError(__FILE__, __LINE__, "setsockopt failed");
191                         handleException();
192                 }
193
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");
200                         handleException();
201                 }
202
203                 return s;
204         }
205
206         RVIConfigureInfo mDstAns;
207         int mReq;
208 };
209
210 static UdpListener udpListener;
211
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) {
216                 return 0;
217         }
218
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) {
222                 return -1;
223         } else {
224                 return 1;
225         }
226 }
227
228 static bool init_module (const char * const location) {
229         bool ret(false);
230         const int fd = open(location, O_RDONLY | O_CLOEXEC);
231         if (fd >= 0) {
232                 struct stat st;
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) {
237                                         ret = true;
238                                 }
239                                 munmap(p, st.st_size);
240                         }
241                 }
242                 close(fd);
243         }
244
245         return ret;
246 }
247
248 static bool setupFilesystem(char* module) {
249         if (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();
252
253                 // if still mounted
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");
256                         handleException();
257                 }
258         }
259
260         const int retval = mountGatorFS();
261         if (retval == 1) {
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;
267         } else {
268                 char command[256]; // arbitrarily large amount
269                 char location[256]; // arbitrarily large amount
270
271                 if (module) {
272                         strncpy(location, module, sizeof(location));
273                 } else {
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");
277                         }
278                         strncat(location, "gator.ko", sizeof(location) - strlen(location) - 1);
279                 }
280
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
284                                 return false;
285                         } else {
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);
288                                 handleException();
289                         }
290                 }
291
292                 // Load driver
293                 bool success = init_module(location);
294                 if (!success) {
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");
300                                 handleException();
301                         }
302                 }
303
304                 if (mountGatorFS() == -1) {
305                         logg->logError(__FILE__, __LINE__, "Unable to mount the gator filesystem needed for profiling.");
306                         handleException();
307                 }
308         }
309
310         return true;
311 }
312
313 static int shutdownFilesystem() {
314         if (driverMountedAtStart == false) {
315                 umount("/dev/gator");
316         }
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) {
321                                 return -1;
322                         }
323                 }
324         }
325
326         return 0; // success
327 }
328
329 static const char OPTSTRING[] = "hvudap:s:c:e:m:o:";
330
331 static bool hasDebugFlag(int argc, char** argv) {
332         int c;
333
334         optind = 1;
335         while ((c = getopt(argc, argv, OPTSTRING)) != -1) {
336                 if (c == 'd') {
337                         return true;
338                 }
339         }
340
341         return false;
342 }
343
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
350         int c;
351
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);
355         } else {
356                 snprintf(version_string, sizeof(version_string), "Streamline gatord development version %d", PROTOCOL_VERSION);
357         }
358
359         optind = 1;
360         while ((c = getopt(argc, argv, OPTSTRING)) != -1) {
361                 switch (c) {
362                         case 'c':
363                                 gSessionData->mConfigurationXMLPath = optarg;
364                                 break;
365                         case 'd':
366                                 // Already handled
367                                 break;
368                         case 'e':
369                                 gSessionData->mEventsXMLPath = optarg;
370                                 break;
371                         case 'm':
372                                 cmdline.module = optarg;
373                                 break;
374                         case 'p':
375                                 cmdline.port = strtol(optarg, NULL, 10);
376                                 break;
377                         case 's':
378                                 gSessionData->mSessionXMLPath = optarg;
379                                 break;
380                         case 'o':
381                                 gSessionData->mTargetPath = optarg;
382                                 break;
383                         case 'u':
384                                 cmdline.update = true;
385                                 break;
386                         case 'a':
387                                 gSessionData->mAllowCommands = true;
388                                 break;
389                         case 'h':
390                         case '?':
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"
403                                         , version_string);
404                                 handleException();
405                                 break;
406                         case 'v':
407                                 logg->logError(__FILE__, __LINE__, version_string);
408                                 handleException();
409                                 break;
410                 }
411         }
412
413         // Error checking
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");
416                 handleException();
417         }
418
419         if (gSessionData->mTargetPath != NULL && gSessionData->mSessionXMLPath == NULL) {
420                 logg->logError(__FILE__, __LINE__, "Missing -s command line option required for a local capture.");
421                 handleException();
422         }
423
424         if (optind < argc) {
425                 logg->logError(__FILE__, __LINE__, "Unknown argument: %s. Use '-h' for help.", argv[optind]);
426                 handleException();
427         }
428
429         return cmdline;
430 }
431
432 static void handleClient() {
433         OlySocket client(sock->acceptConnection());
434
435         int pid = fork();
436         if (pid < 0) {
437                 // Error
438                 logg->logError(__FILE__, __LINE__, "Fork process failed. Please power cycle the target device if this error persists.");
439         } else if (pid == 0) {
440                 // Child
441                 sock->closeServerSocket();
442                 udpListener.close();
443                 monitor.close();
444                 child = new Child(&client, numSessions + 1);
445                 child->run();
446                 delete child;
447                 exit(0);
448         } else {
449                 // Parent
450                 client.closeSocket();
451
452                 pthread_mutex_lock(&numSessions_mutex);
453                 numSessions++;
454                 pthread_mutex_unlock(&numSessions_mutex);
455
456                 // Maximum number of connections is 2
457                 int wait = 0;
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
463                                 kill(pid, SIGALRM);
464                                 break;
465                         }
466                         sleep(1);
467                 }
468         }
469 }
470
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'
475         setsid();
476
477   // Set up global thread-safe logging
478         logg = new Logging(hasDebugFlag(argc, argv));
479         // Global data class
480         gSessionData = new SessionData();
481         // Set up global utility class
482         util = new OlyUtility();
483
484         // Initialize drivers
485         new CCNDriver();
486
487         prctl(PR_SET_NAME, (unsigned long)&"gatord-main", 0, 0, 0);
488         pthread_mutex_init(&numSessions_mutex, NULL);
489
490         signal(SIGINT, handler);
491         signal(SIGTERM, handler);
492         signal(SIGABRT, handler);
493
494         // Set to high priority
495         if (setpriority(PRIO_PROCESS, syscall(__NR_gettid), -19) == -1) {
496                 logg->logMessage("setpriority() failed");
497         }
498
499         // Parse the command line parameters
500         struct cmdline_t cmdline = parseCommandLine(argc, argv);
501
502         if (cmdline.update) {
503                 return update(argv[0]);
504         }
505
506         // Verify root permissions
507         uid_t euid = geteuid();
508         if (euid) {
509                 logg->logError(__FILE__, __LINE__, "gatord must be launched with root privileges");
510                 handleException();
511         }
512
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");
523                         handleException();
524                 }
525         }
526
527         {
528                 EventsXML eventsXML;
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);
533                 }
534                 mxmlDelete(xml);
535         }
536
537         // Handle child exit codes
538         signal(SIGCHLD, child_exit);
539
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);
543
544         // If the command line argument is a session xml file, no need to open a socket
545         if (gSessionData->mSessionXMLPath) {
546                 child = new Child();
547                 child->run();
548                 delete child;
549         } else {
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()) ||
557                                 false) {
558                         logg->logError(__FILE__, __LINE__, "Monitor setup failed");
559                         handleException();
560                 }
561                 // Forever loop, can be exited via a signal or exception
562                 while (1) {
563                         struct epoll_event events[2];
564                         logg->logMessage("Waiting on connection...");
565                         int ready = monitor.wait(events, ARRAY_LENGTH(events), -1);
566                         if (ready < 0) {
567                                 logg->logError(__FILE__, __LINE__, "Monitor::wait failed");
568                                 handleException();
569                         }
570                         for (int i = 0; i < ready; ++i) {
571                                 if (events[i].data.fd == sock->getFd()) {
572                                         handleClient();
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();
577                                 }
578                         }
579                 }
580         }
581
582         cleanUp();
583         return 0;
584 }