| /* |
| * sysinfo.c : information about the running system |
| * |
| * ==================================================================== |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * ==================================================================== |
| */ |
| |
| |
| |
| #ifdef WIN32 |
| #define WIN32_LEAN_AND_MEAN |
| #define PSAPI_VERSION 1 |
| #include <windows.h> |
| #include <psapi.h> |
| #include <Ws2tcpip.h> |
| #endif |
| |
| #define APR_WANT_STRFUNC |
| #include <apr_want.h> |
| |
| #include <apr_lib.h> |
| #include <apr_pools.h> |
| #include <apr_file_info.h> |
| #include <apr_signal.h> |
| #include <apr_strings.h> |
| #include <apr_thread_proc.h> |
| #include <apr_version.h> |
| #include <apu_version.h> |
| |
| #include "svn_pools.h" |
| #include "svn_ctype.h" |
| #include "svn_dirent_uri.h" |
| #include "svn_error.h" |
| #include "svn_io.h" |
| #include "svn_string.h" |
| #include "svn_utf.h" |
| #include "svn_version.h" |
| |
| #include "private/svn_sqlite.h" |
| |
| #include "sysinfo.h" |
| #include "svn_private_config.h" |
| |
| #if HAVE_SYS_UTSNAME_H |
| #include <sys/utsname.h> |
| #endif |
| |
| #ifdef SVN_HAVE_MACOS_PLIST |
| #include <CoreFoundation/CoreFoundation.h> |
| #endif |
| |
| #ifdef SVN_HAVE_MACHO_ITERATE |
| #include <mach-o/dyld.h> |
| #include <mach-o/loader.h> |
| #endif |
| |
| #if HAVE_UNAME |
| static const char *canonical_host_from_uname(apr_pool_t *pool); |
| # ifndef SVN_HAVE_MACOS_PLIST |
| static const char *release_name_from_uname(apr_pool_t *pool); |
| # endif |
| #endif |
| |
| #ifdef WIN32 |
| static const char *win32_canonical_host(apr_pool_t *pool); |
| static const char *win32_release_name(apr_pool_t *pool); |
| static const apr_array_header_t *win32_shared_libs(apr_pool_t *pool); |
| #endif /* WIN32 */ |
| |
| #ifdef SVN_HAVE_MACOS_PLIST |
| static const char *macos_release_name(apr_pool_t *pool); |
| #endif |
| |
| #ifdef SVN_HAVE_MACHO_ITERATE |
| static const apr_array_header_t *macos_shared_libs(apr_pool_t *pool); |
| #endif |
| |
| |
| #if __linux__ |
| static const char *linux_release_name(apr_pool_t *pool); |
| #endif |
| |
| const char * |
| svn_sysinfo__canonical_host(apr_pool_t *pool) |
| { |
| #ifdef WIN32 |
| return win32_canonical_host(pool); |
| #elif HAVE_UNAME |
| return canonical_host_from_uname(pool); |
| #else |
| return "unknown-unknown-unknown"; |
| #endif |
| } |
| |
| |
| const char * |
| svn_sysinfo__release_name(apr_pool_t *pool) |
| { |
| #ifdef WIN32 |
| return win32_release_name(pool); |
| #elif defined(SVN_HAVE_MACOS_PLIST) |
| return macos_release_name(pool); |
| #elif __linux__ |
| return linux_release_name(pool); |
| #elif HAVE_UNAME |
| return release_name_from_uname(pool); |
| #else |
| return NULL; |
| #endif |
| } |
| |
| const apr_array_header_t * |
| svn_sysinfo__linked_libs(apr_pool_t *pool) |
| { |
| svn_version_ext_linked_lib_t *lib; |
| apr_array_header_t *array = apr_array_make(pool, 3, sizeof(*lib)); |
| |
| lib = &APR_ARRAY_PUSH(array, svn_version_ext_linked_lib_t); |
| lib->name = "APR"; |
| lib->compiled_version = APR_VERSION_STRING; |
| lib->runtime_version = apr_pstrdup(pool, apr_version_string()); |
| |
| /* Don't list APR-Util if it isn't linked in, which it may not be if |
| * we're using APR 2.x+ which combined APR-Util into APR. */ |
| #ifdef APU_VERSION_STRING |
| lib = &APR_ARRAY_PUSH(array, svn_version_ext_linked_lib_t); |
| lib->name = "APR-Util"; |
| lib->compiled_version = APU_VERSION_STRING; |
| lib->runtime_version = apr_pstrdup(pool, apu_version_string()); |
| #endif |
| |
| lib = &APR_ARRAY_PUSH(array, svn_version_ext_linked_lib_t); |
| lib->name = "SQLite"; |
| lib->compiled_version = apr_pstrdup(pool, svn_sqlite__compiled_version()); |
| #ifdef SVN_SQLITE_INLINE |
| lib->runtime_version = NULL; |
| #else |
| lib->runtime_version = apr_pstrdup(pool, svn_sqlite__runtime_version()); |
| #endif |
| |
| return array; |
| } |
| |
| const apr_array_header_t * |
| svn_sysinfo__loaded_libs(apr_pool_t *pool) |
| { |
| #ifdef WIN32 |
| return win32_shared_libs(pool); |
| #elif defined(SVN_HAVE_MACHO_ITERATE) |
| return macos_shared_libs(pool); |
| #else |
| return NULL; |
| #endif |
| } |
| |
| |
| #if HAVE_UNAME |
| static const char* |
| canonical_host_from_uname(apr_pool_t *pool) |
| { |
| const char *machine = "unknown"; |
| const char *vendor = "unknown"; |
| const char *sysname = "unknown"; |
| const char *sysver = ""; |
| struct utsname info; |
| |
| if (0 <= uname(&info)) |
| { |
| svn_error_t *err; |
| const char *tmp; |
| |
| err = svn_utf_cstring_to_utf8(&tmp, info.machine, pool); |
| if (err) |
| svn_error_clear(err); |
| else |
| machine = tmp; |
| |
| err = svn_utf_cstring_to_utf8(&tmp, info.sysname, pool); |
| if (err) |
| svn_error_clear(err); |
| else |
| { |
| char *lwr = apr_pstrdup(pool, tmp); |
| char *it = lwr; |
| while (*it) |
| { |
| if (svn_ctype_isupper(*it)) |
| *it = apr_tolower(*it); |
| ++it; |
| } |
| sysname = lwr; |
| } |
| |
| if (0 == strcmp(sysname, "darwin")) |
| vendor = "apple"; |
| if (0 == strcmp(sysname, "linux")) |
| sysver = "-gnu"; |
| else |
| { |
| err = svn_utf_cstring_to_utf8(&tmp, info.release, pool); |
| if (err) |
| svn_error_clear(err); |
| else |
| { |
| apr_size_t n = strspn(tmp, ".0123456789"); |
| if (n > 0) |
| { |
| char *ver = apr_pstrdup(pool, tmp); |
| ver[n] = 0; |
| sysver = ver; |
| } |
| else |
| sysver = tmp; |
| } |
| } |
| } |
| |
| return apr_psprintf(pool, "%s-%s-%s%s", machine, vendor, sysname, sysver); |
| } |
| |
| # ifndef SVN_HAVE_MACOS_PLIST |
| /* Generate a release name from the uname(3) info, effectively |
| returning "`uname -s` `uname -r`". */ |
| static const char * |
| release_name_from_uname(apr_pool_t *pool) |
| { |
| struct utsname info; |
| if (0 <= uname(&info)) |
| { |
| svn_error_t *err; |
| const char *sysname; |
| const char *sysver; |
| |
| err = svn_utf_cstring_to_utf8(&sysname, info.sysname, pool); |
| if (err) |
| { |
| sysname = NULL; |
| svn_error_clear(err); |
| } |
| |
| |
| err = svn_utf_cstring_to_utf8(&sysver, info.release, pool); |
| if (err) |
| { |
| sysver = NULL; |
| svn_error_clear(err); |
| } |
| |
| if (sysname || sysver) |
| { |
| return apr_psprintf(pool, "%s%s%s", |
| (sysname ? sysname : ""), |
| (sysver ? (sysname ? " " : "") : ""), |
| (sysver ? sysver : "")); |
| } |
| } |
| return NULL; |
| } |
| # endif /* !SVN_HAVE_MACOS_PLIST */ |
| #endif /* HAVE_UNAME */ |
| |
| |
| #if __linux__ |
| /* Split a stringbuf into a key/value pair. |
| Return the key, leaving the striped value in the stringbuf. */ |
| static const char * |
| stringbuf_split_key(svn_stringbuf_t *buffer, char delim) |
| { |
| char *key; |
| char *end; |
| |
| end = strchr(buffer->data, delim); |
| if (!end) |
| return NULL; |
| |
| svn_stringbuf_strip_whitespace(buffer); |
| |
| /* Now we split the currently allocated buffer in two parts: |
| - a const char * HEAD |
| - the remaining stringbuf_t. */ |
| |
| /* Create HEAD as '\0' terminated const char * */ |
| key = buffer->data; |
| end = strchr(key, delim); |
| *end = '\0'; |
| |
| /* And update the TAIL to be a smaller, but still valid stringbuf */ |
| buffer->data = end + 1; |
| buffer->len -= 1 + end - key; |
| buffer->blocksize -= 1 + end - key; |
| |
| svn_stringbuf_strip_whitespace(buffer); |
| |
| return key; |
| } |
| |
| /* Parse `/usr/bin/lsb_rlease --all` */ |
| static const char * |
| lsb_release(apr_pool_t *pool) |
| { |
| static const char *const args[3] = |
| { |
| "/usr/bin/lsb_release", |
| "--all", |
| NULL |
| }; |
| |
| const char *distributor = NULL; |
| const char *description = NULL; |
| const char *release = NULL; |
| const char *codename = NULL; |
| |
| apr_proc_t lsbproc; |
| svn_stream_t *lsbinfo; |
| svn_error_t *err; |
| |
| /* Run /usr/bin/lsb_release --all < /dev/null 2>/dev/null */ |
| { |
| apr_file_t *stdin_handle; |
| apr_file_t *stdout_handle; |
| |
| err = svn_io_file_open(&stdin_handle, SVN_NULL_DEVICE_NAME, |
| APR_READ, APR_OS_DEFAULT, pool); |
| if (!err) |
| err = svn_io_file_open(&stdout_handle, SVN_NULL_DEVICE_NAME, |
| APR_WRITE, APR_OS_DEFAULT, pool); |
| if (!err) |
| err = svn_io_start_cmd3(&lsbproc, NULL, args[0], args, NULL, FALSE, |
| FALSE, stdin_handle, |
| TRUE, NULL, |
| FALSE, stdout_handle, |
| pool); |
| if (err) |
| { |
| svn_error_clear(err); |
| return NULL; |
| } |
| } |
| |
| /* Parse the output and try to populate the */ |
| lsbinfo = svn_stream_from_aprfile2(lsbproc.out, TRUE, pool); |
| if (lsbinfo) |
| { |
| for (;;) |
| { |
| svn_boolean_t eof = FALSE; |
| svn_stringbuf_t *line; |
| const char *key; |
| |
| err = svn_stream_readline(lsbinfo, &line, "\n", &eof, pool); |
| if (err || eof) |
| break; |
| |
| key = stringbuf_split_key(line, ':'); |
| if (!key) |
| continue; |
| |
| if (0 == svn_cstring_casecmp(key, "Distributor ID")) |
| distributor = line->data; |
| else if (0 == svn_cstring_casecmp(key, "Description")) |
| description = line->data; |
| else if (0 == svn_cstring_casecmp(key, "Release")) |
| release = line->data; |
| else if (0 == svn_cstring_casecmp(key, "Codename")) |
| codename = line->data; |
| } |
| err = svn_error_compose_create(err, |
| svn_stream_close(lsbinfo)); |
| if (err) |
| { |
| svn_error_clear(err); |
| apr_proc_kill(&lsbproc, SIGKILL); |
| return NULL; |
| } |
| } |
| |
| /* Reap the child process */ |
| err = svn_io_wait_for_cmd(&lsbproc, "", NULL, NULL, pool); |
| if (err) |
| { |
| svn_error_clear(err); |
| return NULL; |
| } |
| |
| if (description) |
| return apr_psprintf(pool, "%s%s%s%s", description, |
| (codename ? " (" : ""), |
| (codename ? codename : ""), |
| (codename ? ")" : "")); |
| if (distributor) |
| return apr_psprintf(pool, "%s%s%s%s%s%s", distributor, |
| (release ? " " : ""), |
| (release ? release : ""), |
| (codename ? " (" : ""), |
| (codename ? codename : ""), |
| (codename ? ")" : "")); |
| |
| return NULL; |
| } |
| |
| /* Read the whole contents of a file. */ |
| static svn_stringbuf_t * |
| read_file_contents(const char *filename, apr_pool_t *pool) |
| { |
| svn_error_t *err; |
| svn_stringbuf_t *buffer; |
| |
| err = svn_stringbuf_from_file2(&buffer, filename, pool); |
| if (err) |
| { |
| svn_error_clear(err); |
| return NULL; |
| } |
| |
| return buffer; |
| } |
| |
| /* Strip everything but the first line from a stringbuf. */ |
| static void |
| stringbuf_first_line_only(svn_stringbuf_t *buffer) |
| { |
| char *eol = strchr(buffer->data, '\n'); |
| if (eol) |
| { |
| *eol = '\0'; |
| buffer->len = 1 + eol - buffer->data; |
| } |
| svn_stringbuf_strip_whitespace(buffer); |
| } |
| |
| /* Look at /etc/redhat_release to detect RHEL/Fedora/CentOS. */ |
| static const char * |
| redhat_release(apr_pool_t *pool) |
| { |
| svn_stringbuf_t *buffer = read_file_contents("/etc/redhat-release", pool); |
| if (buffer) |
| { |
| stringbuf_first_line_only(buffer); |
| return buffer->data; |
| } |
| return NULL; |
| } |
| |
| /* Look at /etc/SuSE-release to detect non-LSB SuSE. */ |
| static const char * |
| suse_release(apr_pool_t *pool) |
| { |
| const char *release = NULL; |
| const char *codename = NULL; |
| |
| svn_stringbuf_t *buffer = read_file_contents("/etc/SuSE-release", pool); |
| svn_stringbuf_t *line; |
| svn_stream_t *stream; |
| svn_boolean_t eof; |
| svn_error_t *err; |
| if (!buffer) |
| return NULL; |
| |
| stream = svn_stream_from_stringbuf(buffer, pool); |
| err = svn_stream_readline(stream, &line, "\n", &eof, pool); |
| if (err || eof) |
| { |
| svn_error_clear(err); |
| return NULL; |
| } |
| |
| svn_stringbuf_strip_whitespace(line); |
| release = line->data; |
| |
| for (;;) |
| { |
| const char *key; |
| |
| err = svn_stream_readline(stream, &line, "\n", &eof, pool); |
| if (err || eof) |
| { |
| svn_error_clear(err); |
| break; |
| } |
| |
| key = stringbuf_split_key(line, '='); |
| if (!key) |
| continue; |
| |
| if (0 == strncmp(key, "CODENAME", 8)) |
| codename = line->data; |
| } |
| |
| return apr_psprintf(pool, "%s%s%s%s", |
| release, |
| (codename ? " (" : ""), |
| (codename ? codename : ""), |
| (codename ? ")" : "")); |
| } |
| |
| /* Look at /etc/debian_version to detect non-LSB Debian. */ |
| static const char * |
| debian_release(apr_pool_t *pool) |
| { |
| svn_stringbuf_t *buffer = read_file_contents("/etc/debian_version", pool); |
| if (!buffer) |
| return NULL; |
| |
| stringbuf_first_line_only(buffer); |
| return apr_pstrcat(pool, "Debian ", buffer->data, NULL); |
| } |
| |
| /* Try to find the Linux distribution name, or return info from uname. */ |
| static const char * |
| linux_release_name(apr_pool_t *pool) |
| { |
| const char *uname_release = release_name_from_uname(pool); |
| |
| /* Try anything that has /usr/bin/lsb_release. |
| Covers, for example, Debian, Ubuntu and SuSE. */ |
| const char *release_name = lsb_release(pool); |
| |
| /* Try RHEL/Fedora/CentOS */ |
| if (!release_name) |
| release_name = redhat_release(pool); |
| |
| /* Try Non-LSB SuSE */ |
| if (!release_name) |
| release_name = suse_release(pool); |
| |
| /* Try non-LSB Debian */ |
| if (!release_name) |
| release_name = debian_release(pool); |
| |
| if (!release_name) |
| return uname_release; |
| |
| if (!uname_release) |
| return release_name; |
| |
| return apr_psprintf(pool, "%s [%s]", release_name, uname_release); |
| } |
| #endif /* __linux__ */ |
| |
| |
| #ifdef WIN32 |
| typedef DWORD (WINAPI *FNGETNATIVESYSTEMINFO)(LPSYSTEM_INFO); |
| typedef BOOL (WINAPI *FNENUMPROCESSMODULES) (HANDLE, HMODULE*, DWORD, LPDWORD); |
| |
| /* Get system and version info, and try to tell the difference |
| between the native system type and the runtime environment of the |
| current process. Populate results in SYSINFO, LOCAL_SYSINFO |
| (optional) and OSINFO. */ |
| static BOOL |
| system_info(SYSTEM_INFO *sysinfo, |
| SYSTEM_INFO *local_sysinfo, |
| OSVERSIONINFOEXW *osinfo) |
| { |
| FNGETNATIVESYSTEMINFO GetNativeSystemInfo_ = (FNGETNATIVESYSTEMINFO) |
| GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetNativeSystemInfo"); |
| |
| ZeroMemory(sysinfo, sizeof *sysinfo); |
| if (local_sysinfo) |
| { |
| ZeroMemory(local_sysinfo, sizeof *local_sysinfo); |
| GetSystemInfo(local_sysinfo); |
| if (GetNativeSystemInfo_) |
| GetNativeSystemInfo_(sysinfo); |
| else |
| memcpy(sysinfo, local_sysinfo, sizeof *sysinfo); |
| } |
| else |
| GetSystemInfo(sysinfo); |
| |
| ZeroMemory(osinfo, sizeof *osinfo); |
| osinfo->dwOSVersionInfoSize = sizeof *osinfo; |
| if (!GetVersionExW((LPVOID)osinfo)) |
| return FALSE; |
| |
| return TRUE; |
| } |
| |
| /* Map the proccessor type from SYSINFO to a string. */ |
| static const char * |
| processor_name(SYSTEM_INFO *sysinfo) |
| { |
| switch (sysinfo->wProcessorArchitecture) |
| { |
| case PROCESSOR_ARCHITECTURE_AMD64: return "x86_64"; |
| case PROCESSOR_ARCHITECTURE_IA64: return "ia64"; |
| case PROCESSOR_ARCHITECTURE_INTEL: return "x86"; |
| case PROCESSOR_ARCHITECTURE_MIPS: return "mips"; |
| case PROCESSOR_ARCHITECTURE_ALPHA: return "alpha32"; |
| case PROCESSOR_ARCHITECTURE_PPC: return "powerpc"; |
| case PROCESSOR_ARCHITECTURE_SHX: return "shx"; |
| case PROCESSOR_ARCHITECTURE_ARM: return "arm"; |
| case PROCESSOR_ARCHITECTURE_ALPHA64: return "alpha"; |
| case PROCESSOR_ARCHITECTURE_MSIL: return "msil"; |
| case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64: return "x86_wow64"; |
| default: return "unknown"; |
| } |
| } |
| |
| /* Return the Windows-specific canonical host name. */ |
| static const char * |
| win32_canonical_host(apr_pool_t *pool) |
| { |
| SYSTEM_INFO sysinfo; |
| SYSTEM_INFO local_sysinfo; |
| OSVERSIONINFOEXW osinfo; |
| |
| if (system_info(&sysinfo, &local_sysinfo, &osinfo)) |
| { |
| const char *arch = processor_name(&local_sysinfo); |
| const char *machine = processor_name(&sysinfo); |
| const char *vendor = "microsoft"; |
| const char *sysname = "windows"; |
| const char *sysver = apr_psprintf(pool, "%u.%u.%u", |
| (unsigned int)osinfo.dwMajorVersion, |
| (unsigned int)osinfo.dwMinorVersion, |
| (unsigned int)osinfo.dwBuildNumber); |
| |
| if (sysinfo.wProcessorArchitecture |
| == local_sysinfo.wProcessorArchitecture) |
| return apr_psprintf(pool, "%s-%s-%s%s", |
| machine, vendor, sysname, sysver); |
| return apr_psprintf(pool, "%s/%s-%s-%s%s", |
| arch, machine, vendor, sysname, sysver); |
| } |
| |
| return "unknown-microsoft-windows"; |
| } |
| |
| /* Convert a Unicode string to UTF-8. */ |
| static char * |
| wcs_to_utf8(const wchar_t *wcs, apr_pool_t *pool) |
| { |
| const int bufsize = WideCharToMultiByte(CP_UTF8, 0, wcs, -1, |
| NULL, 0, NULL, NULL); |
| if (bufsize > 0) |
| { |
| char *const utf8 = apr_palloc(pool, bufsize + 1); |
| WideCharToMultiByte(CP_UTF8, 0, wcs, -1, utf8, bufsize, NULL, NULL); |
| return utf8; |
| } |
| return NULL; |
| } |
| |
| /* Query the value called NAME of the registry key HKEY. */ |
| static char * |
| registry_value(HKEY hkey, wchar_t *name, apr_pool_t *pool) |
| { |
| DWORD size; |
| wchar_t *value; |
| |
| if (RegQueryValueExW(hkey, name, NULL, NULL, NULL, &size)) |
| return NULL; |
| |
| value = apr_palloc(pool, size + sizeof *value); |
| if (RegQueryValueExW(hkey, name, NULL, NULL, (void*)value, &size)) |
| return NULL; |
| value[size / sizeof *value] = 0; |
| return wcs_to_utf8(value, pool); |
| } |
| |
| /* Try to glean the Windows release name and associated info from the |
| registry. Failing that, construct a release name from the version |
| info. */ |
| static const char * |
| win32_release_name(apr_pool_t *pool) |
| { |
| SYSTEM_INFO sysinfo; |
| OSVERSIONINFOEXW osinfo; |
| HKEY hkcv; |
| |
| if (!system_info(&sysinfo, NULL, &osinfo)) |
| return NULL; |
| |
| if (!RegOpenKeyExW(HKEY_LOCAL_MACHINE, |
| L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", |
| 0, KEY_QUERY_VALUE, &hkcv)) |
| { |
| const char *release = registry_value(hkcv, L"ProductName", pool); |
| const char *spack = registry_value(hkcv, L"CSDVersion", pool); |
| const char *curver = registry_value(hkcv, L"CurrentVersion", pool); |
| const char *curtype = registry_value(hkcv, L"CurrentType", pool); |
| const char *install = registry_value(hkcv, L"InstallationType", pool); |
| const char *curbuild = registry_value(hkcv, L"CurrentBuildNumber", pool); |
| |
| if (!spack && *osinfo.szCSDVersion) |
| spack = wcs_to_utf8(osinfo.szCSDVersion, pool); |
| |
| if (!curbuild) |
| curbuild = registry_value(hkcv, L"CurrentBuild", pool); |
| |
| if (release || spack || curver || curtype || curbuild) |
| { |
| const char *bootinfo = ""; |
| if (curver || install || curtype) |
| { |
| bootinfo = apr_psprintf(pool, "[%s%s%s%s%s]", |
| (curver ? curver : ""), |
| (install ? (curver ? " " : "") : ""), |
| (install ? install : ""), |
| (curtype |
| ? (curver||install ? " " : "") |
| : ""), |
| (curtype ? curtype : "")); |
| } |
| |
| return apr_psprintf(pool, "%s%s%s%s%s%s%s", |
| (release ? release : ""), |
| (spack ? (release ? ", " : "") : ""), |
| (spack ? spack : ""), |
| (curbuild |
| ? (release||spack ? ", build " : "build ") |
| : ""), |
| (curbuild ? curbuild : ""), |
| (bootinfo |
| ? (release||spack||curbuild ? " " : "") |
| : ""), |
| (bootinfo ? bootinfo : "")); |
| } |
| } |
| |
| if (*osinfo.szCSDVersion) |
| { |
| const char *servicepack = wcs_to_utf8(osinfo.szCSDVersion, pool); |
| |
| if (servicepack) |
| return apr_psprintf(pool, "Windows NT %u.%u, %s, build %u", |
| (unsigned int)osinfo.dwMajorVersion, |
| (unsigned int)osinfo.dwMinorVersion, |
| servicepack, |
| (unsigned int)osinfo.dwBuildNumber); |
| |
| /* Assume wServicePackMajor > 0 if szCSDVersion is not empty */ |
| if (osinfo.wServicePackMinor) |
| return apr_psprintf(pool, "Windows NT %u.%u SP%u.%u, build %u", |
| (unsigned int)osinfo.dwMajorVersion, |
| (unsigned int)osinfo.dwMinorVersion, |
| (unsigned int)osinfo.wServicePackMajor, |
| (unsigned int)osinfo.wServicePackMinor, |
| (unsigned int)osinfo.dwBuildNumber); |
| |
| return apr_psprintf(pool, "Windows NT %u.%u SP%u, build %u", |
| (unsigned int)osinfo.dwMajorVersion, |
| (unsigned int)osinfo.dwMinorVersion, |
| (unsigned int)osinfo.wServicePackMajor, |
| (unsigned int)osinfo.dwBuildNumber); |
| } |
| |
| return apr_psprintf(pool, "Windows NT %u.%u, build %u", |
| (unsigned int)osinfo.dwMajorVersion, |
| (unsigned int)osinfo.dwMinorVersion, |
| (unsigned int)osinfo.dwBuildNumber); |
| } |
| |
| |
| /* Get a list of handles of shared libs loaded by the current |
| process. Returns a NULL-terminated array alocated from POOL. */ |
| static HMODULE * |
| enum_loaded_modules(apr_pool_t *pool) |
| { |
| HMODULE psapi_dll = 0; |
| HANDLE current = GetCurrentProcess(); |
| HMODULE dummy[1]; |
| HMODULE *handles; |
| DWORD size; |
| FNENUMPROCESSMODULES EnumProcessModules_; |
| |
| psapi_dll = GetModuleHandleA("psapi.dll"); |
| |
| if (!psapi_dll) |
| { |
| /* Load and never unload, just like static linking */ |
| psapi_dll = LoadLibraryA("psapi.dll"); |
| } |
| |
| if (!psapi_dll) |
| return NULL; |
| |
| EnumProcessModules_ = (FNENUMPROCESSMODULES) |
| GetProcAddress(psapi_dll, "EnumProcessModules"); |
| |
| /* Before Windows XP psapi was an optional module */ |
| if (! EnumProcessModules_) |
| return NULL; |
| |
| if (!EnumProcessModules_(current, dummy, sizeof(dummy), &size)) |
| return NULL; |
| |
| handles = apr_palloc(pool, size + sizeof *handles); |
| if (! EnumProcessModules_(current, handles, size, &size)) |
| return NULL; |
| handles[size / sizeof *handles] = NULL; |
| return handles; |
| } |
| |
| /* Find the version number, if any, embedded in FILENAME. */ |
| static const char * |
| file_version_number(const wchar_t *filename, apr_pool_t *pool) |
| { |
| VS_FIXEDFILEINFO info; |
| unsigned int major, minor, micro, nano; |
| void *data; |
| DWORD data_size = GetFileVersionInfoSizeW(filename, NULL); |
| void *vinfo; |
| UINT vinfo_size; |
| |
| if (!data_size) |
| return NULL; |
| |
| data = apr_palloc(pool, data_size); |
| if (!GetFileVersionInfoW(filename, 0, data_size, data)) |
| return NULL; |
| |
| if (!VerQueryValueW(data, L"\\", &vinfo, &vinfo_size)) |
| return NULL; |
| |
| if (vinfo_size != sizeof info) |
| return NULL; |
| |
| memcpy(&info, vinfo, sizeof info); |
| major = (info.dwFileVersionMS >> 16) & 0xFFFF; |
| minor = info.dwFileVersionMS & 0xFFFF; |
| micro = (info.dwFileVersionLS >> 16) & 0xFFFF; |
| nano = info.dwFileVersionLS & 0xFFFF; |
| |
| if (!nano) |
| { |
| if (!micro) |
| return apr_psprintf(pool, "%u.%u", major, minor); |
| else |
| return apr_psprintf(pool, "%u.%u.%u", major, minor, micro); |
| } |
| return apr_psprintf(pool, "%u.%u.%u.%u", major, minor, micro, nano); |
| } |
| |
| /* List the shared libraries loaded by the current process. */ |
| static const apr_array_header_t * |
| win32_shared_libs(apr_pool_t *pool) |
| { |
| apr_array_header_t *array = NULL; |
| wchar_t buffer[MAX_PATH + 1]; |
| HMODULE *handles = enum_loaded_modules(pool); |
| HMODULE *module; |
| |
| for (module = handles; module && *module; ++module) |
| { |
| const char *filename; |
| const char *version; |
| if (GetModuleFileNameW(*module, buffer, MAX_PATH)) |
| { |
| buffer[MAX_PATH] = 0; |
| |
| version = file_version_number(buffer, pool); |
| filename = wcs_to_utf8(buffer, pool); |
| if (filename) |
| { |
| svn_version_ext_loaded_lib_t *lib; |
| |
| if (!array) |
| { |
| array = apr_array_make(pool, 32, sizeof(*lib)); |
| } |
| lib = &APR_ARRAY_PUSH(array, svn_version_ext_loaded_lib_t); |
| lib->name = svn_dirent_local_style(filename, pool); |
| lib->version = version; |
| } |
| } |
| } |
| |
| return array; |
| } |
| #endif /* WIN32 */ |
| |
| |
| #ifdef SVN_HAVE_MACOS_PLIST |
| /* Load the SystemVersion.plist or ServerVersion.plist file into a |
| property list. Set SERVER to TRUE if the file read was |
| ServerVersion.plist. */ |
| static CFDictionaryRef |
| system_version_plist(svn_boolean_t *server, apr_pool_t *pool) |
| { |
| static const UInt8 server_version[] = |
| "/System/Library/CoreServices/ServerVersion.plist"; |
| static const UInt8 system_version[] = |
| "/System/Library/CoreServices/SystemVersion.plist"; |
| |
| CFPropertyListRef plist = NULL; |
| CFDataRef resource = NULL; |
| CFStringRef errstr = NULL; |
| CFURLRef url = NULL; |
| SInt32 errcode; |
| |
| url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, |
| server_version, |
| sizeof(server_version) - 1, |
| FALSE); |
| if (!url) |
| return NULL; |
| |
| if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, |
| url, &resource, |
| NULL, NULL, &errcode)) |
| { |
| CFRelease(url); |
| url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, |
| system_version, |
| sizeof(system_version) - 1, |
| FALSE); |
| if (!url) |
| return NULL; |
| |
| if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, |
| url, &resource, |
| NULL, NULL, &errcode)) |
| { |
| CFRelease(url); |
| return NULL; |
| } |
| else |
| { |
| CFRelease(url); |
| *server = FALSE; |
| } |
| } |
| else |
| { |
| CFRelease(url); |
| *server = TRUE; |
| } |
| |
| /* ### CFPropertyListCreateFromXMLData is obsolete, but its |
| replacement CFPropertyListCreateWithData is only available |
| from Mac OS 1.6 onward. */ |
| plist = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, resource, |
| kCFPropertyListImmutable, |
| &errstr); |
| if (resource) |
| CFRelease(resource); |
| if (errstr) |
| CFRelease(errstr); |
| |
| if (CFDictionaryGetTypeID() != CFGetTypeID(plist)) |
| { |
| /* Oops ... this really should be a dict. */ |
| CFRelease(plist); |
| return NULL; |
| } |
| |
| return plist; |
| } |
| |
| /* Return the value for KEY from PLIST, or NULL if not available. */ |
| static const char * |
| value_from_dict(CFDictionaryRef plist, CFStringRef key, apr_pool_t *pool) |
| { |
| CFStringRef valref; |
| CFIndex bufsize; |
| const void *valptr; |
| const char *value; |
| |
| if (!CFDictionaryGetValueIfPresent(plist, key, &valptr)) |
| return NULL; |
| |
| valref = valptr; |
| if (CFStringGetTypeID() != CFGetTypeID(valref)) |
| return NULL; |
| |
| value = CFStringGetCStringPtr(valref, kCFStringEncodingUTF8); |
| if (value) |
| return apr_pstrdup(pool, value); |
| |
| bufsize = 5 * CFStringGetLength(valref) + 1; |
| value = apr_palloc(pool, bufsize); |
| if (!CFStringGetCString(valref, (char*)value, bufsize, |
| kCFStringEncodingUTF8)) |
| value = NULL; |
| |
| return value; |
| } |
| |
| /* Return the commercial name of the OS, given the version number in |
| a format that matches the regular expression /^10\.\d+(\..*)?$/ */ |
| static const char * |
| release_name_from_version(const char *osver) |
| { |
| char *end = NULL; |
| unsigned long num = strtoul(osver, &end, 10); |
| |
| if (!end || *end != '.' || num != 10) |
| return NULL; |
| |
| osver = end + 1; |
| end = NULL; |
| num = strtoul(osver, &end, 10); |
| if (!end || (*end && *end != '.')) |
| return NULL; |
| |
| /* See http://en.wikipedia.org/wiki/History_of_OS_X#Release_timeline */ |
| switch(num) |
| { |
| case 0: return "Cheetah"; |
| case 1: return "Puma"; |
| case 2: return "Jaguar"; |
| case 3: return "Panther"; |
| case 4: return "Tiger"; |
| case 5: return "Leopard"; |
| case 6: return "Snow Leopard"; |
| case 7: return "Lion"; |
| case 8: return "Mountain Lion"; |
| case 9: return "Mavericks"; |
| } |
| |
| return NULL; |
| } |
| |
| /* Construct the release name from information stored in the Mac OS X |
| "SystemVersion.plist" file (or ServerVersion.plist, for Mac Os |
| Server. */ |
| static const char * |
| macos_release_name(apr_pool_t *pool) |
| { |
| svn_boolean_t server; |
| CFDictionaryRef plist = system_version_plist(&server, pool); |
| |
| if (plist) |
| { |
| const char *osname = value_from_dict(plist, CFSTR("ProductName"), pool); |
| const char *osver = value_from_dict(plist, |
| CFSTR("ProductUserVisibleVersion"), |
| pool); |
| const char *build = value_from_dict(plist, |
| CFSTR("ProductBuildVersion"), |
| pool); |
| const char *release; |
| |
| if (!osver) |
| osver = value_from_dict(plist, CFSTR("ProductVersion"), pool); |
| release = release_name_from_version(osver); |
| |
| CFRelease(plist); |
| return apr_psprintf(pool, "%s%s%s%s%s%s%s%s", |
| (osname ? osname : ""), |
| (osver ? (osname ? " " : "") : ""), |
| (osver ? osver : ""), |
| (release ? (osname||osver ? " " : "") : ""), |
| (release ? release : ""), |
| (build |
| ? (osname||osver||release ? ", " : "") |
| : ""), |
| (build |
| ? (server ? "server build " : "build ") |
| : ""), |
| (build ? build : "")); |
| } |
| |
| return NULL; |
| } |
| #endif /* SVN_HAVE_MACOS_PLIST */ |
| |
| #ifdef SVN_HAVE_MACHO_ITERATE |
| /* List the shared libraries loaded by the current process. |
| Ignore frameworks and system libraries, they're just clutter. */ |
| static const apr_array_header_t * |
| macos_shared_libs(apr_pool_t *pool) |
| { |
| static const char slb_prefix[] = "/usr/lib/system/"; |
| static const char fwk_prefix[] = "/System/Library/Frameworks/"; |
| static const char pfk_prefix[] = "/System/Library/PrivateFrameworks/"; |
| |
| const size_t slb_prefix_len = strlen(slb_prefix); |
| const size_t fwk_prefix_len = strlen(fwk_prefix); |
| const size_t pfk_prefix_len = strlen(pfk_prefix); |
| |
| apr_array_header_t *result = NULL; |
| apr_array_header_t *dylibs = NULL; |
| |
| uint32_t i; |
| for (i = 0;; ++i) |
| { |
| const struct mach_header *header = _dyld_get_image_header(i); |
| const char *filename = _dyld_get_image_name(i); |
| const char *version; |
| char *truename; |
| svn_version_ext_loaded_lib_t *lib; |
| |
| if (!(header && filename)) |
| break; |
| |
| switch (header->cputype) |
| { |
| case CPU_TYPE_I386: version = _("Intel"); break; |
| case CPU_TYPE_X86_64: version = _("Intel 64-bit"); break; |
| case CPU_TYPE_POWERPC: version = _("PowerPC"); break; |
| case CPU_TYPE_POWERPC64: version = _("PowerPC 64-bit"); break; |
| default: |
| version = NULL; |
| } |
| |
| if (0 == apr_filepath_merge(&truename, "", filename, |
| APR_FILEPATH_NATIVE |
| | APR_FILEPATH_TRUENAME, |
| pool)) |
| filename = truename; |
| else |
| filename = apr_pstrdup(pool, filename); |
| |
| if (0 == strncmp(filename, slb_prefix, slb_prefix_len) |
| || 0 == strncmp(filename, fwk_prefix, fwk_prefix_len) |
| || 0 == strncmp(filename, pfk_prefix, pfk_prefix_len)) |
| { |
| /* Ignore frameworks and system libraries. */ |
| continue; |
| } |
| |
| if (header->filetype == MH_EXECUTE) |
| { |
| /* Make sure the program filename is first in the list */ |
| if (!result) |
| { |
| result = apr_array_make(pool, 32, sizeof(*lib)); |
| } |
| lib = &APR_ARRAY_PUSH(result, svn_version_ext_loaded_lib_t); |
| } |
| else |
| { |
| if (!dylibs) |
| { |
| dylibs = apr_array_make(pool, 32, sizeof(*lib)); |
| } |
| lib = &APR_ARRAY_PUSH(dylibs, svn_version_ext_loaded_lib_t); |
| } |
| |
| lib->name = filename; |
| lib->version = version; |
| } |
| |
| /* Gather results into one array. */ |
| if (dylibs) |
| { |
| if (result) |
| apr_array_cat(result, dylibs); |
| else |
| result = dylibs; |
| } |
| |
| return result; |
| } |
| #endif /* SVN_HAVE_MACHO_ITERATE */ |