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
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 *
svn_sysinfo__canonical_host(apr_pool_t * pool)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 *
svn_sysinfo__release_name(apr_pool_t * pool)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 *
svn_sysinfo__linked_libs(apr_pool_t * pool)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 *
svn_sysinfo__loaded_libs(apr_pool_t * pool)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
212 #if HAVE_UNAME
213 static const char*
canonical_host_from_uname(apr_pool_t * pool)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 *
release_name_from_uname(apr_pool_t * pool)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
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
stringbuf_first_whitespace(const svn_stringbuf_t * str)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
stringbuf_skip_whitespace_field(svn_stringbuf_t * str)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 *
stringbuf_split_key(svn_stringbuf_t * buffer,char delim)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 *
lsb_release(apr_pool_t * pool)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 *
systemd_release(apr_pool_t * pool)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 *
read_file_contents(const char * filename,apr_pool_t * pool)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
stringbuf_first_line_only(svn_stringbuf_t * buffer)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 *
redhat_release(apr_pool_t * pool)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 *
suse_release(apr_pool_t * pool)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 *
debian_release(apr_pool_t * pool)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 *
linux_release_name(apr_pool_t * pool)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 *
parse_pointer_value(const char * start,const char * limit,char ** end)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
check_elf_header(const unsigned char * map_start,const unsigned char * map_end)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 *
linux_shared_libs(apr_pool_t * pool)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
844 #ifdef WIN32
845 typedef DWORD (WINAPI *FNGETNATIVESYSTEMINFO)(LPSYSTEM_INFO);
846 typedef BOOL (WINAPI *FNENUMPROCESSMODULES) (HANDLE, HMODULE*, DWORD, LPDWORD);
847
848 svn_boolean_t
svn_sysinfo___fill_windows_version(OSVERSIONINFOEXW * version_info)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
system_info(SYSTEM_INFO * sysinfo,SYSTEM_INFO * local_sysinfo)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 *
processor_name(SYSTEM_INFO * sysinfo)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 *
win32_canonical_host(apr_pool_t * pool)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 *
wcs_to_utf8(const wchar_t * wcs,apr_pool_t * pool)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 *
registry_value(HKEY hkey,wchar_t * name,apr_pool_t * pool)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 *
win32_release_name(apr_pool_t * pool)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 *
enum_loaded_modules(apr_pool_t * pool)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 *
file_version_number(const wchar_t * filename,apr_pool_t * pool)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 *
win32_shared_libs(apr_pool_t * pool)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
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 *
write_to_cfmutabledata(void * baton,const char * data,apr_size_t * len)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
system_version_plist(svn_boolean_t * server,apr_pool_t * pool)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 *
value_from_dict(CFDictionaryRef plist,CFStringRef key,apr_pool_t * pool)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
macos_minor_version(const char * osver)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 *
product_name_from_minor_version(int minor,const char * product_name)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 *
release_name_from_minor_version(int minor,const char * product_name)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 *
macos_release_name(apr_pool_t * pool)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 *
macos_shared_libs(apr_pool_t * pool)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 */
1532