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