1 /**
2 * Copyright (C) ARM Limited 2010-2011. 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) != -1)
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;
180 for (int i = 1; i < argc; i++) {
181 // Is the argument a number?
182 if (strtol(argv[i], NULL, 10) > 0) {
183 cmdline.port = strtol(argv[i], NULL, 10);
184 continue;
185 }
187 if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "-?") == 0 || strcmp(argv[i], "--help") == 0) {
188 logg->logError(__FILE__, __LINE__,
189 "Streamline gatord version %d. All parameters are optional:\n"
190 "port_number\tport upon which the server listens; default is 8080\n"
191 "session_xml\tfilename of a session xml used for local capture\n"
192 "-v/--version\tversion information\n"
193 "-h/--help\tthis help page\n", PROTOCOL_VERSION);
194 handleException();
195 } else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) {
196 logg->logError(__FILE__, __LINE__, "Streamline gatord version %d", PROTOCOL_VERSION);
197 handleException();
198 } else {
199 // Assume it is an .xml file
200 cmdline.sessionXML = argv[i];
201 }
202 }
204 // Error checking
205 if (cmdline.port != 8080 && cmdline.sessionXML != NULL) {
206 logg->logError(__FILE__, __LINE__, "Only a port or a session xml can be specified, not both");
207 handleException();
208 }
210 return cmdline;
211 }
213 // Gator data flow: collector -> collector fifo -> sender
214 int main(int argc, char** argv, char *envp[]) {
215 logg = new Logging(DEBUG); // Set up global thread-safe logging
216 util = new OlyUtility(); // Set up global utility class
218 prctl(PR_SET_NAME, (unsigned int)&"gatord-main", 0, 0, 0);
219 pthread_mutex_init(&numSessions_mutex, NULL);
221 signal(SIGINT, handler);
222 signal(SIGTERM, handler);
223 signal(SIGABRT, handler);
225 // Set to high priority
226 setpriority(PRIO_PROCESS, syscall(__NR_gettid), -19);
228 // Initialize session data
229 gSessionData.initialize();
231 // Parse the command line parameters
232 struct cmdline_t cmdline = parseCommandLine(argc, argv);
234 // Call before setting up the SIGCHLD handler, as system() spawns child processes
235 setupFilesystem();
237 // Handle child exit codes
238 signal(SIGCHLD, child_exit);
240 // Ignore the SIGPIPE signal so that any send to a broken socket will return an error code instead of asserting a signal
241 // Handling the error at the send function call is much easier than trying to do anything intelligent in the sig handler
242 signal(SIGPIPE, SIG_IGN);
244 // If the command line argument is a session xml file, no need to open a socket
245 if (cmdline.sessionXML) {
246 child = new Child(cmdline.sessionXML);
247 child->run();
248 delete child;
249 } else {
250 socket = new OlySocket(cmdline.port, true);
251 // Forever loop, can be exited via a signal or exception
252 while (1) {
253 logg->logMessage("Waiting on connection...");
254 socket->acceptConnection();
256 int pid = fork();
257 if (pid < 0) {
258 // Error
259 logg->logError(__FILE__, __LINE__, "Fork process failed. Please power cycle the target device if this error persists.");
260 } else if (pid == 0) {
261 // Child
262 socket->closeServerSocket();
263 child = new Child(socket, numSessions + 1);
264 child->run();
265 delete child;
266 exit(0);
267 } else {
268 // Parent
269 socket->closeSocket();
271 pthread_mutex_lock(&numSessions_mutex);
272 numSessions++;
273 pthread_mutex_unlock(&numSessions_mutex);
275 // Maximum number of connections is 2
276 int wait = 0;
277 while (numSessions > 1) {
278 // Throttle until one of the children exits before continuing to accept another socket connection
279 logg->logMessage("%d sessions active!", numSessions);
280 if (wait++ >= 10) { // Wait no more than 10 seconds
281 // Kill last created child
282 kill(pid, SIGALRM);
283 break;
284 }
285 sleep(1);
286 }
287 }
288 }
289 }
291 cleanUp();
292 return 0;
293 }