]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/subversion/subversion/libsvn_subr/sysinfo.c
THIS BRANCH IS OBSOLETE, PLEASE READ:
[FreeBSD/FreeBSD.git] / contrib / subversion / subversion / libsvn_subr / sysinfo.c
1
2 /*
3  * sysinfo.c :  information about the running system
4  *
5  * ====================================================================
6  *    Licensed to the Apache Software Foundation (ASF) under one
7  *    or more contributor license agreements.  See the NOTICE file
8  *    distributed with this work for additional information
9  *    regarding copyright ownership.  The ASF licenses this file
10  *    to you under the Apache License, Version 2.0 (the
11  *    "License"); you may not use this file except in compliance
12  *    with the License.  You may obtain a copy of the License at
13  *
14  *      http://www.apache.org/licenses/LICENSE-2.0
15  *
16  *    Unless required by applicable law or agreed to in writing,
17  *    software distributed under the License is distributed on an
18  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
19  *    KIND, either express or implied.  See the License for the
20  *    specific language governing permissions and limitations
21  *    under the License.
22  * ====================================================================
23  */
24
25
26 \f
27 #define APR_WANT_STRFUNC
28 #include <apr_want.h>
29
30 #include <apr_lib.h>
31 #include <apr_pools.h>
32 #include <apr_file_info.h>
33 #include <apr_signal.h>
34 #include <apr_strings.h>
35 #include <apr_thread_proc.h>
36 #include <apr_version.h>
37 #include <apu_version.h>
38
39 #include "svn_pools.h"
40 #include "svn_ctype.h"
41 #include "svn_dirent_uri.h"
42 #include "svn_error.h"
43 #include "svn_io.h"
44 #include "svn_string.h"
45 #include "svn_utf.h"
46 #include "svn_version.h"
47
48 #include "private/svn_sqlite.h"
49 #include "private/svn_subr_private.h"
50 #include "private/svn_utf_private.h"
51
52 #include "sysinfo.h"
53 #include "svn_private_config.h"
54
55 #if HAVE_SYS_TYPES_H
56 #include <sys/types.h>
57 #endif
58
59 #if HAVE_SYS_UTSNAME_H
60 #include <sys/utsname.h>
61 #endif
62
63 #if HAVE_UNISTD_H
64 #include <unistd.h>
65 #endif
66
67 #if HAVE_ELF_H
68 #include <elf.h>
69 #endif
70
71 #ifdef SVN_HAVE_MACOS_PLIST
72 #include <CoreFoundation/CoreFoundation.h>
73 #include <AvailabilityMacros.h>
74 # ifndef MAC_OS_X_VERSION_10_6
75 #  define MAC_OS_X_VERSION_10_6  1060
76 # endif
77 #endif
78
79 #ifdef SVN_HAVE_MACHO_ITERATE
80 #include <mach-o/dyld.h>
81 #include <mach-o/loader.h>
82 #endif
83
84 #if HAVE_UNAME
85 static const char *canonical_host_from_uname(apr_pool_t *pool);
86 # ifndef SVN_HAVE_MACOS_PLIST
87 static const char *release_name_from_uname(apr_pool_t *pool);
88 # endif
89 #endif
90
91 #ifdef WIN32
92 static const char *win32_canonical_host(apr_pool_t *pool);
93 static const char *win32_release_name(apr_pool_t *pool);
94 static const apr_array_header_t *win32_shared_libs(apr_pool_t *pool);
95 #endif /* WIN32 */
96
97 #ifdef SVN_HAVE_MACOS_PLIST
98 static const char *macos_release_name(apr_pool_t *pool);
99 #endif
100
101 #ifdef SVN_HAVE_MACHO_ITERATE
102 static const apr_array_header_t *macos_shared_libs(apr_pool_t *pool);
103 #endif
104
105
106 #if __linux__
107 static const char *linux_release_name(apr_pool_t *pool);
108 static const apr_array_header_t *linux_shared_libs(apr_pool_t *pool);
109 #endif
110
111 const char *
112 svn_sysinfo__canonical_host(apr_pool_t *pool)
113 {
114 #ifdef WIN32
115   return win32_canonical_host(pool);
116 #elif HAVE_UNAME
117   return canonical_host_from_uname(pool);
118 #else
119   return "unknown-unknown-unknown";
120 #endif
121 }
122
123
124 const char *
125 svn_sysinfo__release_name(apr_pool_t *pool)
126 {
127 #ifdef WIN32
128   return win32_release_name(pool);
129 #elif defined(SVN_HAVE_MACOS_PLIST)
130   return macos_release_name(pool);
131 #elif __linux__
132   return linux_release_name(pool);
133 #elif HAVE_UNAME
134   return release_name_from_uname(pool);
135 #else
136   return NULL;
137 #endif
138 }
139
140 const apr_array_header_t *
141 svn_sysinfo__linked_libs(apr_pool_t *pool)
142 {
143   svn_version_ext_linked_lib_t *lib;
144   apr_array_header_t *array = apr_array_make(pool, 7, sizeof(*lib));
145   int lz4_version = svn_lz4__runtime_version();
146
147   lib = &APR_ARRAY_PUSH(array, svn_version_ext_linked_lib_t);
148   lib->name = "APR";
149   lib->compiled_version = APR_VERSION_STRING;
150   lib->runtime_version = apr_pstrdup(pool, apr_version_string());
151
152 /* Don't list APR-Util if it isn't linked in, which it may not be if
153  * we're using APR 2.x+ which combined APR-Util into APR. */
154 #ifdef APU_VERSION_STRING
155   lib = &APR_ARRAY_PUSH(array, svn_version_ext_linked_lib_t);
156   lib->name = "APR-Util";
157   lib->compiled_version = APU_VERSION_STRING;
158   lib->runtime_version = apr_pstrdup(pool, apu_version_string());
159 #endif
160
161   lib = &APR_ARRAY_PUSH(array, svn_version_ext_linked_lib_t);
162   lib->name = "Expat";
163   lib->compiled_version = apr_pstrdup(pool, svn_xml__compiled_version());
164   lib->runtime_version = apr_pstrdup(pool, svn_xml__runtime_version());
165
166   lib = &APR_ARRAY_PUSH(array, svn_version_ext_linked_lib_t);
167   lib->name = "SQLite";
168   lib->compiled_version = apr_pstrdup(pool, svn_sqlite__compiled_version());
169 #ifdef SVN_SQLITE_INLINE
170   lib->runtime_version = NULL;
171 #else
172   lib->runtime_version = apr_pstrdup(pool, svn_sqlite__runtime_version());
173 #endif
174
175   lib = &APR_ARRAY_PUSH(array, svn_version_ext_linked_lib_t);
176   lib->name = "Utf8proc";
177   lib->compiled_version = apr_pstrdup(pool, svn_utf__utf8proc_compiled_version());
178   lib->runtime_version = apr_pstrdup(pool, svn_utf__utf8proc_runtime_version());
179
180   lib = &APR_ARRAY_PUSH(array, svn_version_ext_linked_lib_t);
181   lib->name = "ZLib";
182   lib->compiled_version = apr_pstrdup(pool, svn_zlib__compiled_version());
183   lib->runtime_version = apr_pstrdup(pool, svn_zlib__runtime_version());
184
185   lib = &APR_ARRAY_PUSH(array, svn_version_ext_linked_lib_t);
186   lib->name = "LZ4";
187   lib->compiled_version = apr_pstrdup(pool, svn_lz4__compiled_version());
188
189   lib->runtime_version = apr_psprintf(pool, "%d.%d.%d",
190                                       lz4_version / 100 / 100,
191                                       (lz4_version / 100) % 100,
192                                       lz4_version % 100);
193
194   return array;
195 }
196
197 const apr_array_header_t *
198 svn_sysinfo__loaded_libs(apr_pool_t *pool)
199 {
200 #ifdef WIN32
201   return win32_shared_libs(pool);
202 #elif defined(SVN_HAVE_MACHO_ITERATE)
203   return macos_shared_libs(pool);
204 #elif __linux__
205   return linux_shared_libs(pool);
206 #else
207   return NULL;
208 #endif
209 }
210
211 \f
212 #if HAVE_UNAME
213 static const char*
214 canonical_host_from_uname(apr_pool_t *pool)
215 {
216   const char *machine = "unknown";
217   const char *vendor = "unknown";
218   const char *sysname = "unknown";
219   const char *sysver = "";
220   struct utsname info;
221
222   if (0 <= uname(&info))
223     {
224       svn_error_t *err;
225       const char *tmp;
226
227       err = svn_utf_cstring_to_utf8(&tmp, info.machine, pool);
228       if (err)
229         svn_error_clear(err);
230       else
231         machine = tmp;
232
233       err = svn_utf_cstring_to_utf8(&tmp, info.sysname, pool);
234       if (err)
235         svn_error_clear(err);
236       else
237         {
238           char *lwr = apr_pstrdup(pool, tmp);
239           char *it = lwr;
240           while (*it)
241             {
242               if (svn_ctype_isupper(*it))
243                 *it = apr_tolower(*it);
244               ++it;
245             }
246           sysname = lwr;
247         }
248
249       if (0 == strcmp(sysname, "darwin"))
250         vendor = "apple";
251       if (0 == strcmp(sysname, "linux"))
252         sysver = "-gnu";
253       else
254         {
255           err = svn_utf_cstring_to_utf8(&tmp, info.release, pool);
256           if (err)
257             svn_error_clear(err);
258           else
259             {
260               apr_size_t n = strspn(tmp, ".0123456789");
261               if (n > 0)
262                 {
263                   char *ver = apr_pstrdup(pool, tmp);
264                   ver[n] = 0;
265                   sysver = ver;
266                 }
267               else
268                 sysver = tmp;
269             }
270         }
271     }
272
273   return apr_psprintf(pool, "%s-%s-%s%s", machine, vendor, sysname, sysver);
274 }
275
276 # ifndef SVN_HAVE_MACOS_PLIST
277 /* Generate a release name from the uname(3) info, effectively
278    returning "`uname -s` `uname -r`". */
279 static const char *
280 release_name_from_uname(apr_pool_t *pool)
281 {
282   struct utsname info;
283   if (0 <= uname(&info))
284     {
285       svn_error_t *err;
286       const char *sysname;
287       const char *sysver;
288
289       err = svn_utf_cstring_to_utf8(&sysname, info.sysname, pool);
290       if (err)
291         {
292           sysname = NULL;
293           svn_error_clear(err);
294         }
295
296
297       err = svn_utf_cstring_to_utf8(&sysver, info.release, pool);
298       if (err)
299         {
300           sysver = NULL;
301           svn_error_clear(err);
302         }
303
304       if (sysname || sysver)
305         {
306           return apr_psprintf(pool, "%s%s%s",
307                               (sysname ? sysname : ""),
308                               (sysver ? (sysname ? " " : "") : ""),
309                               (sysver ? sysver : ""));
310         }
311     }
312   return NULL;
313 }
314 # endif  /* !SVN_HAVE_MACOS_PLIST */
315 #endif  /* HAVE_UNAME */
316
317 \f
318 #if __linux__
319 /* Find the first whitespace character in a stringbuf.
320    Analogous to svn_stringbuf_first_non_whitespace. */
321 static apr_size_t
322 stringbuf_first_whitespace(const svn_stringbuf_t *str)
323 {
324   apr_size_t i;
325   for (i = 0; i < str->len; ++i)
326     {
327       if (svn_ctype_isspace(str->data[i]))
328         return i;
329     }
330   return str->len;
331 }
332
333 /* Skip a whitespace-delimited field in a stringbuf. */
334 static void
335 stringbuf_skip_whitespace_field(svn_stringbuf_t *str)
336 {
337   apr_size_t i;
338   i = stringbuf_first_whitespace(str);
339   svn_stringbuf_leftchop(str, i);
340   i = svn_stringbuf_first_non_whitespace(str);
341   svn_stringbuf_leftchop(str, i);
342 }
343
344 /* Split a stringbuf into a key/value pair.
345    Return the key, leaving the stripped value in the stringbuf. */
346 static const char *
347 stringbuf_split_key(svn_stringbuf_t *buffer, char delim)
348 {
349   char *key;
350   char *end;
351
352   end = strchr(buffer->data, delim);
353   if (!end)
354     return NULL;
355
356   svn_stringbuf_strip_whitespace(buffer);
357
358   /* Now we split the currently allocated buffer in two parts:
359       - a const char * HEAD
360       - the remaining stringbuf_t. */
361
362   /* Create HEAD as '\0' terminated const char * */
363   key = buffer->data;
364   end = strchr(key, delim);
365   *end = '\0';
366
367   /* And update the TAIL to be a smaller, but still valid stringbuf */
368   buffer->data = end + 1;
369   buffer->len -= 1 + end - key;
370   buffer->blocksize -= 1 + end - key;
371
372   svn_stringbuf_strip_whitespace(buffer);
373
374   return key;
375 }
376
377 /* Parse `/usr/bin/lsb_rlease --all` */
378 static const char *
379 lsb_release(apr_pool_t *pool)
380 {
381   static const char *const args[3] =
382     {
383       "/usr/bin/lsb_release",
384       "--all",
385       NULL
386     };
387
388   const char *distributor = NULL;
389   const char *description = NULL;
390   const char *release = NULL;
391   const char *codename = NULL;
392
393   apr_proc_t lsbproc;
394   svn_stream_t *lsbinfo;
395   svn_error_t *err;
396
397   /* Run /usr/bin/lsb_release --all < /dev/null 2>/dev/null */
398   {
399     apr_file_t *stdin_handle;
400     apr_file_t *stdout_handle;
401
402     err = svn_io_file_open(&stdin_handle, SVN_NULL_DEVICE_NAME,
403                            APR_READ, APR_OS_DEFAULT, pool);
404     if (!err)
405       err = svn_io_file_open(&stdout_handle, SVN_NULL_DEVICE_NAME,
406                              APR_WRITE, APR_OS_DEFAULT, pool);
407     if (!err)
408       err = svn_io_start_cmd3(&lsbproc, NULL, args[0], args, NULL, FALSE,
409                               FALSE, stdin_handle,
410                               TRUE, NULL,
411                               FALSE, stdout_handle,
412                               pool);
413     if (err)
414       {
415         svn_error_clear(err);
416         return NULL;
417       }
418   }
419
420   /* Parse the output and try to populate the  */
421   lsbinfo = svn_stream_from_aprfile2(lsbproc.out, TRUE, pool);
422   if (lsbinfo)
423     {
424       for (;;)
425         {
426           svn_boolean_t eof = FALSE;
427           svn_stringbuf_t *line;
428           const char *key;
429
430           err = svn_stream_readline(lsbinfo, &line, "\n", &eof, pool);
431           if (err || eof)
432             break;
433
434           key = stringbuf_split_key(line, ':');
435           if (!key)
436             continue;
437
438           if (0 == svn_cstring_casecmp(key, "Distributor ID"))
439             distributor = line->data;
440           else if (0 == svn_cstring_casecmp(key, "Description"))
441             description = line->data;
442           else if (0 == svn_cstring_casecmp(key, "Release"))
443             release = line->data;
444           else if (0 == svn_cstring_casecmp(key, "Codename"))
445             codename = line->data;
446         }
447       err = svn_error_compose_create(err,
448                                      svn_stream_close(lsbinfo));
449       if (err)
450         {
451           svn_error_clear(err);
452           apr_proc_kill(&lsbproc, SIGKILL);
453           return NULL;
454         }
455     }
456
457   /* Reap the child process */
458   err = svn_io_wait_for_cmd(&lsbproc, "", NULL, NULL, pool);
459   if (err)
460     {
461       svn_error_clear(err);
462       return NULL;
463     }
464
465   if (description)
466     return apr_psprintf(pool, "%s%s%s%s", description,
467                         (codename ? " (" : ""),
468                         (codename ? codename : ""),
469                         (codename ? ")" : ""));
470   if (distributor)
471     return apr_psprintf(pool, "%s%s%s%s%s%s", distributor,
472                         (release ? " " : ""),
473                         (release ? release : ""),
474                         (codename ? " (" : ""),
475                         (codename ? codename : ""),
476                         (codename ? ")" : ""));
477
478   return NULL;
479 }
480
481 /* Read /etc/os-release, as documented here:
482  * http://www.freedesktop.org/software/systemd/man/os-release.html
483  */
484 static const char *
485 systemd_release(apr_pool_t *pool)
486 {
487   svn_error_t *err;
488   svn_stream_t *stream;
489
490   /* Open the file. */
491   err = svn_stream_open_readonly(&stream, "/etc/os-release", pool, pool);
492   if (err && APR_STATUS_IS_ENOENT(err->apr_err))
493     {
494       svn_error_clear(err);
495       err = svn_stream_open_readonly(&stream, "/usr/lib/os-release", pool,
496                                      pool);
497     }
498   if (err)
499     {
500       svn_error_clear(err);
501       return NULL;
502     }
503
504   /* Look for the PRETTY_NAME line. */
505   while (TRUE)
506     {
507       svn_stringbuf_t *line;
508       svn_boolean_t eof;
509
510       err = svn_stream_readline(stream, &line, "\n", &eof, pool);
511       if (err)
512         {
513           svn_error_clear(err);
514           return NULL;
515         }
516
517       if (!strncmp(line->data, "PRETTY_NAME=", 12))
518         {
519           svn_stringbuf_t *release_name;
520
521           /* The value may or may not be enclosed by double quotes.  We don't
522            * attempt to strip them. */
523           release_name = svn_stringbuf_create(line->data + 12, pool);
524           svn_error_clear(svn_stream_close(stream));
525           svn_stringbuf_strip_whitespace(release_name);
526           return release_name->data;
527         }
528
529       if (eof)
530         break;
531     }
532
533   /* The file did not contain a PRETTY_NAME line. */
534   svn_error_clear(svn_stream_close(stream));
535   return NULL;
536 }
537
538 /* Read the whole contents of a file. */
539 static svn_stringbuf_t *
540 read_file_contents(const char *filename, apr_pool_t *pool)
541 {
542   svn_error_t *err;
543   svn_stringbuf_t *buffer;
544
545   err = svn_stringbuf_from_file2(&buffer, filename, pool);
546   if (err)
547     {
548       svn_error_clear(err);
549       return NULL;
550     }
551
552   return buffer;
553 }
554
555 /* Strip everything but the first line from a stringbuf. */
556 static void
557 stringbuf_first_line_only(svn_stringbuf_t *buffer)
558 {
559   char *eol = strchr(buffer->data, '\n');
560   if (eol)
561     {
562       *eol = '\0';
563       buffer->len = 1 + eol - buffer->data;
564     }
565   svn_stringbuf_strip_whitespace(buffer);
566 }
567
568 /* Look at /etc/redhat_release to detect RHEL/Fedora/CentOS. */
569 static const char *
570 redhat_release(apr_pool_t *pool)
571 {
572   svn_stringbuf_t *buffer = read_file_contents("/etc/redhat-release", pool);
573   if (buffer)
574     {
575       stringbuf_first_line_only(buffer);
576       return buffer->data;
577     }
578   return NULL;
579 }
580
581 /* Look at /etc/SuSE-release to detect non-LSB SuSE. */
582 static const char *
583 suse_release(apr_pool_t *pool)
584 {
585   const char *release = NULL;
586   const char *codename = NULL;
587
588   svn_stringbuf_t *buffer = read_file_contents("/etc/SuSE-release", pool);
589   svn_stringbuf_t *line;
590   svn_stream_t *stream;
591   svn_boolean_t eof;
592   svn_error_t *err;
593   if (!buffer)
594       return NULL;
595
596   stream = svn_stream_from_stringbuf(buffer, pool);
597   err = svn_stream_readline(stream, &line, "\n", &eof, pool);
598   if (err || eof)
599     {
600       svn_error_clear(err);
601       return NULL;
602     }
603
604   svn_stringbuf_strip_whitespace(line);
605   release = line->data;
606
607   for (;;)
608     {
609       const char *key;
610
611       err = svn_stream_readline(stream, &line, "\n", &eof, pool);
612       if (err || eof)
613         {
614           svn_error_clear(err);
615           break;
616         }
617
618       key = stringbuf_split_key(line, '=');
619       if (!key)
620         continue;
621
622       if (0 == strncmp(key, "CODENAME", 8))
623         codename = line->data;
624     }
625
626   return apr_psprintf(pool, "%s%s%s%s",
627                       release,
628                       (codename ? " (" : ""),
629                       (codename ? codename : ""),
630                       (codename ? ")" : ""));
631 }
632
633 /* Look at /etc/debian_version to detect non-LSB Debian. */
634 static const char *
635 debian_release(apr_pool_t *pool)
636 {
637   svn_stringbuf_t *buffer = read_file_contents("/etc/debian_version", pool);
638   if (!buffer)
639       return NULL;
640
641   stringbuf_first_line_only(buffer);
642   return apr_pstrcat(pool, "Debian ", buffer->data, SVN_VA_NULL);
643 }
644
645 /* Try to find the Linux distribution name, or return info from uname. */
646 static const char *
647 linux_release_name(apr_pool_t *pool)
648 {
649   const char *uname_release = release_name_from_uname(pool);
650
651   /* Try anything that has /usr/bin/lsb_release.
652      Covers, for example, Debian, Ubuntu and SuSE.  */
653   const char *release_name = lsb_release(pool);
654
655   /* Try the systemd way (covers Arch). */
656   if (!release_name)
657     release_name = systemd_release(pool);
658
659   /* Try RHEL/Fedora/CentOS */
660   if (!release_name)
661     release_name = redhat_release(pool);
662
663   /* Try Non-LSB SuSE */
664   if (!release_name)
665     release_name = suse_release(pool);
666
667   /* Try non-LSB Debian */
668   if (!release_name)
669     release_name = debian_release(pool);
670
671   if (!release_name)
672     return uname_release;
673
674   if (!uname_release)
675     return release_name;
676
677   return apr_psprintf(pool, "%s [%s]", release_name, uname_release);
678 }
679
680 #if HAVE_ELF_H
681 /* Parse a hexadecimal number as a pointer value. */
682 static const unsigned char *
683 parse_pointer_value(const char *start, const char *limit, char **end)
684 {
685   const unsigned char *ptr;
686   const apr_uint64_t val = (apr_uint64_t)apr_strtoi64(start, end, 16);
687
688   if (errno                     /* overflow */
689       || *end == start          /* no valid digits */
690       || *end >= limit)         /* representation too long */
691     return NULL;
692
693   ptr = (const unsigned char*)val;
694   if (val != (apr_uint64_t)ptr)  /* truncated value */
695     return NULL;
696
697   return ptr;
698 }
699
700 /* Read the ELF header at the mapping position to check if this is a shared
701    library. We only look at the ELF identification and the type. The format is
702    described here:
703        http://www.skyfree.org/linux/references/ELF_Format.pdf
704 */
705 static svn_boolean_t
706 check_elf_header(const unsigned char *map_start,
707                  const unsigned char *map_end)
708 {
709   /* A union of all known ELF header types, for size checks. */
710   union max_elf_header_size_t
711   {
712     Elf32_Ehdr header_32;
713     Elf64_Ehdr header_64;
714   };
715
716   /* Check the size of the mapping and the ELF magic tag. */
717   if (map_end < map_start
718       || map_end - map_start < sizeof(union max_elf_header_size_t)
719       || memcmp(map_start, ELFMAG, SELFMAG))
720     {
721       return FALSE;
722     }
723
724   /* Check that this is an ELF shared library or executable file. This also
725      implicitly checks that the data encoding of the current process is the
726      same as in the loaded library. */
727   if (map_start[EI_CLASS] == ELFCLASS32)
728     {
729       const Elf32_Ehdr *hdr = (void*)map_start;
730       return (hdr->e_type == ET_DYN || hdr->e_type == ET_EXEC);
731     }
732   else if (map_start[EI_CLASS] == ELFCLASS64)
733     {
734       const Elf64_Ehdr *hdr = (void*)map_start;
735       return (hdr->e_type == ET_DYN || hdr->e_type == ET_EXEC);
736     }
737
738   return FALSE;
739 }
740 #endif  /* HAVE_ELF_H */
741
742 static const apr_array_header_t *
743 linux_shared_libs(apr_pool_t *pool)
744 {
745   /* Read the list of loaded modules from /proc/[pid]/maps
746     The format is described here:
747         http://man7.org/linux/man-pages/man5/proc.5.html
748   */
749
750   const char *maps = apr_psprintf(pool, "/proc/%ld/maps", (long)getpid());
751   apr_array_header_t *result = NULL;
752   svn_boolean_t eof = FALSE;
753   svn_stream_t *stream;
754   svn_error_t *err;
755
756   err = svn_stream_open_readonly(&stream, maps, pool, pool);
757   if (err)
758     {
759       svn_error_clear(err);
760       return NULL;
761     }
762
763   /* Each line in /proc/[pid]/maps consists of whitespace-delimited fields. */
764   while (!eof)
765     {
766       svn_stringbuf_t *line;
767
768 #if HAVE_ELF_H
769       const unsigned char *map_start;
770       const unsigned char *map_end;
771 #endif
772
773       err = svn_stream_readline(stream, &line, "\n", &eof, pool);
774       if (err)
775         {
776           svn_error_clear(err);
777           return NULL;
778         }
779
780 #if HAVE_ELF_H
781       /* Address: The mapped memory address range. */
782       {
783         const char *const limit = line->data + line->len;
784         char *end;
785
786         /* The start of the address range */
787         map_start = parse_pointer_value(line->data, limit, &end);
788         if (!map_start || *end != '-')
789           continue;
790
791         /* The end of the address range */
792         map_end = parse_pointer_value(end + 1, limit, &end);
793         if (!map_end || !svn_ctype_isspace(*end))
794           continue;
795       }
796 #endif
797
798       stringbuf_skip_whitespace_field(line); /* skip address */
799
800       /* Permissions: The memory region must be readable and executable. */
801       if (line->len < 4 || line->data[0] != 'r' || line->data[2] != 'x')
802         continue;
803
804       stringbuf_skip_whitespace_field(line); /* skip perms */
805       stringbuf_skip_whitespace_field(line); /* skip offset */
806       stringbuf_skip_whitespace_field(line); /* skip device */
807
808       /* I-Node: If it is 0, there is no file associated with the region. */
809       if (line->len < 2
810           || (line->data[0] == '0' && svn_ctype_isspace(line->data[1])))
811         continue;
812
813       stringbuf_skip_whitespace_field(line); /* skip inode */
814
815       /* Consider only things that look like absolute paths.
816          Files that were removed since the process was created (due to an
817          upgrade, for example) are marked as '(deleted)'. */
818       if (line->data[0] == '/')
819         {
820           svn_version_ext_loaded_lib_t *lib;
821
822 #if HAVE_ELF_H
823           if (!check_elf_header(map_start, map_end))
824             continue;
825 #endif
826
827           /* We've done our best to find a mapped shared library. */
828           if (!result)
829             {
830               result = apr_array_make(pool, 32, sizeof(*lib));
831             }
832           lib = &APR_ARRAY_PUSH(result, svn_version_ext_loaded_lib_t);
833           lib->name = line->data;
834           lib->version = NULL;
835         }
836     }
837
838   svn_error_clear(svn_stream_close(stream));
839   return result;
840 }
841 #endif /* __linux__ */
842
843 \f
844 #ifdef WIN32
845 typedef DWORD (WINAPI *FNGETNATIVESYSTEMINFO)(LPSYSTEM_INFO);
846 typedef BOOL (WINAPI *FNENUMPROCESSMODULES) (HANDLE, HMODULE*, DWORD, LPDWORD);
847
848 svn_boolean_t
849 svn_sysinfo___fill_windows_version(OSVERSIONINFOEXW *version_info)
850 {
851   memset(version_info, 0, sizeof(*version_info));
852
853   version_info->dwOSVersionInfoSize = sizeof(*version_info);
854
855   /* Kill warnings with the Windows 8 and later platform SDK */
856 #if _MSC_VER > 1600 && NTDDI_VERSION >= _0x06020000
857   /* Windows 8 deprecated the API to retrieve the Windows version to avoid
858      backwards compatibility problems... It might return a constant version
859      in future Windows versions... But let's kill the warning.
860
861      We can implementation this using a different function later. */
862 #pragma warning(push)
863 #pragma warning(disable: 4996)
864 #endif
865
866   /* Prototype supports OSVERSIONINFO */
867   return GetVersionExW((LPVOID)version_info);
868 #if _MSC_VER > 1600 && NTDDI_VERSION >= _0x06020000
869 #pragma warning(pop)
870 #pragma warning(disable: 4996)
871 #endif
872 }
873
874 /* Get system info, and try to tell the difference between the native
875    system type and the runtime environment of the current process.
876    Populate results in SYSINFO and LOCAL_SYSINFO (optional). */
877 static BOOL
878 system_info(SYSTEM_INFO *sysinfo,
879             SYSTEM_INFO *local_sysinfo)
880 {
881   FNGETNATIVESYSTEMINFO GetNativeSystemInfo_ = (FNGETNATIVESYSTEMINFO)
882     GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "GetNativeSystemInfo");
883
884   memset(sysinfo, 0, sizeof *sysinfo);
885   if (local_sysinfo)
886     {
887       memset(local_sysinfo, 0, sizeof *local_sysinfo);
888       GetSystemInfo(local_sysinfo);
889       if (GetNativeSystemInfo_)
890         GetNativeSystemInfo_(sysinfo);
891       else
892         memcpy(sysinfo, local_sysinfo, sizeof *sysinfo);
893     }
894   else
895     GetSystemInfo(sysinfo);
896
897   return TRUE;
898 }
899
900 /* Map the proccessor type from SYSINFO to a string. */
901 static const char *
902 processor_name(SYSTEM_INFO *sysinfo)
903 {
904   switch (sysinfo->wProcessorArchitecture)
905     {
906     case PROCESSOR_ARCHITECTURE_AMD64:         return "x86_64";
907     case PROCESSOR_ARCHITECTURE_IA64:          return "ia64";
908     case PROCESSOR_ARCHITECTURE_INTEL:         return "x86";
909     case PROCESSOR_ARCHITECTURE_MIPS:          return "mips";
910     case PROCESSOR_ARCHITECTURE_ALPHA:         return "alpha32";
911     case PROCESSOR_ARCHITECTURE_PPC:           return "powerpc";
912     case PROCESSOR_ARCHITECTURE_SHX:           return "shx";
913     case PROCESSOR_ARCHITECTURE_ARM:           return "arm";
914     case PROCESSOR_ARCHITECTURE_ALPHA64:       return "alpha";
915     case PROCESSOR_ARCHITECTURE_MSIL:          return "msil";
916     case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64: return "x86_wow64";
917     default: return "unknown";
918     }
919 }
920
921 /* Return the Windows-specific canonical host name. */
922 static const char *
923 win32_canonical_host(apr_pool_t *pool)
924 {
925   SYSTEM_INFO sysinfo;
926   SYSTEM_INFO local_sysinfo;
927   OSVERSIONINFOEXW osinfo;
928
929   if (system_info(&sysinfo, &local_sysinfo)
930       && svn_sysinfo___fill_windows_version(&osinfo))
931     {
932       const char *arch = processor_name(&local_sysinfo);
933       const char *machine = processor_name(&sysinfo);
934       const char *vendor = "microsoft";
935       const char *sysname = "windows";
936       const char *sysver = apr_psprintf(pool, "%u.%u.%u",
937                                         (unsigned int)osinfo.dwMajorVersion,
938                                         (unsigned int)osinfo.dwMinorVersion,
939                                         (unsigned int)osinfo.dwBuildNumber);
940
941       if (sysinfo.wProcessorArchitecture
942           == local_sysinfo.wProcessorArchitecture)
943         return apr_psprintf(pool, "%s-%s-%s%s",
944                             machine, vendor, sysname, sysver);
945       return apr_psprintf(pool, "%s/%s-%s-%s%s",
946                           arch, machine, vendor, sysname, sysver);
947     }
948
949   return "unknown-microsoft-windows";
950 }
951
952 /* Convert a Unicode string to UTF-8. */
953 static char *
954 wcs_to_utf8(const wchar_t *wcs, apr_pool_t *pool)
955 {
956   const int bufsize = WideCharToMultiByte(CP_UTF8, 0, wcs, -1,
957                                           NULL, 0, NULL, NULL);
958   if (bufsize > 0)
959     {
960       char *const utf8 = apr_palloc(pool, bufsize + 1);
961       WideCharToMultiByte(CP_UTF8, 0, wcs, -1, utf8, bufsize, NULL, NULL);
962       return utf8;
963     }
964   return NULL;
965 }
966
967 /* Query the value called NAME of the registry key HKEY. */
968 static char *
969 registry_value(HKEY hkey, wchar_t *name, apr_pool_t *pool)
970 {
971   DWORD size;
972   wchar_t *value;
973
974   if (RegQueryValueExW(hkey, name, NULL, NULL, NULL, &size))
975     return NULL;
976
977   value = apr_palloc(pool, size + sizeof *value);
978   if (RegQueryValueExW(hkey, name, NULL, NULL, (void*)value, &size))
979     return NULL;
980   value[size / sizeof *value] = 0;
981   return wcs_to_utf8(value, pool);
982 }
983
984 /* Try to glean the Windows release name and associated info from the
985    registry. Failing that, construct a release name from the version
986    info. */
987 static const char *
988 win32_release_name(apr_pool_t *pool)
989 {
990   SYSTEM_INFO sysinfo;
991   OSVERSIONINFOEXW osinfo;
992   HKEY hkcv;
993
994   if (!system_info(&sysinfo, NULL)
995       || !svn_sysinfo___fill_windows_version(&osinfo))
996     return NULL;
997
998   if (!RegOpenKeyExW(HKEY_LOCAL_MACHINE,
999                      L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
1000                      0, KEY_QUERY_VALUE, &hkcv))
1001     {
1002       const char *release = registry_value(hkcv, L"ProductName", pool);
1003       const char *spack = registry_value(hkcv, L"CSDVersion", pool);
1004       const char *curver = registry_value(hkcv, L"CurrentVersion", pool);
1005       const char *curtype = registry_value(hkcv, L"CurrentType", pool);
1006       const char *install = registry_value(hkcv, L"InstallationType", pool);
1007       const char *curbuild = registry_value(hkcv, L"CurrentBuildNumber", pool);
1008
1009       if (!spack && *osinfo.szCSDVersion)
1010         spack = wcs_to_utf8(osinfo.szCSDVersion, pool);
1011
1012       if (!curbuild)
1013         curbuild = registry_value(hkcv, L"CurrentBuild", pool);
1014
1015       if (release || spack || curver || curtype || curbuild)
1016         {
1017           const char *bootinfo = "";
1018           if (curver || install || curtype)
1019             {
1020               bootinfo = apr_psprintf(pool, "[%s%s%s%s%s]",
1021                                       (curver ? curver : ""),
1022                                       (install ? (curver ? " " : "") : ""),
1023                                       (install ? install : ""),
1024                                       (curtype
1025                                        ? (curver||install ? " " : "")
1026                                        : ""),
1027                                       (curtype ? curtype : ""));
1028             }
1029
1030           return apr_psprintf(pool, "%s%s%s%s%s%s%s",
1031                               (release ? release : ""),
1032                               (spack ? (release ? ", " : "") : ""),
1033                               (spack ? spack : ""),
1034                               (curbuild
1035                                ? (release||spack ? ", build " : "build ")
1036                                : ""),
1037                               (curbuild ? curbuild : ""),
1038                               (bootinfo
1039                                ? (release||spack||curbuild ? " " : "")
1040                                : ""),
1041                               (bootinfo ? bootinfo : ""));
1042         }
1043     }
1044
1045   if (*osinfo.szCSDVersion)
1046     {
1047       const char *servicepack = wcs_to_utf8(osinfo.szCSDVersion, pool);
1048
1049       if (servicepack)
1050         return apr_psprintf(pool, "Windows NT %u.%u, %s, build %u",
1051                             (unsigned int)osinfo.dwMajorVersion,
1052                             (unsigned int)osinfo.dwMinorVersion,
1053                             servicepack,
1054                             (unsigned int)osinfo.dwBuildNumber);
1055
1056       /* Assume wServicePackMajor > 0 if szCSDVersion is not empty */
1057       if (osinfo.wServicePackMinor)
1058         return apr_psprintf(pool, "Windows NT %u.%u SP%u.%u, build %u",
1059                             (unsigned int)osinfo.dwMajorVersion,
1060                             (unsigned int)osinfo.dwMinorVersion,
1061                             (unsigned int)osinfo.wServicePackMajor,
1062                             (unsigned int)osinfo.wServicePackMinor,
1063                             (unsigned int)osinfo.dwBuildNumber);
1064
1065       return apr_psprintf(pool, "Windows NT %u.%u SP%u, build %u",
1066                           (unsigned int)osinfo.dwMajorVersion,
1067                           (unsigned int)osinfo.dwMinorVersion,
1068                           (unsigned int)osinfo.wServicePackMajor,
1069                           (unsigned int)osinfo.dwBuildNumber);
1070     }
1071
1072   return apr_psprintf(pool, "Windows NT %u.%u, build %u",
1073                       (unsigned int)osinfo.dwMajorVersion,
1074                       (unsigned int)osinfo.dwMinorVersion,
1075                       (unsigned int)osinfo.dwBuildNumber);
1076 }
1077
1078
1079 /* Get a list of handles of shared libs loaded by the current
1080    process. Returns a NULL-terminated array alocated from POOL. */
1081 static HMODULE *
1082 enum_loaded_modules(apr_pool_t *pool)
1083 {
1084   HMODULE psapi_dll = 0;
1085   HANDLE current = GetCurrentProcess();
1086   HMODULE dummy[1];
1087   HMODULE *handles;
1088   DWORD size;
1089   FNENUMPROCESSMODULES EnumProcessModules_;
1090
1091   psapi_dll = GetModuleHandleW(L"psapi.dll");
1092
1093   if (!psapi_dll)
1094     {
1095       /* Load and never unload, just like static linking */
1096       psapi_dll = LoadLibraryW(L"psapi.dll");
1097     }
1098
1099   if (!psapi_dll)
1100       return NULL;
1101
1102   EnumProcessModules_ = (FNENUMPROCESSMODULES)
1103                               GetProcAddress(psapi_dll, "EnumProcessModules");
1104
1105   /* Before Windows XP psapi was an optional module */
1106   if (! EnumProcessModules_)
1107     return NULL;
1108
1109   if (!EnumProcessModules_(current, dummy, sizeof(dummy), &size))
1110     return NULL;
1111
1112   handles = apr_palloc(pool, size + sizeof *handles);
1113   if (! EnumProcessModules_(current, handles, size, &size))
1114     return NULL;
1115   handles[size / sizeof *handles] = NULL;
1116   return handles;
1117 }
1118
1119 /* Find the version number, if any, embedded in FILENAME. */
1120 static const char *
1121 file_version_number(const wchar_t *filename, apr_pool_t *pool)
1122 {
1123   VS_FIXEDFILEINFO info;
1124   unsigned int major, minor, micro, nano;
1125   void *data;
1126   DWORD data_size = GetFileVersionInfoSizeW(filename, NULL);
1127   void *vinfo;
1128   UINT vinfo_size;
1129
1130   if (!data_size)
1131     return NULL;
1132
1133   data = apr_palloc(pool, data_size);
1134   if (!GetFileVersionInfoW(filename, 0, data_size, data))
1135     return NULL;
1136
1137   if (!VerQueryValueW(data, L"\\", &vinfo, &vinfo_size))
1138     return NULL;
1139
1140   if (vinfo_size != sizeof info)
1141     return NULL;
1142
1143   memcpy(&info, vinfo, sizeof info);
1144   major = (info.dwFileVersionMS >> 16) & 0xFFFF;
1145   minor = info.dwFileVersionMS & 0xFFFF;
1146   micro = (info.dwFileVersionLS >> 16) & 0xFFFF;
1147   nano = info.dwFileVersionLS & 0xFFFF;
1148
1149   if (!nano)
1150     {
1151       if (!micro)
1152         return apr_psprintf(pool, "%u.%u", major, minor);
1153       else
1154         return apr_psprintf(pool, "%u.%u.%u", major, minor, micro);
1155     }
1156   return apr_psprintf(pool, "%u.%u.%u.%u", major, minor, micro, nano);
1157 }
1158
1159 /* List the shared libraries loaded by the current process. */
1160 static const apr_array_header_t *
1161 win32_shared_libs(apr_pool_t *pool)
1162 {
1163   apr_array_header_t *array = NULL;
1164   wchar_t buffer[MAX_PATH + 1];
1165   HMODULE *handles = enum_loaded_modules(pool);
1166   HMODULE *module;
1167
1168   for (module = handles; module && *module; ++module)
1169     {
1170       const char *filename;
1171       const char *version;
1172       if (GetModuleFileNameW(*module, buffer, MAX_PATH))
1173         {
1174           buffer[MAX_PATH] = 0;
1175
1176           version = file_version_number(buffer, pool);
1177           filename = wcs_to_utf8(buffer, pool);
1178           if (filename)
1179             {
1180               svn_version_ext_loaded_lib_t *lib;
1181
1182               if (!array)
1183                 {
1184                   array = apr_array_make(pool, 32, sizeof(*lib));
1185                 }
1186               lib = &APR_ARRAY_PUSH(array, svn_version_ext_loaded_lib_t);
1187               lib->name = svn_dirent_local_style(filename, pool);
1188               lib->version = version;
1189             }
1190         }
1191     }
1192
1193   return array;
1194 }
1195 #endif /* WIN32 */
1196
1197 \f
1198 #ifdef SVN_HAVE_MACOS_PLIST
1199 /* implements svn_write_fn_t to copy the data into a CFMutableDataRef that's
1200  * in the baton. */
1201 static svn_error_t *
1202 write_to_cfmutabledata(void *baton, const char *data, apr_size_t *len)
1203 {
1204   CFMutableDataRef *resource = (CFMutableDataRef *) baton;
1205
1206   CFDataAppendBytes(*resource, (UInt8 *)data, *len);
1207
1208   return SVN_NO_ERROR;
1209 }
1210
1211 /* Load the SystemVersion.plist or ServerVersion.plist file into a
1212    property list. Set SERVER to TRUE if the file read was
1213    ServerVersion.plist. */
1214 static CFDictionaryRef
1215 system_version_plist(svn_boolean_t *server, apr_pool_t *pool)
1216 {
1217   static const char server_version[] =
1218     "/System/Library/CoreServices/ServerVersion.plist";
1219   static const char system_version[] =
1220     "/System/Library/CoreServices/SystemVersion.plist";
1221   svn_stream_t *read_stream, *write_stream;
1222   svn_error_t *err;
1223   CFPropertyListRef plist = NULL;
1224   CFMutableDataRef resource = CFDataCreateMutable(kCFAllocatorDefault, 0);
1225
1226   /* failed getting the CFMutableDataRef, shouldn't happen */
1227   if (!resource)
1228     return NULL;
1229
1230   /* Try to open the plist files to get the data */
1231   err = svn_stream_open_readonly(&read_stream, server_version, pool, pool);
1232   if (err)
1233     {
1234       if (!APR_STATUS_IS_ENOENT(err->apr_err))
1235         {
1236           svn_error_clear(err);
1237           CFRelease(resource);
1238           return NULL;
1239         }
1240       else
1241         {
1242           svn_error_clear(err);
1243           err = svn_stream_open_readonly(&read_stream, system_version,
1244                                          pool, pool);
1245           if (err)
1246             {
1247               svn_error_clear(err);
1248               CFRelease(resource);
1249               return NULL;
1250             }
1251
1252           *server = FALSE;
1253         }
1254     }
1255   else
1256     {
1257       *server = TRUE;
1258     }
1259
1260   /* copy the data onto the CFMutableDataRef to allow us to provide it to
1261    * the CoreFoundation functions that parse proprerty lists */
1262   write_stream = svn_stream_create(&resource, pool);
1263   svn_stream_set_write(write_stream, write_to_cfmutabledata);
1264   err = svn_stream_copy3(read_stream, write_stream, NULL, NULL, pool);
1265   if (err)
1266     {
1267       svn_error_clear(err);
1268       return NULL;
1269     }
1270
1271 #if __MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
1272   /* This function is only available from Mac OS 10.6 onward. */
1273   plist = CFPropertyListCreateWithData(kCFAllocatorDefault, resource,
1274                                        kCFPropertyListImmutable,
1275                                        NULL, NULL);
1276 #else  /* Mac OS 10.5 or earlier */
1277   /* This function obsolete and deprecated since Mac OS 10.10. */
1278   plist = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, resource,
1279                                           kCFPropertyListImmutable,
1280                                           NULL);
1281 #endif /* MAC_OS_X_VERSION_10_6 */
1282
1283   if (resource)
1284     CFRelease(resource);
1285
1286   if (!plist)
1287     return NULL;
1288
1289   if (CFDictionaryGetTypeID() != CFGetTypeID(plist))
1290     {
1291       /* Oops ... this really should be a dict. */
1292       CFRelease(plist);
1293       return NULL;
1294     }
1295
1296   return plist;
1297 }
1298
1299 /* Return the value for KEY from PLIST, or NULL if not available. */
1300 static const char *
1301 value_from_dict(CFDictionaryRef plist, CFStringRef key, apr_pool_t *pool)
1302 {
1303   CFStringRef valref;
1304   CFIndex bufsize;
1305   const void *valptr;
1306   const char *value;
1307
1308   if (!CFDictionaryGetValueIfPresent(plist, key, &valptr))
1309     return NULL;
1310
1311   valref = valptr;
1312   if (CFStringGetTypeID() != CFGetTypeID(valref))
1313     return NULL;
1314
1315   value = CFStringGetCStringPtr(valref, kCFStringEncodingUTF8);
1316   if (value)
1317     return apr_pstrdup(pool, value);
1318
1319   bufsize =  5 * CFStringGetLength(valref) + 1;
1320   value = apr_palloc(pool, bufsize);
1321   if (!CFStringGetCString(valref, (char*)value, bufsize,
1322                           kCFStringEncodingUTF8))
1323     value = NULL;
1324
1325   return value;
1326 }
1327
1328 /* Return the minor version the operating system, given the number in
1329    a format that matches the regular expression /^10\.\d+(\..*)?$/ */
1330 static int
1331 macos_minor_version(const char *osver)
1332 {
1333   char *end = NULL;
1334   unsigned long num = strtoul(osver, &end, 10);
1335
1336   if (!end || *end != '.' || num != 10)
1337     return -1;
1338
1339   osver = end + 1;
1340   end = NULL;
1341   num = strtoul(osver, &end, 10);
1342   if (!end || (*end && *end != '.'))
1343     return -1;
1344
1345   return (int)num;
1346 }
1347
1348 /* Return the product name of the operating system. */
1349 static const char *
1350 product_name_from_minor_version(int minor, const char* product_name)
1351 {
1352   /* We can only do this if we know the official product name. */
1353   if (0 != strcmp(product_name, "Mac OS X"))
1354     return product_name;
1355
1356   if (minor <= 7)
1357     return product_name;
1358
1359   if (minor <= 11)
1360     return "OS X";
1361
1362   return "macOS";
1363 }
1364
1365 /* Return the commercial name of the operating system. */
1366 static const char *
1367 release_name_from_minor_version(int minor, const char* product_name)
1368 {
1369   /* We can only do this if we know the official product name. */
1370   if (0 == strcmp(product_name, "Mac OS X"))
1371     {
1372       /* See https://en.wikipedia.org/wiki/MacOS_version_history#Releases */
1373       switch(minor)
1374         {
1375         case  0: return "Cheetah";
1376         case  1: return "Puma";
1377         case  2: return "Jaguar";
1378         case  3: return "Panther";
1379         case  4: return "Tiger";
1380         case  5: return "Leopard";
1381         case  6: return "Snow Leopard";
1382         case  7: return "Lion";
1383         case  8: return "Mountain Lion";
1384         case  9: return "Mavericks";
1385         case 10: return "Yosemite";
1386         case 11: return "El Capitan";
1387         case 12: return "Sierra";
1388         case 13: return "High Sierra";
1389         case 14: return "Mojave";
1390         case 15: return "Catalina";
1391         }
1392     }
1393   return NULL;
1394 }
1395
1396 /* Construct the release name from information stored in the Mac OS X
1397    "SystemVersion.plist" file (or ServerVersion.plist, for Mac Os
1398    Server. */
1399 static const char *
1400 macos_release_name(apr_pool_t *pool)
1401 {
1402   svn_boolean_t server;
1403   CFDictionaryRef plist = system_version_plist(&server, pool);
1404
1405   if (plist)
1406     {
1407       const char *osname = value_from_dict(plist, CFSTR("ProductName"), pool);
1408       const char *osver = value_from_dict(plist,
1409                                           CFSTR("ProductUserVisibleVersion"),
1410                                           pool);
1411       const char *build = value_from_dict(plist,
1412                                           CFSTR("ProductBuildVersion"),
1413                                           pool);
1414       const char *release;
1415       int minor_version;
1416
1417       if (!osver)
1418         osver = value_from_dict(plist, CFSTR("ProductVersion"), pool);
1419       minor_version = macos_minor_version(osver);
1420       release = release_name_from_minor_version(minor_version, osname);
1421       osname = product_name_from_minor_version(minor_version, osname);
1422
1423       CFRelease(plist);
1424       return apr_psprintf(pool, "%s%s%s%s%s%s%s%s",
1425                           (osname ? osname : ""),
1426                           (release ? (osname ? " " : "") : ""),
1427                           (release ? release : ""),
1428                           (osver ? (osname||release ? " " : "") : ""),
1429                           (osver ? osver : ""),
1430                           (build
1431                            ? (osname||release||osver ? ", " : "")
1432                            : ""),
1433                           (build
1434                            ? (server ? "server build " : "build ")
1435                            : ""),
1436                           (build ? build : ""));
1437     }
1438
1439   return NULL;
1440 }
1441 #endif  /* SVN_HAVE_MACOS_PLIST */
1442
1443 #ifdef SVN_HAVE_MACHO_ITERATE
1444 /* List the shared libraries loaded by the current process.
1445    Ignore frameworks and system libraries, they're just clutter. */
1446 static const apr_array_header_t *
1447 macos_shared_libs(apr_pool_t *pool)
1448 {
1449   static const char slb_prefix[] = "/usr/lib/system/";
1450   static const char fwk_prefix[] = "/System/Library/Frameworks/";
1451   static const char pfk_prefix[] = "/System/Library/PrivateFrameworks/";
1452
1453   const size_t slb_prefix_len = strlen(slb_prefix);
1454   const size_t fwk_prefix_len = strlen(fwk_prefix);
1455   const size_t pfk_prefix_len = strlen(pfk_prefix);
1456
1457   apr_array_header_t *result = NULL;
1458   apr_array_header_t *dylibs = NULL;
1459
1460   uint32_t i;
1461   for (i = 0;; ++i)
1462     {
1463       const struct mach_header *header = _dyld_get_image_header(i);
1464       const char *filename = _dyld_get_image_name(i);
1465       const char *version;
1466       char *truename;
1467       svn_version_ext_loaded_lib_t *lib;
1468
1469       if (!(header && filename))
1470         break;
1471
1472       switch (header->cputype)
1473         {
1474         case CPU_TYPE_I386:      version = _("Intel"); break;
1475         case CPU_TYPE_X86_64:    version = _("Intel 64-bit"); break;
1476         case CPU_TYPE_POWERPC:   version = _("PowerPC"); break;
1477         case CPU_TYPE_POWERPC64: version = _("PowerPC 64-bit"); break;
1478         default:
1479           version = NULL;
1480         }
1481
1482       if (0 == apr_filepath_merge(&truename, "", filename,
1483                                   APR_FILEPATH_NATIVE
1484                                   | APR_FILEPATH_TRUENAME,
1485                                   pool))
1486         filename = truename;
1487       else
1488         filename = apr_pstrdup(pool, filename);
1489
1490       if (0 == strncmp(filename, slb_prefix, slb_prefix_len)
1491           || 0 == strncmp(filename, fwk_prefix, fwk_prefix_len)
1492           || 0 == strncmp(filename, pfk_prefix, pfk_prefix_len))
1493         {
1494           /* Ignore frameworks and system libraries. */
1495           continue;
1496         }
1497
1498       if (header->filetype == MH_EXECUTE)
1499         {
1500           /* Make sure the program filename is first in the list */
1501           if (!result)
1502             {
1503               result = apr_array_make(pool, 32, sizeof(*lib));
1504             }
1505           lib = &APR_ARRAY_PUSH(result, svn_version_ext_loaded_lib_t);
1506         }
1507       else
1508         {
1509           if (!dylibs)
1510             {
1511               dylibs = apr_array_make(pool, 32, sizeof(*lib));
1512             }
1513           lib = &APR_ARRAY_PUSH(dylibs, svn_version_ext_loaded_lib_t);
1514         }
1515
1516       lib->name = filename;
1517       lib->version = version;
1518     }
1519
1520   /* Gather results into one array. */
1521   if (dylibs)
1522     {
1523       if (result)
1524         apr_array_cat(result, dylibs);
1525       else
1526         result = dylibs;
1527     }
1528
1529   return result;
1530 }
1531 #endif  /* SVN_HAVE_MACHO_ITERATE */