bfd36b98766c63319b76702d0845d75cc2a92c87
[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) {
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_in6 sockaddr;
110         int on;
111         int family = AF_INET6;
112
113         s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
114         if (s == -1) {
115                 family = AF_INET;
116                 s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
117                 if (s == -1) {
118                         logg->logError(__FILE__, __LINE__, "socket failed");
119                         handleException();
120                 }
121         }
122
123         on = 1;
124         if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on)) != 0) {
125                 logg->logError(__FILE__, __LINE__, "setsockopt failed");
126                 handleException();
127         }
128
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");
135                 handleException();
136         }
137
138         return s;
139 }
140
141 #define UDP_ANS_PORT 30000
142 #define UDP_REQ_PORT 30001
143
144 typedef struct {
145         char rviHeader[8];
146         uint32_t messageID;
147         uint8_t ethernetAddress[8];
148         uint32_t ethernetType;
149         uint32_t dhcp;
150         char dhcpName[40];
151         uint32_t ipAddress;
152         uint32_t defaultGateway;
153         uint32_t subnetMask;
154         uint32_t activeConnections; 
155 } RVIConfigureInfo;
156
157 static const char DST_REQ[] = { 'D', 'S', 'T', '_', 'R', 'E', 'Q', ' ', 0, 0, 0, 0x64 };
158
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);
164
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");
170                 handleException();
171         }
172         // Subvert the defaultGateway field for the port number
173         if (cmdline->port != DEFAULT_PORT) {
174                 dstAns.defaultGateway = cmdline->port;
175         }
176         // Subvert the subnetMask field for the protocol version
177         dstAns.subnetMask = PROTOCOL_VERSION;
178
179         for (;;) {
180                 char buf[128];
181                 struct sockaddr_in6 sockaddr;
182                 socklen_t addrlen;
183                 int read;
184                 addrlen = sizeof(sockaddr);
185                 read = recvfrom(req, &buf, sizeof(buf), 0, (struct sockaddr *)&sockaddr, &addrlen);
186                 if (read < 0) {
187                         logg->logError(__FILE__, __LINE__, "recvfrom failed");
188                         handleException();
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");
192                                 handleException();
193                         }
194                 }
195         }
196 }
197
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) {
202                 return 0;
203         }
204
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) {
208                 return -1;
209         } else {
210                 return 1;
211         }
212 }
213
214 static bool init_module (const char * const location) {
215         bool ret(false);
216         const int fd = open(location, O_RDONLY);
217         if (fd >= 0) {
218                 struct stat st;
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) {
223                                         ret = true;
224                                 }
225                                 munmap(p, st.st_size);
226                         }
227                 }
228                 close(fd);
229         }
230
231         return ret;
232 }
233
234 static int setupFilesystem(char* module) {
235         int retval;
236
237         // Verify root permissions
238         uid_t euid = geteuid();
239         if (euid) {
240                 logg->logError(__FILE__, __LINE__, "gatord must be launched with root privileges");
241                 handleException();
242         }
243
244         if (module) {
245                 // unmount and rmmod if the module was specified on the commandline, i.e. ensure that the specified module is indeed running
246                 shutdownFilesystem();
247
248                 // if still mounted
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");
251                         handleException();
252                 }
253         }
254
255         retval = mountGatorFS();
256         if (retval == 1) {
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;
262         } else {
263                 char command[256]; // arbitrarily large amount
264                 char location[256]; // arbitrarily large amount
265
266                 if (module) {
267                         strncpy(location, module, sizeof(location));
268                 } else {
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");
272                         }
273                         strncat(location, "gator.ko", sizeof(location) - strlen(location) - 1);
274                 }
275
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");
278                         handleException();
279                 }
280
281                 // Load driver
282                 bool success = init_module(location);
283                 if (!success) {
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");
289                                 handleException();
290                         }
291                 }
292
293                 if (mountGatorFS() == -1) {
294                         logg->logError(__FILE__, __LINE__, "Unable to mount the gator filesystem needed for profiling.");
295                         handleException();
296                 }
297         }
298
299         return 0;
300 }
301
302 static int shutdownFilesystem() {
303         if (driverMountedAtStart == false) {
304                 umount("/dev/gator");
305         }
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) {
310                                 return -1;
311                         }
312                 }
313         }
314
315         return 0; // success
316 }
317
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
323         int c;
324
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);
328         } else {
329                 snprintf(version_string, sizeof(version_string), "Streamline gatord development version %d", PROTOCOL_VERSION);
330         }
331
332         while ((c = getopt(argc, argv, "hvp:s:c:e:m:o:")) != -1) {
333                 switch(c) {
334                         case 'c':
335                                 gSessionData->mConfigurationXMLPath = optarg;
336                                 break;
337                         case 'e':
338                                 gSessionData->mEventsXMLPath = optarg;
339                                 break;
340                         case 'm':
341                                 cmdline.module = optarg;
342                                 break;
343                         case 'p':
344                                 cmdline.port = strtol(optarg, NULL, 10);
345                                 break;
346                         case 's':
347                                 gSessionData->mSessionXMLPath = optarg;
348                                 break;
349                         case 'o':
350                                 gSessionData->mTargetPath = optarg;
351                                 break;
352                         case 'h':
353                         case '?':
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"
364                                         , version_string);
365                                 handleException();
366                                 break;
367                         case 'v':
368                                 logg->logError(__FILE__, __LINE__, version_string);
369                                 handleException();
370                                 break;
371                 }
372         }
373
374         // Error checking
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");
377                 handleException();
378         }
379
380         if (gSessionData->mTargetPath != NULL && gSessionData->mSessionXMLPath == NULL) {
381                 logg->logError(__FILE__, __LINE__, "Missing -s command line option required for a local capture.");
382                 handleException();
383         }
384
385         if (optind < argc) {
386                 logg->logError(__FILE__, __LINE__, "Unknown argument: %s. Use '-h' for help.", argv[optind]);
387                 handleException();
388         }
389
390         return cmdline;
391 }
392
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'
397         setsid();
398
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
402
403         // Initialize drivers
404         new KMod();
405
406         prctl(PR_SET_NAME, (unsigned long)&"gatord-main", 0, 0, 0);
407         pthread_mutex_init(&numSessions_mutex, NULL);
408
409         signal(SIGINT, handler);
410         signal(SIGTERM, handler);
411         signal(SIGABRT, handler);
412
413         // Set to high priority
414         if (setpriority(PRIO_PROCESS, syscall(__NR_gettid), -19) == -1) {
415                 logg->logMessage("setpriority() failed");
416         }
417
418         // Parse the command line parameters
419         struct cmdline_t cmdline = parseCommandLine(argc, argv);
420
421         // Call before setting up the SIGCHLD handler, as system() spawns child processes
422         setupFilesystem(cmdline.module);
423
424         // Handle child exit codes
425         signal(SIGCHLD, child_exit);
426
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);
430
431         // If the command line argument is a session xml file, no need to open a socket
432         if (gSessionData->mSessionXMLPath) {
433                 child = new Child();
434                 child->run();
435                 delete child;
436         } else {
437                 pthread_t answerThreadID;
438                 if (pthread_create(&answerThreadID, NULL, answerThread, &cmdline)) {
439                         logg->logError(__FILE__, __LINE__, "Failed to create answer thread");
440                         handleException();
441                 }
442                 sock = new OlySocket(cmdline.port, true);
443                 // Forever loop, can be exited via a signal or exception
444                 while (1) {
445                         logg->logMessage("Waiting on connection...");
446                         sock->acceptConnection();
447
448                         int pid = fork();
449                         if (pid < 0) {
450                                 // Error
451                                 logg->logError(__FILE__, __LINE__, "Fork process failed. Please power cycle the target device if this error persists.");
452                         } else if (pid == 0) {
453                                 // Child
454                                 sock->closeServerSocket();
455                                 child = new Child(sock, numSessions + 1);
456                                 child->run();
457                                 delete child;
458                                 exit(0);
459                         } else {
460                                 // Parent
461                                 sock->closeSocket();
462
463                                 pthread_mutex_lock(&numSessions_mutex);
464                                 numSessions++;
465                                 pthread_mutex_unlock(&numSessions_mutex);
466
467                                 // Maximum number of connections is 2
468                                 int wait = 0;
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
474                                                 kill(pid, SIGALRM);
475                                                 break;
476                                         }
477                                         sleep(1);
478                                 }
479                         }
480                 }
481         }
482
483         cleanUp();
484         return 0;
485 }