VirtualBox

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

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use