diff options
Diffstat (limited to 'subversion/libsvn_subr/sysinfo.c')
-rw-r--r-- | subversion/libsvn_subr/sysinfo.c | 1132 |
1 files changed, 1132 insertions, 0 deletions
diff --git a/subversion/libsvn_subr/sysinfo.c b/subversion/libsvn_subr/sysinfo.c new file mode 100644 index 0000000..455dca4 --- /dev/null +++ b/subversion/libsvn_subr/sysinfo.c @@ -0,0 +1,1132 @@ +/* + * 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); + key = buffer->data; + end = strchr(key, delim); + *end = '\0'; + buffer->len = 1 + end - key; + buffer->data = end + 1; + 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) +{ + HANDLE current = GetCurrentProcess(); + HMODULE dummy[1]; + HMODULE *handles; + DWORD size; + + 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"; + } + + 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 */ |