Ejemplo n.º 1
0
/*!
 * \internal
 * \brief Sort the regions list using mergesort.
 *
 * \param list Allocated regions list to sort.
 * \param length Length of the list.
 *
 * \return Nothing
 */
static void mm_atexit_list_sort(struct region_list *list, size_t length)
{
	/*! Semi-sorted merged list. */
	struct region_list merged = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
	/*! Sublists to merge. (Can only merge two sublists at this time.) */
	struct region_list sub[2] = {
		AST_LIST_HEAD_NOLOCK_INIT_VALUE,
		AST_LIST_HEAD_NOLOCK_INIT_VALUE
	};
	/*! Sublist size. */
	size_t size = 1;
	/*! Remaining elements in the list. */
	size_t remaining;
	/*! Number of sublist merge passes to process the list. */
	int passes;

	for (;;) {
		remaining = length;

		passes = 0;
		while (!AST_LIST_EMPTY(list)) {
			mm_atexit_list_split(list, sub, ARRAY_LEN(sub), size, &remaining);
			mm_atexit_list_merge(&merged, &sub[0], &sub[1]);
			++passes;
		}
		AST_LIST_APPEND_LIST(list, &merged, node);
		if (passes <= 1) {
			/* The list is now sorted. */
			break;
		}

		/* Double the sublist size to remove for next round. */
		size <<= 1;
	}
}
Ejemplo n.º 2
0
/*!
 * \internal
 * \brief Merge the given sorted sublists into sorted order onto the end of the list.
 *
 * \param list Merge sublists onto this list.
 * \param sub1 First sublist to merge.
 * \param sub2 Second sublist to merge.
 *
 * \return Nothing
 */
