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