1 /* SOCKDEV.C (c) Copyright Malcolm Beattie, 2001 */
2 /* Hercules socket device support */
3
4 #include "hstdinc.h"
5 #include "hercules.h"
6 #include "opcode.h"
7
8
9 #if defined(WIN32) && defined(OPTION_DYNAMIC_LOAD) && !defined(HDL_USE_LIBTOOL) && !defined(_MSVC_)
10 extern SYSBLK *psysblk;
11 #define sysblk (*psysblk)
12 #endif
13
14
15 /*===================================================================*/
16 /* S o c k e t D e v i c e s ... */
17 /*===================================================================*/
18
19 // #define DEBUG_SOCKDEV
20
21 #ifdef DEBUG_SOCKDEV
22 #define logdebug logmsg
23 #else
24 #define logdebug 1 ? ((void)0) : logmsg
25 #endif
26
27 /*-------------------------------------------------------------------*/
28 /* Working storage */
29 /*-------------------------------------------------------------------*/
30
31 static int init_done = FALSE;
32
33 static LIST_ENTRY bind_head; /* (bind_struct list anchor) */
34 static LOCK bind_lock; /* (lock for accessing list) */
35
36 /*-------------------------------------------------------------------*/
37 /* Initialization / termination functions... */
38 /*-------------------------------------------------------------------*/
39
40 static void init_sockdev ( void );
41 static void term_sockdev ( void* );
42
init_sockdev(void)43 static void init_sockdev ( void )
44 {
45 if (init_done) return;
46 InitializeListHead( &bind_head );
47 initialize_lock( &bind_lock );
48 hdl_adsc( "term_sockdev", term_sockdev, NULL );
49 init_done = TRUE;
50 }
51
term_sockdev(void * arg)52 static void term_sockdev ( void* arg )
53 {
54 UNREFERENCED( arg );
55 if (!init_done) init_sockdev();
56 SIGNAL_SOCKDEV_THREAD();
57 join_thread ( sysblk.socktid, NULL );
58 detach_thread ( sysblk.socktid );
59 }
60
61 /*-------------------------------------------------------------------*/
62 /* unix_socket create and bind a Unix domain socket */
63 /*-------------------------------------------------------------------*/
64
unix_socket(char * path)65 int unix_socket (char* path)
66 {
67 #if !defined( HAVE_SYS_UN_H )
68 UNREFERENCED(path);
69 logmsg (_("HHCSD024E This build does not support Unix domain sockets.\n") );
70 return -1;
71 #else // defined( HAVE_SYS_UN_H )
72
73 struct sockaddr_un addr;
74 int sd;
75
76 logdebug ("unix_socket(%s)\n", path);
77
78 if (strlen (path) > sizeof(addr.sun_path) - 1)
79 {
80 logmsg (_("HHCSD008E Socket pathname \"%s\" exceeds limit of %d\n"),
81 path, (int) sizeof(addr.sun_path) - 1);
82 return -1;
83 }
84
85 addr.sun_family = AF_UNIX;
86 strcpy (addr.sun_path, path); /* guaranteed room by above check */
87 sd = socket (PF_UNIX, SOCK_STREAM, 0);
88
89 if (sd == -1)
90 {
91 logmsg (_("HHCSD009E Error creating socket for %s: %s\n"),
92 path, strerror(HSO_errno));
93 return -1;
94 }
95
96 unlink (path);
97 fchmod (sd, 0700);
98
99 if (0
100 || bind (sd, (struct sockaddr*) &addr, sizeof(addr)) == -1
101 || listen (sd, 0) == -1
102 )
103 {
104 logmsg (_("HHCSD010E Failed to bind or listen on socket %s: %s\n"),
105 path, strerror(HSO_errno));
106 return -1;
107 }
108
109 return sd;
110
111 #endif // !defined( HAVE_SYS_UN_H )
112 }
113
114
115 /*-------------------------------------------------------------------*/
116 /* inet_socket create and bind a regular TCP/IP socket */
117 /*-------------------------------------------------------------------*/
inet_socket(char * spec)118 int inet_socket (char* spec)
119 {
120 /* We need a copy of the path to overwrite a ':' with '\0' */
121
122 char buf[sizeof(((DEVBLK*)0)->filename)];
123 char* colon;
124 char* node;
125 char* service;
126 int sd;
127 int one = 1;
128 struct sockaddr_in sin;
129
130 logdebug("inet_socket(%s)\n", spec);
131
132 memset(&sin, 0, sizeof(sin));
133 sin.sin_family = AF_INET;
134 strcpy(buf, spec);
135 colon = strchr(buf, ':');
136
137 if (colon)
138 {
139 *colon = '\0';
140 node = buf;
141 service = colon + 1;
142 }
143 else
144 {
145 node = NULL;
146 service = buf;
147 }
148
149 if (!node)
150 sin.sin_addr.s_addr = INADDR_ANY;
151 else
152 {
153 struct hostent* he = gethostbyname(node);
154
155 if (!he)
156 {
157 logmsg (_("HHCSD011E Failed to determine IP address from %s\n"),
158 node);
159 return -1;
160 }
161
162 memcpy(&sin.sin_addr, he->h_addr_list[0], sizeof(sin.sin_addr));
163 }
164
165 if (isdigit(service[0]))
166 {
167 sin.sin_port = htons(atoi(service));
168 }
169 else
170 {
171 struct servent* se = getservbyname(service, "tcp");
172
173 if (!se)
174 {
175 logmsg (_("HHCSD012E Failed to determine port number from %s\n"),
176 service);
177 return -1;
178 }
179
180 sin.sin_port = se->s_port;
181 }
182
183 sd = socket (PF_INET, SOCK_STREAM, 0);
184
185 if (sd == -1)
186 {
187 logmsg (_("HHCSD013E Error creating socket for %s: %s\n"),
188 spec, strerror(HSO_errno));
189 return -1;
190 }
191
192 setsockopt (sd, SOL_SOCKET, SO_REUSEADDR, (GETSET_SOCKOPT_T*)&one, sizeof(one));
193
194 if (0
195 || bind (sd, (struct sockaddr*) &sin, sizeof(sin)) == -1
196 || listen (sd, 0) == -1
197 )
198 {
199 logmsg (_("HHCSD014E Failed to bind or listen on socket %s: %s\n"),
200 spec, strerror(HSO_errno));
201 return -1;
202 }
203
204 return sd;
205 }
206
207
208 /*-------------------------------------------------------------------*/
209 /* add_socket_devices_to_fd_set add all bound socket devices' */
210 /* listening sockets to the FD_SET */
211 /*-------------------------------------------------------------------*/
add_socket_devices_to_fd_set(int maxfd,fd_set * readset)212 int add_socket_devices_to_fd_set (int maxfd, fd_set* readset)
213 {
214 DEVBLK* dev;
215 bind_struct* bs;
216 LIST_ENTRY* pListEntry;
217
218 obtain_lock(&bind_lock);
219
220 pListEntry = bind_head.Flink;
221
222 while (pListEntry != &bind_head)
223 {
224 bs = CONTAINING_RECORD(pListEntry,bind_struct,bind_link);
225
226 if (bs->sd != -1) /* if listening for connections, */
227 {
228 dev = bs->dev;
229
230 FD_SET(bs->sd, readset); /* then add file to set */
231
232 if (bs->sd > maxfd)
233 maxfd = bs->sd;
234 }
235
236 pListEntry = pListEntry->Flink;
237 }
238
239 release_lock(&bind_lock);
240
241 return maxfd;
242 }
243
244
245 /*-------------------------------------------------------------------*/
246 /* socket_device_connection_handler */
247 /*-------------------------------------------------------------------*/
socket_device_connection_handler(bind_struct * bs)248 void socket_device_connection_handler (bind_struct* bs)
249 {
250 struct sockaddr_in client; /* Client address structure */
251 struct hostent* pHE; /* Addr of hostent structure */
252 socklen_t namelen; /* Length of client structure*/
253 char* clientip; /* Addr of client ip address */
254 char* clientname; /* Addr of client hostname */
255 DEVBLK* dev; /* Device Block pointer */
256 int csock; /* Client socket */
257
258 dev = bs->dev;
259
260 logdebug("socket_device_connection_handler(dev=%4.4X)\n",
261 dev->devnum);
262
263 /* Accept the connection... */
264
265 csock = accept(bs->sd, 0, 0);
266
267 if (csock == -1)
268 {
269 logmsg (_("HHCSD017E Connect to device %4.4X (%s) failed: %s\n"),
270 dev->devnum, bs->spec, strerror(HSO_errno));
271 return;
272 }
273
274 /* Determine the connected client's IP address and hostname */
275
276 namelen = sizeof(client);
277 clientip = NULL;
278 clientname = "<unknown>";
279
280 if (1
281 && getpeername(csock, (struct sockaddr*) &client, &namelen) == 0
282 && (clientip = inet_ntoa(client.sin_addr)) != NULL
283 && (pHE = gethostbyaddr((unsigned char*)(&client.sin_addr),
284 sizeof(client.sin_addr), AF_INET)) != NULL
285 && pHE->h_name && *pHE->h_name
286 )
287 {
288 clientname = (char*) pHE->h_name;
289 }
290
291 if (!clientip) clientip = "<unknown>";
292
293 /* Obtain the device lock */
294
295 obtain_lock (&dev->lock);
296
297 /* Reject if device is busy or interrupt pending */
298
299 if (dev->busy || IOPENDING(dev)
300 || (dev->scsw.flag3 & SCSW3_SC_PEND))
301 {
302 close_socket( csock );
303 logmsg (_("HHCSD015E Client %s (%s) connection to device %4.4X "
304 "(%s) rejected: device busy or interrupt pending\n"),
305 clientname, clientip, dev->devnum, bs->spec);
306 release_lock (&dev->lock);
307 return;
308 }
309
310 /* Reject new client if previous client still connected */
311
312 if (dev->fd != -1)
313 {
314 close_socket( csock );
315 logmsg (_("HHCSD016E Client %s (%s) connection to device %4.4X "
316 "(%s) rejected: client %s (%s) still connected\n"),
317 clientname, clientip, dev->devnum, bs->spec,
318 bs->clientname, bs->clientip);
319 release_lock (&dev->lock);
320 return;
321 }
322
323 /* Indicate that a client is now connected to this device */
324
325 dev->fd = csock;
326
327 if (bs->clientip) free(bs->clientip);
328 if (bs->clientname) free(bs->clientname);
329
330 bs->clientip = strdup(clientip);
331 bs->clientname = strdup(clientname);
332
333 /* Call the boolean onconnect callback */
334
335 if (bs->fn && !bs->fn( bs->arg ))
336 {
337 /* Callback says it can't accept it */
338 close_socket( dev->fd );
339 dev->fd = -1;
340 logmsg (_("HHCSD026E Client %s (%s) connection to device %4.4X "
341 "(%s) rejected: by onconnect callback\n"),
342 clientname, clientip, dev->devnum, bs->spec);
343 release_lock (&dev->lock);
344 return;
345 }
346
347 logmsg (_("HHCSD018I Client %s (%s) connected to device %4.4X (%s)\n"),
348 clientname, clientip, dev->devnum, bs->spec);
349
350 release_lock (&dev->lock);
351 device_attention (dev, CSW_DE);
352 }
353
354
355 /*-------------------------------------------------------------------*/
356 /* check_socket_devices_for_connections */
357 /*-------------------------------------------------------------------*/
check_socket_devices_for_connections(fd_set * readset)358 void check_socket_devices_for_connections (fd_set* readset)
359 {
360 bind_struct* bs;
361 LIST_ENTRY* pListEntry;
362
363 obtain_lock(&bind_lock);
364
365 pListEntry = bind_head.Flink;
366
367 while (pListEntry != &bind_head)
368 {
369 bs = CONTAINING_RECORD(pListEntry,bind_struct,bind_link);
370
371 if (bs->sd != -1 && FD_ISSET(bs->sd, readset))
372 {
373 /* Note: there may be other connection requests
374 * waiting to be serviced, but we'll catch them
375 * the next time the panel thread calls us. */
376
377 release_lock(&bind_lock);
378 socket_device_connection_handler(bs);
379 return;
380 }
381
382 pListEntry = pListEntry->Flink;
383 }
384
385 release_lock(&bind_lock);
386 }
387
388 /*-------------------------------------------------------------------*/
389 /* socket_thread listen for socket device connections */
390 /*-------------------------------------------------------------------*/
socket_thread(void * arg)391 void* socket_thread( void* arg )
392 {
393 int rc;
394 fd_set sockset;
395 int maxfd = 0;
396 int select_errno;
397 int exit_now;
398
399 UNREFERENCED( arg );
400
401 /* Display thread started message on control panel */
402 logmsg (_("HHCSD020I Socketdevice listener thread started: "
403 "tid="TIDPAT", pid=%d\n"),
404 thread_id(), getpid());
405
406 for (;;)
407 {
408 /* Set the file descriptors for select */
409 FD_ZERO ( &sockset );
410 maxfd = add_socket_devices_to_fd_set ( 0, &sockset );
411 SUPPORT_WAKEUP_SOCKDEV_SELECT_VIA_PIPE( maxfd, &sockset );
412
413 /* Do the select and save results */
414 rc = select ( maxfd+1, &sockset, NULL, NULL, NULL );
415 select_errno = HSO_errno;
416
417 /* Clear the pipe signal if necessary */
418 RECV_SOCKDEV_THREAD_PIPE_SIGNAL();
419
420 /* Check if it's time to exit yet */
421 obtain_lock( &bind_lock );
422 exit_now = ( sysblk.shutdown || IsListEmpty( &bind_head ) );
423 release_lock( &bind_lock );
424 if ( exit_now ) break;
425
426 /* Log select errors */
427 if ( rc < 0 )
428 {
429 if ( HSO_EINTR != select_errno )
430 logmsg( _( "HHCSD021E select failed; errno=%d: %s\n"),
431 select_errno, strerror( select_errno ) );
432 continue;
433 }
434
435 /* Check if any sockets have received new connections */
436 check_socket_devices_for_connections( &sockset );
437 }
438
439 logmsg( _( "HHCSD022I Socketdevice listener thread terminated\n" ) );
440
441 return NULL;
442 }
443 /* (end socket_thread) */
444
445
446 /*-------------------------------------------------------------------*/
447 /* bind_device bind a device to a socket (adds entry to our list */
448 /* of bound devices) (1=success, 0=failure) */
449 /*-------------------------------------------------------------------*/
bind_device_ex(DEVBLK * dev,char * spec,ONCONNECT fn,void * arg)450 int bind_device_ex (DEVBLK* dev, char* spec, ONCONNECT fn, void* arg )
451 {
452 bind_struct* bs;
453 int was_list_empty;
454
455 if (!init_done) init_sockdev();
456
457 if (sysblk.shutdown) return 0;
458
459 logdebug("bind_device (%4.4X, %s)\n", dev->devnum, spec);
460
461 /* Error if device already bound */
462 if (dev->bs)
463 {
464 logmsg (_("HHCSD001E Device %4.4X already bound to socket %s\n"),
465 dev->devnum, dev->bs->spec);
466 return 0; /* (failure) */
467 }
468
469 /* Create a new bind_struct entry */
470 bs = malloc(sizeof(bind_struct));
471
472 if (!bs)
473 {
474 logmsg (_("HHCSD002E bind_device malloc() failed for device %4.4X\n"),
475 dev->devnum);
476 return 0; /* (failure) */
477 }
478
479 memset(bs,0,sizeof(bind_struct));
480
481 bs->fn = fn;
482 bs->arg = arg;
483
484 if (!(bs->spec = strdup(spec)))
485 {
486 logmsg (_("HHCSD003E bind_device strdup() failed for device %4.4X\n"),
487 dev->devnum);
488 free (bs);
489 return 0; /* (failure) */
490 }
491
492 /* Create a listening socket */
493 if (bs->spec[0] == '/') bs->sd = unix_socket (bs->spec);
494 else bs->sd = inet_socket (bs->spec);
495 if (bs->sd == -1)
496 {
497 /* (error message already issued) */
498 free( bs->spec );
499 free( bs );
500 return 0; /* (failure) */
501 }
502
503 /* Chain device and bind_struct to each other */
504 dev->bs = bs;
505 bs->dev = dev;
506
507 /* Add the new entry to our list of bound devices
508 and create the socket thread that will listen
509 for connections (if it doesn't already exist) */
510
511 obtain_lock( &bind_lock );
512
513 was_list_empty = IsListEmpty( &bind_head );
514
515 InsertListTail( &bind_head, &bs->bind_link );
516
517 if ( was_list_empty )
518 {
519 if ( create_thread( &sysblk.socktid, JOINABLE,
520 socket_thread, NULL, "socket_thread" ) )
521 {
522 logmsg( _( "HHCSD023E Cannot create socketdevice thread: errno=%d: %s\n" ),
523 errno, strerror( errno ) );
524 RemoveListEntry( &bs->bind_link );
525 close_socket(bs->sd);
526 free( bs->spec );
527 free( bs );
528 release_lock( &bind_lock );
529 return 0; /* (failure) */
530 }
531 }
532
533 SIGNAL_SOCKDEV_THREAD();
534
535 release_lock( &bind_lock );
536
537 logmsg (_("HHCSD004I Device %4.4X bound to socket %s\n"),
538 dev->devnum, dev->bs->spec);
539
540 return 1; /* (success) */
541 }
542
543
544 /*-------------------------------------------------------------------*/
545 /* unbind_device unbind a device from a socket (removes entry from */
546 /* our list and discards it) (1=success, 0=failure) */
547 /*-------------------------------------------------------------------*/
unbind_device_ex(DEVBLK * dev,int forced)548 int unbind_device_ex (DEVBLK* dev, int forced)
549 {
550 bind_struct* bs;
551
552 logdebug("unbind_device(%4.4X)\n", dev->devnum);
553
554 /* Error if device not bound */
555 if (!(bs = dev->bs))
556 {
557 logmsg (_("HHCSD005E Device %4.4X not bound to any socket\n"),
558 dev->devnum);
559 return 0; /* (failure) */
560 }
561
562 /* Is anyone still connected? */
563 if (dev->fd != -1)
564 {
565 /* Yes. Should we forcibly disconnect them? */
566 if (forced)
567 {
568 /* Yes. Then do so... */
569 close_socket( dev->fd );
570 dev->fd = -1;
571 logmsg (_("HHCSD025I Client %s (%s) disconnected from device %4.4X (%s)\n"),
572 dev->bs->clientip, dev->bs->clientname, dev->devnum, dev->bs->spec);
573 }
574 else
575 {
576 /* No. Then fail the request. */
577 logmsg (_("HHCSD006E Client %s (%s) still connected to device %4.4X (%s)\n"),
578 dev->bs->clientip, dev->bs->clientname, dev->devnum, dev->bs->spec);
579 return 0; /* (failure) */
580 }
581 }
582
583 /* Remove the entry from our list */
584
585 obtain_lock( &bind_lock );
586 RemoveListEntry( &bs->bind_link );
587 SIGNAL_SOCKDEV_THREAD();
588 release_lock( &bind_lock );
589
590 logmsg (_("HHCSD007I Device %4.4X unbound from socket %s\n"),
591 dev->devnum, bs->spec);
592
593 if (bs->sd != -1)
594 close_socket (bs->sd);
595
596 /* Unchain device and bind_struct from each another */
597
598 dev->bs = NULL;
599 bs->dev = NULL;
600
601 /* Discard the entry */
602
603 if ( bs->clientname ) free( bs->clientname );
604 if ( bs->clientip ) free( bs->clientip );
605
606 bs->clientname = NULL;
607 bs->clientip = NULL;
608
609 free ( bs->spec );
610 free ( bs );
611
612 return 1; /* (success) */
613 }
614