static void mm_atexit_list_merge(struct region_list *list, struct region_list *sub1, struct region_list *sub2)
{
	struct ast_region *reg;

	for (;;) {
		if (AST_LIST_EMPTY(sub1)) {
			/* The remaining sublist goes onto the list. */
			AST_LIST_APPEND_LIST(list, sub2, node);
			break;
		}
		if (AST_LIST_EMPTY(sub2)) {
			/* The remaining sublist goes onto the list. */
			AST_LIST_APPEND_LIST(list, sub1, node);
			break;
		}

		if (mm_atexit_cmp(AST_LIST_FIRST(sub1), AST_LIST_FIRST(sub2)) <= 0) {
			reg = AST_LIST_REMOVE_HEAD(sub1, node);
		} else {
			reg = AST_LIST_REMOVE_HEAD(sub2, node);
		}
		AST_LIST_INSERT_TAIL(list, reg, node);
	}
}
Ejemplo n.º 3
0
int ast_pbx_hangup_handler_run(struct ast_channel *chan)
{
	struct ast_hangup_handler_list *handlers;
	struct ast_hangup_handler *h_handler;

	ast_channel_lock(chan);
	handlers = ast_channel_hangup_handlers(chan);
	if (AST_LIST_EMPTY(handlers)) {
		ast_channel_unlock(chan);
		return 0;
	}

	/*
	 * Make sure that the channel is marked as hungup since we are
	 * going to run the hangup handlers on it.
	 */
	ast_softhangup_nolock(chan, AST_SOFTHANGUP_HANGUP_EXEC);

	for (;;) {
		handlers = ast_channel_hangup_handlers(chan);
		h_handler = AST_LIST_REMOVE_HEAD(handlers, node);
		if (!h_handler) {
			break;
		}

		publish_hangup_handler_message("run", chan, h_handler->args);
		ast_channel_unlock(chan);

		ast_app_exec_sub(NULL, chan, h_handler->args, 1);
		ast_free(h_handler);

		ast_channel_lock(chan);
	}
	ast_channel_unlock(chan);
	return 1;
}
Ejemplo n.º 4
0
static int app_exec(struct ast_channel *chan, void *data)
{
	struct ast_module_user *lu;
	struct playlist_entry *entry;
	const char *args = data;
	int child_stdin[2] = { 0,0 };
	int child_stdout[2] = { 0,0 };
	int child_stderr[2] = { 0,0 };
	int res = -1;
	int test_available_fd = -1;
	int gen_active = 0;
	int pid;
	char *argv[32];
	int argc = 1;
	char *buf, *command;
	FILE *child_commands = NULL;
	FILE *child_errors = NULL;
	FILE *child_events = NULL;
	struct ivr_localuser foo = {
		.playlist = AST_LIST_HEAD_INIT_VALUE,
		.finishlist = AST_LIST_HEAD_INIT_VALUE,
	};
	struct ivr_localuser *u = &foo;
	sigset_t fullset, oldset;

	lu = ast_module_user_add(chan);

	sigfillset(&fullset);
	pthread_sigmask(SIG_BLOCK, &fullset, &oldset);

	u->abort_current_sound = 0;
	u->chan = chan;
	
	if (ast_strlen_zero(args)) {
		ast_log(LOG_WARNING, "ExternalIVR requires a command to execute\n");
		ast_module_user_remove(lu);
		return -1;	
	}

	buf = ast_strdupa(data);

	argc = ast_app_separate_args(buf, '|', argv, sizeof(argv) / sizeof(argv[0]));

	if (pipe(child_stdin)) {
		ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child input: %s\n", strerror(errno));
		goto exit;
	}

	if (pipe(child_stdout)) {
		ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child output: %s\n", strerror(errno));
		goto exit;
	}

	if (pipe(child_stderr)) {
		ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child errors: %s\n", strerror(errno));
		goto exit;
	}

	if (chan->_state != AST_STATE_UP) {
		ast_answer(chan);
	}

	if (ast_activate_generator(chan, &gen, u) < 0) {
		ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n");
		goto exit;
	} else
		gen_active = 1;

	pid = fork();
	if (pid < 0) {
		ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno));
		goto exit;
	}

	if (!pid) {
		/* child process */
		int i;

		signal(SIGPIPE, SIG_DFL);
		pthread_sigmask(SIG_UNBLOCK, &fullset, NULL);

		if (ast_opt_high_priority)
			ast_set_priority(0);

		dup2(child_stdin[0], STDIN_FILENO);
		dup2(child_stdout[1], STDOUT_FILENO);
		dup2(child_stderr[1], STDERR_FILENO);
		for (i = STDERR_FILENO + 1; i < 1024; i++)
			close(i);
		execv(argv[0], argv);
		fprintf(stderr, "Failed to execute '%s': %s\n", argv[0], strerror(errno));
		_exit(1);
	} else {
		/* parent process */
		int child_events_fd = child_stdin[1];
		int child_commands_fd = child_stdout[0];
		int child_errors_fd = child_stderr[0];
		struct ast_frame *f;
		int ms;
		int exception;
		int ready_fd;
		int waitfds[2] = { child_errors_fd, child_commands_fd };
		struct ast_channel *rchan;

		pthread_sigmask(SIG_SETMASK, &oldset, NULL);

		close(child_stdin[0]);
		child_stdin[0] = 0;
		close(child_stdout[1]);
		child_stdout[1] = 0;
		close(child_stderr[1]);
		child_stderr[1] = 0;

		if (!(child_events = fdopen(child_events_fd, "w"))) {
			ast_chan_log(LOG_WARNING, chan, "Could not open stream for child events\n");
			goto exit;
		}

		if (!(child_commands = fdopen(child_commands_fd, "r"))) {
			ast_chan_log(LOG_WARNING, chan, "Could not open stream for child commands\n");
			goto exit;
		}

		if (!(child_errors = fdopen(child_errors_fd, "r"))) {
			ast_chan_log(LOG_WARNING, chan, "Could not open stream for child errors\n");
			goto exit;
		}

		test_available_fd = open("/dev/null", O_RDONLY);

		setvbuf(child_events, NULL, _IONBF, 0);
		setvbuf(child_commands, NULL, _IONBF, 0);
		setvbuf(child_errors, NULL, _IONBF, 0);

		res = 0;

		while (1) {
			if (ast_test_flag(chan, AST_FLAG_ZOMBIE)) {
				ast_chan_log(LOG_NOTICE, chan, "Is a zombie\n");
				res = -1;
				break;
			}

			if (ast_check_hangup(chan)) {
				ast_chan_log(LOG_NOTICE, chan, "Got check_hangup\n");
				send_child_event(child_events, 'H', NULL, chan);
				res = -1;
				break;
			}

			ready_fd = 0;
			ms = 100;
			errno = 0;
			exception = 0;

			rchan = ast_waitfor_nandfds(&chan, 1, waitfds, 2, &exception, &ready_fd, &ms);

			if (!AST_LIST_EMPTY(&u->finishlist)) {
				AST_LIST_LOCK(&u->finishlist);
				while ((entry = AST_LIST_REMOVE_HEAD(&u->finishlist, list))) {
					send_child_event(child_events, 'F', entry->filename, chan);
					free(entry);
				}
				AST_LIST_UNLOCK(&u->finishlist);
			}

			if (rchan) {
				/* the channel has something */
				f = ast_read(chan);
				if (!f) {
					ast_chan_log(LOG_NOTICE, chan, "Returned no frame\n");
					send_child_event(child_events, 'H', NULL, chan);
					res = -1;
					break;
				}

				if (f->frametype == AST_FRAME_DTMF) {
					send_child_event(child_events, f->subclass, NULL, chan);
					if (u->option_autoclear) {
						if (!u->abort_current_sound && !u->playing_silence)
							send_child_event(child_events, 'T', NULL, chan);
						AST_LIST_LOCK(&u->playlist);
						while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
							send_child_event(child_events, 'D', entry->filename, chan);
							free(entry);
						}
						if (!u->playing_silence)
							u->abort_current_sound = 1;
						AST_LIST_UNLOCK(&u->playlist);
					}
				} else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) {
					ast_chan_log(LOG_NOTICE, chan, "Got AST_CONTROL_HANGUP\n");
					send_child_event(child_events, 'H', NULL, chan);
					ast_frfree(f);
					res = -1;
					break;
				}
				ast_frfree(f);
			} else if (ready_fd == child_commands_fd) {
				char input[1024];

				if (exception || feof(child_commands)) {
					ast_chan_log(LOG_WARNING, chan, "Child process went away\n");
					res = -1;
					break;
				}

				if (!fgets(input, sizeof(input), child_commands))
					continue;

				command = ast_strip(input);

				ast_chan_log(LOG_DEBUG, chan, "got command '%s'\n", input);

				if (strlen(input) < 4)
					continue;

				if (input[0] == 'S') {
					if (ast_fileexists(&input[2], NULL, u->chan->language) == -1) {
						ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
						send_child_event(child_events, 'Z', NULL, chan);
						strcpy(&input[2], "exception");
					}
					if (!u->abort_current_sound && !u->playing_silence)
						send_child_event(child_events, 'T', NULL, chan);
					AST_LIST_LOCK(&u->playlist);
					while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
						send_child_event(child_events, 'D', entry->filename, chan);
						free(entry);
					}
					if (!u->playing_silence)
						u->abort_current_sound = 1;
					entry = make_entry(&input[2]);
					if (entry)
						AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
					AST_LIST_UNLOCK(&u->playlist);
				} else if (input[0] == 'A') {
					if (ast_fileexists(&input[2], NULL, u->chan->language) == -1) {
						ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
						send_child_event(child_events, 'Z', NULL, chan);
						strcpy(&input[2], "exception");
					}
					entry = make_entry(&input[2]);
					if (entry) {
						AST_LIST_LOCK(&u->playlist);
						AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
						AST_LIST_UNLOCK(&u->playlist);
					}
				} else if (input[0] == 'H') {
					ast_chan_log(LOG_NOTICE, chan, "Hanging up: %s\n", &input[2]);
					send_child_event(child_events, 'H', NULL, chan);
					break;
				} else if (input[0] == 'O') {
					if (!strcasecmp(&input[2], "autoclear"))
						u->option_autoclear = 1;
					else if (!strcasecmp(&input[2], "noautoclear"))
						u->option_autoclear = 0;
					else
						ast_chan_log(LOG_WARNING, chan, "Unknown option requested '%s'\n", &input[2]);
				}
			} else if (ready_fd == child_errors_fd) {
				char input[1024];

				if (exception || (dup2(child_commands_fd, test_available_fd) == -1) || feof(child_errors)) {
					ast_chan_log(LOG_WARNING, chan, "Child process went away\n");
					res = -1;
					break;
				}

				if (fgets(input, sizeof(input), child_errors)) {
					command = ast_strip(input);
					ast_chan_log(LOG_NOTICE, chan, "stderr: %s\n", command);
				}
			} else if ((ready_fd < 0) && ms) { 
				if (errno == 0 || errno == EINTR)
					continue;

				ast_chan_log(LOG_WARNING, chan, "Wait failed (%s)\n", strerror(errno));
				break;
			}
		}
	}

 exit:
	if (gen_active)
		ast_deactivate_generator(chan);

	if (child_events)
		fclose(child_events);

	if (child_commands)
		fclose(child_commands);

	if (child_errors)
		fclose(child_errors);

	if (test_available_fd > -1) {
		close(test_available_fd);
	}

	if (child_stdin[0])
		close(child_stdin[0]);

	if (child_stdin[1])
		close(child_stdin[1]);

	if (child_stdout[0])
		close(child_stdout[0]);

	if (child_stdout[1])
		close(child_stdout[1]);

	if (child_stderr[0])
		close(child_stderr[0]);

	if (child_stderr[1])
		close(child_stderr[1]);

	while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list)))
		free(entry);

	ast_module_user_remove(lu);

	return res;
}
Ejemplo n.º 5
0
/*!
 * \internal
 * \note This function assumes that we're only called from the "outbound" local channel side
 *
 * \note it is assummed p is locked and reffed before entering this function
 */
