VirtualBox

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

Last change on this file since 57444 was 57358, checked in by vboxsync, 9 years ago

*: scm cleanup run.

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