1362181Sdim 2251881Speter/* 3251881Speter * sysinfo.c : information about the running system 4251881Speter * 5251881Speter * ==================================================================== 6251881Speter * Licensed to the Apache Software Foundation (ASF) under one 7251881Speter * or more contributor license agreements. See the NOTICE file 8251881Speter * distributed with this work for additional information 9251881Speter * regarding copyright ownership. The ASF licenses this file 10251881Speter * to you under the Apache License, Version 2.0 (the 11251881Speter * "License"); you may not use this file except in compliance 12251881Speter * with the License. You may obtain a copy of the License at 13251881Speter * 14251881Speter * http://www.apache.org/licenses/LICENSE-2.0 15251881Speter * 16251881Speter * Unless required by applicable law or agreed to in writing, 17251881Speter * software distributed under the License is distributed on an 18251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 19251881Speter * KIND, either express or implied. See the License for the 20251881Speter * specific language governing permissions and limitations 21251881Speter * under the License. 22251881Speter * ==================================================================== 23251881Speter */ 24251881Speter 25251881Speter 26251881Speter 27251881Speter#define APR_WANT_STRFUNC 28251881Speter#include <apr_want.h> 29251881Speter 30251881Speter#include <apr_lib.h> 31251881Speter#include <apr_pools.h> 32251881Speter#include <apr_file_info.h> 33251881Speter#include <apr_signal.h> 34251881Speter#include <apr_strings.h> 35251881Speter#include <apr_thread_proc.h> 36251881Speter#include <apr_version.h> 37251881Speter#include <apu_version.h> 38251881Speter 39251881Speter#include "svn_pools.h" 40251881Speter#include "svn_ctype.h" 41251881Speter#include "svn_dirent_uri.h" 42251881Speter#include "svn_error.h" 43251881Speter#include "svn_io.h" 44251881Speter#include "svn_string.h" 45251881Speter#include "svn_utf.h" 46251881Speter#include "svn_version.h" 47251881Speter 48251881Speter#include "private/svn_sqlite.h" 49289180Speter#include "private/svn_subr_private.h" 50289180Speter#include "private/svn_utf_private.h" 51251881Speter 52251881Speter#include "sysinfo.h" 53251881Speter#include "svn_private_config.h" 54251881Speter 55362181Sdim#if HAVE_SYS_TYPES_H 56362181Sdim#include <sys/types.h> 57362181Sdim#endif 58362181Sdim 59251881Speter#if HAVE_SYS_UTSNAME_H 60251881Speter#include <sys/utsname.h> 61251881Speter#endif 62251881Speter 63362181Sdim#if HAVE_UNISTD_H 64362181Sdim#include <unistd.h> 65362181Sdim#endif 66362181Sdim 67362181Sdim#if HAVE_ELF_H 68362181Sdim#include <elf.h> 69362181Sdim#endif 70362181Sdim 71251881Speter#ifdef SVN_HAVE_MACOS_PLIST 72251881Speter#include <CoreFoundation/CoreFoundation.h> 73289180Speter#include <AvailabilityMacros.h> 74289180Speter# ifndef MAC_OS_X_VERSION_10_6 75289180Speter# define MAC_OS_X_VERSION_10_6 1060 76289180Speter# endif 77251881Speter#endif 78251881Speter 79251881Speter#ifdef SVN_HAVE_MACHO_ITERATE 80251881Speter#include <mach-o/dyld.h> 81251881Speter#include <mach-o/loader.h> 82251881Speter#endif 83251881Speter 84251881Speter#if HAVE_UNAME 85251881Speterstatic const char *canonical_host_from_uname(apr_pool_t *pool); 86251881Speter# ifndef SVN_HAVE_MACOS_PLIST 87251881Speterstatic const char *release_name_from_uname(apr_pool_t *pool); 88251881Speter# endif 89251881Speter#endif 90251881Speter 91251881Speter#ifdef WIN32 92251881Speterstatic const char *win32_canonical_host(apr_pool_t *pool); 93251881Speterstatic const char *win32_release_name(apr_pool_t *pool); 94251881Speterstatic const apr_array_header_t *win32_shared_libs(apr_pool_t *pool); 95251881Speter#endif /* WIN32 */ 96251881Speter 97251881Speter#ifdef SVN_HAVE_MACOS_PLIST 98251881Speterstatic const char *macos_release_name(apr_pool_t *pool); 99251881Speter#endif 100251881Speter 101251881Speter#ifdef SVN_HAVE_MACHO_ITERATE 102251881Speterstatic const apr_array_header_t *macos_shared_libs(apr_pool_t *pool); 103251881Speter#endif 104251881Speter 105251881Speter 106251881Speter#if __linux__ 107251881Speterstatic const char *linux_release_name(apr_pool_t *pool); 108362181Sdimstatic const apr_array_header_t *linux_shared_libs(apr_pool_t *pool); 109251881Speter#endif 110251881Speter 111251881Speterconst char * 112251881Spetersvn_sysinfo__canonical_host(apr_pool_t *pool) 113251881Speter{ 114251881Speter#ifdef WIN32 115251881Speter return win32_canonical_host(pool); 116251881Speter#elif HAVE_UNAME 117251881Speter return canonical_host_from_uname(pool); 118251881Speter#else 119251881Speter return "unknown-unknown-unknown"; 120251881Speter#endif 121251881Speter} 122251881Speter 123251881Speter 124251881Speterconst char * 125251881Spetersvn_sysinfo__release_name(apr_pool_t *pool) 126251881Speter{ 127251881Speter#ifdef WIN32 128251881Speter return win32_release_name(pool); 129251881Speter#elif defined(SVN_HAVE_MACOS_PLIST) 130251881Speter return macos_release_name(pool); 131251881Speter#elif __linux__ 132251881Speter return linux_release_name(pool); 133251881Speter#elif HAVE_UNAME 134251881Speter return release_name_from_uname(pool); 135251881Speter#else 136251881Speter return NULL; 137251881Speter#endif 138251881Speter} 139251881Speter 140251881Speterconst apr_array_header_t * 141251881Spetersvn_sysinfo__linked_libs(apr_pool_t *pool) 142251881Speter{ 143251881Speter svn_version_ext_linked_lib_t *lib; 144362181Sdim apr_array_header_t *array = apr_array_make(pool, 7, sizeof(*lib)); 145362181Sdim int lz4_version = svn_lz4__runtime_version(); 146251881Speter 147251881Speter lib = &APR_ARRAY_PUSH(array, svn_version_ext_linked_lib_t); 148251881Speter lib->name = "APR"; 149251881Speter lib->compiled_version = APR_VERSION_STRING; 150251881Speter lib->runtime_version = apr_pstrdup(pool, apr_version_string()); 151251881Speter 152251881Speter/* Don't list APR-Util if it isn't linked in, which it may not be if 153251881Speter * we're using APR 2.x+ which combined APR-Util into APR. */ 154251881Speter#ifdef APU_VERSION_STRING 155251881Speter lib = &APR_ARRAY_PUSH(array, svn_version_ext_linked_lib_t); 156251881Speter lib->name = "APR-Util"; 157251881Speter lib->compiled_version = APU_VERSION_STRING; 158251881Speter lib->runtime_version = apr_pstrdup(pool, apu_version_string()); 159251881Speter#endif 160251881Speter 161251881Speter lib = &APR_ARRAY_PUSH(array, svn_version_ext_linked_lib_t); 162289180Speter lib->name = "Expat"; 163289180Speter lib->compiled_version = apr_pstrdup(pool, svn_xml__compiled_version()); 164289180Speter lib->runtime_version = apr_pstrdup(pool, svn_xml__runtime_version()); 165289180Speter 166289180Speter lib = &APR_ARRAY_PUSH(array, svn_version_ext_linked_lib_t); 167251881Speter lib->name = "SQLite"; 168251881Speter lib->compiled_version = apr_pstrdup(pool, svn_sqlite__compiled_version()); 169251881Speter#ifdef SVN_SQLITE_INLINE 170251881Speter lib->runtime_version = NULL; 171251881Speter#else 172251881Speter lib->runtime_version = apr_pstrdup(pool, svn_sqlite__runtime_version()); 173251881Speter#endif 174251881Speter 175289180Speter lib = &APR_ARRAY_PUSH(array, svn_version_ext_linked_lib_t); 176289180Speter lib->name = "Utf8proc"; 177289180Speter lib->compiled_version = apr_pstrdup(pool, svn_utf__utf8proc_compiled_version()); 178289180Speter lib->runtime_version = apr_pstrdup(pool, svn_utf__utf8proc_runtime_version()); 179289180Speter 180289180Speter lib = &APR_ARRAY_PUSH(array, svn_version_ext_linked_lib_t); 181289180Speter lib->name = "ZLib"; 182289180Speter lib->compiled_version = apr_pstrdup(pool, svn_zlib__compiled_version()); 183289180Speter lib->runtime_version = apr_pstrdup(pool, svn_zlib__runtime_version()); 184289180Speter 185362181Sdim lib = &APR_ARRAY_PUSH(array, svn_version_ext_linked_lib_t); 186362181Sdim lib->name = "LZ4"; 187362181Sdim lib->compiled_version = apr_pstrdup(pool, svn_lz4__compiled_version()); 188362181Sdim 189362181Sdim lib->runtime_version = apr_psprintf(pool, "%d.%d.%d", 190362181Sdim lz4_version / 100 / 100, 191362181Sdim (lz4_version / 100) % 100, 192362181Sdim lz4_version % 100); 193362181Sdim 194251881Speter return array; 195251881Speter} 196251881Speter 197251881Speterconst apr_array_header_t * 198251881Spetersvn_sysinfo__loaded_libs(apr_pool_t *pool) 199251881Speter{ 200251881Speter#ifdef WIN32 201251881Speter return win32_shared_libs(pool); 202251881Speter#elif defined(SVN_HAVE_MACHO_ITERATE) 203251881Speter return macos_shared_libs(pool); 204362181Sdim#elif __linux__ 205362181Sdim return linux_shared_libs(pool); 206251881Speter#else 207251881Speter return NULL; 208251881Speter#endif 209251881Speter} 210251881Speter 211251881Speter 212251881Speter#if HAVE_UNAME 213251881Speterstatic const char* 214251881Spetercanonical_host_from_uname(apr_pool_t *pool) 215251881Speter{ 216251881Speter const char *machine = "unknown"; 217251881Speter const char *vendor = "unknown"; 218251881Speter const char *sysname = "unknown"; 219251881Speter const char *sysver = ""; 220251881Speter struct utsname info; 221251881Speter 222251881Speter if (0 <= uname(&info)) 223251881Speter { 224251881Speter svn_error_t *err; 225251881Speter const char *tmp; 226251881Speter 227251881Speter err = svn_utf_cstring_to_utf8(&tmp, info.machine, pool); 228251881Speter if (err) 229251881Speter svn_error_clear(err); 230251881Speter else 231251881Speter machine = tmp; 232251881Speter 233251881Speter err = svn_utf_cstring_to_utf8(&tmp, info.sysname, pool); 234251881Speter if (err) 235251881Speter svn_error_clear(err); 236251881Speter else 237251881Speter { 238251881Speter char *lwr = apr_pstrdup(pool, tmp); 239251881Speter char *it = lwr; 240251881Speter while (*it) 241251881Speter { 242251881Speter if (svn_ctype_isupper(*it)) 243251881Speter *it = apr_tolower(*it); 244251881Speter ++it; 245251881Speter } 246251881Speter sysname = lwr; 247251881Speter } 248251881Speter 249251881Speter if (0 == strcmp(sysname, "darwin")) 250251881Speter vendor = "apple"; 251251881Speter if (0 == strcmp(sysname, "linux")) 252251881Speter sysver = "-gnu"; 253251881Speter else 254251881Speter { 255251881Speter err = svn_utf_cstring_to_utf8(&tmp, info.release, pool); 256251881Speter if (err) 257251881Speter svn_error_clear(err); 258251881Speter else 259251881Speter { 260251881Speter apr_size_t n = strspn(tmp, ".0123456789"); 261251881Speter if (n > 0) 262251881Speter { 263251881Speter char *ver = apr_pstrdup(pool, tmp); 264251881Speter ver[n] = 0; 265251881Speter sysver = ver; 266251881Speter } 267251881Speter else 268251881Speter sysver = tmp; 269251881Speter } 270251881Speter } 271251881Speter } 272251881Speter 273251881Speter return apr_psprintf(pool, "%s-%s-%s%s", machine, vendor, sysname, sysver); 274251881Speter} 275251881Speter 276251881Speter# ifndef SVN_HAVE_MACOS_PLIST 277251881Speter/* Generate a release name from the uname(3) info, effectively 278251881Speter returning "`uname -s` `uname -r`". */ 279251881Speterstatic const char * 280251881Speterrelease_name_from_uname(apr_pool_t *pool) 281251881Speter{ 282251881Speter struct utsname info; 283251881Speter if (0 <= uname(&info)) 284251881Speter { 285251881Speter svn_error_t *err; 286251881Speter const char *sysname; 287251881Speter const char *sysver; 288251881Speter 289251881Speter err = svn_utf_cstring_to_utf8(&sysname, info.sysname, pool); 290251881Speter if (err) 291251881Speter { 292251881Speter sysname = NULL; 293251881Speter svn_error_clear(err); 294251881Speter } 295251881Speter 296251881Speter 297251881Speter err = svn_utf_cstring_to_utf8(&sysver, info.release, pool); 298251881Speter if (err) 299251881Speter { 300251881Speter sysver = NULL; 301251881Speter svn_error_clear(err); 302251881Speter } 303251881Speter 304251881Speter if (sysname || sysver) 305251881Speter { 306251881Speter return apr_psprintf(pool, "%s%s%s", 307251881Speter (sysname ? sysname : ""), 308251881Speter (sysver ? (sysname ? " " : "") : ""), 309251881Speter (sysver ? sysver : "")); 310251881Speter } 311251881Speter } 312251881Speter return NULL; 313251881Speter} 314251881Speter# endif /* !SVN_HAVE_MACOS_PLIST */ 315251881Speter#endif /* HAVE_UNAME */ 316251881Speter 317251881Speter 318251881Speter#if __linux__ 319362181Sdim/* Find the first whitespace character in a stringbuf. 320362181Sdim Analogous to svn_stringbuf_first_non_whitespace. */ 321362181Sdimstatic apr_size_t 322362181Sdimstringbuf_first_whitespace(const svn_stringbuf_t *str) 323362181Sdim{ 324362181Sdim apr_size_t i; 325362181Sdim for (i = 0; i < str->len; ++i) 326362181Sdim { 327362181Sdim if (svn_ctype_isspace(str->data[i])) 328362181Sdim return i; 329362181Sdim } 330362181Sdim return str->len; 331362181Sdim} 332362181Sdim 333362181Sdim/* Skip a whitespace-delimited field in a stringbuf. */ 334362181Sdimstatic void 335362181Sdimstringbuf_skip_whitespace_field(svn_stringbuf_t *str) 336362181Sdim{ 337362181Sdim apr_size_t i; 338362181Sdim i = stringbuf_first_whitespace(str); 339362181Sdim svn_stringbuf_leftchop(str, i); 340362181Sdim i = svn_stringbuf_first_non_whitespace(str); 341362181Sdim svn_stringbuf_leftchop(str, i); 342362181Sdim} 343362181Sdim 344251881Speter/* Split a stringbuf into a key/value pair. 345289180Speter Return the key, leaving the stripped value in the stringbuf. */ 346251881Speterstatic const char * 347251881Speterstringbuf_split_key(svn_stringbuf_t *buffer, char delim) 348251881Speter{ 349251881Speter char *key; 350251881Speter char *end; 351251881Speter 352251881Speter end = strchr(buffer->data, delim); 353251881Speter if (!end) 354251881Speter return NULL; 355251881Speter 356251881Speter svn_stringbuf_strip_whitespace(buffer); 357262250Speter 358262250Speter /* Now we split the currently allocated buffer in two parts: 359262250Speter - a const char * HEAD 360262250Speter - the remaining stringbuf_t. */ 361262250Speter 362262250Speter /* Create HEAD as '\0' terminated const char * */ 363251881Speter key = buffer->data; 364251881Speter end = strchr(key, delim); 365251881Speter *end = '\0'; 366262250Speter 367262250Speter /* And update the TAIL to be a smaller, but still valid stringbuf */ 368251881Speter buffer->data = end + 1; 369262250Speter buffer->len -= 1 + end - key; 370262250Speter buffer->blocksize -= 1 + end - key; 371262250Speter 372251881Speter svn_stringbuf_strip_whitespace(buffer); 373251881Speter 374251881Speter return key; 375251881Speter} 376251881Speter 377251881Speter/* Parse `/usr/bin/lsb_rlease --all` */ 378251881Speterstatic const char * 379251881Speterlsb_release(apr_pool_t *pool) 380251881Speter{ 381251881Speter static const char *const args[3] = 382251881Speter { 383251881Speter "/usr/bin/lsb_release", 384251881Speter "--all", 385251881Speter NULL 386251881Speter }; 387251881Speter 388251881Speter const char *distributor = NULL; 389251881Speter const char *description = NULL; 390251881Speter const char *release = NULL; 391251881Speter const char *codename = NULL; 392251881Speter 393251881Speter apr_proc_t lsbproc; 394251881Speter svn_stream_t *lsbinfo; 395251881Speter svn_error_t *err; 396251881Speter 397251881Speter /* Run /usr/bin/lsb_release --all < /dev/null 2>/dev/null */ 398251881Speter { 399251881Speter apr_file_t *stdin_handle; 400251881Speter apr_file_t *stdout_handle; 401251881Speter 402251881Speter err = svn_io_file_open(&stdin_handle, SVN_NULL_DEVICE_NAME, 403251881Speter APR_READ, APR_OS_DEFAULT, pool); 404251881Speter if (!err) 405251881Speter err = svn_io_file_open(&stdout_handle, SVN_NULL_DEVICE_NAME, 406251881Speter APR_WRITE, APR_OS_DEFAULT, pool); 407251881Speter if (!err) 408251881Speter err = svn_io_start_cmd3(&lsbproc, NULL, args[0], args, NULL, FALSE, 409251881Speter FALSE, stdin_handle, 410251881Speter TRUE, NULL, 411251881Speter FALSE, stdout_handle, 412251881Speter pool); 413251881Speter if (err) 414251881Speter { 415251881Speter svn_error_clear(err); 416251881Speter return NULL; 417251881Speter } 418251881Speter } 419251881Speter 420251881Speter /* Parse the output and try to populate the */ 421251881Speter lsbinfo = svn_stream_from_aprfile2(lsbproc.out, TRUE, pool); 422251881Speter if (lsbinfo) 423251881Speter { 424251881Speter for (;;) 425251881Speter { 426251881Speter svn_boolean_t eof = FALSE; 427251881Speter svn_stringbuf_t *line; 428251881Speter const char *key; 429251881Speter 430251881Speter err = svn_stream_readline(lsbinfo, &line, "\n", &eof, pool); 431251881Speter if (err || eof) 432251881Speter break; 433251881Speter 434251881Speter key = stringbuf_split_key(line, ':'); 435251881Speter if (!key) 436251881Speter continue; 437251881Speter 438251881Speter if (0 == svn_cstring_casecmp(key, "Distributor ID")) 439251881Speter distributor = line->data; 440251881Speter else if (0 == svn_cstring_casecmp(key, "Description")) 441251881Speter description = line->data; 442251881Speter else if (0 == svn_cstring_casecmp(key, "Release")) 443251881Speter release = line->data; 444251881Speter else if (0 == svn_cstring_casecmp(key, "Codename")) 445251881Speter codename = line->data; 446251881Speter } 447251881Speter err = svn_error_compose_create(err, 448251881Speter svn_stream_close(lsbinfo)); 449251881Speter if (err) 450251881Speter { 451251881Speter svn_error_clear(err); 452251881Speter apr_proc_kill(&lsbproc, SIGKILL); 453251881Speter return NULL; 454251881Speter } 455251881Speter } 456251881Speter 457251881Speter /* Reap the child process */ 458251881Speter err = svn_io_wait_for_cmd(&lsbproc, "", NULL, NULL, pool); 459251881Speter if (err) 460251881Speter { 461251881Speter svn_error_clear(err); 462251881Speter return NULL; 463251881Speter } 464251881Speter 465251881Speter if (description) 466251881Speter return apr_psprintf(pool, "%s%s%s%s", description, 467251881Speter (codename ? " (" : ""), 468251881Speter (codename ? codename : ""), 469251881Speter (codename ? ")" : "")); 470251881Speter if (distributor) 471251881Speter return apr_psprintf(pool, "%s%s%s%s%s%s", distributor, 472251881Speter (release ? " " : ""), 473251881Speter (release ? release : ""), 474251881Speter (codename ? " (" : ""), 475251881Speter (codename ? codename : ""), 476251881Speter (codename ? ")" : "")); 477251881Speter 478251881Speter return NULL; 479251881Speter} 480251881Speter 481289180Speter/* Read /etc/os-release, as documented here: 482289180Speter * http://www.freedesktop.org/software/systemd/man/os-release.html 483289180Speter */ 484289180Speterstatic const char * 485289180Spetersystemd_release(apr_pool_t *pool) 486289180Speter{ 487289180Speter svn_error_t *err; 488289180Speter svn_stream_t *stream; 489289180Speter 490289180Speter /* Open the file. */ 491289180Speter err = svn_stream_open_readonly(&stream, "/etc/os-release", pool, pool); 492289180Speter if (err && APR_STATUS_IS_ENOENT(err->apr_err)) 493289180Speter { 494289180Speter svn_error_clear(err); 495289180Speter err = svn_stream_open_readonly(&stream, "/usr/lib/os-release", pool, 496289180Speter pool); 497289180Speter } 498289180Speter if (err) 499289180Speter { 500289180Speter svn_error_clear(err); 501289180Speter return NULL; 502289180Speter } 503289180Speter 504289180Speter /* Look for the PRETTY_NAME line. */ 505289180Speter while (TRUE) 506289180Speter { 507289180Speter svn_stringbuf_t *line; 508289180Speter svn_boolean_t eof; 509289180Speter 510289180Speter err = svn_stream_readline(stream, &line, "\n", &eof, pool); 511289180Speter if (err) 512289180Speter { 513289180Speter svn_error_clear(err); 514289180Speter return NULL; 515289180Speter } 516289180Speter 517289180Speter if (!strncmp(line->data, "PRETTY_NAME=", 12)) 518289180Speter { 519289180Speter svn_stringbuf_t *release_name; 520289180Speter 521289180Speter /* The value may or may not be enclosed by double quotes. We don't 522289180Speter * attempt to strip them. */ 523289180Speter release_name = svn_stringbuf_create(line->data + 12, pool); 524289180Speter svn_error_clear(svn_stream_close(stream)); 525289180Speter svn_stringbuf_strip_whitespace(release_name); 526289180Speter return release_name->data; 527289180Speter } 528289180Speter 529289180Speter if (eof) 530289180Speter break; 531289180Speter } 532289180Speter 533289180Speter /* The file did not contain a PRETTY_NAME line. */ 534289180Speter svn_error_clear(svn_stream_close(stream)); 535289180Speter return NULL; 536289180Speter} 537289180Speter 538251881Speter/* Read the whole contents of a file. */ 539251881Speterstatic svn_stringbuf_t * 540251881Speterread_file_contents(const char *filename, apr_pool_t *pool) 541251881Speter{ 542251881Speter svn_error_t *err; 543251881Speter svn_stringbuf_t *buffer; 544251881Speter 545251881Speter err = svn_stringbuf_from_file2(&buffer, filename, pool); 546251881Speter if (err) 547251881Speter { 548251881Speter svn_error_clear(err); 549251881Speter return NULL; 550251881Speter } 551251881Speter 552251881Speter return buffer; 553251881Speter} 554251881Speter 555251881Speter/* Strip everything but the first line from a stringbuf. */ 556251881Speterstatic void 557251881Speterstringbuf_first_line_only(svn_stringbuf_t *buffer) 558251881Speter{ 559251881Speter char *eol = strchr(buffer->data, '\n'); 560251881Speter if (eol) 561251881Speter { 562251881Speter *eol = '\0'; 563251881Speter buffer->len = 1 + eol - buffer->data; 564251881Speter } 565251881Speter svn_stringbuf_strip_whitespace(buffer); 566251881Speter} 567251881Speter 568251881Speter/* Look at /etc/redhat_release to detect RHEL/Fedora/CentOS. */ 569251881Speterstatic const char * 570251881Speterredhat_release(apr_pool_t *pool) 571251881Speter{ 572251881Speter svn_stringbuf_t *buffer = read_file_contents("/etc/redhat-release", pool); 573251881Speter if (buffer) 574251881Speter { 575251881Speter stringbuf_first_line_only(buffer); 576251881Speter return buffer->data; 577251881Speter } 578251881Speter return NULL; 579251881Speter} 580251881Speter 581251881Speter/* Look at /etc/SuSE-release to detect non-LSB SuSE. */ 582251881Speterstatic const char * 583251881Spetersuse_release(apr_pool_t *pool) 584251881Speter{ 585251881Speter const char *release = NULL; 586251881Speter const char *codename = NULL; 587251881Speter 588251881Speter svn_stringbuf_t *buffer = read_file_contents("/etc/SuSE-release", pool); 589251881Speter svn_stringbuf_t *line; 590251881Speter svn_stream_t *stream; 591251881Speter svn_boolean_t eof; 592251881Speter svn_error_t *err; 593251881Speter if (!buffer) 594251881Speter return NULL; 595251881Speter 596251881Speter stream = svn_stream_from_stringbuf(buffer, pool); 597251881Speter err = svn_stream_readline(stream, &line, "\n", &eof, pool); 598251881Speter if (err || eof) 599251881Speter { 600251881Speter svn_error_clear(err); 601251881Speter return NULL; 602251881Speter } 603251881Speter 604251881Speter svn_stringbuf_strip_whitespace(line); 605251881Speter release = line->data; 606251881Speter 607251881Speter for (;;) 608251881Speter { 609251881Speter const char *key; 610251881Speter 611251881Speter err = svn_stream_readline(stream, &line, "\n", &eof, pool); 612251881Speter if (err || eof) 613251881Speter { 614251881Speter svn_error_clear(err); 615251881Speter break; 616251881Speter } 617251881Speter 618251881Speter key = stringbuf_split_key(line, '='); 619251881Speter if (!key) 620251881Speter continue; 621251881Speter 622251881Speter if (0 == strncmp(key, "CODENAME", 8)) 623251881Speter codename = line->data; 624251881Speter } 625251881Speter 626251881Speter return apr_psprintf(pool, "%s%s%s%s", 627251881Speter release, 628251881Speter (codename ? " (" : ""), 629251881Speter (codename ? codename : ""), 630251881Speter (codename ? ")" : "")); 631251881Speter} 632251881Speter 633251881Speter/* Look at /etc/debian_version to detect non-LSB Debian. */ 634251881Speterstatic const char * 635251881Speterdebian_release(apr_pool_t *pool) 636251881Speter{ 637251881Speter svn_stringbuf_t *buffer = read_file_contents("/etc/debian_version", pool); 638251881Speter if (!buffer) 639251881Speter return NULL; 640251881Speter 641251881Speter stringbuf_first_line_only(buffer); 642289180Speter return apr_pstrcat(pool, "Debian ", buffer->data, SVN_VA_NULL); 643251881Speter} 644251881Speter 645251881Speter/* Try to find the Linux distribution name, or return info from uname. */ 646251881Speterstatic const char * 647251881Speterlinux_release_name(apr_pool_t *pool) 648251881Speter{ 649251881Speter const char *uname_release = release_name_from_uname(pool); 650251881Speter 651251881Speter /* Try anything that has /usr/bin/lsb_release. 652251881Speter Covers, for example, Debian, Ubuntu and SuSE. */ 653251881Speter const char *release_name = lsb_release(pool); 654251881Speter 655289180Speter /* Try the systemd way (covers Arch). */ 656289180Speter if (!release_name) 657289180Speter release_name = systemd_release(pool); 658289180Speter 659251881Speter /* Try RHEL/Fedora/CentOS */ 660251881Speter if (!release_name) 661251881Speter release_name = redhat_release(pool); 662251881Speter 663251881Speter /* Try Non-LSB SuSE */ 664251881Speter if (!release_name) 665251881Speter release_name = suse_release(pool); 666251881Speter 667251881Speter /* Try non-LSB Debian */ 668251881Speter if (!release_name) 669251881Speter release_name = debian_release(pool); 670251881Speter 671251881Speter if (!release_name) 672251881Speter return uname_release; 673251881Speter 674251881Speter if (!uname_release) 675251881Speter return release_name; 676251881Speter 677251881Speter return apr_psprintf(pool, "%s [%s]", release_name, uname_release); 678251881Speter} 679362181Sdim 680362181Sdim#if HAVE_ELF_H 681362181Sdim/* Parse a hexadecimal number as a pointer value. */ 682362181Sdimstatic const unsigned char * 683362181Sdimparse_pointer_value(const char *start, const char *limit, char **end) 684362181Sdim{ 685362181Sdim const unsigned char *ptr; 686362181Sdim const apr_uint64_t val = (apr_uint64_t)apr_strtoi64(start, end, 16); 687362181Sdim 688362181Sdim if (errno /* overflow */ 689362181Sdim || *end == start /* no valid digits */ 690362181Sdim || *end >= limit) /* representation too long */ 691362181Sdim return NULL; 692362181Sdim 693362181Sdim ptr = (const unsigned char*)val; 694362181Sdim if (val != (apr_uint64_t)ptr) /* truncated value */ 695362181Sdim return NULL; 696362181Sdim 697362181Sdim return ptr; 698362181Sdim} 699362181Sdim 700362181Sdim/* Read the ELF header at the mapping position to check if this is a shared 701362181Sdim library. We only look at the ELF identification and the type. The format is 702362181Sdim described here: 703362181Sdim http://www.skyfree.org/linux/references/ELF_Format.pdf 704362181Sdim*/ 705362181Sdimstatic svn_boolean_t 706362181Sdimcheck_elf_header(const unsigned char *map_start, 707362181Sdim const unsigned char *map_end) 708362181Sdim{ 709362181Sdim /* A union of all known ELF header types, for size checks. */ 710362181Sdim union max_elf_header_size_t 711362181Sdim { 712362181Sdim Elf32_Ehdr header_32; 713362181Sdim Elf64_Ehdr header_64; 714362181Sdim }; 715362181Sdim 716362181Sdim /* Check the size of the mapping and the ELF magic tag. */ 717362181Sdim if (map_end < map_start 718362181Sdim || map_end - map_start < sizeof(union max_elf_header_size_t) 719362181Sdim || memcmp(map_start, ELFMAG, SELFMAG)) 720362181Sdim { 721362181Sdim return FALSE; 722362181Sdim } 723362181Sdim 724362181Sdim /* Check that this is an ELF shared library or executable file. This also 725362181Sdim implicitly checks that the data encoding of the current process is the 726362181Sdim same as in the loaded library. */ 727362181Sdim if (map_start[EI_CLASS] == ELFCLASS32) 728362181Sdim { 729362181Sdim const Elf32_Ehdr *hdr = (void*)map_start; 730362181Sdim return (hdr->e_type == ET_DYN || hdr->e_type == ET_EXEC); 731362181Sdim } 732362181Sdim else if (map_start[EI_CLASS] == ELFCLASS64) 733362181Sdim { 734362181Sdim const Elf64_Ehdr *hdr = (void*)map_start; 735362181Sdim return (hdr->e_type == ET_DYN || hdr->e_type == ET_EXEC); 736362181Sdim } 737362181Sdim 738362181Sdim return FALSE; 739362181Sdim} 740362181Sdim#endif /* HAVE_ELF_H */ 741362181Sdim 742362181Sdimstatic const apr_array_header_t * 743362181Sdimlinux_shared_libs(apr_pool_t *pool) 744362181Sdim{ 745362181Sdim /* Read the list of loaded modules from /proc/[pid]/maps 746362181Sdim The format is described here: 747362181Sdim http://man7.org/linux/man-pages/man5/proc.5.html 748362181Sdim */ 749362181Sdim 750362181Sdim const char *maps = apr_psprintf(pool, "/proc/%ld/maps", (long)getpid()); 751362181Sdim apr_array_header_t *result = NULL; 752362181Sdim svn_boolean_t eof = FALSE; 753362181Sdim svn_stream_t *stream; 754362181Sdim svn_error_t *err; 755362181Sdim 756362181Sdim err = svn_stream_open_readonly(&stream, maps, pool, pool); 757362181Sdim if (err) 758362181Sdim { 759362181Sdim svn_error_clear(err); 760362181Sdim return NULL; 761362181Sdim } 762362181Sdim 763362181Sdim /* Each line in /proc/[pid]/maps consists of whitespace-delimited fields. */ 764362181Sdim while (!eof) 765362181Sdim { 766362181Sdim svn_stringbuf_t *line; 767362181Sdim 768362181Sdim#if HAVE_ELF_H 769362181Sdim const unsigned char *map_start; 770362181Sdim const unsigned char *map_end; 771362181Sdim#endif 772362181Sdim 773362181Sdim err = svn_stream_readline(stream, &line, "\n", &eof, pool); 774362181Sdim if (err) 775362181Sdim { 776362181Sdim svn_error_clear(err); 777362181Sdim return NULL; 778362181Sdim } 779362181Sdim 780362181Sdim#if HAVE_ELF_H 781362181Sdim /* Address: The mapped memory address range. */ 782362181Sdim { 783362181Sdim const char *const limit = line->data + line->len; 784362181Sdim char *end; 785362181Sdim 786362181Sdim /* The start of the address range */ 787362181Sdim map_start = parse_pointer_value(line->data, limit, &end); 788362181Sdim if (!map_start || *end != '-') 789362181Sdim continue; 790362181Sdim 791362181Sdim /* The end of the address range */ 792362181Sdim map_end = parse_pointer_value(end + 1, limit, &end); 793362181Sdim if (!map_end || !svn_ctype_isspace(*end)) 794362181Sdim continue; 795362181Sdim } 796362181Sdim#endif 797362181Sdim 798362181Sdim stringbuf_skip_whitespace_field(line); /* skip address */ 799362181Sdim 800362181Sdim /* Permissions: The memory region must be readable and executable. */ 801362181Sdim if (line->len < 4 || line->data[0] != 'r' || line->data[2] != 'x') 802362181Sdim continue; 803362181Sdim 804362181Sdim stringbuf_skip_whitespace_field(line); /* skip perms */ 805362181Sdim stringbuf_skip_whitespace_field(line); /* skip offset */ 806362181Sdim stringbuf_skip_whitespace_field(line); /* skip device */ 807362181Sdim 808362181Sdim /* I-Node: If it is 0, there is no file associated with the region. */ 809362181Sdim if (line->len < 2 810362181Sdim || (line->data[0] == '0' && svn_ctype_isspace(line->data[1]))) 811362181Sdim continue; 812362181Sdim 813362181Sdim stringbuf_skip_whitespace_field(line); /* skip inode */ 814362181Sdim 815362181Sdim /* Consider only things that look like absolute paths. 816362181Sdim Files that were removed since the process was created (due to an 817362181Sdim upgrade, for example) are marked as '(deleted)'. */ 818362181Sdim if (line->data[0] == '/') 819362181Sdim { 820362181Sdim svn_version_ext_loaded_lib_t *lib; 821362181Sdim 822362181Sdim#if HAVE_ELF_H 823362181Sdim if (!check_elf_header(map_start, map_end)) 824362181Sdim continue; 825362181Sdim#endif 826362181Sdim 827362181Sdim /* We've done our best to find a mapped shared library. */ 828362181Sdim if (!result) 829362181Sdim { 830362181Sdim result = apr_array_make(pool, 32, sizeof(*lib)); 831362181Sdim } 832362181Sdim lib = &APR_ARRAY_PUSH(result, svn_version_ext_loaded_lib_t); 833362181Sdim lib->name = line->data; 834362181Sdim lib->version = NULL; 835362181Sdim } 836362181Sdim } 837362181Sdim 838362181Sdim svn_error_clear(svn_stream_close(stream)); 839362181Sdim return result; 840362181Sdim} 841251881Speter#endif /* __linux__ */ 842251881Speter 843251881Speter 844251881Speter#ifdef WIN32 845251881Spetertypedef DWORD (WINAPI *FNGETNATIVESYSTEMINFO)(LPSYSTEM_INFO); 846257936Spetertypedef BOOL (WINAPI *FNENUMPROCESSMODULES) (HANDLE, HMODULE*, DWORD, LPDWORD); 847251881Speter 848289180Spetersvn_boolean_t 849289180Spetersvn_sysinfo___fill_windows_version(OSVERSIONINFOEXW *version_info) 850289180Speter{ 851289180Speter memset(version_info, 0, sizeof(*version_info)); 852289180Speter 853289180Speter version_info->dwOSVersionInfoSize = sizeof(*version_info); 854289180Speter 855289180Speter /* Kill warnings with the Windows 8 and later platform SDK */ 856289180Speter#if _MSC_VER > 1600 && NTDDI_VERSION >= _0x06020000 857289180Speter /* Windows 8 deprecated the API to retrieve the Windows version to avoid 858289180Speter backwards compatibility problems... It might return a constant version 859289180Speter in future Windows versions... But let's kill the warning. 860289180Speter 861289180Speter We can implementation this using a different function later. */ 862289180Speter#pragma warning(push) 863289180Speter#pragma warning(disable: 4996) 864289180Speter#endif 865289180Speter 866289180Speter /* Prototype supports OSVERSIONINFO */ 867289180Speter return GetVersionExW((LPVOID)version_info); 868289180Speter#if _MSC_VER > 1600 && NTDDI_VERSION >= _0x06020000 869289180Speter#pragma warning(pop) 870289180Speter#pragma warning(disable: 4996) 871289180Speter#endif 872289180Speter} 873289180Speter 874289180Speter/* Get system info, and try to tell the difference between the native 875289180Speter system type and the runtime environment of the current process. 876289180Speter Populate results in SYSINFO and LOCAL_SYSINFO (optional). */ 877251881Speterstatic BOOL 878251881Spetersystem_info(SYSTEM_INFO *sysinfo, 879289180Speter SYSTEM_INFO *local_sysinfo) 880251881Speter{ 881251881Speter FNGETNATIVESYSTEMINFO GetNativeSystemInfo_ = (FNGETNATIVESYSTEMINFO) 882362181Sdim GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "GetNativeSystemInfo"); 883251881Speter 884289180Speter memset(sysinfo, 0, sizeof *sysinfo); 885251881Speter if (local_sysinfo) 886251881Speter { 887289180Speter memset(local_sysinfo, 0, sizeof *local_sysinfo); 888251881Speter GetSystemInfo(local_sysinfo); 889251881Speter if (GetNativeSystemInfo_) 890251881Speter GetNativeSystemInfo_(sysinfo); 891251881Speter else 892251881Speter memcpy(sysinfo, local_sysinfo, sizeof *sysinfo); 893251881Speter } 894251881Speter else 895251881Speter GetSystemInfo(sysinfo); 896251881Speter 897251881Speter return TRUE; 898251881Speter} 899251881Speter 900251881Speter/* Map the proccessor type from SYSINFO to a string. */ 901251881Speterstatic const char * 902251881Speterprocessor_name(SYSTEM_INFO *sysinfo) 903251881Speter{ 904251881Speter switch (sysinfo->wProcessorArchitecture) 905251881Speter { 906251881Speter case PROCESSOR_ARCHITECTURE_AMD64: return "x86_64"; 907251881Speter case PROCESSOR_ARCHITECTURE_IA64: return "ia64"; 908251881Speter case PROCESSOR_ARCHITECTURE_INTEL: return "x86"; 909251881Speter case PROCESSOR_ARCHITECTURE_MIPS: return "mips"; 910251881Speter case PROCESSOR_ARCHITECTURE_ALPHA: return "alpha32"; 911251881Speter case PROCESSOR_ARCHITECTURE_PPC: return "powerpc"; 912251881Speter case PROCESSOR_ARCHITECTURE_SHX: return "shx"; 913251881Speter case PROCESSOR_ARCHITECTURE_ARM: return "arm"; 914251881Speter case PROCESSOR_ARCHITECTURE_ALPHA64: return "alpha"; 915251881Speter case PROCESSOR_ARCHITECTURE_MSIL: return "msil"; 916251881Speter case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64: return "x86_wow64"; 917251881Speter default: return "unknown"; 918251881Speter } 919251881Speter} 920251881Speter 921251881Speter/* Return the Windows-specific canonical host name. */ 922251881Speterstatic const char * 923251881Speterwin32_canonical_host(apr_pool_t *pool) 924251881Speter{ 925251881Speter SYSTEM_INFO sysinfo; 926251881Speter SYSTEM_INFO local_sysinfo; 927251881Speter OSVERSIONINFOEXW osinfo; 928251881Speter 929289180Speter if (system_info(&sysinfo, &local_sysinfo) 930289180Speter && svn_sysinfo___fill_windows_version(&osinfo)) 931251881Speter { 932251881Speter const char *arch = processor_name(&local_sysinfo); 933251881Speter const char *machine = processor_name(&sysinfo); 934251881Speter const char *vendor = "microsoft"; 935251881Speter const char *sysname = "windows"; 936251881Speter const char *sysver = apr_psprintf(pool, "%u.%u.%u", 937251881Speter (unsigned int)osinfo.dwMajorVersion, 938251881Speter (unsigned int)osinfo.dwMinorVersion, 939251881Speter (unsigned int)osinfo.dwBuildNumber); 940251881Speter 941251881Speter if (sysinfo.wProcessorArchitecture 942251881Speter == local_sysinfo.wProcessorArchitecture) 943251881Speter return apr_psprintf(pool, "%s-%s-%s%s", 944251881Speter machine, vendor, sysname, sysver); 945251881Speter return apr_psprintf(pool, "%s/%s-%s-%s%s", 946251881Speter arch, machine, vendor, sysname, sysver); 947251881Speter } 948251881Speter 949251881Speter return "unknown-microsoft-windows"; 950251881Speter} 951251881Speter 952251881Speter/* Convert a Unicode string to UTF-8. */ 953251881Speterstatic char * 954251881Speterwcs_to_utf8(const wchar_t *wcs, apr_pool_t *pool) 955251881Speter{ 956251881Speter const int bufsize = WideCharToMultiByte(CP_UTF8, 0, wcs, -1, 957251881Speter NULL, 0, NULL, NULL); 958251881Speter if (bufsize > 0) 959251881Speter { 960251881Speter char *const utf8 = apr_palloc(pool, bufsize + 1); 961251881Speter WideCharToMultiByte(CP_UTF8, 0, wcs, -1, utf8, bufsize, NULL, NULL); 962251881Speter return utf8; 963251881Speter } 964251881Speter return NULL; 965251881Speter} 966251881Speter 967251881Speter/* Query the value called NAME of the registry key HKEY. */ 968251881Speterstatic char * 969251881Speterregistry_value(HKEY hkey, wchar_t *name, apr_pool_t *pool) 970251881Speter{ 971251881Speter DWORD size; 972251881Speter wchar_t *value; 973251881Speter 974251881Speter if (RegQueryValueExW(hkey, name, NULL, NULL, NULL, &size)) 975251881Speter return NULL; 976251881Speter 977251881Speter value = apr_palloc(pool, size + sizeof *value); 978251881Speter if (RegQueryValueExW(hkey, name, NULL, NULL, (void*)value, &size)) 979251881Speter return NULL; 980251881Speter value[size / sizeof *value] = 0; 981251881Speter return wcs_to_utf8(value, pool); 982251881Speter} 983251881Speter 984251881Speter/* Try to glean the Windows release name and associated info from the 985251881Speter registry. Failing that, construct a release name from the version 986251881Speter info. */ 987251881Speterstatic const char * 988251881Speterwin32_release_name(apr_pool_t *pool) 989251881Speter{ 990251881Speter SYSTEM_INFO sysinfo; 991251881Speter OSVERSIONINFOEXW osinfo; 992251881Speter HKEY hkcv; 993251881Speter 994289180Speter if (!system_info(&sysinfo, NULL) 995289180Speter || !svn_sysinfo___fill_windows_version(&osinfo)) 996251881Speter return NULL; 997251881Speter 998251881Speter if (!RegOpenKeyExW(HKEY_LOCAL_MACHINE, 999251881Speter L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", 1000251881Speter 0, KEY_QUERY_VALUE, &hkcv)) 1001251881Speter { 1002251881Speter const char *release = registry_value(hkcv, L"ProductName", pool); 1003251881Speter const char *spack = registry_value(hkcv, L"CSDVersion", pool); 1004251881Speter const char *curver = registry_value(hkcv, L"CurrentVersion", pool); 1005251881Speter const char *curtype = registry_value(hkcv, L"CurrentType", pool); 1006251881Speter const char *install = registry_value(hkcv, L"InstallationType", pool); 1007251881Speter const char *curbuild = registry_value(hkcv, L"CurrentBuildNumber", pool); 1008251881Speter 1009251881Speter if (!spack && *osinfo.szCSDVersion) 1010251881Speter spack = wcs_to_utf8(osinfo.szCSDVersion, pool); 1011251881Speter 1012251881Speter if (!curbuild) 1013251881Speter curbuild = registry_value(hkcv, L"CurrentBuild", pool); 1014251881Speter 1015251881Speter if (release || spack || curver || curtype || curbuild) 1016251881Speter { 1017251881Speter const char *bootinfo = ""; 1018251881Speter if (curver || install || curtype) 1019251881Speter { 1020251881Speter bootinfo = apr_psprintf(pool, "[%s%s%s%s%s]", 1021251881Speter (curver ? curver : ""), 1022251881Speter (install ? (curver ? " " : "") : ""), 1023251881Speter (install ? install : ""), 1024251881Speter (curtype 1025251881Speter ? (curver||install ? " " : "") 1026251881Speter : ""), 1027251881Speter (curtype ? curtype : "")); 1028251881Speter } 1029251881Speter 1030251881Speter return apr_psprintf(pool, "%s%s%s%s%s%s%s", 1031251881Speter (release ? release : ""), 1032251881Speter (spack ? (release ? ", " : "") : ""), 1033251881Speter (spack ? spack : ""), 1034251881Speter (curbuild 1035251881Speter ? (release||spack ? ", build " : "build ") 1036251881Speter : ""), 1037251881Speter (curbuild ? curbuild : ""), 1038251881Speter (bootinfo 1039251881Speter ? (release||spack||curbuild ? " " : "") 1040251881Speter : ""), 1041251881Speter (bootinfo ? bootinfo : "")); 1042251881Speter } 1043251881Speter } 1044251881Speter 1045251881Speter if (*osinfo.szCSDVersion) 1046251881Speter { 1047251881Speter const char *servicepack = wcs_to_utf8(osinfo.szCSDVersion, pool); 1048251881Speter 1049251881Speter if (servicepack) 1050251881Speter return apr_psprintf(pool, "Windows NT %u.%u, %s, build %u", 1051251881Speter (unsigned int)osinfo.dwMajorVersion, 1052251881Speter (unsigned int)osinfo.dwMinorVersion, 1053251881Speter servicepack, 1054251881Speter (unsigned int)osinfo.dwBuildNumber); 1055251881Speter 1056251881Speter /* Assume wServicePackMajor > 0 if szCSDVersion is not empty */ 1057251881Speter if (osinfo.wServicePackMinor) 1058251881Speter return apr_psprintf(pool, "Windows NT %u.%u SP%u.%u, build %u", 1059251881Speter (unsigned int)osinfo.dwMajorVersion, 1060251881Speter (unsigned int)osinfo.dwMinorVersion, 1061251881Speter (unsigned int)osinfo.wServicePackMajor, 1062251881Speter (unsigned int)osinfo.wServicePackMinor, 1063251881Speter (unsigned int)osinfo.dwBuildNumber); 1064251881Speter 1065251881Speter return apr_psprintf(pool, "Windows NT %u.%u SP%u, build %u", 1066251881Speter (unsigned int)osinfo.dwMajorVersion, 1067251881Speter (unsigned int)osinfo.dwMinorVersion, 1068251881Speter (unsigned int)osinfo.wServicePackMajor, 1069251881Speter (unsigned int)osinfo.dwBuildNumber); 1070251881Speter } 1071251881Speter 1072251881Speter return apr_psprintf(pool, "Windows NT %u.%u, build %u", 1073251881Speter (unsigned int)osinfo.dwMajorVersion, 1074251881Speter (unsigned int)osinfo.dwMinorVersion, 1075251881Speter (unsigned int)osinfo.dwBuildNumber); 1076251881Speter} 1077251881Speter 1078251881Speter 1079251881Speter/* Get a list of handles of shared libs loaded by the current 1080251881Speter process. Returns a NULL-terminated array alocated from POOL. */ 1081251881Speterstatic HMODULE * 1082251881Speterenum_loaded_modules(apr_pool_t *pool) 1083251881Speter{ 1084257936Speter HMODULE psapi_dll = 0; 1085251881Speter HANDLE current = GetCurrentProcess(); 1086251881Speter HMODULE dummy[1]; 1087251881Speter HMODULE *handles; 1088251881Speter DWORD size; 1089257936Speter FNENUMPROCESSMODULES EnumProcessModules_; 1090251881Speter 1091362181Sdim psapi_dll = GetModuleHandleW(L"psapi.dll"); 1092257936Speter 1093257936Speter if (!psapi_dll) 1094257936Speter { 1095257936Speter /* Load and never unload, just like static linking */ 1096362181Sdim psapi_dll = LoadLibraryW(L"psapi.dll"); 1097257936Speter } 1098257936Speter 1099257936Speter if (!psapi_dll) 1100257936Speter return NULL; 1101257936Speter 1102257936Speter EnumProcessModules_ = (FNENUMPROCESSMODULES) 1103257936Speter GetProcAddress(psapi_dll, "EnumProcessModules"); 1104257936Speter 1105257936Speter /* Before Windows XP psapi was an optional module */ 1106257936Speter if (! EnumProcessModules_) 1107251881Speter return NULL; 1108251881Speter 1109257936Speter if (!EnumProcessModules_(current, dummy, sizeof(dummy), &size)) 1110257936Speter return NULL; 1111257936Speter 1112251881Speter handles = apr_palloc(pool, size + sizeof *handles); 1113257936Speter if (! EnumProcessModules_(current, handles, size, &size)) 1114251881Speter return NULL; 1115251881Speter handles[size / sizeof *handles] = NULL; 1116251881Speter return handles; 1117251881Speter} 1118251881Speter 1119251881Speter/* Find the version number, if any, embedded in FILENAME. */ 1120251881Speterstatic const char * 1121251881Speterfile_version_number(const wchar_t *filename, apr_pool_t *pool) 1122251881Speter{ 1123251881Speter VS_FIXEDFILEINFO info; 1124251881Speter unsigned int major, minor, micro, nano; 1125251881Speter void *data; 1126251881Speter DWORD data_size = GetFileVersionInfoSizeW(filename, NULL); 1127251881Speter void *vinfo; 1128251881Speter UINT vinfo_size; 1129251881Speter 1130251881Speter if (!data_size) 1131251881Speter return NULL; 1132251881Speter 1133251881Speter data = apr_palloc(pool, data_size); 1134251881Speter if (!GetFileVersionInfoW(filename, 0, data_size, data)) 1135251881Speter return NULL; 1136251881Speter 1137251881Speter if (!VerQueryValueW(data, L"\\", &vinfo, &vinfo_size)) 1138251881Speter return NULL; 1139251881Speter 1140251881Speter if (vinfo_size != sizeof info) 1141251881Speter return NULL; 1142251881Speter 1143251881Speter memcpy(&info, vinfo, sizeof info); 1144251881Speter major = (info.dwFileVersionMS >> 16) & 0xFFFF; 1145251881Speter minor = info.dwFileVersionMS & 0xFFFF; 1146251881Speter micro = (info.dwFileVersionLS >> 16) & 0xFFFF; 1147251881Speter nano = info.dwFileVersionLS & 0xFFFF; 1148251881Speter 1149251881Speter if (!nano) 1150251881Speter { 1151251881Speter if (!micro) 1152251881Speter return apr_psprintf(pool, "%u.%u", major, minor); 1153251881Speter else 1154251881Speter return apr_psprintf(pool, "%u.%u.%u", major, minor, micro); 1155251881Speter } 1156251881Speter return apr_psprintf(pool, "%u.%u.%u.%u", major, minor, micro, nano); 1157251881Speter} 1158251881Speter 1159251881Speter/* List the shared libraries loaded by the current process. */ 1160251881Speterstatic const apr_array_header_t * 1161251881Speterwin32_shared_libs(apr_pool_t *pool) 1162251881Speter{ 1163251881Speter apr_array_header_t *array = NULL; 1164251881Speter wchar_t buffer[MAX_PATH + 1]; 1165251881Speter HMODULE *handles = enum_loaded_modules(pool); 1166251881Speter HMODULE *module; 1167251881Speter 1168251881Speter for (module = handles; module && *module; ++module) 1169251881Speter { 1170251881Speter const char *filename; 1171251881Speter const char *version; 1172251881Speter if (GetModuleFileNameW(*module, buffer, MAX_PATH)) 1173251881Speter { 1174251881Speter buffer[MAX_PATH] = 0; 1175251881Speter 1176251881Speter version = file_version_number(buffer, pool); 1177251881Speter filename = wcs_to_utf8(buffer, pool); 1178251881Speter if (filename) 1179251881Speter { 1180251881Speter svn_version_ext_loaded_lib_t *lib; 1181251881Speter 1182251881Speter if (!array) 1183251881Speter { 1184251881Speter array = apr_array_make(pool, 32, sizeof(*lib)); 1185251881Speter } 1186251881Speter lib = &APR_ARRAY_PUSH(array, svn_version_ext_loaded_lib_t); 1187251881Speter lib->name = svn_dirent_local_style(filename, pool); 1188251881Speter lib->version = version; 1189251881Speter } 1190251881Speter } 1191251881Speter } 1192251881Speter 1193251881Speter return array; 1194251881Speter} 1195251881Speter#endif /* WIN32 */ 1196251881Speter 1197251881Speter 1198251881Speter#ifdef SVN_HAVE_MACOS_PLIST 1199289180Speter/* implements svn_write_fn_t to copy the data into a CFMutableDataRef that's 1200289180Speter * in the baton. */ 1201289180Speterstatic svn_error_t * 1202289180Speterwrite_to_cfmutabledata(void *baton, const char *data, apr_size_t *len) 1203289180Speter{ 1204289180Speter CFMutableDataRef *resource = (CFMutableDataRef *) baton; 1205289180Speter 1206289180Speter CFDataAppendBytes(*resource, (UInt8 *)data, *len); 1207289180Speter 1208289180Speter return SVN_NO_ERROR; 1209289180Speter} 1210289180Speter 1211251881Speter/* Load the SystemVersion.plist or ServerVersion.plist file into a 1212251881Speter property list. Set SERVER to TRUE if the file read was 1213251881Speter ServerVersion.plist. */ 1214251881Speterstatic CFDictionaryRef 1215251881Spetersystem_version_plist(svn_boolean_t *server, apr_pool_t *pool) 1216251881Speter{ 1217289180Speter static const char server_version[] = 1218251881Speter "/System/Library/CoreServices/ServerVersion.plist"; 1219289180Speter static const char system_version[] = 1220251881Speter "/System/Library/CoreServices/SystemVersion.plist"; 1221289180Speter svn_stream_t *read_stream, *write_stream; 1222289180Speter svn_error_t *err; 1223251881Speter CFPropertyListRef plist = NULL; 1224289180Speter CFMutableDataRef resource = CFDataCreateMutable(kCFAllocatorDefault, 0); 1225251881Speter 1226289180Speter /* failed getting the CFMutableDataRef, shouldn't happen */ 1227289180Speter if (!resource) 1228251881Speter return NULL; 1229251881Speter 1230289180Speter /* Try to open the plist files to get the data */ 1231289180Speter err = svn_stream_open_readonly(&read_stream, server_version, pool, pool); 1232289180Speter if (err) 1233251881Speter { 1234289180Speter if (!APR_STATUS_IS_ENOENT(err->apr_err)) 1235251881Speter { 1236289180Speter svn_error_clear(err); 1237289180Speter CFRelease(resource); 1238251881Speter return NULL; 1239251881Speter } 1240251881Speter else 1241251881Speter { 1242289180Speter svn_error_clear(err); 1243289180Speter err = svn_stream_open_readonly(&read_stream, system_version, 1244289180Speter pool, pool); 1245289180Speter if (err) 1246289180Speter { 1247289180Speter svn_error_clear(err); 1248289180Speter CFRelease(resource); 1249289180Speter return NULL; 1250289180Speter } 1251289180Speter 1252251881Speter *server = FALSE; 1253251881Speter } 1254251881Speter } 1255251881Speter else 1256251881Speter { 1257251881Speter *server = TRUE; 1258251881Speter } 1259251881Speter 1260289180Speter /* copy the data onto the CFMutableDataRef to allow us to provide it to 1261289180Speter * the CoreFoundation functions that parse proprerty lists */ 1262289180Speter write_stream = svn_stream_create(&resource, pool); 1263289180Speter svn_stream_set_write(write_stream, write_to_cfmutabledata); 1264289180Speter err = svn_stream_copy3(read_stream, write_stream, NULL, NULL, pool); 1265289180Speter if (err) 1266289180Speter { 1267289180Speter svn_error_clear(err); 1268289180Speter return NULL; 1269289180Speter } 1270289180Speter 1271289180Speter#if __MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 1272289180Speter /* This function is only available from Mac OS 10.6 onward. */ 1273289180Speter plist = CFPropertyListCreateWithData(kCFAllocatorDefault, resource, 1274289180Speter kCFPropertyListImmutable, 1275289180Speter NULL, NULL); 1276289180Speter#else /* Mac OS 10.5 or earlier */ 1277289180Speter /* This function obsolete and deprecated since Mac OS 10.10. */ 1278251881Speter plist = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, resource, 1279251881Speter kCFPropertyListImmutable, 1280289180Speter NULL); 1281289180Speter#endif /* MAC_OS_X_VERSION_10_6 */ 1282289180Speter 1283251881Speter if (resource) 1284251881Speter CFRelease(resource); 1285251881Speter 1286289180Speter if (!plist) 1287289180Speter return NULL; 1288289180Speter 1289251881Speter if (CFDictionaryGetTypeID() != CFGetTypeID(plist)) 1290251881Speter { 1291251881Speter /* Oops ... this really should be a dict. */ 1292251881Speter CFRelease(plist); 1293251881Speter return NULL; 1294251881Speter } 1295251881Speter 1296251881Speter return plist; 1297251881Speter} 1298251881Speter 1299251881Speter/* Return the value for KEY from PLIST, or NULL if not available. */ 1300251881Speterstatic const char * 1301251881Spetervalue_from_dict(CFDictionaryRef plist, CFStringRef key, apr_pool_t *pool) 1302251881Speter{ 1303251881Speter CFStringRef valref; 1304251881Speter CFIndex bufsize; 1305251881Speter const void *valptr; 1306251881Speter const char *value; 1307251881Speter 1308251881Speter if (!CFDictionaryGetValueIfPresent(plist, key, &valptr)) 1309251881Speter return NULL; 1310251881Speter 1311251881Speter valref = valptr; 1312251881Speter if (CFStringGetTypeID() != CFGetTypeID(valref)) 1313251881Speter return NULL; 1314251881Speter 1315251881Speter value = CFStringGetCStringPtr(valref, kCFStringEncodingUTF8); 1316251881Speter if (value) 1317251881Speter return apr_pstrdup(pool, value); 1318251881Speter 1319251881Speter bufsize = 5 * CFStringGetLength(valref) + 1; 1320251881Speter value = apr_palloc(pool, bufsize); 1321251881Speter if (!CFStringGetCString(valref, (char*)value, bufsize, 1322251881Speter kCFStringEncodingUTF8)) 1323251881Speter value = NULL; 1324251881Speter 1325251881Speter return value; 1326251881Speter} 1327251881Speter 1328362181Sdim/* Return the minor version the operating system, given the number in 1329251881Speter a format that matches the regular expression /^10\.\d+(\..*)?$/ */ 1330362181Sdimstatic int 1331362181Sdimmacos_minor_version(const char *osver) 1332251881Speter{ 1333251881Speter char *end = NULL; 1334251881Speter unsigned long num = strtoul(osver, &end, 10); 1335251881Speter 1336251881Speter if (!end || *end != '.' || num != 10) 1337362181Sdim return -1; 1338251881Speter 1339251881Speter osver = end + 1; 1340251881Speter end = NULL; 1341251881Speter num = strtoul(osver, &end, 10); 1342251881Speter if (!end || (*end && *end != '.')) 1343362181Sdim return -1; 1344251881Speter 1345362181Sdim return (int)num; 1346362181Sdim} 1347362181Sdim 1348362181Sdim/* Return the product name of the operating system. */ 1349362181Sdimstatic const char * 1350362181Sdimproduct_name_from_minor_version(int minor, const char* product_name) 1351362181Sdim{ 1352362181Sdim /* We can only do this if we know the official product name. */ 1353362181Sdim if (0 != strcmp(product_name, "Mac OS X")) 1354362181Sdim return product_name; 1355362181Sdim 1356362181Sdim if (minor <= 7) 1357362181Sdim return product_name; 1358362181Sdim 1359362181Sdim if (minor <= 11) 1360362181Sdim return "OS X"; 1361362181Sdim 1362362181Sdim return "macOS"; 1363362181Sdim} 1364362181Sdim 1365362181Sdim/* Return the commercial name of the operating system. */ 1366362181Sdimstatic const char * 1367362181Sdimrelease_name_from_minor_version(int minor, const char* product_name) 1368362181Sdim{ 1369362181Sdim /* We can only do this if we know the official product name. */ 1370362181Sdim if (0 == strcmp(product_name, "Mac OS X")) 1371251881Speter { 1372362181Sdim /* See https://en.wikipedia.org/wiki/MacOS_version_history#Releases */ 1373362181Sdim switch(minor) 1374362181Sdim { 1375362181Sdim case 0: return "Cheetah"; 1376362181Sdim case 1: return "Puma"; 1377362181Sdim case 2: return "Jaguar"; 1378362181Sdim case 3: return "Panther"; 1379362181Sdim case 4: return "Tiger"; 1380362181Sdim case 5: return "Leopard"; 1381362181Sdim case 6: return "Snow Leopard"; 1382362181Sdim case 7: return "Lion"; 1383362181Sdim case 8: return "Mountain Lion"; 1384362181Sdim case 9: return "Mavericks"; 1385362181Sdim case 10: return "Yosemite"; 1386362181Sdim case 11: return "El Capitan"; 1387362181Sdim case 12: return "Sierra"; 1388362181Sdim case 13: return "High Sierra"; 1389362181Sdim case 14: return "Mojave"; 1390362181Sdim case 15: return "Catalina"; 1391362181Sdim } 1392251881Speter } 1393251881Speter return NULL; 1394251881Speter} 1395251881Speter 1396251881Speter/* Construct the release name from information stored in the Mac OS X 1397251881Speter "SystemVersion.plist" file (or ServerVersion.plist, for Mac Os 1398251881Speter Server. */ 1399251881Speterstatic const char * 1400251881Spetermacos_release_name(apr_pool_t *pool) 1401251881Speter{ 1402251881Speter svn_boolean_t server; 1403251881Speter CFDictionaryRef plist = system_version_plist(&server, pool); 1404251881Speter 1405251881Speter if (plist) 1406251881Speter { 1407251881Speter const char *osname = value_from_dict(plist, CFSTR("ProductName"), pool); 1408251881Speter const char *osver = value_from_dict(plist, 1409251881Speter CFSTR("ProductUserVisibleVersion"), 1410251881Speter pool); 1411251881Speter const char *build = value_from_dict(plist, 1412251881Speter CFSTR("ProductBuildVersion"), 1413251881Speter pool); 1414251881Speter const char *release; 1415362181Sdim int minor_version; 1416251881Speter 1417251881Speter if (!osver) 1418251881Speter osver = value_from_dict(plist, CFSTR("ProductVersion"), pool); 1419362181Sdim minor_version = macos_minor_version(osver); 1420362181Sdim release = release_name_from_minor_version(minor_version, osname); 1421362181Sdim osname = product_name_from_minor_version(minor_version, osname); 1422251881Speter 1423251881Speter CFRelease(plist); 1424251881Speter return apr_psprintf(pool, "%s%s%s%s%s%s%s%s", 1425251881Speter (osname ? osname : ""), 1426362181Sdim (release ? (osname ? " " : "") : ""), 1427362181Sdim (release ? release : ""), 1428362181Sdim (osver ? (osname||release ? " " : "") : ""), 1429251881Speter (osver ? osver : ""), 1430251881Speter (build 1431362181Sdim ? (osname||release||osver ? ", " : "") 1432251881Speter : ""), 1433251881Speter (build 1434251881Speter ? (server ? "server build " : "build ") 1435251881Speter : ""), 1436251881Speter (build ? build : "")); 1437251881Speter } 1438251881Speter 1439251881Speter return NULL; 1440251881Speter} 1441251881Speter#endif /* SVN_HAVE_MACOS_PLIST */ 1442251881Speter 1443251881Speter#ifdef SVN_HAVE_MACHO_ITERATE 1444251881Speter/* List the shared libraries loaded by the current process. 1445251881Speter Ignore frameworks and system libraries, they're just clutter. */ 1446251881Speterstatic const apr_array_header_t * 1447251881Spetermacos_shared_libs(apr_pool_t *pool) 1448251881Speter{ 1449251881Speter static const char slb_prefix[] = "/usr/lib/system/"; 1450251881Speter static const char fwk_prefix[] = "/System/Library/Frameworks/"; 1451251881Speter static const char pfk_prefix[] = "/System/Library/PrivateFrameworks/"; 1452251881Speter 1453251881Speter const size_t slb_prefix_len = strlen(slb_prefix); 1454251881Speter const size_t fwk_prefix_len = strlen(fwk_prefix); 1455251881Speter const size_t pfk_prefix_len = strlen(pfk_prefix); 1456251881Speter 1457251881Speter apr_array_header_t *result = NULL; 1458251881Speter apr_array_header_t *dylibs = NULL; 1459251881Speter 1460251881Speter uint32_t i; 1461251881Speter for (i = 0;; ++i) 1462251881Speter { 1463251881Speter const struct mach_header *header = _dyld_get_image_header(i); 1464251881Speter const char *filename = _dyld_get_image_name(i); 1465251881Speter const char *version; 1466251881Speter char *truename; 1467251881Speter svn_version_ext_loaded_lib_t *lib; 1468251881Speter 1469251881Speter if (!(header && filename)) 1470251881Speter break; 1471251881Speter 1472251881Speter switch (header->cputype) 1473251881Speter { 1474251881Speter case CPU_TYPE_I386: version = _("Intel"); break; 1475251881Speter case CPU_TYPE_X86_64: version = _("Intel 64-bit"); break; 1476251881Speter case CPU_TYPE_POWERPC: version = _("PowerPC"); break; 1477251881Speter case CPU_TYPE_POWERPC64: version = _("PowerPC 64-bit"); break; 1478251881Speter default: 1479251881Speter version = NULL; 1480251881Speter } 1481251881Speter 1482251881Speter if (0 == apr_filepath_merge(&truename, "", filename, 1483251881Speter APR_FILEPATH_NATIVE 1484251881Speter | APR_FILEPATH_TRUENAME, 1485251881Speter pool)) 1486251881Speter filename = truename; 1487251881Speter else 1488251881Speter filename = apr_pstrdup(pool, filename); 1489251881Speter 1490251881Speter if (0 == strncmp(filename, slb_prefix, slb_prefix_len) 1491251881Speter || 0 == strncmp(filename, fwk_prefix, fwk_prefix_len) 1492251881Speter || 0 == strncmp(filename, pfk_prefix, pfk_prefix_len)) 1493251881Speter { 1494251881Speter /* Ignore frameworks and system libraries. */ 1495251881Speter continue; 1496251881Speter } 1497251881Speter 1498251881Speter if (header->filetype == MH_EXECUTE) 1499251881Speter { 1500251881Speter /* Make sure the program filename is first in the list */ 1501251881Speter if (!result) 1502251881Speter { 1503251881Speter result = apr_array_make(pool, 32, sizeof(*lib)); 1504251881Speter } 1505251881Speter lib = &APR_ARRAY_PUSH(result, svn_version_ext_loaded_lib_t); 1506251881Speter } 1507251881Speter else 1508251881Speter { 1509251881Speter if (!dylibs) 1510251881Speter { 1511251881Speter dylibs = apr_array_make(pool, 32, sizeof(*lib)); 1512251881Speter } 1513251881Speter lib = &APR_ARRAY_PUSH(dylibs, svn_version_ext_loaded_lib_t); 1514251881Speter } 1515251881Speter 1516251881Speter lib->name = filename; 1517251881Speter lib->version = version; 1518251881Speter } 1519251881Speter 1520251881Speter /* Gather results into one array. */ 1521251881Speter if (dylibs) 1522251881Speter { 1523251881Speter if (result) 1524251881Speter apr_array_cat(result, dylibs); 1525251881Speter else 1526251881Speter result = dylibs; 1527251881Speter } 1528251881Speter 1529251881Speter return result; 1530251881Speter} 1531251881Speter#endif /* SVN_HAVE_MACHO_ITERATE */ 1532