package hudson.plugins.tmpcleaner;
import hudson.os.PosixAPI;
import hudson.remoting.Callable;
import hudson.util.TimeUnit2;
import java.io.File;
import java.io.IOException;
import java.util.StringTokenizer;
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jruby.ext.posix.FileStat;
import org.jruby.ext.posix.POSIX;
import org.kohsuke.stapler.framework.io.IOException2;
/**
* Recursively visits a directory and remove unused files.
*
* @author Kohsuke Kawaguchi
*/
public class TmpCleanTask implements Callable<Void, IOException> {
private transient long criteria;
private transient POSIX posix;
private transient int euid;
//
private String extraDirectories;
private long days;
public TmpCleanTask(String extraDirectories, long days)
{
this.extraDirectories = extraDirectories;
this.days = days;
}
public Void call() throws IOException {
criteria = (System.currentTimeMillis() - TimeUnit2.DAYS.toMillis(days))/1000; // time_t is # of seconds
posix = PosixAPI.get();
euid = posix.geteuid();
File f = File.createTempFile("tmpclean", null);
f.delete();
visit(f.getParentFile());
LOGGER.fine( "extraDirectories " + extraDirectories + ", days " + days );
try
{
if (extraDirectories != null)
{
StringTokenizer stringTokenizer = new StringTokenizer( extraDirectories, "," );
while (stringTokenizer.hasMoreElements())
{
File dir = new File( stringTokenizer.nextToken() );
if (dir.exists())
{
visit( dir );
}
else
{
LOGGER.fine( "dir "+ dir.getPath() + " not exist ");
}
}
}
} catch (Exception e)
{
LOGGER.log( Level.SEVERE, e.getMessage(), e );
throw new IOException2( e.getMessage(), e );
}
finally
{
LOGGER.log(Level.INFO, " end TmpCleanTask " );
}
return null;
}
private void visit(File dir) {
LOGGER.fine("visit "+dir);
File[] children = dir.listFiles();
if (children==null) return; // just being defensive
for (File child : children) {
// lstat so that we won't visit into directories that are symlinked
FileStat stat;
try {
stat = posix.lstat(child.getPath());
} catch (RuntimeException e) {// handle lstat failure gracefully
LOGGER.log(Level.INFO, "lstat failed on "+child + ", " + e.getMessage());
continue;
}
if (stat.uid()!=euid) {
LOGGER.finer("Skipping "+child+" since we don't own it");
continue;
}
if (stat.isDirectory()) {
visit(child);
String[] contents = child.list();
if (contents!=null && contents.length==0) {
LOGGER.info("Deleting empty directory "+child);
child.delete();
} else {
LOGGER.finer(child+" is not empty");
}
}
long atime = stat.atime();
if (atime < criteria) {
LOGGER.info(String.format("Deleting %s (atime=%d, diff=%d)", child, atime,atime-criteria));
child.delete();
} else {
LOGGER.finer("Skipping "+child+" since it's not old enough");
}
}
}
public static void main(String[] args) throws IOException {
// somehow my JVM on Ubuntu gets iso-8859-1 as the default encoding even though LANG=en_US.UTF-8
// System.setProperty("jna.encoding","UTF-8");
LOGGER.setLevel(Level.FINE);
ConsoleHandler h = new ConsoleHandler();
h.setLevel(Level.FINE);
LOGGER.addHandler(h);
new TmpCleanTask("", 2).call();
}
private static final Logger LOGGER = Logger.getLogger(TmpCleanTask.class.getName());
}