8187443: Forest Consolidation: Move files to unified layout

Reviewed-by: darcy, ihse
This commit is contained in:
Erik Joelsson 2017-09-12 19:03:39 +02:00
parent 270fe13182
commit 3789983e89
56923 changed files with 3 additions and 15727 deletions

View file

@ -0,0 +1,271 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include "jni.h"
#include "jni_util.h"
#include "java_lang_ProcessHandleImpl.h"
#include "java_lang_ProcessHandleImpl_Info.h"
#include "ProcessHandleImpl_unix.h"
#include <fcntl.h>
#include <limits.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <ctype.h>
/*
* Implementation of native ProcessHandleImpl functions for Linux.
* See ProcessHandleImpl_unix.c for more details.
*/
/* Signatures for internal OS specific functions. */
static long long getBoottime(JNIEnv *env);
/* A static offset in milliseconds since boot. */
static long long bootTime_ms;
static long clock_ticks_per_second;
static int pageSize;
void os_initNative(JNIEnv *env, jclass clazz) {
bootTime_ms = getBoottime(env);
clock_ticks_per_second = sysconf(_SC_CLK_TCK);
pageSize = sysconf(_SC_PAGESIZE);
}
jint os_getChildren(JNIEnv *env, jlong jpid, jlongArray jarray,
jlongArray jparentArray, jlongArray jstimesArray) {
return unix_getChildren(env, jpid, jarray, jparentArray, jstimesArray);
}
/**
* Read /proc/<pid>/stat and return the ppid, total cputime and start time.
* -1 is fail; >= 0 is parent pid
* 'total' will contain the running time of 'pid' in nanoseconds.
* 'start' will contain the start time of 'pid' in milliseconds since epoch.
*/
pid_t os_getParentPidAndTimings(JNIEnv *env, pid_t pid,
jlong *totalTime, jlong* startTime) {
FILE* fp;
char buffer[2048];
int statlen;
char fn[32];
char* s;
int parentPid;
long unsigned int utime = 0; // clock tics
long unsigned int stime = 0; // clock tics
long long unsigned int start = 0; // microseconds
/*
* Try to stat and then open /proc/%d/stat
*/
snprintf(fn, sizeof fn, "/proc/%d/stat", pid);
fp = fopen(fn, "r");
if (fp == NULL) {
return -1; // fail, no such /proc/pid/stat
}
/*
* The format is: pid (command) state ppid ...
* As the command could be anything we must find the right most
* ")" and then skip the white spaces that follow it.
*/
statlen = fread(buffer, 1, (sizeof buffer - 1), fp);
fclose(fp);
if (statlen < 0) {
return -1; // parent pid is not available
}
buffer[statlen] = '\0';
s = strchr(buffer, '(');
if (s == NULL) {
return -1; // parent pid is not available
}
// Found start of command, skip to end
s++;
s = strrchr(s, ')');
if (s == NULL) {
return -1; // parent pid is not available
}
s++;
// Scan the needed fields from status, retaining only ppid(4),
// utime (14), stime(15), starttime(22)
if (4 != sscanf(s, " %*c %d %*d %*d %*d %*d %*d %*u %*u %*u %*u %lu %lu %*d %*d %*d %*d %*d %*d %llu",
&parentPid, &utime, &stime, &start)) {
return 0; // not all values parsed; return error
}
*totalTime = (utime + stime) * (jlong)(1000000000 / clock_ticks_per_second);
*startTime = bootTime_ms + ((start * 1000) / clock_ticks_per_second);
return parentPid;
}
void os_getCmdlineAndUserInfo(JNIEnv *env, jobject jinfo, pid_t pid) {
int fd;
int cmdlen = 0;
char *cmdline = NULL, *cmdEnd = NULL; // used for command line args and exe
char *args = NULL;
jstring cmdexe = NULL;
char fn[32];
struct stat stat_buf;
/*
* Stat /proc/<pid> to get the user id
*/
snprintf(fn, sizeof fn, "/proc/%d", pid);
if (stat(fn, &stat_buf) == 0) {
unix_getUserInfo(env, jinfo, stat_buf.st_uid);
JNU_CHECK_EXCEPTION(env);
}
/*
* Try to open /proc/<pid>/cmdline
*/
strncat(fn, "/cmdline", sizeof fn - strnlen(fn, sizeof fn) - 1);
if ((fd = open(fn, O_RDONLY)) < 0) {
return;
}
do { // Block to break out of on errors
int i, truncated = 0;
int count;
char *s;
/*
* The path name read by readlink() is limited to PATH_MAX characters.
* The content of /proc/<pid>/cmdline is limited to PAGE_SIZE characters.
*/
cmdline = (char*)malloc((PATH_MAX > pageSize ? PATH_MAX : pageSize) + 1);
if (cmdline == NULL) {
break;
}
/*
* On Linux, the full path to the executable command is the link in
* /proc/<pid>/exe. But it is only readable for processes we own.
*/
snprintf(fn, sizeof fn, "/proc/%d/exe", pid);
if ((cmdlen = readlink(fn, cmdline, PATH_MAX)) > 0) {
// null terminate and create String to store for command
cmdline[cmdlen] = '\0';
cmdexe = JNU_NewStringPlatform(env, cmdline);
(*env)->ExceptionClear(env); // unconditionally clear any exception
}
/*
* The command-line arguments appear as a set of strings separated by
* null bytes ('\0'), with a further null byte after the last
* string. The last string is only null terminated if the whole command
* line is not exceeding (PAGE_SIZE - 1) characters.
*/
cmdlen = 0;
s = cmdline;
while ((count = read(fd, s, pageSize - cmdlen)) > 0) {
cmdlen += count;
s += count;
}
if (count < 0) {
break;
}
// We have to null-terminate because the process may have changed argv[]
// or because the content in /proc/<pid>/cmdline is truncated.
cmdline[cmdlen] = '\0';
if (cmdlen == pageSize && cmdline[pageSize - 1] != '\0') {
truncated = 1;
} else if (cmdlen == 0) {
// /proc/<pid>/cmdline was empty. This usually happens for kernel processes
// like '[kthreadd]'. We could try to read /proc/<pid>/comm in the future.
}
if (cmdlen > 0 && (cmdexe == NULL || truncated)) {
// We have no exact command or the arguments are truncated.
// In this case we save the command line from /proc/<pid>/cmdline.
args = (char*)malloc(pageSize + 1);
if (args != NULL) {
memcpy(args, cmdline, cmdlen + 1);
for (i = 0; i < cmdlen; i++) {
if (args[i] == '\0') {
args[i] = ' ';
}
}
}
}
i = 0;
if (!truncated) {
// Count the arguments
cmdEnd = &cmdline[cmdlen];
for (s = cmdline; *s != '\0' && (s < cmdEnd); i++) {
s += strnlen(s, (cmdEnd - s)) + 1;
}
}
unix_fillArgArray(env, jinfo, i, cmdline, cmdEnd, cmdexe, args);
} while (0);
if (cmdline != NULL) {
free(cmdline);
}
if (args != NULL) {
free(args);
}
if (fd >= 0) {
close(fd);
}
}
/**
* Read the boottime from /proc/stat.
*/
static long long getBoottime(JNIEnv *env) {
FILE *fp;
char *line = NULL;
size_t len = 0;
long long bootTime = 0;
fp = fopen("/proc/stat", "r");
if (fp == NULL) {
return -1;
}
while (getline(&line, &len, fp) != -1) {
if (sscanf(line, "btime %llu", &bootTime) == 1) {
break;
}
}
free(line);
if (fp != 0) {
fclose(fp);
}
return bootTime * 1000;
}

