Implemented connection type "tcp" (GCT_TCP), use <hostname>:<port> as "port"
[gnokii.git] / common / devices / tcp.c
diff --git a/common/devices/tcp.c b/common/devices/tcp.c
new file mode 100644 (file)
index 0000000..ea4918e
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+  
+  $Id$
+
+  G N O K I I
+
+  A Linux/Unix toolset and driver for Nokia mobile phones.
+
+  Copyright (C) 2002 Jan Kratochvil
+
+  Released under the terms of the GNU GPL, see file COPYING for more details.
+
+*/
+
+#include "misc.h"
+#include "cfgreader.h"
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <string.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#if __unices__
+#  include <sys/file.h>
+#endif
+
+#include <termios.h>
+#include "devices/tcp.h"
+#include "devices/unixserial.h"
+
+#ifdef HAVE_SYS_IOCTL_COMPAT_H
+  #include <sys/ioctl_compat.h>
+#endif
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#ifndef O_NONBLOCK
+  #define O_NONBLOCK  0
+#endif
+
+/* Open the serial port and store the settings. */
+
+int tcp_open(__const char *__file) {
+
+  int __fd;
+  int i;
+  struct sockaddr_in addr;
+  static bool atexit_registered=false;
+  char *filedup,*portstr,*end;
+  unsigned long portul;
+  struct hostent *hostent;
+
+  if (!atexit_registered) {
+    memset(serial_close_all_openfds,-1,sizeof(serial_close_all_openfds));
+#if 0  /* Disabled for now as atexit() functions are then called multiple times for pthreads! */
+    signal(SIGINT,unixserial_interrupted);
+#endif
+    atexit(serial_close_all);
+    atexit_registered=true;
+    }
+
+  __fd = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
+  if (__fd == -1) {
+    perror("Gnokii tcp_open: socket()");
+    return (-1);
+  }
+  if (!(filedup=strdup(__file))) {
+fail_close:
+    close(__fd);
+    return (-1);
+  }
+  if (!(portstr=strchr(filedup,':'))) {
+    fprintf(stderr,"Gnokii tcp_open: colon (':') not found in connect strings \"%s\"!\n",filedup);
+fail_free:
+    free(filedup);
+    goto fail_close;
+  }
+  *portstr++='\0';
+  portul=strtoul(portstr,&end,0);
+  if ((end && *end) || portul>=0x10000) {
+    fprintf(stderr,"Gnokii tcp_open: Port string \"%s\" not valid for IPv4 connection!\n",portstr);
+    goto fail_free;
+  }
+  if (!(hostent=gethostbyname(filedup))) {
+    fprintf(stderr,"Gnokii tcp_open: Unknown host \"%s\"!\n",filedup);
+    goto fail_free;
+  }
+  if (hostent->h_addrtype!=AF_INET || hostent->h_length!=sizeof(addr.sin_addr) || !hostent->h_addr_list[0]) {
+    fprintf(stderr,"Gnokii tcp_open: Address resolve for host \"%s\" not compatible!\n",filedup);
+    goto fail_free;
+  }
+  free(filedup);
+  
+  addr.sin_family=AF_INET;
+  addr.sin_port=htons(portul);
+  memcpy(&addr.sin_addr,hostent->h_addr_list[0],sizeof(addr.sin_addr));
+
+  if (connect(__fd,(struct sockaddr *)&addr,sizeof(addr))) {
+    perror("Gnokii tcp_open: connect()");
+    goto fail_close;
+  }
+
+  for (i=0;i<ARRAY_LEN(serial_close_all_openfds);i++)
+    if (serial_close_all_openfds[i]==-1 || serial_close_all_openfds[i]==__fd) {
+      serial_close_all_openfds[i]=__fd;
+      break;
+      }
+
+  return __fd;
+}
+
+int tcp_close(int __fd) {
+  int i;
+
+  for (i=0;i<ARRAY_LEN(serial_close_all_openfds);i++)
+    if (serial_close_all_openfds[i]==__fd)
+      serial_close_all_openfds[i]=-1;          /* fd closed */
+
+  /* handle config file disconnect_script:
+   */
+  if (-1 == device_script(__fd,"disconnect_script"))
+    fprintf(stderr,"Gnokii tcp_close: disconnect_script\n");
+
+  return (close(__fd));
+}
+
+/* Open a device with standard options.
+ * Use value (-1) for "__with_hw_handshake" if its specification is required from the user
+ */
+int tcp_opendevice(__const char *__file, int __with_async) {
+
+  int fd;
+  int retcode;
+
+  /* Open device */
+
+  fd = tcp_open(__file);
+
+  if (fd < 0) 
+    return fd;
+
+  /* handle config file connect_script:
+   */
+  if (-1 == device_script(fd,"connect_script")) {
+    fprintf(stderr,"Gnokii tcp_opendevice: connect_script\n");
+    tcp_close(fd);
+    return(-1);
+  }
+
+  /* Allow process/thread to receive SIGIO */
+
+#if !(__unices__)
+  retcode = fcntl(fd, F_SETOWN, getpid());
+  if (retcode == -1){
+    perror("Gnokii tcp_opendevice: fnctl(F_SETOWN)");
+    tcp_close(fd);
+    return(-1);
+  }
+#endif
+
+  /* Make filedescriptor asynchronous. */
+
+  /* We need to supply FNONBLOCK (or O_NONBLOCK) again as it would get reset
+   * by F_SETFL as a side-effect!
+   */
+  retcode=fcntl(fd, F_SETFL, (__with_async ? FASYNC : 0) | FNONBLOCK);
+  if (retcode == -1){
+    perror("Gnokii tcp_opendevice: fnctl(F_SETFL)");
+    tcp_close(fd);
+    return(-1);
+  }
+  
+  return fd;
+}
+
+int tcp_select(int fd, struct timeval *timeout) {
+
+  return serial_select(fd, timeout);
+}
+
+
+/* Read from serial device. */
+
+size_t tcp_read(int __fd, __ptr_t __buf, size_t __nbytes) {
+
+  return (read(__fd, __buf, __nbytes));
+}
+
+/* Write to serial device. */
+
+size_t tcp_write(int __fd, __const __ptr_t __buf, size_t __n) {
+
+       return(write(__fd, __buf, __n));
+}