/* * 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 #include #include #endif #define APR_WANT_STRFUNC #include #include #include #include #include #include #include #include #include #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 #endif #ifdef SVN_HAVE_MACOS_PLIST #include #endif #ifdef SVN_HAVE_MACHO_ITERATE #include #include #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 */