pass_fd.c
5b532c7f
 /*
53c7e0f1
  * Copyright (C) 2001-2003 FhG Fokus
5b532c7f
  *
6a0f4382
  * This file is part of Kamailio, a free SIP server.
5b532c7f
  *
6a0f4382
  * Kamailio is free software; you can redistribute it and/or modify
5b532c7f
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version
  *
6a0f4382
  * Kamailio is distributed in the hope that it will be useful,
5b532c7f
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
9e1ff448
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
5b532c7f
  */
 
1d0661db
 /*!
  * \file
6a0f4382
  * \brief Kamailio core :: 
1d0661db
  * \ingroup core
  * Module: \ref core
  */
 
5b532c7f
 #ifdef USE_TCP
 
3d4bb4b1
 #include "pass_fd.h"
 
5b532c7f
 #include <sys/types.h>
 #include <sys/socket.h>
b988daef
 #include <sys/uio.h>
 #include <stdlib.h> /* for NULL definition on openbsd */
06aaa54f
 #include <errno.h>
 #include <string.h>
3d4bb4b1
 #ifdef NO_MSG_WAITALL
 #include <poll.h>
 #endif /* NO_MSG_WAITALL */
5b532c7f
 
b988daef
 #include "dprint.h"
5b532c7f
 
 
06aaa54f
 
6a0f4382
 /** receive all the data or returns error (handles EINTR etc.)
0ba367ec
  * params: socket
  *         data     - buffer for the results
  *         data_len - 
  *         flags    - recv flags for the first recv (see recv(2)), only
  *                    0, MSG_WAITALL and MSG_DONTWAIT make sense
  * if flags is set to MSG_DONWAIT (or to 0 and the socket fd is non-blocking),
  * and if no data is queued on the fd, recv_all will not wait (it will 
  * return error and set errno to EAGAIN/EWOULDBLOCK). However if even 1 byte
  *  is queued, the call will block until the whole data_len was read or an
  *  error or eof occured ("semi-nonblocking" behaviour,  some tcp code
  *   counts on it).
  * if flags is set to MSG_WAITALL it will block even if no byte is available.
  *  
06aaa54f
  * returns: bytes read or error (<0)
  * can return < data_len if EOF */
0ba367ec
 int recv_all(int socket, void* data, int data_len, int flags)
06aaa54f
 {
 	int b_read;
 	int n;
3d4bb4b1
 #ifdef NO_MSG_WAITALL
 	struct pollfd pfd;
 #endif /* NO_MSG_WAITALL */
06aaa54f
 	
 	b_read=0;
0ba367ec
 again:
3d4bb4b1
 #ifdef NO_MSG_WAITALL
 	if (flags & MSG_WAITALL){
 		n=-1;
 		goto poll_recv; /* simulate MSG_WAITALL */
 	}
 #endif /* NO_MSG_WAITALL */
0ba367ec
 	n=recv(socket, (char*)data, data_len, flags);
 	if (n<0){
 		/* error */
 		if (errno==EINTR) goto again; /* signal, try again */
 		/* on EAGAIN just return (let the caller know) */
 		if ((errno==EAGAIN)||(errno==EWOULDBLOCK)) return n;
a7652a58
 			LM_CRIT("1st recv on %d failed: %s\n",
0ba367ec
 					socket, strerror(errno));
 			return n;
 	}
 	b_read+=n;
 	while( (b_read!=data_len) && (n)){
3d4bb4b1
 #ifdef NO_MSG_WAITALL
 		/* cygwin & win do not support MSG_WAITALL => workaround using poll */
 poll_recv:
 		n=recv(socket, (char*)data+b_read, data_len-b_read, 0);
 #else /* NO_MSG_WAITALL */
b23a7a00
 		n=recv(socket, (char*)data+b_read, data_len-b_read, MSG_WAITALL);
3d4bb4b1
 #endif /* NO_MSG_WAITALL */
06aaa54f
 		if (n<0){
 			/* error */
 			if (errno==EINTR) continue; /* signal, try again */
3d4bb4b1
 #ifdef NO_MSG_WAITALL
 			if (errno==EAGAIN || errno==EWOULDBLOCK){
 				/* emulate MSG_WAITALL using poll */
 				pfd.fd=socket;
 				pfd.events=POLLIN;
 poll_retry:
 				n=poll(&pfd, 1, -1);
 				if (n<0){ 
 					if (errno==EINTR) goto poll_retry;
a7652a58
 					LM_CRIT("poll on %d failed: %s\n",
 						socket, strerror(errno));
3d4bb4b1
 					return n;
 				} else continue; /* try recv again */
 			}
 #endif /* NO_MSG_WAITALL */
a7652a58
 			LM_CRIT("2nd recv on %d failed: %s\n",
06aaa54f
 					socket, strerror(errno));
 			return n;
 		}
 		b_read+=n;
0ba367ec
 	}
06aaa54f
 	return b_read;
 }
 
 
