Skip to content

Commit e597414

Browse files
authored
AIX: add istat parser as stat is absent (#1668)
1 parent 036b0bb commit e597414

1 file changed

Lines changed: 254 additions & 51 deletions

File tree

byte-buddy-agent/src/main/java/net/bytebuddy/agent/VirtualMachine.java

Lines changed: 254 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333
import java.security.SecureRandom;
3434
import java.util.*;
3535
import java.util.concurrent.TimeUnit;
36+
import java.util.regex.Matcher;
37+
import java.util.regex.Pattern;
3638

3739
/**
3840
* <p>
@@ -2077,20 +2079,7 @@ class ForJnaPosixEnvironment implements Dispatcher {
20772079
*/
20782080
private final PosixLibrary library;
20792081

2080-
/**
2081-
* The maximum amount of attempts for checking the result of a foreign process.
2082-
*/
2083-
private final int attempts;
2084-
2085-
/**
2086-
* The pause between two checks for another process to return.
2087-
*/
2088-
private final long pause;
2089-
2090-
/**
2091-
* The time unit of the pause time.
2092-
*/
2093-
private final TimeUnit timeUnit;
2082+
private final PosixOwner posixOwner;
20942083

20952084
/**
20962085
* Creates a new connector for a POSIX enviornment using JNA.
@@ -2101,9 +2090,13 @@ class ForJnaPosixEnvironment implements Dispatcher {
21012090
*/
21022091
@SuppressWarnings("deprecation")
21032092
public ForJnaPosixEnvironment(int attempts, long pause, TimeUnit timeUnit) {
2104-
this.attempts = attempts;
2105-
this.pause = pause;
2106-
this.timeUnit = timeUnit;
2093+
if (Platform.isMac()) {
2094+
posixOwner = new PosixOwner.ForMacEnvironment(attempts, pause, timeUnit);
2095+
} else if (Platform.isAIX()) {
2096+
posixOwner = new PosixOwner.ForAixEnvironment(attempts, pause, timeUnit);
2097+
} else {
2098+
posixOwner = new PosixOwner.ForLinuxEnvironment(attempts, pause, timeUnit);
2099+
}
21072100
library = Native.loadLibrary("c", PosixLibrary.class);
21082101
}
21092102

@@ -2147,40 +2140,7 @@ public boolean isExistingProcess(int processId) {
21472140
*/
21482141
@SuppressFBWarnings(value = "OS_OPEN_STREAM", justification = "The stream life-cycle is bound to its process.")
21492142
public int getOwnerIdOf(File file) {
2150-
try {
2151-
// The binding for 'stat' is very platform dependant. To avoid the complexity of binding the correct method,
2152-
// stat is called as a separate command. This is less efficient but more portable.
2153-
Process process = Runtime.getRuntime().exec(new String[]{"stat",
2154-
Platform.isMac() ? "-f" : "-c",
2155-
"%u",
2156-
file.getAbsolutePath()});
2157-
int attempts = this.attempts;
2158-
boolean exited = false;
2159-
String line = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8")).readLine();
2160-
do {
2161-
try {
2162-
if (process.exitValue() != 0) {
2163-
throw new IllegalStateException("Error while executing stat");
2164-
}
2165-
exited = true;
2166-
break;
2167-
} catch (IllegalThreadStateException ignored) {
2168-
try {
2169-
Thread.sleep(timeUnit.toMillis(pause));
2170-
} catch (InterruptedException exception) {
2171-
Thread.currentThread().interrupt();
2172-
throw new IllegalStateException(exception);
2173-
}
2174-
}
2175-
} while (--attempts > 0);
2176-
if (!exited) {
2177-
process.destroy();
2178-
throw new IllegalStateException("Command for stat did not exit in time");
2179-
}
2180-
return Integer.parseInt(line);
2181-
} catch (IOException exception) {
2182-
throw new IllegalStateException("Unable to execute stat command", exception);
2183-
}
2143+
return posixOwner.getOwnerIdOf(file);
21842144
}
21852145

21862146
/**
@@ -2385,6 +2345,249 @@ protected List<String> getFieldOrder() {
23852345
}
23862346
}
23872347
}
2348+
2349+
protected interface PosixOwner {
2350+
/**
2351+
* Returns the user id of the owner of the supplied file.
2352+
*
2353+
* @param file The file for which to locate the owner.
2354+
* @return The owner id of the supplied file.
2355+
*/
2356+
int getOwnerIdOf(File file);
2357+
2358+
class ForLinuxEnvironment implements PosixOwner {
2359+
2360+
/**
2361+
* The maximum amount of attempts for checking the result of a foreign process.
2362+
*/
2363+
private final int attempts;
2364+
2365+
/**
2366+
* The pause between two checks for another process to return.
2367+
*/
2368+
private final long pause;
2369+
2370+
/**
2371+
* The time unit of the pause time.
2372+
*/
2373+
private final TimeUnit timeUnit;
2374+
2375+
/**
2376+
* Creates a new connector for a Linux POSIX enviornment using stat.
2377+
*
2378+
* @param attempts The maximum amount of attempts for checking the result of a foreign process.
2379+
* @param pause The pause between two checks for another process to return.
2380+
* @param timeUnit The time unit of the pause time.
2381+
*/
2382+
public ForLinuxEnvironment(int attempts, long pause, TimeUnit timeUnit) {
2383+
this.attempts = attempts;
2384+
this.pause = pause;
2385+
this.timeUnit = timeUnit;
2386+
}
2387+
2388+
/**
2389+
* Returns the user id of the owner of the supplied file.
2390+
*
2391+
* @param file The file for which to locate the owner.
2392+
* @return The owner id of the supplied file.
2393+
*/
2394+
@Override
2395+
public int getOwnerIdOf(File file) {
2396+
try {
2397+
// The binding for 'stat' is very platform dependant. To avoid the complexity of binding the correct method,
2398+
// stat is called as a separate command. This is less efficient but more portable.
2399+
Process process = Runtime.getRuntime().exec(new String[]{"stat",
2400+
"-f",
2401+
"%u",
2402+
file.getAbsolutePath()});
2403+
int attempts = this.attempts;
2404+
boolean exited = false;
2405+
String line = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8")).readLine();
2406+
do {
2407+
try {
2408+
if (process.exitValue() != 0) {
2409+
throw new IllegalStateException("Error while executing stat");
2410+
}
2411+
exited = true;
2412+
break;
2413+
} catch (IllegalThreadStateException ignored) {
2414+
try {
2415+
Thread.sleep(timeUnit.toMillis(pause));
2416+
} catch (InterruptedException exception) {
2417+
Thread.currentThread().interrupt();
2418+
throw new IllegalStateException(exception);
2419+
}
2420+
}
2421+
} while (--attempts > 0);
2422+
if (!exited) {
2423+
process.destroy();
2424+
throw new IllegalStateException("Command for stat did not exit in time");
2425+
}
2426+
return Integer.parseInt(line);
2427+
} catch (IOException exception) {
2428+
throw new IllegalStateException("Unable to execute stat command", exception);
2429+
}
2430+
}
2431+
}
2432+
2433+
class ForAixEnvironment implements PosixOwner {
2434+
2435+
/**
2436+
* The maximum amount of attempts for checking the result of a foreign process.
2437+
*/
2438+
private final int attempts;
2439+
2440+
/**
2441+
* The pause between two checks for another process to return.
2442+
*/
2443+
private final long pause;
2444+
2445+
/**
2446+
* The time unit of the pause time.
2447+
*/
2448+
private final TimeUnit timeUnit;
2449+
2450+
/**
2451+
* Creates a new connector for an AIX POSIX enviornment using stat.
2452+
*
2453+
* @param attempts The maximum amount of attempts for checking the result of a foreign process.
2454+
* @param pause The pause between two checks for another process to return.
2455+
* @param timeUnit The time unit of the pause time.
2456+
*/
2457+
public ForAixEnvironment(int attempts, long pause, TimeUnit timeUnit) {
2458+
this.attempts = attempts;
2459+
this.pause = pause;
2460+
this.timeUnit = timeUnit;
2461+
}
2462+
2463+
private static final Pattern AIX_OWNER_PATTERN = Pattern.compile("Owner: (\\d+)\\(");
2464+
2465+
/**
2466+
* Returns the user id of the owner of the supplied file.
2467+
*
2468+
* @param file The file for which to locate the owner.
2469+
* @return The owner id of the supplied file.
2470+
*/
2471+
@Override
2472+
public int getOwnerIdOf(File file) {
2473+
try {
2474+
Process process = Runtime.getRuntime().exec(new String[]{"istat", file.getAbsolutePath()});
2475+
int attempts = this.attempts;
2476+
boolean exited = false;
2477+
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8"));
2478+
StringBuilder output = new StringBuilder();
2479+
String line;
2480+
while ((line = bufferedReader.readLine()) != null) {
2481+
output.append(line).append("\n");
2482+
}
2483+
do {
2484+
try {
2485+
if (process.exitValue() != 0) {
2486+
throw new IllegalStateException("Error while executing istat");
2487+
}
2488+
exited = true;
2489+
break;
2490+
} catch (IllegalThreadStateException ignored) {
2491+
try {
2492+
Thread.sleep(timeUnit.toMillis(pause));
2493+
} catch (InterruptedException exception) {
2494+
Thread.currentThread().interrupt();
2495+
throw new IllegalStateException(exception);
2496+
}
2497+
}
2498+
} while (--attempts > 0);
2499+
if (!exited) {
2500+
process.destroy();
2501+
throw new IllegalStateException("Command for istat did not exit in time");
2502+
}
2503+
Matcher matcher = AIX_OWNER_PATTERN.matcher(output.toString());
2504+
// Find and print the Owner UID
2505+
if (matcher.find()) {
2506+
return Integer.parseInt(matcher.group(1));
2507+
} else {
2508+
throw new IllegalStateException("Unable to parse response from istat command: " + output);
2509+
}
2510+
} catch (IOException exception) {
2511+
throw new IllegalStateException("Unable to execute istat command", exception);
2512+
}
2513+
}
2514+
}
2515+
2516+
class ForMacEnvironment implements PosixOwner {
2517+
2518+
/**
2519+
* The maximum amount of attempts for checking the result of a foreign process.
2520+
*/
2521+
private final int attempts;
2522+
2523+
/**
2524+
* The pause between two checks for another process to return.
2525+
*/
2526+
private final long pause;
2527+
2528+
/**
2529+
* The time unit of the pause time.
2530+
*/
2531+
private final TimeUnit timeUnit;
2532+
2533+
/**
2534+
* Creates a new connector for a Mac POSIX enviornment using stat.
2535+
*
2536+
* @param attempts The maximum amount of attempts for checking the result of a foreign process.
2537+
* @param pause The pause between two checks for another process to return.
2538+
* @param timeUnit The time unit of the pause time.
2539+
*/
2540+
public ForMacEnvironment(int attempts, long pause, TimeUnit timeUnit) {
2541+
this.attempts = attempts;
2542+
this.pause = pause;
2543+
this.timeUnit = timeUnit;
2544+
}
2545+
2546+
/**
2547+
* Returns the user id of the owner of the supplied file.
2548+
*
2549+
* @param file The file for which to locate the owner.
2550+
* @return The owner id of the supplied file.
2551+
*/
2552+
@Override
2553+
public int getOwnerIdOf(File file) {
2554+
try {
2555+
// The binding for 'stat' is very platform dependant. To avoid the complexity of binding the correct method,
2556+
// stat is called as a separate command. This is less efficient but more portable.
2557+
Process process = Runtime.getRuntime().exec(new String[]{"stat",
2558+
"-f",
2559+
"%u",
2560+
file.getAbsolutePath()});
2561+
int attempts = this.attempts;
2562+
boolean exited = false;
2563+
String line = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8")).readLine();
2564+
do {
2565+
try {
2566+
if (process.exitValue() != 0) {
2567+
throw new IllegalStateException("Error while executing stat");
2568+
}
2569+
exited = true;
2570+
break;
2571+
} catch (IllegalThreadStateException ignored) {
2572+
try {
2573+
Thread.sleep(timeUnit.toMillis(pause));
2574+
} catch (InterruptedException exception) {
2575+
Thread.currentThread().interrupt();
2576+
throw new IllegalStateException(exception);
2577+
}
2578+
}
2579+
} while (--attempts > 0);
2580+
if (!exited) {
2581+
process.destroy();
2582+
throw new IllegalStateException("Command for stat did not exit in time");
2583+
}
2584+
return Integer.parseInt(line);
2585+
} catch (IOException exception) {
2586+
throw new IllegalStateException("Unable to execute stat command", exception);
2587+
}
2588+
}
2589+
}
2590+
}
23882591
}
23892592

23902593
/**

0 commit comments

Comments
 (0)