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