View file

@ -0,0 +1,230 @@
/*
* Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
/* CopyrightVersion 1.2 */
/* This is a special library that should be loaded before libc &
* libthread to interpose the signal handler installation functions:
* sigaction(), signal(), sigset().
* Used for signal-chaining. See RFE 4381843.
*/
#include <signal.h>
#include <dlfcn.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#define bool int
#define true 1
#define false 0
#define MASK(sig) ((uint64_t)1 << (sig-1)) // 0 is not a signal.
// Check whether all signals fit into jvmsigs. -1 as MASK shifts by -1.
#if (64 < NSIG-1)
#error "Not all signals can be encoded in jvmsigs. Adapt its type!"
#endif
static struct sigaction sact[NSIG]; /* saved signal handlers */
static uint64_t jvmsigs = 0; /* signals used by jvm */
/* used to synchronize the installation of signal handlers */
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
static pthread_t tid = 0;
typedef void (*sa_handler_t)(int);
typedef void (*sa_sigaction_t)(int, siginfo_t *, void *);
typedef sa_handler_t (*signal_t)(int, sa_handler_t);
typedef int (*sigaction_t)(int, const struct sigaction *, struct sigaction *);
static signal_t os_signal = 0; /* os's version of signal()/sigset() */
static sigaction_t os_sigaction = 0; /* os's version of sigaction() */
static bool jvm_signal_installing = false;
static bool jvm_signal_installed = false;
static void signal_lock() {
pthread_mutex_lock(&mutex);
/* When the jvm is installing its set of signal handlers, threads
* other than the jvm thread should wait */
if (jvm_signal_installing) {
if (tid != pthread_self()) {
pthread_cond_wait(&cond, &mutex);
}
}
}
static void signal_unlock() {
pthread_mutex_unlock(&mutex);
}
static sa_handler_t call_os_signal(int sig, sa_handler_t disp,
bool is_sigset) {
if (os_signal == NULL) {
if (!is_sigset) {
os_signal = (signal_t)dlsym(RTLD_NEXT, "signal");
} else {
os_signal = (signal_t)dlsym(RTLD_NEXT, "sigset");
}
if (os_signal == NULL) {
printf("%s\n", dlerror());
exit(0);
}
}
return (*os_signal)(sig, disp);
}
static void save_signal_handler(int sig, sa_handler_t disp) {
sigset_t set;
sact[sig].sa_handler = disp;
sigemptyset(&set);
sact[sig].sa_mask = set;
sact[sig].sa_flags = 0;
}
static sa_handler_t set_signal(int sig, sa_handler_t disp, bool is_sigset) {
sa_handler_t oldhandler;
bool sigused;
signal_lock();
sigused = (sig < NSIG) && ((MASK(sig) & jvmsigs) != 0);
if (jvm_signal_installed && sigused) {
/* jvm has installed its signal handler for this signal. */
/* Save the handler. Don't really install it. */
oldhandler = sact[sig].sa_handler;
save_signal_handler(sig, disp);
signal_unlock();
return oldhandler;
} else if (sig < NSIG && jvm_signal_installing) {
/* jvm is installing its signal handlers. Install the new
* handlers and save the old ones. jvm uses sigaction().
* Leave the piece here just in case. */
oldhandler = call_os_signal(sig, disp, is_sigset);
save_signal_handler(sig, oldhandler);
/* Record the signals used by jvm */
jvmsigs |= MASK(sig);
signal_unlock();
return oldhandler;
} else {
/* jvm has no relation with this signal (yet). Install the
* the handler. */
oldhandler = call_os_signal(sig, disp, is_sigset);
signal_unlock();
return oldhandler;
}
}
sa_handler_t signal(int sig, sa_handler_t disp) {
return set_signal(sig, disp, false);
}
sa_handler_t sigset(int sig, sa_handler_t disp) {
return set_signal(sig, disp, true);
}
static int call_os_sigaction(int sig, const struct sigaction *act,
struct sigaction *oact) {
if (os_sigaction == NULL) {
os_sigaction = (sigaction_t)dlsym(RTLD_NEXT, "sigaction");
if (os_sigaction == NULL) {
printf("%s\n", dlerror());
exit(0);
}
}
return (*os_sigaction)(sig, act, oact);
}
int sigaction(int sig, const struct sigaction *act, struct sigaction *oact) {
int res;
bool sigused;
struct sigaction oldAct;
signal_lock();
sigused = (sig < NSIG) && ((MASK(sig) & jvmsigs) != 0);
if (jvm_signal_installed && sigused) {
/* jvm has installed its signal handler for this signal. */
/* Save the handler. Don't really install it. */
if (oact != NULL) {
*oact = sact[sig];
}
if (act != NULL) {
sact[sig] = *act;
}
signal_unlock();
return 0;
} else if (sig < NSIG && jvm_signal_installing) {
/* jvm is installing its signal handlers. Install the new
* handlers and save the old ones. */
res = call_os_sigaction(sig, act, &oldAct);
sact[sig] = oldAct;
if (oact != NULL) {
*oact = oldAct;
}
/* Record the signals used by jvm */
jvmsigs |= MASK(sig);
signal_unlock();
return res;
} else {
/* jvm has no relation with this signal (yet). Install the
* the handler. */
res = call_os_sigaction(sig, act, oact);
signal_unlock();
return res;
}
}
/* The three functions for the jvm to call into */
void JVM_begin_signal_setting() {
signal_lock();
jvm_signal_installing = true;
tid = pthread_self();
signal_unlock();
}
void JVM_end_signal_setting() {
signal_lock();
jvm_signal_installed = true;
jvm_signal_installing = false;
pthread_cond_broadcast(&cond);
signal_unlock();
}
struct sigaction *JVM_get_signal_action(int sig) {
/* Does race condition make sense here? */
if ((MASK(sig) & jvmsigs) != 0) {
return &sact[sig];
}
return NULL;
}

