You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1695 lines
38 KiB

/*
* $Id: socket.c,v 1.118 2006/11/07 21:13:24 adridg Exp $
*
* socket.c: Berkeley sockets style interface to Pilot
*
* Copyright (c) 1996, D. Jeff Dionne.
* Copyright (c) 1997-1999, Kenneth Albanowski
* Copyright (c) 1999, Tilo Christ
* Copyright (c) 2000-2001, JP Rosevear
* Copyright (c) 2004-2005, Florent Pillet
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Library General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This library 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 Library
* General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <fcntl.h>
#include <string.h>
#include "pi-source.h"
#include "pi-serial.h"
#ifdef HAVE_USB
#include "pi-usb.h"
#endif
#include "pi-bluetooth.h"
#include "pi-inet.h"
#include "pi-slp.h"
#include "pi-sys.h"
#include "pi-padp.h"
#include "pi-cmp.h"
#include "pi-net.h"
#include "pi-dlp.h"
#include "pi-syspkt.h"
#include "pi-debug.h"
#include "pi-error.h"
#include "pi-threadsafe.h"
/* Declare function prototypes */
static pi_socket_list_t *ps_list_append (pi_socket_list_t *list,
pi_socket_t *ps);
static pi_socket_t *ps_list_find (pi_socket_list_t *list,
int pi_sd);
static pi_socket_list_t *ps_list_remove (pi_socket_list_t *list,
int pi_sd);
static pi_socket_list_t *ps_list_copy (pi_socket_list_t *list);
static void ps_list_free (pi_socket_list_t *list);
static void protocol_queue_add (pi_socket_t *ps, pi_protocol_t *prot);
static void protocol_cmd_queue_add (pi_socket_t *ps, pi_protocol_t *prot);
static pi_protocol_t *protocol_queue_find (pi_socket_t *ps, int level);
static pi_protocol_t *protocol_queue_find_next (pi_socket_t *ps, int level);
int pi_socket_init(pi_socket_t *ps);
static int is_connected (pi_socket_t *ps);
static int is_listener (pi_socket_t *ps);
/* GLOBALS */
static PI_MUTEX_DEFINE(psl_mutex);
static pi_socket_list_t *psl = NULL;
static PI_MUTEX_DEFINE(watch_list_mutex);
static pi_socket_list_t *watch_list = NULL;
/* Automated tickling interval */
static unsigned int interval = 0;
/* Indicates that the exit function has already been installed. Made non-static
* so that library users can choose to not have an exit function installed */
int pi_sock_installedexit = 0;
/* Linked List Code */
/***********************************************************************
*
* Function: ps_list_dump
*
* Summary: internal debugging function
*
* Parameters: pi_socket_list_t *
*
* Returns: void
*
***********************************************************************/
#if 0
static void
ps_list_dump (pi_socket_list_t *list)
{
fprintf(stderr, "* Dumping pi_socket_list @ %p:\n",(void*)list);
while (list != NULL) {
fprintf(stderr," %p: ps=%p, pi_sd=%d\n", (void*)list, (void*)list->ps, list->ps->sd);
list = list->next;
}
}
#endif
/***********************************************************************
*
* Function: ps_list_append
*
* Summary: creates and appends a new pi_socket_list element to
* the (possibly empty) pi_socket_list and fills in the
* pi_socket_t* member to point to the given pi_socket.
*
* Parameters: pi_socket_list_t*, pi_socket_t*
*
* Returns: pi_socket_list_t*, or NULL if operation failed
*
***********************************************************************/
static pi_socket_list_t *
ps_list_append (pi_socket_list_t *list, pi_socket_t *ps)
{
pi_socket_list_t *elem, *new_elem;
ASSERT (ps != NULL);
new_elem = (pi_socket_list_t *) malloc (sizeof(pi_socket_list_t));
if (new_elem == NULL)
return list;
new_elem->ps = ps;
new_elem->next = NULL;
if (list == NULL)
return new_elem;
elem = list;
while (elem->next != NULL)
elem = elem->next;
elem->next = new_elem;
return list;
}
/***********************************************************************
*
* Function: ps_list_find
*
* Summary: traverse a (possibly empty) pi_socket_list and find
* the first list element whose pi_socket_t* member points
* to a pi_socket matching the given socket descriptor
*
* Parameters: pi_socket_list_t *, socket descriptor
*
* Returns: pi_socket_t *, or NULL if no match
*
* NOTE: ps_list_find returns a pointer which points directly to
* the socket (pi_socket_t *) whereas ps_find_elem returns a
* pointer to the list element (pi_socket_list_t *) which
* _contains_ a pointer to the socket
*
***********************************************************************/
static pi_socket_t *
ps_list_find (pi_socket_list_t *list, int pi_sd)
{
pi_socket_list_t *elem;
for (elem = list; elem != NULL; elem = elem->next)
if (elem->ps != NULL && elem->ps->sd == pi_sd)
return elem->ps;
return NULL;
}
/***********************************************************************
*
* Function: ps_list_remove
*
* Summary: remove first pi_socket_list element pointing to a pi_socket
* member matching socket descriptor
*
* Parameters: pi_socket_list_t *, socket descriptor
*
* Returns: the (possibly NULL) head pi_socket_list_t *
*
* NOTE: only the pi_socket_list element is freed,
* _not_ the pi_socket. Consequently, this function
* makes the (risky) assumption that the pi_socket will
* be freed elsewhere.
*
***********************************************************************/
static pi_socket_list_t *
ps_list_remove (pi_socket_list_t *list, int pi_sd)
{
pi_socket_list_t *elem,
*new_list = list,
*prev_elem = NULL;
for (elem = list; elem != NULL; elem = elem->next) {
if (elem->ps == NULL)
continue;
else if (elem->ps->sd == pi_sd) {
if (prev_elem == NULL)
new_list = elem->next;
else
prev_elem->next = elem->next;
free(elem);
break;
}
prev_elem = elem;
}
return new_list;
}
/***********************************************************************
*
* Function: ps_list_copy
*
* Summary: copy pi_socket_list
*
* Parameters: pi_socket_list_t *
*
* Returns: pi_socket_list_t* (new list head)
*
* NOTE: pi_list_copy does _not_ copy the pi_socket member, it
* copies only the list elements
*
***********************************************************************/
static pi_socket_list_t *
ps_list_copy (pi_socket_list_t *list)
{
pi_socket_list_t *l, *new_list = NULL;
for (l = list; l != NULL; l = l->next)
new_list = ps_list_append (new_list, l->ps);
return new_list;
}
/***********************************************************************
*
* Function: ps_list_free
*
* Summary: free pi_socket_list elements
*
* Parameters: pi_socket_list_t *
*
* Returns: void
*
* NOTE: only the pi_socket_list elements are freed,
* _not_ the pi_sockets. Consequently, this function
* makes the (risky) assumption that the pi_sockets will
* be freed elsewhere.
*
***********************************************************************/
static void
ps_list_free (pi_socket_list_t *list)
{
pi_socket_list_t *l, *next;
if (list == NULL)
return;
l = list;
do {
next = l->next;
free(l);
l = next;
} while (l != NULL);
}
/* Protocol Queue */
/***********************************************************************
*
* Function: protocol_queue_add
*
* Summary: adds protocol queue to pi_socket
*
* Parameters: pi_socket_t*, pi_protocol_t*
*
* Returns: void
*
***********************************************************************/
static void
protocol_queue_add (pi_socket_t *ps, pi_protocol_t *prot)
{
ps->protocol_queue = realloc(ps->protocol_queue,
(sizeof(pi_protocol_t *)) * (ps->queue_len + 1));
if (ps->protocol_queue != NULL) {
ps->protocol_queue[ps->queue_len] = prot;
ps->queue_len++;
} else {
errno = ENOMEM;
ps->queue_len = 0;
}
}
/***********************************************************************
*
* Function: cmd_queue_add
*
* Summary: adds command queue to pi_socket
*
* Parameters: pi_socket_t*, pi_protocol*
*
* Returns: void
*
***********************************************************************/
static void
protocol_cmd_queue_add (pi_socket_t *ps, pi_protocol_t *prot)
{
ps->cmd_queue = realloc(ps->cmd_queue,
(sizeof(pi_protocol_t *)) * (ps->cmd_len + 1));
if (ps->cmd_queue != NULL) {
ps->cmd_queue[ps->cmd_len] = prot;
ps->cmd_len++;
} else {
errno = ENOMEM;
ps->cmd_len = 0;
}
}
/***********************************************************************
*
* Function: protocol_queue_find
*
* Summary: find queue entry
*
* Parameters: pi_socket_t*, level
*
* Returns: pi_protocol*, or NULL if queue entry not found
*
***********************************************************************/
static pi_protocol_t*
protocol_queue_find (pi_socket_t *ps, int level)
{
int i;
if (ps->command) {
for (i = 0; i < ps->cmd_len; i++) {
if (ps->cmd_queue[i]->level == level)
return ps->cmd_queue[i];
}
} else {
for (i = 0; i < ps->queue_len; i++) {
if (ps->protocol_queue[i]->level == level)
return ps->protocol_queue[i];
}
}
return NULL;
}
/***********************************************************************
*
* Function: protocol_queue_find_next
*
* Summary: find next queue entry
*
* Parameters: pi_socket*, level
*
* Returns: pi_protocol_t* or NULL if next queue entry not found
*
***********************************************************************/
static pi_protocol_t*
protocol_queue_find_next (pi_socket_t *ps, int level)
{
int i;
if (ps->command && ps->cmd_len == 0)
return NULL;
if (!ps->command && ps->queue_len == 0)
return NULL;
if (ps->command && level == 0)
return ps->cmd_queue[0];
if (!ps->command && level == 0)
return ps->protocol_queue[0];
if (ps->command) {
for (i = 0; i < ps->cmd_len - 1; i++) {
if (ps->cmd_queue[i]->level == level)
return ps->cmd_queue[i + 1];
}
} else {
for (i = 0; i < ps->queue_len - 1; i++) {
if (ps->protocol_queue[i]->level == level)
return ps->protocol_queue[i + 1];
}
}
return NULL;
}
/***********************************************************************
*
* Function: protocol_queue_build
*
* Summary: build protocol queue
*
* Parameters: pi_socket_t*, autodetect
*
* Returns: void
*
***********************************************************************/
static void
protocol_queue_build (pi_socket_t *ps, int autodetect)
{
int protocol,
result;
pi_protocol_t
*dev_prot,
*dev_cmd_prot;
LOG((PI_DBG_SOCK,PI_DBG_LVL_DEBUG, "SOCK fd=%d auto=%d\n",ps->sd,autodetect));
/* The device protocol */
dev_prot = ps->device->protocol (ps->device);
dev_cmd_prot = ps->device->protocol (ps->device);
/* When opening the device in RAW mode, we stay low-level */
if (ps->type == PI_SOCK_RAW) {
LOG((PI_DBG_SOCK,PI_DBG_LVL_DEBUG, "RAW mode, no protocol\n",ps->sd,autodetect));
protocol_queue_add (ps, dev_prot);
protocol_cmd_queue_add (ps, dev_cmd_prot);
return;
}
protocol = ps->protocol;
LOG((PI_DBG_SOCK,PI_DBG_LVL_DEBUG, "SOCK proto=%s (%d)\n",
protocol == PI_PF_DEV ? "DEV" :
protocol == PI_PF_SLP ? "SLP" :
protocol == PI_PF_SYS ? "SYS" :
protocol == PI_PF_PADP? "PADP" :
protocol == PI_PF_NET ? "NET" :
protocol == PI_PF_DLP ? "DLP" :
"unknown", protocol));
if (protocol == PI_PF_DLP && autodetect) {
int skipped_bytes = 0,
bytes_to_skip;
pi_buffer_t
*detect_buf = pi_buffer_new(64);
for (;;) {
/* try to peek a header start from the input sent by the device */
result = dev_prot->read (ps, detect_buf, 10, PI_MSG_PEEK);
if (result < 0)
break;
if (result != 10) {
pi_buffer_clear(detect_buf);
continue;
}
bytes_to_skip = 1;
/* detect a valid PADP header packet */
if (detect_buf->data[0] == PI_SLP_SIG_BYTE1 &&
detect_buf->data[1] == PI_SLP_SIG_BYTE2 &&
detect_buf->data[2] == PI_SLP_SIG_BYTE3)
{
/* compute the checksum */
int i;
unsigned char header_checksum;
for (header_checksum = i = 0; i < 9; i++)
header_checksum += detect_buf->data[i];
if (header_checksum == detect_buf->data[9]) { /* sum */
if (detect_buf->data[3] == PI_SLP_SOCK_DLP && /* src */
detect_buf->data[4] == PI_SLP_SOCK_DLP && /* dest */
detect_buf->data[5] == PI_SLP_TYPE_PADP && /* type */
detect_buf->data[8] == 0xff) /* txid */
{
protocol = PI_PF_PADP;
LOG((PI_DBG_SOCK, PI_DBG_LVL_INFO,
"\nusing PADP/SLP protocol (skipped %d bytes)\n",
skipped_bytes));
break;
}
else {
/* valid but not what we're looking for, skip it altogether */
bytes_to_skip = 10;
}
} else {
/* skip the SLP SIG bytes */
bytes_to_skip = 3;
}
}
/* detect NET header packets */
else if (detect_buf->data[0] == 0x01 && /* NET packet */
detect_buf->data[2] == 0x00 && /* length byte 0 */
detect_buf->data[3] == 0x00 && /* length byte 1 */
detect_buf->data[4] == 0x00 && /* length byte 2 */
detect_buf->data[5] > 0 && /* length byte 3 */
detect_buf->data[6] == 0x90) /* PI_NET_SIG_BYTE1 */
{
protocol = PI_PF_NET;
LOG((PI_DBG_SOCK, PI_DBG_LVL_INFO,
"\nusing NET protocol (skipped %d bytes)\n",
skipped_bytes));
break;
}
/* detect NET packet for cases where we lost the first 6 bytes
* (this unfortunately happens on Linux with unixserial, and the
* correct way to cope with this is to recognize the second
* part of the NET handshake packet)
*/
else if (detect_buf->data[0] == 0x90 && /* PI_NET_SIG_BYTE1 */
detect_buf->data[1] == 0x01 &&
detect_buf->data[2] == 0x00 &&
detect_buf->data[3] == 0x00 &&
detect_buf->data[4] == 0x00 &&
detect_buf->data[5] == 0x00 &&
detect_buf->data[6] == 0x00 &&
detect_buf->data[7] == 0x00 &&
detect_buf->data[8] == 0x00 &&
detect_buf->data[9] == 0x20)
{
protocol = PI_PF_NET;
LOG((PI_DBG_SOCK, PI_DBG_LVL_INFO,
"\nusing NET protocol (skipped %d bytes)\n",
skipped_bytes));
break;
}
/* eliminate one byte from the input, trying to frame a proper header */
result = dev_prot->read (ps, detect_buf, bytes_to_skip, 0);
if (result < 0)
break;
skipped_bytes += bytes_to_skip;
pi_buffer_clear(detect_buf);
}
pi_buffer_free(detect_buf);
if (result < 0) {
/* if there was an error (i.e. disconnect), temporarily fallback
* on PADP protocol. In further releases, we should really make
* this function return an error if there was a problem. -- fpillet
*/
LOG((PI_DBG_SOCK, PI_DBG_LVL_DEBUG,
"Error: last read returned %d; switching to PADP by default\n",
result));
protocol = PI_PF_PADP;
}
} else if (protocol == PI_PF_DLP) {
protocol = PI_PF_PADP;
}
/* The connected protocol queue */
switch (protocol) {
case PI_PF_PADP:
protocol_queue_add (ps, padp_protocol ());
case PI_PF_SLP:
protocol_queue_add (ps, slp_protocol ());
break;
case PI_PF_NET:
protocol_queue_add (ps, net_protocol ());
break;
case PI_PF_SYS:
protocol_queue_add (ps, sys_protocol ());
protocol_queue_add (ps, slp_protocol ());
break;
}
/* The command protocol queue */
switch (protocol) {
case PI_PF_PADP:
case PI_PF_SLP:
protocol_cmd_queue_add (ps, cmp_protocol ());
protocol_cmd_queue_add (ps, padp_protocol ());
protocol_cmd_queue_add (ps, slp_protocol ());
ps->cmd = PI_CMD_CMP;
break;
case PI_PF_NET:
protocol_cmd_queue_add (ps, net_protocol ());
ps->cmd = PI_CMD_NET;
break;
case PI_PF_SYS:
ps->cmd = PI_CMD_SYS;
break;
default:
LOG((PI_DBG_SOCK, PI_DBG_LVL_ERR, "invalid protocol (%d)", protocol));
break;
}
protocol_queue_add (ps, dev_prot);
protocol_cmd_queue_add (ps, dev_cmd_prot);
}
/***********************************************************************
*
* Function: protocol_queue_destroy
*
* Summary: destroy protocol queue
*
* Parameters: pi_socket_t *
*
* Returns: void
*
***********************************************************************/
static void
protocol_queue_destroy (pi_socket_t *ps)
{
int i;
for (i = 0; i < ps->queue_len; i++)
ps->protocol_queue[i]->free(ps->protocol_queue[i]);
for (i = 0; i < ps->cmd_len; i++)
ps->cmd_queue[i]->free(ps->cmd_queue[i]);
if (ps->queue_len > 0)
free(ps->protocol_queue);
if (ps->cmd_len > 0)
free(ps->cmd_queue);
}
/***********************************************************************
*
* Function: pi_protocol
*
* Summary: destroy protocol queue
*
* Parameters: socket descriptor, level
*
* Returns: pi_protocol_t*
*
***********************************************************************/
pi_protocol_t *
pi_protocol (int pi_sd, int level)
{
pi_socket_t *ps;
if (!(ps = find_pi_socket(pi_sd))) {
errno = ESRCH;
return NULL;
}
return protocol_queue_find(ps, level);
}
pi_protocol_t *
pi_protocol_next (int pi_sd, int level)
{
pi_socket_t *ps;
if (!(ps = find_pi_socket(pi_sd))) {
errno = ESRCH;
return NULL;
}
return protocol_queue_find_next(ps, level);
}
/* Environment Code */
/***********************************************************************
*
* Function: env_check
*
* Summary: configures Pilot-Link debug environment
*
* Parameters: void
*
* Returns: void
*
***********************************************************************/
static void
env_dbgcheck (void)
{
if (getenv("PILOT_DEBUG")) {
int types = 0,
done;
char *debug,
*b,
*e;
debug = strdup(getenv("PILOT_DEBUG"));
b = debug;
done = 0;
while (!done) {
e = strchr(b, ' ');
if (e)
*e = '\0';
else
done = 1;
if (!strcmp(b, "SYS"))
types |= PI_DBG_SYS;
else if (!strcmp(b, "DEV"))
types |= PI_DBG_DEV;
else if (!strcmp(b, "SLP"))
types |= PI_DBG_SLP;
else if (!strcmp(b, "PADP"))
types |= PI_DBG_PADP;
else if (!strcmp(b, "DLP"))
types |= PI_DBG_DLP;
else if (!strcmp(b, "NET"))
types |= PI_DBG_NET;
else if (!strcmp(b, "CMP"))
types |= PI_DBG_CMP;
else if (!strcmp(b, "SOCK"))
types |= PI_DBG_SOCK;
else if (!strcmp(b, "API"))
types |= PI_DBG_API;
else if (!strcmp(b, "USER"))
types |= PI_DBG_USER;
else if (!strcmp(b, "ALL"))
types |= PI_DBG_ALL;
e++;
b = e;
}
pi_debug_set_types(types);
free(debug);
}
/* PILOT Debug Level */
if (getenv("PILOT_DEBUG_LEVEL")) {
int level = 0;
const char *debug;
debug = getenv("PILOT_DEBUG_LEVEL");
if (!strcmp(debug, "NONE"))
level |= PI_DBG_LVL_NONE;
else if (!strcmp(debug, "ERR"))
level |= PI_DBG_LVL_ERR;
else if (!strcmp(debug, "WARN"))
level |= PI_DBG_LVL_WARN;
else if (!strcmp(debug, "INFO"))
level |= PI_DBG_LVL_INFO;
else if (!strcmp(debug, "DEBUG"))
level |= PI_DBG_LVL_DEBUG;
pi_debug_set_level (level);
}
/* log file name */
if (getenv("PILOT_LOG") && atoi(getenv("PILOT_LOG"))) {
const char *logfile;
logfile = getenv("PILOT_LOGFILE");
if (logfile == NULL)
pi_debug_set_file("pilot-link.debug");
else
pi_debug_set_file(logfile);
}
}
/* Util functions */
/***********************************************************************
*
* Function: is_connected
*
* Summary: interrogate socket connection state
*
* Parameters: pi_socket*
*
* Returns: 1 if socket is connected, 0 otherwise
*
***********************************************************************/
static int
is_connected (pi_socket_t *ps)
{
return (ps->state == PI_SOCK_CONN_INIT || ps->state == PI_SOCK_CONN_ACCEPT) ? 1 : 0;
}
/***********************************************************************
*
* Function: is_listener
*
* Summary: interrogate socket listener state
*
* Parameters: pi_socket*
*
* Returns: 1 if socket is listener, 0 otherwise
*
***********************************************************************/
static int
is_listener (pi_socket_t *ps)
{
return (ps->state == PI_SOCK_LISTEN) ? 1 : 0;
}
/* Alarm Handling Code */
static RETSIGTYPE
onalarm(int signo)
{
pi_socket_list_t *l;
signal(signo, onalarm);
pi_mutex_lock(&watch_list_mutex);
for (l = watch_list; l != NULL; l = l->next) {
pi_socket_t *ps = l->ps;
if (!is_connected(ps))
continue;
if (pi_tickle(ps->sd) < 0) {
LOG((PI_DBG_SOCK, PI_DBG_LVL_INFO,
"SOCKET Socket %d is busy during tickle\n",
ps->sd));
alarm(1);
} else {
LOG((PI_DBG_SOCK, PI_DBG_LVL_INFO,
"SOCKET Tickling socket %d\n", ps->sd));
alarm(interval);
}
}
pi_mutex_unlock(&watch_list_mutex);
}
/* Exit Handling Code */
/***********************************************************************
*
* Function: onexit
*
* Summary: this function closes and destroys all pi_sockets and
* frees the global pi_socket_list
*
* Parameters: void
*
* Returns: void
*
***********************************************************************/
static void
onexit(void)
{
pi_socket_list_t *l,
*list;
pi_mutex_lock(&psl_mutex);
list = ps_list_copy (psl);
pi_mutex_unlock(&psl_mutex);
for (l = list; l != NULL; l = l->next)
pi_close(l->ps->sd);
ps_list_free (list);
}
/***********************************************************************
*
* Function: installexit
*
* Summary: install exit function using atexit()
*
* Parameters: void
*
* Returns: void
*
***********************************************************************/
static void
installexit(void)
{
if (!pi_sock_installedexit) {
atexit(onexit);
pi_sock_installedexit = 1;
}
}
int
pi_socket(int domain, int type, int protocol)
{
pi_socket_t *ps;
pi_socket_list_t *list;
env_dbgcheck ();
if (protocol == 0) {
if (type == PI_SOCK_STREAM)
protocol = PI_PF_DLP;
else if (type == PI_SOCK_RAW)
protocol = PI_PF_DEV;
}
ps = calloc(1, sizeof(pi_socket_t));
if (ps == NULL) {
errno = ENOMEM;
return -1;
}
/* Create unique socket descriptor */
if ((ps->sd = open(NULL_DEVICE, O_RDWR)) == -1) {
int err = errno; /* Save errno of open */
free(ps);
errno = err;
return -1;
}
/* Initialize the rest of the fields (calloc zeroes out
all the fields we don't touch) */
ps->type = type;
ps->protocol = protocol;
ps->state = PI_SOCK_CLOSE;
ps->honor_rx_to = 1;
ps->command = 1;
/* post the new socket to the list */
list = pi_socket_recognize(ps);
if (list == NULL) {
close (ps->sd);
free(ps);
errno = ENOMEM;
return -1;
}
installexit();
return ps->sd;
}
int
pi_socket_setsd(pi_socket_t *ps, int pi_sd)
{
#ifdef HAVE_DUP2
ps->sd = dup2(pi_sd, ps->sd);
#else
close(ps->sd);
#ifdef F_DUPFD
ps->sd = fcntl(pi_sd, F_DUPFD, ps->sd);
#else
ps->sd = dup(pi_sd);
#endif
#endif
if (ps->sd == -1)
return pi_set_error(ps->sd, PI_ERR_GENERIC_SYSTEM);
if (ps->sd != pi_sd)
close(pi_sd);
return 0;
}
/***********************************************************************
*
* Function: pi_socket_init
*
* Summary: inits the pi_socket
*
* Parameters: pi_socket_t*
*
* Returns: 0
*
***********************************************************************/
int
pi_socket_init(pi_socket_t *ps)
{
protocol_queue_build (ps, 1);
return 0;
}
/***********************************************************************
*
* Function: pi_socket_recognize
*
* Summary: appends the pi_socket to global list
*
* Parameters: pi_socket*
*
* Returns: pi_socket_list_t *
*
***********************************************************************/
pi_socket_list_t *
pi_socket_recognize(pi_socket_t *ps)
{
pi_mutex_lock(&psl_mutex);
psl = ps_list_append(psl, ps);
pi_mutex_unlock(&psl_mutex);
return psl;
}
/***********************************************************************
*
* Function: pi_devsocket (static)
*
* Summary: Looks up a socket and creates a new device
* FIXME: Decide whether or not to create the socket here
*
* Parameters: None
*
* Returns: Nothing
*
***********************************************************************/
static pi_socket_t *
pi_devsocket(int pi_sd, const char *port, struct pi_sockaddr *addr)
{
pi_socket_t *ps;
if (!(ps = find_pi_socket(pi_sd))) {
errno = ESRCH;
return NULL;
}
if (port == NULL && (port = getenv("PILOTPORT")) == NULL) {
errno = ENXIO;
return NULL;
}
/* Create the device and sockaddr */
addr->pi_family = PI_AF_PILOT;
if (!strncmp (port, "serial:", 7)) {
strncpy(addr->pi_device, port + 7, sizeof(addr->pi_device));
ps->device = pi_serial_device (PI_SERIAL_DEV);
#ifdef HAVE_USB
} else if (!strncmp (port, "usb:", 4)) {
strncpy(addr->pi_device, port + 4, sizeof(addr->pi_device));
ps->device = pi_usb_device (PI_USB_DEV);
#endif
} else if (!strncmp (port, "net:", 4)) {
strncpy(addr->pi_device, port + 4, sizeof(addr->pi_device));
ps->device = pi_inet_device (PI_NET_DEV);
#ifdef HAVE_BLUEZ
} else if (!strncmp (port, "bluetooth:", 10) || !strncmp (port, "bt:", 3)) {
strncpy(addr->pi_device, strchr(port, ':') + 1, sizeof(addr->pi_device));
ps->device = pi_bluetooth_device (PI_BLUETOOTH_DEV);
#endif
} else {
/* No prefix assumed to be serial: (for compatibility) */
strncpy(addr->pi_device, port, sizeof(addr->pi_device));
ps->device = pi_serial_device (PI_SERIAL_DEV);
}
return ps;
}
int
pi_connect(int pi_sd, const char *port)
{
pi_socket_t *ps;
struct pi_sockaddr addr;
int result;
ps = pi_devsocket(pi_sd, port, &addr);
if (!ps)
return PI_ERR_SOCK_INVALID;
/* Build the protocol queue */
protocol_queue_build (ps, 0);
result = ps->device->connect (ps, (struct sockaddr *)&addr, sizeof(addr));
if (result < 0)
pi_close(pi_sd);
return result;
}
int
pi_bind(int pi_sd, const char *port)
{
int bind_return;
pi_socket_t *ps;
struct pi_sockaddr addr;
ps = pi_devsocket(pi_sd, port, &addr);
if (!ps)
return PI_ERR_SOCK_INVALID;
bind_return =
ps->device->bind (ps, (struct sockaddr *)&addr, sizeof(addr));
if (bind_return < 0) {
ps->device->free (ps->device);
ps->device = NULL;
}
return bind_return;
}
int
pi_listen(int pi_sd, int backlog)
{
pi_socket_t *ps;
if (!(ps = find_pi_socket(pi_sd))) {
errno = ESRCH;
return PI_ERR_SOCK_INVALID;
}
return ps->device->listen (ps, backlog);
}
int
pi_accept(int pi_sd, struct sockaddr *addr, size_t *addrlen)
{
return pi_accept_to(pi_sd, addr, addrlen, 0);
}
int
pi_accept_to(int pi_sd, struct sockaddr *addr, size_t *addrlen, int timeout)
{
pi_socket_t *ps;
int result;
if (!(ps = find_pi_socket(pi_sd))) {
errno = ESRCH;
return PI_ERR_SOCK_INVALID;
}
if (!is_listener (ps))
return PI_ERR_SOCK_LISTENER;
ps->accept_to = timeout;
result = ps->device->accept(ps, addr, addrlen);
if (result < 0) {
LOG((PI_DBG_SOCK, PI_DBG_LVL_DEBUG,
"pi_accept_to: ps->device->accept returned %d, calling pi_close()\n",
result));
pi_close(pi_sd);
}
return result;
}
int
pi_getsockopt(int pi_sd, int level, int option_name,
void *option_value, size_t *option_len)
{
pi_socket_t *ps;
pi_protocol_t *prot;
if (!(ps = find_pi_socket(pi_sd))) {
errno = ESRCH;
return PI_ERR_SOCK_INVALID;
}
/* handle getsockopt at socket level */
if (level == PI_LEVEL_SOCK) {
switch (option_name) {
case PI_SOCK_STATE:
if (*option_len != sizeof (ps->state))
goto argerr;
memcpy (option_value, &ps->state, sizeof (ps->state));
break;
case PI_SOCK_HONOR_RX_TIMEOUT:
if (*option_len != sizeof (ps->honor_rx_to))
goto argerr;
memcpy (option_value, &ps->honor_rx_to, sizeof (ps->honor_rx_to));
break;
default:
goto argerr;
}
return 0;
}
/* find the protocol at the requested level and forward it the getsockopt request */
prot = protocol_queue_find (ps, level);
if (prot == NULL || prot->level != level) {
errno = EINVAL;
return pi_set_error(pi_sd, PI_ERR_SOCK_INVALID);
}
return prot->getsockopt (ps, level, option_name, option_value, option_len);
argerr:
errno = EINVAL;
return pi_set_error(pi_sd, PI_ERR_GENERIC_ARGUMENT);
}
int
pi_setsockopt(int pi_sd, int level, int option_name,
const void *option_value, size_t *option_len)
{
pi_socket_t *ps;
pi_protocol_t *prot;
if (!(ps = find_pi_socket(pi_sd))) {
errno = ESRCH;
return PI_ERR_SOCK_INVALID;
}
/* handle setsockopt at socket level */
if (level == PI_LEVEL_SOCK) {
switch (option_name) {
case PI_SOCK_STATE:
if (*option_len != sizeof (ps->state))
goto argerr;
memcpy (&ps->state, option_value, sizeof (ps->state));
break;
case PI_SOCK_HONOR_RX_TIMEOUT:
if (*option_len != sizeof (ps->honor_rx_to))
goto argerr;
memcpy (&ps->honor_rx_to, option_value, sizeof (ps->honor_rx_to));
break;
default:
goto argerr;
}
return 0;
}
/* find the protocol at the requested level and forward it the setsockopt request */
prot = protocol_queue_find (ps, level);
if (prot == NULL || prot->level != level) {
errno = EINVAL;
return PI_ERR_SOCK_INVALID;
}
return prot->setsockopt (ps, level, option_name, option_value, option_len);
argerr:
errno = EINVAL;
return pi_set_error(pi_sd, PI_ERR_GENERIC_ARGUMENT);
}
/***********************************************************************
*
* Function: pi_send
*
* Summary: Send message on a connected socket
*
* Parameters: None
*
* Returns: Nothing
*
***********************************************************************/
int
pi_send(int pi_sd, const void *msg, size_t len, int flags)
{
pi_socket_t *ps;
if (!(ps = find_pi_socket(pi_sd))) {
errno = ESRCH;
return PI_ERR_SOCK_INVALID;
}
if (!is_connected (ps))
return PI_ERR_SOCK_DISCONNECTED;
if (interval)
alarm(interval);
return ps->protocol_queue[0]->write (ps, (void *)msg, len, flags);
}
/***********************************************************************
*
* Function: pi_recv
*
* Summary: Receive msg on a connected socket
*
* Parameters: None
*
* Returns: Nothing
*
***********************************************************************/
ssize_t
pi_recv(int pi_sd, pi_buffer_t *msg, size_t len, int flags)
{
pi_socket_t *ps;
if (!(ps = find_pi_socket(pi_sd))) {
errno = ESRCH;
return PI_ERR_SOCK_INVALID;
}
if (!is_connected (ps))
return PI_ERR_SOCK_DISCONNECTED;
return ps->protocol_queue[0]->read (ps, msg, len, flags);
}
/***********************************************************************
*
* Function: pi_read
*
* Summary: Wrapper for receive
*
* Parameters: None
*
* Returns: Nothing
*
***********************************************************************/
ssize_t
pi_read(int pi_sd, pi_buffer_t *msg, size_t len)
{
return pi_recv(pi_sd, msg, len, 0);
}
/***********************************************************************
*
* Function: pi_write
*
* Summary: Wrapper for send
*
* Parameters: None
*
* Returns: Nothing
*
***********************************************************************/
ssize_t
pi_write(int pi_sd, const void *msg, size_t len)
{
return pi_send(pi_sd, msg, len, 0);
}
void
pi_flush(int pi_sd, int flags)
{
pi_socket_t *ps;
if (!(ps = find_pi_socket(pi_sd))) {
errno = ESRCH;
return;
}
if (!is_connected (ps))
return;
ps->protocol_queue[0]->flush (ps, flags);
}
int
pi_tickle(int pi_sd)
{
int result=0,
type,
oldtype;
size_t len = 0,
size;
unsigned char msg[1];
pi_socket_t *ps;
if (!(ps = find_pi_socket(pi_sd))) {
errno = ESRCH;
return PI_ERR_SOCK_INVALID;
}
if (!is_connected (ps))
return PI_ERR_SOCK_DISCONNECTED;
LOG((PI_DBG_SOCK, PI_DBG_LVL_INFO,
"SOCKET Tickling socket %d\n", pi_sd));
switch (ps->cmd) {
case PI_CMD_CMP:
/* save previous packet type */
size = sizeof(type);
pi_getsockopt(ps->sd, PI_LEVEL_PADP, PI_PADP_TYPE, &oldtype, &size);
/* set packet type to "tickle" */
type = padTickle;
size = sizeof(type);
pi_setsockopt(ps->sd, PI_LEVEL_PADP, PI_PADP_TYPE, &type, &size);
/* send packet */
result = ps->protocol_queue[0]->write (ps, msg, len, 0);
/* restore previous packet type */
size = sizeof(type);
pi_setsockopt(ps->sd, PI_LEVEL_PADP, PI_PADP_TYPE, &oldtype, &size);
break;
case PI_CMD_NET:
/* Enter command state */
ps->command = 1;
/* Set the type to "tickle" */
type = PI_NET_TYPE_TCKL;
size = sizeof(type);
pi_setsockopt(ps->sd, PI_LEVEL_NET, PI_NET_TYPE, &type, &size);
/* send packet */
result = ps->cmd_queue[0]->write (ps, msg, len, 0);
/* Exit command state */
ps->command = 0;
break;
}
return result;
}
int
pi_close(int pi_sd)
{
int result = 0;
pi_socket_t *ps;
if (!(ps = find_pi_socket(pi_sd))) {
errno = ESRCH;
return PI_ERR_SOCK_INVALID;
}
if (ps->type == PI_SOCK_STREAM && ps->cmd != PI_CMD_SYS) {
if (is_connected (ps)) {
ps->command = 1;
/* then end sync, with clean status */
result = dlp_EndOfSync(ps->sd, 0);
ps->command = 0;
}
}
if (result == 0) {
/* we need to remove the entry from the list prior to
* closing it, because closing it will reset the pi_sd */
pi_mutex_lock(&psl_mutex);
psl = ps_list_remove (psl, pi_sd);
pi_mutex_unlock(&psl_mutex);
pi_mutex_lock(&watch_list_mutex);
watch_list = ps_list_remove (watch_list, pi_sd);
pi_mutex_unlock(&watch_list_mutex);
if (ps->device != NULL)
result = ps->device->close (ps);
protocol_queue_destroy(ps);
if (ps->device != NULL)
ps->device->free(ps->device);
if (ps->sd > 0)
close(ps->sd);
free(ps);
}
return result;
}
/***********************************************************************
*
* Function: pi_getsockname
*
* Summary: Get the local address for a socket
*
* Parameters: None
*
* Returns: Nothing
*
***********************************************************************/
int
pi_getsockname(int pi_sd, struct sockaddr *addr, size_t *namelen)
{
pi_socket_t *ps;
if (!(ps = find_pi_socket(pi_sd))) {
errno = ESRCH;
return PI_ERR_SOCK_INVALID;
}
if (*namelen > ps->laddrlen)
*namelen = ps->laddrlen;
memcpy(addr, &ps->laddr, *namelen);
return 0;
}
/***********************************************************************
*
* Function: pi_getsockpeer
*
* Summary: Get the remote address for a socket
*
* Parameters: None
*
* Returns: Nothing
*
***********************************************************************/
int
pi_getsockpeer(int pi_sd, struct sockaddr *addr, size_t *namelen)
{
pi_socket_t *ps;
if (!(ps = find_pi_socket(pi_sd))) {
errno = ESRCH;
return PI_ERR_SOCK_INVALID;
}
if (*namelen > ps->raddrlen)
*namelen = ps->raddrlen;
memcpy(addr, &ps->raddr, *namelen);
return 0;
}
int
pi_version(int pi_sd)
{
size_t size;
pi_socket_t *ps;
if (!(ps = find_pi_socket(pi_sd))) {
errno = ESRCH;
return PI_ERR_SOCK_INVALID;
}
if (ps->dlpversion)
return ps->dlpversion;
if (ps->cmd == PI_CMD_CMP) {
/* Enter command state */
ps->command = 1;
/* Get the version */
size = sizeof(ps->dlpversion);
pi_getsockopt(ps->sd, PI_LEVEL_CMP, PI_CMP_VERS, &ps->dlpversion, &size);
ps->maxrecsize = DLP_BUF_SIZE;
/* Exit command state */
ps->command = 0;
}
return ps->dlpversion;
}
unsigned long
pi_maxrecsize(int pi_sd)
{
pi_socket_t *ps;
if (!(ps = find_pi_socket(pi_sd))) {
errno = ESRCH;
return 0;
}
/* pi_version will read necessary info from device */
if (pi_version(pi_sd) == 0)
return DLP_BUF_SIZE;
return ps->maxrecsize;
}
/***********************************************************************
*
* Function: find_pi_socket
*
* Summary: Thread-safe wrapper for ps_list_find
*
* Parameters: None
*
* Returns: Nothing
*
***********************************************************************/
pi_socket_t *
find_pi_socket(int pi_sd)
{
pi_socket_t *result;
pi_mutex_lock(&psl_mutex);
result = ps_list_find (psl, pi_sd);
pi_mutex_unlock(&psl_mutex);
return result;
}
int
pi_watchdog(int pi_sd, int newinterval)
{
pi_socket_t *ps;
if (!(ps = find_pi_socket(pi_sd))) {
errno = ESRCH;
return PI_ERR_SOCK_INVALID;
}
pi_mutex_lock(&watch_list_mutex);
watch_list = ps_list_append (watch_list, ps);
pi_mutex_unlock(&watch_list_mutex);
signal(SIGALRM, onalarm);
interval = newinterval;
alarm(interval);
return 0;
}
int
pi_error(int pi_sd)
{
pi_socket_t *ps;
if ((ps = find_pi_socket(pi_sd)) == NULL) {
errno = ESRCH;
return PI_ERR_SOCK_INVALID;
}
return ps->last_error;
}
int
pi_set_error(int pi_sd, int error_code)
{
pi_socket_t *ps;
if ((ps = find_pi_socket(pi_sd)))
ps->last_error = error_code;
else
errno = ESRCH;
/* also update errno if makes sense */
if (error_code == PI_ERR_GENERIC_MEMORY)
errno = ENOMEM;
return error_code;
}
int
pi_palmos_error(int pi_sd)
{
pi_socket_t *ps;
if ((ps = find_pi_socket(pi_sd)) == NULL) {
errno = ESRCH;
return PI_ERR_SOCK_INVALID;
}
return ps->palmos_error;
}
int
pi_set_palmos_error(int pi_sd, int error_code)
{
pi_socket_t *ps;
if ((ps = find_pi_socket(pi_sd)))
ps->palmos_error = error_code;
else
errno = ESRCH;
return error_code;
}
void
pi_reset_errors(int pi_sd)
{
pi_socket_t *ps;
if ((ps = find_pi_socket(pi_sd))) {
ps->last_error = 0;
ps->palmos_error = 0;
} else
errno = ESRCH;
}
int
pi_socket_connected(int pi_sd)
{
pi_socket_t *ps;
if ((ps = find_pi_socket(pi_sd)))
return is_connected(ps);
errno = ESRCH;
return 0;
}
/* vi: set ts=8 sw=4 sts=4 noexpandtab: cin */
/* ex: set tabstop=4 expandtab: */
/* Local Variables: */
/* indent-tabs-mode: t */
/* c-basic-offset: 8 */
/* End: */