VirtualBox

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

Last change on this file since 62477 was 62477, checked in by vboxsync, 8 years ago

(C) 2016

  • 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 62477 2016-07-22 18:27:37Z vboxsync $ */
2/** @file
3 * IPRT - Debugging Configuration.
4 */
5
6/*
7 * Copyright (C) 2013-2016 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 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 PFNRTDBGCFGOPEN 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 PFNRTDBGCFGOPEN 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 PFNRTDBGCFGOPEN 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 PFNRTDBGCFGOPEN 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 paMnemonics 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 hDbgCfg The config instance handle.
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 hDbgCfg The config instance handle.
2129 * @param uValue The value to query.
2130 * @param paMnemonics 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