]> git.sven.stormbind.net Git - sven/jattach.git/commitdiff
releasing package jattach version 2.2-1 master debian/2.2-1
authorSven Hoexter <sven@stormbind.net>
Tue, 16 Jan 2024 12:49:37 +0000 (13:49 +0100)
committerSven Hoexter <sven@stormbind.net>
Tue, 16 Jan 2024 12:49:37 +0000 (13:49 +0100)
14 files changed:
Makefile
README.md
debian/changelog
debian/control
debian/copyright
debian/jattach.1
debian/rules
jattach.spec
src/posix/jattach.c
src/posix/jattach_hotspot.c
src/posix/jattach_openj9.c
src/posix/psutil.c
src/posix/psutil.h
src/windows/jattach.c

index 4b04acfd31455cd610d7a7eb5b7616550aa12be2..078bca37c221f23d484ec4d05b86634836ff34e1 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-JATTACH_VERSION=2.0
+JATTACH_VERSION=2.2
 
 ifneq ($(findstring Windows,$(OS)),)
   CL=cl.exe
@@ -6,13 +6,14 @@ ifneq ($(findstring Windows,$(OS)),)
   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
 
@@ -38,7 +39,7 @@ build/jattach: src/posix/*.c src/posix/*.h
        $(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
index 711ffe647bc81b9d5fa14c873cfa8c431cdf2e5a..958420452d3341ae33bb7f1d58cddeeb8edbde79 100644 (file)
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
 
 ### 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.
@@ -24,7 +24,7 @@ https://docs.oracle.com/javase/8/docs/jdk/api/attach/spec/
 
 ### Download
 
-Binaries are available on the [Releases](https://github.com/apangin/jattach/releases) page.
+Binaries are available on the [Releases](https://github.com/jattach/jattach/releases) page.
 
 On some platforms, you can also [install](#installation) jattach with a package manager.
 
@@ -46,23 +46,30 @@ which takes .jar path and its arguments as a single options string.
 
 #### List available jcmd commands 
 
-    $ jattach <pid> jcmd "help -all"
+    $ jattach <pid> jcmd help -all
 
 ### Installation
-#### FreeBSD
+#### Debian, Ubuntu
 
-On FreeBSD, you can use the following command to install `jattach` package:
+On Debian and Ubuntu, you can install `jattach` from the official repository:
 
-    $ pkg install jattach
+    # apt install jattach
 
 #### Alpine Linux
 
-On Alpine Linux, you can use the following command to install `jattach` package from the edge/community repository:
+On Alpine Linux, you can install `jattach` package from the edge/community repository:
 
-    $ apk add --no-cache jattach --repository http://dl-cdn.alpinelinux.org/alpine/edge/community/
+    # apk add --no-cache jattach --repository http://dl-cdn.alpinelinux.org/alpine/edge/community/
 
 #### Archlinux
 
 [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
+    # yay -S jattach
+
+#### FreeBSD
+
+On FreeBSD, you can use the following command to install `jattach`:
+
+    # pkg install jattach
+
index 146d11903603d77f43fc7698147095915658225d..c11538517e969115c88c33b3e29075897c3f8d21 100644 (file)
@@ -1,4 +1,23 @@
-jattach (2.0-1) UNRELEASED; urgency=medium
+jattach (2.2-1) unstable; urgency=medium
+
+  * New upstream release.
+  * Update Standards-Version to 4.6.2 no changes required.
+  * Update debian/copyright, remove years for debian/* content.
+
+ -- Sven Hoexter <hoexter@debian.org>  Tue, 16 Jan 2024 13:49:13 +0100
+
+jattach (2.1-1) unstable; urgency=medium
+
+  [ Sven Hoexter ]
+  * New upstream release.
+  * Update Standards-Version to 4.6.1 - no changes required.
+  * Drop additional LDFLAGS -Wl,--as-needed.
+  * Update dates to 2022 in debian/copyright.
+  * Set Rules-Requires-Root: no in debian/control.
+
+ -- Sven Hoexter <hoexter@debian.org>  Mon, 25 Jul 2022 21:01:27 +0200
+
+jattach (2.0-1) unstable; urgency=medium
 
   * New upstream release.
     + Adds OpenJ9 support.
@@ -7,7 +26,7 @@ jattach (2.0-1) UNRELEASED; urgency=medium
   * Update Standards-Version to 4.5.1 - no changes required.
   * Raise debhelper-compat level from 12 to 13.
 
- -- Sven Hoexter <sven@stormbind.net>  Mon, 16 Aug 2021 10:50:52 +0200
+ -- Sven Hoexter <hoexter@debian.org>  Mon, 16 Aug 2021 20:06:29 +0200
 
 jattach (1.5-2) unstable; urgency=medium
 
index 3f232aadb50fb4477ea63c1f3b554e5c2342d611..0f168f5f347e92c3be18d2caedee16617e0d0dbd 100644 (file)
@@ -3,10 +3,11 @@ Section: java
 Priority: optional
 Maintainer: Sven Hoexter <hoexter@debian.org>
 Build-Depends: debhelper-compat (=13)
-Standards-Version: 4.5.1
+Standards-Version: 4.6.2
 Homepage: https://github.com/apangin/jattach
 Vcs-Browser: https://git.sven.stormbind.net/?p=sven/jattach.git
 Vcs-Git: https://git.sven.stormbind.net/jattach.git
+Rules-Requires-Root: no
 
 Package: jattach
 Architecture: any
index 43de9086115d763ebdd70bf94f3f9825b8012698..6073fd7c74241785fdc5e4008c0269ca1e155429 100644 (file)
@@ -3,11 +3,11 @@ Upstream-Name: jattach
 Source: https://github.com/apangin/jattach/releases
 
 Files: *
-Copyright: 2016-2019 Andrei Pangin
+Copyright: 2016-2024 Andrei Pangin, jattach authors
 License: Apache 
 
 Files: debian/*
-Copyright: 2019 Sven Hoexter <sven@stormbind.net>
+Copyright: Sven Hoexter <sven@stormbind.net>
 License: Apache
 
 License: Apache
index 22335f910150124640ad10e9a205a534adddf16a..62c2195d805f50a3e4e2d1475db364669bcc6648 100644 (file)
@@ -1,10 +1,10 @@
 .\"                                      Hey, EMACS: -*- nroff -*-
-.\" (C) Copyright 2019-2020 Sven Hoexter <sven@stormbind.net>,
+.\" (C) Copyright 2019-2024 Sven Hoexter <sven@stormbind.net>,
 .\"
 .\" First parameter, NAME, should be all caps
 .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
 .\" other parameters are allowed: see man(7), man(1)
-.TH JATTACH 1 "February 28 2020"
+.TH JATTACH 1 "January 16 2024"
 .\" Please adjust this date whenever revising the manpage.
 .\"
 .\" Some roff macros, for reference:
@@ -23,7 +23,7 @@ jattach \- dynamc attach utility for the jvm
 .B jattach
 .RI "pid [load|properties|agentProperties|datadump|threaddump|dumpheap|inspectheap|setflag|printflag|jcmd] [args]"
 .SH DESCRIPTION
-jattach is an all in one jamp, jstack, jcmd, jinfo implementation as a tiny single C program.
+jattach is an all in one jmap, jstack, jcmd, jinfo implementation as a tiny single C program.
 .SH OPTIONS
 .B load
 load agent library
index b959739bb4e0bc8d259a0ad145f024a90193fcbd..ac1a23611beda83f2d06a7a3be61f5dea1cc91a4 100755 (executable)
@@ -1,7 +1,6 @@
 #!/usr/bin/make -f
 #export DH_VERBOSE = 1
 export DEB_BUILD_MAINT_OPTIONS = hardening=+all
-export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed
 
 %:
        dh $@
index 529cf0e7b1c18687b083ebbcdab149af7108b21c..fdac74214ee55151667ddcd400945ea137e98e56 100644 (file)
@@ -1,11 +1,11 @@
 Name:          jattach
-Version:       2.0
+Version:       2.2
 Release:       1
 Summary:       JVM Dynamic Attach utility
 
 Group:         Development/Tools
 License:       ASL 2.0
-URL:           https://github.com/apangin/jattach
+URL:           https://github.com/jattach/jattach
 Vendor:                Andrei Pangin
 Packager:      Vadim Tsesko <incubos@yandex.com>
 
@@ -35,6 +35,15 @@ install -p -m 555 %{_sourcedir}/bin/jattach ${BIN}
 /usr/bin/jattach
 
 %changelog
+* Wed Jan 10 2024 Andrei Pangin <noreply@pangin.pro> - 2.2-1
+- Automatically concatenate jcmd arguments
+- Fixed attach to OpenJ9 on macOS
+- Fixed container support on Linux 3.x
+
+* 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
index 804d13daee52beff86aef0e1dae473c8c611620a..a8a851e11d5d5298239f2c1378bba8275fa4acf8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2021 Andrei Pangin
+ * Copyright jattach authors
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 
 
 extern int is_openj9_process(int pid);
-extern int jattach_openj9(int pid, int nspid, int argc, char** argv);
-extern int jattach_hotspot(int pid, int nspid, int argc, char** argv);
+extern int jattach_openj9(int pid, int nspid, int argc, char** argv, int print_output);
+extern int jattach_hotspot(int pid, int nspid, int argc, char** argv, int print_output);
+
+int mnt_changed = 0;
 
 
 __attribute__((visibility("default")))
-int jattach(int pid, int argc, char** argv) {
+int jattach(int pid, int argc, char** argv, int print_output) {
     uid_t my_uid = geteuid();
     gid_t my_gid = getegid();
     uid_t target_uid = my_uid;
@@ -42,7 +44,7 @@ int jattach(int pid, int argc, char** argv) {
     // Network and IPC namespaces are essential for OpenJ9 connection.
     enter_ns(pid, "net");
     enter_ns(pid, "ipc");
-    int mnt_changed = enter_ns(pid, "mnt");
+    mnt_changed = enter_ns(pid, "mnt");
 
     // In HotSpot, dynamic attach is allowed only for the clients with the same euid/egid.
     // If we are running under root, switch to the required euid/egid automatically.
@@ -58,16 +60,17 @@ int jattach(int pid, int argc, char** argv) {
     signal(SIGPIPE, SIG_IGN);
 
     if (is_openj9_process(nspid)) {
-        return jattach_openj9(pid, nspid, argc, argv);
+        return jattach_openj9(pid, nspid, argc, argv, print_output);
     } else {
-        return jattach_hotspot(pid, nspid, argc, argv);
+        return jattach_hotspot(pid, nspid, argc, argv, print_output);
     }
 }
 
+#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"
@@ -84,5 +87,7 @@ int main(int argc, char** argv) {
         return 1;
     }
 
-    return jattach(pid, argc - 2, argv + 2);
+    return jattach(pid, argc - 2, argv + 2, 1);
 }
+
+#endif // JATTACH_VERSION
index e23e4609a0e3dcb31e38ad1e12475f31b1413cea..68d8805c05f9005644e9e9c146fff71f7bae03ee 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2021 Andrei Pangin
+ * Copyright jattach authors
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -27,6 +27,8 @@
 #include "psutil.h"
 
 
+extern int mnt_changed;
+
 // Check if remote JVM has already opened socket for Dynamic Attach
 static int check_socket(int pid) {
     char path[MAX_PATH];
@@ -46,7 +48,7 @@ static uid_t get_file_owner(const char* path) {
 // HotSpot will start Attach listener in response to SIGQUIT if it sees .attach_pid file
 static int start_attach_mechanism(int pid, int nspid) {
     char path[MAX_PATH];
-    snprintf(path, sizeof(path), "/proc/%d/cwd/.attach_pid%d", nspid, nspid);
+    snprintf(path, sizeof(path), "/proc/%d/cwd/.attach_pid%d", mnt_changed > 0 ? nspid : pid, nspid);
 
     int fd = creat(path, 0660);
     if (fd == -1 || (close(fd) == 0 && get_file_owner(path) != geteuid())) {
@@ -102,23 +104,37 @@ static int connect_socket(int pid) {
 
 // Send command with arguments to socket
 static int write_command(int fd, int argc, char** argv) {
+    char buf[8192];
+    const char* const limit = buf + sizeof(buf);
+
+    // jcmd has 2 arguments maximum; merge excessive arguments into one
+    int cmd_args = argc >= 2 && strcmp(argv[0], "jcmd") == 0 ? 2 : argc >= 4 ? 4 : argc;
+
     // Protocol version
-    if (write(fd, "1", 2) <= 0) {
-        return -1;
-    }
+    char* p = stpncpy(buf, "1", sizeof(buf)) + 1;
 
     int i;
-    for (i = 0; i < 4; i++) {
-        const char* arg = i < argc ? argv[i] : "";
-        if (write(fd, arg, strlen(arg) + 1) <= 0) {
+    for (i = 0; i < argc && p < limit; i++) {
+        if (i >= cmd_args) p[-1] = ' ';
+        p = stpncpy(p, argv[i], limit - p) + 1;
+    }
+    for (i = cmd_args; i < 4 && p < limit; i++) {
+        *p++ = 0;
+    }
+
+    const char* q = p < limit ? p : limit;
+    for (p = buf; p < q; ) {
+        ssize_t bytes = write(fd, p, q - p);
+        if (bytes <= 0) {
             return -1;
         }
+        p += (size_t)bytes;
     }
     return 0;
 }
 
 // Mirror response from remote JVM to stdout
-static int read_response(int fd, int argc, char** argv) {
+static int read_response(int fd, int argc, char** argv, int print_output) {
     char buf[8192];
     ssize_t bytes = read(fd, buf, sizeof(buf) - 1);
     if (bytes == 0) {
@@ -146,18 +162,20 @@ static int read_response(int fd, int argc, char** argv) {
         result = atoi(strncmp(buf + 2, "return code: ", 13) == 0 ? buf + 15 : buf + 2);
     }
 
-    // Mirror JVM response to stdout
-    printf("JVM response code = ");
-    do {
-        fwrite(buf, 1, bytes, stdout);
-        bytes = read(fd, buf, sizeof(buf));
-    } while (bytes > 0);
-    printf("\n");
+    if (print_output) {
+        // Mirror JVM response to stdout
+        printf("JVM response code = ");
+        do {
+            fwrite(buf, 1, bytes, stdout);
+            bytes = read(fd, buf, sizeof(buf));
+        } while (bytes > 0);
+        printf("\n");
+    }
 
     return result;
 }
 
-int jattach_hotspot(int pid, int nspid, int argc, char** argv) {
+int jattach_hotspot(int pid, int nspid, int argc, char** argv, int print_output) {
     if (check_socket(nspid) != 0 && start_attach_mechanism(pid, nspid) != 0) {
         perror("Could not start attach mechanism");
         return 1;
@@ -169,7 +187,9 @@ int jattach_hotspot(int pid, int nspid, int argc, char** argv) {
         return 1;
     }
 
-    printf("Connected to remote JVM\n");
+    if (print_output) {
+        printf("Connected to remote JVM\n");
+    }
 
     if (write_command(fd, argc, argv) != 0) {
         perror("Error writing to socket");
@@ -177,7 +197,7 @@ int jattach_hotspot(int pid, int nspid, int argc, char** argv) {
         return 1;
     }
 
-    int result = read_response(fd, argc, argv);
+    int result = read_response(fd, argc, argv, print_output);
     close(fd);
 
     return result;
index f34f4cc79447eec298f021df132235d4f2a444ba..90683c503c1d2b65835cf873bb96d82fb8eee718 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2021 Andrei Pangin
+ * Copyright jattach authors
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -47,7 +47,11 @@ static void translate_command(char* buf, size_t bufsize, int argc, char** argv)
         }
 
     } else if (strcmp(cmd, "jcmd") == 0) {
-        snprintf(buf, bufsize, "ATTACH_DIAGNOSTICS:%s,%s", argc > 1 ? argv[1] : "help", argc > 2 ? argv[2] : "");
+        size_t n = snprintf(buf, bufsize, "ATTACH_DIAGNOSTICS:%s", argc > 1 ? argv[1] : "help");
+        int i;
+        for (i = 2; i < argc && n < bufsize; i++) {
+            n += snprintf(buf + n, bufsize - n, ",%s", argv[i]);
+        }
 
     } else if (strcmp(cmd, "threaddump") == 0) {
         snprintf(buf, bufsize, "ATTACH_DIAGNOSTICS:Thread.print,%s", argc > 1 ? argv[1] : "");
@@ -123,7 +127,7 @@ static int write_command(int fd, const char* cmd) {
 }
 
 // Mirror response from remote JVM to stdout
-static int read_response(int fd, const char* cmd) {
+static int read_response(int fd, const char* cmd, int print_output) {
     size_t size = 8192;
     char* buf = malloc(size);
 
@@ -160,7 +164,7 @@ static int read_response(int fd, const char* cmd) {
             // AgentOnLoad error code comes right after AgentInitializationException
             result = strncmp(buf, "ATTACH_ERR AgentInitializationException", 39) == 0 ? atoi(buf + 39) : -1;
         }
-    } else if (strncmp(cmd, "ATTACH_DIAGNOSTICS:", 19) == 0) {
+    } else if (strncmp(cmd, "ATTACH_DIAGNOSTICS:", 19) == 0 && print_output) {
         char* p = strstr(buf, "openj9_diagnostics.string_result=");
         if (p != NULL) {
             // The result of a diagnostic command is encoded in Java Properties format
@@ -170,8 +174,10 @@ static int read_response(int fd, const char* cmd) {
         }
     }
 
-    buf[off - 1] = '\n';
-    fwrite(buf, 1, off, stdout);
+    if (print_output) {
+        buf[off - 1] = '\n';
+        fwrite(buf, 1, off, stdout);
+    }
 
     free(buf);
     return result;
@@ -221,7 +227,8 @@ static int create_attach_socket(int* port) {
     // Try IPv6 socket first, then fall back to IPv4
     int s = socket(AF_INET6, SOCK_STREAM, 0);
     if (s != -1) {
-        struct sockaddr_in6 addr = {AF_INET6, 0};
+        struct sockaddr_in6 addr = {0};
+        addr.sin6_family = AF_INET6;
         socklen_t addrlen = sizeof(addr);
         if (bind(s, (struct sockaddr*)&addr, addrlen) == 0 && listen(s, 0) == 0
                 && getsockname(s, (struct sockaddr*)&addr, &addrlen) == 0) {
@@ -229,7 +236,8 @@ static int create_attach_socket(int* port) {
             return s;
         }
     } else if ((s = socket(AF_INET, SOCK_STREAM, 0)) != -1) {
-        struct sockaddr_in addr = {AF_INET, 0};
+        struct sockaddr_in addr = {0};
+        addr.sin_family = AF_INET;
         socklen_t addrlen = sizeof(addr);
         if (bind(s, (struct sockaddr*)&addr, addrlen) == 0 && listen(s, 0) == 0
                 && getsockname(s, (struct sockaddr*)&addr, &addrlen) == 0) {
@@ -273,7 +281,8 @@ static int write_reply_info(int pid, int port, unsigned long long key) {
     }
 
     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;
@@ -327,6 +336,10 @@ static int accept_client(int s, unsigned long long key) {
         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;
 }
 
@@ -368,7 +381,7 @@ int is_openj9_process(int pid) {
     return stat(path, &stats) == 0;
 }
 
-int jattach_openj9(int pid, int nspid, int argc, char** argv) {
+int jattach_openj9(int pid, int nspid, int argc, char** argv, int print_output) {
     int attach_lock = acquire_lock("", "_attachlock");
     if (attach_lock < 0) {
         perror("Could not acquire attach lock");
@@ -406,7 +419,9 @@ int jattach_openj9(int pid, int nspid, int argc, char** argv) {
     notify_semaphore(-1, notif_count);
     release_lock(attach_lock);
 
-    printf("Connected to remote JVM\n");
+    if (print_output) {
+        printf("Connected to remote JVM\n");
+    }
 
     char cmd[8192];
     translate_command(cmd, sizeof(cmd), argc, argv);
@@ -417,7 +432,7 @@ int jattach_openj9(int pid, int nspid, int argc, char** argv) {
         return 1;
     }
 
-    int result = read_response(fd, cmd);
+    int result = read_response(fd, cmd, print_output);
     if (result != 1) {
         detach(fd);
     }
index 847a0609a332f1eff46eebe5515ee76c873b8347..d0287f598e2955c4c418be2a1e3cc24b735d5634 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2021 Andrei Pangin
+ * Copyright jattach authors
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -93,15 +93,14 @@ static int alt_lookup_nspid(int pid) {
                 // Check if /proc/<container-pid>/sched points back to <host-pid>
                 snprintf(path, sizeof(path), "/proc/%d/root/proc/%s/sched", pid, entry->d_name);
                 if (sched_get_host_pid(path) == pid) {
-                    closedir(dir);
-                    return atoi(entry->d_name);
+                    pid = atoi(entry->d_name);
+                    break;
                 }
             }
         }
         closedir(dir);
     }
 
-    // Could not find container pid; return host pid as the last resort
     return pid;
 }
 
@@ -129,15 +128,18 @@ int get_process_info(int pid, uid_t* uid, gid_t* gid, int* nspid) {
     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;
         }
     }
index fa1c4169ad8925ec334da8dc08214eab18e9420c..5dc3040dc7722c8921e5c83aaba75a8bad0edaab 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2021 Andrei Pangin
+ * Copyright jattach authors
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 #include <sys/types.h>
 
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 #define MAX_PATH 1024
 extern char tmp_path[];
 
@@ -43,4 +47,8 @@ int get_process_info(int pid, uid_t* uid, gid_t* gid, int* nspid);
 //        -1, if the attempt failed.
 int enter_ns(int pid, const char* type);
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif // _PSUTIL_H
index b43e2f875e2aeaef9f692c4282859ee7e0ce5652..f25f1d6f049f1f2acac970e02850736e0104d82b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016 Andrei Pangin
+ * Copyright jattach authors
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,7 +16,8 @@
 
 #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);
@@ -84,10 +85,19 @@ static LPVOID allocate_data(HANDLE hProcess, char* pipeName, int argc, char** ar
     strcpy(data.strJvm, "jvm");
     strcpy(data.strEnqueue, "_JVM_EnqueueOperation");
     strcpy(data.pipeName, pipeName);
+    data.args[0][0] = data.args[1][0] = data.args[2][0] = data.args[3][0] = 0;
 
+    // jcmd has 2 arguments maximum; merge excessive arguments into one
+    int cmd_args = argc >= 2 && strcmp(argv[0], "jcmd") == 0 ? 2 : argc >= 4 ? 4 : argc;
+
+    size_t n = 0;
     int i;
-    for (i = 0; i < 4; i++) {
-        strcpy(data.args[i], i < argc ? argv[i] : "");
+    for (i = 0; i < argc; i++) {
+        if (i < cmd_args) {
+            n = snprintf(data.args[i], sizeof(data.args[i]), "%s", argv[i]);
+        } else if (n < sizeof(data.args[cmd_args - 1])) {
+            n += snprintf(data.args[cmd_args - 1] + n, sizeof(data.args[cmd_args - 1]) - n, " %s", argv[i]);
+        }
     }
 
     LPVOID remoteData = VirtualAllocEx(hProcess, NULL, sizeof(CallData), MEM_COMMIT, PAGE_READWRITE);
@@ -202,7 +212,7 @@ static int inject_thread(int pid, char* pipeName, int argc, char** argv) {
 }
 
 // JVM response is read from the pipe and mirrored to stdout
-static int read_response(HANDLE hPipe) {
+static int read_response(HANDLE hPipe, int print_output) {
     ConnectNamedPipe(hPipe, NULL);
 
     char buf[8192];
@@ -216,17 +226,54 @@ static int read_response(HANDLE hPipe) {
     buf[bytesRead] = 0;
     int result = atoi(buf);
 
-    do {
-        fwrite(buf, 1, bytesRead, stdout);
-    } while (ReadFile(hPipe, buf, sizeof(buf), &bytesRead, NULL));
+    if (print_output) {
+        // Mirror JVM response to stdout
+        printf("JVM response code = ");
+        do {
+            fwrite(buf, 1, bytesRead, stdout);
+        } while (ReadFile(hPipe, buf, sizeof(buf), &bytesRead, NULL));
+         printf("\n");
+     }
 
     return result;
 }
 
+int jattach(int pid, int argc, char** argv, int print_output) {
+    // 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, &sec);
+    if (hPipe == INVALID_HANDLE_VALUE) {
+        print_error("Could not create pipe", GetLastError());
+        LocalFree(sec.lpSecurityDescriptor);
+        return 1;
+    }
+
+    LocalFree(sec.lpSecurityDescriptor);
+
+    if (!inject_thread(pid, pipeName, argc, argv)) {
+        CloseHandle(hPipe);
+        return 1;
+    }
+
+    int result = read_response(hPipe, print_output);
+    CloseHandle(hPipe);
+
+    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"
@@ -238,27 +285,12 @@ int main(int argc, char** argv) {
     }
 
     int pid = atoi(argv[1]);
-
-    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);
-    if (hPipe == NULL) {
-        print_error("Could not create pipe", GetLastError());
+    if (pid <= 0) {
+        fprintf(stderr, "%s is not a valid process ID\n", argv[1]);
         return 1;
     }
 
-    if (!inject_thread(pid, pipeName, argc - 2, argv + 2)) {
-        CloseHandle(hPipe);
-        return 1;
-    }
-
-    printf("Response code = ");
-    fflush(stdout);
-
-    int result = read_response(hPipe);
-    printf("\n");
-    CloseHandle(hPipe);
-
-    return result;
+    return jattach(pid, argc - 2, argv + 2, 1);
 }
+
+#endif // JATTACH_VERSION