View file

@ -0,0 +1,458 @@
/*
* Copyright (c) 2001, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include <assert.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/uio.h>
#include <unistd.h>
#include <errno.h>
#include <poll.h>
#include "jvm.h"
#include "net_util.h"
/*
* Stack allocated by thread when doing blocking operation
*/
typedef struct threadEntry {
pthread_t thr; /* this thread */
struct threadEntry *next; /* next thread */
int intr; /* interrupted */
} threadEntry_t;
/*
* Heap allocated during initialized - one entry per fd
*/
typedef struct {
pthread_mutex_t lock; /* fd lock */
threadEntry_t *threads; /* threads blocked on fd */
} fdEntry_t;
/*
* Signal to unblock thread
*/
static int sigWakeup = (__SIGRTMAX - 2);
/*
* fdTable holds one entry per file descriptor, up to a certain
* maximum.
* Theoretically, the number of possible file descriptors can get
* large, though usually it does not. Entries for small value file
* descriptors are kept in a simple table, which covers most scenarios.
* Entries for large value file descriptors are kept in an overflow
* table, which is organized as a sparse two dimensional array whose
* slabs are allocated on demand. This covers all corner cases while
* keeping memory consumption reasonable.
*/
/* Base table for low value file descriptors */
static fdEntry_t* fdTable = NULL;
/* Maximum size of base table (in number of entries). */
static const int fdTableMaxSize = 0x1000; /* 4K */
/* Actual size of base table (in number of entries) */
static int fdTableLen = 0;
/* Max. theoretical number of file descriptors on system. */
static int fdLimit = 0;
/* Overflow table, should base table not be large enough. Organized as
* an array of n slabs, each holding 64k entries.
*/
static fdEntry_t** fdOverflowTable = NULL;
/* Number of slabs in the overflow table */
static int fdOverflowTableLen = 0;
/* Number of entries in one slab */
static const int fdOverflowTableSlabSize = 0x10000; /* 64k */
pthread_mutex_t fdOverflowTableLock = PTHREAD_MUTEX_INITIALIZER;
/*
* Null signal handler
*/
static void sig_wakeup(int sig) {
}
/*
* Initialization routine (executed when library is loaded)
* Allocate fd tables and sets up signal handler.
*/
static void __attribute((constructor)) init() {
struct rlimit nbr_files;
sigset_t sigset;
struct sigaction sa;
int i = 0;
/* Determine the maximum number of possible file descriptors. */
if (-1 == getrlimit(RLIMIT_NOFILE, &nbr_files)) {
fprintf(stderr, "library initialization failed - "
"unable to get max # of allocated fds\n");
abort();
}
if (nbr_files.rlim_max != RLIM_INFINITY) {
fdLimit = nbr_files.rlim_max;
} else {
/* We just do not know. */
fdLimit = INT_MAX;
}
/* Allocate table for low value file descriptors. */
fdTableLen = fdLimit < fdTableMaxSize ? fdLimit : fdTableMaxSize;
fdTable = (fdEntry_t*) calloc(fdTableLen, sizeof(fdEntry_t));
if (fdTable == NULL) {
fprintf(stderr, "library initialization failed - "
"unable to allocate file descriptor table - out of memory");
abort();
} else {
for (i = 0; i < fdTableLen; i ++) {
pthread_mutex_init(&fdTable[i].lock, NULL);
}
}
/* Allocate overflow table, if needed */
if (fdLimit > fdTableMaxSize) {
fdOverflowTableLen = ((fdLimit - fdTableMaxSize) / fdOverflowTableSlabSize) + 1;
fdOverflowTable = (fdEntry_t**) calloc(fdOverflowTableLen, sizeof(fdEntry_t*));
if (fdOverflowTable == NULL) {
fprintf(stderr, "library initialization failed - "
"unable to allocate file descriptor overflow table - out of memory");
abort();
}
}
/*
* Setup the signal handler
*/
sa.sa_handler = sig_wakeup;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
sigaction(sigWakeup, &sa, NULL);
sigemptyset(&sigset);
sigaddset(&sigset, sigWakeup);
sigprocmask(SIG_UNBLOCK, &sigset, NULL);
}
/*
* Return the fd table for this fd.
*/
static inline fdEntry_t *getFdEntry(int fd)
{
fdEntry_t* result = NULL;
if (fd < 0) {
return NULL;
}
/* This should not happen. If it does, our assumption about
* max. fd value was wrong. */
assert(fd < fdLimit);
if (fd < fdTableMaxSize) {
/* fd is in base table. */
assert(fd < fdTableLen);
result = &fdTable[fd];
} else {
/* fd is in overflow table. */
const int indexInOverflowTable = fd - fdTableMaxSize;
const int rootindex = indexInOverflowTable / fdOverflowTableSlabSize;
const int slabindex = indexInOverflowTable % fdOverflowTableSlabSize;
fdEntry_t* slab = NULL;
assert(rootindex < fdOverflowTableLen);
assert(slabindex < fdOverflowTableSlabSize);
pthread_mutex_lock(&fdOverflowTableLock);
/* Allocate new slab in overflow table if needed */
if (fdOverflowTable[rootindex] == NULL) {
fdEntry_t* const newSlab =
(fdEntry_t*)calloc(fdOverflowTableSlabSize, sizeof(fdEntry_t));
if (newSlab == NULL) {
fprintf(stderr, "Unable to allocate file descriptor overflow"
" table slab - out of memory");
pthread_mutex_unlock(&fdOverflowTableLock);
abort();
} else {
int i;
for (i = 0; i < fdOverflowTableSlabSize; i ++) {
pthread_mutex_init(&newSlab[i].lock, NULL);
}
fdOverflowTable[rootindex] = newSlab;
}
}
pthread_mutex_unlock(&fdOverflowTableLock);
slab = fdOverflowTable[rootindex];
result = &slab[slabindex];
}
return result;
}
/*
* Start a blocking operation :-
* Insert thread onto thread list for the fd.
*/
static inline void startOp(fdEntry_t *fdEntry, threadEntry_t *self)
{
self->thr = pthread_self();
self->intr = 0;
pthread_mutex_lock(&(fdEntry->lock));
{
self->next = fdEntry->threads;
fdEntry->threads = self;
}
pthread_mutex_unlock(&(fdEntry->lock));
}
/*
* End a blocking operation :-
* Remove thread from thread list for the fd
* If fd has been interrupted then set errno to EBADF
*/
static inline void endOp
(fdEntry_t *fdEntry, threadEntry_t *self)
{
int orig_errno = errno;
pthread_mutex_lock(&(fdEntry->lock));
{
threadEntry_t *curr, *prev=NULL;
curr = fdEntry->threads;
while (curr != NULL) {
if (curr == self) {
if (curr->intr) {
orig_errno = EBADF;
}
if (prev == NULL) {
fdEntry->threads = curr->next;
} else {
prev->next = curr->next;
}
break;
}
prev = curr;
curr = curr->next;
}
}
pthread_mutex_unlock(&(fdEntry->lock));
errno = orig_errno;
}
/*
* Close or dup2 a file descriptor ensuring that all threads blocked on
* the file descriptor are notified via a wakeup signal.
*
* fd1 < 0 => close(fd2)
* fd1 >= 0 => dup2(fd1, fd2)
*
* Returns -1 with errno set if operation fails.
*/
static int closefd(int fd1, int fd2) {
int rv, orig_errno;
fdEntry_t *fdEntry = getFdEntry(fd2);
if (fdEntry == NULL) {
errno = EBADF;
return -1;
}
/*
* Lock the fd to hold-off additional I/O on this fd.
*/
pthread_mutex_lock(&(fdEntry->lock));
{
/*
* And close/dup the file descriptor
* (restart if interrupted by signal)
*/
do {
if (fd1 < 0) {
rv = close(fd2);
} else {
rv = dup2(fd1, fd2);
}
} while (rv == -1 && errno == EINTR);
/*
* Send a wakeup signal to all threads blocked on this
* file descriptor.
*/
threadEntry_t *curr = fdEntry->threads;
while (curr != NULL) {
curr->intr = 1;
pthread_kill( curr->thr, sigWakeup );
curr = curr->next;
}
}
/*
* Unlock without destroying errno
*/
orig_errno = errno;
pthread_mutex_unlock(&(fdEntry->lock));
errno = orig_errno;
return rv;
}
/*
* Wrapper for dup2 - same semantics as dup2 system call except
* that any threads blocked in an I/O system call on fd2 will be
* preempted and return -1/EBADF;
*/
int NET_Dup2(int fd, int fd2) {
if (fd < 0) {
errno = EBADF;
return -1;
}
return closefd(fd, fd2);
}
/*
* Wrapper for close - same semantics as close system call
* except that any threads blocked in an I/O on fd will be
* preempted and the I/O system call will return -1/EBADF.
*/
int NET_SocketClose(int fd) {
return closefd(-1, fd);
}
/************** Basic I/O operations here ***************/
/*
* Macro to perform a blocking IO operation. Restarts
* automatically if interrupted by signal (other than
* our wakeup signal)
*/
#define BLOCKING_IO_RETURN_INT(FD, FUNC) { \
int ret; \
threadEntry_t self; \
fdEntry_t *fdEntry = getFdEntry(FD); \
if (fdEntry == NULL) { \
errno = EBADF; \
return -1; \
} \
do { \
startOp(fdEntry, &self); \
ret = FUNC; \
endOp(fdEntry, &self); \
} while (ret == -1 && errno == EINTR); \
return ret; \
}
int NET_Read(int s, void* buf, size_t len) {
BLOCKING_IO_RETURN_INT( s, recv(s, buf, len, 0) );
}
int NET_NonBlockingRead(int s, void* buf, size_t len) {
BLOCKING_IO_RETURN_INT( s, recv(s, buf, len, MSG_DONTWAIT) );
}
int NET_ReadV(int s, const struct iovec * vector, int count) {
BLOCKING_IO_RETURN_INT( s, readv(s, vector, count) );
}
int NET_RecvFrom(int s, void *buf, int len, unsigned int flags,
struct sockaddr *from, socklen_t *fromlen) {
BLOCKING_IO_RETURN_INT( s, recvfrom(s, buf, len, flags, from, fromlen) );
}
int NET_Send(int s, void *msg, int len, unsigned int flags) {
BLOCKING_IO_RETURN_INT( s, send(s, msg, len, flags) );
}
int NET_WriteV(int s, const struct iovec * vector, int count) {
BLOCKING_IO_RETURN_INT( s, writev(s, vector, count) );
}
int NET_SendTo(int s, const void *msg, int len, unsigned int
flags, const struct sockaddr *to, int tolen) {
BLOCKING_IO_RETURN_INT( s, sendto(s, msg, len, flags, to, tolen) );
}
int NET_Accept(int s, struct sockaddr *addr, socklen_t *addrlen) {
BLOCKING_IO_RETURN_INT( s, accept(s, addr, addrlen) );
}
int NET_Connect(int s, struct sockaddr *addr, int addrlen) {
BLOCKING_IO_RETURN_INT( s, connect(s, addr, addrlen) );
}
int NET_Poll(struct pollfd *ufds, unsigned int nfds, int timeout) {
BLOCKING_IO_RETURN_INT( ufds[0].fd, poll(ufds, nfds, timeout) );
}
/*
* Wrapper for poll(s, timeout).
* Auto restarts with adjusted timeout if interrupted by
* signal other than our wakeup signal.
*/
int NET_Timeout(JNIEnv *env, int s, long timeout, jlong nanoTimeStamp) {
jlong prevNanoTime = nanoTimeStamp;
jlong nanoTimeout = (jlong)timeout * NET_NSEC_PER_MSEC;
fdEntry_t *fdEntry = getFdEntry(s);
/*
* Check that fd hasn't been closed.
*/
if (fdEntry == NULL) {
errno = EBADF;
return -1;
}
for(;;) {
struct pollfd pfd;
int rv;
threadEntry_t self;
/*
* Poll the fd. If interrupted by our wakeup signal
* errno will be set to EBADF.
*/
pfd.fd = s;
pfd.events = POLLIN | POLLERR;
startOp(fdEntry, &self);
rv = poll(&pfd, 1, nanoTimeout / NET_NSEC_PER_MSEC);
endOp(fdEntry, &self);
/*
* If interrupted then adjust timeout. If timeout
* has expired return 0 (indicating timeout expired).
*/
if (rv < 0 && errno == EINTR) {
jlong newNanoTime = JVM_NanoTime(env, 0);
nanoTimeout -= newNanoTime - prevNanoTime;
if (nanoTimeout < NET_NSEC_PER_MSEC) {
return 0;
}
prevNanoTime = newNanoTime;
} else {
return rv;
}
}
}

