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](...)
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))