Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/solaris/native/sun/tools/attach/LinuxVirtualMachine.c
32288 views
1
/*
2
* Copyright (c) 2005, 2017, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
#include "jni.h"
27
#include "jni_util.h"
28
#include "jvm.h"
29
30
#include <stdio.h>
31
#include <stdlib.h>
32
#include <string.h>
33
#include <errno.h>
34
#include <unistd.h>
35
#include <signal.h>
36
#include <dirent.h>
37
#include <ctype.h>
38
#include <sys/types.h>
39
#include <sys/types.h>
40
#include <sys/socket.h>
41
#include <sys/stat.h>
42
#include <sys/un.h>
43
44
#include "sun_tools_attach_LinuxVirtualMachine.h"
45
46
#define RESTARTABLE(_cmd, _result) do { \
47
do { \
48
_result = _cmd; \
49
} while((_result == -1) && (errno == EINTR)); \
50
} while(0)
51
52
/*
53
* Defines a callback that is invoked for each process
54
*/
55
typedef void (*ProcessCallback)(const pid_t pid, void* user_data);
56
57
/*
58
* Invokes the callback function for each process
59
*/
60
static void forEachProcess(ProcessCallback f, void* user_data) {
61
DIR* dir;
62
struct dirent* ptr;
63
64
/*
65
* To locate the children we scan /proc looking for files that have a
66
* position integer as a filename.
67
*/
68
if ((dir = opendir("/proc")) == NULL) {
69
return;
70
}
71
while ((ptr = readdir(dir)) != NULL) {
72
pid_t pid;
73
74
/* skip current/parent directories */
75
if (strcmp(ptr->d_name, ".") == 0 || strcmp(ptr->d_name, "..") == 0) {
76
continue;
77
}
78
79
/* skip files that aren't numbers */
80
pid = (pid_t)atoi(ptr->d_name);
81
if ((int)pid <= 0) {
82
continue;
83
}
84
85
/* invoke the callback */
86
(*f)(pid, user_data);
87
}
88
closedir(dir);
89
}
90
91
92
/*
93
* Returns the parent pid of a given pid, or -1 if not found
94
*/
95
static pid_t getParent(pid_t pid) {
96
char state;
97
FILE* fp;
98
char stat[2048];
99
int statlen;
100
char fn[32];
101
int i, p;
102
char* s;
103
104
/*
105
* try to open /proc/%d/stat
106
*/
107
sprintf(fn, "/proc/%d/stat", pid);
108
fp = fopen(fn, "r");
109
if (fp == NULL) {
110
return -1;
111
}
112
113
/*
114
* The format is: pid (command) state ppid ...
115
* As the command could be anything we must find the right most
116
* ")" and then skip the white spaces that follow it.
117
*/
118
statlen = fread(stat, 1, 2047, fp);
119
stat[statlen] = '\0';
120
fclose(fp);
121
s = strrchr(stat, ')');
122
if (s == NULL) {
123
return -1;
124
}
125
do s++; while (isspace(*s));
126
i = sscanf(s, "%c %d", &state, &p);
127
return (pid_t)p;
128
}
129
130
131
/*
132
* Class: sun_tools_attach_LinuxVirtualMachine
133
* Method: socket
134
* Signature: ()I
135
*/
136
JNIEXPORT jint JNICALL Java_sun_tools_attach_LinuxVirtualMachine_socket
137
(JNIEnv *env, jclass cls)
138
{
139
int fd = socket(PF_UNIX, SOCK_STREAM, 0);
140
if (fd == -1) {
141
JNU_ThrowIOExceptionWithLastError(env, "socket");
142
}
143
return (jint)fd;
144
}
145
146
/*
147
* Class: sun_tools_attach_LinuxVirtualMachine
148
* Method: connect
149
* Signature: (ILjava/lang/String;)I
150
*/
151
JNIEXPORT void JNICALL Java_sun_tools_attach_LinuxVirtualMachine_connect
152
(JNIEnv *env, jclass cls, jint fd, jstring path)
153
{
154
jboolean isCopy;
155
const char* p = GetStringPlatformChars(env, path, &isCopy);
156
if (p != NULL) {
157
struct sockaddr_un addr;
158
socklen_t sockLen = sizeof(addr);
159
int err = 0;
160
161
memset(&addr, 0, sizeof(addr));
162
addr.sun_family = AF_UNIX;
163
/* strncpy is safe because addr.sun_path was zero-initialized before. */
164
#ifndef __ANDROID__
165
strncpy(addr.sun_path, p, sizeof(addr.sun_path) - 1);
166
#else
167
/* Abstract namespace, first char is '\0', don't use strcpy */
168
jint len = (*env)->GetStringLength(env, path);
169
sockLen = offsetof(struct sockaddr_un, sun_path) + len;
170
memcpy(addr.sun_path, p, len);
171
#endif
172
173
if (connect(fd, (struct sockaddr*)&addr, sockLen) == -1) {
174
err = errno;
175
}
176
177
if (isCopy) {
178
JNU_ReleaseStringPlatformChars(env, path, p);
179
}
180
181
/*
182
* If the connect failed then we throw the appropriate exception
183
* here (can't throw it before releasing the string as can't call
184
* JNI with pending exception)
185
*/
186
if (err != 0) {
187
if (err == ENOENT) {
188
JNU_ThrowByName(env, "java/io/FileNotFoundException", NULL);
189
} else {
190
char* msg = strdup(strerror(err));
191
JNU_ThrowIOException(env, msg);
192
if (msg != NULL) {
193
free(msg);
194
}
195
}
196
}
197
}
198
}
199
200
/*
201
* Class: sun_tools_attach_LinuxVirtualMachine
202
* Method: isLinuxThreads
203
* Signature: ()V
204
*/
205
JNIEXPORT jboolean JNICALL Java_sun_tools_attach_LinuxVirtualMachine_isLinuxThreads
206
(JNIEnv *env, jclass cls)
207
{
208
# ifndef _CS_GNU_LIBPTHREAD_VERSION
209
# define _CS_GNU_LIBPTHREAD_VERSION 3
210
# endif
211
size_t n;
212
char* s;
213
jboolean res;
214
215
#ifndef __ANDROID__
216
n = confstr(_CS_GNU_LIBPTHREAD_VERSION, NULL, 0);
217
if (n <= 0) {
218
/* glibc before 2.3.2 only has LinuxThreads */
219
return JNI_TRUE;
220
}
221
222
s = (char *)malloc(n);
223
if (s == NULL) {
224
JNU_ThrowOutOfMemoryError(env, "malloc failed");
225
return JNI_TRUE;
226
}
227
confstr(_CS_GNU_LIBPTHREAD_VERSION, s, n);
228
#else
229
s = "Android NPTL";
230
#endif
231
232
/*
233
* If the LIBPTHREAD version include "NPTL" then we know we
234
* have the new threads library and not LinuxThreads
235
*/
236
res = (jboolean)(strstr(s, "NPTL") == NULL);
237
free(s);
238
return res;
239
}
240
241
/*
242
* Structure and callback function used to count the children of
243
* a given process, and record the pid of the "manager thread".
244
*/
245
typedef struct {
246
pid_t ppid;
247
int count;
248
pid_t mpid;
249
} ChildCountContext;
250
251
static void ChildCountCallback(const pid_t pid, void* user_data) {
252
ChildCountContext* context = (ChildCountContext*)user_data;
253
if (getParent(pid) == context->ppid) {
254
context->count++;
255
/*
256
* Remember the pid of the first child. If the final count is
257
* one then this is the pid of the LinuxThreads manager.
258
*/
259
if (context->count == 1) {
260
context->mpid = pid;
261
}
262
}
263
}
264
265
/*
266
* Class: sun_tools_attach_LinuxVirtualMachine
267
* Method: getLinuxThreadsManager
268
* Signature: (I)I
269
*/
270
JNIEXPORT jint JNICALL Java_sun_tools_attach_LinuxVirtualMachine_getLinuxThreadsManager
271
(JNIEnv *env, jclass cls, jint pid)
272
{
273
ChildCountContext context;
274
275
/*
276
* Iterate over all processes to find how many children 'pid' has
277
*/
278
context.ppid = pid;
279
context.count = 0;
280
context.mpid = (pid_t)0;
281
forEachProcess(ChildCountCallback, (void*)&context);
282
283
/*
284
* If there's no children then this is likely the pid of the primordial
285
* created by the launcher - in that case the LinuxThreads manager is the
286
* parent of this process.
287
*/
288
if (context.count == 0) {
289
pid_t parent = getParent(pid);
290
if ((int)parent > 0) {
291
return (jint)parent;
292
}
293
}
294
295
/*
296
* There's one child so this is likely the embedded VM case where the
297
* the primordial thread == LinuxThreads initial thread. The LinuxThreads
298
* manager in that case is the child.
299
*/
300
if (context.count == 1) {
301
return (jint)context.mpid;
302
}
303
304
/*
305
* If we get here it's most likely we were given the wrong pid
306
*/
307
JNU_ThrowIOException(env, "Unable to get pid of LinuxThreads manager thread");
308
return -1;
309
}
310
311
/*
312
* Structure and callback function used to send a QUIT signal to all
313
* children of a given process
314
*/
315
typedef struct {
316
pid_t ppid;
317
} SendQuitContext;
318
319
static void SendQuitCallback(const pid_t pid, void* user_data) {
320
SendQuitContext* context = (SendQuitContext*)user_data;
321
pid_t parent = getParent(pid);
322
if (parent == context->ppid) {
323
#ifndef __ANDROID__
324
kill(pid, SIGQUIT);
325
#else
326
/* Dalvik intercepts SIGQUIT so use SIGTERM */
327
kill(pid, SIGTERM);
328
#endif
329
}
330
}
331
332
/*
333
* Class: sun_tools_attach_LinuxVirtualMachine
334
* Method: sendQuitToChildrenOf
335
* Signature: (I)V
336
*/
337
JNIEXPORT void JNICALL Java_sun_tools_attach_LinuxVirtualMachine_sendQuitToChildrenOf
338
(JNIEnv *env, jclass cls, jint pid)
339
{
340
SendQuitContext context;
341
context.ppid = (pid_t)pid;
342
343
/*
344
* Iterate over all children of 'pid' and send a QUIT signal to each.
345
*/
346
forEachProcess(SendQuitCallback, (void*)&context);
347
}
348
349
/*
350
* Class: sun_tools_attach_LinuxVirtualMachine
351
* Method: sendQuitTo
352
* Signature: (I)V
353
*/
354
JNIEXPORT void JNICALL Java_sun_tools_attach_LinuxVirtualMachine_sendQuitTo
355
(JNIEnv *env, jclass cls, jint pid)
356
{
357
if (kill((pid_t)pid, SIGQUIT)) {
358
JNU_ThrowIOExceptionWithLastError(env, "kill");
359
}
360
}
361
362
/*
363
* Class: sun_tools_attach_LinuxVirtualMachine
364
* Method: checkPermissions
365
* Signature: (Ljava/lang/String;)V
366
*/
367
JNIEXPORT void JNICALL Java_sun_tools_attach_LinuxVirtualMachine_checkPermissions
368
(JNIEnv *env, jclass cls, jstring path)
369
{
370
#ifndef __ANDROID__
371
jboolean isCopy;
372
const char* p = GetStringPlatformChars(env, path, &isCopy);
373
if (p != NULL) {
374
struct stat64 sb;
375
uid_t uid, gid;
376
int res;
377
378
/*
379
* Check that the path is owned by the effective uid/gid of this
380
* process. Also check that group/other access is not allowed.
381
*/
382
uid = geteuid();
383
gid = getegid();
384
385
res = stat64(p, &sb);
386
if (res != 0) {
387
/* save errno */
388
res = errno;
389
}
390
391
if (res == 0) {
392
char msg[100];
393
jboolean isError = JNI_FALSE;
394
if (sb.st_uid != uid) {
395
jio_snprintf(msg, sizeof(msg)-1,
396
"file should be owned by the current user (which is %d) but is owned by %d", uid, sb.st_uid);
397
isError = JNI_TRUE;
398
} else if (sb.st_gid != gid) {
399
jio_snprintf(msg, sizeof(msg)-1,
400
"file's group should be the current group (which is %d) but the group is %d", gid, sb.st_gid);
401
isError = JNI_TRUE;
402
} else if ((sb.st_mode & (S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) != 0) {
403
jio_snprintf(msg, sizeof(msg)-1,
404
"file should only be readable and writable by the owner but has 0%03o access", sb.st_mode & 0777);
405
isError = JNI_TRUE;
406
}
407
if (isError) {
408
char buf[256];
409
jio_snprintf(buf, sizeof(buf)-1, "well-known file %s is not secure: %s", p, msg);
410
JNU_ThrowIOException(env, buf);
411
}
412
} else {
413
char* msg = strdup(strerror(res));
414
JNU_ThrowIOException(env, msg);
415
if (msg != NULL) {
416
free(msg);
417
}
418
}
419
420
if (isCopy) {
421
JNU_ReleaseStringPlatformChars(env, path, p);
422
}
423
}
424
#endif
425
}
426
427
/*
428
* Class: sun_tools_attach_LinuxVirtualMachine
429
* Method: close
430
* Signature: (I)V
431
*/
432
JNIEXPORT void JNICALL Java_sun_tools_attach_LinuxVirtualMachine_close
433
(JNIEnv *env, jclass cls, jint fd)
434
{
435
int res;
436
RESTARTABLE(close(fd), res);
437
}
438
439
/*
440
* Class: sun_tools_attach_LinuxVirtualMachine
441
* Method: read
442
* Signature: (I[BI)I
443
*/
444
JNIEXPORT jint JNICALL Java_sun_tools_attach_LinuxVirtualMachine_read
445
(JNIEnv *env, jclass cls, jint fd, jbyteArray ba, jint off, jint baLen)
446
{
447
unsigned char buf[128];
448
size_t len = sizeof(buf);
449
ssize_t n;
450
451
size_t remaining = (size_t)(baLen - off);
452
if (len > remaining) {
453
len = remaining;
454
}
455
456
RESTARTABLE(read(fd, buf, len), n);
457
if (n == -1) {
458
JNU_ThrowIOExceptionWithLastError(env, "read");
459
} else {
460
if (n == 0) {
461
n = -1; // EOF
462
} else {
463
(*env)->SetByteArrayRegion(env, ba, off, (jint)n, (jbyte *)(buf));
464
}
465
}
466
return n;
467
}
468
469
/*
470
* Class: sun_tools_attach_LinuxVirtualMachine
471
* Method: write
472
* Signature: (I[B)V
473
*/
474
JNIEXPORT void JNICALL Java_sun_tools_attach_LinuxVirtualMachine_write
475
(JNIEnv *env, jclass cls, jint fd, jbyteArray ba, jint off, jint bufLen)
476
{
477
size_t remaining = bufLen;
478
do {
479
unsigned char buf[128];
480
size_t len = sizeof(buf);
481
int n;
482
483
if (len > remaining) {
484
len = remaining;
485
}
486
(*env)->GetByteArrayRegion(env, ba, off, len, (jbyte *)buf);
487
488
RESTARTABLE(write(fd, buf, len), n);
489
if (n > 0) {
490
off += n;
491
remaining -= n;
492
} else {
493
JNU_ThrowIOExceptionWithLastError(env, "write");
494
return;
495
}
496
497
} while (remaining > 0);
498
}
499
500