1/*
2 * (c) Copyright 1996 by Sebastien Marineau and Holger Veit
3 *			<marineau@genie.uottawa.ca>
4 *                      <Holger.Veit@gmd.de>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19 * HOLGER VEIT  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
21 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 *
24 * Except as contained in this notice, the name of Sebastien Marineau or Holger Veit
25 * shall not be used in advertising or otherwise to promote the sale, use or other
26 * dealings in this Software without prior written authorization from Holger Veit or
27 * Sebastien Marineau.
28 *
29 */
30
31
32/* A few OS/2 functions needed in the X11 lib. Mainly, the file path redirection
33 * functions and the "optimized" select() for the clients */
34
35#define I_NEED_OS2_H
36#ifdef HAVE_CONFIG_H
37#include <config.h>
38#endif
39#include <X11/Xpoll.h>
40#include <stdio.h>
41#include <sys/errno.h>
42#define INCL_DOSSEMAPHORES
43#define INCL_DOSNPIPES
44#define INCL_DOSMISC
45#define INCL_DOSMODULEMGR
46#undef BOOL
47#undef BYTE
48#include <os2.h>
49#include <sys/select.h>
50#include <sys/time.h>
51
52char *__XOS2RedirRoot(char *fname)
53{
54    /* This adds a further redirection by allowing the ProjectRoot
55     * to be prepended by the content of the envvar X11ROOT.
56     * This is for the purpose to move the whole X11 stuff to a different
57     * disk drive.
58     * The feature was added despite various environment variables
59     * because not all file opens respect them.
60     */
61    static char redirname[300]; /* enough for long filenames */
62    char *root;
63
64    /* if name does not start with /, assume it is not root-based */
65    if (fname==0 || !(fname[0]=='/' || fname[0]=='\\'))
66	return fname;
67
68    root = (char*)getenv("X11ROOT");
69    if (root==0 ||
70	(fname[1]==':' && isalpha(fname[0])) ||
71        (strlen(fname)+strlen(root)+2) > 300)
72	return fname;
73    sprintf(redirname,"%s%s",root,fname);
74    return redirname;
75}
76
77char *__XOS2RedirRoot1(char *format, char *arg1, char *arg2, char *arg3)
78{
79    /* this first constructs a name from a format and up to three
80     * components, then adds a path
81     */
82    char buf[300];
83    sprintf(buf,format,arg1,arg2,arg3);
84    return __XOS2RedirRoot(buf);
85}
86
87/* This below implements select() for the calls in this file. It has been */
88/* somewhat optimized for improved performance, but assumes a few */
89/* things so it cannot be used as a general select. If both pipes and     */
90/* sockets are present, this may call the emx select                          */
91
92
93HEV hPipeSem;
94HMODULE hmod_so32dll;
95static int (*os2_tcp_select)(int*,int,int,int,long);
96ULONG os2_get_sys_millis();
97extern int _files[];
98
99#define MAX_TCP 256
100/* These lifted from sys/emx.h. Change if that changes there! */
101#define F_SOCKET 0x10000000
102#define F_PIPE 0x20000000
103
104struct select_data
105{
106   fd_set read_copy;
107   fd_set write_copy;
108   BOOL have_read;
109   BOOL have_write;
110   int tcp_select_mask[MAX_TCP];
111   int tcp_emx_handles[MAX_TCP];
112   int tcp_select_copy[MAX_TCP];
113   int socket_nread;
114   int socket_nwrite;
115   int socket_ntotal;
116   int pipe_ntotal;
117   int pipe_have_write;
118   int max_fds;
119};
120
121int os2ClientSelect(int nfds, fd_set *readfds, fd_set *writefds,
122        fd_set *exceptfds, struct timeval *timeout)
123{
124static BOOL FirstTime=TRUE;
125static haveTCPIP=TRUE;
126ULONG timeout_ms;
127ULONG postCount, start_millis,now_millis;
128char faildata[16];
129struct select_data sd;
130BOOL any_ready;
131int np,ns, i,ready_handles,n;
132APIRET rc;
133
134sd.have_read=FALSE; sd.have_write=FALSE;
135sd.socket_nread=0; sd.socket_nwrite=0; sd.socket_ntotal=0;
136sd.max_fds=31; ready_handles=0; any_ready=FALSE;
137sd.pipe_ntotal=0; sd.pipe_have_write=FALSE;
138
139if(FirstTime){
140   /* First load the so32dll.dll module and get a pointer to the SELECT fn */
141
142   if((rc=DosLoadModule(faildata,sizeof(faildata),"SO32DLL",&hmod_so32dll))!=0){
143        fprintf(stderr, "Could not load module so32dll.dll, rc = %d. Error note %s\n",rc,faildata);
144        haveTCPIP=FALSE;
145        }
146   if((rc = DosQueryProcAddr(hmod_so32dll, 0, "SELECT", (PPFN)&os2_tcp_select))!=0){
147        fprintf(stderr, "Could not query address of SELECT, rc = %d.\n",rc);
148        haveTCPIP=FALSE;
149        }
150   /* Call these a first time to set the semaphore */
151    rc = DosCreateEventSem(NULL, &hPipeSem, DC_SEM_SHARED, FALSE);
152    if(rc) {
153             fprintf(stderr, "Could not create event semaphore, rc=%d\n",rc);
154             return(-1);
155             }
156    rc = DosResetEventSem(hPipeSem, &postCount);
157    FirstTime = FALSE;
158}
159
160/* Set up the time delay structs */
161
162    if(timeout!=NULL) {
163	timeout_ms=timeout->tv_sec*1000+timeout->tv_usec/1000;
164	}
165    else { timeout_ms=1000000; }  /* This should be large enough... */
166    if(timeout_ms>0) start_millis=os2_get_sys_millis();
167
168/* Copy the masks */
169    {FD_ZERO(&sd.read_copy);}
170    {FD_ZERO(&sd.write_copy);}
171    if(readfds!=NULL){ XFD_COPYSET(readfds,&sd.read_copy); sd.have_read=TRUE;}
172    if(writefds!=NULL) {XFD_COPYSET(writefds,&sd.write_copy);sd.have_write=TRUE;}
173
174/* And zero the original masks */
175    if(sd.have_read){ FD_ZERO(readfds);}
176    if(sd.have_write) {FD_ZERO(writefds);}
177    if(exceptfds != NULL) {FD_ZERO(exceptfds);}
178
179/* Now we parse the fd_sets passed to select and separate pipe/sockets */
180        n = os2_parse_select(&sd,nfds);
181        if(n == -1) {
182           errno = EBADF;
183           return (-1);
184           }
185
186/* Now we have three cases: either we have sockets, pipes, or both */
187/* We handle all three cases differently to optimize things */
188
189/* Case 1: only pipes! */
190        if((sd.pipe_ntotal >0) && (!sd.socket_ntotal)){
191            np = os2_check_pipes(&sd,readfds,writefds);
192            if(np > 0){
193               return (np);
194               }
195            else if (np == -1) { return(-1); }
196            while(!any_ready){
197                 rc = DosWaitEventSem(hPipeSem, timeout_ms);
198                 if(rc == 640)  {
199                     return(0);
200                     }
201                 if((rc != 0) && (rc != 95)) {errno= EBADF; return(-1);}
202                 np = os2_check_pipes(&sd,readfds,writefds);
203                 if (np > 0){
204                    return(np);
205                    }
206                 else if (np < 0){ return(-1); }
207                 }
208          }
209
210/* Case 2: only sockets. Just let the os/2 tcp select do the work */
211        if((sd.socket_ntotal > 0) && (!sd.pipe_ntotal)){
212                ns = os2_check_sockets(&sd, readfds, writefds, timeout_ms);
213                return (ns);
214                }
215
216/* Case 3: combination of both */
217        if((sd.socket_ntotal > 0) && (sd.pipe_ntotal)){
218           np = os2_check_pipes(&sd,readfds,writefds);
219              if(np > 0){
220                 any_ready=TRUE;
221                 ready_handles += np;
222                 }
223              else if (np == -1) { return(-1); }
224
225           ns = os2_check_sockets(&sd,readfds,writefds, 0);
226           if(ns>0){
227               ready_handles+=ns;
228               any_ready = TRUE;
229               }
230           else if (ns == -1) {return(-1);}
231
232           while (!any_ready && timeout_ms){
233
234                rc = DosWaitEventSem(hPipeSem, 10L);
235                if(rc == 0){
236                        np = os2_check_pipes(&sd,readfds,writefds);
237                        if(np > 0){
238                        ready_handles+=np;
239                        any_ready = TRUE;
240                        }
241                        else if (np == -1) {
242                                return(-1); }
243                      }
244
245                 ns = os2_check_sockets(&sd,readfds,writefds,exceptfds, 0);
246                 if(ns>0){
247                      ready_handles+=ns;
248                      any_ready = TRUE;
249                     }
250                 else if (ns == -1) {return(-1);}
251
252                  if (i%8 == 0) {
253                    now_millis = os2_get_sys_millis();
254                    if((now_millis-start_millis) > timeout_ms) timeout_ms = 0;
255                    }
256                   i++;
257                  }
258        }
259
260return(ready_handles);
261}
262
263
264ULONG os2_get_sys_millis()
265{
266   APIRET rc;
267   ULONG milli;
268
269   rc = DosQuerySysInfo(14, 14, &milli, sizeof(milli));
270   if(rc) {
271        fprintf(stderr,"Bad return code querying the millisecond counter! rc=%d\n",rc);
272        return(0);
273        }
274   return(milli);
275}
276
277int os2_parse_select(sd,nfds)
278struct select_data *sd;
279int nfds;
280{
281   int i;
282   APIRET rc;
283/* First we determine up to which descriptor we need to check.              */
284/* No need to check up to 256 if we don't have to (and usually we don't...)*/
285/* Note: stuff here is hardcoded for fd_sets which are int[8] as in EMX!!!    */
286
287  if(nfds > sd->max_fds){
288     for(i=0;i<((FD_SETSIZE+31)/32);i++){
289        if(sd->read_copy.fds_bits[i] ||
290            sd->write_copy.fds_bits[i])
291                        sd->max_fds=(i*32) +32;
292        }
293     }
294   else { sd->max_fds = nfds; }
295/* Check if result is greater than specified in select() call */
296  if(sd->max_fds > nfds) sd->max_fds = nfds;
297
298  if (sd->have_read)
299    {
300      for (i = 0; i < sd->max_fds; ++i) {
301        if (FD_ISSET (i, &sd->read_copy)){
302         if(_files[i] & F_SOCKET)
303           {
304            sd->tcp_select_mask[sd->socket_ntotal]=_getsockhandle(i);
305            sd->tcp_emx_handles[sd->socket_ntotal]=i;
306            sd->socket_ntotal++; sd->socket_nread++;
307           }
308         else if (_files[i] & F_PIPE)
309          {
310            sd -> pipe_ntotal++;
311            rc = DosSetNPipeSem((HPIPE)i, (HSEM) hPipeSem, i);
312            if(rc) { fprintf(stderr,"Error SETNPIPE rc = %d\n",rc); return -1;}
313          }
314        }
315      }
316    }
317
318  if (sd->have_write)
319    {
320      for (i = 0; i < sd->max_fds; ++i) {
321        if (FD_ISSET (i, &sd->write_copy)){
322         if(_files[i] & F_SOCKET)
323         {
324            sd->tcp_select_mask[sd->socket_ntotal]=_getsockhandle(i);
325            sd->tcp_emx_handles[sd->socket_ntotal]=i;
326            sd->socket_ntotal++; sd->socket_nwrite++;
327         }
328         else if (_files[i] & F_PIPE)
329          {
330            sd -> pipe_ntotal++;
331            rc = DosSetNPipeSem((HPIPE)i, (HSEM) hPipeSem, i);
332            if(rc) { fprintf(stderr,"Error SETNPIPE rc = %d\n",rc); return -1;}
333            sd -> pipe_have_write=TRUE;
334          }
335        }
336      }
337    }
338
339
340return(sd->socket_ntotal);
341}
342
343
344int os2_check_sockets(sd,readfds,writefds)
345struct select_data *sd;
346fd_set *readfds,*writefds;
347{
348   int e,i;
349   int j,n;
350        memcpy(sd->tcp_select_copy,sd->tcp_select_mask,
351                sd->socket_ntotal*sizeof(int));
352
353        e = os2_tcp_select(sd->tcp_select_copy,sd->socket_nread,
354                sd->socket_nwrite, 0, 0);
355
356        if(e == 0) return(e);
357/* We have something ready? */
358        if(e>0){
359            j = 0; n = 0;
360            for (i = 0; i < sd->socket_nread; ++i, ++j)
361                 if (sd->tcp_select_copy[j] != -1)
362                    {
363                    FD_SET (sd->tcp_emx_handles[j], readfds);
364                    n ++;
365                    }
366             for (i = 0; i < sd->socket_nwrite; ++i, ++j)
367                  if (sd->tcp_select_copy[j] != -1)
368                     {
369                     FD_SET (sd->tcp_emx_handles[j], writefds);
370                     n ++;
371                     }
372               errno = 0;
373
374               return n;
375              }
376        if(e<0){
377           /*Error -- TODO */
378           fprintf(stderr,"Error in server select! e=%d\n",e);
379           errno = EBADF;
380           return (-1);
381           }
382 }
383
384/* Check to see if anything is ready on pipes */
385
386int os2_check_pipes(sd,readfds,writefds)
387struct select_data *sd;
388fd_set *readfds,*writefds;
389{
390int i,e;
391ULONG ulPostCount;
392PIPESEMSTATE pipeSemState[128];
393APIRET rc;
394        e = 0;
395        rc = DosResetEventSem(hPipeSem,&ulPostCount);
396        rc = DosQueryNPipeSemState((HSEM) hPipeSem, (PPIPESEMSTATE)&pipeSemState,
397                sizeof(pipeSemState));
398        if(rc) fprintf(stderr,"SELECT: rc from QueryNPipeSem: %d\n",rc);
399        i=0;
400        while (pipeSemState[i].fStatus != 0) {
401           /*fprintf(stderr,"SELECT: sem entry, stat=%d, flag=%d, key=%d,avail=%d\n",
402                pipeSemState[i].fStatus,pipeSemState[i].fFlag,pipeSemState[i].usKey,
403                pipeSemState[i].usAvail); */
404           if((pipeSemState[i].fStatus == 1) &&
405                    (FD_ISSET(pipeSemState[i].usKey,&sd->read_copy))){
406                FD_SET(pipeSemState[i].usKey,readfds);
407                e++;
408                }
409           else if((pipeSemState[i].fStatus == 2)  &&
410                    (FD_ISSET(pipeSemState[i].usKey,&sd->write_copy))){
411                FD_SET(pipeSemState[i].usKey,writefds);
412                e++;
413                }
414            else if( (pipeSemState[i].fStatus == 3) &&
415                ( (FD_ISSET(pipeSemState[i].usKey,&sd->read_copy)) ||
416                  (FD_ISSET(pipeSemState[i].usKey,&sd->write_copy)) )){
417                errno = EBADF;
418                return (-1);
419                }
420            i++;
421            } /* endwhile */
422        /*fprintf(stderr,"Done listing pipe sem entries, total %d entries, total ready entries %d\n",i,e);*/
423errno = 0;
424return(e);
425}
426
427
428
429