]> err.no Git - sope/blob - sope-core/NGStreams/NGDescriptorFunctions.m
renamed packages as discussed in the developer list
[sope] / sope-core / NGStreams / NGDescriptorFunctions.m
1 /*
2   Copyright (C) 2000-2004 SKYRIX Software AG
3
4   This file is part of OpenGroupware.org.
5
6   OGo is free software; you can redistribute it and/or modify it under
7   the terms of the GNU Lesser General Public License as published by the
8   Free Software Foundation; either version 2, or (at your option) any
9   later version.
10
11   OGo is distributed in the hope that it will be useful, but WITHOUT ANY
12   WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14   License for more details.
15
16   You should have received a copy of the GNU Lesser General Public
17   License along with OGo; see the file COPYING.  If not, write to the
18   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19   02111-1307, USA.
20 */
21 // $Id$
22
23 #if !defined(WIN32) || defined(__CYGWIN32__)
24
25 // similiar functions for Windows can be found in NGSocket.[hm]
26
27 #include "NGDescriptorFunctions.h"
28 #include "NGStreamExceptions.h"
29 #include "common.h"
30 #include "config.h"
31
32 #ifdef HAVE_POLL
33 #  ifdef HAVE_POLL_H
34 #    include <poll.h>
35 #  endif
36 #  ifdef HAVE_SYS_POLL_H
37 #    include <sys/poll.h>
38 #  endif
39 #  ifndef POLLRDNORM
40 #    define POLLRDNORM POLLIN /* needed on Linux */
41 #  endif
42 #else
43 #  ifdef HAVE_SELECT_H
44 #    include <select.h>
45 #  endif
46 #endif
47
48 #if defined(HAVE_SYS_SOCKET_H) || defined(__APPLE__)
49 #  include <sys/socket.h>
50 #endif
51
52 #ifdef HAVE_SYS_FCNTL_H
53 #  include <sys/fcntl.h>
54 #endif
55 #if defined(HAVE_FCNTL_H) || defined(__APPLE__)
56 #  include <fcntl.h>
57 #endif
58
59 #if HAVE_UNISTD_H || defined(__APPLE__)
60 #  include <unistd.h>
61 #endif
62 #if HAVE_LIMITS_H
63 #  include <limits.h>
64 #endif
65 #if HAVE_SYS_TIME_H || defined(__APPLE__)
66 #  include <sys/time.h>
67 #endif
68 #if HAVE_SYS_TYPES_H || defined(__APPLE__)
69 #  include <sys/types.h>
70 #endif
71
72 #if !HAVE_TTYNAME_R
73 #  if LIB_FOUNDATION_LIBRARY
74 extern NSRecursiveLock *libFoundationLock = nil;
75 #    define systemLock libFoundationLock
76 #  else
77 #    ifndef __APPLE__
78 #      warning "No locking support for ttyname on this platform"
79 #    endif
80 #    define systemLock (id)nil
81 #  endif
82 #endif
83
84 // ******************** Poll *********************
85
86 int NGPollDescriptor(int _fd, short _events, int _timeout) {
87 #ifdef HAVE_POLL
88   struct pollfd pfd;
89   int           result;
90
91   pfd.fd      = _fd;
92   pfd.events  = _events;
93   pfd.revents = 0;
94
95   do {
96     result = poll(&pfd, 1, _timeout);
97
98     if (result < 0) { // error
99       int e = errno;
100
101       if (e == 0) {
102         NSLog(@"%s: errno is 0, but return value of poll is <0 (%i) (retry) ?!",
103               __PRETTY_FUNCTION__, result);
104         continue;
105       }
106
107       if ((e != EAGAIN) && (e != EINTR)) 
108         // only retry of interrupted or repeatable
109         break;
110     }
111   }
112   while (result < 0);
113
114   /* revents: POLLERR POLLHUP POLLNVAL */
115
116   return (result < 0) ? -1 : result;
117 #else
118   struct timeval timeout;
119   fd_set rSet;
120   fd_set wSet;
121   fd_set eSet;
122   int    result;
123   FD_ZERO(&rSet);
124   FD_ZERO(&wSet);
125   FD_ZERO(&eSet);
126
127   if (_events & POLLIN)  FD_SET(_fd, &rSet);
128   if (_events & POLLOUT) FD_SET(_fd, &wSet);
129   if (_events & POLLERR) FD_SET(_fd, &eSet);
130
131   timeout.tv_sec  = _timeout / 1000;
132   timeout.tv_usec = _timeout * 1000 - timeout.tv_sec * 1000000;
133
134   do {
135     result = select(FD_SETSIZE, &rSet, &wSet, &eSet, &timeout);
136     if (result == -1) { // error
137       int e = errno;
138       if ((e != EAGAIN) && (e != EINTR)) 
139         // only retry of interrupted or repeatable
140         break;
141     }
142   }
143   while (result == -1);
144
145   return (result < 0) ? -1 : result;
146 #endif
147 }
148
149 // ******************** Flags ********************
150
151 int NGGetDescriptorFlags(int _fd) {
152   int val;
153
154   val = fcntl(_fd, F_GETFL, 0);
155   if (val < 0)
156     [NGIOException raiseWithReason:@"could not get descriptor flags"];
157   return val;
158 }
159 void NGSetDescriptorFlags(int _fd, int _flags) {
160   if (fcntl(_fd, F_SETFL, _flags) == -1)
161     [NGIOException raiseWithReason:@"could not set descriptor flags"];
162 }
163
164 void NGAddDescriptorFlag(int _fd, int _flag) {
165   int val = NGGetDescriptorFlags(_fd);
166   NGSetDescriptorFlags(_fd, val | _flag);
167 }
168
169 // ******************** NonBlocking IO ************
170
171 static int enableDescLogging = -1;
172
173 int NGDescriptorRecv(int _fd, char *_buf, int _len, int _flags, int _timeout /* in ms */) {
174   int errorCode;
175   int result;
176
177   if (enableDescLogging == -1) {
178     enableDescLogging = 
179       [[[NSUserDefaults standardUserDefaults] 
180          objectForKey:@"NGLogDescriptorRecv"] boolValue] ? YES : NO;
181   }
182   
183   if (enableDescLogging) {
184     NSLog(@"%s(fd=%i,buf=0x%08X,len=%i,flags=%i,timeout=%i)", 
185           __PRETTY_FUNCTION__, _fd,_buf,_len,_flags,_timeout);
186   }
187   
188   if (_timeout == -1)
189     _timeout = 1000 * 60 * 60; /* default timeout: 1 hour */
190   
191   result = recv(_fd, _buf, _len, _flags);
192   errorCode = errno;
193   if (result == 0) return 0; // EOF
194   
195   if (enableDescLogging) {
196     if ((result < 0) && (errorCode == EINVAL)) {
197       NSLog(@"%s: invalid argument in recv(%i, 0x%08X, %i, %i)",
198             __PRETTY_FUNCTION__, _fd, _buf, _len, _flags);
199     }
200   }
201   
202   if (enableDescLogging) {
203     NSLog(@"result=%i, error=%i(%s)", result, errorCode, strerror(errorCode));
204   }
205   
206   if ((result == -1) && (errorCode == EWOULDBLOCK)) { // retry
207 #if HAVE_POLL
208     struct pollfd pfd;
209     pfd.fd      = _fd;
210     pfd.events  = POLLRDNORM;
211     pfd.revents = 0;
212     
213     do {
214       if (enableDescLogging) NSLog(@"starting poll, loop (to=%i)", _timeout);
215       
216       if ((result = poll(&pfd, 1, _timeout)) < 0) {
217         errorCode = errno;
218         
219         if (enableDescLogging) {
220           if (errno == EINVAL)
221             NSLog(@"%s: invalid argument to poll(...)", __PRETTY_FUNCTION__);
222         }
223         
224         if (errorCode == 0) {
225           NSLog(@"%s: errno is 0, but return value of poll is <0 (%i) (retry) ?!",
226                 __PRETTY_FUNCTION__, result);
227           continue;
228         }
229         
230         // retry if interrupted
231         if ((errorCode != EINTR) && (errorCode != EAGAIN)) 
232           break;
233       }
234     }
235     while (result < 0);
236 #else
237     struct timeval timeout;
238     fd_set rSet;
239     fd_set wSet;
240     fd_set eSet;
241     FD_ZERO(&rSet);
242     FD_ZERO(&wSet);
243     FD_ZERO(&eSet);
244
245     FD_SET(_fd, &rSet);
246     
247     timeout.tv_sec  = _timeout / 1000;
248     timeout.tv_usec = _timeout * 1000 - timeout.tv_sec * 1000000;
249     
250     do {
251       result = select(FD_SETSIZE, &rSet, &wSet, &eSet, &timeout);
252       if (enableDescLogging) {
253         if ((result < 0) && (errno == EINVAL))
254           NSLog(@"%s: invalid argument in select(...)", __PRETTY_FUNCTION__);
255       }
256       
257       if (result == -1) { // error
258         int e = errno;
259         if ((e != EAGAIN) && (e != EINTR)) 
260           // only retry of interrupted or repeatable
261           break;
262       }
263     }
264     while (result == -1);
265 #endif
266     
267     if (result == 1) { // data waiting, try to read
268       if (enableDescLogging) NSLog(@"receiving data ...");
269       
270       result = recv(_fd, _buf, _len, _flags);
271       if (result == 0)
272         return 0; // EOF
273       else if (result == -1) {
274         errorCode = errno;
275
276         if (errorCode == EWOULDBLOCK)
277           NSLog(@"WARNING: would block although descriptor was polled ..");
278       }
279     }
280     else if (result == 0) {
281       if (enableDescLogging) {
282         NSLog(@"nonblock: recv on %i timed out after %i milliseconds ..",
283               _fd, _timeout);
284       }
285       result = -2;
286     }
287     else
288       result = -1;
289   }
290
291   return result;
292 }
293
294 int NGDescriptorSend
295 (int _fd, const char *_buf, int _len, int _flags, int _timeout) 
296 {
297   int errorCode;
298   int result;
299
300   result = send(_fd, _buf, _len, _flags);
301   if (result == 0) return 0; // EOF
302
303   errorCode = errno;
304
305   if ((result == -1) && (errorCode == EWOULDBLOCK)) { // retry
306 #if HAVE_POLL
307     struct pollfd pfd;
308     pfd.fd      = _fd;
309     pfd.events  = POLLWRNORM;
310     pfd.revents = 0;
311     
312     do {
313       if ((result = poll(&pfd, 1, _timeout)) < 0) {
314         errorCode = errno;
315
316         if (errorCode == 0) {
317           NSLog(@"%s: errno is 0, but return value of poll is <0 (%i) (retry) ?!",
318                 __PRETTY_FUNCTION__, result);
319           continue;
320         }
321         
322         if (errorCode != EINTR) // retry only if interrupted
323           break;
324       }
325     }
326     while (result < 0);
327 #else
328     struct timeval timeout;
329     fd_set rSet;
330     fd_set wSet;
331     fd_set eSet;
332     FD_ZERO(&rSet);
333     FD_ZERO(&wSet);
334     FD_ZERO(&eSet);
335
336     FD_SET(_fd, &wSet);
337     
338     timeout.tv_sec  = _timeout / 1000;
339     timeout.tv_usec = _timeout * 1000 - timeout.tv_sec * 1000000;
340
341     do {
342       result = select(FD_SETSIZE, &rSet, &wSet, &eSet, &timeout);
343       if (result == -1) { // error
344         int e = errno;
345         if ((e != EAGAIN) && (e != EINTR)) 
346           // only retry of interrupted or repeatable
347           break;
348       }
349     }
350     while (result == -1);
351 #endif
352
353     if (result == 1) { // data waiting, try to read
354       result = send(_fd, _buf, _len, _flags);
355       if (result == 0) return 0; // EOF
356     }
357     else if (result == 0) {
358       NSLog(@"nonblock: send on %i timed out after %i milliseconds ..",
359              _fd, _timeout);
360       result = -2;
361     }
362     else
363       result = -1;
364   }
365
366   return result;
367 }
368
369 // ******************** TTY *********************
370
371 /*
372   Check whether the descriptor is associated to a terminal device.
373   Get the name of the associated terminal device.
374 */
375
376 BOOL NGDescriptorIsAtty(int _fd) {
377 #if HAVE_ISATTY
378   return isatty(_fd) == 1 ? YES : NO;
379 #else
380   return NO;
381 #endif
382 }
383
384 NSString *NGDescriptorGetTtyName(int _fd) {
385 #if HAVE_ISATTY
386   if (isatty(_fd) != 1) // not connected to a terminal device ?
387     return nil;
388 #endif
389   {
390 #if HAVE_TTYNAME_R
391 #  ifndef sparc
392    extern int ttyname_r(int, char*, size_t);
393 #  endif
394 #  ifdef _POSIX_PATH_MAX
395     char namebuffer[_POSIX_PATH_MAX + 128];
396 #  else
397     char namebuffer[4096];
398 #  endif
399
400     if (ttyname_r(_fd, namebuffer, sizeof(namebuffer)))
401       return [NSString stringWithCString:namebuffer];
402     
403 #elif HAVE_TTYNAME
404     char     *result = NULL;
405     NSString *str    = nil;
406     int      errCode = 0;
407
408     [systemLock lock];
409     {
410       result  = ttyname(_fd);
411       errCode = errno;
412       if (result) str = [NSString stringWithCString:result];
413     }
414     [systemLock unlock];
415     
416     if (str) return str;
417 #endif
418   }
419   return nil;
420 }
421
422 #endif // WIN32