Implemented connection type "tcp" (GCT_TCP), use <hostname>:<port> as "port"
[gnokii.git] / common / devices / tcp.c
1 /*
2   
3   $Id$
4
5   G N O K I I
6
7   A Linux/Unix toolset and driver for Nokia mobile phones.
8
9   Copyright (C) 2002 Jan Kratochvil
10
11   Released under the terms of the GNU GPL, see file COPYING for more details.
12
13 */
14
15 #include "misc.h"
16 #include "cfgreader.h"
17
18 #include <stdio.h>
19 #include <fcntl.h>
20 #include <sys/ioctl.h>
21 #include <string.h>
22 #include <limits.h>
23 #include <stdlib.h>
24 #include <errno.h>
25 #include <sys/types.h>
26 #include <sys/wait.h>
27 #include <sys/ioctl.h>
28 #include <netinet/in.h>
29 #include <sys/socket.h>
30 #include <netdb.h>
31
32 #if __unices__
33 #  include <sys/file.h>
34 #endif
35
36 #include <termios.h>
37 #include "devices/tcp.h"
38 #include "devices/unixserial.h"
39
40 #ifdef HAVE_SYS_IOCTL_COMPAT_H
41   #include <sys/ioctl_compat.h>
42 #endif
43
44 #ifdef HAVE_SYS_SELECT_H
45 #include <sys/select.h>
46 #endif
47
48 #ifndef O_NONBLOCK
49   #define O_NONBLOCK  0
50 #endif
51
52 /* Open the serial port and store the settings. */
53
54 int tcp_open(__const char *__file) {
55
56   int __fd;
57   int i;
58   struct sockaddr_in addr;
59   static bool atexit_registered=false;
60   char *filedup,*portstr,*end;
61   unsigned long portul;
62   struct hostent *hostent;
63
64   if (!atexit_registered) {
65     memset(serial_close_all_openfds,-1,sizeof(serial_close_all_openfds));
66 #if 0   /* Disabled for now as atexit() functions are then called multiple times for pthreads! */
67     signal(SIGINT,unixserial_interrupted);
68 #endif
69     atexit(serial_close_all);
70     atexit_registered=true;
71     }
72
73   __fd = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
74   if (__fd == -1) {
75     perror("Gnokii tcp_open: socket()");
76     return (-1);
77   }
78   if (!(filedup=strdup(__file))) {
79 fail_close:
80     close(__fd);
81     return (-1);
82   }
83   if (!(portstr=strchr(filedup,':'))) {
84     fprintf(stderr,"Gnokii tcp_open: colon (':') not found in connect strings \"%s\"!\n",filedup);
85 fail_free:
86     free(filedup);
87     goto fail_close;
88   }
89   *portstr++='\0';
90   portul=strtoul(portstr,&end,0);
91   if ((end && *end) || portul>=0x10000) {
92     fprintf(stderr,"Gnokii tcp_open: Port string \"%s\" not valid for IPv4 connection!\n",portstr);
93     goto fail_free;
94   }
95   if (!(hostent=gethostbyname(filedup))) {
96     fprintf(stderr,"Gnokii tcp_open: Unknown host \"%s\"!\n",filedup);
97     goto fail_free;
98   }
99   if (hostent->h_addrtype!=AF_INET || hostent->h_length!=sizeof(addr.sin_addr) || !hostent->h_addr_list[0]) {
100     fprintf(stderr,"Gnokii tcp_open: Address resolve for host \"%s\" not compatible!\n",filedup);
101     goto fail_free;
102   }
103   free(filedup);
104   
105   addr.sin_family=AF_INET;
106   addr.sin_port=htons(portul);
107   memcpy(&addr.sin_addr,hostent->h_addr_list[0],sizeof(addr.sin_addr));
108
109   if (connect(__fd,(struct sockaddr *)&addr,sizeof(addr))) {
110     perror("Gnokii tcp_open: connect()");
111     goto fail_close;
112   }
113
114   for (i=0;i<ARRAY_LEN(serial_close_all_openfds);i++)
115     if (serial_close_all_openfds[i]==-1 || serial_close_all_openfds[i]==__fd) {
116       serial_close_all_openfds[i]=__fd;
117       break;
118       }
119
120   return __fd;
121 }
122
123 int tcp_close(int __fd) {
124   int i;
125
126   for (i=0;i<ARRAY_LEN(serial_close_all_openfds);i++)
127     if (serial_close_all_openfds[i]==__fd)
128       serial_close_all_openfds[i]=-1;           /* fd closed */
129
130   /* handle config file disconnect_script:
131    */
132   if (-1 == device_script(__fd,"disconnect_script"))
133     fprintf(stderr,"Gnokii tcp_close: disconnect_script\n");
134
135   return (close(__fd));
136 }
137
138 /* Open a device with standard options.
139  * Use value (-1) for "__with_hw_handshake" if its specification is required from the user
140  */
141 int tcp_opendevice(__const char *__file, int __with_async) {
142
143   int fd;
144   int retcode;
145
146   /* Open device */
147
148   fd = tcp_open(__file);
149
150   if (fd < 0) 
151     return fd;
152
153   /* handle config file connect_script:
154    */
155   if (-1 == device_script(fd,"connect_script")) {
156     fprintf(stderr,"Gnokii tcp_opendevice: connect_script\n");
157     tcp_close(fd);
158     return(-1);
159   }
160
161   /* Allow process/thread to receive SIGIO */
162
163 #if !(__unices__)
164   retcode = fcntl(fd, F_SETOWN, getpid());
165   if (retcode == -1){
166     perror("Gnokii tcp_opendevice: fnctl(F_SETOWN)");
167     tcp_close(fd);
168     return(-1);
169   }
170 #endif
171
172   /* Make filedescriptor asynchronous. */
173
174   /* We need to supply FNONBLOCK (or O_NONBLOCK) again as it would get reset
175    * by F_SETFL as a side-effect!
176    */
177   retcode=fcntl(fd, F_SETFL, (__with_async ? FASYNC : 0) | FNONBLOCK);
178   if (retcode == -1){
179     perror("Gnokii tcp_opendevice: fnctl(F_SETFL)");
180     tcp_close(fd);
181     return(-1);
182   }
183   
184   return fd;
185 }
186
187 int tcp_select(int fd, struct timeval *timeout) {
188
189   return serial_select(fd, timeout);
190 }
191
192
193 /* Read from serial device. */
194
195 size_t tcp_read(int __fd, __ptr_t __buf, size_t __nbytes) {
196
197   return (read(__fd, __buf, __nbytes));
198 }
199
200 /* Write to serial device. */
201
202 size_t tcp_write(int __fd, __const __ptr_t __buf, size_t __n) {
203
204         return(write(__fd, __buf, __n));
205 }