Back to index

texmacs  1.0.7.15
socket_link.cpp
Go to the documentation of this file.
00001 
00002 /******************************************************************************
00003 * MODULE     : socket_link.cpp
00004 * DESCRIPTION: TeXmacs links by sockets
00005 * COPYRIGHT  : (C) 2003  Joris van der Hoeven
00006 * THANKS     : Beej's Guide to Network Programming has been helpful
00007 *******************************************************************************
00008 * This software falls under the GNU general public license version 3 or later.
00009 * It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
00010 * in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.
00011 ******************************************************************************/
00012 
00013 #include "socket_link.hpp"
00014 #include "sys_utils.hpp"
00015 #include "hashset.hpp"
00016 #include "iterator.hpp"
00017 #include "timer.hpp"
00018 #include "scheme.hpp"
00019 #include <stdio.h>
00020 #include <string.h>
00021 #ifndef __MINGW32__
00022 #include <unistd.h>
00023 #include <fcntl.h>
00024 #include <sys/wait.h>
00025 #include <sys/types.h>
00026 #include <sys/socket.h>
00027 #include <netinet/in.h>
00028 #include <netdb.h>
00029 #endif
00030 #include <errno.h>
00031 
00032 hashset<pointer> socket_link_set;
00033 void socket_callback (void *obj, void* info);
00034 
00035 /******************************************************************************
00036 * Constructors and destructors for socket_links
00037 ******************************************************************************/
00038 
00039 socket_link_rep::socket_link_rep (string host2, int port2, int type2, int fd):
00040   host (host2), port (port2), type (type2)
00041 {
00042   socket_link_set->insert ((pointer) this);
00043   io     = fd;
00044   outbuf = "";
00045   alive  = (fd != -1);
00046   if (type == SOCKET_SERVER) call ("server-add", object (io));
00047   else if (type == SOCKET_CLIENT) call ("client-add");
00048 }
00049 
00050 socket_link_rep::~socket_link_rep () {
00051   stop ();
00052   socket_link_set->remove ((pointer) this);
00053 }
00054 
00055 tm_link
00056 make_socket_link (string host, int port, int type, int fd) {
00057   return tm_new<socket_link_rep> (host, port, type, fd);
00058 }
00059 
00060 tm_link
00061 find_socket_link (int fd) {
00062   iterator<pointer> it= iterate (socket_link_set);
00063   while (it->busy()) {
00064     socket_link_rep* con= (socket_link_rep*) it->next();
00065     if (con->io == fd && con->alive) return tm_link (con);
00066   }
00067   return tm_link ();
00068 }
00069 
00070 void
00071 close_all_sockets () {
00072 #ifndef __MINGW32__
00073   iterator<pointer> it= iterate (socket_link_set);
00074   while (it->busy()) {
00075     socket_link_rep* con= (socket_link_rep*) it->next();
00076     if (con->alive) {
00077       // FIXME: cleanly close the connection to the socket here
00078       con->alive= false;
00079     }
00080   }
00081 #endif
00082 }
00083 
00084 /******************************************************************************
00085 * Routines for socket_links
00086 ******************************************************************************/
00087 
00088 string
00089 socket_link_rep::start () {
00090 #ifndef __MINGW32__
00091   if (alive) return "busy";
00092   if (DEBUG_AUTO)
00093     cout << "TeXmacs] Connecting to '" << host << ":" << port << "'\n";
00094   
00095   // getting host
00096   char* _host= as_charp (host);
00097   struct hostent *hp = gethostbyname (_host);
00098   tm_delete_array (_host);
00099   if (hp == NULL) return "Error: no connection for '" * host * "'";
00100 
00101   // creating socket
00102   io= socket (AF_INET, SOCK_STREAM, 0);
00103   if (io < 0) return "Error: socket could not be created";
00104 
00105   // connecting to socket
00106   struct sockaddr_in insock;
00107   string where= host * ":" * as_string (port);
00108   memset ((char*) &insock, 0, sizeof (insock));
00109   insock.sin_family = AF_INET;
00110   insock.sin_port = htons ((unsigned short) port);
00111   memcpy ((char*) &insock.sin_addr, hp->h_addr, hp->h_length);
00112   if (connect (io, (struct sockaddr*) &insock, sizeof (insock)) < 0)
00113     return "Error: refused connection to '" * where * "'";
00114 
00115   // testing whether it works
00116   int flags = O_NONBLOCK;
00117   if (fcntl (io, F_SETFL, flags) < 0)
00118     return "Error: non working connection to '" * where * "'";
00119   alive = true;
00120   sn = socket_notifier (io, &socket_callback, this, NULL);  
00121   return "ok";
00122 #else
00123   return "Error: sockets not implemented";
00124 #endif
00125 }
00126 
00127 static string
00128 debug_io_string (string s) {
00129   int i, n= N(s);
00130   string r;
00131   for (i=0; i<n; i++) {
00132     unsigned char c= (unsigned char) s[i];
00133     if (c == DATA_BEGIN) r << "[BEGIN]";
00134     else if (c == DATA_END) r << "[END]";
00135     else if (c == DATA_COMMAND) r << "[COMMAND]";
00136     else if (c == DATA_ESCAPE) r << "[ESCAPE]";
00137     else r << s[i];
00138   }
00139   return r;
00140 }
00141 
00142 static int
00143 send_all (int s, char *buf, int *len) {
00144 #ifndef __MINGW32__
00145   int total= 0;          // how many bytes we've sent
00146   int bytes_left= *len;  // how many we have left to send
00147   int n= 0;
00148 
00149   while (total < *len) {
00150     n= send (s, buf + total, bytes_left, 0);
00151     if (n == -1) break;
00152     total += n;
00153     bytes_left -= n;
00154   }
00155 
00156   *len= total;
00157   return n==-1? -1: 0;
00158 #else
00159   return 0;
00160 #endif
00161 } 
00162 
00163 
00164 void
00165 socket_link_rep::write (string s, int channel) {
00166   if ((!alive) || (channel != LINK_IN)) return;
00167   if (DEBUG_IO) cout << "---> " << debug_io_string (s) << "\n";
00168   int len= N(s);
00169   if (send_all (io, &(s[0]), &len) == -1) {
00170     cerr << "TeXmacs] write to '" << host << ":" << port << "' failed\n";
00171     stop ();
00172   }
00173 }
00174 
00175 void
00176 socket_link_rep::feed (int channel) {
00177 #ifndef __MINGW32__
00178   if ((!alive) || (channel != LINK_OUT)) return;
00179   char tempout[1024];
00180   int r= recv (io, tempout, 1024, 0);
00181   if (r <= 0) {
00182     if (r == 0) cout << "TeXmacs] '" << host << ":" << port << "' hung up\n";
00183     else cerr << "TeXmacs] read failed from '" << host << ":" << port << "'\n";
00184     stop ();
00185   }
00186   else if (r != 0) {
00187     if (DEBUG_IO) cout << debug_io_string (string (tempout, r));
00188     outbuf << string (tempout, r);
00189   }
00190 #endif
00191 }
00192 
00193 string&
00194 socket_link_rep::watch (int channel) {
00195   static string empty_string= "";
00196   if (channel == LINK_OUT) return outbuf;
00197   else return empty_string;
00198 }
00199 
00200 string
00201 socket_link_rep::read (int channel) {
00202   if (channel == LINK_OUT) {
00203     string r= outbuf;
00204     outbuf= "";
00205     return r;
00206   }
00207   else return "";
00208 }
00209 
00210 void
00211 socket_link_rep::listen (int msecs) {
00212 #ifndef __MINGW32__
00213   if (!alive) return;
00214   fd_set rfds;
00215   FD_ZERO (&rfds);
00216   FD_SET (io, &rfds);
00217   struct timeval tv;
00218   tv.tv_sec  = msecs / 1000;
00219   tv.tv_usec = 1000 * (msecs % 1000);
00220   int nr= select (io+1, &rfds, NULL, NULL, &tv);
00221   if (nr != 0 && FD_ISSET (io, &rfds)) feed (LINK_OUT);
00222 #endif
00223 }
00224 
00225 void
00226 socket_link_rep::interrupt () {
00227 }
00228 
00229 void
00230 socket_link_rep::stop () {
00231 #ifndef __MINGW32__
00232   if (!alive) return;
00233   if (type == SOCKET_SERVER) call ("server-remove", object (io));
00234   else if (type == SOCKET_CLIENT) call ("client-remove");
00235   close (io);
00236   io= -1;
00237   alive= false;
00238   sn = socket_notifier ();
00239   wait (NULL);
00240 #endif
00241 }
00242 
00243 /******************************************************************************
00244 * Call back for new information on socket
00245 ******************************************************************************/
00246 
00247 void
00248 socket_callback (void *obj, void* info) {
00249 #ifndef __MINGW32__
00250   (void) info;
00251   socket_link_rep* con= (socket_link_rep*) obj;  
00252   bool busy= true;
00253   bool news= false;
00254   while (busy) {
00255     fd_set rfds;
00256     FD_ZERO (&rfds);
00257     int max_fd= con->io + 1;
00258     FD_SET (con->io, &rfds);
00259   
00260     struct timeval tv;
00261     tv.tv_sec  = 0;
00262     tv.tv_usec = 0;
00263     select (max_fd, &rfds, NULL, NULL, &tv);
00264 
00265     busy= false;
00266     if (con->alive && FD_ISSET (con->io, &rfds)) {
00267       //cout << "socket_callback OUT" << LF;
00268       con->feed (LINK_OUT);
00269       busy= news= true;
00270     }
00271   }
00272   if (!is_nil (con->feed_cmd) && news)
00273     con->feed_cmd->apply ();
00274 #endif
00275 }