-JATTACH_VERSION=2.0
+JATTACH_VERSION=2.1
ifneq ($(findstring Windows,$(OS)),)
CL=cl.exe
JATTACH_EXE=jattach.exe
JATTACH_DLL=jattach.dll
else
- CFLAGS ?= -O3
JATTACH_EXE=jattach
UNAME_S:=$(shell uname -s)
ifeq ($(UNAME_S),Darwin)
+ CFLAGS ?= -O3 -arch x86_64 -arch arm64 -mmacos-version-min=10.12
JATTACH_DLL=libjattach.dylib
else
+ CFLAGS ?= -O3
JATTACH_DLL=libjattach.so
endif
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -DJATTACH_VERSION=\"$(JATTACH_VERSION)\" -o $@ src/posix/*.c
build/$(JATTACH_DLL): src/posix/*.c src/posix/*.h
- $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -DJATTACH_VERSION=\"$(JATTACH_VERSION)\" -fPIC -shared -fvisibility=hidden -o $@ src/posix/*.c
+ $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -fPIC -shared -fvisibility=hidden -o $@ src/posix/*.c
build/jattach.exe: src/windows/jattach.c
$(CL) $(CFLAGS) /DJATTACH_VERSION=\"$(JATTACH_VERSION)\" /Fobuild/jattach.obj /Fe$@ $^ advapi32.lib /link /SUBSYSTEM:CONSOLE,5.02
### JVM Dynamic Attach utility
-The utility to send commands to remote JVM via Dynamic Attach mechanism.
+The utility to send commands to a JVM process via Dynamic Attach mechanism.
All-in-one **jmap + jstack + jcmd + jinfo** functionality in a single tiny program.
No installed JDK required, works with just JRE. Supports Linux containers.
[jattach](https://aur.archlinux.org/packages/jattach/) package can be installed from [AUR](https://wiki.archlinux.org/index.php/Arch_User_Repository) using one of [AUR helpers](https://wiki.archlinux.org/index.php/AUR_helpers), e.g., `yay`:
$ yay -S jattach
+
+#### Debian Linux
+
+On Debian Linux, you can use the following command to install `jattach` from the [official repository](https://packages.debian.org/search?keywords=jattach):
+
+ $ apt install jattach
+
+Packages are provided for **bullseye** (stable), **bookworm** (testing) and **sid** (unstable).
Name: jattach
-Version: 2.0
+Version: 2.1
Release: 1
Summary: JVM Dynamic Attach utility
/usr/bin/jattach
%changelog
+* Mon Jul 25 2022 Vadim Tsesko <incubos@yandex.com> - 2.1-1
+- Handle both tabs and spaces when parsing /proc/pid/status
+- Socket timeout while reading response from OpenJ9 VM
+
* Wed Aug 11 2021 Vadim Tsesko <incubos@yandex.com> - 2.0-1
- Attach to OpenJ9 VMs
- Pass agent error codes
}
}
+#ifdef JATTACH_VERSION
+
int main(int argc, char** argv) {
if (argc < 3) {
printf("jattach " JATTACH_VERSION " built on " __DATE__ "\n"
return jattach(pid, argc - 2, argv + 2);
}
+
+#endif // JATTACH_VERSION
}
int chars = snprintf(path, sizeof(path), "%016llx\n%d\n", key, port);
- write(fd, path, chars);
+ ssize_t r = write(fd, path, chars);
+ (void)r;
close(fd);
return 0;
return -1;
}
+ // Reset the timeout, as the command execution may take arbitrary long time
+ struct timeval tv0 = {0, 0};
+ setsockopt(client, SOL_SOCKET, SO_RCVTIMEO, &tv0, sizeof(tv0));
+
return client;
}
int nspid_found = 0;
while (getline(&line, &size, status_file) != -1) {
- if (strncmp(line, "Uid:", 4) == 0) {
+ if (strncmp(line, "Uid:", 4) == 0 && strtok(line + 4, "\t ") != NULL) {
// Get the effective UID, which is the second value in the line
- *uid = (uid_t)atoi(strchr(line + 5, '\t'));
- } else if (strncmp(line, "Gid:", 4) == 0) {
+ *uid = (uid_t)atoi(strtok(NULL, "\t "));
+ } else if (strncmp(line, "Gid:", 4) == 0 && strtok(line + 4, "\t ") != NULL) {
// Get the effective GID, which is the second value in the line
- *gid = (gid_t)atoi(strchr(line + 5, '\t'));
+ *gid = (gid_t)atoi(strtok(NULL, "\t "));
} else if (strncmp(line, "NStgid:", 7) == 0) {
// PID namespaces can be nested; the last one is the innermost one
- *nspid = atoi(strrchr(line, '\t'));
+ char* s;
+ for (s = strtok(line + 7, "\t "); s != NULL; s = strtok(NULL, "\t ")) {
+ *nspid = atoi(s);
+ }
nspid_found = 1;
}
}
#include <stdio.h>
#include <stdlib.h>
-#include <Windows.h>
+#include <windows.h>
+#include <sddl.h>
typedef HMODULE (WINAPI *GetModuleHandle_t)(LPCTSTR lpModuleName);
typedef FARPROC (WINAPI *GetProcAddress_t)(HMODULE hModule, LPCSTR lpProcName);
return result;
}
-int main(int argc, char** argv) {
- if (argc < 3) {
- printf("jattach " JATTACH_VERSION " built on " __DATE__ "\n"
- "Copyright 2021 Andrei Pangin\n"
- "\n"
- "Usage: jattach <pid> <cmd> [args ...]\n"
- "\n"
- "Commands:\n"
- " load threaddump dumpheap setflag properties\n"
- " jcmd inspectheap datadump printflag agentProperties\n"
- );
- return 1;
- }
-
- int pid = atoi(argv[1]);
+int jattach(int pid, int argc, char** argv) {
+ // When attaching as an Administrator, make sure the target process can connect to our pipe,
+ // i.e. allow read-write access to everyone. For the complete format description, see
+ // https://docs.microsoft.com/en-us/windows/win32/secauthz/security-descriptor-string-format
+ SECURITY_ATTRIBUTES sec = {sizeof(SECURITY_ATTRIBUTES), NULL, FALSE};
+ ConvertStringSecurityDescriptorToSecurityDescriptor("D:(A;;GRGW;;;WD)", SDDL_REVISION_1,
+ &sec.lpSecurityDescriptor, NULL);
char pipeName[MAX_PATH];
sprintf(pipeName, "\\\\.\\pipe\\javatool%d", GetTickCount());
HANDLE hPipe = CreateNamedPipe(pipeName, PIPE_ACCESS_INBOUND, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
- 1, 4096, 8192, NMPWAIT_USE_DEFAULT_WAIT, NULL);
+ 1, 4096, 8192, NMPWAIT_USE_DEFAULT_WAIT, &sec);
if (hPipe == NULL) {
print_error("Could not create pipe", GetLastError());
+ LocalFree(sec.lpSecurityDescriptor);
return 1;
}
- if (!inject_thread(pid, pipeName, argc - 2, argv + 2)) {
+ LocalFree(sec.lpSecurityDescriptor);
+
+ if (!inject_thread(pid, pipeName, argc, argv)) {
CloseHandle(hPipe);
return 1;
}
return result;
}
+
+#ifdef JATTACH_VERSION
+
+int main(int argc, char** argv) {
+ if (argc < 3) {
+ printf("jattach " JATTACH_VERSION " built on " __DATE__ "\n"
+ "Copyright 2021 Andrei Pangin\n"
+ "\n"
+ "Usage: jattach <pid> <cmd> [args ...]\n"
+ "\n"
+ "Commands:\n"
+ " load threaddump dumpheap setflag properties\n"
+ " jcmd inspectheap datadump printflag agentProperties\n"
+ );
+ return 1;
+ }
+
+ int pid = atoi(argv[1]);
+ if (pid <= 0) {
+ fprintf(stderr, "%s is not a valid process ID\n", argv[1]);
+ return 1;
+ }
+
+ return jattach(pid, argc - 2, argv + 2);
+}
+
+#endif // JATTACH_VERSION