]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
config.c: Log #exec include failures.
authorSean Bright <sean@seanbright.com>
Wed, 22 Nov 2023 19:25:19 +0000 (14:25 -0500)
committerSean Bright <sean@seanbright.com>
Wed, 6 Dec 2023 14:48:20 +0000 (14:48 +0000)
If the script referenced by `#exec` does not exist, writes anything to
stderr, or exits abnormally or with a non-zero exit status, we log
that to Asterisk's error logging channel.

Additionally, write out a warning if the script produces no output.

Fixes #259

main/config.c

index 1522d8f24e3eee9b145b1657c5f56335a4786e7b..3dc47088def4d8143d02b1815a19a113dcc895f4 100644 (file)
@@ -1812,6 +1812,99 @@ static void config_cache_attribute(const char *configfile, enum config_cache_att
        AST_LIST_UNLOCK(&cfmtime_head);
 }
 
+/*!
+ * \internal
+ * \brief Process an #exec include, reporting errors.
+ *
+ * For backwards compatibility we return success in most cases because we
+ * do not want to prevent the rest of the configuration (or the module
+ * loading that configuration) from loading.
+ *
+ * \param command The command to execute
+ * \param output_file The filename to write to
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+static int handle_include_exec(const char *command, const char *output_file)
+{
+       char buf[1024];
+       FILE *fp;
+       int status;
+       struct stat output_file_info;
+
+       /* stderr to stdout, stdout to file */
+       if (snprintf(buf, sizeof(buf), "%s 2>&1 > %s", command, output_file) >= sizeof(buf)) {
+               ast_log(LOG_ERROR, "Failed to construct command string to execute %s.\n", command);
+               return -1;
+       }
+
+       ast_replace_sigchld();
+
+       errno = 0;
+
+       fp = popen(buf, "r");
+       if (!fp) {
+               ast_log(LOG_ERROR, "#exec <%s>: Failed to execute: %s\n",
+                       command,
+                       strerror(errno));
+               ast_unreplace_sigchld();
+               return 0;
+       }
+
+       while (fgets(buf, sizeof(buf), fp)) {
+               /* Ensure we have a \n at the end */
+               if (strlen(buf) == sizeof(buf) - 1 && buf[sizeof(buf) - 2] != '\n') {
+                       ast_log(LOG_ERROR, "#exec <%s>: %s... <truncated>\n",
+                               command,
+                               buf);
+
+                       /* Consume the rest of the line */
+                       while (fgets(buf, sizeof(buf), fp)) {
+                               if (strlen(buf) != sizeof(buf) - 1 || buf[sizeof(buf) - 2] == '\n') {
+                                       break;
+                               }
+                       }
+
+                       continue;
+               }
+
+               /* `buf` has the newline, so we don't need to print it ourselves */
+               ast_log(LOG_ERROR, "#exec <%s>: %s",
+                       command,
+                       buf);
+       }
+
+       status = pclose(fp);
+       if (status == -1) {
+               ast_log(LOG_ERROR, "#exec <%s>: Failed to retrieve exit status: %s\n",
+                       command,
+                       strerror(errno));
+       } else {
+               status = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
+               if (status) {
+                       ast_log(LOG_ERROR, "#exec <%s>: Exited with return value %d\n",
+                               command,
+                               status);
+               }
+       }
+
+       ast_unreplace_sigchld();
+
+       /* Check that the output file contains something */
+       if (stat(output_file, &output_file_info) == -1) {
+               ast_log(LOG_ERROR, "#exec <%s>: Unable to stat() temporary file `%s': %s\n",
+                       command,
+                       output_file,
+                       strerror(errno));
+       } else if (output_file_info.st_size == 0) {
+               ast_log(LOG_WARNING, "#exec <%s>: The program generated no usable output.\n",
+                       command);
+       }
+
+       return 0;
+}
+
 /*! \brief parse one line in the configuration.
  * \verbatim
  * We can have a category header       [foo](...)
@@ -2002,17 +2095,13 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
                   We create a tmp file, then we #include it, then we delete it. */
                if (!do_include) {
                        struct timeval now = ast_tvnow();
-                       char cmd[1024];
 
                        if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
                                config_cache_attribute(configfile, ATTRIBUTE_EXEC, NULL, who_asked);
                        snprintf(exec_file, sizeof(exec_file), "/var/tmp/exec.%d%d.%ld", (int)now.tv_sec, (int)now.tv_usec, (long)pthread_self());
-                       if (snprintf(cmd, sizeof(cmd), "%s > %s 2>&1", cur, exec_file) >= sizeof(cmd)) {
-                               ast_log(LOG_ERROR, "Failed to construct command string to execute %s.\n", cur);
-
+                       if (handle_include_exec(cur, exec_file)) {
                                return -1;
                        }
-                       ast_safe_system(cmd);
                        cur = exec_file;
                } else {
                        if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))