Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AIX: add istat parser as stat is absent #1668

Merged
merged 1 commit into from
Jul 8, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
305 changes: 254 additions & 51 deletions byte-buddy-agent/src/main/java/net/bytebuddy/agent/VirtualMachine.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
import java.security.SecureRandom;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* <p>
Expand Down Expand Up @@ -2077,20 +2079,7 @@ class ForJnaPosixEnvironment implements Dispatcher {
*/
private final PosixLibrary library;

/**
* The maximum amount of attempts for checking the result of a foreign process.
*/
private final int attempts;

/**
* The pause between two checks for another process to return.
*/
private final long pause;

/**
* The time unit of the pause time.
*/
private final TimeUnit timeUnit;
private final PosixOwner posixOwner;

/**
* Creates a new connector for a POSIX enviornment using JNA.
Expand All @@ -2101,9 +2090,13 @@ class ForJnaPosixEnvironment implements Dispatcher {
*/
@SuppressWarnings("deprecation")
public ForJnaPosixEnvironment(int attempts, long pause, TimeUnit timeUnit) {
this.attempts = attempts;
this.pause = pause;
this.timeUnit = timeUnit;
if (Platform.isMac()) {
posixOwner = new PosixOwner.ForMacEnvironment(attempts, pause, timeUnit);
} else if (Platform.isAIX()) {
posixOwner = new PosixOwner.ForAixEnvironment(attempts, pause, timeUnit);
} else {
posixOwner = new PosixOwner.ForLinuxEnvironment(attempts, pause, timeUnit);
}
library = Native.loadLibrary("c", PosixLibrary.class);
}

Expand Down Expand Up @@ -2147,40 +2140,7 @@ public boolean isExistingProcess(int processId) {
*/
@SuppressFBWarnings(value = "OS_OPEN_STREAM", justification = "The stream life-cycle is bound to its process.")
public int getOwnerIdOf(File file) {
try {
// The binding for 'stat' is very platform dependant. To avoid the complexity of binding the correct method,
// stat is called as a separate command. This is less efficient but more portable.
Process process = Runtime.getRuntime().exec(new String[]{"stat",
Platform.isMac() ? "-f" : "-c",
"%u",
file.getAbsolutePath()});
int attempts = this.attempts;
boolean exited = false;
String line = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8")).readLine();
do {
try {
if (process.exitValue() != 0) {
throw new IllegalStateException("Error while executing stat");
}
exited = true;
break;
} catch (IllegalThreadStateException ignored) {
try {
Thread.sleep(timeUnit.toMillis(pause));
} catch (InterruptedException exception) {
Thread.currentThread().interrupt();
throw new IllegalStateException(exception);
}
}
} while (--attempts > 0);
if (!exited) {
process.destroy();
throw new IllegalStateException("Command for stat did not exit in time");
}
return Integer.parseInt(line);
} catch (IOException exception) {
throw new IllegalStateException("Unable to execute stat command", exception);
}
return posixOwner.getOwnerIdOf(file);
}

/**
Expand Down Expand Up @@ -2385,6 +2345,249 @@ protected List<String> getFieldOrder() {
}
}
}

protected interface PosixOwner {
/**
* Returns the user id of the owner of the supplied file.
*
* @param file The file for which to locate the owner.
* @return The owner id of the supplied file.
*/
int getOwnerIdOf(File file);

class ForLinuxEnvironment implements PosixOwner {

/**
* The maximum amount of attempts for checking the result of a foreign process.
*/
private final int attempts;

/**
* The pause between two checks for another process to return.
*/
private final long pause;

/**
* The time unit of the pause time.
*/
private final TimeUnit timeUnit;

/**
* Creates a new connector for a Linux POSIX enviornment using stat.
*
* @param attempts The maximum amount of attempts for checking the result of a foreign process.
* @param pause The pause between two checks for another process to return.
* @param timeUnit The time unit of the pause time.
*/
public ForLinuxEnvironment(int attempts, long pause, TimeUnit timeUnit) {
this.attempts = attempts;
this.pause = pause;
this.timeUnit = timeUnit;
}

/**
* Returns the user id of the owner of the supplied file.
*
* @param file The file for which to locate the owner.
* @return The owner id of the supplied file.
*/
@Override
public int getOwnerIdOf(File file) {
try {
// The binding for 'stat' is very platform dependant. To avoid the complexity of binding the correct method,
// stat is called as a separate command. This is less efficient but more portable.
Process process = Runtime.getRuntime().exec(new String[]{"stat",
"-f",
"%u",
file.getAbsolutePath()});
int attempts = this.attempts;
boolean exited = false;
String line = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8")).readLine();
do {
try {
if (process.exitValue() != 0) {
throw new IllegalStateException("Error while executing stat");
}
exited = true;
break;
} catch (IllegalThreadStateException ignored) {
try {
Thread.sleep(timeUnit.toMillis(pause));
} catch (InterruptedException exception) {
Thread.currentThread().interrupt();
throw new IllegalStateException(exception);
}
}
} while (--attempts > 0);
if (!exited) {
process.destroy();
throw new IllegalStateException("Command for stat did not exit in time");
}
return Integer.parseInt(line);
} catch (IOException exception) {
throw new IllegalStateException("Unable to execute stat command", exception);
}
}
}

class ForAixEnvironment implements PosixOwner {

/**
* The maximum amount of attempts for checking the result of a foreign process.
*/
private final int attempts;

/**
* The pause between two checks for another process to return.
*/
private final long pause;

/**
* The time unit of the pause time.
*/
private final TimeUnit timeUnit;

/**
* Creates a new connector for an AIX POSIX enviornment using stat.
*
* @param attempts The maximum amount of attempts for checking the result of a foreign process.
* @param pause The pause between two checks for another process to return.
* @param timeUnit The time unit of the pause time.
*/
public ForAixEnvironment(int attempts, long pause, TimeUnit timeUnit) {
this.attempts = attempts;
this.pause = pause;
this.timeUnit = timeUnit;
}

private static final Pattern AIX_OWNER_PATTERN = Pattern.compile("Owner: (\\d+)\\(");

/**
* Returns the user id of the owner of the supplied file.
*
* @param file The file for which to locate the owner.
* @return The owner id of the supplied file.
*/
@Override
public int getOwnerIdOf(File file) {
try {
Process process = Runtime.getRuntime().exec(new String[]{"istat", file.getAbsolutePath()});
int attempts = this.attempts;
boolean exited = false;
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8"));
StringBuilder output = new StringBuilder();
String line;
while ((line = bufferedReader.readLine()) != null) {
output.append(line).append("\n");
}
do {
try {
if (process.exitValue() != 0) {
throw new IllegalStateException("Error while executing istat");
}
exited = true;
break;
} catch (IllegalThreadStateException ignored) {
try {
Thread.sleep(timeUnit.toMillis(pause));
} catch (InterruptedException exception) {
Thread.currentThread().interrupt();
throw new IllegalStateException(exception);
}
}
} while (--attempts > 0);
if (!exited) {
process.destroy();
throw new IllegalStateException("Command for istat did not exit in time");
}
Matcher matcher = AIX_OWNER_PATTERN.matcher(output.toString());
// Find and print the Owner UID
if (matcher.find()) {
return Integer.parseInt(matcher.group(1));
} else {
throw new IllegalStateException("Unable to parse response from istat command: " + output);
}
} catch (IOException exception) {
throw new IllegalStateException("Unable to execute istat command", exception);
}
}
}

class ForMacEnvironment implements PosixOwner {

/**
* The maximum amount of attempts for checking the result of a foreign process.
*/
private final int attempts;

/**
* The pause between two checks for another process to return.
*/
private final long pause;

/**
* The time unit of the pause time.
*/
private final TimeUnit timeUnit;

/**
* Creates a new connector for a Mac POSIX enviornment using stat.
*
* @param attempts The maximum amount of attempts for checking the result of a foreign process.
* @param pause The pause between two checks for another process to return.
* @param timeUnit The time unit of the pause time.
*/
public ForMacEnvironment(int attempts, long pause, TimeUnit timeUnit) {
this.attempts = attempts;
this.pause = pause;
this.timeUnit = timeUnit;
}

/**
* Returns the user id of the owner of the supplied file.
*
* @param file The file for which to locate the owner.
* @return The owner id of the supplied file.
*/
@Override
public int getOwnerIdOf(File file) {
try {
// The binding for 'stat' is very platform dependant. To avoid the complexity of binding the correct method,
// stat is called as a separate command. This is less efficient but more portable.
Process process = Runtime.getRuntime().exec(new String[]{"stat",
"-f",
"%u",
file.getAbsolutePath()});
int attempts = this.attempts;
boolean exited = false;
String line = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8")).readLine();
do {
try {
if (process.exitValue() != 0) {
throw new IllegalStateException("Error while executing stat");
}
exited = true;
break;
} catch (IllegalThreadStateException ignored) {
try {
Thread.sleep(timeUnit.toMillis(pause));
} catch (InterruptedException exception) {
Thread.currentThread().interrupt();
throw new IllegalStateException(exception);
}
}
} while (--attempts > 0);
if (!exited) {
process.destroy();
throw new IllegalStateException("Command for stat did not exit in time");
}
return Integer.parseInt(line);
} catch (IOException exception) {
throw new IllegalStateException("Unable to execute stat command", exception);
}
}
}
}
}

/**
Expand Down
Loading