MpdWebScreenshots.cxx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634
  1. // -------------------------------------------------------------------------
  2. // ----- MpdWebScreenshots source file -----
  3. // -------------------------------------------------------------------------
  4. #include "MpdWebScreenshots.h"
  5. #include "TEveManager.h"
  6. #include "TGLViewer.h"
  7. #include <TThread.h>
  8. #include <sys/stat.h>
  9. #include <sys/socket.h>
  10. #include <sys/wait.h>
  11. #include <netinet/in.h>
  12. #include <iostream>
  13. using namespace std;
  14. // ----- Default constructor (private) ----------------------------------
  15. MpdWebScreenshots::MpdWebScreenshots()
  16. : FairTask("MpdWebScreenshots", 0)
  17. {}
  18. // ----- Destructor ----------------------------------------------------
  19. MpdWebScreenshots::~MpdWebScreenshots()
  20. {}
  21. // ----- Standard constructor ------------------------------------------
  22. MpdWebScreenshots::MpdWebScreenshots(const char* name, char* output_dir, bool isWebServer, Int_t iVerbose)
  23. : FairTask(name, iVerbose)
  24. {
  25. outputDir = output_dir;
  26. outputDir += "/";
  27. isWebStarted = false;
  28. isWeb = isWebServer;
  29. if (isWebServer)
  30. return;
  31. if (strlen(outputDir) > 0)
  32. {
  33. struct stat st = {0};
  34. if (stat(outputDir, &st) == -1) mkdir(outputDir, 0700);
  35. cout<<"Images of the event display will be saved in directory: "<<outputDir<<endl;
  36. }
  37. }
  38. // -------------------------------------------------------------------------
  39. InitStatus MpdWebScreenshots::Init()
  40. {
  41. if (fVerbose > 1) cout<<"MpdWebScreenshots::Init()"<<endl;
  42. if (!IsActive())
  43. return kERROR;
  44. fMan = MpdEventManager::Instance();
  45. // start web server if required
  46. if (isWeb && (!isWebStarted))
  47. {
  48. // Set daemon to FALSE
  49. daemon = 0;
  50. www_thread_par* pPar = new www_thread_par();
  51. pPar->web_port = web_port;
  52. pPar->outputDir = outputDir;
  53. pPar->daemon = daemon;
  54. TThread* threadWebServ = new TThread((TThread::VoidFunc_t)&start_server, (void*)pPar, TThread::kNormalPriority);
  55. threadWebServ->Run();
  56. isWebStarted = true;
  57. }
  58. return kSUCCESS;
  59. }
  60. // -------------------------------------------------------------------------
  61. void MpdWebScreenshots::Exec(Option_t* option)
  62. {
  63. // redraw event display to capture current event
  64. gEve->Redraw3D();
  65. gSystem->ProcessEvents();
  66. TString fileName = outputDir;
  67. if (isMultiFiles == 1)
  68. fileName += "event_" + TString::Format("%d", fMan->GetCurrentEvent());
  69. else
  70. fileName += "event";
  71. if ((iFormatFiles == 0) || (iFormatFiles == 2))
  72. {
  73. fileName += ".png";
  74. gEve->GetDefaultGLViewer()->SavePicture(fileName.Data());
  75. }
  76. if (iFormatFiles > 0)
  77. {
  78. fileName += ".jpg";
  79. gEve->GetDefaultGLViewer()->SavePicture(fileName.Data());
  80. }
  81. return;
  82. }
  83. // -------------------------------------------------------------------------
  84. void MpdWebScreenshots::SetParContainers()
  85. {}
  86. // -------------------------------------------------------------------------
  87. void MpdWebScreenshots::Finish()
  88. {}
  89. // -------------------------------------------------------------------------
  90. int MpdWebScreenshots::daemonize()
  91. {
  92. pid_t pid;
  93. // already a daemon
  94. if (getppid() == 1)
  95. return -1;
  96. // Fork off the parent process
  97. pid = fork();
  98. if (pid < 0)
  99. return -2;
  100. // If we got a good PID, then we can exit the parent process.
  101. if (pid > 0)
  102. return 0;
  103. // At this point we are executing as the child process
  104. // Change the file mode mask
  105. umask(0);
  106. // Create a new SID for the child process
  107. if (setsid() < 0)
  108. return -3;
  109. // Change the current working directory. This prevents the current directory from being locked; hence not being able to remove it.
  110. if ((chdir("/")) < 0)
  111. return -4;
  112. return 0;
  113. }
  114. int MpdWebScreenshots::sendString(const char *message, int socket)
  115. {
  116. return send(socket, message, strlen(message), 0);
  117. }
  118. void MpdWebScreenshots::sendHeader(const char* Status_code, char* Content_Type, int TotalSize, int socket)
  119. {
  120. char* head = (char*)"\r\nHTTP/1.1 ";
  121. char* content_head = (char*)"\r\nContent-Type: ";
  122. char* server_head = (char*)"\r\nServer: PT06";
  123. char* length_head = (char*)"\r\nContent-Length: ";
  124. char* date_head = (char*)"\r\nDate: ";
  125. char* newline = (char*)"\r\n";
  126. time_t rawtime;
  127. time(&rawtime);
  128. char contentLength[100];
  129. sprintf(contentLength, "%i", TotalSize);
  130. char* message = (char*)malloc(
  131. (strlen(head) +
  132. strlen(content_head) +
  133. strlen(server_head) +
  134. strlen(length_head) +
  135. strlen(date_head) +
  136. strlen(newline) +
  137. strlen(Status_code) +
  138. strlen(Content_Type) +
  139. strlen(contentLength) +
  140. 28 +
  141. sizeof(char)
  142. )
  143. * 2
  144. );
  145. if (message != nullptr)
  146. {
  147. strcpy(message, head);
  148. strcat(message, Status_code);
  149. strcat(message, content_head);
  150. strcat(message, Content_Type);
  151. strcat(message, server_head);
  152. strcat(message, length_head);
  153. strcat(message, contentLength);
  154. strcat(message, date_head);
  155. strcat(message, (char*)ctime(&rawtime));
  156. strcat(message, newline);
  157. sendString(message, socket);
  158. free(message);
  159. }
  160. }
  161. // send file
  162. void MpdWebScreenshots::sendFile(FILE* fp, int connecting_socket)
  163. {
  164. int current_char = 0;
  165. do
  166. {
  167. current_char = fgetc(fp);
  168. // send binary data
  169. int bytes_sent = send(connecting_socket, &current_char, sizeof(char), 0);
  170. }
  171. while (current_char != EOF);
  172. }
  173. int MpdWebScreenshots::scan(char* input, char* output, unsigned int start, int max)
  174. {
  175. if (start >= strlen(input))
  176. return -1;
  177. int appending_char_count = 0;
  178. int count = 0;
  179. unsigned int i = start;
  180. for (; i < strlen(input); i++)
  181. {
  182. if (*(input + i) != '\t' && *(input + i) != ' ' && *(input + i) != '\n' && *(input + i) != '\r')
  183. {
  184. if(count < (max-1))
  185. {
  186. *(output + appending_char_count) = *(input + i ) ;
  187. appending_char_count += 1;
  188. count++;
  189. }
  190. }
  191. else
  192. break;
  193. }
  194. *(output + appending_char_count) = '\0';
  195. // Find next word start
  196. i += 1;
  197. for (; i < strlen(input); i++)
  198. {
  199. if (*(input + i ) != '\t' && *(input + i) != ' ' && *(input + i) != '\n' && *(input + i) != '\r')
  200. break;
  201. }
  202. return i;
  203. }
  204. int MpdWebScreenshots::checkMime(char* extension, char* mime_type)
  205. {
  206. char* current_word = (char*) malloc(600);
  207. char* word_holder = (char*) malloc(600);
  208. char* line = (char*) malloc(200);
  209. int startline = 0;
  210. TString mime_file = "mime.types";
  211. FILE* mimeFile = fopen(mime_file.Data(), "r");
  212. free(mime_type);
  213. mime_type = (char*)malloc(200);
  214. memset(mime_type,'\0',200);
  215. while (fgets(line, 200, mimeFile) != nullptr)
  216. {
  217. if (line[0] != '#')
  218. {
  219. startline = scan(line, current_word, 0, 600);
  220. while (1)
  221. {
  222. startline = scan(line, word_holder, startline, 600);
  223. if (startline != -1)
  224. {
  225. if (strcmp(word_holder, extension) == 0)
  226. {
  227. memcpy(mime_type, current_word, strlen(current_word));
  228. free(current_word);
  229. free(word_holder);
  230. free(line);
  231. return 1;
  232. }
  233. }
  234. else
  235. break;
  236. }
  237. }
  238. memset(line, '\0', 200);
  239. }
  240. free(current_word);
  241. free(word_holder);
  242. free(line);
  243. return 0;
  244. }
  245. int MpdWebScreenshots::getHttpVersion(char* input, char* output)
  246. {
  247. char* filename = (char*) malloc(100);
  248. int start = scan(input, filename, 4, 100);
  249. if (start > 0)
  250. {
  251. if (scan(input, output, start, 20))
  252. {
  253. output[strlen(output)+1] = '\0';
  254. if (strcmp("HTTP/1.1" , output) == 0)
  255. return 1;
  256. else
  257. {
  258. if (strcmp("HTTP/1.0", output) == 0)
  259. return 0;
  260. else
  261. return -1;
  262. }
  263. }
  264. else
  265. return -1;
  266. }
  267. return -1;
  268. }
  269. int MpdWebScreenshots::GetExtension(char* input, char* output, int max)
  270. {
  271. int in_position = 0;
  272. int appended_position = 0;
  273. unsigned int i = 0, count = 0;
  274. for (; i < strlen(input); i++)
  275. {
  276. if (in_position == 1)
  277. {
  278. if (count < (unsigned int) max)
  279. {
  280. output[appended_position] = input[i];
  281. appended_position += 1;
  282. count++;
  283. }
  284. }
  285. if (input[i] == '.')
  286. in_position = 1;
  287. }
  288. output[appended_position+1] = '\0';
  289. if (strlen(output) > 0)
  290. return 1;
  291. return -1;
  292. }
  293. // IF NOT EXISTS - RETURN -1. IF EXISTS - RETURN 1
  294. int MpdWebScreenshots::handleHttpGET(char* input, TString output_dir, int connecting_socket)
  295. {
  296. char* filename = (char*) malloc(200 * sizeof(char));
  297. char* path = (char*) malloc(1000 * sizeof(char));
  298. char* extension = (char*) malloc(10 * sizeof(char));
  299. char* mime = (char*) malloc(200 * sizeof(char));
  300. char* httpVersion = (char*) malloc(20 * sizeof(char));
  301. int contentLength = 0, mimeSupported = 0;
  302. memset(path, '\0', 1000);
  303. memset(filename, '\0', 200);
  304. memset(extension, '\0', 10);
  305. memset(mime, '\0', 200);
  306. memset(httpVersion, '\0', 20);
  307. int fileNameLength = scan(input, filename, 5, 200);
  308. int i = 0;
  309. while (filename[i]!='\0' && filename[i]!='?')
  310. i++;
  311. if (filename[i] == '?')
  312. filename[i] = '\0';
  313. if (fileNameLength <= 0)
  314. return -1;
  315. if (getHttpVersion(input, httpVersion) == -1)
  316. {
  317. sendString("501 Not Implemented\n", connecting_socket);
  318. return -1;
  319. }
  320. if (GetExtension(filename, extension, 10) == -1)
  321. {
  322. printf("File extension not existing");
  323. sendString("400 Bad Request\n", connecting_socket);
  324. free(filename);
  325. free(mime);
  326. free(path);
  327. free(extension);
  328. return -1;
  329. }
  330. mimeSupported = checkMime(extension, mime);
  331. if (mimeSupported != 1)
  332. {
  333. printf("Mime not supported");
  334. sendString("400 Bad Request\n", connecting_socket);
  335. free(filename);
  336. free(mime);
  337. free(path);
  338. free(extension);
  339. return -1;
  340. }
  341. // Open the requesting file as binary
  342. strcpy(path, output_dir.Data());
  343. strcat(path, filename);
  344. FILE* fp = fopen(path, "rb");
  345. if (fp == nullptr)
  346. {
  347. printf("\nUnable to open file %s\n",path);
  348. sendString("404 Not Found\n", connecting_socket);
  349. free(filename);
  350. free(mime);
  351. free(extension);
  352. free(path);
  353. return -1;
  354. }
  355. // Calculate Content Length
  356. fseek(fp, 0, SEEK_END);
  357. contentLength = ftell(fp);
  358. rewind(fp);
  359. if (contentLength < 0)
  360. {
  361. printf("File size is zero");
  362. free(filename);
  363. free(mime);
  364. free(extension);
  365. free(path);
  366. fclose(fp);
  367. return -1;
  368. }
  369. // Send File Content
  370. sendHeader("200 OK", mime, contentLength, connecting_socket);
  371. sendFile(fp, connecting_socket);
  372. free(filename);
  373. free(mime);
  374. free(extension);
  375. free(path);
  376. fclose(fp);
  377. return 1;
  378. }
  379. // IF NOT VALID REQUEST - RETURN -1. IF VALID REQUEST - RETURN 1. IF GET - RETURN 2. IF HEAD - RETURN 0.
  380. int MpdWebScreenshots::getRequestType(char* input)
  381. {
  382. int type = -1;
  383. if (strlen(input) > 0)
  384. type = 1;
  385. char* requestType = (char*) malloc(5);
  386. scan(input, requestType, 0, 5);
  387. if (type == 1 && strcmp("GET", requestType) == 0) type = 1;
  388. else if (type == 1 && strcmp("HEAD", requestType) == 0) type = 2;
  389. else if (strlen(input) > 4 && strcmp("POST", requestType) == 0) type = 0;
  390. else type = -1;
  391. return type;
  392. }
  393. int MpdWebScreenshots::receive(int connecting_socket, TString output_dir)
  394. {
  395. char buffer[BUFFER_SIZE];
  396. memset(buffer, '\0', BUFFER_SIZE);
  397. if ((recv(connecting_socket, buffer, BUFFER_SIZE, 0)) == -1)
  398. {
  399. printf("Error handling incoming request");
  400. return -1;
  401. }
  402. int request = getRequestType(buffer);
  403. if (request == 1) // GET
  404. handleHttpGET(buffer, output_dir, connecting_socket);
  405. else
  406. if (request == 2) // HEAD
  407. ; //SendHeader();
  408. else
  409. if (request == 0) // POST
  410. sendString("501 Not Implemented\n", connecting_socket);
  411. else // GARBAGE
  412. sendString("400 Bad Request\n", connecting_socket);
  413. return 1;
  414. }
  415. int MpdWebScreenshots::acceptConnection(int currentSocket, TString output_dir)
  416. {
  417. sockaddr_storage connectorSocket;
  418. socklen_t addressSize = sizeof(connectorSocket);
  419. int connecting_socket = accept(currentSocket, (struct sockaddr *)&(connectorSocket), &addressSize);
  420. if (connecting_socket < 0)
  421. {
  422. perror("Accepting sockets");
  423. return -1;
  424. }
  425. // --- Workflow --- //
  426. // 1. Receive ( recv() ) the GET / HEAD
  427. // 2. Process the request and see if the file exists
  428. // 3. Read the file content
  429. // 4. Send out with correct mine and http 1.1
  430. if (receive(connecting_socket, output_dir) < 0)
  431. {
  432. perror("Receive");
  433. return -1;
  434. }
  435. close(connecting_socket);
  436. while (-1 != waitpid(-1, nullptr, WNOHANG));
  437. return 0;
  438. }
  439. int MpdWebScreenshots::start(int webPort, TString output_dir)
  440. {
  441. // Create a socket and assign currentSocket to the descriptor
  442. int currentSocket = socket(AF_INET, SOCK_STREAM, 0);
  443. if (currentSocket == -1)
  444. {
  445. perror("Create socket");
  446. return -1;
  447. }
  448. // Bind to the currentSocket descriptor and listen to the port in PORT
  449. struct sockaddr_in address;
  450. address.sin_family = AF_INET;
  451. address.sin_addr.s_addr = INADDR_ANY;
  452. address.sin_port = htons(webPort);
  453. if (bind(currentSocket, (struct sockaddr *)&address, sizeof(address)) < 0)
  454. {
  455. perror("Bind to port");
  456. return -2;
  457. }
  458. // Start listening for connections and accept no more than MAX_CONNECTIONS in the Quee
  459. if (listen(currentSocket, MAX_CONNECTIONS) < 0)
  460. {
  461. perror("Listen on port");
  462. return -3;
  463. }
  464. while (1)
  465. acceptConnection(currentSocket, output_dir);
  466. }
  467. int MpdWebScreenshots::start_server(void* ptr)
  468. {
  469. www_thread_par* pPar = (www_thread_par*)ptr;
  470. int webPort = pPar->web_port;
  471. TString output_dir = pPar->outputDir;
  472. int daemon = pPar->daemon;
  473. int argc = 0;
  474. char** argv = nullptr;
  475. TString log_file;
  476. for (int parameterCount = 1; parameterCount < argc; parameterCount++)
  477. {
  478. // If flag -p is used, set port
  479. if (strcmp(argv[parameterCount], "-p") == 0)
  480. {
  481. // Indicate that we want to jump over the next parameter
  482. parameterCount++;
  483. printf("Setting port to %i\n", atoi(argv[parameterCount]));
  484. webPort = atoi(argv[parameterCount]);
  485. }
  486. // If flag -d is used, set daemon to TRUE;
  487. else
  488. {
  489. if (strcmp(argv[parameterCount], "-d") == 0)
  490. {
  491. printf("Setting daemon = TRUE");
  492. daemon = 1;
  493. }
  494. else
  495. {
  496. if (strcmp(argv[parameterCount], "-l") == 0)
  497. {
  498. // Indicate that we want to jump over the next parameter
  499. parameterCount++;
  500. printf("Setting logfile = %s\n", argv[parameterCount]);
  501. log_file = (TString) (char*) argv[parameterCount];
  502. }
  503. else
  504. {
  505. printf("Usage: %s [-p port] [-d] [-l logfile]\n", argv[0]);
  506. printf("\t\t-p port\t\tWhich port to listen to.\n");
  507. printf("\t\t-d\t\tEnables daemon mode.\n");
  508. printf("\t\t-l logfile\tWhich file to store the log to.\n");
  509. return -1;
  510. }
  511. }
  512. }
  513. }
  514. printf("Settings:\n");
  515. printf("Port:\t\t\t%i\n", webPort);
  516. printf("Server root:\t\t%s\n", output_dir.Data());
  517. printf("Logfile:\t\t%s\n", log_file.Data());
  518. printf("daemon:\t\t\t%i\n", daemon);
  519. if (daemon == 1)
  520. daemonize();
  521. start(webPort, output_dir);
  522. return 0;
  523. }
  524. ClassImp(MpdWebScreenshots)