source: git/MP/MP/MP_TcpTransp.c @ 4439165

fieker-DuValspielwiese
Last change on this file since 4439165 was d848372, checked in by Hans Schönemann <hannes@…>, 16 years ago
*hannes: sleep not needed, just use -O2, not -O3 (gcc 2.95) git-svn-id: file:///usr/local/Singular/svn/trunk@10888 2c84dea3-7e68-4137-9b89-c4e89433aadc
  • Property mode set to 100644
File size: 57.0 KB
Line 
1/***********************************************************************
2 *
3 *                    MP version 1.1.2:  Multi Protocol
4 *                    Kent State University, Kent, OH
5 *                 Authors:  S. Gray, N. Kajler, P. Wang
6 *         (C) 1993, 1994, 1995, 1996, 1997 All Rights Reserved
7 *
8 *                                 NOTICE
9 *
10 *  Permission to use, copy, modify, and distribute this software and
11 *  its documentation for non-commercial purposes and without fee is
12 *  hereby granted provided that the above copyright notice appear in all
13 *  copies and that both the copyright notice and this permission notice
14 *  appear in supporting documentation.
15 *
16 *  Neither Kent State University nor the Authors make any representations
17 *  about the suitability of this software for any purpose.  The MP Library
18 *  is distributed in the hope that it will be useful, but is provided  "as
19 *  is" and without any warranty of any kind and without even the implied
20 *  warranty of merchantability or fitness for a particular purpose.
21 *
22 *    IMPORTANT ADDITION: as of September 2006, MP is also licenced under GPL
23 *
24 *   IMPLEMENTATION FILE:  MP_TcpTransp.c
25 *
26 *      Public interface routines to the "tcp" transport device.
27 *      These include read, write, open, close, and init routines.
28 *      There are some support routines as well.
29 *
30 *  Change Log:
31 *       08/16/96   hennig     - tcp transport device ported to Windows95/NT
32 *       11/25/95   sgray      - added support to negotiate endian word order.
33 *       11/20/95   sgray      - added tcp_has_data() routine to determine
34 *                               if there is any data on the link to read and
35 *                               put it in the transp_op structure
36 *       11/15/95   obachman   - use vfork instead of fork
37 *                             - major change to open_tcp_launch_mode:
38 *                             1.) -MPapplication takes one string: application
39 *                                 and its arguments
40 *                             2.) MP appends -MPhost -MPport to arguments of
41 *                                 application
42 *                             3.) rsh is started with -n to disable
43 *                                 standard input
44 *                             4.) rsh has fixed number of args, namely:
45 *                                 -n host application
46 *       11/14/95   obachman   - introduced macro for remote shell command
47 *       11/10/95   obachman   - cleaned up things around the select call,
48 *                               so that it runs under Linux, and uses
49 *                               the timestruct to actually come back after
50 *                               an unsuccessful select
51 *                             - fixed nasty bug on line 348:
52 *                      tcp_rec->peerhost = (char *) malloc(sizeof(host) + 1);
53 *                      should have been
54 *                      tcp_rec->peerhost = (char *) malloc(strlen(host) + 1);
55 *                              - similar in tcp_inti_transport
56 *                              - one minor cast to (Char **) of dmy_args
57 *       September 11, 1995 SG - Reorganization of files.  Cleaned up
58 *                               header files and relocated source to
59 *       3/3/96    sgray - moved call to tcp_init_transport() inside
60 *                         tcp_open_transport() and removed the init
61 *                         routine from the tcp_ops struct.
62 *       5/16/96   sgray - Changed the tcp_read() routine to loop until it
63 *                         has read all the data requested.  This was done
64 *                         b/c the higher level routines in the buffering
65 *                         layer return failure if the number of bytes read
66 *                         is not the number of bytes requested.  We ran into
67 *                         the problem that occasionally the application was
68 *                         trying to read the data faster than the TCP layer
69 *                         could provide it, providing a "false failure".
70 *       3/11/97   sgray - Changed all memory management calls that allocate
71 *                         or deallocate strings to use the "Raw" calls.
72 *
73 ***************************************************************************/
74
75#ifndef lint
76static char vcid[] = "@(#) $Id: MP_TcpTransp.c,v 1.15 2008-07-18 09:04:04 Singular Exp $";
77#endif /* lint */
78
79#include "MP.h"
80#include <string.h>
81#include <stdlib.h>
82#include <signal.h>
83
84
85#ifdef __WIN32__
86
87#     include <process.h>
88
89#else /* not __WIN32__ */
90
91
92#include <unistd.h>
93#include <netdb.h>
94
95#endif /* not __WIN32__ */
96
97
98
99#define log_msg_len 128
100#define MAX_ARGS     24
101
102static char log_msg[log_msg_len];  /* for event logging */
103
104
105static MP_Status_t tcp_negotiate_word_order _ANSI_ARGS_((MP_Link_pt));
106static MP_Status_t tcp_negotiate_fp_format _ANSI_ARGS_((MP_Link_pt));
107static MP_Status_t tcp_negotiate_bigint_format _ANSI_ARGS_((MP_Link_pt));
108static MP_Status_t tcp_exchange_pids _ANSI_ARGS_((MP_Link_pt));
109static int get_tcp_mode _ANSI_ARGS_((int, char**));
110
111#ifdef __WIN32__
112static int splitargs _ANSI_ARGS_((char*));
113#endif
114
115
116MP_TranspOps_t tcp_ops = {
117    tcp_write,
118    tcp_read,
119    tcp_flush,
120    tcp_get_status,
121    tcp_open_connection,
122    tcp_close_connection,
123    tcp_kill_connection
124};
125
126
127/****  Private routines ******/
128
129/***********************************************************************
130 * FUNCTION:  get_tcp_mode
131 * INPUT:     argc - number of arguments in argv
132 *            argv - arguments as strings
133 * OUTPUT:    Success:  one of MP_CONNECT_MODE, MP_LISTEN_MODE, MP_LAUNCH_MODE.
134 *            Failure:  MP_NO_SUCH_TCP_MODE
135 * OPERATION: Get the argument to the "-MPmode" option and see what it is.
136 ***********************************************************************/
137#ifdef __STDC__
138static int get_tcp_mode(int argc, char **argv)
139#else
140static int get_tcp_mode(argc, argv)
141    int    argc;
142    char **argv;
143#endif
144{
145    char *cmode;
146
147#ifdef MP_DEBUG
148    fprintf(stderr, "get_tcp_mode: entering\n");
149    fflush(stderr);
150#endif
151
152    if ((cmode = IMP_GetCmdlineArg(argc, argv, "-MPmode")) != NULL)
153        if (!strcmp(cmode, "connect"))
154            return MP_CONNECT_MODE;
155        else if (!strcmp(cmode, "listen"))
156            return MP_LISTEN_MODE;
157        else if (!strcmp(cmode, "launch"))
158            return MP_LAUNCH_MODE;
159        else if (!strcmp(cmode, "fork"))
160          return MP_FORK_MODE;
161   
162
163#ifdef MP_DEBUG
164    fprintf(stderr, "get_tcp_mode: exiting\n");
165    fflush(stderr);
166#endif
167
168    return MP_NO_SUCH_TCP_MODE;
169}
170
171
172/***********************************************************************
173 * FUNCTION:  open_tcp_launch_mode
174 * INPUT:     link - pointer to link structure for this connection
175 *            argc - number of arguments in argv
176 *            argv - arguments as strings
177 * OUTPUT:    Success:  MP_Success
178 *                      socket connected to the specified host, port
179 *            Failure:  MP_Failure
180 * OPERATION: Launch an application remotely.  rsh is used for this,
181 *            which isn't portable beyond Unix.  The application name
182 *            must be supplied. First we open a port in listening mode,
183 *            then the application is launched with arguments asking it
184 *            to establish a connection to this host at the opened port.
185 *            Immediately after the launch, we do a blocking accept(),
186 *            waiting for the application to make the connection.
187 *            Unfortunately there may be problems, so maybe a non-blocking
188 *            accept would be better.
189 *
190 *            Windows95/NT: Launching applications is only supported on
191 *            the local machine. This is done using the spawnv function.
192 *            If the "-MPhost <host>" option is given on the command line
193 *            <host> is first checked to see if it is identical to the
194 *            local host.
195 ***********************************************************************/
196
197/* Returns a dupliucate of string `str' in which string `replace_this'
198   is replaced by string `replace_by_that' provided all strings are !=
199   NULL */
200#ifdef __STDC__
201static char* strdup_replace(const char* str,
202                            const char* replace_this,
203                            const char* replace_by_that)
204#else
205static char* strdup_replace(str, replace_this, replace_by_that)
206  const char* str;
207  const char* replace_this;
208  const char* replace_by_that;
209#endif
210{
211  char *sub, *ret;
212 
213  if (str == NULL) return NULL;
214 
215  if (replace_this != NULL && 
216      (sub = strstr(str, replace_this)) != NULL &&
217      replace_by_that != NULL)
218  {
219    ret = (char*) IMP_RawMemAllocFnc((strlen(str) 
220                              + strlen(replace_by_that) - strlen(replace_this)
221                              + 1)*sizeof(char));
222    strcpy(ret, str);
223    sprintf((void*) ret + ((void*) sub - (void*) str), 
224            "%s%s", replace_by_that, &sub[strlen(replace_this)]);
225  }
226  else
227  {
228    ret = IMP_RawMemAllocFnc((strlen(str) + 1)*sizeof(char));
229    strcpy(ret, str);
230  }
231  return ret;
232}
233
234#ifdef __STDC__
235static int UsesLongOpt(int argc, char** argv, const char* opt)
236#else
237static int UsesLongOpt(argc, argv, opt)
238  int argc; 
239  char** argv; 
240  const char* opt;
241#endif
242{
243  int i=0;
244  if (opt == NULL) return -1;
245 
246  while (i < argc && (argv[i][0] != '-' || strstr(argv[i], opt))) i++;
247 
248  if (i >= argc) return -1;
249  if (argv[i][1] == '-') return 1;
250  return 0;
251}
252
253#ifdef __STDC__
254MP_Status_t open_tcp_launch_mode(MP_Link_pt   link,
255                                 int          argc,
256                                 char       **argv)
257#else
258MP_Status_t open_tcp_launch_mode(link, argc, argv)
259    MP_Link_pt   link;
260    int          argc;
261    char       **argv;
262#endif
263{
264#ifdef __WIN32__
265    char       myhost[64], cport[10];
266    char     **dmy_args, **spawn_argv, *rhost, *p, *mpapp;
267    int        spawn_pid, i, applen, appopts;
268    MP_TCP_t  *tcp_rec;
269    HOSTENT   *myhostent, *rhostent;
270
271#ifdef MP_DEBUG
272    fprintf(stderr, "open_tcp_launch_mode: entering\n");
273    fflush(stderr);
274#endif /* MP_DEBUG */
275
276    TEST_LINK_NULL(link);
277    tcp_rec = (MP_TCP_t *)link->transp.private1;
278    tcp_rec->peerhost = NULL;
279
280    if (gethostname(myhost, 64) == SOCKET_ERROR)
281        return MP_SetError(link, MP_Failure);
282
283    /*
284     * Let's assume that there is no rsh application available in Windows95/NT.
285     * Therefore, all we can do is spawn a local process. So we must check if
286     * the -MPhost argument is either equal to "localhost", myhost or not
287     * specified at all. If not, we must bail out.
288     */
289    rhost = IMP_GetCmdlineArg(argc, argv, "-MPhost");
290    if (rhost != NULL && strcmp(rhost, "localhost")) {
291
292        /*
293         * OK. So the user absolutely had to specify a host name. Then we have to
294         * find out if myhost and the remote host are really the same machine.
295         *
296         * First, get host information for both host names.
297         */
298        myhostent = gethostbyname(myhost);
299        rhostent  = gethostbyname(rhost);
300
301        if (myhostent == NULL) {
302            MP_LogEvent(link, MP_ERROR_EVENT,
303                        "MP_OpenLink: cannot get local host information.");
304            return MP_SetError(link, MP_Failure);
305        }
306
307        if (rhostent == NULL) {
308            MP_LogEvent(link, MP_ERROR_EVENT,
309                        "MP_OpenLink: cannot get remote host information.");
310            return MP_SetError(link, MP_Failure);
311        }
312
313        /* Now compare both official host names and see if they are equal. */
314        if (strcmp(myhostent->h_name, rhostent->h_name)) {
315            MP_LogEvent(link, MP_ERROR_EVENT,
316                        "MP_OpenLink: launching remote processes is not"
317                        "supported (yet).");
318            return MP_SetError(link, MP_Failure);
319        }
320    }
321
322    /* Get the application to be launched. */
323    p = IMP_GetCmdlineArg(argc, argv, "-MPapplication");
324    if (p == NULL) {
325        MP_LogEvent(link, MP_ERROR_EVENT,
326                    "MP_OpenLink: bad or missing -MPapplication argument");
327        return MP_SetError(link, MP_Failure);
328    }
329
330    /*
331     * If we could execute an rsh application we could simply pass it the
332     * -MPapplication argument string and let the rsh do the parsing. However,
333     * as we need to use the spawnv function to launch the child process we must
334     * split the application's argument string into the argv's ourselves:
335     */
336
337    /* Make a copy of the -MPapplication argument string. */
338    applen = strlen(p) + 1;
339    mpapp = (char*)IMP_RawMemAllocFnc(applen);
340    if (mpapp == NULL) {
341        MP_LogEvent(link, MP_ERROR_EVENT,
342                    "MP_OpenLink: memory allocation error");
343        return MP_SetError(link, MP_MemAlloc);
344    }
345    strcpy(mpapp, p);
346
347    /* Split the argument string into several Null-terminated strings. */
348    appopts = splitargs(mpapp);
349
350    /* Allocate memory for the application's argv array. */
351    spawn_argv = (char**)IMP_MemAllocFnc((appopts + 9) * sizeof(char*));
352    if (spawn_argv == NULL) {
353        MP_LogEvent(link, MP_ERROR_EVENT,
354                    "MP_OpenLink: memory allocation error");
355        return MP_SetError(link, MP_MemAlloc);
356    }
357
358    /* Fill the argv array with pointers to the arguments. */
359    p = mpapp;
360    for (i = 0; i < appopts; i++) {
361        /* Take one argument and put it to the argv's. */
362        spawn_argv[i] = p;
363        /* Advance the pointer to the next argument. */
364        while (*p++);
365    }
366
367    /* User shouldn't have specified a -MPport option, but we'll use it if
368       it is there. */
369    if (IMP_GetCmdlineArg(argc, argv, "-MPport") == NULL) {
370        dmy_args = (char**)IMP_MemAllocFnc((argc + 2) * sizeof(char *));
371        if (dmy_args == NULL) {
372            MP_LogEvent(link, MP_ERROR_EVENT,
373                        "MP_OpenLink: memory allocation error");
374            return MP_SetError(link, MP_MemAlloc);
375        }
376
377        for (i = 0; i < argc; i++)
378            dmy_args[i] = argv[i];
379        dmy_args[i++] = "-MPport";
380        dmy_args[i++] = MP_INIT_PORT;
381
382        if (open_tcp_listen_mode(link, i, dmy_args) != MP_Success) {
383            MP_LogEvent(link, MP_ERROR_EVENT,
384                        "MP_OpenLink: can't open a listening socket");
385            return MP_SetError(link, MP_CantConnect);
386        }
387
388        IMP_MemFreeFnc(dmy_args, (argc + 2) * sizeof(char*));
389    }
390
391    else  /* User specified port number, so we use original argc/argv pair. */
392        if (open_tcp_listen_mode(link, argc, argv) != MP_Success) {
393            MP_LogEvent(link, MP_ERROR_EVENT,
394                        "MP_OpenLink: can't open a listening socket");
395            return MP_SetError(link, MP_CantConnect);
396        }
397
398    sprintf(cport, "%hd", tcp_rec->peerport);
399
400    /* Add the remaining MP options to the application's argument list. */
401    spawn_argv[appopts  ] = "-MPtransp";
402    spawn_argv[appopts+1] = "TCP";
403    spawn_argv[appopts+2] = "-MPmode";
404    spawn_argv[appopts+3] = "connect";
405    spawn_argv[appopts+4] = "-MPhost";
406    spawn_argv[appopts+5] = myhost;
407    spawn_argv[appopts+6] = "-MPport";
408    spawn_argv[appopts+7] = cport;
409    spawn_argv[appopts+8] = NULL;
410
411    /* Launch the application. */
412    spawn_pid = spawnv(P_NOWAIT, spawn_argv[0], spawn_argv);
413    if (spawn_pid == -1) {
414        MP_LogEvent(link, MP_ERROR_EVENT,
415                    "MP_OpenLink: can't launch application");
416        return MP_SetError(link, MP_Failure);
417    }
418
419    /* Free the application argument memory. */
420    IMP_MemFreeFnc(spawn_argv, (appopts + 9) * sizeof(char*));
421    IMP_RawMemFreeFnc(mpapp);
422
423    /* Wait for the child application to connect. */
424    socket_accept_blocking(link, &tcp_rec->sock);
425
426#ifdef MP_DEBUG
427    fprintf(stderr, "open_tcp_launch_mode: exiting\n");
428    fflush(stderr);
429#endif
430
431    RETURN_OK(link);
432
433#else /*  not __WIN32__ */
434
435    char *rsh_argv[5], myhost[64], cport[5], **dmy_args, *appstr, *sub;
436    int       rsh_pid = -1, i;
437    MP_TCP_t *tcp_rec;
438    MP_Boolean_t localhost = MP_FALSE;
439
440#if defined(HAVE_GETHOSTBYNAME) && defined(RSH_CAN_LOCALHOST)
441    if (gethostbyname("localhost") != NULL) localhost = MP_TRUE;
442#endif
443   
444   
445#ifdef MP_DEBUG
446    fprintf(stderr, "open_tcp_launch_mode: entering\n");
447    fflush(stderr);
448#endif
449
450    TEST_LINK_NULL(link);
451    tcp_rec = (MP_TCP_t *)link->transp.private1;
452    tcp_rec->peerhost = NULL;
453
454    if (gethostname(myhost, 64) == -1)
455        return MP_SetError(link, MP_Failure);
456   
457    /* NOTE: Ultrix doesn't like this order. It expects rsh host [-n]
458       command. */
459    /* I tried on every platform I know using all the rsh commands
460       available, and
461
462       $rsh hostname -n command
463
464       worked everywhere */
465
466    rsh_argv[0] = IMP_GetCmdlineArg(argc, argv, "-MPrsh");
467    if (rsh_argv[0] == NULL)
468      rsh_argv[0] = MP_RSH_COMMAND;
469    tcp_rec->rsh = IMP_StrDup(rsh_argv[0]);
470
471    rsh_argv[1] = IMP_GetCmdlineArg(argc, argv, "-MPhost");
472    /* Let's not be too strict, and allow an empty -MPhost argument */
473    if (localhost)
474    {
475      if ((rsh_argv[1] != NULL && (strcmp(rsh_argv[1], myhost) == 0)) ||
476          rsh_argv[1] == NULL) 
477        rsh_argv[1] = "localhost";
478    }
479    else
480    {
481      rsh_argv[1] = myhost;
482    }
483    rsh_argv[2] = "-n";
484    rsh_argv[3] = IMP_GetCmdlineArg(argc, argv, "-MPapplication");
485    if (rsh_argv[3] == NULL) {
486        MP_LogEvent(link, MP_ERROR_EVENT,
487                    "MP_OpenLink: bad or missing -MPapplication argument");
488        return MP_SetError(link, MP_Failure);
489    }
490
491    /*
492     * User shouldn't have specified a -MPport option, but we'll use it if
493     * it is there.
494     */
495
496    if (IMP_GetCmdlineArg(argc, argv, "-MPport") == NULL) {
497        dmy_args = (char**)IMP_MemAllocFnc((argc + 2) * sizeof(char*));
498        if (dmy_args == NULL) {
499            MP_LogEvent(link, MP_ERROR_EVENT,
500                        "MP_OpenLink: memory allocation error");
501            return MP_SetError(link, MP_MemAlloc);
502        }
503
504        for (i = 0; i < argc; i++)
505            dmy_args[i] = argv[i];
506        dmy_args[i++] = "-MPport";
507        dmy_args[i++] = MP_INIT_PORT;
508
509        if (open_tcp_listen_mode(link, i, dmy_args) != MP_Success) {
510            MP_LogEvent(link, MP_ERROR_EVENT,
511                        "MP_OpenLink: can't open a listening socket");
512            return MP_SetError(link, MP_CantConnect);
513        }
514
515        IMP_MemFreeFnc(dmy_args, (argc + 2) * sizeof(char *));
516    }
517
518    else
519        /* User specified port number, so we use original argc/argv pair. */
520        if (open_tcp_listen_mode(link, argc, argv) != MP_Success) {
521            MP_LogEvent(link, MP_ERROR_EVENT,
522                        "MP_OpenLink: can't open a listening socket");
523            return MP_SetError(link, MP_CantConnect);
524        }
525
526    sprintf(cport, "%hd", tcp_rec->peerport);
527    if (localhost  &&
528        (strcmp(rsh_argv[1], myhost) == 0 ||
529         strcmp(rsh_argv[1], "localhost") == 0))
530      sprintf(myhost, "localhost");
531   
532    if (strstr(rsh_argv[3], "$MPport") != NULL ||
533        strstr(rsh_argv[3], "$MPhost") != NULL)
534    {
535      sub = strdup_replace(rsh_argv[3], "$MPport", cport);
536      appstr = strdup_replace(sub, "$MPhost", myhost);
537      IMP_RawMemFreeFnc(sub);
538    }
539    else
540    {
541      appstr = (char*)IMP_RawMemAllocFnc(strlen(rsh_argv[3]) + 110);
542      strcpy(appstr, rsh_argv[3]);
543     
544      if (UsesLongOpt(argc, argv, "MPapplication") > 0)
545      {
546        strcat(appstr, " --MPtransp TCP --MPmode connect --MPhost ");
547        strcat(appstr, myhost);
548        strcat(appstr, " --MPport ");
549        strcat(appstr, cport);
550      }
551      else
552      {
553        strcat(appstr, " -MPtransp TCP -MPmode connect -MPhost ");
554        strcat(appstr, myhost);
555        strcat(appstr, " -MPport ");
556        strcat(appstr, cport);
557      }
558     
559    }
560
561    rsh_argv[3] = appstr;
562    rsh_argv[4] = NULL;
563    if (tcp_rec->peerhost == NULL) 
564      tcp_rec->peerhost = IMP_StrDup(rsh_argv[1]);
565
566    if ((rsh_pid = vfork()) == -1) {
567        MP_LogEvent(link, MP_ERROR_EVENT,
568                    "MP_OpenLink: can't fork to launch application");
569        return MP_SetError(link, MP_Failure);
570    }
571
572    if (!rsh_pid) {
573        execvp(rsh_argv[0], rsh_argv);
574        fputs("open_tcp_launch_mode: execvp failed - bailing out\n", stderr);
575        fflush(stderr);
576        _exit(1);
577    }
578
579    socket_accept_blocking(link, &tcp_rec->sock);
580
581    IMP_RawMemFreeFnc(appstr);
582
583#ifdef MP_DEBUG
584    fprintf(stderr, "open_tcp_launch_mode: exiting\n");
585    fflush(stderr);
586#endif
587
588    RETURN_OK(link);
589#endif /* not __WIN32__ */
590}
591
592
593
594/***********************************************************************
595 * FUNCTION:  open_tcp_listen_mode
596 * INPUT:     link - pointer to link structure for this connection
597 *            argc - number of arguments in argv
598 *            argv - arguments as strings
599 * OUTPUT:    Success:  MP_Success
600 *                      socket connected to the specified host, port
601 *            Failure:  MP_Failure
602 * OPERATION: Open a socket in the AF_INET domain, verify that the port
603 *            is in the legal range (don't annoy anyone), bind the socket,
604 *            finally, do a listen.  We update the link structure with
605 *            the socket and port information.
606 ***********************************************************************/
607#ifdef __STDC__
608MP_Status_t open_tcp_listen_mode(MP_Link_pt   link,
609                                 int          argc,
610                                 char       **argv)
611#else
612MP_Status_t open_tcp_listen_mode(link, argc, argv)
613    MP_Link_pt   link;
614    int          argc;
615    char       **argv;
616#endif
617{
618    SOCKADDR_IN  isin;
619    SOCKET       sock;
620    int          attempts = 0;
621    char        *cport;
622    short        port;
623    MP_TCP_t    *tcp_rec = (MP_TCP_t *)link->transp.private1;
624
625#ifdef MP_DEBUG
626    fprintf(stderr, "open_tcp_listen_mode: entering\n");
627    fflush(stderr);
628#endif /* MP_DEBUG */
629
630    tcp_rec->peerhost = NULL;
631
632    if ((cport = IMP_GetCmdlineArg(argc, argv, "-MPport")) == NULL)
633        return MP_SetError(link, MP_Failure);
634
635    port = atoi(cport);
636    if (port < IPPORT_RESERVED)
637        port = IPPORT_RESERVED;
638
639    sock = socket(AF_INET, SOCK_STREAM, 0);
640    if (sock == INVALID_SOCKET) {
641        sprintf(log_msg,
642                "open_tcp_listen_mode: "
643                "Cannot open stream socket (socket error %d)",
644                LASTERROR);
645        MP_LogEvent(link, MP_ERROR_EVENT, log_msg);
646        return MP_SetError(link, MP_Failure);
647    }
648
649    /* Initialize socket's address structure */
650    isin.sin_family      = AF_INET;
651    isin.sin_addr.s_addr = htonl(INADDR_ANY);
652    isin.sin_port        = htons(port);
653
654    while (bind(sock, (SOCKADDR*)&isin, sizeof(isin)) == SOCKET_ERROR) {
655        if (LASTERROR == ERRORCODE(EADDRINUSE)) {
656            port++;
657
658            /* since port is an unsigned short, it may wrap around */
659            if (port < IPPORT_RESERVED)
660                port = IPPORT_RESERVED;
661
662            isin.sin_port = htons(port);
663            attempts = 0;
664        }
665
666        if (attempts++ > MP_MAX_BIND_ATTEMPTS) {
667            MP_LogEvent(link, MP_ERROR_EVENT,
668                        "open_tcp_listen_mode: Cannot bind");
669            return MP_SetError(link, MP_Failure);
670        }
671    }
672
673    /* Now listen for the anticipated connect request */
674    listen(sock, 1);
675    tcp_rec->bindsock = sock;
676    tcp_rec->sock     = sock;
677    tcp_rec->peerport = port;
678
679#ifndef NO_LOGGING
680    if (link->logmask & MP_LOG_INIT_EVENTS) {
681        sprintf(log_msg,
682                "open_tcp_listen_mode: socket -> %d, port -> %d",
683                sock, port);
684        MP_LogEvent(link, MP_INIT_EVENT, log_msg);
685    }
686#endif /* NO_LOGGING */
687
688#ifdef MP_DEBUG
689    fprintf(stderr, "\t  tcp_rec->sock = %d\n", tcp_rec->sock);
690    fprintf(stderr, "\t  tcp_rec->port = %d\n", tcp_rec->peerport);
691    fprintf(stderr,
692            "open_tcp_listen_mode: exiting - socket = %d, port = %d\n",
693            sock, port);
694    fflush(stderr);
695#endif
696
697    RETURN_OK(link);
698}
699
700
701
702/***********************************************************************
703 * FUNCTION:  open_tcp_fork_mode
704 * INPUT:     link - pointer to link structure for this connection
705 * OUTPUT:    Success:  MP_Success
706 *                      socket connected to the a forked child
707 *            Failure:  MP_Failure - couldn't make the connection.
708 * OPERATION: forks a child, establishes a connection to it and uses
709 *            the return of the fork to set parent/child attribute
710 ***********************************************************************/
711#ifdef __STDC__
712MP_Status_t open_tcp_fork_mode(MP_Link_pt link, int argc, char** argv)
713#else
714MP_Status_t open_tcp_fork_mode(link, argc, argv)
715    MP_Link_pt   link;
716    int          argc;
717    char**       argv;
718#endif
719{
720  int pid;
721  MP_TCP_t *tcp_rec = (MP_TCP_t *) link->transp.private1;
722#ifndef NO_LOGGING
723  char log_msg[200];
724  char *parent_logfilename = link->env->logfilename;
725  FILE *parent_logfd = link->env->logfd;
726#endif
727 
728  /* prepare the socket */
729  if (open_tcp_listen_mode(link, argc, argv) != MP_Success)
730  {
731    MP_LogEvent(link, MP_ERROR_EVENT,
732                "MP_OpenLink: can't open a listening socket");
733    return MP_SetError(link, MP_CantConnect);
734  }
735
736#ifndef NO_LOGGING
737  /* Open the log file before the fork, so that the processes do not
738     get out of sync */
739  sprintf(log_msg,
740          "open_tcp_fork: Preparing to create fork link, parent id: %d",
741          getpid());
742  if (open_logfile(link->env) != MP_Success) return MP_Failure;
743  MP_LogEvent(link, MP_INIT_EVENT,
744              "open_tcp_fork: MP Log file for a forked (child) link");
745  sprintf(log_msg, "open_tcp_fork: Parent link: L%d, logfile: %s, pid: %d",
746          link->link_id, parent_logfilename, getpid());
747  MP_LogEvent(link, MP_INIT_EVENT, log_msg);
748#endif
749 
750  /* Do the fork */
751  if ((pid = fork()) == -1)
752  {
753#ifndef NO_LOGGING
754    link->env->logfilename = parent_logfilename;
755    link->env->logfd = parent_logfd;
756#endif   
757    MP_LogEvent(link, MP_ERROR_EVENT, "MP_OpenLink: can't fork");
758    return MP_SetError(link, MP_Failure);
759  }
760
761  if (!pid)
762  {
763    /* Child's business */
764    MP_Status_t stat;
765    char cport[12];
766    char *argv[] = {"MPtransp","TCP","-MPmode","connect","-MPhost",
767                    "localhost", "-MPport", ""};
768    sprintf(cport,"%d",tcp_rec->peerport);
769    argv[7] = cport;
770#ifndef NO_LOGGING
771    sprintf(log_msg, "open_tcp_fork:  child starti (pid=%d)",getpid());
772#endif
773    /* establish connection */
774    stat = open_tcp_connect_mode(link, 8, argv);
775
776#ifndef NO_LOGGING
777    IMP_RawMemFreeFnc(parent_logfilename);
778    if (stat == MP_Success)
779      sprintf(log_msg, "open_tcp_fork: opened forked link, child id: %d",
780              getpid());
781    else
782      sprintf(log_msg,
783              "open_tcp_fork: open of forked link failed, child is exited");
784    MP_LogEvent(link, MP_ERROR_EVENT, log_msg);
785#endif
786    if (stat != MP_Success)
787      _exit(1);
788    else
789    {
790      tcp_rec->status = MP_LinkIsChild;
791      return MP_Success;
792    }
793  }
794  else
795  {
796    /* parent's business */
797#ifndef NO_LOGGING
798    /* reset logging stuff */
799    link->env->logfilename = parent_logfilename;
800    link->env->logfd = parent_logfd;
801#endif
802
803    /* establish connection */
804    socket_accept_blocking(link, &tcp_rec->sock);
805    tcp_rec->status = MP_LinkIsParent;
806
807#ifndef NO_LOGGING
808    sprintf(log_msg,"open_tcp_fork: opened fork link, parent id: %d",
809            getpid()); 
810    MP_LogEvent(link, MP_INIT_EVENT, log_msg);
811#endif
812
813    RETURN_OK(link);
814  }
815}
816 
817/***********************************************************************
818 * FUNCTION:  open_tcp_connect_mode
819 * INPUT:     link - pointer to link structure for this connection
820 *            argc - number of arguments in argv
821 *            argv - arguments as strings
822 * OUTPUT:    Success:  MP_Success
823 *                      socket connected to the specified host, port
824 *            Failure:  MP_Failure - couldn't make the connection.
825 *                      Maybe the peer isn't listening.
826 * OPERATION: Establish a TCP connection to the peer's port. Update the
827 *            private tcp structure accordingly.  We hope that there
828 *            is a peer actually listening on the given port.  We make
829 *            1000 attempts before giving up.
830 ***********************************************************************/
831#ifdef __STDC__
832MP_Status_t open_tcp_connect_mode(MP_Link_pt   link,
833                                  int          argc,
834                                  char       **argv)
835#else
836MP_Status_t open_tcp_connect_mode(link, argc, argv)
837    MP_Link_pt   link;
838    int          argc;
839    char       **argv;
840#endif
841{
842    SOCKET       sock;
843    int          attempts = 0;
844    HOSTENT     *hp;
845    SOCKADDR_IN  isin;
846    char        *host, *cport, *ptr;
847    short        port;
848    MP_TCP_t    *tcp_rec = (MP_TCP_t *)link->transp.private1;
849
850#ifdef MP_DEBUG
851    fprintf(stderr, "open_tcp_connect_mode: entering\n");
852    fflush(stderr);
853#endif /* MP_DEBUG */
854
855    tcp_rec->peerhost = NULL;
856
857    /* get the port number */
858    if (
859        (cport = IMP_GetCmdlineArg(argc, argv, "-MPport")) == NULL)
860        return MP_SetError(link, MP_Failure);
861
862    port = (short) strtol(cport, &ptr, 10);
863    if ((host = IMP_GetCmdlineArg(argc, argv, "-MPhost")) == NULL)
864      host = link->env->thishost;
865   
866    if (ptr == cport || errno == ERANGE)
867      return MP_SetError(link, MP_Failure);
868
869    if ((hp = gethostbyname(host)) == NULL) {
870        MP_LogEvent(link, MP_ERROR_EVENT,
871                    "open_tcp_connect_mode - cannot get hostname");
872        return MP_SetError(link, MP_Failure);
873    }
874
875    sock = socket(AF_INET, SOCK_STREAM, 0);
876    if (sock == INVALID_SOCKET) {
877        sprintf(log_msg,
878                "open_tcp_connect_mode - Cannot open socket (socket error %d)",
879                LASTERROR);
880        MP_LogEvent(link, MP_ERROR_EVENT, log_msg);
881        return MP_SetError(link, MP_Failure);
882    }
883
884    /* Initialize the socket address to the server's address. */
885    memset((char *)&isin, 0, sizeof(isin));
886    isin.sin_family = AF_INET;
887    memcpy(&isin.sin_addr.s_addr, hp->h_addr, hp->h_length);
888    isin.sin_port = htons(port);
889
890    /* Connect to the server. */
891    while (connect(sock, (SOCKADDR*)&isin, sizeof(isin)) == SOCKET_ERROR) {
892        CLOSE_SOCKET(sock);
893        if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
894            sprintf(log_msg,
895                    "open_tcp_connect_mode "
896                    "- Cannot open socket (socket error %d)", LASTERROR);
897            MP_LogEvent(link, MP_ERROR_EVENT, log_msg);
898            return MP_SetError(link, MP_Failure);
899        }
900
901        if (attempts > MP_MAX_BIND_ATTEMPTS) {
902            MP_LogEvent(link, MP_ERROR_EVENT,
903                        "open_tcp_connect_mode - Cannot connect");
904            return MP_SetError(link, MP_Failure);
905        }
906
907        attempts++;
908    }
909
910    if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char*)&sock, sizeof(sock))
911        == SOCKET_ERROR) {
912        sprintf(log_msg,
913                "open_tcp_connect_mode "
914                "- cannot set socket option (socket error %d)", LASTERROR);
915        MP_LogEvent(link, MP_ERROR_EVENT, log_msg);
916        return MP_SetError(link, MP_Failure);
917    }
918
919    tcp_rec->bindsock = INVALID_SOCKET;
920    tcp_rec->sock     = sock;
921    tcp_rec->peerport = port;
922
923    tcp_rec->peerhost = (char*)IMP_RawMemAllocFnc(strlen(host) +1);
924    TEST_MEM_ALLOC_ERROR(link, tcp_rec->peerhost);
925    strcpy(tcp_rec->peerhost, host);
926
927    tcp_rec->myhost = (char*)IMP_RawMemAllocFnc(MP_HOST_NAME_LEN);
928    TEST_MEM_ALLOC_ERROR(link, tcp_rec->myhost);
929    if (gethostname(tcp_rec->myhost, MP_HOST_NAME_LEN) == SOCKET_ERROR) {
930        IMP_RawMemFreeFnc(tcp_rec->peerhost);
931        IMP_RawMemFreeFnc(tcp_rec->myhost);
932        tcp_rec->myhost = NULL;
933    }
934
935#ifdef MP_DEBUG
936    fprintf(stderr, "open_tcp_connect_mode: exiting - socket is %d\n", sock);
937    fflush(stderr);
938#endif
939
940    RETURN_OK(link);
941}
942
943
944
945/***********************************************************************
946 * FUNCTION:  socket_accept_non_blocking
947 * INPUT:     sock   - the socket to do the accept on
948 * OUTPUT:    Success: the socket to be used for io
949 *                     Failure: MP_Failure
950 *                     sock contains the socket number of the accepted
951 *                     socket.
952 * PURPOSE:   Perform a non-blocking accept.  If we timeout before getting
953 *            a connection request, return MP_Failure.
954 ***********************************************************************/
955#ifdef __STDC__
956MP_Status_t socket_accept_non_blocking(MP_Link_pt link, SOCKET *sock)
957#else
958MP_Status_t socket_accept_non_blocking(link, sock)
959    MP_Link_pt  link;
960    SOCKET     *sock;
961#endif
962{
963    SOCKADDR addr;
964    SOCKET   nsock = 0;
965    int      attempts, addrlen = sizeof(addr);
966    fd_set   read_template;
967    TIMEVAL  timeout;
968
969#ifdef MP_DEBUG
970    fprintf(stderr,
971            "socket_accept_non_blocking: entering - socket = %d\n", *sock);
972    fflush(stderr);
973#endif
974
975    timeout.tv_sec  = MP_ACCEPT_WAIT_SEC;
976    timeout.tv_usec = MP_ACCEPT_WAIT_USEC;
977
978    for (attempts = 0; attempts < 2; attempts++) {
979        FD_ZERO(&read_template);
980        FD_SET(*sock, &read_template);
981
982        if (SELECT(FD_SETSIZE, &read_template, NULL, NULL, &timeout)
983            != SOCKET_ERROR) {
984
985            if ((nsock = accept(*sock, &addr, &addrlen)) != INVALID_SOCKET)
986                break;
987
988            sprintf(log_msg,
989                    "socket_accept_non_blocking: failed (socket error %d)",
990                    LASTERROR);
991            MP_LogEvent(link, MP_ERROR_EVENT, log_msg);
992            return MP_Failure;
993        }
994    }
995
996    *sock = nsock;
997
998#ifdef MP_DEBUG
999    fprintf(stderr,
1000            "socket_accept_non_blocking: returning - new socket =  %d\n",
1001            *sock);
1002    fflush(stderr);
1003#endif
1004
1005    return MP_Success;
1006}
1007
1008
1009
1010/***********************************************************************
1011 * FUNCTION:  socket_accept_blocking
1012 * INPUT:     link - link with which sock will be associated, need this
1013 *                   really only for the error handling.
1014 *            sock - the socket to do the accept on
1015 * OUTPUT:    int - Success: the socket to be used for io
1016 *                  Failure: MP_Failure
1017 * PURPOSE:   Perform a blocking accept.  We won't return until we get an
1018 *            connect request or are interrupted.
1019 ***********************************************************************/
1020#ifdef __STDC__
1021MP_Status_t socket_accept_blocking(MP_Link_pt link, SOCKET *sock)
1022#else
1023MP_Status_t socket_accept_blocking (link, sock)
1024    MP_Link_pt  link;
1025    SOCKET     *sock;
1026#endif
1027{
1028    SOCKADDR addr;
1029    SOCKET   nsock = 0;
1030    int      attempts, addrlen = sizeof(addr);
1031    fd_set   read_template;
1032
1033#ifdef MP_DEBUG
1034    fprintf(stderr, "socket_accept_blocking: entering - sock = %d\n", *sock);
1035    fflush(stderr);
1036#endif
1037
1038    for (attempts = 0; attempts < 2; attempts++) {
1039        FD_ZERO(&read_template);
1040        FD_SET(*sock, &read_template);
1041
1042        if (SELECT(FD_SETSIZE, &read_template, NULL, NULL, NULL)
1043            != SOCKET_ERROR) {
1044            if ((nsock = accept(*sock, &addr, &addrlen)) != INVALID_SOCKET)
1045                break;
1046
1047            sprintf(log_msg,
1048                    "socket_accept_blocking: failed (socket error %d)",
1049                    LASTERROR);
1050            MP_LogEvent(link, MP_ERROR_EVENT, log_msg);
1051            return MP_Failure;
1052        }
1053    }
1054
1055    *sock = nsock;
1056
1057#ifdef MP_DEBUG
1058    fprintf(stderr, "socket_accept_blocking: returning - new socket =  %d\n", *sock);
1059    fflush(stderr);
1060#endif
1061
1062    return MP_Success;
1063}
1064
1065
1066
1067
1068
1069/****  TCP "public" routines ******/
1070
1071#ifdef __STDC__
1072MP_Boolean_t tcp_get_status(MP_Link_pt link, MP_LinkStatus_t status_to_check)
1073#else
1074MP_Boolean_t tcp_get_status(link, status_to_check)
1075    MP_Link_pt       link;
1076    MP_LinkStatus_t  status_to_check;
1077#endif
1078{
1079    SOCKET  sock = ((MP_TCP_t *)link->transp.private1)->sock;
1080    fd_set  mask, fdmask;
1081    TIMEVAL wt;
1082
1083#ifdef MP_DEBUG
1084    printf("tcp_get_status: entering for link %d\n", link->link_id);
1085#endif
1086  if (status_to_check == MP_LinkIsParent)
1087  {
1088    if (((MP_TCP_t *)link->transp.private1)->status == MP_LinkIsParent)
1089      return MP_TRUE;
1090    else
1091      return MP_FALSE;
1092  }
1093  else if (status_to_check == MP_LinkIsChild)
1094  {
1095    if (((MP_TCP_t *)link->transp.private1)->status == MP_LinkIsChild)
1096      return MP_TRUE;
1097    else
1098      return MP_FALSE;
1099  }
1100 
1101
1102    FD_ZERO(&mask);
1103    FD_SET(sock, &mask);
1104
1105    fdmask = mask;
1106
1107    /* Don't block. Return socket status immediately. */
1108    wt.tv_sec  = 0;
1109    wt.tv_usec = 0;
1110
1111    switch (status_to_check) {
1112    case MP_LinkReadyReading:
1113        switch (SELECT(FD_SETSIZE, &fdmask, NULL, NULL, &wt)) {
1114        case 0:
1115            return MP_FALSE;
1116        case SOCKET_ERROR:
1117            return MP_FALSE;
1118        }
1119        if (FD_ISSET(sock, &fdmask))
1120            return MP_TRUE;
1121
1122        return MP_FALSE;
1123
1124    case MP_LinkReadyWriting:
1125        switch (SELECT(FD_SETSIZE, NULL, &fdmask, NULL, &wt)) {
1126        case 0:
1127            return MP_FALSE;
1128        case SOCKET_ERROR:
1129            return MP_FALSE;
1130        }
1131        if (FD_ISSET(sock, &fdmask))
1132            return MP_TRUE;
1133
1134        return MP_FALSE;
1135
1136    default:
1137        sprintf(log_msg,
1138                "tcp_get_status: illegal option %d", status_to_check);
1139        MP_LogEvent(link, MP_ERROR_EVENT, log_msg);
1140    }
1141
1142#ifdef MP_DEBUG
1143    printf("tcp_get_status: exiting\n");
1144#endif
1145
1146    return MP_FALSE;
1147}
1148
1149
1150
1151#ifdef __STDC__
1152long tcp_read(MP_Link_pt link, char * buf, long len)
1153#else
1154long tcp_read(link, buf, len)
1155    MP_Link_pt   link;
1156    char *      buf;
1157    long         len;
1158#endif
1159{
1160    SOCKET   sock = ((MP_TCP_t *)link->transp.private1)->sock;
1161    fd_set   mask, readfs;
1162    long     total = 0, n;
1163
1164#ifdef MP_DEBUG
1165    fprintf(stderr, "tcp_read: entering - len = %ld\n", len);
1166    fflush(stderr);
1167#endif
1168
1169    while (len > 0) {
1170        FD_ZERO(&mask);
1171        FD_SET(sock, &mask);
1172
1173        while (MP_TRUE) {
1174            readfs = mask;
1175
1176            /* Do blocking read. */
1177            switch (SELECT(FD_SETSIZE, &readfs, NULL, NULL, NULL)) {
1178            case 0:
1179                MP_LogEvent(link, MP_ERROR_EVENT, "tcp_read: timed out");
1180                return -1;
1181
1182            case SOCKET_ERROR:
1183                if (LASTERROR == ERRORCODE(EINTR))
1184                    continue;
1185                return -1;
1186            }
1187
1188            if (FD_ISSET(sock, &readfs))
1189                break;
1190        }
1191
1192        if ((n = READ_SOCKET(sock, buf, len)) == SOCKET_ERROR) {
1193            sprintf(log_msg, "tcp_read: can't do read (socket error %d)",
1194                    LASTERROR);
1195            MP_LogEvent(link, MP_ERROR_EVENT, log_msg);
1196            return MP_SetError(link, MP_CantReadLink);
1197        }
1198
1199        total += n;
1200        len   -= n;
1201        buf   += n;
1202    }
1203
1204#ifndef NO_LOGGING
1205    if (link->logmask & MP_LOG_READ_EVENTS) {
1206        sprintf(log_msg, "tcp_read: read %ld bytes", total);
1207        MP_LogEvent(link, MP_READ_EVENT, log_msg);
1208    }
1209#endif /* NO_LOGGING */
1210
1211#ifdef MP_DEBUG
1212    fprintf(stderr, "tcp_read: exiting - read %ld bytes \n", total);
1213    fflush(stderr);
1214#endif /* MP_DEBUG */
1215
1216    return total;
1217}
1218
1219
1220MP_Status_t
1221#ifdef __STDC__
1222tcp_flush(MP_Link_pt link)
1223#else
1224tcp_flush(link)
1225    MP_Link_pt  link;
1226#endif
1227{
1228
1229#ifdef MP_DEBUG
1230    fprintf(stderr, "tcp_flush: entering\n");
1231    fflush(stderr);
1232#endif
1233#ifdef MP_DEBUG
1234    fprintf(stderr, "tcp_flush: exiting  \n");
1235    fflush(stderr);
1236#endif
1237  return MP_ClearError(link);
1238}
1239
1240
1241#ifdef __STDC__
1242long tcp_write(MP_Link_pt link,
1243               char *    buf,
1244               long       len)
1245#else
1246long tcp_write(link, buf, len)
1247    MP_Link_pt  link;
1248    char *     buf;
1249    long        len;
1250#endif
1251{
1252    long   i, cnt;
1253    SOCKET sock = ((MP_TCP_t *)link->transp.private1)->sock;
1254    fd_set mask, writefs;
1255
1256#ifdef MP_DEBUG
1257    fprintf(stderr, "tcp_write: entering - len = %ld\n", len);
1258    fflush(stderr);
1259#endif
1260
1261    FD_ZERO(&mask);
1262    FD_SET(sock, &mask);
1263
1264    while (MP_TRUE) {
1265        writefs = mask;
1266        switch (SELECT(FD_SETSIZE, NULL, &writefs, NULL, NULL)) {
1267        case 0:
1268            MP_LogEvent(link, MP_ERROR_EVENT, "tcp_write: timed out");
1269            return -1;
1270
1271        case SOCKET_ERROR:
1272            if (LASTERROR == ERRORCODE(EINTR))
1273                continue;
1274            MP_LogEvent(link, MP_ERROR_EVENT,
1275                        "tcp_write: errno isn't EINTR - this is bad");
1276            return -1;
1277        }
1278
1279        if (FD_ISSET(sock, &writefs))
1280            break;
1281    }
1282
1283
1284    for (cnt = len; cnt > 0; cnt -= i, buf += i) {
1285        if ((i = WRITE_SOCKET(sock, buf, cnt)) == SOCKET_ERROR) {
1286            sprintf(log_msg, "tcp_write: can't do write (socket error %d)",
1287                    LASTERROR);
1288            MP_LogEvent(link, MP_ERROR_EVENT, log_msg);
1289            return MP_SetError(link, MP_CantWriteLink);
1290        }
1291
1292#ifndef NO_LOGGING
1293        if (link->logmask & MP_LOG_WRITE_EVENTS) {
1294            sprintf(log_msg, "tcp_write: wrote %ld bytes", i);
1295            MP_LogEvent(link, MP_WRITE_EVENT, log_msg);
1296        }
1297#endif /* NO_LOGGING */
1298    }
1299
1300#ifdef MP_DEBUG
1301    fprintf(stderr, "tcp_write: exiting - wrote %ld bytes \n", i);
1302    fflush(stderr);
1303#endif
1304
1305    return len;
1306}
1307
1308
1309
1310/***********************************************************************
1311 * FUNCTION:  tcp_init_transport
1312 * INPUT:     link - pointer to the link structure for this connection
1313 * OUTPUT:    Success:  MP_Success and link structure initialized for the
1314 *                      tcp connection.
1315 *            Failure:  MP_Failure
1316 * OPERATION: If there are no problems, allocate a tcp structure and
1317 *            attach it to the private pointer inside link.
1318 ***********************************************************************/
1319#ifdef __STDC__
1320MP_Status_t tcp_init_transport(MP_Link_pt link)
1321#else
1322MP_Status_t tcp_init_transport(link)
1323    MP_Link_pt link;
1324#endif
1325{
1326    MP_TCP_t *tcp_rec;
1327
1328#ifdef MP_DEBUG
1329    fprintf(stderr, "tcp_init_transport: entering\n"); fflush(stderr);
1330#endif
1331
1332    TEST_LINK_NULL(link);
1333    link->transp.transp_dev =  MP_TcpTransportDev;
1334    link->transp.transp_ops = &tcp_ops;
1335
1336    tcp_rec = (MP_TCP_t*)IMP_MemAllocFnc(sizeof(MP_TCP_t));
1337    TEST_MEM_ALLOC_ERROR(link, tcp_rec);
1338
1339    /* Initialize TCP record. */
1340    tcp_rec->bindsock = INVALID_SOCKET;
1341    tcp_rec->sock     = INVALID_SOCKET;
1342    tcp_rec->peerport = 0;
1343    tcp_rec->myhost   = NULL;
1344    tcp_rec->peerhost = NULL;
1345    tcp_rec->status   = MP_UnknownStatus;
1346    tcp_rec->rsh      = NULL;
1347
1348    link->transp.private1 = (char *)tcp_rec;
1349
1350#ifdef MP_DEBUG
1351    fprintf(stderr, "tcp_init_transport: exiting\n");
1352    fflush(stderr);
1353#endif
1354
1355    RETURN_OK(link);
1356}
1357
1358/***********************************************************************
1359 * FUNCTION:  tcp_close_connection
1360 * INPUT:     link  - pointer to the link structure for this connection
1361 * OUTPUT:    Success:  MP_Success
1362 *                      Release the tcp structure pointed to by private1.
1363 *            Failure:  MP_Failure
1364 * OPERATION:
1365 ***********************************************************************/
1366#ifdef __STDC__
1367MP_Status_t tcp_close_connection(MP_Link_pt link)
1368#else
1369MP_Status_t tcp_close_connection(link)
1370    MP_Link_pt link;
1371#endif
1372{
1373    MP_TCP_t *tcp_rec;
1374
1375#ifdef MP_DEBUG
1376    fprintf(stderr, "tcp_close_connection: entering\n");
1377    fflush(stderr);
1378#endif /* MP_DEBUG */
1379
1380    TEST_LINK_NULL(link);
1381    if (link->transp.private1 == NULL)
1382        return MP_SetError(link, MP_NullTransport);
1383    tcp_rec = (MP_TCP_t*)link->transp.private1;
1384
1385    CLOSE_SOCKET(tcp_rec->sock);
1386    if (tcp_rec->bindsock != INVALID_SOCKET)
1387        CLOSE_SOCKET(tcp_rec->bindsock);
1388
1389    if (tcp_rec->myhost != NULL)
1390        IMP_RawMemFreeFnc(tcp_rec->myhost);
1391    if (tcp_rec->peerhost != NULL)
1392        IMP_RawMemFreeFnc(tcp_rec->peerhost);
1393    if (tcp_rec->rsh != NULL)
1394        IMP_RawMemFreeFnc(tcp_rec->rsh);
1395    IMP_MemFreeFnc(tcp_rec, sizeof(MP_TCP_t));
1396    link->transp.private1 = NULL;
1397
1398#ifdef MP_DEBUG
1399    fprintf(stderr, "tcp_close_connection: exiting\n");
1400    fflush(stderr);
1401#endif /* MP_DEBUG */
1402
1403    RETURN_OK(link);
1404}
1405
1406#ifdef __STDC__
1407MP_Status_t tcp_kill_connection(MP_Link_pt link)
1408#else
1409MP_Status_t tcp_kill_connection(link)
1410    MP_Link_pt link;
1411#endif
1412{
1413    MP_TCP_t *tcp_rec;
1414    int fork_pid = -1;
1415   
1416#ifdef MP_DEBUG
1417    fprintf(stderr, "tcp_kill_connection: entering\n");
1418    fflush(stderr);
1419#endif /* MP_DEBUG */
1420
1421    TEST_LINK_NULL(link);
1422    if (link->transp.private1 == NULL)
1423        return MP_SetError(link, MP_NullTransport);
1424    tcp_rec = (MP_TCP_t*)link->transp.private1;
1425   
1426    if (tcp_rec->mode == MP_LAUNCH_MODE)
1427    {
1428      char  *rsh_argv[5];
1429      char  rsh_kill[20];
1430      rsh_argv[0] = tcp_rec->rsh;
1431      rsh_argv[1] = tcp_rec->peerhost;
1432      rsh_argv[2] = "-n";
1433      sprintf(rsh_kill, "kill -9 %d", tcp_rec->peerpid);
1434      rsh_argv[3] = rsh_kill;
1435      rsh_argv[4] = NULL;
1436
1437      if ((fork_pid = vfork()) == -1)
1438      {
1439        MP_LogEvent(link, MP_ERROR_EVENT,
1440                    "MP_OpenLink: can't fork to kill application");
1441        return MP_SetError(link, MP_Failure);
1442      }
1443
1444      if (! fork_pid)
1445      {
1446        execvp(rsh_argv[0], rsh_argv);
1447        fputs("tcp_kill_connection: execvp failed - bailing out\n", stderr);
1448        fflush(stderr);
1449        _exit(1);
1450      }
1451    }
1452    else if (tcp_rec->mode == MP_FORK_MODE)
1453    {
1454      kill(tcp_rec->peerpid, SIGKILL);
1455    }
1456   
1457    return tcp_close_connection(link);
1458}
1459
1460/***********************************************************************
1461 * FUNCTION:  tcp_open_connection
1462 * INPUT:     link - pointer to link structure for this connection
1463 *            argc - number of arguments in argv
1464 *            argv - arguments as strings
1465 * OUTPUT:    Success:  MP_Success
1466 *                      link established as a tcp connection
1467 *            Failure:  MP_Failure
1468 * OPERATION: Determine the tcp mode we are using and attempt to
1469 *            establish a connection accordingly.  We could fail
1470 *            for any of a number of reasons.  Bad hostname, bad
1471 *            port number, missing arguments....
1472 ***********************************************************************/
1473#ifdef __STDC__
1474MP_Status_t tcp_open_connection(MP_Link_pt   link,
1475                                int          argc,
1476                                char       **argv)
1477#else
1478MP_Status_t tcp_open_connection(link, argc, argv)
1479    MP_Link_pt   link;
1480    int          argc;
1481    char       **argv;
1482#endif
1483{
1484    MP_Status_t status;
1485    struct protoent *protostruct = NULL;
1486    int sockopt; 
1487
1488#ifdef MP_DEBUG
1489    fprintf(stderr, "tcp_open_connection: entering\n");
1490    fflush(stderr);
1491#endif
1492
1493    TEST_LINK_NULL(link);
1494    if (tcp_init_transport(link) != MP_Success)
1495        return MP_Failure;
1496
1497    ((MP_TCP_t *)link->transp.private1)->mode = get_tcp_mode(argc, argv);
1498    switch (((MP_TCP_t *)link->transp.private1)->mode)
1499    {
1500    case MP_LISTEN_MODE:
1501        status = open_tcp_listen_mode(link, argc, argv);
1502        if (status != MP_Success)
1503            break;
1504        status = socket_accept_blocking(link,
1505                        &((MP_TCP_t *)link->transp.private1)->sock);
1506        break;
1507
1508    case MP_CONNECT_MODE:
1509        status = open_tcp_connect_mode(link, argc, argv);
1510        break;
1511
1512    case MP_LAUNCH_MODE:
1513        status = open_tcp_launch_mode(link, argc, argv);
1514        break;
1515
1516        case MP_FORK_MODE:
1517          status = open_tcp_fork_mode(link, argc, argv);
1518          break;
1519         
1520    default:
1521        MP_LogEvent(link, MP_ERROR_EVENT,
1522                    "Can't open connection, unknown -MPmode argument");
1523        return MP_SetError(link, MP_Failure);
1524    }
1525
1526    if (status != MP_Success)
1527        return MP_Failure;
1528
1529    /* get the protocol number for TCP from /etc/protocols */
1530    if ((protostruct = getprotobyname("tcp")) == NULL) {
1531      MP_LogEvent(link, MP_ERROR_EVENT,
1532                    "Can't open connection: can't get TCP protocol info!!");
1533      return MP_SetError(link, MP_Failure);
1534      }
1535
1536    /* set socket to not coalesce small amounts of data in a single packet */
1537    if (setsockopt(((MP_TCP_t *)link->transp.private1)->sock, 
1538                   protostruct->p_proto, TCP_NODELAY, &sockopt, sizeof(int)) == -1) {
1539      MP_LogEvent(link, MP_ERROR_EVENT,
1540                 "Can't open connection: can't set NODELAY option on socket"); 
1541      return MP_SetError(link, MP_Failure);
1542      }
1543
1544    ERR_CHK(tcp_negotiate_word_order(link));
1545    ERR_CHK(tcp_negotiate_fp_format(link));
1546    ERR_CHK(tcp_negotiate_bigint_format(link));
1547    ERR_CHK(tcp_exchange_pids(link));
1548
1549#ifdef MP_DEBUG
1550    fprintf(stderr, "tcp_open_connection: exiting\n");
1551    fflush(stderr);
1552#endif
1553
1554    RETURN_OK(link);
1555}
1556
1557
1558#ifdef __STDC__
1559static MP_Status_t tcp_exchange_pids(MP_Link_pt link)
1560#else
1561static MP_Status_t tcp_exchange_pids(link)
1562    MP_Link_pt link;
1563#endif
1564{
1565  MP_Uint32_t peerpid;
1566  MP_NumAnnot_t na;
1567
1568  /* Hmm... should communicate pid_t -- could be 64 bit !! */
1569  ERR_CHK(MP_PutUint32Packet(link, (MP_Uint32_t) getpid(), 0));
1570  MP_EndMsgReset(link);
1571 
1572  ERR_CHK(MP_SkipMsg(link));
1573  ERR_CHK(MP_GetUint32Packet(link, &peerpid, &na));
1574  ((MP_TCP_t *)link->transp.private1)->peerpid = peerpid;
1575
1576  return MP_Success;
1577}
1578
1579/*
1580 * Currently this is a canned routine.  There is little parsing.  It
1581 * assumes that the first thing the two partners will do is to negotiate
1582 * the word order.  If that is a correct assumption, this should work
1583 * like a champ, otherwise we are in trouble.
1584 */
1585#ifdef __STDC__
1586static MP_Status_t tcp_negotiate_word_order(MP_Link_pt link)
1587#else
1588static MP_Status_t tcp_negotiate_word_order(link)
1589    MP_Link_pt link;
1590#endif
1591{
1592   MP_DictTag_t   dtag;
1593   MP_NumAnnot_t  na;
1594   MP_NumChild_t  nc;
1595   unsigned long  req_word_order;
1596   MP_Common_t    mp_op;
1597
1598   /*
1599    * Step 1:  We send our word order preference to our partner
1600    */
1601   ERR_CHK(MP_PutCommonOperatorPacket(link, MP_MpDict, 
1602              MP_CopMpByteOrderRequest, 0, 1)); 
1603   ERR_CHK(IMP_PutUint32(link, (unsigned long)link->env->native_word_order));
1604   MP_EndMsgReset(link);
1605
1606   /*
1607    * Step 2: Get a reply.  It should be MP_CopMpByteOrderRequest.
1608    * Probably should peek at the data stream here to be sure we are
1609    * getting the right thing.
1610    */
1611   ERR_CHK(MP_SkipMsg(link));
1612   ERR_CHK(MP_GetCommonOperatorPacket(link, &dtag, &mp_op, &na, &nc));
1613   ERR_CHK(IMP_GetUint32(link, &req_word_order));
1614
1615   if (mp_op != MP_CopMpByteOrderRequest || dtag != MP_MpDict) {
1616      MP_LogEvent(link, MP_ERROR_EVENT, 
1617          "Problem exchanging initialization information: byte order");
1618      return MP_Failure;
1619   }
1620
1621
1622   /*
1623    * We only have to do something if the word order has changed.
1624    * This will only happen if both parties are Little Endian
1625    */
1626   if (req_word_order == (unsigned long)link->env->native_word_order
1627       && link->env->native_word_order != link->link_word_order) {
1628       link->link_word_order = link->env->native_word_order;
1629       MP_LogEvent(link, MP_INIT_EVENT,
1630                   "Link word order changed to Little Endian");
1631   }
1632
1633   return MP_Success;
1634}
1635
1636
1637
1638#ifdef __STDC__
1639static MP_Status_t tcp_negotiate_fp_format(MP_Link_pt link)
1640#else
1641static MP_Status_t tcp_negotiate_fp_format(link)
1642    MP_Link_pt link;
1643#endif
1644{
1645    MP_DictTag_t   dtag;
1646    MP_NumAnnot_t  na;
1647    MP_NumChild_t  nc;
1648    unsigned long  req_fp_format;
1649    MP_Common_t    mp_op;
1650
1651
1652#ifdef MP_DEBUG
1653    printf("tcp_negotiate_fp_format: entering\n");
1654#endif
1655
1656    /*
1657     * Step 1:  We send our fp format preference to our partner
1658     */
1659    ERR_CHK(MP_PutCommonOperatorPacket(link, MP_MpDict, 
1660                                       MP_CopMpFpFormatRequest, 0, 1));
1661    ERR_CHK(IMP_PutUint32(link, (unsigned long)link->env->native_fp_format));
1662    MP_EndMsgReset(link);
1663
1664    /*
1665     * Step 2: Get a reply.  It should be "MP_FpFormatRequest"
1666     * probably should peek at the data stream here to be sure we are
1667     *  getting the right thing
1668     */
1669    ERR_CHK(MP_SkipMsg(link));
1670    ERR_CHK(MP_GetCommonOperatorPacket(link, &dtag, &mp_op, &na, &nc));
1671    ERR_CHK(IMP_GetUint32(link, &req_fp_format));
1672
1673   if (mp_op != MP_CopMpFpFormatRequest || dtag != MP_MpDict) {
1674      MP_LogEvent(link, MP_ERROR_EVENT, 
1675          "Problem exchanging initialization information: fp format");
1676      return MP_Failure;
1677   }
1678
1679    /*
1680     * We only have to do something if the fp format has changed.
1681     * This will only happen if both parties are something other than IEEE.
1682     */
1683    if (req_fp_format == (unsigned long)link->env->native_fp_format
1684        && link->env->native_fp_format != link->link_fp_format) {
1685        link->link_fp_format = link->env->native_fp_format;
1686        MP_LogEvent(link, MP_INIT_EVENT,
1687                    "Link floating point format changed to VAX");
1688    }
1689
1690#ifdef MP_DEBUG
1691    printf("tcp_negotiate_fp_format: exiting\n");
1692#endif
1693
1694    return MP_Success;
1695}
1696
1697
1698
1699#ifdef __STDC__
1700static MP_Status_t tcp_negotiate_bigint_format(MP_Link_pt link)
1701#else
1702static MP_Status_t tcp_negotiate_bigint_format(link)
1703    MP_Link_pt link;
1704#endif
1705{
1706    MP_DictTag_t   dtag;
1707    MP_NumAnnot_t  na;
1708    MP_NumChild_t  nc;
1709    unsigned long  req_bigint_format;
1710    MP_Common_t    mp_op;
1711
1712#ifdef MP_DEBUG
1713    printf("tcp_negotiate_bigint_format: entering\n");
1714#endif
1715
1716    /*
1717     * Step 1:  We send our bigint format preference to our partner
1718     */
1719    ERR_CHK(MP_PutCommonOperatorPacket(link, MP_MpDict, 
1720                                       MP_CopMpBigIntFormatRequest, 0, 1));
1721    ERR_CHK(IMP_PutUint32(link, (unsigned long)link->link_bigint_format));
1722    MP_EndMsgReset(link);
1723
1724    /*
1725     * Step 2: Get a reply.  It should be "MP_BigIntFormatRequest"
1726     * probably should peek at the data stream here to be sure we are
1727     * getting the right thing
1728     */
1729    ERR_CHK(MP_SkipMsg(link));
1730    ERR_CHK(MP_GetCommonOperatorPacket(link, &dtag, &mp_op, &na, &nc));
1731    ERR_CHK(IMP_GetUint32(link, &req_bigint_format));
1732
1733    if (mp_op != MP_CopMpBigIntFormatRequest || dtag != MP_MpDict) {
1734      MP_LogEvent(link, MP_ERROR_EVENT, 
1735          "Problem exchanging initialization information: big int format");
1736      return MP_Failure;
1737    }
1738
1739    /*
1740     * We only have to do something if both ends are not already the same
1741     */
1742    if (req_bigint_format != (unsigned long)link->link_bigint_format) {
1743        link->link_bigint_format = MP_GMP;
1744        MP_LogEvent(link, MP_INIT_EVENT,
1745                    "Link multiple precision integer format set to GMP");
1746    }
1747
1748#ifdef MP_DEBUG
1749    printf("tcp_negotiate_bigint_format: exiting\n");
1750#endif
1751
1752    return MP_Success;
1753}
1754
1755
1756
1757#ifdef __STDC__
1758MP_Status_t tcp_get_port(SOCKET *sock, short *port)
1759#else
1760MP_Status_t tcp_get_port(sock, port)
1761    SOCKET *s;
1762    short  *port;
1763#endif
1764{
1765    SOCKADDR_IN isin;
1766    int         attempts = 0;
1767
1768    if (*port < IPPORT_RESERVED)
1769        *port = IPPORT_RESERVED;
1770
1771    if ((*sock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
1772        return MP_Failure;
1773
1774    /*
1775     * Initialize socket's address structure
1776     */
1777    isin.sin_family      = AF_INET;
1778    isin.sin_addr.s_addr = htonl(INADDR_ANY);
1779    isin.sin_port        = htons(*port);
1780
1781    while (bind(*sock, (SOCKADDR *)&isin, sizeof(isin)) == SOCKET_ERROR) {
1782        if (LASTERROR == ERRORCODE(EADDRINUSE)) {
1783            (*port)++;
1784            /* since port is an unsigned short, it may wrap around */
1785            if (*port < IPPORT_RESERVED)
1786                *port = IPPORT_RESERVED;
1787            isin.sin_port = htons(*port);
1788            attempts = 0;
1789        }
1790
1791        if (attempts++ > MP_MAX_BIND_ATTEMPTS)
1792            return MP_Failure;
1793    }
1794
1795    return MP_Success;
1796}
1797
1798
1799
1800#ifdef __WIN32__
1801/*
1802 * WinSockInitialize calls the WinSock API initialization function WSAStartup
1803 * and returns a value != 0 if something went wrong. Extended error information
1804 * can be retrieved later by calling WSAGetLastError();
1805 */
1806#ifdef __STDC__
1807int WinSockInitialize(void)
1808#else /* __STDC__ */
1809int WinSockInitialize()
1810#endif /* __STDC__ */
1811{
1812   WORD        wVersionRequired;
1813   WSADATA     wsaData;
1814
1815   wVersionRequired = MAKEWORD(1, 1);
1816   return WSAStartup(wVersionRequired, &wsaData);
1817}
1818
1819
1820
1821/*
1822 * splitargs splits a string of words separated by white-space characters into
1823 * multiple strings by inserting Null characters after the ends of the words.
1824 * The function returns the number of words found in the string.
1825 */
1826#ifdef __STDC__
1827static int splitargs(char *argstr)
1828#else /* __STDC__ */
1829static int splitargs(argstr)
1830   char *argstr;
1831#endif /* __STDC__ */
1832{
1833    char *p, *q;
1834    int   argcnt, wordflag, quoted;
1835
1836    p = q = argstr;
1837    argcnt = wordflag = quoted = 0;
1838
1839    /* Step through all characters in the string. */
1840    while (*p)
1841        switch (*p) {
1842            /* Is *p a space or a tab character? */
1843        case ' ':
1844        case '\t':
1845            /* Simply pass it on if it is part of a `quoted` string. */
1846            if (quoted) {
1847                *q++ = *p++;
1848                break;
1849            }
1850
1851            /*
1852             * If the character marks the end of a word then terminate that
1853             * word with a Null character, mark the word as read and increase
1854             * the argument counter.
1855             */
1856            if (wordflag) {
1857                *q++ = '\0';
1858                wordflag = 0;
1859                ++argcnt;
1860            }
1861            p++;
1862            break;
1863
1864            /* Is *p a backquote character? */
1865        case '`':
1866
1867            /* If the next character is another backquote then regard the
1868             * combination of both as a single literal backquote and pass it
1869             * on to the result string. If not, set the quote mode, i.e. pass
1870             * on all following characters until the next backquote is found or
1871             * the end of the string reached.
1872             */
1873            if (*++p == '`') {
1874                *q++ = *p++;
1875                wordflag = 1;
1876            } else
1877                quoted = !quoted;
1878            break;
1879
1880            /* Put all other characters into the result string. */
1881        default:
1882            wordflag = 1;
1883            *q++ = *p++;
1884            break;
1885        }
1886
1887    /* Terminate the last word with a Null character. */
1888    if (wordflag) {
1889        ++argcnt;
1890        *q = '\0';
1891    }
1892
1893    return argcnt;
1894}
1895
1896#endif /* __WIN32__ */
1897
1898
Note: See TracBrowser for help on using the repository browser.