gator: Version 5.16
[firefly-linux-kernel-4.4.55.git] / tools / gator / daemon / main.cpp
1 /**
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>
22 #include <arpa/inet.h>
23 #include <sys/socket.h>
24 #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
33 extern Child* child;
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;
40
41 struct cmdline_t {
42         int port;
43         char* module;
44 };
45
46 #define DEFAULT_PORT 8080
47
48 void cleanUp() {
49         if (shutdownFilesystem() == -1) {
50                 logg->logMessage("Error shutting down gator filesystem");
51         }
52         delete sock;
53         delete util;
54         delete logg;
55 }
56
57 // CTRL C Signal Handler
58 static 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
96 static 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
107 static 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
139 typedef 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
152 static const char DST_REQ[] = { 'D', 'S', 'T', '_', 'R', 'E', 'Q', ' ', 0, 0, 0, 0x64 };
153
154 static 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
193 // retval: -1 = failure; 0 = was already mounted; 1 = successfully mounted
194 static 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
209 static 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
229 static 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
297 static 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
313 static struct cmdline_t parseCommandLine(int argc, char** argv) {
314         struct cmdline_t cmdline;
315         cmdline.port = DEFAULT_PORT;
316         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) {
322                 snprintf(version_string, sizeof(version_string), "Streamline gatord version %d (DS-5 v5.%d)", PROTOCOL_VERSION, PROTOCOL_VERSION);
323         } 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
370         if (cmdline.port != DEFAULT_PORT && gSessionData->mSessionXMLPath != NULL) {
371                 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
389 int 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 {
432                 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);
438                 // Forever loop, can be exited via a signal or exception
439                 while (1) {
440                         logg->logMessage("Waiting on connection...");
441                         sock->acceptConnection();
442
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
449                                 sock->closeServerSocket();
450                                 child = new Child(sock, numSessions + 1);
451                                 child->run();
452                                 delete child;
453                                 exit(0);
454                         } else {
455                                 // Parent
456                                 sock->closeSocket();
457
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 }