mirror of
https://github.com/game-stop/veejay.git
synced 2025-12-18 13:49:58 +01:00
added threadsafe gethostbyname fix bug in reloaded set keyframes drop vj_client_send_buf* functions drop vj_client_setup_timeout keep thread local buffer to store incoming frame (unicast stream) recover gracefully when lock or unlock fails (unicast stream) adapt poll frequency to find acceptable delay times recover gracefully from network errors retry to create connection when it fails while playing destroy thread when locking functions fail simpler get_frame for tag dropped multicast stream for now fix veejay_strncpy, null terminate string drop use of bzero, use memset instead rewrite part of cmd, socket peer-to-peer rewrite VIMS verify message rewrite VIMS parsing message
365 lines
8.1 KiB
C
365 lines
8.1 KiB
C
/* vjnet - low level network I/O for VeeJay
|
|
*
|
|
* (C) 2005 Niels Elburg <nwelburg@gmail.com>
|
|
*
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* 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.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* 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
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
#include <config.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include "cmd.h"
|
|
#include <libvjmsg/vj-msg.h>
|
|
#include <sys/types.h>
|
|
#include <sys/utsname.h>
|
|
#include <pthread.h>
|
|
#ifdef STRICT_CHECKING
|
|
#include <assert.h>
|
|
#endif
|
|
|
|
|
|
struct host_list {
|
|
struct hostent hostent;
|
|
char h_addr_space[1024];
|
|
};
|
|
|
|
static int ref_count = 0;
|
|
static pthread_key_t ghbn_key;
|
|
static pthread_once_t ghbn_key_once = PTHREAD_ONCE_INIT;
|
|
|
|
static void ghbn_cleanup(void *data) {
|
|
struct host_list *current = (struct host_list *) data;
|
|
ref_count--;
|
|
free(current);
|
|
}
|
|
|
|
static void create_ghbn_key() {
|
|
pthread_key_create(&ghbn_key, ghbn_cleanup);
|
|
}
|
|
|
|
struct hostent *sock_gethostbyname(const char *name) {
|
|
struct hostent *result;
|
|
int local_errno;
|
|
|
|
pthread_once(&ghbn_key_once, create_ghbn_key);
|
|
|
|
struct host_list *current = (struct host_list *) pthread_getspecific(ghbn_key);
|
|
|
|
if (!current) {
|
|
current = (struct host_list *) calloc(1, sizeof(struct host_list));
|
|
current->hostent.h_name = "busy";
|
|
ref_count++;
|
|
pthread_setspecific(ghbn_key, current);
|
|
}
|
|
|
|
if (gethostbyname_r(name, &(current->hostent), current->h_addr_space,sizeof(current->h_addr_space),&result, &local_errno)) {
|
|
h_errno = local_errno;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
vj_sock_t *alloc_sock_t(void)
|
|
{
|
|
vj_sock_t *s = (vj_sock_t*) malloc(sizeof(vj_sock_t));
|
|
memset( s, 0, sizeof(vj_sock_t));
|
|
if(!s) return NULL;
|
|
return s;
|
|
}
|
|
|
|
void sock_t_free(vj_sock_t *s )
|
|
{
|
|
if(s) free(s);
|
|
}
|
|
|
|
#define TIMEOUT 3
|
|
|
|
int sock_t_connect_and_send_http( vj_sock_t *s, char *host, int port, char *buf, int buf_len )
|
|
{
|
|
s->he = sock_gethostbyname( host );
|
|
if(s->he==NULL)
|
|
return 0;
|
|
s->sock_fd = socket( AF_INET, SOCK_STREAM , 0);
|
|
if(s->sock_fd < 0)
|
|
{
|
|
return 0;
|
|
}
|
|
s->port_num = port;
|
|
s->addr.sin_family = AF_INET;
|
|
s->addr.sin_port = htons( port );
|
|
s->addr.sin_addr = *( (struct in_addr*) s->he->h_addr );
|
|
if( connect( s->sock_fd, (struct sockaddr*) &s->addr,
|
|
sizeof( struct sockaddr )) == -1 )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
struct sockaddr_in sinfo;
|
|
socklen_t sinfolen=0;
|
|
char server_name[1024];
|
|
if( getsockname(s->sock_fd,(struct sockaddr*) &sinfo,&sinfolen)==0) {
|
|
char *tmp = inet_ntoa( sinfo.sin_addr );
|
|
strncpy( server_name, tmp, 1024);
|
|
} else {
|
|
return 0;
|
|
}
|
|
|
|
int len = strlen(server_name) + 128 + buf_len;
|
|
char *msg = (char*) malloc(sizeof(char) * len );
|
|
struct utsname name;
|
|
if( uname(&name) == -1 ) {
|
|
snprintf(msg,len,"%s%s/veejay-%s\n\n",buf,server_name,PACKAGE_VERSION);
|
|
} else {
|
|
snprintf(msg,len,"%s%s/veejay-%s/%s-%s\n\n",buf,server_name,PACKAGE_VERSION,name.sysname,name.release );
|
|
}
|
|
int msg_len = strlen(msg);
|
|
int n = send(s->sock_fd,msg,msg_len, 0 );
|
|
free(msg);
|
|
|
|
if( n == -1 )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
int sock_t_connect( vj_sock_t *s, char *host, int port )
|
|
{
|
|
s->he = sock_gethostbyname( host );
|
|
|
|
if(s->he==NULL)
|
|
return 0;
|
|
|
|
s->sock_fd = socket( AF_INET, SOCK_STREAM , 0);
|
|
if(s->sock_fd < 0)
|
|
{
|
|
veejay_msg(VEEJAY_MSG_ERROR, "Socket error with Veejay host %s:%d %s ", host,port,strerror(errno));
|
|
return 0;
|
|
}
|
|
s->port_num = port;
|
|
s->addr.sin_family = AF_INET;
|
|
s->addr.sin_port = htons( port );
|
|
|
|
s->addr.sin_addr = *( (struct in_addr*) s->he->h_addr );
|
|
|
|
if( connect( s->sock_fd, (struct sockaddr*) &s->addr,
|
|
sizeof( struct sockaddr )) == -1 )
|
|
{
|
|
veejay_msg(VEEJAY_MSG_ERROR, "Connection error with Veejay host %s:%d %s",
|
|
host, port, strerror(errno));
|
|
return 0;
|
|
}
|
|
unsigned int tmp = sizeof(int);
|
|
if( getsockopt( s->sock_fd , SOL_SOCKET, SO_SNDBUF, (unsigned char*) &(s->send_size), &tmp) < 0 )
|
|
{
|
|
veejay_msg(VEEJAY_MSG_ERROR, "Unable to get buffer size for output: %s", strerror(errno));
|
|
return 0;
|
|
}
|
|
if( getsockopt( s->sock_fd, SOL_SOCKET, SO_RCVBUF, (unsigned char*) &(s->recv_size), &tmp) < 0 )
|
|
{
|
|
veejay_msg(VEEJAY_MSG_ERROR, "Unable to get buffer size for input: %s", strerror(errno));
|
|
return 0;
|
|
}
|
|
|
|
veejay_msg(VEEJAY_MSG_DEBUG, "Connected to host '%s' port %d, fd %d", host,port,s->sock_fd );
|
|
|
|
return 1;
|
|
}
|
|
|
|
int sock_t_wds_isset( vj_sock_t *s ) {
|
|
return FD_ISSET( s->sock_fd, &(s->wds));
|
|
}
|
|
|
|
int sock_t_rds_isset( vj_sock_t *s ) {
|
|
return FD_ISSET( s->sock_fd, &(s->rds) );
|
|
}
|
|
|
|
int sock_t_poll( vj_sock_t *s )
|
|
{
|
|
int status;
|
|
struct timeval no_wait;
|
|
memset( &no_wait, 0, sizeof(no_wait) );
|
|
|
|
FD_ZERO( &(s->rds) );
|
|
FD_ZERO( &(s->wds) );
|
|
|
|
FD_SET( s->sock_fd, &(s->rds) );
|
|
FD_SET( s->sock_fd, &(s->wds) );
|
|
|
|
status = select( s->sock_fd + 1, &(s->rds),&(s->wds), 0, &no_wait );
|
|
|
|
if( status < 0 )
|
|
{
|
|
veejay_msg(VEEJAY_MSG_ERROR, "Unable to poll socket for immediate read: %s", strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
if( sock_t_rds_isset( s ) )
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
static int timed_recv( int fd, void *buf, const int len, int timeout )
|
|
{
|
|
fd_set fds;
|
|
int n;
|
|
|
|
struct timeval tv;
|
|
memset( &tv, 0,sizeof(timeval));
|
|
FD_ZERO(&fds);
|
|
FD_SET( fd,&fds );
|
|
|
|
tv.tv_sec = TIMEOUT;
|
|
|
|
n = select( fd + 1, &fds, NULL, NULL, &tv );
|
|
if( n == 0 ) {
|
|
veejay_msg(VEEJAY_MSG_DEBUG, "\tsocket %x :: requested %d bytes", fd, len );
|
|
}
|
|
|
|
if( n == -1 )
|
|
return -1;
|
|
|
|
if( n == 0 )
|
|
return -5;
|
|
|
|
return recv( fd, buf, len, 0 );
|
|
}*/
|
|
|
|
void sock_t_set_timeout( vj_sock_t *s, int t )
|
|
{
|
|
int opt = t;
|
|
setsockopt( s->sock_fd, SOL_SOCKET, SO_SNDTIMEO, (char*) &opt, sizeof(int));
|
|
setsockopt( s->sock_fd, SOL_SOCKET, SO_RCVTIMEO, (char*) &opt, sizeof(int));
|
|
}
|
|
|
|
int sock_t_recv( vj_sock_t *s, void *dst, int len )
|
|
{
|
|
int done = 0;
|
|
int bytes_left = s->recv_size;
|
|
int n;
|
|
|
|
if( len < bytes_left )
|
|
bytes_left = len;
|
|
|
|
while( done < len )
|
|
{
|
|
//@ setup socket with SO_RCVTIMEO
|
|
n = recv( s->sock_fd, dst+done,bytes_left, 0 );
|
|
if ( n <= 0 ) {
|
|
return -1;
|
|
}
|
|
|
|
done += n;
|
|
|
|
if( (len-done) < s->recv_size )
|
|
bytes_left = len - done;
|
|
}
|
|
return done;
|
|
}
|
|
|
|
int sock_t_send( vj_sock_t *s, unsigned char *buf, int len )
|
|
{
|
|
int n;
|
|
#ifdef STRICT_CHECKING
|
|
assert( buf != NULL );
|
|
|
|
if( sock_t_wds_isset( s ) == 0 ) {
|
|
veejay_msg(VEEJAY_MSG_DEBUG, "%s: socket %d", __FUNCTION__, s->sock_fd);
|
|
assert(sock_t_wds_isset(s) == 0);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
int length = len;
|
|
int bw = 0;
|
|
int done = 0;
|
|
while( length > 0 ) {
|
|
bw = length;
|
|
n = send( s->sock_fd, buf, length , MSG_NOSIGNAL );
|
|
if( n == -1 ) {
|
|
#ifdef STRICT_CHECKING
|
|
veejay_msg(0, "Error sending buffer:%s",strerror(errno));
|
|
#endif
|
|
return -1;
|
|
}
|
|
if( n == 0 ) {
|
|
veejay_msg(VEEJAY_MSG_DEBUG, "Remote closed connection.");
|
|
return -1;
|
|
}
|
|
buf += n;
|
|
length -= n;
|
|
done += n;
|
|
}
|
|
return done;
|
|
}
|
|
|
|
int sock_t_send_fd( int fd, int send_size, unsigned char *buf, int len )
|
|
{
|
|
int n;
|
|
#ifdef STRICT_CHECKING
|
|
assert( buf != NULL );
|
|
#endif
|
|
|
|
unsigned int length = len;
|
|
unsigned int done = 0;
|
|
unsigned char *ptr = buf;
|
|
while( length > 0 ) {
|
|
n = send( fd, ptr, length , MSG_NOSIGNAL );
|
|
if( n == -1 ) {
|
|
veejay_msg(0, "Error sending buffer:%s", strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
if( n == 0 ) {
|
|
veejay_msg(VEEJAY_MSG_DEBUG, "Remote closed connection.");
|
|
return -1;
|
|
}
|
|
|
|
ptr += n; //@ advance ptr by bytes send
|
|
length -= n; //@ decrement length by bytes send
|
|
done += n; //@ keep count of bytes done
|
|
}
|
|
|
|
#ifdef STRICT_CHECKING
|
|
assert( length == 0 );
|
|
assert( done == len );
|
|
#endif
|
|
|
|
return done;
|
|
|
|
}
|
|
|
|
void sock_t_close( vj_sock_t *s )
|
|
{
|
|
if(s)
|
|
{
|
|
#ifdef STRICT_CHECKING
|
|
veejay_msg(VEEJAY_MSG_DEBUG, "\tclosing socket %d", s->sock_fd );
|
|
#endif
|
|
if( s->sock_fd ) {
|
|
close(s->sock_fd);
|
|
s->sock_fd = -1;
|
|
}
|
|
|
|
FD_ZERO(&(s->rds));
|
|
FD_ZERO(&(s->wds));
|
|
}
|
|
}
|