]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/subversion/subversion/libsvn_subr/sysinfo.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / subversion / subversion / libsvn_subr / sysinfo.c
1 /*
2  * sysinfo.c :  information about the running system
3  *
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
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
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
20  *    under the License.
21  * ====================================================================
22  */
23
24
25 \f
26 #ifdef WIN32
27 #define WIN32_LEAN_AND_MEAN
28 #define PSAPI_VERSION 1
29 #include <windows.h>
30 #include <psapi.h>
31 #include <Ws2tcpip.h>
32 #endif
33
34 #define APR_WANT_STRFUNC
35 #include <apr_want.h>
36
37 #include <apr_lib.h>
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>
45
46 #include "svn_pools.h"
47 #include "svn_ctype.h"
48 #include "svn_dirent_uri.h"
49 #include "svn_error.h"
50 #include "svn_io.h"
51 #include "svn_string.h"
52 #include "svn_utf.h"
53 #include "svn_version.h"
54
55 #include "private/svn_sqlite.h"
56
57 #include "sysinfo.h"
58 #include "svn_private_config.h"
59
60 #if HAVE_SYS_UTSNAME_H
61 #include <sys/utsname.h>
62 #endif
63
64 #ifdef SVN_HAVE_MACOS_PLIST
65 #include <CoreFoundation/CoreFoundation.h>
66 #endif
67
68 #ifdef SVN_HAVE_MACHO_ITERATE
69 #include <mach-o/dyld.h>
70 #include <mach-o/loader.h>
71 #endif
72
73 #if HAVE_UNAME
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);
77 # endif
78 #endif
79
80 #ifdef WIN32
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);
84 #endif /* WIN32 */
85
86 #ifdef SVN_HAVE_MACOS_PLIST
87 static const char *macos_release_name(apr_pool_t *pool);
88 #endif
89
90 #ifdef SVN_HAVE_MACHO_ITERATE
91 static const apr_array_header_t *macos_shared_libs(apr_pool_t *pool);
92 #endif
93
94
95 #if __linux__
96 static const char *linux_release_name(apr_pool_t *pool);
97 #endif
98
99 const char *
100 svn_sysinfo__canonical_host(apr_pool_t *pool)
101 {
102 #ifdef WIN32
103   return win32_canonical_host(pool);
104 #elif HAVE_UNAME
105   return canonical_host_from_uname(pool);
106 #else
107   return "unknown-unknown-unknown";
108 #endif
109 }
110
111
112 const char *
113 svn_sysinfo__release_name(apr_pool_t *pool)
114 {
115 #ifdef WIN32
116   return win32_release_name(pool);
117 #elif defined(SVN_HAVE_MACOS_PLIST)
118   return macos_release_name(pool);
119 #elif __linux__
120   return linux_release_name(pool);
121 #elif HAVE_UNAME
122   return release_name_from_uname(pool);
123 #else
124   return NULL;
125 #endif
126 }
127
128 const apr_array_header_t *
129 svn_sysinfo__linked_libs(apr_pool_t *pool)
130 {
131   svn_version_ext_linked_lib_t *lib;
132   apr_array_header_t *array = apr_array_make(pool, 3, sizeof(*lib));
133
134   lib = &APR_ARRAY_PUSH(array, svn_version_ext_linked_lib_t);
135   lib->name = "APR";
136   lib->compiled_version = APR_VERSION_STRING;
137   lib->runtime_version = apr_pstrdup(pool, apr_version_string());
138
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());
146 #endif
147
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;
153 #else
154   lib->runtime_version = apr_pstrdup(pool, svn_sqlite__runtime_version());
155 #endif
156
157   return array;
158 }
159
160 const apr_array_header_t *
161 svn_sysinfo__loaded_libs(apr_pool_t *pool)
162 {
163 #ifdef WIN32
164   return win32_shared_libs(pool);
165 #elif defined(SVN_HAVE_MACHO_ITERATE)
166   return macos_shared_libs(pool);
167 #else
168   return NULL;
169 #endif
170 }
171
172 \f
173 #if HAVE_UNAME
174 static const char*
175 canonical_host_from_uname(apr_pool_t *pool)
176 {
177   const char *machine = "unknown";
178   const char *vendor = "unknown";
179   const char *sysname = "unknown";
180   const char *sysver = "";
181   struct utsname info;
182
183   if (0 <= uname(&info))
184     {
185       svn_error_t *err;
186       const char *tmp;
187
188       err = svn_utf_cstring_to_utf8(&tmp, info.machine, pool);
189       if (err)
190         svn_error_clear(err);
191       else
192         machine = tmp;
193
194       err = svn_utf_cstring_to_utf8(&tmp, info.sysname, pool);
195       if (err)
196         svn_error_clear(err);
197       else
198         {
199           char *lwr = apr_pstrdup(pool, tmp);
200           char *it = lwr;
201           while (*it)
202             {
203               if (svn_ctype_isupper(*it))
204                 *it = apr_tolower(*it);
205               ++it;
206             }
207           sysname = lwr;
208         }
209
210       if (0 == strcmp(sysname, "darwin"))
211         vendor = "apple";
212       if (0 == strcmp(sysname, "linux"))
213         sysver = "-gnu";
214       else
215         {
216           err = svn_utf_cstring_to_utf8(&tmp, info.release, pool);
217           if (err)
218             svn_error_clear(err);
219           else
220             {
221               apr_size_t n = strspn(tmp, ".0123456789");
222               if (n > 0)
223                 {
224                   char *ver = apr_pstrdup(pool, tmp);
225                   ver[n] = 0;
226                   sysver = ver;
227                 }
228               else
229                 sysver = tmp;
230             }
231         }
232     }
233
234   return apr_psprintf(pool, "%s-%s-%s%s", machine, vendor, sysname, sysver);
235 }
236
237 # ifndef SVN_HAVE_MACOS_PLIST
238 /* Generate a release name from the uname(3) info, effectively
239    returning "`uname -s` `uname -r`". */
240 static const char *
241 release_name_from_uname(apr_pool_t *pool)
242 {
243   struct utsname info;
244   if (0 <= uname(&info))
245     {
246       svn_error_t *err;
247       const char *sysname;
248       const char *sysver;
249
250       err = svn_utf_cstring_to_utf8(&sysname, info.sysname, pool);
251       if (err)
252         {
253           sysname = NULL;
254           svn_error_clear(err);
255         }
256
257
258       err = svn_utf_cstring_to_utf8(&sysver, info.release, pool);
259       if (err)
260         {
261           sysver = NULL;
262           svn_error_clear(err);
263         }
264
265       if (sysname || sysver)
266         {
267           return apr_psprintf(pool, "%s%s%s",
268                               (sysname ? sysname : ""),
269                               (sysver ? (sysname ? " " : "") : ""),
270                               (sysver ? sysver : ""));
271         }
272     }
273   return NULL;
274 }
275 # endif  /* !SVN_HAVE_MACOS_PLIST */
276 #endif  /* HAVE_UNAME */
277
278 \f
279 #if __linux__
280 /* Split a stringbuf into a key/value pair.
281    Return the key, leaving the striped value in the stringbuf. */
282 static const char *
283 stringbuf_split_key(svn_stringbuf_t *buffer, char delim)
284 {
285   char *key;
286   char *end;
287
288   end = strchr(buffer->data, delim);
289   if (!end)
290     return NULL;
291
292   svn_stringbuf_strip_whitespace(buffer);
293   key = buffer->data;
294   end = strchr(key, delim);
295   *end = '\0';
296   buffer->len = 1 + end - key;
297   buffer->data = end + 1;
298   svn_stringbuf_strip_whitespace(buffer);
299
300   return key;
301 }
302
303 /* Parse `/usr/bin/lsb_rlease --all` */
304 static const char *
305 lsb_release(apr_pool_t *pool)
306 {
307   static const char *const args[3] =
308     {
309       "/usr/bin/lsb_release",
310       "--all",
311       NULL
312     };
313
314   const char *distributor = NULL;
315   const char *description = NULL;
316   const char *release = NULL;
317   const char *codename = NULL;
318
319   apr_proc_t lsbproc;
320   svn_stream_t *lsbinfo;
321   svn_error_t *err;
322
323   /* Run /usr/bin/lsb_release --all < /dev/null 2>/dev/null */
324   {
325     apr_file_t *stdin_handle;
326     apr_file_t *stdout_handle;
327
328     err = svn_io_file_open(&stdin_handle, SVN_NULL_DEVICE_NAME,
329                            APR_READ, APR_OS_DEFAULT, pool);
330     if (!err)
331       err = svn_io_file_open(&stdout_handle, SVN_NULL_DEVICE_NAME,
332                              APR_WRITE, APR_OS_DEFAULT, pool);
333     if (!err)
334       err = svn_io_start_cmd3(&lsbproc, NULL, args[0], args, NULL, FALSE,
335                               FALSE, stdin_handle,
336                               TRUE, NULL,
337                               FALSE, stdout_handle,
338                               pool);
339     if (err)
340       {
341         svn_error_clear(err);
342         return NULL;
343       }
344   }
345
346   /* Parse the output and try to populate the  */
347   lsbinfo = svn_stream_from_aprfile2(lsbproc.out, TRUE, pool);
348   if (lsbinfo)
349     {
350       for (;;)
351         {
352           svn_boolean_t eof = FALSE;
353           svn_stringbuf_t *line;
354           const char *key;
355
356           err = svn_stream_readline(lsbinfo, &line, "\n", &eof, pool);
357           if (err || eof)
358             break;
359
360           key = stringbuf_split_key(line, ':');
361           if (!key)
362             continue;
363
364           if (0 == svn_cstring_casecmp(key, "Distributor ID"))
365             distributor = line->data;
366           else if (0 == svn_cstring_casecmp(key, "Description"))
367             description = line->data;
368           else if (0 == svn_cstring_casecmp(key, "Release"))
369             release = line->data;
370           else if (0 == svn_cstring_casecmp(key, "Codename"))
371             codename = line->data;
372         }
373       err = svn_error_compose_create(err,
374                                      svn_stream_close(lsbinfo));
375       if (err)
376         {
377           svn_error_clear(err);
378           apr_proc_kill(&lsbproc, SIGKILL);
379           return NULL;
380         }
381     }
382
383   /* Reap the child process */
384   err = svn_io_wait_for_cmd(&lsbproc, "", NULL, NULL, pool);
385   if (err)
386     {
387       svn_error_clear(err);
388       return NULL;
389     }
390
391   if (description)
392     return apr_psprintf(pool, "%s%s%s%s", description,
393                         (codename ? " (" : ""),
394                         (codename ? codename : ""),
395                         (codename ? ")" : ""));
396   if (distributor)
397     return apr_psprintf(pool, "%s%s%s%s%s%s", distributor,
398                         (release ? " " : ""),
399                         (release ? release : ""),
400                         (codename ? " (" : ""),
401                         (codename ? codename : ""),
402                         (codename ? ")" : ""));
403
404   return NULL;
405 }
406
407 /* Read the whole contents of a file. */
408 static svn_stringbuf_t *
409 read_file_contents(const char *filename, apr_pool_t *pool)
410 {
411   svn_error_t *err;
412   svn_stringbuf_t *buffer;
413
414   err = svn_stringbuf_from_file2(&buffer, filename, pool);
415   if (err)
416     {
417       svn_error_clear(err);
418       return NULL;
419     }
420
421   return buffer;
422 }
423
424 /* Strip everything but the first line from a stringbuf. */
425 static void
426 stringbuf_first_line_only(svn_stringbuf_t *buffer)
427 {
428   char *eol = strchr(buffer->data, '\n');
429   if (eol)
430     {
431       *eol = '\0';
432       buffer->len = 1 + eol - buffer->data;
433     }
434   svn_stringbuf_strip_whitespace(buffer);
435 }
436
437 /* Look at /etc/redhat_release to detect RHEL/Fedora/CentOS. */
438 static const char *
439 redhat_release(apr_pool_t *pool)
440 {
441   svn_stringbuf_t *buffer = read_file_contents("/etc/redhat-release", pool);
442   if (buffer)
443     {
444       stringbuf_first_line_only(buffer);
445       return buffer->data;
446     }
447   return NULL;
448 }
449
450 /* Look at /etc/SuSE-release to detect non-LSB SuSE. */
451 static const char *
452 suse_release(apr_pool_t *pool)
453 {
454   const char *release = NULL;
455   const char *codename = NULL;
456
457   svn_stringbuf_t *buffer = read_file_contents("/etc/SuSE-release", pool);
458   svn_stringbuf_t *line;
459   svn_stream_t *stream;
460   svn_boolean_t eof;
461   svn_error_t *err;
462   if (!buffer)
463       return NULL;
464
465   stream = svn_stream_from_stringbuf(buffer, pool);
466   err = svn_stream_readline(stream, &line, "\n", &eof, pool);
467   if (err || eof)
468     {
469       svn_error_clear(err);
470       return NULL;
471     }
472
473   svn_stringbuf_strip_whitespace(line);
474   release = line->data;
475
476   for (;;)
477     {
478       const char *key;
479
480       err = svn_stream_readline(stream, &line, "\n", &eof, pool);
481       if (err || eof)
482         {
483           svn_error_clear(err);
484           break;
485         }
486
487       key = stringbuf_split_key(line, '=');
488       if (!key)
489         continue;
490
491       if (0 == strncmp(key, "CODENAME", 8))
492         codename = line->data;
493     }
494
495   return apr_psprintf(pool, "%s%s%s%s",
496                       release,
497                       (codename ? " (" : ""),
498                       (codename ? codename : ""),
499                       (codename ? ")" : ""));
500 }
501
502 /* Look at /etc/debian_version to detect non-LSB Debian. */
503 static const char *
504 debian_release(apr_pool_t *pool)
505 {
506   svn_stringbuf_t *buffer = read_file_contents("/etc/debian_version", pool);
507   if (!buffer)
508       return NULL;
509
510   stringbuf_first_line_only(buffer);
511   return apr_pstrcat(pool, "Debian ", buffer->data, NULL);
512 }
513
514 /* Try to find the Linux distribution name, or return info from uname. */
515 static const char *
516 linux_release_name(apr_pool_t *pool)
517 {
518   const char *uname_release = release_name_from_uname(pool);
519
520   /* Try anything that has /usr/bin/lsb_release.
521      Covers, for example, Debian, Ubuntu and SuSE.  */
522   const char *release_name = lsb_release(pool);
523
524   /* Try RHEL/Fedora/CentOS */
525   if (!release_name)
526     release_name = redhat_release(pool);
527
528   /* Try Non-LSB SuSE */
529   if (!release_name)
530     release_name = suse_release(pool);
531
532   /* Try non-LSB Debian */
533   if (!release_name)
534     release_name = debian_release(pool);
535
536   if (!release_name)
537     return uname_release;
538
539   if (!uname_release)
540     return release_name;
541
542   return apr_psprintf(pool, "%s [%s]", release_name, uname_release);
543 }
544 #endif /* __linux__ */
545
546 \f
547 #ifdef WIN32
548 typedef DWORD (WINAPI *FNGETNATIVESYSTEMINFO)(LPSYSTEM_INFO);
549 typedef BOOL (WINAPI *FNENUMPROCESSMODULES) (HANDLE, HMODULE, DWORD, LPDWORD);
550
551 /* Get system and version info, and try to tell the difference
552    between the native system type and the runtime environment of the
553    current process. Populate results in SYSINFO, LOCAL_SYSINFO
554    (optional) and OSINFO. */
555 static BOOL
556 system_info(SYSTEM_INFO *sysinfo,
557             SYSTEM_INFO *local_sysinfo,
558             OSVERSIONINFOEXW *osinfo)
559 {
560   FNGETNATIVESYSTEMINFO GetNativeSystemInfo_ = (FNGETNATIVESYSTEMINFO)
561     GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetNativeSystemInfo");
562
563   ZeroMemory(sysinfo, sizeof *sysinfo);
564   if (local_sysinfo)
565     {
566       ZeroMemory(local_sysinfo, sizeof *local_sysinfo);
567       GetSystemInfo(local_sysinfo);
568       if (GetNativeSystemInfo_)
569         GetNativeSystemInfo_(sysinfo);
570       else
571         memcpy(sysinfo, local_sysinfo, sizeof *sysinfo);
572     }
573   else
574     GetSystemInfo(sysinfo);
575
576   ZeroMemory(osinfo, sizeof *osinfo);
577   osinfo->dwOSVersionInfoSize = sizeof *osinfo;
578   if (!GetVersionExW((LPVOID)osinfo))
579     return FALSE;
580
581   return TRUE;
582 }
583
584 /* Map the proccessor type from SYSINFO to a string. */
585 static const char *
586 processor_name(SYSTEM_INFO *sysinfo)
587 {
588   switch (sysinfo->wProcessorArchitecture)
589     {
590     case PROCESSOR_ARCHITECTURE_AMD64:         return "x86_64";
591     case PROCESSOR_ARCHITECTURE_IA64:          return "ia64";
592     case PROCESSOR_ARCHITECTURE_INTEL:         return "x86";
593     case PROCESSOR_ARCHITECTURE_MIPS:          return "mips";
594     case PROCESSOR_ARCHITECTURE_ALPHA:         return "alpha32";
595     case PROCESSOR_ARCHITECTURE_PPC:           return "powerpc";
596     case PROCESSOR_ARCHITECTURE_SHX:           return "shx";
597     case PROCESSOR_ARCHITECTURE_ARM:           return "arm";
598     case PROCESSOR_ARCHITECTURE_ALPHA64:       return "alpha";
599     case PROCESSOR_ARCHITECTURE_MSIL:          return "msil";
600     case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64: return "x86_wow64";
601     default: return "unknown";
602     }
603 }
604
605 /* Return the Windows-specific canonical host name. */
606 static const char *
607 win32_canonical_host(apr_pool_t *pool)
608 {
609   SYSTEM_INFO sysinfo;
610   SYSTEM_INFO local_sysinfo;
611   OSVERSIONINFOEXW osinfo;
612
613   if (system_info(&sysinfo, &local_sysinfo, &osinfo))
614     {
615       const char *arch = processor_name(&local_sysinfo);
616       const char *machine = processor_name(&sysinfo);
617       const char *vendor = "microsoft";
618       const char *sysname = "windows";
619       const char *sysver = apr_psprintf(pool, "%u.%u.%u",
620                                         (unsigned int)osinfo.dwMajorVersion,
621                                         (unsigned int)osinfo.dwMinorVersion,
622                                         (unsigned int)osinfo.dwBuildNumber);
623
624       if (sysinfo.wProcessorArchitecture
625           == local_sysinfo.wProcessorArchitecture)
626         return apr_psprintf(pool, "%s-%s-%s%s",
627                             machine, vendor, sysname, sysver);
628       return apr_psprintf(pool, "%s/%s-%s-%s%s",
629                           arch, machine, vendor, sysname, sysver);
630     }
631
632   return "unknown-microsoft-windows";
633 }
634
635 /* Convert a Unicode string to UTF-8. */
636 static char *
637 wcs_to_utf8(const wchar_t *wcs, apr_pool_t *pool)
638 {
639   const int bufsize = WideCharToMultiByte(CP_UTF8, 0, wcs, -1,
640                                           NULL, 0, NULL, NULL);
641   if (bufsize > 0)
642     {
643       char *const utf8 = apr_palloc(pool, bufsize + 1);
644       WideCharToMultiByte(CP_UTF8, 0, wcs, -1, utf8, bufsize, NULL, NULL);
645       return utf8;
646     }
647   return NULL;
648 }
649
650 /* Query the value called NAME of the registry key HKEY. */
651 static char *
652 registry_value(HKEY hkey, wchar_t *name, apr_pool_t *pool)
653 {
654   DWORD size;
655   wchar_t *value;
656
657   if (RegQueryValueExW(hkey, name, NULL, NULL, NULL, &size))
658     return NULL;
659
660   value = apr_palloc(pool, size + sizeof *value);
661   if (RegQueryValueExW(hkey, name, NULL, NULL, (void*)value, &size))
662     return NULL;
663   value[size / sizeof *value] = 0;
664   return wcs_to_utf8(value, pool);
665 }
666
667 /* Try to glean the Windows release name and associated info from the
668    registry. Failing that, construct a release name from the version
669    info. */
670 static const char *
671 win32_release_name(apr_pool_t *pool)
672 {
673   SYSTEM_INFO sysinfo;
674   OSVERSIONINFOEXW osinfo;
675   HKEY hkcv;
676
677   if (!system_info(&sysinfo, NULL, &osinfo))
678     return NULL;
679
680   if (!RegOpenKeyExW(HKEY_LOCAL_MACHINE,
681                      L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
682                      0, KEY_QUERY_VALUE, &hkcv))
683     {
684       const char *release = registry_value(hkcv, L"ProductName", pool);
685       const char *spack = registry_value(hkcv, L"CSDVersion", pool);
686       const char *curver = registry_value(hkcv, L"CurrentVersion", pool);
687       const char *curtype = registry_value(hkcv, L"CurrentType", pool);
688       const char *install = registry_value(hkcv, L"InstallationType", pool);
689       const char *curbuild = registry_value(hkcv, L"CurrentBuildNumber", pool);
690
691       if (!spack && *osinfo.szCSDVersion)
692         spack = wcs_to_utf8(osinfo.szCSDVersion, pool);
693
694       if (!curbuild)
695         curbuild = registry_value(hkcv, L"CurrentBuild", pool);
696
697       if (release || spack || curver || curtype || curbuild)
698         {
699           const char *bootinfo = "";
700           if (curver || install || curtype)
701             {
702               bootinfo = apr_psprintf(pool, "[%s%s%s%s%s]",
703                                       (curver ? curver : ""),
704                                       (install ? (curver ? " " : "") : ""),
705                                       (install ? install : ""),
706                                       (curtype
707                                        ? (curver||install ? " " : "")
708                                        : ""),
709                                       (curtype ? curtype : ""));
710             }
711
712           return apr_psprintf(pool, "%s%s%s%s%s%s%s",
713                               (release ? release : ""),
714                               (spack ? (release ? ", " : "") : ""),
715                               (spack ? spack : ""),
716                               (curbuild
717                                ? (release||spack ? ", build " : "build ")
718                                : ""),
719                               (curbuild ? curbuild : ""),
720                               (bootinfo
721                                ? (release||spack||curbuild ? " " : "")
722                                : ""),
723                               (bootinfo ? bootinfo : ""));
724         }
725     }
726
727   if (*osinfo.szCSDVersion)
728     {
729       const char *servicepack = wcs_to_utf8(osinfo.szCSDVersion, pool);
730
731       if (servicepack)
732         return apr_psprintf(pool, "Windows NT %u.%u, %s, build %u",
733                             (unsigned int)osinfo.dwMajorVersion,
734                             (unsigned int)osinfo.dwMinorVersion,
735                             servicepack,
736                             (unsigned int)osinfo.dwBuildNumber);
737
738       /* Assume wServicePackMajor > 0 if szCSDVersion is not empty */
739       if (osinfo.wServicePackMinor)
740         return apr_psprintf(pool, "Windows NT %u.%u SP%u.%u, build %u",
741                             (unsigned int)osinfo.dwMajorVersion,
742                             (unsigned int)osinfo.dwMinorVersion,
743                             (unsigned int)osinfo.wServicePackMajor,
744                             (unsigned int)osinfo.wServicePackMinor,
745                             (unsigned int)osinfo.dwBuildNumber);
746
747       return apr_psprintf(pool, "Windows NT %u.%u SP%u, build %u",
748                           (unsigned int)osinfo.dwMajorVersion,
749                           (unsigned int)osinfo.dwMinorVersion,
750                           (unsigned int)osinfo.wServicePackMajor,
751                           (unsigned int)osinfo.dwBuildNumber);
752     }
753
754   return apr_psprintf(pool, "Windows NT %u.%u, build %u",
755                       (unsigned int)osinfo.dwMajorVersion,
756                       (unsigned int)osinfo.dwMinorVersion,
757                       (unsigned int)osinfo.dwBuildNumber);
758 }
759
760
761 /* Get a list of handles of shared libs loaded by the current
762    process. Returns a NULL-terminated array alocated from POOL. */
763 static HMODULE *
764 enum_loaded_modules(apr_pool_t *pool)
765 {
766   HANDLE current = GetCurrentProcess();
767   HMODULE dummy[1];
768   HMODULE *handles;
769   DWORD size;
770
771   if (!EnumProcessModules(current, dummy, sizeof(dummy), &size))
772     return NULL;
773
774   handles = apr_palloc(pool, size + sizeof *handles);
775   if (!EnumProcessModules(current, handles, size, &size))
776     return NULL;
777   handles[size / sizeof *handles] = NULL;
778   return handles;
779 }
780
781 /* Find the version number, if any, embedded in FILENAME. */
782 static const char *
783 file_version_number(const wchar_t *filename, apr_pool_t *pool)
784 {
785   VS_FIXEDFILEINFO info;
786   unsigned int major, minor, micro, nano;
787   void *data;
788   DWORD data_size = GetFileVersionInfoSizeW(filename, NULL);
789   void *vinfo;
790   UINT vinfo_size;
791
792   if (!data_size)
793     return NULL;
794
795   data = apr_palloc(pool, data_size);
796   if (!GetFileVersionInfoW(filename, 0, data_size, data))
797     return NULL;
798
799   if (!VerQueryValueW(data, L"\\", &vinfo, &vinfo_size))
800     return NULL;
801
802   if (vinfo_size != sizeof info)
803     return NULL;
804
805   memcpy(&info, vinfo, sizeof info);
806   major = (info.dwFileVersionMS >> 16) & 0xFFFF;
807   minor = info.dwFileVersionMS & 0xFFFF;
808   micro = (info.dwFileVersionLS >> 16) & 0xFFFF;
809   nano = info.dwFileVersionLS & 0xFFFF;
810
811   if (!nano)
812     {
813       if (!micro)
814         return apr_psprintf(pool, "%u.%u", major, minor);
815       else
816         return apr_psprintf(pool, "%u.%u.%u", major, minor, micro);
817     }
818   return apr_psprintf(pool, "%u.%u.%u.%u", major, minor, micro, nano);
819 }
820
821 /* List the shared libraries loaded by the current process. */
822 static const apr_array_header_t *
823 win32_shared_libs(apr_pool_t *pool)
824 {
825   apr_array_header_t *array = NULL;
826   wchar_t buffer[MAX_PATH + 1];
827   HMODULE *handles = enum_loaded_modules(pool);
828   HMODULE *module;
829
830   for (module = handles; module && *module; ++module)
831     {
832       const char *filename;
833       const char *version;
834       if (GetModuleFileNameW(*module, buffer, MAX_PATH))
835         {
836           buffer[MAX_PATH] = 0;
837
838           version = file_version_number(buffer, pool);
839           filename = wcs_to_utf8(buffer, pool);
840           if (filename)
841             {
842               svn_version_ext_loaded_lib_t *lib;
843
844               if (!array)
845                 {
846                   array = apr_array_make(pool, 32, sizeof(*lib));
847                 }
848               lib = &APR_ARRAY_PUSH(array, svn_version_ext_loaded_lib_t);
849               lib->name = svn_dirent_local_style(filename, pool);
850               lib->version = version;
851             }
852         }
853     }
854
855   return array;
856 }
857 #endif /* WIN32 */
858
859 \f
860 #ifdef SVN_HAVE_MACOS_PLIST
861 /* Load the SystemVersion.plist or ServerVersion.plist file into a
862    property list. Set SERVER to TRUE if the file read was
863    ServerVersion.plist. */
864 static CFDictionaryRef
865 system_version_plist(svn_boolean_t *server, apr_pool_t *pool)
866 {
867   static const UInt8 server_version[] =
868     "/System/Library/CoreServices/ServerVersion.plist";
869   static const UInt8 system_version[] =
870     "/System/Library/CoreServices/SystemVersion.plist";
871
872   CFPropertyListRef plist = NULL;
873   CFDataRef resource = NULL;
874   CFStringRef errstr = NULL;
875   CFURLRef url = NULL;
876   SInt32 errcode;
877
878   url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
879                                                 server_version,
880                                                 sizeof(server_version) - 1,
881                                                 FALSE);
882   if (!url)
883     return NULL;
884
885   if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault,
886                                                 url, &resource,
887                                                 NULL, NULL, &errcode))
888     {
889       CFRelease(url);
890       url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
891                                                     system_version,
892                                                     sizeof(system_version) - 1,
893                                                     FALSE);
894       if (!url)
895         return NULL;
896
897       if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault,
898                                                     url, &resource,
899                                                     NULL, NULL, &errcode))
900         {
901           CFRelease(url);
902           return NULL;
903         }
904       else
905         {
906           CFRelease(url);
907           *server = FALSE;
908         }
909     }
910   else
911     {
912       CFRelease(url);
913       *server = TRUE;
914     }
915
916   /* ### CFPropertyListCreateFromXMLData is obsolete, but its
917          replacement CFPropertyListCreateWithData is only available
918          from Mac OS 1.6 onward. */
919   plist = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, resource,
920                                           kCFPropertyListImmutable,
921                                           &errstr);
922   if (resource)
923     CFRelease(resource);
924   if (errstr)
925     CFRelease(errstr);
926
927   if (CFDictionaryGetTypeID() != CFGetTypeID(plist))
928     {
929       /* Oops ... this really should be a dict. */
930       CFRelease(plist);
931       return NULL;
932     }
933
934   return plist;
935 }
936
937 /* Return the value for KEY from PLIST, or NULL if not available. */
938 static const char *
939 value_from_dict(CFDictionaryRef plist, CFStringRef key, apr_pool_t *pool)
940 {
941   CFStringRef valref;
942   CFIndex bufsize;
943   const void *valptr;
944   const char *value;
945
946   if (!CFDictionaryGetValueIfPresent(plist, key, &valptr))
947     return NULL;
948
949   valref = valptr;
950   if (CFStringGetTypeID() != CFGetTypeID(valref))
951     return NULL;
952
953   value = CFStringGetCStringPtr(valref, kCFStringEncodingUTF8);
954   if (value)
955     return apr_pstrdup(pool, value);
956
957   bufsize =  5 * CFStringGetLength(valref) + 1;
958   value = apr_palloc(pool, bufsize);
959   if (!CFStringGetCString(valref, (char*)value, bufsize,
960                           kCFStringEncodingUTF8))
961     value = NULL;
962
963   return value;
964 }
965
966 /* Return the commercial name of the OS, given the version number in
967    a format that matches the regular expression /^10\.\d+(\..*)?$/ */
968 static const char *
969 release_name_from_version(const char *osver)
970 {
971   char *end = NULL;
972   unsigned long num = strtoul(osver, &end, 10);
973
974   if (!end || *end != '.' || num != 10)
975     return NULL;
976
977   osver = end + 1;
978   end = NULL;
979   num = strtoul(osver, &end, 10);
980   if (!end || (*end && *end != '.'))
981     return NULL;
982
983   /* See http://en.wikipedia.org/wiki/History_of_OS_X#Release_timeline */
984   switch(num)
985     {
986     case 0: return "Cheetah";
987     case 1: return "Puma";
988     case 2: return "Jaguar";
989     case 3: return "Panther";
990     case 4: return "Tiger";
991     case 5: return "Leopard";
992     case 6: return "Snow Leopard";
993     case 7: return "Lion";
994     case 8: return "Mountain Lion";
995     }
996
997   return NULL;
998 }
999
1000 /* Construct the release name from information stored in the Mac OS X
1001    "SystemVersion.plist" file (or ServerVersion.plist, for Mac Os
1002    Server. */
1003 static const char *
1004 macos_release_name(apr_pool_t *pool)
1005 {
1006   svn_boolean_t server;
1007   CFDictionaryRef plist = system_version_plist(&server, pool);
1008
1009   if (plist)
1010     {
1011       const char *osname = value_from_dict(plist, CFSTR("ProductName"), pool);
1012       const char *osver = value_from_dict(plist,
1013                                           CFSTR("ProductUserVisibleVersion"),
1014                                           pool);
1015       const char *build = value_from_dict(plist,
1016                                           CFSTR("ProductBuildVersion"),
1017                                           pool);
1018       const char *release;
1019
1020       if (!osver)
1021         osver = value_from_dict(plist, CFSTR("ProductVersion"), pool);
1022       release = release_name_from_version(osver);
1023
1024       CFRelease(plist);
1025       return apr_psprintf(pool, "%s%s%s%s%s%s%s%s",
1026                           (osname ? osname : ""),
1027                           (osver ? (osname ? " " : "") : ""),
1028                           (osver ? osver : ""),
1029                           (release ? (osname||osver ? " " : "") : ""),
1030                           (release ? release : ""),
1031                           (build
1032                            ? (osname||osver||release ? ", " : "")
1033                            : ""),
1034                           (build
1035                            ? (server ? "server build " : "build ")
1036                            : ""),
1037                           (build ? build : ""));
1038     }
1039
1040   return NULL;
1041 }
1042 #endif  /* SVN_HAVE_MACOS_PLIST */
1043
1044 #ifdef SVN_HAVE_MACHO_ITERATE
1045 /* List the shared libraries loaded by the current process.
1046    Ignore frameworks and system libraries, they're just clutter. */
1047 static const apr_array_header_t *
1048 macos_shared_libs(apr_pool_t *pool)
1049 {
1050   static const char slb_prefix[] = "/usr/lib/system/";
1051   static const char fwk_prefix[] = "/System/Library/Frameworks/";
1052   static const char pfk_prefix[] = "/System/Library/PrivateFrameworks/";
1053
1054   const size_t slb_prefix_len = strlen(slb_prefix);
1055   const size_t fwk_prefix_len = strlen(fwk_prefix);
1056   const size_t pfk_prefix_len = strlen(pfk_prefix);
1057
1058   apr_array_header_t *result = NULL;
1059   apr_array_header_t *dylibs = NULL;
1060
1061   uint32_t i;
1062   for (i = 0;; ++i)
1063     {
1064       const struct mach_header *header = _dyld_get_image_header(i);
1065       const char *filename = _dyld_get_image_name(i);
1066       const char *version;
1067       char *truename;
1068       svn_version_ext_loaded_lib_t *lib;
1069
1070       if (!(header && filename))
1071         break;
1072
1073       switch (header->cputype)
1074         {
1075         case CPU_TYPE_I386:      version = _("Intel"); break;
1076         case CPU_TYPE_X86_64:    version = _("Intel 64-bit"); break;
1077         case CPU_TYPE_POWERPC:   version = _("PowerPC"); break;
1078         case CPU_TYPE_POWERPC64: version = _("PowerPC 64-bit"); break;
1079         default:
1080           version = NULL;
1081         }
1082
1083       if (0 == apr_filepath_merge(&truename, "", filename,
1084                                   APR_FILEPATH_NATIVE
1085                                   | APR_FILEPATH_TRUENAME,
1086                                   pool))
1087         filename = truename;
1088       else
1089         filename = apr_pstrdup(pool, filename);
1090
1091       if (0 == strncmp(filename, slb_prefix, slb_prefix_len)
1092           || 0 == strncmp(filename, fwk_prefix, fwk_prefix_len)
1093           || 0 == strncmp(filename, pfk_prefix, pfk_prefix_len))
1094         {
1095           /* Ignore frameworks and system libraries. */
1096           continue;
1097         }
1098
1099       if (header->filetype == MH_EXECUTE)
1100         {
1101           /* Make sure the program filename is first in the list */
1102           if (!result)
1103             {
1104               result = apr_array_make(pool, 32, sizeof(*lib));
1105             }
1106           lib = &APR_ARRAY_PUSH(result, svn_version_ext_loaded_lib_t);
1107         }
1108       else
1109         {
1110           if (!dylibs)
1111             {
1112               dylibs = apr_array_make(pool, 32, sizeof(*lib));
1113             }
1114           lib = &APR_ARRAY_PUSH(dylibs, svn_version_ext_loaded_lib_t);
1115         }
1116
1117       lib->name = filename;
1118       lib->version = version;
1119     }
1120
1121   /* Gather results into one array. */
1122   if (dylibs)
1123     {
1124       if (result)
1125         apr_array_cat(result, dylibs);
1126       else
1127         result = dylibs;
1128     }
1129
1130   return result;
1131 }
1132 #endif  /* SVN_HAVE_MACHO_ITERATE */