2 /******************************************************************************
3 * MODULE : socket_link.cpp
4 * DESCRIPTION: TeXmacs links by sockets
5 * COPYRIGHT : (C) 2003 Joris van der Hoeven
6 * THANKS : Beej's Guide to Network Programming has been helpful
7 *******************************************************************************
8 * This software falls under the GNU general public license version 3 or later.
9 * It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
10 * in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.
11 ******************************************************************************/
13 #include "socket_link.hpp"
14 #include "sys_utils.hpp"
15 #include "hashset.hpp"
16 #include "iterator.hpp"
18 #include "Scheme/object.hpp"
25 #include <sys/types.h>
26 #include <sys/socket.h>
27 #include <netinet/in.h>
32 hashset
<pointer
> socket_link_set
;
33 void socket_callback (void *obj
, void* info
);
35 /******************************************************************************
36 * Constructors and destructors for socket_links
37 ******************************************************************************/
39 socket_link_rep::socket_link_rep (string host2
, int port2
, int type2
, int fd
):
40 host (host2
), port (port2
), type (type2
)
42 socket_link_set
->insert ((pointer
) this);
46 if (type
== SOCKET_SERVER
) call ("server-add", object (io
));
47 else if (type
== SOCKET_CLIENT
) call ("client-add");
50 socket_link_rep::~socket_link_rep () {
52 socket_link_set
->remove ((pointer
) this);
56 make_socket_link (string host
, int port
, int type
, int fd
) {
57 return tm_new
<socket_link_rep
> (host
, port
, type
, fd
);
61 find_socket_link (int fd
) {
62 iterator
<pointer
> it
= iterate (socket_link_set
);
64 socket_link_rep
* con
= (socket_link_rep
*) it
->next();
65 if (con
->io
== fd
&& con
->alive
) return tm_link (con
);
71 close_all_sockets () {
73 iterator
<pointer
> it
= iterate (socket_link_set
);
75 socket_link_rep
* con
= (socket_link_rep
*) it
->next();
77 // FIXME: cleanly close the connection to the socket here
84 /******************************************************************************
85 * Routines for socket_links
86 ******************************************************************************/
89 socket_link_rep::start () {
91 if (alive
) return "busy";
93 cout
<< "TeXmacs] Connecting to '" << host
<< ":" << port
<< "'\n";
96 char* _host
= as_charp (host
);
97 struct hostent
*hp
= gethostbyname (_host
);
98 tm_delete_array (_host
);
99 if (hp
== NULL
) return "Error: no connection for '" * host
* "'";
102 io
= socket (AF_INET
, SOCK_STREAM
, 0);
103 if (io
< 0) return "Error: socket could not be created";
105 // connecting to socket
106 struct sockaddr_in insock
;
107 string where
= host
* ":" * as_string (port
);
108 memset ((char*) &insock
, 0, sizeof (insock
));
109 insock
.sin_family
= AF_INET
;
110 insock
.sin_port
= htons ((unsigned short) port
);
111 memcpy ((char*) &insock
.sin_addr
, hp
->h_addr
, hp
->h_length
);
112 if (connect (io
, (struct sockaddr
*) &insock
, sizeof (insock
)) < 0)
113 return "Error: refused connection to '" * where
* "'";
115 // testing whether it works
116 int flags
= O_NONBLOCK
;
117 if (fcntl (io
, F_SETFL
, flags
) < 0)
118 return "Error: non working connection to '" * where
* "'";
120 sn
= socket_notifier (io
, &socket_callback
, this, NULL
);
123 return "Error: sockets not implemented";
128 debug_io_string (string s
) {
131 for (i
=0; i
<n
; i
++) {
132 unsigned char c
= (unsigned char) s
[i
];
133 if (c
== DATA_BEGIN
) r
<< "[BEGIN]";
134 else if (c
== DATA_END
) r
<< "[END]";
135 else if (c
== DATA_COMMAND
) r
<< "[COMMAND]";
136 else if (c
== DATA_ESCAPE
) r
<< "[ESCAPE]";
143 send_all (int s
, char *buf
, int *len
) {
145 int total
= 0; // how many bytes we've sent
146 int bytes_left
= *len
; // how many we have left to send
149 while (total
< *len
) {
150 n
= send (s
, buf
+ total
, bytes_left
, 0);
165 socket_link_rep::write (string s
, int channel
) {
166 if ((!alive
) || (channel
!= LINK_IN
)) return;
167 if (DEBUG_IO
) cout
<< "---> " << debug_io_string (s
) << "\n";
169 if (send_all (io
, &(s
[0]), &len
) == -1) {
170 cerr
<< "TeXmacs] write to '" << host
<< ":" << port
<< "' failed\n";
176 socket_link_rep::feed (int channel
) {
178 if ((!alive
) || (channel
!= LINK_OUT
)) return;
180 int r
= recv (io
, tempout
, 1024, 0);
182 if (r
== 0) cout
<< "TeXmacs] '" << host
<< ":" << port
<< "' hung up\n";
183 else cerr
<< "TeXmacs] read failed from '" << host
<< ":" << port
<< "'\n";
187 if (DEBUG_IO
) cout
<< debug_io_string (string (tempout
, r
));
188 outbuf
<< string (tempout
, r
);
194 socket_link_rep::watch (int channel
) {
195 static string empty_string
= "";
196 if (channel
== LINK_OUT
) return outbuf
;
197 else return empty_string
;
201 socket_link_rep::read (int channel
) {
202 if (channel
== LINK_OUT
) {
211 socket_link_rep::listen (int msecs
) {
218 tv
.tv_sec
= msecs
/ 1000;
219 tv
.tv_usec
= 1000 * (msecs
% 1000);
220 int nr
= select (io
+1, &rfds
, NULL
, NULL
, &tv
);
221 if (nr
!= 0 && FD_ISSET (io
, &rfds
)) feed (LINK_OUT
);
226 socket_link_rep::interrupt () {
230 socket_link_rep::stop () {
233 if (type
== SOCKET_SERVER
) call ("server-remove", object (io
));
234 else if (type
== SOCKET_CLIENT
) call ("client-remove");
238 sn
= socket_notifier ();
243 /******************************************************************************
244 * Call back for new information on socket
245 ******************************************************************************/
248 socket_callback (void *obj
, void* info
) {
251 socket_link_rep
* con
= (socket_link_rep
*) obj
;
257 int max_fd
= con
->io
+ 1;
258 FD_SET (con
->io
, &rfds
);
263 select (max_fd
, &rfds
, NULL
, NULL
, &tv
);
266 if (con
->alive
&& FD_ISSET (con
->io
, &rfds
)) {
267 //cout << "socket_callback OUT" << LF;
268 con
->feed (LINK_OUT
);
272 if (!is_nil (con
->feed_cmd
) && news
)
273 con
->feed_cmd
->apply ();