1/*
2 * Copyright (c) 2001, 2016, Oracle and/or its affiliates. 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.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * 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 <poll.h>
32#include <unistd.h>
33#include <sys/time.h>
34
35#ifdef  __cplusplus
36extern "C" {
37#endif
38
39typedef uint32_t        caddr32_t;
40
41/* /dev/poll ioctl */
42#define         DPIOC   (0xD0 << 8)
43#define DP_POLL         (DPIOC | 1)     /* poll on fds in cached in /dev/poll */
44#define DP_ISPOLLED     (DPIOC | 2)     /* is this fd cached in /dev/poll */
45#define DEVPOLLSIZE     1000            /* /dev/poll table size increment */
46#define POLLREMOVE      0x0800          /* Removes fd from monitored set */
47
48/*
49 * /dev/poll DP_POLL ioctl format
50 */
51typedef struct dvpoll {
52        pollfd_t        *dp_fds;        /* pollfd array */
53        nfds_t          dp_nfds;        /* num of pollfd's in dp_fds[] */
54        int             dp_timeout;     /* time out in millisec */
55} dvpoll_t;
56
57typedef struct dvpoll32 {
58        caddr32_t       dp_fds;         /* pollfd array */
59        uint32_t        dp_nfds;        /* num of pollfd's in dp_fds[] */
60        int32_t         dp_timeout;     /* time out in millisec */
61} dvpoll32_t;
62
63#ifdef  __cplusplus
64}
65#endif
66
67#define RESTARTABLE(_cmd, _result) do { \
68  do { \
69    _result = _cmd; \
70  } while((_result == -1) && (errno == EINTR)); \
71} while(0)
72
73static int
74idevpoll(jint wfd, int dpctl, struct dvpoll a)
75{
76    jlong start, now;
77    int remaining = a.dp_timeout;
78    struct timeval t;
79    int diff;
80
81    gettimeofday(&t, NULL);
82    start = t.tv_sec * 1000 + t.tv_usec / 1000;
83
84    for (;;) {
85        /*  poll(7d) ioctl does not return remaining count */
86        int res = ioctl(wfd, dpctl, &a);
87        if (res < 0 && errno == EINTR) {
88            if (remaining >= 0) {
89                gettimeofday(&t, NULL);
90                now = t.tv_sec * 1000 + t.tv_usec / 1000;
91                diff = now - start;
92                remaining -= diff;
93                if (diff < 0 || remaining <= 0) {
94                    return 0;
95                }
96                start = now;
97                a.dp_timeout = remaining;
98            }
99        } else {
100            return res;
101        }
102    }
103}
104
105JNIEXPORT jint JNICALL
106Java_sun_nio_ch_DevPollArrayWrapper_init(JNIEnv *env, jobject this)
107{
108    int wfd = open("/dev/poll", O_RDWR);
109    if (wfd < 0) {
110       JNU_ThrowIOExceptionWithLastError(env, "Error opening driver");
111       return -1;
112    }
113    return wfd;
114}
115
116JNIEXPORT void JNICALL
117Java_sun_nio_ch_DevPollArrayWrapper_register(JNIEnv *env, jobject this,
118                                             jint wfd, jint fd, jint mask)
119{
120    struct pollfd a[1];
121    int n;
122
123    a[0].fd = fd;
124    a[0].events = mask;
125    a[0].revents = 0;
126
127    n = write(wfd, &a[0], sizeof(a));
128    if (n != sizeof(a)) {
129        if (n < 0) {
130            JNU_ThrowIOExceptionWithLastError(env, "Error writing pollfds");
131        } else {
132            JNU_ThrowIOException(env, "Unexpected number of bytes written");
133        }
134    }
135}
136
137JNIEXPORT void JNICALL
138Java_sun_nio_ch_DevPollArrayWrapper_registerMultiple(JNIEnv *env, jobject this,
139                                                     jint wfd, jlong address,
140                                                     jint len)
141{
142    unsigned char *pollBytes = (unsigned char *)jlong_to_ptr(address);
143    unsigned char *pollEnd = pollBytes + sizeof(struct pollfd) * len;
144    while (pollBytes < pollEnd) {
145        int bytesWritten = write(wfd, pollBytes, (int)(pollEnd - pollBytes));
146        if (bytesWritten < 0) {
147            JNU_ThrowIOExceptionWithLastError(env, "Error writing pollfds");
148            return;
149        }
150        pollBytes += bytesWritten;
151    }
152}
153
154JNIEXPORT jint JNICALL
155Java_sun_nio_ch_DevPollArrayWrapper_poll0(JNIEnv *env, jobject this,
156                                       jlong address, jint numfds,
157                                       jlong timeout, jint wfd)
158{
159    struct dvpoll a;
160    void *pfd = (void *) jlong_to_ptr(address);
161    int result = 0;
162
163    a.dp_fds = pfd;
164    a.dp_nfds = numfds;
165    a.dp_timeout = (int)timeout;
166
167    if (timeout <= 0) {             /* Indefinite or no wait */
168        RESTARTABLE (ioctl(wfd, DP_POLL, &a), result);
169    } else {                        /* Bounded wait; bounded restarts */
170        result = idevpoll(wfd, DP_POLL, a);
171    }
172
173    if (result < 0) {
174        JNU_ThrowIOExceptionWithLastError(env, "Error reading driver");
175        return -1;
176    }
177    return result;
178}
179
180JNIEXPORT void JNICALL
181Java_sun_nio_ch_DevPollArrayWrapper_interrupt(JNIEnv *env, jclass this, jint fd)
182{
183    int fakebuf[1];
184    fakebuf[0] = 1;
185    if (write(fd, fakebuf, 1) < 0) {
186        JNU_ThrowIOExceptionWithLastError(env,
187                                          "Write to interrupt fd failed");
188    }
189}
190