More progress. More work.
[tripe-android] / jni.c
CommitLineData
8eabb4ff
MW
1/* -*-c-*-
2 *
3 * Native-code portions of the project
4 *
5 * (c) 2018 Straylight/Edgeware
6 */
7
8/*----- Licensing notice --------------------------------------------------*
9 *
10 * This file is part of the Trivial IP Encryption (TrIPE) Android app.
11 *
12 * TrIPE is free software: you can redistribute it and/or modify it under
13 * the terms of the GNU General Public License as published by the Free
14 * Software Foundation; either version 3 of the License, or (at your
15 * option) any later version.
16 *
17 * TrIPE is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 * for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with TrIPE. If not, see <https://www.gnu.org/licenses/>.
24 */
25
26/*----- Header files ------------------------------------------------------*/
27
28#define _FILE_OFFSET_BITS 64
29
3a2f1a4b 30#include <assert.h>
8eabb4ff 31#include <ctype.h>
3a2f1a4b
MW
32#include <errno.h>
33#include <inttypes.h>
8eabb4ff 34#include <stdarg.h>
3a2f1a4b
MW
35#include <stdio.h>
36#include <stdlib.h>
37#include <string.h>
38
39#include <jni.h>
40
41#include <sys/types.h>
04a5abae 42#include <sys/select.h>
3a2f1a4b 43#include <sys/socket.h>
8eabb4ff
MW
44#include <sys/stat.h>
45#include <sys/sysmacros.h>
3a2f1a4b 46#include <sys/un.h>
8eabb4ff 47#include <fcntl.h>
3a2f1a4b 48#include <unistd.h>
8eabb4ff
MW
49#include <dirent.h>
50
51#include <mLib/align.h>
52#include <mLib/bits.h>
53#include <mLib/dstr.h>
54#include <mLib/macros.h>
55
56#include <catacomb/ghash.h>
3a2f1a4b
MW
57
58#undef sun
59
8eabb4ff
MW
60/*----- Magic class names and similar -------------------------------------*/
61
62/* The name decoration is horrific. Hide it. */
25c35469 63#define JNIFUNC(f) Java_uk_org_distorted_tripe_sys_package_00024_##f
8eabb4ff
MW
64
65/* The little class for bundling up error codes. */
25c35469 66#define ERRENTRY "uk/org/distorted/tripe/sys/package$ErrorEntry"
8eabb4ff
MW
67
68/* The `stat' class. */
25c35469 69#define STAT "uk/org/distorted/tripe/sys/package$FileInfo"
8eabb4ff
MW
70
71/* Exception class names. */
72#define NULLERR "java/lang/NullPointerException"
25c35469 73#define TYPEERR "uk/org/distorted/tripe/sys/package$NativeObjectTypeException"
8eabb4ff
MW
74#define SYSERR "uk/org/distorted/tripe/sys/package$SystemError"
75#define ARGERR "java/lang/IllegalArgumentException"
76#define BOUNDSERR "java/lang/IndexOutOfBoundsException"
77
78/*----- Miscellaneous utilities -------------------------------------------*/
79
80static void put_cstring(JNIEnv *jni, jbyteArray v, const char *p)
81 { if (p) (*jni)->ReleaseByteArrayElements(jni, v, (jbyte *)p, JNI_ABORT); }
82
83static void vexcept(JNIEnv *jni, const char *clsname,
25c35469 84 const char *msg, va_list *ap)
8eabb4ff
MW
85{
86 jclass cls;
87 int rc;
88 dstr d = DSTR_INIT;
89
90 cls = (*jni)->FindClass(jni, clsname); assert(cls);
91 if (!msg)
92 rc = (*jni)->ThrowNew(jni, cls, 0);
93 else {
25c35469 94 dstr_vputf(&d, msg, ap);
8eabb4ff
MW
95 rc = (*jni)->ThrowNew(jni, cls, d.buf);
96 assert(!rc);
97 dstr_destroy(&d);
98 }
99 assert(!rc);
100}
101
102static void except(JNIEnv *jni, const char *clsname, const char *msg, ...)
103{
104 va_list ap;
105
106 va_start(ap, msg);
25c35469 107 vexcept(jni, clsname, msg, &ap);
8eabb4ff
MW
108 va_end(ap);
109}
110
111#ifdef DEBUG
112static void dump_bytes(const void *p, size_t n, size_t o)
113{
114 const unsigned char *q = p;
115 size_t i;
116
117 if (!n) return;
118 for (;;) {
119 fprintf(stderr, ";; %08zx\n", o);
120 for (i = 0; i < 8; i++)
121 if (i < n) fprintf(stderr, "%02x ", q[i]);
122 else fprintf(stderr, "** ");
123 fprintf(stderr, ": ");
124 for (i = 0; i < 8; i++)
125 fputc(i >= n ? '*' : isprint(q[i]) ? q[i] : '.', stderr);
126 fputc('\n', stderr);
127 if (n <= 8) break;
128 q += 8; n -= 8;
129 }
130}
131
132static void dump_byte_array(JNIEnv *jni, const char *what, jbyteArray v)
133{
134 jsize n;
135 jbyte *p;
136
137 fprintf(stderr, ";; %s\n", what);
138 if (!v) { fprintf(stderr, ";; <null>\n"); return; }
139 n = (*jni)->GetArrayLength(jni, v);
140 p = (*jni)->GetByteArrayElements(jni, v, 0);
141 dump_bytes(p, n, 0);
142 (*jni)->ReleaseByteArrayElements(jni, v, p, JNI_ABORT);
143}
144#endif
145
146static jbyteArray wrap_cstring(JNIEnv *jni, const char *p)
147{
148 size_t n;
149 jbyteArray v;
150 jbyte *q;
151
152 if (!p) return (0);
153 n = strlen(p) + 1;
154 v = (*jni)->NewByteArray(jni, n); if (!v) return (0);
155 q = (*jni)->GetByteArrayElements(jni, v, 0); if (!q) return (0);
156 memcpy(q, p, n);
157 (*jni)->ReleaseByteArrayElements(jni, v, q, 0);
158 return (v);
159}
160
161static const char *get_cstring(JNIEnv *jni, jbyteArray v)
162{
163 if (!v) { except(jni, NULLERR, 0); return (0); }
164 return ((const char *)(*jni)->GetByteArrayElements(jni, v, 0));
165}
166
167static void vexcept_syserror(JNIEnv *jni, const char *clsname,
25c35469 168 int err, const char *msg, va_list *ap)
8eabb4ff
MW
169{
170 jclass cls;
171 int rc;
172 dstr d = DSTR_INIT;
173 jbyteArray msgstr;
174 jthrowable e;
175 jmethodID init;
176
177 cls = (*jni)->FindClass(jni, clsname); assert(cls);
178 init = (*jni)->GetMethodID(jni, cls, "<init>", "(I[B)V"); assert(init);
25c35469 179 dstr_vputf(&d, msg, ap);
8eabb4ff
MW
180 msgstr = wrap_cstring(jni, d.buf); assert(msgstr);
181 dstr_destroy(&d);
182 e = (*jni)->NewObject(jni, cls, init, err, msgstr); assert(e);
183 rc = (*jni)->Throw(jni, e); assert(!rc);
184}
185
186static void except_syserror(JNIEnv *jni, const char *clsname,
187 int err, const char *msg, ...)
188{
189 va_list ap;
190
191 va_start(ap, msg);
25c35469 192 vexcept_syserror(jni, clsname, err, msg, &ap);
8eabb4ff
MW
193 va_end(ap);
194}
195
04a5abae
MW
196static int set_nonblocking(JNIEnv *jni, int fd, int nb)
197{
198 int f0 = fcntl(fd, F_GETFL), f1;
199 if (f0 < 0) goto err;
200 if (nb) f1 = f0 | O_NONBLOCK;
201 else f1 = f0&~O_NONBLOCK;
202 if (fcntl(fd, F_SETFL, f1)) goto err;
203 return (f0 & O_NONBLOCK);
204err:
205 except_syserror(jni, SYSERR, errno,
206 "failed to set descriptor nonblocking");
207 return (-1);
208}
209
210static int set_closeonexec(JNIEnv *jni, int fd)
211{
212 int f = fcntl(fd, F_GETFD);
213 if (f < 0 || fcntl(fd, F_SETFD, f | FD_CLOEXEC)) {
214 except_syserror(jni, SYSERR, errno,
215 "failed to set descriptor close-on-exec");
216 return (-1);
217 }
218 return (0);
219}
220
8eabb4ff
MW
221/*----- Wrapping native types ---------------------------------------------*/
222
223/* There's no way defined in the JNI to stash a C pointer in a Java object.
224 * It seems that the usual approach is to cast to `jlong', but this is
225 * clearly unsatisfactory. Instead, we store structures as Java byte arrays,
226 * with a 32-bit tag on the front.
227 */
3a2f1a4b
MW
228
229struct native_type {
230 const char *name;
231 size_t sz;
8eabb4ff 232 uint32 tag;
3a2f1a4b
MW
233};
234
8eabb4ff 235typedef jbyteArray wrapper;
3a2f1a4b 236
8eabb4ff
MW
237struct native_base {
238 uint32 tag;
3a2f1a4b
MW
239};
240
8eabb4ff
MW
241static int unwrap(JNIEnv *jni, void *p,
242 const struct native_type *ty, wrapper w)
243{
244 jbyte *q;
245 jclass cls;
246 struct native_base *b = p;
247 jsize n;
248
249 if (!w) { except(jni, NULLERR, 0); return (-1); }
250 cls = (*jni)->FindClass(jni, "[B"); assert(cls);
251 if (!(*jni)->IsInstanceOf(jni, w, cls)) {
252 except(jni, TYPEERR,
253 "corrupted native object wrapper: expected a byte array");
254 return (-1);
255 }
256 n = (*jni)->GetArrayLength(jni, w);
257 if (n != ty->sz) {
258 except(jni, TYPEERR,
259 "corrupted native object wrapper: wrong size for `%s'",
260 ty->name);
261 return (-1);
262 }
263 q = (*jni)->GetByteArrayElements(jni, w, 0); if (!q) return (-1);
264 memcpy(b, q, ty->sz);
265 (*jni)->ReleaseByteArrayElements(jni, w, q, JNI_ABORT);
266 if (b->tag != ty->tag) {
267 except(jni, TYPEERR,
268 "corrupted native object wrapper: expected tag for `%s'",
269 ty->name);
270 return (-1);
271 }
272 return (0);
273}
274
275static int update_wrapper(JNIEnv *jni, const struct native_type *ty,
276 wrapper w, const void *p)
277{
278 jbyte *q;
279
280 q = (*jni)->GetByteArrayElements(jni, w, 0); if (!q) return (-1);
281 memcpy(q, p, ty->sz);
282 (*jni)->ReleaseByteArrayElements(jni, w, q, 0);
283 return (0);
284}
285
286static wrapper wrap(JNIEnv *jni, const struct native_type *ty, const void *p)
287{
288 wrapper w;
289
290 w = (*jni)->NewByteArray(jni, ty->sz); if (!w) return (0);
291 if (update_wrapper(jni, ty, w, p)) return (0);
292 return (w);
293}
294
295#define INIT_NATIVE(type, p) do (p)->_base.tag = type##_type.tag; while (0)
296
297/*----- Crypto information ------------------------------------------------*/
298
299JNIEXPORT jint JNICALL JNIFUNC(hashsz)(JNIEnv *jni, jobject cls,
300 jstring hnamestr)
301{
302 jint rc = -1;
303 const char *hname;
304 const gchash *hc;
305
306 hname = (*jni)->GetStringUTFChars(jni, hnamestr, 0);
307 if (!hname) goto end;
308 hc = ghash_byname(hname); if (!hc) goto end;
309 rc = hc->hashsz;
310
311end:
312 if (hname) (*jni)->ReleaseStringUTFChars(jni, hnamestr, hname);
313 return (rc);
314}
315
316/*----- System errors -----------------------------------------------------*/
317
318static const struct errtab { const char *tag; int err; } errtab[] = {
319 /*
320 ;;; The errno name table is very boring to type. To make life less
321 ;;; awful, put the errno names in this list and evaluate the code to
322 ;;; get Emacs to regenerate it.
323
324 (let ((errors '(EPERM ENOENT ESRCH EINTR EIO ENXIO E2BIG ENOEXEC EBADF
325 ECHILD EAGAIN ENOMEM EACCES EFAULT ENOTBLK EBUSY EEXIST
326 EXDEV ENODEV ENOTDIR EISDIR EINVAL ENFILE EMFILE ENOTTY
327 ETXTBSY EFBIG ENOSPC ESPIPE EROFS EMLINK EPIPE EDOM
328 ERANGE
329
330 EDEADLK ENAMETOOLONG ENOLCK ENOSYS ENOTEMPTY ELOOP
331 EWOULDBLOCK ENOMSG EIDRM ECHRNG EL2NSYNC EL3HLT EL3RST
332 ELNRNG EUNATCH ENOCSI EL2HLT EBADE EBADR EXFULL ENOANO
333 EBADRQC EBADSLT EDEADLOCK EBFONT ENOSTR ENODATA ETIME
334 ENOSR ENONET ENOPKG EREMOTE ENOLINK EADV ESRMNT ECOMM
335 EPROTO EMULTIHOP EDOTDOT EBADMSG EOVERFLOW ENOTUNIQ
336 EBADFD EREMCHG ELIBACC ELIBBAD ELIBSCN ELIBMAX ELIBEXEC
337 EILSEQ ERESTART ESTRPIPE EUSERS ENOTSOCK EDESTADDRREQ
338 EMSGSIZE EPROTOTYPE ENOPROTOOPT EPROTONOSUPPORT
339 ESOCKTNOSUPPORT EOPNOTSUPP EPFNOSUPPORT EAFNOSUPPORT
340 EADDRINUSE EADDRNOTAVAIL ENETDOWN ENETUNREACH ENETRESET
341 ECONNABORTED ECONNRESET ENOBUFS EISCONN ENOTCONN
342 ESHUTDOWN ETOOMANYREFS ETIMEDOUT ECONNREFUSED EHOSTDOWN
343 EHOSTUNREACH EALREADY EINPROGRESS ESTALE EUCLEAN ENOTNAM
344 ENAVAIL EISNAM EREMOTEIO EDQUOT ENOMEDIUM EMEDIUMTYPE
345 ECANCELED ENOKEY EKEYEXPIRED EKEYREVOKED EKEYREJECTED
346 EOWNERDEAD ENOTRECOVERABLE ERFKILL EHWPOISON)))
347 (save-excursion
c8292b34
MW
348 (goto-char (point-min))
349 (search-forward (concat "***" "BEGIN errtab" "***"))
350 (beginning-of-line 2)
351 (delete-region (point)
352 (progn
353 (search-forward "***END***")
354 (beginning-of-line)
355 (point)))
356 (dolist (err errors)
357 (insert (format "#ifdef %s\n { \"%s\", %s },\n#endif\n"
358 err err err)))))
8eabb4ff
MW
359 */
360 /***BEGIN errtab***/
361#ifdef EPERM
362 { "EPERM", EPERM },
363#endif
364#ifdef ENOENT
365 { "ENOENT", ENOENT },
366#endif
367#ifdef ESRCH
368 { "ESRCH", ESRCH },
369#endif
370#ifdef EINTR
371 { "EINTR", EINTR },
372#endif
373#ifdef EIO
374 { "EIO", EIO },
375#endif
376#ifdef ENXIO
377 { "ENXIO", ENXIO },
378#endif
379#ifdef E2BIG
380 { "E2BIG", E2BIG },
381#endif
382#ifdef ENOEXEC
383 { "ENOEXEC", ENOEXEC },
384#endif
385#ifdef EBADF
386 { "EBADF", EBADF },
387#endif
388#ifdef ECHILD
389 { "ECHILD", ECHILD },
390#endif
391#ifdef EAGAIN
392 { "EAGAIN", EAGAIN },
393#endif
394#ifdef ENOMEM
395 { "ENOMEM", ENOMEM },
396#endif
397#ifdef EACCES
398 { "EACCES", EACCES },
399#endif
400#ifdef EFAULT
401 { "EFAULT", EFAULT },
402#endif
403#ifdef ENOTBLK
404 { "ENOTBLK", ENOTBLK },
405#endif
406#ifdef EBUSY
407 { "EBUSY", EBUSY },
408#endif
409#ifdef EEXIST
410 { "EEXIST", EEXIST },
411#endif
412#ifdef EXDEV
413 { "EXDEV", EXDEV },
414#endif
415#ifdef ENODEV
416 { "ENODEV", ENODEV },
417#endif
418#ifdef ENOTDIR
419 { "ENOTDIR", ENOTDIR },
420#endif
421#ifdef EISDIR
422 { "EISDIR", EISDIR },
423#endif
424#ifdef EINVAL
425 { "EINVAL", EINVAL },
426#endif
427#ifdef ENFILE
428 { "ENFILE", ENFILE },
429#endif
430#ifdef EMFILE
431 { "EMFILE", EMFILE },
432#endif
433#ifdef ENOTTY
434 { "ENOTTY", ENOTTY },
435#endif
436#ifdef ETXTBSY
437 { "ETXTBSY", ETXTBSY },
438#endif
439#ifdef EFBIG
440 { "EFBIG", EFBIG },
441#endif
442#ifdef ENOSPC
443 { "ENOSPC", ENOSPC },
444#endif
445#ifdef ESPIPE
446 { "ESPIPE", ESPIPE },
447#endif
448#ifdef EROFS
449 { "EROFS", EROFS },
450#endif
451#ifdef EMLINK
452 { "EMLINK", EMLINK },
453#endif
454#ifdef EPIPE
455 { "EPIPE", EPIPE },
456#endif
457#ifdef EDOM
458 { "EDOM", EDOM },
459#endif
460#ifdef ERANGE
461 { "ERANGE", ERANGE },
462#endif
463#ifdef EDEADLK
464 { "EDEADLK", EDEADLK },
465#endif
466#ifdef ENAMETOOLONG
467 { "ENAMETOOLONG", ENAMETOOLONG },
468#endif
469#ifdef ENOLCK
470 { "ENOLCK", ENOLCK },
471#endif
472#ifdef ENOSYS
473 { "ENOSYS", ENOSYS },
474#endif
475#ifdef ENOTEMPTY
476 { "ENOTEMPTY", ENOTEMPTY },
477#endif
478#ifdef ELOOP
479 { "ELOOP", ELOOP },
480#endif
481#ifdef EWOULDBLOCK
482 { "EWOULDBLOCK", EWOULDBLOCK },
483#endif
484#ifdef ENOMSG
485 { "ENOMSG", ENOMSG },
486#endif
487#ifdef EIDRM
488 { "EIDRM", EIDRM },
489#endif
490#ifdef ECHRNG
491 { "ECHRNG", ECHRNG },
492#endif
493#ifdef EL2NSYNC
494 { "EL2NSYNC", EL2NSYNC },
495#endif
496#ifdef EL3HLT
497 { "EL3HLT", EL3HLT },
498#endif
499#ifdef EL3RST
500 { "EL3RST", EL3RST },
501#endif
502#ifdef ELNRNG
503 { "ELNRNG", ELNRNG },
504#endif
505#ifdef EUNATCH
506 { "EUNATCH", EUNATCH },
507#endif
508#ifdef ENOCSI
509 { "ENOCSI", ENOCSI },
510#endif
511#ifdef EL2HLT
512 { "EL2HLT", EL2HLT },
513#endif
514#ifdef EBADE
515 { "EBADE", EBADE },
516#endif
517#ifdef EBADR
518 { "EBADR", EBADR },
519#endif
520#ifdef EXFULL
521 { "EXFULL", EXFULL },
522#endif
523#ifdef ENOANO
524 { "ENOANO", ENOANO },
525#endif
526#ifdef EBADRQC
527 { "EBADRQC", EBADRQC },
528#endif
529#ifdef EBADSLT
530 { "EBADSLT", EBADSLT },
531#endif
532#ifdef EDEADLOCK
533 { "EDEADLOCK", EDEADLOCK },
534#endif
535#ifdef EBFONT
536 { "EBFONT", EBFONT },
537#endif
538#ifdef ENOSTR
539 { "ENOSTR", ENOSTR },
540#endif
541#ifdef ENODATA
542 { "ENODATA", ENODATA },
543#endif
544#ifdef ETIME
545 { "ETIME", ETIME },
546#endif
547#ifdef ENOSR
548 { "ENOSR", ENOSR },
549#endif
550#ifdef ENONET
551 { "ENONET", ENONET },
552#endif
553#ifdef ENOPKG
554 { "ENOPKG", ENOPKG },
555#endif
556#ifdef EREMOTE
557 { "EREMOTE", EREMOTE },
558#endif
559#ifdef ENOLINK
560 { "ENOLINK", ENOLINK },
561#endif
562#ifdef EADV
563 { "EADV", EADV },
564#endif
565#ifdef ESRMNT
566 { "ESRMNT", ESRMNT },
567#endif
568#ifdef ECOMM
569 { "ECOMM", ECOMM },
570#endif
571#ifdef EPROTO
572 { "EPROTO", EPROTO },
573#endif
574#ifdef EMULTIHOP
575 { "EMULTIHOP", EMULTIHOP },
576#endif
577#ifdef EDOTDOT
578 { "EDOTDOT", EDOTDOT },
579#endif
580#ifdef EBADMSG
581 { "EBADMSG", EBADMSG },
582#endif
583#ifdef EOVERFLOW
584 { "EOVERFLOW", EOVERFLOW },
585#endif
586#ifdef ENOTUNIQ
587 { "ENOTUNIQ", ENOTUNIQ },
588#endif
589#ifdef EBADFD
590 { "EBADFD", EBADFD },
591#endif
592#ifdef EREMCHG
593 { "EREMCHG", EREMCHG },
594#endif
595#ifdef ELIBACC
596 { "ELIBACC", ELIBACC },
597#endif
598#ifdef ELIBBAD
599 { "ELIBBAD", ELIBBAD },
600#endif
601#ifdef ELIBSCN
602 { "ELIBSCN", ELIBSCN },
603#endif
604#ifdef ELIBMAX
605 { "ELIBMAX", ELIBMAX },
606#endif
607#ifdef ELIBEXEC
608 { "ELIBEXEC", ELIBEXEC },
609#endif
610#ifdef EILSEQ
611 { "EILSEQ", EILSEQ },
612#endif
613#ifdef ERESTART
614 { "ERESTART", ERESTART },
615#endif
616#ifdef ESTRPIPE
617 { "ESTRPIPE", ESTRPIPE },
618#endif
619#ifdef EUSERS
620 { "EUSERS", EUSERS },
621#endif
622#ifdef ENOTSOCK
623 { "ENOTSOCK", ENOTSOCK },
624#endif
625#ifdef EDESTADDRREQ
626 { "EDESTADDRREQ", EDESTADDRREQ },
627#endif
628#ifdef EMSGSIZE
629 { "EMSGSIZE", EMSGSIZE },
630#endif
631#ifdef EPROTOTYPE
632 { "EPROTOTYPE", EPROTOTYPE },
633#endif
634#ifdef ENOPROTOOPT
635 { "ENOPROTOOPT", ENOPROTOOPT },
636#endif
637#ifdef EPROTONOSUPPORT
638 { "EPROTONOSUPPORT", EPROTONOSUPPORT },
639#endif
640#ifdef ESOCKTNOSUPPORT
641 { "ESOCKTNOSUPPORT", ESOCKTNOSUPPORT },
642#endif
643#ifdef EOPNOTSUPP
644 { "EOPNOTSUPP", EOPNOTSUPP },
645#endif
646#ifdef EPFNOSUPPORT
647 { "EPFNOSUPPORT", EPFNOSUPPORT },
648#endif
649#ifdef EAFNOSUPPORT
650 { "EAFNOSUPPORT", EAFNOSUPPORT },
651#endif
652#ifdef EADDRINUSE
653 { "EADDRINUSE", EADDRINUSE },
654#endif
655#ifdef EADDRNOTAVAIL
656 { "EADDRNOTAVAIL", EADDRNOTAVAIL },
657#endif
658#ifdef ENETDOWN
659 { "ENETDOWN", ENETDOWN },
660#endif
661#ifdef ENETUNREACH
662 { "ENETUNREACH", ENETUNREACH },
663#endif
664#ifdef ENETRESET
665 { "ENETRESET", ENETRESET },
666#endif
667#ifdef ECONNABORTED
668 { "ECONNABORTED", ECONNABORTED },
669#endif
670#ifdef ECONNRESET
671 { "ECONNRESET", ECONNRESET },
672#endif
673#ifdef ENOBUFS
674 { "ENOBUFS", ENOBUFS },
675#endif
676#ifdef EISCONN
677 { "EISCONN", EISCONN },
678#endif
679#ifdef ENOTCONN
680 { "ENOTCONN", ENOTCONN },
681#endif
682#ifdef ESHUTDOWN
683 { "ESHUTDOWN", ESHUTDOWN },
684#endif
685#ifdef ETOOMANYREFS
686 { "ETOOMANYREFS", ETOOMANYREFS },
687#endif
688#ifdef ETIMEDOUT
689 { "ETIMEDOUT", ETIMEDOUT },
690#endif
691#ifdef ECONNREFUSED
692 { "ECONNREFUSED", ECONNREFUSED },
693#endif
694#ifdef EHOSTDOWN
695 { "EHOSTDOWN", EHOSTDOWN },
696#endif
697#ifdef EHOSTUNREACH
698 { "EHOSTUNREACH", EHOSTUNREACH },
699#endif
700#ifdef EALREADY
701 { "EALREADY", EALREADY },
702#endif
703#ifdef EINPROGRESS
704 { "EINPROGRESS", EINPROGRESS },
705#endif
706#ifdef ESTALE
707 { "ESTALE", ESTALE },
708#endif
709#ifdef EUCLEAN
710 { "EUCLEAN", EUCLEAN },
711#endif
712#ifdef ENOTNAM
713 { "ENOTNAM", ENOTNAM },
714#endif
715#ifdef ENAVAIL
716 { "ENAVAIL", ENAVAIL },
717#endif
718#ifdef EISNAM
719 { "EISNAM", EISNAM },
720#endif
721#ifdef EREMOTEIO
722 { "EREMOTEIO", EREMOTEIO },
723#endif
724#ifdef EDQUOT
725 { "EDQUOT", EDQUOT },
726#endif
727#ifdef ENOMEDIUM
728 { "ENOMEDIUM", ENOMEDIUM },
729#endif
730#ifdef EMEDIUMTYPE
731 { "EMEDIUMTYPE", EMEDIUMTYPE },
732#endif
733#ifdef ECANCELED
734 { "ECANCELED", ECANCELED },
735#endif
736#ifdef ENOKEY
737 { "ENOKEY", ENOKEY },
738#endif
739#ifdef EKEYEXPIRED
740 { "EKEYEXPIRED", EKEYEXPIRED },
741#endif
742#ifdef EKEYREVOKED
743 { "EKEYREVOKED", EKEYREVOKED },
744#endif
745#ifdef EKEYREJECTED
746 { "EKEYREJECTED", EKEYREJECTED },
747#endif
748#ifdef EOWNERDEAD
749 { "EOWNERDEAD", EOWNERDEAD },
750#endif
751#ifdef ENOTRECOVERABLE
752 { "ENOTRECOVERABLE", ENOTRECOVERABLE },
753#endif
754#ifdef ERFKILL
755 { "ERFKILL", ERFKILL },
756#endif
757#ifdef EHWPOISON
758 { "EHWPOISON", EHWPOISON },
759#endif
760 /***END***/
3a2f1a4b
MW
761};
762
8eabb4ff 763JNIEXPORT jobject JNIFUNC(errtab)(JNIEnv *jni, jobject cls)
3a2f1a4b 764{
8eabb4ff
MW
765 size_t i;
766 jclass eltcls;
767 jarray v;
768 jmethodID init;
769 jobject e;
3a2f1a4b 770
8eabb4ff
MW
771 eltcls =
772 (*jni)->FindClass(jni, ERRENTRY);
773 assert(eltcls);
774 v = (*jni)->NewObjectArray(jni, N(errtab), eltcls, 0); if (!v) return (0);
775 init = (*jni)->GetMethodID(jni, eltcls, "<init>",
776 "(Ljava/lang/String;I)V");
777 assert(init);
778
779 for (i = 0; i < N(errtab); i++) {
780 e = (*jni)->NewObject(jni, eltcls, init,
781 (*jni)->NewStringUTF(jni, errtab[i].tag),
782 errtab[i].err);
783 (*jni)->SetObjectArrayElement(jni, v, i, e);
784 }
785 return (v);
786}
787
788JNIEXPORT jobject JNIFUNC(strerror)(JNIEnv *jni, jobject cls, jint err)
789 { return (wrap_cstring(jni, strerror(err))); }
790
c8292b34
MW
791/*----- Messing with file descriptors -------------------------------------*/
792
793static void fdguts(JNIEnv *jni, jclass *cls, jfieldID *fid)
794{
795 *cls = (*jni)->FindClass(jni, "java/io/FileDescriptor"); assert(cls);
796 *fid = (*jni)->GetFieldID(jni, *cls, "fd", "I"); // OpenJDK
797 if (!*fid) *fid = (*jni)->GetFieldID(jni, *cls, "descriptor", "I"); // Android
798 assert(*fid);
799}
800
801static int fdint(JNIEnv *jni, jobject jfd)
802{
803 jclass cls;
804 jfieldID fid;
805
806 fdguts(jni, &cls, &fid);
807 return ((*jni)->GetIntField(jni, jfd, fid));
808}
809
810static jobject newfd(JNIEnv *jni, int fd)
811{
812 jobject jfd;
813 jclass cls;
814 jmethodID init;
815 jfieldID fid;
816
817 fdguts(jni, &cls, &fid);
818 init = (*jni)->GetMethodID(jni, cls, "<init>", "()V"); assert(init);
819 jfd = (*jni)->NewObject(jni, cls, init);
820 (*jni)->SetIntField(jni, jfd, fid, fd);
821 return (jfd);
822}
823
824JNIEXPORT jint JNIFUNC(fdint)(JNIEnv *jni, jobject cls, jobject jfd)
825 { return (fdint(jni, jfd)); }
826
827JNIEXPORT jobject JNIFUNC(newfd)(JNIEnv *jni, jobject cls, jint fd)
828 { return (newfd(jni, fd)); }
829
830JNIEXPORT jboolean JNIFUNC(isatty)(JNIEnv *jni, jobject cls, jobject jfd)
831 { return (isatty(fdint(jni, jfd))); }
832
8eabb4ff
MW
833/*----- Low-level file operations -----------------------------------------*/
834
835/* Java has these already, as methods on `java.io.File' objects. Alas, these
836 * methods are useless at reporting errors: they tend to return a `boolean'
04a5abae 837 * success/fail indicator, and throw away any more detailed information.
8eabb4ff
MW
838 * There's better functionality in `java.nio.file.Files', but that only turns
839 * up in Android API 26 (in 7.0 Nougat). There's `android.system.Os', which
840 * has a bunch of POSIX-shaped functions -- but they're only in Android API
841 * 21 (in 5.0 Lollipop), and there's nothing in the support library to help.
842 *
843 * So the other option is to implement them ourselves.
844 */
845
846JNIEXPORT void JNIFUNC(unlink)(JNIEnv *jni, jobject cls, jobject path)
847{
848 const char *pathstr = 0;
849
850 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
851 if (unlink(pathstr)) {
852 except_syserror(jni, SYSERR, errno,
853 "failed to delete file `%s'", pathstr);
854 goto end;
855 }
856end:
857 put_cstring(jni, path, pathstr);
3a2f1a4b
MW
858}
859
8eabb4ff
MW
860JNIEXPORT void JNIFUNC(rmdir)(JNIEnv *jni, jobject cls, jobject path)
861{
862 const char *pathstr = 0;
3a2f1a4b 863
8eabb4ff
MW
864 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
865 if (rmdir(pathstr)) {
866 except_syserror(jni, SYSERR, errno,
867 "failed to delete directory `%s'", pathstr);
868 goto end;
869 }
870end:
871 put_cstring(jni, path, pathstr);
872}
873
874JNIEXPORT void JNIFUNC(mkdir)(JNIEnv *jni, jobject cls,
875 jobject path, jint mode)
3a2f1a4b 876{
8eabb4ff
MW
877 const char *pathstr = 0;
878
879 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
880 if (mkdir(pathstr, mode)) {
881 except_syserror(jni, SYSERR, errno,
882 "failed to create directory `%s'", pathstr);
883 goto end;
884 }
885end:
886 put_cstring(jni, path, pathstr);
3a2f1a4b
MW
887}
888
8eabb4ff
MW
889JNIEXPORT void JNIFUNC(mkfile)(JNIEnv *jni, jobject cls,
890 jobject path, jint mode)
3a2f1a4b 891{
8eabb4ff
MW
892 const char *pathstr = 0;
893 int fd = -1;
3a2f1a4b 894
8eabb4ff
MW
895 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
896 fd = open(pathstr, O_WRONLY | O_CREAT | O_EXCL, mode);
897 if (fd < 0) {
898 except_syserror(jni, SYSERR, errno,
899 "failed to create fresh file `%s'", pathstr);
900 goto end;
3a2f1a4b 901 }
8eabb4ff
MW
902end:
903 if (fd != -1) close(fd);
904 put_cstring(jni, path, pathstr);
3a2f1a4b
MW
905}
906
8eabb4ff
MW
907JNIEXPORT void JNIFUNC(rename)(JNIEnv *jni, jobject cls,
908 jobject from, jobject to)
3a2f1a4b 909{
8eabb4ff
MW
910 const char *fromstr = 0, *tostr = 0;
911
912 fromstr = get_cstring(jni, from); if (!fromstr) goto end;
913 tostr = get_cstring(jni, to); if (!tostr) goto end;
914 if (rename(fromstr, tostr)) {
915 except_syserror(jni, SYSERR, errno,
916 "failed to rename `%s' as `%s'", fromstr, tostr);
917 goto end;
918 }
919end:
920 put_cstring(jni, from, fromstr);
921 put_cstring(jni, to, tostr);
3a2f1a4b
MW
922}
923
c8292b34
MW
924#define LKF_EXCL 0x1000u
925#define LKF_WAIT 0x2000u
8eabb4ff
MW
926struct lockf {
927 struct native_base _base;
928 int fd;
929};
930static struct native_type lockf_type =
931 { "lock", sizeof(struct lockf), 0xb2648926};
932JNIEXPORT wrapper JNIFUNC(lock)(JNIEnv *jni, jobject cls,
933 jobject path, jint flags)
934{
935 const char *pathstr = 0;
936 int fd = -1;
937 struct flock l;
938 struct lockf lk;
939 struct stat st0, st1;
940 int f;
941 wrapper r = 0;
942
943 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
944
945again:
c8292b34 946 fd = open(pathstr, O_RDWR | O_CREAT, flags&07777); if (fd < 0) goto err;
8eabb4ff
MW
947 if (fstat(fd, &st0)) goto err;
948 f = fcntl(fd, F_GETFD); if (f < 0) goto err;
949 if (fcntl(fd, F_SETFD, f | FD_CLOEXEC)) goto err;
950 l.l_type = (flags&LKF_EXCL) ? F_WRLCK : F_RDLCK;
951 l.l_whence = SEEK_SET;
952 l.l_start = 0;
953 l.l_len = 0;
954 if (fcntl(fd, (flags&LKF_WAIT) ? F_SETLKW : F_SETLK, &l)) goto err;
955 if (stat(pathstr, &st1))
956 { if (errno == ENOENT) goto again; else goto err; }
957 if (st0.st_dev != st1.st_dev || st0.st_ino != st1.st_ino)
958 { close(fd); fd = -1; goto again; }
959
960 INIT_NATIVE(lockf, &lk); lk.fd = fd; fd = -1;
961 r = wrap(jni, &lockf_type, &lk);
962 goto end;
963
964err:
965 except_syserror(jni, SYSERR, errno, "failed to lock file `%s'", pathstr);
966end:
967 if (fd != -1) close(fd);
968 put_cstring(jni, path, pathstr);
969 return (r);
970}
971
972JNIEXPORT void JNIFUNC(unlock)(JNIEnv *jni, jobject cls, wrapper wlk)
973{
974 struct lockf lk;
975 struct flock l;
976 int rc;
977
978 if (unwrap(jni, &lk, &lockf_type, wlk)) goto end;
979 if (lk.fd == -1) goto end;
980 l.l_type = F_UNLCK;
981 l.l_whence = SEEK_SET;
982 l.l_start = 0;
983 l.l_len = 0;
984 if (fcntl(lk.fd, F_SETLK, &l)) goto end;
985 close(lk.fd); lk.fd = -1;
986 rc = update_wrapper(jni, &lockf_type, wlk, &lk); assert(!rc);
987end:;
988}
989
990static jlong xlttimespec(const struct timespec *ts)
991 { return (1000*(jlong)ts->tv_sec + ts->tv_nsec/1000000); }
992
993static jobject xltstat(JNIEnv *jni, const struct stat *st)
994{
995 jclass cls;
996 jmethodID init;
997 jint modehack;
c8292b34 998
8eabb4ff
MW
999 modehack = st->st_mode&07777;
1000 if (S_ISFIFO(st->st_mode)) modehack |= 0010000;
1001 else if (S_ISCHR(st->st_mode)) modehack |= 0020000;
1002 else if (S_ISDIR(st->st_mode)) modehack |= 0040000;
1003 else if (S_ISBLK(st->st_mode)) modehack |= 0060000;
1004 else if (S_ISREG(st->st_mode)) modehack |= 0100000;
1005 else if (S_ISLNK(st->st_mode)) modehack |= 0120000;
1006 else if (S_ISSOCK(st->st_mode)) modehack |= 0140000;
1007
1008 cls = (*jni)->FindClass(jni, STAT); assert(cls);
1009 init = (*jni)->GetMethodID(jni, cls, "<init>", "(IIJIIIIIIJIJJJJ)V");
1010 assert(init);
1011 return ((*jni)->NewObject(jni, cls, init,
1012 (jint)major(st->st_dev), (jint)minor(st->st_dev),
1013 (jlong)st->st_ino,
1014 modehack,
1015 (jint)st->st_nlink,
1016 (jint)st->st_uid, (jint)st->st_gid,
1017 (jint)major(st->st_rdev), (jint)minor(st->st_rdev),
1018 (jlong)st->st_size,
1019 (jint)st->st_blksize, (jlong)st->st_blocks,
1020 xlttimespec(&st->st_atim),
1021 xlttimespec(&st->st_mtim),
1022 xlttimespec(&st->st_ctim)));
1023}
1024
1025JNIEXPORT jobject JNIFUNC(stat)(JNIEnv *jni, jobject cls, jobject path)
3a2f1a4b 1026{
8eabb4ff
MW
1027 jobject r = 0;
1028 const char *pathstr = 0;
1029 struct stat st;
1030
1031 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
1032 if (stat(pathstr, &st)) {
1033 except_syserror(jni, SYSERR, errno,
1034 "failed to read information about `%s'", pathstr);
1035 goto end;
1036 }
1037 r = xltstat(jni, &st);
1038end:
1039 put_cstring(jni, path, pathstr);
1040 return (r);
3a2f1a4b
MW
1041}
1042
8eabb4ff
MW
1043JNIEXPORT jobject JNIFUNC(lstat)(JNIEnv *jni, jobject cls, jobject path)
1044{
1045 jobject r = 0;
1046 const char *pathstr = 0;
1047 struct stat st;
3a2f1a4b 1048
8eabb4ff
MW
1049 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
1050 if (lstat(pathstr, &st)) {
1051 except_syserror(jni, SYSERR, errno,
1052 "failed to read information about `%s'", pathstr);
1053 goto end;
1054 }
1055 r = xltstat(jni, &st);
1056end:
1057 put_cstring(jni, path, pathstr);
1058 return (r);
1059}
1060
1061struct dir {
1062 struct native_base _base;
1063 DIR *d;
3a2f1a4b 1064};
8eabb4ff
MW
1065static const struct native_type dir_type =
1066 { "dir", sizeof(struct dir), 0x0f5ca477 };
3a2f1a4b 1067
8eabb4ff 1068JNIEXPORT jobject JNIFUNC(opendir)(JNIEnv *jni, jobject cls, jobject path)
3a2f1a4b 1069{
8eabb4ff
MW
1070 const char *pathstr = 0;
1071 struct dir dir;
1072 wrapper r = 0;
3a2f1a4b 1073
8eabb4ff
MW
1074 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
1075 INIT_NATIVE(dir, &dir);
1076 dir.d = opendir(pathstr);
1077 if (!dir.d) {
1078 except_syserror(jni, SYSERR, errno,
1079 "failed to open directory `%s'", pathstr);
1080 goto end;
1081 }
1082 r = wrap(jni, &dir_type, &dir);
1083end:
1084 put_cstring(jni, path, pathstr);
1085 return (r);
3a2f1a4b
MW
1086}
1087
8eabb4ff
MW
1088JNIEXPORT jbyteArray JNIFUNC(readdir)(JNIEnv *jni, jobject cls,
1089 jobject path, jobject wdir)
3a2f1a4b 1090{
8eabb4ff
MW
1091 const char *pathstr = 0;
1092 struct dir dir;
1093 struct dirent *d;
1094 jbyteArray r = 0;
3a2f1a4b 1095
8eabb4ff
MW
1096 if (unwrap(jni, &dir, &dir_type, wdir)) goto end;
1097 if (!dir.d) { except(jni, ARGERR, "directory has been closed"); goto end; }
1098 errno = 0; d = readdir(dir.d);
1099 if (errno) {
1100 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
1101 except_syserror(jni, SYSERR, errno,
1102 "failed to read directory `%s'", pathstr);
1103 goto end;
1104 }
1105 if (d) r = wrap_cstring(jni, d->d_name);
1106end:
1107 put_cstring(jni, path, pathstr);
1108 return (r);
1109}
1110
1111JNIEXPORT void JNIFUNC(closedir)(JNIEnv *jni, jobject cls,
1112 jobject path, jobject wdir)
1113{
1114 const char *pathstr = 0;
1115 struct dir dir;
1116
1117 if (unwrap(jni, &dir, &dir_type, wdir)) goto end;
1118 if (!dir.d) goto end;
1119 if (closedir(dir.d)) {
1120 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
1121 except_syserror(jni, SYSERR, errno,
1122 "failed to close directory `%s'", pathstr);
1123 goto end;
1124 }
1125 dir.d = 0;
1126 if (update_wrapper(jni, &dir_type, wdir, &dir)) goto end;
1127end:
1128 put_cstring(jni, path, pathstr);
3a2f1a4b
MW
1129}
1130
04a5abae
MW
1131/*----- Triggers ----------------------------------------------------------*/
1132
1133/* A trigger is a gadget for waking up a thread which is blocking on I/O,
1134 * and it's used to implement interruptability.
1135 *
1136 * Really, a trigger is a pipe. A `blocking' I/O operation secretly uses
1137 * select(2) to block on the descriptor of interest /and/ the read side of
1138 * the trigger pipe. To wake up a thread that's blocked, we just write a
1139 * byte (nobody cares /which/ byte) to the write end.
1140 */
1141
1142struct trigger {
1143 struct native_base _base;
1144 int rfd, wfd;
1145};
1146static const struct native_type trigger_type =
1147 { "trigger", sizeof(struct trigger), 0x65ffd8b4 };
1148
1149JNIEXPORT wrapper JNICALL JNIFUNC(makeTrigger)(JNIEnv *jni, jobject cls)
1150{
1151 struct trigger trig;
1152 int fd[2];
1153 int i;
1154 wrapper ret = 0;
1155
1156 fd[0] = fd[1] = -1;
1157 if (pipe(fd)) {
1158 except_syserror(jni, SYSERR, errno, "failed to create pipe");
1159 goto end;
1160 }
1161 for (i = 0; i < 2; i++) {
1162 if (set_nonblocking(jni, fd[i], 1) < 0 || set_closeonexec(jni, fd[i]))
1163 goto end;
1164 }
1165
1166 INIT_NATIVE(trigger, &trig);
1167 trig.rfd = fd[0]; fd[0] = -1;
1168 trig.wfd = fd[1]; fd[1] = -1;
1169 ret = wrap(jni, &trigger_type, &trig);
1170
1171end:
1172 for (i = 0; i < 2; i++)
1173 if (fd[i] != -1) close(fd[i]);
1174 return (ret);
1175}
1176
1177JNIEXPORT void JNICALL JNIFUNC(destroyTrigger)(JNIEnv *jni, jobject cls,
1178 wrapper wtrig)
1179{
1180 struct trigger trig;
1181
1182 if (unwrap(jni, &trig, &trigger_type, wtrig)) return;
1183 if (trig.rfd != -1) { close(trig.rfd); trig.rfd = -1; }
1184 if (trig.wfd != -1) { close(trig.wfd); trig.wfd = -1; }
1185 update_wrapper(jni, &trigger_type, wtrig, &trig);
1186}
1187
1188JNIEXPORT void JNICALL JNIFUNC(resetTrigger)(JNIEnv *jni, jobject cls,
1189 wrapper wtrig)
1190{
1191 struct trigger trig;
1192 char buf[64];
1193 ssize_t n;
1194
1195 if (unwrap(jni, &trig, &trigger_type, wtrig)) return;
1196 for (;;) {
1197 n = read(trig.rfd, buf, sizeof(buf));
1198 if (n > 0) continue;
1199 assert(n < 0);
1200 if (errno == EAGAIN || errno == EWOULDBLOCK) break;
1201 else {
1202 except_syserror(jni, SYSERR, errno, "failed to reset trigger");
1203 break;
1204 }
1205 }
1206}
1207
1208JNIEXPORT void JNICALL JNIFUNC(trigger)(JNIEnv *jni, jobject cls,
1209 wrapper wtrig)
1210{
1211 struct trigger trig;
1212 ssize_t n;
1213 char c = 0;
1214
1215 if (unwrap(jni, &trig, &trigger_type, wtrig)) return;
1216 n = write(trig.wfd, &c, 1);
1217 if (n < 0 && errno != EAGAIN && errno != EWOULDBLOCK)
1218 except_syserror(jni, SYSERR, errno, "failed to pull trigger");
1219}
1220
8eabb4ff
MW
1221/*----- A server connection, using a Unix-domain socket -------------------*/
1222
3a2f1a4b 1223struct conn {
8eabb4ff 1224 struct native_base _base;
3a2f1a4b
MW
1225 int fd;
1226 unsigned f;
1227#define CF_CLOSERD 1u
1228#define CF_CLOSEWR 2u
1229#define CF_CLOSEMASK (CF_CLOSERD | CF_CLOSEWR)
1230};
8eabb4ff
MW
1231static const struct native_type conn_type =
1232 { "conn", sizeof(struct conn), 0xed030167 };
3a2f1a4b 1233
8eabb4ff 1234JNIEXPORT wrapper JNICALL JNIFUNC(connect)(JNIEnv *jni, jobject cls,
04a5abae 1235 jobject path, wrapper wtrig)
3a2f1a4b 1236{
8eabb4ff 1237 struct conn conn;
04a5abae 1238 struct trigger trig;
3a2f1a4b 1239 struct sockaddr_un sun;
04a5abae
MW
1240 int rc, maxfd;
1241 fd_set rfds, wfds;
8eabb4ff 1242 const char *pathstr = 0;
04a5abae
MW
1243 int err;
1244 socklen_t sz;
1245 wrapper ret = 0;
1246 int nb;
3a2f1a4b
MW
1247 int fd = -1;
1248
04a5abae 1249 if (unwrap(jni, &trig, &trigger_type, wtrig)) goto end;
8eabb4ff
MW
1250 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
1251 if (strlen(pathstr) >= sizeof(sun.sun_path)) {
1252 except(jni, ARGERR,
1253 "Unix-domain socket path `%s' too long", pathstr);
1254 goto end;
1255 }
3a2f1a4b 1256
8eabb4ff 1257 INIT_NATIVE(conn, &conn);
04a5abae
MW
1258 fd = socket(PF_UNIX, SOCK_STREAM, 0); if (fd < 0) goto err;
1259 nb = set_nonblocking(jni, fd, 1); if (nb < 0) goto end;
3a2f1a4b
MW
1260
1261 sun.sun_family = AF_UNIX;
8eabb4ff 1262 strcpy(sun.sun_path, (char *)pathstr);
04a5abae
MW
1263 if (!connect(fd, (struct sockaddr *)&sun, sizeof(sun))) goto connected;
1264 else if (errno != EINPROGRESS) goto err;
1265
1266 maxfd = trig.rfd;
1267 if (maxfd < fd) maxfd = fd;
1268 for (;;) {
1269 FD_ZERO(&rfds); FD_SET(trig.rfd, &rfds);
1270 FD_ZERO(&wfds); FD_SET(fd, &wfds);
1271 rc = select(maxfd + 1, &rfds, &wfds, 0, 0); if (rc < 0) goto err;
1272 if (FD_ISSET(trig.rfd, &rfds)) goto end;
1273 if (FD_ISSET(fd, &wfds)) {
1274 sz = sizeof(sun);
1275 if (!getpeername(fd, (struct sockaddr *)&sun, &sz)) goto connected;
1276 else if (errno != ENOTCONN) goto err;
1277 sz = sizeof(err);
1278 if (!getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &sz)) errno = err;
1279 goto err;
1280 }
1281 }
3a2f1a4b 1282
04a5abae
MW
1283connected:
1284 if (set_nonblocking(jni, fd, nb) < 0) goto end;
8eabb4ff
MW
1285 conn.fd = fd; fd = -1;
1286 conn.f = 0;
1287 ret = wrap(jni, &conn_type, &conn);
1288 goto end;
3a2f1a4b 1289
3a2f1a4b 1290err:
8eabb4ff
MW
1291 except_syserror(jni, SYSERR, errno,
1292 "failed to connect to Unix-domain socket `%s'", pathstr);
1293end:
04a5abae 1294 if (fd != -1) close(fd);
8eabb4ff
MW
1295 put_cstring(jni, path, pathstr);
1296 return (ret);
3a2f1a4b
MW
1297}
1298
8eabb4ff
MW
1299static int check_buffer_bounds(JNIEnv *jni, const char *what,
1300 jbyteArray buf, jint start, jint len)
3a2f1a4b 1301{
3a2f1a4b 1302 jsize bufsz;
8eabb4ff 1303 jclass cls;
3a2f1a4b 1304
8eabb4ff
MW
1305 cls = (*jni)->FindClass(jni, "[B"); assert(cls);
1306 if (!(*jni)->IsInstanceOf(jni, buf, cls)) {
1307 except(jni, ARGERR,
1308 "expected a byte array");
1309 return (-1);
1310 }
3a2f1a4b 1311 bufsz = (*jni)->GetArrayLength(jni, buf);
8eabb4ff
MW
1312 if (start > bufsz) {
1313 except(jni, BOUNDSERR,
1314 "bad %s buffer bounds: start %d > buffer size %d", start, bufsz);
1315 return (-1);
1316 }
1317 if (len > bufsz - start) {
1318 except(jni, BOUNDSERR,
1319 "bad %s buffer bounds: length %d > remaining buffer size %d",
1320 len, bufsz - start);
1321 return (-1);
3a2f1a4b 1322 }
8eabb4ff
MW
1323 return (0);
1324}
1325
1326JNIEXPORT void JNICALL JNIFUNC(send)(JNIEnv *jni, jobject cls,
1327 wrapper wconn, jbyteArray buf,
04a5abae
MW
1328 jint start, jint len,
1329 wrapper wtrig)
8eabb4ff
MW
1330{
1331 struct conn conn;
04a5abae
MW
1332 struct trigger trig;
1333 int rc, maxfd;
8eabb4ff 1334 ssize_t n;
04a5abae 1335 fd_set rfds, wfds;
8eabb4ff
MW
1336 jbyte *p = 0;
1337
1338 if (unwrap(jni, &conn, &conn_type, wconn)) goto end;
04a5abae 1339 if (unwrap(jni, &trig, &trigger_type, wtrig)) goto end;
8eabb4ff 1340 if (check_buffer_bounds(jni, "send", buf, start, len)) goto end;
3a2f1a4b 1341
8eabb4ff 1342 p = (*jni)->GetByteArrayElements(jni, buf, 0);
3a2f1a4b
MW
1343 if (!p) goto end;
1344
04a5abae
MW
1345 maxfd = trig.rfd;
1346 if (maxfd < conn.fd) maxfd = conn.fd;
3a2f1a4b 1347 while (len) {
04a5abae
MW
1348 FD_ZERO(&rfds); FD_SET(trig.rfd, &rfds);
1349 FD_ZERO(&wfds); FD_SET(conn.fd, &wfds);
1350 rc = select(maxfd + 1, &rfds, &wfds, 0, 0); if (rc < 0) goto err;
1351 if (FD_ISSET(trig.rfd, &rfds)) break;
1352 if (FD_ISSET(conn.fd, &wfds)) {
1353 n = send(conn.fd, p + start, len, 0);
1354 if (n >= 0) { start += n; len -= n; }
1355 else if (errno != EAGAIN && errno != EWOULDBLOCK) goto err;
3a2f1a4b 1356 }
3a2f1a4b 1357 }
04a5abae 1358 goto end;
3a2f1a4b 1359
04a5abae
MW
1360err:
1361 except_syserror(jni, SYSERR, errno, "failed to send on connection");
3a2f1a4b
MW
1362end:
1363 if (p) (*jni)->ReleaseByteArrayElements(jni, buf, p, JNI_ABORT);
3a2f1a4b
MW
1364 return;
1365}
1366
8eabb4ff
MW
1367JNIEXPORT jint JNICALL JNIFUNC(recv)(JNIEnv *jni, jobject cls,
1368 wrapper wconn, jbyteArray buf,
04a5abae
MW
1369 jint start, jint len,
1370 wrapper wtrig)
3a2f1a4b 1371{
8eabb4ff 1372 struct conn conn;
04a5abae
MW
1373 struct trigger trig;
1374 int maxfd;
1375 fd_set rfds;
3a2f1a4b
MW
1376 jbyte *p = 0;
1377 jint rc = -1;
1378
8eabb4ff 1379 if (unwrap(jni, &conn, &conn_type, wconn)) goto end;
04a5abae 1380 if (unwrap(jni, &trig, &trigger_type, wtrig)) goto end;
8eabb4ff 1381 if (check_buffer_bounds(jni, "send", buf, start, len)) goto end;
3a2f1a4b 1382
8eabb4ff 1383 p = (*jni)->GetByteArrayElements(jni, buf, 0);
3a2f1a4b
MW
1384 if (!p) goto end;
1385
04a5abae
MW
1386 maxfd = trig.rfd;
1387 if (maxfd < conn.fd) maxfd = conn.fd;
1388 for (;;) {
1389 FD_ZERO(&rfds); FD_SET(trig.rfd, &rfds); FD_SET(conn.fd, &rfds);
1390 rc = select(maxfd + 1, &rfds, 0, 0, 0); if (rc < 0) goto err;
1391 if (FD_ISSET(trig.rfd, &rfds)) {
1392 break;
1393 }
1394 if (FD_ISSET(conn.fd, &rfds)) {
1395 rc = recv(conn.fd, p + start, len, 0);
1396 if (rc >= 0) break;
1397 else if (errno != EAGAIN && errno != EWOULDBLOCK) goto err;
1398 }
3a2f1a4b
MW
1399 }
1400 if (!rc) rc = -1;
04a5abae 1401 goto end;
3a2f1a4b 1402
04a5abae
MW
1403err:
1404 except_syserror(jni, SYSERR, errno, "failed to read from connection");
3a2f1a4b
MW
1405end:
1406 if (p) (*jni)->ReleaseByteArrayElements(jni, buf, p, 0);
3a2f1a4b
MW
1407 return (rc);
1408}
1409
25c35469
MW
1410JNIEXPORT void JNICALL JNIFUNC(closeconn)(JNIEnv *jni, jobject cls,
1411 wrapper wconn, jint how)
3a2f1a4b 1412{
8eabb4ff
MW
1413 struct conn conn;
1414 int rc;
1415
1416 if (unwrap(jni, &conn, &conn_type, wconn)) goto end;
1417 if (conn.fd == -1) goto end;
1418
1419 how &= CF_CLOSEMASK&~conn.f;
1420 conn.f |= how;
1421 if ((conn.f&CF_CLOSEMASK) == CF_CLOSEMASK) {
1422 close(conn.fd);
1423 conn.fd = -1;
3a2f1a4b 1424 } else {
8eabb4ff
MW
1425 if (how&CF_CLOSERD) shutdown(conn.fd, SHUT_RD);
1426 if (how&CF_CLOSEWR) shutdown(conn.fd, SHUT_WR);
3a2f1a4b 1427 }
8eabb4ff 1428 rc = update_wrapper(jni, &conn_type, wconn, &conn); assert(!rc);
3a2f1a4b
MW
1429
1430end:
3a2f1a4b
MW
1431 return;
1432}
8eabb4ff
MW
1433
1434/*----- That's all, folks -------------------------------------------------*/