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