51237cbcf758d9699a8a72c16ce086add4c8856e
1 /**
2 * Copyright (C) ARM Limited 2010-2012. 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 */
9 #include <stdint.h>
10 #include <stdlib.h>
11 #include <signal.h>
12 #include <ctype.h>
13 #include <sys/wait.h>
14 #include <unistd.h>
15 #include <sys/syscall.h>
16 #include <sys/prctl.h>
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <sys/mount.h>
20 #include "Child.h"
21 #include "SessionData.h"
22 #include "OlySocket.h"
23 #include "Logging.h"
24 #include "OlyUtility.h"
26 #define DEBUG false
28 extern Child* child;
29 extern void handleException();
30 int shutdownFilesystem();
31 static pthread_mutex_t numSessions_mutex;
32 static int numSessions = 0;
33 static OlySocket* socket = NULL;
34 static bool driverRunningAtStart = false;
35 static bool driverMountedAtStart = false;
37 struct cmdline_t {
38 int port;
39 char* sessionXML;
40 };
42 void cleanUp() {
43 if (shutdownFilesystem() == -1) {
44 logg->logMessage("Error shutting down gator filesystem");
45 }
46 delete socket;
47 delete util;
48 delete logg;
49 }
51 // CTRL C Signal Handler
52 void handler(int signum) {
53 logg->logMessage("Received signal %d, gator daemon exiting", signum);
55 // Case 1: both child and parent receive the signal
56 if (numSessions > 0) {
57 // Arbitrary sleep of 1 second to give time for the child to exit;
58 // if something bad happens, continue the shutdown process regardless
59 sleep(1);
60 }
62 // Case 2: only the parent received the signal
63 if (numSessions > 0) {
64 // Kill child threads - the first signal exits gracefully
65 logg->logMessage("Killing process group as %d child was running when signal was received", numSessions);
66 kill(0, SIGINT);
68 // Give time for the child to exit
69 sleep(1);
71 if (numSessions > 0) {
72 // The second signal force kills the child
73 logg->logMessage("Force kill the child");
74 kill(0, SIGINT);
75 // Again, sleep for 1 second
76 sleep(1);
78 if (numSessions > 0) {
79 // Something bad has really happened; the child is not exiting and therefore may hold the /dev/gator resource open
80 printf("Unable to kill the gatord child process, thus gator.ko may still be loaded.\n");
81 }
82 }
83 }
85 cleanUp();
86 exit(0);
87 }
89 // Child exit Signal Handler
90 void child_exit(int signum) {
91 int status;
92 int pid = wait(&status);
93 if (pid != -1) {
94 pthread_mutex_lock(&numSessions_mutex);
95 numSessions--;
96 pthread_mutex_unlock(&numSessions_mutex);
97 logg->logMessage("Child process %d exited with status %d", pid, status);
98 }
99 }
101 // retval: -1 = failure; 0 = was already mounted; 1 = successfully mounted
102 int mountGatorFS() {
103 // If already mounted,
104 if (access("/dev/gator/buffer", F_OK) == 0)
105 return 0;
107 // else, mount the filesystem
108 mkdir("/dev/gator", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
109 if (mount("nodev", "/dev/gator", "gatorfs", 0, NULL) != 0)
110 return -1;
111 else
112 return 1;
113 }
115 int setupFilesystem() {
116 int retval;
118 // Verify root permissions
119 uid_t euid = geteuid();
120 if (euid) {
121 logg->logError(__FILE__, __LINE__, "gatord must be launched with root privileges");
122 handleException();
123 }
125 retval = mountGatorFS();
126 if (retval == 1) {
127 logg->logMessage("Driver already running at startup");
128 driverRunningAtStart = true;
129 } else if (retval == 0) {
130 logg->logMessage("Driver already mounted at startup");
131 driverRunningAtStart = driverMountedAtStart = true;
132 } else {
133 char command[256]; // arbitrarily large amount
135 // Is the driver co-located in the same directory?
136 if (util->getApplicationFullPath(command, sizeof(command)) != 0) { // allow some buffer space
137 logg->logMessage("Unable to determine the full path of gatord, the cwd will be used");
138 }
139 strcat(command, "gator.ko");
140 if (access(command, F_OK) == -1) {
141 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");
142 handleException();
143 }
145 // Load driver
146 strcpy(command, "insmod ");
147 util->getApplicationFullPath(&command[7], sizeof(command) - 64); // allow some buffer space
148 strcat(command, "gator.ko >/dev/null 2>&1");
150 if (system(command) != 0) {
151 logg->logMessage("Unable to load gator.ko driver with command: %s", command);
152 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");
153 handleException();
154 }
156 if (mountGatorFS() == -1) {
157 logg->logError(__FILE__, __LINE__, "Unable to mount the gator filesystem needed for profiling.");
158 handleException();
159 }
160 }
162 return 0;
163 }
165 int shutdownFilesystem() {
166 if (driverMountedAtStart == false)
167 umount("/dev/gator");
168 if (driverRunningAtStart == false)
169 if (system("rmmod gator >/dev/null 2>&1") != 0)
170 return -1;
172 return 0; // success
173 }
175 struct cmdline_t parseCommandLine(int argc, char** argv) {
176 struct cmdline_t cmdline;
177 cmdline.port = 8080;
178 cmdline.sessionXML = NULL;
179 int c;
181 while ((c = getopt (argc, argv, "hvp:s:c:")) != -1) {
182 switch(c) {
183 case 'p':
184 cmdline.port = strtol(optarg, NULL, 10);
185 break;
186 case 's':
187 cmdline.sessionXML = optarg;
188 break;
189 case 'c':
190 gSessionData->configurationXMLPath = optarg;
191 break;
192 case 'h':
193 case '?':
194 logg->logError(__FILE__, __LINE__,
195 "Streamline gatord version %d. All parameters are optional:\n"
196 "-p port_number\tport upon which the server listens; default is 8080\n"
197 "-s session_xml\tpath and filename of a session xml used for local capture\n"
198 "-c config_xml\tpath and filename of the configuration.xml to use\n"
199 "-v\t\tversion information\n"
200 "-h\t\tthis help page\n", PROTOCOL_VERSION);
201 handleException();
202 break;
203 case 'v':
204 logg->logError(__FILE__, __LINE__, "Streamline gatord version %d", PROTOCOL_VERSION);
205 handleException();
206 break;
207 }
208 }
210 // Error checking
211 if (cmdline.port != 8080 && cmdline.sessionXML != NULL) {
212 logg->logError(__FILE__, __LINE__, "Only a port or a session xml can be specified, not both");
213 handleException();
214 }
216 if (optind < argc) {
217 logg->logError(__FILE__, __LINE__, "Unknown argument: %s. Use '-h' for help.", argv[optind]);
218 handleException();
219 }
221 return cmdline;
222 }
224 // Gator data flow: collector -> collector fifo -> sender
225 int main(int argc, char** argv, char *envp[]) {
226 gSessionData = new SessionData(); // Global data class
227 logg = new Logging(DEBUG); // Set up global thread-safe logging
228 util = new OlyUtility(); // Set up global utility class
230 prctl(PR_SET_NAME, (unsigned int)&"gatord-main", 0, 0, 0);
231 pthread_mutex_init(&numSessions_mutex, NULL);
233 signal(SIGINT, handler);
234 signal(SIGTERM, handler);
235 signal(SIGABRT, handler);
237 // Set to high priority
238 if (setpriority(PRIO_PROCESS, syscall(__NR_gettid), -19) == -1)
239 logg->logMessage("setpriority() failed");
241 // Initialize session data
242 gSessionData->initialize();
244 // Parse the command line parameters
245 struct cmdline_t cmdline = parseCommandLine(argc, argv);
247 // Call before setting up the SIGCHLD handler, as system() spawns child processes
248 setupFilesystem();
250 // Handle child exit codes
251 signal(SIGCHLD, child_exit);
253 // Ignore the SIGPIPE signal so that any send to a broken socket will return an error code instead of asserting a signal
254 // Handling the error at the send function call is much easier than trying to do anything intelligent in the sig handler
255 signal(SIGPIPE, SIG_IGN);
257 // If the command line argument is a session xml file, no need to open a socket
258 if (cmdline.sessionXML) {
259 child = new Child(cmdline.sessionXML);
260 child->run();
261 delete child;
262 } else {
263 socket = new OlySocket(cmdline.port, true);
264 // Forever loop, can be exited via a signal or exception
265 while (1) {
266 logg->logMessage("Waiting on connection...");
267 socket->acceptConnection();
269 int pid = fork();
270 if (pid < 0) {
271 // Error
272 logg->logError(__FILE__, __LINE__, "Fork process failed. Please power cycle the target device if this error persists.");
273 } else if (pid == 0) {
274 // Child
275 socket->closeServerSocket();
276 child = new Child(socket, numSessions + 1);
277 child->run();
278 delete child;
279 exit(0);
280 } else {
281 // Parent
282 socket->closeSocket();
284 pthread_mutex_lock(&numSessions_mutex);
285 numSessions++;
286 pthread_mutex_unlock(&numSessions_mutex);
288 // Maximum number of connections is 2
289 int wait = 0;
290 while (numSessions > 1) {
291 // Throttle until one of the children exits before continuing to accept another socket connection
292 logg->logMessage("%d sessions active!", numSessions);
293 if (wait++ >= 10) { // Wait no more than 10 seconds
294 // Kill last created child
295 kill(pid, SIGALRM);
296 break;
297 }
298 sleep(1);
299 }
300 }
301 }
302 }
304 cleanUp();
305 return 0;
306 }