mirror of https://github.com/jenkinsci/jenkins.git
Merge branch 'SECURITY-144' into SECURITY-144-rc
Conflicts: core/src/main/java/hudson/EnvVars.java core/src/main/java/hudson/FilePath.java core/src/main/java/hudson/cli/ClientAuthenticationCache.java core/src/main/java/hudson/markup/MyspacePolicy.java core/src/main/java/hudson/model/Computer.java core/src/main/java/hudson/node_monitors/DiskSpaceMonitorDescriptor.java core/src/main/java/hudson/node_monitors/ResponseTimeMonitor.java core/src/main/java/hudson/node_monitors/TemporarySpaceMonitor.java core/src/main/java/hudson/security/AbstractPasswordBasedSecurityRealm.java core/src/main/java/hudson/slaves/Channels.java core/src/main/java/hudson/slaves/SlaveComputer.java core/src/main/java/hudson/tasks/Fingerprinter.java core/src/main/java/hudson/tasks/Shell.java core/src/main/java/hudson/tasks/junit/JUnitParser.java core/src/main/java/hudson/tasks/test/DefaultTestResultParserImpl.java core/src/main/java/hudson/util/ProcessTree.java core/src/main/java/hudson/util/RemotingDiagnostics.java core/src/main/java/jenkins/model/Jenkins.java core/src/test/java/hudson/LauncherTest.java pom.xml test/src/test/java/hudson/tasks/junit/CaseResultTest.java
This commit is contained in:
commit
89cbfc4639
|
|
@ -23,12 +23,12 @@
|
|||
*/
|
||||
package hudson;
|
||||
|
||||
import hudson.remoting.Callable;
|
||||
import hudson.remoting.VirtualChannel;
|
||||
import hudson.util.CaseInsensitiveComparator;
|
||||
import hudson.util.CyclicGraphDetector;
|
||||
import hudson.util.CyclicGraphDetector.CycleDetectedException;
|
||||
import hudson.util.VariableResolver;
|
||||
import jenkins.security.MasterToSlaveCallable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
|
@ -404,7 +404,7 @@ public class EnvVars extends TreeMap<String,String> {
|
|||
return channel.call(new GetEnvVars());
|
||||
}
|
||||
|
||||
private static final class GetEnvVars implements Callable<EnvVars,RuntimeException> {
|
||||
private static final class GetEnvVars extends MasterToSlaveCallable<EnvVars,RuntimeException> {
|
||||
public EnvVars call() {
|
||||
return new EnvVars(EnvVars.masterEnvVars);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,6 +112,13 @@ import java.util.regex.Pattern;
|
|||
import static hudson.FilePath.TarCompression.*;
|
||||
import static hudson.Util.*;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import jenkins.FilePathFilter;
|
||||
import jenkins.MasterToSlaveFileCallable;
|
||||
import jenkins.SlaveToMasterFileCallable;
|
||||
import jenkins.security.MasterToSlaveCallable;
|
||||
import org.jenkinsci.remoting.RoleChecker;
|
||||
import org.jenkinsci.remoting.RoleSensitive;
|
||||
|
||||
/**
|
||||
* {@link File} like object with remoting support.
|
||||
|
|
@ -197,6 +204,17 @@ public final class FilePath implements Serializable {
|
|||
// since the platform of the slave might be different, can't use java.io.File
|
||||
private final String remote;
|
||||
|
||||
/**
|
||||
* If this {@link FilePath} is deserialized to handle file access request from a remote computer,
|
||||
* this field is set to the filter that performs access control.
|
||||
*
|
||||
* <p>
|
||||
* If null, no access control is needed.
|
||||
*
|
||||
* @see #filterNonNull()
|
||||
*/
|
||||
private transient @Nullable FilePathFilter filter;
|
||||
|
||||
/**
|
||||
* Creates a {@link FilePath} that represents a path on the given node.
|
||||
*
|
||||
|
|
@ -423,11 +441,11 @@ public final class FilePath implements Serializable {
|
|||
*/
|
||||
public int archive(final ArchiverFactory factory, OutputStream os, final DirScanner scanner) throws IOException, InterruptedException {
|
||||
final OutputStream out = (channel!=null)?new RemoteOutputStream(os):os;
|
||||
return act(new FileCallable<Integer>() {
|
||||
return act(new SecureFileCallable<Integer>() {
|
||||
public Integer invoke(File f, VirtualChannel channel) throws IOException {
|
||||
Archiver a = factory.create(out);
|
||||
try {
|
||||
scanner.scan(f,a);
|
||||
scanner.scan(f,reading(a));
|
||||
} finally {
|
||||
a.close();
|
||||
}
|
||||
|
|
@ -455,13 +473,13 @@ public final class FilePath implements Serializable {
|
|||
* @see #unzipFrom(InputStream)
|
||||
*/
|
||||
public void unzip(final FilePath target) throws IOException, InterruptedException {
|
||||
target.act(new FileCallable<Void>() {
|
||||
target.act(new SecureFileCallable<Void>() {
|
||||
|
||||
public Void invoke(File dir, VirtualChannel channel) throws IOException {
|
||||
public Void invoke(File dir, VirtualChannel channel) throws IOException, InterruptedException {
|
||||
if (FilePath.this.isRemote())
|
||||
unzip(dir, FilePath.this.read()); // use streams
|
||||
else
|
||||
unzip(dir, new File(FilePath.this.getRemote())); // shortcut to local file
|
||||
unzip(dir, reading(new File(FilePath.this.getRemote()))); // shortcut to local file
|
||||
return null;
|
||||
}
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
|
@ -479,8 +497,8 @@ public final class FilePath implements Serializable {
|
|||
* @see #untarFrom(InputStream, TarCompression)
|
||||
*/
|
||||
public void untar(final FilePath target, final TarCompression compression) throws IOException, InterruptedException {
|
||||
target.act(new FileCallable<Void>() {
|
||||
public Void invoke(File dir, VirtualChannel channel) throws IOException {
|
||||
target.act(new SecureFileCallable<Void>() {
|
||||
public Void invoke(File dir, VirtualChannel channel) throws IOException, InterruptedException {
|
||||
readFromTar(FilePath.this.getName(),dir,compression.extract(FilePath.this.read()));
|
||||
return null;
|
||||
}
|
||||
|
|
@ -498,7 +516,7 @@ public final class FilePath implements Serializable {
|
|||
*/
|
||||
public void unzipFrom(InputStream _in) throws IOException, InterruptedException {
|
||||
final InputStream in = new RemoteInputStream(_in);
|
||||
act(new FileCallable<Void>() {
|
||||
act(new SecureFileCallable<Void>() {
|
||||
public Void invoke(File dir, VirtualChannel channel) throws IOException {
|
||||
unzip(dir, in);
|
||||
return null;
|
||||
|
|
@ -507,7 +525,7 @@ public final class FilePath implements Serializable {
|
|||
});
|
||||
}
|
||||
|
||||
private static void unzip(File dir, InputStream in) throws IOException {
|
||||
private void unzip(File dir, InputStream in) throws IOException {
|
||||
File tmpFile = File.createTempFile("tmpzip", null); // uses java.io.tmpdir
|
||||
try {
|
||||
// TODO why does this not simply use ZipInputStream?
|
||||
|
|
@ -519,7 +537,7 @@ public final class FilePath implements Serializable {
|
|||
}
|
||||
}
|
||||
|
||||
static private void unzip(File dir, File zipFile) throws IOException {
|
||||
private void unzip(File dir, File zipFile) throws IOException {
|
||||
dir = dir.getAbsoluteFile(); // without absolutization, getParentFile below seems to fail
|
||||
ZipFile zip = new ZipFile(zipFile);
|
||||
@SuppressWarnings("unchecked")
|
||||
|
|
@ -530,15 +548,15 @@ public final class FilePath implements Serializable {
|
|||
ZipEntry e = entries.nextElement();
|
||||
File f = new File(dir, e.getName());
|
||||
if (e.isDirectory()) {
|
||||
f.mkdirs();
|
||||
mkdirs(f);
|
||||
} else {
|
||||
File p = f.getParentFile();
|
||||
if (p != null) {
|
||||
p.mkdirs();
|
||||
mkdirs(p);
|
||||
}
|
||||
InputStream input = zip.getInputStream(e);
|
||||
try {
|
||||
IOUtils.copy(input, f);
|
||||
IOUtils.copy(input, writing(f));
|
||||
} finally {
|
||||
input.close();
|
||||
}
|
||||
|
|
@ -562,7 +580,7 @@ public final class FilePath implements Serializable {
|
|||
* Absolutizes this {@link FilePath} and returns the new one.
|
||||
*/
|
||||
public FilePath absolutize() throws IOException, InterruptedException {
|
||||
return new FilePath(channel,act(new FileCallable<String>() {
|
||||
return new FilePath(channel,act(new SecureFileCallable<String>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
public String invoke(File f, VirtualChannel channel) throws IOException {
|
||||
return f.getAbsolutePath();
|
||||
|
|
@ -580,9 +598,10 @@ public final class FilePath implements Serializable {
|
|||
* @since 1.456
|
||||
*/
|
||||
public void symlinkTo(final String target, final TaskListener listener) throws IOException, InterruptedException {
|
||||
act(new FileCallable<Void>() {
|
||||
act(new SecureFileCallable<Void>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
public Void invoke(File f, VirtualChannel channel) throws IOException, InterruptedException {
|
||||
writing(f);
|
||||
Util.createSymlink(f.getParentFile(),target,f.getName(),listener);
|
||||
return null;
|
||||
}
|
||||
|
|
@ -597,10 +616,10 @@ public final class FilePath implements Serializable {
|
|||
* @since 1.456
|
||||
*/
|
||||
public String readLink() throws IOException, InterruptedException {
|
||||
return act(new FileCallable<String>() {
|
||||
return act(new SecureFileCallable<String>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
public String invoke(File f, VirtualChannel channel) throws IOException, InterruptedException {
|
||||
return Util.resolveSymlink(f);
|
||||
return Util.resolveSymlink(reading(f));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -666,7 +685,7 @@ public final class FilePath implements Serializable {
|
|||
public void untarFrom(InputStream _in, final TarCompression compression) throws IOException, InterruptedException {
|
||||
try {
|
||||
final InputStream in = new RemoteInputStream(_in);
|
||||
act(new FileCallable<Void>() {
|
||||
act(new SecureFileCallable<Void>() {
|
||||
public Void invoke(File dir, VirtualChannel channel) throws IOException {
|
||||
readFromTar("input stream",dir, compression.extract(in));
|
||||
return null;
|
||||
|
|
@ -776,7 +795,8 @@ public final class FilePath implements Serializable {
|
|||
}
|
||||
}
|
||||
|
||||
private static final class Unpack implements FileCallable<Void> {
|
||||
// this reads from arbitrary URL
|
||||
private final class Unpack extends MasterToSlaveFileCallable<Void> {
|
||||
private final URL archive;
|
||||
Unpack(URL archive) {
|
||||
this.archive = archive;
|
||||
|
|
@ -845,7 +865,7 @@ public final class FilePath implements Serializable {
|
|||
public void copyFrom(FileItem file) throws IOException, InterruptedException {
|
||||
if(channel==null) {
|
||||
try {
|
||||
file.write(new File(remote));
|
||||
file.write(writing(new File(remote)));
|
||||
} catch (IOException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
|
|
@ -870,9 +890,14 @@ public final class FilePath implements Serializable {
|
|||
* Code that gets executed on the machine where the {@link FilePath} is local.
|
||||
* Used to act on {@link FilePath}.
|
||||
* <strong>Warning:</code> implementations must be serializable, so prefer a static nested class to an inner class.
|
||||
*
|
||||
* <p>
|
||||
* Subtypes would likely want to extend from either {@link MasterToSlaveCallable}
|
||||
* or {@link SlaveToMasterFileCallable}.
|
||||
*
|
||||
* @see FilePath#act(FileCallable)
|
||||
*/
|
||||
public interface FileCallable<T> extends Serializable {
|
||||
public interface FileCallable<T> extends Serializable, RoleSensitive {
|
||||
/**
|
||||
* Performs the computational task on the node where the data is located.
|
||||
*
|
||||
|
|
@ -888,6 +913,15 @@ public final class FilePath implements Serializable {
|
|||
T invoke(File f, VirtualChannel channel) throws IOException, InterruptedException;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link FileCallable}s that can be executed anywhere, including the master.
|
||||
*
|
||||
* The code is the same as {@link SlaveToMasterFileCallable}, but used as a marker to
|
||||
* designate those impls that use {@link FilePathFilter}.
|
||||
*/
|
||||
/*package*/ static abstract class SecureFileCallable<T> extends SlaveToMasterFileCallable<T> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes some program on the machine that this {@link FilePath} exists,
|
||||
* so that one can perform local file operations.
|
||||
|
|
@ -1023,6 +1057,12 @@ public final class FilePath implements Serializable {
|
|||
throw (IOException)new InterruptedIOException().initCause(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkRoles(RoleChecker checker) throws SecurityException {
|
||||
task.checkRoles(checker);
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
};
|
||||
}
|
||||
|
|
@ -1032,7 +1072,7 @@ public final class FilePath implements Serializable {
|
|||
* on which this file is available.
|
||||
*/
|
||||
public URI toURI() throws IOException, InterruptedException {
|
||||
return act(new FileCallable<URI>() {
|
||||
return act(new SecureFileCallable<URI>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
public URI invoke(File f, VirtualChannel channel) {
|
||||
return f.toURI();
|
||||
|
|
@ -1070,10 +1110,10 @@ public final class FilePath implements Serializable {
|
|||
* Creates this directory.
|
||||
*/
|
||||
public void mkdirs() throws IOException, InterruptedException {
|
||||
if(!act(new FileCallable<Boolean>() {
|
||||
if(!act(new SecureFileCallable<Boolean>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
public Boolean invoke(File f, VirtualChannel channel) throws IOException, InterruptedException {
|
||||
if(f.mkdirs() || f.exists())
|
||||
if(mkdirs(f) || f.exists())
|
||||
return true; // OK
|
||||
|
||||
// following Ant <mkdir> task to avoid possible race condition.
|
||||
|
|
@ -1089,10 +1129,10 @@ public final class FilePath implements Serializable {
|
|||
* Deletes this directory, including all its contents recursively.
|
||||
*/
|
||||
public void deleteRecursive() throws IOException, InterruptedException {
|
||||
act(new FileCallable<Void>() {
|
||||
act(new SecureFileCallable<Void>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
public Void invoke(File f, VirtualChannel channel) throws IOException {
|
||||
Util.deleteRecursive(f);
|
||||
deleteRecursive(deleting(f));
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
|
@ -1102,15 +1142,38 @@ public final class FilePath implements Serializable {
|
|||
* Deletes all the contents of this directory, but not the directory itself
|
||||
*/
|
||||
public void deleteContents() throws IOException, InterruptedException {
|
||||
act(new FileCallable<Void>() {
|
||||
act(new SecureFileCallable<Void>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
public Void invoke(File f, VirtualChannel channel) throws IOException {
|
||||
Util.deleteContentsRecursive(f);
|
||||
deleteContentsRecursive(f);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void deleteRecursive(File dir) throws IOException {
|
||||
if(!isSymlink(dir))
|
||||
deleteContentsRecursive(dir);
|
||||
try {
|
||||
deleteFile(deleting(dir));
|
||||
} catch (IOException e) {
|
||||
// if some of the child directories are big, it might take long enough to delete that
|
||||
// it allows others to create new files, causing problemsl ike JENKINS-10113
|
||||
// so give it one more attempt before we give up.
|
||||
if(!isSymlink(dir))
|
||||
deleteContentsRecursive(dir);
|
||||
deleteFile(deleting(dir));
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteContentsRecursive(File file) throws IOException {
|
||||
File[] files = file.listFiles();
|
||||
if(files==null)
|
||||
return; // the directory didn't exist in the first place
|
||||
for (File child : files)
|
||||
deleteRecursive(child);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the file name portion except the extension.
|
||||
*
|
||||
|
|
@ -1196,10 +1259,10 @@ public final class FilePath implements Serializable {
|
|||
*/
|
||||
public FilePath createTempFile(final String prefix, final String suffix) throws IOException, InterruptedException {
|
||||
try {
|
||||
return new FilePath(this,act(new FileCallable<String>() {
|
||||
return new FilePath(this,act(new SecureFileCallable<String>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
public String invoke(File dir, VirtualChannel channel) throws IOException {
|
||||
File f = File.createTempFile(prefix, suffix, dir);
|
||||
File f = writing(File.createTempFile(prefix, suffix, dir));
|
||||
return f.getName();
|
||||
}
|
||||
}));
|
||||
|
|
@ -1252,22 +1315,22 @@ public final class FilePath implements Serializable {
|
|||
*/
|
||||
public FilePath createTextTempFile(final String prefix, final String suffix, final String contents, final boolean inThisDirectory) throws IOException, InterruptedException {
|
||||
try {
|
||||
return new FilePath(channel,act(new FileCallable<String>() {
|
||||
return new FilePath(channel,act(new SecureFileCallable<String>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
public String invoke(File dir, VirtualChannel channel) throws IOException {
|
||||
if(!inThisDirectory)
|
||||
dir = new File(System.getProperty("java.io.tmpdir"));
|
||||
else
|
||||
dir.mkdirs();
|
||||
mkdirs(dir);
|
||||
|
||||
File f;
|
||||
try {
|
||||
f = File.createTempFile(prefix, suffix, dir);
|
||||
f = creating(File.createTempFile(prefix, suffix, dir));
|
||||
} catch (IOException e) {
|
||||
throw new IOException("Failed to create a temporary directory in "+dir,e);
|
||||
}
|
||||
|
||||
Writer w = new FileWriter(f);
|
||||
Writer w = new FileWriter(writing(f));
|
||||
try {
|
||||
w.write(contents);
|
||||
} finally {
|
||||
|
|
@ -1298,7 +1361,7 @@ public final class FilePath implements Serializable {
|
|||
*/
|
||||
public FilePath createTempDir(final String prefix, final String suffix) throws IOException, InterruptedException {
|
||||
try {
|
||||
return new FilePath(this,act(new FileCallable<String>() {
|
||||
return new FilePath(this,act(new SecureFileCallable<String>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
public String invoke(File dir, VirtualChannel channel) throws IOException {
|
||||
File f = File.createTempFile(prefix, suffix, dir);
|
||||
|
|
@ -1318,10 +1381,10 @@ public final class FilePath implements Serializable {
|
|||
* @return true, for a modicum of compatibility
|
||||
*/
|
||||
public boolean delete() throws IOException, InterruptedException {
|
||||
act(new FileCallable<Void>() {
|
||||
act(new SecureFileCallable<Void>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
public Void invoke(File f, VirtualChannel channel) throws IOException {
|
||||
Util.deleteFile(f);
|
||||
Util.deleteFile(deleting(f));
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
|
@ -1332,10 +1395,10 @@ public final class FilePath implements Serializable {
|
|||
* Checks if the file exists.
|
||||
*/
|
||||
public boolean exists() throws IOException, InterruptedException {
|
||||
return act(new FileCallable<Boolean>() {
|
||||
return act(new SecureFileCallable<Boolean>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
public Boolean invoke(File f, VirtualChannel channel) throws IOException {
|
||||
return f.exists();
|
||||
return stating(f).exists();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -1348,10 +1411,10 @@ public final class FilePath implements Serializable {
|
|||
* @see #touch(long)
|
||||
*/
|
||||
public long lastModified() throws IOException, InterruptedException {
|
||||
return act(new FileCallable<Long>() {
|
||||
return act(new SecureFileCallable<Long>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
public Long invoke(File f, VirtualChannel channel) throws IOException {
|
||||
return f.lastModified();
|
||||
return stating(f).lastModified();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -1362,12 +1425,12 @@ public final class FilePath implements Serializable {
|
|||
* @since 1.299
|
||||
*/
|
||||
public void touch(final long timestamp) throws IOException, InterruptedException {
|
||||
act(new FileCallable<Void>() {
|
||||
act(new SecureFileCallable<Void>() {
|
||||
private static final long serialVersionUID = -5094638816500738429L;
|
||||
public Void invoke(File f, VirtualChannel channel) throws IOException {
|
||||
if(!f.exists())
|
||||
new FileOutputStream(f).close();
|
||||
if(!f.setLastModified(timestamp))
|
||||
new FileOutputStream(creating(f)).close();
|
||||
if(!stating(f).setLastModified(timestamp))
|
||||
throw new IOException("Failed to set the timestamp of "+f+" to "+timestamp);
|
||||
return null;
|
||||
}
|
||||
|
|
@ -1375,10 +1438,10 @@ public final class FilePath implements Serializable {
|
|||
}
|
||||
|
||||
private void setLastModifiedIfPossible(final long timestamp) throws IOException, InterruptedException {
|
||||
String message = act(new FileCallable<String>() {
|
||||
String message = act(new SecureFileCallable<String>() {
|
||||
private static final long serialVersionUID = -828220335793641630L;
|
||||
public String invoke(File f, VirtualChannel channel) throws IOException {
|
||||
if(!f.setLastModified(timestamp)) {
|
||||
if(!writing(f).setLastModified(timestamp)) {
|
||||
if (Functions.isWindows()) {
|
||||
// On Windows this seems to fail often. See JENKINS-11073
|
||||
// Therefore don't fail, but just log a warning
|
||||
|
|
@ -1400,10 +1463,10 @@ public final class FilePath implements Serializable {
|
|||
* Checks if the file is a directory.
|
||||
*/
|
||||
public boolean isDirectory() throws IOException, InterruptedException {
|
||||
return act(new FileCallable<Boolean>() {
|
||||
return act(new SecureFileCallable<Boolean>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
public Boolean invoke(File f, VirtualChannel channel) throws IOException {
|
||||
return f.isDirectory();
|
||||
return stating(f).isDirectory();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -1414,10 +1477,10 @@ public final class FilePath implements Serializable {
|
|||
* @since 1.129
|
||||
*/
|
||||
public long length() throws IOException, InterruptedException {
|
||||
return act(new FileCallable<Long>() {
|
||||
return act(new SecureFileCallable<Long>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
public Long invoke(File f, VirtualChannel channel) throws IOException {
|
||||
return f.length();
|
||||
return stating(f).length();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -1427,7 +1490,7 @@ public final class FilePath implements Serializable {
|
|||
* @since 1.542
|
||||
*/
|
||||
public long getFreeDiskSpace() throws IOException, InterruptedException {
|
||||
return act(new FileCallable<Long>() {
|
||||
return act(new SecureFileCallable<Long>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
@Override public Long invoke(File f, VirtualChannel channel) throws IOException {
|
||||
return f.getFreeSpace();
|
||||
|
|
@ -1440,7 +1503,7 @@ public final class FilePath implements Serializable {
|
|||
* @since 1.542
|
||||
*/
|
||||
public long getTotalDiskSpace() throws IOException, InterruptedException {
|
||||
return act(new FileCallable<Long>() {
|
||||
return act(new SecureFileCallable<Long>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
@Override public Long invoke(File f, VirtualChannel channel) throws IOException {
|
||||
return f.getTotalSpace();
|
||||
|
|
@ -1453,7 +1516,7 @@ public final class FilePath implements Serializable {
|
|||
* @since 1.542
|
||||
*/
|
||||
public long getUsableDiskSpace() throws IOException, InterruptedException {
|
||||
return act(new FileCallable<Long>() {
|
||||
return act(new SecureFileCallable<Long>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
@Override public Long invoke(File f, VirtualChannel channel) throws IOException {
|
||||
return f.getUsableSpace();
|
||||
|
|
@ -1478,10 +1541,10 @@ public final class FilePath implements Serializable {
|
|||
*/
|
||||
public void chmod(final int mask) throws IOException, InterruptedException {
|
||||
if(!isUnix() || mask==-1) return;
|
||||
act(new FileCallable<Void>() {
|
||||
act(new SecureFileCallable<Void>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
public Void invoke(File f, VirtualChannel channel) throws IOException {
|
||||
_chmod(f, mask);
|
||||
_chmod(writing(f), mask);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
|
@ -1509,10 +1572,10 @@ public final class FilePath implements Serializable {
|
|||
*/
|
||||
public int mode() throws IOException, InterruptedException, PosixException {
|
||||
if(!isUnix()) return -1;
|
||||
return act(new FileCallable<Integer>() {
|
||||
return act(new SecureFileCallable<Integer>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
public Integer invoke(File f, VirtualChannel channel) throws IOException {
|
||||
return IOUtils.mode(f);
|
||||
return IOUtils.mode(stating(f));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -1556,10 +1619,10 @@ public final class FilePath implements Serializable {
|
|||
if (filter != null && !(filter instanceof Serializable)) {
|
||||
throw new IllegalArgumentException("Non-serializable filter of " + filter.getClass());
|
||||
}
|
||||
return act(new FileCallable<List<FilePath>>() {
|
||||
return act(new SecureFileCallable<List<FilePath>>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
public List<FilePath> invoke(File f, VirtualChannel channel) throws IOException {
|
||||
File[] children = f.listFiles(filter);
|
||||
File[] children = reading(f).listFiles(filter);
|
||||
if(children ==null) return null;
|
||||
|
||||
ArrayList<FilePath> r = new ArrayList<FilePath>(children.length);
|
||||
|
|
@ -1609,10 +1672,10 @@ public final class FilePath implements Serializable {
|
|||
* @since 1.465
|
||||
*/
|
||||
public FilePath[] list(final String includes, final String excludes, final boolean defaultExcludes) throws IOException, InterruptedException {
|
||||
return act(new FileCallable<FilePath[]>() {
|
||||
return act(new SecureFileCallable<FilePath[]>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
public FilePath[] invoke(File f, VirtualChannel channel) throws IOException {
|
||||
String[] files = glob(f, includes, excludes, defaultExcludes);
|
||||
String[] files = glob(reading(f), includes, excludes, defaultExcludes);
|
||||
|
||||
FilePath[] r = new FilePath[files.length];
|
||||
for( int i=0; i<r.length; i++ )
|
||||
|
|
@ -1642,18 +1705,20 @@ public final class FilePath implements Serializable {
|
|||
/**
|
||||
* Reads this file.
|
||||
*/
|
||||
public InputStream read() throws IOException {
|
||||
public InputStream read() throws IOException, InterruptedException {
|
||||
if(channel==null)
|
||||
return new FileInputStream(new File(remote));
|
||||
return new FileInputStream(reading(new File(remote)));
|
||||
|
||||
final Pipe p = Pipe.createRemoteToLocal();
|
||||
channel.callAsync(new Callable<Void,IOException>() {
|
||||
actAsync(new SecureFileCallable<Void>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
public Void call() throws IOException {
|
||||
FileInputStream fis=null;
|
||||
|
||||
@Override
|
||||
public Void invoke(File f, VirtualChannel channel) throws IOException, InterruptedException {
|
||||
FileInputStream fis = null;
|
||||
try {
|
||||
fis = new FileInputStream(new File(remote));
|
||||
Util.copyStream(fis,p.getOut());
|
||||
fis = new FileInputStream(reading(f));
|
||||
Util.copyStream(fis, p.getOut());
|
||||
return null;
|
||||
} finally {
|
||||
org.apache.commons.io.IOUtils.closeQuietly(fis);
|
||||
|
|
@ -1668,7 +1733,7 @@ public final class FilePath implements Serializable {
|
|||
/**
|
||||
* Reads this file into a string, by using the current system encoding.
|
||||
*/
|
||||
public String readToString() throws IOException {
|
||||
public String readToString() throws IOException, InterruptedException {
|
||||
InputStream in = read();
|
||||
try {
|
||||
return org.apache.commons.io.IOUtils.toString(in);
|
||||
|
|
@ -1695,16 +1760,16 @@ public final class FilePath implements Serializable {
|
|||
public OutputStream write() throws IOException, InterruptedException {
|
||||
if(channel==null) {
|
||||
File f = new File(remote).getAbsoluteFile();
|
||||
f.getParentFile().mkdirs();
|
||||
return new FileOutputStream(f);
|
||||
mkdirs(f.getParentFile());
|
||||
return new FileOutputStream(writing(f));
|
||||
}
|
||||
|
||||
return channel.call(new Callable<OutputStream,IOException>() {
|
||||
return act(new SecureFileCallable<OutputStream>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
public OutputStream call() throws IOException {
|
||||
File f = new File(remote).getAbsoluteFile();
|
||||
f.getParentFile().mkdirs();
|
||||
FileOutputStream fos = new FileOutputStream(f);
|
||||
public OutputStream invoke(File f, VirtualChannel channel) throws IOException, InterruptedException {
|
||||
f = f.getAbsoluteFile();
|
||||
mkdirs(f.getParentFile());
|
||||
FileOutputStream fos = new FileOutputStream(writing(f));
|
||||
return new RemoteOutputStream(fos);
|
||||
}
|
||||
});
|
||||
|
|
@ -1718,11 +1783,11 @@ public final class FilePath implements Serializable {
|
|||
* @since 1.105
|
||||
*/
|
||||
public void write(final String content, final String encoding) throws IOException, InterruptedException {
|
||||
act(new FileCallable<Void>() {
|
||||
act(new SecureFileCallable<Void>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
public Void invoke(File f, VirtualChannel channel) throws IOException {
|
||||
f.getParentFile().mkdirs();
|
||||
FileOutputStream fos = new FileOutputStream(f);
|
||||
mkdirs(f.getParentFile());
|
||||
FileOutputStream fos = new FileOutputStream(writing(f));
|
||||
Writer w = encoding != null ? new OutputStreamWriter(fos, encoding) : new OutputStreamWriter(fos);
|
||||
try {
|
||||
w.write(content);
|
||||
|
|
@ -1739,10 +1804,10 @@ public final class FilePath implements Serializable {
|
|||
* @see Util#getDigestOf(File)
|
||||
*/
|
||||
public String digest() throws IOException, InterruptedException {
|
||||
return act(new FileCallable<String>() {
|
||||
return act(new SecureFileCallable<String>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
public String invoke(File f, VirtualChannel channel) throws IOException {
|
||||
return Util.getDigestOf(f);
|
||||
return Util.getDigestOf(reading(f));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -1755,10 +1820,10 @@ public final class FilePath implements Serializable {
|
|||
if(this.channel != target.channel) {
|
||||
throw new IOException("renameTo target must be on the same host");
|
||||
}
|
||||
act(new FileCallable<Void>() {
|
||||
act(new SecureFileCallable<Void>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
public Void invoke(File f, VirtualChannel channel) throws IOException {
|
||||
f.renameTo(new File(target.remote));
|
||||
reading(f).renameTo(creating(new File(target.remote)));
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
|
@ -1773,7 +1838,7 @@ public final class FilePath implements Serializable {
|
|||
if(this.channel != target.channel) {
|
||||
throw new IOException("pullUpTo target must be on the same host");
|
||||
}
|
||||
act(new FileCallable<Void>() {
|
||||
act(new SecureFileCallable<Void>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
public Void invoke(File f, VirtualChannel channel) throws IOException {
|
||||
// JENKINS-16846: if f.getName() is the same as one of the files/directories in f,
|
||||
|
|
@ -1784,12 +1849,12 @@ public final class FilePath implements Serializable {
|
|||
|
||||
File t = new File(target.getRemote());
|
||||
|
||||
for(File child : tmp.listFiles()) {
|
||||
for(File child : reading(tmp).listFiles()) {
|
||||
File target = new File(t, child.getName());
|
||||
if(!child.renameTo(target))
|
||||
if(!stating(child).renameTo(creating(target)))
|
||||
throw new IOException("Failed to rename "+child+" to "+target);
|
||||
}
|
||||
tmp.delete();
|
||||
deleting(tmp).delete();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
|
@ -1828,12 +1893,12 @@ public final class FilePath implements Serializable {
|
|||
public void copyTo(OutputStream os) throws IOException, InterruptedException {
|
||||
final OutputStream out = new RemoteOutputStream(os);
|
||||
|
||||
act(new FileCallable<Void>() {
|
||||
act(new SecureFileCallable<Void>() {
|
||||
private static final long serialVersionUID = 4088559042349254141L;
|
||||
public Void invoke(File f, VirtualChannel channel) throws IOException {
|
||||
FileInputStream fis = null;
|
||||
try {
|
||||
fis = new FileInputStream(f);
|
||||
fis = new FileInputStream(reading(f));
|
||||
Util.copyStream(fis,out);
|
||||
return null;
|
||||
} finally {
|
||||
|
|
@ -1875,21 +1940,6 @@ public final class FilePath implements Serializable {
|
|||
channel.syncLocalIO();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remoting interface used for {@link FilePath#copyRecursiveTo(String, FilePath)}.
|
||||
*
|
||||
* TODO: this might not be the most efficient way to do the copy.
|
||||
*/
|
||||
interface RemoteCopier {
|
||||
/**
|
||||
* @param fileName
|
||||
* relative path name to the output file. Path separator must be '/'.
|
||||
*/
|
||||
void open(String fileName) throws IOException;
|
||||
void write(byte[] buf, int len) throws IOException;
|
||||
void close() throws IOException;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the contents of this directory recursively into the specified target directory.
|
||||
*
|
||||
|
|
@ -1944,35 +1994,41 @@ public final class FilePath implements Serializable {
|
|||
public int copyRecursiveTo(final DirScanner scanner, final FilePath target, final String description) throws IOException, InterruptedException {
|
||||
if(this.channel==target.channel) {
|
||||
// local to local copy.
|
||||
return act(new FileCallable<Integer>() {
|
||||
return act(new SecureFileCallable<Integer>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
public Integer invoke(File base, VirtualChannel channel) throws IOException {
|
||||
if(!base.exists()) return 0;
|
||||
assert target.channel==null;
|
||||
final File dest = new File(target.remote);
|
||||
final AtomicInteger count = new AtomicInteger();
|
||||
scanner.scan(base, new FileVisitor() {
|
||||
@Override public void visit(File f, String relativePath) throws IOException {
|
||||
scanner.scan(base, reading(new FileVisitor() {
|
||||
@Override
|
||||
public void visit(File f, String relativePath) throws IOException {
|
||||
if (f.isFile()) {
|
||||
File target = new File(dest, relativePath);
|
||||
IOUtils.mkdirs(target.getParentFile());
|
||||
Util.copyFile(f, target);
|
||||
mkdirsE(target.getParentFile());
|
||||
Util.copyFile(f, writing(target));
|
||||
count.incrementAndGet();
|
||||
}
|
||||
}
|
||||
@Override public boolean understandsSymlink() {
|
||||
|
||||
@Override
|
||||
public boolean understandsSymlink() {
|
||||
return true;
|
||||
}
|
||||
@Override public void visitSymlink(File link, String target, String relativePath) throws IOException {
|
||||
|
||||
@Override
|
||||
public void visitSymlink(File link, String target, String relativePath) throws IOException {
|
||||
try {
|
||||
IOUtils.mkdirs(new File(dest, relativePath).getParentFile());
|
||||
mkdirsE(new File(dest, relativePath).getParentFile());
|
||||
writing(new File(dest, target));
|
||||
Util.createSymlink(dest, target, relativePath, TaskListener.NULL);
|
||||
} catch (InterruptedException x) {
|
||||
throw (IOException) new IOException(x.toString()).initCause(x);
|
||||
}
|
||||
count.incrementAndGet();
|
||||
}
|
||||
});
|
||||
}));
|
||||
return count.get();
|
||||
}
|
||||
});
|
||||
|
|
@ -1981,7 +2037,7 @@ public final class FilePath implements Serializable {
|
|||
// local -> remote copy
|
||||
final Pipe pipe = Pipe.createLocalToRemote();
|
||||
|
||||
Future<Void> future = target.actAsync(new FileCallable<Void>() {
|
||||
Future<Void> future = target.actAsync(new SecureFileCallable<Void>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
public Void invoke(File f, VirtualChannel channel) throws IOException {
|
||||
try {
|
||||
|
|
@ -1992,7 +2048,7 @@ public final class FilePath implements Serializable {
|
|||
}
|
||||
}
|
||||
});
|
||||
Future<Integer> future2 = actAsync(new FileCallable<Integer>() {
|
||||
Future<Integer> future2 = actAsync(new SecureFileCallable<Integer>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
@Override public Integer invoke(File f, VirtualChannel channel) throws IOException, InterruptedException {
|
||||
return writeToTar(new File(remote), scanner, TarCompression.GZIP.compress(pipe.getOut()));
|
||||
|
|
@ -2009,7 +2065,7 @@ public final class FilePath implements Serializable {
|
|||
// remote -> local copy
|
||||
final Pipe pipe = Pipe.createRemoteToLocal();
|
||||
|
||||
Future<Integer> future = actAsync(new FileCallable<Integer>() {
|
||||
Future<Integer> future = actAsync(new SecureFileCallable<Integer>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
public Integer invoke(File f, VirtualChannel channel) throws IOException {
|
||||
try {
|
||||
|
|
@ -2069,10 +2125,10 @@ public final class FilePath implements Serializable {
|
|||
* @return
|
||||
* number of files/directories that are written.
|
||||
*/
|
||||
private static Integer writeToTar(File baseDir, DirScanner scanner, OutputStream out) throws IOException {
|
||||
private Integer writeToTar(File baseDir, DirScanner scanner, OutputStream out) throws IOException {
|
||||
Archiver tw = ArchiverFactory.TAR.create(out);
|
||||
try {
|
||||
scanner.scan(baseDir,tw);
|
||||
scanner.scan(baseDir,reading(tw));
|
||||
} finally {
|
||||
tw.close();
|
||||
}
|
||||
|
|
@ -2082,17 +2138,18 @@ public final class FilePath implements Serializable {
|
|||
/**
|
||||
* Reads from a tar stream and stores obtained files to the base dir.
|
||||
*/
|
||||
private static void readFromTar(String name, File baseDir, InputStream in) throws IOException {
|
||||
private void readFromTar(String name, File baseDir, InputStream in) throws IOException {
|
||||
TarInputStream t = new TarInputStream(in);
|
||||
try {
|
||||
TarEntry te;
|
||||
while ((te = t.getNextEntry()) != null) {
|
||||
File f = new File(baseDir,te.getName());
|
||||
if(te.isDirectory()) {
|
||||
f.mkdirs();
|
||||
mkdirs(f);
|
||||
} else {
|
||||
File parent = f.getParentFile();
|
||||
if (parent != null) parent.mkdirs();
|
||||
if (parent != null) mkdirs(parent);
|
||||
writing(f);
|
||||
|
||||
byte linkFlag = (Byte) LINKFLAG_FIELD.get(te);
|
||||
if (linkFlag==TarEntry.LF_SYMLINK) {
|
||||
|
|
@ -2131,7 +2188,7 @@ public final class FilePath implements Serializable {
|
|||
return new RemoteLauncher(listener,channel,channel.call(new IsUnix()));
|
||||
}
|
||||
|
||||
private static final class IsUnix implements Callable<Boolean,IOException> {
|
||||
private static final class IsUnix extends MasterToSlaveCallable<Boolean,IOException> {
|
||||
public Boolean call() throws IOException {
|
||||
return File.pathSeparatorChar==':';
|
||||
}
|
||||
|
|
@ -2168,7 +2225,7 @@ public final class FilePath implements Serializable {
|
|||
* @since 1.484
|
||||
*/
|
||||
public String validateAntFileMask(final String fileMasks, final int bound) throws IOException, InterruptedException {
|
||||
return act(new FileCallable<String>() {
|
||||
return act(new MasterToSlaveFileCallable<String>() {
|
||||
private static final long serialVersionUID = 1;
|
||||
public String invoke(File dir, VirtualChannel channel) throws IOException, InterruptedException {
|
||||
if(fileMasks.startsWith("~"))
|
||||
|
|
@ -2209,7 +2266,7 @@ public final class FilePath implements Serializable {
|
|||
|
||||
{// check the (2) above next as this is more expensive.
|
||||
// Try prepending "**/" to see if that results in a match
|
||||
FileSet fs = Util.createFileSet(dir,"**/"+fileMask);
|
||||
FileSet fs = Util.createFileSet(reading(dir),"**/"+fileMask);
|
||||
DirectoryScanner ds = fs.getDirectoryScanner(new Project());
|
||||
if(ds.getIncludedFilesCount()!=0) {
|
||||
// try shorter name first so that the suggestion results in least amount of changes
|
||||
|
|
@ -2278,7 +2335,7 @@ public final class FilePath implements Serializable {
|
|||
return super.isCaseSensitive();
|
||||
}
|
||||
};
|
||||
ds.setBasedir(dir);
|
||||
ds.setBasedir(reading(dir));
|
||||
ds.setIncludes(new String[] {pattern});
|
||||
try {
|
||||
ds.scan();
|
||||
|
|
@ -2451,8 +2508,14 @@ public final class FilePath implements Serializable {
|
|||
ois.defaultReadObject();
|
||||
if(ois.readBoolean()) {
|
||||
this.channel = channel;
|
||||
this.filter = null;
|
||||
} else {
|
||||
this.channel = null;
|
||||
// If the remote channel wants us to create a FilePath that points to a local file,
|
||||
// we need to make sure the access control takes place.
|
||||
// This covers the immediate case of FileCallables taking FilePath into reference closure implicitly,
|
||||
// but it also covers more general case of FilePath sent as a return value or argument.
|
||||
this.filter = FilePathFilter.current();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2487,6 +2550,14 @@ public final class FilePath implements Serializable {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Role check comes from {@link FileCallable}s.
|
||||
*/
|
||||
@Override
|
||||
public void checkRoles(RoleChecker checker) throws SecurityException {
|
||||
callable.checkRoles(checker);
|
||||
}
|
||||
|
||||
public ClassLoader getClassLoader() {
|
||||
return classLoader;
|
||||
}
|
||||
|
|
@ -2529,7 +2600,7 @@ public final class FilePath implements Serializable {
|
|||
* (User's home directory in the Unix sense) of the given channel.
|
||||
*/
|
||||
public static FilePath getHomeDirectory(VirtualChannel ch) throws InterruptedException, IOException {
|
||||
return ch.call(new Callable<FilePath,IOException>() {
|
||||
return ch.call(new MasterToSlaveCallable<FilePath,IOException>() {
|
||||
public FilePath call() throws IOException {
|
||||
return new FilePath(new File(System.getProperty("user.home")));
|
||||
}
|
||||
|
|
@ -2563,7 +2634,6 @@ public final class FilePath implements Serializable {
|
|||
scanSingle(new File(dir, workspacePath), archivedPath, visitor);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final ExecutorService threadPoolForRemoting = new ContextResettingExecutorService(
|
||||
|
|
@ -2573,4 +2643,93 @@ public final class FilePath implements Serializable {
|
|||
));
|
||||
|
||||
public static final LocalChannel localChannel = new LocalChannel(threadPoolForRemoting);
|
||||
|
||||
private @Nonnull FilePathFilter filterNonNull() {
|
||||
return filter!=null ? filter : FilePathFilter.EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps {@link FileVisitor} to notify read access to {@link FilePathFilter}.
|
||||
*/
|
||||
private FileVisitor reading(final FileVisitor v) {
|
||||
final FilePathFilter filter = FilePathFilter.current();
|
||||
if (filter==null) return v;
|
||||
|
||||
return new FileVisitor() {
|
||||
@Override
|
||||
public void visit(File f, String relativePath) throws IOException {
|
||||
filter.read(f);
|
||||
v.visit(f,relativePath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitSymlink(File link, String target, String relativePath) throws IOException {
|
||||
filter.read(link);
|
||||
v.visitSymlink(link, target, relativePath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean understandsSymlink() {
|
||||
return v.understandsSymlink();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass through 'f' after ensuring that we can read that file.
|
||||
*/
|
||||
private File reading(File f) {
|
||||
filterNonNull().read(f);
|
||||
return f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass through 'f' after ensuring that we can access the file attributes.
|
||||
*/
|
||||
private File stating(File f) {
|
||||
filterNonNull().stat(f);
|
||||
return f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass through 'f' after ensuring that we can create that file/dir.
|
||||
*/
|
||||
private File creating(File f) {
|
||||
filterNonNull().create(f);
|
||||
return f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass through 'f' after ensuring that we can write to that file.
|
||||
*/
|
||||
private File writing(File f) {
|
||||
FilePathFilter filter = filterNonNull();
|
||||
if (!f.exists())
|
||||
filter.create(f);
|
||||
filter.write(f);
|
||||
return f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass through 'f' after ensuring that we can delete that file.
|
||||
*/
|
||||
private File deleting(File f) {
|
||||
filterNonNull().delete(f);
|
||||
return f;
|
||||
}
|
||||
|
||||
private boolean mkdirs(File dir) {
|
||||
if (dir.exists()) return false;
|
||||
|
||||
filterNonNull().mkdirs(dir);
|
||||
return dir.mkdirs();
|
||||
}
|
||||
|
||||
private File mkdirsE(File dir) throws IOException {
|
||||
if (dir.exists()) {
|
||||
return dir;
|
||||
}
|
||||
filterNonNull().mkdirs(dir);
|
||||
return IOUtils.mkdirs(dir);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import hudson.remoting.VirtualChannel;
|
|||
import hudson.util.StreamCopyThread;
|
||||
import hudson.util.ArgumentListBuilder;
|
||||
import hudson.util.ProcessTree;
|
||||
import jenkins.security.MasterToSlaveCallable;
|
||||
import org.apache.commons.io.input.NullInputStream;
|
||||
import org.kohsuke.accmod.Restricted;
|
||||
import org.kohsuke.accmod.restrictions.NoExternalUse;
|
||||
|
|
@ -940,7 +941,7 @@ public abstract class Launcher {
|
|||
getChannel().call(new KillTask(modelEnvVars));
|
||||
}
|
||||
|
||||
private static final class KillTask implements Callable<Void,RuntimeException> {
|
||||
private static final class KillTask extends MasterToSlaveCallable<Void,RuntimeException> {
|
||||
private final Map<String, String> modelEnvVars;
|
||||
|
||||
public KillTask(Map<String, String> modelEnvVars) {
|
||||
|
|
@ -1097,7 +1098,7 @@ public abstract class Launcher {
|
|||
IOTriplet getIOtriplet();
|
||||
}
|
||||
|
||||
private static class RemoteLaunchCallable implements Callable<RemoteProcess,IOException> {
|
||||
private static class RemoteLaunchCallable extends MasterToSlaveCallable<RemoteProcess,IOException> {
|
||||
private final List<String> cmd;
|
||||
private final boolean[] masks;
|
||||
private final String[] env;
|
||||
|
|
@ -1169,7 +1170,7 @@ public abstract class Launcher {
|
|||
private static final long serialVersionUID = 1L;
|
||||
}
|
||||
|
||||
private static class RemoteChannelLaunchCallable implements Callable<OutputStream,IOException> {
|
||||
private static class RemoteChannelLaunchCallable extends MasterToSlaveCallable<OutputStream,IOException> {
|
||||
private final String[] cmd;
|
||||
private final Pipe out;
|
||||
private final String workDir;
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import hudson.remoting.Channel;
|
|||
import hudson.remoting.ChannelProperty;
|
||||
import hudson.security.CliAuthenticator;
|
||||
import hudson.security.SecurityRealm;
|
||||
import jenkins.security.MasterToSlaveCallable;
|
||||
import org.acegisecurity.Authentication;
|
||||
import org.acegisecurity.BadCredentialsException;
|
||||
import org.acegisecurity.context.SecurityContext;
|
||||
|
|
@ -409,7 +410,7 @@ public abstract class CLICommand implements ExtensionPoint, Cloneable {
|
|||
return checkChannel().call(new GetSystemProperty(name));
|
||||
}
|
||||
|
||||
private static final class GetSystemProperty implements Callable<String, IOException> {
|
||||
private static final class GetSystemProperty extends MasterToSlaveCallable<String, IOException> {
|
||||
private final String name;
|
||||
|
||||
private GetSystemProperty(String name) {
|
||||
|
|
@ -438,7 +439,7 @@ public abstract class CLICommand implements ExtensionPoint, Cloneable {
|
|||
}
|
||||
}
|
||||
|
||||
private static final class GetCharset implements Callable<String, IOException> {
|
||||
private static final class GetCharset extends MasterToSlaveCallable<String, IOException> {
|
||||
public String call() throws IOException {
|
||||
return Charset.defaultCharset().name();
|
||||
}
|
||||
|
|
@ -453,7 +454,7 @@ public abstract class CLICommand implements ExtensionPoint, Cloneable {
|
|||
return checkChannel().call(new GetEnvironmentVariable(name));
|
||||
}
|
||||
|
||||
private static final class GetEnvironmentVariable implements Callable<String, IOException> {
|
||||
private static final class GetEnvironmentVariable extends MasterToSlaveCallable<String, IOException> {
|
||||
private final String name;
|
||||
|
||||
private GetEnvironmentVariable(String name) {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
package hudson.cli;
|
||||
|
||||
import hudson.FilePath;
|
||||
import hudson.remoting.Callable;
|
||||
import hudson.remoting.Channel;
|
||||
import hudson.util.Secret;
|
||||
import jenkins.model.Jenkins;
|
||||
import jenkins.security.MasterToSlaveCallable;
|
||||
import org.acegisecurity.Authentication;
|
||||
import org.acegisecurity.AuthenticationException;
|
||||
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
|
||||
|
|
@ -39,7 +39,7 @@ public class ClientAuthenticationCache implements Serializable {
|
|||
private final Properties props = new Properties();
|
||||
|
||||
public ClientAuthenticationCache(Channel channel) throws IOException, InterruptedException {
|
||||
store = (channel==null ? FilePath.localChannel : channel).call(new Callable<FilePath, IOException>() {
|
||||
store = (channel==null ? FilePath.localChannel : channel).call(new MasterToSlaveCallable<FilePath, IOException>() {
|
||||
public FilePath call() throws IOException {
|
||||
File home = new File(System.getProperty("user.home"));
|
||||
File hudsonHome = new File(home, ".hudson");
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import jenkins.model.Jenkins;
|
|||
import hudson.model.Job;
|
||||
import hudson.model.Run;
|
||||
import hudson.remoting.Callable;
|
||||
import jenkins.security.MasterToSlaveCallable;
|
||||
import org.kohsuke.args4j.CmdLineException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
|
@ -84,7 +85,7 @@ public abstract class CommandDuringBuild extends CLICommand {
|
|||
/**
|
||||
* Gets the environment variables that points to the build being executed.
|
||||
*/
|
||||
private static final class GetCharacteristicEnvironmentVariables implements Callable<String[],IOException> {
|
||||
private static final class GetCharacteristicEnvironmentVariables extends MasterToSlaveCallable<String[],IOException> {
|
||||
public String[] call() throws IOException {
|
||||
return new String[] {
|
||||
System.getenv("JOB_NAME"),
|
||||
|
|
|
|||
|
|
@ -31,10 +31,7 @@ import hudson.model.AbstractProject;
|
|||
import hudson.model.Run;
|
||||
import hudson.model.Executor;
|
||||
import hudson.model.Node;
|
||||
import hudson.model.EnvironmentSpecific;
|
||||
import hudson.model.Item;
|
||||
import hudson.remoting.Callable;
|
||||
import hudson.slaves.NodeSpecific;
|
||||
import hudson.util.EditDistance;
|
||||
import hudson.util.StreamTaskListener;
|
||||
import hudson.tools.ToolDescriptor;
|
||||
|
|
@ -44,6 +41,7 @@ import java.util.List;
|
|||
import java.util.ArrayList;
|
||||
import java.io.IOException;
|
||||
|
||||
import jenkins.security.MasterToSlaveCallable;
|
||||
import org.kohsuke.args4j.Argument;
|
||||
|
||||
/**
|
||||
|
|
@ -131,7 +129,7 @@ public class InstallToolCommand extends CLICommand {
|
|||
return 0;
|
||||
}
|
||||
|
||||
private static final class BuildIDs implements Callable<BuildIDs, IOException> {
|
||||
private static final class BuildIDs extends MasterToSlaveCallable<BuildIDs, IOException> {
|
||||
String job,number,id;
|
||||
|
||||
public BuildIDs call() throws IOException {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
package hudson.cli.util;
|
||||
|
||||
import hudson.AbortException;
|
||||
import hudson.remoting.Callable;
|
||||
import jenkins.security.MasterToSlaveCallable;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
|
@ -9,15 +11,12 @@ import java.io.InputStream;
|
|||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author vjuranek
|
||||
* Reads a file (either a path or URL) over a channel.
|
||||
*
|
||||
* @author vjuranek
|
||||
*/
|
||||
public class ScriptLoader implements Callable<String,IOException> {
|
||||
public class ScriptLoader extends MasterToSlaveCallable<String,IOException> {
|
||||
|
||||
private final String script;
|
||||
|
||||
|
|
@ -43,5 +42,4 @@ public class ScriptLoader implements Callable<String,IOException> {
|
|||
s.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ import hudson.slaves.ComputerListener;
|
|||
import hudson.util.CopyOnWriteList;
|
||||
import hudson.util.RingBufferLogHandler;
|
||||
import hudson.util.XStream2;
|
||||
import jenkins.security.MasterToSlaveCallable;
|
||||
import net.sf.json.JSONObject;
|
||||
import org.kohsuke.stapler.*;
|
||||
import org.kohsuke.stapler.interceptor.RequirePOST;
|
||||
|
|
@ -207,7 +208,7 @@ public class LogRecorder extends AbstractModelObject implements Saveable {
|
|||
|
||||
}
|
||||
|
||||
private static final class SetLevel implements Callable<Void,Error> {
|
||||
private static final class SetLevel extends MasterToSlaveCallable<Void,Error> {
|
||||
/** known loggers (kept per slave), to avoid GC */
|
||||
@SuppressWarnings("MismatchedQueryAndUpdateOfCollection") private static final Set<Logger> loggers = new HashSet<Logger>();
|
||||
private final String name;
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ import hudson.cli.declarative.CLIMethod;
|
|||
import hudson.cli.declarative.CLIResolver;
|
||||
import hudson.model.listeners.ItemListener;
|
||||
import hudson.model.listeners.SaveableListener;
|
||||
import hudson.remoting.Callable;
|
||||
import hudson.security.AccessControlled;
|
||||
import hudson.security.Permission;
|
||||
import hudson.security.ACL;
|
||||
|
|
@ -43,6 +42,7 @@ import hudson.util.AlternativeUiTextProvider.Message;
|
|||
import hudson.util.AtomicFileWriter;
|
||||
import hudson.util.IOUtils;
|
||||
import jenkins.model.Jenkins;
|
||||
import jenkins.security.NotReallyRoleSensitiveCallable;
|
||||
import org.acegisecurity.Authentication;
|
||||
import org.apache.tools.ant.taskdefs.Copy;
|
||||
import org.apache.tools.ant.types.FileSet;
|
||||
|
|
@ -226,7 +226,7 @@ public abstract class AbstractItem extends Actionable implements Item, HttpDelet
|
|||
|
||||
// the test to see if the project already exists or not needs to be done in escalated privilege
|
||||
// to avoid overwriting
|
||||
ACL.impersonate(ACL.SYSTEM,new Callable<Void,IOException>() {
|
||||
ACL.impersonate(ACL.SYSTEM,new NotReallyRoleSensitiveCallable<Void,IOException>() {
|
||||
final Authentication user = Jenkins.getAuthentication();
|
||||
@Override
|
||||
public Void call() throws IOException {
|
||||
|
|
@ -657,7 +657,7 @@ public abstract class AbstractItem extends Actionable implements Item, HttpDelet
|
|||
throw new IOException("Expecting "+this.getClass()+" but got "+o.getClass()+" instead");
|
||||
}
|
||||
|
||||
Items.whileUpdatingByXml(new Callable<Void,IOException>() {
|
||||
Items.whileUpdatingByXml(new NotReallyRoleSensitiveCallable<Void,IOException>() {
|
||||
@Override public Void call() throws IOException {
|
||||
onLoad(getParent(), getRootDir().getName());
|
||||
return null;
|
||||
|
|
@ -689,7 +689,7 @@ public abstract class AbstractItem extends Actionable implements Item, HttpDelet
|
|||
|
||||
// try to reflect the changes by reloading
|
||||
getConfigFile().unmarshal(this);
|
||||
Items.whileUpdatingByXml(new Callable<Void, IOException>() {
|
||||
Items.whileUpdatingByXml(new NotReallyRoleSensitiveCallable<Void, IOException>() {
|
||||
@Override
|
||||
public Void call() throws IOException {
|
||||
onLoad(getParent(), getRootDir().getName());
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@ import hudson.model.queue.WorkUnit;
|
|||
import hudson.node_monitors.NodeMonitor;
|
||||
import hudson.remoting.Channel;
|
||||
import hudson.remoting.VirtualChannel;
|
||||
import hudson.remoting.Callable;
|
||||
import hudson.security.ACL;
|
||||
import hudson.security.AccessControlled;
|
||||
import hudson.security.Permission;
|
||||
|
|
@ -61,6 +60,7 @@ import hudson.util.Futures;
|
|||
import hudson.util.NamingThreadFactory;
|
||||
import jenkins.model.Jenkins;
|
||||
import jenkins.util.ContextResettingExecutorService;
|
||||
import jenkins.security.MasterToSlaveCallable;
|
||||
import org.kohsuke.args4j.Argument;
|
||||
import org.kohsuke.args4j.CmdLineException;
|
||||
import org.kohsuke.stapler.StaplerRequest;
|
||||
|
|
@ -1042,7 +1042,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
|
|||
oneOffExecutors.remove(e);
|
||||
}
|
||||
|
||||
private static class ListPossibleNames implements Callable<List<String>,IOException> {
|
||||
private static class ListPossibleNames extends MasterToSlaveCallable<List<String>,IOException> {
|
||||
public List<String> call() throws IOException {
|
||||
List<String> names = new ArrayList<String>();
|
||||
|
||||
|
|
@ -1072,7 +1072,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
|
|||
private static final long serialVersionUID = 1L;
|
||||
}
|
||||
|
||||
private static class GetFallbackName implements Callable<String,IOException> {
|
||||
private static class GetFallbackName extends MasterToSlaveCallable<String,IOException> {
|
||||
public String call() throws IOException {
|
||||
return System.getProperty("host.name");
|
||||
}
|
||||
|
|
@ -1150,7 +1150,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
|
|||
w.close();
|
||||
}
|
||||
|
||||
private static final class DumpExportTableTask implements Callable<String,IOException> {
|
||||
private static final class DumpExportTableTask extends MasterToSlaveCallable<String,IOException> {
|
||||
public String call() throws IOException {
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw);
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ package hudson.model;
|
|||
|
||||
import hudson.FilePath;
|
||||
import hudson.Util;
|
||||
import hudson.remoting.Callable;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
|
@ -43,6 +42,7 @@ import java.util.logging.Logger;
|
|||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import jenkins.model.Jenkins;
|
||||
import jenkins.security.MasterToSlaveCallable;
|
||||
import jenkins.util.VirtualFile;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.tools.zip.ZipEntry;
|
||||
|
|
@ -461,7 +461,7 @@ public final class DirectoryBrowserSupport implements HttpResponse {
|
|||
}
|
||||
}
|
||||
|
||||
private static final class BuildChildPaths implements Callable<List<List<Path>>,IOException> {
|
||||
private static final class BuildChildPaths extends MasterToSlaveCallable<List<List<Path>>,IOException> {
|
||||
private final VirtualFile cur;
|
||||
private final Locale locale;
|
||||
BuildChildPaths(VirtualFile cur, Locale locale) {
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ package hudson.model;
|
|||
import hudson.Util;
|
||||
import hudson.XmlFile;
|
||||
import hudson.model.listeners.ItemListener;
|
||||
import hudson.remoting.Callable;
|
||||
import hudson.security.AccessControlled;
|
||||
import hudson.util.CopyOnWriteMap;
|
||||
import hudson.util.Function1;
|
||||
|
|
@ -44,6 +43,7 @@ import java.io.InputStream;
|
|||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import jenkins.security.NotReallyRoleSensitiveCallable;
|
||||
|
||||
/**
|
||||
* Defines a bunch of static methods to be used as a "mix-in" for {@link ItemGroup}
|
||||
|
|
@ -222,7 +222,7 @@ public abstract class ItemGroupMixIn {
|
|||
|
||||
// reload from the new config
|
||||
final File rootDir = result.getRootDir();
|
||||
result = Items.whileUpdatingByXml(new Callable<T,IOException>() {
|
||||
result = Items.whileUpdatingByXml(new NotReallyRoleSensitiveCallable<T,IOException>() {
|
||||
@Override public T call() throws IOException {
|
||||
return (T) Items.load(parent, rootDir);
|
||||
}
|
||||
|
|
@ -253,7 +253,7 @@ public abstract class ItemGroupMixIn {
|
|||
IOUtils.copy(xml,configXml);
|
||||
|
||||
// load it
|
||||
TopLevelItem result = Items.whileUpdatingByXml(new Callable<TopLevelItem,IOException>() {
|
||||
TopLevelItem result = Items.whileUpdatingByXml(new NotReallyRoleSensitiveCallable<TopLevelItem,IOException>() {
|
||||
@Override public TopLevelItem call() throws IOException {
|
||||
return (TopLevelItem) Items.load(parent, dir);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ import javax.servlet.ServletException;
|
|||
import javax.annotation.CheckForNull;
|
||||
import javax.annotation.Nonnull;
|
||||
import jenkins.model.Jenkins;
|
||||
import jenkins.security.MasterToSlaveCallable;
|
||||
import jenkins.slaves.WorkspaceLocator;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
|
@ -455,7 +456,7 @@ public abstract class Slave extends Node implements Serializable {
|
|||
* <li>When it's read on this side as a return value, it morphs itself into {@link ClockDifference}.
|
||||
* </ol>
|
||||
*/
|
||||
private static final class GetClockDifference1 implements Callable<ClockDifference,IOException> {
|
||||
private static final class GetClockDifference1 extends MasterToSlaveCallable<ClockDifference,IOException> {
|
||||
public ClockDifference call() {
|
||||
// this method must be being invoked locally, which means the clock is in sync
|
||||
return new ClockDifference(0);
|
||||
|
|
@ -468,7 +469,7 @@ public abstract class Slave extends Node implements Serializable {
|
|||
private static final long serialVersionUID = 1L;
|
||||
}
|
||||
|
||||
private static final class GetClockDifference2 implements Callable<GetClockDifference3,IOException> {
|
||||
private static final class GetClockDifference2 extends MasterToSlaveCallable<GetClockDifference3,IOException> {
|
||||
/**
|
||||
* Capture the time on the master when this object is sent to remote, which is when
|
||||
* {@link GetClockDifference1#writeReplace()} is run.
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ package hudson.node_monitors;
|
|||
import hudson.model.Computer;
|
||||
import hudson.remoting.Callable;
|
||||
import hudson.Extension;
|
||||
import jenkins.security.MasterToSlaveCallable;
|
||||
import net.sf.json.JSONObject;
|
||||
import org.kohsuke.stapler.StaplerRequest;
|
||||
|
||||
|
|
@ -56,7 +57,7 @@ public class ArchitectureMonitor extends NodeMonitor {
|
|||
/**
|
||||
* Obtains the string that represents the architecture.
|
||||
*/
|
||||
private static class GetArchTask implements Callable<String,IOException> {
|
||||
private static class GetArchTask extends MasterToSlaveCallable<String,IOException> {
|
||||
public String call() {
|
||||
String os = System.getProperty("os.name");
|
||||
String arch = System.getProperty("os.arch");
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@
|
|||
*/
|
||||
package hudson.node_monitors;
|
||||
|
||||
import hudson.FilePath.FileCallable;
|
||||
import hudson.Functions;
|
||||
import jenkins.MasterToSlaveFileCallable;
|
||||
import hudson.remoting.VirtualChannel;
|
||||
import hudson.Util;
|
||||
import hudson.slaves.OfflineCause;
|
||||
|
|
@ -154,7 +154,7 @@ public abstract class DiskSpaceMonitorDescriptor extends AbstractAsyncNodeMonito
|
|||
private static final long serialVersionUID = 2L;
|
||||
}
|
||||
|
||||
protected static final class GetUsableSpace implements FileCallable<DiskSpace> {
|
||||
protected static final class GetUsableSpace extends MasterToSlaveFileCallable<DiskSpace> {
|
||||
public GetUsableSpace() {}
|
||||
public DiskSpace invoke(File f, VirtualChannel channel) throws IOException {
|
||||
long s = f.getUsableSpace();
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import hudson.Extension;
|
|||
import hudson.slaves.OfflineCause;
|
||||
import hudson.model.Computer;
|
||||
import hudson.remoting.Callable;
|
||||
import jenkins.security.MasterToSlaveCallable;
|
||||
import net.sf.json.JSONObject;
|
||||
import org.kohsuke.stapler.StaplerRequest;
|
||||
|
||||
|
|
@ -85,7 +86,7 @@ public class ResponseTimeMonitor extends NodeMonitor {
|
|||
}
|
||||
};
|
||||
|
||||
private static final class Step1 implements Callable<Data,IOException> {
|
||||
private static final class Step1 extends MasterToSlaveCallable<Data,IOException> {
|
||||
private Data cur;
|
||||
|
||||
private Step1(Data cur) {
|
||||
|
|
@ -104,7 +105,7 @@ public class ResponseTimeMonitor extends NodeMonitor {
|
|||
private static final long serialVersionUID = 1L;
|
||||
}
|
||||
|
||||
private static final class Step2 implements Callable<Step3,IOException> {
|
||||
private static final class Step2 extends MasterToSlaveCallable<Step3,IOException> {
|
||||
private final Data cur;
|
||||
private final long start = System.currentTimeMillis();
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ import hudson.Extension;
|
|||
import hudson.Functions;
|
||||
import hudson.model.Computer;
|
||||
import jenkins.model.Jenkins;
|
||||
import hudson.remoting.Callable;
|
||||
import jenkins.security.MasterToSlaveCallable;
|
||||
import net.sf.json.JSONObject;
|
||||
import org.jvnet.hudson.MemoryMonitor;
|
||||
import org.jvnet.hudson.MemoryUsage;
|
||||
|
|
@ -100,7 +100,7 @@ public class SwapSpaceMonitor extends NodeMonitor {
|
|||
/**
|
||||
* Obtains the string that represents the architecture.
|
||||
*/
|
||||
private static class MonitorTask implements Callable<MemoryUsage,IOException> {
|
||||
private static class MonitorTask extends MasterToSlaveCallable<MemoryUsage,IOException> {
|
||||
public MemoryUsage call() throws IOException {
|
||||
MemoryMonitor mm;
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ package hudson.node_monitors;
|
|||
|
||||
import hudson.Extension;
|
||||
import hudson.FilePath;
|
||||
import hudson.FilePath.FileCallable;
|
||||
import jenkins.MasterToSlaveFileCallable;
|
||||
import hudson.model.Computer;
|
||||
import hudson.model.Node;
|
||||
import hudson.remoting.Callable;
|
||||
|
|
@ -83,7 +83,7 @@ public class TemporarySpaceMonitor extends AbstractDiskSpaceMonitor {
|
|||
return DESCRIPTOR;
|
||||
}
|
||||
|
||||
protected static final class GetTempSpace implements FileCallable<DiskSpace> {
|
||||
protected static final class GetTempSpace extends MasterToSlaveFileCallable<DiskSpace> {
|
||||
public DiskSpace invoke(File f, VirtualChannel channel) throws IOException {
|
||||
// if the disk is really filled up we can't even create a single file,
|
||||
// so calling File.createTempFile and figuring out the directory won't reliably work.
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import hudson.util.HudsonIsRestarting;
|
|||
import hudson.util.StreamTaskListener;
|
||||
import static hudson.util.jna.GNUCLibrary.*;
|
||||
|
||||
import jenkins.security.MasterToSlaveCallable;
|
||||
import org.apache.commons.io.output.ByteArrayOutputStream;
|
||||
import org.jvnet.libpam.impl.CLibrary.passwd;
|
||||
import org.jvnet.solaris.libzfs.ACLBuilder;
|
||||
|
|
@ -167,7 +168,7 @@ public class ZFSInstaller extends AdministrativeMonitor implements Serializable
|
|||
|
||||
// this is the actual creation of the file system.
|
||||
// return true indicating a success
|
||||
return SU.execute(listener, rootUsername, rootPassword, new Callable<String,IOException>() {
|
||||
return SU.execute(listener, rootUsername, rootPassword, new MasterToSlaveCallable<String,IOException>() {
|
||||
private static final long serialVersionUID = 7731167233498214301L;
|
||||
|
||||
public String call() throws IOException {
|
||||
|
|
|
|||
|
|
@ -23,13 +23,13 @@
|
|||
*/
|
||||
package hudson.os.solaris;
|
||||
|
||||
import jenkins.MasterToSlaveFileCallable;
|
||||
import hudson.FileSystemProvisioner;
|
||||
import hudson.FilePath;
|
||||
import hudson.WorkspaceSnapshot;
|
||||
import hudson.FileSystemProvisionerDescriptor;
|
||||
import hudson.Extension;
|
||||
import hudson.remoting.VirtualChannel;
|
||||
import hudson.FilePath.FileCallable;
|
||||
import hudson.model.AbstractBuild;
|
||||
import hudson.model.TaskListener;
|
||||
import hudson.model.AbstractProject;
|
||||
|
|
@ -52,7 +52,7 @@ public class ZFSProvisioner extends FileSystemProvisioner implements Serializabl
|
|||
private final String rootDataset;
|
||||
|
||||
public ZFSProvisioner(Node node) throws IOException, InterruptedException {
|
||||
rootDataset = node.getRootPath().act(new FileCallable<String>() {
|
||||
rootDataset = node.getRootPath().act(new MasterToSlaveFileCallable<String>() {
|
||||
private static final long serialVersionUID = -2142349338699797436L;
|
||||
|
||||
public String invoke(File f, VirtualChannel channel) throws IOException {
|
||||
|
|
@ -68,7 +68,7 @@ public class ZFSProvisioner extends FileSystemProvisioner implements Serializabl
|
|||
public void prepareWorkspace(AbstractBuild<?,?> build, FilePath ws, final TaskListener listener) throws IOException, InterruptedException {
|
||||
final String name = build.getProject().getFullName();
|
||||
|
||||
ws.act(new FileCallable<Void>() {
|
||||
ws.act(new MasterToSlaveFileCallable<Void>() {
|
||||
private static final long serialVersionUID = 2129531727963121198L;
|
||||
|
||||
public Void invoke(File f, VirtualChannel channel) throws IOException {
|
||||
|
|
@ -87,7 +87,7 @@ public class ZFSProvisioner extends FileSystemProvisioner implements Serializabl
|
|||
}
|
||||
|
||||
public void discardWorkspace(AbstractProject<?, ?> project, FilePath ws) throws IOException, InterruptedException {
|
||||
ws.act(new FileCallable<Void>() {
|
||||
ws.act(new MasterToSlaveFileCallable<Void>() {
|
||||
private static final long serialVersionUID = 1916618107019257530L;
|
||||
|
||||
public Void invoke(File f, VirtualChannel channel) throws IOException {
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@ package hudson.security;
|
|||
import groovy.lang.Binding;
|
||||
import hudson.FilePath;
|
||||
import hudson.cli.CLICommand;
|
||||
import hudson.remoting.Callable;
|
||||
import hudson.util.spring.BeanBuilder;
|
||||
import java.io.Console;
|
||||
import java.io.IOException;
|
||||
import jenkins.model.Jenkins;
|
||||
import jenkins.security.ImpersonatingUserDetailsService;
|
||||
import jenkins.security.SecurityListener;
|
||||
import jenkins.security.MasterToSlaveCallable;
|
||||
import org.acegisecurity.Authentication;
|
||||
import org.acegisecurity.AuthenticationException;
|
||||
import org.acegisecurity.AuthenticationManager;
|
||||
|
|
@ -152,7 +152,7 @@ public abstract class AbstractPasswordBasedSecurityRealm extends SecurityRealm i
|
|||
/**
|
||||
* Asks for the password.
|
||||
*/
|
||||
private static class InteractivelyAskForPassword implements Callable<String,IOException> {
|
||||
private static class InteractivelyAskForPassword extends MasterToSlaveCallable<String,IOException> {
|
||||
public String call() throws IOException {
|
||||
Console console = System.console();
|
||||
if (console == null) return null; // no terminal
|
||||
|
|
|
|||
|
|
@ -28,9 +28,9 @@ import hudson.FilePath;
|
|||
import hudson.model.Computer;
|
||||
import hudson.model.Slave;
|
||||
import hudson.model.TaskListener;
|
||||
import hudson.remoting.Callable;
|
||||
import hudson.remoting.Channel;
|
||||
import hudson.remoting.PingThread;
|
||||
import jenkins.security.MasterToSlaveCallable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
|
@ -89,7 +89,7 @@ public class ChannelPinger extends ComputerListener {
|
|||
setUpPingForChannel(channel, pingInterval);
|
||||
}
|
||||
|
||||
private static class SetUpRemotePing implements Callable<Void, IOException> {
|
||||
private static class SetUpRemotePing extends MasterToSlaveCallable<Void, IOException> {
|
||||
private static final long serialVersionUID = -2702219700841759872L;
|
||||
private int pingInterval;
|
||||
public SetUpRemotePing(int pingInterval) {
|
||||
|
|
|
|||
|
|
@ -29,14 +29,18 @@ import hudson.FilePath;
|
|||
import hudson.model.Computer;
|
||||
import hudson.model.TaskListener;
|
||||
import hudson.remoting.Channel;
|
||||
import hudson.remoting.ChannelBuilder;
|
||||
import hudson.remoting.CommandTransport;
|
||||
import hudson.remoting.Launcher;
|
||||
import hudson.remoting.SocketChannelStream;
|
||||
import hudson.util.ClasspathBuilder;
|
||||
import hudson.util.JVMBuilder;
|
||||
import hudson.util.StreamCopyThread;
|
||||
import jenkins.security.ChannelConfigurator;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.IOError;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
|
@ -67,66 +71,84 @@ public class Channels {
|
|||
* we kill the process.
|
||||
*/
|
||||
public static Channel forProcess(String name, ExecutorService execService, InputStream in, OutputStream out, OutputStream header, final Proc proc) throws IOException {
|
||||
return new Channel(name, execService, in, out, header) {
|
||||
/**
|
||||
* Kill the process when the channel is severed.
|
||||
*/
|
||||
ChannelBuilder cb = new ChannelBuilder(name,execService) {
|
||||
@Override
|
||||
public synchronized void terminate(IOException e) {
|
||||
super.terminate(e);
|
||||
try {
|
||||
proc.kill();
|
||||
} catch (IOException x) {
|
||||
// we are already in the error recovery mode, so just record it and move on
|
||||
LOGGER.log(Level.INFO, "Failed to terminate the severed connection",x);
|
||||
} catch (InterruptedException x) {
|
||||
// process the interrupt later
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
public Channel build(CommandTransport transport) throws IOException {
|
||||
return new Channel(this,transport) {
|
||||
/**
|
||||
* Kill the process when the channel is severed.
|
||||
*/
|
||||
@Override
|
||||
public synchronized void terminate(IOException e) {
|
||||
super.terminate(e);
|
||||
try {
|
||||
proc.kill();
|
||||
} catch (IOException x) {
|
||||
// we are already in the error recovery mode, so just record it and move on
|
||||
LOGGER.log(Level.INFO, "Failed to terminate the severed connection",x);
|
||||
} catch (InterruptedException x) {
|
||||
// process the interrupt later
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() throws IOException {
|
||||
super.close();
|
||||
// wait for the child process to complete
|
||||
try {
|
||||
proc.join();
|
||||
} catch (InterruptedException e) {
|
||||
// process the interrupt later
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
@Override
|
||||
public synchronized void join() throws InterruptedException {
|
||||
super.join();
|
||||
// wait for the child process to complete, too
|
||||
try {
|
||||
proc.join();
|
||||
} catch (IOException e) {
|
||||
throw new IOError(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
cb.withHeaderStream(header);
|
||||
|
||||
for (ChannelConfigurator cc : ChannelConfigurator.all()) {
|
||||
cc.onChannelBuilding(cb,null); // TODO: what to pass as a context?
|
||||
}
|
||||
|
||||
return cb.build(in,out);
|
||||
}
|
||||
|
||||
public static Channel forProcess(String name, ExecutorService execService, final Process proc, OutputStream header) throws IOException {
|
||||
final Thread thread = new StreamCopyThread(name + " stderr", proc.getErrorStream(), header);
|
||||
thread.start();
|
||||
|
||||
return new Channel(name, execService, proc.getInputStream(), proc.getOutputStream(), header) {
|
||||
/**
|
||||
* Kill the process when the channel is severed.
|
||||
*/
|
||||
ChannelBuilder cb = new ChannelBuilder(name,execService) {
|
||||
@Override
|
||||
public synchronized void terminate(IOException e) {
|
||||
super.terminate(e);
|
||||
proc.destroy();
|
||||
// the stderr copier should exit by itself
|
||||
}
|
||||
public Channel build(CommandTransport transport) throws IOException {
|
||||
return new Channel(this,transport) {
|
||||
/**
|
||||
* Kill the process when the channel is severed.
|
||||
*/
|
||||
@Override
|
||||
public synchronized void terminate(IOException e) {
|
||||
super.terminate(e);
|
||||
proc.destroy();
|
||||
// the stderr copier should exit by itself
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() throws IOException {
|
||||
super.close();
|
||||
// wait for Maven to complete
|
||||
try {
|
||||
proc.waitFor();
|
||||
thread.join();
|
||||
} catch (InterruptedException e) {
|
||||
// process the interrupt later
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
@Override
|
||||
public synchronized void join() throws InterruptedException {
|
||||
super.join();
|
||||
// wait for the child process to complete, too
|
||||
proc.waitFor();
|
||||
thread.join();
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
cb.withHeaderStream(header);
|
||||
|
||||
for (ChannelConfigurator cc : ChannelConfigurator.all()) {
|
||||
cc.onChannelBuilding(cb,null); // TODO: what to pass as a context?
|
||||
}
|
||||
|
||||
return cb.build(proc.getInputStream(),proc.getOutputStream());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -23,16 +23,18 @@
|
|||
*/
|
||||
package hudson.slaves;
|
||||
|
||||
import hudson.model.Computer;
|
||||
import jenkins.model.Jenkins;
|
||||
import hudson.model.TaskListener;
|
||||
import hudson.model.Node;
|
||||
import hudson.ExtensionPoint;
|
||||
import hudson.AbortException;
|
||||
import hudson.Extension;
|
||||
import hudson.ExtensionList;
|
||||
import hudson.ExtensionPoint;
|
||||
import hudson.FilePath;
|
||||
import hudson.model.Computer;
|
||||
import hudson.model.Node;
|
||||
import hudson.model.TaskListener;
|
||||
import org.jenkinsci.remoting.CallableDecorator;
|
||||
import hudson.remoting.Channel;
|
||||
import hudson.AbortException;
|
||||
import hudson.remoting.ChannelBuilder;
|
||||
import jenkins.model.Jenkins;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
|
@ -46,6 +48,7 @@ import javax.annotation.Nonnull;
|
|||
* @since 1.246
|
||||
*/
|
||||
public abstract class ComputerListener implements ExtensionPoint {
|
||||
|
||||
/**
|
||||
* Called before a {@link ComputerLauncher} is asked to launch a connection with {@link Computer}.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -30,8 +30,8 @@ import hudson.model.Computer;
|
|||
import hudson.util.TimeUnit2;
|
||||
import hudson.remoting.VirtualChannel;
|
||||
import hudson.remoting.Channel;
|
||||
import hudson.remoting.Callable;
|
||||
import hudson.Extension;
|
||||
import jenkins.security.SlaveToMasterCallable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Logger;
|
||||
|
|
@ -102,7 +102,7 @@ public class ConnectionActivityMonitor extends AsyncPeriodicWork {
|
|||
public boolean enabled = Boolean.getBoolean(ConnectionActivityMonitor.class.getName()+".enabled");
|
||||
|
||||
private static final PingCommand PING_COMMAND = new PingCommand();
|
||||
private static final class PingCommand implements Callable<Void,RuntimeException> {
|
||||
private static final class PingCommand extends SlaveToMasterCallable<Void,RuntimeException> {
|
||||
public Void call() throws RuntimeException {
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@
|
|||
package hudson.slaves;
|
||||
|
||||
import hudson.AbortException;
|
||||
import hudson.remoting.ChannelBuilder;
|
||||
import hudson.util.IOUtils;
|
||||
import hudson.FilePath;
|
||||
import hudson.Util;
|
||||
import hudson.model.Computer;
|
||||
|
|
@ -34,7 +36,6 @@ import hudson.model.Queue;
|
|||
import hudson.model.Slave;
|
||||
import hudson.model.TaskListener;
|
||||
import hudson.model.User;
|
||||
import hudson.remoting.Callable;
|
||||
import hudson.remoting.Channel;
|
||||
import hudson.remoting.Launcher;
|
||||
import hudson.remoting.VirtualChannel;
|
||||
|
|
@ -46,18 +47,10 @@ import hudson.util.RingBufferLogHandler;
|
|||
import hudson.util.StreamTaskListener;
|
||||
import hudson.util.io.ReopenableFileOutputStream;
|
||||
import hudson.util.io.ReopenableRotatingFileOutputStream;
|
||||
import jenkins.model.Jenkins;
|
||||
import jenkins.slaves.EncryptedSlaveAgentJnlpFile;
|
||||
import jenkins.slaves.JnlpSlaveAgentProtocol;
|
||||
import jenkins.slaves.systemInfo.SlaveSystemInfo;
|
||||
import org.acegisecurity.context.SecurityContext;
|
||||
import org.acegisecurity.context.SecurityContextHolder;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.kohsuke.stapler.HttpRedirect;
|
||||
import org.kohsuke.stapler.HttpResponse;
|
||||
import org.kohsuke.stapler.QueryParameter;
|
||||
import org.kohsuke.stapler.StaplerRequest;
|
||||
import org.kohsuke.stapler.StaplerResponse;
|
||||
import org.kohsuke.stapler.WebMethod;
|
||||
import org.kohsuke.stapler.interceptor.RequirePOST;
|
||||
|
||||
|
|
@ -81,6 +74,14 @@ import java.util.logging.Logger;
|
|||
import javax.annotation.CheckForNull;
|
||||
import jenkins.model.Jenkins;
|
||||
import static hudson.slaves.SlaveComputer.LogHolder.*;
|
||||
import jenkins.security.ChannelConfigurator;
|
||||
import jenkins.security.MasterToSlaveCallable;
|
||||
import jenkins.slaves.JnlpSlaveAgentProtocol;
|
||||
import org.kohsuke.stapler.StaplerRequest;
|
||||
import org.kohsuke.stapler.StaplerResponse;
|
||||
import org.kohsuke.stapler.QueryParameter;
|
||||
import org.kohsuke.stapler.HttpResponse;
|
||||
import org.kohsuke.stapler.HttpRedirect;
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -345,7 +346,15 @@ public class SlaveComputer extends Computer {
|
|||
* so the implementation of the listener doesn't need to do that again.
|
||||
*/
|
||||
public void setChannel(InputStream in, OutputStream out, OutputStream launchLog, Channel.Listener listener) throws IOException, InterruptedException {
|
||||
Channel channel = new Channel(nodeName,threadPoolForRemoting, Channel.Mode.NEGOTIATE, in,out, launchLog);
|
||||
ChannelBuilder cb = new ChannelBuilder(nodeName,threadPoolForRemoting)
|
||||
.withMode(Channel.Mode.NEGOTIATE)
|
||||
.withHeaderStream(launchLog);
|
||||
|
||||
for (ChannelConfigurator cc : ChannelConfigurator.all()) {
|
||||
cc.onChannelBuilding(cb,this);
|
||||
}
|
||||
|
||||
Channel channel = cb.build(in,out);
|
||||
setChannel(channel,launchLog,listener);
|
||||
}
|
||||
|
||||
|
|
@ -393,7 +402,7 @@ public class SlaveComputer extends Computer {
|
|||
return channel.call(new LoadingTime(true));
|
||||
}
|
||||
|
||||
static class LoadingCount implements Callable<Integer,RuntimeException> {
|
||||
static class LoadingCount extends MasterToSlaveCallable<Integer,RuntimeException> {
|
||||
private final boolean resource;
|
||||
LoadingCount(boolean resource) {
|
||||
this.resource = resource;
|
||||
|
|
@ -404,13 +413,13 @@ public class SlaveComputer extends Computer {
|
|||
}
|
||||
}
|
||||
|
||||
static class LoadingPrefetchCacheCount implements Callable<Integer,RuntimeException> {
|
||||
static class LoadingPrefetchCacheCount extends MasterToSlaveCallable<Integer,RuntimeException> {
|
||||
@Override public Integer call() {
|
||||
return Channel.current().classLoadingPrefetchCacheCount.get();
|
||||
}
|
||||
}
|
||||
|
||||
static class LoadingTime implements Callable<Long,RuntimeException> {
|
||||
static class LoadingTime extends MasterToSlaveCallable<Long,RuntimeException> {
|
||||
private final boolean resource;
|
||||
LoadingTime(boolean resource) {
|
||||
this.resource = resource;
|
||||
|
|
@ -677,19 +686,19 @@ public class SlaveComputer extends Computer {
|
|||
|
||||
private static final Logger logger = Logger.getLogger(SlaveComputer.class.getName());
|
||||
|
||||
private static final class SlaveVersion implements Callable<String,IOException> {
|
||||
private static final class SlaveVersion extends MasterToSlaveCallable<String,IOException> {
|
||||
public String call() throws IOException {
|
||||
try { return Launcher.VERSION; }
|
||||
catch (Throwable ex) { return "< 1.335"; } // Older slave.jar won't have VERSION
|
||||
}
|
||||
}
|
||||
private static final class DetectOS implements Callable<Boolean,IOException> {
|
||||
private static final class DetectOS extends MasterToSlaveCallable<Boolean,IOException> {
|
||||
public Boolean call() throws IOException {
|
||||
return File.pathSeparatorChar==':';
|
||||
}
|
||||
}
|
||||
|
||||
private static final class DetectDefaultCharset implements Callable<String,IOException> {
|
||||
private static final class DetectDefaultCharset extends MasterToSlaveCallable<String,IOException> {
|
||||
public String call() throws IOException {
|
||||
return Charset.defaultCharset().name();
|
||||
}
|
||||
|
|
@ -706,7 +715,7 @@ public class SlaveComputer extends Computer {
|
|||
static final RingBufferLogHandler SLAVE_LOG_HANDLER = new RingBufferLogHandler();
|
||||
}
|
||||
|
||||
private static class SlaveInitializer implements Callable<Void,RuntimeException> {
|
||||
private static class SlaveInitializer extends MasterToSlaveCallable<Void,RuntimeException> {
|
||||
public Void call() {
|
||||
// avoid double installation of the handler. JNLP slaves can reconnect to the master multiple times
|
||||
// and each connection gets a different RemoteClassLoader, so we need to evict them by class name,
|
||||
|
|
@ -760,7 +769,7 @@ public class SlaveComputer extends Computer {
|
|||
return SlaveSystemInfo.all();
|
||||
}
|
||||
|
||||
private static class SlaveLogFetcher implements Callable<List<LogRecord>,RuntimeException> {
|
||||
private static class SlaveLogFetcher extends MasterToSlaveCallable<List<LogRecord>,RuntimeException> {
|
||||
public List<LogRecord> call() {
|
||||
return new ArrayList<LogRecord>(SLAVE_LOG_HANDLER.getView());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
package hudson.tasks;
|
||||
|
||||
import hudson.FilePath;
|
||||
import jenkins.MasterToSlaveFileCallable;
|
||||
import hudson.Launcher;
|
||||
import hudson.Util;
|
||||
import hudson.Extension;
|
||||
|
|
@ -247,7 +248,7 @@ public class ArtifactArchiver extends Recorder implements SimpleBuildStep {
|
|||
}
|
||||
}
|
||||
|
||||
private static final class ListFiles implements FilePath.FileCallable<Map<String,String>> {
|
||||
private static final class ListFiles extends MasterToSlaveFileCallable<Map<String,String>> {
|
||||
private static final long serialVersionUID = 1;
|
||||
private final String includes, excludes;
|
||||
private final boolean defaultExcludes;
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ import com.google.common.collect.ImmutableMap;
|
|||
import hudson.EnvVars;
|
||||
import hudson.Extension;
|
||||
import hudson.FilePath;
|
||||
import hudson.FilePath.FileCallable;
|
||||
import jenkins.MasterToSlaveFileCallable;
|
||||
import hudson.Launcher;
|
||||
import hudson.Util;
|
||||
import hudson.model.AbstractBuild;
|
||||
|
|
@ -209,7 +209,7 @@ public class Fingerprinter extends Recorder implements Serializable, DependencyD
|
|||
|
||||
final long buildTimestamp = build.getTimeInMillis();
|
||||
|
||||
List<Record> records = ws.act(new FileCallable<List<Record>>() {
|
||||
List<Record> records = ws.act(new MasterToSlaveFileCallable<List<Record>>() {
|
||||
public List<Record> invoke(File baseDir, VirtualChannel channel) throws IOException {
|
||||
List<Record> results = new ArrayList<Record>();
|
||||
|
||||
|
|
|
|||
|
|
@ -24,13 +24,13 @@
|
|||
package hudson.tasks;
|
||||
|
||||
import hudson.Extension;
|
||||
import jenkins.MasterToSlaveFileCallable;
|
||||
import hudson.Launcher;
|
||||
import hudson.Functions;
|
||||
import hudson.EnvVars;
|
||||
import hudson.Util;
|
||||
import hudson.CopyOnWrite;
|
||||
import hudson.Launcher.LocalLauncher;
|
||||
import hudson.FilePath.FileCallable;
|
||||
import hudson.model.AbstractBuild;
|
||||
import hudson.model.AbstractProject;
|
||||
import hudson.model.BuildListener;
|
||||
|
|
@ -42,7 +42,6 @@ import jenkins.mvn.GlobalMavenConfig;
|
|||
import jenkins.mvn.GlobalSettingsProvider;
|
||||
import jenkins.mvn.SettingsProvider;
|
||||
import hudson.model.TaskListener;
|
||||
import hudson.remoting.Callable;
|
||||
import hudson.remoting.VirtualChannel;
|
||||
import hudson.slaves.NodeSpecific;
|
||||
import hudson.tasks._maven.MavenConsoleAnnotator;
|
||||
|
|
@ -59,6 +58,7 @@ import hudson.util.VariableResolver.ByMap;
|
|||
import hudson.util.VariableResolver.Union;
|
||||
import hudson.util.FormValidation;
|
||||
import hudson.util.XStream2;
|
||||
import jenkins.security.MasterToSlaveCallable;
|
||||
import net.sf.json.JSONObject;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
|
@ -217,7 +217,7 @@ public class Maven extends Builder {
|
|||
* Looks for <tt>pom.xlm</tt> or <tt>project.xml</tt> to determine the maven executable
|
||||
* name.
|
||||
*/
|
||||
private static final class DecideDefaultMavenCommand implements FileCallable<String> {
|
||||
private static final class DecideDefaultMavenCommand extends MasterToSlaveFileCallable<String> {
|
||||
private static final long serialVersionUID = -2327576423452215146L;
|
||||
// command line arguments.
|
||||
private final String arguments;
|
||||
|
|
@ -506,7 +506,7 @@ public class Maven extends Builder {
|
|||
public boolean meetsMavenReqVersion(Launcher launcher, int mavenReqVersion) throws IOException, InterruptedException {
|
||||
// FIXME using similar stuff as in the maven plugin could be better
|
||||
// olamy : but will add a dependency on maven in core -> so not so good
|
||||
String mavenVersion = launcher.getChannel().call(new Callable<String,IOException>() {
|
||||
String mavenVersion = launcher.getChannel().call(new MasterToSlaveCallable<String,IOException>() {
|
||||
private static final long serialVersionUID = -4143159957567745621L;
|
||||
|
||||
public String call() throws IOException {
|
||||
|
|
@ -562,7 +562,7 @@ public class Maven extends Builder {
|
|||
* Gets the executable path of this maven on the given target system.
|
||||
*/
|
||||
public String getExecutable(Launcher launcher) throws IOException, InterruptedException {
|
||||
return launcher.getChannel().call(new Callable<String,IOException>() {
|
||||
return launcher.getChannel().call(new MasterToSlaveCallable<String,IOException>() {
|
||||
private static final long serialVersionUID = 2373163112639943768L;
|
||||
|
||||
public String call() throws IOException {
|
||||
|
|
|
|||
|
|
@ -28,12 +28,12 @@ import hudson.Functions;
|
|||
import hudson.Util;
|
||||
import hudson.Extension;
|
||||
import hudson.model.AbstractProject;
|
||||
import hudson.remoting.Callable;
|
||||
import hudson.remoting.VirtualChannel;
|
||||
import hudson.util.FormValidation;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectStreamException;
|
||||
import hudson.util.LineEndingConversion;
|
||||
import jenkins.security.MasterToSlaveCallable;
|
||||
import net.sf.json.JSONObject;
|
||||
import org.kohsuke.stapler.DataBoundConstructor;
|
||||
import org.kohsuke.stapler.StaplerRequest;
|
||||
|
|
@ -172,7 +172,7 @@ public class Shell extends CommandInterpreter {
|
|||
return FormValidation.validateExecutable(value);
|
||||
}
|
||||
|
||||
private static final class Shellinterpreter implements Callable<String, IOException> {
|
||||
private static final class Shellinterpreter extends MasterToSlaveCallable<String, IOException> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ import hudson.util.FormValidation;
|
|||
import hudson.util.HttpResponses;
|
||||
import hudson.util.Secret;
|
||||
import jenkins.model.Jenkins;
|
||||
import jenkins.security.MasterToSlaveCallable;
|
||||
import net.sf.json.JSONObject;
|
||||
import org.apache.commons.httpclient.Cookie;
|
||||
import org.apache.commons.httpclient.HttpClient;
|
||||
|
|
@ -297,7 +298,7 @@ public class JDKInstaller extends ToolInstaller {
|
|||
public interface FileSystem {
|
||||
void delete(String file) throws IOException, InterruptedException;
|
||||
void chmod(String file,int mode) throws IOException, InterruptedException;
|
||||
InputStream read(String file) throws IOException;
|
||||
InputStream read(String file) throws IOException, InterruptedException;
|
||||
/**
|
||||
* List sub-directories of the given directory and just return the file name portion.
|
||||
*/
|
||||
|
|
@ -320,7 +321,7 @@ public class JDKInstaller extends ToolInstaller {
|
|||
$(file).chmod(mode);
|
||||
}
|
||||
|
||||
public InputStream read(String file) throws IOException {
|
||||
public InputStream read(String file) throws IOException, InterruptedException {
|
||||
return $(file).read();
|
||||
}
|
||||
|
||||
|
|
@ -530,7 +531,7 @@ public class JDKInstaller extends ToolInstaller {
|
|||
throw new DetectionFailedException("Unknown CPU name: "+arch);
|
||||
}
|
||||
|
||||
static class GetCurrentPlatform implements Callable<Platform,DetectionFailedException> {
|
||||
static class GetCurrentPlatform extends MasterToSlaveCallable<Platform,DetectionFailedException> {
|
||||
private static final long serialVersionUID = 1L;
|
||||
public Platform call() throws DetectionFailedException {
|
||||
return current();
|
||||
|
|
@ -592,7 +593,7 @@ public class JDKInstaller extends ToolInstaller {
|
|||
throw new DetectionFailedException("Unknown CPU architecture: "+arch);
|
||||
}
|
||||
|
||||
static class GetCurrentCPU implements Callable<CPU,DetectionFailedException> {
|
||||
static class GetCurrentCPU extends MasterToSlaveCallable<CPU,DetectionFailedException> {
|
||||
private static final long serialVersionUID = 1L;
|
||||
public CPU call() throws DetectionFailedException {
|
||||
return current();
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ package hudson.tools;
|
|||
|
||||
import hudson.Extension;
|
||||
import hudson.FilePath;
|
||||
import hudson.FilePath.FileCallable;
|
||||
import jenkins.MasterToSlaveFileCallable;
|
||||
import hudson.ProxyConfiguration;
|
||||
import hudson.Util;
|
||||
import hudson.Functions;
|
||||
|
|
@ -116,7 +116,7 @@ public class ZipExtractionInstaller extends ToolInstaller {
|
|||
* Sets execute permission on all files, since unzip etc. might not do this.
|
||||
* Hackish, is there a better way?
|
||||
*/
|
||||
static class ChmodRecAPlusX implements FileCallable<Void> {
|
||||
static class ChmodRecAPlusX extends MasterToSlaveFileCallable<Void> {
|
||||
private static final long serialVersionUID = 1L;
|
||||
public Void invoke(File d, VirtualChannel channel) throws IOException {
|
||||
if(!Functions.isWindows())
|
||||
|
|
|
|||
|
|
@ -29,14 +29,13 @@ import com.sun.jna.ptr.IntByReference;
|
|||
import hudson.EnvVars;
|
||||
import hudson.FilePath;
|
||||
import hudson.Util;
|
||||
import jenkins.model.Jenkins;
|
||||
import hudson.remoting.Callable;
|
||||
import hudson.remoting.Channel;
|
||||
import hudson.remoting.VirtualChannel;
|
||||
import hudson.slaves.SlaveComputer;
|
||||
import hudson.util.ProcessTree.OSProcess;
|
||||
import hudson.util.ProcessTreeRemoting.IOSProcess;
|
||||
import hudson.util.ProcessTreeRemoting.IProcessTree;
|
||||
import jenkins.security.SlaveToMasterCallable;
|
||||
import org.jvnet.winp.WinProcess;
|
||||
import org.jvnet.winp.WinpException;
|
||||
|
||||
|
|
@ -150,7 +149,7 @@ public abstract class ProcessTree implements Iterable<OSProcess>, IProcessTree,
|
|||
try {
|
||||
VirtualChannel channelToMaster = SlaveComputer.getChannelToMaster();
|
||||
if (channelToMaster!=null) {
|
||||
killers = channelToMaster.call(new Callable<List<ProcessKiller>, IOException>() {
|
||||
killers = channelToMaster.call(new SlaveToMasterCallable<List<ProcessKiller>, IOException>() {
|
||||
public List<ProcessKiller> call() throws IOException {
|
||||
return new ArrayList<ProcessKiller>(ProcessKiller.all());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,11 +29,11 @@ import hudson.FilePath;
|
|||
import hudson.Functions;
|
||||
import jenkins.model.Jenkins;
|
||||
import hudson.remoting.AsyncFutureImpl;
|
||||
import hudson.remoting.Callable;
|
||||
import hudson.remoting.DelegatingCallable;
|
||||
import hudson.remoting.Future;
|
||||
import hudson.remoting.VirtualChannel;
|
||||
import hudson.security.AccessControlled;
|
||||
import jenkins.security.MasterToSlaveCallable;
|
||||
import org.codehaus.groovy.control.CompilerConfiguration;
|
||||
import org.codehaus.groovy.control.customizers.ImportCustomizer;
|
||||
import org.kohsuke.stapler.StaplerRequest;
|
||||
|
|
@ -70,7 +70,7 @@ public final class RemotingDiagnostics {
|
|||
return channel.call(new GetSystemProperties());
|
||||
}
|
||||
|
||||
private static final class GetSystemProperties implements Callable<Map<Object,Object>,RuntimeException> {
|
||||
private static final class GetSystemProperties extends MasterToSlaveCallable<Map<Object,Object>,RuntimeException> {
|
||||
public Map<Object,Object> call() {
|
||||
return new TreeMap<Object,Object>(System.getProperties());
|
||||
}
|
||||
|
|
@ -89,7 +89,7 @@ public final class RemotingDiagnostics {
|
|||
return channel.callAsync(new GetThreadDump());
|
||||
}
|
||||
|
||||
private static final class GetThreadDump implements Callable<Map<String,String>,RuntimeException> {
|
||||
private static final class GetThreadDump extends MasterToSlaveCallable<Map<String,String>,RuntimeException> {
|
||||
public Map<String,String> call() {
|
||||
Map<String,String> r = new LinkedHashMap<String,String>();
|
||||
ThreadInfo[] data = Functions.getThreadInfos();
|
||||
|
|
@ -108,7 +108,7 @@ public final class RemotingDiagnostics {
|
|||
return channel.call(new Script(script));
|
||||
}
|
||||
|
||||
private static final class Script implements DelegatingCallable<String,RuntimeException> {
|
||||
private static final class Script extends MasterToSlaveCallable<String,RuntimeException> implements DelegatingCallable<String,RuntimeException> {
|
||||
private final String script;
|
||||
private transient ClassLoader cl;
|
||||
|
||||
|
|
@ -150,7 +150,7 @@ public final class RemotingDiagnostics {
|
|||
* Obtains the heap dump in an HPROF file.
|
||||
*/
|
||||
public static FilePath getHeapDump(VirtualChannel channel) throws IOException, InterruptedException {
|
||||
return channel.call(new Callable<FilePath, IOException>() {
|
||||
return channel.call(new MasterToSlaveCallable<FilePath, IOException>() {
|
||||
public FilePath call() throws IOException {
|
||||
final File hprof = File.createTempFile("hudson-heapdump", "hprof");
|
||||
hprof.delete();
|
||||
|
|
|
|||
|
|
@ -25,9 +25,9 @@ package hudson.util.io;
|
|||
|
||||
import hudson.ExtensionList;
|
||||
import hudson.ExtensionPoint;
|
||||
import hudson.remoting.Callable;
|
||||
import hudson.remoting.Channel;
|
||||
import jenkins.model.Jenkins;
|
||||
import jenkins.security.SlaveToMasterCallable;
|
||||
import org.dom4j.io.SAXReader;
|
||||
|
||||
import java.io.IOException;
|
||||
|
|
@ -78,7 +78,7 @@ public abstract class ParserConfigurator implements ExtensionPoint, Serializable
|
|||
if (Jenkins.getInstance()==null) {
|
||||
Channel ch = Channel.current();
|
||||
if (ch!=null)
|
||||
all = ch.call(new Callable<Collection<ParserConfigurator>, IOException>() {
|
||||
all = ch.call(new SlaveToMasterCallable<Collection<ParserConfigurator>, IOException>() {
|
||||
|
||||
private static final long serialVersionUID = -2178106894481500733L;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,106 @@
|
|||
package jenkins;
|
||||
|
||||
import hudson.FilePath;
|
||||
import hudson.remoting.Channel;
|
||||
import hudson.remoting.ChannelBuilder;
|
||||
|
||||
import javax.annotation.CheckForNull;
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Inspects {@link FilePath} access from remote channels.
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
* @see FilePath
|
||||
* @since 1.THU
|
||||
*/
|
||||
public abstract class FilePathFilter {
|
||||
/**
|
||||
* Checks if the given file/directory can be read.
|
||||
*
|
||||
* On POSIX, this corresponds to the 'r' permission of the file/directory itself.
|
||||
*/
|
||||
public void read(File f) throws SecurityException {}
|
||||
|
||||
/**
|
||||
* Checks if the given file can be written.
|
||||
*
|
||||
* On POSIX, this corresponds to the 'w' permission of the file itself.
|
||||
*/
|
||||
public void write(File f) throws SecurityException {}
|
||||
|
||||
/**
|
||||
* Checks if the given directory can be created.
|
||||
*
|
||||
* On POSIX, this corresponds to the 'w' permission of the parent directory.
|
||||
*/
|
||||
public void mkdirs(File f) throws SecurityException {}
|
||||
|
||||
/**
|
||||
* Checks if the given file can be created.
|
||||
*
|
||||
* On POSIX, this corresponds to the 'w' permission of the parent directory.
|
||||
*/
|
||||
public void create(File f) throws SecurityException {}
|
||||
|
||||
/**
|
||||
* Checks if the given file/directory can be deleted.
|
||||
*
|
||||
* On POSIX, this corresponds to the 'w' permission of the parent directory.
|
||||
*/
|
||||
public void delete(File f) throws SecurityException {}
|
||||
|
||||
/**
|
||||
* Checks if the metadata of the given file/directory (as opposed to the content) can be accessed.
|
||||
*
|
||||
* On POSIX, this corresponds to the 'r' permission of the parent directory.
|
||||
*/
|
||||
public void stat(File f) throws SecurityException {}
|
||||
|
||||
|
||||
public final void installTo(ChannelBuilder cb) {
|
||||
synchronized (cb) {
|
||||
FilePathFilterAggregator filters = (FilePathFilterAggregator) cb.getProperties().get(FilePathFilterAggregator.KEY);
|
||||
if (filters==null) {
|
||||
filters = new FilePathFilterAggregator();
|
||||
cb.withProperty(FilePathFilterAggregator.KEY,filters);
|
||||
}
|
||||
filters.add(this);
|
||||
}
|
||||
}
|
||||
|
||||
public final void uninstallFrom(Channel ch) {
|
||||
synchronized (ch) {
|
||||
FilePathFilterAggregator filters = ch.getProperty(FilePathFilterAggregator.KEY);
|
||||
if (filters!=null) {
|
||||
filters.remove(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an {@link FilePathFilter} object that represents all the in-scope filters,
|
||||
* or null if none is needed.
|
||||
*/
|
||||
public static @CheckForNull FilePathFilter current() {
|
||||
Channel ch = Channel.current();
|
||||
if (ch==null) return null;
|
||||
|
||||
return ch.getProperty(FilePathFilterAggregator.KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Immutable instance that represents the empty filter.
|
||||
*/
|
||||
public static final FilePathFilter EMPTY = new FilePathFilterAggregator() {
|
||||
@Override
|
||||
public void add(FilePathFilter f) {
|
||||
// noop
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "None";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
package jenkins;
|
||||
|
||||
import hudson.FilePath;
|
||||
import hudson.remoting.ChannelProperty;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* Maintains a bundle of {@link FilePathFilter} and implement a hook that broadcasts to all the filters.
|
||||
*
|
||||
* Accessible as channel property.
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
* @see FilePath
|
||||
* @since 1.THU
|
||||
*/
|
||||
class FilePathFilterAggregator extends FilePathFilter {
|
||||
private final CopyOnWriteArrayList<FilePathFilter> all = new CopyOnWriteArrayList<FilePathFilter>();
|
||||
|
||||
public void add(FilePathFilter f) {
|
||||
all.add(f);
|
||||
}
|
||||
|
||||
public void remove(FilePathFilter f) {
|
||||
all.remove(f);
|
||||
}
|
||||
|
||||
public void read(File f) throws SecurityException {
|
||||
for (FilePathFilter filter : all) {
|
||||
filter.read(f);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mkdirs(File f) throws SecurityException {
|
||||
for (FilePathFilter filter : all) {
|
||||
filter.mkdirs(f);
|
||||
}
|
||||
}
|
||||
|
||||
public void write(File f) throws SecurityException {
|
||||
for (FilePathFilter filter : all) {
|
||||
filter.write(f);
|
||||
}
|
||||
}
|
||||
public void create(File f) throws SecurityException {
|
||||
for (FilePathFilter filter : all) {
|
||||
filter.create(f);
|
||||
}
|
||||
}
|
||||
public void delete(File f) throws SecurityException {
|
||||
for (FilePathFilter filter : all) {
|
||||
filter.delete(f);
|
||||
}
|
||||
}
|
||||
public void stat(File f) throws SecurityException {
|
||||
for (FilePathFilter filter : all) {
|
||||
filter.stat(f);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return "FilePathFilterAggregator" + all;
|
||||
}
|
||||
|
||||
static final ChannelProperty<FilePathFilterAggregator> KEY = new ChannelProperty<FilePathFilterAggregator>(FilePathFilterAggregator.class, "FilePathFilters");
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package jenkins;
|
||||
|
||||
import hudson.FilePath.FileCallable;
|
||||
import jenkins.security.Roles;
|
||||
import org.jenkinsci.remoting.RoleChecker;
|
||||
|
||||
/**
|
||||
* {@link FileCallable}s that are meant to be only used on the master.
|
||||
*
|
||||
* @since 1.THU
|
||||
*/
|
||||
public abstract class MasterToSlaveFileCallable<T> implements FileCallable<T> {
|
||||
@Override
|
||||
public void checkRoles(RoleChecker checker) throws SecurityException {
|
||||
checker.check(this, Roles.SLAVE);
|
||||
}
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package jenkins;
|
||||
|
||||
import hudson.FilePath.FileCallable;
|
||||
import jenkins.security.Roles;
|
||||
import org.jenkinsci.remoting.RoleChecker;
|
||||
|
||||
/**
|
||||
* {@link FileCallable}s that can be executed on the master, sent by the slave.
|
||||
*
|
||||
* @since 1.THU
|
||||
*/
|
||||
public abstract class SlaveToMasterFileCallable<T> implements FileCallable<T> {
|
||||
@Override
|
||||
public void checkRoles(RoleChecker checker) throws SecurityException {
|
||||
checker.check(this, Roles.MASTER);
|
||||
}
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
||||
|
|
@ -196,6 +196,7 @@ import jenkins.model.ProjectNamingStrategy.DefaultProjectNamingStrategy;
|
|||
import jenkins.security.ConfidentialKey;
|
||||
import jenkins.security.ConfidentialStore;
|
||||
import jenkins.security.SecurityListener;
|
||||
import jenkins.security.MasterToSlaveCallable;
|
||||
import jenkins.slaves.WorkspaceLocator;
|
||||
import jenkins.util.Timer;
|
||||
import jenkins.util.io.FileBoolean;
|
||||
|
|
@ -2034,7 +2035,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
|
|||
|
||||
@Override
|
||||
public Callable<ClockDifference, IOException> getClockDifferenceCallable() {
|
||||
return new Callable<ClockDifference, IOException>() {
|
||||
return new MasterToSlaveCallable<ClockDifference, IOException>() {
|
||||
public ClockDifference call() throws IOException {
|
||||
return new ClockDifference(0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,132 @@
|
|||
package jenkins.security;
|
||||
|
||||
import hudson.Extension;
|
||||
import hudson.remoting.Callable;
|
||||
import hudson.remoting.ChannelBuilder;
|
||||
import org.jenkinsci.remoting.Role;
|
||||
import org.jenkinsci.remoting.RoleChecker;
|
||||
import org.jenkinsci.remoting.RoleSensitive;
|
||||
import org.kohsuke.accmod.Restricted;
|
||||
import org.kohsuke.accmod.restrictions.DoNotUse;
|
||||
import org.kohsuke.accmod.restrictions.NoExternalUse;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Inspects {@link Callable}s that run on the master.
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
* @since 1.THU
|
||||
*/
|
||||
@Restricted(NoExternalUse.class) // used implicitly via listener
|
||||
public class CallableDirectionChecker extends RoleChecker {
|
||||
|
||||
private static final String BYPASS_PROP = CallableDirectionChecker.class.getName()+".allow";
|
||||
|
||||
/**
|
||||
* Switch to disable all the defense mechanism completely.
|
||||
*
|
||||
* This is an escape hatch in case the fix breaks something critical, to allow the user
|
||||
* to keep operation.
|
||||
*/
|
||||
public static boolean BYPASS = Boolean.getBoolean(BYPASS_PROP);
|
||||
|
||||
/**
|
||||
* Whitelist of {@link RoleSensitive} class names (mostly {@link Callable} classes) that are allowed
|
||||
* to pass through.
|
||||
*/
|
||||
public static ConcurrentMap<String,Void> BYPASS_CALLABLES = new ConcurrentHashMap<String,Void>();
|
||||
|
||||
/**
|
||||
* Test feature that is meant to replace all logging, and log what would have been violations.
|
||||
* TODO delete before release
|
||||
*/
|
||||
private static final PrintWriter BYPASS_LOG;
|
||||
|
||||
static {
|
||||
String log = System.getProperty(CallableDirectionChecker.class.getName()+".log");
|
||||
if (log == null) {
|
||||
BYPASS_LOG = null;
|
||||
} else {
|
||||
try {
|
||||
BYPASS_LOG = new PrintWriter(new OutputStreamWriter(new FileOutputStream(log, true)), true);
|
||||
} catch (FileNotFoundException x) {
|
||||
throw new ExceptionInInitializerError(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private CallableDirectionChecker() {}
|
||||
|
||||
@Override
|
||||
public void check(RoleSensitive subject, @Nonnull Collection<Role> expected) throws SecurityException {
|
||||
final String name = subject.getClass().getName();
|
||||
|
||||
if (expected.contains(Roles.MASTER)) {
|
||||
if (BYPASS_LOG == null) {
|
||||
LOGGER.log(Level.FINE, "Executing {0} is allowed since it is targeted for the master role", name);
|
||||
}
|
||||
return; // known to be safe
|
||||
}
|
||||
|
||||
if (BYPASS_LOG != null) {
|
||||
BYPASS_LOG.println(name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isWhitelisted(name)) {
|
||||
// this subject is dubious, but we are letting it through as per whitelisting
|
||||
LOGGER.log(Level.FINE, "Explicitly allowing {0} to be sent from slave to master", name);
|
||||
return;
|
||||
}
|
||||
|
||||
throw new SecurityException("Sending " + name + " from slave to master is prohibited");
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this subject class name whitelisted?
|
||||
*/
|
||||
private boolean isWhitelisted(String name) {
|
||||
if (BYPASS) // everything is whitelisted
|
||||
return true;
|
||||
|
||||
if (BYPASS_CALLABLES.containsKey(name))
|
||||
return true;
|
||||
|
||||
if (Boolean.getBoolean(BYPASS_PROP+"."+name)) {
|
||||
BYPASS_CALLABLES.put(name,null);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs {@link CallableDirectionChecker} to every channel.
|
||||
*/
|
||||
@Restricted(DoNotUse.class) // impl
|
||||
@Extension
|
||||
public static class ChannelConfiguratorImpl extends ChannelConfigurator {
|
||||
@Override
|
||||
public void onChannelBuilding(ChannelBuilder builder, Object context) {
|
||||
// if the big red emergency button is pressed, then we need to disable the defense mechanism,
|
||||
// including enabling classloading.
|
||||
if (!BYPASS && BYPASS_LOG == null) {
|
||||
builder.withRemoteClassLoadingAllowed(false);
|
||||
}
|
||||
// In either of the above cases, the check method will return normally, but may log things.
|
||||
builder.withRoleChecker(new CallableDirectionChecker());
|
||||
}
|
||||
}
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(CallableDirectionChecker.class.getName());
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
package jenkins.security;
|
||||
|
||||
import hudson.ExtensionList;
|
||||
import hudson.ExtensionPoint;
|
||||
import hudson.remoting.Channel;
|
||||
import hudson.remoting.ChannelBuilder;
|
||||
import hudson.slaves.SlaveComputer;
|
||||
import jenkins.model.Jenkins;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Intercepts the new creation of {@link Channel} and tweak its configuration.
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
* @since 1.THU
|
||||
*/
|
||||
public abstract class ChannelConfigurator implements ExtensionPoint {
|
||||
/**
|
||||
* Called whenever a new channel is being built.
|
||||
*
|
||||
* @param builder
|
||||
* Configures the newly built channel. The callee
|
||||
* can call its methods to modify its settings.
|
||||
* @param context
|
||||
* The parameter that helps the callee determines what this channel is for.
|
||||
* Legacy callers do not always provide this information, in which case this value might be null.
|
||||
*
|
||||
* Possible known values include:
|
||||
*
|
||||
* <dl>
|
||||
* <dt>{@link SlaveComputer}
|
||||
* <dd>When a channel is being established to talk to a slave.
|
||||
* </dl>
|
||||
*/
|
||||
public void onChannelBuilding(ChannelBuilder builder, @Nullable Object context) {}
|
||||
|
||||
/**
|
||||
* All the registered {@link ChannelConfigurator}s.
|
||||
*/
|
||||
public static ExtensionList<ChannelConfigurator> all() {
|
||||
return Jenkins.getInstance().getExtensionList(ChannelConfigurator.class);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright 2014 Jesse Glick.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package jenkins.security;
|
||||
|
||||
import hudson.Extension;
|
||||
import hudson.FilePath;
|
||||
import hudson.model.Computer;
|
||||
import hudson.model.TaskListener;
|
||||
import hudson.remoting.Channel;
|
||||
import hudson.remoting.ChannelBuilder;
|
||||
import hudson.slaves.ComputerListener;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import jenkins.FilePathFilter;
|
||||
import org.kohsuke.accmod.Restricted;
|
||||
import org.kohsuke.accmod.restrictions.DoNotUse;
|
||||
|
||||
/**
|
||||
* Blocks slaves from writing to files on the master by default.
|
||||
*/
|
||||
@Restricted(DoNotUse.class) // impl
|
||||
@Extension public class DefaultFilePathFilter extends ChannelConfigurator {
|
||||
|
||||
/**
|
||||
* Escape hatch to disable this check completely.
|
||||
*/
|
||||
public static boolean BYPASS = Boolean.getBoolean(DefaultFilePathFilter.class.getName()+".allow");
|
||||
private static final PrintWriter BYPASS_LOG; // TODO delete before release
|
||||
static {
|
||||
String log = System.getProperty("jenkins.security.DefaultFilePathFilter.log");
|
||||
if (log == null) {
|
||||
BYPASS_LOG = null;
|
||||
} else {
|
||||
try {
|
||||
BYPASS_LOG = new PrintWriter(new OutputStreamWriter(new FileOutputStream(log, true)), true);
|
||||
} catch (FileNotFoundException x) {
|
||||
throw new ExceptionInInitializerError(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
private static final Logger LOGGER = Logger.getLogger(DefaultFilePathFilter.class.getName());
|
||||
|
||||
@Override
|
||||
public void onChannelBuilding(ChannelBuilder builder, Object context) {
|
||||
new FilePathFilter() {
|
||||
private void op(String op, File f) throws SecurityException {
|
||||
if (BYPASS_LOG != null) {
|
||||
BYPASS_LOG.println(op + " " + f);
|
||||
return;
|
||||
}
|
||||
if (BYPASS) {
|
||||
LOGGER.log(Level.FINE, "slave allowed to {0} {1}", new Object[] {op, f});
|
||||
} else {
|
||||
// TODO allow finer-grained control, for example by regexp (or Ant pattern) of relative path inside $JENKINS_HOME
|
||||
throw new SecurityException("slave may not " + op + " " + f);
|
||||
}
|
||||
}
|
||||
@Override public void read(File f) throws SecurityException {
|
||||
op("read", f);
|
||||
}
|
||||
@Override public void write(File f) throws SecurityException {
|
||||
op("write", f);
|
||||
}
|
||||
@Override public void mkdirs(File f) throws SecurityException {
|
||||
op("mkdirs", f);
|
||||
}
|
||||
@Override public void create(File f) throws SecurityException {
|
||||
op("create", f);
|
||||
}
|
||||
@Override public void delete(File f) throws SecurityException {
|
||||
op("delete", f);
|
||||
}
|
||||
@Override public void stat(File f) throws SecurityException {
|
||||
op("stat", f);
|
||||
}
|
||||
}.installTo(builder);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package jenkins.security;
|
||||
|
||||
import hudson.remoting.Callable;
|
||||
import org.jenkinsci.remoting.Role;
|
||||
import org.jenkinsci.remoting.RoleChecker;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Convenient {@link Callable} meant to be run on slave.
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
* @since 1.THU
|
||||
*/
|
||||
public abstract class MasterToSlaveCallable<V, T extends Throwable> implements Callable<V,T> {
|
||||
@Override
|
||||
public void checkRoles(RoleChecker checker) throws SecurityException {
|
||||
checker.check(this,Roles.SLAVE);
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package jenkins.security;
|
||||
|
||||
import hudson.remoting.Callable;
|
||||
import org.jenkinsci.remoting.RoleChecker;
|
||||
|
||||
/**
|
||||
* {@link Callable} adapter for situations where Callable is not used for remoting but
|
||||
* just as a convenient function that has parameterized return value and exception type.
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
* @since 1.THU
|
||||
*/
|
||||
public abstract class NotReallyRoleSensitiveCallable<V,T extends Throwable> implements Callable<V,T> {
|
||||
@Override
|
||||
public void checkRoles(RoleChecker checker) throws SecurityException {
|
||||
// not meant to be used where this matters
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
package jenkins.security;
|
||||
|
||||
import org.jenkinsci.remoting.Role;
|
||||
|
||||
/**
|
||||
* Predefined {@link Role}s in Jenkins.
|
||||
*
|
||||
* <p>
|
||||
* In Jenkins, there is really only one interesting role, which is the Jenkins master.
|
||||
* Slaves, CLI, and Maven processes are all going to load classes from the master,
|
||||
* which means it accepts anything that the master asks for, and thus they need
|
||||
* not have any role.
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
* @since 1.THU
|
||||
*/
|
||||
public class Roles {
|
||||
/**
|
||||
* Indicates that a callable runs on masters, requested by slaves/CLI/maven/whatever.
|
||||
*/
|
||||
public static final Role MASTER = new Role("master");
|
||||
|
||||
/**
|
||||
* Indicates that a callable is meant to run on slaves.
|
||||
*
|
||||
* This isn't used to reject callables to run on the slave, but rather to allow
|
||||
* the master to promptly reject callables that are really not meant to be run on
|
||||
* the master (as opposed to ones that do not have that information, which gets
|
||||
* {@link Role#UNKNOWN})
|
||||
*/
|
||||
public static final Role SLAVE = new Role("slave");
|
||||
|
||||
private Roles() {}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package jenkins.security;
|
||||
|
||||
import hudson.remoting.Callable;
|
||||
import org.jenkinsci.remoting.Role;
|
||||
import org.jenkinsci.remoting.RoleChecker;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Convenient {@link Callable} that are meant to run on the master (sent by slave/CLI/etc).
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
* @since 1.THU
|
||||
*/
|
||||
public abstract class SlaveToMasterCallable<V, T extends Throwable> implements Callable<V,T> {
|
||||
@Override
|
||||
public void checkRoles(RoleChecker checker) throws SecurityException {
|
||||
checker.check(this,Roles.MASTER);
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
||||
|
|
@ -4,11 +4,11 @@ import hudson.Extension;
|
|||
import hudson.FilePath;
|
||||
import hudson.model.Computer;
|
||||
import hudson.model.TaskListener;
|
||||
import hudson.remoting.Callable;
|
||||
import hudson.remoting.Channel;
|
||||
import hudson.remoting.StandardOutputStream;
|
||||
import hudson.slaves.ComputerListener;
|
||||
import hudson.util.jna.GNUCLibrary;
|
||||
import jenkins.security.MasterToSlaveCallable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileDescriptor;
|
||||
|
|
@ -35,7 +35,7 @@ public class StandardOutputSwapper extends ComputerListener {
|
|||
}
|
||||
}
|
||||
|
||||
private static final class ChannelSwapper implements Callable<Boolean,Exception> {
|
||||
private static final class ChannelSwapper extends MasterToSlaveCallable<Boolean,Exception> {
|
||||
public Boolean call() throws Exception {
|
||||
if (File.pathSeparatorChar==';') return false; // Windows
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package jenkins.slaves.restarter;
|
|||
import hudson.Extension;
|
||||
import hudson.model.Computer;
|
||||
import hudson.model.TaskListener;
|
||||
import hudson.remoting.Callable;
|
||||
import hudson.remoting.Engine;
|
||||
import hudson.remoting.EngineListener;
|
||||
import hudson.remoting.EngineListenerAdapter;
|
||||
|
|
@ -19,6 +18,7 @@ import java.util.List;
|
|||
import java.util.logging.Logger;
|
||||
|
||||
import static java.util.logging.Level.*;
|
||||
import jenkins.security.MasterToSlaveCallable;
|
||||
|
||||
/**
|
||||
* Actual slave restart logic.
|
||||
|
|
@ -49,7 +49,7 @@ public class JnlpSlaveRestarterInstaller extends ComputerListener implements Ser
|
|||
VirtualChannel ch = c.getChannel();
|
||||
if (ch==null) return; // defensive check
|
||||
|
||||
List<SlaveRestarter> effective = ch.call(new Callable<List<SlaveRestarter>, IOException>() {
|
||||
List<SlaveRestarter> effective = ch.call(new MasterToSlaveCallable<List<SlaveRestarter>, IOException>() {
|
||||
public List<SlaveRestarter> call() throws IOException {
|
||||
Engine e = Engine.current();
|
||||
if (e == null) return null; // not running under Engine
|
||||
|
|
|
|||
|
|
@ -41,6 +41,8 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import jenkins.MasterToSlaveFileCallable;
|
||||
|
||||
/**
|
||||
* Abstraction over {@link File}, {@link FilePath}, or other items such as network resources or ZIP entries.
|
||||
* Assumed to be read-only and makes very limited assumptions, just enough to display content and traverse directories.
|
||||
|
|
@ -354,7 +356,11 @@ public abstract class VirtualFile implements Comparable<VirtualFile>, Serializab
|
|||
}
|
||||
}
|
||||
@Override public InputStream open() throws IOException {
|
||||
return f.read();
|
||||
try {
|
||||
return f.read();
|
||||
} catch (InterruptedException x) {
|
||||
throw (IOException) new IOException(x.toString()).initCause(x);
|
||||
}
|
||||
}
|
||||
@Override public <V> V run(Callable<V,IOException> callable) throws IOException {
|
||||
try {
|
||||
|
|
@ -364,7 +370,7 @@ public abstract class VirtualFile implements Comparable<VirtualFile>, Serializab
|
|||
}
|
||||
}
|
||||
}
|
||||
private static final class Scanner implements FilePath.FileCallable<String[]> {
|
||||
private static final class Scanner extends MasterToSlaveFileCallable<String[]> {
|
||||
private final String glob;
|
||||
Scanner(String glob) {
|
||||
this.glob = glob;
|
||||
|
|
@ -381,7 +387,7 @@ public abstract class VirtualFile implements Comparable<VirtualFile>, Serializab
|
|||
}
|
||||
|
||||
}
|
||||
private static final class Readable implements FilePath.FileCallable<Boolean> {
|
||||
private static final class Readable extends MasterToSlaveFileCallable<Boolean> {
|
||||
@Override public Boolean invoke(File f, VirtualChannel channel) throws IOException, InterruptedException {
|
||||
return f.canRead();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import hudson.model.TaskListener;
|
|||
import hudson.remoting.Callable;
|
||||
import hudson.util.ProcessTree;
|
||||
import hudson.util.StreamTaskListener;
|
||||
import jenkins.security.MasterToSlaveCallable;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.jvnet.hudson.test.Bug;
|
||||
|
||||
|
|
@ -73,7 +74,7 @@ public class LauncherTest extends ChannelTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
private static final Callable<Object,RuntimeException> NOOP = new Callable<Object,RuntimeException>() {
|
||||
private static final Callable<Object,RuntimeException> NOOP = new MasterToSlaveCallable<Object,RuntimeException>() {
|
||||
public Object call() throws RuntimeException {
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package hudson.os;
|
|||
|
||||
import hudson.remoting.Callable;
|
||||
import hudson.util.StreamTaskListener;
|
||||
import jenkins.security.MasterToSlaveCallable;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
|
||||
|
|
@ -10,7 +11,7 @@ import java.io.FileOutputStream;
|
|||
*/
|
||||
public class SUTester {
|
||||
public static void main(String[] args) throws Throwable {
|
||||
SU.execute(StreamTaskListener.fromStdout(),"kohsuke","bogus",new Callable<Object, Throwable>() {
|
||||
SU.execute(StreamTaskListener.fromStdout(),"kohsuke","bogus",new MasterToSlaveCallable<Object, Throwable>() {
|
||||
public Object call() throws Throwable {
|
||||
System.out.println("Touching /tmp/x");
|
||||
new FileOutputStream("/tmp/x").close();
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import hudson.remoting.Callable;
|
|||
import hudson.remoting.VirtualChannel;
|
||||
import hudson.util.ProcessTree.OSProcess;
|
||||
import hudson.util.ProcessTree.ProcessCallable;
|
||||
import jenkins.security.MasterToSlaveCallable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
|
|
@ -39,7 +40,7 @@ public class ProcessTreeTest extends ChannelTestCase {
|
|||
t.p.act(new ProcessCallableImpl());
|
||||
}
|
||||
|
||||
private static class MyCallable implements Callable<Tag, IOException>, Serializable {
|
||||
private static class MyCallable extends MasterToSlaveCallable<Tag, IOException> implements Serializable {
|
||||
public Tag call() throws IOException {
|
||||
Tag t = new Tag();
|
||||
t.tree = ProcessTree.get();
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import java.io.PrintWriter;
|
|||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
public class ReopenableRotatingFileOutputStreamTest extends TestCase {
|
||||
public void testRotation() throws IOException {
|
||||
public void testRotation() throws IOException, InterruptedException {
|
||||
File base = File.createTempFile("test", "log");
|
||||
ReopenableRotatingFileOutputStream os = new ReopenableRotatingFileOutputStream(base,3);
|
||||
PrintWriter w = new PrintWriter(os,true);
|
||||
|
|
|
|||
4
pom.xml
4
pom.xml
|
|
@ -95,7 +95,7 @@ THE SOFTWARE.
|
|||
<patch.tracker.serverId>jenkins-jira</patch.tracker.serverId>
|
||||
|
||||
<slf4jVersion>1.7.7</slf4jVersion> <!-- < 1.6.x version didn't specify the license (MIT) -->
|
||||
<maven-plugin.version>2.5</maven-plugin.version>
|
||||
<maven-plugin.version>2.6.1-SNAPSHOT</maven-plugin.version> <!-- TODO SECURITY-144 -->
|
||||
<animal.sniffer.skip>${skipTests}</animal.sniffer.skip>
|
||||
|
||||
<java.level>6</java.level>
|
||||
|
|
@ -173,7 +173,7 @@ THE SOFTWARE.
|
|||
<dependency>
|
||||
<groupId>org.jenkins-ci.main</groupId>
|
||||
<artifactId>remoting</artifactId>
|
||||
<version>2.45</version>
|
||||
<version>2.46-SNAPSHOT</version><!-- SECURITY-144 branch -->
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
|
|
|||
|
|
@ -3,13 +3,12 @@ package hudson;
|
|||
import hudson.Launcher.LocalLauncher;
|
||||
import hudson.Launcher.RemoteLauncher;
|
||||
import hudson.Proc.RemoteProc;
|
||||
import hudson.remoting.Callable;
|
||||
import hudson.remoting.Future;
|
||||
import hudson.remoting.Pipe;
|
||||
import hudson.remoting.VirtualChannel;
|
||||
import hudson.slaves.DumbSlave;
|
||||
import hudson.util.IOUtils;
|
||||
import hudson.util.StreamTaskListener;
|
||||
import jenkins.security.MasterToSlaveCallable;
|
||||
import org.jvnet.hudson.test.Bug;
|
||||
import org.jvnet.hudson.test.HudsonTestCase;
|
||||
|
||||
|
|
@ -70,7 +69,7 @@ public class ProcTest extends HudsonTestCase {
|
|||
return ch;
|
||||
}
|
||||
|
||||
private static class ChannelFiller implements Callable<Void,IOException> {
|
||||
private static class ChannelFiller extends MasterToSlaveCallable<Void,IOException> {
|
||||
private final OutputStream o;
|
||||
|
||||
private ChannelFiller(OutputStream o) {
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
*/
|
||||
package hudson.logging;
|
||||
|
||||
import jenkins.security.MasterToSlaveCallable;
|
||||
import org.jvnet.hudson.test.Url;
|
||||
import com.gargoylesoftware.htmlunit.html.HtmlPage;
|
||||
import com.gargoylesoftware.htmlunit.html.HtmlForm;
|
||||
|
|
@ -109,7 +110,7 @@ public class LogRecorderManagerTest {
|
|||
assertFalse(text, text.contains("msg #4"));
|
||||
}
|
||||
|
||||
private static final class Log implements Callable<Boolean,Error> {
|
||||
private static final class Log extends MasterToSlaveCallable<Boolean,Error> {
|
||||
private final Level level;
|
||||
private final String logger;
|
||||
private final String message;
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ import hudson.remoting.Callable;
|
|||
import hudson.remoting.Which;
|
||||
import hudson.util.ArgumentListBuilder;
|
||||
|
||||
import jenkins.security.SlaveToMasterCallable;
|
||||
import org.jvnet.hudson.test.HudsonTestCase;
|
||||
import org.jvnet.hudson.test.TestExtension;
|
||||
|
||||
|
|
@ -154,7 +155,7 @@ public class JNLPLauncherTest extends HudsonTestCase {
|
|||
return c;
|
||||
}
|
||||
|
||||
private static class NoopTask implements Callable<String,RuntimeException> {
|
||||
private static class NoopTask extends SlaveToMasterCallable<String,RuntimeException> {
|
||||
public String call() {
|
||||
return "done";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright 2014 Jesse Glick.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package jenkins.security;
|
||||
|
||||
import hudson.FilePath;
|
||||
import hudson.model.Slave;
|
||||
import hudson.remoting.Callable;
|
||||
import java.io.File;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.jenkinsci.remoting.Role;
|
||||
import org.jenkinsci.remoting.RoleChecker;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Rule;
|
||||
import org.jvnet.hudson.test.JenkinsRule;
|
||||
|
||||
public class DefaultFilePathFilterTest {
|
||||
|
||||
@Rule public JenkinsRule r = new JenkinsRule();
|
||||
|
||||
@Test public void remotePath() throws Exception {
|
||||
Slave s = r.createOnlineSlave();
|
||||
FilePath forward = s.getRootPath().child("forward");
|
||||
forward.write("hello", null);
|
||||
assertEquals("hello", s.getRootPath().act(new LocalCallable(forward)));
|
||||
FilePath reverse = new FilePath(new File(r.jenkins.root, "reverse"));
|
||||
assertFalse(reverse.exists());
|
||||
try {
|
||||
s.getChannel().call(new ReverseCallable(reverse));
|
||||
fail("should have failed");
|
||||
} catch (SecurityException x) {
|
||||
// good
|
||||
|
||||
// make sure that the stack trace contains the call site info to help assist diagnosis
|
||||
StringWriter sw = new StringWriter();
|
||||
x.printStackTrace(new PrintWriter(sw));
|
||||
assertTrue(sw.toString().contains(DefaultFilePathFilterTest.class.getName()+".remotePath"));
|
||||
}
|
||||
assertFalse(reverse.exists());
|
||||
DefaultFilePathFilter.BYPASS = true;
|
||||
s.getChannel().call(new ReverseCallable(reverse));
|
||||
assertTrue(reverse.exists());
|
||||
assertEquals("goodbye", reverse.readToString());
|
||||
}
|
||||
|
||||
private static class LocalCallable implements Callable<String,Exception> {
|
||||
private final FilePath p;
|
||||
LocalCallable(FilePath p) {
|
||||
this.p = p;
|
||||
}
|
||||
@Override public String call() throws Exception {
|
||||
assertFalse(p.isRemote());
|
||||
return p.readToString();
|
||||
}
|
||||
@Override
|
||||
public void checkRoles(RoleChecker checker) throws SecurityException {
|
||||
throw new NoSuchMethodError(); // simulate legacy Callable impls
|
||||
}
|
||||
}
|
||||
|
||||
private static class ReverseCallable implements Callable<Void,Exception> {
|
||||
private final FilePath p;
|
||||
ReverseCallable(FilePath p) {
|
||||
this.p = p;
|
||||
}
|
||||
@Override public Void call() throws Exception {
|
||||
assertTrue(p.isRemote());
|
||||
p.write("goodbye", null);
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public void checkRoles(RoleChecker checker) throws SecurityException {
|
||||
throw new NoSuchMethodError(); // simulate legacy Callable impls
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue