VirtualBox

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

Last change on this file since 46083 was 46078, checked in by vboxsync, 12 years ago

More debugging fixes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 58.7 KB
Line 
1/* $Id: dbgcfg.cpp 46078 2013-05-14 18:22:53Z vboxsync $ */
2/** @file
3 * IPRT - Debugging Configuration.
4 */
5
6/*
7 * Copyright (C) 2013 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/asm.h>
36#include <iprt/assert.h>
37#include <iprt/critsect.h>
38#include <iprt/ctype.h>
39#include <iprt/dir.h>
40#include <iprt/err.h>
41#include <iprt/env.h>
42#include <iprt/file.h>
43#ifdef IPRT_WITH_HTTP
44# include <iprt/http.h>
45#endif
46#include <iprt/list.h>
47#include <iprt/log.h>
48#include <iprt/mem.h>
49#include <iprt/path.h>
50#include <iprt/process.h>
51#include <iprt/semaphore.h>
52#include <iprt/string.h>
53#include <iprt/uuid.h>
54#include "internal/magics.h"
55
56
57/*******************************************************************************
58* Structures and Typedefs *
59*******************************************************************************/
60/**
61 * String list entry.
62 */
63typedef struct RTDBGCFGSTR
64{
65 /** List entry. */
66 RTLISTNODE ListEntry;
67 /** Domain specific flags. */
68 uint16_t fFlags;
69 /** The length of the string. */
70 uint16_t cch;
71 /** The string. */
72 char sz[1];
73} RTDBGCFGSTR;
74/** Pointer to a string list entry. */
75typedef RTDBGCFGSTR *PRTDBGCFGSTR;
76
77
78/**
79 * Configuration instance.
80 */
81typedef struct RTDBGCFGINT
82{
83 /** The magic value (RTDBGCFG_MAGIC). */
84 uint32_t u32Magic;
85 /** Reference counter. */
86 uint32_t volatile cRefs;
87 /** Flags, see RTDBGCFG_FLAGS_XXX. */
88 uint64_t fFlags;
89
90 /** List of paths to search for debug files and executable images. */
91 RTLISTANCHOR PathList;
92 /** List of debug file suffixes. */
93 RTLISTANCHOR SuffixList;
94 /** List of paths to search for source files. */
95 RTLISTANCHOR SrcPathList;
96
97#ifdef RT_OS_WINDOWS
98 /** The _NT_ALT_SYMBOL_PATH and _NT_SYMBOL_PATH combined. */
99 RTLISTANCHOR NtSymbolPathList;
100 /** The _NT_EXECUTABLE_PATH. */
101 RTLISTANCHOR NtExecutablePathList;
102 /** The _NT_SOURCE_PATH. */
103 RTLISTANCHOR NtSourcePath;
104#endif
105
106 /** Log callback function. */
107 PFNRTDBGCFGLOG pfnLogCallback;
108 /** User argument to pass to the log callback. */
109 void *pvLogUser;
110
111 /** Critical section protecting the instance data. */
112 RTCRITSECTRW CritSect;
113} *PRTDBGCFGINT;
114
115/**
116 * Mnemonics map entry for a 64-bit unsigned property value.
117 */
118typedef struct RTDBGCFGU64MNEMONIC
119{
120 /** The flags to set or clear. */
121 uint64_t fFlags;
122 /** The mnemonic. */
123 const char *pszMnemonic;
124 /** The length of the mnemonic. */
125 uint8_t cchMnemonic;
126 /** If @c true, the bits in fFlags will be set, if @c false they will be
127 * cleared. */
128 bool fSet;
129} RTDBGCFGU64MNEMONIC;
130/** Pointer to a read only mnemonic map entry for a uint64_t property. */
131typedef RTDBGCFGU64MNEMONIC const *PCRTDBGCFGU64MNEMONIC;
132
133
134/** @name Open flags.
135 * @{ */
136/** The operative system mask. The values are RT_OPSYS_XXX. */
137#define RTDBGCFG_O_OPSYS_MASK UINT32_C(0x000000ff)
138/** The files may be compressed MS styled. */
139#define RTDBGCFG_O_MAYBE_COMPRESSED_MS RT_BIT_32(26)
140/** Whether to make a recursive search. */
141#define RTDBGCFG_O_RECURSIVE RT_BIT_32(27)
142/** We're looking for a separate debug file. */
143#define RTDBGCFG_O_EXT_DEBUG_FILE RT_BIT_32(28)
144/** We're looking for an executable image. */
145#define RTDBGCFG_O_EXECUTABLE_IMAGE RT_BIT_32(29)
146/** The file search should be done in an case insensitive fashion. */
147#define RTDBGCFG_O_CASE_INSENSITIVE RT_BIT_32(30)
148/** Use Windbg style symbol servers when encountered in the path. */
149#define RTDBGCFG_O_SYMSRV RT_BIT_32(31)
150/** @} */
151
152
153/*******************************************************************************
154* Defined Constants And Macros *
155*******************************************************************************/
156/** Validates a debug module handle and returns rc if not valid. */
157#define RTDBGCFG_VALID_RETURN_RC(pThis, rc) \
158 do { \
159 AssertPtrReturn((pThis), (rc)); \
160 AssertReturn((pThis)->u32Magic == RTDBGCFG_MAGIC, (rc)); \
161 AssertReturn((pThis)->cRefs > 0, (rc)); \
162 } while (0)
163
164
165/*******************************************************************************
166* Global Variables *
167*******************************************************************************/
168/** Mnemonics map for RTDBGCFGPROP_FLAGS. */
169static const RTDBGCFGU64MNEMONIC g_aDbgCfgFlags[] =
170{
171 { RTDBGCFG_FLAGS_DEFERRED, RT_STR_TUPLE("deferred"), true },
172 { RTDBGCFG_FLAGS_DEFERRED, RT_STR_TUPLE("nodeferred"), false },
173 { RTDBGCFG_FLAGS_NO_SYM_SRV, RT_STR_TUPLE("symsrv"), false },
174 { RTDBGCFG_FLAGS_NO_SYM_SRV, RT_STR_TUPLE("nosymsrv"), true },
175 { RTDBGCFG_FLAGS_NO_SYSTEM_PATHS, RT_STR_TUPLE("syspaths"), false },
176 { RTDBGCFG_FLAGS_NO_SYSTEM_PATHS, RT_STR_TUPLE("nosyspaths"), true },
177 { RTDBGCFG_FLAGS_NO_RECURSIV_SEARCH, RT_STR_TUPLE("rec"), false },
178 { RTDBGCFG_FLAGS_NO_RECURSIV_SEARCH, RT_STR_TUPLE("norec"), true },
179 { RTDBGCFG_FLAGS_NO_RECURSIV_SRC_SEARCH, RT_STR_TUPLE("recsrc"), false },
180 { RTDBGCFG_FLAGS_NO_RECURSIV_SRC_SEARCH, RT_STR_TUPLE("norecsrc"), true },
181 { 0, NULL, 0, false }
182};
183
184
185
186/**
187 * Runtime logging, level 1.
188 *
189 * @param pThis The debug config instance data.
190 * @param pszFormat The message format string.
191 * @param ... Arguments references in the format string.
192 */
193static void rtDbgCfgLog1(PRTDBGCFGINT pThis, const char *pszFormat, ...)
194{
195 if (LogIsEnabled() || (pThis && pThis->pfnLogCallback))
196 {
197 va_list va;
198 va_start(va, pszFormat);
199 char *pszMsg = RTStrAPrintf2V(pszFormat, va);
200 va_end(va);
201
202 Log(("RTDbgCfg: %s", pszMsg));
203 if (pThis && pThis->pfnLogCallback)
204 pThis->pfnLogCallback(pThis, 1, pszMsg, pThis->pvLogUser);
205 RTStrFree(pszMsg);
206 }
207}
208
209
210/**
211 * Runtime logging, level 2.
212 *
213 * @param pThis The debug config instance data.
214 * @param pszFormat The message format string.
215 * @param ... Arguments references in the format string.
216 */
217static void rtDbgCfgLog2(PRTDBGCFGINT pThis, const char *pszFormat, ...)
218{
219 if (LogIs2Enabled() || (pThis && pThis->pfnLogCallback))
220 {
221 va_list va;
222 va_start(va, pszFormat);
223 char *pszMsg = RTStrAPrintf2V(pszFormat, va);
224 va_end(va);
225
226 Log(("RTDbgCfg: %s", pszMsg));
227 if (pThis && pThis->pfnLogCallback)
228 pThis->pfnLogCallback(pThis, 2, pszMsg, pThis->pvLogUser);
229 RTStrFree(pszMsg);
230 }
231}
232
233
234/**
235 * Checks if the file system at the given path is case insensitive or not.
236 *
237 * @returns true / false
238 * @param pszPath The path to query about.
239 */
240static int rtDbgCfgIsFsCaseInsensitive(const char *pszPath)
241{
242 RTFSPROPERTIES Props;
243 int rc = RTFsQueryProperties(pszPath, &Props);
244 if (RT_FAILURE(rc))
245 return RT_OPSYS == RT_OPSYS_DARWIN
246 || RT_OPSYS == RT_OPSYS_DOS
247 || RT_OPSYS == RT_OPSYS_OS2
248 || RT_OPSYS == RT_OPSYS_NT
249 || RT_OPSYS == RT_OPSYS_WINDOWS;
250 return !Props.fCaseSensitive;
251}
252
253
254/**
255 * Worker that does case sensitive file/dir searching.
256 *
257 * @returns true / false.
258 * @param pszPath The path buffer containing an existing directory.
259 * RTPATH_MAX in size. On success, this will contain
260 * the combined path with @a pszName case correct.
261 * @param offLastComp The offset of the last component (for chopping it
262 * off).
263 * @param pszName What we're looking for.
264 * @param enmType What kind of thing we're looking for.
265 */
266static bool rtDbgCfgIsXxxxAndFixCaseWorker(char *pszPath, size_t offLastComp, const char *pszName,
267 RTDIRENTRYTYPE enmType)
268{
269 /** @todo IPRT should generalize this so we can use host specific tricks to
270 * speed it up. */
271
272 /* Return straight away if the name isn't case foldable. */
273 if (!RTStrIsCaseFoldable(pszName))
274 return false;
275
276 /*
277 * Try some simple case folding games.
278 */
279 RTStrToLower(&pszPath[offLastComp]);
280 if (RTFileExists(pszPath))
281 return true;
282
283 RTStrToUpper(&pszPath[offLastComp]);
284 if (RTFileExists(pszPath))
285 return true;
286
287 /*
288 * Open the directory and check each entry in it.
289 */
290 pszPath[offLastComp] = '\0';
291 PRTDIR pDir;
292 int rc = RTDirOpen(&pDir, pszPath);
293 if (RT_FAILURE(rc))
294 return false;
295
296 for (;;)
297 {
298 /* Read the next entry. */
299 union
300 {
301 RTDIRENTRY Entry;
302 uint8_t ab[_4K];
303 } u;
304 size_t cbBuf = sizeof(u);
305 rc = RTDirRead(pDir, &u.Entry, &cbBuf);
306 if (RT_FAILURE(rc))
307 break;
308
309 if ( !RTStrICmp(pszName, u.Entry.szName)
310 && ( u.Entry.enmType == enmType
311 || u.Entry.enmType == RTDIRENTRYTYPE_UNKNOWN
312 || u.Entry.enmType == RTDIRENTRYTYPE_SYMLINK) )
313 {
314 pszPath[offLastComp] = '\0';
315 rc = RTPathAppend(pszPath, RTPATH_MAX, u.Entry.szName);
316 if ( u.Entry.enmType != enmType
317 && RT_SUCCESS(rc))
318 RTDirQueryUnknownType(pszPath, true /*fFollowSymlinks*/, &u.Entry.enmType);
319
320 if ( u.Entry.enmType == enmType
321 || RT_FAILURE(rc))
322 {
323 RTDirClose(pDir);
324 if (RT_FAILURE(rc))
325 {
326 pszPath[offLastComp] = '\0';
327 return false;
328 }
329 return true;
330 }
331 }
332 }
333
334 RTDirClose(pDir);
335 pszPath[offLastComp] = '\0';
336
337 return false;
338}
339
340
341/**
342 * Appends @a pszSubDir to @a pszPath and check whether it exists and is a
343 * directory.
344 *
345 * If @a fCaseInsensitive is set, we will do a case insensitive search for a
346 * matching sub directory.
347 *
348 * @returns true / false
349 * @param pszPath The path buffer containing an existing
350 * directory. RTPATH_MAX in size.
351 * @param pszSubDir The sub directory to append.
352 * @param fCaseInsensitive Whether case insensitive searching is required.
353 */
354static bool rtDbgCfgIsDirAndFixCase(char *pszPath, const char *pszSubDir, bool fCaseInsensitive)
355{
356 /* Save the length of the input path so we can restore it in the case
357 insensitive branch further down. */
358 size_t const cchPath = strlen(pszPath);
359
360 /*
361 * Append the sub directory and check if we got a hit.
362 */
363 int rc = RTPathAppend(pszPath, RTPATH_MAX, pszSubDir);
364 if (RT_FAILURE(rc))
365 return false;
366
367 if (RTDirExists(pszPath))
368 return true;
369
370 /*
371 * Do case insensitive lookup if requested.
372 */
373 if (fCaseInsensitive)
374 return rtDbgCfgIsXxxxAndFixCaseWorker(pszPath, cchPath, pszSubDir, RTDIRENTRYTYPE_DIRECTORY);
375
376 pszPath[cchPath] = '\0';
377 return false;
378}
379
380
381/**
382 * Appends @a pszFilename to @a pszPath and check whether it exists and is a
383 * directory.
384 *
385 * If @a fCaseInsensitive is set, we will do a case insensitive search for a
386 * matching filename.
387 *
388 * @returns true / false
389 * @param pszPath The path buffer containing an existing
390 * directory. RTPATH_MAX in size.
391 * @param pszFilename The file name to append.
392 * @param fCaseInsensitive Whether case insensitive searching is required.
393 * @param fMsCompressed Whether to look for the MS compressed file name
394 * variant.
395 * @param pfProbablyCompressed This is set to true if a MS compressed
396 * filename variant is returned. Optional.
397 */
398static bool rtDbgCfgIsFileAndFixCase(char *pszPath, const char *pszFilename, bool fCaseInsensitive,
399 bool fMsCompressed, bool *pfProbablyCompressed)
400{
401 /* Save the length of the input path so we can restore it in the case
402 insensitive branch further down. */
403 size_t cchPath = strlen(pszPath);
404 if (pfProbablyCompressed)
405 *pfProbablyCompressed = false;
406
407 /*
408 * Append the filename and check if we got a hit.
409 */
410 int rc = RTPathAppend(pszPath, RTPATH_MAX, pszFilename);
411 if (RT_FAILURE(rc))
412 return false;
413
414 if (RTFileExists(pszPath))
415 return true;
416
417 /*
418 * Do case insensitive file lookup if requested.
419 */
420 if (fCaseInsensitive)
421 {
422 if (rtDbgCfgIsXxxxAndFixCaseWorker(pszPath, cchPath, pszFilename, RTDIRENTRYTYPE_FILE))
423 return true;
424 }
425
426 /*
427 * Look for MS compressed file if requested.
428 */
429 if ( fMsCompressed
430 && (unsigned char)pszFilename[strlen(pszFilename) - 1] < 0x7f)
431 {
432 pszPath[cchPath] = '\0';
433 rc = RTPathAppend(pszPath, RTPATH_MAX, pszFilename);
434 AssertRCReturn(rc, false);
435 pszPath[strlen(pszPath) - 1] = '_';
436
437 if (pfProbablyCompressed)
438 *pfProbablyCompressed = true;
439
440 if (RTFileExists(pszPath))
441 return true;
442
443 if (fCaseInsensitive)
444 {
445 /* Note! Ugly hack here, the pszName parameter points into pszPath! */
446 if (rtDbgCfgIsXxxxAndFixCaseWorker(pszPath, cchPath, RTPathFilename(pszPath), RTDIRENTRYTYPE_FILE))
447 return true;
448 }
449
450 if (pfProbablyCompressed)
451 *pfProbablyCompressed = false;
452 }
453
454 pszPath[cchPath] = '\0';
455 return false;
456}
457
458
459static int rtDbgCfgTryOpenDir(PRTDBGCFGINT pThis, char *pszPath, PRTPATHSPLIT pSplitFn, uint32_t fFlags,
460 PFNDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
461{
462 int rcRet = VWRN_NOT_FOUND;
463 int rc2;
464
465 /* If the directory doesn't exist, just quit immediately.
466 Note! Our case insensitivity doesn't extend to the search dirs themselfs,
467 only to the bits under neath them. */
468 if (!RTDirExists(pszPath))
469 {
470 rtDbgCfgLog2(pThis, "Dir does not exist: '%s'\n", pszPath);
471 return rcRet;
472 }
473
474 /* Figure out whether we have to do a case sensitive search or not.
475 Note! As a simplification, we don't ask for case settings in each
476 directory under the user specified path, we assume the file
477 systems that mounted there have compatible settings. Faster
478 that way. */
479 bool const fCaseInsensitive = (fFlags & RTDBGCFG_O_CASE_INSENSITIVE)
480 && !rtDbgCfgIsFsCaseInsensitive(pszPath);
481
482 size_t const cchPath = strlen(pszPath);
483
484 /*
485 * Look for the file with less and less of the original path given.
486 */
487 for (unsigned i = RTPATH_PROP_HAS_ROOT_SPEC(pSplitFn->fProps); i < pSplitFn->cComps; i++)
488 {
489 pszPath[cchPath] = '\0';
490
491 rc2 = VINF_SUCCESS;
492 for (unsigned j = i; j < pSplitFn->cComps - 1U && RT_SUCCESS(rc2); j++)
493 if (!rtDbgCfgIsDirAndFixCase(pszPath, pSplitFn->apszComps[i], fCaseInsensitive))
494 rc2 = VERR_FILE_NOT_FOUND;
495
496 if (RT_SUCCESS(rc2))
497 {
498 if (rtDbgCfgIsFileAndFixCase(pszPath, pSplitFn->apszComps[pSplitFn->cComps - 1], fCaseInsensitive, false, NULL))
499 {
500 rtDbgCfgLog1(pThis, "Trying '%s'...\n", pszPath);
501 rc2 = pfnCallback(pThis, pszPath, pvUser1, pvUser2);
502 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
503 {
504 if (rc2 == VINF_CALLBACK_RETURN)
505 rtDbgCfgLog1(pThis, "Found '%s'.\n", pszPath);
506 else
507 rtDbgCfgLog1(pThis, "Error opening '%s'.\n", pszPath);
508 return rc2;
509 }
510 rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc2, pszPath);
511 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
512 rcRet = rc2;
513 }
514 }
515 }
516
517 /*
518 * Do a recursive search if requested.
519 */
520 if ( (fFlags & RTDBGCFG_O_RECURSIVE)
521 && !(pThis->fFlags & RTDBGCFG_FLAGS_NO_RECURSIV_SEARCH) )
522 {
523 /** @todo Recursive searching will be done later. */
524 }
525
526 return rcRet;
527}
528
529static int rtDbgCfgUnpackMsCacheFile(PRTDBGCFGINT pThis, char *pszPath, const char *pszFilename)
530{
531 rtDbgCfgLog2(pThis, "Unpacking '%s'...\n", pszPath);
532
533 /*
534 * Duplicate the source file path, just for simplicity and restore the
535 * final character in the orignal. We cheerfully ignorining any
536 * possibility of multibyte UTF-8 sequences just like the caller did when
537 * setting it to '_'.
538 */
539 char *pszSrcArchive = RTStrDup(pszPath);
540 if (!pszSrcArchive)
541 return VERR_NO_STR_MEMORY;
542
543 pszPath[strlen(pszPath) - 1] = RT_C_TO_LOWER(pszFilename[strlen(pszFilename) - 1]);
544
545
546 /*
547 * Figuring out the argument list for the platform specific unpack util.
548 */
549#ifdef RT_OS_WINDOWS
550 const char *papszArgs[] =
551 {
552 "expand.exe",
553 pszSrcArchive,
554 pszPath,
555 NULL
556 };
557
558#else
559 char szExtractDir[RTPATH_MAX];
560 strcpy(szExtractDir, pszPath);
561 RTPathStripFilename(szExtractDir);
562
563 const char *papszArgs[] =
564 {
565 "cabextract",
566 "-L", /* Lower case extracted files. */
567 "-d", szExtractDir, /* Extraction path */
568 pszSrcArchive,
569 NULL
570 };
571#endif
572
573 /*
574 * Do the unpacking.
575 */
576 RTPROCESS hChild;
577 int rc = RTProcCreate(papszArgs[0], papszArgs, RTENV_DEFAULT,
578#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
579 RTPROC_FLAGS_NO_WINDOW | RTPROC_FLAGS_HIDDEN | RTPROC_FLAGS_SEARCH_PATH,
580#else
581 RTPROC_FLAGS_SEARCH_PATH,
582#endif
583 &hChild);
584 if (RT_SUCCESS(rc))
585 {
586 RTPROCSTATUS ProcStatus;
587 rc = RTProcWait(hChild, RTPROCWAIT_FLAGS_BLOCK, &ProcStatus);
588 if (RT_SUCCESS(rc))
589 {
590 if ( ProcStatus.enmReason == RTPROCEXITREASON_NORMAL
591 && ProcStatus.iStatus == 0)
592 {
593 if (RTPathExists(pszPath))
594 {
595 rtDbgCfgLog1(pThis, "Successfully unpacked '%s' to '%s'.\n", pszSrcArchive, pszPath);
596 rc = VINF_SUCCESS;
597 }
598 else
599 {
600 rtDbgCfgLog1(pThis, "Successfully ran unpacker on '%s', but '%s' is missing!\n", pszSrcArchive, pszPath);
601 rc = VERR_ZIP_ERROR;
602 }
603 }
604 else
605 {
606 rtDbgCfgLog2(pThis, "Unpacking '%s' failed: iStatus=%d enmReason=%d\n",
607 pszSrcArchive, ProcStatus.iStatus, ProcStatus.enmReason);
608 rc = VERR_ZIP_CORRUPTED;
609 }
610 }
611 else
612 rtDbgCfgLog1(pThis, "Error waiting for process: %Rrc\n", rc);
613
614 }
615 else
616 rtDbgCfgLog1(pThis, "Error starting unpack process '%s': %Rrc\n", papszArgs[0], rc);
617
618 return rc;
619}
620
621static int rtDbgCfgTryDownloadAndOpen(PRTDBGCFGINT pThis, const char *pszServer,
622 char *pszPath, const char *pszCacheSubDir, PRTPATHSPLIT pSplitFn,
623 uint32_t fFlags, PFNDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
624{
625#ifdef IPRT_WITH_HTTP
626 if (pThis->fFlags & RTDBGCFG_FLAGS_NO_SYM_SRV)
627 return VWRN_NOT_FOUND;
628
629 /*
630 * Create the path.
631 */
632 size_t cchTmp = strlen(pszPath);
633
634 int rc = RTDirCreateFullPath(pszPath, 0766);
635 if (!RTDirExists(pszPath))
636 {
637 Log(("Error creating cache dir '%s': %Rrc\n", pszPath, rc));
638 return rc;
639 }
640
641 const char *pszFilename = pSplitFn->apszComps[pSplitFn->cComps - 1];
642 rc = RTPathAppend(pszPath, RTPATH_MAX, pszFilename);
643 if (RT_FAILURE(rc))
644 return rc;
645 RTStrToLower(&pszPath[cchTmp]);
646 if (!RTDirExists(pszPath))
647 {
648 rc = RTDirCreate(pszPath, 0766, 0);
649 if (RT_FAILURE(rc))
650 {
651 Log(("RTDirCreate(%s) -> %Rrc\n", pszPath, rc));
652 }
653 }
654
655 rc = RTPathAppend(pszPath, RTPATH_MAX, pszCacheSubDir);
656 if (RT_FAILURE(rc))
657 return rc;
658 if (!RTDirExists(pszPath))
659 {
660 rc = RTDirCreate(pszPath, 0766, 0);
661 if (RT_FAILURE(rc))
662 {
663 Log(("RTDirCreate(%s) -> %Rrc\n", pszPath, rc));
664 }
665 }
666
667 /* Prepare the destination file name while we're here. */
668 cchTmp = strlen(pszPath);
669 RTStrToLower(&pszPath[cchTmp]);
670 rc = RTPathAppend(pszPath, RTPATH_MAX, pszFilename);
671 if (RT_FAILURE(rc))
672 return rc;
673
674 /*
675 * Download the file.
676 */
677 RTHTTP hHttp;
678 rc = RTHttpCreate(&hHttp);
679 if (RT_FAILURE(rc))
680 return rc;
681 RTHttpUseSystemProxySettings(hHttp);
682
683 static const char * const s_apszHeaders[] =
684 {
685 "User-Agent: Microsoft-Symbol-Server/6.6.0999.9",
686 "Pragma: no-cache",
687 };
688
689 rc = RTHttpSetHeaders(hHttp, RT_ELEMENTS(s_apszHeaders), s_apszHeaders);
690 if (RT_SUCCESS(rc))
691 {
692 char szUrl[_2K];
693 RTStrPrintf(szUrl, sizeof(szUrl), "%s/%s/%s/%s", pszServer, pszFilename, pszCacheSubDir, pszFilename);
694
695 /** @todo Use some temporary file name and rename it after the operation
696 * since not all systems support read-deny file sharing
697 * settings. */
698 rtDbgCfgLog2(pThis, "Downloading '%s' to '%s'...\n", szUrl, pszPath);
699 rc = RTHttpGetFile(hHttp, szUrl, pszPath);
700 if (RT_FAILURE(rc))
701 {
702 RTFileDelete(pszPath);
703 rtDbgCfgLog1(pThis, "%Rrc on URL '%s'\n", rc, pszPath);
704 }
705 if (rc == VERR_HTTP_NOT_FOUND)
706 {
707 /* Try the compressed version of the file. */
708 pszPath[strlen(pszPath) - 1] = '_';
709 szUrl[strlen(szUrl) - 1] = '_';
710 rtDbgCfgLog2(pThis, "Downloading '%s' to '%s'...\n", szUrl, pszPath);
711 rc = RTHttpGetFile(hHttp, szUrl, pszPath);
712 if (RT_SUCCESS(rc))
713 rc = rtDbgCfgUnpackMsCacheFile(pThis, pszPath, pszFilename);
714 else
715 {
716 rtDbgCfgLog1(pThis, "%Rrc on URL '%s'\n", rc, pszPath);
717 RTFileDelete(pszPath);
718 }
719 }
720 }
721
722 RTHttpDestroy(hHttp);
723
724 /*
725 * If we succeeded, give it a try.
726 */
727 if (RT_SUCCESS(rc))
728 {
729 Assert(RTFileExists(pszPath));
730 rtDbgCfgLog1(pThis, "Trying '%s'...\n", pszPath);
731 rc = pfnCallback(pThis, pszPath, pvUser1, pvUser2);
732 if (rc == VINF_CALLBACK_RETURN)
733 rtDbgCfgLog1(pThis, "Found '%s'.\n", pszPath);
734 else if (rc == VERR_CALLBACK_RETURN)
735 rtDbgCfgLog1(pThis, "Error opening '%s'.\n", pszPath);
736 else
737 rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc, pszPath);
738 }
739
740 return rc;
741
742#else /* !IPRT_WITH_HTTP */
743 return VWRN_NOT_FOUND;
744#endif /* !IPRT_WITH_HTTP */
745}
746
747
748static int rtDbgCfgCopyFileToCache(PRTDBGCFGINT pThis, char const *pszSrc, const char *pchCache, size_t cchCache,
749 const char *pszCacheSubDir, PRTPATHSPLIT pSplitFn)
750{
751 /** @todo copy to cache */
752 return VINF_SUCCESS;
753}
754
755
756static int rtDbgCfgTryOpenCache(PRTDBGCFGINT pThis, char *pszPath, const char *pszCacheSubDir, PRTPATHSPLIT pSplitFn,
757 uint32_t fFlags, PFNDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
758{
759 /*
760 * If the cache doesn't exist, fail right away.
761 */
762 if (!pszCacheSubDir || !*pszCacheSubDir)
763 return VWRN_NOT_FOUND;
764 if (!RTDirExists(pszPath))
765 {
766 rtDbgCfgLog2(pThis, "Cache does not exist: '%s'\n", pszPath);
767 return VWRN_NOT_FOUND;
768 }
769
770 size_t cchPath = strlen(pszPath);
771
772 /*
773 * Carefully construct the cache path with case insensitivity in mind.
774 */
775 bool const fCaseInsensitive = (fFlags & RTDBGCFG_O_CASE_INSENSITIVE)
776 && !rtDbgCfgIsFsCaseInsensitive(pszPath);
777 const char *pszFilename = pSplitFn->apszComps[pSplitFn->cComps - 1];
778
779 if (!rtDbgCfgIsDirAndFixCase(pszPath, pszFilename, fCaseInsensitive))
780 return VWRN_NOT_FOUND;
781
782 if (!rtDbgCfgIsDirAndFixCase(pszPath, pszCacheSubDir, fCaseInsensitive))
783 return VWRN_NOT_FOUND;
784
785 bool fProbablyCompressed = false;
786 if (!rtDbgCfgIsFileAndFixCase(pszPath, pszFilename, fCaseInsensitive,
787 RT_BOOL(fFlags & RTDBGCFG_O_MAYBE_COMPRESSED_MS), &fProbablyCompressed))
788 return VWRN_NOT_FOUND;
789 if (fProbablyCompressed)
790 {
791 int rc = rtDbgCfgUnpackMsCacheFile(pThis, pszPath, pszFilename);
792 if (RT_FAILURE(rc))
793 return VWRN_NOT_FOUND;
794 }
795
796 rtDbgCfgLog1(pThis, "Trying '%s'...\n", pszPath);
797 int rc2 = pfnCallback(pThis, pszPath, pvUser1, pvUser2);
798 if (rc2 == VINF_CALLBACK_RETURN)
799 rtDbgCfgLog1(pThis, "Found '%s'.\n", pszPath);
800 else if (rc2 == VERR_CALLBACK_RETURN)
801 rtDbgCfgLog1(pThis, "Error opening '%s'.\n", pszPath);
802 else
803 rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc2, pszPath);
804 return rc2;
805}
806
807
808static int rtDbgCfgTryOpenList(PRTDBGCFGINT pThis, PRTLISTANCHOR pList, PRTPATHSPLIT pSplitFn, const char *pszCacheSubDir,
809 uint32_t fFlags, char *pszPath, PFNDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
810{
811 int rcRet = VWRN_NOT_FOUND;
812 int rc2;
813
814 const char *pchCache = NULL;
815 size_t cchCache = 0;
816 int rcCache = VWRN_NOT_FOUND;
817
818 PRTDBGCFGSTR pCur;
819 RTListForEach(pList, pCur, RTDBGCFGSTR, ListEntry)
820 {
821 size_t cchDir = pCur->cch;
822 const char *pszDir = pCur->sz;
823 rtDbgCfgLog2(pThis, "Path list entry: '%s'\n", pszDir);
824
825 /* This is very simplistic, but we have a unreasonably large path
826 buffer, so it'll work just fine and simplify things greatly below. */
827 if (cchDir >= RTPATH_MAX - 8U)
828 {
829 if (RT_SUCCESS_NP(rcRet))
830 rcRet = VERR_FILENAME_TOO_LONG;
831 continue;
832 }
833
834 /*
835 * Process the path according to it's type.
836 */
837 if (!strncmp(pszDir, RT_STR_TUPLE("srv*")))
838 {
839 /*
840 * Symbol server.
841 */
842 pszDir += sizeof("srv*") - 1;
843 cchDir -= sizeof("srv*") - 1;
844 bool fSearchCache = false;
845 const char *pszServer = (const char *)memchr(pszDir, '*', cchDir);
846 if (!pszServer)
847 pszServer = pszDir;
848 else if (pszServer == pszDir)
849 continue;
850 {
851 fSearchCache = true;
852 pchCache = pszDir;
853 cchCache = pszServer - pszDir;
854 pszServer++;
855 }
856
857 /* We don't have any default cache directory, so skip if the cache is missing. */
858 if (cchCache == 0)
859 continue;
860
861 /* Search the cache first (if we haven't already done so). */
862 if (fSearchCache)
863 {
864 memcpy(pszPath, pchCache, cchCache);
865 pszPath[cchCache] = '\0';
866 rcCache = rc2 = rtDbgCfgTryOpenCache(pThis, pszPath, pszCacheSubDir, pSplitFn, fFlags,
867 pfnCallback, pvUser1, pvUser2);
868 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
869 return rc2;
870 }
871
872 /* Try downloading the file. */
873 if (rcCache == VWRN_NOT_FOUND)
874 {
875 memcpy(pszPath, pchCache, cchCache);
876 pszPath[cchCache] = '\0';
877 rc2 = rtDbgCfgTryDownloadAndOpen(pThis, pszServer, pszPath, pszCacheSubDir, pSplitFn, fFlags,
878 pfnCallback, pvUser1, pvUser2);
879 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
880 return rc2;
881 }
882 }
883 else if (!strncmp(pszDir, RT_STR_TUPLE("cache*")))
884 {
885 /*
886 * Cache directory.
887 */
888 pszDir += sizeof("cache*") - 1;
889 cchDir -= sizeof("cache*") - 1;
890 if (!cchDir)
891 continue;
892 pchCache = pszDir;
893 cchCache = cchDir;
894
895 memcpy(pszPath, pchCache, cchCache);
896 pszPath[cchCache] = '\0';
897 rcCache = rc2 = rtDbgCfgTryOpenCache(pThis, pszPath, pszCacheSubDir, pSplitFn, fFlags,
898 pfnCallback, pvUser1, pvUser2);
899 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
900 return rc2;
901 }
902 else
903 {
904 /*
905 * Normal directory. Check for our own 'rec*' and 'norec*' prefix
906 * flags governing recursive searching.
907 */
908 uint32_t fFlagsDir = fFlags;
909 if (!strncmp(pszDir, RT_STR_TUPLE("rec*")))
910 {
911 pszDir += sizeof("rec*") - 1;
912 cchDir -= sizeof("rec*") - 1;
913 fFlagsDir |= RTDBGCFG_O_RECURSIVE;
914 }
915 else if (!strncmp(pszDir, RT_STR_TUPLE("norec*")))
916 {
917 pszDir += sizeof("norec*") - 1;
918 cchDir -= sizeof("norec*") - 1;
919 fFlagsDir &= ~RTDBGCFG_O_RECURSIVE;
920 }
921
922 /* Copy the path into the buffer and do the searching. */
923 memcpy(pszPath, pszDir, cchDir);
924 pszPath[cchDir] = '\0';
925
926 rc2 = rtDbgCfgTryOpenDir(pThis, pszPath, pSplitFn, fFlagsDir, pfnCallback, pvUser1, pvUser2);
927 if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
928 {
929 if ( rc2 == VINF_CALLBACK_RETURN
930 && cchCache > 0)
931 rtDbgCfgCopyFileToCache(pThis, pszPath, pchCache, cchCache, pszCacheSubDir, pSplitFn);
932 return rc2;
933 }
934 }
935
936 /* Propagate errors. */
937 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
938 rcRet = rc2;
939 }
940
941 return rcRet;
942}
943
944
945/**
946 * Common worker routine for Image and debug info opening.
947 *
948 * This will not search using for suffixes.
949 *
950 * @returns IPRT status code.
951 * @param hDbgCfg The debugging configuration handle. NIL_RTDBGCFG is
952 * accepted, but the result is that no paths will be
953 * searched beyond the given and the current directory.
954 * @param pszFilename The filename to search for. This may or may not
955 * include a full or partial path.
956 * @param pszCacheSubDir The cache subdirectory to look in.
957 * @param fFlags Flags and hints.
958 * @param pfnCallback The open callback routine.
959 * @param pvUser1 User parameter 1.
960 * @param pvUser2 User parameter 2.
961 */
962static int rtDbgCfgOpenWithSubDir(RTDBGCFG hDbgCfg, const char *pszFilename, const char *pszCacheSubDir,
963 uint32_t fFlags, PFNDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
964{
965 int rcRet = VINF_SUCCESS;
966 int rc2;
967
968 /*
969 * Do a little validating first.
970 */
971 PRTDBGCFGINT pThis = hDbgCfg;
972 if (pThis != NIL_RTDBGCFG)
973 RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
974 else
975 pThis = NULL;
976 AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
977 AssertPtrReturn(pszCacheSubDir, VERR_INVALID_POINTER);
978 AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
979
980 /*
981 * Do some guessing as to the way we should parse the filename and whether
982 * it's case exact or not.
983 */
984 bool fDosPath = strchr(pszFilename, ':') != NULL
985 || strchr(pszFilename, '\\') != NULL
986 || RT_OPSYS_USES_DOS_PATHS(fFlags & RTDBGCFG_O_OPSYS_MASK)
987 || (fFlags & RTDBGCFG_O_CASE_INSENSITIVE);
988 if (fDosPath)
989 fFlags |= RTDBGCFG_O_CASE_INSENSITIVE;
990
991 rtDbgCfgLog2(pThis, "Looking for '%s' w/ cache subdir '%s' and %#x flags...\n", pszFilename, pszCacheSubDir, fFlags);
992
993 PRTPATHSPLIT pSplitFn;
994 rc2 = RTPathSplitA(pszFilename, &pSplitFn, fDosPath ? RTPATH_STR_F_STYLE_DOS : RTPATH_STR_F_STYLE_UNIX);
995 if (RT_FAILURE(rc2))
996 return rc2;
997
998 /*
999 * Try the stored file name first if it has a kind of absolute path.
1000 */
1001 char szPath[RTPATH_MAX];
1002 if (RTPATH_PROP_HAS_ROOT_SPEC(pSplitFn->fProps))
1003 {
1004 rc2 = RTPathSplitReassemble(pSplitFn, RTPATH_STR_F_STYLE_HOST, szPath, sizeof(szPath));
1005 if (RT_SUCCESS(rc2) && RTFileExists(szPath))
1006 rc2 = pfnCallback(pThis, pszFilename, pvUser1, pvUser2);
1007 }
1008 if ( rc2 != VINF_CALLBACK_RETURN
1009 && rc2 != VERR_CALLBACK_RETURN)
1010 {
1011 /*
1012 * Try the current directory (will take cover relative paths
1013 * skipped above).
1014 */
1015 rc2 = RTPathGetCurrent(szPath, sizeof(szPath));
1016 if (RT_FAILURE(rc2))
1017 strcpy(szPath, ".");
1018 rc2 = rtDbgCfgTryOpenDir(pThis, szPath, pSplitFn, fFlags, pfnCallback, pvUser1, pvUser2);
1019 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
1020 rcRet = rc2;
1021
1022 if ( rc2 != VINF_CALLBACK_RETURN
1023 && rc2 != VERR_CALLBACK_RETURN
1024 && pThis)
1025 {
1026 rc2 = RTCritSectRwEnterShared(&pThis->CritSect);
1027 if (RT_SUCCESS(rc2))
1028 {
1029 /*
1030 * Run the applicable lists.
1031 */
1032 rc2 = rtDbgCfgTryOpenList(pThis, &pThis->PathList, pSplitFn, pszCacheSubDir, fFlags, szPath,
1033 pfnCallback, pvUser1, pvUser2);
1034 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
1035 rcRet = rc2;
1036
1037#ifdef RT_OS_WINDOWS
1038 if ( rc2 != VINF_CALLBACK_RETURN
1039 && rc2 != VERR_CALLBACK_RETURN
1040 && (fFlags & RTDBGCFG_O_EXECUTABLE_IMAGE)
1041 && !(pThis->fFlags & RTDBGCFG_FLAGS_NO_SYSTEM_PATHS) )
1042 {
1043 rc2 = rtDbgCfgTryOpenList(pThis, &pThis->NtExecutablePathList, pSplitFn, pszCacheSubDir, fFlags, szPath,
1044 pfnCallback, pvUser1, pvUser2);
1045 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
1046 rcRet = rc2;
1047 }
1048
1049 if ( rc2 != VINF_CALLBACK_RETURN
1050 && rc2 != VERR_CALLBACK_RETURN
1051 && !(pThis->fFlags & RTDBGCFG_FLAGS_NO_SYSTEM_PATHS) )
1052 {
1053 rc2 = rtDbgCfgTryOpenList(pThis, &pThis->NtSymbolPathList, pSplitFn, pszCacheSubDir, fFlags, szPath,
1054 pfnCallback, pvUser1, pvUser2);
1055 if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
1056 rcRet = rc2;
1057 }
1058#endif
1059 RTCritSectRwLeaveShared(&pThis->CritSect);
1060 }
1061 else if (RT_SUCCESS(rcRet))
1062 rcRet = rc2;
1063 }
1064 }
1065
1066 RTPathSplitFree(pSplitFn);
1067 if ( rc2 == VINF_CALLBACK_RETURN
1068 || rc2 == VERR_CALLBACK_RETURN)
1069 rcRet = rc2;
1070 else if (RT_SUCCESS(rcRet))
1071 rcRet = VERR_NOT_FOUND;
1072 return rcRet;
1073}
1074
1075
1076RTDECL(int) RTDbgCfgOpenPeImage(RTDBGCFG hDbgCfg, const char *pszFilename, uint32_t cbImage, uint32_t uTimestamp,
1077 PFNDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1078{
1079 char szSubDir[32];
1080 RTStrPrintf(szSubDir, sizeof(szSubDir), "%08X%x", uTimestamp, cbImage);
1081 return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir,
1082 RT_OPSYS_WINDOWS /* approx */ | RTDBGCFG_O_SYMSRV | RTDBGCFG_O_CASE_INSENSITIVE
1083 | RTDBGCFG_O_MAYBE_COMPRESSED_MS | RTDBGCFG_O_EXECUTABLE_IMAGE,
1084 pfnCallback, pvUser1, pvUser2);
1085}
1086
1087
1088RTDECL(int) RTDbgCfgOpenPdb70(RTDBGCFG hDbgCfg, const char *pszFilename, PCRTUUID pUuid, uint32_t uAge,
1089 PFNDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1090{
1091 char szSubDir[64];
1092 if (!pUuid)
1093 szSubDir[0] = '\0';
1094 else
1095 {
1096 /* Stringify the UUID and remove the dashes. */
1097 int rc2 = RTUuidToStr(pUuid, szSubDir, sizeof(szSubDir));
1098 AssertRCReturn(rc2, rc2);
1099
1100 char *pszSrc = szSubDir;
1101 char *pszDst = szSubDir;
1102 char ch;
1103 while ((ch = *pszSrc++))
1104 if (ch != '-')
1105 *pszDst++ = RT_C_TO_UPPER(ch);
1106
1107 RTStrPrintf(pszDst, &szSubDir[sizeof(szSubDir)] - pszDst, "%X", uAge);
1108 }
1109
1110 return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir,
1111 RT_OPSYS_WINDOWS /* approx */ | RTDBGCFG_O_SYMSRV | RTDBGCFG_O_CASE_INSENSITIVE
1112 | RTDBGCFG_O_MAYBE_COMPRESSED_MS | RTDBGCFG_O_EXT_DEBUG_FILE,
1113 pfnCallback, pvUser1, pvUser2);
1114}
1115
1116
1117RTDECL(int) RTDbgCfgOpenPdb20(RTDBGCFG hDbgCfg, const char *pszFilename, uint32_t cbImage, uint32_t uTimestamp, uint32_t uAge,
1118 PFNDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1119{
1120 /** @todo test this! */
1121 char szSubDir[32];
1122 RTStrPrintf(szSubDir, sizeof(szSubDir), "%08X%x", uTimestamp, uAge);
1123 return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir,
1124 RT_OPSYS_WINDOWS /* approx */ | RTDBGCFG_O_SYMSRV | RTDBGCFG_O_CASE_INSENSITIVE
1125 | RTDBGCFG_O_MAYBE_COMPRESSED_MS | RTDBGCFG_O_EXT_DEBUG_FILE,
1126 pfnCallback, pvUser1, pvUser2);
1127}
1128
1129
1130RTDECL(int) RTDbgCfgOpenDbg(RTDBGCFG hDbgCfg, const char *pszFilename, uint32_t cbImage, uint32_t uTimestamp,
1131 PFNDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1132{
1133 char szSubDir[32];
1134 RTStrPrintf(szSubDir, sizeof(szSubDir), "%08X%x", uTimestamp, cbImage);
1135 return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir,
1136 RT_OPSYS_WINDOWS /* approx */ | RTDBGCFG_O_SYMSRV | RTDBGCFG_O_CASE_INSENSITIVE
1137 | RTDBGCFG_O_MAYBE_COMPRESSED_MS | RTDBGCFG_O_EXT_DEBUG_FILE,
1138 pfnCallback, pvUser1, pvUser2);
1139}
1140
1141
1142RTDECL(int) RTDbgCfgOpenDwo(RTDBGCFG hDbgCfg, const char *pszFilename, uint32_t uCrc32,
1143 PFNDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
1144{
1145 char szSubDir[32];
1146 RTStrPrintf(szSubDir, sizeof(szSubDir), "%08x", uCrc32);
1147 return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir,
1148 RT_OPSYS_UNKNOWN | RTDBGCFG_O_EXT_DEBUG_FILE,
1149 pfnCallback, pvUser1, pvUser2);
1150}
1151
1152
1153RTDECL(int) RTDbgCfgSetLogCallback(RTDBGCFG hDbgCfg, PFNRTDBGCFGLOG pfnCallback, void *pvUser)
1154{
1155 PRTDBGCFGINT pThis = hDbgCfg;
1156 RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
1157 AssertPtrNullReturn(pfnCallback, VERR_INVALID_POINTER);
1158
1159 int rc = RTCritSectRwEnterExcl(&pThis->CritSect);
1160 if (RT_SUCCESS(rc))
1161 {
1162 if ( pThis->pfnLogCallback == NULL
1163 || pfnCallback == NULL
1164 || pfnCallback == pThis->pfnLogCallback)
1165 {
1166 pThis->pfnLogCallback = NULL;
1167 pThis->pvLogUser = NULL;
1168 ASMCompilerBarrier(); /* paranoia */
1169 pThis->pvLogUser = pvUser;
1170 pThis->pfnLogCallback = pfnCallback;
1171 rc = VINF_SUCCESS;
1172 }
1173 else
1174 rc = VERR_ACCESS_DENIED;
1175 RTCritSectRwLeaveExcl(&pThis->CritSect);
1176 }
1177
1178 return rc;
1179}
1180
1181
1182/**
1183 * Frees a string list.
1184 *
1185 * @param pList The list to free.
1186 */
1187static void rtDbgCfgFreeStrList(PRTLISTANCHOR pList)
1188{
1189 PRTDBGCFGSTR pCur;
1190 PRTDBGCFGSTR pNext;
1191 RTListForEachSafe(pList, pCur, pNext, RTDBGCFGSTR, ListEntry)
1192 {
1193 RTListNodeRemove(&pCur->ListEntry);
1194 RTMemFree(pCur);
1195 }
1196}
1197
1198
1199/**
1200 * Make changes to a string list, given a semicolon separated input string.
1201 *
1202 * @returns VINF_SUCCESS, VERR_FILENAME_TOO_LONG, VERR_NO_MEMORY
1203 * @param pThis The config instance.
1204 * @param enmOp The change operation.
1205 * @param pszValue The input strings separated by semicolon.
1206 * @param fPaths Indicates that this is a path list and that we
1207 * should look for srv and cache prefixes.
1208 * @param pList The string list anchor.
1209 */
1210static int rtDbgCfgChangeStringList(PRTDBGCFGINT pThis, RTDBGCFGOP enmOp, const char *pszValue, bool fPaths,
1211 PRTLISTANCHOR pList)
1212{
1213 if (enmOp == RTDBGCFGOP_SET)
1214 rtDbgCfgFreeStrList(pList);
1215
1216 while (*pszValue)
1217 {
1218 /* Skip separators. */
1219 while (*pszValue == ';')
1220 pszValue++;
1221 if (!*pszValue)
1222 break;
1223
1224 /* Find the end of this path. */
1225 const char *pchPath = pszValue++;
1226 char ch;
1227 while ((ch = *pszValue) && ch != ';')
1228 pszValue++;
1229 size_t cchPath = pszValue - pchPath;
1230 if (cchPath >= UINT16_MAX)
1231 return VERR_FILENAME_TOO_LONG;
1232
1233 if (enmOp == RTDBGCFGOP_REMOVE)
1234 {
1235 /*
1236 * Remove all occurences.
1237 */
1238 PRTDBGCFGSTR pCur;
1239 PRTDBGCFGSTR pNext;
1240 RTListForEachSafe(pList, pCur, pNext, RTDBGCFGSTR, ListEntry)
1241 {
1242 if ( pCur->cch == cchPath
1243 && !memcmp(pCur->sz, pchPath, cchPath))
1244 {
1245 RTListNodeRemove(&pCur->ListEntry);
1246 RTMemFree(pCur);
1247 }
1248 }
1249 }
1250 else
1251 {
1252 /*
1253 * We're adding a new one.
1254 */
1255 PRTDBGCFGSTR pNew = (PRTDBGCFGSTR)RTMemAlloc(RT_OFFSETOF(RTDBGCFGSTR, sz[cchPath + 1]));
1256 if (!pNew)
1257 return VERR_NO_MEMORY;
1258 pNew->cch = (uint16_t)cchPath;
1259 pNew->fFlags = 0;
1260 memcpy(pNew->sz, pchPath, cchPath);
1261 pNew->sz[cchPath] = '\0';
1262
1263 if (enmOp == RTDBGCFGOP_PREPEND)
1264 RTListPrepend(pList, &pNew->ListEntry);
1265 else
1266 RTListAppend(pList, &pNew->ListEntry);
1267 }
1268 }
1269
1270 return VINF_SUCCESS;
1271}
1272
1273
1274/**
1275 * Make changes to a 64-bit value
1276 *
1277 * @returns VINF_SUCCESS, VERR_DBG_CFG_INVALID_VALUE.
1278 * @param pThis The config instance.
1279 * @param enmOp The change operation.
1280 * @param pszValue The input value.
1281 * @param pszMnemonics The mnemonics map for this value.
1282 * @param puValue The value to change.
1283 */
1284static int rtDbgCfgChangeStringU64(PRTDBGCFGINT pThis, RTDBGCFGOP enmOp, const char *pszValue,
1285 PCRTDBGCFGU64MNEMONIC paMnemonics, uint64_t *puValue)
1286{
1287 uint64_t uNew = enmOp == RTDBGCFGOP_SET ? 0 : *puValue;
1288
1289 char ch;
1290 while ((ch = *pszValue))
1291 {
1292 /* skip whitespace and separators */
1293 while (RT_C_IS_SPACE(ch) || RT_C_IS_CNTRL(ch) || ch == ';' || ch == ':')
1294 ch = *++pszValue;
1295 if (!ch)
1296 break;
1297
1298 if (RT_C_IS_DIGIT(ch))
1299 {
1300 uint64_t uTmp;
1301 int rc = RTStrToUInt64Ex(pszValue, (char **)&pszValue, 0, &uTmp);
1302 if (RT_FAILURE(rc) || rc == VWRN_NUMBER_TOO_BIG)
1303 return VERR_DBG_CFG_INVALID_VALUE;
1304
1305 if (enmOp != RTDBGCFGOP_REMOVE)
1306 uNew |= uTmp;
1307 else
1308 uNew &= ~uTmp;
1309 }
1310 else
1311 {
1312 /* A mnemonic, find the end of it. */
1313 const char *pszMnemonic = pszValue - 1;
1314 do
1315 ch = *++pszValue;
1316 while (ch && !RT_C_IS_SPACE(ch) && !RT_C_IS_CNTRL(ch) && ch != ';' && ch != ':');
1317 size_t cchMnemonic = pszValue - pszMnemonic;
1318
1319 /* Look it up in the map and apply it. */
1320 unsigned i = 0;
1321 while (paMnemonics[i].pszMnemonic)
1322 {
1323 if ( cchMnemonic == paMnemonics[i].cchMnemonic
1324 && !memcmp(pszMnemonic, paMnemonics[i].pszMnemonic, cchMnemonic))
1325 {
1326 if (paMnemonics[i].fSet ? enmOp != RTDBGCFGOP_REMOVE : enmOp == RTDBGCFGOP_REMOVE)
1327 uNew |= paMnemonics[i].fFlags;
1328 else
1329 uNew &= ~paMnemonics[i].fFlags;
1330 break;
1331 }
1332 i++;
1333 }
1334
1335 if (!paMnemonics[i].pszMnemonic)
1336 return VERR_DBG_CFG_INVALID_VALUE;
1337 }
1338 }
1339
1340 *puValue = uNew;
1341 return VINF_SUCCESS;
1342}
1343
1344
1345RTDECL(int) RTDbgCfgChangeString(RTDBGCFG hDbgCfg, RTDBGCFGPROP enmProp, RTDBGCFGOP enmOp, const char *pszValue)
1346{
1347 PRTDBGCFGINT pThis = hDbgCfg;
1348 RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
1349 AssertReturn(enmProp > RTDBGCFGPROP_INVALID && enmProp < RTDBGCFGPROP_END, VERR_INVALID_PARAMETER);
1350 AssertReturn(enmOp > RTDBGCFGOP_INVALID && enmOp < RTDBGCFGOP_END, VERR_INVALID_PARAMETER);
1351 if (!pszValue)
1352 pszValue = "";
1353 else
1354 AssertPtrReturn(pszValue, VERR_INVALID_POINTER);
1355
1356 int rc = RTCritSectRwEnterExcl(&pThis->CritSect);
1357 if (RT_SUCCESS(rc))
1358 {
1359 switch (enmProp)
1360 {
1361 case RTDBGCFGPROP_FLAGS:
1362 rc = rtDbgCfgChangeStringU64(pThis, enmOp, pszValue, g_aDbgCfgFlags, &pThis->fFlags);
1363 break;
1364 case RTDBGCFGPROP_PATH:
1365 rc = rtDbgCfgChangeStringList(pThis, enmOp, pszValue, true, &pThis->PathList);
1366 break;
1367 case RTDBGCFGPROP_SUFFIXES:
1368 rc = rtDbgCfgChangeStringList(pThis, enmOp, pszValue, false, &pThis->SuffixList);
1369 break;
1370 case RTDBGCFGPROP_SRC_PATH:
1371 rc = rtDbgCfgChangeStringList(pThis, enmOp, pszValue, true, &pThis->SrcPathList);
1372 break;
1373 default:
1374 AssertFailed();
1375 rc = VERR_INTERNAL_ERROR_3;
1376 }
1377
1378 RTCritSectRwLeaveExcl(&pThis->CritSect);
1379 }
1380
1381 return rc;
1382}
1383
1384
1385RTDECL(int) RTDbgCfgChangeUInt(RTDBGCFG hDbgCfg, RTDBGCFGPROP enmProp, RTDBGCFGOP enmOp, uint64_t uValue)
1386{
1387 PRTDBGCFGINT pThis = hDbgCfg;
1388 RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
1389 AssertReturn(enmProp > RTDBGCFGPROP_INVALID && enmProp < RTDBGCFGPROP_END, VERR_INVALID_PARAMETER);
1390 AssertReturn(enmOp > RTDBGCFGOP_INVALID && enmOp < RTDBGCFGOP_END, VERR_INVALID_PARAMETER);
1391
1392 int rc = RTCritSectRwEnterExcl(&pThis->CritSect);
1393 if (RT_SUCCESS(rc))
1394 {
1395 uint64_t *puValue = NULL;
1396 switch (enmProp)
1397 {
1398 case RTDBGCFGPROP_FLAGS:
1399 puValue = &pThis->fFlags;
1400 break;
1401 default:
1402 rc = VERR_DBG_CFG_NOT_UINT_PROP;
1403 }
1404 if (RT_SUCCESS(rc))
1405 {
1406 switch (enmOp)
1407 {
1408 case RTDBGCFGOP_SET:
1409 *puValue = uValue;
1410 break;
1411 case RTDBGCFGOP_APPEND:
1412 case RTDBGCFGOP_PREPEND:
1413 *puValue |= uValue;
1414 break;
1415 case RTDBGCFGOP_REMOVE:
1416 *puValue &= ~uValue;
1417 break;
1418 default:
1419 AssertFailed();
1420 rc = VERR_INTERNAL_ERROR_2;
1421 }
1422 }
1423
1424 RTCritSectRwLeaveExcl(&pThis->CritSect);
1425 }
1426
1427 return rc;
1428}
1429
1430
1431/**
1432 * Querys a string list as a single string (semicolon separators).
1433 *
1434 * @returns VINF_SUCCESS, VERR_BUFFER_OVERFLOW.
1435 * @param pThis The config instance.
1436 * @param pList The string list anchor.
1437 * @param pszValue The output buffer.
1438 * @param cbValue The size of the output buffer.
1439 */
1440static int rtDbgCfgQueryStringList(RTDBGCFG hDbgCfg, PRTLISTANCHOR pList,
1441 char *pszValue, size_t cbValue)
1442{
1443 /*
1444 * Check the length first.
1445 */
1446 size_t cbReq = 1;
1447 PRTDBGCFGSTR pCur;
1448 RTListForEach(pList, pCur, RTDBGCFGSTR, ListEntry)
1449 cbReq += pCur->cch + 1;
1450 if (cbReq > cbValue)
1451 return VERR_BUFFER_OVERFLOW;
1452
1453 /*
1454 * Construct the string list in the buffer.
1455 */
1456 char *psz = pszValue;
1457 RTListForEach(pList, pCur, RTDBGCFGSTR, ListEntry)
1458 {
1459 if (psz != pszValue)
1460 *psz++ = ';';
1461 memcpy(psz, pCur->sz, pCur->cch);
1462 psz += pCur->cch;
1463 }
1464 *psz = '\0';
1465
1466 return VINF_SUCCESS;
1467}
1468
1469
1470/**
1471 * Querys the string value of a 64-bit unsigned int.
1472 *
1473 * @returns VINF_SUCCESS, VERR_BUFFER_OVERFLOW.
1474 * @param pThis The config instance.
1475 * @param uValue The value to query.
1476 * @param pszMnemonics The mnemonics map for this value.
1477 * @param pszValue The output buffer.
1478 * @param cbValue The size of the output buffer.
1479 */
1480static int rtDbgCfgQueryStringU64(RTDBGCFG hDbgCfg, uint64_t uValue, PCRTDBGCFGU64MNEMONIC paMnemonics,
1481 char *pszValue, size_t cbValue)
1482{
1483 /*
1484 * If no mnemonics, just return the hex value.
1485 */
1486 if (!paMnemonics || paMnemonics[0].pszMnemonic)
1487 {
1488 char szTmp[64];
1489 size_t cch = RTStrPrintf(szTmp, sizeof(szTmp), "%#x", uValue);
1490 if (cch + 1 > cbValue)
1491 return VERR_BUFFER_OVERFLOW;
1492 memcpy(pszValue, szTmp, cbValue);
1493 return VINF_SUCCESS;
1494 }
1495
1496 /*
1497 * Check that there is sufficient buffer space first.
1498 */
1499 size_t cbReq = 1;
1500 for (unsigned i = 0; paMnemonics[i].pszMnemonic; i++)
1501 if ( paMnemonics[i].fSet
1502 ? (paMnemonics[i].fFlags & uValue)
1503 : !(paMnemonics[i].fFlags & uValue))
1504 cbReq += (cbReq != 1) + paMnemonics[i].cchMnemonic;
1505 if (cbReq > cbValue)
1506 return VERR_BUFFER_OVERFLOW;
1507
1508 /*
1509 * Construct the string.
1510 */
1511 char *psz = pszValue;
1512 for (unsigned i = 0; paMnemonics[i].pszMnemonic; i++)
1513 if ( paMnemonics[i].fSet
1514 ? (paMnemonics[i].fFlags & uValue)
1515 : !(paMnemonics[i].fFlags & uValue))
1516 {
1517 if (psz != pszValue)
1518 *psz++ = ' ';
1519 memcpy(psz, paMnemonics[i].pszMnemonic, paMnemonics[i].cchMnemonic);
1520 psz += paMnemonics[i].cchMnemonic;
1521 }
1522 *psz = '\0';
1523 return VINF_SUCCESS;
1524}
1525
1526
1527RTDECL(int) RTDbgCfgQueryString(RTDBGCFG hDbgCfg, RTDBGCFGPROP enmProp, char *pszValue, size_t cbValue)
1528{
1529 PRTDBGCFGINT pThis = hDbgCfg;
1530 RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
1531 AssertReturn(enmProp > RTDBGCFGPROP_INVALID && enmProp < RTDBGCFGPROP_END, VERR_INVALID_PARAMETER);
1532 AssertPtrReturn(pszValue, VERR_INVALID_POINTER);
1533
1534 int rc = RTCritSectRwEnterShared(&pThis->CritSect);
1535 if (RT_SUCCESS(rc))
1536 {
1537 switch (enmProp)
1538 {
1539 case RTDBGCFGPROP_FLAGS:
1540 rc = rtDbgCfgQueryStringU64(pThis, pThis->fFlags, g_aDbgCfgFlags, pszValue, cbValue);
1541 break;
1542 case RTDBGCFGPROP_PATH:
1543 rc = rtDbgCfgQueryStringList(pThis, &pThis->PathList, pszValue, cbValue);
1544 break;
1545 case RTDBGCFGPROP_SUFFIXES:
1546 rc = rtDbgCfgQueryStringList(pThis, &pThis->SuffixList, pszValue, cbValue);
1547 break;
1548 case RTDBGCFGPROP_SRC_PATH:
1549 rc = rtDbgCfgQueryStringList(pThis, &pThis->SrcPathList, pszValue, cbValue);
1550 break;
1551 default:
1552 AssertFailed();
1553 rc = VERR_INTERNAL_ERROR_3;
1554 }
1555
1556 RTCritSectRwLeaveShared(&pThis->CritSect);
1557 }
1558
1559 return rc;
1560}
1561
1562
1563RTDECL(int) RTDbgCfgQueryUInt(RTDBGCFG hDbgCfg, RTDBGCFGPROP enmProp, uint64_t *puValue)
1564{
1565 PRTDBGCFGINT pThis = hDbgCfg;
1566 RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
1567 AssertReturn(enmProp > RTDBGCFGPROP_INVALID && enmProp < RTDBGCFGPROP_END, VERR_INVALID_PARAMETER);
1568 AssertPtrReturn(puValue, VERR_INVALID_POINTER);
1569
1570 int rc = RTCritSectRwEnterShared(&pThis->CritSect);
1571 if (RT_SUCCESS(rc))
1572 {
1573 switch (enmProp)
1574 {
1575 case RTDBGCFGPROP_FLAGS:
1576 *puValue = pThis->fFlags;
1577 break;
1578 default:
1579 rc = VERR_DBG_CFG_NOT_UINT_PROP;
1580 }
1581
1582 RTCritSectRwLeaveShared(&pThis->CritSect);
1583 }
1584
1585 return rc;
1586}
1587
1588RTDECL(uint32_t) RTDbgCfgRetain(RTDBGCFG hDbgCfg)
1589{
1590 PRTDBGCFGINT pThis = hDbgCfg;
1591 RTDBGCFG_VALID_RETURN_RC(pThis, UINT32_MAX);
1592
1593 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
1594 Assert(cRefs < UINT32_MAX / 2);
1595 return cRefs;
1596}
1597
1598
1599RTDECL(uint32_t) RTDbgCfgRelease(RTDBGCFG hDbgCfg)
1600{
1601 if (hDbgCfg == NIL_RTDBGCFG)
1602 return 0;
1603
1604 PRTDBGCFGINT pThis = hDbgCfg;
1605 RTDBGCFG_VALID_RETURN_RC(pThis, UINT32_MAX);
1606
1607 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
1608 if (!cRefs)
1609 {
1610 /*
1611 * Last reference - free all memory.
1612 */
1613 ASMAtomicWriteU32(&pThis->u32Magic, ~RTDBGCFG_MAGIC);
1614 rtDbgCfgFreeStrList(&pThis->PathList);
1615 rtDbgCfgFreeStrList(&pThis->SuffixList);
1616 rtDbgCfgFreeStrList(&pThis->SrcPathList);
1617#ifdef RT_OS_WINDOWS
1618 rtDbgCfgFreeStrList(&pThis->NtSymbolPathList);
1619 rtDbgCfgFreeStrList(&pThis->NtExecutablePathList);
1620 rtDbgCfgFreeStrList(&pThis->NtSourcePath);
1621#endif
1622 RTCritSectRwDelete(&pThis->CritSect);
1623 RTMemFree(pThis);
1624 }
1625 else
1626 Assert(cRefs < UINT32_MAX / 2);
1627 return cRefs;
1628}
1629
1630
1631RTDECL(int) RTDbgCfgCreate(PRTDBGCFG phDbgCfg, const char *pszEnvVarPrefix, bool fNativePaths)
1632{
1633 /*
1634 * Validate input.
1635 */
1636 AssertPtrReturn(phDbgCfg, VERR_INVALID_POINTER);
1637 if (pszEnvVarPrefix)
1638 {
1639 AssertPtrReturn(pszEnvVarPrefix, VERR_INVALID_POINTER);
1640 AssertReturn(*pszEnvVarPrefix, VERR_INVALID_PARAMETER);
1641 }
1642
1643 /*
1644 * Allocate and initialize a new instance.
1645 */
1646 PRTDBGCFGINT pThis = (PRTDBGCFGINT)RTMemAllocZ(sizeof(*pThis));
1647 if (!pThis)
1648 return VERR_NO_MEMORY;
1649
1650 pThis->u32Magic = RTDBGCFG_MAGIC;
1651 pThis->cRefs = 1;
1652 RTListInit(&pThis->PathList);
1653 RTListInit(&pThis->SuffixList);
1654 RTListInit(&pThis->SrcPathList);
1655#ifdef RT_OS_WINDOWS
1656 RTListInit(&pThis->NtSymbolPathList);
1657 RTListInit(&pThis->NtExecutablePathList);
1658 RTListInit(&pThis->NtSourcePath);
1659#endif
1660
1661 int rc = RTCritSectRwInit(&pThis->CritSect);
1662 if (RT_FAILURE(rc))
1663 {
1664 RTMemFree(pThis);
1665 return rc;
1666 }
1667
1668 /*
1669 * Read configurtion from the environment if requested to do so.
1670 */
1671 if (pszEnvVarPrefix || fNativePaths)
1672 {
1673 const size_t cbEnvVar = 256;
1674 const size_t cbEnvVal = 65536 - cbEnvVar;
1675 char *pszEnvVar = (char *)RTMemTmpAlloc(cbEnvVar + cbEnvVal);
1676 if (pszEnvVar)
1677 {
1678 char *pszEnvVal = pszEnvVar + cbEnvVar;
1679
1680 if (pszEnvVarPrefix)
1681 {
1682 static struct
1683 {
1684 RTDBGCFGPROP enmProp;
1685 const char *pszVar;
1686 } const s_aProps[] =
1687 {
1688 { RTDBGCFGPROP_FLAGS, "FLAGS" },
1689 { RTDBGCFGPROP_PATH, "PATH" },
1690 { RTDBGCFGPROP_SUFFIXES, "SUFFIXES" },
1691 { RTDBGCFGPROP_SRC_PATH, "SRC_PATH" },
1692 };
1693
1694 for (unsigned i = 0; i < RT_ELEMENTS(s_aProps); i++)
1695 {
1696 size_t cchEnvVar = RTStrPrintf(pszEnvVar, cbEnvVar, "%s_%s", pszEnvVarPrefix, s_aProps[i].pszVar);
1697 if (cchEnvVar >= cbEnvVar - 1)
1698 {
1699 rc = VERR_BUFFER_OVERFLOW;
1700 break;
1701 }
1702
1703 rc = RTEnvGetEx(RTENV_DEFAULT, pszEnvVar, pszEnvVal, cbEnvVal, NULL);
1704 if (RT_SUCCESS(rc))
1705 {
1706 rc = RTDbgCfgChangeString(pThis, s_aProps[i].enmProp, RTDBGCFGOP_SET, pszEnvVal);
1707 if (RT_FAILURE(rc))
1708 break;
1709 }
1710 else if (rc != VERR_ENV_VAR_NOT_FOUND)
1711 break;
1712 else
1713 rc = VINF_SUCCESS;
1714 }
1715 }
1716
1717 /*
1718 * Pick up system specific search paths.
1719 */
1720 if (RT_SUCCESS(rc) && fNativePaths)
1721 {
1722 struct
1723 {
1724 PRTLISTANCHOR pList;
1725 const char *pszVar;
1726 char chSep;
1727 } aNativePaths[] =
1728 {
1729#ifdef RT_OS_WINDOWS
1730 { &pThis->NtExecutablePathList, "_NT_EXECUTABLE_PATH", ';' },
1731 { &pThis->NtSymbolPathList, "_NT_ALT_SYMBOL_PATH", ';' },
1732 { &pThis->NtSymbolPathList, "_NT_SYMBOL_PATH", ';' },
1733 { &pThis->NtSourcePath, "_NT_SOURCE_PATH", ';' },
1734#endif
1735 { NULL, NULL, 0 }
1736 };
1737 for (unsigned i = 0; aNativePaths[i].pList; i++)
1738 {
1739 Assert(aNativePaths[i].chSep == ';'); /* fix when needed */
1740 rc = RTEnvGetEx(RTENV_DEFAULT, aNativePaths[i].pszVar, pszEnvVal, cbEnvVal, NULL);
1741 if (RT_SUCCESS(rc))
1742 {
1743 rc = rtDbgCfgChangeStringList(pThis, RTDBGCFGOP_APPEND, pszEnvVal, true, aNativePaths[i].pList);
1744 if (RT_FAILURE(rc))
1745 break;
1746 }
1747 else if (rc != VERR_ENV_VAR_NOT_FOUND)
1748 break;
1749 else
1750 rc = VINF_SUCCESS;
1751 }
1752 }
1753 RTMemTmpFree(pszEnvVar);
1754 }
1755 else
1756 rc = VERR_NO_TMP_MEMORY;
1757 if (RT_FAILURE(rc))
1758 {
1759 /*
1760 * Error, bail out.
1761 */
1762 RTDbgCfgRelease(pThis);
1763 return rc;
1764 }
1765 }
1766
1767 /*
1768 * Returns successfully.
1769 */
1770 *phDbgCfg = pThis;
1771
1772 return VINF_SUCCESS;
1773}
1774
Note: See TracBrowser for help on using the repository browser.

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