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 * ====================================================================
26 #define APR_WANT_STRFUNC
30 #include <apr_pools.h>
31 #include <apr_file_info.h>
32 #include <apr_signal.h>
33 #include <apr_strings.h>
34 #include <apr_thread_proc.h>
35 #include <apr_version.h>
36 #include <apu_version.h>
38 #include "svn_pools.h"
39 #include "svn_ctype.h"
40 #include "svn_dirent_uri.h"
41 #include "svn_error.h"
43 #include "svn_string.h"
45 #include "svn_version.h"
47 #include "private/svn_sqlite.h"
48 #include "private/svn_subr_private.h"
49 #include "private/svn_utf_private.h"
52 #include "svn_private_config.h"
54 #if HAVE_SYS_UTSNAME_H
55 #include <sys/utsname.h>
58 #ifdef SVN_HAVE_MACOS_PLIST
59 #include <CoreFoundation/CoreFoundation.h>
60 #include <AvailabilityMacros.h>
61 # ifndef MAC_OS_X_VERSION_10_6
62 # define MAC_OS_X_VERSION_10_6 1060
66 #ifdef SVN_HAVE_MACHO_ITERATE
67 #include <mach-o/dyld.h>
68 #include <mach-o/loader.h>
72 static const char *canonical_host_from_uname(apr_pool_t *pool);
73 # ifndef SVN_HAVE_MACOS_PLIST
74 static const char *release_name_from_uname(apr_pool_t *pool);
79 static const char *win32_canonical_host(apr_pool_t *pool);
80 static const char *win32_release_name(apr_pool_t *pool);
81 static const apr_array_header_t *win32_shared_libs(apr_pool_t *pool);
84 #ifdef SVN_HAVE_MACOS_PLIST
85 static const char *macos_release_name(apr_pool_t *pool);
88 #ifdef SVN_HAVE_MACHO_ITERATE
89 static const apr_array_header_t *macos_shared_libs(apr_pool_t *pool);
94 static const char *linux_release_name(apr_pool_t *pool);
98 svn_sysinfo__canonical_host(apr_pool_t *pool)
101 return win32_canonical_host(pool);
103 return canonical_host_from_uname(pool);
105 return "unknown-unknown-unknown";
111 svn_sysinfo__release_name(apr_pool_t *pool)
114 return win32_release_name(pool);
115 #elif defined(SVN_HAVE_MACOS_PLIST)
116 return macos_release_name(pool);
118 return linux_release_name(pool);
120 return release_name_from_uname(pool);
126 const apr_array_header_t *
127 svn_sysinfo__linked_libs(apr_pool_t *pool)
129 svn_version_ext_linked_lib_t *lib;
130 apr_array_header_t *array = apr_array_make(pool, 6, sizeof(*lib));
132 lib = &APR_ARRAY_PUSH(array, svn_version_ext_linked_lib_t);
134 lib->compiled_version = APR_VERSION_STRING;
135 lib->runtime_version = apr_pstrdup(pool, apr_version_string());
137 /* Don't list APR-Util if it isn't linked in, which it may not be if
138 * we're using APR 2.x+ which combined APR-Util into APR. */
139 #ifdef APU_VERSION_STRING
140 lib = &APR_ARRAY_PUSH(array, svn_version_ext_linked_lib_t);
141 lib->name = "APR-Util";
142 lib->compiled_version = APU_VERSION_STRING;
143 lib->runtime_version = apr_pstrdup(pool, apu_version_string());
146 lib = &APR_ARRAY_PUSH(array, svn_version_ext_linked_lib_t);
148 lib->compiled_version = apr_pstrdup(pool, svn_xml__compiled_version());
149 lib->runtime_version = apr_pstrdup(pool, svn_xml__runtime_version());
151 lib = &APR_ARRAY_PUSH(array, svn_version_ext_linked_lib_t);
152 lib->name = "SQLite";
153 lib->compiled_version = apr_pstrdup(pool, svn_sqlite__compiled_version());
154 #ifdef SVN_SQLITE_INLINE
155 lib->runtime_version = NULL;
157 lib->runtime_version = apr_pstrdup(pool, svn_sqlite__runtime_version());
160 lib = &APR_ARRAY_PUSH(array, svn_version_ext_linked_lib_t);
161 lib->name = "Utf8proc";
162 lib->compiled_version = apr_pstrdup(pool, svn_utf__utf8proc_compiled_version());
163 lib->runtime_version = apr_pstrdup(pool, svn_utf__utf8proc_runtime_version());
165 lib = &APR_ARRAY_PUSH(array, svn_version_ext_linked_lib_t);
167 lib->compiled_version = apr_pstrdup(pool, svn_zlib__compiled_version());
168 lib->runtime_version = apr_pstrdup(pool, svn_zlib__runtime_version());
173 const apr_array_header_t *
174 svn_sysinfo__loaded_libs(apr_pool_t *pool)
177 return win32_shared_libs(pool);
178 #elif defined(SVN_HAVE_MACHO_ITERATE)
179 return macos_shared_libs(pool);
188 canonical_host_from_uname(apr_pool_t *pool)
190 const char *machine = "unknown";
191 const char *vendor = "unknown";
192 const char *sysname = "unknown";
193 const char *sysver = "";
196 if (0 <= uname(&info))
201 err = svn_utf_cstring_to_utf8(&tmp, info.machine, pool);
203 svn_error_clear(err);
207 err = svn_utf_cstring_to_utf8(&tmp, info.sysname, pool);
209 svn_error_clear(err);
212 char *lwr = apr_pstrdup(pool, tmp);
216 if (svn_ctype_isupper(*it))
217 *it = apr_tolower(*it);
223 if (0 == strcmp(sysname, "darwin"))
225 if (0 == strcmp(sysname, "linux"))
229 err = svn_utf_cstring_to_utf8(&tmp, info.release, pool);
231 svn_error_clear(err);
234 apr_size_t n = strspn(tmp, ".0123456789");
237 char *ver = apr_pstrdup(pool, tmp);
247 return apr_psprintf(pool, "%s-%s-%s%s", machine, vendor, sysname, sysver);
250 # ifndef SVN_HAVE_MACOS_PLIST
251 /* Generate a release name from the uname(3) info, effectively
252 returning "`uname -s` `uname -r`". */
254 release_name_from_uname(apr_pool_t *pool)
257 if (0 <= uname(&info))
263 err = svn_utf_cstring_to_utf8(&sysname, info.sysname, pool);
267 svn_error_clear(err);
271 err = svn_utf_cstring_to_utf8(&sysver, info.release, pool);
275 svn_error_clear(err);
278 if (sysname || sysver)
280 return apr_psprintf(pool, "%s%s%s",
281 (sysname ? sysname : ""),
282 (sysver ? (sysname ? " " : "") : ""),
283 (sysver ? sysver : ""));
288 # endif /* !SVN_HAVE_MACOS_PLIST */
289 #endif /* HAVE_UNAME */
293 /* Split a stringbuf into a key/value pair.
294 Return the key, leaving the stripped value in the stringbuf. */
296 stringbuf_split_key(svn_stringbuf_t *buffer, char delim)
301 end = strchr(buffer->data, delim);
305 svn_stringbuf_strip_whitespace(buffer);
307 /* Now we split the currently allocated buffer in two parts:
308 - a const char * HEAD
309 - the remaining stringbuf_t. */
311 /* Create HEAD as '\0' terminated const char * */
313 end = strchr(key, delim);
316 /* And update the TAIL to be a smaller, but still valid stringbuf */
317 buffer->data = end + 1;
318 buffer->len -= 1 + end - key;
319 buffer->blocksize -= 1 + end - key;
321 svn_stringbuf_strip_whitespace(buffer);
326 /* Parse `/usr/bin/lsb_rlease --all` */
328 lsb_release(apr_pool_t *pool)
330 static const char *const args[3] =
332 "/usr/bin/lsb_release",
337 const char *distributor = NULL;
338 const char *description = NULL;
339 const char *release = NULL;
340 const char *codename = NULL;
343 svn_stream_t *lsbinfo;
346 /* Run /usr/bin/lsb_release --all < /dev/null 2>/dev/null */
348 apr_file_t *stdin_handle;
349 apr_file_t *stdout_handle;
351 err = svn_io_file_open(&stdin_handle, SVN_NULL_DEVICE_NAME,
352 APR_READ, APR_OS_DEFAULT, pool);
354 err = svn_io_file_open(&stdout_handle, SVN_NULL_DEVICE_NAME,
355 APR_WRITE, APR_OS_DEFAULT, pool);
357 err = svn_io_start_cmd3(&lsbproc, NULL, args[0], args, NULL, FALSE,
360 FALSE, stdout_handle,
364 svn_error_clear(err);
369 /* Parse the output and try to populate the */
370 lsbinfo = svn_stream_from_aprfile2(lsbproc.out, TRUE, pool);
375 svn_boolean_t eof = FALSE;
376 svn_stringbuf_t *line;
379 err = svn_stream_readline(lsbinfo, &line, "\n", &eof, pool);
383 key = stringbuf_split_key(line, ':');
387 if (0 == svn_cstring_casecmp(key, "Distributor ID"))
388 distributor = line->data;
389 else if (0 == svn_cstring_casecmp(key, "Description"))
390 description = line->data;
391 else if (0 == svn_cstring_casecmp(key, "Release"))
392 release = line->data;
393 else if (0 == svn_cstring_casecmp(key, "Codename"))
394 codename = line->data;
396 err = svn_error_compose_create(err,
397 svn_stream_close(lsbinfo));
400 svn_error_clear(err);
401 apr_proc_kill(&lsbproc, SIGKILL);
406 /* Reap the child process */
407 err = svn_io_wait_for_cmd(&lsbproc, "", NULL, NULL, pool);
410 svn_error_clear(err);
415 return apr_psprintf(pool, "%s%s%s%s", description,
416 (codename ? " (" : ""),
417 (codename ? codename : ""),
418 (codename ? ")" : ""));
420 return apr_psprintf(pool, "%s%s%s%s%s%s", distributor,
421 (release ? " " : ""),
422 (release ? release : ""),
423 (codename ? " (" : ""),
424 (codename ? codename : ""),
425 (codename ? ")" : ""));
430 /* Read /etc/os-release, as documented here:
431 * http://www.freedesktop.org/software/systemd/man/os-release.html
434 systemd_release(apr_pool_t *pool)
437 svn_stream_t *stream;
440 err = svn_stream_open_readonly(&stream, "/etc/os-release", pool, pool);
441 if (err && APR_STATUS_IS_ENOENT(err->apr_err))
443 svn_error_clear(err);
444 err = svn_stream_open_readonly(&stream, "/usr/lib/os-release", pool,
449 svn_error_clear(err);
453 /* Look for the PRETTY_NAME line. */
456 svn_stringbuf_t *line;
459 err = svn_stream_readline(stream, &line, "\n", &eof, pool);
462 svn_error_clear(err);
466 if (!strncmp(line->data, "PRETTY_NAME=", 12))
468 svn_stringbuf_t *release_name;
470 /* The value may or may not be enclosed by double quotes. We don't
471 * attempt to strip them. */
472 release_name = svn_stringbuf_create(line->data + 12, pool);
473 svn_error_clear(svn_stream_close(stream));
474 svn_stringbuf_strip_whitespace(release_name);
475 return release_name->data;
482 /* The file did not contain a PRETTY_NAME line. */
483 svn_error_clear(svn_stream_close(stream));
487 /* Read the whole contents of a file. */
488 static svn_stringbuf_t *
489 read_file_contents(const char *filename, apr_pool_t *pool)
492 svn_stringbuf_t *buffer;
494 err = svn_stringbuf_from_file2(&buffer, filename, pool);
497 svn_error_clear(err);
504 /* Strip everything but the first line from a stringbuf. */
506 stringbuf_first_line_only(svn_stringbuf_t *buffer)
508 char *eol = strchr(buffer->data, '\n');
512 buffer->len = 1 + eol - buffer->data;
514 svn_stringbuf_strip_whitespace(buffer);
517 /* Look at /etc/redhat_release to detect RHEL/Fedora/CentOS. */
519 redhat_release(apr_pool_t *pool)
521 svn_stringbuf_t *buffer = read_file_contents("/etc/redhat-release", pool);
524 stringbuf_first_line_only(buffer);
530 /* Look at /etc/SuSE-release to detect non-LSB SuSE. */
532 suse_release(apr_pool_t *pool)
534 const char *release = NULL;
535 const char *codename = NULL;
537 svn_stringbuf_t *buffer = read_file_contents("/etc/SuSE-release", pool);
538 svn_stringbuf_t *line;
539 svn_stream_t *stream;
545 stream = svn_stream_from_stringbuf(buffer, pool);
546 err = svn_stream_readline(stream, &line, "\n", &eof, pool);
549 svn_error_clear(err);
553 svn_stringbuf_strip_whitespace(line);
554 release = line->data;
560 err = svn_stream_readline(stream, &line, "\n", &eof, pool);
563 svn_error_clear(err);
567 key = stringbuf_split_key(line, '=');
571 if (0 == strncmp(key, "CODENAME", 8))
572 codename = line->data;
575 return apr_psprintf(pool, "%s%s%s%s",
577 (codename ? " (" : ""),
578 (codename ? codename : ""),
579 (codename ? ")" : ""));
582 /* Look at /etc/debian_version to detect non-LSB Debian. */
584 debian_release(apr_pool_t *pool)
586 svn_stringbuf_t *buffer = read_file_contents("/etc/debian_version", pool);
590 stringbuf_first_line_only(buffer);
591 return apr_pstrcat(pool, "Debian ", buffer->data, SVN_VA_NULL);
594 /* Try to find the Linux distribution name, or return info from uname. */
596 linux_release_name(apr_pool_t *pool)
598 const char *uname_release = release_name_from_uname(pool);
600 /* Try anything that has /usr/bin/lsb_release.
601 Covers, for example, Debian, Ubuntu and SuSE. */
602 const char *release_name = lsb_release(pool);
604 /* Try the systemd way (covers Arch). */
606 release_name = systemd_release(pool);
608 /* Try RHEL/Fedora/CentOS */
610 release_name = redhat_release(pool);
612 /* Try Non-LSB SuSE */
614 release_name = suse_release(pool);
616 /* Try non-LSB Debian */
618 release_name = debian_release(pool);
621 return uname_release;
626 return apr_psprintf(pool, "%s [%s]", release_name, uname_release);
628 #endif /* __linux__ */
632 typedef DWORD (WINAPI *FNGETNATIVESYSTEMINFO)(LPSYSTEM_INFO);
633 typedef BOOL (WINAPI *FNENUMPROCESSMODULES) (HANDLE, HMODULE*, DWORD, LPDWORD);
636 svn_sysinfo___fill_windows_version(OSVERSIONINFOEXW *version_info)
638 memset(version_info, 0, sizeof(*version_info));
640 version_info->dwOSVersionInfoSize = sizeof(*version_info);
642 /* Kill warnings with the Windows 8 and later platform SDK */
643 #if _MSC_VER > 1600 && NTDDI_VERSION >= _0x06020000
644 /* Windows 8 deprecated the API to retrieve the Windows version to avoid
645 backwards compatibility problems... It might return a constant version
646 in future Windows versions... But let's kill the warning.
648 We can implementation this using a different function later. */
649 #pragma warning(push)
650 #pragma warning(disable: 4996)
653 /* Prototype supports OSVERSIONINFO */
654 return GetVersionExW((LPVOID)version_info);
655 #if _MSC_VER > 1600 && NTDDI_VERSION >= _0x06020000
657 #pragma warning(disable: 4996)
661 /* Get system info, and try to tell the difference between the native
662 system type and the runtime environment of the current process.
663 Populate results in SYSINFO and LOCAL_SYSINFO (optional). */
665 system_info(SYSTEM_INFO *sysinfo,
666 SYSTEM_INFO *local_sysinfo)
668 FNGETNATIVESYSTEMINFO GetNativeSystemInfo_ = (FNGETNATIVESYSTEMINFO)
669 GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetNativeSystemInfo");
671 memset(sysinfo, 0, sizeof *sysinfo);
674 memset(local_sysinfo, 0, sizeof *local_sysinfo);
675 GetSystemInfo(local_sysinfo);
676 if (GetNativeSystemInfo_)
677 GetNativeSystemInfo_(sysinfo);
679 memcpy(sysinfo, local_sysinfo, sizeof *sysinfo);
682 GetSystemInfo(sysinfo);
687 /* Map the proccessor type from SYSINFO to a string. */
689 processor_name(SYSTEM_INFO *sysinfo)
691 switch (sysinfo->wProcessorArchitecture)
693 case PROCESSOR_ARCHITECTURE_AMD64: return "x86_64";
694 case PROCESSOR_ARCHITECTURE_IA64: return "ia64";
695 case PROCESSOR_ARCHITECTURE_INTEL: return "x86";
696 case PROCESSOR_ARCHITECTURE_MIPS: return "mips";
697 case PROCESSOR_ARCHITECTURE_ALPHA: return "alpha32";
698 case PROCESSOR_ARCHITECTURE_PPC: return "powerpc";
699 case PROCESSOR_ARCHITECTURE_SHX: return "shx";
700 case PROCESSOR_ARCHITECTURE_ARM: return "arm";
701 case PROCESSOR_ARCHITECTURE_ALPHA64: return "alpha";
702 case PROCESSOR_ARCHITECTURE_MSIL: return "msil";
703 case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64: return "x86_wow64";
704 default: return "unknown";
708 /* Return the Windows-specific canonical host name. */
710 win32_canonical_host(apr_pool_t *pool)
713 SYSTEM_INFO local_sysinfo;
714 OSVERSIONINFOEXW osinfo;
716 if (system_info(&sysinfo, &local_sysinfo)
717 && svn_sysinfo___fill_windows_version(&osinfo))
719 const char *arch = processor_name(&local_sysinfo);
720 const char *machine = processor_name(&sysinfo);
721 const char *vendor = "microsoft";
722 const char *sysname = "windows";
723 const char *sysver = apr_psprintf(pool, "%u.%u.%u",
724 (unsigned int)osinfo.dwMajorVersion,
725 (unsigned int)osinfo.dwMinorVersion,
726 (unsigned int)osinfo.dwBuildNumber);
728 if (sysinfo.wProcessorArchitecture
729 == local_sysinfo.wProcessorArchitecture)
730 return apr_psprintf(pool, "%s-%s-%s%s",
731 machine, vendor, sysname, sysver);
732 return apr_psprintf(pool, "%s/%s-%s-%s%s",
733 arch, machine, vendor, sysname, sysver);
736 return "unknown-microsoft-windows";
739 /* Convert a Unicode string to UTF-8. */
741 wcs_to_utf8(const wchar_t *wcs, apr_pool_t *pool)
743 const int bufsize = WideCharToMultiByte(CP_UTF8, 0, wcs, -1,
744 NULL, 0, NULL, NULL);
747 char *const utf8 = apr_palloc(pool, bufsize + 1);
748 WideCharToMultiByte(CP_UTF8, 0, wcs, -1, utf8, bufsize, NULL, NULL);
754 /* Query the value called NAME of the registry key HKEY. */
756 registry_value(HKEY hkey, wchar_t *name, apr_pool_t *pool)
761 if (RegQueryValueExW(hkey, name, NULL, NULL, NULL, &size))
764 value = apr_palloc(pool, size + sizeof *value);
765 if (RegQueryValueExW(hkey, name, NULL, NULL, (void*)value, &size))
767 value[size / sizeof *value] = 0;
768 return wcs_to_utf8(value, pool);
771 /* Try to glean the Windows release name and associated info from the
772 registry. Failing that, construct a release name from the version
775 win32_release_name(apr_pool_t *pool)
778 OSVERSIONINFOEXW osinfo;
781 if (!system_info(&sysinfo, NULL)
782 || !svn_sysinfo___fill_windows_version(&osinfo))
785 if (!RegOpenKeyExW(HKEY_LOCAL_MACHINE,
786 L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
787 0, KEY_QUERY_VALUE, &hkcv))
789 const char *release = registry_value(hkcv, L"ProductName", pool);
790 const char *spack = registry_value(hkcv, L"CSDVersion", pool);
791 const char *curver = registry_value(hkcv, L"CurrentVersion", pool);
792 const char *curtype = registry_value(hkcv, L"CurrentType", pool);
793 const char *install = registry_value(hkcv, L"InstallationType", pool);
794 const char *curbuild = registry_value(hkcv, L"CurrentBuildNumber", pool);
796 if (!spack && *osinfo.szCSDVersion)
797 spack = wcs_to_utf8(osinfo.szCSDVersion, pool);
800 curbuild = registry_value(hkcv, L"CurrentBuild", pool);
802 if (release || spack || curver || curtype || curbuild)
804 const char *bootinfo = "";
805 if (curver || install || curtype)
807 bootinfo = apr_psprintf(pool, "[%s%s%s%s%s]",
808 (curver ? curver : ""),
809 (install ? (curver ? " " : "") : ""),
810 (install ? install : ""),
812 ? (curver||install ? " " : "")
814 (curtype ? curtype : ""));
817 return apr_psprintf(pool, "%s%s%s%s%s%s%s",
818 (release ? release : ""),
819 (spack ? (release ? ", " : "") : ""),
820 (spack ? spack : ""),
822 ? (release||spack ? ", build " : "build ")
824 (curbuild ? curbuild : ""),
826 ? (release||spack||curbuild ? " " : "")
828 (bootinfo ? bootinfo : ""));
832 if (*osinfo.szCSDVersion)
834 const char *servicepack = wcs_to_utf8(osinfo.szCSDVersion, pool);
837 return apr_psprintf(pool, "Windows NT %u.%u, %s, build %u",
838 (unsigned int)osinfo.dwMajorVersion,
839 (unsigned int)osinfo.dwMinorVersion,
841 (unsigned int)osinfo.dwBuildNumber);
843 /* Assume wServicePackMajor > 0 if szCSDVersion is not empty */
844 if (osinfo.wServicePackMinor)
845 return apr_psprintf(pool, "Windows NT %u.%u SP%u.%u, build %u",
846 (unsigned int)osinfo.dwMajorVersion,
847 (unsigned int)osinfo.dwMinorVersion,
848 (unsigned int)osinfo.wServicePackMajor,
849 (unsigned int)osinfo.wServicePackMinor,
850 (unsigned int)osinfo.dwBuildNumber);
852 return apr_psprintf(pool, "Windows NT %u.%u SP%u, build %u",
853 (unsigned int)osinfo.dwMajorVersion,
854 (unsigned int)osinfo.dwMinorVersion,
855 (unsigned int)osinfo.wServicePackMajor,
856 (unsigned int)osinfo.dwBuildNumber);
859 return apr_psprintf(pool, "Windows NT %u.%u, build %u",
860 (unsigned int)osinfo.dwMajorVersion,
861 (unsigned int)osinfo.dwMinorVersion,
862 (unsigned int)osinfo.dwBuildNumber);
866 /* Get a list of handles of shared libs loaded by the current
867 process. Returns a NULL-terminated array alocated from POOL. */
869 enum_loaded_modules(apr_pool_t *pool)
871 HMODULE psapi_dll = 0;
872 HANDLE current = GetCurrentProcess();
876 FNENUMPROCESSMODULES EnumProcessModules_;
878 psapi_dll = GetModuleHandleA("psapi.dll");
882 /* Load and never unload, just like static linking */
883 psapi_dll = LoadLibraryA("psapi.dll");
889 EnumProcessModules_ = (FNENUMPROCESSMODULES)
890 GetProcAddress(psapi_dll, "EnumProcessModules");
892 /* Before Windows XP psapi was an optional module */
893 if (! EnumProcessModules_)
896 if (!EnumProcessModules_(current, dummy, sizeof(dummy), &size))
899 handles = apr_palloc(pool, size + sizeof *handles);
900 if (! EnumProcessModules_(current, handles, size, &size))
902 handles[size / sizeof *handles] = NULL;
906 /* Find the version number, if any, embedded in FILENAME. */
908 file_version_number(const wchar_t *filename, apr_pool_t *pool)
910 VS_FIXEDFILEINFO info;
911 unsigned int major, minor, micro, nano;
913 DWORD data_size = GetFileVersionInfoSizeW(filename, NULL);
920 data = apr_palloc(pool, data_size);
921 if (!GetFileVersionInfoW(filename, 0, data_size, data))
924 if (!VerQueryValueW(data, L"\\", &vinfo, &vinfo_size))
927 if (vinfo_size != sizeof info)
930 memcpy(&info, vinfo, sizeof info);
931 major = (info.dwFileVersionMS >> 16) & 0xFFFF;
932 minor = info.dwFileVersionMS & 0xFFFF;
933 micro = (info.dwFileVersionLS >> 16) & 0xFFFF;
934 nano = info.dwFileVersionLS & 0xFFFF;
939 return apr_psprintf(pool, "%u.%u", major, minor);
941 return apr_psprintf(pool, "%u.%u.%u", major, minor, micro);
943 return apr_psprintf(pool, "%u.%u.%u.%u", major, minor, micro, nano);
946 /* List the shared libraries loaded by the current process. */
947 static const apr_array_header_t *
948 win32_shared_libs(apr_pool_t *pool)
950 apr_array_header_t *array = NULL;
951 wchar_t buffer[MAX_PATH + 1];
952 HMODULE *handles = enum_loaded_modules(pool);
955 for (module = handles; module && *module; ++module)
957 const char *filename;
959 if (GetModuleFileNameW(*module, buffer, MAX_PATH))
961 buffer[MAX_PATH] = 0;
963 version = file_version_number(buffer, pool);
964 filename = wcs_to_utf8(buffer, pool);
967 svn_version_ext_loaded_lib_t *lib;
971 array = apr_array_make(pool, 32, sizeof(*lib));
973 lib = &APR_ARRAY_PUSH(array, svn_version_ext_loaded_lib_t);
974 lib->name = svn_dirent_local_style(filename, pool);
975 lib->version = version;
985 #ifdef SVN_HAVE_MACOS_PLIST
986 /* implements svn_write_fn_t to copy the data into a CFMutableDataRef that's
989 write_to_cfmutabledata(void *baton, const char *data, apr_size_t *len)
991 CFMutableDataRef *resource = (CFMutableDataRef *) baton;
993 CFDataAppendBytes(*resource, (UInt8 *)data, *len);
998 /* Load the SystemVersion.plist or ServerVersion.plist file into a
999 property list. Set SERVER to TRUE if the file read was
1000 ServerVersion.plist. */
1001 static CFDictionaryRef
1002 system_version_plist(svn_boolean_t *server, apr_pool_t *pool)
1004 static const char server_version[] =
1005 "/System/Library/CoreServices/ServerVersion.plist";
1006 static const char system_version[] =
1007 "/System/Library/CoreServices/SystemVersion.plist";
1008 svn_stream_t *read_stream, *write_stream;
1010 CFPropertyListRef plist = NULL;
1011 CFMutableDataRef resource = CFDataCreateMutable(kCFAllocatorDefault, 0);
1013 /* failed getting the CFMutableDataRef, shouldn't happen */
1017 /* Try to open the plist files to get the data */
1018 err = svn_stream_open_readonly(&read_stream, server_version, pool, pool);
1021 if (!APR_STATUS_IS_ENOENT(err->apr_err))
1023 svn_error_clear(err);
1024 CFRelease(resource);
1029 svn_error_clear(err);
1030 err = svn_stream_open_readonly(&read_stream, system_version,
1034 svn_error_clear(err);
1035 CFRelease(resource);
1047 /* copy the data onto the CFMutableDataRef to allow us to provide it to
1048 * the CoreFoundation functions that parse proprerty lists */
1049 write_stream = svn_stream_create(&resource, pool);
1050 svn_stream_set_write(write_stream, write_to_cfmutabledata);
1051 err = svn_stream_copy3(read_stream, write_stream, NULL, NULL, pool);
1054 svn_error_clear(err);
1058 #if __MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
1059 /* This function is only available from Mac OS 10.6 onward. */
1060 plist = CFPropertyListCreateWithData(kCFAllocatorDefault, resource,
1061 kCFPropertyListImmutable,
1063 #else /* Mac OS 10.5 or earlier */
1064 /* This function obsolete and deprecated since Mac OS 10.10. */
1065 plist = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, resource,
1066 kCFPropertyListImmutable,
1068 #endif /* MAC_OS_X_VERSION_10_6 */
1071 CFRelease(resource);
1076 if (CFDictionaryGetTypeID() != CFGetTypeID(plist))
1078 /* Oops ... this really should be a dict. */
1086 /* Return the value for KEY from PLIST, or NULL if not available. */
1088 value_from_dict(CFDictionaryRef plist, CFStringRef key, apr_pool_t *pool)
1095 if (!CFDictionaryGetValueIfPresent(plist, key, &valptr))
1099 if (CFStringGetTypeID() != CFGetTypeID(valref))
1102 value = CFStringGetCStringPtr(valref, kCFStringEncodingUTF8);
1104 return apr_pstrdup(pool, value);
1106 bufsize = 5 * CFStringGetLength(valref) + 1;
1107 value = apr_palloc(pool, bufsize);
1108 if (!CFStringGetCString(valref, (char*)value, bufsize,
1109 kCFStringEncodingUTF8))
1115 /* Return the commercial name of the OS, given the version number in
1116 a format that matches the regular expression /^10\.\d+(\..*)?$/ */
1118 release_name_from_version(const char *osver)
1121 unsigned long num = strtoul(osver, &end, 10);
1123 if (!end || *end != '.' || num != 10)
1128 num = strtoul(osver, &end, 10);
1129 if (!end || (*end && *end != '.'))
1132 /* See http://en.wikipedia.org/wiki/History_of_OS_X#Release_timeline */
1135 case 0: return "Cheetah";
1136 case 1: return "Puma";
1137 case 2: return "Jaguar";
1138 case 3: return "Panther";
1139 case 4: return "Tiger";
1140 case 5: return "Leopard";
1141 case 6: return "Snow Leopard";
1142 case 7: return "Lion";
1143 case 8: return "Mountain Lion";
1144 case 9: return "Mavericks";
1145 case 10: return "Yosemite";
1151 /* Construct the release name from information stored in the Mac OS X
1152 "SystemVersion.plist" file (or ServerVersion.plist, for Mac Os
1155 macos_release_name(apr_pool_t *pool)
1157 svn_boolean_t server;
1158 CFDictionaryRef plist = system_version_plist(&server, pool);
1162 const char *osname = value_from_dict(plist, CFSTR("ProductName"), pool);
1163 const char *osver = value_from_dict(plist,
1164 CFSTR("ProductUserVisibleVersion"),
1166 const char *build = value_from_dict(plist,
1167 CFSTR("ProductBuildVersion"),
1169 const char *release;
1172 osver = value_from_dict(plist, CFSTR("ProductVersion"), pool);
1173 release = release_name_from_version(osver);
1176 return apr_psprintf(pool, "%s%s%s%s%s%s%s%s",
1177 (osname ? osname : ""),
1178 (osver ? (osname ? " " : "") : ""),
1179 (osver ? osver : ""),
1180 (release ? (osname||osver ? " " : "") : ""),
1181 (release ? release : ""),
1183 ? (osname||osver||release ? ", " : "")
1186 ? (server ? "server build " : "build ")
1188 (build ? build : ""));
1193 #endif /* SVN_HAVE_MACOS_PLIST */
1195 #ifdef SVN_HAVE_MACHO_ITERATE
1196 /* List the shared libraries loaded by the current process.
1197 Ignore frameworks and system libraries, they're just clutter. */
1198 static const apr_array_header_t *
1199 macos_shared_libs(apr_pool_t *pool)
1201 static const char slb_prefix[] = "/usr/lib/system/";
1202 static const char fwk_prefix[] = "/System/Library/Frameworks/";
1203 static const char pfk_prefix[] = "/System/Library/PrivateFrameworks/";
1205 const size_t slb_prefix_len = strlen(slb_prefix);
1206 const size_t fwk_prefix_len = strlen(fwk_prefix);
1207 const size_t pfk_prefix_len = strlen(pfk_prefix);
1209 apr_array_header_t *result = NULL;
1210 apr_array_header_t *dylibs = NULL;
1215 const struct mach_header *header = _dyld_get_image_header(i);
1216 const char *filename = _dyld_get_image_name(i);
1217 const char *version;
1219 svn_version_ext_loaded_lib_t *lib;
1221 if (!(header && filename))
1224 switch (header->cputype)
1226 case CPU_TYPE_I386: version = _("Intel"); break;
1227 case CPU_TYPE_X86_64: version = _("Intel 64-bit"); break;
1228 case CPU_TYPE_POWERPC: version = _("PowerPC"); break;
1229 case CPU_TYPE_POWERPC64: version = _("PowerPC 64-bit"); break;
1234 if (0 == apr_filepath_merge(&truename, "", filename,
1236 | APR_FILEPATH_TRUENAME,
1238 filename = truename;
1240 filename = apr_pstrdup(pool, filename);
1242 if (0 == strncmp(filename, slb_prefix, slb_prefix_len)
1243 || 0 == strncmp(filename, fwk_prefix, fwk_prefix_len)
1244 || 0 == strncmp(filename, pfk_prefix, pfk_prefix_len))
1246 /* Ignore frameworks and system libraries. */
1250 if (header->filetype == MH_EXECUTE)
1252 /* Make sure the program filename is first in the list */
1255 result = apr_array_make(pool, 32, sizeof(*lib));
1257 lib = &APR_ARRAY_PUSH(result, svn_version_ext_loaded_lib_t);
1263 dylibs = apr_array_make(pool, 32, sizeof(*lib));
1265 lib = &APR_ARRAY_PUSH(dylibs, svn_version_ext_loaded_lib_t);
1268 lib->name = filename;
1269 lib->version = version;
1272 /* Gather results into one array. */
1276 apr_array_cat(result, dylibs);
1283 #endif /* SVN_HAVE_MACHO_ITERATE */