1 /* 2 * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 #include "Platform.h" 27 28 #ifdef LINUX 29 30 #include "JavaVirtualMachine.h" 31 #include "LinuxPlatform.h" 32 #include "PlatformString.h" 33 #include "IniFile.h" 34 #include "Helpers.h" 35 36 #include <stdlib.h> 37 #include <pwd.h> 38 #include <sys/file.h> 39 #include <sys/stat.h> 40 #include <errno.h> 41 #include <unistd.h> 42 #include <sys/types.h> 43 #include <limits.h> 44 45 #define LINUX_JPACKAGER_TMP_DIR "/.java/jpackager/tmp" 46 47 48 TString GetEnv(const TString &name) { 49 TString result; 50 51 char *value = ::getenv((TCHAR*)name.c_str()); 52 53 if (value != NULL) { 54 result = value; 55 } 56 57 return result; 58 } 59 60 LinuxPlatform::LinuxPlatform(void) : Platform(), 61 GenericPlatform(), PosixPlatform() { 62 FMainThread = pthread_self(); 63 } 64 65 LinuxPlatform::~LinuxPlatform(void) { 66 } 67 68 void LinuxPlatform::ShowMessage(TString title, TString description) { 69 printf("%s %s\n", PlatformString(title).toPlatformString(), 70 PlatformString(description).toPlatformString()); 71 fflush(stdout); 72 } 73 74 void LinuxPlatform::ShowMessage(TString description) { 75 TString appname = GetModuleFileName(); 76 appname = FilePath::ExtractFileName(appname); 77 ShowMessage(PlatformString(appname).toPlatformString(), 78 PlatformString(description).toPlatformString()); 79 } 80 81 TCHAR* LinuxPlatform::ConvertStringToFileSystemString(TCHAR* Source, 82 bool &release) { 83 // Not Implemented. 84 return NULL; 85 } 86 87 TCHAR* LinuxPlatform::ConvertFileSystemStringToString(TCHAR* Source, 88 bool &release) { 89 // Not Implemented. 90 return NULL; 91 } 92 93 TString LinuxPlatform::GetModuleFileName() { 94 ssize_t len = 0; 95 TString result; 96 DynamicBuffer<TCHAR> buffer(MAX_PATH); 97 if (buffer.GetData() == NULL) { 98 return result; 99 } 100 101 if ((len = readlink("/proc/self/exe", buffer.GetData(), 102 MAX_PATH - 1)) != -1) { 103 buffer[len] = '\0'; 104 result = buffer.GetData(); 105 } 106 107 return result; 108 } 109 110 void LinuxPlatform::SetCurrentDirectory(TString Value) { 111 chdir(PlatformString(Value).toPlatformString()); 112 } 113 114 TString LinuxPlatform::GetPackageRootDirectory() { 115 TString filename = GetModuleFileName(); 116 return FilePath::ExtractFilePath(filename); 117 } 118 119 TString LinuxPlatform::GetAppDataDirectory() { 120 TString result; 121 TString home = GetEnv(_T("HOME")); 122 123 if (home.empty() == false) { 124 result += FilePath::IncludeTrailingSeparator(home) + _T(".local"); 125 } 126 127 return result; 128 } 129 130 ISectionalPropertyContainer* LinuxPlatform::GetConfigFile(TString FileName) { 131 IniFile *result = new IniFile(); 132 if (result == NULL) { 133 return NULL; 134 } 135 136 if (result->LoadFromFile(FileName) == false) { 137 // New property file format was not found, 138 // attempt to load old property file format. 139 Helpers::LoadOldConfigFile(FileName, result); 140 } 141 142 return result; 143 } 144 145 TString LinuxPlatform::GetBundledJVMLibraryFileName(TString RuntimePath) { 146 TString result = FilePath::IncludeTrailingSeparator(RuntimePath) + 147 "lib/libjli.so"; 148 149 if (FilePath::FileExists(result) == false) { 150 result = FilePath::IncludeTrailingSeparator(RuntimePath) + 151 "lib/jli/libjli.so"; 152 if (FilePath::FileExists(result) == false) { 153 printf("Cannot find libjli.so!"); 154 } 155 } 156 157 return result; 158 } 159 160 bool LinuxPlatform::IsMainThread() { 161 bool result = (FMainThread == pthread_self()); 162 return result; 163 } 164 165 TString LinuxPlatform::getTmpDirString() { 166 return TString(LINUX_JPACKAGER_TMP_DIR); 167 } 168 169 void LinuxPlatform::reactivateAnotherInstance() { 170 if (singleInstanceProcessId == 0) { 171 printf("Unable to reactivate another instance, PID is undefined"); 172 return; 173 } 174 175 const ProcessReactivator reactivator(singleInstanceProcessId); 176 } 177 178 TPlatformNumber LinuxPlatform::GetMemorySize() { 179 long pages = sysconf(_SC_PHYS_PAGES); 180 long page_size = sysconf(_SC_PAGE_SIZE); 181 TPlatformNumber result = pages * page_size; 182 result = result / 1048576; // Convert from bytes to megabytes. 183 return result; 184 } 185 186 #ifdef DEBUG 187 bool LinuxPlatform::IsNativeDebuggerPresent() { 188 // gdb opens file descriptors stdin=3, stdout=4, stderr=5 whereas 189 // a typical prog uses only stdin=0, stdout=1, stderr=2. 190 bool result = false; 191 FILE *fd = fopen("/tmp", "r"); 192 193 if (fileno(fd) > 5) { 194 result = true; 195 } 196 197 fclose(fd); 198 return result; 199 } 200 201 int LinuxPlatform::GetProcessID() { 202 int pid = getpid(); 203 return pid; 204 } 205 #endif //DEBUG 206 207 //---------------------------------------------------------------------------- 208 209 #ifndef __UNIX_JPACKAGER_PLATFORM__ 210 #define __UNIX_JPACKAGER_PLATFORM__ 211 212 /** Provide an abstraction for difference in the platform APIs, 213 e.g. string manipulation functions, etc. */ 214 #include <stdio.h> 215 #include <string.h> 216 #include <strings.h> 217 #include <sys/stat.h> 218 219 #define TCHAR char 220 221 #define _T(x) x 222 223 #define JPACKAGER_MULTIBYTE_SNPRINTF snprintf 224 225 #define JPACKAGER_SNPRINTF(buffer, sizeOfBuffer, count, format, ...) \ 226 snprintf((buffer), (count), (format), __VA_ARGS__) 227 228 #define JPACKAGER_PRINTF(format, ...) \ 229 printf((format), ##__VA_ARGS__) 230 231 #define JPACKAGER_FPRINTF(dest, format, ...) \ 232 fprintf((dest), (format), __VA_ARGS__) 233 234 #define JPACKAGER_SSCANF(buf, format, ...) \ 235 sscanf((buf), (format), __VA_ARGS__) 236 237 #define JPACKAGER_STRDUP(strSource) \ 238 strdup((strSource)) 239 240 //return "error code" (like on Windows) 241 static int JPACKAGER_STRNCPY(char *strDest, size_t numberOfElements, 242 const char *strSource, size_t count) { 243 char *s = strncpy(strDest, strSource, count); 244 // Duplicate behavior of the Windows' _tcsncpy_s() by adding a NULL 245 // terminator at the end of the string. 246 if (count < numberOfElements) { 247 s[count] = '\0'; 248 } else { 249 s[numberOfElements - 1] = '\0'; 250 } 251 return (s == strDest) ? 0 : 1; 252 } 253 254 #define JPACKAGER_STRICMP(x, y) \ 255 strcasecmp((x), (y)) 256 257 #define JPACKAGER_STRNICMP(x, y, cnt) \ 258 strncasecmp((x), (y), (cnt)) 259 260 #define JPACKAGER_STRNCMP(x, y, cnt) \ 261 strncmp((x), (y), (cnt)) 262 263 #define JPACKAGER_STRLEN(x) \ 264 strlen((x)) 265 266 #define JPACKAGER_STRSTR(x, y) \ 267 strstr((x), (y)) 268 269 #define JPACKAGER_STRCHR(x, y) \ 270 strchr((x), (y)) 271 272 #define JPACKAGER_STRRCHR(x, y) \ 273 strrchr((x), (y)) 274 275 #define JPACKAGER_STRPBRK(x, y) \ 276 strpbrk((x), (y)) 277 278 #define JPACKAGER_GETENV(x) \ 279 getenv((x)) 280 281 #define JPACKAGER_PUTENV(x) \ 282 putenv((x)) 283 284 #define JPACKAGER_STRCMP(x, y) \ 285 strcmp((x), (y)) 286 287 #define JPACKAGER_STRCPY(x, y) \ 288 strcpy((x), (y)) 289 290 #define JPACKAGER_STRCAT(x, y) \ 291 strcat((x), (y)) 292 293 #define JPACKAGER_ATOI(x) \ 294 atoi((x)) 295 296 #define JPACKAGER_FOPEN(x, y) \ 297 fopen((x), (y)) 298 299 #define JPACKAGER_FGETS(x, y, z) \ 300 fgets((x), (y), (z)) 301 302 #define JPACKAGER_REMOVE(x) \ 303 remove((x)) 304 305 #define JPACKAGER_SPAWNV(mode, cmd, args) \ 306 spawnv((mode), (cmd), (args)) 307 308 #define JPACKAGER_ISDIGIT(ch) isdigit(ch) 309 310 // for non-unicode, just return the input string for 311 // the following 2 conversions 312 #define JPACKAGER_NEW_MULTIBYTE(message) message 313 314 #define JPACKAGER_NEW_FROM_MULTIBYTE(message) message 315 316 // for non-unicode, no-op for the relase operation 317 // since there is no memory allocated for the 318 // string conversions 319 #define JPACKAGER_RELEASE_MULTIBYTE(tmpMBCS) 320 321 #define JPACKAGER_RELEASE_FROM_MULTIBYTE(tmpMBCS) 322 323 // The size will be used for converting from 1 byte to 1 byte encoding. 324 // Ensure have space for zero-terminator. 325 #define JPACKAGER_GET_SIZE_FOR_ENCODING(message, theLength) (theLength + 1) 326 327 #endif 328 #define xmlTagType 0 329 #define xmlPCDataType 1 330 331 typedef struct _xmlNode XMLNode; 332 typedef struct _xmlAttribute XMLAttribute; 333 334 struct _xmlNode { 335 int _type; // Type of node: tag, pcdata, cdate 336 TCHAR* _name; // Contents of node 337 XMLNode* _next; // Next node at same level 338 XMLNode* _sub; // First sub-node 339 XMLAttribute* _attributes; // List of attributes 340 }; 341 342 struct _xmlAttribute { 343 TCHAR* _name; // Name of attribute 344 TCHAR* _value; // Value of attribute 345 XMLAttribute* _next; // Next attribute for this tag 346 }; 347 348 // Public interface 349 static void RemoveNonAsciiUTF8FromBuffer(char *buf); 350 XMLNode* ParseXMLDocument (TCHAR* buf); 351 void FreeXMLDocument (XMLNode* root); 352 353 // Utility methods for parsing document 354 XMLNode* FindXMLChild (XMLNode* root, const TCHAR* name); 355 TCHAR* FindXMLAttribute (XMLAttribute* attr, const TCHAR* name); 356 357 // Debugging 358 void PrintXMLDocument(XMLNode* node, int indt); 359 360 361 #include <sys/types.h> 362 #include <sys/stat.h> 363 #include <setjmp.h> 364 #include <stdlib.h> 365 #include <wctype.h> 366 367 368 #define JWS_assert(s, msg) \ 369 if (!(s)) { Abort(msg); } 370 371 372 // Internal declarations 373 static XMLNode* ParseXMLElement(void); 374 static XMLAttribute* ParseXMLAttribute(void); 375 static TCHAR* SkipWhiteSpace(TCHAR *p); 376 static TCHAR* SkipXMLName(TCHAR *p); 377 static TCHAR* SkipXMLComment(TCHAR *p); 378 static TCHAR* SkipXMLDocType(TCHAR *p); 379 static TCHAR* SkipXMLProlog(TCHAR *p); 380 static TCHAR* SkipPCData(TCHAR *p); 381 static int IsPCData(TCHAR *p); 382 static void ConvertBuiltInEntities(TCHAR* p); 383 static void SetToken(int type, TCHAR* start, TCHAR* end); 384 static void GetNextToken(void); 385 static XMLNode* CreateXMLNode(int type, TCHAR* name); 386 static XMLAttribute* CreateXMLAttribute(TCHAR *name, TCHAR* value); 387 static XMLNode* ParseXMLElement(void); 388 static XMLAttribute* ParseXMLAttribute(void); 389 static void FreeXMLAttribute(XMLAttribute* attr); 390 static void PrintXMLAttributes(XMLAttribute* attr); 391 static void indent(int indt); 392 393 static jmp_buf jmpbuf; 394 static XMLNode* root_node = NULL; 395 396 /** definition of error codes for setjmp/longjmp, 397 * that can be handled in ParseXMLDocument() 398 */ 399 #define JMP_NO_ERROR 0 400 #define JMP_OUT_OF_RANGE 1 401 402 #define NEXT_CHAR(p) { \ 403 if (*p != 0) { \ 404 p++; \ 405 } else { \ 406 longjmp(jmpbuf, JMP_OUT_OF_RANGE); \ 407 } \ 408 } 409 #define NEXT_CHAR_OR_BREAK(p) { \ 410 if (*p != 0) { \ 411 p++; \ 412 } else { \ 413 break; \ 414 } \ 415 } 416 #define NEXT_CHAR_OR_RETURN(p) { \ 417 if (*p != 0) { \ 418 p++; \ 419 } else { \ 420 return; \ 421 } \ 422 } 423 #define SKIP_CHARS(p,n) { \ 424 int i; \ 425 for (i = 0; i < (n); i++) { \ 426 if (*p != 0) { \ 427 p++; \ 428 } else { \ 429 longjmp(jmpbuf, JMP_OUT_OF_RANGE); \ 430 } \ 431 } \ 432 } 433 #define SKIP_CHARS_OR_BREAK(p,n) { \ 434 int i; \ 435 for (i = 0; i < (n); i++) { \ 436 if (*p != 0) { \ 437 p++; \ 438 } else { \ 439 break; \ 440 } \ 441 } \ 442 if (i < (n)) { \ 443 break; \ 444 } \ 445 } 446 447 /** Iterates through the null-terminated buffer (i.e., C string) and 448 * replaces all UTF-8 encoded character >255 with 255 449 * 450 * UTF-8 encoding: 451 * 452 * Range A: 0x0000 - 0x007F 453 * 0 | bits 0 - 7 454 * Range B : 0x0080 - 0x07FF : 455 * 110 | bits 6 - 10 456 * 10 | bits 0 - 5 457 * Range C : 0x0800 - 0xFFFF : 458 * 1110 | bits 12-15 459 * 10 | bits 6-11 460 * 10 | bits 0-5 461 */ 462 static void RemoveNonAsciiUTF8FromBuffer(char *buf) { 463 char* p; 464 char* q; 465 char c; 466 p = q = buf; 467 // We are not using NEXT_CHAR() to check if *q is NULL, as q is output 468 // location and offset for q is smaller than for p. 469 while(*p != '\0') { 470 c = *p; 471 if ( (c & 0x80) == 0) { 472 /* Range A */ 473 *q++ = *p; 474 NEXT_CHAR(p); 475 } else if ((c & 0xE0) == 0xC0){ 476 /* Range B */ 477 *q++ = (char)0xFF; 478 NEXT_CHAR(p); 479 NEXT_CHAR_OR_BREAK(p); 480 } else { 481 /* Range C */ 482 *q++ = (char)0xFF; 483 NEXT_CHAR(p); 484 SKIP_CHARS_OR_BREAK(p, 2); 485 } 486 } 487 /* Null terminate string */ 488 *q = '\0'; 489 } 490 491 static TCHAR* SkipWhiteSpace(TCHAR *p) { 492 if (p != NULL) { 493 while(iswspace(*p)) 494 NEXT_CHAR_OR_BREAK(p); 495 } 496 return p; 497 } 498 499 static TCHAR* SkipXMLName(TCHAR *p) { 500 TCHAR c = *p; 501 /* Check if start of token */ 502 if ( ('a' <= c && c <= 'z') || 503 ('A' <= c && c <= 'Z') || 504 c == '_' || c == ':') { 505 506 while( ('a' <= c && c <= 'z') || 507 ('A' <= c && c <= 'Z') || 508 ('0' <= c && c <= '9') || 509 c == '_' || c == ':' || c == '.' || c == '-' ) { 510 NEXT_CHAR(p); 511 c = *p; 512 if (c == '\0') break; 513 } 514 } 515 return p; 516 } 517 518 static TCHAR* SkipXMLComment(TCHAR *p) { 519 if (p != NULL) { 520 if (JPACKAGER_STRNCMP(p, _T("<!--"), 4) == 0) { 521 SKIP_CHARS(p, 4); 522 do { 523 if (JPACKAGER_STRNCMP(p, _T("-->"), 3) == 0) { 524 SKIP_CHARS(p, 3); 525 return p; 526 } 527 NEXT_CHAR(p); 528 } while(*p != '\0'); 529 } 530 } 531 return p; 532 } 533 534 static TCHAR* SkipXMLDocType(TCHAR *p) { 535 if (p != NULL) { 536 if (JPACKAGER_STRNCMP(p, _T("<!"), 2) == 0) { 537 SKIP_CHARS(p, 2); 538 while (*p != '\0') { 539 if (*p == '>') { 540 NEXT_CHAR(p); 541 return p; 542 } 543 NEXT_CHAR(p); 544 } 545 } 546 } 547 return p; 548 } 549 550 static TCHAR* SkipXMLProlog(TCHAR *p) { 551 if (p != NULL) { 552 if (JPACKAGER_STRNCMP(p, _T("<?"), 2) == 0) { 553 SKIP_CHARS(p, 2); 554 do { 555 if (JPACKAGER_STRNCMP(p, _T("?>"), 2) == 0) { 556 SKIP_CHARS(p, 2); 557 return p; 558 } 559 NEXT_CHAR(p); 560 } while(*p != '\0'); 561 } 562 } 563 return p; 564 } 565 566 /* Search for the built-in XML entities: 567 * & (&), < (<), > (>), ' ('), and "e(") 568 * and convert them to a real TCHARacter 569 */ 570 static void ConvertBuiltInEntities(TCHAR* p) { 571 TCHAR* q; 572 q = p; 573 // We are not using NEXT_CHAR() to check if *q is NULL, 574 // as q is output location and offset for q is smaller than for p. 575 while(*p) { 576 if (IsPCData(p)) { 577 /* dont convert &xxx values within PData */ 578 TCHAR *end; 579 end = SkipPCData(p); 580 while(p < end) { 581 *q++ = *p; 582 NEXT_CHAR(p); 583 } 584 } else { 585 if (JPACKAGER_STRNCMP(p, _T("&"), 5) == 0) { 586 *q++ = '&'; 587 SKIP_CHARS(p, 5); 588 } else if (JPACKAGER_STRNCMP(p, _T("<"), 4) == 0) { 589 *q = '<'; 590 SKIP_CHARS(p, 4); 591 } else if (JPACKAGER_STRNCMP(p, _T(">"), 4) == 0) { 592 *q = '>'; 593 SKIP_CHARS(p, 4); 594 } else if (JPACKAGER_STRNCMP(p, _T("'"), 6) == 0) { 595 *q = '\''; 596 SKIP_CHARS(p, 6); 597 } else if (JPACKAGER_STRNCMP(p, _T(""e;"), 7) == 0) { 598 *q = '\"'; 599 SKIP_CHARS(p, 7); 600 } else { 601 *q++ = *p; 602 NEXT_CHAR(p); 603 } 604 } 605 } 606 *q = '\0'; 607 } 608 609 /* ------------------------------------------------------------- */ 610 /* XML tokenizer */ 611 612 #define TOKEN_UNKNOWN 0 613 #define TOKEN_BEGIN_TAG 1 /* <tag */ 614 #define TOKEN_END_TAG 2 /* </tag */ 615 #define TOKEN_CLOSE_BRACKET 3 /* > */ 616 #define TOKEN_EMPTY_CLOSE_BRACKET 4 /* /> */ 617 #define TOKEN_PCDATA 5 /* pcdata */ 618 #define TOKEN_CDATA 6 /* cdata */ 619 #define TOKEN_EOF 7 620 621 static TCHAR* CurPos = NULL; 622 static TCHAR* CurTokenName = NULL; 623 static int CurTokenType; 624 static int MaxTokenSize = -1; 625 626 /* Copy token from buffer to Token variable */ 627 static void SetToken(int type, TCHAR* start, TCHAR* end) { 628 int len = end - start; 629 if (len > MaxTokenSize) { 630 if (CurTokenName != NULL) free(CurTokenName); 631 CurTokenName = (TCHAR *)malloc((len + 1) * sizeof(TCHAR)); 632 if (CurTokenName == NULL ) { 633 return; 634 } 635 MaxTokenSize = len; 636 } 637 638 CurTokenType = type; 639 JPACKAGER_STRNCPY(CurTokenName, len + 1, start, len); 640 CurTokenName[len] = '\0'; 641 } 642 643 /* Skip XML comments, doctypes, and prolog tags */ 644 static TCHAR* SkipFilling(void) { 645 TCHAR *q = CurPos; 646 647 /* Skip white space and comment sections */ 648 do { 649 q = CurPos; 650 CurPos = SkipWhiteSpace(CurPos); 651 CurPos = SkipXMLComment(CurPos); /* Must be called befor DocTypes */ 652 CurPos = SkipXMLDocType(CurPos); /* <! ... > directives */ 653 CurPos = SkipXMLProlog(CurPos); /* <? ... ?> directives */ 654 } while(CurPos != q); 655 656 return CurPos; 657 } 658 659 /* Parses next token and initializes the global token variables above 660 The tokennizer automatically skips comments (<!-- comment -->) and 661 <! ... > directives. 662 */ 663 static void GetNextToken(void) { 664 TCHAR *p, *q; 665 666 /* Skip white space and comment sections */ 667 p = SkipFilling(); 668 669 if (p == NULL || *p == '\0') { 670 CurTokenType = TOKEN_EOF; 671 return; 672 } else if (p[0] == '<' && p[1] == '/') { 673 /* TOKEN_END_TAG */ 674 q = SkipXMLName(p + 2); 675 SetToken(TOKEN_END_TAG, p + 2, q); 676 p = q; 677 } else if (*p == '<') { 678 /* TOKEN_BEGIN_TAG */ 679 q = SkipXMLName(p + 1); 680 SetToken(TOKEN_BEGIN_TAG, p + 1, q); 681 p = q; 682 } else if (p[0] == '>') { 683 CurTokenType = TOKEN_CLOSE_BRACKET; 684 NEXT_CHAR(p); 685 } else if (p[0] == '/' && p[1] == '>') { 686 CurTokenType = TOKEN_EMPTY_CLOSE_BRACKET; 687 SKIP_CHARS(p, 2); 688 } else { 689 /* Search for end of data */ 690 q = p + 1; 691 while(*q && *q != '<') { 692 if (IsPCData(q)) { 693 q = SkipPCData(q); 694 } else { 695 NEXT_CHAR(q); 696 } 697 } 698 SetToken(TOKEN_PCDATA, p, q); 699 /* Convert all entities inside token */ 700 ConvertBuiltInEntities(CurTokenName); 701 p = q; 702 } 703 /* Advance pointer to beginning of next token */ 704 CurPos = p; 705 } 706 707 static XMLNode* CreateXMLNode(int type, TCHAR* name) { 708 XMLNode* node; 709 node = (XMLNode*)malloc(sizeof(XMLNode)); 710 if (node == NULL) { 711 return NULL; 712 } 713 node->_type = type; 714 node->_name = name; 715 node->_next = NULL; 716 node->_sub = NULL; 717 node->_attributes = NULL; 718 return node; 719 } 720 721 static XMLAttribute* CreateXMLAttribute(TCHAR *name, TCHAR* value) { 722 XMLAttribute* attr; 723 attr = (XMLAttribute*)malloc(sizeof(XMLAttribute)); 724 if (attr == NULL) { 725 return NULL; 726 } 727 attr->_name = name; 728 attr->_value = value; 729 attr->_next = NULL; 730 return attr; 731 } 732 733 XMLNode* ParseXMLDocument(TCHAR* buf) { 734 XMLNode* root; 735 int err_code = setjmp(jmpbuf); 736 switch (err_code) 737 { 738 case JMP_NO_ERROR: 739 #ifndef _UNICODE 740 /* Remove UTF-8 encoding from buffer */ 741 RemoveNonAsciiUTF8FromBuffer(buf); 742 #endif 743 744 /* Get first Token */ 745 CurPos = buf; 746 GetNextToken(); 747 748 /* Parse document*/ 749 root = ParseXMLElement(); 750 break; 751 case JMP_OUT_OF_RANGE: 752 /* cleanup: */ 753 if (root_node != NULL) { 754 FreeXMLDocument(root_node); 755 root_node = NULL; 756 } 757 if (CurTokenName != NULL) free(CurTokenName); 758 fprintf(stderr,"Error during parsing jnlp file...\n"); 759 exit(-1); 760 break; 761 default: 762 root = NULL; 763 break; 764 } 765 766 return root; 767 } 768 769 static XMLNode* ParseXMLElement(void) { 770 XMLNode* node = NULL; 771 XMLNode* subnode = NULL; 772 XMLNode* nextnode = NULL; 773 XMLAttribute* attr = NULL; 774 775 if (CurTokenType == TOKEN_BEGIN_TAG) { 776 777 /* Create node for new element tag */ 778 node = CreateXMLNode(xmlTagType, JPACKAGER_STRDUP(CurTokenName)); 779 /* We need to save root node pointer to be able to cleanup 780 if an error happens during parsing */ 781 if(!root_node) { 782 root_node = node; 783 } 784 /* Parse attributes. This section eats a all input until 785 EOF, a > or a /> */ 786 attr = ParseXMLAttribute(); 787 while(attr != NULL) { 788 attr->_next = node->_attributes; 789 node->_attributes = attr; 790 attr = ParseXMLAttribute(); 791 } 792 793 /* This will eihter be a TOKEN_EOF, TOKEN_CLOSE_BRACKET, or a 794 * TOKEN_EMPTY_CLOSE_BRACKET */ 795 GetNextToken(); 796 797 /* Skip until '>', '/>' or EOF. This should really be an error, */ 798 /* but we are loose */ 799 // if(CurTokenType == TOKEN_EMPTY_CLOSE_BRACKET || 800 // CurTokenType == TOKEN_CLOSE_BRACKET || 801 // CurTokenType == TOKEN_EOF) { 802 // println("XML Parsing error: wrong kind of token found"); 803 // return NULL; 804 // } 805 806 if (CurTokenType == TOKEN_EMPTY_CLOSE_BRACKET) { 807 GetNextToken(); 808 /* We are done with the sublevel - fall through to continue */ 809 /* parsing tags at the same level */ 810 } else if (CurTokenType == TOKEN_CLOSE_BRACKET) { 811 GetNextToken(); 812 813 /* Parse until end tag if found */ 814 node->_sub = ParseXMLElement(); 815 816 if (CurTokenType == TOKEN_END_TAG) { 817 /* Find closing bracket '>' for end tag */ 818 do { 819 GetNextToken(); 820 } while(CurTokenType != TOKEN_EOF && 821 CurTokenType != TOKEN_CLOSE_BRACKET); 822 GetNextToken(); 823 } 824 } 825 826 /* Continue parsing rest on same level */ 827 if (CurTokenType != TOKEN_EOF) { 828 /* Parse rest of stream at same level */ 829 node->_next = ParseXMLElement(); 830 } 831 return node; 832 833 } else if (CurTokenType == TOKEN_PCDATA) { 834 /* Create node for pcdata */ 835 node = CreateXMLNode(xmlPCDataType, JPACKAGER_STRDUP(CurTokenName)); 836 /* We need to save root node pointer to be able to cleanup 837 if an error happens during parsing */ 838 if(!root_node) { 839 root_node = node; 840 } 841 GetNextToken(); 842 return node; 843 } 844 845 /* Something went wrong. */ 846 return NULL; 847 } 848 849 /* Parses an XML attribute. */ 850 static XMLAttribute* ParseXMLAttribute(void) { 851 TCHAR* q = NULL; 852 TCHAR* name = NULL; 853 TCHAR* PrevPos = NULL; 854 855 do 856 { 857 /* We need to check this condition to avoid endless loop 858 in case if an error happend during parsing. */ 859 if (PrevPos == CurPos) { 860 if (name != NULL) { 861 free(name); 862 name = NULL; 863 } 864 865 return NULL; 866 } 867 868 PrevPos = CurPos; 869 870 /* Skip whitespace etc. */ 871 SkipFilling(); 872 873 /* Check if we are done witht this attribute section */ 874 if (CurPos[0] == '\0' || 875 CurPos[0] == '>' || 876 (CurPos[0] == '/' && CurPos[1] == '>')) { 877 878 if (name != NULL) { 879 free(name); 880 name = NULL; 881 } 882 883 return NULL; 884 } 885 886 /* Find end of name */ 887 q = CurPos; 888 while(*q && !iswspace(*q) && *q !='=') NEXT_CHAR(q); 889 890 SetToken(TOKEN_UNKNOWN, CurPos, q); 891 if (name) { 892 free(name); 893 name = NULL; 894 } 895 name = JPACKAGER_STRDUP(CurTokenName); 896 897 /* Skip any whitespace */ 898 CurPos = q; 899 CurPos = SkipFilling(); 900 901 /* Next TCHARacter must be '=' for a valid attribute. 902 If it is not, this is really an error. 903 We ignore this, and just try to parse an attribute 904 out of the rest of the string. 905 */ 906 } while(*CurPos != '='); 907 908 NEXT_CHAR(CurPos); 909 CurPos = SkipWhiteSpace(CurPos); 910 /* Parse CDATA part of attribute */ 911 if ((*CurPos == '\"') || (*CurPos == '\'')) { 912 TCHAR quoteChar = *CurPos; 913 q = ++CurPos; 914 while(*q != '\0' && *q != quoteChar) NEXT_CHAR(q); 915 SetToken(TOKEN_CDATA, CurPos, q); 916 CurPos = q + 1; 917 } else { 918 q = CurPos; 919 while(*q != '\0' && !iswspace(*q)) NEXT_CHAR(q); 920 SetToken(TOKEN_CDATA, CurPos, q); 921 CurPos = q; 922 } 923 924 //Note: no need to free name and CurTokenName duplicate; they're assigned 925 // to an XMLAttribute structure in CreateXMLAttribute 926 927 return CreateXMLAttribute(name, JPACKAGER_STRDUP(CurTokenName)); 928 } 929 930 void FreeXMLDocument(XMLNode* root) { 931 if (root == NULL) return; 932 FreeXMLDocument(root->_sub); 933 FreeXMLDocument(root->_next); 934 FreeXMLAttribute(root->_attributes); 935 free(root->_name); 936 free(root); 937 } 938 939 static void FreeXMLAttribute(XMLAttribute* attr) { 940 if (attr == NULL) return; 941 free(attr->_name); 942 free(attr->_value); 943 FreeXMLAttribute(attr->_next); 944 free(attr); 945 } 946 947 /* Find element at current level with a given name */ 948 XMLNode* FindXMLChild(XMLNode* root, const TCHAR* name) { 949 if (root == NULL) return NULL; 950 951 if (root->_type == xmlTagType && JPACKAGER_STRCMP(root->_name, name) == 0) { 952 return root; 953 } 954 955 return FindXMLChild(root->_next, name); 956 } 957 958 /* Search for an attribute with the given name and returns the contents. Returns NULL if 959 * attribute is not found 960 */ 961 TCHAR* FindXMLAttribute(XMLAttribute* attr, const TCHAR* name) { 962 if (attr == NULL) return NULL; 963 if (JPACKAGER_STRCMP(attr->_name, name) == 0) return attr->_value; 964 return FindXMLAttribute(attr->_next, name); 965 } 966 967 968 void PrintXMLDocument(XMLNode* node, int indt) { 969 if (node == NULL) return; 970 971 if (node->_type == xmlTagType) { 972 JPACKAGER_PRINTF(_T("\n")); 973 indent(indt); 974 JPACKAGER_PRINTF(_T("<%s"), node->_name); 975 PrintXMLAttributes(node->_attributes); 976 if (node->_sub == NULL) { 977 JPACKAGER_PRINTF(_T("/>\n")); 978 } else { 979 JPACKAGER_PRINTF(_T(">")); 980 PrintXMLDocument(node->_sub, indt + 1); 981 indent(indt); 982 JPACKAGER_PRINTF(_T("</%s>"), node->_name); 983 } 984 } else { 985 JPACKAGER_PRINTF(_T("%s"), node->_name); 986 } 987 PrintXMLDocument(node->_next, indt); 988 } 989 990 static void PrintXMLAttributes(XMLAttribute* attr) { 991 if (attr == NULL) return; 992 993 JPACKAGER_PRINTF(_T(" %s=\"%s\""), attr->_name, attr->_value); 994 PrintXMLAttributes(attr->_next); 995 } 996 997 static void indent(int indt) { 998 int i; 999 for(i = 0; i < indt; i++) { 1000 JPACKAGER_PRINTF(_T(" ")); 1001 } 1002 } 1003 1004 const TCHAR *CDStart = _T("<![CDATA["); 1005 const TCHAR *CDEnd = _T("]]>"); 1006 1007 1008 static TCHAR* SkipPCData(TCHAR *p) { 1009 TCHAR *end = JPACKAGER_STRSTR(p, CDEnd); 1010 if (end != NULL) { 1011 return end+sizeof(CDEnd); 1012 } 1013 return (++p); 1014 } 1015 1016 static int IsPCData(TCHAR *p) { 1017 const int size = sizeof(CDStart); 1018 return (JPACKAGER_STRNCMP(CDStart, p, size) == 0); 1019 } 1020 1021 namespace { 1022 template<class funcType> 1023 class DllFunction { 1024 const Library& lib; 1025 funcType funcPtr; 1026 std::string theName; 1027 1028 public: 1029 DllFunction(const Library& library, 1030 const std::string &funcName): lib(library) { 1031 funcPtr = reinterpret_cast<funcType>(lib.GetProcAddress(funcName)); 1032 if (!funcPtr) { 1033 throw std::runtime_error("Failed to load function \"" 1034 + funcName + "\" from \"" 1035 + library.GetName() + "\" library"); 1036 } 1037 } 1038 1039 operator funcType() const { 1040 return funcPtr; 1041 } 1042 }; 1043 } // namespace 1044 1045 extern "C" { 1046 typedef Status (*XInitThreadsFuncPtr)(); 1047 typedef Display* (*XOpenDisplayFuncPtr)(char *display_name); 1048 1049 typedef Atom (*XInternAtomFuncPtr)( 1050 Display *display, char *atom_name, Bool only_if_exists); 1051 1052 typedef Window (*XDefaultRootWindowFuncPtr)(Display *display); 1053 1054 typedef int (*XCloseDisplayFuncPtr)(Display *display); 1055 } 1056 1057 ProcessReactivator::ProcessReactivator(pid_t pid): _pid(pid) { 1058 const std::string libname = "libX11.so"; 1059 if(!libX11.Load(libname)) { 1060 throw std::runtime_error("Failed to load \"" + libname + "\" library"); 1061 } 1062 1063 DllFunction<XInitThreadsFuncPtr> XInitThreadsFunc(libX11, "XInitThreads"); 1064 1065 XInitThreadsFunc(); 1066 1067 DllFunction<XOpenDisplayFuncPtr> XOpenDisplayFunc(libX11, "XOpenDisplay"); 1068 1069 _display = XOpenDisplayFunc(NULL); 1070 1071 DllFunction<XInternAtomFuncPtr> XInternAtomFunc(libX11, "XInternAtom"); 1072 1073 _atomPid = XInternAtomFunc(_display, (char*)"_NET_WM_PID", True); 1074 1075 if (_atomPid == None) { 1076 return; 1077 } 1078 1079 DllFunction<XDefaultRootWindowFuncPtr> XDefaultRootWindowFunc(libX11, 1080 "XDefaultRootWindow"); 1081 1082 searchWindowHelper(XDefaultRootWindowFunc(_display)); 1083 1084 reactivateProcess(); 1085 1086 DllFunction<XCloseDisplayFuncPtr> XCloseDisplayFunc(libX11, 1087 "XCloseDisplay"); 1088 1089 XCloseDisplayFunc(_display); 1090 } 1091 1092 extern "C" { 1093 typedef int (*XGetWindowPropertyFuncPtr)( 1094 Display *display, Window w, Atom property, long long_offset, 1095 long long_length, Bool d, Atom req_type, Atom *actual_type_return, 1096 int *actual_format_return, unsigned long *nitems_return, 1097 unsigned long *bytes_after_return, unsigned char **prop_return); 1098 1099 typedef Status (*XQueryTreeFuncPtr)( 1100 Display *display, Window w, Window *root_return, Window *parent_return, 1101 Window **children_return, unsigned int *nchildren_return); 1102 1103 typedef int (*XFreeFuncPtr)(void *data); 1104 } 1105 1106 void ProcessReactivator::searchWindowHelper(Window w) { 1107 1108 DllFunction<XGetWindowPropertyFuncPtr> XGetWindowPropertyFunc(libX11, 1109 "XGetWindowProperty"); 1110 1111 DllFunction<XFreeFuncPtr> XFreeFunc(libX11, "XFree"); 1112 1113 Atom type; 1114 int format; 1115 unsigned long num, bytesAfter; 1116 unsigned char* propPid = 0; 1117 if (Success == XGetWindowPropertyFunc(_display, w, _atomPid, 0, 1, 1118 False, XA_CARDINAL, &type, &format, &num, &bytesAfter, &propPid)) { 1119 if (propPid != 0) { 1120 if (_pid == *((pid_t *)propPid)) { 1121 _result.push_back(w); 1122 } 1123 XFreeFunc(propPid); 1124 } 1125 } 1126 1127 DllFunction<XQueryTreeFuncPtr> XQueryTreeFunc(libX11, "XQueryTree"); 1128 1129 Window root, parent; 1130 Window* child; 1131 unsigned int numChildren; 1132 if (0 != XQueryTreeFunc(_display, w, &root, 1133 &parent, &child, &numChildren)) { 1134 for (unsigned int i = 0; i < numChildren; i++) { 1135 searchWindowHelper(child[i]); 1136 } 1137 } 1138 } 1139 1140 1141 extern "C" { 1142 typedef Status (*XGetWindowAttributesFuncPtr)(Display *display, Window w, 1143 XWindowAttributes *window_attributes_return); 1144 1145 typedef Status (*XSendEventFuncPtr)(Display *display, Window w, Bool propagate, 1146 long event_mask, XEvent *event_send); 1147 1148 typedef int (*XRaiseWindowFuncPtr)(Display *display, Window w); 1149 } 1150 1151 void ProcessReactivator::reactivateProcess() { 1152 1153 DllFunction<XGetWindowAttributesFuncPtr> XGetWindowAttributesFunc(libX11, 1154 "XGetWindowAttributes"); 1155 1156 DllFunction<XSendEventFuncPtr> XSendEventFunc(libX11, "XSendEvent"); 1157 1158 DllFunction<XRaiseWindowFuncPtr> XRaiseWindowFunc(libX11, "XRaiseWindow"); 1159 1160 DllFunction<XInternAtomFuncPtr> XInternAtomFunc(libX11, "XInternAtom"); 1161 1162 for (std::list<Window>::const_iterator it = _result.begin(); 1163 it != _result.end(); it++) { 1164 // try sending an event to activate window, 1165 // after that we can try to raise it. 1166 XEvent xev; 1167 Atom atom = XInternAtomFunc ( 1168 _display, (char*)"_NET_ACTIVE_WINDOW", False); 1169 xev.xclient.type = ClientMessage; 1170 xev.xclient.serial = 0; 1171 xev.xclient.send_event = True; 1172 xev.xclient.display = _display; 1173 xev.xclient.window = *it; 1174 xev.xclient.message_type = atom; 1175 xev.xclient.format = 32; 1176 xev.xclient.data.l[0] = 2; 1177 xev.xclient.data.l[1] = 0; 1178 xev.xclient.data.l[2] = 0; 1179 xev.xclient.data.l[3] = 0; 1180 xev.xclient.data.l[4] = 0; 1181 XWindowAttributes attr; 1182 XGetWindowAttributesFunc(_display, *it, &attr); 1183 XSendEventFunc(_display, attr.root, False, 1184 SubstructureRedirectMask | SubstructureNotifyMask, &xev); 1185 XRaiseWindowFunc(_display, *it); 1186 } 1187 } 1188 1189 1190 #endif // LINUX