View file

@ -0,0 +1,97 @@
/*
* Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include "jni.h"
#include "jni_util.h"
#include "jvm.h"
#include "jlong.h"
#include "nio_util.h"
#include "sun_nio_ch_EPoll.h"
#include <dlfcn.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/epoll.h>
JNIEXPORT jint JNICALL
Java_sun_nio_ch_EPoll_eventSize(JNIEnv* env, jclass this)
{
return sizeof(struct epoll_event);
}
JNIEXPORT jint JNICALL
Java_sun_nio_ch_EPoll_eventsOffset(JNIEnv* env, jclass this)
{
return offsetof(struct epoll_event, events);
}
JNIEXPORT jint JNICALL
Java_sun_nio_ch_EPoll_dataOffset(JNIEnv* env, jclass this)
{
return offsetof(struct epoll_event, data);
}
JNIEXPORT jint JNICALL
Java_sun_nio_ch_EPoll_epollCreate(JNIEnv *env, jclass c) {
/*
* epoll_create expects a size as a hint to the kernel about how to
* dimension internal structures. We can't predict the size in advance.
*/
int epfd = epoll_create(256);
if (epfd < 0) {
JNU_ThrowIOExceptionWithLastError(env, "epoll_create failed");
}
return epfd;
}
JNIEXPORT jint JNICALL
Java_sun_nio_ch_EPoll_epollCtl(JNIEnv *env, jclass c, jint epfd,
jint opcode, jint fd, jint events)
{
struct epoll_event event;
int res;
event.events = events;
event.data.fd = fd;
RESTARTABLE(epoll_ctl(epfd, (int)opcode, (int)fd, &event), res);
return (res == 0) ? 0 : errno;
}
JNIEXPORT jint JNICALL
Java_sun_nio_ch_EPoll_epollWait(JNIEnv *env, jclass c,
jint epfd, jlong address, jint numfds)
{
struct epoll_event *events = jlong_to_ptr(address);
int res;
RESTARTABLE(epoll_wait(epfd, events, numfds, -1), res);
if (res < 0) {
JNU_ThrowIOExceptionWithLastError(env, "epoll_wait failed");
}
return res;
}

