constification
[exim.git] / src / src / exim.c
index 42db457c0e80d6c74462423d5aa26437168148a6..bedac628b0a852d3494194ea962b8c48188700d5 100644 (file)
@@ -262,12 +262,39 @@ exit(1);
 ***********************************************/
 
 static void
+#ifdef SA_SIGINFO
+segv_handler(int sig, siginfo_t * info, void * uctx)
+{
+log_write(0, LOG_MAIN|LOG_PANIC, "SIGSEGV (fault address: %p)", info->si_addr);
+# if defined(SEGV_MAPERR) && defined(SEGV_ACCERR) && defined(SEGV_BNDERR) && defined(SEGV_PKUERR)
+switch (info->si_code)
+  {
+  case SEGV_MAPERR: log_write(0, LOG_MAIN|LOG_PANIC, "SEGV_MAPERR"); break;
+  case SEGV_ACCERR: log_write(0, LOG_MAIN|LOG_PANIC, "SEGV_ACCERR"); break;
+  case SEGV_BNDERR: log_write(0, LOG_MAIN|LOG_PANIC, "SEGV_BNDERR"); break;
+  case SEGV_PKUERR: log_write(0, LOG_MAIN|LOG_PANIC, "SEGV_PKUERR"); break;
+  }
+# endif
+if (US info->si_addr < US 4096)
+  log_write(0, LOG_MAIN|LOG_PANIC, "SIGSEGV (null pointer indirection)");
+else
+  log_write(0, LOG_MAIN|LOG_PANIC, "SIGSEGV (maybe attempt to write to immutable memory)");
+if (process_info_len > 0)
+  log_write(0, LOG_MAIN|LOG_PANIC, "SIGSEGV (%.*s)", process_info_len, process_info);
+signal(SIGSEGV, SIG_DFL);
+kill(getpid(), sig);
+}
+
+#else
 segv_handler(int sig)
 {
 log_write(0, LOG_MAIN|LOG_PANIC, "SIGSEGV (maybe attempt to write to immutable memory)");
+if (process_info_len > 0)
+  log_write(0, LOG_MAIN|LOG_PANIC, "SIGSEGV (%.*s)", process_info_len, process_info);
 signal(SIGSEGV, SIG_DFL);
 kill(getpid(), sig);
 }
+#endif
 
 
 /*************************************************
@@ -712,7 +739,7 @@ Returns:     nothing; bombs out on failure
 */
 
 void
-exim_setugid(uid_t uid, gid_t gid, BOOL igflag, uschar *msg)
+exim_setugid(uid_t uid, gid_t gid, BOOL igflag, const uschar * msg)
 {
 uid_t euid = geteuid();
 gid_t egid = getegid();
@@ -921,7 +948,7 @@ else
   int rc = verify_address(deliver_make_addr(address,TRUE), stdout, flags, -1,
     -1, -1, NULL, NULL, NULL);
   if (rc == FAIL) *exit_value = 2;
-    else if (rc == DEFER && *exit_value == 0) *exit_value = 1;
+  else if (rc == DEFER && *exit_value == 0) *exit_value = 1;
   }
 }
 
@@ -932,54 +959,66 @@ else
 *************************************************/
 
 static void
