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