1 /*
   2  * Copyright 2001-2005 Sun Microsystems, Inc.  All Rights Reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Sun designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Sun in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  22  * CA 95054 USA or visit www.sun.com if you need additional information or
  23  * have any questions.
  24  */
  25 
  26 #include "jni.h"
  27 #include "jni_util.h"
  28 #include "jvm.h"
  29 #include "jlong.h"
  30 #include "sun_nio_ch_DevPollArrayWrapper.h"
  31 #include "java_lang_Integer.h"
  32 #include <sys/poll.h>
  33 #include <sys/resource.h>
  34 #include <unistd.h>
  35 #include <sys/time.h>
  36 
  37 #ifdef  __cplusplus
  38 extern "C" {
  39 #endif
  40 
  41 typedef uint32_t        caddr32_t;
  42 
  43 /* /dev/poll ioctl */
  44 #define         DPIOC   (0xD0 << 8)
  45 #define DP_POLL         (DPIOC | 1)     /* poll on fds in cached in /dev/poll */
  46 #define DP_ISPOLLED     (DPIOC | 2)     /* is this fd cached in /dev/poll */
  47 #define DEVPOLLSIZE     1000            /* /dev/poll table size increment */
  48 #define POLLREMOVE      0x0800          /* Removes fd from monitored set */
  49 
  50 /*
  51  * /dev/poll DP_POLL ioctl format
  52  */
  53 typedef struct dvpoll {
  54         pollfd_t        *dp_fds;        /* pollfd array */
  55         nfds_t          dp_nfds;        /* num of pollfd's in dp_fds[] */
  56         int             dp_timeout;     /* time out in millisec */
  57 } dvpoll_t;
  58 
  59 typedef struct dvpoll32 {
  60         caddr32_t       dp_fds;         /* pollfd array */
  61         uint32_t        dp_nfds;        /* num of pollfd's in dp_fds[] */
  62         int32_t         dp_timeout;     /* time out in millisec */
  63 } dvpoll32_t;
  64 
  65 #ifdef  __cplusplus
  66 }
  67 #endif
  68 
  69 #define RESTARTABLE(_cmd, _result) do { \
  70   do { \
  71     _result = _cmd; \
  72   } while((_result == -1) && (errno == EINTR)); \
  73 } while(0)
  74 
  75 static int
  76 idevpoll(jint wfd, int dpctl, struct dvpoll a)
  77 {
  78     jlong start, now;
  79     int remaining = a.dp_timeout;
  80     struct timeval t;
  81     int diff;
  82 
  83     gettimeofday(&t, NULL);
  84     start = t.tv_sec * 1000 + t.tv_usec / 1000;
  85 
  86     for (;;) {
  87         /*  poll(7d) ioctl does not return remaining count */
  88         int res = ioctl(wfd, dpctl, &a);
  89         if (res < 0 && errno == EINTR) {
  90             if (remaining >= 0) {
  91                 gettimeofday(&t, NULL);
  92                 now = t.tv_sec * 1000 + t.tv_usec / 1000;
  93                 diff = now - start;
  94                 remaining -= diff;
  95                 if (diff < 0 || remaining <= 0) {
  96                     return 0;
  97                 }
  98                 start = now;
  99             }
 100         } else {
 101             return res;
 102         }
 103     }
 104 }
 105 
 106 JNIEXPORT jint JNICALL
 107 Java_sun_nio_ch_DevPollArrayWrapper_init(JNIEnv *env, jobject this)
 108 {
 109     int wfd = open("/dev/poll", O_RDWR);
 110     if (wfd < 0) {
 111        JNU_ThrowIOExceptionWithLastError(env, "Error opening driver");
 112        return -1;
 113     }
 114     return wfd;
 115 }
 116 
 117 JNIEXPORT void JNICALL
 118 Java_sun_nio_ch_DevPollArrayWrapper_register(JNIEnv *env, jobject this,
 119                                              jint wfd, jint fd, jint mask)
 120 {
 121     struct pollfd a[2];
 122     unsigned char *pollBytes = (unsigned char *)&a[0];
 123     unsigned char *pollEnd = pollBytes + sizeof(struct pollfd) * 2;
 124 
 125     /* We clear it first, otherwise any entries between poll invocations
 126        get OR'd together */
 127     a[0].fd = fd;
 128     a[0].events = POLLREMOVE;
 129     a[0].revents = 0;
 130 
 131     a[1].fd = fd;
 132     a[1].events = mask;
 133     a[1].revents = 0;
 134 
 135     while (pollBytes < pollEnd) {
 136         int bytesWritten = write(wfd, pollBytes, (int)(pollEnd - pollBytes));
 137         if (bytesWritten < 0) {
 138             JNU_ThrowIOExceptionWithLastError(env, "Error writing pollfds");
 139             return;
 140         }
 141         pollBytes += bytesWritten;
 142     }
 143 }
 144 
 145 JNIEXPORT void JNICALL
 146 Java_sun_nio_ch_DevPollArrayWrapper_registerMultiple(JNIEnv *env, jobject this,
 147                                                      jint wfd, jlong address,
 148                                                      jint len)
 149 {
 150     unsigned char *pollBytes = (unsigned char *)jlong_to_ptr(address);
 151     unsigned char *pollEnd = pollBytes + sizeof(struct pollfd) * len;
 152     while (pollBytes < pollEnd) {
 153         int bytesWritten = write(wfd, pollBytes, (int)(pollEnd - pollBytes));
 154         if (bytesWritten < 0) {
 155             JNU_ThrowIOExceptionWithLastError(env, "Error writing pollfds");
 156             return;
 157         }
 158         pollBytes += bytesWritten;
 159     }
 160 }
 161 
 162 JNIEXPORT jint JNICALL
 163 Java_sun_nio_ch_DevPollArrayWrapper_poll0(JNIEnv *env, jobject this,
 164                                        jlong address, jint numfds,
 165                                        jlong timeout, jint wfd)
 166 {
 167     struct dvpoll a;
 168     void *pfd = (void *) jlong_to_ptr(address);
 169     int result = 0;
 170 
 171     a.dp_fds = pfd;
 172     a.dp_nfds = numfds;
 173     a.dp_timeout = (int)timeout;
 174 
 175     if (timeout <= 0) {             /* Indefinite or no wait */
 176         RESTARTABLE (ioctl(wfd, DP_POLL, &a), result);
 177     } else {                        /* Bounded wait; bounded restarts */
 178         result = idevpoll(wfd, DP_POLL, a);
 179     }
 180 
 181     if (result < 0) {
 182         JNU_ThrowIOExceptionWithLastError(env, "Error reading driver");
 183         return -1;
 184     }
 185     return result;
 186 }
 187 
 188 JNIEXPORT jint JNICALL
 189 Java_sun_nio_ch_DevPollArrayWrapper_fdLimit(JNIEnv *env, jclass this)
 190 {
 191     struct rlimit rlp;
 192     if (getrlimit(RLIMIT_NOFILE, &rlp) < 0) {
 193         JNU_ThrowIOExceptionWithLastError(env,
 194                                           "getrlimit failed");
 195     }
 196     if (rlp.rlim_max < 0 || rlp.rlim_max > java_lang_Integer_MAX_VALUE) {
 197         return java_lang_Integer_MAX_VALUE;
 198     } else {
 199         return (jint)rlp.rlim_max;
 200     }
 201 }
 202 
 203 JNIEXPORT void JNICALL
 204 Java_sun_nio_ch_DevPollArrayWrapper_interrupt(JNIEnv *env, jclass this, jint fd)
 205 {
 206     int fakebuf[1];
 207     fakebuf[0] = 1;
 208     if (write(fd, fakebuf, 1) < 0) {
 209         JNU_ThrowIOExceptionWithLastError(env,
 210                                           "Write to interrupt fd failed");
 211     }
 212 }