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

spielwiese
Last change on this file since 16105c was 16105c, checked in by Hans Schönemann <hannes@…>, 22 years ago
*hannes: syntax git-svn-id: file:///usr/local/Singular/svn/trunk@5672 2c84dea3-7e68-4137-9b89-c4e89433aadc
  • Property mode set to 100644
File size: 56.8 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 *
23 *   IMPLEMENTATION FILE:  MP_TcpTransp.c
24 *
25 *      Public interface routines to the "tcp" transport device.
26 *      These include read, write, open, close, and init routines.
27 *      There are some support routines as well.
28 *
29 *  Change Log:
30 *       08/16/96   hennig     - tcp transport device ported to Windows95/NT
31 *       11/25/95   sgray      - added support to negotiate endian word order.
32 *       11/20/95   sgray      - added tcp_has_data() routine to determine
33 *                               if there is any data on the link to read and
34 *                               put it in the transp_op structure
35 *       11/15/95   obachman   - use vfork instead of fork
36 *                             - major change to open_tcp_launch_mode:
37 *                             1.) -MPapplication takes one string: application
38 *                                 and its arguments
39 *                             2.) MP appends -MPhost -MPport to arguments of
40 *                                 application
41 *                             3.) rsh is started with -n to disable
42 *                                 standard input
43 *                             4.) rsh has fixed number of args, namely:
44 *                                 -n host application
45 *       11/14/95   obachman   - introduced macro for remote shell command
46 *       11/10/95   obachman   - cleaned up things around the select call,
47 *                               so that it runs under Linux, and uses
48 *                               the timestruct to actually come back after
49 *                               an unsuccessful select
50 *                             - fixed nasty bug on line 348:
51 *                      tcp_rec->peerhost = (char *) malloc(sizeof(host) + 1);
52 *                      should have been
53 *                      tcp_rec->peerhost = (char *) malloc(strlen(host) + 1);
54 *                              - similar in tcp_inti_transport
55 *                              - one minor cast to (Char **) of dmy_args
56 *       September 11, 1995 SG - Reorganization of files.  Cleaned up
57 *                               header files and relocated source to
58 *       3/3/96    sgray - moved call to tcp_init_transport() inside
59 *                         tcp_open_transport() and removed the init
60 *                         routine from the tcp_ops struct.
61 *       5/16/96   sgray - Changed the tcp_read() routine to loop until it
62 *                         has read all the data requested.  This was done
63 *                         b/c the higher level routines in the buffering
64 *                         layer return failure if the number of bytes read
65 *                         is not the number of bytes requested.  We ran into
66 *                         the problem that occasionally the application was
67 *                         trying to read the data faster than the TCP layer
68 *                         could provide it, providing a "false failure".
69 *       3/11/97   sgray - Changed all memory management calls that allocate
70 *                         or deallocate strings to use the "Raw" calls.
71 *
72 ***************************************************************************/
73
74
75#ifndef lint
76static char vcid[] = "@(#) $Id: MP_TcpTransp.c,v 1.11 2001-11-05 17:11:11 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
771    /* establish connection */
772    stat = open_tcp_connect_mode(link, 8, argv);
773
774#ifndef NO_LOGGING
775    IMP_RawMemFreeFnc(parent_logfilename);
776    if (stat == MP_Success)
777      sprintf(log_msg, "open_tcp_fork: opened forked link, child id: %d",
778              getpid());
779    else
780      sprintf(log_msg,
781              "open_tcp_fork: open of forked link failed, child is exited");
782    MP_LogEvent(link, MP_ERROR_EVENT, log_msg);
783#endif
784    if (stat != MP_Success)
785      _exit(1);
786    else
787    {
788      tcp_rec->status = MP_LinkIsChild;
789      return MP_Success;
790    }
791  }
792  else
793  {
794    /* parent's business */
795#ifndef NO_LOGGING
796    /* reset logging stuff */
797    link->env->logfilename = parent_logfilename;
798    link->env->logfd = parent_logfd;
799#endif
800
801    /* establish connection */
802    socket_accept_blocking(link, &tcp_rec->sock);
803    tcp_rec->status = MP_LinkIsParent;
804
805#ifndef NO_LOGGING
806    sprintf(log_msg,"open_tcp_fork: opened fork link, parent id: %d",
807            getpid()); 
808    MP_LogEvent(link, MP_INIT_EVENT, log_msg);
809#endif
810
811    RETURN_OK(link);
812  }
813}
814 
815/***********************************************************************
816 * FUNCTION:  open_tcp_connect_mode
817 * INPUT:     link - pointer to link structure for this connection
818 *            argc - number of arguments in argv
819 *            argv - arguments as strings
820 * OUTPUT:    Success:  MP_Success
821 *                      socket connected to the specified host, port
822 *            Failure:  MP_Failure - couldn't make the connection.
823 *                      Maybe the peer isn't listening.
824 * OPERATION: Establish a TCP connection to the peer's port. Update the
825 *            private tcp structure accordingly.  We hope that there
826 *            is a peer actually listening on the given port.  We make
827 *            1000 attempts before giving up.
828 ***********************************************************************/
829#ifdef __STDC__
830MP_Status_t open_tcp_connect_mode(MP_Link_pt   link,
831                                  int          argc,
832                                  char       **argv)
833#else
834MP_Status_t open_tcp_connect_mode(link, argc, argv)
835    MP_Link_pt   link;
836    int          argc;
837    char       **argv;
838#endif
839{
840    SOCKET       sock;
841    int          attempts = 0;
842    HOSTENT     *hp;
843    SOCKADDR_IN  isin;
844    char        *host, *cport, *ptr;
845    short        port;
846    MP_TCP_t    *tcp_rec = (MP_TCP_t *)link->transp.private1;
847
848#ifdef MP_DEBUG
849    fprintf(stderr, "open_tcp_connect_mode: entering\n");
850    fflush(stderr);
851#endif /* MP_DEBUG */
852
853    tcp_rec->peerhost = NULL;
854
855    /* get the port number */
856    if (
857        (cport = IMP_GetCmdlineArg(argc, argv, "-MPport")) == NULL)
858        return MP_SetError(link, MP_Failure);
859
860    port = (short) strtol(cport, &ptr, 10);
861    if ((host = IMP_GetCmdlineArg(argc, argv, "-MPhost")) == NULL)
862      host = link->env->thishost;
863   
864    if (ptr == cport || errno == ERANGE)
865      return MP_SetError(link, MP_Failure);
866
867    if ((hp = gethostbyname(host)) == NULL) {
868        MP_LogEvent(link, MP_ERROR_EVENT,
869                    "open_tcp_connect_mode - cannot get hostname");
870        return MP_SetError(link, MP_Failure);
871    }
872
873    sock = socket(AF_INET, SOCK_STREAM, 0);
874    if (sock == INVALID_SOCKET) {
875        sprintf(log_msg,
876                "open_tcp_connect_mode - Cannot open socket (socket error %d)",
877                LASTERROR);
878        MP_LogEvent(link, MP_ERROR_EVENT, log_msg);
879        return MP_SetError(link, MP_Failure);
880    }
881
882    /* Initialize the socket address to the server's address. */
883    memset((char *)&isin, 0, sizeof(isin));
884    isin.sin_family = AF_INET;
885    memcpy(&isin.sin_addr.s_addr, hp->h_addr, hp->h_length);
886    isin.sin_port = htons(port);
887
888    /* Connect to the server. */
889    while (connect(sock, (SOCKADDR*)&isin, sizeof(isin)) == SOCKET_ERROR) {
890        CLOSE_SOCKET(sock);
891        if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
892            sprintf(log_msg,
893                    "open_tcp_connect_mode "
894                    "- Cannot open socket (socket error %d)", LASTERROR);
895            MP_LogEvent(link, MP_ERROR_EVENT, log_msg);
896            return MP_SetError(link, MP_Failure);
897        }
898
899        if (attempts > MP_MAX_BIND_ATTEMPTS) {
900            MP_LogEvent(link, MP_ERROR_EVENT,
901                        "open_tcp_connect_mode - Cannot connect");
902            return MP_SetError(link, MP_Failure);
903        }
904
905        attempts++;
906    }
907
908    if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char*)&sock, sizeof(sock))
909        == SOCKET_ERROR) {
910        sprintf(log_msg,
911                "open_tcp_connect_mode "
912                "- cannot set socket option (socket error %d)", LASTERROR);
913        MP_LogEvent(link, MP_ERROR_EVENT, log_msg);
914        return MP_SetError(link, MP_Failure);
915    }
916
917    tcp_rec->bindsock = INVALID_SOCKET;
918    tcp_rec->sock     = sock;
919    tcp_rec->peerport = port;
920
921    tcp_rec->peerhost = (char*)IMP_RawMemAllocFnc(strlen(host) +1);
922    TEST_MEM_ALLOC_ERROR(link, tcp_rec->peerhost);
923    strcpy(tcp_rec->peerhost, host);
924
925    tcp_rec->myhost = (char*)IMP_RawMemAllocFnc(MP_HOST_NAME_LEN);
926    TEST_MEM_ALLOC_ERROR(link, tcp_rec->myhost);
927    if (gethostname(tcp_rec->myhost, MP_HOST_NAME_LEN) == SOCKET_ERROR) {
928        IMP_RawMemFreeFnc(tcp_rec->peerhost);
929        IMP_RawMemFreeFnc(tcp_rec->myhost);
930        tcp_rec->myhost = NULL;
931    }
932
933#ifdef MP_DEBUG
934    fprintf(stderr, "open_tcp_connect_mode: exiting - socket is %d\n", sock);
935    fflush(stderr);
936#endif
937
938    RETURN_OK(link);
939}
940
941
942
943/***********************************************************************
944 * FUNCTION:  socket_accept_non_blocking
945 * INPUT:     sock   - the socket to do the accept on
946 * OUTPUT:    Success: the socket to be used for io
947 *                     Failure: MP_Failure
948 *                     sock contains the socket number of the accepted
949 *                     socket.
950 * PURPOSE:   Perform a non-blocking accept.  If we timeout before getting
951 *            a connection request, return MP_Failure.
952 ***********************************************************************/
953#ifdef __STDC__
954MP_Status_t socket_accept_non_blocking(MP_Link_pt link, SOCKET *sock)
955#else
956MP_Status_t socket_accept_non_blocking(link, sock)
957    MP_Link_pt  link;
958    SOCKET     *sock;
959#endif
960{
961    SOCKADDR addr;
962    SOCKET   nsock = 0;
963    int      attempts, addrlen = sizeof(addr);
964    fd_set   read_template;
965    TIMEVAL  timeout;
966
967#ifdef MP_DEBUG
968    fprintf(stderr,
969            "socket_accept_non_blocking: entering - socket = %d\n", *sock);
970    fflush(stderr);
971#endif
972
973    timeout.tv_sec  = MP_ACCEPT_WAIT_SEC;
974    timeout.tv_usec = MP_ACCEPT_WAIT_USEC;
975
976    for (attempts = 0; attempts < 2; attempts++) {
977        FD_ZERO(&read_template);
978        FD_SET(*sock, &read_template);
979
980        if (SELECT(FD_SETSIZE, &read_template, NULL, NULL, &timeout)
981            != SOCKET_ERROR) {
982
983            if ((nsock = accept(*sock, &addr, &addrlen)) != INVALID_SOCKET)
984                break;
985
986            sprintf(log_msg,
987                    "socket_accept_non_blocking: failed (socket error %d)",
988                    LASTERROR);
989            MP_LogEvent(link, MP_ERROR_EVENT, log_msg);
990            return MP_Failure;
991        }
992    }
993
994    *sock = nsock;
995
996#ifdef MP_DEBUG
997    fprintf(stderr,
998            "socket_accept_non_blocking: returning - new socket =  %d\n",
999            *sock);
1000    fflush(stderr);
1001#endif
1002
1003    return MP_Success;
1004}
1005
1006
1007
1008/***********************************************************************
1009 * FUNCTION:  socket_accept_blocking
1010 * INPUT:     link - link with which sock will be associated, need this
1011 *                   really only for the error handling.
1012 *            sock - the socket to do the accept on
1013 * OUTPUT:    int - Success: the socket to be used for io
1014 *                  Failure: MP_Failure
1015 * PURPOSE:   Perform a blocking accept.  We won't return until we get an
1016 *            connect request or are interrupted.
1017 ***********************************************************************/
1018#ifdef __STDC__
1019MP_Status_t socket_accept_blocking(MP_Link_pt link, SOCKET *sock)
1020#else
1021MP_Status_t socket_accept_blocking (link, sock)
1022    MP_Link_pt  link;
1023    SOCKET     *sock;
1024#endif
1025{
1026    SOCKADDR addr;
1027    SOCKET   nsock = 0;
1028    int      attempts, addrlen = sizeof(addr);
1029    fd_set   read_template;
1030
1031#ifdef MP_DEBUG
1032    fprintf(stderr, "socket_accept_blocking: entering - sock = %d\n", *sock);
1033    fflush(stderr);
1034#endif
1035
1036    for (attempts = 0; attempts < 2; attempts++) {
1037        FD_ZERO(&read_template);
1038        FD_SET(*sock, &read_template);
1039
1040        if (SELECT(FD_SETSIZE, &read_template, NULL, NULL, NULL)
1041            != SOCKET_ERROR) {
1042            if ((nsock = accept(*sock, &addr, &addrlen)) != INVALID_SOCKET)
1043                break;
1044
1045            sprintf(log_msg,
1046                    "socket_accept_blocking: failed (socket error %d)",
1047                    LASTERROR);
1048            MP_LogEvent(link, MP_ERROR_EVENT, log_msg);
1049            return MP_Failure;
1050        }
1051    }
1052
1053    *sock = nsock;
1054
1055#ifdef MP_DEBUG
1056    fprintf(stderr, "socket_accept_blocking: returning - new socket =  %d\n", *sock);
1057    fflush(stderr);
1058#endif
1059
1060    return MP_Success;
1061}
1062
1063
1064
1065
1066
1067/****  TCP "public" routines ******/
1068
1069#ifdef __STDC__
1070MP_Boolean_t tcp_get_status(MP_Link_pt link, MP_LinkStatus_t status_to_check)
1071#else
1072MP_Boolean_t tcp_get_status(link, status_to_check)
1073    MP_Link_pt       link;
1074    MP_LinkStatus_t  status_to_check;
1075#endif
1076{
1077    SOCKET  sock = ((MP_TCP_t *)link->transp.private1)->sock;
1078    fd_set  mask, fdmask;
1079    TIMEVAL wt;
1080
1081#ifdef MP_DEBUG
1082    printf("tcp_get_status: entering for link %d\n", link->link_id);
1083#endif
1084  if (status_to_check == MP_LinkIsParent)
1085  {
1086    if (((MP_TCP_t *)link->transp.private1)->status == MP_LinkIsParent)
1087      return MP_TRUE;
1088    else
1089      return MP_FALSE;
1090  }
1091  else if (status_to_check == MP_LinkIsChild)
1092  {
1093    if (((MP_TCP_t *)link->transp.private1)->status == MP_LinkIsChild)
1094      return MP_TRUE;
1095    else
1096      return MP_FALSE;
1097  }
1098 
1099
1100    FD_ZERO(&mask);
1101    FD_SET(sock, &mask);
1102
1103    fdmask = mask;
1104
1105    /* Don't block. Return socket status immediately. */
1106    wt.tv_sec  = 0;
1107    wt.tv_usec = 0;
1108
1109    switch (status_to_check) {
1110    case MP_LinkReadyReading:
1111        switch (SELECT(FD_SETSIZE, &fdmask, NULL, NULL, &wt)) {
1112        case 0:
1113            return MP_FALSE;
1114        case SOCKET_ERROR:
1115            return MP_FALSE;
1116        }
1117        if (FD_ISSET(sock, &fdmask))
1118            return MP_TRUE;
1119
1120        return MP_FALSE;
1121
1122    case MP_LinkReadyWriting:
1123        switch (SELECT(FD_SETSIZE, NULL, &fdmask, NULL, &wt)) {
1124        case 0:
1125            return MP_FALSE;
1126        case SOCKET_ERROR:
1127            return MP_FALSE;
1128        }
1129        if (FD_ISSET(sock, &fdmask))
1130            return MP_TRUE;
1131
1132        return MP_FALSE;
1133
1134    default:
1135        sprintf(log_msg,
1136                "tcp_get_status: illegal option %d", status_to_check);
1137        MP_LogEvent(link, MP_ERROR_EVENT, log_msg);
1138    }
1139
1140#ifdef MP_DEBUG
1141    printf("tcp_get_status: exiting\n");
1142#endif
1143
1144    return MP_FALSE;
1145}
1146
1147
1148
1149#ifdef __STDC__
1150long tcp_read(MP_Link_pt link, char * buf, long len)
1151#else
1152long tcp_read(link, buf, len)
1153    MP_Link_pt   link;
1154    char *      buf;
1155    long         len;
1156#endif
1157{
1158    SOCKET   sock = ((MP_TCP_t *)link->transp.private1)->sock;
1159    fd_set   mask, readfs;
1160    long     total = 0, n;
1161
1162#ifdef MP_DEBUG
1163    fprintf(stderr, "tcp_read: entering - len = %ld\n", len);
1164    fflush(stderr);
1165#endif
1166
1167    while (len > 0) {
1168        FD_ZERO(&mask);
1169        FD_SET(sock, &mask);
1170
1171        while (MP_TRUE) {
1172            readfs = mask;
1173
1174            /* Do blocking read. */
1175            switch (SELECT(FD_SETSIZE, &readfs, NULL, NULL, NULL)) {
1176            case 0:
1177                MP_LogEvent(link, MP_ERROR_EVENT, "tcp_read: timed out");
1178                return -1;
1179
1180            case SOCKET_ERROR:
1181                if (LASTERROR == ERRORCODE(EINTR))
1182                    continue;
1183                return -1;
1184            }
1185
1186            if (FD_ISSET(sock, &readfs))
1187                break;
1188        }
1189
1190        if ((n = READ_SOCKET(sock, buf, len)) == SOCKET_ERROR) {
1191            sprintf(log_msg, "tcp_read: can't do read (socket error %d)",
1192                    LASTERROR);
1193            MP_LogEvent(link, MP_ERROR_EVENT, log_msg);
1194            return MP_SetError(link, MP_CantReadLink);
1195        }
1196
1197        total += n;
1198        len   -= n;
1199        buf   += n;
1200    }
1201
1202#ifndef NO_LOGGING
1203    if (link->logmask & MP_LOG_READ_EVENTS) {
1204        sprintf(log_msg, "tcp_read: read %ld bytes", total);
1205        MP_LogEvent(link, MP_READ_EVENT, log_msg);
1206    }
1207#endif /* NO_LOGGING */
1208
1209#ifdef MP_DEBUG
1210    fprintf(stderr, "tcp_read: exiting - read %ld bytes \n", total);
1211    fflush(stderr);
1212#endif /* MP_DEBUG */
1213
1214    return total;
1215}
1216
1217
1218MP_Status_t
1219#ifdef __STDC__
1220tcp_flush(MP_Link_pt link)
1221#else
1222tcp_flush(link)
1223    MP_Link_pt  link;
1224#endif
1225{
1226
1227#ifdef MP_DEBUG
1228    fprintf(stderr, "tcp_flush: entering\n");
1229    fflush(stderr);
1230#endif
1231#ifdef MP_DEBUG
1232    fprintf(stderr, "tcp_flush: exiting  \n");
1233    fflush(stderr);
1234#endif
1235  return MP_ClearError(link);
1236}
1237
1238
1239#ifdef __STDC__
1240long tcp_write(MP_Link_pt link,
1241               char *    buf,
1242               long       len)
1243#else
1244long tcp_write(link, buf, len)
1245    MP_Link_pt  link;
1246    char *     buf;
1247    long        len;
1248#endif
1249{
1250    long   i, cnt;
1251    SOCKET sock = ((MP_TCP_t *)link->transp.private1)->sock;
1252    fd_set mask, writefs;
1253
1254#ifdef MP_DEBUG
1255    fprintf(stderr, "tcp_write: entering - len = %ld\n", len);
1256    fflush(stderr);
1257#endif
1258
1259    FD_ZERO(&mask);
1260    FD_SET(sock, &mask);
1261
1262    while (MP_TRUE) {
1263        writefs = mask;
1264        switch (SELECT(FD_SETSIZE, NULL, &writefs, NULL, NULL)) {
1265        case 0:
1266            MP_LogEvent(link, MP_ERROR_EVENT, "tcp_write: timed out");
1267            return -1;
1268
1269        case SOCKET_ERROR:
1270            if (LASTERROR == ERRORCODE(EINTR))
1271                continue;
1272            MP_LogEvent(link, MP_ERROR_EVENT,
1273                        "tcp_write: errno isn't EINTR - this is bad");
1274            return -1;
1275        }
1276
1277        if (FD_ISSET(sock, &writefs))
1278            break;
1279    }
1280
1281
1282    for (cnt = len; cnt > 0; cnt -= i, buf += i) {
1283        if ((i = WRITE_SOCKET(sock, buf, cnt)) == SOCKET_ERROR) {
1284            sprintf(log_msg, "tcp_write: can't do write (socket error %d)",
1285                    LASTERROR);
1286            MP_LogEvent(link, MP_ERROR_EVENT, log_msg);
1287            return MP_SetError(link, MP_CantWriteLink);
1288        }
1289
1290#ifndef NO_LOGGING
1291        if (link->logmask & MP_LOG_WRITE_EVENTS) {
1292            sprintf(log_msg, "tcp_write: wrote %ld bytes", i);
1293            MP_LogEvent(link, MP_WRITE_EVENT, log_msg);
1294        }
1295#endif /* NO_LOGGING */
1296    }
1297
1298#ifdef MP_DEBUG
1299    fprintf(stderr, "tcp_write: exiting - wrote %ld bytes \n", i);
1300    fflush(stderr);
1301#endif
1302
1303    return len;
1304}
1305
1306
1307
1308/***********************************************************************
1309 * FUNCTION:  tcp_init_transport
1310 * INPUT:     link - pointer to the link structure for this connection
1311 * OUTPUT:    Success:  MP_Success and link structure initialized for the
1312 *                      tcp connection.
1313 *            Failure:  MP_Failure
1314 * OPERATION: If there are no problems, allocate a tcp structure and
1315 *            attach it to the private pointer inside link.
1316 ***********************************************************************/
1317#ifdef __STDC__
1318MP_Status_t tcp_init_transport(MP_Link_pt link)
1319#else
1320MP_Status_t tcp_init_transport(link)
1321    MP_Link_pt link;
1322#endif
1323{
1324    MP_TCP_t *tcp_rec;
1325
1326#ifdef MP_DEBUG
1327    fprintf(stderr, "tcp_init_transport: entering\n"); fflush(stderr);
1328#endif
1329
1330    TEST_LINK_NULL(link);
1331    link->transp.transp_dev =  MP_TcpTransportDev;
1332    link->transp.transp_ops = &tcp_ops;
1333
1334    tcp_rec = (MP_TCP_t*)IMP_MemAllocFnc(sizeof(MP_TCP_t));
1335    TEST_MEM_ALLOC_ERROR(link, tcp_rec);
1336
1337    /* Initialize TCP record. */
1338    tcp_rec->bindsock = INVALID_SOCKET;
1339    tcp_rec->sock     = INVALID_SOCKET;
1340    tcp_rec->peerport = 0;
1341    tcp_rec->myhost   = NULL;
1342    tcp_rec->peerhost = NULL;
1343    tcp_rec->status   = MP_UnknownStatus;
1344    tcp_rec->rsh      = NULL;
1345
1346    link->transp.private1 = (char *)tcp_rec;
1347
1348#ifdef MP_DEBUG
1349    fprintf(stderr, "tcp_init_transport: exiting\n");
1350    fflush(stderr);
1351#endif
1352
1353    RETURN_OK(link);
1354}
1355
1356/***********************************************************************
1357 * FUNCTION:  tcp_close_connection
1358 * INPUT:     link  - pointer to the link structure for this connection
1359 * OUTPUT:    Success:  MP_Success
1360 *                      Release the tcp structure pointed to by private1.
1361 *            Failure:  MP_Failure
1362 * OPERATION:
1363 ***********************************************************************/
1364#ifdef __STDC__
1365MP_Status_t tcp_close_connection(MP_Link_pt link)
1366#else
1367MP_Status_t tcp_close_connection(link)
1368    MP_Link_pt link;
1369#endif
1370{
1371    MP_TCP_t *tcp_rec;
1372
1373#ifdef MP_DEBUG
1374    fprintf(stderr, "tcp_close_connection: entering\n");
1375    fflush(stderr);
1376#endif /* MP_DEBUG */
1377
1378    TEST_LINK_NULL(link);
1379    if (link->transp.private1 == NULL)
1380        return MP_SetError(link, MP_NullTransport);
1381    tcp_rec = (MP_TCP_t*)link->transp.private1;
1382
1383    CLOSE_SOCKET(tcp_rec->sock);
1384    if (tcp_rec->bindsock != INVALID_SOCKET)
1385        CLOSE_SOCKET(tcp_rec->bindsock);
1386
1387    if (tcp_rec->myhost != NULL)
1388        IMP_RawMemFreeFnc(tcp_rec->myhost);
1389    if (tcp_rec->peerhost != NULL)
1390        IMP_RawMemFreeFnc(tcp_rec->peerhost);
1391    if (tcp_rec->rsh != NULL)
1392        IMP_RawMemFreeFnc(tcp_rec->rsh);
1393    IMP_MemFreeFnc(tcp_rec, sizeof(MP_TCP_t));
1394    link->transp.private1 = NULL;
1395
1396#ifdef MP_DEBUG
1397    fprintf(stderr, "tcp_close_connection: exiting\n");
1398    fflush(stderr);
1399#endif /* MP_DEBUG */
1400
1401    RETURN_OK(link);
1402}
1403
1404#ifdef __STDC__
1405MP_Status_t tcp_kill_connection(MP_Link_pt link)
1406#else
1407MP_Status_t tcp_kill_connection(link)
1408    MP_Link_pt link;
1409#endif
1410{
1411    MP_TCP_t *tcp_rec;
1412    int fork_pid = -1;
1413   
1414#ifdef MP_DEBUG
1415    fprintf(stderr, "tcp_kill_connection: entering\n");
1416    fflush(stderr);
1417#endif /* MP_DEBUG */
1418
1419    TEST_LINK_NULL(link);
1420    if (link->transp.private1 == NULL)
1421        return MP_SetError(link, MP_NullTransport);
1422    tcp_rec = (MP_TCP_t*)link->transp.private1;
1423   
1424    if (tcp_rec->mode == MP_LAUNCH_MODE)
1425    {
1426      char  *rsh_argv[5];
1427      char  rsh_kill[20];
1428      rsh_argv[0] = tcp_rec->rsh;
1429      rsh_argv[1] = tcp_rec->peerhost;
1430      rsh_argv[2] = "-n";
1431      sprintf(rsh_kill, "kill -9 %d", tcp_rec->peerpid);
1432      rsh_argv[3] = rsh_kill;
1433      rsh_argv[4] = NULL;
1434
1435      if ((fork_pid = vfork()) == -1)
1436      {
1437        MP_LogEvent(link, MP_ERROR_EVENT,
1438                    "MP_OpenLink: can't fork to kill application");
1439        return MP_SetError(link, MP_Failure);
1440      }
1441
1442      if (! fork_pid)
1443      {
1444        execvp(rsh_argv[0], rsh_argv);
1445        fputs("tcp_kill_connection: execvp failed - bailing out\n", stderr);
1446        fflush(stderr);
1447        _exit(1);
1448      }
1449    }
1450    else if (tcp_rec->mode == MP_FORK_MODE)
1451    {
1452      kill(tcp_rec->peerpid, SIGKILL);
1453    }
1454   
1455    return tcp_close_connection(link);
1456}
1457
1458/***********************************************************************
1459 * FUNCTION:  tcp_open_connection
1460 * INPUT:     link - pointer to link structure for this connection
1461 *            argc - number of arguments in argv
1462 *            argv - arguments as strings
1463 * OUTPUT:    Success:  MP_Success
1464 *                      link established as a tcp connection
1465 *            Failure:  MP_Failure
1466 * OPERATION: Determine the tcp mode we are using and attempt to
1467 *            establish a connection accordingly.  We could fail
1468 *            for any of a number of reasons.  Bad hostname, bad
1469 *            port number, missing arguments....
1470 ***********************************************************************/
1471#ifdef __STDC__
1472MP_Status_t tcp_open_connection(MP_Link_pt   link,
1473                                int          argc,
1474                                char       **argv)
1475#else
1476MP_Status_t tcp_open_connection(link, argc, argv)
1477    MP_Link_pt   link;
1478    int          argc;
1479    char       **argv;
1480#endif
1481{
1482    MP_Status_t status;
1483    struct protoent *protostruct = NULL;
1484    int sockopt; 
1485
1486#ifdef MP_DEBUG
1487    fprintf(stderr, "tcp_open_connection: entering\n");
1488    fflush(stderr);
1489#endif
1490
1491    TEST_LINK_NULL(link);
1492    if (tcp_init_transport(link) != MP_Success)
1493        return MP_Failure;
1494
1495    ((MP_TCP_t *)link->transp.private1)->mode = get_tcp_mode(argc, argv);
1496    switch (((MP_TCP_t *)link->transp.private1)->mode)
1497    {
1498    case MP_LISTEN_MODE:
1499        status = open_tcp_listen_mode(link, argc, argv);
1500        if (status != MP_Success)
1501            break;
1502        status = socket_accept_blocking(link,
1503                        &((MP_TCP_t *)link->transp.private1)->sock);
1504        break;
1505
1506    case MP_CONNECT_MODE:
1507        status = open_tcp_connect_mode(link, argc, argv);
1508        break;
1509
1510    case MP_LAUNCH_MODE:
1511        status = open_tcp_launch_mode(link, argc, argv);
1512        break;
1513
1514        case MP_FORK_MODE:
1515          status = open_tcp_fork_mode(link, argc, argv);
1516          break;
1517         
1518    default:
1519        MP_LogEvent(link, MP_ERROR_EVENT,
1520                    "Can't open connection, unknown -MPmode argument");
1521        return MP_SetError(link, MP_Failure);
1522    }
1523
1524    if (status != MP_Success)
1525        return MP_Failure;
1526
1527    /* get the protocol number for TCP from /etc/protocols */
1528    if ((protostruct = getprotobyname("tcp")) == NULL) {
1529      MP_LogEvent(link, MP_ERROR_EVENT,
1530                    "Can't open connection: can't get TCP protocol info!!");
1531      return MP_SetError(link, MP_Failure);
1532      }
1533
1534    /* set socket to not coalesce small amounts of data in a single packet */
1535    if (setsockopt(((MP_TCP_t *)link->transp.private1)->sock, 
1536                   protostruct->p_proto, TCP_NODELAY, &sockopt, sizeof(int)) == -1) {
1537      MP_LogEvent(link, MP_ERROR_EVENT,
1538                 "Can't open connection: can't set NODELAY option on socket"); 
1539      return MP_SetError(link, MP_Failure);
1540      }
1541
1542    ERR_CHK(tcp_negotiate_word_order(link));
1543    ERR_CHK(tcp_negotiate_fp_format(link));
1544    ERR_CHK(tcp_negotiate_bigint_format(link));
1545    ERR_CHK(tcp_exchange_pids(link));
1546
1547#ifdef MP_DEBUG
1548    fprintf(stderr, "tcp_open_connection: exiting\n");
1549    fflush(stderr);
1550#endif
1551
1552    RETURN_OK(link);
1553}
1554
1555
1556#ifdef __STDC__
1557static MP_Status_t tcp_exchange_pids(MP_Link_pt link)
1558#else
1559static MP_Status_t tcp_exchange_pids(link)
1560    MP_Link_pt link;
1561#endif
1562{
1563  MP_Uint32_t peerpid;
1564  MP_NumAnnot_t na;
1565
1566  /* Hmm... should communicate pid_t -- could be 64 bit !! */
1567  ERR_CHK(MP_PutUint32Packet(link, (MP_Uint32_t) getpid(), 0));
1568  MP_EndMsgReset(link);
1569 
1570  ERR_CHK(MP_SkipMsg(link));
1571  ERR_CHK(MP_GetUint32Packet(link, &peerpid, &na));
1572  ((MP_TCP_t *)link->transp.private1)->peerpid = peerpid;
1573
1574  return MP_Success;
1575}
1576
1577/*
1578 * Currently this is a canned routine.  There is little parsing.  It
1579 * assumes that the first thing the two partners will do is to negotiate
1580 * the word order.  If that is a correct assumption, this should work
1581 * like a champ, otherwise we are in trouble.
1582 */
1583#ifdef __STDC__
1584static MP_Status_t tcp_negotiate_word_order(MP_Link_pt link)
1585#else
1586static MP_Status_t tcp_negotiate_word_order(link)
1587    MP_Link_pt link;
1588#endif
1589{
1590   MP_DictTag_t   dtag;
1591   MP_NumAnnot_t  na;
1592   MP_NumChild_t  nc;
1593   unsigned long  req_word_order;
1594   MP_Common_t    mp_op;
1595
1596   /*
1597    * Step 1:  We send our word order preference to our partner
1598    */
1599   ERR_CHK(MP_PutCommonOperatorPacket(link, MP_MpDict, 
1600              MP_CopMpByteOrderRequest, 0, 1)); 
1601   ERR_CHK(IMP_PutUint32(link, (unsigned long)link->env->native_word_order));
1602   MP_EndMsgReset(link);
1603
1604   /*
1605    * Step 2: Get a reply.  It should be MP_CopMpByteOrderRequest.
1606    * Probably should peek at the data stream here to be sure we are
1607    * getting the right thing.
1608    */
1609   ERR_CHK(MP_SkipMsg(link));
1610   ERR_CHK(MP_GetCommonOperatorPacket(link, &dtag, &mp_op, &na, &nc));
1611   ERR_CHK(IMP_GetUint32(link, &req_word_order));
1612
1613   if (mp_op != MP_CopMpByteOrderRequest || dtag != MP_MpDict) {
1614      MP_LogEvent(link, MP_ERROR_EVENT, 
1615          "Problem exchanging initialization information: byte order");
1616      return MP_Failure;
1617   }
1618
1619
1620   /*
1621    * We only have to do something if the word order has changed.
1622    * This will only happen if both parties are Little Endian
1623    */
1624   if (req_word_order == (unsigned long)link->env->native_word_order
1625       && link->env->native_word_order != link->link_word_order) {
1626       link->link_word_order = link->env->native_word_order;
1627       MP_LogEvent(link, MP_INIT_EVENT,
1628                   "Link word order changed to Little Endian");
1629   }
1630
1631   return MP_Success;
1632}
1633
1634
1635
1636#ifdef __STDC__
1637static MP_Status_t tcp_negotiate_fp_format(MP_Link_pt link)
1638#else
1639static MP_Status_t tcp_negotiate_fp_format(link)
1640    MP_Link_pt link;
1641#endif
1642{
1643    MP_DictTag_t   dtag;
1644    MP_NumAnnot_t  na;
1645    MP_NumChild_t  nc;
1646    unsigned long  req_fp_format;
1647    MP_Common_t    mp_op;
1648
1649
1650#ifdef MP_DEBUG
1651    printf("tcp_negotiate_fp_format: entering\n");
1652#endif
1653
1654    /*
1655     * Step 1:  We send our fp format preference to our partner
1656     */
1657    ERR_CHK(MP_PutCommonOperatorPacket(link, MP_MpDict, 
1658                                       MP_CopMpFpFormatRequest, 0, 1));
1659    ERR_CHK(IMP_PutUint32(link, (unsigned long)link->env->native_fp_format));
1660    MP_EndMsgReset(link);
1661
1662    /*
1663     * Step 2: Get a reply.  It should be "MP_FpFormatRequest"
1664     * probably should peek at the data stream here to be sure we are
1665     *  getting the right thing
1666     */
1667    ERR_CHK(MP_SkipMsg(link));
1668    ERR_CHK(MP_GetCommonOperatorPacket(link, &dtag, &mp_op, &na, &nc));
1669    ERR_CHK(IMP_GetUint32(link, &req_fp_format));
1670
1671   if (mp_op != MP_CopMpFpFormatRequest || dtag != MP_MpDict) {
1672      MP_LogEvent(link, MP_ERROR_EVENT, 
1673          "Problem exchanging initialization information: fp format");
1674      return MP_Failure;
1675   }
1676
1677    /*
1678     * We only have to do something if the fp format has changed.
1679     * This will only happen if both parties are something other than IEEE.
1680     */
1681    if (req_fp_format == (unsigned long)link->env->native_fp_format
1682        && link->env->native_fp_format != link->link_fp_format) {
1683        link->link_fp_format = link->env->native_fp_format;
1684        MP_LogEvent(link, MP_INIT_EVENT,
1685                    "Link floating point format changed to VAX");
1686    }
1687
1688#ifdef MP_DEBUG
1689    printf("tcp_negotiate_fp_format: exiting\n");
1690#endif
1691
1692    return MP_Success;
1693}
1694
1695
1696
1697#ifdef __STDC__
1698static MP_Status_t tcp_negotiate_bigint_format(MP_Link_pt link)
1699#else
1700static MP_Status_t tcp_negotiate_bigint_format(link)
1701    MP_Link_pt link;
1702#endif
1703{
1704    MP_DictTag_t   dtag;
1705    MP_NumAnnot_t  na;
1706    MP_NumChild_t  nc;
1707    unsigned long  req_bigint_format;
1708    MP_Common_t    mp_op;
1709
1710#ifdef MP_DEBUG
1711    printf("tcp_negotiate_bigint_format: entering\n");
1712#endif
1713
1714    /*
1715     * Step 1:  We send our bigint format preference to our partner
1716     */
1717    ERR_CHK(MP_PutCommonOperatorPacket(link, MP_MpDict, 
1718                                       MP_CopMpBigIntFormatRequest, 0, 1));
1719    ERR_CHK(IMP_PutUint32(link, (unsigned long)link->link_bigint_format));
1720    MP_EndMsgReset(link);
1721
1722    /*
1723     * Step 2: Get a reply.  It should be "MP_BigIntFormatRequest"
1724     * probably should peek at the data stream here to be sure we are
1725     * getting the right thing
1726     */
1727    ERR_CHK(MP_SkipMsg(link));
1728    ERR_CHK(MP_GetCommonOperatorPacket(link, &dtag, &mp_op, &na, &nc));
1729    ERR_CHK(IMP_GetUint32(link, &req_bigint_format));
1730
1731    if (mp_op != MP_CopMpBigIntFormatRequest || dtag != MP_MpDict) {
1732      MP_LogEvent(link, MP_ERROR_EVENT, 
1733          "Problem exchanging initialization information: big int format");
1734      return MP_Failure;
1735    }
1736
1737    /*
1738     * We only have to do something if both ends are not already the same
1739     */
1740    if (req_bigint_format != (unsigned long)link->link_bigint_format) {
1741        link->link_bigint_format = MP_GMP;
1742        MP_LogEvent(link, MP_INIT_EVENT,
1743                    "Link multiple precision integer format set to GMP");
1744    }
1745
1746#ifdef MP_DEBUG
1747    printf("tcp_negotiate_bigint_format: exiting\n");
1748#endif
1749
1750    return MP_Success;
1751}
1752
1753
1754
1755#ifdef __STDC__
1756MP_Status_t tcp_get_port(SOCKET *sock, short *port)
1757#else
1758MP_Status_t tcp_get_port(sock, port)
1759    SOCKET *s;
1760    short  *port;
1761#endif
1762{
1763    SOCKADDR_IN isin;
1764    int         attempts = 0;
1765
1766    if (*port < IPPORT_RESERVED)
1767        *port = IPPORT_RESERVED;
1768
1769    if ((*sock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
1770        return MP_Failure;
1771
1772    /*
1773     * Initialize socket's address structure
1774     */
1775    isin.sin_family      = AF_INET;
1776    isin.sin_addr.s_addr = htonl(INADDR_ANY);
1777    isin.sin_port        = htons(*port);
1778
1779    while (bind(*sock, (SOCKADDR *)&isin, sizeof(isin)) == SOCKET_ERROR) {
1780        if (LASTERROR == ERRORCODE(EADDRINUSE)) {
1781            (*port)++;
1782            /* since port is an unsigned short, it may wrap around */
1783            if (*port < IPPORT_RESERVED)
1784                *port = IPPORT_RESERVED;
1785            isin.sin_port = htons(*port);
1786            attempts = 0;
1787        }
1788
1789        if (attempts++ > MP_MAX_BIND_ATTEMPTS)
1790            return MP_Failure;
1791    }
1792
1793    return MP_Success;
1794}
1795
1796
1797
1798#ifdef __WIN32__
1799/*
1800 * WinSockInitialize calls the WinSock API initialization function WSAStartup
1801 * and returns a value != 0 if something went wrong. Extended error information
1802 * can be retrieved later by calling WSAGetLastError();
1803 */
1804#ifdef __STDC__
1805int WinSockInitialize(void)
1806#else /* __STDC__ */
1807int WinSockInitialize()
1808#endif /* __STDC__ */
1809{
1810   WORD        wVersionRequired;
1811   WSADATA     wsaData;
1812
1813   wVersionRequired = MAKEWORD(1, 1);
1814   return WSAStartup(wVersionRequired, &wsaData);
1815}
1816
1817
1818
1819/*
1820 * splitargs splits a string of words separated by white-space characters into
1821 * multiple strings by inserting Null characters after the ends of the words.
1822 * The function returns the number of words found in the string.
1823 */
1824#ifdef __STDC__
1825static int splitargs(char *argstr)
1826#else /* __STDC__ */
1827static int splitargs(argstr)
1828   char *argstr;
1829#endif /* __STDC__ */
1830{
1831    char *p, *q;
1832    int   argcnt, wordflag, quoted;
1833
1834    p = q = argstr;
1835    argcnt = wordflag = quoted = 0;
1836
1837    /* Step through all characters in the string. */
1838    while (*p)
1839        switch (*p) {
1840            /* Is *p a space or a tab character? */
1841        case ' ':
1842        case '\t':
1843            /* Simply pass it on if it is part of a `quoted` string. */
1844            if (quoted) {
1845                *q++ = *p++;
1846                break;
1847            }
1848
1849            /*
1850             * If the character marks the end of a word then terminate that
1851             * word with a Null character, mark the word as read and increase
1852             * the argument counter.
1853             */
1854            if (wordflag) {
1855                *q++ = '\0';
1856                wordflag = 0;
1857                ++argcnt;
1858            }
1859            p++;
1860            break;
1861
1862            /* Is *p a backquote character? */
1863        case '`':
1864
1865            /* If the next character is another backquote then regard the
1866             * combination of both as a single literal backquote and pass it
1867             * on to the result string. If not, set the quote mode, i.e. pass
1868             * on all following characters until the next backquote is found or
1869             * the end of the string reached.
1870             */
1871            if (*++p == '`') {
1872                *q++ = *p++;
1873                wordflag = 1;
1874            } else
1875                quoted = !quoted;
1876            break;
1877
1878            /* Put all other characters into the result string. */
1879        default:
1880            wordflag = 1;
1881            *q++ = *p++;
1882            break;
1883        }
1884
1885    /* Terminate the last word with a Null character. */
1886    if (wordflag) {
1887        ++argcnt;
1888        *q = '\0';
1889    }
1890
1891    return argcnt;
1892}
1893
1894#endif /* __WIN32__ */
1895
1896
Note: See TracBrowser for help on using the repository browser.