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