2 * sysinfo.c : information about the running system
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
21 * ====================================================================
27 #define WIN32_LEAN_AND_MEAN
28 #define PSAPI_VERSION 1
34 #define APR_WANT_STRFUNC
38 #include <apr_pools.h>
39 #include <apr_file_info.h>
40 #include <apr_signal.h>
41 #include <apr_strings.h>
42 #include <apr_thread_proc.h>
43 #include <apr_version.h>
44 #include <apu_version.h>
46 #include "svn_pools.h"
47 #include "svn_ctype.h"
48 #include "svn_dirent_uri.h"
49 #include "svn_error.h"
51 #include "svn_string.h"
53 #include "svn_version.h"
55 #include "private/svn_sqlite.h"
58 #include "svn_private_config.h"
60 #if HAVE_SYS_UTSNAME_H
61 #include <sys/utsname.h>
64 #ifdef SVN_HAVE_MACOS_PLIST
65 #include <CoreFoundation/CoreFoundation.h>
68 #ifdef SVN_HAVE_MACHO_ITERATE
69 #include <mach-o/dyld.h>
70 #include <mach-o/loader.h>
74 static const char *canonical_host_from_uname(apr_pool_t *pool);
75 # ifndef SVN_HAVE_MACOS_PLIST
76 static const char *release_name_from_uname(apr_pool_t *pool);
81 static const char *win32_canonical_host(apr_pool_t *pool);
82 static const char *win32_release_name(apr_pool_t *pool);
83 static const apr_array_header_t *win32_shared_libs(apr_pool_t *pool);
86 #ifdef SVN_HAVE_MACOS_PLIST
87 static const char *macos_release_name(apr_pool_t *pool);
90 #ifdef SVN_HAVE_MACHO_ITERATE
91 static const apr_array_header_t *macos_shared_libs(apr_pool_t *pool);
96 static const char *linux_release_name(apr_pool_t *pool);
100 svn_sysinfo__canonical_host(apr_pool_t *pool)
103 return win32_canonical_host(pool);
105 return canonical_host_from_uname(pool);
107 return "unknown-unknown-unknown";
113 svn_sysinfo__release_name(apr_pool_t *pool)
116 return win32_release_name(pool);
117 #elif defined(SVN_HAVE_MACOS_PLIST)
118 return macos_release_name(pool);
120 return linux_release_name(pool);
122 return release_name_from_uname(pool);
128 const apr_array_header_t *
129 svn_sysinfo__linked_libs(apr_pool_t *pool)
131 svn_version_ext_linked_lib_t *lib;
132 apr_array_header_t *array = apr_array_make(pool, 3, sizeof(*lib));
134 lib = &APR_ARRAY_PUSH(array, svn_version_ext_linked_lib_t);
136 lib->compiled_version = APR_VERSION_STRING;
137 lib->runtime_version = apr_pstrdup(pool, apr_version_string());
139 /* Don't list APR-Util if it isn't linked in, which it may not be if
140 * we're using APR 2.x+ which combined APR-Util into APR. */
141 #ifdef APU_VERSION_STRING
142 lib = &APR_ARRAY_PUSH(array, svn_version_ext_linked_lib_t);
143 lib->name = "APR-Util";
144 lib->compiled_version = APU_VERSION_STRING;
145 lib->runtime_version = apr_pstrdup(pool, apu_version_string());
148 lib = &APR_ARRAY_PUSH(array, svn_version_ext_linked_lib_t);
149 lib->name = "SQLite";
150 lib->compiled_version = apr_pstrdup(pool, svn_sqlite__compiled_version());
151 #ifdef SVN_SQLITE_INLINE
152 lib->runtime_version = NULL;
154 lib->runtime_version = apr_pstrdup(pool, svn_sqlite__runtime_version());
160 const apr_array_header_t *
161 svn_sysinfo__loaded_libs(apr_pool_t *pool)
164 return win32_shared_libs(pool);
165 #elif defined(SVN_HAVE_MACHO_ITERATE)
166 return macos_shared_libs(pool);
175 canonical_host_from_uname(apr_pool_t *pool)
177 const char *machine = "unknown";
178 const char *vendor = "unknown";
179 const char *sysname = "unknown";
180 const char *sysver = "";
183 if (0 <= uname(&info))
188 err = svn_utf_cstring_to_utf8(&tmp, info.machine, pool);
190 svn_error_clear(err);
194 err = svn_utf_cstring_to_utf8(&tmp, info.sysname, pool);
196 svn_error_clear(err);
199 char *lwr = apr_pstrdup(pool, tmp);
203 if (svn_ctype_isupper(*it))
204 *it = apr_tolower(*it);
210 if (0 == strcmp(sysname, "darwin"))
212 if (0 == strcmp(sysname, "linux"))
216 err = svn_utf_cstring_to_utf8(&tmp, info.release, pool);
218 svn_error_clear(err);
221 apr_size_t n = strspn(tmp, ".0123456789");
224 char *ver = apr_pstrdup(pool, tmp);
234 return apr_psprintf(pool, "%s-%s-%s%s", machine, vendor, sysname, sysver);
237 # ifndef SVN_HAVE_MACOS_PLIST
238 /* Generate a release name from the uname(3) info, effectively
239 returning "`uname -s` `uname -r`". */
241 release_name_from_uname(apr_pool_t *pool)
244 if (0 <= uname(&info))
250 err = svn_utf_cstring_to_utf8(&sysname, info.sysname, pool);
254 svn_error_clear(err);
258 err = svn_utf_cstring_to_utf8(&sysver, info.release, pool);
262 svn_error_clear(err);
265 if (sysname || sysver)
267 return apr_psprintf(pool, "%s%s%s",
268 (sysname ? sysname : ""),
269 (sysver ? (sysname ? " " : "") : ""),
270 (sysver ? sysver : ""));
275 # endif /* !SVN_HAVE_MACOS_PLIST */
276 #endif /* HAVE_UNAME */
280 /* Split a stringbuf into a key/value pair.
281 Return the key, leaving the striped value in the stringbuf. */
283 stringbuf_split_key(svn_stringbuf_t *buffer, char delim)
288 end = strchr(buffer->data, delim);
292 svn_stringbuf_strip_whitespace(buffer);
294 /* Now we split the currently allocated buffer in two parts:
295 - a const char * HEAD
296 - the remaining stringbuf_t. */
298 /* Create HEAD as '\0' terminated const char * */
300 end = strchr(key, delim);
303 /* And update the TAIL to be a smaller, but still valid stringbuf */
304 buffer->data = end + 1;
305 buffer->len -= 1 + end - key;
306 buffer->blocksize -= 1 + end - key;
308 svn_stringbuf_strip_whitespace(buffer);
313 /* Parse `/usr/bin/lsb_rlease --all` */
315 lsb_release(apr_pool_t *pool)
317 static const char *const args[3] =
319 "/usr/bin/lsb_release",
324 const char *distributor = NULL;
325 const char *description = NULL;
326 const char *release = NULL;
327 const char *codename = NULL;
330 svn_stream_t *lsbinfo;
333 /* Run /usr/bin/lsb_release --all < /dev/null 2>/dev/null */
335 apr_file_t *stdin_handle;
336 apr_file_t *stdout_handle;
338 err = svn_io_file_open(&stdin_handle, SVN_NULL_DEVICE_NAME,
339 APR_READ, APR_OS_DEFAULT, pool);
341 err = svn_io_file_open(&stdout_handle, SVN_NULL_DEVICE_NAME,
342 APR_WRITE, APR_OS_DEFAULT, pool);
344 err = svn_io_start_cmd3(&lsbproc, NULL, args[0], args, NULL, FALSE,
347 FALSE, stdout_handle,
351 svn_error_clear(err);
356 /* Parse the output and try to populate the */
357 lsbinfo = svn_stream_from_aprfile2(lsbproc.out, TRUE, pool);
362 svn_boolean_t eof = FALSE;
363 svn_stringbuf_t *line;
366 err = svn_stream_readline(lsbinfo, &line, "\n", &eof, pool);
370 key = stringbuf_split_key(line, ':');
374 if (0 == svn_cstring_casecmp(key, "Distributor ID"))
375 distributor = line->data;
376 else if (0 == svn_cstring_casecmp(key, "Description"))
377 description = line->data;
378 else if (0 == svn_cstring_casecmp(key, "Release"))
379 release = line->data;
380 else if (0 == svn_cstring_casecmp(key, "Codename"))
381 codename = line->data;
383 err = svn_error_compose_create(err,
384 svn_stream_close(lsbinfo));
387 svn_error_clear(err);
388 apr_proc_kill(&lsbproc, SIGKILL);
393 /* Reap the child process */
394 err = svn_io_wait_for_cmd(&lsbproc, "", NULL, NULL, pool);
397 svn_error_clear(err);
402 return apr_psprintf(pool, "%s%s%s%s", description,
403 (codename ? " (" : ""),
404 (codename ? codename : ""),
405 (codename ? ")" : ""));
407 return apr_psprintf(pool, "%s%s%s%s%s%s", distributor,
408 (release ? " " : ""),
409 (release ? release : ""),
410 (codename ? " (" : ""),
411 (codename ? codename : ""),
412 (codename ? ")" : ""));
417 /* Read the whole contents of a file. */
418 static svn_stringbuf_t *
419 read_file_contents(const char *filename, apr_pool_t *pool)
422 svn_stringbuf_t *buffer;
424 err = svn_stringbuf_from_file2(&buffer, filename, pool);
427 svn_error_clear(err);
434 /* Strip everything but the first line from a stringbuf. */
436 stringbuf_first_line_only(svn_stringbuf_t *buffer)
438 char *eol = strchr(buffer->data, '\n');
442 buffer->len = 1 + eol - buffer->data;
444 svn_stringbuf_strip_whitespace(buffer);
447 /* Look at /etc/redhat_release to detect RHEL/Fedora/CentOS. */
449 redhat_release(apr_pool_t *pool)
451 svn_stringbuf_t *buffer = read_file_contents("/etc/redhat-release", pool);
454 stringbuf_first_line_only(buffer);
460 /* Look at /etc/SuSE-release to detect non-LSB SuSE. */
462 suse_release(apr_pool_t *pool)
464 const char *release = NULL;
465 const char *codename = NULL;
467 svn_stringbuf_t *buffer = read_file_contents("/etc/SuSE-release", pool);
468 svn_stringbuf_t *line;
469 svn_stream_t *stream;
475 stream = svn_stream_from_stringbuf(buffer, pool);
476 err = svn_stream_readline(stream, &line, "\n", &eof, pool);
479 svn_error_clear(err);
483 svn_stringbuf_strip_whitespace(line);
484 release = line->data;
490 err = svn_stream_readline(stream, &line, "\n", &eof, pool);
493 svn_error_clear(err);
497 key = stringbuf_split_key(line, '=');
501 if (0 == strncmp(key, "CODENAME", 8))
502 codename = line->data;
505 return apr_psprintf(pool, "%s%s%s%s",
507 (codename ? " (" : ""),
508 (codename ? codename : ""),
509 (codename ? ")" : ""));
512 /* Look at /etc/debian_version to detect non-LSB Debian. */
514 debian_release(apr_pool_t *pool)
516 svn_stringbuf_t *buffer = read_file_contents("/etc/debian_version", pool);
520 stringbuf_first_line_only(buffer);
521 return apr_pstrcat(pool, "Debian ", buffer->data, NULL);
524 /* Try to find the Linux distribution name, or return info from uname. */
526 linux_release_name(apr_pool_t *pool)
528 const char *uname_release = release_name_from_uname(pool);
530 /* Try anything that has /usr/bin/lsb_release.
531 Covers, for example, Debian, Ubuntu and SuSE. */
532 const char *release_name = lsb_release(pool);
534 /* Try RHEL/Fedora/CentOS */
536 release_name = redhat_release(pool);
538 /* Try Non-LSB SuSE */
540 release_name = suse_release(pool);
542 /* Try non-LSB Debian */
544 release_name = debian_release(pool);
547 return uname_release;
552 return apr_psprintf(pool, "%s [%s]", release_name, uname_release);
554 #endif /* __linux__ */
558 typedef DWORD (WINAPI *FNGETNATIVESYSTEMINFO)(LPSYSTEM_INFO);
559 typedef BOOL (WINAPI *FNENUMPROCESSMODULES) (HANDLE, HMODULE*, DWORD, LPDWORD);
561 /* Get system and version info, and try to tell the difference
562 between the native system type and the runtime environment of the
563 current process. Populate results in SYSINFO, LOCAL_SYSINFO
564 (optional) and OSINFO. */
566 system_info(SYSTEM_INFO *sysinfo,
567 SYSTEM_INFO *local_sysinfo,
568 OSVERSIONINFOEXW *osinfo)
570 FNGETNATIVESYSTEMINFO GetNativeSystemInfo_ = (FNGETNATIVESYSTEMINFO)
571 GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetNativeSystemInfo");
573 ZeroMemory(sysinfo, sizeof *sysinfo);
576 ZeroMemory(local_sysinfo, sizeof *local_sysinfo);
577 GetSystemInfo(local_sysinfo);
578 if (GetNativeSystemInfo_)
579 GetNativeSystemInfo_(sysinfo);
581 memcpy(sysinfo, local_sysinfo, sizeof *sysinfo);
584 GetSystemInfo(sysinfo);
586 ZeroMemory(osinfo, sizeof *osinfo);
587 osinfo->dwOSVersionInfoSize = sizeof *osinfo;
588 if (!GetVersionExW((LPVOID)osinfo))
594 /* Map the proccessor type from SYSINFO to a string. */
596 processor_name(SYSTEM_INFO *sysinfo)
598 switch (sysinfo->wProcessorArchitecture)
600 case PROCESSOR_ARCHITECTURE_AMD64: return "x86_64";
601 case PROCESSOR_ARCHITECTURE_IA64: return "ia64";
602 case PROCESSOR_ARCHITECTURE_INTEL: return "x86";
603 case PROCESSOR_ARCHITECTURE_MIPS: return "mips";
604 case PROCESSOR_ARCHITECTURE_ALPHA: return "alpha32";
605 case PROCESSOR_ARCHITECTURE_PPC: return "powerpc";
606 case PROCESSOR_ARCHITECTURE_SHX: return "shx";
607 case PROCESSOR_ARCHITECTURE_ARM: return "arm";
608 case PROCESSOR_ARCHITECTURE_ALPHA64: return "alpha";
609 case PROCESSOR_ARCHITECTURE_MSIL: return "msil";
610 case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64: return "x86_wow64";
611 default: return "unknown";
615 /* Return the Windows-specific canonical host name. */
617 win32_canonical_host(apr_pool_t *pool)
620 SYSTEM_INFO local_sysinfo;
621 OSVERSIONINFOEXW osinfo;
623 if (system_info(&sysinfo, &local_sysinfo, &osinfo))
625 const char *arch = processor_name(&local_sysinfo);
626 const char *machine = processor_name(&sysinfo);
627 const char *vendor = "microsoft";
628 const char *sysname = "windows";
629 const char *sysver = apr_psprintf(pool, "%u.%u.%u",
630 (unsigned int)osinfo.dwMajorVersion,
631 (unsigned int)osinfo.dwMinorVersion,
632 (unsigned int)osinfo.dwBuildNumber);
634 if (sysinfo.wProcessorArchitecture
635 == local_sysinfo.wProcessorArchitecture)
636 return apr_psprintf(pool, "%s-%s-%s%s",
637 machine, vendor, sysname, sysver);
638 return apr_psprintf(pool, "%s/%s-%s-%s%s",
639 arch, machine, vendor, sysname, sysver);
642 return "unknown-microsoft-windows";
645 /* Convert a Unicode string to UTF-8. */
647 wcs_to_utf8(const wchar_t *wcs, apr_pool_t *pool)
649 const int bufsize = WideCharToMultiByte(CP_UTF8, 0, wcs, -1,
650 NULL, 0, NULL, NULL);
653 char *const utf8 = apr_palloc(pool, bufsize + 1);
654 WideCharToMultiByte(CP_UTF8, 0, wcs, -1, utf8, bufsize, NULL, NULL);
660 /* Query the value called NAME of the registry key HKEY. */
662 registry_value(HKEY hkey, wchar_t *name, apr_pool_t *pool)
667 if (RegQueryValueExW(hkey, name, NULL, NULL, NULL, &size))
670 value = apr_palloc(pool, size + sizeof *value);
671 if (RegQueryValueExW(hkey, name, NULL, NULL, (void*)value, &size))
673 value[size / sizeof *value] = 0;
674 return wcs_to_utf8(value, pool);
677 /* Try to glean the Windows release name and associated info from the
678 registry. Failing that, construct a release name from the version
681 win32_release_name(apr_pool_t *pool)
684 OSVERSIONINFOEXW osinfo;
687 if (!system_info(&sysinfo, NULL, &osinfo))
690 if (!RegOpenKeyExW(HKEY_LOCAL_MACHINE,
691 L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
692 0, KEY_QUERY_VALUE, &hkcv))
694 const char *release = registry_value(hkcv, L"ProductName", pool);
695 const char *spack = registry_value(hkcv, L"CSDVersion", pool);
696 const char *curver = registry_value(hkcv, L"CurrentVersion", pool);
697 const char *curtype = registry_value(hkcv, L"CurrentType", pool);
698 const char *install = registry_value(hkcv, L"InstallationType", pool);
699 const char *curbuild = registry_value(hkcv, L"CurrentBuildNumber", pool);
701 if (!spack && *osinfo.szCSDVersion)
702 spack = wcs_to_utf8(osinfo.szCSDVersion, pool);
705 curbuild = registry_value(hkcv, L"CurrentBuild", pool);
707 if (release || spack || curver || curtype || curbuild)
709 const char *bootinfo = "";
710 if (curver || install || curtype)
712 bootinfo = apr_psprintf(pool, "[%s%s%s%s%s]",
713 (curver ? curver : ""),
714 (install ? (curver ? " " : "") : ""),
715 (install ? install : ""),
717 ? (curver||install ? " " : "")
719 (curtype ? curtype : ""));
722 return apr_psprintf(pool, "%s%s%s%s%s%s%s",
723 (release ? release : ""),
724 (spack ? (release ? ", " : "") : ""),
725 (spack ? spack : ""),
727 ? (release||spack ? ", build " : "build ")
729 (curbuild ? curbuild : ""),
731 ? (release||spack||curbuild ? " " : "")
733 (bootinfo ? bootinfo : ""));
737 if (*osinfo.szCSDVersion)
739 const char *servicepack = wcs_to_utf8(osinfo.szCSDVersion, pool);
742 return apr_psprintf(pool, "Windows NT %u.%u, %s, build %u",
743 (unsigned int)osinfo.dwMajorVersion,
744 (unsigned int)osinfo.dwMinorVersion,
746 (unsigned int)osinfo.dwBuildNumber);
748 /* Assume wServicePackMajor > 0 if szCSDVersion is not empty */
749 if (osinfo.wServicePackMinor)
750 return apr_psprintf(pool, "Windows NT %u.%u SP%u.%u, build %u",
751 (unsigned int)osinfo.dwMajorVersion,
752 (unsigned int)osinfo.dwMinorVersion,
753 (unsigned int)osinfo.wServicePackMajor,
754 (unsigned int)osinfo.wServicePackMinor,
755 (unsigned int)osinfo.dwBuildNumber);
757 return apr_psprintf(pool, "Windows NT %u.%u SP%u, build %u",
758 (unsigned int)osinfo.dwMajorVersion,
759 (unsigned int)osinfo.dwMinorVersion,
760 (unsigned int)osinfo.wServicePackMajor,
761 (unsigned int)osinfo.dwBuildNumber);
764 return apr_psprintf(pool, "Windows NT %u.%u, build %u",
765 (unsigned int)osinfo.dwMajorVersion,
766 (unsigned int)osinfo.dwMinorVersion,
767 (unsigned int)osinfo.dwBuildNumber);
771 /* Get a list of handles of shared libs loaded by the current
772 process. Returns a NULL-terminated array alocated from POOL. */
774 enum_loaded_modules(apr_pool_t *pool)
776 HMODULE psapi_dll = 0;
777 HANDLE current = GetCurrentProcess();
781 FNENUMPROCESSMODULES EnumProcessModules_;
783 psapi_dll = GetModuleHandleA("psapi.dll");
787 /* Load and never unload, just like static linking */
788 psapi_dll = LoadLibraryA("psapi.dll");
794 EnumProcessModules_ = (FNENUMPROCESSMODULES)
795 GetProcAddress(psapi_dll, "EnumProcessModules");
797 /* Before Windows XP psapi was an optional module */
798 if (! EnumProcessModules_)
801 if (!EnumProcessModules_(current, dummy, sizeof(dummy), &size))
804 handles = apr_palloc(pool, size + sizeof *handles);
805 if (! EnumProcessModules_(current, handles, size, &size))
807 handles[size / sizeof *handles] = NULL;
811 /* Find the version number, if any, embedded in FILENAME. */
813 file_version_number(const wchar_t *filename, apr_pool_t *pool)
815 VS_FIXEDFILEINFO info;
816 unsigned int major, minor, micro, nano;
818 DWORD data_size = GetFileVersionInfoSizeW(filename, NULL);
825 data = apr_palloc(pool, data_size);
826 if (!GetFileVersionInfoW(filename, 0, data_size, data))
829 if (!VerQueryValueW(data, L"\\", &vinfo, &vinfo_size))
832 if (vinfo_size != sizeof info)
835 memcpy(&info, vinfo, sizeof info);
836 major = (info.dwFileVersionMS >> 16) & 0xFFFF;
837 minor = info.dwFileVersionMS & 0xFFFF;
838 micro = (info.dwFileVersionLS >> 16) & 0xFFFF;
839 nano = info.dwFileVersionLS & 0xFFFF;
844 return apr_psprintf(pool, "%u.%u", major, minor);
846 return apr_psprintf(pool, "%u.%u.%u", major, minor, micro);
848 return apr_psprintf(pool, "%u.%u.%u.%u", major, minor, micro, nano);
851 /* List the shared libraries loaded by the current process. */
852 static const apr_array_header_t *
853 win32_shared_libs(apr_pool_t *pool)
855 apr_array_header_t *array = NULL;
856 wchar_t buffer[MAX_PATH + 1];
857 HMODULE *handles = enum_loaded_modules(pool);
860 for (module = handles; module && *module; ++module)
862 const char *filename;
864 if (GetModuleFileNameW(*module, buffer, MAX_PATH))
866 buffer[MAX_PATH] = 0;
868 version = file_version_number(buffer, pool);
869 filename = wcs_to_utf8(buffer, pool);
872 svn_version_ext_loaded_lib_t *lib;
876 array = apr_array_make(pool, 32, sizeof(*lib));
878 lib = &APR_ARRAY_PUSH(array, svn_version_ext_loaded_lib_t);
879 lib->name = svn_dirent_local_style(filename, pool);
880 lib->version = version;
890 #ifdef SVN_HAVE_MACOS_PLIST
891 /* Load the SystemVersion.plist or ServerVersion.plist file into a
892 property list. Set SERVER to TRUE if the file read was
893 ServerVersion.plist. */
894 static CFDictionaryRef
895 system_version_plist(svn_boolean_t *server, apr_pool_t *pool)
897 static const UInt8 server_version[] =
898 "/System/Library/CoreServices/ServerVersion.plist";
899 static const UInt8 system_version[] =
900 "/System/Library/CoreServices/SystemVersion.plist";
902 CFPropertyListRef plist = NULL;
903 CFDataRef resource = NULL;
904 CFStringRef errstr = NULL;
908 url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
910 sizeof(server_version) - 1,
915 if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault,
917 NULL, NULL, &errcode))
920 url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
922 sizeof(system_version) - 1,
927 if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault,
929 NULL, NULL, &errcode))
946 /* ### CFPropertyListCreateFromXMLData is obsolete, but its
947 replacement CFPropertyListCreateWithData is only available
948 from Mac OS 1.6 onward. */
949 plist = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, resource,
950 kCFPropertyListImmutable,
957 if (CFDictionaryGetTypeID() != CFGetTypeID(plist))
959 /* Oops ... this really should be a dict. */
967 /* Return the value for KEY from PLIST, or NULL if not available. */
969 value_from_dict(CFDictionaryRef plist, CFStringRef key, apr_pool_t *pool)
976 if (!CFDictionaryGetValueIfPresent(plist, key, &valptr))
980 if (CFStringGetTypeID() != CFGetTypeID(valref))
983 value = CFStringGetCStringPtr(valref, kCFStringEncodingUTF8);
985 return apr_pstrdup(pool, value);
987 bufsize = 5 * CFStringGetLength(valref) + 1;
988 value = apr_palloc(pool, bufsize);
989 if (!CFStringGetCString(valref, (char*)value, bufsize,
990 kCFStringEncodingUTF8))
996 /* Return the commercial name of the OS, given the version number in
997 a format that matches the regular expression /^10\.\d+(\..*)?$/ */
999 release_name_from_version(const char *osver)
1002 unsigned long num = strtoul(osver, &end, 10);
1004 if (!end || *end != '.' || num != 10)
1009 num = strtoul(osver, &end, 10);
1010 if (!end || (*end && *end != '.'))
1013 /* See http://en.wikipedia.org/wiki/History_of_OS_X#Release_timeline */
1016 case 0: return "Cheetah";
1017 case 1: return "Puma";
1018 case 2: return "Jaguar";
1019 case 3: return "Panther";
1020 case 4: return "Tiger";
1021 case 5: return "Leopard";
1022 case 6: return "Snow Leopard";
1023 case 7: return "Lion";
1024 case 8: return "Mountain Lion";
1025 case 9: return "Mavericks";
1031 /* Construct the release name from information stored in the Mac OS X
1032 "SystemVersion.plist" file (or ServerVersion.plist, for Mac Os
1035 macos_release_name(apr_pool_t *pool)
1037 svn_boolean_t server;
1038 CFDictionaryRef plist = system_version_plist(&server, pool);
1042 const char *osname = value_from_dict(plist, CFSTR("ProductName"), pool);
1043 const char *osver = value_from_dict(plist,
1044 CFSTR("ProductUserVisibleVersion"),
1046 const char *build = value_from_dict(plist,
1047 CFSTR("ProductBuildVersion"),
1049 const char *release;
1052 osver = value_from_dict(plist, CFSTR("ProductVersion"), pool);
1053 release = release_name_from_version(osver);
1056 return apr_psprintf(pool, "%s%s%s%s%s%s%s%s",
1057 (osname ? osname : ""),
1058 (osver ? (osname ? " " : "") : ""),
1059 (osver ? osver : ""),
1060 (release ? (osname||osver ? " " : "") : ""),
1061 (release ? release : ""),
1063 ? (osname||osver||release ? ", " : "")
1066 ? (server ? "server build " : "build ")
1068 (build ? build : ""));
1073 #endif /* SVN_HAVE_MACOS_PLIST */
1075 #ifdef SVN_HAVE_MACHO_ITERATE
1076 /* List the shared libraries loaded by the current process.
1077 Ignore frameworks and system libraries, they're just clutter. */
1078 static const apr_array_header_t *
1079 macos_shared_libs(apr_pool_t *pool)
1081 static const char slb_prefix[] = "/usr/lib/system/";
1082 static const char fwk_prefix[] = "/System/Library/Frameworks/";
1083 static const char pfk_prefix[] = "/System/Library/PrivateFrameworks/";
1085 const size_t slb_prefix_len = strlen(slb_prefix);
1086 const size_t fwk_prefix_len = strlen(fwk_prefix);
1087 const size_t pfk_prefix_len = strlen(pfk_prefix);
1089 apr_array_header_t *result = NULL;
1090 apr_array_header_t *dylibs = NULL;
1095 const struct mach_header *header = _dyld_get_image_header(i);
1096 const char *filename = _dyld_get_image_name(i);
1097 const char *version;
1099 svn_version_ext_loaded_lib_t *lib;
1101 if (!(header && filename))
1104 switch (header->cputype)
1106 case CPU_TYPE_I386: version = _("Intel"); break;
1107 case CPU_TYPE_X86_64: version = _("Intel 64-bit"); break;
1108 case CPU_TYPE_POWERPC: version = _("PowerPC"); break;
1109 case CPU_TYPE_POWERPC64: version = _("PowerPC 64-bit"); break;
1114 if (0 == apr_filepath_merge(&truename, "", filename,
1116 | APR_FILEPATH_TRUENAME,
1118 filename = truename;
1120 filename = apr_pstrdup(pool, filename);
1122 if (0 == strncmp(filename, slb_prefix, slb_prefix_len)
1123 || 0 == strncmp(filename, fwk_prefix, fwk_prefix_len)
1124 || 0 == strncmp(filename, pfk_prefix, pfk_prefix_len))
1126 /* Ignore frameworks and system libraries. */
1130 if (header->filetype == MH_EXECUTE)
1132 /* Make sure the program filename is first in the list */
1135 result = apr_array_make(pool, 32, sizeof(*lib));
1137 lib = &APR_ARRAY_PUSH(result, svn_version_ext_loaded_lib_t);
1143 dylibs = apr_array_make(pool, 32, sizeof(*lib));
1145 lib = &APR_ARRAY_PUSH(dylibs, svn_version_ext_loaded_lib_t);
1148 lib->name = filename;
1149 lib->version = version;
1152 /* Gather results into one array. */
1156 apr_array_cat(result, dylibs);
1163 #endif /* SVN_HAVE_MACHO_ITERATE */