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