View file

@ -0,0 +1,160 @@
/*
* Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include "jni.h"
#include "jni_util.h"
#include "jvm.h"
#include "jlong.h"
#include "sun_nio_ch_EPollArrayWrapper.h"
#include <unistd.h>
#include <sys/time.h>
#include <sys/epoll.h>
#define RESTARTABLE(_cmd, _result) do { \
do { \
_result = _cmd; \
} while((_result == -1) && (errno == EINTR)); \
} while(0)
static int
iepoll(int epfd, struct epoll_event *events, int numfds, jlong timeout)
{
jlong start, now;
int remaining = timeout;
struct timeval t;
int diff;
gettimeofday(&t, NULL);
start = t.tv_sec * 1000 + t.tv_usec / 1000;
for (;;) {
int res = epoll_wait(epfd, events, numfds, remaining);
if (res < 0 && errno == EINTR) {
if (remaining >= 0) {
gettimeofday(&t, NULL);
now = t.tv_sec * 1000 + t.tv_usec / 1000;
diff = now - start;
remaining -= diff;
if (diff < 0 || remaining <= 0) {
return 0;
}
start = now;
}
} else {
return res;
}
}
}
JNIEXPORT void JNICALL
Java_sun_nio_ch_EPollArrayWrapper_init(JNIEnv *env, jclass this)
{
}
JNIEXPORT jint JNICALL
Java_sun_nio_ch_EPollArrayWrapper_epollCreate(JNIEnv *env, jobject this)
{
/*
* epoll_create expects a size as a hint to the kernel about how to
* dimension internal structures. We can't predict the size in advance.
*/
int epfd = epoll_create(256);
if (epfd < 0) {
JNU_ThrowIOExceptionWithLastError(env, "epoll_create failed");
}
return epfd;
}
JNIEXPORT jint JNICALL
Java_sun_nio_ch_EPollArrayWrapper_sizeofEPollEvent(JNIEnv* env, jclass this)
{
return sizeof(struct epoll_event);
}
JNIEXPORT jint JNICALL
Java_sun_nio_ch_EPollArrayWrapper_offsetofData(JNIEnv* env, jclass this)
{
return offsetof(struct epoll_event, data);
}
JNIEXPORT void JNICALL
Java_sun_nio_ch_EPollArrayWrapper_epollCtl(JNIEnv *env, jobject this, jint epfd,
jint opcode, jint fd, jint events)
{
struct epoll_event event;
int res;
event.events = events;
event.data.fd = fd;
RESTARTABLE(epoll_ctl(epfd, (int)opcode, (int)fd, &event), res);
/*
* A channel may be registered with several Selectors. When each Selector
* is polled a EPOLL_CTL_DEL op will be inserted into its pending update
* list to remove the file descriptor from epoll. The "last" Selector will
* close the file descriptor which automatically unregisters it from each
* epoll descriptor. To avoid costly synchronization between Selectors we
* allow pending updates to be processed, ignoring errors. The errors are
* harmless as the last update for the file descriptor is guaranteed to
* be EPOLL_CTL_DEL.
*/
if (res < 0 && errno != EBADF && errno != ENOENT && errno != EPERM) {
JNU_ThrowIOExceptionWithLastError(env, "epoll_ctl failed");
}
}
JNIEXPORT jint JNICALL
Java_sun_nio_ch_EPollArrayWrapper_epollWait(JNIEnv *env, jobject this,
jlong address, jint numfds,
jlong timeout, jint epfd)
{
struct epoll_event *events = jlong_to_ptr(address);
int res;
if (timeout <= 0) { /* Indefinite or no wait */
RESTARTABLE(epoll_wait(epfd, events, numfds, timeout), res);
} else { /* Bounded wait; bounded restarts */
res = iepoll(epfd, events, numfds, timeout);
}
if (res < 0) {
JNU_ThrowIOExceptionWithLastError(env, "epoll_wait failed");
}
return res;
}
JNIEXPORT void JNICALL
Java_sun_nio_ch_EPollArrayWrapper_interrupt(JNIEnv *env, jobject this, jint fd)
{
int fakebuf[1];
fakebuf[0] = 1;
if (write(fd, fakebuf, 1) < 0) {
JNU_ThrowIOExceptionWithLastError(env,"write to interrupt fd failed");
}
}

