/* * $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 #endif #include #include #include #include #include #include #include #include #include #include #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: */