xref: /petsc/src/sys/classes/viewer/impls/socket/send.c (revision 811af0c4b09a35de4306c442f88bd09fdc09897d)
1 
2 #include <petscsys.h>
3 
4 #if defined(PETSC_NEEDS_UTYPE_TYPEDEFS)
5 /* Some systems have inconsistent include files that use but do not
6    ensure that the following definitions are made */
7 typedef unsigned char  u_char;
8 typedef unsigned short u_short;
9 typedef unsigned short ushort;
10 typedef unsigned int   u_int;
11 typedef unsigned long  u_long;
12 #endif
13 
14 #include <errno.h>
15 #include <ctype.h>
16 #if defined(PETSC_HAVE_MACHINE_ENDIAN_H)
17 #include <machine/endian.h>
18 #endif
19 #if defined(PETSC_HAVE_UNISTD_H)
20 #include <unistd.h>
21 #endif
22 #if defined(PETSC_HAVE_SYS_SOCKET_H)
23 #include <sys/socket.h>
24 #endif
25 #if defined(PETSC_HAVE_SYS_WAIT_H)
26 #include <sys/wait.h>
27 #endif
28 #if defined(PETSC_HAVE_NETINET_IN_H)
29 #include <netinet/in.h>
30 #endif
31 #if defined(PETSC_HAVE_NETDB_H)
32 #include <netdb.h>
33 #endif
34 #if defined(PETSC_HAVE_FCNTL_H)
35 #include <fcntl.h>
36 #endif
37 #if defined(PETSC_HAVE_IO_H)
38 #include <io.h>
39 #endif
40 #if defined(PETSC_HAVE_WINSOCK2_H)
41 #include <Winsock2.h>
42 #endif
43 #include <sys/stat.h>
44 #include <../src/sys/classes/viewer/impls/socket/socket.h>
45 
46 #if defined(PETSC_NEED_CLOSE_PROTO)
47 PETSC_EXTERN int close(int);
48 #endif
49 #if defined(PETSC_NEED_SOCKET_PROTO)
50 PETSC_EXTERN int socket(int, int, int);
51 #endif
52 #if defined(PETSC_NEED_SLEEP_PROTO)
53 PETSC_EXTERN int sleep(unsigned);
54 #endif
55 #if defined(PETSC_NEED_CONNECT_PROTO)
56 PETSC_EXTERN int connect(int, struct sockaddr *, int);
57 #endif
58 
59 /*--------------------------------------------------------------*/
60 static PetscErrorCode PetscViewerDestroy_Socket(PetscViewer viewer) {
61   PetscViewer_Socket *vmatlab = (PetscViewer_Socket *)viewer->data;
62   PetscErrorCode      ierr;
63 
64   PetscFunctionBegin;
65   if (vmatlab->port) {
66 #if defined(PETSC_HAVE_CLOSESOCKET)
67     ierr = closesocket(vmatlab->port);
68 #else
69     ierr = close(vmatlab->port);
70 #endif
71     PetscCheck(!ierr, PETSC_COMM_SELF, PETSC_ERR_SYS, "System error closing socket");
72   }
73   PetscCall(PetscFree(vmatlab));
74   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerBinarySetSkipHeader_C", NULL));
75   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerBinaryGetSkipHeader_C", NULL));
76   PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerBinaryGetFlowControl_C", NULL));
77   PetscFunctionReturn(0);
78 }
79 
80 /*--------------------------------------------------------------*/
81 /*@C
82     PetscSocketOpen - handles connected to an open port where someone is waiting.
83 
84     Input Parameters:
85 +    url - for example www.mcs.anl.gov
86 -    portnum - for example 80
87 
88     Output Parameter:
89 .    t - the socket number
90 
91     Notes:
92     Use close() to close the socket connection
93 
94     Use read() or `PetscHTTPRequest()` to read from the socket
95 
96     Level: advanced
97 
98 .seealso: `PetscSocketListen()`, `PetscSocketEstablish()`, `PetscHTTPRequest()`, `PetscHTTPSConnect()`
99 @*/
100 PetscErrorCode PetscOpenSocket(const char hostname[], int portnum, int *t) {
101   struct sockaddr_in sa;
102   struct hostent    *hp;
103   int                s      = 0;
104   PetscBool          flg    = PETSC_TRUE;
105   static int         refcnt = 0;
106 
107   PetscFunctionBegin;
108   if (!(hp = gethostbyname(hostname))) {
109     perror("SEND: error gethostbyname: ");
110     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SYS, "system error open connection to %s", hostname);
111   }
112   PetscCall(PetscMemzero(&sa, sizeof(sa)));
113   PetscCall(PetscMemcpy(&sa.sin_addr, hp->h_addr_list[0], hp->h_length));
114 
115   sa.sin_family = hp->h_addrtype;
116   sa.sin_port   = htons((u_short)portnum);
117   while (flg) {
118     if ((s = socket(hp->h_addrtype, SOCK_STREAM, 0)) < 0) {
119       perror("SEND: error socket");
120       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SYS, "system error");
121     }
122     if (connect(s, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
123 #if defined(PETSC_HAVE_WSAGETLASTERROR)
124       ierr = WSAGetLastError();
125       if (ierr == WSAEADDRINUSE) (*PetscErrorPrintf)("SEND: address is in use\n");
126       else if (ierr == WSAEALREADY) (*PetscErrorPrintf)("SEND: socket is non-blocking \n");
127       else if (ierr == WSAEISCONN) {
128         (*PetscErrorPrintf)("SEND: socket already connected\n");
129         Sleep((unsigned)1);
130       } else if (ierr == WSAECONNREFUSED) {
131         /* (*PetscErrorPrintf)("SEND: forcefully rejected\n"); */
132         Sleep((unsigned)1);
133       } else {
134         perror(NULL);
135         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SYS, "system error");
136       }
137 #else
138       if (errno == EADDRINUSE) (*PetscErrorPrintf)("SEND: address is in use\n");
139       else if (errno == EALREADY) (*PetscErrorPrintf)("SEND: socket is non-blocking \n");
140       else if (errno == EISCONN) {
141         (*PetscErrorPrintf)("SEND: socket already connected\n");
142         sleep((unsigned)1);
143       } else if (errno == ECONNREFUSED) {
144         refcnt++;
145         PetscCheck(refcnt <= 5, PETSC_COMM_SELF, PETSC_ERR_SYS, "Connection refused by remote host %s port %d", hostname, portnum);
146         PetscCall(PetscInfo(NULL, "Connection refused in attaching socket, trying again\n"));
147         sleep((unsigned)1);
148       } else {
149         perror(NULL);
150         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SYS, "system error");
151       }
152 #endif
153       flg = PETSC_TRUE;
154 #if defined(PETSC_HAVE_CLOSESOCKET)
155       closesocket(s);
156 #else
157       close(s);
158 #endif
159     } else flg = PETSC_FALSE;
160   }
161   *t = s;
162   PetscFunctionReturn(0);
163 }
164 
165 /*@C
166    PetscSocketEstablish - starts a listener on a socket
167 
168    Input Parameters:
169 .    portnumber - the port to wait at
170 
171    Output Parameters:
172 .     ss - the socket to be used with `PetscSocketListen()`
173 
174     Level: advanced
175 
176 .seealso: `PetscSocketListen()`, `PetscOpenSocket()`
177 @*/
178 PETSC_INTERN PetscErrorCode PetscSocketEstablish(int portnum, int *ss) {
179   static size_t      MAXHOSTNAME = 100;
180   char               myname[MAXHOSTNAME + 1];
181   int                s;
182   struct sockaddr_in sa;
183   struct hostent    *hp;
184 
185   PetscFunctionBegin;
186   PetscCall(PetscGetHostName(myname, sizeof(myname)));
187 
188   PetscCall(PetscMemzero(&sa, sizeof(struct sockaddr_in)));
189 
190   hp = gethostbyname(myname);
191   PetscCheck(hp, PETSC_COMM_SELF, PETSC_ERR_SYS, "Unable to get hostent information from system");
192 
193   sa.sin_family = hp->h_addrtype;
194   sa.sin_port   = htons((u_short)portnum);
195 
196   PetscCheck((s = socket(AF_INET, SOCK_STREAM, 0)) >= 0, PETSC_COMM_SELF, PETSC_ERR_SYS, "Error running socket() command");
197 #if defined(PETSC_HAVE_SO_REUSEADDR)
198   {
199     int optval = 1; /* Turn on the option */
200     PetscCall(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&optval, sizeof(optval)));
201   }
202 #endif
203 
204   while (bind(s, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
205 #if defined(PETSC_HAVE_WSAGETLASTERROR)
206     ierr = WSAGetLastError();
207     if (ierr != WSAEADDRINUSE) {
208 #else
209     if (errno != EADDRINUSE) {
210 #endif
211       close(s);
212       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SYS, "Error from bind()");
213     }
214   }
215   listen(s, 0);
216   *ss = s;
217   return (0);
218 }
219 
220 /*@C
221    PetscSocketListen - Listens at a socket created with `PetscSocketEstablish()`
222 
223    Input Parameter:
224 .    listenport - obtained with `PetscSocketEstablish()`
225 
226    Output Parameter:
227 .     t - pass this to read() to read what is passed to this connection
228 
229     Level: advanced
230 
231 .seealso: `PetscSocketEstablish()`
232 @*/
233 PETSC_INTERN PetscErrorCode PetscSocketListen(int listenport, int *t) {
234   struct sockaddr_in isa;
235 #if defined(PETSC_HAVE_ACCEPT_SIZE_T)
236   size_t i;
237 #else
238   int i;
239 #endif
240 
241   PetscFunctionBegin;
242   /* wait for someone to try to connect */
243   i = sizeof(struct sockaddr_in);
244   PetscCheck((*t = accept(listenport, (struct sockaddr *)&isa, (socklen_t *)&i)) >= 0, PETSC_COMM_SELF, PETSC_ERR_SYS, "error from accept()");
245   PetscFunctionReturn(0);
246 }
247 
248 /*@C
249    PetscViewerSocketOpen - Opens a connection to a MATLAB or other socket based server.
250 
251    Collective
252 
253    Input Parameters:
254 +  comm - the MPI communicator
255 .  machine - the machine the server is running on,, use NULL for the local machine, use "server" to passively wait for
256              a connection from elsewhere
257 -  port - the port to connect to, use `PETSC_DEFAULT` for the default
258 
259    Output Parameter:
260 .  lab - a context to use when communicating with the server
261 
262    Options Database Keys:
263    For use with  `PETSC_VIEWER_SOCKET_WORLD`, `PETSC_VIEWER_SOCKET_SELF`,
264    `PETSC_VIEWER_SOCKET_()` or if
265     NULL is passed for machine or PETSC_DEFAULT is passed for port
266 $    -viewer_socket_machine <machine>
267 $    -viewer_socket_port <port>
268 
269    Environmental variables:
270 +   `PETSC_VIEWER_SOCKET_PORT` - portnumber
271 -   `PETSC_VIEWER_SOCKET_MACHINE` - machine name
272 
273    Level: intermediate
274 
275    Notes:
276    Most users should employ the following commands to access the
277    MATLAB `PetscViewer`
278 $
279 $    PetscViewerSocketOpen(MPI_Comm comm, char *machine,int port,PetscViewer &viewer)
280 $    MatView(Mat matrix,PetscViewer viewer)
281 $
282 $                or
283 $
284 $    PetscViewerSocketOpen(MPI_Comm comm,char *machine,int port,PetscViewer &viewer)
285 $    VecView(Vec vector,PetscViewer viewer)
286 
287      Currently the only socket client available is MATLAB. See
288      src/dm/tests/ex12.c and ex12.m for an example of usage.
289 
290     The socket viewer is in some sense a subclass of the binary viewer, to read and write to the socket
291     use `PetscViewerBinaryRead()`, `PetscViewerBinaryWrite()`, `PetscViewerBinarWriteStringArray()`, `PetscViewerBinaryGetDescriptor()`.
292 
293      Use this for communicating with an interactive MATLAB session, see `PETSC_VIEWER_MATLAB_()` for writing output to a
294      .mat file. Use `PetscMatlabEngineCreate()` or `PETSC_MATLAB_ENGINE_()`, `PETSC_MATLAB_ENGINE_SELF`, or `PETSC_MATLAB_ENGINE_WORLD`
295      for communicating with a MATLAB Engine
296 
297 .seealso: `PETSCVIEWERBINARY`, `PETSCVIEWERSOCKET`, `MatView()`, `VecView()`, `PetscViewerDestroy()`, `PetscViewerCreate()`, `PetscViewerSetType()`,
298           `PetscViewerSocketSetConnection()`, `PETSC_VIEWER_SOCKET_`, `PETSC_VIEWER_SOCKET_WORLD`,
299           `PETSC_VIEWER_SOCKET_SELF`, `PetscViewerBinaryWrite()`, `PetscViewerBinaryRead()`, `PetscViewerBinaryWriteStringArray()`,
300           `PetscBinaryViewerGetDescriptor()`, `PetscMatlabEngineCreate()`
301 @*/
302 PetscErrorCode PetscViewerSocketOpen(MPI_Comm comm, const char machine[], int port, PetscViewer *lab) {
303   PetscFunctionBegin;
304   PetscCall(PetscViewerCreate(comm, lab));
305   PetscCall(PetscViewerSetType(*lab, PETSCVIEWERSOCKET));
306   PetscCall(PetscViewerSocketSetConnection(*lab, machine, port));
307   PetscFunctionReturn(0);
308 }
309 
310 static PetscErrorCode PetscViewerSetFromOptions_Socket(PetscViewer v, PetscOptionItems *PetscOptionsObject) {
311   PetscInt  def = -1;
312   char      sdef[256];
313   PetscBool tflg;
314 
315   PetscFunctionBegin;
316   /*
317        These options are not processed here, they are processed in PetscViewerSocketSetConnection(), they
318     are listed here for the GUI to display
319   */
320   PetscOptionsHeadBegin(PetscOptionsObject, "Socket PetscViewer Options");
321   PetscCall(PetscOptionsGetenv(PetscObjectComm((PetscObject)v), "PETSC_VIEWER_SOCKET_PORT", sdef, 16, &tflg));
322   if (tflg) {
323     PetscCall(PetscOptionsStringToInt(sdef, &def));
324   } else def = PETSCSOCKETDEFAULTPORT;
325   PetscCall(PetscOptionsInt("-viewer_socket_port", "Port number to use for socket", "PetscViewerSocketSetConnection", def, NULL, NULL));
326 
327   PetscCall(PetscOptionsString("-viewer_socket_machine", "Machine to use for socket", "PetscViewerSocketSetConnection", sdef, NULL, sizeof(sdef), NULL));
328   PetscCall(PetscOptionsGetenv(PetscObjectComm((PetscObject)v), "PETSC_VIEWER_SOCKET_MACHINE", sdef, sizeof(sdef), &tflg));
329   if (!tflg) PetscCall(PetscGetHostName(sdef, sizeof(sdef)));
330   PetscOptionsHeadEnd();
331   PetscFunctionReturn(0);
332 }
333 
334 static PetscErrorCode PetscViewerBinaryGetSkipHeader_Socket(PetscViewer viewer, PetscBool *skip) {
335   PetscViewer_Socket *vsocket = (PetscViewer_Socket *)viewer->data;
336 
337   PetscFunctionBegin;
338   *skip = vsocket->skipheader;
339   PetscFunctionReturn(0);
340 }
341 
342 static PetscErrorCode PetscViewerBinarySetSkipHeader_Socket(PetscViewer viewer, PetscBool skip) {
343   PetscViewer_Socket *vsocket = (PetscViewer_Socket *)viewer->data;
344 
345   PetscFunctionBegin;
346   vsocket->skipheader = skip;
347   PetscFunctionReturn(0);
348 }
349 
350 static PetscErrorCode PetscViewerBinaryGetFlowControl_Socket(PetscViewer viewer, PetscInt *fc) {
351   PetscFunctionBegin;
352   *fc = 0;
353   PetscFunctionReturn(0);
354 }
355 
356 /*MC
357    PETSCVIEWERSOCKET - A viewer that writes to a Unix socket
358 
359   Level: beginner
360 
361 .seealso: `PETSC_VIEWERBINARY`, `PetscViewerSocketOpen()`, `PetscViewerDrawOpen()`, `PETSC_VIEWER_DRAW_()`, `PETSC_VIEWER_DRAW_SELF`, `PETSC_VIEWER_DRAW_WORLD`,
362           `PetscViewerCreate()`, `PetscViewerASCIIOpen()`, `PetscViewerBinaryOpen()`, `PETSCVIEWERBINARY`, `PETSCVIEWERDRAW`,
363           `PetscViewerMatlabOpen()`, `VecView()`, `DMView()`, `PetscViewerMatlabPutArray()`, `PETSCVIEWERASCII`, `PETSCVIEWERMATLAB`,
364           `PetscViewerFileSetName()`, `PetscViewerFileSetMode()`, `PetscViewerFormat`, `PetscViewerType`, `PetscViewerSetType()`
365 M*/
366 
367 PETSC_EXTERN PetscErrorCode PetscViewerCreate_Socket(PetscViewer v) {
368   PetscViewer_Socket *vmatlab;
369 
370   PetscFunctionBegin;
371   PetscCall(PetscNewLog(v, &vmatlab));
372   vmatlab->port          = 0;
373   v->data                = (void *)vmatlab;
374   v->ops->destroy        = PetscViewerDestroy_Socket;
375   v->ops->flush          = NULL;
376   v->ops->setfromoptions = PetscViewerSetFromOptions_Socket;
377 
378   /* lie and say this is a binary viewer; then all the XXXView_Binary() methods will work correctly on it */
379   PetscCall(PetscObjectChangeTypeName((PetscObject)v, PETSCVIEWERBINARY));
380   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerBinarySetSkipHeader_C", PetscViewerBinarySetSkipHeader_Socket));
381   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerBinaryGetSkipHeader_C", PetscViewerBinaryGetSkipHeader_Socket));
382   PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerBinaryGetFlowControl_C", PetscViewerBinaryGetFlowControl_Socket));
383 
384   PetscFunctionReturn(0);
385 }
386 
387 /*@C
388       PetscViewerSocketSetConnection - Sets the machine and port that a PETSc socket
389              viewer is to use
390 
391   Logically Collective on v
392 
393   Input Parameters:
394 +   v - viewer to connect
395 .   machine - host to connect to, use NULL for the local machine,use "server" to passively wait for
396              a connection from elsewhere
397 -   port - the port on the machine one is connecting to, use `PETSC_DEFAULT` for default
398 
399     Level: advanced
400 
401 .seealso: `PETSCVIEWERMATLAB`, `PETSCVIEWERSOCKET`, `PetscViewerSocketOpen()`
402 @*/
403 PetscErrorCode PetscViewerSocketSetConnection(PetscViewer v, const char machine[], int port) {
404   PetscMPIInt         rank;
405   char                mach[256];
406   PetscBool           tflg;
407   PetscViewer_Socket *vmatlab;
408 
409   PetscFunctionBegin;
410   PetscValidHeaderSpecific(v, PETSC_VIEWER_CLASSID, 1);
411   if (machine) PetscValidCharPointer(machine, 2);
412   vmatlab = (PetscViewer_Socket *)v->data;
413   /* PetscValidLogicalCollectiveInt(v,port,3); not a PetscInt */
414   if (port <= 0) {
415     char portn[16];
416     PetscCall(PetscOptionsGetenv(PetscObjectComm((PetscObject)v), "PETSC_VIEWER_SOCKET_PORT", portn, 16, &tflg));
417     if (tflg) {
418       PetscInt pport;
419       PetscCall(PetscOptionsStringToInt(portn, &pport));
420       port = (int)pport;
421     } else port = PETSCSOCKETDEFAULTPORT;
422   }
423   if (!machine) {
424     PetscCall(PetscOptionsGetenv(PetscObjectComm((PetscObject)v), "PETSC_VIEWER_SOCKET_MACHINE", mach, sizeof(mach), &tflg));
425     if (!tflg) PetscCall(PetscGetHostName(mach, sizeof(mach)));
426   } else {
427     PetscCall(PetscStrncpy(mach, machine, sizeof(mach)));
428   }
429 
430   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)v), &rank));
431   if (rank == 0) {
432     PetscCall(PetscStrcmp(mach, "server", &tflg));
433     if (tflg) {
434       int listenport;
435       PetscCall(PetscInfo(v, "Waiting for connection from socket process on port %d\n", port));
436       PetscCall(PetscSocketEstablish(port, &listenport));
437       PetscCall(PetscSocketListen(listenport, &vmatlab->port));
438       close(listenport);
439     } else {
440       PetscCall(PetscInfo(v, "Connecting to socket process on port %d machine %s\n", port, mach));
441       PetscCall(PetscOpenSocket(mach, port, &vmatlab->port));
442     }
443   }
444   PetscFunctionReturn(0);
445 }
446 
447 /* ---------------------------------------------------------------------*/
448 /*
449     The variable Petsc_Viewer_Socket_keyval is used to indicate an MPI attribute that
450   is attached to a communicator, in this case the attribute is a PetscViewer.
451 */
452 PetscMPIInt Petsc_Viewer_Socket_keyval = MPI_KEYVAL_INVALID;
453 
454 /*@C
455      PETSC_VIEWER_SOCKET_ - Creates a socket viewer shared by all processors in a communicator.
456 
457      Collective
458 
459      Input Parameter:
460 .    comm - the MPI communicator to share the  `PETSCVIEWERSOCKET` `PetscViewer`
461 
462      Level: intermediate
463 
464    Options Database Keys:
465    For use with the default `PETSC_VIEWER_SOCKET_WORLD` or if
466     NULL is passed for machine or `PETSC_DEFAULT` is passed for port
467 $    -viewer_socket_machine <machine>
468 $    -viewer_socket_port <port>
469 
470    Environmental variables:
471 +   `PETSC_VIEWER_SOCKET_PORT` - portnumber
472 -   `PETSC_VIEWER_SOCKET_MACHINE` - machine name
473 
474      Notes:
475      Unlike almost all other PETSc routines, `PETSC_VIEWER_SOCKET_()` does not return
476      an error code, it returns NULL if it fails. The  `PETSCVIEWERSOCKET`  `PetscViewer` is usually used in the form
477 $       XXXView(XXX object,PETSC_VIEWER_SOCKET_(comm));
478 
479      Currently the only socket client available is MATLAB. See
480      src/dm/tests/ex12.c and ex12.m for an example of usage.
481 
482      Connects to a waiting socket and stays connected until `PetscViewerDestroy()` is called.
483 
484      Use this for communicating with an interactive MATLAB session, see `PETSC_VIEWER_MATLAB_()` for writing output to a
485      .mat file. Use `PetscMatlabEngineCreate()` or `PETSC_MATLAB_ENGINE_()`, `PETSC_MATLAB_ENGINE_SELF`, or `PETSC_MATLAB_ENGINE_WORLD`
486      for communicating with a MATLAB Engine
487 
488 .seealso: `PETSCVIEWERMATLAB`, `PETSCVIEWERSOCKET`, `PETSC_VIEWER_SOCKET_WORLD`, `PETSC_VIEWER_SOCKET_SELF`, `PetscViewerSocketOpen()`, `PetscViewerCreate()`,
489           `PetscViewerSocketSetConnection()`, `PetscViewerDestroy()`, `PETSC_VIEWER_SOCKET_()`, `PetscViewerBinaryWrite()`, `PetscViewerBinaryRead()`,
490           `PetscViewerBinaryWriteStringArray()`, `PetscViewerBinaryGetDescriptor()`, `PETSC_VIEWER_MATLAB_()`
491 @*/
492 PetscViewer PETSC_VIEWER_SOCKET_(MPI_Comm comm) {
493   PetscErrorCode ierr;
494   PetscBool      flg;
495   PetscViewer    viewer;
496   MPI_Comm       ncomm;
497 
498   PetscFunctionBegin;
499   ierr = PetscCommDuplicate(comm, &ncomm, NULL);
500   if (ierr) {
501     PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_SOCKET_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
502     PetscFunctionReturn(NULL);
503   }
504   if (Petsc_Viewer_Socket_keyval == MPI_KEYVAL_INVALID) {
505     ierr = MPI_Comm_create_keyval(MPI_COMM_NULL_COPY_FN, MPI_COMM_NULL_DELETE_FN, &Petsc_Viewer_Socket_keyval, NULL);
506     if (ierr) {
507       PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_SOCKET_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
508       PetscFunctionReturn(NULL);
509     }
510   }
511   ierr = MPI_Comm_get_attr(ncomm, Petsc_Viewer_Socket_keyval, (void **)&viewer, (int *)&flg);
512   if (ierr) {
513     PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_SOCKET_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
514     PetscFunctionReturn(NULL);
515   }
516   if (!flg) { /* PetscViewer not yet created */
517     ierr = PetscViewerSocketOpen(ncomm, NULL, 0, &viewer);
518     if (ierr) {
519       PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_SOCKET_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_REPEAT, " ");
520       PetscFunctionReturn(NULL);
521     }
522     ierr = PetscObjectRegisterDestroy((PetscObject)viewer);
523     if (ierr) {
524       PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_SOCKET_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_REPEAT, " ");
525       PetscFunctionReturn(NULL);
526     }
527     ierr = MPI_Comm_set_attr(ncomm, Petsc_Viewer_Socket_keyval, (void *)viewer);
528     if (ierr) {
529       PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_SOCKET_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
530       PetscFunctionReturn(NULL);
531     }
532   }
533   ierr = PetscCommDestroy(&ncomm);
534   if (ierr) {
535     PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_VIEWER_SOCKET_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_REPEAT, " ");
536     PetscFunctionReturn(NULL);
537   }
538   PetscFunctionReturn(viewer);
539 }
540