View file

@ -0,0 +1,76 @@
/*
* Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include "jni.h"
#include "jni_util.h"
#include "jvm.h"
#include "jlong.h"
#include "nio_util.h"
#include "sun_nio_ch_EPollPort.h"
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
JNIEXPORT void JNICALL
Java_sun_nio_ch_EPollPort_socketpair(JNIEnv* env, jclass clazz, jintArray sv) {
int sp[2];
if (socketpair(PF_UNIX, SOCK_STREAM, 0, sp) == -1) {
JNU_ThrowIOExceptionWithLastError(env, "socketpair failed");
} else {
jint res[2];
res[0] = (jint)sp[0];
res[1] = (jint)sp[1];
(*env)->SetIntArrayRegion(env, sv, 0, 2, &res[0]);
}
}
JNIEXPORT void JNICALL
Java_sun_nio_ch_EPollPort_interrupt(JNIEnv *env, jclass c, jint fd) {
int res;
int buf[1];
buf[0] = 1;
RESTARTABLE(write(fd, buf, 1), res);
if (res < 0) {
JNU_ThrowIOExceptionWithLastError(env, "write failed");
}
}
JNIEXPORT void JNICALL
Java_sun_nio_ch_EPollPort_drain1(JNIEnv *env, jclass cl, jint fd) {
int res;
char buf[1];
RESTARTABLE(read(fd, buf, 1), res);
if (res < 0) {
JNU_ThrowIOExceptionWithLastError(env, "drain1 failed");
}
}
JNIEXPORT void JNICALL
Java_sun_nio_ch_EPollPort_close0(JNIEnv *env, jclass c, jint fd) {
int res;
RESTARTABLE(close(fd), res);
}

View file

@ -0,0 +1,232 @@
/*
* Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include "jni.h"
#include "jni_util.h"
#include "jvm.h"
#include "jlong.h"
#include <stdio.h>
#include <string.h>
#include <dlfcn.h>
#include <errno.h>
#include <mntent.h>
#include "sun_nio_fs_LinuxNativeDispatcher.h"
typedef size_t fgetxattr_func(int fd, const char* name, void* value, size_t size);
typedef int fsetxattr_func(int fd, const char* name, void* value, size_t size, int flags);
typedef int fremovexattr_func(int fd, const char* name);
typedef int flistxattr_func(int fd, char* list, size_t size);
fgetxattr_func* my_fgetxattr_func = NULL;
fsetxattr_func* my_fsetxattr_func = NULL;
fremovexattr_func* my_fremovexattr_func = NULL;
flistxattr_func* my_flistxattr_func = NULL;
static jfieldID entry_name;
static jfieldID entry_dir;
static jfieldID entry_fstype;
static jfieldID entry_options;
static void throwUnixException(JNIEnv* env, int errnum) {
jobject x = JNU_NewObjectByName(env, "sun/nio/fs/UnixException",
"(I)V", errnum);
if (x != NULL) {
(*env)->Throw(env, x);
}
}
JNIEXPORT void JNICALL
Java_sun_nio_fs_LinuxNativeDispatcher_init(JNIEnv *env, jclass clazz)
{
my_fgetxattr_func = (fgetxattr_func*)dlsym(RTLD_DEFAULT, "fgetxattr");
my_fsetxattr_func = (fsetxattr_func*)dlsym(RTLD_DEFAULT, "fsetxattr");
my_fremovexattr_func = (fremovexattr_func*)dlsym(RTLD_DEFAULT, "fremovexattr");
my_flistxattr_func = (flistxattr_func*)dlsym(RTLD_DEFAULT, "flistxattr");
clazz = (*env)->FindClass(env, "sun/nio/fs/UnixMountEntry");
CHECK_NULL(clazz);
entry_name = (*env)->GetFieldID(env, clazz, "name", "[B");
CHECK_NULL(entry_name);
entry_dir = (*env)->GetFieldID(env, clazz, "dir", "[B");
CHECK_NULL(entry_dir);
entry_fstype = (*env)->GetFieldID(env, clazz, "fstype", "[B");
CHECK_NULL(entry_fstype);
entry_options = (*env)->GetFieldID(env, clazz, "opts", "[B");
CHECK_NULL(entry_options);
}
JNIEXPORT jint JNICALL
Java_sun_nio_fs_LinuxNativeDispatcher_fgetxattr0(JNIEnv* env, jclass clazz,
jint fd, jlong nameAddress, jlong valueAddress, jint valueLen)
{
size_t res = -1;
const char* name = jlong_to_ptr(nameAddress);
void* value = jlong_to_ptr(valueAddress);
if (my_fgetxattr_func == NULL) {
errno = ENOTSUP;
} else {
/* EINTR not documented */
res = (*my_fgetxattr_func)(fd, name, value, valueLen);
}
if (res == (size_t)-1)
throwUnixException(env, errno);
return (jint)res;
}
JNIEXPORT void JNICALL
Java_sun_nio_fs_LinuxNativeDispatcher_fsetxattr0(JNIEnv* env, jclass clazz,
jint fd, jlong nameAddress, jlong valueAddress, jint valueLen)
{
int res = -1;
const char* name = jlong_to_ptr(nameAddress);
void* value = jlong_to_ptr(valueAddress);
if (my_fsetxattr_func == NULL) {
errno = ENOTSUP;
} else {
/* EINTR not documented */
res = (*my_fsetxattr_func)(fd, name, value, valueLen, 0);
}
if (res == -1)
throwUnixException(env, errno);
}
JNIEXPORT void JNICALL
Java_sun_nio_fs_LinuxNativeDispatcher_fremovexattr0(JNIEnv* env, jclass clazz,
jint fd, jlong nameAddress)
{
int res = -1;
const char* name = jlong_to_ptr(nameAddress);
if (my_fremovexattr_func == NULL) {
errno = ENOTSUP;
} else {
/* EINTR not documented */
res = (*my_fremovexattr_func)(fd, name);
}
if (res == -1)
throwUnixException(env, errno);
}
JNIEXPORT jint JNICALL
Java_sun_nio_fs_LinuxNativeDispatcher_flistxattr(JNIEnv* env, jclass clazz,
jint fd, jlong listAddress, jint size)
{
size_t res = -1;
char* list = jlong_to_ptr(listAddress);
if (my_flistxattr_func == NULL) {
errno = ENOTSUP;
} else {
/* EINTR not documented */
res = (*my_flistxattr_func)(fd, list, (size_t)size);
}
if (res == (size_t)-1)
throwUnixException(env, errno);
return (jint)res;
}
JNIEXPORT jlong JNICALL
Java_sun_nio_fs_LinuxNativeDispatcher_setmntent0(JNIEnv* env, jclass this, jlong pathAddress,
jlong modeAddress)
{
FILE* fp = NULL;
const char* path = (const char*)jlong_to_ptr(pathAddress);
const char* mode = (const char*)jlong_to_ptr(modeAddress);
do {
fp = setmntent(path, mode);
} while (fp == NULL && errno == EINTR);
if (fp == NULL) {
throwUnixException(env, errno);
}
return ptr_to_jlong(fp);
}
JNIEXPORT jint JNICALL
Java_sun_nio_fs_LinuxNativeDispatcher_getmntent(JNIEnv* env, jclass this,
jlong value, jobject entry)
{
struct mntent ent;
char buf[1024];
int buflen = sizeof(buf);
struct mntent* m;
FILE* fp = jlong_to_ptr(value);
jsize len;
jbyteArray bytes;
char* name;
char* dir;
char* fstype;
char* options;
m = getmntent_r(fp, &ent, (char*)&buf, buflen);
if (m == NULL)
return -1;
name = m->mnt_fsname;
dir = m->mnt_dir;
fstype = m->mnt_type;
options = m->mnt_opts;
len = strlen(name);
bytes = (*env)->NewByteArray(env, len);
if (bytes == NULL)
return -1;
(*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte*)name);
(*env)->SetObjectField(env, entry, entry_name, bytes);
len = strlen(dir);
bytes = (*env)->NewByteArray(env, len);
if (bytes == NULL)
return -1;
(*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte*)dir);
(*env)->SetObjectField(env, entry, entry_dir, bytes);
len = strlen(fstype);
bytes = (*env)->NewByteArray(env, len);
if (bytes == NULL)
return -1;
(*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte*)fstype);
(*env)->SetObjectField(env, entry, entry_fstype, bytes);
len = strlen(options);
bytes = (*env)->NewByteArray(env, len);
if (bytes == NULL)
return -1;
(*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte*)options);
(*env)->SetObjectField(env, entry, entry_options, bytes);
return 0;
}
JNIEXPORT void JNICALL
Java_sun_nio_fs_LinuxNativeDispatcher_endmntent(JNIEnv* env, jclass this, jlong stream)
{
FILE* fp = jlong_to_ptr(stream);
/* FIXME - man page doesn't explain how errors are returned */
endmntent(fp);
}

