Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * main.c
4 : : * Stub main() routine for the postgres executable.
5 : : *
6 : : * This does some essential startup tasks for any incarnation of postgres
7 : : * (postmaster, standalone backend, standalone bootstrap process, or a
8 : : * separately exec'd child of a postmaster) and then dispatches to the
9 : : * proper FooMain() routine for the incarnation.
10 : : *
11 : : *
12 : : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
13 : : * Portions Copyright (c) 1994, Regents of the University of California
14 : : *
15 : : *
16 : : * IDENTIFICATION
17 : : * src/backend/main/main.c
18 : : *
19 : : *-------------------------------------------------------------------------
20 : : */
21 : : #include "postgres.h"
22 : :
23 : : #include <unistd.h>
24 : :
25 : : #if defined(WIN32)
26 : : #include <crtdbg.h>
27 : : #endif
28 : :
29 : : #if defined(__NetBSD__)
30 : : #include <sys/param.h>
31 : : #endif
32 : :
33 : : #include "bootstrap/bootstrap.h"
34 : : #include "common/username.h"
35 : : #include "miscadmin.h"
36 : : #include "postmaster/postmaster.h"
37 : : #include "tcop/tcopprot.h"
38 : : #include "utils/help_config.h"
39 : : #include "utils/memutils.h"
40 : : #include "utils/pg_locale.h"
41 : : #include "utils/ps_status.h"
42 : :
43 : :
44 : : const char *progname;
45 : : static bool reached_main = false;
46 : :
47 : : /* names of special must-be-first options for dispatching to subprograms */
48 : : static const char *const DispatchOptionNames[] =
49 : : {
50 : : [DISPATCH_CHECK] = "check",
51 : : [DISPATCH_BOOT] = "boot",
52 : : [DISPATCH_FORKCHILD] = "forkchild",
53 : : [DISPATCH_DESCRIBE_CONFIG] = "describe-config",
54 : : [DISPATCH_SINGLE] = "single",
55 : : /* DISPATCH_POSTMASTER has no name */
56 : : };
57 : :
58 : : StaticAssertDecl(lengthof(DispatchOptionNames) == DISPATCH_POSTMASTER,
59 : : "array length mismatch");
60 : :
61 : : static void startup_hacks(const char *progname);
62 : : static void init_locale(const char *categoryname, int category, const char *locale);
63 : : static void help(const char *progname);
64 : : static void check_root(const char *progname);
65 : :
66 : :
67 : : /*
68 : : * Any Postgres server process begins execution here.
69 : : */
70 : : int
7020 peter_e@gmx.net 71 :CBC 1807 : main(int argc, char *argv[])
72 : : {
4173 tgl@sss.pgh.pa.us 73 : 1807 : bool do_check_root = true;
276 nathan@postgresql.or 74 : 1807 : DispatchOption dispatch_option = DISPATCH_POSTMASTER;
75 : :
1020 andres@anarazel.de 76 : 1807 : reached_main = true;
77 : :
78 : : /*
79 : : * If supported on the current platform, set up a handler to be called if
80 : : * the backend/postmaster crashes with a fatal signal or exception.
81 : : */
82 : : #if defined(WIN32)
83 : : pgwin32_install_crashdump_handler();
84 : : #endif
85 : :
7157 bruce@momjian.us 86 : 1807 : progname = get_progname(argv[0]);
87 : :
88 : : /*
89 : : * Platform-specific startup hacks
90 : : */
7020 peter_e@gmx.net 91 : 1807 : startup_hacks(progname);
92 : :
93 : : /*
94 : : * Remember the physical location of the initially given argv[] array for
95 : : * possible use by ps display. On some platforms, the argv[] storage must
96 : : * be overwritten in order to set the process title for ps. In such cases
97 : : * save_ps_display_args makes and returns a new copy of the argv[] array.
98 : : *
99 : : * save_ps_display_args may also move the environment strings to make
100 : : * extra room. Therefore this should be done as early as possible during
101 : : * startup, to avoid entanglements with code that might save a getenv()
102 : : * result pointer.
103 : : */
7867 tgl@sss.pgh.pa.us 104 : 1807 : argv = save_ps_display_args(argc, argv);
105 : :
106 : : /*
107 : : * Fire up essential subsystems: error and memory management
108 : : *
109 : : * Code after this point is allowed to use elog/ereport, though
110 : : * localization of messages may not work right away, and messages won't go
111 : : * anywhere but stderr until GUC settings get loaded.
112 : : */
270 noah@leadboat.com 113 : 1807 : MyProcPid = getpid();
4256 tgl@sss.pgh.pa.us 114 : 1807 : MemoryContextInit();
115 : :
116 : : /*
117 : : * Set reference point for stack-depth checking. (There's no point in
118 : : * enabling this before error reporting works.)
119 : : */
263 120 : 1807 : (void) set_stack_base();
121 : :
122 : : /*
123 : : * Set up locale information
124 : : */
6113 peter_e@gmx.net 125 : 1807 : set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("postgres"));
126 : :
127 : : /*
128 : : * Collation is handled by pg_locale.c, and the behavior is dependent on
129 : : * the provider. strcoll(), etc., should not be called directly.
130 : : */
52 jdavis@postgresql.or 131 :GNC 1807 : init_locale("LC_COLLATE", LC_COLLATE, "C");
132 : :
133 : : /*
134 : : * In the postmaster, absorb the environment value for LC_CTYPE.
135 : : * Individual backends will change it later to pg_database.datctype, but
136 : : * the postmaster cannot do that. If we leave it set to "C" then message
137 : : * localization might not work well in the postmaster.
138 : : */
3742 tgl@sss.pgh.pa.us 139 :CBC 1807 : init_locale("LC_CTYPE", LC_CTYPE, "");
140 : :
141 : : /*
142 : : * LC_MESSAGES will get set later during GUC option processing, but we set
143 : : * it here to allow startup error messages to be localized.
144 : : */
145 : : #ifdef LC_MESSAGES
146 : 1807 : init_locale("LC_MESSAGES", LC_MESSAGES, "");
147 : : #endif
148 : :
149 : : /* We keep these set to "C" always. See pg_locale.c for explanation. */
150 : 1807 : init_locale("LC_MONETARY", LC_MONETARY, "C");
151 : 1807 : init_locale("LC_NUMERIC", LC_NUMERIC, "C");
152 : 1807 : init_locale("LC_TIME", LC_TIME, "C");
153 : :
154 : : /*
155 : : * Now that we have absorbed as much as we wish to from the locale
156 : : * environment, remove any LC_ALL setting, so that the environment
157 : : * variables installed by pg_perm_setlocale have force.
158 : : */
7192 159 : 1807 : unsetenv("LC_ALL");
160 : :
161 : : /*
162 : : * Catch standard options before doing much else, in particular before we
163 : : * insist on not being root.
164 : : */
7020 peter_e@gmx.net 165 [ + - ]: 1807 : if (argc > 1)
166 : : {
167 [ + + - + ]: 1807 : if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
168 : : {
169 : 1 : help(progname);
170 : 1 : exit(0);
171 : : }
172 [ + + + + ]: 1806 : if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
173 : : {
2992 tgl@sss.pgh.pa.us 174 : 739 : fputs(PG_BACKEND_VERSIONSTR, stdout);
7020 peter_e@gmx.net 175 : 739 : exit(0);
176 : : }
177 : :
178 : : /*
179 : : * In addition to the above, we allow "--describe-config" and "-C var"
180 : : * to be called by root. This is reasonably safe since these are
181 : : * read-only activities. The -C case is important because pg_ctl may
182 : : * try to invoke it while still holding administrator privileges on
183 : : * Windows. Note that while -C can normally be in any argv position,
184 : : * if you want to bypass the root check you must put it first. This
185 : : * reduces the risk that we might misinterpret some other mode's -C
186 : : * switch as being the postmaster/postgres one.
187 : : */
4173 tgl@sss.pgh.pa.us 188 [ - + ]: 1067 : if (strcmp(argv[1], "--describe-config") == 0)
4173 tgl@sss.pgh.pa.us 189 :UBC 0 : do_check_root = false;
4173 tgl@sss.pgh.pa.us 190 [ + + + + ]:CBC 1067 : else if (argc > 2 && strcmp(argv[1], "-C") == 0)
191 : 1 : do_check_root = false;
192 : : }
193 : :
194 : : /*
195 : : * Make sure we are not running as root, unless it's safe for the selected
196 : : * option.
197 : : */
198 [ + + ]: 1067 : if (do_check_root)
199 : 1066 : check_root(progname);
200 : :
201 : : /*
202 : : * Dispatch to one of various subprograms depending on first argument.
203 : : */
204 : :
276 nathan@postgresql.or 205 [ + - + - : 1067 : if (argc > 1 && argv[1][0] == '-' && argv[1][1] == '-')
+ + ]
206 : 247 : dispatch_option = parse_dispatch_option(&argv[1][2]);
207 : :
208 [ + + - - : 1067 : switch (dispatch_option)
+ + - ]
209 : : {
210 : 124 : case DISPATCH_CHECK:
211 : 124 : BootstrapModeMain(argc, argv, true);
212 : : break;
213 : 51 : case DISPATCH_BOOT:
214 : 51 : BootstrapModeMain(argc, argv, false);
215 : : break;
276 nathan@postgresql.or 216 :UBC 0 : case DISPATCH_FORKCHILD:
217 : : #ifdef EXEC_BACKEND
218 : : SubPostmasterMain(argc, argv);
219 : : #else
220 : 0 : Assert(false); /* should never happen */
221 : : #endif
222 : : break;
223 : 0 : case DISPATCH_DESCRIBE_CONFIG:
224 : 0 : GucInfoMain();
225 : : break;
276 nathan@postgresql.or 226 :CBC 71 : case DISPATCH_SINGLE:
227 : 71 : PostgresSingleUserMain(argc, argv,
228 : 71 : strdup(get_user_name_or_exit(progname)));
229 : : break;
230 : 821 : case DISPATCH_POSTMASTER:
231 : 821 : PostmasterMain(argc, argv);
232 : : break;
233 : : }
234 : :
235 : : /* the functions above should not return */
1493 andres@anarazel.de 236 :UBC 0 : abort();
237 : : }
238 : :
239 : : /*
240 : : * Returns the matching DispatchOption value for the given option name. If no
241 : : * match is found, DISPATCH_POSTMASTER is returned.
242 : : */
243 : : DispatchOption
276 nathan@postgresql.or 244 :CBC 970 : parse_dispatch_option(const char *name)
245 : : {
246 [ + + ]: 4925 : for (int i = 0; i < lengthof(DispatchOptionNames); i++)
247 : : {
248 : : /*
249 : : * Unlike the other dispatch options, "forkchild" takes an argument,
250 : : * so we just look for the prefix for that one. For non-EXEC_BACKEND
251 : : * builds, we never want to return DISPATCH_FORKCHILD, so skip over it
252 : : * in that case.
253 : : */
254 [ + + ]: 4201 : if (i == DISPATCH_FORKCHILD)
255 : : {
256 : : #ifdef EXEC_BACKEND
257 : : if (strncmp(DispatchOptionNames[DISPATCH_FORKCHILD], name,
258 : : strlen(DispatchOptionNames[DISPATCH_FORKCHILD])) == 0)
259 : : return DISPATCH_FORKCHILD;
260 : : #endif
261 : 795 : continue;
262 : : }
263 : :
264 [ + + ]: 3406 : if (strcmp(DispatchOptionNames[i], name) == 0)
265 : 246 : return (DispatchOption) i;
266 : : }
267 : :
268 : : /* no match means this is a postmaster */
269 : 724 : return DISPATCH_POSTMASTER;
270 : : }
271 : :
272 : : /*
273 : : * Place platform-specific startup hacks here. This is the right
274 : : * place to put code that must be executed early in the launch of any new
275 : : * server process. Note that this code will NOT be executed when a backend
276 : : * or sub-bootstrap process is forked, unless we are in a fork/exec
277 : : * environment (ie EXEC_BACKEND is defined).
278 : : *
279 : : * XXX The need for code here is proof that the platform in question
280 : : * is too brain-dead to provide a standard C execution environment
281 : : * without help. Avoid adding more here, if you can.
282 : : */
283 : : static void
7020 peter_e@gmx.net 284 : 1807 : startup_hacks(const char *progname)
285 : : {
286 : : /*
287 : : * Windows-specific execution environment hacking.
288 : : */
289 : : #ifdef WIN32
290 : : {
291 : : WSADATA wsaData;
292 : : int err;
293 : :
294 : : /* Make output streams unbuffered by default */
295 : : setvbuf(stdout, NULL, _IONBF, 0);
296 : : setvbuf(stderr, NULL, _IONBF, 0);
297 : :
298 : : /* Prepare Winsock */
299 : : err = WSAStartup(MAKEWORD(2, 2), &wsaData);
300 : : if (err != 0)
301 : : {
302 : : write_stderr("%s: WSAStartup failed: %d\n",
303 : : progname, err);
304 : : exit(1);
305 : : }
306 : :
307 : : /*
308 : : * By default abort() only generates a crash-dump in *non* debug
309 : : * builds. As our Assert() / ExceptionalCondition() uses abort(),
310 : : * leaving the default in place would make debugging harder.
311 : : *
312 : : * MINGW's own C runtime doesn't have _set_abort_behavior(). When
313 : : * targeting Microsoft's UCRT with mingw, it never links to the debug
314 : : * version of the library and thus doesn't need the call to
315 : : * _set_abort_behavior() either.
316 : : */
317 : : #if !defined(__MINGW32__) && !defined(__MINGW64__)
318 : : _set_abort_behavior(_CALL_REPORTFAULT | _WRITE_ABORT_MSG,
319 : : _CALL_REPORTFAULT | _WRITE_ABORT_MSG);
320 : : #endif /* !defined(__MINGW32__) &&
321 : : * !defined(__MINGW64__) */
322 : :
323 : : /*
324 : : * SEM_FAILCRITICALERRORS causes more errors to be reported to
325 : : * callers.
326 : : *
327 : : * We used to also specify SEM_NOGPFAULTERRORBOX, but that prevents
328 : : * windows crash reporting from working. Which includes registered
329 : : * just-in-time debuggers, making it unnecessarily hard to debug
330 : : * problems on windows. Now we try to disable sources of popups
331 : : * separately below (note that SEM_NOGPFAULTERRORBOX did not actually
332 : : * prevent all sources of such popups).
333 : : */
334 : : SetErrorMode(SEM_FAILCRITICALERRORS);
335 : :
336 : : /*
337 : : * Show errors on stderr instead of popup box (note this doesn't
338 : : * affect errors originating in the C runtime, see below).
339 : : */
340 : : _set_error_mode(_OUT_TO_STDERR);
341 : :
342 : : /*
343 : : * In DEBUG builds, errors, including assertions, C runtime errors are
344 : : * reported via _CrtDbgReport. By default such errors are displayed
345 : : * with a popup (even with NOGPFAULTERRORBOX), preventing forward
346 : : * progress. Instead report such errors stderr (and the debugger).
347 : : * This is C runtime specific and thus the above incantations aren't
348 : : * sufficient to suppress these popups.
349 : : */
350 : : _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
351 : : _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
352 : : _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
353 : : _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
354 : : _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
355 : : _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
356 : : }
357 : : #endif /* WIN32 */
358 : 1807 : }
359 : :
360 : :
361 : : /*
362 : : * Make the initial permanent setting for a locale category. If that fails,
363 : : * perhaps due to LC_foo=invalid in the environment, use locale C. If even
364 : : * that fails, perhaps due to out-of-memory, the entire startup fails with it.
365 : : * When this returns, we are guaranteed to have a setting for the given
366 : : * category's environment variable.
367 : : */
368 : : static void
3742 tgl@sss.pgh.pa.us 369 : 10842 : init_locale(const char *categoryname, int category, const char *locale)
370 : : {
3895 noah@leadboat.com 371 [ - + - - ]: 10842 : if (pg_perm_setlocale(category, locale) == NULL &&
3895 noah@leadboat.com 372 :UBC 0 : pg_perm_setlocale(category, "C") == NULL)
3742 tgl@sss.pgh.pa.us 373 [ # # ]: 0 : elog(FATAL, "could not adopt \"%s\" locale nor C locale for %s",
374 : : locale, categoryname);
3895 noah@leadboat.com 375 :CBC 10842 : }
376 : :
377 : :
378 : :
379 : : /*
380 : : * Help display should match the options accepted by PostmasterMain()
381 : : * and PostgresMain().
382 : : *
383 : : * XXX On Windows, non-ASCII localizations of these messages only display
384 : : * correctly if the console output code page covers the necessary characters.
385 : : * Messages emitted in write_console() do not exhibit this problem.
386 : : */
387 : : static void
7020 peter_e@gmx.net 388 : 1 : help(const char *progname)
389 : : {
390 : 1 : printf(_("%s is the PostgreSQL server.\n\n"), progname);
391 : 1 : printf(_("Usage:\n %s [OPTION]...\n\n"), progname);
392 : 1 : printf(_("Options:\n"));
4859 393 : 1 : printf(_(" -B NBUFFERS number of shared buffers\n"));
394 : 1 : printf(_(" -c NAME=VALUE set run-time parameter\n"));
4833 395 : 1 : printf(_(" -C NAME print value of run-time parameter, then exit\n"));
4859 396 : 1 : printf(_(" -d 1-5 debugging level\n"));
397 : 1 : printf(_(" -D DATADIR database directory\n"));
398 : 1 : printf(_(" -e use European date input format (DMY)\n"));
399 : 1 : printf(_(" -F turn fsync off\n"));
400 : 1 : printf(_(" -h HOSTNAME host name or IP address to listen on\n"));
919 dgustafsson@postgres 401 : 1 : printf(_(" -i enable TCP/IP connections (deprecated)\n"));
4859 peter_e@gmx.net 402 : 1 : printf(_(" -k DIRECTORY Unix-domain socket location\n"));
403 : : #ifdef USE_SSL
404 : 1 : printf(_(" -l enable SSL connections\n"));
405 : : #endif
406 : 1 : printf(_(" -N MAX-CONNECT maximum number of allowed connections\n"));
407 : 1 : printf(_(" -p PORT port number to listen on\n"));
408 : 1 : printf(_(" -s show statistics after each query\n"));
409 : 1 : printf(_(" -S WORK-MEM set amount of memory for sorts (in kB)\n"));
4828 410 : 1 : printf(_(" -V, --version output version information, then exit\n"));
4859 411 : 1 : printf(_(" --NAME=VALUE set run-time parameter\n"));
7020 412 : 1 : printf(_(" --describe-config describe configuration parameters, then exit\n"));
4828 413 : 1 : printf(_(" -?, --help show this help, then exit\n"));
414 : :
7020 415 : 1 : printf(_("\nDeveloper options:\n"));
1118 michael@paquier.xyz 416 : 1 : printf(_(" -f s|i|o|b|t|n|m|h forbid use of some plan types\n"));
4859 peter_e@gmx.net 417 : 1 : printf(_(" -O allow system table structure changes\n"));
418 : 1 : printf(_(" -P disable system indexes\n"));
419 : 1 : printf(_(" -t pa|pl|ex show timings after each query\n"));
1020 tgl@sss.pgh.pa.us 420 : 1 : printf(_(" -T send SIGABRT to all backend processes if one dies\n"));
4859 peter_e@gmx.net 421 : 1 : printf(_(" -W NUM wait NUM seconds to allow attach from a debugger\n"));
422 : :
7020 423 : 1 : printf(_("\nOptions for single-user mode:\n"));
4859 424 : 1 : printf(_(" --single selects single-user mode (must be first argument)\n"));
425 : 1 : printf(_(" DBNAME database name (defaults to user name)\n"));
426 : 1 : printf(_(" -d 0-5 override debugging level\n"));
427 : 1 : printf(_(" -E echo statement before execution\n"));
428 : 1 : printf(_(" -j do not use newline as interactive query delimiter\n"));
429 : 1 : printf(_(" -r FILENAME send stdout and stderr to given file\n"));
430 : :
7020 431 : 1 : printf(_("\nOptions for bootstrapping mode:\n"));
4859 432 : 1 : printf(_(" --boot selects bootstrapping mode (must be first argument)\n"));
1493 andres@anarazel.de 433 : 1 : printf(_(" --check selects check mode (must be first argument)\n"));
4859 peter_e@gmx.net 434 : 1 : printf(_(" DBNAME database name (mandatory argument in bootstrapping mode)\n"));
435 : 1 : printf(_(" -r FILENAME send stdout and stderr to given file\n"));
436 : :
7020 437 : 1 : printf(_("\nPlease read the documentation for the complete list of run-time\n"
438 : : "configuration settings and how to set them on the command line or in\n"
439 : : "the configuration file.\n\n"
440 : : "Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
2017 peter@eisentraut.org 441 : 1 : printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
7020 peter_e@gmx.net 442 : 1 : }
443 : :
444 : :
445 : :
446 : : static void
447 : 1066 : check_root(const char *progname)
448 : : {
449 : : #ifndef WIN32
450 [ - + ]: 1066 : if (geteuid() == 0)
451 : : {
7020 peter_e@gmx.net 452 :UBC 0 : write_stderr("\"root\" execution of the PostgreSQL server is not permitted.\n"
453 : : "The server must be started under an unprivileged user ID to prevent\n"
454 : : "possible system security compromise. See the documentation for\n"
455 : : "more information on how to properly start the server.\n");
456 : 0 : exit(1);
457 : : }
458 : :
459 : : /*
460 : : * Also make sure that real and effective uids are the same. Executing as
461 : : * a setuid program from a root shell is a security hole, since on many
462 : : * platforms a nefarious subroutine could setuid back to root if real uid
463 : : * is root. (Since nobody actually uses postgres as a setuid program,
464 : : * trying to actively fix this situation seems more trouble than it's
465 : : * worth; we'll just expend the effort to check for it.)
466 : : */
7020 peter_e@gmx.net 467 [ - + ]:CBC 1066 : if (getuid() != geteuid())
468 : : {
7020 peter_e@gmx.net 469 :UBC 0 : write_stderr("%s: real and effective user IDs must match\n",
470 : : progname);
471 : 0 : exit(1);
472 : : }
473 : : #else /* WIN32 */
474 : : if (pgwin32_is_admin())
475 : : {
476 : : write_stderr("Execution of PostgreSQL by a user with administrative permissions is not\n"
477 : : "permitted.\n"
478 : : "The server must be started under an unprivileged user ID to prevent\n"
479 : : "possible system security compromises. See the documentation for\n"
480 : : "more information on how to properly start the server.\n");
481 : : exit(1);
482 : : }
483 : : #endif /* WIN32 */
7020 peter_e@gmx.net 484 :CBC 1066 : }
485 : :
486 : : /*
487 : : * At least on linux, set_ps_display() breaks /proc/$pid/environ. The
488 : : * sanitizer library uses /proc/$pid/environ to implement getenv() as it wants
489 : : * to work independent of libc. When just using undefined and alignment
490 : : * sanitizers, the sanitizer library is only initialized when the first error
491 : : * occurs, by which time we've often already called set_ps_display(),
492 : : * preventing the sanitizer libraries from seeing the options.
493 : : *
494 : : * We can work around that by defining __ubsan_default_options, a weak symbol
495 : : * libsanitizer uses to get defaults from the application, and return
496 : : * getenv("UBSAN_OPTIONS"). But only if main already was reached, so that we
497 : : * don't end up relying on a not-yet-working getenv().
498 : : *
499 : : * As this function won't get called when not running a sanitizer, it doesn't
500 : : * seem necessary to only compile it conditionally.
501 : : */
502 : : const char *__ubsan_default_options(void);
503 : : const char *
1020 andres@anarazel.de 504 :UBC 0 : __ubsan_default_options(void)
505 : : {
506 : : /* don't call libc before it's guaranteed to be initialized */
507 [ # # ]: 0 : if (!reached_main)
508 : 0 : return "";
509 : :
510 : 0 : return getenv("UBSAN_OPTIONS");
511 : : }
|