[asterisk-dev] asterisk 1.4 rtp port exhaustion fix

Paul Albrecht albrecht at glccom.com
Mon May 16 13:02:18 CDT 2011


Hi,

When I run the mp3 application I sometimes run out of rtp ports because
asterisk doesn't close open files and sockets before exec'ing the mpg123
process. Here's a fix:

diff -Naur asterisk-1.4.30/apps/app_mp3.c asterisk-1.4.30-hacked/apps/app_mp3.c
--- asterisk-1.4.30/apps/app_mp3.c	2010-01-06 09:18:22.000000000 -0600
+++ asterisk-1.4.30-hacked/apps/app_mp3.c	2011-05-12 13:28:28.000000000 -0500
@@ -39,6 +39,9 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <errno.h>
 #include <sys/time.h>
 #ifdef HAVE_CAP
 #include <sys/capability.h>
@@ -67,10 +70,9 @@
 "any key on the dialpad, or by hanging up."; 
 
 
-static int mp3play(char *filename, int fd)
+static int mp3play(char *filename, struct sockaddr_un ast_address)
 {
 	int res;
-	int x;
 	sigset_t fullset, oldset;
 #ifdef HAVE_CAP
 	cap_t cap;
@@ -100,18 +102,68 @@
 	signal(SIGPIPE, SIG_DFL);
 	pthread_sigmask(SIG_UNBLOCK, &fullset, NULL);
 
-	dup2(fd, STDOUT_FILENO);
-	for (x=STDERR_FILENO + 1;x<256;x++) {
-		close(x);
+	//
+	// create and connect unix socket file descriptors
+	//
+
+	int mp3_sock;
+	if ((mp3_sock = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
+		ast_log(LOG_ERROR, "unable to create mp3 socket: %s\n", strerror(errno));
+		_exit(1);
+	}
+
+	if((res = connect(mp3_sock, (const struct sockaddr *) &ast_address, sizeof(struct sockaddr_un))) != 0) {
+		ast_log(LOG_ERROR, "unable to connect mp3 socket: %s\n", strerror(errno));
+		_exit(1);
+	}
+
+	//
+	// create pipe for mp3 program
+	//
+
+	int mp3_fds[2] = { 0, };
+	if (pipe2(mp3_fds,O_CLOEXEC)) {
+		ast_log(LOG_ERROR, "unable to create mp3 pipe: %s\n", strerror(errno));
+		_exit(1);
+	}
+
+	//
+	// send mp3 file descriptors to asterisk
+	//
+
+	char mp3_data[1] = { '*' };
+	struct iovec mp3_iov[1] = { { .iov_base = mp3_data, .iov_len = 1 } };
+
+	char mp3_buf[CMSG_SPACE(2 * sizeof(int))] = { 0, };
+
+	struct msghdr mp3_message = { .msg_iov = mp3_iov, .msg_iovlen = 1, .msg_control = mp3_buf, .msg_controllen = sizeof mp3_buf };
+
+	struct cmsghdr *mp3_cmsg = CMSG_FIRSTHDR(&mp3_message);
+	mp3_cmsg->cmsg_level = SOL_SOCKET;
+	mp3_cmsg->cmsg_type = SCM_RIGHTS;
+	mp3_cmsg->cmsg_len = CMSG_LEN(2 * sizeof(int));
+
+	int *mp3_cmsg_fds_ptr = (int *)CMSG_DATA(mp3_cmsg);
+	memcpy(mp3_cmsg_fds_ptr, mp3_fds, 2 * sizeof(int));
+	mp3_message.msg_controllen = mp3_cmsg->cmsg_len;
+
+	if ((res = sendmsg(mp3_sock, &mp3_message, 0)) < 0) {
+		ast_log(LOG_ERROR, "mp3 fds sendmsg failed: %s\n", strerror(errno));
+		_exit(1);
 	}
+
+	close(mp3_sock);
+
+	dup2(mp3_fds[1], STDOUT_FILENO);
+
 	/* Execute mpg123, but buffer if it's a net connection */
-	if (!strncasecmp(filename, "http://", 7)) {
+	if (!strncasecmp(filename, "http://", 7) || !strncasecmp(filename, "- at http://", 9)) {
 		/* Most commonly installed in /usr/local/bin */
-	    execl(LOCAL_MPG_123, "mpg123", "-q", "-s", "-b", "1024", "-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
+	    execl(LOCAL_MPG_123, "mpg123", "-q", "-s", "-b", "128", "-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
 		/* But many places has it in /usr/bin */
-	    execl(MPG_123, "mpg123", "-q", "-s", "-b", "1024","-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
+	    execl(MPG_123, "mpg123", "-q", "-s", "-b", "128","-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
 		/* As a last-ditch effort, try to use PATH */
-	    execlp("mpg123", "mpg123", "-q", "-s", "-b", "1024",  "-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
+	    execlp("mpg123", "mpg123", "-q", "-s", "-b", "128",  "-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
 	}
 	else {
 		/* Most commonly installed in /usr/local/bin */
@@ -166,12 +218,6 @@
 
 	u = ast_module_user_add(chan);
 
-	if (pipe(fds)) {
-		ast_log(LOG_WARNING, "Unable to create pipe\n");
-		ast_module_user_remove(u);
-		return -1;
-	}
-	
 	ast_stopstream(chan);
 
 	owriteformat = chan->writeformat;
@@ -181,10 +227,62 @@
 		ast_module_user_remove(u);
 		return -1;
 	}
+
+	//
+	// create and bind unix socket for file descriptors
+	//
+
+	int ast_sock;
+	if ((ast_sock = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0)) < 0) {
+		ast_log(LOG_ERROR, "unable to create ast socket: %s\n", strerror(errno));
+		ast_module_user_remove(u);
+		return -1;
+	}
+
+	struct sockaddr_un ast_address;
+	ast_address.sun_family = AF_UNIX;
+	sprintf(ast_address.sun_path, "/tmp/asterisk-%s",chan->uniqueid);
+
+	if((res = bind(ast_sock, (const struct sockaddr *) &ast_address, sizeof(struct sockaddr_un))) != 0) {
+		ast_log(LOG_ERROR, "ast_sock bind failed, %s\n", strerror(errno));
+		ast_module_user_remove(u);
+		return -1;
+	}
 	
-	res = mp3play((char *)data, fds[1]);
-	if (!strncasecmp((char *)data, "http://", 7)) {
-		timeout = 10000;
+	if ((res = mp3play((char *)data, ast_address) < 0)) {
+		ast_log(LOG_WARNING, "Unable to fork child process\n");
+		ast_module_user_remove(u);
+		return -1;
+	}
+
+	//
+	// receive file descriptors from mp3 process
+	//
+
+	char ast_data[1] = { 0, };
+	struct iovec ast_iov[1] = { { .iov_base = ast_data, .iov_len = 1 } };
+
+	char ast_buf[CMSG_SPACE(2 * sizeof(int))] = { 0, };
+
+	struct msghdr ast_message = { .msg_iov = ast_iov, .msg_iovlen = 1, .msg_control = ast_buf, .msg_controllen = CMSG_LEN(2 * sizeof(int)) };
+
+	if ((res = recvmsg(ast_sock, &ast_message, MSG_CMSG_CLOEXEC)) < 0) {
+		ast_log(LOG_ERROR, "ast fds recvmsg failed: %s\n", strerror(errno));
+		ast_module_user_remove(u);
+		return -1;
+	}
+
+	struct cmsghdr *ast_cmsg = CMSG_FIRSTHDR(&ast_message);
+	memcpy(fds,CMSG_DATA(ast_cmsg),2 * sizeof(int));
+
+	//ast_log(LOG_NOTICE, "recvmsg: fds = %d,%d\n", fds[0], fds[1]);
+
+	close(ast_sock);
+
+	unlink(ast_address.sun_path);
+
+	if (!strncasecmp((char *)data, "http://", 7) || !strncasecmp((char *)data, "- at http://", 9)) {
+		timeout = 40000;
 	}
 	/* Wait 1000 ms first */
 	next = ast_tvnow();
@@ -245,7 +343,7 @@
 	}
 	close(fds[0]);
 	close(fds[1]);
-	
+
 	if (pid > -1)
 		kill(pid, SIGKILL);
 	if (!res && owriteformat)
diff -Naur asterisk-1.4.30/channels/chan_sip.c asterisk-1.4.30-hacked/channels/chan_sip.c
--- asterisk-1.4.30/channels/chan_sip.c	2010-02-26 11:04:29.000000000 -0600
+++ asterisk-1.4.30-hacked/channels/chan_sip.c	2011-05-05 13:24:51.000000000 -0500
@@ -18796,7 +18796,7 @@
 		sipsock = -1;
 	}
 	if (sipsock < 0) {
-		sipsock = socket(AF_INET, SOCK_DGRAM, 0);
+		sipsock = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
 		if (sipsock < 0) {
 			ast_log(LOG_WARNING, "Unable to create SIP socket: %s\n", strerror(errno));
 			ast_config_destroy(cfg);
diff -Naur asterisk-1.4.30/contrib/scripts/safe_asterisk asterisk-1.4.30-hacked/contrib/scripts/safe_asterisk
--- asterisk-1.4.30/contrib/scripts/safe_asterisk	2010-01-11 21:18:36.000000000 -0600
+++ asterisk-1.4.30-hacked/contrib/scripts/safe_asterisk	2011-05-05 10:38:04.000000000 -0500
@@ -131,6 +131,9 @@
 		if test "x$EXITSTATUS" = "x0" ; then
 			# Properly shutdown....
 			echo "Asterisk shutdown normally."
+			if test "0$KILLALLMPG123" -gt "0" ; then
+				killall -9 mpg123
+			fi
 			exit 0
 		elif test "0$EXITSTATUS" -gt "128" ; then
 			EXITSIGNAL=$(($EXITSTATUS - 128))
diff -Naur asterisk-1.4.30/funcs/func_curl.c asterisk-1.4.30-hacked/funcs/func_curl.c
--- asterisk-1.4.30/funcs/func_curl.c	2008-10-06 15:52:04.000000000 -0500
+++ asterisk-1.4.30-hacked/funcs/func_curl.c	2011-04-25 14:10:19.000000000 -0500
@@ -82,6 +82,11 @@
 	return realsize;
 }
 
+int mySocketOpen(void *clientp, curlsocktype purpose,  struct curl_sockaddr *address)
+{
+	return socket(address->family, address->socktype | SOCK_CLOEXEC, address->protocol);
+}
+
 static const char *global_useragent = "asterisk-libcurl-agent/1.0";
 
 static void curl_instance_cleanup(void *data)
@@ -110,6 +115,8 @@
 		curl_easy_setopt(*curl, CURLOPT_TIMEOUT, 180);
 		curl_easy_setopt(*curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
 		curl_easy_setopt(*curl, CURLOPT_USERAGENT, global_useragent);
+		curl_easy_setopt(*curl, CURLOPT_FORBID_REUSE, 1);
+		curl_easy_setopt(*curl, CURLOPT_OPENSOCKETFUNCTION, mySocketOpen);
 	}
 
 	curl_easy_setopt(*curl, CURLOPT_URL, url);
diff -Naur asterisk-1.4.30/main/asterisk.c asterisk-1.4.30-hacked/main/asterisk.c
--- asterisk-1.4.30/main/asterisk.c	2010-02-25 15:21:05.000000000 -0600
+++ asterisk-1.4.30-hacked/main/asterisk.c	2011-05-09 13:06:57.000000000 -0500
@@ -1015,14 +1015,14 @@
 			continue;
 		}
 		len = sizeof(sunaddr);
-		s = accept(ast_socket, (struct sockaddr *)&sunaddr, &len);
+		s = accept4(ast_socket, (struct sockaddr *)&sunaddr, &len, SOCK_CLOEXEC);
 		if (s < 0) {
 			if (errno != EINTR)
 				ast_log(LOG_WARNING, "Accept returned %d: %s\n", s, strerror(errno));
 		} else {
 			for (x = 0; x < AST_MAX_CONNECTS; x++) {
 				if (consoles[x].fd < 0) {
-					if (socketpair(AF_LOCAL, SOCK_STREAM, 0, consoles[x].p)) {
+					if (socketpair(AF_LOCAL, SOCK_STREAM|SOCK_CLOEXEC, 0, consoles[x].p)) {
 						ast_log(LOG_ERROR, "Unable to create pipe: %s\n", strerror(errno));
 						consoles[x].fd = -1;
 						fdprint(s, "Server failed to create pipe\n");
@@ -1068,7 +1068,7 @@
 	for (x = 0; x < AST_MAX_CONNECTS; x++)	
 		consoles[x].fd = -1;
 	unlink(ast_config_AST_SOCKET);
-	ast_socket = socket(PF_LOCAL, SOCK_STREAM, 0);
+	ast_socket = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
 	if (ast_socket < 0) {
 		ast_log(LOG_WARNING, "Unable to create control socket: %s\n", strerror(errno));
 		return -1;
@@ -1130,7 +1130,7 @@
 {
 	struct sockaddr_un sunaddr;
 	int res;
-	ast_consock = socket(PF_LOCAL, SOCK_STREAM, 0);
+	ast_consock = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
 	if (ast_consock < 0) {
 		ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
 		return 0;
@@ -3188,7 +3188,7 @@
 	if (ast_opt_no_fork)
 		consolethread = pthread_self();
 
-	if (pipe(sig_alert_pipe))
+	if (pipe2(sig_alert_pipe, O_CLOEXEC))
 		sig_alert_pipe[0] = sig_alert_pipe[1] = -1;
 
 	ast_set_flag(&ast_options, AST_OPT_FLAG_FULLY_BOOTED);
diff -Naur asterisk-1.4.30/main/channel.c asterisk-1.4.30-hacked/main/channel.c
--- asterisk-1.4.30/main/channel.c	2010-02-12 17:30:17.000000000 -0600
+++ asterisk-1.4.30-hacked/main/channel.c	2011-04-25 14:15:54.000000000 -0500
@@ -758,7 +758,7 @@
 #endif					
 
 	if (needqueue) {
-		if (pipe(tmp->alertpipe)) {
+		if (pipe2(tmp->alertpipe,O_CLOEXEC)) {
 			ast_log(LOG_WARNING, "Channel allocation failed: Can't create alert pipe! Try increasing max file descriptors with ulimit -n\n");
 alertpipe_failed:
 #ifdef HAVE_DAHDI
@@ -1591,7 +1591,8 @@
 	}
 			
 	ast_channel_unlock(chan);
-	manager_event(EVENT_FLAG_CALL, "Hangup",
+	//manager_event(EVENT_FLAG_CALL, "Hangup",
+	manager_event(EVENT_FLAG_USER, "Hangup",
 			"Channel: %s\r\n"
 			"Uniqueid: %s\r\n"
 			"Cause: %d\r\n"
diff -Naur asterisk-1.4.30/main/file.c asterisk-1.4.30-hacked/main/file.c
--- asterisk-1.4.30/main/file.c	2010-01-08 13:20:44.000000000 -0600
+++ asterisk-1.4.30-hacked/main/file.c	2011-04-20 13:45:03.000000000 -0500
@@ -457,6 +457,7 @@
 			 */
 			if (action == ACTION_OPEN) {
 				struct ast_channel *chan = (struct ast_channel *)arg2;
+				int fd;
 				FILE *bfile;
 				struct ast_filestream *s;
 
@@ -465,7 +466,14 @@
 					free(fn);
 					continue;	/* not a supported format */
 				}
-				if ( (bfile = fopen(fn, "r")) == NULL) {
+				fd = open(fn, O_RDONLY|O_CLOEXEC);
+				if (fd > -1) {
+					if ( (bfile = fdopen(fd, "r")) == NULL) {
+						free(fn);
+						close(fd);
+						continue;	/* cannot open file */
+					}
+				} else {
 					free(fn);
 					continue;	/* cannot open file */
 				}
@@ -1080,7 +1088,7 @@
 			format_found = 1;
 
 		fn = build_filename(filename, type);
-		fd = open(fn, flags | myflags, mode);
+		fd = open(fn, flags | myflags | O_CLOEXEC, mode);
 		if (fd > -1) {
 			/* fdopen() the resulting file stream */
 			bfile = fdopen(fd, ((flags | myflags) & O_RDWR) ? "w+" : "w");
diff -Naur asterisk-1.4.30/main/logger.c asterisk-1.4.30-hacked/main/logger.c
--- asterisk-1.4.30/main/logger.c	2010-02-24 15:02:18.000000000 -0600
+++ asterisk-1.4.30-hacked/main/logger.c	2011-05-12 13:35:42.000000000 -0500
@@ -38,6 +38,7 @@
 #include <stdlib.h>
 #include <errno.h>
 #include <sys/stat.h>
+#include <fcntl.h>
 #if ((defined(AST_DEVMODE)) && (defined(linux)))
 #include <execinfo.h>
 #define MAX_BACKTRACE_FRAMES 20
@@ -264,7 +265,8 @@
 			snprintf(chan->filename, sizeof(chan->filename), "%s/%s",
 				 channel[0] != '/' ? ast_config_AST_LOG_DIR : "", channel);
 		}
-		chan->fileptr = fopen(chan->filename, "a");
+		int fd = open((char *)(chan->filename), O_CREAT | O_APPEND | O_WRONLY | O_CLOEXEC, 0644);
+		chan->fileptr = fdopen(fd, "a");
 		if (!chan->fileptr) {
 			/* Can't log here, since we're called with a lock */
 			fprintf(stderr, "Logger Warning: Unable to open log file '%s': %s\n", chan->filename, strerror(errno));
@@ -432,7 +434,8 @@
 				ast_log(LOG_ERROR, "Unable to rename file '%s' to '%s'\n", old, new);
 		}
 
-		eventlog = fopen(old, "a");
+		int efd = open((char *)old, O_CREAT | O_APPEND | O_WRONLY | O_CLOEXEC, 0644);
+		eventlog = fdopen(efd, "a");
 		if (eventlog) {
 			ast_log(LOG_EVENT, "Restarted Asterisk Event Logger\n");
 			if (option_verbose)
@@ -460,7 +463,8 @@
 				ast_log(LOG_ERROR, "Unable to rename file '%s' to '%s'\n", old, new);
 		}
 
-		qlog = fopen(old, "a");
+		int qfd = open((char *)old, O_CREAT | O_APPEND | O_WRONLY | O_CLOEXEC, 0644);
+		qlog = fdopen(qfd, "a");
 		if (qlog) {
 			ast_queue_log("NONE", "NONE", "NONE", "CONFIGRELOAD", "%s", "");
 			ast_log(LOG_EVENT, "Restarted Asterisk Queue Logger\n");
@@ -600,7 +604,8 @@
 	if (logfiles.event_log) {
 		mkdir((char *)ast_config_AST_LOG_DIR, 0755);
 		snprintf(tmp, sizeof(tmp), "%s/%s", (char *)ast_config_AST_LOG_DIR, EVENTLOG);
-		eventlog = fopen((char *)tmp, "a");
+		int efd = open((char *)tmp, O_CREAT | O_APPEND | O_WRONLY | O_CLOEXEC, 0644);
+		eventlog = fdopen(efd, "a");
 		if (eventlog) {
 			ast_log(LOG_EVENT, "Started Asterisk Event Logger\n");
 			if (option_verbose)
@@ -613,7 +618,8 @@
 
 	if (logfiles.queue_log) {
 		snprintf(tmp, sizeof(tmp), "%s/%s", (char *)ast_config_AST_LOG_DIR, QUEUELOG);
-		qlog = fopen(tmp, "a");
+		int qfd = open((char *)tmp, O_CREAT | O_APPEND | O_WRONLY | O_CLOEXEC, 0644);
+		qlog = fdopen(qfd, "a");
 		ast_queue_log("NONE", "NONE", "NONE", "QUEUESTART", "%s", "");
 	}
 	return res;
diff -Naur asterisk-1.4.30/main/manager.c asterisk-1.4.30-hacked/main/manager.c
--- asterisk-1.4.30/main/manager.c	2010-01-25 14:08:32.000000000 -0600
+++ asterisk-1.4.30-hacked/main/manager.c	2011-04-25 13:33:42.000000000 -0500
@@ -2493,7 +2493,7 @@
 		   we can ditch any old manager sessions */
 		if (ast_poll(pfds, 1, 5000) < 1)
 			continue;
-		as = accept(asock, (struct sockaddr *)&sin, &sinlen);
+		as = accept4(asock, (struct sockaddr *)&sin, &sinlen,SOCK_CLOEXEC);
 		if (as < 0) {
 			ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno));
 			continue;
@@ -3289,7 +3289,7 @@
 		return 0;
 
 	if (asock < 0) {
-		asock = socket(AF_INET, SOCK_STREAM, 0);
+		asock = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
 		if (asock < 0) {
 			ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
 			return -1;
diff -Naur asterisk-1.4.30/main/rtp.c asterisk-1.4.30-hacked/main/rtp.c
--- asterisk-1.4.30/main/rtp.c	2010-02-20 16:25:42.000000000 -0600
+++ asterisk-1.4.30-hacked/main/rtp.c	2011-04-18 14:33:09.000000000 -0500
@@ -1905,7 +1905,7 @@
 {
 	int s;
 	long flags;
-	s = socket(AF_INET, SOCK_DGRAM, 0);
+	s = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
 	if (s > -1) {
 		flags = fcntl(s, F_GETFL);
 		fcntl(s, F_SETFL, flags | O_NONBLOCK);
diff -Naur asterisk-1.4.30/res/res_agi.c asterisk-1.4.30-hacked/res/res_agi.c
--- asterisk-1.4.30/res/res_agi.c	2010-01-04 12:19:00.000000000 -0600
+++ asterisk-1.4.30-hacked/res/res_agi.c	2011-05-12 09:29:22.000000000 -0500
@@ -34,6 +34,9 @@
 #include <sys/types.h>
 #include <netdb.h>
 #include <sys/socket.h>
+
+#include <sys/un.h>
+
 #include <netinet/in.h>
 #include <netinet/tcp.h>
 #include <arpa/inet.h>
@@ -253,12 +256,10 @@
 	return AGI_RESULT_SUCCESS_FAST;
 }
 
-static enum agi_result launch_script(char *script, char *argv[], int *fds, int *efd, int *opid)
+static enum agi_result launch_script(struct ast_channel *chan, char *script, char *argv[], int *fds, int *efd, int *opid)
 {
 	char tmp[256];
 	int pid;
-	int toast[2];
-	int fromast[2];
 	int audio[2];
 	int x;
 	int res;
@@ -271,23 +272,9 @@
 		snprintf(tmp, sizeof(tmp), "%s/%s", (char *)ast_config_AST_AGI_DIR, script);
 		script = tmp;
 	}
-	if (pipe(toast)) {
-		ast_log(LOG_WARNING, "Unable to create toast pipe: %s\n",strerror(errno));
-		return AGI_RESULT_FAILURE;
-	}
-	if (pipe(fromast)) {
-		ast_log(LOG_WARNING, "unable to create fromast pipe: %s\n", strerror(errno));
-		close(toast[0]);
-		close(toast[1]);
-		return AGI_RESULT_FAILURE;
-	}
 	if (efd) {
 		if (pipe(audio)) {
 			ast_log(LOG_WARNING, "unable to create audio pipe: %s\n", strerror(errno));
-			close(fromast[0]);
-			close(fromast[1]);
-			close(toast[0]);
-			close(toast[1]);
 			return AGI_RESULT_FAILURE;
 		}
 		res = fcntl(audio[1], F_GETFL);
@@ -295,16 +282,31 @@
 			res = fcntl(audio[1], F_SETFL, res | O_NONBLOCK);
 		if (res < 0) {
 			ast_log(LOG_WARNING, "unable to set audio pipe parameters: %s\n", strerror(errno));
-			close(fromast[0]);
-			close(fromast[1]);
-			close(toast[0]);
-			close(toast[1]);
 			close(audio[0]);
 			close(audio[1]);
 			return AGI_RESULT_FAILURE;
 		}
 	}
 
+	//
+	// create and bind unix socket for file descriptors
+	//
+
+	int ast_sock;
+	if ((ast_sock = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0)) < 0) {
+		ast_log(LOG_ERROR, "unable to create ast socket: %s\n", strerror(errno));
+		return AGI_RESULT_FAILURE;
+	}
+
+	struct sockaddr_un ast_address;
+	ast_address.sun_family = AF_UNIX;
+	sprintf(ast_address.sun_path, "/tmp/asterisk-%s",chan->uniqueid);
+
+	if((res = bind(ast_sock, (const struct sockaddr *) &ast_address, sizeof(struct sockaddr_un))) != 0) {
+		ast_log(LOG_ERROR, "ast_sock bind failed, %s\n", strerror(errno));
+		return AGI_RESULT_FAILURE;
+	}
+
 	/* Block SIGHUP during the fork - prevents a race */
 	sigfillset(&signal_set);
 	pthread_sigmask(SIG_BLOCK, &signal_set, &old_set);
@@ -312,9 +314,72 @@
 	if (pid < 0) {
 		ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno));
 		pthread_sigmask(SIG_SETMASK, &old_set, NULL);
+
+		close(ast_sock);
+
 		return AGI_RESULT_FAILURE;
 	}
 	if (!pid) {
+
+		//
+		// create and connect unix socket for file descriptors
+		//
+
+		int agi_sock;
+		if ((agi_sock = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
+			ast_log(LOG_ERROR, "unable to create agi socket: %s\n", strerror(errno));
+			_exit(1);
+		}
+
+		if((res = connect(agi_sock, (const struct sockaddr *) &ast_address, sizeof(struct sockaddr_un))) != 0) {
+			ast_log(LOG_ERROR, "agi_sock unable to connect agi sock: %s\n", strerror(errno));
+			_exit(1);
+		}
+
+		//
+		// send toast/fromast file descriptors to asterisk
+		//
+
+		int agi_fds[2] = { 0, };
+		int toast[2] = { 0, };
+		int fromast[2] = { 0, };
+
+		if (!pipe2(toast,O_CLOEXEC)) {
+			agi_fds[0] = toast[0];
+		} else {
+			ast_log(LOG_ERROR, "Unable to create toast pipe: %s\n",strerror(errno));
+			_exit(1);
+		}
+		if (!pipe2(fromast,O_CLOEXEC)) {
+			agi_fds[1] = fromast[1];
+		} else {
+			ast_log(LOG_ERROR, "unable to create fromast pipe: %s\n", strerror(errno));
+			_exit(1);
+		}
+
+		char agi_data[1] = { '*' };
+		struct iovec agi_iov[1] = { { .iov_base = agi_data, .iov_len = 1 } };
+
+		char agi_buf[CMSG_SPACE(2 * sizeof(int))] = { 0, };
+
+		struct msghdr agi_message = { .msg_iov = agi_iov, .msg_iovlen = 1, .msg_control = agi_buf, .msg_controllen = sizeof agi_buf };
+
+		struct cmsghdr *agi_cmsg = CMSG_FIRSTHDR(&agi_message);
+		agi_cmsg->cmsg_level = SOL_SOCKET;
+		agi_cmsg->cmsg_type = SCM_RIGHTS;
+		agi_cmsg->cmsg_len = CMSG_LEN(2 * sizeof(int));
+
+		int *agi_cmsg_fds_ptr = (int *)CMSG_DATA(agi_cmsg);
+		memcpy(agi_cmsg_fds_ptr, agi_fds, 2 * sizeof(int));
+		agi_message.msg_controllen = agi_cmsg->cmsg_len;
+
+		if ((res = sendmsg(agi_sock, &agi_message, 0)) < 0) {
+			ast_log(LOG_ERROR, "agi fds sendmsg failed: %s\n", strerror(errno));
+			_exit(1);
+		}
+
+		close(agi_sock);
+
 #ifdef HAVE_CAP
 		cap_t cap = cap_from_text("cap_net_admin-eip");
 
@@ -366,9 +431,10 @@
 		}
 
 		/* Close everything but stdin/out/error */
+		/*
 		for (x=STDERR_FILENO + 2;x<1024;x++) 
 			close(x);
-
+		*/
 		/* Execute script */
 		execv(script, argv);
 		/* Can't use ast_log since FD's are closed */
@@ -379,16 +445,35 @@
 		_exit(1);
 	}
 	pthread_sigmask(SIG_SETMASK, &old_set, NULL);
+
+	//
+	//receive toast/fromast file descriptors from agi process
+	//
+
+	char ast_data[1] = { 0, };
+	struct iovec ast_iov[1] = { { .iov_base = ast_data, .iov_len = 1 } };
+
+	char ast_buf[CMSG_SPACE(2 * sizeof(int))] = { 0, };
+
+	struct msghdr ast_message = { .msg_iov = ast_iov, .msg_iovlen = 1, .msg_control = ast_buf, .msg_controllen = CMSG_LEN(2 * sizeof(int)) };
+
+	if ((res = recvmsg(ast_sock, &ast_message, MSG_CMSG_CLOEXEC)) < 0) {
+		ast_log(LOG_ERROR, "ast fds recvmsg failed: %s\n", strerror(errno));
+		return AGI_RESULT_FAILURE;
+	}
+
+	struct cmsghdr *ast_cmsg = CMSG_FIRSTHDR(&ast_message);
+	memcpy(fds,CMSG_DATA(ast_cmsg),2 * sizeof(int));
+
+	close(ast_sock);
+
+	unlink(ast_address.sun_path);
+
 	if (option_verbose > 2) 
 		ast_verbose(VERBOSE_PREFIX_3 "Launched AGI Script %s\n", script);
-	fds[0] = toast[0];
-	fds[1] = fromast[1];
 	if (efd) {
 		*efd = audio[1];
 	}
-	/* close what we're not using in the parent */
-	close(toast[1]);
-	close(fromast[0]);
 
 	if (efd)
 		close(audio[0]);
@@ -2143,7 +2228,7 @@
 	}
 #endif
 	ast_replace_sigchld();
-	res = launch_script(argv[0], argv, fds, enhanced ? &efd : NULL, &pid);
+	res = launch_script(chan, argv[0], argv, fds, enhanced ? &efd : NULL, &pid);
 	if (res == AGI_RESULT_SUCCESS || res == AGI_RESULT_SUCCESS_FAST) {
 		int status = 0;
 		agi.fd = fds[1];


-- 
Paul Albrecht




More information about the asterisk-dev mailing list