-show_db_version(FILE * f)
+show_string(BOOL is_stdout, gstring * g)
+{
+const uschar * s = string_from_gstring(g);
+if (s)
+  if (is_stdout) fputs(CCS s, stdout);
+  else debug_printf("%s", s);
+}
+
+
+static gstring *
+show_db_version(gstring * g)
 {
 #ifdef DB_VERSION_STRING
 DEBUG(D_any)
   {
-  fprintf(f, "Library version: BDB: Compile: %s\n", DB_VERSION_STRING);
-  fprintf(f, "                      Runtime: %s\n",
+  g = string_fmt_append(g, "Library version: BDB: Compile: %s\n", DB_VERSION_STRING);
+  g = string_fmt_append(g, "                      Runtime: %s\n",
     db_version(NULL, NULL, NULL));
   }
 else
-  fprintf(f, "Berkeley DB: %s\n", DB_VERSION_STRING);
+  g = string_fmt_append(g, "Berkeley DB: %s\n", DB_VERSION_STRING);
 
 #elif defined(BTREEVERSION) && defined(HASHVERSION)
-  #ifdef USE_DB
-  fprintf(f, "Probably Berkeley DB version 1.8x (native mode)\n");
-  #else
-  fprintf(f, "Probably Berkeley DB version 1.8x (compatibility mode)\n");
-  #endif
+ifdef USE_DB
+  g = string_cat(g, US"Probably Berkeley DB version 1.8x (native mode)\n");
+else
+  g = string_cat(g, US"Probably Berkeley DB version 1.8x (compatibility mode)\n");
+endif
 
 #elif defined(_DBM_RDONLY) || defined(dbm_dirfno)
-fprintf(f, "Probably ndbm\n");
+g = string_cat(g, US"Probably ndbm\n");
 #elif defined(USE_TDB)
-fprintf(f, "Using tdb\n");
+g = string_cat(g, US"Using tdb\n");
 #else
-  #ifdef USE_GDBM
-  fprintf(f, "Probably GDBM (native mode)\n");
-  #else
-  fprintf(f, "Probably GDBM (compatibility mode)\n");
-  #endif
+ifdef USE_GDBM
+  g = string_cat(g, US"Probably GDBM (native mode)\n");
+else
+  g = string_cat(g, US"Probably GDBM (compatibility mode)\n");
+endif
 #endif
+return g;
 }
 
 
 /* This function is called for -bV/--version and for -d to output the optional
 features of the current Exim binary.
 
-Arguments:  a FILE for printing
+Arguments:  BOOL, true for stdout else debug channel
 Returns:    nothing
 */
 
 static void
-show_whats_supported(FILE * fp)
+show_whats_supported(BOOL is_stdout)
 {
 rmark reset_point = store_mark();
-gstring * g;
-DEBUG(D_any) {} else show_db_version(fp);
+gstring * g = NULL;
+
+DEBUG(D_any) {} else g = show_db_version(g);
 
-g = string_cat(NULL, US"Support for:");
+g = string_cat(g, US"Support for:");
 #ifdef SUPPORT_CRYPTEQ
   g = string_cat(g, US" crypteq");
 #endif
@@ -1086,9 +1125,6 @@ g = string_cat(NULL, US"Support for:");
 #ifdef EXPERIMENTAL_QUEUEFILE
   g = string_cat(g, US" Experimental_QUEUEFILE");
 #endif
-#if defined(EXPERIMENTAL_SRS_ALT)
-  g = string_cat(g, US" Experimental_SRS");
-#endif
 g = string_cat(g, US"\n");
 
 g = string_cat(g, US"Lookups (built-in):");
@@ -1158,6 +1194,7 @@ g = transport_show_supported(g);
 #ifdef WITH_CONTENT_SCAN
 g = malware_show_supported(g);
 #endif
+show_string(is_stdout, g); g = NULL;
 
 if (fixed_never_users[0] > 0)
   {
@@ -1169,19 +1206,19 @@ if (fixed_never_users[0] > 0)
   }
 
 g = string_fmt_append(g, "Configure owner: %d:%d\n", config_uid, config_gid);
-fputs(CS string_from_gstring(g), fp);
 
-fprintf(fp, "Size of off_t: " SIZE_T_FMT "\n", sizeof(off_t));
+g = string_fmt_append(g, "Size of off_t: " SIZE_T_FMT "\n", sizeof(off_t));
 
 /* Everything else is details which are only worth reporting when debugging.
 Perhaps the tls_version_report should move into this too. */
-DEBUG(D_any) do {
+DEBUG(D_any)
+  {
 
 /* clang defines __GNUC__ (at least, for me) so test for it first */
 #if defined(__clang__)
-  fprintf(fp, "Compiler: CLang [%s]\n", __clang_version__);
+  g = string_fmt_append(g, "Compiler: CLang [%s]\n", __clang_version__);
 #elif defined(__GNUC__)
-  fprintf(fp, "Compiler: GCC [%s]\n",
+  g = string_fmt_append(g, "Compiler: GCC [%s]\n",
 # ifdef __VERSION__
       __VERSION__
 # else
@@ -1189,35 +1226,38 @@ DEBUG(D_any) do {
 # endif
       );
 #else
-  fprintf(fp, "Compiler: <unknown>\n");
+  g = string_cat(g, US"Compiler: <unknown>\n");
 #endif
 
 #if defined(__GLIBC__) && !defined(__UCLIBC__)
-  fprintf(fp, "Library version: Glibc: Compile: %d.%d\n",
+  g = string_fmt_append(g, "Library version: Glibc: Compile: %d.%d\n",
                __GLIBC__, __GLIBC_MINOR__);
   if (__GLIBC_PREREQ(2, 1))
-    fprintf(fp, "                        Runtime: %s\n",
+    g = string_fmt_append(g, "                        Runtime: %s\n",
                gnu_get_libc_version());
 #endif
 
-show_db_version(fp);
+g = show_db_version(g);
 
 #ifndef DISABLE_TLS
-  tls_version_report(fp);
+  g = tls_version_report(g);
 #endif
 #ifdef SUPPORT_I18N
-  utf8_version_report(fp);
+  g = utf8_version_report(g);
 #endif
 #ifdef SUPPORT_DMARC
-  dmarc_version_report(fp);
+  g = dmarc_version_report(g);
 #endif
 #ifdef SUPPORT_SPF
-  spf_lib_version_report(fp);
+  g = spf_lib_version_report(g);
 #endif
 
-  for (auth_info * authi = auths_available; *authi->driver_name != '\0'; ++authi)
-    if (authi->version_report)
-      (*authi->version_report)(fp);
+show_string(is_stdout, g);
+g = NULL;
+
+for (auth_info * authi = auths_available; *authi->driver_name != '\0'; ++authi)
+  if (authi->version_report)
+    g = (*authi->version_report)(g);
 
   /* PCRE_PRERELEASE is either defined and empty or a bare sequence of
   characters; unless it's an ancient version of PCRE in which case it
@@ -1230,7 +1270,7 @@ show_db_version(fp);
   {
   uschar buf[24];
   pcre2_config(PCRE2_CONFIG_VERSION, buf);
-  fprintf(fp, "Library version: PCRE2: Compile: %d.%d%s\n"
+  g = string_fmt_append(g, "Library version: PCRE2: Compile: %d.%d%s\n"
               "                        Runtime: %s\n",
           PCRE2_MAJOR, PCRE2_MINOR,
           EXPAND_AND_QUOTE(PCRE2_PRERELEASE) "",
@@ -1239,23 +1279,29 @@ show_db_version(fp);
 #undef QUOTE
 #undef EXPAND_AND_QUOTE
 
-  init_lookup_list();
-  for (int i = 0; i < lookup_list_count; i++)
-    if (lookup_list[i]->version_report)
-      lookup_list[i]->version_report(fp);
+show_string(is_stdout, g);
+g = NULL;
+
+init_lookup_list();
+for (int i = 0; i < lookup_list_count; i++)
+  if (lookup_list[i]->version_report)
+    g = lookup_list[i]->version_report(g);
+show_string(is_stdout, g);
+g = NULL;
 
 #ifdef WHITELIST_D_MACROS
-  fprintf(fp, "WHITELIST_D_MACROS: \"%s\"\n", WHITELIST_D_MACROS);
+  g = string_fmt_append(g, "WHITELIST_D_MACROS: \"%s\"\n", WHITELIST_D_MACROS);
 #else
-  fprintf(fp, "WHITELIST_D_MACROS unset\n");
+  g = string_cat(g, US"WHITELIST_D_MACROS unset\n");
 #endif
 #ifdef TRUSTED_CONFIG_LIST
-  fprintf(fp, "TRUSTED_CONFIG_LIST: \"%s\"\n", TRUSTED_CONFIG_LIST);
+  g = string_fmt_append(g, "TRUSTED_CONFIG_LIST: \"%s\"\n", TRUSTED_CONFIG_LIST);
 #else
-  fprintf(fp, "TRUSTED_CONFIG_LIST unset\n");
+  g = string_cat(g, US"TRUSTED_CONFIG_LIST unset\n");
 #endif
+  }
 
-} while (0);
+show_string(is_stdout, g);
 store_reset(reset_point);
 }
 
@@ -1407,56 +1453,58 @@ static uschar *
 get_stdinput(char *(*fn_readline)(const char *), void(*fn_addhist)(const char *))
 {
 gstring * g = NULL;
+BOOL had_input = FALSE;
 
 if (!fn_readline) { printf("> "); fflush(stdout); }
 
 for (int i = 0;; i++)
   {
   uschar buffer[1024];
-  uschar *p, *ss;
+  uschar * p, * ss;
 
-  #ifdef USE_READLINE
+#ifdef USE_READLINE
   char *readline_line = NULL;
   if (fn_readline)
     {
     if (!(readline_line = fn_readline((i > 0)? "":"> "))) break;
-    if (*readline_line != 0 && fn_addhist) fn_addhist(readline_line);
+    if (*readline_line && fn_addhist) fn_addhist(readline_line);
     p = US readline_line;
     }
   else
-  #endif
+#endif
 
   /* readline() not in use */
 
     {
-    if (Ufgets(buffer, sizeof(buffer), stdin) == NULL) break;
+    if (Ufgets(buffer, sizeof(buffer), stdin) == NULL) break;  /*EOF*/
     p = buffer;
     }
 
   /* Handle the line */
 
-  ss = p + (int)Ustrlen(p);
-  while (ss > p && isspace(ss[-1])) ss--;
+  had_input = TRUE;
+  ss = p + Ustrlen(p);
+  while (ss > p && isspace(ss[-1])) ss--; /* strip trailing newline (and spaces) */
 
   if (i > 0)
-    while (p < ss && isspace(*p)) p++;   /* leading space after cont */
+    while (p < ss && isspace(*p)) p++;   /* strip leading space after cont */
 
   g = string_catn(g, p, ss - p);
 
-  #ifdef USE_READLINE
+#ifdef USE_READLINE
   if (fn_readline) free(readline_line);
-  #endif
+#endif
 
   /* g can only be NULL if ss==p */
-  if (ss == p || g->s[g->ptr-1] != '\\')
+  if (ss == p || g->s[g->ptr-1] != '\\') /* not continuation; done */
     break;
 
-  --g->ptr;
-  (void) string_from_gstring(g);
+  --g->ptr;                            /* drop the \ */
   }
 
-if (!g) printf("\n");
-return string_from_gstring(g);
+if (had_input) return g ? string_from_gstring(g) : US"";
+printf("\n");
+return NULL;
 }
 
 
@@ -1665,7 +1713,7 @@ int  i, rv;
 int  list_queue_option = 0;
 int  msg_action = 0;
 int  msg_action_arg = -1;
-int  namelen = (argv[0] == NULL)? 0 : Ustrlen(argv[0]);
+int  namelen = argv[0] ? Ustrlen(argv[0]) : 0;
 int  queue_only_reason = 0;
 #ifdef EXIM_PERL
 int  perl_start_option = 0;
@@ -1809,6 +1857,7 @@ if (f.running_in_test_harness)
   debug_store = TRUE;
 
 /* Protect against abusive argv[0] */
+if (!argv[0] || !argc) exim_fail("exim: executable name required\n");
 exim_str_fail_toolong(argv[0], PATH_MAX, "argv[0]");
 
 /* The C standard says that the equivalent of setlocale(LC_ALL, "C") is obeyed
@@ -1856,7 +1905,14 @@ descriptive text. */
 process_info = store_get(PROCESS_INFO_SIZE, TRUE);     /* tainted */
 set_process_info("initializing");
 os_restarting_signal(SIGUSR1, usr1_handler);           /* exiwhat */
+#ifdef SA_SIGINFO
+  {
+  struct sigaction act = { .sa_sigaction = segv_handler, .sa_flags = SA_RESETHAND | SA_SIGINFO };
+  sigaction(SIGSEGV, &act, NULL);
+  }
+#else
 signal(SIGSEGV, segv_handler);                         /* log faults */
+#endif
 
 /* If running in a dockerized environment, the TERM signal is only
 delegated to the PID 1 if we request it by setting an signal handler */
@@ -2408,7 +2464,7 @@ on the second character (the one after '-'), to save some effort. */
              version_cnumber, version_date);
            printf("%s\n", CS version_copyright);
            version_printed = TRUE;
-           show_whats_supported(stdout);
+           show_whats_supported(TRUE);
            f.log_testing_mode = TRUE;
            }
          else badarg = TRUE;
@@ -2603,21 +2659,36 @@ on the second character (the one after '-'), to save some effort. */
     #endif
     break;
 
-    /* -d: Set debug level (see also -v below) or set the drop_cr option.
-    The latter is now a no-op, retained for compatibility only. If -dd is used,
-    debugging subprocesses of the daemon is disabled. */
-
     case 'd':
+
+    /* -dropcr: Set this option.  Now a no-op, retained for compatibility only. */
+
     if (Ustrcmp(argrest, "ropcr") == 0)
       {
       /* drop_cr = TRUE; */
       }
 
-    /* Use an intermediate variable so that we don't set debugging while
-    decoding the debugging bits. */
+    /* -dp: Set up a debug pretrigger buffer with given size. */
+
+    else if (Ustrcmp(argrest, "p") == 0)
+      if (++i >= argc)
+       badarg = TRUE;
+      else
+       debug_pretrigger_setup(argv[i]);
+
+    /* -dt: Set a debug trigger selector */
+
+    else if (Ustrncmp(argrest, "t=", 2) == 0)
+      dtrigger_selector = (unsigned int) Ustrtol(argrest + 2, NULL, 0);
+
+    /* -d: Set debug level (see also -v below).
+    If -dd is used, debugging subprocesses of the daemon is disabled. */
 
     else
       {
+      /* Use an intermediate variable so that we don't set debugging while
+      decoding the debugging bits. */
+
       unsigned int selector = D_default;
       debug_selector = 0;
       debug_file = NULL;
@@ -3721,7 +3792,7 @@ if (debug_selector != 0)
       version_string, (long int)real_uid, (long int)real_gid, (int)getpid(),
       debug_selector);
     if (!version_printed)
-      show_whats_supported(stderr);
+      show_whats_supported(FALSE);
     }
   }