0ba367ec
 
6a0f4382
 /** sends all data (takes care of signals) (assumes blocking fd)
06aaa54f
  * returns number of bytes sent or < 0 for an error */
 int send_all(int socket, void* data, int data_len)
 {
 	int n;
 	
 again:
 	n=send(socket, data, data_len, 0);
 	if (n<0){
 			/* error */
 		if (errno==EINTR) goto again; /* signal, try again */
28260509
 		if ((errno!=EAGAIN) &&(errno!=EWOULDBLOCK))
a7652a58
 			LM_CRIT("send on %d failed: %s\n",
06aaa54f
 					socket, strerror(errno));
 	}
 	return n;
 }
 
 
6a0f4382
 /** at least 1 byte must be sent! */
5b532c7f
 int send_fd(int unix_socket, void* data, int data_len, int fd)
 {
 	struct msghdr msg;
 	struct iovec iov[1];
 	int ret;
6c6659cb
 #ifdef HAVE_MSGHDR_MSG_CONTROL
a19584f9
 	int* pi;
6c6659cb
 	struct cmsghdr* cmsg;
1d5ef189
 	/* make sure msg_control will point to properly aligned data */
5b532c7f
 	union {
 		struct cmsghdr cm;
 		char control[CMSG_SPACE(sizeof(fd))];
 	}control_un;
 	
 	msg.msg_control=control_un.control;
1d5ef189
 	/* openbsd doesn't like "more space", msg_controllen must not
 	 * include the end padding */
 	msg.msg_controllen=CMSG_LEN(sizeof(fd));
5b532c7f
 	
6c6659cb
 	cmsg=CMSG_FIRSTHDR(&msg);
 	cmsg->cmsg_level = SOL_SOCKET;
 	cmsg->cmsg_type = SCM_RIGHTS;
 	cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
a19584f9
 	pi=(int*)CMSG_DATA(cmsg);
 	*pi=fd;
6c6659cb
 	msg.msg_flags=0;
 #else
 	msg.msg_accrights=(caddr_t) &fd;
 	msg.msg_accrightslen=sizeof(fd);
 #endif
 	
5b532c7f
 	msg.msg_name=0;
 	msg.msg_namelen=0;
 	
 	iov[0].iov_base=data;
 	iov[0].iov_len=data_len;
 	msg.msg_iov=iov;
 	msg.msg_iovlen=1;
 	
06aaa54f
 again:
5b532c7f
 	ret=sendmsg(unix_socket, &msg, 0);
06aaa54f
 	if (ret<0){
 		if (errno==EINTR) goto again;
28260509
 		if ((errno!=EAGAIN) && (errno!=EWOULDBLOCK))
a7652a58
 			LM_CRIT("sendmsg failed sending %d on %d: %s (%d)\n",
 				fd, unix_socket, strerror(errno), errno);
06aaa54f
 	}
5b532c7f
 	
 	return ret;
 }
 
 
 
6a0f4382
 /** receives a fd and data_len data
0ba367ec
  * params: unix_socket 
  *         data
  *         data_len
  *         fd         - will be set to the passed fd value or -1 if no fd
  *                      was passed
  *         flags      - 0, MSG_DONTWAIT, MSG_WAITALL; same as recv_all flags
  * returns: bytes read on success, -1 on error (and sets errno) */
 int receive_fd(int unix_socket, void* data, int data_len, int* fd, int flags)