static void check_bridge(struct ast_channel *ast, struct local_pvt *p)
{
	struct ast_channel *owner;
	struct ast_channel *chan;
	struct ast_channel *bridged_chan;
	struct ast_frame *f;

	/* Do a few conditional checks early on just to see if this optimization is possible */
	if (ast_test_flag(p, LOCAL_NO_OPTIMIZATION | LOCAL_ALREADY_MASQED)
		|| !p->chan || !p->owner) {
		return;
	}

	/* Safely get the channel bridged to p->chan */
	chan = ast_channel_ref(p->chan);

	ao2_unlock(p); /* don't call bridged channel with the pvt locked */
	bridged_chan = ast_bridged_channel(chan);
	ao2_lock(p);

	chan = ast_channel_unref(chan);

	/* since we had to unlock p to get the bridged chan, validate our
	 * data once again and verify the bridged channel is what we expect
	 * it to be in order to perform this optimization */
	if (ast_test_flag(p, LOCAL_NO_OPTIMIZATION | LOCAL_ALREADY_MASQED)
		|| !p->chan || !p->owner
		|| (ast_channel_internal_bridged_channel(p->chan) != bridged_chan)) {
		return;
	}

	/* only do the masquerade if we are being called on the outbound channel,
	   if it has been bridged to another channel and if there are no pending
	   frames on the owner channel (because they would be transferred to the
	   outbound channel during the masquerade)
	*/
	if (!ast_channel_internal_bridged_channel(p->chan) /* Not ast_bridged_channel!  Only go one step! */
		|| !AST_LIST_EMPTY(ast_channel_readq(p->owner))
		|| ast != p->chan /* Sanity check (should always be false) */) {
		return;
	}

	/* Masquerade bridged channel into owner */
	/* Lock everything we need, one by one, and give up if
	   we can't get everything.  Remember, we'll get another
	   chance in just a little bit */
	if (ast_channel_trylock(ast_channel_internal_bridged_channel(p->chan))) {
		return;
	}
	if (ast_check_hangup(ast_channel_internal_bridged_channel(p->chan))
		|| ast_channel_trylock(p->owner)) {
		ast_channel_unlock(ast_channel_internal_bridged_channel(p->chan));
		return;
	}

	/*
	 * At this point we have 4 locks:
	 * p, p->chan (same as ast), p->chan->_bridge, p->owner
	 *
	 * Flush a voice or video frame on the outbound channel to make
	 * the queue empty faster so we can get optimized out.
	 */
	f = AST_LIST_FIRST(ast_channel_readq(p->chan));
	if (f && (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_VIDEO)) {
		AST_LIST_REMOVE_HEAD(ast_channel_readq(p->chan), frame_list);
		ast_frfree(f);
		f = AST_LIST_FIRST(ast_channel_readq(p->chan));
	}

	if (f
		|| ast_check_hangup(p->owner)
		|| ast_channel_masquerade(p->owner, ast_channel_internal_bridged_channel(p->chan))) {
		ast_channel_unlock(p->owner);
		ast_channel_unlock(ast_channel_internal_bridged_channel(p->chan));
		return;
	}

	/* Masquerade got setup. */
	ast_debug(4, "Masquerading %s <- %s\n",
		ast_channel_name(p->owner),
		ast_channel_name(ast_channel_internal_bridged_channel(p->chan)));
	if (ast_channel_monitor(p->owner)
		&& !ast_channel_monitor(ast_channel_internal_bridged_channel(p->chan))) {
		struct ast_channel_monitor *tmp;

		/* If a local channel is being monitored, we don't want a masquerade
		 * to cause the monitor to go away. Since the masquerade swaps the monitors,
		 * pre-swapping the monitors before the masquerade will ensure that the monitor
		 * ends up where it is expected.
		 */
		tmp = ast_channel_monitor(p->owner);
		ast_channel_monitor_set(p->owner, ast_channel_monitor(ast_channel_internal_bridged_channel(p->chan)));
		ast_channel_monitor_set(ast_channel_internal_bridged_channel(p->chan), tmp);
	}
	if (ast_channel_audiohooks(p->chan)) {
		struct ast_audiohook_list *audiohooks_swapper;

		audiohooks_swapper = ast_channel_audiohooks(p->chan);
		ast_channel_audiohooks_set(p->chan, ast_channel_audiohooks(p->owner));
		ast_channel_audiohooks_set(p->owner, audiohooks_swapper);
	}

	/* If any Caller ID was set, preserve it after masquerade like above. We must check
	 * to see if Caller ID was set because otherwise we'll mistakingly copy info not
	 * set from the dialplan and will overwrite the real channel Caller ID. The reason
	 * for this whole preswapping action is because the Caller ID is set on the channel
	 * thread (which is the to be masqueraded away local channel) before both local
	 * channels are optimized away.
	 */
	if (ast_channel_caller(p->owner)->id.name.valid || ast_channel_caller(p->owner)->id.number.valid
		|| ast_channel_caller(p->owner)->id.subaddress.valid || ast_channel_caller(p->owner)->ani.name.valid
		|| ast_channel_caller(p->owner)->ani.number.valid || ast_channel_caller(p->owner)->ani.subaddress.valid) {
		SWAP(*ast_channel_caller(p->owner), *ast_channel_caller(ast_channel_internal_bridged_channel(p->chan)));
	}
	if (ast_channel_redirecting(p->owner)->from.name.valid || ast_channel_redirecting(p->owner)->from.number.valid
		|| ast_channel_redirecting(p->owner)->from.subaddress.valid || ast_channel_redirecting(p->owner)->to.name.valid
		|| ast_channel_redirecting(p->owner)->to.number.valid || ast_channel_redirecting(p->owner)->to.subaddress.valid) {
		SWAP(*ast_channel_redirecting(p->owner), *ast_channel_redirecting(ast_channel_internal_bridged_channel(p->chan)));
	}
	if (ast_channel_dialed(p->owner)->number.str || ast_channel_dialed(p->owner)->subaddress.valid) {
		SWAP(*ast_channel_dialed(p->owner), *ast_channel_dialed(ast_channel_internal_bridged_channel(p->chan)));
	}
	ast_app_group_update(p->chan, p->owner);
	ast_set_flag(p, LOCAL_ALREADY_MASQED);

	ast_channel_unlock(p->owner);
	ast_channel_unlock(ast_channel_internal_bridged_channel(p->chan));

	/* Do the masquerade now. */
	owner = ast_channel_ref(p->owner);
	ao2_unlock(p);
	ast_channel_unlock(ast);
	ast_do_masquerade(owner);
	ast_channel_unref(owner);
	ast_channel_lock(ast);
	ao2_lock(p);
}