VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/dbg/dbgcfg.cpp@ 58314

Last change on this file since 58314 was 57944, checked in by vboxsync, 9 years ago

iprt: More doxygen corrections.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 83.4 KB
Line 
1/* $Id: dbgcfg.cpp 57944 2015-09-29 15:07:09Z vboxsync $ */
2/** @file
3 * IPRT - Debugging Configuration.
4 */
5
6/*
7 * Copyright (C) 2013-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP RTLOGGROUP_DBG
32#include <iprt/dbg.h>
33#include "internal/iprt.h"
34
35#include <iprt/alloca.h>
36#include <iprt/asm.h>
37#include <iprt/assert.h>
38#include <iprt/critsect.h>
39#include <iprt/ctype.h>
40#include <iprt/dir.h>
41#include <iprt/err.h>
42#include <iprt/env.h>
43#include <iprt/file.h>
44#ifdef IPRT_WITH_HTTP
45# include <iprt/http.h>
46#endif
47#include <iprt/list.h>
48#include <iprt/log.h>
49#include <iprt/mem.h>
50#include <iprt/path.h>
51#include <iprt/process.h>
52#include <iprt/semaphore.h>
53#include <iprt/string.h>
54#include <iprt/uuid.h>
55#include "internal/magics.h"
56
57
58/*********************************************************************************************************************************
59* Structures and Typedefs *
60*********************************************************************************************************************************/
61/**
62 * String list entry.
63 */
64typedef struct RTDBGCFGSTR
65{
66 /** List entry. */
67 RTLISTNODE ListEntry;
68 /** Domain specific flags. */
69 uint16_t fFlags;
70 /** The length of the string. */
71 uint16_t cch;
72 /** The string. */
73 char sz[1];
74} RTDBGCFGSTR;
75/** Pointer to a string list entry. */
76typedef RTDBGCFGSTR *PRTDBGCFGSTR;
77
78
79/**
80 * Configuration instance.
81 */
82typedef struct RTDBGCFGINT
83{
84 /** The magic value (RTDBGCFG_MAGIC). */
85 uint32_t u32Magic;
86 /** Reference counter. */
87 uint32_t volatile cRefs;
88 /** Flags, see RTDBGCFG_FLAGS_XXX. */
89 uint64_t fFlags;
90
91 /** List of paths to search for debug files and executable images. */
92 RTLISTANCHOR PathList;
93 /** List of debug file suffixes. */
94 RTLISTANCHOR SuffixList;
95 /** List of paths to search for source files. */
96 RTLISTANCHOR SrcPathList;
97
98#ifdef RT_OS_WINDOWS
99 /** The _NT_ALT_SYMBOL_PATH and _NT_SYMBOL_PATH combined. */
100 RTLISTANCHOR NtSymbolPathList;
101 /** The _NT_EXECUTABLE_PATH. */
102 RTLISTANCHOR NtExecutablePathList;
103 /** The _NT_SOURCE_PATH. */
104 RTLISTANCHOR NtSourcePath;
105#endif
106
107 /** Log callback function. */
108 PFNRTDBGCFGLOG pfnLogCallback;
109 /** User argument to pass to the log callback. */
110 void *pvLogUser;
111
112 /** Critical section protecting the instance data. */
113 RTCRITSECTRW CritSect;
114} *PRTDBGCFGINT;
115
116/**
117 * Mnemonics map entry for a 64-bit unsigned property value.
118 */
119typedef struct RTDBGCFGU64MNEMONIC
120{
121 /** The flags to set or clear. */
122 uint64_t fFlags;
123 /** The mnemonic. */
124 const char *pszMnemonic;
125 /** The length of the mnemonic. */
126 uint8_t cchMnemonic;
127 /** If @c true, the bits in fFlags will be set, if @c false they will be
128 * cleared. */
129 bool fSet;
130} RTDBGCFGU64MNEMONIC;
131/** Pointer to a read only mnemonic map entry for a uint64_t property. */
132typedef RTDBGCFGU64MNEMONIC const *PCRTDBGCFGU64MNEMONIC;
133
134
135/** @name Open flags.
136 * @{ */
137/** The operative system mask. The values are RT_OPSYS_XXX. */
138#define RTDBGCFG_O_OPSYS_MASK UINT32_C(0x000000ff)
139/** The files may be compressed MS styled. */
140#define RTDBGCFG_O_MAYBE_COMPRESSED_MS RT_BIT_32(26)
141/** Whether to make a recursive search. */
142#define RTDBGCFG_O_RECURSIVE RT_BIT_32(27)
143/** We're looking for a separate debug file. */
144#define RTDBGCFG_O_EXT_DEBUG_FILE RT_BIT_32(28)
145/** We're looking for an executable image. */
146#define RTDBGCFG_O_EXECUTABLE_IMAGE RT_BIT_32(29)
147/** The file search should be done in an case insensitive fashion. */
148#define RTDBGCFG_O_CASE_INSENSITIVE RT_BIT_32(30)
149/** Use Windbg style symbol servers when encountered in the path. */
150#define RTDBGCFG_O_SYMSRV RT_BIT_32(31)
151/** @} */
152
153
154/*********************************************************************************************************************************
155* Defined Constants And Macros *
156*********************************************************************************************************************************/
157/** Validates a debug module handle and returns rc if not valid. */
158#define RTDBGCFG_VALID_RETURN_RC(pThis, rc) \
159 do { \
160 AssertPtrReturn((pThis), (rc)); \
161 AssertReturn((pThis)->u32Magic == RTDBGCFG_MAGIC, (rc)); \
162 AssertReturn((pThis)->cRefs > 0, (rc)); \
163 } while (0)
164
165
166/*********************************************************************************************************************************
167* Global Variables *
168*********************************************************************************************************************************/
169/** Mnemonics map for RTDBGCFGPROP_FLAGS. */
170static const RTDBGCFGU64MNEMONIC g_aDbgCfgFlags[] =
171{
172 { RTDBGCFG_FLAGS_DEFERRED, RT_STR_TUPLE("deferred"), true },
173 { RTDBGCFG_FLAGS_DEFERRED, RT_STR_TUPLE("nodeferred"), false },
174 { RTDBGCFG_FLAGS_NO_SYM_SRV, RT_STR_TUPLE("symsrv"), false },
175 { RTDBGCFG_FLAGS_NO_SYM_SRV, RT_STR_TUPLE("nosymsrv"), true },
176 { RTDBGCFG_FLAGS_NO_SYSTEM_PATHS, RT_STR_TUPLE("syspaths"), false },
177 { RTDBGCFG_FLAGS_NO_SYSTEM_PATHS, RT_STR_TUPLE("nosyspaths"), true },
178 { RTDBGCFG_FLAGS_NO_RECURSIV_SEARCH, RT_STR_TUPLE("rec"), false },
179 { RTDBGCFG_FLAGS_NO_RECURSIV_SEARCH, RT_STR_TUPLE("norec"), true },
180 { RTDBGCFG_FLAGS_NO_RECURSIV_SRC_SEARCH, RT_STR_TUPLE("recsrc"), false },
181 { RTDBGCFG_FLAGS_NO_RECURSIV_SRC_SEARCH, RT_STR_TUPLE("norecsrc"), true },
182 { 0, NULL, 0, false }
183};
184
185
186/** Interesting bundle suffixes. */
187static const char * const g_apszBundleSuffixes[] =
188{
189 ".kext",
190 ".app",
191 ".framework",
192 ".component",
193 ".action",
194 ".caction",
195 ".bundle",
196 ".sourcebundle",
197 ".menu",
198 ".plugin",
199 ".ppp",
200 ".monitorpanel",
201 ".scripting",
202 ".prefPane",
203 ".qlgenerator",
204 ".brailledriver",
205 ".saver",
206 ".SpeechVoice",
207 ".SpeechRecognizer",
208 ".SpeechSynthesizer",
209 ".mdimporter",
210 ".spreporter",
211 ".xpc",
212 NULL
213};
214
215/** Debug bundle suffixes. (Same as above + .dSYM) */
216static const char * const g_apszDSymBundleSuffixes[] =
217{
218 ".dSYM",
219 ".kext.dSYM",
220 ".app.dSYM",
221 ".framework.dSYM",
222 ".component.dSYM",
223 ".action.dSYM",
224 ".caction.dSYM",
225 ".bundle.dSYM",
226 ".sourcebundle.dSYM",
227 ".menu.dSYM",
228 ".plugin.dSYM",
229 ".ppp.dSYM",
230 ".monitorpanel.dSYM",
231 ".scripting.dSYM",
232 ".prefPane.dSYM",
233 ".qlgenerator.dSYM",
234 ".brailledriver.dSYM",
235 ".saver.dSYM",
236 ".SpeechVoice.dSYM",
237 ".SpeechRecognizer.dSYM",
238 ".SpeechSynthesizer.dSYM",
239 ".mdimporter.dSYM",
240 ".spreporter.dSYM",
241 ".xpc.dSYM",
242 NULL
243};
244
245
246
247/**
248 * Runtime logging, level 1.
249 *
250 * @param pThis The debug config instance data.
251 * @param pszFormat The message format string.
252 * @param ... Arguments references in the format string.
253 */
254static void rtDbgCfgLog1(PRTDBGCFGINT pThis, const char *pszFormat, ...)
255{
256 if (LogIsEnabled() || (pThis && pThis->pfnLogCallback))
257 {
258 va_list va;
259 va_start(va, pszFormat);
260 char *pszMsg = RTStrAPrintf2V(pszFormat, va);
261 va_end(va);
262
263 Log(("RTDbgCfg: %s", pszMsg));
264 if (pThis && pThis->pfnLogCallback)
265 pThis->pfnLogCallback(pThis, 1, pszMsg, pThis->pvLogUser);
266 RTStrFree(pszMsg);
267 }
268}
269
270
271/**
272 * Runtime logging, level 2.
273 *
274 * @param pThis The debug config instance data.
275 * @param pszFormat The message format string.
276 * @param ... Arguments references in the format string.
277 */
278static void rtDbgCfgLog2(PRTDBGCFGINT pThis, const char *pszFormat, ...)
279{
280 if (LogIs2Enabled() || (pThis && pThis->pfnLogCallback))
281 {
282 va_list va;
283 va_start(va, pszFormat);
284 char *pszMsg = RTStrAPrintf2V(pszFormat, va);
285 va_end(va);
286
287 Log(("RTDbgCfg: %s", pszMsg));
288 if (pThis && pThis->pfnLogCallback)
289 pThis->pfnLogCallback(pThis, 2, pszMsg, pThis->pvLogUser);
290 RTStrFree(pszMsg);
291 }
292}
293
294
295/**
296 * Checks if the file system at the given path is case insensitive or not.
297 *
298 * @returns true / false
299 * @param pszPath The path to query about.
300 */
301static int rtDbgCfgIsFsCaseInsensitive(const char *pszPath)
302{
303 RTFSPROPERTIES Props;
304 int rc = RTFsQueryProperties(pszPath, &Props);
305 if (RT_FAILURE(rc))
306 return RT_OPSYS == RT_OPSYS_DARWIN
307 || RT_OPSYS == RT_OPSYS_DOS
308 || RT_OPSYS == RT_OPSYS_OS2
309 || RT_OPSYS == RT_OPSYS_NT
310 || RT_OPSYS == RT_OPSYS_WINDOWS;
311 return !Props.fCaseSensitive;
312}
313
314
315/**
316 * Worker that does case sensitive file/dir searching.
317 *
318 * @returns true / false.
319 * @param pszPath The path buffer containing an existing directory and
320 * at @a offLastComp the name we're looking for.
321 * RTPATH_MAX in size. On success, this last component
322 * will have the correct case. On failure, the last
323 * component is stripped off.
324 * @param offLastComp The offset of the last component (for chopping it
325 * off).
326 * @param enmType What kind of thing we're looking for.
327 */
328static bool rtDbgCfgIsXxxxAndFixCaseWorker(char *pszPath, size_t offLastComp, RTDIRENTRYTYPE enmType)
329{
330 /** @todo IPRT should generalize this so we can use host specific tricks to
331 * speed it up. */
332
333 char *pszName = &pszPath[offLastComp];
334
335 /* Return straight away if the name isn't case foldable. */
336 if (!RTStrIsCaseFoldable(pszName))
337 {
338 *pszName = '\0';
339 return false;
340 }
341
342 /*
343 * Try some simple case folding games.
344 */
345 RTStrToLower(pszName);
346 if (RTFileExists(pszPath))
347 return true;
348
349 RTStrToUpper(pszName);
350 if (RTFileExists(pszPath))
351 return true;
352
353 /*
354 * Open the directory and check each entry in it.
355 */
356 char chSaved = *pszName;
357 *pszName = '\0';
358
359 PRTDIR pDir;
360 int rc = RTDirOpen(&pDir, pszPath);
361 if (RT_FAILURE(rc))
362 return false;
363
364 *pszName = chSaved;
365
366 for (;;)
367 {
368 /* Read the next entry. */
369 union
370 {
371 RTDIRENTRY Entry;
372 uint8_t ab[_4K];
373 } u;
374 size_t cbBuf = sizeof(u);
375 rc = RTDirRead(pDir, &u.Entry, &cbBuf);
376 if (RT_FAILURE(rc))
377 break;
378
379 if ( !RTStrICmp(pszName, u.Entry.szName)
380 && ( u.Entry.enmType == enmType
381 || u.Entry.enmType == RTDIRENTRYTYPE_UNKNOWN
382 || u.Entry.enmType == RTDIRENTRYTYPE_SYMLINK) )
383 {
384 strcpy(pszName, u.Entry.szName);
385 if (u.Entry.enmType != enmType)
386 RTDirQueryUnknownType(pszPath, true /*fFollowSymlinks*/, &u.Entry.enmType);
387 if (u.Entry.enmType == enmType)
388 {
389 RTDirClose(pDir);
390 return true;
391 }
392 }
393 }
394
395 RTDirClose(pDir);
396 *pszName = '\0';
397
398 return false;
399}
400
401
402/**
403 * Appends @a pszSubDir to @a pszPath and check whether it exists and is a
404 * directory.
405 *
406 * If @a fCaseInsensitive is set, we will do a case insensitive search for a
407 * matching sub directory.
408 *
409 * @returns true / false
410 * @param pszPath The path buffer containing an existing
411 * directory. RTPATH_MAX in size.
412 * @param pszSubDir The sub directory to append.
413 * @param fCaseInsensitive Whether case insensitive searching is required.
414 */
415static bool rtDbgCfgIsDirAndFixCase(char *pszPath, const char *pszSubDir, bool fCaseInsensitive)
416{
417 /* Save the length of the input path so we can restore it in the case
418 insensitive branch further down. */
419 size_t const cchPath = strlen(pszPath);
420
421 /*
422 * Append the sub directory and check if we got a hit.
423 */
424 int rc = RTPathAppend(pszPath, RTPATH_MAX, pszSubDir);
425 if (RT_FAILURE(rc))
426 return false;
427
428 if (RTDirExists(pszPath))
429 return true;
430
431 /*
432 * Do case insensitive lookup if requested.
433 */
434 if (fCaseInsensitive)
435 return rtDbgCfgIsXxxxAndFixCaseWorker(pszPath, cchPath, RTDIRENTRYTYPE_DIRECTORY);
436
437 pszPath[cchPath] = '\0';
438 return false;
439}
440
441
442/**
443 * Appends @a pszSubDir1 and @a pszSuffix to @a pszPath and check whether it
444 * exists and is a directory.
445 *
446 * If @a fCaseInsensitive is set, we will do a case insensitive search for a
447 * matching sub directory.
448 *
449 * @returns true / false
450 * @param pszPath The path buffer containing an existing
451 * directory. RTPATH_MAX in size.
452 * @param pszSubDir The sub directory to append.
453 * @param pszSuffix The suffix to append.
454 * @param fCaseInsensitive Whether case insensitive searching is required.
455 */
456static bool rtDbgCfgIsDirAndFixCase2(char *pszPath, const char *pszSubDir, const char *pszSuffix, bool fCaseInsensitive)
457{
458 Assert(!strpbrk(pszSuffix, ":/\\"));
459
460 /* Save the length of the input path so we can restore it in the case
461 insensitive branch further down. */
462 size_t const cchPath = strlen(pszPath);
463
464 /*
465 * Append the subdirectory and suffix, then check if we got a hit.
466 */
467 int rc = RTPathAppend(pszPath, RTPATH_MAX, pszSubDir);
468 if (RT_SUCCESS(rc))
469 {
470 rc = RTStrCat(pszPath, RTPATH_MAX, pszSuffix);
471 if (RT_SUCCESS(rc))
472 {
473 if (RTDirExists(pszPath))
474 return true;
475
476 /*
477 * Do case insensitive lookup if requested.
478 */
479 if (fCaseInsensitive)
480 return rtDbgCfgIsXxxxAndFixCaseWorker(pszPath, cchPath, RTDIRENTRYTYPE_DIRECTORY);
481 }
482 }
483
484 pszPath[cchPath] = '\0';
485 return false;
486}
487
488
489/**
490 * Appends @a pszFilename to @a pszPath and check whether it exists and is a
491 * directory.
492 *
493 * If @a fCaseInsensitive is set, we will do a case insensitive search for a
494 * matching filename.
495 *
496 * @returns true / false
497 * @param pszPath The path buffer containing an existing
498 * directory. RTPATH_MAX in size.
499 * @param pszFilename The filename to append.
500 * @param pszSuffix Optional filename suffix to append.
501 * @param fCaseInsensitive Whether case insensitive searching is required.
502 * @param fMsCompressed Whether to look for the MS compressed file name
503 * variant.
504 * @param pfProbablyCompressed This is set to true if a MS compressed
505 * filename variant is returned. Optional.
506 */
507static bool rtDbgCfgIsFileAndFixCase(char *pszPath, const char *pszFilename, const char *pszSuffix, bool fCaseInsensitive,
508 bool fMsCompressed, bool *pfProbablyCompressed)
509{
510 /* Save the length of the input path so we can restore it in the case
511 insensitive branch further down. */
512 size_t cchPath = strlen(pszPath);
513 if (pfProbablyCompressed)
514 *pfProbablyCompressed = false;
515
516 /*
517 * Append the filename and optionally suffix, then check if we got a hit.
518 */
519 int rc = RTPathAppend(pszPath, RTPATH_MAX, pszFilename);
520 if (RT_FAILURE(rc))
521 return false;
522 if (pszSuffix)
523 {
524 Assert(!fMsCompressed);
525 rc = RTStrCat(pszPath, RTPATH_MAX, pszSuffix);
526 if (RT_FAILURE(rc))
527 return false;
528 }
529
530 if (RTFileExists(pszPath))
531 return true;
532
533 /*
534 * Do case insensitive file lookup if requested.
535 */
536 if (fCaseInsensitive)
537 {
538 if (rtDbgCfgIsXxxxAndFixCaseWorker(pszPath, cchPath, RTDIRENTRYTYPE_FILE))
539 return true;
540 }
541
542 /*
543 * Look for MS compressed file if requested.
544 */
545 if ( fMsCompressed
546 && (unsigned char)pszFilename[strlen(pszFilename) - 1] < 0x7f)
547 {
548 pszPath[cchPath] = '\0';
549 rc = RTPathAppend(pszPath, RTPATH_MAX, pszFilename);
550 AssertRCReturn(rc, false);
551 pszPath[strlen(pszPath) - 1] = '_';
552
553 if (pfProbablyCompressed)
554 *pfProbablyCompressed = true;
555
556 if ( RTFileExists(pszPath)
557 || ( fCaseInsensitive
558 && rtDbgCfgIsXxxxAndFixCaseWorker(pszPath, cchPath, RTDIRENTRYTYPE_FILE) ))
559 return true;
560
561 if (pfProbablyCompressed)
562 *pfProbablyCompressed = false;
563 }
564
565 pszPath[cchPath] = '\0';
566 return false;
567}
568
569
570static int rtDbgCfgTryOpenDir(PRTDBGCFGINT pThis, char *pszPath, PRTPATHSPLIT pSplitFn, uint32_t fFlags,
571 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
572{
573 int rcRet = VWRN_NOT_FOUND;
574 int rc2;
575
576 /* If the directory doesn't exist, just quit immediately.
577 Note! Our case insensitivity doesn't extend to the search dirs themselfs,
578 only to the bits under neath them. */
579 if (!RTDirExists(pszPath))
580 {
581 rtDbgCfgLog2(pThis, "Dir does not exist: '%s'\n", pszPath);
582 return rcRet;
583 }
584
585 /* Figure out whether we have to do a case sensitive search or not.
586 Note! As a simplification, we don't ask for case settings in each
587 directory under the user specified path, we assume the file
588 systems that mounted there have compatible settings. Faster
589 that way. */
590 bool const fCaseInsensitive = (fFlags & RTDBGCFG_O_CASE_INSENSITIVE)
591 && !rtDbgCfgIsFsCaseInsensitive(pszPath);
592
593 size_t const cchPath = strlen(pszPath);
594
595 /*
596 * Look for the file with less and less of the original path given.
597 */
598 for (unsigned i = RTPATH_PROP_HAS_ROOT_SPEC(pSplitFn->fProps); i < pSplitFn->cComps; i++)
599 {
600 pszPath[cchPath] = '\0';
601
602 rc2 = VINF_SUCCESS;
603 for (unsigned j = i; j < pSplitFn->cComps - 1U && RT_SUCCESS(rc2); j++)
604 if (!rtDbgCfgIsDirAndFixCase(pszPath, pSplitFn->apszComps[i], fCaseInsensitive))
605 rc2 = VERR_FILE_NOT_FOUND;
606
607 if (RT_SUCCESS(rc2))
608 {
609 if (rtDbgCfgIsFileAndFixCase(pszPath, pSplitFn->apszComps[pSplitFn->cComps - 1], NULL /*pszSuffix*/,
610 fCaseInsensitive, false, NULL))
611 {
612 rtDbgCfgLog1(pThis, "Trying '%s'...\n", pszPath);
613 rc2 = pfnCallback(pThis, pszPath, pvUser1, pvUser2);
614 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
615 {
616 if (rc2 == VINF_CALLBACK_RETURN)
617 rtDbgCfgLog1(pThis, "Found '%s'.\n", pszPath);
618 else
619 rtDbgCfgLog1(pThis, "Error opening '%s'.\n", pszPath);
620 return rc2;
621 }
622 rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc2, pszPath);
623 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
624 rcRet = rc2;
625 }
626 }
627 }
628
629 /*
630 * Do a recursive search if requested.
631 */
632 if ( (fFlags & RTDBGCFG_O_RECURSIVE)
633 && !(pThis->fFlags & RTDBGCFG_FLAGS_NO_RECURSIV_SEARCH) )
634 {
635 /** @todo Recursive searching will be done later. */
636 }
637
638 return rcRet;
639}
640
641static int rtDbgCfgUnpackMsCacheFile(PRTDBGCFGINT pThis, char *pszPath, const char *pszFilename)
642{
643 rtDbgCfgLog2(pThis, "Unpacking '%s'...\n", pszPath);
644
645 /*
646 * Duplicate the source file path, just for simplicity and restore the
647 * final character in the orignal. We cheerfully ignorining any
648 * possibility of multibyte UTF-8 sequences just like the caller did when
649 * setting it to '_'.
650 */
651 char *pszSrcArchive = RTStrDup(pszPath);
652 if (!pszSrcArchive)
653 return VERR_NO_STR_MEMORY;
654
655 pszPath[strlen(pszPath) - 1] = RT_C_TO_LOWER(pszFilename[strlen(pszFilename) - 1]);
656
657
658 /*
659 * Figuring out the argument list for the platform specific unpack util.
660 */
661#ifdef RT_OS_WINDOWS
662 RTPathChangeToDosSlashes(pszSrcArchive, false /*fForce*/);
663 RTPathChangeToDosSlashes(pszPath, false /*fForce*/);
664 const char *papszArgs[] =
665 {
666 "expand.exe",
667 pszSrcArchive,
668 pszPath,
669 NULL
670 };
671
672#else
673 char szExtractDir[RTPATH_MAX];
674 strcpy(szExtractDir, pszPath);
675 RTPathStripFilename(szExtractDir);
676
677 const char *papszArgs[] =
678 {
679 "cabextract",
680 "-L", /* Lower case extracted files. */
681 "-d", szExtractDir, /* Extraction path */
682 pszSrcArchive,
683 NULL
684 };
685#endif
686
687 /*
688 * Do the unpacking.
689 */
690 RTPROCESS hChild;
691 int rc = RTProcCreate(papszArgs[0], papszArgs, RTENV_DEFAULT,
692#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
693 RTPROC_FLAGS_NO_WINDOW | RTPROC_FLAGS_HIDDEN | RTPROC_FLAGS_SEARCH_PATH,
694#else
695 RTPROC_FLAGS_SEARCH_PATH,
696#endif
697 &hChild);
698 if (RT_SUCCESS(rc))
699 {
700 RTPROCSTATUS ProcStatus;
701 rc = RTProcWait(hChild, RTPROCWAIT_FLAGS_BLOCK, &ProcStatus);
702 if (RT_SUCCESS(rc))
703 {
704 if ( ProcStatus.enmReason == RTPROCEXITREASON_NORMAL
705 && ProcStatus.iStatus == 0)
706 {
707 if (RTPathExists(pszPath))
708 {
709 rtDbgCfgLog1(pThis, "Successfully unpacked '%s' to '%s'.\n", pszSrcArchive, pszPath);
710 rc = VINF_SUCCESS;
711 }
712 else
713 {
714 rtDbgCfgLog1(pThis, "Successfully ran unpacker on '%s', but '%s' is missing!\n", pszSrcArchive, pszPath);
715 rc = VERR_ZIP_ERROR;
716 }
717 }
718 else
719 {
720 rtDbgCfgLog2(pThis, "Unpacking '%s' failed: iStatus=%d enmReason=%d\n",
721 pszSrcArchive, ProcStatus.iStatus, ProcStatus.enmReason);
722 rc = VERR_ZIP_CORRUPTED;
723 }
724 }
725 else
726 rtDbgCfgLog1(pThis, "Error waiting for process: %Rrc\n", rc);
727
728 }
729 else
730 rtDbgCfgLog1(pThis, "Error starting unpack process '%s': %Rrc\n", papszArgs[0], rc);
731
732 return rc;
733}
734
735
736static int rtDbgCfgTryDownloadAndOpen(PRTDBGCFGINT pThis, const char *pszServer, char *pszPath,
737 const char *pszCacheSubDir, const char *pszUuidMappingSubDir,
738 PRTPATHSPLIT pSplitFn, const char *pszCacheSuffix, uint32_t fFlags,
739 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
740{
741#ifdef IPRT_WITH_HTTP
742 NOREF(pszUuidMappingSubDir); /** @todo do we bother trying pszUuidMappingSubDir? */
743 NOREF(pszCacheSuffix); /** @todo do we bother trying pszUuidMappingSubDir? */
744
745 if (pThis->fFlags & RTDBGCFG_FLAGS_NO_SYM_SRV)
746 return VWRN_NOT_FOUND;
747 if (!pszCacheSubDir || !*pszCacheSubDir)
748 return VWRN_NOT_FOUND;
749
750 /*
751 * Create the path.
752 */
753 size_t cchTmp = strlen(pszPath);
754
755 int rc = RTDirCreateFullPath(pszPath, 0766);
756 if (!RTDirExists(pszPath))
757 {
758 Log(("Error creating cache dir '%s': %Rrc\n", pszPath, rc));
759 return rc;
760 }
761
762 const char *pszFilename = pSplitFn->apszComps[pSplitFn->cComps - 1];
763 rc = RTPathAppend(pszPath, RTPATH_MAX, pszFilename);
764 if (RT_FAILURE(rc))
765 return rc;
766 RTStrToLower(&pszPath[cchTmp]);
767 if (!RTDirExists(pszPath))
768 {
769 rc = RTDirCreate(pszPath, 0766, 0);
770 if (RT_FAILURE(rc))
771 {
772 Log(("RTDirCreate(%s) -> %Rrc\n", pszPath, rc));
773 }
774 }
775
776 rc = RTPathAppend(pszPath, RTPATH_MAX, pszCacheSubDir);
777 if (RT_FAILURE(rc))
778 return rc;
779 if (!RTDirExists(pszPath))
780 {
781 rc = RTDirCreate(pszPath, 0766, 0);
782 if (RT_FAILURE(rc))
783 {
784 Log(("RTDirCreate(%s) -> %Rrc\n", pszPath, rc));
785 }
786 }
787
788 /* Prepare the destination file name while we're here. */
789 cchTmp = strlen(pszPath);
790 RTStrToLower(&pszPath[cchTmp]);
791 rc = RTPathAppend(pszPath, RTPATH_MAX, pszFilename);
792 if (RT_FAILURE(rc))
793 return rc;
794
795 /*
796 * Download the file.
797 */
798 RTHTTP hHttp;
799 rc = RTHttpCreate(&hHttp);
800 if (RT_FAILURE(rc))
801 return rc;
802 RTHttpUseSystemProxySettings(hHttp);
803
804 static const char * const s_apszHeaders[] =
805 {
806 "User-Agent: Microsoft-Symbol-Server/6.6.0999.9",
807 "Pragma: no-cache",
808 };
809
810 rc = RTHttpSetHeaders(hHttp, RT_ELEMENTS(s_apszHeaders), s_apszHeaders);
811 if (RT_SUCCESS(rc))
812 {
813 char szUrl[_2K];
814 RTStrPrintf(szUrl, sizeof(szUrl), "%s/%s/%s/%s", pszServer, pszFilename, pszCacheSubDir, pszFilename);
815
816 /** @todo Use some temporary file name and rename it after the operation
817 * since not all systems support read-deny file sharing
818 * settings. */
819 rtDbgCfgLog2(pThis, "Downloading '%s' to '%s'...\n", szUrl, pszPath);
820 rc = RTHttpGetFile(hHttp, szUrl, pszPath);
821 if (RT_FAILURE(rc))
822 {
823 RTFileDelete(pszPath);
824 rtDbgCfgLog1(pThis, "%Rrc on URL '%s'\n", rc, szUrl);
825 }
826 if (rc == VERR_HTTP_NOT_FOUND)
827 {
828 /* Try the compressed version of the file. */
829 pszPath[strlen(pszPath) - 1] = '_';
830 szUrl[strlen(szUrl) - 1] = '_';
831 rtDbgCfgLog2(pThis, "Downloading '%s' to '%s'...\n", szUrl, pszPath);
832 rc = RTHttpGetFile(hHttp, szUrl, pszPath);
833 if (RT_SUCCESS(rc))
834 rc = rtDbgCfgUnpackMsCacheFile(pThis, pszPath, pszFilename);
835 else
836 {
837 rtDbgCfgLog1(pThis, "%Rrc on URL '%s'\n", rc, pszPath);
838 RTFileDelete(pszPath);
839 }
840 }
841 }
842
843 RTHttpDestroy(hHttp);
844
845 if (RT_SUCCESS(rc))
846 {
847 /*
848 * Succeeded in downloading it. Add UUID mapping?
849 */
850 if (pszUuidMappingSubDir)
851 {
852 /** @todo UUID mapping when downloading. */
853 }
854
855 /*
856 * Give the file a try.
857 */
858 Assert(RTFileExists(pszPath));
859 rtDbgCfgLog1(pThis, "Trying '%s'...\n", pszPath);
860 rc = pfnCallback(pThis, pszPath, pvUser1, pvUser2);
861 if (rc == VINF_CALLBACK_RETURN)
862 rtDbgCfgLog1(pThis, "Found '%s'.\n", pszPath);
863 else if (rc == VERR_CALLBACK_RETURN)
864 rtDbgCfgLog1(pThis, "Error opening '%s'.\n", pszPath);
865 else
866 rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc, pszPath);
867 }
868
869 return rc;
870
871#else /* !IPRT_WITH_HTTP */
872 return VWRN_NOT_FOUND;
873#endif /* !IPRT_WITH_HTTP */
874}
875
876
877static int rtDbgCfgCopyFileToCache(PRTDBGCFGINT pThis, char const *pszSrc, const char *pchCache, size_t cchCache,
878 const char *pszCacheSubDir, const char *pszUuidMappingSubDir, PRTPATHSPLIT pSplitFn)
879{
880 if (!pszCacheSubDir || !*pszCacheSubDir)
881 return VINF_SUCCESS;
882
883 /** @todo copy to cache */
884 return VINF_SUCCESS;
885}
886
887
888static int rtDbgCfgTryOpenCache(PRTDBGCFGINT pThis, char *pszPath, size_t cchCachePath,
889 const char *pszCacheSubDir, const char *pszUuidMappingSubDir,
890 PCRTPATHSPLIT pSplitFn, const char *pszCacheSuffix, uint32_t fFlags,
891 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
892{
893 Assert(pszPath[cchCachePath] == '\0');
894
895 /*
896 * If the cache doesn't exist, fail right away.
897 */
898 if (!pszCacheSubDir || !*pszCacheSubDir)
899 return VWRN_NOT_FOUND;
900 if (!RTDirExists(pszPath))
901 {
902 rtDbgCfgLog2(pThis, "Cache does not exist: '%s'\n", pszPath);
903 return VWRN_NOT_FOUND;
904 }
905
906 /*
907 * If we got a UUID mapping option, try it first as we can hopefully
908 * dispense with case folding.
909 */
910 if (pszUuidMappingSubDir)
911 {
912 int rc = RTPathAppend(pszPath, RTPATH_MAX, pszUuidMappingSubDir);
913 if ( RT_SUCCESS(rc)
914 && RTFileExists(pszPath))
915 {
916 /* Try resolve the path before presenting it to the client, a
917 12 digit filename is of little worth. */
918 char szBackup[RTPATH_MAX];
919 strcpy(szBackup, pszPath);
920 rc = RTPathAbs(szBackup, pszPath, RTPATH_MAX);
921 if (RT_FAILURE(rc))
922 strcpy(pszPath, szBackup);
923
924 /* Do the callback thing. */
925 rtDbgCfgLog1(pThis, "Trying '%s'...\n", pszPath);
926 int rc2 = pfnCallback(pThis, pszPath, pvUser1, pvUser2);
927 if (rc2 == VINF_CALLBACK_RETURN)
928 rtDbgCfgLog1(pThis, "Found '%s' via uuid mapping.\n", pszPath);
929 else if (rc2 == VERR_CALLBACK_RETURN)
930 rtDbgCfgLog1(pThis, "Error opening '%s'.\n", pszPath);
931 else
932 rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc2, pszPath);
933 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
934 return rc2;
935
936 /* Failed, restore the cache path. */
937 memcpy(pszPath, szBackup, cchCachePath);
938 }
939 pszPath[cchCachePath] = '\0';
940 }
941
942 /*
943 * Carefully construct the cache path with case insensitivity in mind.
944 */
945 bool const fCaseInsensitive = (fFlags & RTDBGCFG_O_CASE_INSENSITIVE)
946 && !rtDbgCfgIsFsCaseInsensitive(pszPath);
947 const char *pszFilename = pSplitFn->apszComps[pSplitFn->cComps - 1];
948
949 if (!rtDbgCfgIsDirAndFixCase(pszPath, pszFilename, fCaseInsensitive))
950 return VWRN_NOT_FOUND;
951
952 if (!rtDbgCfgIsDirAndFixCase(pszPath, pszCacheSubDir, fCaseInsensitive))
953 return VWRN_NOT_FOUND;
954
955 bool fProbablyCompressed = false;
956 if (!rtDbgCfgIsFileAndFixCase(pszPath, pszFilename, pszCacheSuffix, fCaseInsensitive,
957 RT_BOOL(fFlags & RTDBGCFG_O_MAYBE_COMPRESSED_MS), &fProbablyCompressed))
958 return VWRN_NOT_FOUND;
959 if (fProbablyCompressed)
960 {
961 int rc = rtDbgCfgUnpackMsCacheFile(pThis, pszPath, pszFilename);
962 if (RT_FAILURE(rc))
963 return VWRN_NOT_FOUND;
964 }
965
966 rtDbgCfgLog1(pThis, "Trying '%s'...\n", pszPath);
967 int rc2 = pfnCallback(pThis, pszPath, pvUser1, pvUser2);
968 if (rc2 == VINF_CALLBACK_RETURN)
969 rtDbgCfgLog1(pThis, "Found '%s'.\n", pszPath);
970 else if (rc2 == VERR_CALLBACK_RETURN)
971 rtDbgCfgLog1(pThis, "Error opening '%s'.\n", pszPath);
972 else
973 rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc2, pszPath);
974 return rc2;
975}
976
977
978static int rtDbgCfgTryOpenList(PRTDBGCFGINT pThis, PRTLISTANCHOR pList, PRTPATHSPLIT pSplitFn, const char *pszCacheSubDir,
979 const char *pszUuidMappingSubDir, uint32_t fFlags, char *pszPath,
980 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
981{
982 int rcRet = VWRN_NOT_FOUND;
983 int rc2 = VINF_SUCCESS;
984
985 const char *pchCache = NULL;
986 size_t cchCache = 0;
987 int rcCache = VWRN_NOT_FOUND;
988
989 PRTDBGCFGSTR pCur;
990 RTListForEach(pList, pCur, RTDBGCFGSTR, ListEntry)
991 {
992 size_t cchDir = pCur->cch;
993 const char *pszDir = pCur->sz;
994 rtDbgCfgLog2(pThis, "Path list entry: '%s'\n", pszDir);
995
996 /* This is very simplistic, but we have a unreasonably large path
997 buffer, so it'll work just fine and simplify things greatly below. */
998 if (cchDir >= RTPATH_MAX - 8U)
999 {
1000 if (RT_SUCCESS_NP(rcRet))
1001 rcRet = VERR_FILENAME_TOO_LONG;
1002 continue;
1003 }
1004
1005 /*
1006 * Process the path according to it's type.
1007 */
1008 if (!RTStrNICmp(pszDir, RT_STR_TUPLE("srv*")))
1009 {
1010 /*
1011 * Symbol server.
1012 */
1013 pszDir += sizeof("srv*") - 1;
1014 cchDir -= sizeof("srv*") - 1;
1015 bool fSearchCache = false;
1016 const char *pszServer = (const char *)memchr(pszDir, '*', cchDir);
1017 if (!pszServer)
1018 pszServer = pszDir;
1019 else if (pszServer == pszDir)
1020 continue;
1021 else
1022 {
1023 fSearchCache = true;
1024 pchCache = pszDir;
1025 cchCache = pszServer - pszDir;
1026 pszServer++;
1027 }
1028
1029 /* We don't have any default cache directory, so skip if the cache is missing. */
1030 if (cchCache == 0)
1031 continue;
1032
1033 /* Search the cache first (if we haven't already done so). */
1034 if (fSearchCache)
1035 {
1036 memcpy(pszPath, pchCache, cchCache);
1037 pszPath[cchCache] = '\0';
1038 RTPathChangeToUnixSlashes(pszPath, false);
1039
1040 rcCache = rc2 = rtDbgCfgTryOpenCache(pThis, pszPath, cchCache, pszCacheSubDir, pszUuidMappingSubDir,
1041 pSplitFn, NULL /*pszCacheSuffix*/, fFlags, pfnCallback, pvUser1, pvUser2);
1042 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
1043 return rc2;
1044 }
1045
1046 /* Try downloading the file. */
1047 if (rcCache == VWRN_NOT_FOUND)
1048 {
1049 memcpy(pszPath, pchCache, cchCache);
1050 pszPath[cchCache] = '\0';
1051 RTPathChangeToUnixSlashes(pszPath, false);
1052
1053 rc2 = rtDbgCfgTryDownloadAndOpen(pThis, pszServer, pszPath, pszCacheSubDir, pszUuidMappingSubDir,
1054 pSplitFn, NULL /*pszCacheSuffix*/, fFlags, pfnCallback, pvUser1, pvUser2);
1055 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
1056 return rc2;
1057 }
1058 }
1059 else if (!RTStrNICmp(pszDir, RT_STR_TUPLE("cache*")))
1060 {
1061 /*
1062 * Cache directory.
1063 */
1064 pszDir += sizeof("cache*") - 1;
1065 cchDir -= sizeof("cache*") - 1;
1066 if (!cchDir)
1067 continue;
1068 pchCache = pszDir;
1069 cchCache = cchDir;
1070
1071 memcpy(pszPath, pchCache, cchCache);
1072 pszPath[cchCache] = '\0';
1073 RTPathChangeToUnixSlashes(pszPath, false);
1074
1075 rcCache = rc2 = rtDbgCfgTryOpenCache(pThis, pszPath, cchCache, pszCacheSubDir, pszUuidMappingSubDir,
1076 pSplitFn, NULL /*pszCacheSuffix*/, fFlags, pfnCallback, pvUser1, pvUser2);
1077 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
1078 return rc2;
1079 }
1080 else
1081 {
1082 /*
1083 * Normal directory. Check for our own 'rec*' and 'norec*' prefix
1084 * flags governing recursive searching.
1085 */
1086 uint32_t fFlagsDir = fFlags;
1087 if (!RTStrNICmp(pszDir, RT_STR_TUPLE("rec*")))
1088 {
1089 pszDir += sizeof("rec*") - 1;
1090 cchDir -= sizeof("rec*") - 1;
1091 fFlagsDir |= RTDBGCFG_O_RECURSIVE;
1092 }
1093 else if (!RTStrNICmp(pszDir, RT_STR_TUPLE("norec*")))
1094 {
1095 pszDir += sizeof("norec*") - 1;
1096 cchDir -= sizeof("norec*") - 1;
1097 fFlagsDir &= ~RTDBGCFG_O_RECURSIVE;
1098 }
1099
1100 /* Copy the path into the buffer and do the searching. */
1101 memcpy(pszPath, pszDir, cchDir);
1102 pszPath[cchDir] = '\0';
1103 RTPathChangeToUnixSlashes(pszPath, false);
1104
1105 rc2 = rtDbgCfgTryOpenDir(pThis, pszPath, pSplitFn, fFlagsDir, pfnCallback, pvUser1, pvUser2);
1106 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
1107 {
1108 if ( rc2 == VINF_CALLBACK_RETURN
1109 && cchCache > 0)
1110 rtDbgCfgCopyFileToCache(pThis, pszPath, pchCache, cchCache,
1111 pszCacheSubDir, pszUuidMappingSubDir, pSplitFn);
1112 return rc2;
1113 }
1114 }
1115
1116 /* Propagate errors. */
1117 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
1118 rcRet = rc2;
1119 }
1120
1121 return rcRet;
1122}
1123
1124
1125/**
1126 * Common worker routine for Image and debug info opening.
1127 *
1128 * This will not search using for suffixes.
1129 *
1130 * @returns IPRT status code.
1131 * @param hDbgCfg The debugging configuration handle.
1132 * NIL_RTDBGCFG is accepted, but the result is
1133 * that no paths will be searched beyond the
1134 * given and the current directory.
1135 * @param pszFilename The filename to search for. This may or may
1136 * not include a full or partial path.
1137 * @param pszCacheSubDir The cache subdirectory to look in.
1138 * @param pszUuidMappingSubDir UUID mapping subdirectory to check, NULL if
1139 * no mapping wanted.
1140 * @param fFlags Flags and hints.
1141 * @param pfnCallback The open callback routine.
1142 * @param pvUser1 User parameter 1.
1143 * @param pvUser2 User parameter 2.
1144 */
1145static int rtDbgCfgOpenWithSubDir(RTDBGCFG hDbgCfg, const char *pszFilename, const char *pszCacheSubDir,
1146 const char *pszUuidMappingSubDir, uint32_t fFlags,
1147 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1148{
1149 int rcRet = VINF_SUCCESS;
1150 int rc2;
1151
1152 /*
1153 * Do a little validating first.
1154 */
1155 PRTDBGCFGINT pThis = hDbgCfg;
1156 if (pThis != NIL_RTDBGCFG)
1157 RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
1158 else
1159 pThis = NULL;
1160 AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
1161 AssertPtrReturn(pszCacheSubDir, VERR_INVALID_POINTER);
1162 AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
1163
1164 /*
1165 * Do some guessing as to the way we should parse the filename and whether
1166 * it's case exact or not.
1167 */
1168 bool fDosPath = strchr(pszFilename, ':') != NULL
1169 || strchr(pszFilename, '\\') != NULL
1170 || RT_OPSYS_USES_DOS_PATHS(fFlags & RTDBGCFG_O_OPSYS_MASK)
1171 || (fFlags & RTDBGCFG_O_CASE_INSENSITIVE);
1172 if (fDosPath)
1173 fFlags |= RTDBGCFG_O_CASE_INSENSITIVE;
1174
1175 rtDbgCfgLog2(pThis, "Looking for '%s' w/ cache subdir '%s' and %#x flags...\n", pszFilename, pszCacheSubDir, fFlags);
1176
1177 PRTPATHSPLIT pSplitFn;
1178 rc2 = RTPathSplitA(pszFilename, &pSplitFn, fDosPath ? RTPATH_STR_F_STYLE_DOS : RTPATH_STR_F_STYLE_UNIX);
1179 if (RT_FAILURE(rc2))
1180 return rc2;
1181 AssertReturnStmt(pSplitFn->fProps & RTPATH_PROP_FILENAME, RTPathSplitFree(pSplitFn), VERR_IS_A_DIRECTORY);
1182
1183 /*
1184 * Try the stored file name first if it has a kind of absolute path.
1185 */
1186 char szPath[RTPATH_MAX];
1187 if (RTPATH_PROP_HAS_ROOT_SPEC(pSplitFn->fProps))
1188 {
1189 rc2 = RTPathSplitReassemble(pSplitFn, RTPATH_STR_F_STYLE_HOST, szPath, sizeof(szPath));
1190 if (RT_SUCCESS(rc2) && RTFileExists(szPath))
1191 {
1192 RTPathChangeToUnixSlashes(szPath, false);
1193 rtDbgCfgLog1(pThis, "Trying '%s'...\n", szPath);
1194 rc2 = pfnCallback(pThis, szPath, pvUser1, pvUser2);
1195 if (rc2 == VINF_CALLBACK_RETURN)
1196 rtDbgCfgLog1(pThis, "Found '%s'.\n", szPath);
1197 else if (rc2 == VERR_CALLBACK_RETURN)
1198 rtDbgCfgLog1(pThis, "Error opening '%s'.\n", szPath);
1199 else
1200 rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc2, szPath);
1201 }
1202 }
1203 if ( rc2 != VINF_CALLBACK_RETURN
1204 && rc2 != VERR_CALLBACK_RETURN)
1205 {
1206 /*
1207 * Try the current directory (will take cover relative paths
1208 * skipped above).
1209 */
1210 rc2 = RTPathGetCurrent(szPath, sizeof(szPath));
1211 if (RT_FAILURE(rc2))
1212 strcpy(szPath, ".");
1213 RTPathChangeToUnixSlashes(szPath, false);
1214
1215 rc2 = rtDbgCfgTryOpenDir(pThis, szPath, pSplitFn, fFlags, pfnCallback, pvUser1, pvUser2);
1216 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
1217 rcRet = rc2;
1218
1219 if ( rc2 != VINF_CALLBACK_RETURN
1220 && rc2 != VERR_CALLBACK_RETURN
1221 && pThis)
1222 {
1223 rc2 = RTCritSectRwEnterShared(&pThis->CritSect);
1224 if (RT_SUCCESS(rc2))
1225 {
1226 /*
1227 * Run the applicable lists.
1228 */
1229 rc2 = rtDbgCfgTryOpenList(pThis, &pThis->PathList, pSplitFn, pszCacheSubDir,
1230 pszUuidMappingSubDir, fFlags, szPath, pfnCallback, pvUser1, pvUser2);
1231 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
1232 rcRet = rc2;
1233
1234#ifdef RT_OS_WINDOWS
1235 if ( rc2 != VINF_CALLBACK_RETURN
1236 && rc2 != VERR_CALLBACK_RETURN
1237 && (fFlags & RTDBGCFG_O_EXECUTABLE_IMAGE)
1238 && !(pThis->fFlags & RTDBGCFG_FLAGS_NO_SYSTEM_PATHS) )
1239 {
1240 rc2 = rtDbgCfgTryOpenList(pThis, &pThis->NtExecutablePathList, pSplitFn, pszCacheSubDir,
1241 pszUuidMappingSubDir, fFlags, szPath, pfnCallback, pvUser1, pvUser2);
1242 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
1243 rcRet = rc2;
1244 }
1245
1246 if ( rc2 != VINF_CALLBACK_RETURN
1247 && rc2 != VERR_CALLBACK_RETURN
1248 && !(pThis->fFlags & RTDBGCFG_FLAGS_NO_SYSTEM_PATHS) )
1249 {
1250 rc2 = rtDbgCfgTryOpenList(pThis, &pThis->NtSymbolPathList, pSplitFn, pszCacheSubDir,
1251 pszUuidMappingSubDir, fFlags, szPath, pfnCallback, pvUser1, pvUser2);
1252 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
1253 rcRet = rc2;
1254 }
1255#endif
1256 RTCritSectRwLeaveShared(&pThis->CritSect);
1257 }
1258 else if (RT_SUCCESS(rcRet))
1259 rcRet = rc2;
1260 }
1261 }
1262
1263 RTPathSplitFree(pSplitFn);
1264 if ( rc2 == VINF_CALLBACK_RETURN
1265 || rc2 == VERR_CALLBACK_RETURN)
1266 rcRet = rc2;
1267 else if (RT_SUCCESS(rcRet))
1268 rcRet = VERR_NOT_FOUND;
1269 return rcRet;
1270}
1271
1272
1273RTDECL(int) RTDbgCfgOpenPeImage(RTDBGCFG hDbgCfg, const char *pszFilename, uint32_t cbImage, uint32_t uTimestamp,
1274 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1275{
1276 char szSubDir[32];
1277 RTStrPrintf(szSubDir, sizeof(szSubDir), "%08X%x", uTimestamp, cbImage);
1278 return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir, NULL,
1279 RT_OPSYS_WINDOWS /* approx */ | RTDBGCFG_O_SYMSRV | RTDBGCFG_O_CASE_INSENSITIVE
1280 | RTDBGCFG_O_MAYBE_COMPRESSED_MS | RTDBGCFG_O_EXECUTABLE_IMAGE,
1281 pfnCallback, pvUser1, pvUser2);
1282}
1283
1284
1285RTDECL(int) RTDbgCfgOpenPdb70(RTDBGCFG hDbgCfg, const char *pszFilename, PCRTUUID pUuid, uint32_t uAge,
1286 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1287{
1288 char szSubDir[64];
1289 if (!pUuid)
1290 szSubDir[0] = '\0';
1291 else
1292 {
1293 /* Stringify the UUID and remove the dashes. */
1294 int rc2 = RTUuidToStr(pUuid, szSubDir, sizeof(szSubDir));
1295 AssertRCReturn(rc2, rc2);
1296
1297 char *pszSrc = szSubDir;
1298 char *pszDst = szSubDir;
1299 char ch;
1300 while ((ch = *pszSrc++))
1301 if (ch != '-')
1302 *pszDst++ = RT_C_TO_UPPER(ch);
1303
1304 RTStrPrintf(pszDst, &szSubDir[sizeof(szSubDir)] - pszDst, "%X", uAge);
1305 }
1306
1307 return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir, NULL,
1308 RT_OPSYS_WINDOWS /* approx */ | RTDBGCFG_O_SYMSRV | RTDBGCFG_O_CASE_INSENSITIVE
1309 | RTDBGCFG_O_MAYBE_COMPRESSED_MS | RTDBGCFG_O_EXT_DEBUG_FILE,
1310 pfnCallback, pvUser1, pvUser2);
1311}
1312
1313
1314RTDECL(int) RTDbgCfgOpenPdb20(RTDBGCFG hDbgCfg, const char *pszFilename, uint32_t cbImage, uint32_t uTimestamp, uint32_t uAge,
1315 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1316{
1317 /** @todo test this! */
1318 char szSubDir[32];
1319 RTStrPrintf(szSubDir, sizeof(szSubDir), "%08X%x", uTimestamp, uAge);
1320 return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir, NULL,
1321 RT_OPSYS_WINDOWS /* approx */ | RTDBGCFG_O_SYMSRV | RTDBGCFG_O_CASE_INSENSITIVE
1322 | RTDBGCFG_O_MAYBE_COMPRESSED_MS | RTDBGCFG_O_EXT_DEBUG_FILE,
1323 pfnCallback, pvUser1, pvUser2);
1324}
1325
1326
1327RTDECL(int) RTDbgCfgOpenDbg(RTDBGCFG hDbgCfg, const char *pszFilename, uint32_t cbImage, uint32_t uTimestamp,
1328 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1329{
1330 char szSubDir[32];
1331 RTStrPrintf(szSubDir, sizeof(szSubDir), "%08X%x", uTimestamp, cbImage);
1332 return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir, NULL,
1333 RT_OPSYS_WINDOWS /* approx */ | RTDBGCFG_O_SYMSRV | RTDBGCFG_O_CASE_INSENSITIVE
1334 | RTDBGCFG_O_MAYBE_COMPRESSED_MS | RTDBGCFG_O_EXT_DEBUG_FILE,
1335 pfnCallback, pvUser1, pvUser2);
1336}
1337
1338
1339RTDECL(int) RTDbgCfgOpenDwo(RTDBGCFG hDbgCfg, const char *pszFilename, uint32_t uCrc32,
1340 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1341{
1342 char szSubDir[32];
1343 RTStrPrintf(szSubDir, sizeof(szSubDir), "%08x", uCrc32);
1344 return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir, NULL,
1345 RT_OPSYS_UNKNOWN | RTDBGCFG_O_EXT_DEBUG_FILE,
1346 pfnCallback, pvUser1, pvUser2);
1347}
1348
1349
1350
1351/*
1352 *
1353 * D a r w i n . d S Y M b u n d l e s
1354 * D a r w i n . d S Y M b u n d l e s
1355 * D a r w i n . d S Y M b u n d l e s
1356 *
1357 */
1358
1359/**
1360 * Very similar to rtDbgCfgTryOpenDir.
1361 */
1362static int rtDbgCfgTryOpenDsymBundleInDir(PRTDBGCFGINT pThis, char *pszPath, PRTPATHSPLIT pSplitFn,
1363 const char * const *papszSuffixes, uint32_t fFlags,
1364 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1365{
1366 int rcRet = VWRN_NOT_FOUND;
1367 int rc2;
1368
1369 /* If the directory doesn't exist, just quit immediately.
1370 Note! Our case insensitivity doesn't extend to the search dirs themselfs,
1371 only to the bits under neath them. */
1372 if (!RTDirExists(pszPath))
1373 {
1374 rtDbgCfgLog2(pThis, "Dir does not exist: '%s'\n", pszPath);
1375 return rcRet;
1376 }
1377
1378 /* Figure out whether we have to do a case sensitive search or not.
1379 Note! As a simplification, we don't ask for case settings in each
1380 directory under the user specified path, we assume the file
1381 systems that mounted there have compatible settings. Faster
1382 that way. */
1383 bool const fCaseInsensitive = (fFlags & RTDBGCFG_O_CASE_INSENSITIVE)
1384 && !rtDbgCfgIsFsCaseInsensitive(pszPath);
1385
1386 size_t const cchPath = strlen(pszPath);
1387
1388 /*
1389 * Look for the file with less and less of the original path given.
1390 * Also try out typical bundle extension variations.
1391 */
1392 const char *pszName = pSplitFn->apszComps[pSplitFn->cComps - 1];
1393 for (unsigned i = RTPATH_PROP_HAS_ROOT_SPEC(pSplitFn->fProps); i < pSplitFn->cComps; i++)
1394 {
1395 pszPath[cchPath] = '\0';
1396
1397 rc2 = VINF_SUCCESS;
1398 for (unsigned j = i; j < pSplitFn->cComps - 1U && RT_SUCCESS(rc2); j++)
1399 if (!rtDbgCfgIsDirAndFixCase(pszPath, pSplitFn->apszComps[i], fCaseInsensitive))
1400 rc2 = VERR_FILE_NOT_FOUND;
1401 if (RT_SUCCESS(rc2))
1402 {
1403 size_t cchCurPath = cchPath + strlen(&pszPath[cchPath]);
1404 for (uint32_t iSuffix = 0; papszSuffixes[iSuffix]; iSuffix++)
1405 {
1406 if ( !rtDbgCfgIsDirAndFixCase2(pszPath, pszName, papszSuffixes[iSuffix], fCaseInsensitive)
1407 && !rtDbgCfgIsDirAndFixCase(pszPath, "Contents", fCaseInsensitive)
1408 && !rtDbgCfgIsDirAndFixCase(pszPath, "Resources", fCaseInsensitive)
1409 && !rtDbgCfgIsDirAndFixCase(pszPath, "DWARF", fCaseInsensitive))
1410 {
1411 if (rtDbgCfgIsFileAndFixCase(pszPath, pszName, NULL /*pszSuffix*/, fCaseInsensitive, false, NULL))
1412 {
1413 rtDbgCfgLog1(pThis, "Trying '%s'...\n", pszPath);
1414 rc2 = pfnCallback(pThis, pszPath, pvUser1, pvUser2);
1415 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
1416 {
1417 if (rc2 == VINF_CALLBACK_RETURN)
1418 rtDbgCfgLog1(pThis, "Found '%s'.\n", pszPath);
1419 else
1420 rtDbgCfgLog1(pThis, "Error opening '%s'.\n", pszPath);
1421 return rc2;
1422 }
1423 rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc2, pszPath);
1424 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
1425 rcRet = rc2;
1426 }
1427 }
1428 }
1429 }
1430 rc2 = VERR_FILE_NOT_FOUND;
1431 }
1432
1433 /*
1434 * Do a recursive search if requested.
1435 */
1436 if ( (fFlags & RTDBGCFG_O_RECURSIVE)
1437 && !(pThis->fFlags & RTDBGCFG_FLAGS_NO_RECURSIV_SEARCH) )
1438 {
1439 /** @todo Recursive searching will be done later. */
1440 }
1441
1442 return rcRet;
1443}
1444
1445
1446/**
1447 * Very similar to rtDbgCfgTryOpenList.
1448 */
1449static int rtDbgCfgTryOpenBundleInList(PRTDBGCFGINT pThis, PRTLISTANCHOR pList, PRTPATHSPLIT pSplitFn,
1450 const char * const *papszSuffixes, const char *pszCacheSubDir,
1451 const char *pszCacheSuffix, const char *pszUuidMappingSubDir,
1452 uint32_t fFlags, char *pszPath,
1453 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1454{
1455 int rcRet = VWRN_NOT_FOUND;
1456 int rc2;
1457
1458 const char *pchCache = NULL;
1459 size_t cchCache = 0;
1460 int rcCache = VWRN_NOT_FOUND;
1461
1462 PRTDBGCFGSTR pCur;
1463 RTListForEach(pList, pCur, RTDBGCFGSTR, ListEntry)
1464 {
1465 size_t cchDir = pCur->cch;
1466 const char *pszDir = pCur->sz;
1467 rtDbgCfgLog2(pThis, "Path list entry: '%s'\n", pszDir);
1468
1469 /* This is very simplistic, but we have a unreasonably large path
1470 buffer, so it'll work just fine and simplify things greatly below. */
1471 if (cchDir >= RTPATH_MAX - 8U)
1472 {
1473 if (RT_SUCCESS_NP(rcRet))
1474 rcRet = VERR_FILENAME_TOO_LONG;
1475 continue;
1476 }
1477
1478 /*
1479 * Process the path according to it's type.
1480 */
1481 rc2 = VINF_SUCCESS;
1482 if (!RTStrNICmp(pszDir, RT_STR_TUPLE("srv*")))
1483 {
1484 /*
1485 * Symbol server.
1486 */
1487 pszDir += sizeof("srv*") - 1;
1488 cchDir -= sizeof("srv*") - 1;
1489 bool fSearchCache = false;
1490 const char *pszServer = (const char *)memchr(pszDir, '*', cchDir);
1491 if (!pszServer)
1492 pszServer = pszDir;
1493 else if (pszServer == pszDir)
1494 continue;
1495 else
1496 {
1497 fSearchCache = true;
1498 pchCache = pszDir;
1499 cchCache = pszServer - pszDir;
1500 pszServer++;
1501 }
1502
1503 /* We don't have any default cache directory, so skip if the cache is missing. */
1504 if (cchCache == 0)
1505 continue;
1506
1507 /* Search the cache first (if we haven't already done so). */
1508 if (fSearchCache)
1509 {
1510 memcpy(pszPath, pchCache, cchCache);
1511 pszPath[cchCache] = '\0';
1512 RTPathChangeToUnixSlashes(pszPath, false);
1513
1514 rcCache = rc2 = rtDbgCfgTryOpenCache(pThis, pszPath, cchCache, pszCacheSubDir, pszUuidMappingSubDir,
1515 pSplitFn, pszCacheSuffix, fFlags, pfnCallback, pvUser1, pvUser2);
1516 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
1517 return rc2;
1518 }
1519
1520 /* Try downloading the file. */
1521 if (rcCache == VWRN_NOT_FOUND)
1522 {
1523 memcpy(pszPath, pchCache, cchCache);
1524 pszPath[cchCache] = '\0';
1525 RTPathChangeToUnixSlashes(pszPath, false);
1526
1527 rc2 = rtDbgCfgTryDownloadAndOpen(pThis, pszServer, pszPath, pszCacheSubDir, pszUuidMappingSubDir,
1528 pSplitFn, pszCacheSuffix, fFlags, pfnCallback, pvUser1, pvUser2);
1529 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
1530 return rc2;
1531 }
1532 }
1533 else if (!RTStrNICmp(pszDir, RT_STR_TUPLE("cache*")))
1534 {
1535 /*
1536 * Cache directory.
1537 */
1538 pszDir += sizeof("cache*") - 1;
1539 cchDir -= sizeof("cache*") - 1;
1540 if (!cchDir)
1541 continue;
1542 pchCache = pszDir;
1543 cchCache = cchDir;
1544
1545 memcpy(pszPath, pchCache, cchCache);
1546 pszPath[cchCache] = '\0';
1547 RTPathChangeToUnixSlashes(pszPath, false);
1548
1549 rcCache = rc2 = rtDbgCfgTryOpenCache(pThis, pszPath, cchCache, pszCacheSubDir, pszUuidMappingSubDir,
1550 pSplitFn, pszCacheSuffix, fFlags, pfnCallback, pvUser1, pvUser2);
1551 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
1552 return rc2;
1553 }
1554 else
1555 {
1556 /*
1557 * Normal directory. Check for our own 'rec*' and 'norec*' prefix
1558 * flags governing recursive searching.
1559 */
1560 uint32_t fFlagsDir = fFlags;
1561 if (!RTStrNICmp(pszDir, RT_STR_TUPLE("rec*")))
1562 {
1563 pszDir += sizeof("rec*") - 1;
1564 cchDir -= sizeof("rec*") - 1;
1565 fFlagsDir |= RTDBGCFG_O_RECURSIVE;
1566 }
1567 else if (!RTStrNICmp(pszDir, RT_STR_TUPLE("norec*")))
1568 {
1569 pszDir += sizeof("norec*") - 1;
1570 cchDir -= sizeof("norec*") - 1;
1571 fFlagsDir &= ~RTDBGCFG_O_RECURSIVE;
1572 }
1573
1574 /* Copy the path into the buffer and do the searching. */
1575 memcpy(pszPath, pszDir, cchDir);
1576 pszPath[cchDir] = '\0';
1577 RTPathChangeToUnixSlashes(pszPath, false);
1578
1579 rc2 = rtDbgCfgTryOpenDsymBundleInDir(pThis, pszPath, pSplitFn, papszSuffixes, fFlagsDir,
1580 pfnCallback, pvUser1, pvUser2);
1581 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
1582 {
1583 if ( rc2 == VINF_CALLBACK_RETURN
1584 && cchCache > 0)
1585 rtDbgCfgCopyFileToCache(pThis, pszPath, pchCache, cchCache,
1586 pszCacheSubDir, pszUuidMappingSubDir, pSplitFn);
1587 return rc2;
1588 }
1589 }
1590
1591 /* Propagate errors. */
1592 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
1593 rcRet = rc2;
1594 }
1595
1596 return rcRet;
1597}
1598
1599
1600/**
1601 * Creating a UUID mapping subdirectory path for use in caches.
1602 *
1603 * @returns IPRT status code.
1604 * @param pszSubDir The output buffer.
1605 * @param cbSubDir The size of the output buffer. (Top dir length +
1606 * slash + UUID string len + extra dash.)
1607 * @param pszTopDir The top level cache directory name. No slashes
1608 * or other directory separators, please.
1609 * @param pUuid The UUID.
1610 */
1611static int rtDbgCfgConstructUuidMappingSubDir(char *pszSubDir, size_t cbSubDir, const char *pszTopDir, PCRTUUID pUuid)
1612{
1613 Assert(!strpbrk(pszTopDir, ":/\\"));
1614
1615 size_t cchTopDir = strlen(pszTopDir);
1616 if (cchTopDir + 1 + 1 + RTUUID_STR_LENGTH + 1 > cbSubDir)
1617 return VERR_BUFFER_OVERFLOW;
1618 memcpy(pszSubDir, pszTopDir, cchTopDir);
1619
1620 pszSubDir += cchTopDir;
1621 *pszSubDir++ = RTPATH_SLASH;
1622 cbSubDir -= cchTopDir + 1;
1623
1624 /* ed5a8336-35c2-4892-9122-21d5572924a3 -> ED5A/8336/35C2/4892/9122/21D5572924A3 */
1625 int rc = RTUuidToStr(pUuid, pszSubDir + 1, cbSubDir - 1); AssertRCReturn(rc, rc);
1626 RTStrToUpper(pszSubDir + 1);
1627 memmove(pszSubDir, pszSubDir + 1, 4);
1628 pszSubDir += 4;
1629 *pszSubDir = RTPATH_SLASH;
1630 pszSubDir += 5;
1631 *pszSubDir = RTPATH_SLASH;
1632 pszSubDir += 5;
1633 *pszSubDir = RTPATH_SLASH;
1634 pszSubDir += 5;
1635 *pszSubDir = RTPATH_SLASH;
1636 pszSubDir += 5;
1637 *pszSubDir = RTPATH_SLASH;
1638
1639 return VINF_SUCCESS;
1640}
1641
1642
1643static int rtDbgCfgOpenBundleFile(RTDBGCFG hDbgCfg, const char *pszImage, const char * const *papszSuffixes,
1644 const char *pszBundleSubDir, PCRTUUID pUuid, const char *pszUuidMapDirName,
1645 const char *pszCacheSuffix, bool fOpenImage,
1646 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1647{
1648 /*
1649 * Bundles are directories, means we can forget about sharing code much
1650 * with the other RTDbgCfgOpenXXX methods. Thus we're duplicating a lot of
1651 * code from rtDbgCfgOpenWithSubDir with .dSYM/.kext/.dylib/.app/.* related
1652 * adjustments, so, a bug found here or there probably means the other
1653 * version needs updating.
1654 */
1655 int rcRet = VINF_SUCCESS;
1656 int rc2;
1657
1658 /*
1659 * Do a little validating first.
1660 */
1661 PRTDBGCFGINT pThis = hDbgCfg;
1662 if (pThis != NIL_RTDBGCFG)
1663 RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
1664 else
1665 pThis = NULL;
1666 AssertPtrReturn(pszImage, VERR_INVALID_POINTER);
1667 AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
1668
1669 /*
1670 * Set up rtDbgCfgOpenWithSubDir and uuid map parameters.
1671 */
1672 uint32_t fFlags = RTDBGCFG_O_EXT_DEBUG_FILE | RT_OPSYS_DARWIN;
1673 const char *pszCacheSubDir = NULL;
1674 char szCacheSubDir[RTUUID_STR_LENGTH];
1675 const char *pszUuidMappingSubDir = NULL;
1676 char szUuidMappingSubDir[RTUUID_STR_LENGTH + 16];
1677 if (pUuid)
1678 {
1679 /* Since Mac debuggers uses UUID mappings, we just the slashing default
1680 UUID string representation instead of stripping dashes like for PDB. */
1681 RTUuidToStr(pUuid, szCacheSubDir, sizeof(szCacheSubDir));
1682 pszCacheSubDir = szCacheSubDir;
1683
1684 rc2 = rtDbgCfgConstructUuidMappingSubDir(szUuidMappingSubDir, sizeof(szUuidMappingSubDir), pszUuidMapDirName, pUuid);
1685 AssertRCReturn(rc2, rc2);
1686 pszUuidMappingSubDir = szUuidMappingSubDir;
1687 }
1688
1689 /*
1690 * Do some guessing as to the way we should parse the filename and whether
1691 * it's case exact or not.
1692 */
1693 bool fDosPath = strchr(pszImage, ':') != NULL
1694 || strchr(pszImage, '\\') != NULL
1695 || RT_OPSYS_USES_DOS_PATHS(fFlags & RTDBGCFG_O_OPSYS_MASK)
1696 || (fFlags & RTDBGCFG_O_CASE_INSENSITIVE);
1697 if (fDosPath)
1698 fFlags |= RTDBGCFG_O_CASE_INSENSITIVE;
1699
1700 rtDbgCfgLog2(pThis, "Looking for '%s' with %#x flags...\n", pszImage, fFlags);
1701
1702 PRTPATHSPLIT pSplitFn;
1703 rc2 = RTPathSplitA(pszImage, &pSplitFn, fDosPath ? RTPATH_STR_F_STYLE_DOS : RTPATH_STR_F_STYLE_UNIX);
1704 if (RT_FAILURE(rc2))
1705 return rc2;
1706 AssertReturnStmt(pSplitFn->fProps & RTPATH_PROP_FILENAME, RTPathSplitFree(pSplitFn), VERR_IS_A_DIRECTORY);
1707
1708 /*
1709 * Try the image directory first.
1710 */
1711 char szPath[RTPATH_MAX];
1712 if (pSplitFn->cComps > 0)
1713 {
1714 rc2 = RTPathSplitReassemble(pSplitFn, RTPATH_STR_F_STYLE_HOST, szPath, sizeof(szPath));
1715 if (fOpenImage && RT_SUCCESS(rc2))
1716 {
1717 rc2 = RTStrCat(szPath, sizeof(szPath), papszSuffixes[0]);
1718 if (RT_SUCCESS(rc2))
1719 rc2 = RTStrCat(szPath, sizeof(szPath), pszBundleSubDir);
1720 if (RT_SUCCESS(rc2))
1721 rc2 = RTPathAppend(szPath, sizeof(szPath), pSplitFn->apszComps[pSplitFn->cComps - 1]);
1722 }
1723 if (RT_SUCCESS(rc2) && RTPathExists(szPath))
1724 {
1725 RTPathChangeToUnixSlashes(szPath, false);
1726 rtDbgCfgLog1(pThis, "Trying '%s'...\n", szPath);
1727 rc2 = pfnCallback(pThis, szPath, pvUser1, pvUser2);
1728 if (rc2 == VINF_CALLBACK_RETURN)
1729 rtDbgCfgLog1(pThis, "Found '%s'.\n", szPath);
1730 else if (rc2 == VERR_CALLBACK_RETURN)
1731 rtDbgCfgLog1(pThis, "Error opening '%s'.\n", szPath);
1732 else
1733 rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc2, szPath);
1734 }
1735 }
1736 if ( rc2 != VINF_CALLBACK_RETURN
1737 && rc2 != VERR_CALLBACK_RETURN)
1738 {
1739 /*
1740 * Try the current directory (will take cover relative paths
1741 * skipped above).
1742 */
1743 rc2 = RTPathGetCurrent(szPath, sizeof(szPath));
1744 if (RT_FAILURE(rc2))
1745 strcpy(szPath, ".");
1746 RTPathChangeToUnixSlashes(szPath, false);
1747
1748 rc2 = rtDbgCfgTryOpenDsymBundleInDir(pThis, szPath, pSplitFn, g_apszDSymBundleSuffixes,
1749 fFlags, pfnCallback, pvUser1, pvUser2);
1750 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
1751 rcRet = rc2;
1752
1753 if ( rc2 != VINF_CALLBACK_RETURN
1754 && rc2 != VERR_CALLBACK_RETURN
1755 && pThis)
1756 {
1757 rc2 = RTCritSectRwEnterShared(&pThis->CritSect);
1758 if (RT_SUCCESS(rc2))
1759 {
1760 /*
1761 * Run the applicable lists.
1762 */
1763 rc2 = rtDbgCfgTryOpenBundleInList(pThis, &pThis->PathList, pSplitFn, g_apszDSymBundleSuffixes,
1764 pszCacheSubDir, pszCacheSuffix,
1765 pszUuidMappingSubDir, fFlags, szPath,
1766 pfnCallback, pvUser1, pvUser2);
1767 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
1768 rcRet = rc2;
1769
1770 RTCritSectRwLeaveShared(&pThis->CritSect);
1771 }
1772 else if (RT_SUCCESS(rcRet))
1773 rcRet = rc2;
1774 }
1775 }
1776
1777 RTPathSplitFree(pSplitFn);
1778 if ( rc2 == VINF_CALLBACK_RETURN
1779 || rc2 == VERR_CALLBACK_RETURN)
1780 rcRet = rc2;
1781 else if (RT_SUCCESS(rcRet))
1782 rcRet = VERR_NOT_FOUND;
1783 return rcRet;
1784}
1785
1786
1787RTDECL(int) RTDbgCfgOpenDsymBundle(RTDBGCFG hDbgCfg, const char *pszImage, PCRTUUID pUuid,
1788 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1789{
1790 return rtDbgCfgOpenBundleFile(hDbgCfg, pszImage, g_apszDSymBundleSuffixes,
1791 "Contents" RTPATH_SLASH_STR "Resources" RTPATH_SLASH_STR "DWARF",
1792 pUuid, RTDBG_CACHE_UUID_MAP_DIR_DSYMS, RTDBG_CACHE_DSYM_FILE_SUFFIX, false /* fOpenImage */,
1793 pfnCallback, pvUser1, pvUser2);
1794}
1795
1796
1797RTDECL(int) RTDbgCfgOpenMachOImage(RTDBGCFG hDbgCfg, const char *pszImage, PCRTUUID pUuid,
1798 PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1799{
1800 return rtDbgCfgOpenBundleFile(hDbgCfg, pszImage, g_apszBundleSuffixes,
1801 "Contents" RTPATH_SLASH_STR "MacOS",
1802 pUuid, RTDBG_CACHE_UUID_MAP_DIR_IMAGES, NULL /*pszCacheSuffix*/, true /* fOpenImage */,
1803 pfnCallback, pvUser1, pvUser2);
1804}
1805
1806
1807
1808RTDECL(int) RTDbgCfgSetLogCallback(RTDBGCFG hDbgCfg, PFNRTDBGCFGLOG pfnCallback, void *pvUser)
1809{
1810 PRTDBGCFGINT pThis = hDbgCfg;
1811 RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
1812 AssertPtrNullReturn(pfnCallback, VERR_INVALID_POINTER);
1813
1814 int rc = RTCritSectRwEnterExcl(&pThis->CritSect);
1815 if (RT_SUCCESS(rc))
1816 {
1817 if ( pThis->pfnLogCallback == NULL
1818 || pfnCallback == NULL
1819 || pfnCallback == pThis->pfnLogCallback)
1820 {
1821 pThis->pfnLogCallback = NULL;
1822 pThis->pvLogUser = NULL;
1823 ASMCompilerBarrier(); /* paranoia */
1824 pThis->pvLogUser = pvUser;
1825 pThis->pfnLogCallback = pfnCallback;
1826 rc = VINF_SUCCESS;
1827 }
1828 else
1829 rc = VERR_ACCESS_DENIED;
1830 RTCritSectRwLeaveExcl(&pThis->CritSect);
1831 }
1832
1833 return rc;
1834}
1835
1836
1837/**
1838 * Frees a string list.
1839 *
1840 * @param pList The list to free.
1841 */
1842static void rtDbgCfgFreeStrList(PRTLISTANCHOR pList)
1843{
1844 PRTDBGCFGSTR pCur;
1845 PRTDBGCFGSTR pNext;
1846 RTListForEachSafe(pList, pCur, pNext, RTDBGCFGSTR, ListEntry)
1847 {
1848 RTListNodeRemove(&pCur->ListEntry);
1849 RTMemFree(pCur);
1850 }
1851}
1852
1853
1854/**
1855 * Make changes to a string list, given a semicolon separated input string.
1856 *
1857 * @returns VINF_SUCCESS, VERR_FILENAME_TOO_LONG, VERR_NO_MEMORY
1858 * @param pThis The config instance.
1859 * @param enmOp The change operation.
1860 * @param pszValue The input strings separated by semicolon.
1861 * @param fPaths Indicates that this is a path list and that we
1862 * should look for srv and cache prefixes.
1863 * @param pList The string list anchor.
1864 */
1865static int rtDbgCfgChangeStringList(PRTDBGCFGINT pThis, RTDBGCFGOP enmOp, const char *pszValue, bool fPaths,
1866 PRTLISTANCHOR pList)
1867{
1868 if (enmOp == RTDBGCFGOP_SET)
1869 rtDbgCfgFreeStrList(pList);
1870
1871 while (*pszValue)
1872 {
1873 /* Skip separators. */
1874 while (*pszValue == ';')
1875 pszValue++;
1876 if (!*pszValue)
1877 break;
1878
1879 /* Find the end of this path. */
1880 const char *pchPath = pszValue++;
1881 char ch;
1882 while ((ch = *pszValue) && ch != ';')
1883 pszValue++;
1884 size_t cchPath = pszValue - pchPath;
1885 if (cchPath >= UINT16_MAX)
1886 return VERR_FILENAME_TOO_LONG;
1887
1888 if (enmOp == RTDBGCFGOP_REMOVE)
1889 {
1890 /*
1891 * Remove all occurences.
1892 */
1893 PRTDBGCFGSTR pCur;
1894 PRTDBGCFGSTR pNext;
1895 RTListForEachSafe(pList, pCur, pNext, RTDBGCFGSTR, ListEntry)
1896 {
1897 if ( pCur->cch == cchPath
1898 && !memcmp(pCur->sz, pchPath, cchPath))
1899 {
1900 RTListNodeRemove(&pCur->ListEntry);
1901 RTMemFree(pCur);
1902 }
1903 }
1904 }
1905 else
1906 {
1907 /*
1908 * We're adding a new one.
1909 */
1910 PRTDBGCFGSTR pNew = (PRTDBGCFGSTR)RTMemAlloc(RT_OFFSETOF(RTDBGCFGSTR, sz[cchPath + 1]));
1911 if (!pNew)
1912 return VERR_NO_MEMORY;
1913 pNew->cch = (uint16_t)cchPath;
1914 pNew->fFlags = 0;
1915 memcpy(pNew->sz, pchPath, cchPath);
1916 pNew->sz[cchPath] = '\0';
1917
1918 if (enmOp == RTDBGCFGOP_PREPEND)
1919 RTListPrepend(pList, &pNew->ListEntry);
1920 else
1921 RTListAppend(pList, &pNew->ListEntry);
1922 }
1923 }
1924
1925 return VINF_SUCCESS;
1926}
1927
1928
1929/**
1930 * Make changes to a 64-bit value
1931 *
1932 * @returns VINF_SUCCESS, VERR_DBG_CFG_INVALID_VALUE.
1933 * @param pThis The config instance.
1934 * @param enmOp The change operation.
1935 * @param pszValue The input value.
1936 * @param paMnemonics The mnemonics map for this value.
1937 * @param puValue The value to change.
1938 */
1939static int rtDbgCfgChangeStringU64(PRTDBGCFGINT pThis, RTDBGCFGOP enmOp, const char *pszValue,
1940 PCRTDBGCFGU64MNEMONIC paMnemonics, uint64_t *puValue)
1941{
1942 uint64_t uNew = enmOp == RTDBGCFGOP_SET ? 0 : *puValue;
1943
1944 char ch;
1945 while ((ch = *pszValue))
1946 {
1947 /* skip whitespace and separators */
1948 while (RT_C_IS_SPACE(ch) || RT_C_IS_CNTRL(ch) || ch == ';' || ch == ':')
1949 ch = *++pszValue;
1950 if (!ch)
1951 break;
1952
1953 if (RT_C_IS_DIGIT(ch))
1954 {
1955 uint64_t uTmp;
1956 int rc = RTStrToUInt64Ex(pszValue, (char **)&pszValue, 0, &uTmp);
1957 if (RT_FAILURE(rc) || rc == VWRN_NUMBER_TOO_BIG)
1958 return VERR_DBG_CFG_INVALID_VALUE;
1959
1960 if (enmOp != RTDBGCFGOP_REMOVE)
1961 uNew |= uTmp;
1962 else
1963 uNew &= ~uTmp;
1964 }
1965 else
1966 {
1967 /* A mnemonic, find the end of it. */
1968 const char *pszMnemonic = pszValue - 1;
1969 do
1970 ch = *++pszValue;
1971 while (ch && !RT_C_IS_SPACE(ch) && !RT_C_IS_CNTRL(ch) && ch != ';' && ch != ':');
1972 size_t cchMnemonic = pszValue - pszMnemonic;
1973
1974 /* Look it up in the map and apply it. */
1975 unsigned i = 0;
1976 while (paMnemonics[i].pszMnemonic)
1977 {
1978 if ( cchMnemonic == paMnemonics[i].cchMnemonic
1979 && !memcmp(pszMnemonic, paMnemonics[i].pszMnemonic, cchMnemonic))
1980 {
1981 if (paMnemonics[i].fSet ? enmOp != RTDBGCFGOP_REMOVE : enmOp == RTDBGCFGOP_REMOVE)
1982 uNew |= paMnemonics[i].fFlags;
1983 else
1984 uNew &= ~paMnemonics[i].fFlags;
1985 break;
1986 }
1987 i++;
1988 }
1989
1990 if (!paMnemonics[i].pszMnemonic)
1991 return VERR_DBG_CFG_INVALID_VALUE;
1992 }
1993 }
1994
1995 *puValue = uNew;
1996 return VINF_SUCCESS;
1997}
1998
1999
2000RTDECL(int) RTDbgCfgChangeString(RTDBGCFG hDbgCfg, RTDBGCFGPROP enmProp, RTDBGCFGOP enmOp, const char *pszValue)
2001{
2002 PRTDBGCFGINT pThis = hDbgCfg;
2003 RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
2004 AssertReturn(enmProp > RTDBGCFGPROP_INVALID && enmProp < RTDBGCFGPROP_END, VERR_INVALID_PARAMETER);
2005 AssertReturn(enmOp > RTDBGCFGOP_INVALID && enmOp < RTDBGCFGOP_END, VERR_INVALID_PARAMETER);
2006 if (!pszValue)
2007 pszValue = "";
2008 else
2009 AssertPtrReturn(pszValue, VERR_INVALID_POINTER);
2010
2011 int rc = RTCritSectRwEnterExcl(&pThis->CritSect);
2012 if (RT_SUCCESS(rc))
2013 {
2014 switch (enmProp)
2015 {
2016 case RTDBGCFGPROP_FLAGS:
2017 rc = rtDbgCfgChangeStringU64(pThis, enmOp, pszValue, g_aDbgCfgFlags, &pThis->fFlags);
2018 break;
2019 case RTDBGCFGPROP_PATH:
2020 rc = rtDbgCfgChangeStringList(pThis, enmOp, pszValue, true, &pThis->PathList);
2021 break;
2022 case RTDBGCFGPROP_SUFFIXES:
2023 rc = rtDbgCfgChangeStringList(pThis, enmOp, pszValue, false, &pThis->SuffixList);
2024 break;
2025 case RTDBGCFGPROP_SRC_PATH:
2026 rc = rtDbgCfgChangeStringList(pThis, enmOp, pszValue, true, &pThis->SrcPathList);
2027 break;
2028 default:
2029 AssertFailed();
2030 rc = VERR_INTERNAL_ERROR_3;
2031 }
2032
2033 RTCritSectRwLeaveExcl(&pThis->CritSect);
2034 }
2035
2036 return rc;
2037}
2038
2039
2040RTDECL(int) RTDbgCfgChangeUInt(RTDBGCFG hDbgCfg, RTDBGCFGPROP enmProp, RTDBGCFGOP enmOp, uint64_t uValue)
2041{
2042 PRTDBGCFGINT pThis = hDbgCfg;
2043 RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
2044 AssertReturn(enmProp > RTDBGCFGPROP_INVALID && enmProp < RTDBGCFGPROP_END, VERR_INVALID_PARAMETER);
2045 AssertReturn(enmOp > RTDBGCFGOP_INVALID && enmOp < RTDBGCFGOP_END, VERR_INVALID_PARAMETER);
2046
2047 int rc = RTCritSectRwEnterExcl(&pThis->CritSect);
2048 if (RT_SUCCESS(rc))
2049 {
2050 uint64_t *puValue = NULL;
2051 switch (enmProp)
2052 {
2053 case RTDBGCFGPROP_FLAGS:
2054 puValue = &pThis->fFlags;
2055 break;
2056 default:
2057 rc = VERR_DBG_CFG_NOT_UINT_PROP;
2058 }
2059 if (RT_SUCCESS(rc))
2060 {
2061 switch (enmOp)
2062 {
2063 case RTDBGCFGOP_SET:
2064 *puValue = uValue;
2065 break;
2066 case RTDBGCFGOP_APPEND:
2067 case RTDBGCFGOP_PREPEND:
2068 *puValue |= uValue;
2069 break;
2070 case RTDBGCFGOP_REMOVE:
2071 *puValue &= ~uValue;
2072 break;
2073 default:
2074 AssertFailed();
2075 rc = VERR_INTERNAL_ERROR_2;
2076 }
2077 }
2078
2079 RTCritSectRwLeaveExcl(&pThis->CritSect);
2080 }
2081
2082 return rc;
2083}
2084
2085
2086/**
2087 * Querys a string list as a single string (semicolon separators).
2088 *
2089 * @returns VINF_SUCCESS, VERR_BUFFER_OVERFLOW.
2090 * @param hDbgCfg The config instance handle.
2091 * @param pList The string list anchor.
2092 * @param pszValue The output buffer.
2093 * @param cbValue The size of the output buffer.
2094 */
2095static int rtDbgCfgQueryStringList(RTDBGCFG hDbgCfg, PRTLISTANCHOR pList,
2096 char *pszValue, size_t cbValue)
2097{
2098 /*
2099 * Check the length first.
2100 */
2101 size_t cbReq = 1;
2102 PRTDBGCFGSTR pCur;
2103 RTListForEach(pList, pCur, RTDBGCFGSTR, ListEntry)
2104 cbReq += pCur->cch + 1;
2105 if (cbReq > cbValue)
2106 return VERR_BUFFER_OVERFLOW;
2107
2108 /*
2109 * Construct the string list in the buffer.
2110 */
2111 char *psz = pszValue;
2112 RTListForEach(pList, pCur, RTDBGCFGSTR, ListEntry)
2113 {
2114 if (psz != pszValue)
2115 *psz++ = ';';
2116 memcpy(psz, pCur->sz, pCur->cch);
2117 psz += pCur->cch;
2118 }
2119 *psz = '\0';
2120
2121 return VINF_SUCCESS;
2122}
2123
2124
2125/**
2126 * Querys the string value of a 64-bit unsigned int.
2127 *
2128 * @returns VINF_SUCCESS, VERR_BUFFER_OVERFLOW.
2129 * @param hDbgCfg The config instance handle.
2130 * @param uValue The value to query.
2131 * @param paMnemonics The mnemonics map for this value.
2132 * @param pszValue The output buffer.
2133 * @param cbValue The size of the output buffer.
2134 */
2135static int rtDbgCfgQueryStringU64(RTDBGCFG hDbgCfg, uint64_t uValue, PCRTDBGCFGU64MNEMONIC paMnemonics,
2136 char *pszValue, size_t cbValue)
2137{
2138 /*
2139 * If no mnemonics, just return the hex value.
2140 */
2141 if (!paMnemonics || paMnemonics[0].pszMnemonic)
2142 {
2143 char szTmp[64];
2144 size_t cch = RTStrPrintf(szTmp, sizeof(szTmp), "%#x", uValue);
2145 if (cch + 1 > cbValue)
2146 return VERR_BUFFER_OVERFLOW;
2147 memcpy(pszValue, szTmp, cbValue);
2148 return VINF_SUCCESS;
2149 }
2150
2151 /*
2152 * Check that there is sufficient buffer space first.
2153 */
2154 size_t cbReq = 1;
2155 for (unsigned i = 0; paMnemonics[i].pszMnemonic; i++)
2156 if ( paMnemonics[i].fSet
2157 ? (paMnemonics[i].fFlags & uValue)
2158 : !(paMnemonics[i].fFlags & uValue))
2159 cbReq += (cbReq != 1) + paMnemonics[i].cchMnemonic;
2160 if (cbReq > cbValue)
2161 return VERR_BUFFER_OVERFLOW;
2162
2163 /*
2164 * Construct the string.
2165 */
2166 char *psz = pszValue;
2167 for (unsigned i = 0; paMnemonics[i].pszMnemonic; i++)
2168 if ( paMnemonics[i].fSet
2169 ? (paMnemonics[i].fFlags & uValue)
2170 : !(paMnemonics[i].fFlags & uValue))
2171 {
2172 if (psz != pszValue)
2173 *psz++ = ' ';
2174 memcpy(psz, paMnemonics[i].pszMnemonic, paMnemonics[i].cchMnemonic);
2175 psz += paMnemonics[i].cchMnemonic;
2176 }
2177 *psz = '\0';
2178 return VINF_SUCCESS;
2179}
2180
2181
2182RTDECL(int) RTDbgCfgQueryString(RTDBGCFG hDbgCfg, RTDBGCFGPROP enmProp, char *pszValue, size_t cbValue)
2183{
2184 PRTDBGCFGINT pThis = hDbgCfg;
2185 RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
2186 AssertReturn(enmProp > RTDBGCFGPROP_INVALID && enmProp < RTDBGCFGPROP_END, VERR_INVALID_PARAMETER);
2187 AssertPtrReturn(pszValue, VERR_INVALID_POINTER);
2188
2189 int rc = RTCritSectRwEnterShared(&pThis->CritSect);
2190 if (RT_SUCCESS(rc))
2191 {
2192 switch (enmProp)
2193 {
2194 case RTDBGCFGPROP_FLAGS:
2195 rc = rtDbgCfgQueryStringU64(pThis, pThis->fFlags, g_aDbgCfgFlags, pszValue, cbValue);
2196 break;
2197 case RTDBGCFGPROP_PATH:
2198 rc = rtDbgCfgQueryStringList(pThis, &pThis->PathList, pszValue, cbValue);
2199 break;
2200 case RTDBGCFGPROP_SUFFIXES:
2201 rc = rtDbgCfgQueryStringList(pThis, &pThis->SuffixList, pszValue, cbValue);
2202 break;
2203 case RTDBGCFGPROP_SRC_PATH:
2204 rc = rtDbgCfgQueryStringList(pThis, &pThis->SrcPathList, pszValue, cbValue);
2205 break;
2206 default:
2207 AssertFailed();
2208 rc = VERR_INTERNAL_ERROR_3;
2209 }
2210
2211 RTCritSectRwLeaveShared(&pThis->CritSect);
2212 }
2213
2214 return rc;
2215}
2216
2217
2218RTDECL(int) RTDbgCfgQueryUInt(RTDBGCFG hDbgCfg, RTDBGCFGPROP enmProp, uint64_t *puValue)
2219{
2220 PRTDBGCFGINT pThis = hDbgCfg;
2221 RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
2222 AssertReturn(enmProp > RTDBGCFGPROP_INVALID && enmProp < RTDBGCFGPROP_END, VERR_INVALID_PARAMETER);
2223 AssertPtrReturn(puValue, VERR_INVALID_POINTER);
2224
2225 int rc = RTCritSectRwEnterShared(&pThis->CritSect);
2226 if (RT_SUCCESS(rc))
2227 {
2228 switch (enmProp)
2229 {
2230 case RTDBGCFGPROP_FLAGS:
2231 *puValue = pThis->fFlags;
2232 break;
2233 default:
2234 rc = VERR_DBG_CFG_NOT_UINT_PROP;
2235 }
2236
2237 RTCritSectRwLeaveShared(&pThis->CritSect);
2238 }
2239
2240 return rc;
2241}
2242
2243RTDECL(uint32_t) RTDbgCfgRetain(RTDBGCFG hDbgCfg)
2244{
2245 PRTDBGCFGINT pThis = hDbgCfg;
2246 RTDBGCFG_VALID_RETURN_RC(pThis, UINT32_MAX);
2247
2248 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
2249 Assert(cRefs < UINT32_MAX / 2);
2250 return cRefs;
2251}
2252
2253
2254RTDECL(uint32_t) RTDbgCfgRelease(RTDBGCFG hDbgCfg)
2255{
2256 if (hDbgCfg == NIL_RTDBGCFG)
2257 return 0;
2258
2259 PRTDBGCFGINT pThis = hDbgCfg;
2260 RTDBGCFG_VALID_RETURN_RC(pThis, UINT32_MAX);
2261
2262 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
2263 if (!cRefs)
2264 {
2265 /*
2266 * Last reference - free all memory.
2267 */
2268 ASMAtomicWriteU32(&pThis->u32Magic, ~RTDBGCFG_MAGIC);
2269 rtDbgCfgFreeStrList(&pThis->PathList);
2270 rtDbgCfgFreeStrList(&pThis->SuffixList);
2271 rtDbgCfgFreeStrList(&pThis->SrcPathList);
2272#ifdef RT_OS_WINDOWS
2273 rtDbgCfgFreeStrList(&pThis->NtSymbolPathList);
2274 rtDbgCfgFreeStrList(&pThis->NtExecutablePathList);
2275 rtDbgCfgFreeStrList(&pThis->NtSourcePath);
2276#endif
2277 RTCritSectRwDelete(&pThis->CritSect);
2278 RTMemFree(pThis);
2279 }
2280 else
2281 Assert(cRefs < UINT32_MAX / 2);
2282 return cRefs;
2283}
2284
2285
2286RTDECL(int) RTDbgCfgCreate(PRTDBGCFG phDbgCfg, const char *pszEnvVarPrefix, bool fNativePaths)
2287{
2288 /*
2289 * Validate input.
2290 */
2291 AssertPtrReturn(phDbgCfg, VERR_INVALID_POINTER);
2292 if (pszEnvVarPrefix)
2293 {
2294 AssertPtrReturn(pszEnvVarPrefix, VERR_INVALID_POINTER);
2295 AssertReturn(*pszEnvVarPrefix, VERR_INVALID_PARAMETER);
2296 }
2297
2298 /*
2299 * Allocate and initialize a new instance.
2300 */
2301 PRTDBGCFGINT pThis = (PRTDBGCFGINT)RTMemAllocZ(sizeof(*pThis));
2302 if (!pThis)
2303 return VERR_NO_MEMORY;
2304
2305 pThis->u32Magic = RTDBGCFG_MAGIC;
2306 pThis->cRefs = 1;
2307 RTListInit(&pThis->PathList);
2308 RTListInit(&pThis->SuffixList);
2309 RTListInit(&pThis->SrcPathList);
2310#ifdef RT_OS_WINDOWS
2311 RTListInit(&pThis->NtSymbolPathList);
2312 RTListInit(&pThis->NtExecutablePathList);
2313 RTListInit(&pThis->NtSourcePath);
2314#endif
2315
2316 int rc = RTCritSectRwInit(&pThis->CritSect);
2317 if (RT_FAILURE(rc))
2318 {
2319 RTMemFree(pThis);
2320 return rc;
2321 }
2322
2323 /*
2324 * Read configurtion from the environment if requested to do so.
2325 */
2326 if (pszEnvVarPrefix || fNativePaths)
2327 {
2328 const size_t cbEnvVar = 256;
2329 const size_t cbEnvVal = 65536 - cbEnvVar;
2330 char *pszEnvVar = (char *)RTMemTmpAlloc(cbEnvVar + cbEnvVal);
2331 if (pszEnvVar)
2332 {
2333 char *pszEnvVal = pszEnvVar + cbEnvVar;
2334
2335 if (pszEnvVarPrefix)
2336 {
2337 static struct
2338 {
2339 RTDBGCFGPROP enmProp;
2340 const char *pszVar;
2341 } const s_aProps[] =
2342 {
2343 { RTDBGCFGPROP_FLAGS, "FLAGS" },
2344 { RTDBGCFGPROP_PATH, "PATH" },
2345 { RTDBGCFGPROP_SUFFIXES, "SUFFIXES" },
2346 { RTDBGCFGPROP_SRC_PATH, "SRC_PATH" },
2347 };
2348
2349 for (unsigned i = 0; i < RT_ELEMENTS(s_aProps); i++)
2350 {
2351 size_t cchEnvVar = RTStrPrintf(pszEnvVar, cbEnvVar, "%s_%s", pszEnvVarPrefix, s_aProps[i].pszVar);
2352 if (cchEnvVar >= cbEnvVar - 1)
2353 {
2354 rc = VERR_BUFFER_OVERFLOW;
2355 break;
2356 }
2357
2358 rc = RTEnvGetEx(RTENV_DEFAULT, pszEnvVar, pszEnvVal, cbEnvVal, NULL);
2359 if (RT_SUCCESS(rc))
2360 {
2361 rc = RTDbgCfgChangeString(pThis, s_aProps[i].enmProp, RTDBGCFGOP_SET, pszEnvVal);
2362 if (RT_FAILURE(rc))
2363 break;
2364 }
2365 else if (rc != VERR_ENV_VAR_NOT_FOUND)
2366 break;
2367 else
2368 rc = VINF_SUCCESS;
2369 }
2370 }
2371
2372 /*
2373 * Pick up system specific search paths.
2374 */
2375 if (RT_SUCCESS(rc) && fNativePaths)
2376 {
2377 struct
2378 {
2379 PRTLISTANCHOR pList;
2380 const char *pszVar;
2381 char chSep;
2382 } aNativePaths[] =
2383 {
2384#ifdef RT_OS_WINDOWS
2385 { &pThis->NtExecutablePathList, "_NT_EXECUTABLE_PATH", ';' },
2386 { &pThis->NtSymbolPathList, "_NT_ALT_SYMBOL_PATH", ';' },
2387 { &pThis->NtSymbolPathList, "_NT_SYMBOL_PATH", ';' },
2388 { &pThis->NtSourcePath, "_NT_SOURCE_PATH", ';' },
2389#endif
2390 { NULL, NULL, 0 }
2391 };
2392 for (unsigned i = 0; aNativePaths[i].pList; i++)
2393 {
2394 Assert(aNativePaths[i].chSep == ';'); /* fix when needed */
2395 rc = RTEnvGetEx(RTENV_DEFAULT, aNativePaths[i].pszVar, pszEnvVal, cbEnvVal, NULL);
2396 if (RT_SUCCESS(rc))
2397 {
2398 rc = rtDbgCfgChangeStringList(pThis, RTDBGCFGOP_APPEND, pszEnvVal, true, aNativePaths[i].pList);
2399 if (RT_FAILURE(rc))
2400 break;
2401 }
2402 else if (rc != VERR_ENV_VAR_NOT_FOUND)
2403 break;
2404 else
2405 rc = VINF_SUCCESS;
2406 }
2407 }
2408 RTMemTmpFree(pszEnvVar);
2409 }
2410 else
2411 rc = VERR_NO_TMP_MEMORY;
2412 if (RT_FAILURE(rc))
2413 {
2414 /*
2415 * Error, bail out.
2416 */
2417 RTDbgCfgRelease(pThis);
2418 return rc;
2419 }
2420 }
2421
2422 /*
2423 * Returns successfully.
2424 */
2425 *phDbgCfg = pThis;
2426
2427 return VINF_SUCCESS;
2428}
2429
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette