1251881Speter/*
2251881Speter * sysinfo.c :  information about the running system
3251881Speter *
4251881Speter * ====================================================================
5251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
6251881Speter *    or more contributor license agreements.  See the NOTICE file
7251881Speter *    distributed with this work for additional information
8251881Speter *    regarding copyright ownership.  The ASF licenses this file
9251881Speter *    to you under the Apache License, Version 2.0 (the
10251881Speter *    "License"); you may not use this file except in compliance
11251881Speter *    with the License.  You may obtain a copy of the License at
12251881Speter *
13251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
14251881Speter *
15251881Speter *    Unless required by applicable law or agreed to in writing,
16251881Speter *    software distributed under the License is distributed on an
17251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18251881Speter *    KIND, either express or implied.  See the License for the
19251881Speter *    specific language governing permissions and limitations
20251881Speter *    under the License.
21251881Speter * ====================================================================
22251881Speter */
23251881Speter
24251881Speter
25251881Speter
26251881Speter#define APR_WANT_STRFUNC
27251881Speter#include <apr_want.h>
28251881Speter
29251881Speter#include <apr_lib.h>
30251881Speter#include <apr_pools.h>
31251881Speter#include <apr_file_info.h>
32251881Speter#include <apr_signal.h>
33251881Speter#include <apr_strings.h>
34251881Speter#include <apr_thread_proc.h>
35251881Speter#include <apr_version.h>
36251881Speter#include <apu_version.h>
37251881Speter
38251881Speter#include "svn_pools.h"
39251881Speter#include "svn_ctype.h"
40251881Speter#include "svn_dirent_uri.h"
41251881Speter#include "svn_error.h"
42251881Speter#include "svn_io.h"
43251881Speter#include "svn_string.h"
44251881Speter#include "svn_utf.h"
45251881Speter#include "svn_version.h"
46251881Speter
47251881Speter#include "private/svn_sqlite.h"
48299742Sdim#include "private/svn_subr_private.h"
49299742Sdim#include "private/svn_utf_private.h"
50251881Speter
51251881Speter#include "sysinfo.h"
52251881Speter#include "svn_private_config.h"
53251881Speter
54251881Speter#if HAVE_SYS_UTSNAME_H
55251881Speter#include <sys/utsname.h>
56251881Speter#endif
57251881Speter
58251881Speter#ifdef SVN_HAVE_MACOS_PLIST
59251881Speter#include <CoreFoundation/CoreFoundation.h>
60299742Sdim#include <AvailabilityMacros.h>
61299742Sdim# ifndef MAC_OS_X_VERSION_10_6
62299742Sdim#  define MAC_OS_X_VERSION_10_6  1060
63299742Sdim# endif
64251881Speter#endif
65251881Speter
66251881Speter#ifdef SVN_HAVE_MACHO_ITERATE
67251881Speter#include <mach-o/dyld.h>
68251881Speter#include <mach-o/loader.h>
69251881Speter#endif
70251881Speter
71251881Speter#if HAVE_UNAME
72251881Speterstatic const char *canonical_host_from_uname(apr_pool_t *pool);
73251881Speter# ifndef SVN_HAVE_MACOS_PLIST
74251881Speterstatic const char *release_name_from_uname(apr_pool_t *pool);
75251881Speter# endif
76251881Speter#endif
77251881Speter
78251881Speter#ifdef WIN32
79251881Speterstatic const char *win32_canonical_host(apr_pool_t *pool);
80251881Speterstatic const char *win32_release_name(apr_pool_t *pool);
81251881Speterstatic const apr_array_header_t *win32_shared_libs(apr_pool_t *pool);
82251881Speter#endif /* WIN32 */
83251881Speter
84251881Speter#ifdef SVN_HAVE_MACOS_PLIST
85251881Speterstatic const char *macos_release_name(apr_pool_t *pool);
86251881Speter#endif
87251881Speter
88251881Speter#ifdef SVN_HAVE_MACHO_ITERATE
89251881Speterstatic const apr_array_header_t *macos_shared_libs(apr_pool_t *pool);
90251881Speter#endif
91251881Speter
92251881Speter
93251881Speter#if __linux__
94251881Speterstatic const char *linux_release_name(apr_pool_t *pool);
95251881Speter#endif
96251881Speter
97251881Speterconst char *
98251881Spetersvn_sysinfo__canonical_host(apr_pool_t *pool)
99251881Speter{
100251881Speter#ifdef WIN32
101251881Speter  return win32_canonical_host(pool);
102251881Speter#elif HAVE_UNAME
103251881Speter  return canonical_host_from_uname(pool);
104251881Speter#else
105251881Speter  return "unknown-unknown-unknown";
106251881Speter#endif
107251881Speter}
108251881Speter
109251881Speter
110251881Speterconst char *
111251881Spetersvn_sysinfo__release_name(apr_pool_t *pool)
112251881Speter{
113251881Speter#ifdef WIN32
114251881Speter  return win32_release_name(pool);
115251881Speter#elif defined(SVN_HAVE_MACOS_PLIST)
116251881Speter  return macos_release_name(pool);
117251881Speter#elif __linux__
118251881Speter  return linux_release_name(pool);
119251881Speter#elif HAVE_UNAME
120251881Speter  return release_name_from_uname(pool);
121251881Speter#else
122251881Speter  return NULL;
123251881Speter#endif
124251881Speter}
125251881Speter
126251881Speterconst apr_array_header_t *
127251881Spetersvn_sysinfo__linked_libs(apr_pool_t *pool)
128251881Speter{
129251881Speter  svn_version_ext_linked_lib_t *lib;
130299742Sdim  apr_array_header_t *array = apr_array_make(pool, 6, sizeof(*lib));
131251881Speter
132251881Speter  lib = &APR_ARRAY_PUSH(array, svn_version_ext_linked_lib_t);
133251881Speter  lib->name = "APR";
134251881Speter  lib->compiled_version = APR_VERSION_STRING;
135251881Speter  lib->runtime_version = apr_pstrdup(pool, apr_version_string());
136251881Speter
137251881Speter/* Don't list APR-Util if it isn't linked in, which it may not be if
138251881Speter * we're using APR 2.x+ which combined APR-Util into APR. */
139251881Speter#ifdef APU_VERSION_STRING
140251881Speter  lib = &APR_ARRAY_PUSH(array, svn_version_ext_linked_lib_t);
141251881Speter  lib->name = "APR-Util";
142251881Speter  lib->compiled_version = APU_VERSION_STRING;
143251881Speter  lib->runtime_version = apr_pstrdup(pool, apu_version_string());
144251881Speter#endif
145251881Speter
146251881Speter  lib = &APR_ARRAY_PUSH(array, svn_version_ext_linked_lib_t);
147299742Sdim  lib->name = "Expat";
148299742Sdim  lib->compiled_version = apr_pstrdup(pool, svn_xml__compiled_version());
149299742Sdim  lib->runtime_version = apr_pstrdup(pool, svn_xml__runtime_version());
150299742Sdim
151299742Sdim  lib = &APR_ARRAY_PUSH(array, svn_version_ext_linked_lib_t);
152251881Speter  lib->name = "SQLite";
153251881Speter  lib->compiled_version = apr_pstrdup(pool, svn_sqlite__compiled_version());
154251881Speter#ifdef SVN_SQLITE_INLINE
155251881Speter  lib->runtime_version = NULL;
156251881Speter#else
157251881Speter  lib->runtime_version = apr_pstrdup(pool, svn_sqlite__runtime_version());
158251881Speter#endif
159251881Speter
160299742Sdim  lib = &APR_ARRAY_PUSH(array, svn_version_ext_linked_lib_t);
161299742Sdim  lib->name = "Utf8proc";
162299742Sdim  lib->compiled_version = apr_pstrdup(pool, svn_utf__utf8proc_compiled_version());
163299742Sdim  lib->runtime_version = apr_pstrdup(pool, svn_utf__utf8proc_runtime_version());
164299742Sdim
165299742Sdim  lib = &APR_ARRAY_PUSH(array, svn_version_ext_linked_lib_t);
166299742Sdim  lib->name = "ZLib";
167299742Sdim  lib->compiled_version = apr_pstrdup(pool, svn_zlib__compiled_version());
168299742Sdim  lib->runtime_version = apr_pstrdup(pool, svn_zlib__runtime_version());
169299742Sdim
170251881Speter  return array;
171251881Speter}
172251881Speter
173251881Speterconst apr_array_header_t *
174251881Spetersvn_sysinfo__loaded_libs(apr_pool_t *pool)
175251881Speter{
176251881Speter#ifdef WIN32
177251881Speter  return win32_shared_libs(pool);
178251881Speter#elif defined(SVN_HAVE_MACHO_ITERATE)
179251881Speter  return macos_shared_libs(pool);
180251881Speter#else
181251881Speter  return NULL;
182251881Speter#endif
183251881Speter}
184251881Speter
185251881Speter
186251881Speter#if HAVE_UNAME
187251881Speterstatic const char*
188251881Spetercanonical_host_from_uname(apr_pool_t *pool)
189251881Speter{
190251881Speter  const char *machine = "unknown";
191251881Speter  const char *vendor = "unknown";
192251881Speter  const char *sysname = "unknown";
193251881Speter  const char *sysver = "";
194251881Speter  struct utsname info;
195251881Speter
196251881Speter  if (0 <= uname(&info))
197251881Speter    {
198251881Speter      svn_error_t *err;
199251881Speter      const char *tmp;
200251881Speter
201251881Speter      err = svn_utf_cstring_to_utf8(&tmp, info.machine, pool);
202251881Speter      if (err)
203251881Speter        svn_error_clear(err);
204251881Speter      else
205251881Speter        machine = tmp;
206251881Speter
207251881Speter      err = svn_utf_cstring_to_utf8(&tmp, info.sysname, pool);
208251881Speter      if (err)
209251881Speter        svn_error_clear(err);
210251881Speter      else
211251881Speter        {
212251881Speter          char *lwr = apr_pstrdup(pool, tmp);
213251881Speter          char *it = lwr;
214251881Speter          while (*it)
215251881Speter            {
216251881Speter              if (svn_ctype_isupper(*it))
217251881Speter                *it = apr_tolower(*it);
218251881Speter              ++it;
219251881Speter            }
220251881Speter          sysname = lwr;
221251881Speter        }
222251881Speter
223251881Speter      if (0 == strcmp(sysname, "darwin"))
224251881Speter        vendor = "apple";
225251881Speter      if (0 == strcmp(sysname, "linux"))
226251881Speter        sysver = "-gnu";
227251881Speter      else
228251881Speter        {
229251881Speter          err = svn_utf_cstring_to_utf8(&tmp, info.release, pool);
230251881Speter          if (err)
231251881Speter            svn_error_clear(err);
232251881Speter          else
233251881Speter            {
234251881Speter              apr_size_t n = strspn(tmp, ".0123456789");
235251881Speter              if (n > 0)
236251881Speter                {
237251881Speter                  char *ver = apr_pstrdup(pool, tmp);
238251881Speter                  ver[n] = 0;
239251881Speter                  sysver = ver;
240251881Speter                }
241251881Speter              else
242251881Speter                sysver = tmp;
243251881Speter            }
244251881Speter        }
245251881Speter    }
246251881Speter
247251881Speter  return apr_psprintf(pool, "%s-%s-%s%s", machine, vendor, sysname, sysver);
248251881Speter}
249251881Speter
250251881Speter# ifndef SVN_HAVE_MACOS_PLIST
251251881Speter/* Generate a release name from the uname(3) info, effectively
252251881Speter   returning "`uname -s` `uname -r`". */
253251881Speterstatic const char *
254251881Speterrelease_name_from_uname(apr_pool_t *pool)
255251881Speter{
256251881Speter  struct utsname info;
257251881Speter  if (0 <= uname(&info))
258251881Speter    {
259251881Speter      svn_error_t *err;
260251881Speter      const char *sysname;
261251881Speter      const char *sysver;
262251881Speter
263251881Speter      err = svn_utf_cstring_to_utf8(&sysname, info.sysname, pool);
264251881Speter      if (err)
265251881Speter        {
266251881Speter          sysname = NULL;
267251881Speter          svn_error_clear(err);
268251881Speter        }
269251881Speter
270251881Speter
271251881Speter      err = svn_utf_cstring_to_utf8(&sysver, info.release, pool);
272251881Speter      if (err)
273251881Speter        {
274251881Speter          sysver = NULL;
275251881Speter          svn_error_clear(err);
276251881Speter        }
277251881Speter
278251881Speter      if (sysname || sysver)
279251881Speter        {
280251881Speter          return apr_psprintf(pool, "%s%s%s",
281251881Speter                              (sysname ? sysname : ""),
282251881Speter                              (sysver ? (sysname ? " " : "") : ""),
283251881Speter                              (sysver ? sysver : ""));
284251881Speter        }
285251881Speter    }
286251881Speter  return NULL;
287251881Speter}
288251881Speter# endif  /* !SVN_HAVE_MACOS_PLIST */
289251881Speter#endif  /* HAVE_UNAME */
290251881Speter
291251881Speter
292251881Speter#if __linux__
293251881Speter/* Split a stringbuf into a key/value pair.
294299742Sdim   Return the key, leaving the stripped value in the stringbuf. */
295251881Speterstatic const char *
296251881Speterstringbuf_split_key(svn_stringbuf_t *buffer, char delim)
297251881Speter{
298251881Speter  char *key;
299251881Speter  char *end;
300251881Speter
301251881Speter  end = strchr(buffer->data, delim);
302251881Speter  if (!end)
303251881Speter    return NULL;
304251881Speter
305251881Speter  svn_stringbuf_strip_whitespace(buffer);
306262253Speter
307262253Speter  /* Now we split the currently allocated buffer in two parts:
308262253Speter      - a const char * HEAD
309262253Speter      - the remaining stringbuf_t. */
310262253Speter
311262253Speter  /* Create HEAD as '\0' terminated const char * */
312251881Speter  key = buffer->data;
313251881Speter  end = strchr(key, delim);
314251881Speter  *end = '\0';
315262253Speter
316262253Speter  /* And update the TAIL to be a smaller, but still valid stringbuf */
317251881Speter  buffer->data = end + 1;
318262253Speter  buffer->len -= 1 + end - key;
319262253Speter  buffer->blocksize -= 1 + end - key;
320262253Speter
321251881Speter  svn_stringbuf_strip_whitespace(buffer);
322251881Speter
323251881Speter  return key;
324251881Speter}
325251881Speter
326251881Speter/* Parse `/usr/bin/lsb_rlease --all` */
327251881Speterstatic const char *
328251881Speterlsb_release(apr_pool_t *pool)
329251881Speter{
330251881Speter  static const char *const args[3] =
331251881Speter    {
332251881Speter      "/usr/bin/lsb_release",
333251881Speter      "--all",
334251881Speter      NULL
335251881Speter    };
336251881Speter
337251881Speter  const char *distributor = NULL;
338251881Speter  const char *description = NULL;
339251881Speter  const char *release = NULL;
340251881Speter  const char *codename = NULL;
341251881Speter
342251881Speter  apr_proc_t lsbproc;
343251881Speter  svn_stream_t *lsbinfo;
344251881Speter  svn_error_t *err;
345251881Speter
346251881Speter  /* Run /usr/bin/lsb_release --all < /dev/null 2>/dev/null */
347251881Speter  {
348251881Speter    apr_file_t *stdin_handle;
349251881Speter    apr_file_t *stdout_handle;
350251881Speter
351251881Speter    err = svn_io_file_open(&stdin_handle, SVN_NULL_DEVICE_NAME,
352251881Speter                           APR_READ, APR_OS_DEFAULT, pool);
353251881Speter    if (!err)
354251881Speter      err = svn_io_file_open(&stdout_handle, SVN_NULL_DEVICE_NAME,
355251881Speter                             APR_WRITE, APR_OS_DEFAULT, pool);
356251881Speter    if (!err)
357251881Speter      err = svn_io_start_cmd3(&lsbproc, NULL, args[0], args, NULL, FALSE,
358251881Speter                              FALSE, stdin_handle,
359251881Speter                              TRUE, NULL,
360251881Speter                              FALSE, stdout_handle,
361251881Speter                              pool);
362251881Speter    if (err)
363251881Speter      {
364251881Speter        svn_error_clear(err);
365251881Speter        return NULL;
366251881Speter      }
367251881Speter  }
368251881Speter
369251881Speter  /* Parse the output and try to populate the  */
370251881Speter  lsbinfo = svn_stream_from_aprfile2(lsbproc.out, TRUE, pool);
371251881Speter  if (lsbinfo)
372251881Speter    {
373251881Speter      for (;;)
374251881Speter        {
375251881Speter          svn_boolean_t eof = FALSE;
376251881Speter          svn_stringbuf_t *line;
377251881Speter          const char *key;
378251881Speter
379251881Speter          err = svn_stream_readline(lsbinfo, &line, "\n", &eof, pool);
380251881Speter          if (err || eof)
381251881Speter            break;
382251881Speter
383251881Speter          key = stringbuf_split_key(line, ':');
384251881Speter          if (!key)
385251881Speter            continue;
386251881Speter
387251881Speter          if (0 == svn_cstring_casecmp(key, "Distributor ID"))
388251881Speter            distributor = line->data;
389251881Speter          else if (0 == svn_cstring_casecmp(key, "Description"))
390251881Speter            description = line->data;
391251881Speter          else if (0 == svn_cstring_casecmp(key, "Release"))
392251881Speter            release = line->data;
393251881Speter          else if (0 == svn_cstring_casecmp(key, "Codename"))
394251881Speter            codename = line->data;
395251881Speter        }
396251881Speter      err = svn_error_compose_create(err,
397251881Speter                                     svn_stream_close(lsbinfo));
398251881Speter      if (err)
399251881Speter        {
400251881Speter          svn_error_clear(err);
401251881Speter          apr_proc_kill(&lsbproc, SIGKILL);
402251881Speter          return NULL;
403251881Speter        }
404251881Speter    }
405251881Speter
406251881Speter  /* Reap the child process */
407251881Speter  err = svn_io_wait_for_cmd(&lsbproc, "", NULL, NULL, pool);
408251881Speter  if (err)
409251881Speter    {
410251881Speter      svn_error_clear(err);
411251881Speter      return NULL;
412251881Speter    }
413251881Speter
414251881Speter  if (description)
415251881Speter    return apr_psprintf(pool, "%s%s%s%s", description,
416251881Speter                        (codename ? " (" : ""),
417251881Speter                        (codename ? codename : ""),
418251881Speter                        (codename ? ")" : ""));
419251881Speter  if (distributor)
420251881Speter    return apr_psprintf(pool, "%s%s%s%s%s%s", distributor,
421251881Speter                        (release ? " " : ""),
422251881Speter                        (release ? release : ""),
423251881Speter                        (codename ? " (" : ""),
424251881Speter                        (codename ? codename : ""),
425251881Speter                        (codename ? ")" : ""));
426251881Speter
427251881Speter  return NULL;
428251881Speter}
429251881Speter
430299742Sdim/* Read /etc/os-release, as documented here:
431299742Sdim * http://www.freedesktop.org/software/systemd/man/os-release.html
432299742Sdim */
433299742Sdimstatic const char *
434299742Sdimsystemd_release(apr_pool_t *pool)
435299742Sdim{
436299742Sdim  svn_error_t *err;
437299742Sdim  svn_stream_t *stream;
438299742Sdim
439299742Sdim  /* Open the file. */
440299742Sdim  err = svn_stream_open_readonly(&stream, "/etc/os-release", pool, pool);
441299742Sdim  if (err && APR_STATUS_IS_ENOENT(err->apr_err))
442299742Sdim    {
443299742Sdim      svn_error_clear(err);
444299742Sdim      err = svn_stream_open_readonly(&stream, "/usr/lib/os-release", pool,
445299742Sdim                                     pool);
446299742Sdim    }
447299742Sdim  if (err)
448299742Sdim    {
449299742Sdim      svn_error_clear(err);
450299742Sdim      return NULL;
451299742Sdim    }
452299742Sdim
453299742Sdim  /* Look for the PRETTY_NAME line. */
454299742Sdim  while (TRUE)
455299742Sdim    {
456299742Sdim      svn_stringbuf_t *line;
457299742Sdim      svn_boolean_t eof;
458299742Sdim
459299742Sdim      err = svn_stream_readline(stream, &line, "\n", &eof, pool);
460299742Sdim      if (err)
461299742Sdim        {
462299742Sdim          svn_error_clear(err);
463299742Sdim          return NULL;
464299742Sdim        }
465299742Sdim
466299742Sdim      if (!strncmp(line->data, "PRETTY_NAME=", 12))
467299742Sdim        {
468299742Sdim          svn_stringbuf_t *release_name;
469299742Sdim
470299742Sdim          /* The value may or may not be enclosed by double quotes.  We don't
471299742Sdim           * attempt to strip them. */
472299742Sdim          release_name = svn_stringbuf_create(line->data + 12, pool);
473299742Sdim          svn_error_clear(svn_stream_close(stream));
474299742Sdim          svn_stringbuf_strip_whitespace(release_name);
475299742Sdim          return release_name->data;
476299742Sdim        }
477299742Sdim
478299742Sdim      if (eof)
479299742Sdim        break;
480299742Sdim    }
481299742Sdim
482299742Sdim  /* The file did not contain a PRETTY_NAME line. */
483299742Sdim  svn_error_clear(svn_stream_close(stream));
484299742Sdim  return NULL;
485299742Sdim}
486299742Sdim
487251881Speter/* Read the whole contents of a file. */
488251881Speterstatic svn_stringbuf_t *
489251881Speterread_file_contents(const char *filename, apr_pool_t *pool)
490251881Speter{
491251881Speter  svn_error_t *err;
492251881Speter  svn_stringbuf_t *buffer;
493251881Speter
494251881Speter  err = svn_stringbuf_from_file2(&buffer, filename, pool);
495251881Speter  if (err)
496251881Speter    {
497251881Speter      svn_error_clear(err);
498251881Speter      return NULL;
499251881Speter    }
500251881Speter
501251881Speter  return buffer;
502251881Speter}
503251881Speter
504251881Speter/* Strip everything but the first line from a stringbuf. */
505251881Speterstatic void
506251881Speterstringbuf_first_line_only(svn_stringbuf_t *buffer)
507251881Speter{
508251881Speter  char *eol = strchr(buffer->data, '\n');
509251881Speter  if (eol)
510251881Speter    {
511251881Speter      *eol = '\0';
512251881Speter      buffer->len = 1 + eol - buffer->data;
513251881Speter    }
514251881Speter  svn_stringbuf_strip_whitespace(buffer);
515251881Speter}
516251881Speter
517251881Speter/* Look at /etc/redhat_release to detect RHEL/Fedora/CentOS. */
518251881Speterstatic const char *
519251881Speterredhat_release(apr_pool_t *pool)
520251881Speter{
521251881Speter  svn_stringbuf_t *buffer = read_file_contents("/etc/redhat-release", pool);
522251881Speter  if (buffer)
523251881Speter    {
524251881Speter      stringbuf_first_line_only(buffer);
525251881Speter      return buffer->data;
526251881Speter    }
527251881Speter  return NULL;
528251881Speter}
529251881Speter
530251881Speter/* Look at /etc/SuSE-release to detect non-LSB SuSE. */
531251881Speterstatic const char *
532251881Spetersuse_release(apr_pool_t *pool)
533251881Speter{
534251881Speter  const char *release = NULL;
535251881Speter  const char *codename = NULL;
536251881Speter
537251881Speter  svn_stringbuf_t *buffer = read_file_contents("/etc/SuSE-release", pool);
538251881Speter  svn_stringbuf_t *line;
539251881Speter  svn_stream_t *stream;
540251881Speter  svn_boolean_t eof;
541251881Speter  svn_error_t *err;
542251881Speter  if (!buffer)
543251881Speter      return NULL;
544251881Speter
545251881Speter  stream = svn_stream_from_stringbuf(buffer, pool);
546251881Speter  err = svn_stream_readline(stream, &line, "\n", &eof, pool);
547251881Speter  if (err || eof)
548251881Speter    {
549251881Speter      svn_error_clear(err);
550251881Speter      return NULL;
551251881Speter    }
552251881Speter
553251881Speter  svn_stringbuf_strip_whitespace(line);
554251881Speter  release = line->data;
555251881Speter
556251881Speter  for (;;)
557251881Speter    {
558251881Speter      const char *key;
559251881Speter
560251881Speter      err = svn_stream_readline(stream, &line, "\n", &eof, pool);
561251881Speter      if (err || eof)
562251881Speter        {
563251881Speter          svn_error_clear(err);
564251881Speter          break;
565251881Speter        }
566251881Speter
567251881Speter      key = stringbuf_split_key(line, '=');
568251881Speter      if (!key)
569251881Speter        continue;
570251881Speter
571251881Speter      if (0 == strncmp(key, "CODENAME", 8))
572251881Speter        codename = line->data;
573251881Speter    }
574251881Speter
575251881Speter  return apr_psprintf(pool, "%s%s%s%s",
576251881Speter                      release,
577251881Speter                      (codename ? " (" : ""),
578251881Speter                      (codename ? codename : ""),
579251881Speter                      (codename ? ")" : ""));
580251881Speter}
581251881Speter
582251881Speter/* Look at /etc/debian_version to detect non-LSB Debian. */
583251881Speterstatic const char *
584251881Speterdebian_release(apr_pool_t *pool)
585251881Speter{
586251881Speter  svn_stringbuf_t *buffer = read_file_contents("/etc/debian_version", pool);
587251881Speter  if (!buffer)
588251881Speter      return NULL;
589251881Speter
590251881Speter  stringbuf_first_line_only(buffer);
591299742Sdim  return apr_pstrcat(pool, "Debian ", buffer->data, SVN_VA_NULL);
592251881Speter}
593251881Speter
594251881Speter/* Try to find the Linux distribution name, or return info from uname. */
595251881Speterstatic const char *
596251881Speterlinux_release_name(apr_pool_t *pool)
597251881Speter{
598251881Speter  const char *uname_release = release_name_from_uname(pool);
599251881Speter
600251881Speter  /* Try anything that has /usr/bin/lsb_release.
601251881Speter     Covers, for example, Debian, Ubuntu and SuSE.  */
602251881Speter  const char *release_name = lsb_release(pool);
603251881Speter
604299742Sdim  /* Try the systemd way (covers Arch). */
605299742Sdim  if (!release_name)
606299742Sdim    release_name = systemd_release(pool);
607299742Sdim
608251881Speter  /* Try RHEL/Fedora/CentOS */
609251881Speter  if (!release_name)
610251881Speter    release_name = redhat_release(pool);
611251881Speter
612251881Speter  /* Try Non-LSB SuSE */
613251881Speter  if (!release_name)
614251881Speter    release_name = suse_release(pool);
615251881Speter
616251881Speter  /* Try non-LSB Debian */
617251881Speter  if (!release_name)
618251881Speter    release_name = debian_release(pool);
619251881Speter
620251881Speter  if (!release_name)
621251881Speter    return uname_release;
622251881Speter
623251881Speter  if (!uname_release)
624251881Speter    return release_name;
625251881Speter
626251881Speter  return apr_psprintf(pool, "%s [%s]", release_name, uname_release);
627251881Speter}
628251881Speter#endif /* __linux__ */
629251881Speter
630251881Speter
631251881Speter#ifdef WIN32
632251881Spetertypedef DWORD (WINAPI *FNGETNATIVESYSTEMINFO)(LPSYSTEM_INFO);
633262253Spetertypedef BOOL (WINAPI *FNENUMPROCESSMODULES) (HANDLE, HMODULE*, DWORD, LPDWORD);
634251881Speter
635299742Sdimsvn_boolean_t
636299742Sdimsvn_sysinfo___fill_windows_version(OSVERSIONINFOEXW *version_info)
637299742Sdim{
638299742Sdim  memset(version_info, 0, sizeof(*version_info));
639299742Sdim
640299742Sdim  version_info->dwOSVersionInfoSize = sizeof(*version_info);
641299742Sdim
642299742Sdim  /* Kill warnings with the Windows 8 and later platform SDK */
643299742Sdim#if _MSC_VER > 1600 && NTDDI_VERSION >= _0x06020000
644299742Sdim  /* Windows 8 deprecated the API to retrieve the Windows version to avoid
645299742Sdim     backwards compatibility problems... It might return a constant version
646299742Sdim     in future Windows versions... But let's kill the warning.
647299742Sdim
648299742Sdim     We can implementation this using a different function later. */
649299742Sdim#pragma warning(push)
650299742Sdim#pragma warning(disable: 4996)
651299742Sdim#endif
652299742Sdim
653299742Sdim  /* Prototype supports OSVERSIONINFO */
654299742Sdim  return GetVersionExW((LPVOID)version_info);
655299742Sdim#if _MSC_VER > 1600 && NTDDI_VERSION >= _0x06020000
656299742Sdim#pragma warning(pop)
657299742Sdim#pragma warning(disable: 4996)
658299742Sdim#endif
659299742Sdim}
660299742Sdim
661299742Sdim/* Get system info, and try to tell the difference between the native
662299742Sdim   system type and the runtime environment of the current process.
663299742Sdim   Populate results in SYSINFO and LOCAL_SYSINFO (optional). */
664251881Speterstatic BOOL
665251881Spetersystem_info(SYSTEM_INFO *sysinfo,
666299742Sdim            SYSTEM_INFO *local_sysinfo)
667251881Speter{
668251881Speter  FNGETNATIVESYSTEMINFO GetNativeSystemInfo_ = (FNGETNATIVESYSTEMINFO)
669251881Speter    GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetNativeSystemInfo");
670251881Speter
671299742Sdim  memset(sysinfo, 0, sizeof *sysinfo);
672251881Speter  if (local_sysinfo)
673251881Speter    {
674299742Sdim      memset(local_sysinfo, 0, sizeof *local_sysinfo);
675251881Speter      GetSystemInfo(local_sysinfo);
676251881Speter      if (GetNativeSystemInfo_)
677251881Speter        GetNativeSystemInfo_(sysinfo);
678251881Speter      else
679251881Speter        memcpy(sysinfo, local_sysinfo, sizeof *sysinfo);
680251881Speter    }
681251881Speter  else
682251881Speter    GetSystemInfo(sysinfo);
683251881Speter
684251881Speter  return TRUE;
685251881Speter}
686251881Speter
687251881Speter/* Map the proccessor type from SYSINFO to a string. */
688251881Speterstatic const char *
689251881Speterprocessor_name(SYSTEM_INFO *sysinfo)
690251881Speter{
691251881Speter  switch (sysinfo->wProcessorArchitecture)
692251881Speter    {
693251881Speter    case PROCESSOR_ARCHITECTURE_AMD64:         return "x86_64";
694251881Speter    case PROCESSOR_ARCHITECTURE_IA64:          return "ia64";
695251881Speter    case PROCESSOR_ARCHITECTURE_INTEL:         return "x86";
696251881Speter    case PROCESSOR_ARCHITECTURE_MIPS:          return "mips";
697251881Speter    case PROCESSOR_ARCHITECTURE_ALPHA:         return "alpha32";
698251881Speter    case PROCESSOR_ARCHITECTURE_PPC:           return "powerpc";
699251881Speter    case PROCESSOR_ARCHITECTURE_SHX:           return "shx";
700251881Speter    case PROCESSOR_ARCHITECTURE_ARM:           return "arm";
701251881Speter    case PROCESSOR_ARCHITECTURE_ALPHA64:       return "alpha";
702251881Speter    case PROCESSOR_ARCHITECTURE_MSIL:          return "msil";
703251881Speter    case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64: return "x86_wow64";
704251881Speter    default: return "unknown";
705251881Speter    }
706251881Speter}
707251881Speter
708251881Speter/* Return the Windows-specific canonical host name. */
709251881Speterstatic const char *
710251881Speterwin32_canonical_host(apr_pool_t *pool)
711251881Speter{
712251881Speter  SYSTEM_INFO sysinfo;
713251881Speter  SYSTEM_INFO local_sysinfo;
714251881Speter  OSVERSIONINFOEXW osinfo;
715251881Speter
716299742Sdim  if (system_info(&sysinfo, &local_sysinfo)
717299742Sdim      && svn_sysinfo___fill_windows_version(&osinfo))
718251881Speter    {
719251881Speter      const char *arch = processor_name(&local_sysinfo);
720251881Speter      const char *machine = processor_name(&sysinfo);
721251881Speter      const char *vendor = "microsoft";
722251881Speter      const char *sysname = "windows";
723251881Speter      const char *sysver = apr_psprintf(pool, "%u.%u.%u",
724251881Speter                                        (unsigned int)osinfo.dwMajorVersion,
725251881Speter                                        (unsigned int)osinfo.dwMinorVersion,
726251881Speter                                        (unsigned int)osinfo.dwBuildNumber);
727251881Speter
728251881Speter      if (sysinfo.wProcessorArchitecture
729251881Speter          == local_sysinfo.wProcessorArchitecture)
730251881Speter        return apr_psprintf(pool, "%s-%s-%s%s",
731251881Speter                            machine, vendor, sysname, sysver);
732251881Speter      return apr_psprintf(pool, "%s/%s-%s-%s%s",
733251881Speter                          arch, machine, vendor, sysname, sysver);
734251881Speter    }
735251881Speter
736251881Speter  return "unknown-microsoft-windows";
737251881Speter}
738251881Speter
739251881Speter/* Convert a Unicode string to UTF-8. */
740251881Speterstatic char *
741251881Speterwcs_to_utf8(const wchar_t *wcs, apr_pool_t *pool)
742251881Speter{
743251881Speter  const int bufsize = WideCharToMultiByte(CP_UTF8, 0, wcs, -1,
744251881Speter                                          NULL, 0, NULL, NULL);
745251881Speter  if (bufsize > 0)
746251881Speter    {
747251881Speter      char *const utf8 = apr_palloc(pool, bufsize + 1);
748251881Speter      WideCharToMultiByte(CP_UTF8, 0, wcs, -1, utf8, bufsize, NULL, NULL);
749251881Speter      return utf8;
750251881Speter    }
751251881Speter  return NULL;
752251881Speter}
753251881Speter
754251881Speter/* Query the value called NAME of the registry key HKEY. */
755251881Speterstatic char *
756251881Speterregistry_value(HKEY hkey, wchar_t *name, apr_pool_t *pool)
757251881Speter{
758251881Speter  DWORD size;
759251881Speter  wchar_t *value;
760251881Speter
761251881Speter  if (RegQueryValueExW(hkey, name, NULL, NULL, NULL, &size))
762251881Speter    return NULL;
763251881Speter
764251881Speter  value = apr_palloc(pool, size + sizeof *value);
765251881Speter  if (RegQueryValueExW(hkey, name, NULL, NULL, (void*)value, &size))
766251881Speter    return NULL;
767251881Speter  value[size / sizeof *value] = 0;
768251881Speter  return wcs_to_utf8(value, pool);
769251881Speter}
770251881Speter
771251881Speter/* Try to glean the Windows release name and associated info from the
772251881Speter   registry. Failing that, construct a release name from the version
773251881Speter   info. */
774251881Speterstatic const char *
775251881Speterwin32_release_name(apr_pool_t *pool)
776251881Speter{
777251881Speter  SYSTEM_INFO sysinfo;
778251881Speter  OSVERSIONINFOEXW osinfo;
779251881Speter  HKEY hkcv;
780251881Speter
781299742Sdim  if (!system_info(&sysinfo, NULL)
782299742Sdim      || !svn_sysinfo___fill_windows_version(&osinfo))
783251881Speter    return NULL;
784251881Speter
785251881Speter  if (!RegOpenKeyExW(HKEY_LOCAL_MACHINE,
786251881Speter                     L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
787251881Speter                     0, KEY_QUERY_VALUE, &hkcv))
788251881Speter    {
789251881Speter      const char *release = registry_value(hkcv, L"ProductName", pool);
790251881Speter      const char *spack = registry_value(hkcv, L"CSDVersion", pool);
791251881Speter      const char *curver = registry_value(hkcv, L"CurrentVersion", pool);
792251881Speter      const char *curtype = registry_value(hkcv, L"CurrentType", pool);
793251881Speter      const char *install = registry_value(hkcv, L"InstallationType", pool);
794251881Speter      const char *curbuild = registry_value(hkcv, L"CurrentBuildNumber", pool);
795251881Speter
796251881Speter      if (!spack && *osinfo.szCSDVersion)
797251881Speter        spack = wcs_to_utf8(osinfo.szCSDVersion, pool);
798251881Speter
799251881Speter      if (!curbuild)
800251881Speter        curbuild = registry_value(hkcv, L"CurrentBuild", pool);
801251881Speter
802251881Speter      if (release || spack || curver || curtype || curbuild)
803251881Speter        {
804251881Speter          const char *bootinfo = "";
805251881Speter          if (curver || install || curtype)
806251881Speter            {
807251881Speter              bootinfo = apr_psprintf(pool, "[%s%s%s%s%s]",
808251881Speter                                      (curver ? curver : ""),
809251881Speter                                      (install ? (curver ? " " : "") : ""),
810251881Speter                                      (install ? install : ""),
811251881Speter                                      (curtype
812251881Speter                                       ? (curver||install ? " " : "")
813251881Speter                                       : ""),
814251881Speter                                      (curtype ? curtype : ""));
815251881Speter            }
816251881Speter
817251881Speter          return apr_psprintf(pool, "%s%s%s%s%s%s%s",
818251881Speter                              (release ? release : ""),
819251881Speter                              (spack ? (release ? ", " : "") : ""),
820251881Speter                              (spack ? spack : ""),
821251881Speter                              (curbuild
822251881Speter                               ? (release||spack ? ", build " : "build ")
823251881Speter                               : ""),
824251881Speter                              (curbuild ? curbuild : ""),
825251881Speter                              (bootinfo
826251881Speter                               ? (release||spack||curbuild ? " " : "")
827251881Speter                               : ""),
828251881Speter                              (bootinfo ? bootinfo : ""));
829251881Speter        }
830251881Speter    }
831251881Speter
832251881Speter  if (*osinfo.szCSDVersion)
833251881Speter    {
834251881Speter      const char *servicepack = wcs_to_utf8(osinfo.szCSDVersion, pool);
835251881Speter
836251881Speter      if (servicepack)
837251881Speter        return apr_psprintf(pool, "Windows NT %u.%u, %s, build %u",
838251881Speter                            (unsigned int)osinfo.dwMajorVersion,
839251881Speter                            (unsigned int)osinfo.dwMinorVersion,
840251881Speter                            servicepack,
841251881Speter                            (unsigned int)osinfo.dwBuildNumber);
842251881Speter
843251881Speter      /* Assume wServicePackMajor > 0 if szCSDVersion is not empty */
844251881Speter      if (osinfo.wServicePackMinor)
845251881Speter        return apr_psprintf(pool, "Windows NT %u.%u SP%u.%u, build %u",
846251881Speter                            (unsigned int)osinfo.dwMajorVersion,
847251881Speter                            (unsigned int)osinfo.dwMinorVersion,
848251881Speter                            (unsigned int)osinfo.wServicePackMajor,
849251881Speter                            (unsigned int)osinfo.wServicePackMinor,
850251881Speter                            (unsigned int)osinfo.dwBuildNumber);
851251881Speter
852251881Speter      return apr_psprintf(pool, "Windows NT %u.%u SP%u, build %u",
853251881Speter                          (unsigned int)osinfo.dwMajorVersion,
854251881Speter                          (unsigned int)osinfo.dwMinorVersion,
855251881Speter                          (unsigned int)osinfo.wServicePackMajor,
856251881Speter                          (unsigned int)osinfo.dwBuildNumber);
857251881Speter    }
858251881Speter
859251881Speter  return apr_psprintf(pool, "Windows NT %u.%u, build %u",
860251881Speter                      (unsigned int)osinfo.dwMajorVersion,
861251881Speter                      (unsigned int)osinfo.dwMinorVersion,
862251881Speter                      (unsigned int)osinfo.dwBuildNumber);
863251881Speter}
864251881Speter
865251881Speter
866251881Speter/* Get a list of handles of shared libs loaded by the current
867251881Speter   process. Returns a NULL-terminated array alocated from POOL. */
868251881Speterstatic HMODULE *
869251881Speterenum_loaded_modules(apr_pool_t *pool)
870251881Speter{
871262253Speter  HMODULE psapi_dll = 0;
872251881Speter  HANDLE current = GetCurrentProcess();
873251881Speter  HMODULE dummy[1];
874251881Speter  HMODULE *handles;
875251881Speter  DWORD size;
876262253Speter  FNENUMPROCESSMODULES EnumProcessModules_;
877251881Speter
878262253Speter  psapi_dll = GetModuleHandleA("psapi.dll");
879262253Speter
880262253Speter  if (!psapi_dll)
881262253Speter    {
882262253Speter      /* Load and never unload, just like static linking */
883262253Speter      psapi_dll = LoadLibraryA("psapi.dll");
884262253Speter    }
885262253Speter
886262253Speter  if (!psapi_dll)
887262253Speter      return NULL;
888262253Speter
889262253Speter  EnumProcessModules_ = (FNENUMPROCESSMODULES)
890262253Speter                              GetProcAddress(psapi_dll, "EnumProcessModules");
891262253Speter
892262253Speter  /* Before Windows XP psapi was an optional module */
893262253Speter  if (! EnumProcessModules_)
894251881Speter    return NULL;
895251881Speter
896262253Speter  if (!EnumProcessModules_(current, dummy, sizeof(dummy), &size))
897262253Speter    return NULL;
898262253Speter
899251881Speter  handles = apr_palloc(pool, size + sizeof *handles);
900262253Speter  if (! EnumProcessModules_(current, handles, size, &size))
901251881Speter    return NULL;
902251881Speter  handles[size / sizeof *handles] = NULL;
903251881Speter  return handles;
904251881Speter}
905251881Speter
906251881Speter/* Find the version number, if any, embedded in FILENAME. */
907251881Speterstatic const char *
908251881Speterfile_version_number(const wchar_t *filename, apr_pool_t *pool)
909251881Speter{
910251881Speter  VS_FIXEDFILEINFO info;
911251881Speter  unsigned int major, minor, micro, nano;
912251881Speter  void *data;
913251881Speter  DWORD data_size = GetFileVersionInfoSizeW(filename, NULL);
914251881Speter  void *vinfo;
915251881Speter  UINT vinfo_size;
916251881Speter
917251881Speter  if (!data_size)
918251881Speter    return NULL;
919251881Speter
920251881Speter  data = apr_palloc(pool, data_size);
921251881Speter  if (!GetFileVersionInfoW(filename, 0, data_size, data))
922251881Speter    return NULL;
923251881Speter
924251881Speter  if (!VerQueryValueW(data, L"\\", &vinfo, &vinfo_size))
925251881Speter    return NULL;
926251881Speter
927251881Speter  if (vinfo_size != sizeof info)
928251881Speter    return NULL;
929251881Speter
930251881Speter  memcpy(&info, vinfo, sizeof info);
931251881Speter  major = (info.dwFileVersionMS >> 16) & 0xFFFF;
932251881Speter  minor = info.dwFileVersionMS & 0xFFFF;
933251881Speter  micro = (info.dwFileVersionLS >> 16) & 0xFFFF;
934251881Speter  nano = info.dwFileVersionLS & 0xFFFF;
935251881Speter
936251881Speter  if (!nano)
937251881Speter    {
938251881Speter      if (!micro)
939251881Speter        return apr_psprintf(pool, "%u.%u", major, minor);
940251881Speter      else
941251881Speter        return apr_psprintf(pool, "%u.%u.%u", major, minor, micro);
942251881Speter    }
943251881Speter  return apr_psprintf(pool, "%u.%u.%u.%u", major, minor, micro, nano);
944251881Speter}
945251881Speter
946251881Speter/* List the shared libraries loaded by the current process. */
947251881Speterstatic const apr_array_header_t *
948251881Speterwin32_shared_libs(apr_pool_t *pool)
949251881Speter{
950251881Speter  apr_array_header_t *array = NULL;
951251881Speter  wchar_t buffer[MAX_PATH + 1];
952251881Speter  HMODULE *handles = enum_loaded_modules(pool);
953251881Speter  HMODULE *module;
954251881Speter
955251881Speter  for (module = handles; module && *module; ++module)
956251881Speter    {
957251881Speter      const char *filename;
958251881Speter      const char *version;
959251881Speter      if (GetModuleFileNameW(*module, buffer, MAX_PATH))
960251881Speter        {
961251881Speter          buffer[MAX_PATH] = 0;
962251881Speter
963251881Speter          version = file_version_number(buffer, pool);
964251881Speter          filename = wcs_to_utf8(buffer, pool);
965251881Speter          if (filename)
966251881Speter            {
967251881Speter              svn_version_ext_loaded_lib_t *lib;
968251881Speter
969251881Speter              if (!array)
970251881Speter                {
971251881Speter                  array = apr_array_make(pool, 32, sizeof(*lib));
972251881Speter                }
973251881Speter              lib = &APR_ARRAY_PUSH(array, svn_version_ext_loaded_lib_t);
974251881Speter              lib->name = svn_dirent_local_style(filename, pool);
975251881Speter              lib->version = version;
976251881Speter            }
977251881Speter        }
978251881Speter    }
979251881Speter
980251881Speter  return array;
981251881Speter}
982251881Speter#endif /* WIN32 */
983251881Speter
984251881Speter
985251881Speter#ifdef SVN_HAVE_MACOS_PLIST
986299742Sdim/* implements svn_write_fn_t to copy the data into a CFMutableDataRef that's
987299742Sdim * in the baton. */
988299742Sdimstatic svn_error_t *
989299742Sdimwrite_to_cfmutabledata(void *baton, const char *data, apr_size_t *len)
990299742Sdim{
991299742Sdim  CFMutableDataRef *resource = (CFMutableDataRef *) baton;
992299742Sdim
993299742Sdim  CFDataAppendBytes(*resource, (UInt8 *)data, *len);
994299742Sdim
995299742Sdim  return SVN_NO_ERROR;
996299742Sdim}
997299742Sdim
998251881Speter/* Load the SystemVersion.plist or ServerVersion.plist file into a
999251881Speter   property list. Set SERVER to TRUE if the file read was
1000251881Speter   ServerVersion.plist. */
1001251881Speterstatic CFDictionaryRef
1002251881Spetersystem_version_plist(svn_boolean_t *server, apr_pool_t *pool)
1003251881Speter{
1004299742Sdim  static const char server_version[] =
1005251881Speter    "/System/Library/CoreServices/ServerVersion.plist";
1006299742Sdim  static const char system_version[] =
1007251881Speter    "/System/Library/CoreServices/SystemVersion.plist";
1008299742Sdim  svn_stream_t *read_stream, *write_stream;
1009299742Sdim  svn_error_t *err;
1010251881Speter  CFPropertyListRef plist = NULL;
1011299742Sdim  CFMutableDataRef resource = CFDataCreateMutable(kCFAllocatorDefault, 0);
1012251881Speter
1013299742Sdim  /* failed getting the CFMutableDataRef, shouldn't happen */
1014299742Sdim  if (!resource)
1015251881Speter    return NULL;
1016251881Speter
1017299742Sdim  /* Try to open the plist files to get the data */
1018299742Sdim  err = svn_stream_open_readonly(&read_stream, server_version, pool, pool);
1019299742Sdim  if (err)
1020251881Speter    {
1021299742Sdim      if (!APR_STATUS_IS_ENOENT(err->apr_err))
1022251881Speter        {
1023299742Sdim          svn_error_clear(err);
1024299742Sdim          CFRelease(resource);
1025251881Speter          return NULL;
1026251881Speter        }
1027251881Speter      else
1028251881Speter        {
1029299742Sdim          svn_error_clear(err);
1030299742Sdim          err = svn_stream_open_readonly(&read_stream, system_version,
1031299742Sdim                                         pool, pool);
1032299742Sdim          if (err)
1033299742Sdim            {
1034299742Sdim              svn_error_clear(err);
1035299742Sdim              CFRelease(resource);
1036299742Sdim              return NULL;
1037299742Sdim            }
1038299742Sdim
1039251881Speter          *server = FALSE;
1040251881Speter        }
1041251881Speter    }
1042251881Speter  else
1043251881Speter    {
1044251881Speter      *server = TRUE;
1045251881Speter    }
1046251881Speter
1047299742Sdim  /* copy the data onto the CFMutableDataRef to allow us to provide it to
1048299742Sdim   * the CoreFoundation functions that parse proprerty lists */
1049299742Sdim  write_stream = svn_stream_create(&resource, pool);
1050299742Sdim  svn_stream_set_write(write_stream, write_to_cfmutabledata);
1051299742Sdim  err = svn_stream_copy3(read_stream, write_stream, NULL, NULL, pool);
1052299742Sdim  if (err)
1053299742Sdim    {
1054299742Sdim      svn_error_clear(err);
1055299742Sdim      return NULL;
1056299742Sdim    }
1057299742Sdim
1058299742Sdim#if __MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
1059299742Sdim  /* This function is only available from Mac OS 10.6 onward. */
1060299742Sdim  plist = CFPropertyListCreateWithData(kCFAllocatorDefault, resource,
1061299742Sdim                                       kCFPropertyListImmutable,
1062299742Sdim                                       NULL, NULL);
1063299742Sdim#else  /* Mac OS 10.5 or earlier */
1064299742Sdim  /* This function obsolete and deprecated since Mac OS 10.10. */
1065251881Speter  plist = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, resource,
1066251881Speter                                          kCFPropertyListImmutable,
1067299742Sdim                                          NULL);
1068299742Sdim#endif /* MAC_OS_X_VERSION_10_6 */
1069299742Sdim
1070251881Speter  if (resource)
1071251881Speter    CFRelease(resource);
1072251881Speter
1073299742Sdim  if (!plist)
1074299742Sdim    return NULL;
1075299742Sdim
1076251881Speter  if (CFDictionaryGetTypeID() != CFGetTypeID(plist))
1077251881Speter    {
1078251881Speter      /* Oops ... this really should be a dict. */
1079251881Speter      CFRelease(plist);
1080251881Speter      return NULL;
1081251881Speter    }
1082251881Speter
1083251881Speter  return plist;
1084251881Speter}
1085251881Speter
1086251881Speter/* Return the value for KEY from PLIST, or NULL if not available. */
1087251881Speterstatic const char *
1088251881Spetervalue_from_dict(CFDictionaryRef plist, CFStringRef key, apr_pool_t *pool)
1089251881Speter{
1090251881Speter  CFStringRef valref;
1091251881Speter  CFIndex bufsize;
1092251881Speter  const void *valptr;
1093251881Speter  const char *value;
1094251881Speter
1095251881Speter  if (!CFDictionaryGetValueIfPresent(plist, key, &valptr))
1096251881Speter    return NULL;
1097251881Speter
1098251881Speter  valref = valptr;
1099251881Speter  if (CFStringGetTypeID() != CFGetTypeID(valref))
1100251881Speter    return NULL;
1101251881Speter
1102251881Speter  value = CFStringGetCStringPtr(valref, kCFStringEncodingUTF8);
1103251881Speter  if (value)
1104251881Speter    return apr_pstrdup(pool, value);
1105251881Speter
1106251881Speter  bufsize =  5 * CFStringGetLength(valref) + 1;
1107251881Speter  value = apr_palloc(pool, bufsize);
1108251881Speter  if (!CFStringGetCString(valref, (char*)value, bufsize,
1109251881Speter                          kCFStringEncodingUTF8))
1110251881Speter    value = NULL;
1111251881Speter
1112251881Speter  return value;
1113251881Speter}
1114251881Speter
1115251881Speter/* Return the commercial name of the OS, given the version number in
1116251881Speter   a format that matches the regular expression /^10\.\d+(\..*)?$/ */
1117251881Speterstatic const char *
1118251881Speterrelease_name_from_version(const char *osver)
1119251881Speter{
1120251881Speter  char *end = NULL;
1121251881Speter  unsigned long num = strtoul(osver, &end, 10);
1122251881Speter
1123251881Speter  if (!end || *end != '.' || num != 10)
1124251881Speter    return NULL;
1125251881Speter
1126251881Speter  osver = end + 1;
1127251881Speter  end = NULL;
1128251881Speter  num = strtoul(osver, &end, 10);
1129251881Speter  if (!end || (*end && *end != '.'))
1130251881Speter    return NULL;
1131251881Speter
1132251881Speter  /* See http://en.wikipedia.org/wiki/History_of_OS_X#Release_timeline */
1133251881Speter  switch(num)
1134251881Speter    {
1135299742Sdim    case  0: return "Cheetah";
1136299742Sdim    case  1: return "Puma";
1137299742Sdim    case  2: return "Jaguar";
1138299742Sdim    case  3: return "Panther";
1139299742Sdim    case  4: return "Tiger";
1140299742Sdim    case  5: return "Leopard";
1141299742Sdim    case  6: return "Snow Leopard";
1142299742Sdim    case  7: return "Lion";
1143299742Sdim    case  8: return "Mountain Lion";
1144299742Sdim    case  9: return "Mavericks";
1145299742Sdim    case 10: return "Yosemite";
1146309512Speter    case 11: return "El Capitan";
1147309512Speter    case 12: return "Sierra";
1148251881Speter    }
1149251881Speter
1150251881Speter  return NULL;
1151251881Speter}
1152251881Speter
1153251881Speter/* Construct the release name from information stored in the Mac OS X
1154251881Speter   "SystemVersion.plist" file (or ServerVersion.plist, for Mac Os
1155251881Speter   Server. */
1156251881Speterstatic const char *
1157251881Spetermacos_release_name(apr_pool_t *pool)
1158251881Speter{
1159251881Speter  svn_boolean_t server;
1160251881Speter  CFDictionaryRef plist = system_version_plist(&server, pool);
1161251881Speter
1162251881Speter  if (plist)
1163251881Speter    {
1164251881Speter      const char *osname = value_from_dict(plist, CFSTR("ProductName"), pool);
1165251881Speter      const char *osver = value_from_dict(plist,
1166251881Speter                                          CFSTR("ProductUserVisibleVersion"),
1167251881Speter                                          pool);
1168251881Speter      const char *build = value_from_dict(plist,
1169251881Speter                                          CFSTR("ProductBuildVersion"),
1170251881Speter                                          pool);
1171251881Speter      const char *release;
1172251881Speter
1173251881Speter      if (!osver)
1174251881Speter        osver = value_from_dict(plist, CFSTR("ProductVersion"), pool);
1175251881Speter      release = release_name_from_version(osver);
1176251881Speter
1177251881Speter      CFRelease(plist);
1178251881Speter      return apr_psprintf(pool, "%s%s%s%s%s%s%s%s",
1179251881Speter                          (osname ? osname : ""),
1180251881Speter                          (osver ? (osname ? " " : "") : ""),
1181251881Speter                          (osver ? osver : ""),
1182251881Speter                          (release ? (osname||osver ? " " : "") : ""),
1183251881Speter                          (release ? release : ""),
1184251881Speter                          (build
1185251881Speter                           ? (osname||osver||release ? ", " : "")
1186251881Speter                           : ""),
1187251881Speter                          (build
1188251881Speter                           ? (server ? "server build " : "build ")
1189251881Speter                           : ""),
1190251881Speter                          (build ? build : ""));
1191251881Speter    }
1192251881Speter
1193251881Speter  return NULL;
1194251881Speter}
1195251881Speter#endif  /* SVN_HAVE_MACOS_PLIST */
1196251881Speter
1197251881Speter#ifdef SVN_HAVE_MACHO_ITERATE
1198251881Speter/* List the shared libraries loaded by the current process.
1199251881Speter   Ignore frameworks and system libraries, they're just clutter. */
1200251881Speterstatic const apr_array_header_t *
1201251881Spetermacos_shared_libs(apr_pool_t *pool)
1202251881Speter{
1203251881Speter  static const char slb_prefix[] = "/usr/lib/system/";
1204251881Speter  static const char fwk_prefix[] = "/System/Library/Frameworks/";
1205251881Speter  static const char pfk_prefix[] = "/System/Library/PrivateFrameworks/";
1206251881Speter
1207251881Speter  const size_t slb_prefix_len = strlen(slb_prefix);
1208251881Speter  const size_t fwk_prefix_len = strlen(fwk_prefix);
1209251881Speter  const size_t pfk_prefix_len = strlen(pfk_prefix);
1210251881Speter
1211251881Speter  apr_array_header_t *result = NULL;
1212251881Speter  apr_array_header_t *dylibs = NULL;
1213251881Speter
1214251881Speter  uint32_t i;
1215251881Speter  for (i = 0;; ++i)
1216251881Speter    {
1217251881Speter      const struct mach_header *header = _dyld_get_image_header(i);
1218251881Speter      const char *filename = _dyld_get_image_name(i);
1219251881Speter      const char *version;
1220251881Speter      char *truename;
1221251881Speter      svn_version_ext_loaded_lib_t *lib;
1222251881Speter
1223251881Speter      if (!(header && filename))
1224251881Speter        break;
1225251881Speter
1226251881Speter      switch (header->cputype)
1227251881Speter        {
1228251881Speter        case CPU_TYPE_I386:      version = _("Intel"); break;
1229251881Speter        case CPU_TYPE_X86_64:    version = _("Intel 64-bit"); break;
1230251881Speter        case CPU_TYPE_POWERPC:   version = _("PowerPC"); break;
1231251881Speter        case CPU_TYPE_POWERPC64: version = _("PowerPC 64-bit"); break;
1232251881Speter        default:
1233251881Speter          version = NULL;
1234251881Speter        }
1235251881Speter
1236251881Speter      if (0 == apr_filepath_merge(&truename, "", filename,
1237251881Speter                                  APR_FILEPATH_NATIVE
1238251881Speter                                  | APR_FILEPATH_TRUENAME,
1239251881Speter                                  pool))
1240251881Speter        filename = truename;
1241251881Speter      else
1242251881Speter        filename = apr_pstrdup(pool, filename);
1243251881Speter
1244251881Speter      if (0 == strncmp(filename, slb_prefix, slb_prefix_len)
1245251881Speter          || 0 == strncmp(filename, fwk_prefix, fwk_prefix_len)
1246251881Speter          || 0 == strncmp(filename, pfk_prefix, pfk_prefix_len))
1247251881Speter        {
1248251881Speter          /* Ignore frameworks and system libraries. */
1249251881Speter          continue;
1250251881Speter        }
1251251881Speter
1252251881Speter      if (header->filetype == MH_EXECUTE)
1253251881Speter        {
1254251881Speter          /* Make sure the program filename is first in the list */
1255251881Speter          if (!result)
1256251881Speter            {
1257251881Speter              result = apr_array_make(pool, 32, sizeof(*lib));
1258251881Speter            }
1259251881Speter          lib = &APR_ARRAY_PUSH(result, svn_version_ext_loaded_lib_t);
1260251881Speter        }
1261251881Speter      else
1262251881Speter        {
1263251881Speter          if (!dylibs)
1264251881Speter            {
1265251881Speter              dylibs = apr_array_make(pool, 32, sizeof(*lib));
1266251881Speter            }
1267251881Speter          lib = &APR_ARRAY_PUSH(dylibs, svn_version_ext_loaded_lib_t);
1268251881Speter        }
1269251881Speter
1270251881Speter      lib->name = filename;
1271251881Speter      lib->version = version;
1272251881Speter    }
1273251881Speter
1274251881Speter  /* Gather results into one array. */
1275251881Speter  if (dylibs)
1276251881Speter    {
1277251881Speter      if (result)
1278251881Speter        apr_array_cat(result, dylibs);
1279251881Speter      else
1280251881Speter        result = dylibs;
1281251881Speter    }
1282251881Speter
1283251881Speter  return result;
1284251881Speter}
1285251881Speter#endif  /* SVN_HAVE_MACHO_ITERATE */
1286