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