/*
** Copyright 2009 The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
/** socket testing */
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <sys/uio.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/rfcomm.h>
#include <bluetooth/sco.h>
#include <bluetooth/l2cap.h>
enum sock_type {
UNIX = 0,
RFCOMM,
SCO,
L2CAP,
TCP,
};
struct thread_args {
int fd;
int type;
int delay;
};
struct sockaddr_un local_addr_un = {AF_UNIX, "/data/foo"};
struct sockaddr_rc local_addr_rc = {AF_BLUETOOTH, *BDADDR_ANY, 4};
struct sockaddr_sco local_addr_sco = {AF_BLUETOOTH, *BDADDR_LOCAL};
struct sockaddr_l2 local_addr_l2 = {AF_BLUETOOTH, htobs(0x1001), *BDADDR_ANY, 0};
struct sockaddr_in local_addr_in = {AF_INET, 9999, {0}, {0}};
struct sockaddr_un remote_addr_un ;
struct sockaddr_rc remote_addr_rc ;
struct sockaddr_sco remote_addr_sco ;
struct sockaddr_l2 remote_addr_l2 ;
struct sockaddr_in remote_addr_in ;
static void print_events(int events) {
if (events & POLLIN) printf("POLLIN ");
if (events & POLLPRI) printf("POLLPRI ");
if (events & POLLOUT) printf("POLLOUT ");
if (events & POLLERR) printf("POLLERR ");
if (events & POLLHUP) printf("POLLHUP ");
if (events & POLLNVAL) printf("POLLNVAL ");
printf("\n");
}
static void print_fds(struct pollfd *ufds, nfds_t nfds) {
unsigned int i;
for (i=0; i<nfds; i++)
printf("%d ", ufds[i].fd);
}
static int _socket(int type) {
int ret;
int family = -1;
int typ = -1;
int protocol = -1;
switch (type) {
case UNIX:
family = PF_UNIX;
typ = SOCK_STREAM;
protocol = 0;
break;
case RFCOMM:
family = PF_BLUETOOTH;
typ = SOCK_STREAM;
protocol = BTPROTO_RFCOMM;
break;
case SCO:
family = PF_BLUETOOTH;
typ = SOCK_SEQPACKET;
protocol = BTPROTO_SCO;
break;
case L2CAP:
family = PF_BLUETOOTH;
typ = SOCK_SEQPACKET;
protocol = BTPROTO_L2CAP;
break;
case TCP:
family = PF_INET;
typ = SOCK_STREAM;
protocol = 0;
break;
}
printf("%ld: socket()\n", pthread_self());
ret = socket(family, typ, protocol);
printf("%ld: socket() = %d\n", pthread_self(), ret);
if (ret < 0) printf("\terr %d (%s)\n", errno, strerror(errno));
return ret;
}
static int _close(int fd, int type) {
int ret;
printf("%ld: close(%d)\n", pthread_self(), fd);
ret = close(fd);
printf("%ld: close(%d) = %d\n", pthread_self(), fd, ret);
if (ret < 0) printf("\terr %d (%s)\n", errno, strerror(errno));
return ret;
}
static int _bind(int fd, int type) {
int len = 0;
int ret;
struct sockaddr *addr = NULL;
switch (type) {
case UNIX:
unlink(local_addr_un.sun_path);
addr = (struct sockaddr *) &local_addr_un;
len = sizeof(local_addr_un);
break;
case RFCOMM:
addr = (struct sockaddr *) &local_addr_rc;
len = sizeof(local_addr_rc);
break;
case SCO:
addr = (struct sockaddr *) &local_addr_sco;
len = sizeof(local_addr_sco);
break;
case L2CAP:
addr = (struct sockaddr *) &local_addr_l2;
len = sizeof(local_addr_l2);
break;
case TCP:
addr = (struct sockaddr *) &local_addr_in;
len = sizeof(local_addr_in);
break;
}
printf("%ld: bind(%d)\n", pthread_self(), fd);
ret = bind(fd, addr, len);
printf("%ld: bind(%d) = %d\n", pthread_self(), fd, ret);
if (ret < 0) printf("\terr %d (%s)\n", errno, strerror(errno));
return ret;
}
static int _listen(int fd, int type) {
int ret;
printf("%ld: listen(%d)\n", pthread_self(), fd);
ret = listen(fd, 1);
printf("%ld: listen(%d) = %d\n", pthread_self(), fd, ret);
if (ret < 0) printf("\terr %d (%s)\n", errno, strerror(errno));
return ret;
}
static int _read(int fd) {
int ret;
char buf;
printf("%ld: read(%d)\n", pthread_self(), fd);
ret = read(fd, &buf, 1);
printf("%ld: read(%d) = %d [%d]\n", pthread_self(), fd, ret, (int)buf);
if (ret < 0) printf("\terr %d (%s)\n", errno, strerror(errno));
return ret;
}
static int _accept(int fd, int type) {
int ret;
int len;
struct sockaddr *addr = NULL;
switch (type) {
case UNIX:
addr = (struct sockaddr *) &remote_addr_un;
len = sizeof(remote_addr_un);
break;
case RFCOMM:
addr = (struct sockaddr *) &remote_addr_rc;
len = sizeof(remote_addr_rc);
break;
case SCO:
addr = (struct sockaddr *) &remote_addr_sco;
len = sizeof(remote_addr_sco);
break;
case L2CAP:
addr = (struct sockaddr *) &remote_addr_l2;
len = sizeof(remote_addr_l2);
break;
case TCP:
addr = (struct sockaddr *) &remote_addr_in;
len = sizeof(remote_addr_in);
break;
}
printf("%ld: accept(%d)\n", pthread_self(), fd);
ret = accept(fd, addr, &len);
printf("%ld: accept(%d) = %d\n", pthread_self(), fd, ret);
if (ret < 0) printf("\terr %d (%s)\n", errno, strerror(errno));
else {
printf("\tlen = %d\n", len);
}
return ret;
}
int get_bdaddr(const char *str, bdaddr_t *ba) {
char *d = ((char *)ba) + 5, *endp;
int i;
for(i = 0; i < 6; i++) {
*d-- = strtol(str, &endp, 16);
if (*endp != ':' && i != 5) {
memset(ba, 0, sizeof(bdaddr_t));
return -1;
}
str = endp + 1;
}
return 0;
}
static int _connect(int fd, int type) {
int ret;
int len = 0;
struct sockaddr *addr = NULL;
switch (type) {
case UNIX:
addr = (struct sockaddr *) &local_addr_un;
len = sizeof(local_addr_un);
break;
case RFCOMM:
get_bdaddr("00:11:22:33:44:55", &local_addr_rc.rc_bdaddr);
addr = (struct sockaddr *) &local_addr_rc;
len = sizeof(local_addr_rc);
break;
case SCO:
addr = (struct sockaddr *) &local_addr_sco;
len = sizeof(local_addr_sco);
break;
case L2CAP:
addr = (struct sockaddr *) &local_addr_l2;
len = sizeof(local_addr_l2);
break;
case TCP:
addr = (struct sockaddr *) &local_addr_in;
len = sizeof(local_addr_in);
break;
}
printf("%ld: connect(%d)\n", pthread_self(), fd);
ret = connect(fd, addr, len);
printf("%ld: connect(%d) = %d\n", pthread_self(), fd, ret);
if (ret < 0) printf("\terr %d (%s)\n", errno, strerror(errno));
return ret;
}
static int _write(int fd, int type) {
int ret;
char buf = 69;
printf("%ld: write(%d)\n", pthread_self(), fd);
ret = write(fd, &buf, 1);
printf("%ld: write(%d) = %d\n", pthread_self(), fd, ret);
if (ret < 0) printf("\terr %d (%s)\n", errno, strerror(errno));
return ret;
}
static int _shutdown(int fd, int how) {
int ret;
printf("%ld: shutdown(%d)\n", pthread_self(), fd);
ret = shutdown(fd, how);
printf("%ld: shutdown(%d) = %d\n", pthread_self(), fd, ret);
if (ret < 0) printf("\terr %d (%s)\n", errno, strerror(errno));
return ret;
}
static int _poll(struct pollfd *ufds, nfds_t nfds, int timeout) {
int ret;
unsigned int i;
printf("%ld: poll(", pthread_self());
print_fds(ufds, nfds);
printf(")\n");
ret = poll(ufds, nfds, timeout);
printf("%ld: poll() = %d\n", pthread_self(), ret);
if (ret < 0) printf("\terr %d (%s)\n", errno, strerror(errno));
if (ret > 0) {
for (i=0; i<nfds; i++) {
if (ufds[i].revents) {
printf("\tfd %d ", ufds[i].fd); print_events(ufds[i].revents);
}
}
}
return ret;
}
static void thread_delay_close(struct thread_args *args) {
printf("%ld: START\n", pthread_self());
sleep(args->delay);
_close(args->fd, args->type);
printf("%ld: END\n", pthread_self());
}
static void thread_poll(void *args) {
int fd = (int)args;
struct pollfd pfd;
printf("%ld: START\n", pthread_self());
pfd.fd = fd;
pfd.events = 0;
_poll(&pfd, 1, -1);
printf("%ld: END\n", pthread_self());
}
static void thread_read(void *args) {
int fd = (int)args;
printf("%ld: START\n", pthread_self());
_read(fd);
printf("%ld: END\n", pthread_self());
}
static void thread_pollin(void *args) {
int fd = (int)args;
struct pollfd pfd;
printf("%ld: START\n", pthread_self());
pfd.fd = fd;
pfd.events = POLLIN;
_poll(&pfd, 1, -1);
printf("%ld: END\n", pthread_self());
}
static void thread_shutdown(int fd) {
printf("%ld: START\n", pthread_self());
sleep(4);
_shutdown(fd, SHUT_RDWR);
printf("%ld: END\n", pthread_self());
}
static void thread_accept(struct thread_args *args) {
printf("%ld: START\n", pthread_self());
sleep(args->delay);
_accept(args->fd, args->type);
printf("%ld: END\n", pthread_self());
}
static void thread_connect(struct thread_args *args) {
printf("%ld: START\n", pthread_self());
sleep(args->delay);
_connect(args->fd, args->type);
printf("%ld: END\n", pthread_self());
}
static void thread_delay_close_write(struct thread_args *args) {
printf("%ld: START\n", pthread_self());
sleep(args->delay);
_close(args->fd, args->type);
sleep(args->delay);
_write(args->fd, args->type);
printf("%ld: END\n", pthread_self());
}
static void thread_accept_write(struct thread_args *args) {
printf("%ld: START\n", pthread_self());
sleep(args->delay);
_accept(args->fd, args->type);
sleep(args->delay);
_write(args->fd, args->type);
printf("%ld: END\n", pthread_self());
}
static void thread_delay_connect(struct thread_args *args) {
printf("%ld: START\n", pthread_self());
sleep(args->delay);
args->fd = _socket(args->type);
_connect(args->fd, args->type);
printf("%ld: END\n", pthread_self());
}
static int do_accept_accept_accept(int type) {
int fd;
fd = _socket(type);
if (fd < 0) goto error;
if (_bind(fd, type) < 0) goto error;
if (_listen(fd, type) < 0) goto error;
while (1) {
_accept(fd, type);
}
return 0;
error:
return -1;
}
static int do_accept_and_close(int type) {
int fd;
pthread_t thread;
struct thread_args args = {-1, type, 1};
fd = _socket(type);
if (fd < 0) goto error;
if (_bind(fd, type) < 0) goto error;
if (_listen(fd, type) < 0) goto error;
args.fd = fd;
pthread_create(&thread, NULL, (void *)thread_delay_close, (void *)&args);
_accept(fd, type);
pthread_join(thread, NULL);
return 0;
error:
return -1;
}
static int do_accept_shutdown(int type) {
int fd;
pthread_t thread;
struct thread_args args = {-1, type, 0};
fd = _socket(type);
if (fd < 0) goto error;
if (_bind(fd, type) < 0) goto error;
if (_listen(fd, type) < 0) goto error;
args.fd = fd;
pthread_create(&thread, NULL, (void *)thread_accept, (void *)&args);
sleep(4);
_shutdown(fd, SHUT_RDWR);
pthread_join(thread, NULL);
_close(fd, type);
return 0;
error:
return -1;
}
static int do_connect_shutdown(int type) {
int fd;
pthread_t thread;
struct thread_args args = {-1, type, 0};
fd = _socket(type);
if (fd < 0) goto error;
args.fd = fd;
pthread_create(&thread, NULL, (void *)thread_connect, (void *)&args);
sleep(4);
_shutdown(fd, SHUT_RDWR);
pthread_join(thread, NULL);
_close(fd, type);
return 0;
error:
return -1;
}
static int do_connectnb_shutdown(int type) {
int fd;
int flags;
pthread_t thread;
struct thread_args args = {-1, type, 0};
fd = _socket(type);
if (fd < 0) goto error;
flags = fcntl(fd, F_GETFL);
if (flags == -1)
return -1;
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK))
return -1;
_connect(fd, type);
sleep(1);
_shutdown(fd, SHUT_RDWR);
sleep(2);
_close(fd, type);
return 0;
error:
return -1;
}
static int do_connectnb_close(int type) {
int fd;
pthread_t thread;
struct thread_args args = {-1, type, 0};
int flags;
fd = _socket(type);
if (fd < 0) goto error;
flags = fcntl(fd, F_GETFL);
if (flags == -1)
return -1;
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK))
return -1;
_connect(fd, type);
sleep(2);
_close(fd, type);
return 0;
error:
return -1;
}
// accept in one thread. close then write in another
static int do_accept_close_write(int type) {
int fd;
pthread_t thread;
struct thread_args args = {-1, type, 1};
fd = _socket(type);
if (fd < 0) goto error;
if (_bind(fd, type) < 0) goto error;
if (_listen(fd, type) < 0) goto error;
args.fd = fd;
pthread_create(&thread, NULL, (void *)thread_delay_close_write, (void *)&args);
_accept(fd, type);
pthread_join(thread, NULL);
return 0;
error:
return -1;
}
static int do_poll_poll_poll_shutdown(int type) {
const int MAX_T = 32;
int fd;
pthread_t t[MAX_T];
int i;
fd = _socket(type);
for (i=0; i<MAX_T; i++)
pthread_create(&t[i], NULL, (void *)thread_poll, (void *)fd);
sleep(1);
_shutdown(fd, SHUT_RDWR);
for (i=0; i<MAX_T; i++)
pthread_join(t[i], NULL);
_close(fd, type);
return 0;
}
static int do_poll_poll_poll_close(int type) {
const int MAX_T = 32;
int fd;
pthread_t t[MAX_T];
int i;
fd = _socket(type);
for (i=0; i<MAX_T; i++)
pthread_create(&t[i], NULL, (void *)thread_poll, (void *)fd);
sleep(1);
_close(fd, type);
for (i=0; i<MAX_T; i++)
pthread_join(t[i], NULL);
return 0;
}
static int do_read_read_read_close(int type) {
const int MAX_T = 32;
int fd;
pthread_t t[MAX_T];
int i;
fd = _socket(type);
for (i=0; i<MAX_T; i++)
pthread_create(&t[i], NULL, (void *)thread_read, (void *)fd);
sleep(1);
_close(fd, type);
for (i=0; i<MAX_T; i++)
pthread_join(t[i], NULL);
return 0;
}
static int do_read_read_read_shutdown(int type) {
const int MAX_T = 32;
int fd;
pthread_t t[MAX_T];
int i;
fd = _socket(type);
for (i=0; i<MAX_T; i++)
pthread_create(&t[i], NULL, (void *)thread_read, (void *)fd);
sleep(1);
_shutdown(fd, SHUT_RDWR);
for (i=0; i<MAX_T; i++)
pthread_join(t[i], NULL);
_close(fd, type);
return 0;
}
static int do_connected_read1_shutdown1(int type) {
int fd1, fd2;
pthread_t t1;
pthread_t t2;
struct thread_args a1 = {-1, type, 0};
struct thread_args a2 = {-1, type, 2};
fd1 = _socket(type);
if (fd1 < 0) goto error;
if (_bind(fd1, type) < 0) goto error;
if (_listen(fd1, type) < 0) goto error;
a1.fd = fd1;
pthread_create(&t1, NULL, (void *)thread_accept_write, (void *)&a1);
fd2 = _socket(type);
if (_connect(fd2, type)) goto error;
pthread_create(&t2, NULL, (void *)thread_shutdown, (void *)&fd2);
while (1) if (_read(fd2)) break;
pthread_join(t1, NULL);
pthread_join(t2, NULL);
return 0;
error:
return -1;
}
// accept in one thread, connect from two different threads
static int do_accept_connect_connect(int type) {
int fd;
pthread_t t1;
pthread_t t2;
struct thread_args a1 = {-1, type, 1};
struct thread_args a2 = {-1, type, 2};
fd = _socket(type);
if (fd < 0) goto error;
if (_bind(fd, type) < 0) goto error;
if (_listen(fd, type) < 0) goto error;
pthread_create(&t1, NULL, (void *)thread_delay_connect, (void *)&a1);
pthread_create(&t2, NULL, (void *)thread_delay_connect, (void *)&a2);
_accept(fd, type);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
return 0;
error:
return -1;
}
struct {
char *name;
int (*ptr)(int);
} action_table[] = {
{"accept_accept_accept", do_accept_accept_accept},
{"accept_and_close", do_accept_and_close},
{"accept_shutdown", do_accept_shutdown},
{"connect_shutdown", do_connect_shutdown},
{"connectnb_shutdown", do_connectnb_shutdown},
{"connectnb_close", do_connectnb_close},
{"accept_close_write", do_accept_close_write},
{"accept_connect_connect", do_accept_connect_connect},
{"poll_poll_poll_shutdown", do_poll_poll_poll_shutdown},
{"poll_poll_poll_close", do_poll_poll_poll_close},
{"read_read_read_shutdown", do_read_read_read_shutdown},
{"read_read_read_close", do_read_read_read_close},
{"connected_read1_shutdown1", do_connected_read1_shutdown1},
{NULL, NULL},
};
struct {
char *name;
enum sock_type type;
} type_table[] = {
{"unix", UNIX},
{"rfcomm", RFCOMM},
{"sco", SCO},
{"l2cap", L2CAP},
{"tcp", TCP},
{NULL, -1},
};
static void usage() {
int i;
printf("socktest TYPE ACTION\n");
printf("\nTYPE:\n");
for (i = 0; type_table[i].name; i++) {
printf("\t%s\n", type_table[i].name);
}
printf("\nACTION:\n");
for (i = 0; action_table[i].name; i++) {
printf("\t%s\n", action_table[i].name);
}
}
int main(int argc, char **argv) {
int i;
int type = -1;
if (argc != 3) {
usage();
return -1;
}
for (i = 0; type_table[i].name; i++) {
if (!strcmp(argv[1], type_table[i].name)) {
type = type_table[i].type;
break;
}
}
if (type == -1) {
usage();
return -1;
}
for (i = 0; action_table[i].name; i++) {
if (!strcmp(argv[2], action_table[i].name)) {
printf("TYPE = %s ACTION = %s\n", type_table[type].name,
action_table[i].name);
return (*action_table[i].ptr)(type);
}
}
usage();
return -1;
}