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