e_sys - fix l2ping code to work and actually properly ping

it wasn't working. first response may not be a 200 ident so keep
looking for them. also send a bit more than 1 byte to be sure, and
chekc the response is what we sent to be sure. also enforce a timeout
(10sec here) where we give up so it doesn't hang possibly forever.
all in all l2ping in e_sys works again. now. in the process i added a
timeout param too.

@fix
This commit is contained in:
Carsten Haitzler 2018-12-12 11:08:20 +00:00
parent d72a7e6800
commit 959b041e12
2 changed files with 133 additions and 56 deletions

View File

@ -4,97 +4,170 @@
#ifdef HAVE_BLUETOOTH #ifdef HAVE_BLUETOOTH
#include <unistd.h> #include <unistd.h>
#include <bluetooth/bluetooth.h> #include <bluetooth/bluetooth.h>
#include <bluetooth/l2cap.h> #include <bluetooth/l2cap.h>
#include <sys/select.h>
#include <fcntl.h>
#endif #endif
#define MAX_SZ 500
double double
e_sys_l2ping(const char *bluetooth_mac) e_sys_l2ping(const char *bluetooth_mac, int timeout_ms)
{ {
#ifdef HAVE_BLUETOOTH #ifdef HAVE_BLUETOOTH
char send_buf[L2CAP_CMD_HDR_SIZE + 1]; char send_buf[L2CAP_CMD_HDR_SIZE + MAX_SZ];
char recv_buf[L2CAP_CMD_HDR_SIZE + 1]; char recv_buf[L2CAP_CMD_HDR_SIZE + MAX_SZ];
char tmp[18]; char tmp[18];
bdaddr_t bdaddr;
l2cap_cmd_hdr *send_cmd; l2cap_cmd_hdr *send_cmd;
l2cap_cmd_hdr *recv_cmd; l2cap_cmd_hdr *recv_cmd;
struct sockaddr_l2 addr; struct sockaddr_l2 addr;
socklen_t optlen; socklen_t optlen;
double start; double start;
int fd; int fd, err, size, i;
fd_set rfds, wfds, exfds;
struct timeval tv;
/* Create socket */ // Create socket
fd = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP); fd = socket(PF_BLUETOOTH, SOCK_RAW | SOCK_NONBLOCK, BTPROTO_L2CAP);
if (fd < 0) { if (fd < 0)
perror("Can't create socket");
return -1;
}
/* Bind to local address */
memset(&addr, 0, sizeof(addr));
addr.l2_family = AF_BLUETOOTH;
bacpy(&addr.l2_bdaddr, BDADDR_ANY);
if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0)
{ {
perror("Can't bind socket"); perror("Can't create socket");
return -1; return -1;
} }
/* Connect to remote device */ // Bind to local address
memset(&addr, 0, sizeof(addr));
addr.l2_family = AF_BLUETOOTH;
bacpy(&bdaddr, BDADDR_ANY);
bacpy(&addr.l2_bdaddr, &bdaddr);
if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
{
perror("Can't bind socket");
return -1;
}
fcntl(fd, F_SETFL, O_NONBLOCK);
// Connect to remote device
memset(&addr, 0, sizeof(addr)); memset(&addr, 0, sizeof(addr));
addr.l2_family = AF_BLUETOOTH; addr.l2_family = AF_BLUETOOTH;
str2ba(bluetooth_mac, &addr.l2_bdaddr); str2ba(bluetooth_mac, &addr.l2_bdaddr);
connect(fd, (struct sockaddr *)&addr, sizeof(addr));
if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) FD_ZERO(&rfds);
{ FD_ZERO(&wfds);
perror("Can't connect"); FD_ZERO(&exfds);
return -1; FD_SET(fd, &wfds);
} tv.tv_sec = timeout_ms / 1000;
tv.tv_usec = (timeout_ms % 1000) * 1000;
start = ecore_time_get(); start = ecore_time_get();
err = select(fd + 1, &rfds, &wfds, &exfds, &tv);
if (err == 0)
{
fprintf(stderr, "Connect timeout %s\n", bluetooth_mac);
return -1;
}
// adjust timeout by how long we waited to connect
timeout_ms -= (ecore_time_get() - start) * 1000;
if (timeout_ms < 1) timeout_ms = 1;
/* Get local address */ // Get local address
memset(&addr, 0, sizeof(addr)); memset(&addr, 0, sizeof(addr));
optlen = sizeof(addr); optlen = sizeof(addr);
if (getsockname(fd, (struct sockaddr *)&addr, &optlen) < 0)
if (getsockname(fd, (struct sockaddr *) &addr, &optlen) < 0)
{ {
perror("Can't get local address"); perror("Can't get local address");
return -1; return -1;
} }
ba2str(&addr.l2_bdaddr, tmp); ba2str(&addr.l2_bdaddr, tmp);
send_cmd = (l2cap_cmd_hdr *) send_buf; size = 44; // use std 44 byte ping size, but no more than MAX_SZ
send_cmd->ident = 200;
send_cmd->len = htobs(1);
send_cmd->code = L2CAP_ECHO_REQ;
send_buf[L2CAP_CMD_HDR_SIZE] = 'A';
if (send(fd, send_buf, L2CAP_CMD_HDR_SIZE + 1, 0) <= 0) send_cmd = (l2cap_cmd_hdr *)send_buf;
send_cmd->ident = 200;
send_cmd->len = htobs(size);
send_cmd->code = L2CAP_ECHO_REQ;
// init buffer with some content
for (i = 0; i < size; i++)
{
// ABCDEF....WXYZABCEF... up to "size" chars
send_buf[L2CAP_CMD_HDR_SIZE + i] = 'A' + (i % 26);
}
// get our time just before a send
start = ecore_time_get();
// send the ping
if (send(fd, send_buf, L2CAP_CMD_HDR_SIZE + size, 0) <= 0)
{ {
perror("Send failed"); perror("Send failed");
return -1; return -1;
} }
if (recv(fd, recv_buf, L2CAP_CMD_HDR_SIZE + 1, 0) < 0) do
{ {
perror("Recv failed"); FD_ZERO(&rfds);
return -1; FD_ZERO(&wfds);
} FD_ZERO(&exfds);
FD_SET(fd, &rfds);
tv.tv_sec = timeout_ms / 1000;
tv.tv_usec = (timeout_ms % 1000) * 1000;
err = select(fd + 1, &rfds, &wfds, &exfds, &tv);
if (err < 0)
{
perror("Select failed");
return -1;
}
else if (err == 0)
{
fprintf(stderr, "Select timeout %s\n", bluetooth_mac);
return -1;
}
err = recv(fd, recv_buf, L2CAP_CMD_HDR_SIZE + size, 0);
if (err == 0)
{
fprintf(stderr, "Disconnect %s\n", bluetooth_mac);
return -1;
}
else if (err < 0)
{
perror("Recv failed");
return -1;
}
recv_cmd = (l2cap_cmd_hdr *) recv_buf; recv_cmd = (l2cap_cmd_hdr *)recv_buf;
recv_cmd->len = btohs(recv_cmd->len); recv_cmd->len = btohs(recv_cmd->len);
if (recv_cmd->ident != 200) // we only want the 200 ident response packets
return -1; /* Wrong packet */ if (recv_cmd->ident != 200) continue;
if (recv_cmd->code == L2CAP_COMMAND_REJ)
{
fprintf(stderr, "Peer %s doesn't do echo\n", bluetooth_mac);
return -1;
}
if (recv_cmd->len != size)
{
fprintf(stderr, "Size %i echo for %s does not match %i\n",
recv_cmd->len, bluetooth_mac, size);
return -1;
}
if (memcmp(send_buf + L2CAP_CMD_HDR_SIZE,
recv_buf + L2CAP_CMD_HDR_SIZE, size) != 0)
{
fprintf(stderr, "Echo response for %s data does not match sent data\n", bluetooth_mac);
return -1;
}
fprintf(stderr, "Device %s responded\n", bluetooth_mac);
break;
}
while (1);
close(fd); close(fd);
// time it took to send and get our response
return ecore_time_get() - start; return ecore_time_get() - start;
#else #else
(void) bluetooth_mac; (void) bluetooth_mac;
fprintf(stderr, "e_sys_l2ping nop\n"); fprintf(stderr, "e_sys_l2ping nop %s\n", bluetooth_mac);
return -1; return -1;
#endif #endif
} }