View file

@ -0,0 +1,153 @@
/*
* Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include "jni.h"
#include "jni_util.h"
#include "jvm.h"
#include "jlong.h"
#include <stdlib.h>
#include <dlfcn.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <poll.h>
#include <sys/inotify.h>
#include "sun_nio_fs_LinuxWatchService.h"
static void throwUnixException(JNIEnv* env, int errnum) {
jobject x = JNU_NewObjectByName(env, "sun/nio/fs/UnixException",
"(I)V", errnum);
if (x != NULL) {
(*env)->Throw(env, x);
}
}
JNIEXPORT jint JNICALL
Java_sun_nio_fs_LinuxWatchService_eventSize(JNIEnv *env, jclass clazz)
{
return (jint)sizeof(struct inotify_event);
}
JNIEXPORT jintArray JNICALL
Java_sun_nio_fs_LinuxWatchService_eventOffsets(JNIEnv *env, jclass clazz)
{
jintArray result = (*env)->NewIntArray(env, 5);
if (result != NULL) {
jint arr[5];
arr[0] = (jint)offsetof(struct inotify_event, wd);
arr[1] = (jint)offsetof(struct inotify_event, mask);
arr[2] = (jint)offsetof(struct inotify_event, cookie);
arr[3] = (jint)offsetof(struct inotify_event, len);
arr[4] = (jint)offsetof(struct inotify_event, name);
(*env)->SetIntArrayRegion(env, result, 0, 5, arr);
}
return result;
}
JNIEXPORT jint JNICALL
Java_sun_nio_fs_LinuxWatchService_inotifyInit
(JNIEnv* env, jclass clazz)
{
int ifd = inotify_init();
if (ifd == -1) {
throwUnixException(env, errno);
}
return (jint)ifd;
}
JNIEXPORT jint JNICALL
Java_sun_nio_fs_LinuxWatchService_inotifyAddWatch
(JNIEnv* env, jclass clazz, jint fd, jlong address, jint mask)
{
int wfd = -1;
const char* path = (const char*)jlong_to_ptr(address);
wfd = inotify_add_watch((int)fd, path, mask);
if (wfd == -1) {
throwUnixException(env, errno);
}
return (jint)wfd;
}
JNIEXPORT void JNICALL
Java_sun_nio_fs_LinuxWatchService_inotifyRmWatch
(JNIEnv* env, jclass clazz, jint fd, jint wd)
{
int err = inotify_rm_watch((int)fd, (int)wd);
if (err == -1)
throwUnixException(env, errno);
}
JNIEXPORT void JNICALL
Java_sun_nio_fs_LinuxWatchService_configureBlocking
(JNIEnv* env, jclass clazz, jint fd, jboolean blocking)
{
int flags = fcntl(fd, F_GETFL);
if ((blocking == JNI_FALSE) && !(flags & O_NONBLOCK))
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
else if ((blocking == JNI_TRUE) && (flags & O_NONBLOCK))
fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
}
JNIEXPORT void JNICALL
Java_sun_nio_fs_LinuxWatchService_socketpair
(JNIEnv* env, jclass clazz, jintArray sv)
{
int sp[2];
if (socketpair(PF_UNIX, SOCK_STREAM, 0, sp) == -1) {
throwUnixException(env, errno);
} else {
jint res[2];
res[0] = (jint)sp[0];
res[1] = (jint)sp[1];
(*env)->SetIntArrayRegion(env, sv, 0, 2, &res[0]);
}
}
JNIEXPORT jint JNICALL
Java_sun_nio_fs_LinuxWatchService_poll
(JNIEnv* env, jclass clazz, jint fd1, jint fd2)
{
struct pollfd ufds[2];
int n;
ufds[0].fd = fd1;
ufds[0].events = POLLIN;
ufds[1].fd = fd2;
ufds[1].events = POLLIN;
n = poll(&ufds[0], 2, -1);
if (n == -1) {
if (errno == EINTR) {
n = 0;
} else {
throwUnixException(env, errno);
}
}
return (jint)n;
}