admin.scala: Start on understanding the administration protocol.
[tripe-android] / jni.c
CommitLineData
3a2f1a4b
MW
1#include <assert.h>
2#include <errno.h>
3#include <inttypes.h>
4#include <stdint.h>
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8
9#include <jni.h>
10
11#include <sys/types.h>
12#include <sys/socket.h>
13#include <sys/un.h>
14#include <unistd.h>
15
16#undef sun
17
18union align {
19 int i;
20 long l;
21 double d;
22 void *p;
23 void (*f)(void *);
24 struct notexist *s;
25};
26
27struct native_type {
28 const char *name;
29 size_t sz;
30 uint32_t tag;
31};
32
33typedef jbyteArray wrapped;
34
35struct open {
36 wrapped obj;
37 jbyte *arr;
38};
39
40struct base {
41 uint32_t tag;
42};
43
44static void except(JNIEnv *jni, const char *clsname, const char *msg)
45{
46 jclass cls;
47 int rc;
48
49 cls = (*jni)->FindClass(jni, clsname); assert(cls);
50 rc = (*jni)->ThrowNew(jni, cls, msg); assert(!rc);
51}
52
53static void except_errno(JNIEnv *jni, const char *clsname, int err)
54 { except(jni, clsname, strerror(err)); }
55
56static void *open_struct_unchecked(JNIEnv *jni, wrapped obj, struct open *op)
57{
58 jboolean copyp;
59 uintptr_t p, q;
60
61 op->obj = obj;
62 op->arr = (*jni)->GetByteArrayElements(jni, obj, &copyp);
63 if (!op->arr) return (0);
64 p = (uintptr_t)op->arr;
65 q = p + sizeof(union align) - 1;
66 q -= q%sizeof(union align);
67 fprintf(stderr, ";; offset = %"PRIuPTR"\n", q - p);
68 return (op->arr + (q - p));
69}
70
71static void *open_struct(JNIEnv *jni, wrapped obj,
72 const struct native_type *ty, struct open *op)
73{
74 struct base *p;
75 jsize n;
76
77 if (!obj) { except(jni, "java/lang/NullPointerException", 0); return (0); }
78 n = (*jni)->GetArrayLength(jni, obj);
79 if ((*jni)->ExceptionOccurred(jni)) return (0);
80 p = open_struct_unchecked(jni, obj, op);
81 if (!p) return (0);
82 if (n < ty->sz + sizeof(union align) - 1 || p->tag != ty->tag)
83 {
84 (*jni)->ReleaseByteArrayElements(jni, obj, op->arr, JNI_ABORT);
85 except(jni, "uk/org/distorted/tripe/JNI$NativeObjectTypeException", 0);
86 return (0);
87 }
88 return (p);
89}
90
91static wrapped close_struct(JNIEnv *jni, struct open *op)
92{
93 (*jni)->ReleaseByteArrayElements(jni, op->obj, op->arr, 0);
94 return (op->obj);
95}
96
97static void *alloc_struct(JNIEnv *jni, const struct native_type *ty,
98 struct open *op)
99{
100 wrapped obj;
101 struct base *p;
102
103 obj = (*jni)->NewByteArray(jni, ty->sz + sizeof(union align) - 1);
104 if (!obj) return (0);
105 p = open_struct_unchecked(jni, obj, op);
106 if (!p) { (*jni)->DeleteLocalRef(jni, obj); return (0); }
107 p->tag = ty->tag;
108 return (p);
109}
110
111JNIEXPORT void JNICALL Java_uk_org_distorted_tripe_JNI_test
112 (JNIEnv *jni, jobject cls)
113 { printf("Hello from C!\n"); }
114
115struct toy {
116 struct base _base;
117 const char *p;
118};
119static const struct native_type toy_type =
120 { "toy", sizeof(struct toy), 0x58008918 };
121
122JNIEXPORT wrapped JNICALL Java_uk_org_distorted_tripe_JNI_make
123 (JNIEnv *jni, jobject cls)
124{
125 struct open op_toy;
126 struct toy *toy;
127
128 toy = alloc_struct(jni, &toy_type, &op_toy);
129 if (!toy) return (0);
130 toy->p = "A working thing";
131 return (close_struct(jni, &op_toy));
132}
133
134JNIEXPORT void JNICALL Java_uk_org_distorted_tripe_JNI_check
135 (JNIEnv *jni, jobject cls, wrapped wtoy)
136{
137 struct toy *toy;
138 struct open op_toy;
139
140 toy = open_struct(jni, wtoy, &toy_type, &op_toy);
141 if (!toy) return;
142 printf("Toy says: %s\n", toy->p);
143 close_struct(jni, &op_toy);
144}
145
146struct conn {
147 struct base _base;
148 int fd;
149 unsigned f;
150#define CF_CLOSERD 1u
151#define CF_CLOSEWR 2u
152#define CF_CLOSEMASK (CF_CLOSERD | CF_CLOSEWR)
153};
154static const struct native_type conn_type
155 = { "conn", sizeof(struct conn), 0xed030167 };
156
157JNIEXPORT wrapped JNICALL Java_uk_org_distorted_tripe_JNI_connect
158 (JNIEnv *jni, jobject cls)
159{
160 struct conn *conn;
161 struct open op;
162 struct sockaddr_un sun;
163 int fd = -1;
164
165 conn = alloc_struct(jni, &conn_type, &op);
166 if (!conn) goto err;
167
168 fd = socket(SOCK_STREAM, PF_UNIX, 0);
169 if (!fd) goto err_except;
170
171 sun.sun_family = AF_UNIX;
172 strcpy(sun.sun_path, "/tmp/mdw/sk");
173 if (connect(fd, (struct sockaddr *)&sun, sizeof(sun))) goto err_except;
174
175 conn->fd = fd;
176 return (close_struct(jni, &op));
177
178err_except:
179 except_errno(jni, "java/io/IOException", errno);
180err:
181 if (fd) close(fd);
182 return (0);
183}
184
185JNIEXPORT void JNICALL Java_uk_org_distorted_tripe_JNI_send
186 (JNIEnv *jni, jobject cls, wrapped wconn, jbyteArray buf,
187 jint start, jint len)
188{
189 struct conn *conn = 0;
190 struct open op;
191 jboolean copyp;
192 jsize bufsz;
193 ssize_t n;
194 jbyte *p = 0;
195
196 conn = open_struct(jni, wconn, &conn_type, &op);
197 if (!conn) goto end;
198
199 bufsz = (*jni)->GetArrayLength(jni, buf);
200 if ((*jni)->ExceptionOccurred(jni)) goto end;
201 if (bufsz < start || bufsz - start < len) {
202 except(jni, "java/lang/IndexOutOfBoundsException",
203 "bad send-buffer bounds");
204 goto end;
205 }
206
207 p = (*jni)->GetByteArrayElements(jni, buf, &copyp);
208 if (!p) goto end;
209
210 while (len) {
211 n = send(conn->fd, p + start, len, 0);
212 if (n < 0) {
213 except_errno(jni, "java/io/IOException", errno);
214 goto end;
215 }
216 start += n; len -= n;
217 }
218
219end:
220 if (p) (*jni)->ReleaseByteArrayElements(jni, buf, p, JNI_ABORT);
221 if (conn) close_struct(jni, &op);
222 return;
223}
224
225JNIEXPORT jint JNICALL Java_uk_org_distorted_tripe_JNI_recv
226 (JNIEnv *jni, jobject cls, wrapped wconn, jbyteArray buf,
227 jint start, jint len)
228{
229 struct conn *conn = 0;
230 struct open op;
231 jboolean copyp;
232 jsize bufsz;
233 jbyte *p = 0;
234 jint rc = -1;
235
236 conn = open_struct(jni, wconn, &conn_type, &op);
237 if (!conn) goto end;
238
239 bufsz = (*jni)->GetArrayLength(jni, buf);
240 if ((*jni)->ExceptionOccurred(jni)) goto end;
241 if (bufsz < start || bufsz - start < len) {
242 except(jni, "java/lang/IndexOutOfBoundsException",
243 "bad receive-buffer bounds");
244 goto end;
245 }
246
247 p = (*jni)->GetByteArrayElements(jni, buf, &copyp);
248 if (!p) goto end;
249
250 rc = recv(conn->fd, p + start, len, 0);
251 if (rc < 0) {
252 except_errno(jni, "java/io/IOException", errno);
253 goto end;
254 }
255 if (!rc) rc = -1;
256
257end:
258 if (p) (*jni)->ReleaseByteArrayElements(jni, buf, p, 0);
259 if (conn) close_struct(jni, &op);
260 return (rc);
261}
262
263JNIEXPORT void JNICALL Java_uk_org_distorted_tripe_JNI_close
264 (JNIEnv *jni, jobject cls, wrapped wconn, jint how)
265{
266 struct conn *conn = 0;
267 struct open op;
268
269 conn = open_struct(jni, wconn, &conn_type, &op);
270 if (!conn || conn->fd == -1) goto end;
271
272 how &= CF_CLOSEMASK&~conn->f;
273 conn->f |= how;
274fprintf(stderr, ";; closing %u\n", how);
275 if ((conn->f&CF_CLOSEMASK) == CF_CLOSEMASK) {
276 close(conn->fd);
277 conn->fd = -1;
278 } else {
279 if (how&CF_CLOSERD) shutdown(conn->fd, SHUT_RD);
280 if (how&CF_CLOSEWR) shutdown(conn->fd, SHUT_WR);
281 }
282
283end:
284 if (conn) close_struct(jni, &op);
285 return;
286}