View File

@ -31,7 +31,7 @@
extern char **environ; extern char **environ;
#endif #endif
double e_sys_l2ping(const char *bluetooth_mac); double e_sys_l2ping(const char *bluetooth_mac, int timeout_ms);
/* local subsystem functions */ /* local subsystem functions */
#ifdef HAVE_EEZE_MOUNT #ifdef HAVE_EEZE_MOUNT
@ -61,7 +61,7 @@ main(int argc,
int i, gn; int i, gn;
int test = 0; int test = 0;
char *action = NULL, *cmd; char *action = NULL, *cmd;
char *output = NULL; char *output = NULL, *param = NULL;
#ifdef HAVE_EEZE_MOUNT #ifdef HAVE_EEZE_MOUNT
Eina_Bool mnt = EINA_FALSE; Eina_Bool mnt = EINA_FALSE;
const char *act = NULL; const char *act = NULL;
@ -93,6 +93,7 @@ main(int argc,
{ {
action = argv[1]; action = argv[1];
output = argv[2]; output = argv[2];
if (argc >= 4) param = argv[3];
} }
else if (!strcmp(argv[1], "rfkill-unblock")) else if (!strcmp(argv[1], "rfkill-unblock"))
{ {
@ -130,7 +131,6 @@ main(int argc,
exit(1); exit(1);
} }
if (!action) exit(1); if (!action) exit(1);
fprintf(stderr, "action %s %i\n", action, argc);
eina_init(); eina_init();
@ -175,14 +175,18 @@ main(int argc,
if (!test && !strcmp(action, "l2ping")) if (!test && !strcmp(action, "l2ping"))
{ {
char tmp[128]; char tmp[128];
double latency; double latency;
int timeout = 10 * 1000;
latency = e_sys_l2ping(output); if (param) timeout = atoi(param);
if (timeout < 1) timeout = 1;// min timeout 1ms
else if (timeout > (60 * 60 * 1000)) timeout = 60 * 60 * 1000;// max 1h
latency = e_sys_l2ping(output, timeout);
eina_convert_dtoa(latency, tmp); eina_convert_dtoa(latency, tmp);
fputs(tmp, stdout); fputs(tmp, stdout);
return (latency < 0) ? 1 : 0; return (latency < 0) ? 1 : 0;
} }
/* sanitize environment */ /* sanitize environment */
#ifdef HAVE_UNSETENV #ifdef HAVE_UNSETENV