sysinfo.c revision 262253
1/* 2 * sysinfo.c : information about the running system 3 * 4 * ==================================================================== 5 * Licensed to the Apache Software Foundation (ASF) under one 6 * or more contributor license agreements. See the NOTICE file 7 * distributed with this work for additional information 8 * regarding copyright ownership. The ASF licenses this file 9 * to you under the Apache License, Version 2.0 (the 10 * "License"); you may not use this file except in compliance 11 * with the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, 16 * software distributed under the License is distributed on an 17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 * KIND, either express or implied. See the License for the 19 * specific language governing permissions and limitations 20 * under the License. 21 * ==================================================================== 22 */ 23 24 25 26#ifdef WIN32 27#define WIN32_LEAN_AND_MEAN 28#define PSAPI_VERSION 1 29#include <windows.h> 30#include <psapi.h> 31#include <Ws2tcpip.h> 32#endif 33 34#define APR_WANT_STRFUNC 35#include <apr_want.h> 36 37#include <apr_lib.h> 38#include <apr_pools.h> 39#include <apr_file_info.h> 40#include <apr_signal.h> 41#include <apr_strings.h> 42#include <apr_thread_proc.h> 43#include <apr_version.h> 44#include <apu_version.h> 45 46#include "svn_pools.h" 47#include "svn_ctype.h" 48#include "svn_dirent_uri.h" 49#include "svn_error.h" 50#include "svn_io.h" 51#include "svn_string.h" 52#include "svn_utf.h" 53#include "svn_version.h" 54 55#include "private/svn_sqlite.h" 56 57#include "sysinfo.h" 58#include "svn_private_config.h" 59 60#if HAVE_SYS_UTSNAME_H 61#include <sys/utsname.h> 62#endif 63 64#ifdef SVN_HAVE_MACOS_PLIST 65#include <CoreFoundation/CoreFoundation.h> 66#endif 67 68#ifdef SVN_HAVE_MACHO_ITERATE 69#include <mach-o/dyld.h> 70#include <mach-o/loader.h> 71#endif 72 73#if HAVE_UNAME 74static const char *canonical_host_from_uname(apr_pool_t *pool); 75# ifndef SVN_HAVE_MACOS_PLIST 76static const char *release_name_from_uname(apr_pool_t *pool); 77# endif 78#endif 79 80#ifdef WIN32 81static const char *win32_canonical_host(apr_pool_t *pool); 82static const char *win32_release_name(apr_pool_t *pool); 83static const apr_array_header_t *win32_shared_libs(apr_pool_t *pool); 84#endif /* WIN32 */ 85 86#ifdef SVN_HAVE_MACOS_PLIST 87static const char *macos_release_name(apr_pool_t *pool); 88#endif 89 90#ifdef SVN_HAVE_MACHO_ITERATE 91static const apr_array_header_t *macos_shared_libs(apr_pool_t *pool); 92#endif 93 94 95#if __linux__ 96static const char *linux_release_name(apr_pool_t *pool); 97#endif 98 99const char * 100svn_sysinfo__canonical_host(apr_pool_t *pool) 101{ 102#ifdef WIN32 103 return win32_canonical_host(pool); 104#elif HAVE_UNAME 105 return canonical_host_from_uname(pool); 106#else 107 return "unknown-unknown-unknown"; 108#endif 109} 110 111 112const char * 113svn_sysinfo__release_name(apr_pool_t *pool) 114{ 115#ifdef WIN32 116 return win32_release_name(pool); 117#elif defined(SVN_HAVE_MACOS_PLIST) 118 return macos_release_name(pool); 119#elif __linux__ 120 return linux_release_name(pool); 121#elif HAVE_UNAME 122 return release_name_from_uname(pool); 123#else 124 return NULL; 125#endif 126} 127 128const apr_array_header_t * 129svn_sysinfo__linked_libs(apr_pool_t *pool) 130{ 131 svn_version_ext_linked_lib_t *lib; 132 apr_array_header_t *array = apr_array_make(pool, 3, sizeof(*lib)); 133 134 lib = &APR_ARRAY_PUSH(array, svn_version_ext_linked_lib_t); 135 lib->name = "APR"; 136 lib->compiled_version = APR_VERSION_STRING; 137 lib->runtime_version = apr_pstrdup(pool, apr_version_string()); 138 139/* Don't list APR-Util if it isn't linked in, which it may not be if 140 * we're using APR 2.x+ which combined APR-Util into APR. */ 141#ifdef APU_VERSION_STRING 142 lib = &APR_ARRAY_PUSH(array, svn_version_ext_linked_lib_t); 143 lib->name = "APR-Util"; 144 lib->compiled_version = APU_VERSION_STRING; 145 lib->runtime_version = apr_pstrdup(pool, apu_version_string()); 146#endif 147 148 lib = &APR_ARRAY_PUSH(array, svn_version_ext_linked_lib_t); 149 lib->name = "SQLite"; 150 lib->compiled_version = apr_pstrdup(pool, svn_sqlite__compiled_version()); 151#ifdef SVN_SQLITE_INLINE 152 lib->runtime_version = NULL; 153#else 154 lib->runtime_version = apr_pstrdup(pool, svn_sqlite__runtime_version()); 155#endif 156 157 return array; 158} 159 160const apr_array_header_t * 161svn_sysinfo__loaded_libs(apr_pool_t *pool) 162{ 163#ifdef WIN32 164 return win32_shared_libs(pool); 165#elif defined(SVN_HAVE_MACHO_ITERATE) 166 return macos_shared_libs(pool); 167#else 168 return NULL; 169#endif 170} 171 172 173#if HAVE_UNAME 174static const char* 175canonical_host_from_uname(apr_pool_t *pool) 176{ 177 const char *machine = "unknown"; 178 const char *vendor = "unknown"; 179 const char *sysname = "unknown"; 180 const char *sysver = ""; 181 struct utsname info; 182 183 if (0 <= uname(&info)) 184 { 185 svn_error_t *err; 186 const char *tmp; 187 188 err = svn_utf_cstring_to_utf8(&tmp, info.machine, pool); 189 if (err) 190 svn_error_clear(err); 191 else 192 machine = tmp; 193 194 err = svn_utf_cstring_to_utf8(&tmp, info.sysname, pool); 195 if (err) 196 svn_error_clear(err); 197 else 198 { 199 char *lwr = apr_pstrdup(pool, tmp); 200 char *it = lwr; 201 while (*it) 202 { 203 if (svn_ctype_isupper(*it)) 204 *it = apr_tolower(*it); 205 ++it; 206 } 207 sysname = lwr; 208 } 209 210 if (0 == strcmp(sysname, "darwin")) 211 vendor = "apple"; 212 if (0 == strcmp(sysname, "linux")) 213 sysver = "-gnu"; 214 else 215 { 216 err = svn_utf_cstring_to_utf8(&tmp, info.release, pool); 217 if (err) 218 svn_error_clear(err); 219 else 220 { 221 apr_size_t n = strspn(tmp, ".0123456789"); 222 if (n > 0) 223 { 224 char *ver = apr_pstrdup(pool, tmp); 225 ver[n] = 0; 226 sysver = ver; 227 } 228 else 229 sysver = tmp; 230 } 231 } 232 } 233 234 return apr_psprintf(pool, "%s-%s-%s%s", machine, vendor, sysname, sysver); 235} 236 237# ifndef SVN_HAVE_MACOS_PLIST 238/* Generate a release name from the uname(3) info, effectively 239 returning "`uname -s` `uname -r`". */ 240static const char * 241release_name_from_uname(apr_pool_t *pool) 242{ 243 struct utsname info; 244 if (0 <= uname(&info)) 245 { 246 svn_error_t *err; 247 const char *sysname; 248 const char *sysver; 249 250 err = svn_utf_cstring_to_utf8(&sysname, info.sysname, pool); 251 if (err) 252 { 253 sysname = NULL; 254 svn_error_clear(err); 255 } 256 257 258 err = svn_utf_cstring_to_utf8(&sysver, info.release, pool); 259 if (err) 260 { 261 sysver = NULL; 262 svn_error_clear(err); 263 } 264 265 if (sysname || sysver) 266 { 267 return apr_psprintf(pool, "%s%s%s", 268 (sysname ? sysname : ""), 269 (sysver ? (sysname ? " " : "") : ""), 270 (sysver ? sysver : "")); 271 } 272 } 273 return NULL; 274} 275# endif /* !SVN_HAVE_MACOS_PLIST */ 276#endif /* HAVE_UNAME */ 277 278 279#if __linux__ 280/* Split a stringbuf into a key/value pair. 281 Return the key, leaving the striped value in the stringbuf. */ 282static const char * 283stringbuf_split_key(svn_stringbuf_t *buffer, char delim) 284{ 285 char *key; 286 char *end; 287 288 end = strchr(buffer->data, delim); 289 if (!end) 290 return NULL; 291 292 svn_stringbuf_strip_whitespace(buffer); 293 294 /* Now we split the currently allocated buffer in two parts: 295 - a const char * HEAD 296 - the remaining stringbuf_t. */ 297 298 /* Create HEAD as '\0' terminated const char * */ 299 key = buffer->data; 300 end = strchr(key, delim); 301 *end = '\0'; 302 303 /* And update the TAIL to be a smaller, but still valid stringbuf */ 304 buffer->data = end + 1; 305 buffer->len -= 1 + end - key; 306 buffer->blocksize -= 1 + end - key; 307 308 svn_stringbuf_strip_whitespace(buffer); 309 310 return key; 311} 312 313/* Parse `/usr/bin/lsb_rlease --all` */ 314static const char * 315lsb_release(apr_pool_t *pool) 316{ 317 static const char *const args[3] = 318 { 319 "/usr/bin/lsb_release", 320 "--all", 321 NULL 322 }; 323 324 const char *distributor = NULL; 325 const char *description = NULL; 326 const char *release = NULL; 327 const char *codename = NULL; 328 329 apr_proc_t lsbproc; 330 svn_stream_t *lsbinfo; 331 svn_error_t *err; 332 333 /* Run /usr/bin/lsb_release --all < /dev/null 2>/dev/null */ 334 { 335 apr_file_t *stdin_handle; 336 apr_file_t *stdout_handle; 337 338 err = svn_io_file_open(&stdin_handle, SVN_NULL_DEVICE_NAME, 339 APR_READ, APR_OS_DEFAULT, pool); 340 if (!err) 341 err = svn_io_file_open(&stdout_handle, SVN_NULL_DEVICE_NAME, 342 APR_WRITE, APR_OS_DEFAULT, pool); 343 if (!err) 344 err = svn_io_start_cmd3(&lsbproc, NULL, args[0], args, NULL, FALSE, 345 FALSE, stdin_handle, 346 TRUE, NULL, 347 FALSE, stdout_handle, 348 pool); 349 if (err) 350 { 351 svn_error_clear(err); 352 return NULL; 353 } 354 } 355 356 /* Parse the output and try to populate the */ 357 lsbinfo = svn_stream_from_aprfile2(lsbproc.out, TRUE, pool); 358 if (lsbinfo) 359 { 360 for (;;) 361 { 362 svn_boolean_t eof = FALSE; 363 svn_stringbuf_t *line; 364 const char *key; 365 366 err = svn_stream_readline(lsbinfo, &line, "\n", &eof, pool); 367 if (err || eof) 368 break; 369 370 key = stringbuf_split_key(line, ':'); 371 if (!key) 372 continue; 373 374 if (0 == svn_cstring_casecmp(key, "Distributor ID")) 375 distributor = line->data; 376 else if (0 == svn_cstring_casecmp(key, "Description")) 377 description = line->data; 378 else if (0 == svn_cstring_casecmp(key, "Release")) 379 release = line->data; 380 else if (0 == svn_cstring_casecmp(key, "Codename")) 381 codename = line->data; 382 } 383 err = svn_error_compose_create(err, 384 svn_stream_close(lsbinfo)); 385 if (err) 386 { 387 svn_error_clear(err); 388 apr_proc_kill(&lsbproc, SIGKILL); 389 return NULL; 390 } 391 } 392 393 /* Reap the child process */ 394 err = svn_io_wait_for_cmd(&lsbproc, "", NULL, NULL, pool); 395 if (err) 396 { 397 svn_error_clear(err); 398 return NULL; 399 } 400 401 if (description) 402 return apr_psprintf(pool, "%s%s%s%s", description, 403 (codename ? " (" : ""), 404 (codename ? codename : ""), 405 (codename ? ")" : "")); 406 if (distributor) 407 return apr_psprintf(pool, "%s%s%s%s%s%s", distributor, 408 (release ? " " : ""), 409 (release ? release : ""), 410 (codename ? " (" : ""), 411 (codename ? codename : ""), 412 (codename ? ")" : "")); 413 414 return NULL; 415} 416 417/* Read the whole contents of a file. */ 418static svn_stringbuf_t * 419read_file_contents(const char *filename, apr_pool_t *pool) 420{ 421 svn_error_t *err; 422 svn_stringbuf_t *buffer; 423 424 err = svn_stringbuf_from_file2(&buffer, filename, pool); 425 if (err) 426 { 427 svn_error_clear(err); 428 return NULL; 429 } 430 431 return buffer; 432} 433 434/* Strip everything but the first line from a stringbuf. */ 435static void 436stringbuf_first_line_only(svn_stringbuf_t *buffer) 437{ 438 char *eol = strchr(buffer->data, '\n'); 439 if (eol) 440 { 441 *eol = '\0'; 442 buffer->len = 1 + eol - buffer->data; 443 } 444 svn_stringbuf_strip_whitespace(buffer); 445} 446 447/* Look at /etc/redhat_release to detect RHEL/Fedora/CentOS. */ 448static const char * 449redhat_release(apr_pool_t *pool) 450{ 451 svn_stringbuf_t *buffer = read_file_contents("/etc/redhat-release", pool); 452 if (buffer) 453 { 454 stringbuf_first_line_only(buffer); 455 return buffer->data; 456 } 457 return NULL; 458} 459 460/* Look at /etc/SuSE-release to detect non-LSB SuSE. */ 461static const char * 462suse_release(apr_pool_t *pool) 463{ 464 const char *release = NULL; 465 const char *codename = NULL; 466 467 svn_stringbuf_t *buffer = read_file_contents("/etc/SuSE-release", pool); 468 svn_stringbuf_t *line; 469 svn_stream_t *stream; 470 svn_boolean_t eof; 471 svn_error_t *err; 472 if (!buffer) 473 return NULL; 474 475 stream = svn_stream_from_stringbuf(buffer, pool); 476 err = svn_stream_readline(stream, &line, "\n", &eof, pool); 477 if (err || eof) 478 { 479 svn_error_clear(err); 480 return NULL; 481 } 482 483 svn_stringbuf_strip_whitespace(line); 484 release = line->data; 485 486 for (;;) 487 { 488 const char *key; 489 490 err = svn_stream_readline(stream, &line, "\n", &eof, pool); 491 if (err || eof) 492 { 493 svn_error_clear(err); 494 break; 495 } 496 497 key = stringbuf_split_key(line, '='); 498 if (!key) 499 continue; 500 501 if (0 == strncmp(key, "CODENAME", 8)) 502 codename = line->data; 503 } 504 505 return apr_psprintf(pool, "%s%s%s%s", 506 release, 507 (codename ? " (" : ""), 508 (codename ? codename : ""), 509 (codename ? ")" : "")); 510} 511 512/* Look at /etc/debian_version to detect non-LSB Debian. */ 513static const char * 514debian_release(apr_pool_t *pool) 515{ 516 svn_stringbuf_t *buffer = read_file_contents("/etc/debian_version", pool); 517 if (!buffer) 518 return NULL; 519 520 stringbuf_first_line_only(buffer); 521 return apr_pstrcat(pool, "Debian ", buffer->data, NULL); 522} 523 524/* Try to find the Linux distribution name, or return info from uname. */ 525static const char * 526linux_release_name(apr_pool_t *pool) 527{ 528 const char *uname_release = release_name_from_uname(pool); 529 530 /* Try anything that has /usr/bin/lsb_release. 531 Covers, for example, Debian, Ubuntu and SuSE. */ 532 const char *release_name = lsb_release(pool); 533 534 /* Try RHEL/Fedora/CentOS */ 535 if (!release_name) 536 release_name = redhat_release(pool); 537 538 /* Try Non-LSB SuSE */ 539 if (!release_name) 540 release_name = suse_release(pool); 541 542 /* Try non-LSB Debian */ 543 if (!release_name) 544 release_name = debian_release(pool); 545 546 if (!release_name) 547 return uname_release; 548 549 if (!uname_release) 550 return release_name; 551 552 return apr_psprintf(pool, "%s [%s]", release_name, uname_release); 553} 554#endif /* __linux__ */ 555 556 557#ifdef WIN32 558typedef DWORD (WINAPI *FNGETNATIVESYSTEMINFO)(LPSYSTEM_INFO); 559typedef BOOL (WINAPI *FNENUMPROCESSMODULES) (HANDLE, HMODULE*, DWORD, LPDWORD); 560 561/* Get system and version info, and try to tell the difference 562 between the native system type and the runtime environment of the 563 current process. Populate results in SYSINFO, LOCAL_SYSINFO 564 (optional) and OSINFO. */ 565static BOOL 566system_info(SYSTEM_INFO *sysinfo, 567 SYSTEM_INFO *local_sysinfo, 568 OSVERSIONINFOEXW *osinfo) 569{ 570 FNGETNATIVESYSTEMINFO GetNativeSystemInfo_ = (FNGETNATIVESYSTEMINFO) 571 GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetNativeSystemInfo"); 572 573 ZeroMemory(sysinfo, sizeof *sysinfo); 574 if (local_sysinfo) 575 { 576 ZeroMemory(local_sysinfo, sizeof *local_sysinfo); 577 GetSystemInfo(local_sysinfo); 578 if (GetNativeSystemInfo_) 579 GetNativeSystemInfo_(sysinfo); 580 else 581 memcpy(sysinfo, local_sysinfo, sizeof *sysinfo); 582 } 583 else 584 GetSystemInfo(sysinfo); 585 586 ZeroMemory(osinfo, sizeof *osinfo); 587 osinfo->dwOSVersionInfoSize = sizeof *osinfo; 588 if (!GetVersionExW((LPVOID)osinfo)) 589 return FALSE; 590 591 return TRUE; 592} 593 594/* Map the proccessor type from SYSINFO to a string. */ 595static const char * 596processor_name(SYSTEM_INFO *sysinfo) 597{ 598 switch (sysinfo->wProcessorArchitecture) 599 { 600 case PROCESSOR_ARCHITECTURE_AMD64: return "x86_64"; 601 case PROCESSOR_ARCHITECTURE_IA64: return "ia64"; 602 case PROCESSOR_ARCHITECTURE_INTEL: return "x86"; 603 case PROCESSOR_ARCHITECTURE_MIPS: return "mips"; 604 case PROCESSOR_ARCHITECTURE_ALPHA: return "alpha32"; 605 case PROCESSOR_ARCHITECTURE_PPC: return "powerpc"; 606 case PROCESSOR_ARCHITECTURE_SHX: return "shx"; 607 case PROCESSOR_ARCHITECTURE_ARM: return "arm"; 608 case PROCESSOR_ARCHITECTURE_ALPHA64: return "alpha"; 609 case PROCESSOR_ARCHITECTURE_MSIL: return "msil"; 610 case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64: return "x86_wow64"; 611 default: return "unknown"; 612 } 613} 614 615/* Return the Windows-specific canonical host name. */ 616static const char * 617win32_canonical_host(apr_pool_t *pool) 618{ 619 SYSTEM_INFO sysinfo; 620 SYSTEM_INFO local_sysinfo; 621 OSVERSIONINFOEXW osinfo; 622 623 if (system_info(&sysinfo, &local_sysinfo, &osinfo)) 624 { 625 const char *arch = processor_name(&local_sysinfo); 626 const char *machine = processor_name(&sysinfo); 627 const char *vendor = "microsoft"; 628 const char *sysname = "windows"; 629 const char *sysver = apr_psprintf(pool, "%u.%u.%u", 630 (unsigned int)osinfo.dwMajorVersion, 631 (unsigned int)osinfo.dwMinorVersion, 632 (unsigned int)osinfo.dwBuildNumber); 633 634 if (sysinfo.wProcessorArchitecture 635 == local_sysinfo.wProcessorArchitecture) 636 return apr_psprintf(pool, "%s-%s-%s%s", 637 machine, vendor, sysname, sysver); 638 return apr_psprintf(pool, "%s/%s-%s-%s%s", 639 arch, machine, vendor, sysname, sysver); 640 } 641 642 return "unknown-microsoft-windows"; 643} 644 645/* Convert a Unicode string to UTF-8. */ 646static char * 647wcs_to_utf8(const wchar_t *wcs, apr_pool_t *pool) 648{ 649 const int bufsize = WideCharToMultiByte(CP_UTF8, 0, wcs, -1, 650 NULL, 0, NULL, NULL); 651 if (bufsize > 0) 652 { 653 char *const utf8 = apr_palloc(pool, bufsize + 1); 654 WideCharToMultiByte(CP_UTF8, 0, wcs, -1, utf8, bufsize, NULL, NULL); 655 return utf8; 656 } 657 return NULL; 658} 659 660/* Query the value called NAME of the registry key HKEY. */ 661static char * 662registry_value(HKEY hkey, wchar_t *name, apr_pool_t *pool) 663{ 664 DWORD size; 665 wchar_t *value; 666 667 if (RegQueryValueExW(hkey, name, NULL, NULL, NULL, &size)) 668 return NULL; 669 670 value = apr_palloc(pool, size + sizeof *value); 671 if (RegQueryValueExW(hkey, name, NULL, NULL, (void*)value, &size)) 672 return NULL; 673 value[size / sizeof *value] = 0; 674 return wcs_to_utf8(value, pool); 675} 676 677/* Try to glean the Windows release name and associated info from the 678 registry. Failing that, construct a release name from the version 679 info. */ 680static const char * 681win32_release_name(apr_pool_t *pool) 682{ 683 SYSTEM_INFO sysinfo; 684 OSVERSIONINFOEXW osinfo; 685 HKEY hkcv; 686 687 if (!system_info(&sysinfo, NULL, &osinfo)) 688 return NULL; 689 690 if (!RegOpenKeyExW(HKEY_LOCAL_MACHINE, 691 L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", 692 0, KEY_QUERY_VALUE, &hkcv)) 693 { 694 const char *release = registry_value(hkcv, L"ProductName", pool); 695 const char *spack = registry_value(hkcv, L"CSDVersion", pool); 696 const char *curver = registry_value(hkcv, L"CurrentVersion", pool); 697 const char *curtype = registry_value(hkcv, L"CurrentType", pool); 698 const char *install = registry_value(hkcv, L"InstallationType", pool); 699 const char *curbuild = registry_value(hkcv, L"CurrentBuildNumber", pool); 700 701 if (!spack && *osinfo.szCSDVersion) 702 spack = wcs_to_utf8(osinfo.szCSDVersion, pool); 703 704 if (!curbuild) 705 curbuild = registry_value(hkcv, L"CurrentBuild", pool); 706 707 if (release || spack || curver || curtype || curbuild) 708 { 709 const char *bootinfo = ""; 710 if (curver || install || curtype) 711 { 712 bootinfo = apr_psprintf(pool, "[%s%s%s%s%s]", 713 (curver ? curver : ""), 714 (install ? (curver ? " " : "") : ""), 715 (install ? install : ""), 716 (curtype 717 ? (curver||install ? " " : "") 718 : ""), 719 (curtype ? curtype : "")); 720 } 721 722 return apr_psprintf(pool, "%s%s%s%s%s%s%s", 723 (release ? release : ""), 724 (spack ? (release ? ", " : "") : ""), 725 (spack ? spack : ""), 726 (curbuild 727 ? (release||spack ? ", build " : "build ") 728 : ""), 729 (curbuild ? curbuild : ""), 730 (bootinfo 731 ? (release||spack||curbuild ? " " : "") 732 : ""), 733 (bootinfo ? bootinfo : "")); 734 } 735 } 736 737 if (*osinfo.szCSDVersion) 738 { 739 const char *servicepack = wcs_to_utf8(osinfo.szCSDVersion, pool); 740 741 if (servicepack) 742 return apr_psprintf(pool, "Windows NT %u.%u, %s, build %u", 743 (unsigned int)osinfo.dwMajorVersion, 744 (unsigned int)osinfo.dwMinorVersion, 745 servicepack, 746 (unsigned int)osinfo.dwBuildNumber); 747 748 /* Assume wServicePackMajor > 0 if szCSDVersion is not empty */ 749 if (osinfo.wServicePackMinor) 750 return apr_psprintf(pool, "Windows NT %u.%u SP%u.%u, build %u", 751 (unsigned int)osinfo.dwMajorVersion, 752 (unsigned int)osinfo.dwMinorVersion, 753 (unsigned int)osinfo.wServicePackMajor, 754 (unsigned int)osinfo.wServicePackMinor, 755 (unsigned int)osinfo.dwBuildNumber); 756 757 return apr_psprintf(pool, "Windows NT %u.%u SP%u, build %u", 758 (unsigned int)osinfo.dwMajorVersion, 759 (unsigned int)osinfo.dwMinorVersion, 760 (unsigned int)osinfo.wServicePackMajor, 761 (unsigned int)osinfo.dwBuildNumber); 762 } 763 764 return apr_psprintf(pool, "Windows NT %u.%u, build %u", 765 (unsigned int)osinfo.dwMajorVersion, 766 (unsigned int)osinfo.dwMinorVersion, 767 (unsigned int)osinfo.dwBuildNumber); 768} 769 770 771/* Get a list of handles of shared libs loaded by the current 772 process. Returns a NULL-terminated array alocated from POOL. */ 773static HMODULE * 774enum_loaded_modules(apr_pool_t *pool) 775{ 776 HMODULE psapi_dll = 0; 777 HANDLE current = GetCurrentProcess(); 778 HMODULE dummy[1]; 779 HMODULE *handles; 780 DWORD size; 781 FNENUMPROCESSMODULES EnumProcessModules_; 782 783 psapi_dll = GetModuleHandleA("psapi.dll"); 784 785 if (!psapi_dll) 786 { 787 /* Load and never unload, just like static linking */ 788 psapi_dll = LoadLibraryA("psapi.dll"); 789 } 790 791 if (!psapi_dll) 792 return NULL; 793 794 EnumProcessModules_ = (FNENUMPROCESSMODULES) 795 GetProcAddress(psapi_dll, "EnumProcessModules"); 796 797 /* Before Windows XP psapi was an optional module */ 798 if (! EnumProcessModules_) 799 return NULL; 800 801 if (!EnumProcessModules_(current, dummy, sizeof(dummy), &size)) 802 return NULL; 803 804 handles = apr_palloc(pool, size + sizeof *handles); 805 if (! EnumProcessModules_(current, handles, size, &size)) 806 return NULL; 807 handles[size / sizeof *handles] = NULL; 808 return handles; 809} 810 811/* Find the version number, if any, embedded in FILENAME. */ 812static const char * 813file_version_number(const wchar_t *filename, apr_pool_t *pool) 814{ 815 VS_FIXEDFILEINFO info; 816 unsigned int major, minor, micro, nano; 817 void *data; 818 DWORD data_size = GetFileVersionInfoSizeW(filename, NULL); 819 void *vinfo; 820 UINT vinfo_size; 821 822 if (!data_size) 823 return NULL; 824 825 data = apr_palloc(pool, data_size); 826 if (!GetFileVersionInfoW(filename, 0, data_size, data)) 827 return NULL; 828 829 if (!VerQueryValueW(data, L"\\", &vinfo, &vinfo_size)) 830 return NULL; 831 832 if (vinfo_size != sizeof info) 833 return NULL; 834 835 memcpy(&info, vinfo, sizeof info); 836 major = (info.dwFileVersionMS >> 16) & 0xFFFF; 837 minor = info.dwFileVersionMS & 0xFFFF; 838 micro = (info.dwFileVersionLS >> 16) & 0xFFFF; 839 nano = info.dwFileVersionLS & 0xFFFF; 840 841 if (!nano) 842 { 843 if (!micro) 844 return apr_psprintf(pool, "%u.%u", major, minor); 845 else 846 return apr_psprintf(pool, "%u.%u.%u", major, minor, micro); 847 } 848 return apr_psprintf(pool, "%u.%u.%u.%u", major, minor, micro, nano); 849} 850 851/* List the shared libraries loaded by the current process. */ 852static const apr_array_header_t * 853win32_shared_libs(apr_pool_t *pool) 854{ 855 apr_array_header_t *array = NULL; 856 wchar_t buffer[MAX_PATH + 1]; 857 HMODULE *handles = enum_loaded_modules(pool); 858 HMODULE *module; 859 860 for (module = handles; module && *module; ++module) 861 { 862 const char *filename; 863 const char *version; 864 if (GetModuleFileNameW(*module, buffer, MAX_PATH)) 865 { 866 buffer[MAX_PATH] = 0; 867 868 version = file_version_number(buffer, pool); 869 filename = wcs_to_utf8(buffer, pool); 870 if (filename) 871 { 872 svn_version_ext_loaded_lib_t *lib; 873 874 if (!array) 875 { 876 array = apr_array_make(pool, 32, sizeof(*lib)); 877 } 878 lib = &APR_ARRAY_PUSH(array, svn_version_ext_loaded_lib_t); 879 lib->name = svn_dirent_local_style(filename, pool); 880 lib->version = version; 881 } 882 } 883 } 884 885 return array; 886} 887#endif /* WIN32 */ 888 889 890#ifdef SVN_HAVE_MACOS_PLIST 891/* Load the SystemVersion.plist or ServerVersion.plist file into a 892 property list. Set SERVER to TRUE if the file read was 893 ServerVersion.plist. */ 894static CFDictionaryRef 895system_version_plist(svn_boolean_t *server, apr_pool_t *pool) 896{ 897 static const UInt8 server_version[] = 898 "/System/Library/CoreServices/ServerVersion.plist"; 899 static const UInt8 system_version[] = 900 "/System/Library/CoreServices/SystemVersion.plist"; 901 902 CFPropertyListRef plist = NULL; 903 CFDataRef resource = NULL; 904 CFStringRef errstr = NULL; 905 CFURLRef url = NULL; 906 SInt32 errcode; 907 908 url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, 909 server_version, 910 sizeof(server_version) - 1, 911 FALSE); 912 if (!url) 913 return NULL; 914 915 if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, 916 url, &resource, 917 NULL, NULL, &errcode)) 918 { 919 CFRelease(url); 920 url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, 921 system_version, 922 sizeof(system_version) - 1, 923 FALSE); 924 if (!url) 925 return NULL; 926 927 if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, 928 url, &resource, 929 NULL, NULL, &errcode)) 930 { 931 CFRelease(url); 932 return NULL; 933 } 934 else 935 { 936 CFRelease(url); 937 *server = FALSE; 938 } 939 } 940 else 941 { 942 CFRelease(url); 943 *server = TRUE; 944 } 945 946 /* ### CFPropertyListCreateFromXMLData is obsolete, but its 947 replacement CFPropertyListCreateWithData is only available 948 from Mac OS 1.6 onward. */ 949 plist = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, resource, 950 kCFPropertyListImmutable, 951 &errstr); 952 if (resource) 953 CFRelease(resource); 954 if (errstr) 955 CFRelease(errstr); 956 957 if (CFDictionaryGetTypeID() != CFGetTypeID(plist)) 958 { 959 /* Oops ... this really should be a dict. */ 960 CFRelease(plist); 961 return NULL; 962 } 963 964 return plist; 965} 966 967/* Return the value for KEY from PLIST, or NULL if not available. */ 968static const char * 969value_from_dict(CFDictionaryRef plist, CFStringRef key, apr_pool_t *pool) 970{ 971 CFStringRef valref; 972 CFIndex bufsize; 973 const void *valptr; 974 const char *value; 975 976 if (!CFDictionaryGetValueIfPresent(plist, key, &valptr)) 977 return NULL; 978 979 valref = valptr; 980 if (CFStringGetTypeID() != CFGetTypeID(valref)) 981 return NULL; 982 983 value = CFStringGetCStringPtr(valref, kCFStringEncodingUTF8); 984 if (value) 985 return apr_pstrdup(pool, value); 986 987 bufsize = 5 * CFStringGetLength(valref) + 1; 988 value = apr_palloc(pool, bufsize); 989 if (!CFStringGetCString(valref, (char*)value, bufsize, 990 kCFStringEncodingUTF8)) 991 value = NULL; 992 993 return value; 994} 995 996/* Return the commercial name of the OS, given the version number in 997 a format that matches the regular expression /^10\.\d+(\..*)?$/ */ 998static const char * 999release_name_from_version(const char *osver) 1000{ 1001 char *end = NULL; 1002 unsigned long num = strtoul(osver, &end, 10); 1003 1004 if (!end || *end != '.' || num != 10) 1005 return NULL; 1006 1007 osver = end + 1; 1008 end = NULL; 1009 num = strtoul(osver, &end, 10); 1010 if (!end || (*end && *end != '.')) 1011 return NULL; 1012 1013 /* See http://en.wikipedia.org/wiki/History_of_OS_X#Release_timeline */ 1014 switch(num) 1015 { 1016 case 0: return "Cheetah"; 1017 case 1: return "Puma"; 1018 case 2: return "Jaguar"; 1019 case 3: return "Panther"; 1020 case 4: return "Tiger"; 1021 case 5: return "Leopard"; 1022 case 6: return "Snow Leopard"; 1023 case 7: return "Lion"; 1024 case 8: return "Mountain Lion"; 1025 } 1026 1027 return NULL; 1028} 1029 1030/* Construct the release name from information stored in the Mac OS X 1031 "SystemVersion.plist" file (or ServerVersion.plist, for Mac Os 1032 Server. */ 1033static const char * 1034macos_release_name(apr_pool_t *pool) 1035{ 1036 svn_boolean_t server; 1037 CFDictionaryRef plist = system_version_plist(&server, pool); 1038 1039 if (plist) 1040 { 1041 const char *osname = value_from_dict(plist, CFSTR("ProductName"), pool); 1042 const char *osver = value_from_dict(plist, 1043 CFSTR("ProductUserVisibleVersion"), 1044 pool); 1045 const char *build = value_from_dict(plist, 1046 CFSTR("ProductBuildVersion"), 1047 pool); 1048 const char *release; 1049 1050 if (!osver) 1051 osver = value_from_dict(plist, CFSTR("ProductVersion"), pool); 1052 release = release_name_from_version(osver); 1053 1054 CFRelease(plist); 1055 return apr_psprintf(pool, "%s%s%s%s%s%s%s%s", 1056 (osname ? osname : ""), 1057 (osver ? (osname ? " " : "") : ""), 1058 (osver ? osver : ""), 1059 (release ? (osname||osver ? " " : "") : ""), 1060 (release ? release : ""), 1061 (build 1062 ? (osname||osver||release ? ", " : "") 1063 : ""), 1064 (build 1065 ? (server ? "server build " : "build ") 1066 : ""), 1067 (build ? build : "")); 1068 } 1069 1070 return NULL; 1071} 1072#endif /* SVN_HAVE_MACOS_PLIST */ 1073 1074#ifdef SVN_HAVE_MACHO_ITERATE 1075/* List the shared libraries loaded by the current process. 1076 Ignore frameworks and system libraries, they're just clutter. */ 1077static const apr_array_header_t * 1078macos_shared_libs(apr_pool_t *pool) 1079{ 1080 static const char slb_prefix[] = "/usr/lib/system/"; 1081 static const char fwk_prefix[] = "/System/Library/Frameworks/"; 1082 static const char pfk_prefix[] = "/System/Library/PrivateFrameworks/"; 1083 1084 const size_t slb_prefix_len = strlen(slb_prefix); 1085 const size_t fwk_prefix_len = strlen(fwk_prefix); 1086 const size_t pfk_prefix_len = strlen(pfk_prefix); 1087 1088 apr_array_header_t *result = NULL; 1089 apr_array_header_t *dylibs = NULL; 1090 1091 uint32_t i; 1092 for (i = 0;; ++i) 1093 { 1094 const struct mach_header *header = _dyld_get_image_header(i); 1095 const char *filename = _dyld_get_image_name(i); 1096 const char *version; 1097 char *truename; 1098 svn_version_ext_loaded_lib_t *lib; 1099 1100 if (!(header && filename)) 1101 break; 1102 1103 switch (header->cputype) 1104 { 1105 case CPU_TYPE_I386: version = _("Intel"); break; 1106 case CPU_TYPE_X86_64: version = _("Intel 64-bit"); break; 1107 case CPU_TYPE_POWERPC: version = _("PowerPC"); break; 1108 case CPU_TYPE_POWERPC64: version = _("PowerPC 64-bit"); break; 1109 default: 1110 version = NULL; 1111 } 1112 1113 if (0 == apr_filepath_merge(&truename, "", filename, 1114 APR_FILEPATH_NATIVE 1115 | APR_FILEPATH_TRUENAME, 1116 pool)) 1117 filename = truename; 1118 else 1119 filename = apr_pstrdup(pool, filename); 1120 1121 if (0 == strncmp(filename, slb_prefix, slb_prefix_len) 1122 || 0 == strncmp(filename, fwk_prefix, fwk_prefix_len) 1123 || 0 == strncmp(filename, pfk_prefix, pfk_prefix_len)) 1124 { 1125 /* Ignore frameworks and system libraries. */ 1126 continue; 1127 } 1128 1129 if (header->filetype == MH_EXECUTE) 1130 { 1131 /* Make sure the program filename is first in the list */ 1132 if (!result) 1133 { 1134 result = apr_array_make(pool, 32, sizeof(*lib)); 1135 } 1136 lib = &APR_ARRAY_PUSH(result, svn_version_ext_loaded_lib_t); 1137 } 1138 else 1139 { 1140 if (!dylibs) 1141 { 1142 dylibs = apr_array_make(pool, 32, sizeof(*lib)); 1143 } 1144 lib = &APR_ARRAY_PUSH(dylibs, svn_version_ext_loaded_lib_t); 1145 } 1146 1147 lib->name = filename; 1148 lib->version = version; 1149 } 1150 1151 /* Gather results into one array. */ 1152 if (dylibs) 1153 { 1154 if (result) 1155 apr_array_cat(result, dylibs); 1156 else 1157 result = dylibs; 1158 } 1159 1160 return result; 1161} 1162#endif /* SVN_HAVE_MACHO_ITERATE */ 1163