5b532c7f
 {
 	struct msghdr msg;
 	struct iovec iov[1];
 	int new_fd;
 	int ret;
06aaa54f
 	int n;
3d4bb4b1
 #ifdef NO_MSG_WAITALL
 	struct pollfd pfd;
 	int f;
 #endif /*NO_MSG_WAITALL */
6c6659cb
 #ifdef HAVE_MSGHDR_MSG_CONTROL
a19584f9
 	int* pi;
6c6659cb
 	struct cmsghdr* cmsg;
5b532c7f
 	union{
 		struct cmsghdr cm;
 		char control[CMSG_SPACE(sizeof(new_fd))];
 	}control_un;
 	
 	msg.msg_control=control_un.control;
 	msg.msg_controllen=sizeof(control_un.control);
6c6659cb
 #else
 	msg.msg_accrights=(caddr_t) &new_fd;
 	msg.msg_accrightslen=sizeof(int);
 #endif
5b532c7f
 	
 	msg.msg_name=0;
 	msg.msg_namelen=0;
 	
 	iov[0].iov_base=data;
 	iov[0].iov_len=data_len;
 	msg.msg_iov=iov;
 	msg.msg_iovlen=1;
 	
3d4bb4b1
 #ifdef NO_MSG_WAITALL
 	f=flags & ~MSG_WAITALL;
 #endif /* NO_MSG_WAITALL */
 
06aaa54f
 again:
3d4bb4b1
 #ifdef NO_MSG_WAITALL
 		ret=recvmsg(unix_socket, &msg, f);
 #else /* NO_MSG_WAITALL */
 		ret=recvmsg(unix_socket, &msg, flags);
 #endif /* NO_MSG_WAITALL */
06aaa54f
 	if (ret<0){
 		if (errno==EINTR) goto again;
3d4bb4b1
 		if ((errno==EAGAIN)||(errno==EWOULDBLOCK)){
 #ifdef NO_MSG_WAITALL
 			if (flags & MSG_WAITALL){
 				/* emulate MSG_WAITALL using poll */
 				pfd.fd=unix_socket;
 				pfd.events=POLLIN;
 poll_again:
 				ret=poll(&pfd, 1, -1);
 				if (ret>=0) goto again;
 				else if (errno==EINTR) goto poll_again;
a7652a58
 				LM_CRIT("poll on %d failed: %s\n",
 					unix_socket, strerror(errno));
3d4bb4b1
 			}
 #endif /* NO_MSG_WAITALL */
 			goto error;
 		}
a7652a58
 		LM_CRIT("recvmsg on %d failed: %s\n",
06aaa54f
 				unix_socket, strerror(errno));
 		goto error;
 	}
 	if (ret==0){
 		/* EOF */
a7652a58
 		LM_CRIT("EOF on %d\n", unix_socket);
06aaa54f
 		goto error;
 	}
 	if (ret<data_len){
a7652a58
 		LM_WARN("too few bytes read (%d from %d) trying to fix...\n",
 				ret, data_len);
0ba367ec
 		/* blocking recv_all */
 		n=recv_all(unix_socket, (char*)data+ret, data_len-ret, MSG_WAITALL);
06aaa54f
 		if (n>=0) ret+=n;
 		else{
 			ret=n;
 			goto error;
 		}
 	}
5b532c7f
 	
6c6659cb
 #ifdef HAVE_MSGHDR_MSG_CONTROL
5b532c7f
 	cmsg=CMSG_FIRSTHDR(&msg);
 	if ((cmsg!=0) && (cmsg->cmsg_len==CMSG_LEN(sizeof(new_fd)))){
 		if (cmsg->cmsg_type!= SCM_RIGHTS){
a7652a58
 			LM_ERR("msg control type != SCM_RIGHTS\n");
5b532c7f
 			ret=-1;
 			goto error;
 		}
 		if (cmsg->cmsg_level!= SOL_SOCKET){
a7652a58
 			LM_ERR("msg level != SOL_SOCKET\n");
5b532c7f
 			ret=-1;
 			goto error;
 		}
a19584f9
 		pi=(int*) CMSG_DATA(cmsg);
 		*fd=*pi;
5b532c7f
 	}else{
a7652a58
 		/*LM_ERR("no descriptor passed, cmsg=%p, len=%d\n",
 			cmsg, (unsigned)cmsg->cmsg_len); */
5b532c7f
 		*fd=-1;
 		/* it's not really an error */
 	}
6c6659cb
 #else
 	if (msg.msg_accrightslen==sizeof(int)){
 		*fd=new_fd;
 	}else{
a7652a58
 		/*LM_ERR("no descriptor passed, accrightslen=%d\n",
 			msg.msg_accrightslen); */
6c6659cb
 		*fd=-1;
 	}
 #endif
5b532c7f
 	
 error:
 	return ret;
 }
 #endif