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