OSDN Git Service

Merged gcj-eclipse branch to trunk.
[pf3gnuchains/gcc-fork.git] / libjava / classpath / native / jni / java-nio / gnu_java_nio_VMChannel.c
1 /* gnu_java_nio_VMChannel.c -
2    Copyright (C) 2003, 2004, 2005, 2006  Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37
38
39 #ifdef HAVE_CONFIG_H
40 #include <config.h>
41 #endif
42
43 #include <config-int.h>
44
45 #include <sys/types.h>
46 #include <sys/ioctl.h>
47 #include <sys/mman.h>
48 #include <sys/socket.h>
49 #include <sys/stat.h>
50 #include <sys/uio.h>
51
52 #include <netinet/in.h>
53
54 #include <stdlib.h>
55 #include <errno.h>
56 #include <unistd.h>
57 #include <string.h>
58
59 #include <jni.h>
60 #include <jcl.h>
61
62 #include "cpio.h"
63 #include "gnu_java_nio_VMChannel.h"
64 #include "javanio.h"
65
66 #ifdef HAVE_FCNTL_H
67 #include <fcntl.h>
68 #endif /* HAVE_FCNTL_H */
69
70 #define CONNECT_EXCEPTION "java/net/ConnectException"
71 #define IO_EXCEPTION "java/io/IOException"
72 #define SOCKET_EXCEPTION "java/net/SocketException"
73 #define INTERRUPTED_IO_EXCEPTION "java/io/InterruptedIOException"
74 #define NON_READABLE_CHANNEL_EXCEPTION "java/nio/channels/NonReadableChannelException"
75 #define NON_WRITABLE_CHANNEL_EXCEPTION "java/nio/channels/NonWritableChannelException"
76 #define SOCKET_TIMEOUT_EXCEPTION "java/net/SocketTimeoutException"
77
78 /* Align a value up or down to a multiple of the pagesize. */
79 #define ALIGN_DOWN(p,s) ((p) - ((p) % (s)))
80 #define ALIGN_UP(p,s) ((p) + ((s) - ((p) % (s))))
81
82 /*
83  * Limit to maximum of 16 buffers
84  */
85 #define JCL_IOV_MAX 16
86
87 #ifdef __cplusplus
88 extern "C"
89 {
90 #endif
91
92 enum JCL_buffer_type { DIRECT, HEAP, ARRAY, UNKNOWN };
93
94 struct JCL_buffer
95 {
96   enum JCL_buffer_type type;
97   jbyte *ptr;
98   jint offset;
99   jint position;
100   jint limit;
101   jint count;
102 };
103
104 jmethodID get_method_id(JNIEnv *, jclass, const char *, const char *);
105 void JCL_print_buffer(JNIEnv *, struct JCL_buffer *);
106 int JCL_init_buffer(JNIEnv *, struct JCL_buffer *, jobject);
107 void JCL_release_buffer(JNIEnv *, struct JCL_buffer *, jobject, jint);
108 void JCL_cleanup_buffers(JNIEnv *, struct JCL_buffer *, jint, jobjectArray, jint, jlong);
109 int JCL_thread_interrupted(JNIEnv *);
110
111 static jfieldID address_fid;
112 static jmethodID get_position_mid;
113 static jmethodID set_position_mid;
114 static jmethodID get_limit_mid;
115 static jmethodID set_limit_mid;
116 static jmethodID has_array_mid;
117 static jmethodID array_mid;
118 static jmethodID array_offset_mid;
119 static jmethodID thread_interrupted_mid;
120 static jclass vm_channel_class;
121
122 jmethodID
123 get_method_id(JNIEnv *env,  jclass clazz, const char *name, 
124                   const char *sig)
125 {
126   jmethodID mid = (*env)->GetMethodID(env, clazz, name, sig);
127 /*   NIODBG("name: %s; sig: %s", name, sig); */
128   if (mid == NULL)
129     {
130       JCL_ThrowException(env, "java/lang/InternalError", name);
131       return NULL;
132     }
133   
134   return mid;
135 }
136
137 inline void
138 JCL_print_buffer(JNIEnv *env __attribute__((__unused__)), struct JCL_buffer *buf)
139 {
140   fprintf (stderr, "Buffer - type: %d, ptr: %p\n", buf->type, buf->ptr);
141 }
142
143
144 int
145 JCL_init_buffer(JNIEnv *env, struct JCL_buffer *buf, jobject bbuf)
146 {
147   void *addr = (*env)->GetDirectBufferAddress (env, bbuf);
148
149 /*   NIODBG("buf: %p; bbuf: %p; addr: %p", (void *) buf, bbuf, addr); */
150   
151   buf->position = (*env)->CallIntMethod(env, bbuf, get_position_mid);
152   buf->limit = (*env)->CallIntMethod(env, bbuf, get_limit_mid);
153   buf->offset = 0;
154   buf->count = 0;
155   buf->type = UNKNOWN;
156     
157   if (addr != NULL)
158     {
159       buf->ptr = (jbyte *) addr;
160       buf->type = DIRECT;
161     }
162   else
163     {
164       jboolean has_array;
165       has_array = (*env)->CallBooleanMethod(env, bbuf, has_array_mid);
166       
167       if (has_array == JNI_TRUE)
168         {
169           jbyteArray arr;
170           buf->offset = (*env)->CallIntMethod(env, bbuf, array_offset_mid);
171           arr = (*env)->CallObjectMethod(env, bbuf, array_mid);
172           buf->ptr = (*env)->GetByteArrayElements(env, arr, 0);
173           buf->type = ARRAY;
174           (*env)->DeleteLocalRef(env, arr);
175         }
176       else
177         {
178           jobject address = (*env)->GetObjectField (env, bbuf, address_fid);
179           if (address == NULL)
180             return -1; /* XXX handle non-array, non-native buffers? */
181           buf->ptr = (jbyte *) JCL_GetRawData(env, address);
182           buf->type = HEAP;
183           (*env)->DeleteLocalRef(env, address);
184         }
185     }
186       
187   return 0;
188 }
189
190 void
191 JCL_release_buffer(JNIEnv *env, struct JCL_buffer *buf, jobject bbuf, 
192     jint action)
193 {
194   jbyteArray arr;
195
196 /*   NIODBG("buf: %p; bbuf: %p; action: %x", (void *) buf, bbuf, action); */
197   
198   /* Set the position to the appropriate value */
199   if (buf->count > 0)
200     {
201       jobject bbufTemp;
202       bbufTemp = (*env)->CallObjectMethod(env, bbuf, set_position_mid, 
203                                           buf->position + buf->count);
204       (*env)->DeleteLocalRef(env, bbufTemp);
205     }
206     
207   switch (buf->type)
208     {
209     case DIRECT:
210     case HEAP:
211       break;
212     case ARRAY:
213       arr = (*env)->CallObjectMethod(env, bbuf, array_mid);
214       (*env)->ReleaseByteArrayElements(env, arr, buf->ptr, action);
215       (*env)->DeleteLocalRef(env, arr);
216       break;
217     case UNKNOWN:
218       /* TODO: Handle buffers that are not direct or array backed */
219       break;
220     }
221 }
222
223 void
224 JCL_cleanup_buffers(JNIEnv *env, 
225                     struct JCL_buffer *bi_list, 
226                     jint vec_len, 
227                     jobjectArray bbufs, 
228                     jint offset,
229                     jlong num_bytes)
230 {
231   jint i;
232
233 /*   NIODBG("bi_list: %p; vec_len: %d; bbufs: %p; offset: %d; num_bytes: %lld", */
234 /*       (void *) bi_list, vec_len, bbufs, offset, num_bytes); */
235   
236   /* Update all of the bbufs with the approriate information */
237   for (i = 0; i < vec_len; i++)
238     {
239       struct JCL_buffer* buf;
240       jobject bbuf;
241       
242       buf = &bi_list[i];
243       bbuf = (*env)->GetObjectArrayElement(env, bbufs, offset + i);
244
245       if (num_bytes > (buf->limit - buf->position))
246         buf->count = (buf->limit - buf->position);
247       else
248         buf->count = num_bytes;
249         
250       num_bytes -= buf->count;
251       
252       JCL_release_buffer(env, buf, bbuf, JNI_ABORT);
253       (*env)->DeleteLocalRef(env, bbuf);
254     }
255 }
256
257
258 int
259 JCL_thread_interrupted(JNIEnv *env)
260 {
261   return (int) (*env)->CallStaticBooleanMethod(env, vm_channel_class,
262                                                thread_interrupted_mid);
263 }
264
265
266 /*
267  * Class:     gnu_java_nio_VMChannel
268  * Method:    stdin_fd
269  * Signature: ()I
270  */
271 JNIEXPORT jint JNICALL
272 Java_gnu_java_nio_VMChannel_stdin_1fd (JNIEnv *env __attribute__((unused)),
273                                        jclass c __attribute__((unused)))
274 {
275 /*   NIODBG("%d", fileno (stdin)); */
276   return fileno (stdin);
277 }
278
279
280 /*
281  * Class:     gnu_java_nio_VMChannel
282  * Method:    stdout_fd
283  * Signature: ()I
284  */
285 JNIEXPORT jint JNICALL
286 Java_gnu_java_nio_VMChannel_stdout_1fd (JNIEnv *env __attribute__((unused)),
287                                        jclass c __attribute__((unused)))
288 {
289 /*   NIODBG("%d", fileno (stdout)); */
290   return fileno (stdout);
291 }
292
293
294 /*
295  * Class:     gnu_java_nio_VMChannel
296  * Method:    stderr_fd
297  * Signature: ()I
298  */
299 JNIEXPORT jint JNICALL
300 Java_gnu_java_nio_VMChannel_stderr_1fd (JNIEnv *env __attribute__((unused)),
301                                        jclass c __attribute__((unused)))
302 {
303 /*   NIODBG("%d", fileno (stderr)); */
304   return fileno (stderr);
305 }
306
307
308 JNIEXPORT void JNICALL 
309 Java_gnu_java_nio_VMChannel_initIDs  (JNIEnv *env, 
310         jclass clazz)
311 {
312   jclass bufferClass = JCL_FindClass(env, "java/nio/Buffer");
313   jclass byteBufferClass = JCL_FindClass(env, "java/nio/ByteBuffer");
314
315 /*   NIODBG("%s", "..."); */
316
317   address_fid = (*env)->GetFieldID(env, bufferClass, "address", 
318                                    "Lgnu/classpath/Pointer;");
319   if (address_fid == NULL)
320     {
321           JCL_ThrowException(env, "java/lang/InternalError", 
322                 "Unable to find internal field");
323       return;
324     }
325   
326   get_position_mid = get_method_id(env, bufferClass, "position", "()I");
327   set_position_mid = get_method_id(env, bufferClass, "position", 
328                                    "(I)Ljava/nio/Buffer;");
329   get_limit_mid = get_method_id(env, bufferClass, "limit", "()I");
330   set_limit_mid = get_method_id(env, bufferClass, "limit", 
331                                 "(I)Ljava/nio/Buffer;");
332   has_array_mid = get_method_id(env, byteBufferClass, "hasArray", "()Z");
333   array_mid = get_method_id(env, byteBufferClass, "array", "()[B");
334   array_offset_mid = get_method_id(env, byteBufferClass, "arrayOffset", "()I");
335   
336   vm_channel_class = clazz;
337   thread_interrupted_mid = (*env)->GetStaticMethodID(env, clazz,
338                                                   "isThreadInterrupted",
339                                                   "()Z");
340 }
341
342 JNIEXPORT void JNICALL 
343 Java_gnu_java_nio_VMChannel_setBlocking (JNIEnv *env, 
344         jobject o __attribute__ ((__unused__)), 
345         jint fd, 
346         jboolean blocking)
347 {
348   int opts;
349   
350 /*   NIODBG("fd: %d; blocking: %d", fd, blocking); */
351
352   opts = fcntl(fd, F_GETFL);
353   if (opts < 0)
354     {
355       JCL_ThrowException(env, IO_EXCEPTION, 
356         "Failed to get flags for file desriptor");
357       return;
358     }
359   
360   if (blocking == JNI_TRUE)
361     opts &= ~(O_NONBLOCK);
362   else
363     opts |= O_NONBLOCK;
364   
365   opts = fcntl(fd, F_SETFL, opts);
366   
367   if (opts < 0)
368     {
369       JCL_ThrowException(env, IO_EXCEPTION, 
370         "Failed to set flags for file desriptor");
371       return;
372     }  
373 }
374
375 /* Return true if fd is in non-blocking mode. */
376 static jboolean
377 is_non_blocking_fd(jint fd)
378 {
379   int opts;
380   opts = fcntl(fd, F_GETFL);
381   if (opts == -1)
382     {
383       /* Assume blocking on error. */
384       return 0;
385     }
386   return (opts & O_NONBLOCK) != 0;
387 }
388
389 JNIEXPORT jint JNICALL 
390 Java_gnu_java_nio_VMChannel_read__ILjava_nio_ByteBuffer_2 (JNIEnv *env,
391                                                            jobject o __attribute__ ((__unused__)), 
392                                                            jint fd, 
393                                                            jobject bbuf)
394 {
395 #ifdef HAVE_READ
396   jint len;
397   ssize_t result;
398   struct JCL_buffer buf;
399   int tmp_errno;
400
401 /*   NIODBG("fd: %d; bbuf: %p", fd, bbuf); */
402   
403   if (JCL_init_buffer(env, &buf, bbuf) < 0)
404     {
405       /* TODO: Rethrown exception */
406       JCL_ThrowException (env, IO_EXCEPTION, "Buffer initialisation failed");
407       return -1;
408     }
409
410   len = buf.limit - buf.position;
411
412   if (len == 0)
413     {
414       JCL_release_buffer (env, &buf, bbuf, JNI_ABORT);
415       return 0;
416     }
417   
418   do 
419     {
420       result = cpnio_read (fd, &(buf.ptr[buf.position + buf.offset]), len);
421       tmp_errno = errno;
422     }
423   while (result == -1 && errno == EINTR && ! JCL_thread_interrupted(env));
424   errno = tmp_errno;
425   
426   if (result == 0)
427     {
428       result = -1;
429       buf.count = 0;
430     }
431   else if (result == -1)
432     {
433       buf.count = 0;
434       if (errno == EAGAIN)
435         {
436           if (is_non_blocking_fd(fd))
437             {
438               /* Non-blocking */
439               result = 0;
440             }
441           else
442             {
443               /* Read timeout on a socket with SO_RCVTIMEO != 0. */
444               JCL_release_buffer(env, &buf, bbuf, JNI_ABORT);
445               JCL_ThrowException(env, SOCKET_TIMEOUT_EXCEPTION, "read timed out");
446               return -1;
447             }
448         }
449       else if (errno == EBADF) /* Bad fd */
450         {
451           JCL_release_buffer(env, &buf, bbuf, JNI_ABORT);
452           JCL_ThrowException (env, NON_READABLE_CHANNEL_EXCEPTION, 
453                               strerror(errno));
454           return -1;
455         }
456       else if (EINTR == errno) /* read interrupted */
457         {
458           JCL_release_buffer(env, &buf, bbuf, JNI_ABORT);
459           JCL_ThrowException(env, INTERRUPTED_IO_EXCEPTION, strerror (errno));
460           return -1;
461         }
462       else
463         {
464           JCL_release_buffer(env, &buf, bbuf, JNI_ABORT);
465           JCL_ThrowException (env, IO_EXCEPTION, strerror(errno));
466           return -1;
467         }
468     }
469   else 
470     buf.count = result;
471       
472   JCL_release_buffer(env, &buf, bbuf, 0);
473   
474   return result;
475 #else
476   (void) fd;
477   (void) bbuf;
478   JCL_ThrowException (env, IO_EXCEPTION, "read not supported");
479   return -1;
480 #endif /* HAVE_READ */
481 }
482
483 JNIEXPORT jint JNICALL 
484 Java_gnu_java_nio_VMChannel_write__ILjava_nio_ByteBuffer_2 (JNIEnv *env, 
485                                                             jobject o __attribute__ ((__unused__)), 
486                                                             jint fd, 
487                                                             jobject bbuf)
488 {
489 #ifdef HAVE_WRITE
490   jint len;
491   ssize_t result;
492   struct JCL_buffer buf;
493   int tmp_errno;
494
495 /*   NIODBG("fd: %d; bbuf: %p", fd, bbuf); */
496   
497   if (JCL_init_buffer(env, &buf, bbuf) < 0)
498     {
499       /* TODO: Rethrown exception */
500       JCL_ThrowException (env, IO_EXCEPTION, "Buffer initialisation failed");
501       return -1;
502     }
503
504   len = buf.limit - buf.position;
505
506   if (len == 0)
507     {
508       JCL_release_buffer (env, &buf, bbuf, JNI_ABORT);
509       return 0;
510     }
511   
512   do
513     {
514       result = cpnio_write (fd, &(buf.ptr[buf.position + buf.offset]), len);
515       tmp_errno = errno;
516     }
517   while (result == -1 && errno == EINTR && ! JCL_thread_interrupted(env));
518   errno = tmp_errno;
519
520   buf.count = result;
521
522   if (result == -1)
523     {
524       if (errno == EAGAIN) /* Non-blocking */
525         {
526           result = 0;
527         }
528       else
529         {
530           JCL_release_buffer(env, &buf, bbuf, JNI_ABORT);
531           JCL_ThrowException(env, IO_EXCEPTION, strerror(errno));
532           return -1;
533         }
534     }
535     
536   JCL_release_buffer(env, &buf, bbuf, JNI_ABORT);
537   
538   return result;
539 #else
540   (void) fd;
541   (void) bbuf;
542   JCL_ThrowException (env, IO_EXCEPTION, "write not supported");
543   return -1;
544 #endif /* HAVE_WRITE */
545 }
546
547
548 /*
549  * Implementation of a scattering read.  Will use the appropriate
550  * vector based read call (currently readv on Linux).
551  * 
552  * This has a limit to the number of buffers that will be read.  It
553  * will not make muliple readv calls.  This is to ensure that operations 
554  * are atomic.  Currently it is limited to 16 buffers.  This is for 
555  * compatibiliy with Sun.
556  */
557 JNIEXPORT jlong JNICALL 
558 Java_gnu_java_nio_VMChannel_readScattering (JNIEnv *env, 
559         jobject o __attribute__ ((__unused__)), 
560         jint fd, 
561         jobjectArray bbufs, 
562         jint offset, 
563         jint length)
564 {
565   jint i;
566 /*   jboolean is_error = JNI_FALSE; */
567 /*   char *error_msg; */
568   struct iovec buffers[JCL_IOV_MAX];
569   struct JCL_buffer bi_list[JCL_IOV_MAX];
570   ssize_t result;
571   jint vec_len = length < JCL_IOV_MAX ? length : JCL_IOV_MAX;
572   jlong bytes_read = 0;
573   int tmp_errno;
574
575 /*   NIODBG("fd: %d; bbufs: %p; offset: %d; length: %d", */
576 /*          fd, bbufs, offset, length); */
577   
578   /* Build the vector of buffers to read into */
579   for (i = 0; i < vec_len; i++)
580     {
581       struct JCL_buffer* buf;
582       jobject bbuf;
583       
584       buf = &bi_list[i];
585       bbuf = (*env)->GetObjectArrayElement(env, bbufs, offset + i);
586       
587       JCL_init_buffer(env, buf, bbuf);
588
589 /*       JCL_print_buffer (env, buf); */
590       
591       buffers[i].iov_base = &(buf->ptr[buf->position + buf->offset]);
592       buffers[i].iov_len = buf->limit - buf->position;
593       (*env)->DeleteLocalRef(env, bbuf);
594     }
595     
596   /* Work the scattering magic */
597   do
598     {
599       result = cpnio_readv (fd, buffers, vec_len);
600       tmp_errno = errno;
601     }
602   while (result == -1 && errno == EINTR && ! JCL_thread_interrupted(env));
603   errno = tmp_errno;
604   bytes_read = (jlong) result;
605   
606   /* Handle the response */
607   if (result < 0)
608     {
609       if (errno == EAGAIN)
610         {
611           if (is_non_blocking_fd(fd))
612             {
613               /* Non-blocking */
614               result = 0;
615             }
616           else
617             {
618               /* Read timeout on a socket with SO_RCVTIMEO != 0. */
619               JCL_cleanup_buffers(env, bi_list, vec_len, bbufs, offset, bytes_read);
620               JCL_ThrowException(env, SOCKET_TIMEOUT_EXCEPTION, "read timed out");
621               return -1;
622             }
623         }
624       else if (errno == EBADF) /* Bad fd */
625         {
626           JCL_cleanup_buffers(env, bi_list, vec_len, bbufs, offset, bytes_read);
627           JCL_ThrowException (env, NON_READABLE_CHANNEL_EXCEPTION, 
628                               strerror(errno));
629           return -1;
630         } 
631       else
632         {
633           JCL_cleanup_buffers(env, bi_list, vec_len, bbufs, offset, bytes_read);
634           JCL_ThrowException (env, IO_EXCEPTION, strerror(errno));
635           return -1;
636         }
637       bytes_read = 0;
638     }
639   else if (result == 0) /* EOF */
640     {
641       result = -1;
642     }
643     
644   JCL_cleanup_buffers(env, bi_list, vec_len, bbufs, offset, bytes_read);
645                   
646   return (jlong) result;
647 }
648
649
650 /*
651  * Implementation of a gathering write.  Will use the appropriate
652  * vector based read call (currently readv on Linux).
653  * 
654  * This has a limit to the number of buffers that will be read.  It
655  * will not make muliple readv calls.  This is to ensure that operations 
656  * are atomic.  Currently it is limited to 16 buffers.  This is for 
657  * compatibiliy with Sun.
658  */
659 JNIEXPORT jlong JNICALL 
660 Java_gnu_java_nio_VMChannel_writeGathering (JNIEnv *env, 
661         jobject o __attribute__ ((__unused__)), 
662         jint fd, 
663         jobjectArray bbufs, 
664         jint offset, 
665         jint length)
666 {
667   int i;
668 /*   jboolean is_error = JNI_FALSE; */
669 /*   char *error_msg; */
670   struct iovec buffers[JCL_IOV_MAX];
671   struct JCL_buffer bi_list[JCL_IOV_MAX];
672   ssize_t result;
673   jint vec_len = length < JCL_IOV_MAX ? length : JCL_IOV_MAX;
674   jlong bytes_written;
675   int tmp_errno;
676   
677 /*   NIODBG("fd: %d; bbufs: %p; offset: %d; length: %d", */
678 /*          fd, bbufs, offset, length); */
679   
680   /* Build the vector of buffers to read into */
681   for (i = 0; i < vec_len; i++)
682     {
683       struct JCL_buffer* buf;
684       jobject bbuf;
685       
686       buf = &bi_list[i];
687       bbuf = (*env)->GetObjectArrayElement(env, bbufs, offset + i);
688       
689       JCL_init_buffer(env, buf, bbuf); 
690       
691 /*       JCL_print_buffer(env, buf); */
692
693       buffers[i].iov_base = &(buf->ptr[buf->position + buf->offset]);
694       buffers[i].iov_len = buf->limit - buf->position;
695       (*env)->DeleteLocalRef(env, bbuf);
696     }
697     
698   /* Work the gathering magic */
699   do
700     {
701       result = cpnio_writev (fd, buffers, vec_len);
702       tmp_errno = errno;
703     }
704   while (result == -1 && tmp_errno == EINTR && ! JCL_thread_interrupted(env));
705   errno = tmp_errno;
706
707   bytes_written = (jlong) result;
708
709   if (result < 0)
710     {
711       bytes_written = 0;
712       if (errno == EAGAIN) /* Non blocking */
713         result = 0;
714       else if (errno == EBADF) /* Bad fd */
715         {
716           JCL_cleanup_buffers(env, bi_list, vec_len, bbufs, offset, 
717                               bytes_written);
718           JCL_ThrowException (env, NON_WRITABLE_CHANNEL_EXCEPTION, 
719                               strerror(errno));
720           return -1;
721         } 
722       else
723         {
724           JCL_cleanup_buffers(env, bi_list, vec_len, bbufs, offset,
725                               bytes_written);
726           JCL_ThrowException (env, IO_EXCEPTION, strerror(errno));
727           return -1;
728         }
729     }
730   else if (result == 0) /* EOF??  Does this happen on a write */
731     result = -1;
732     
733   JCL_cleanup_buffers(env, bi_list, vec_len, bbufs, offset, bytes_written);    
734   return (jlong) result;
735 }
736
737
738 /*
739  * Class:     gnu_java_nio_VMChannel
740  * Method:    receive
741  * Signature: (Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;)I
742  */
743 JNIEXPORT jint JNICALL
744 Java_gnu_java_nio_VMChannel_receive (JNIEnv *env,
745                                      jclass c __attribute__((unused)),
746                                      jint fd, jobject dst, jobject addrPort)
747 {
748 #ifdef HAVE_RECVFROM
749   char *addrPortPtr = (*env)->GetDirectBufferAddress (env, addrPort);
750   struct JCL_buffer buf;
751 #ifdef HAVE_INET6
752   struct sockaddr_in6 sock_storage;
753   struct sockaddr_in6 *sock6;
754   socklen_t slen = sizeof (struct sockaddr_in6);
755 #else
756   struct sockaddr_in sock_storage;
757   socklen_t slen = sizeof (struct sockaddr_in);
758 #endif /* HAVE_INET6 */
759   struct sockaddr *sockaddr = (struct sockaddr *) &sock_storage;
760   struct sockaddr_in *sock4;
761   int ret;
762   jint result = -1;
763
764   if (JCL_init_buffer (env, &buf, dst) == -1)
765     JCL_ThrowException (env, IO_EXCEPTION, "loading buffer failed");
766
767   ret = cpnio_recvfrom (fd, &(buf.ptr[buf.position + buf.offset]),
768                         buf.limit - buf.position, MSG_WAITALL,
769                         sockaddr, &slen);
770
771   if (-1 == ret)
772     {
773       JCL_release_buffer (env, &buf, dst, JNI_ABORT);
774       if (EINTR == errno)
775         JCL_ThrowException (env, "java/io/InterruptedIOException", strerror (errno));
776       else if (EAGAIN == errno)
777         {
778           /* If the socket is in blocking mode, our timeout expired. */
779           int val = fcntl (fd, F_GETFL, 0);
780           if (val == -1)
781             JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
782           else if ((val & O_NONBLOCK) == 0)
783             JCL_ThrowException (env, "java/net/SocketTimeoutException",
784                                 "read timed out");
785         }
786       else
787         JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
788       return 0;
789     }
790
791   if (sockaddr->sa_family == AF_INET)
792     {
793       sock4 = (struct sockaddr_in *) sockaddr;
794       memcpy (addrPortPtr, &(sock4->sin_addr.s_addr), 4);
795       ;memcpy (addrPortPtr + 4, &(sock4->sin_port), 2);
796       result = 4;
797     }
798 #ifdef HAVE_INET6
799   else if (sockaddr->sa_family == AF_INET6)
800     {
801       sock6 = (struct sockaddr_in6 *) sockaddr;
802       memcpy (addrPortPtr, &(sock6->sin6_addr.s6_addr), 16);
803       memcpy (addrPortPtr + 16, &(sock6->sin6_port), 2);
804       result = 16;
805     }
806 #endif /* HAVE_INET6 */
807   else if (ret == 0)
808     {
809       result = 0;
810     }
811   else
812     {
813       JCL_ThrowException (env, "java/net/SocketException",
814                           "unsupported address type returned");
815     }
816
817   buf.count += ret;
818   JCL_release_buffer (env, &buf, dst, 0);
819   return result;
820 #else
821   (void) fd;
822   (void) dst;
823   (void) addrPort;
824   JCL_ThrowException (env, IO_EXCEPTION, "recvfrom not supported");
825 #endif /* HAVE_RECVFROM */
826 }
827
828
829 /*
830  * Class:     gnu_java_nio_VMChannel
831  * Method:    send
832  * Signature: (Ljava/nio/ByteBuffer;[BI)I
833  */
834 JNIEXPORT jint JNICALL
835 Java_gnu_java_nio_VMChannel_send (JNIEnv *env,
836                                   jclass c __attribute__((unused)),
837                                   int fd, jobject src, jbyteArray addr, jint port)
838 {
839 #ifdef HAVE_SENDTO
840   struct sockaddr_in sockaddr;
841   jbyte *elems;
842   struct JCL_buffer buf;
843   int ret;
844
845 /*   NIODBG("fd: %d; src: %p; addr: %p; port: %d", */
846 /*          fd, src, addr, port); */
847
848   if (JCL_init_buffer (env, &buf, src) == -1)
849     {
850       JCL_ThrowException (env, IO_EXCEPTION, "loading buffer failed");
851       return -1;
852     }
853
854 /*   JCL_print_buffer (env, &buf); */
855
856   elems = (*env)->GetByteArrayElements (env, addr, NULL);
857
858   sockaddr.sin_family = AF_INET;
859   sockaddr.sin_addr.s_addr = *((uint32_t *) elems);
860   sockaddr.sin_port = htons (port);
861
862   do
863     {
864       ret = cpnio_sendto (fd, &(buf.ptr[buf.position + buf.offset]),
865                           buf.limit - buf.position,
866                           0, (const struct sockaddr *) &sockaddr,
867                           sizeof (struct sockaddr_in));
868     }
869   while (-1 == ret && EINTR == errno);
870
871   (*env)->ReleaseByteArrayElements (env, addr, elems, JNI_ABORT);
872
873   if (-1 == ret)
874     {
875       if (errno != EAGAIN)
876         JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
877       JCL_release_buffer (env, &buf, src, JNI_ABORT);
878       return 0;
879     }
880
881   buf.count += ret;
882   JCL_release_buffer (env, &buf, src, JNI_ABORT);
883   return ret;
884 #else
885   (void) fd;
886   (void) src;
887   (void) addr;
888   (void) port;
889 #endif /* HAVE_SENDTO */
890 }
891
892
893 /*
894  * Class:     gnu_java_nio_VMChannel
895  * Method:    send6
896  * Signature: (Ljava/nio/ByteBuffer;[BI)I
897  */
898 JNIEXPORT jint JNICALL
899 Java_gnu_java_nio_VMChannel_send6 (JNIEnv *env,
900                                    jclass c __attribute__((unused)),
901                                    int fd, jobject src, jbyteArray addr, jint port)
902 {
903 #if defined(HAVE_SENDTO) && defined(HAVE_INET6)
904   struct sockaddr_in6 sockaddr;
905   jbyte *elems;
906   struct JCL_buffer buf;
907   int ret;
908
909 /*   NIODBG("fd: %d; src: %p; addr: %p; port: %d", */
910 /*          fd, src, addr, port); */
911
912   if (JCL_init_buffer (env, &buf, src) == -1)
913     {
914       JCL_ThrowException (env, IO_EXCEPTION, "loading buffer failed");
915       return -1;
916     }
917
918 /*   JCL_print_buffer (env, &buf); */
919
920   elems = (*env)->GetByteArrayElements (env, addr, NULL);
921
922   sockaddr.sin6_family = AF_INET6;
923   memcpy (&sockaddr.sin6_addr.s6_addr, elems, 16);
924   sockaddr.sin6_port = htons (port);
925
926   do
927     {
928       ret = cpnio_sendto (fd, (const void *) (buf.ptr + buf.offset),
929                           buf.limit - buf.position,
930                           0, (const struct sockaddr *) &sockaddr,
931                           sizeof (struct sockaddr_in6));
932     }
933   while (-1 == ret && EINTR == errno);
934
935   (*env)->ReleaseByteArrayElements (env, addr, elems, JNI_ABORT);
936
937   if (-1 == ret)
938     {
939       if (errno != EAGAIN)
940         JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
941       JCL_release_buffer (env, &buf, src, JNI_ABORT);
942       return 0;
943     }
944
945   buf.count += ret;
946   JCL_release_buffer (env, &buf, src, JNI_ABORT);
947   return ret;
948 #else
949   (void) fd;
950   (void) src;
951   (void) addr;
952   (void) port;
953   JCL_ThrowException (env, IO_EXCEPTION, "IPv6 sendto not supported");
954   return -1;
955 #endif /* HAVE_SENDTO && HAVE_INET6 */
956 }
957
958
959 /*
960  * Class:     gnu_java_nio_VMChannel
961  * Method:    read
962  * Signature: (I)I
963  */
964 JNIEXPORT jint JNICALL
965 Java_gnu_java_nio_VMChannel_read__I (JNIEnv *env,
966                                      jclass c __attribute__((unused)),
967                                      jint fd)
968 {
969 #ifdef HAVE_READ
970   char in;
971   int ret;
972   int tmp_errno;
973
974 /*   NIODBG("fd: %d", fd); */
975
976   do
977     {
978       ret = cpnio_read (fd, &in, 1);
979       tmp_errno = errno;
980     }
981   while (ret == -1 && errno == EINTR && ! JCL_thread_interrupted(env));
982   errno = tmp_errno;
983
984   if (-1 == ret)
985     {
986       if (errno == EAGAIN && !is_non_blocking_fd(fd))
987         {
988           /* Read timeout on a socket with SO_RCVTIMEO != 0. */
989           JCL_ThrowException(env, SOCKET_TIMEOUT_EXCEPTION, "read timed out");
990         }
991       else
992         JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
993       return -1;
994     }
995   
996   if (0 == ret)
997     return -1;
998
999   return (in & 0xFF);
1000 #else
1001   (void) fd;
1002   JCL_ThrowException (env, IO_EXCEPTION, "read not supported");
1003 #endif /* HAVE_READ */
1004 }
1005
1006
1007 /*
1008  * Class:     gnu_java_nio_VMChannel
1009  * Method:    write
1010  * Signature: (I)V
1011  */
1012 JNIEXPORT void JNICALL
1013 Java_gnu_java_nio_VMChannel_write__II (JNIEnv *env,
1014                                        jclass c __attribute__((unused)),
1015                                        jint fd, jint data)
1016 {
1017 #ifdef HAVE_WRITE
1018   char out = (char) data;
1019   int ret;
1020   int tmp_errno;
1021
1022 /*   NIODBG("fd: %d; data: %d", fd, data); */
1023
1024   do
1025     {
1026       ret = cpnio_write (fd, &out, 1);
1027       tmp_errno = errno;
1028     }
1029   while (ret == -1 && errno == EINTR && ! JCL_thread_interrupted(env));
1030   errno = tmp_errno;
1031
1032   if (-1 == ret)
1033     JCL_ThrowException(env, IO_EXCEPTION, strerror (errno));
1034 #else
1035   (void) fd;
1036   (void) data;
1037   JCL_ThrowException (env, IO_EXCEPTION, "write not supported");
1038 #endif /* HAVE_WRITE */
1039 }
1040
1041
1042 /*
1043  * Class:     gnu_java_nio_VMChannel
1044  * Method:    socket
1045  * Signature: (Z)I
1046  */
1047 JNIEXPORT jint JNICALL
1048 Java_gnu_java_nio_VMChannel_socket (JNIEnv *env, jclass clazz __attribute__((unused)),
1049                                     jboolean stream)
1050 {
1051 #ifdef HAVE_SOCKET
1052   int ret;
1053
1054   do
1055     {
1056       ret = cpnio_socket (AF_INET, stream ? SOCK_STREAM : SOCK_DGRAM, 0);
1057     }
1058   while (-1 == ret && EINTR == errno);
1059
1060   if (ret == -1)
1061     JCL_ThrowException (env, "java/net/SocketException", strerror (errno));
1062 /*   NIODBG("created socket %d", ret); */
1063
1064   return ret;
1065 #else
1066   (void) stream;
1067   JCL_ThrowException (env, IO_EXCEPTION, "socket not supported");
1068   return -1;
1069 #endif /* HAVE_SOCKET */
1070 }
1071
1072
1073 /*
1074  * Class:     gnu_java_nio_VMChannel
1075  * Method:    connect
1076  * Signature: (I[BI)Z
1077  */
1078 JNIEXPORT jboolean JNICALL
1079 Java_gnu_java_nio_VMChannel_connect (JNIEnv *env, jclass clazz __attribute__((unused)),
1080                                      jint fd, jbyteArray addr, jint port, jint timeout)
1081 {
1082 #ifdef HAVE_CONNECT
1083   struct sockaddr_in sockaddr;
1084   struct timeval timeo;
1085   int origflags = 0, flags;
1086   jbyte *addr_elems;
1087   int ret;
1088   int tmpErrno;
1089
1090   if ((*env)->GetArrayLength (env, addr) != 4)
1091     {
1092       JCL_ThrowException (env, SOCKET_EXCEPTION,
1093                           "expecting 4-byte address");
1094       return JNI_FALSE;
1095     }
1096
1097   if (timeout > 0)
1098     {
1099       timeo.tv_sec = timeout / 1000;
1100       timeo.tv_usec = (timeout % 1000) * 1000;
1101       origflags = fcntl (fd, F_GETFL, 0);
1102       if (origflags == -1)
1103         {
1104           JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
1105           return JNI_FALSE;
1106         }
1107       /* Set nonblocking mode, if not already set. */
1108       if (!(origflags & O_NONBLOCK))
1109         {
1110           flags = origflags | O_NONBLOCK;
1111           if (fcntl (fd, F_SETFL, flags) == -1)
1112             {
1113               JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
1114               return JNI_FALSE;
1115             }
1116         }
1117     }
1118
1119   addr_elems = (*env)->GetByteArrayElements (env, addr, NULL);
1120
1121   memset (&sockaddr, 0, sizeof (struct sockaddr_in));
1122   sockaddr.sin_family = AF_INET;
1123   sockaddr.sin_port = htons (port);
1124   sockaddr.sin_addr.s_addr = *((uint32_t *) addr_elems);
1125
1126
1127   do
1128     {
1129       ret = cpnio_connect (fd, (struct sockaddr *) &sockaddr,
1130                            sizeof (struct sockaddr_in));
1131       tmpErrno = errno;
1132     }
1133   while (ret == -1 && errno == EINTR && ! JCL_thread_interrupted(env));
1134   errno = tmpErrno;
1135
1136   (*env)->ReleaseByteArrayElements (env, addr, addr_elems, JNI_ABORT);
1137
1138   /* If a timeout was specified, select on the file descriptor with
1139      the timeout. */
1140   if (timeout > 0 && ret == -1)
1141     {
1142       /* Reset the non-blocking flag, if needed. */
1143       if (!(origflags & O_NONBLOCK))
1144         {
1145           if (fcntl (fd, F_SETFL, origflags) == -1)
1146             {
1147               /* oops */
1148               JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
1149               return JNI_FALSE;
1150             }
1151         }
1152       if (EINPROGRESS == errno)
1153         {
1154           fd_set wrfds;
1155           FD_ZERO(&wrfds);
1156           FD_SET(fd, &wrfds);
1157           ret = cpnio_select (fd + 1, NULL, &wrfds, NULL, &timeo);
1158           if (ret == -1)
1159             {
1160               JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
1161               return JNI_FALSE;
1162             }
1163           if (ret == 0) /* connect timed out */
1164             {
1165               JCL_ThrowException (env, SOCKET_TIMEOUT_EXCEPTION,
1166                                   "connect timed out");
1167               return JNI_FALSE;
1168             }
1169           return JNI_TRUE; /* Connected! */
1170         }
1171       else if (ECONNREFUSED == errno)
1172         {
1173           JCL_ThrowException (env, CONNECT_EXCEPTION,
1174                               strerror (errno));
1175           return JNI_FALSE;
1176         }
1177       else
1178         {
1179           JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
1180           return JNI_FALSE;
1181         }
1182     }
1183
1184   if (ret == -1)
1185     {
1186       if (EINPROGRESS == errno)
1187         return JNI_FALSE;
1188       else if (ECONNREFUSED == errno)
1189         {
1190           JCL_ThrowException (env, CONNECT_EXCEPTION,
1191                               strerror (errno));
1192           return JNI_FALSE;
1193         }
1194       else
1195         {
1196           JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
1197           return JNI_FALSE;
1198         }
1199     }
1200
1201   return JNI_TRUE;
1202 #else
1203   (void) fd;
1204   (void) addr;
1205   (void) port;
1206   (void) timeout;
1207   JCL_ThrowException (env, SOCKET_EXCEPTION, "connect not supported");
1208   return JNI_FALSE;
1209 #endif /* HAVE_CONNECT */
1210 }
1211
1212
1213 /*
1214  * Class:     gnu_java_nio_VMChannel
1215  * Method:    connect6
1216  * Signature: (I[BI)Z
1217  */
1218 JNIEXPORT jboolean JNICALL
1219 Java_gnu_java_nio_VMChannel_connect6 (JNIEnv *env, jclass clazz __attribute__((unused)),
1220                                       jint fd, jbyteArray addr, jint port, int timeout)
1221 {
1222 #if defined(HAVE_CONNECT) && defined(HAVE_INET6)
1223   struct sockaddr_in6 sockaddr;
1224   struct timeval timeo;
1225   int flags, origflags = 0;
1226   jbyte *addr_elems;
1227   int ret;
1228
1229   if (timeout > 0)
1230     {
1231       timeo.tv_sec = timeout / 1000;
1232       timeo.tv_usec = (timeout % 1000) * 1000;
1233       origflags = fcntl (fd, F_GETFL, 0);
1234       if (origflags == -1)
1235         {
1236           JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
1237           return JNI_FALSE;
1238         }
1239       /* Set nonblocking mode, if not already set. */
1240       if (!(origflags & O_NONBLOCK))
1241         {
1242           flags = origflags | O_NONBLOCK;
1243           if (fcntl (fd, F_SETFL, flags) == -1)
1244             {
1245               JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
1246               return JNI_FALSE;
1247             }
1248         }
1249     }
1250
1251   addr_elems = (*env)->GetByteArrayElements (env, addr, NULL);
1252
1253   memset (&sockaddr, 0, sizeof (struct sockaddr_in6));
1254   sockaddr.sin6_family = AF_INET6;
1255   sockaddr.sin6_port = htons (port);
1256   memcpy (&sockaddr.sin6_addr.s6_addr, addr_elems, 16);
1257
1258   ret = cpnio_connect (fd, (struct sockaddr *) &sockaddr,
1259                        sizeof (struct sockaddr_in6));
1260
1261   (*env)->ReleaseByteArrayElements (env, addr, addr_elems, JNI_ABORT);
1262
1263   /* If a timeout was specified, select on the file descriptor with
1264      the timeout. */
1265   if (timeout > 0 && ret == -1)
1266     {
1267       /* Reset the non-blocking flag, if needed. */
1268       if (!(origflags & O_NONBLOCK))
1269         {
1270           if (fcntl (fd, F_SETFL, origflags) == -1)
1271             {
1272               /* oops */
1273               JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
1274               return JNI_FALSE;
1275             }
1276         }
1277       if (EINPROGRESS == errno)
1278         {
1279           fd_set wrfds;
1280           FD_ZERO(&wrfds);
1281           FD_SET(fd, &wrfds);
1282           ret = cpnio_select (fd + 1, NULL, &wrfds, NULL, &timeo);
1283           if (ret == -1)
1284             {
1285               JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
1286               return JNI_FALSE;
1287             }
1288           if (ret == 0) /* connect timed out */
1289             {
1290               JCL_ThrowException (env, SOCKET_TIMEOUT_EXCEPTION,
1291                                   "connect timed out");
1292               return JNI_FALSE;
1293             }
1294           return JNI_TRUE; /* Connected! */
1295         }
1296       else if (ECONNREFUSED == errno)
1297         {
1298           JCL_ThrowException (env, CONNECT_EXCEPTION,
1299                               strerror (errno));
1300           return JNI_FALSE;
1301         }
1302       else
1303         {
1304           JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
1305           return JNI_FALSE;
1306         }
1307     }
1308
1309   if (ret == -1)
1310     {
1311       if (EAGAIN == errno)
1312         return JNI_FALSE;
1313       else if (ECONNREFUSED == errno)
1314         {
1315           JCL_ThrowException (env, CONNECT_EXCEPTION,
1316                               strerror (errno));
1317           return JNI_FALSE;
1318         }
1319       else
1320         {
1321           JCL_ThrowException (env, SOCKET_EXCEPTION, strerror (errno));
1322           return JNI_FALSE;
1323         }
1324     }
1325
1326   return JNI_TRUE;
1327 #else
1328   (void) fd;
1329   (void) addr;
1330   (void) port;
1331   (void) timeout;
1332   JCL_ThrowException (env, SOCKET_EXCEPTION, "IPv6 connect not supported");
1333   return JNI_FALSE;
1334 #endif /* HAVE_CONNECT && HAVE_INET6 */
1335 }
1336
1337
1338 /*
1339  * Class:     gnu_java_nio_VMChannel
1340  * Method:    getsockname
1341  * Signature: (ILjava/nio/ByteBuffer;)I
1342  */
1343 JNIEXPORT jint JNICALL
1344 Java_gnu_java_nio_VMChannel_getsockname (JNIEnv *env, jclass clazz __attribute__((unused)),
1345                                          jint fd, jobject name)
1346 {
1347 #ifdef HAVE_GETSOCKNAME
1348 #ifdef HAVE_INET6
1349   struct sockaddr_in6 *addr6;
1350   struct sockaddr_in6 sock_storage;
1351   socklen_t socklen = sizeof (struct sockaddr_in6);
1352 #else
1353   struct sockaddr_in sock_storage;
1354   socklen_t socklen = sizeof (struct sockaddr_in);
1355 #endif /* HAVE_INET6 */
1356
1357   struct sockaddr *sockaddr = (struct sockaddr *) &sock_storage;
1358   struct sockaddr_in *addr4;
1359   int ret;
1360   char *nameptr = (*env)->GetDirectBufferAddress (env, name);
1361
1362   ret = getsockname (fd, sockaddr, &socklen);
1363   if (ret == -1)
1364     {
1365       JCL_ThrowException (env, "java/net/SocketException", strerror (errno));
1366       return 0;
1367     }
1368
1369   if (sockaddr->sa_family == AF_INET)
1370     {
1371       addr4 = (struct sockaddr_in *) sockaddr;
1372       memcpy (nameptr, &(addr4->sin_addr.s_addr), 4);
1373       memcpy (nameptr + 4, &(addr4->sin_port), 2);
1374       return 4;
1375     }
1376
1377 #ifdef HAVE_INET6
1378   /* IPv6 */
1379   if (sockaddr->sa_family == AF_INET6)
1380     {
1381       addr6 = (struct sockaddr_in6 *) sockaddr;
1382       memcpy (nameptr, &(addr6->sin6_addr.s6_addr), 16);
1383       memcpy (nameptr + 16, &(addr6->sin6_port), 2);
1384       return 16;
1385     }
1386 #endif /* HAVE_INET6 */
1387   JCL_ThrowException (env, IO_EXCEPTION, "unsupported address format");
1388   return -1;
1389 #else
1390   (void) fd;
1391   (void) name;
1392   JCL_ThrowException (env, IO_EXCEPTION, "getsockname not supported");
1393   return -1;
1394 #endif /* HAVE_GETSOCKNAME */
1395 }
1396
1397
1398 /*
1399  * Class:     gnu_java_nio_VMChannel
1400  * Method:    getpeername
1401  * Signature: (ILjava/nio/ByteBuffer;)I
1402  */
1403 JNIEXPORT jint JNICALL
1404 Java_gnu_java_nio_VMChannel_getpeername (JNIEnv *env, jclass clazz __attribute__((unused)),
1405                                          jint fd, jobject name)
1406 {
1407 #ifdef HAVE_GETPEERNAME
1408 #ifdef HAVE_INET6
1409   struct sockaddr_in6 *addr6;
1410   struct sockaddr_in6 sock_storage;
1411   socklen_t socklen = sizeof (struct sockaddr_in6);
1412 #else
1413   struct sockaddr_in sock_storage;
1414   socklen_t socklen = sizeof (struct sockaddr_in);
1415 #endif /* HAVE_INET6 */
1416
1417   struct sockaddr *sockaddr = (struct sockaddr *) &sock_storage;
1418   struct sockaddr_in *addr4;
1419   int ret;
1420   char *nameptr = (*env)->GetDirectBufferAddress (env, name);
1421   
1422   ret = getpeername (fd, sockaddr, &socklen);
1423   if (ret == -1)
1424     {
1425       if (ENOTCONN != errno)
1426         JCL_ThrowException (env, "java/net/SocketException", strerror (errno));
1427       return 0;
1428     }
1429
1430   if (sockaddr->sa_family == AF_INET)
1431     {
1432       addr4 = (struct sockaddr_in *) sockaddr;
1433       memcpy (nameptr, &(addr4->sin_addr.s_addr), 4);
1434       memcpy (nameptr + 4, &(addr4->sin_port), 2);
1435       return 4;
1436     }
1437 #ifdef HAVE_INET6
1438   else if (sockaddr->sa_family == AF_INET6)
1439     {
1440       addr6 = (struct sockaddr_in6 *) sockaddr;
1441       memcpy (nameptr, &(addr6->sin6_addr.s6_addr), 16);
1442       memcpy (nameptr + 16, &(addr6->sin6_port), 2);
1443       return 16;
1444     }
1445 #endif /* HAVE_INET6 */
1446
1447   JCL_ThrowException (env, "java/net/SocketException",
1448                       "unsupported address type");
1449   return -1;
1450 #else
1451   (void) fd;
1452   (void) name;
1453   JCL_ThrowException (env, IO_EXCEPTION, "getpeername not supported");
1454   return -1;
1455 #endif /* HAVE_GETPEERNAME */
1456 }
1457
1458
1459 /*
1460  * Class:     gnu_java_nio_VMChannel
1461  * Method:    accept
1462  * Signature: (I)I
1463  */
1464 JNIEXPORT jint JNICALL
1465 Java_gnu_java_nio_VMChannel_accept (JNIEnv *env,
1466                                     jclass c __attribute__((unused)),
1467                                     jint fd)
1468 {
1469 #ifdef HAVE_ACCEPT
1470   int ret;
1471   int tmp_errno = 0;
1472
1473 #ifdef HAVE_INET6
1474   struct sockaddr_in6 addr;
1475   socklen_t alen = sizeof (struct sockaddr_in6);
1476 #else
1477   struct sockaddr_in addr;
1478   socklen_t alen = sizeof (struct sockaddr_in);
1479 #endif /* HAVE_INET6 */
1480
1481   do
1482     {
1483       ret = cpnio_accept (fd, (struct sockaddr *) &addr, &alen);
1484       tmp_errno = errno;
1485       
1486       if (ret == -1)
1487         switch (tmp_errno)
1488         {
1489           case EINTR:
1490             /* Check if interrupted by Thread.interrupt(). If not then some
1491              * other unrelated signal interrupted the system function and
1492              * we should start over again.
1493              */
1494             if (JCL_thread_interrupted(env))
1495               {
1496                 JCL_ThrowException (env, "java/net/SocketException", strerror (tmp_errno));
1497                 return -1;
1498               }
1499             break;
1500 #if defined(EWOULDBLOCK) && defined(EAGAIN) && EWOULDBLOCK != EAGAIN
1501           case EWOULDBLOCK:
1502 #endif
1503           case EAGAIN:
1504             /* Socket in non-blocking mode and no pending connection. */
1505             return -1;
1506           default:
1507             JCL_ThrowException (env, "java/net/SocketException", strerror (tmp_errno));
1508             return -1;
1509         }
1510       else
1511         break;
1512     }
1513   while (1);
1514
1515   cpio_closeOnExec(ret);
1516
1517   return ret;
1518 #else
1519   (void) fd;
1520   JCL_ThrowException (env, IO_EXCEPTION, "accept not supported");
1521   return -1;
1522 #endif /* HAVE_ACCEPT */
1523 }
1524
1525
1526
1527 /*
1528  * Class:     gnu_java_nio_VMChannel
1529  * Method:    disconnect
1530  * Signature: (I)V
1531  */
1532 JNIEXPORT void JNICALL
1533 Java_gnu_java_nio_VMChannel_disconnect (JNIEnv *env,
1534                                         jclass c __attribute__((unused)),
1535                                         jint fd)
1536 {
1537   struct sockaddr sockaddr;
1538
1539   sockaddr.sa_family = AF_UNSPEC;
1540   if (connect (fd, &sockaddr, sizeof (struct sockaddr)) == -1)
1541     {
1542       /* The expected error for a successful disconnect is EAFNOSUPPORT. */
1543       if (errno != EAFNOSUPPORT)
1544         JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
1545     }
1546 }
1547
1548
1549 /*
1550  * Class:     gnu_java_nio_VMChannel
1551  * Method:    close
1552  * Signature: (I)V
1553  */
1554 JNIEXPORT void JNICALL
1555 Java_gnu_java_nio_VMChannel_close (JNIEnv *env,
1556                                    jclass c __attribute__((unused)),
1557                                    jint fd)
1558 {
1559   if (close (fd) == -1)
1560     JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
1561 }
1562
1563
1564 /*
1565  * Class:     gnu_java_nio_VMChannel
1566  * Method:    available
1567  * Signature: (I)I
1568  */
1569 JNIEXPORT jint JNICALL
1570 Java_gnu_java_nio_VMChannel_available (JNIEnv *env,
1571                                        jclass c __attribute__((unused)),
1572                                        jint fd)
1573 {
1574   jint avail = 0;
1575
1576 /*   NIODBG("fd: %d", fd); */
1577   if (ioctl (fd, FIONREAD, &avail) == -1)
1578     JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
1579 /*   NIODBG("avail: %d", avail); */
1580
1581   return avail;
1582 }
1583
1584
1585 enum FileChannel_mode {
1586   CPNIO_READ   = 1,
1587   CPNIO_WRITE  = 2,
1588   CPNIO_APPEND = 4,
1589   CPNIO_EXCL   = 8,
1590   CPNIO_SYNC   = 16,
1591   CPNIO_DSYNC  = 32
1592 };
1593
1594
1595 /*
1596  * Class:     gnu_java_nio_VMChannel
1597  * Method:    open
1598  * Signature: (Ljava/lang/String;I)I
1599  */
1600 JNIEXPORT jint JNICALL
1601 Java_gnu_java_nio_VMChannel_open (JNIEnv *env,
1602                                   jclass c __attribute__((unused)),
1603                                   jstring path, jint mode)
1604 {
1605   int nmode = 0;
1606   int ret;
1607   const char *npath;
1608   mode_t mask = umask (0);
1609   umask (mask);
1610
1611   if ((mode & CPNIO_READ) && (mode & CPNIO_WRITE))
1612     nmode = O_RDWR;
1613   else if (mode & CPNIO_WRITE)
1614     nmode = O_WRONLY;
1615   else
1616     nmode = O_RDONLY;
1617
1618   nmode = (nmode
1619            | ((nmode == O_RDWR || nmode == O_WRONLY) ? O_CREAT : 0)
1620            | ((mode & CPNIO_APPEND) ? O_APPEND :
1621               ((nmode == O_RDWR || nmode == O_WRONLY) ? O_TRUNC : 0))
1622            | ((mode & CPNIO_EXCL) ? O_EXCL : 0)
1623            | ((mode & CPNIO_SYNC) ? O_SYNC : 0));
1624
1625   npath = JCL_jstring_to_cstring (env, path);
1626
1627 /*   NIODBG("path: %s; mode: %x", npath, nmode); */
1628
1629   ret = open (npath, nmode, 0777 & ~mask);
1630
1631 /*   NIODBG("ret: %d\n", ret); */
1632
1633   JCL_free_cstring (env, path, npath);
1634
1635   if (-1 == ret)
1636     JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
1637
1638   return ret;
1639 }
1640
1641
1642 /*
1643  * Class:     gnu_java_nio_VMChannel
1644  * Method:    position
1645  * Signature: (I)J
1646  */
1647 JNIEXPORT jlong JNICALL
1648 Java_gnu_java_nio_VMChannel_position (JNIEnv *env,
1649                                       jclass c __attribute__((unused)),
1650                                       jint fd)
1651 {
1652 #ifdef HAVE_LSEEK
1653   off_t ret;
1654
1655   ret = lseek (fd, 0, SEEK_CUR);
1656
1657   if (-1 == ret)
1658     JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
1659
1660   return (jlong) ret;
1661 #else
1662   JCL_ThrowException (env, IO_EXCEPTION, "position not supported");
1663   return -1;
1664 #endif /* HAVE_LSEEK */
1665 }
1666
1667
1668 /*
1669  * Class:     gnu_java_nio_VMChannel
1670  * Method:    seek
1671  * Signature: (IJ)V
1672  */
1673 JNIEXPORT void JNICALL
1674 Java_gnu_java_nio_VMChannel_seek (JNIEnv *env,
1675                                   jclass c __attribute__((unused)),
1676                                   jint fd, jlong pos)
1677 {
1678 #ifdef HAVE_LSEEK
1679   if (lseek (fd, (off_t) pos, SEEK_SET) == -1)
1680     JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
1681 #else
1682   JCL_ThrowException (env, IO_EXCEPTION, "seek not supported");
1683 #endif /* HAVE_LSEEK */
1684 }
1685
1686
1687 /*
1688  * Class:     gnu_java_nio_VMChannel
1689  * Method:    truncate
1690  * Signature: (IJ)V
1691  */
1692 JNIEXPORT void JNICALL
1693 Java_gnu_java_nio_VMChannel_truncate (JNIEnv *env,
1694                                       jclass c __attribute__((unused)),
1695                                       jint fd, jlong len)
1696 {
1697 #if defined(HAVE_FTRUNCATE) && defined(HAVE_LSEEK)
1698   off_t pos = lseek (fd, 0, SEEK_CUR);
1699   if (pos == -1)
1700     {
1701       JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
1702       return;
1703     }
1704   if (ftruncate (fd, (off_t) len) == -1)
1705     {
1706       JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
1707       return;
1708     }
1709   if (pos > len)
1710     {
1711       if (lseek (fd, len, SEEK_SET) == -1)
1712         JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
1713     }
1714 #else
1715   JCL_ThrowException (env, IO_EXCEPTION, "truncate not supported");
1716 #endif /* HAVE_FTRUNCATE && HAVE_LSEEK */
1717 }
1718
1719
1720 /*
1721  * Class:     gnu_java_nio_VMChannel
1722  * Method:    lock
1723  * Signature: (IJJZZ)Z
1724  */
1725 JNIEXPORT jboolean JNICALL
1726 Java_gnu_java_nio_VMChannel_lock (JNIEnv *env,
1727                                   jclass c __attribute__((unused)),
1728                                   jint fd, jlong pos, jlong len,
1729                                   jboolean shared, jboolean wait)
1730 {
1731 #if HAVE_FCNTL
1732   struct flock fl;
1733
1734   fl.l_start  = (off_t) pos;
1735   /* Long.MAX_VALUE means lock everything possible starting at pos. */
1736   if (len == 9223372036854775807LL)
1737     fl.l_len = 0;
1738   else
1739     fl.l_len = (off_t) len;
1740   fl.l_pid    = getpid ();
1741   fl.l_type   = (shared ? F_RDLCK : F_WRLCK);
1742   fl.l_whence = SEEK_SET;
1743
1744   if (cpnio_fcntl (fd, (wait ? F_SETLKW : F_SETLK), (long) &fl) == -1)
1745     {
1746       if (errno != EAGAIN)
1747         JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
1748       return JNI_FALSE;
1749     }
1750
1751   return JNI_TRUE;
1752 #else
1753   JCL_ThrowException (env, IO_EXCEPTION, "lock not supported");
1754   return JNI_FALSE;
1755 #endif /* HAVE_FCNTL */
1756 }
1757
1758 /*
1759  * Class:     gnu_java_nio_VMChannel
1760  * Method:    unlock
1761  * Signature: (IJJ)V
1762  */
1763 JNIEXPORT void JNICALL
1764 Java_gnu_java_nio_VMChannel_unlock (JNIEnv *env,
1765                                     jclass c __attribute__((unused)),
1766                                     jint fd, jlong pos, jlong len)
1767 {
1768 #if HAVE_FCNTL
1769   struct flock fl;
1770
1771   fl.l_start  = (off_t) pos;
1772   fl.l_len    = (off_t) len;
1773   fl.l_pid    = getpid ();
1774   fl.l_type   = F_UNLCK;
1775   fl.l_whence = SEEK_SET;
1776
1777   if (cpnio_fcntl (fd, F_SETLK, (long) &fl) == -1)
1778     {
1779       JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
1780     }
1781 #else
1782   JCL_ThrowException (env, IO_EXCEPTION, "unlock not supported");
1783 #endif /* HAVE_FCNTL */
1784 }
1785
1786 /*
1787  * Class:     gnu_java_nio_VMChannel
1788  * Method:    size
1789  * Signature: (I)J
1790  */
1791 JNIEXPORT jlong JNICALL
1792 Java_gnu_java_nio_VMChannel_size (JNIEnv *env,
1793                                   jclass c __attribute__((unused)),
1794                                   jint fd)
1795 {
1796 #ifdef HAVE_FSTAT
1797   struct stat st;
1798
1799   if (fstat (fd, &st) == -1)
1800     JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
1801
1802   return (jlong) st.st_size;
1803 #else
1804   JCL_ThrowException (env, IO_EXCEPTION, "size not supported");
1805   return 0;
1806 #endif
1807 }
1808
1809 /*
1810  * Class:     gnu_java_nio_VMChannel
1811  * Method:    map
1812  * Signature: (ICJI)Lgnu/classpath/Pointer;
1813  */
1814 JNIEXPORT jobject JNICALL
1815 Java_gnu_java_nio_VMChannel_map (JNIEnv *env,
1816                                  jclass clazz __attribute__((unused)),
1817                                  jint fd, jchar mode, jlong position, jint size)
1818 {
1819 #ifdef HAVE_MMAP
1820   jclass MappedByteBufferImpl_class;
1821   jmethodID MappedByteBufferImpl_init = NULL;
1822   jobject Pointer_instance;
1823   volatile jobject buffer;
1824   long pagesize;
1825   int prot, flags;
1826   void *p;
1827   void *address;
1828
1829 /*   NIODBG("fd: %d; mode: %x; position: %lld; size: %d", */
1830 /*          fd, mode, position, size); */
1831
1832   /* FIXME: should we just assume we're on an OS modern enough to
1833      have 'sysconf'? And not check for 'getpagesize'? */
1834 #if defined(HAVE_GETPAGESIZE)
1835   pagesize = getpagesize ();
1836 #elif defined(HAVE_SYSCONF)
1837   pagesize = sysconf (_SC_PAGESIZE);
1838 #else
1839   JCL_ThrowException (env, IO_EXCEPTION,
1840                       "can't determine memory page size");
1841   return NULL;
1842 #endif /* HAVE_GETPAGESIZE/HAVE_SYSCONF */
1843
1844   if ((*env)->ExceptionOccurred (env))
1845     {
1846       return NULL;
1847     }
1848
1849   prot = PROT_READ;
1850   if (mode == '+' || mode == 'c')
1851     {
1852       /* When writing we need to make sure the file is big enough,
1853          otherwise the result of mmap is undefined. */
1854       struct stat st;
1855       if (fstat (fd, &st) == -1)
1856         {
1857           JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
1858           return NULL;
1859         }
1860       if (position + size > st.st_size)
1861         {
1862           if (ftruncate(fd, position + size) == -1)
1863             {
1864               JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
1865               return NULL;
1866             }
1867         }
1868       prot |= PROT_WRITE;
1869     }
1870
1871   flags = (mode == 'c' ? MAP_PRIVATE : MAP_SHARED);
1872   p = mmap (NULL, (size_t) ALIGN_UP (size, pagesize), prot, flags,
1873             fd, ALIGN_DOWN (position, pagesize));
1874   if (p == MAP_FAILED)
1875     {
1876       JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
1877       return NULL;
1878     }
1879
1880   /* Unalign the mapped value back up, since we aligned offset
1881      down to a multiple of the page size. */
1882   address = (void *) ((char *) p + (position % pagesize));
1883
1884   Pointer_instance = JCL_NewRawDataObject(env, address);
1885
1886   MappedByteBufferImpl_class = (*env)->FindClass (env,
1887                                                   "java/nio/MappedByteBufferImpl");
1888   if (MappedByteBufferImpl_class != NULL)
1889     {
1890       MappedByteBufferImpl_init =
1891         (*env)->GetMethodID (env, MappedByteBufferImpl_class,
1892                              "<init>", "(Lgnu/classpath/Pointer;IZ)V");
1893     }
1894
1895   if ((*env)->ExceptionOccurred (env))
1896     {
1897       munmap (p, ALIGN_UP (size, pagesize));
1898       return NULL;
1899     }
1900   if (MappedByteBufferImpl_init == NULL)
1901     {
1902       JCL_ThrowException (env, "java/lang/InternalError",
1903                           "could not get MappedByteBufferImpl constructor");
1904       munmap (p, ALIGN_UP (size, pagesize));
1905       return NULL;
1906     }
1907
1908   buffer = (*env)->NewObject (env, MappedByteBufferImpl_class,
1909                               MappedByteBufferImpl_init, Pointer_instance,
1910                               (jint) size, mode == 'r');
1911   return buffer;
1912 #else
1913   (void) fd;
1914   (void) mode;
1915   (void) position;
1916   (void) size;
1917   JCL_ThrowException (env, IO_EXCEPTION,
1918                       "memory-mapped files not implemented");
1919   return 0;
1920 #endif /* HAVE_MMAP */
1921 }
1922
1923 /*
1924  * Class:     gnu_java_nio_VMChannel
1925  * Method:    flush
1926  * Signature: (IZ)Z
1927  */
1928 JNIEXPORT jboolean JNICALL
1929 Java_gnu_java_nio_VMChannel_flush (JNIEnv *env,
1930                                    jclass c __attribute__((unused)),
1931                                    jint fd, jboolean metadata __attribute__((unused)))
1932 {
1933 #ifdef HAVE_FSYNC
1934   /* XXX blocking? */
1935   if (fsync (fd) == -1)
1936     {
1937       JCL_ThrowException (env, IO_EXCEPTION, strerror (errno));
1938       return JNI_FALSE;
1939     }
1940   return JNI_TRUE;
1941 #else
1942   JCL_ThrowException (env, IO_EXCEPTION, "flush not implemented");
1943   return JNI_TRUE;
1944 #endif /* HAVE_FSYNC */
1945 }
1946
1947
1948 #ifdef __cplusplus
1949 }
1950 #endif