summaryrefslogtreecommitdiffstats
path: root/src/main/java
diff options
context:
space:
mode:
authorSimon Rettberg2014-06-03 16:47:36 +0200
committerSimon Rettberg2014-06-03 16:47:36 +0200
commit32dc5354e2916387a2c62eadae0a4568023f1151 (patch)
tree7fd9a0173d6073e86d1d48e545646b1bc8c1a5eb /src/main/java
downloadtmlite-bwlp-32dc5354e2916387a2c62eadae0a4568023f1151.tar.gz
tmlite-bwlp-32dc5354e2916387a2c62eadae0a4568023f1151.tar.xz
tmlite-bwlp-32dc5354e2916387a2c62eadae0a4568023f1151.zip
Initial commit
Diffstat (limited to 'src/main/java')
-rw-r--r--src/main/java/org/openslx/satserver/util/Archive.java217
-rw-r--r--src/main/java/org/openslx/satserver/util/Constants.java8
-rw-r--r--src/main/java/org/openslx/satserver/util/Util.java66
-rw-r--r--src/main/java/org/openslx/taskmanager/tasks/CompileIPxe.java83
-rw-r--r--src/main/java/org/openslx/taskmanager/tasks/CreateAdConfig.java161
-rw-r--r--src/main/java/org/openslx/taskmanager/tasks/DeleteFile.java57
-rw-r--r--src/main/java/org/openslx/taskmanager/tasks/DownloadFile.java91
-rw-r--r--src/main/java/org/openslx/taskmanager/tasks/DownloadText.java82
-rw-r--r--src/main/java/org/openslx/taskmanager/tasks/DummyTask.java58
-rw-r--r--src/main/java/org/openslx/taskmanager/tasks/LdadpLauncher.java79
-rw-r--r--src/main/java/org/openslx/taskmanager/tasks/LdapSearch.java122
-rw-r--r--src/main/java/org/openslx/taskmanager/tasks/LinkConfigTgz.java61
-rw-r--r--src/main/java/org/openslx/taskmanager/tasks/ListArchive.java116
-rw-r--r--src/main/java/org/openslx/taskmanager/tasks/LocalAddressesList.java76
-rw-r--r--src/main/java/org/openslx/taskmanager/tasks/LsTask.java80
-rw-r--r--src/main/java/org/openslx/taskmanager/tasks/MountVmStore.java84
-rw-r--r--src/main/java/org/openslx/taskmanager/tasks/RecompressArchive.java193
17 files changed, 1634 insertions, 0 deletions
diff --git a/src/main/java/org/openslx/satserver/util/Archive.java b/src/main/java/org/openslx/satserver/util/Archive.java
new file mode 100644
index 0000000..dcc1380
--- /dev/null
+++ b/src/main/java/org/openslx/satserver/util/Archive.java
@@ -0,0 +1,217 @@
+package org.openslx.satserver.util;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+
+import org.apache.commons.compress.archivers.ArchiveEntry;
+import org.apache.commons.compress.archivers.ArchiveException;
+import org.apache.commons.compress.archivers.ArchiveInputStream;
+import org.apache.commons.compress.archivers.ArchiveStreamFactory;
+import org.apache.commons.compress.archivers.ar.ArArchiveEntry;
+import org.apache.commons.compress.archivers.cpio.CpioArchiveEntry;
+import org.apache.commons.compress.archivers.dump.DumpArchiveEntry;
+import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
+import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
+import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
+import org.apache.commons.compress.compressors.CompressorStreamFactory;
+import org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream;
+import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;
+import org.apache.commons.compress.compressors.xz.XZCompressorOutputStream;
+import org.apache.commons.io.FileUtils;
+
+public class Archive
+{
+
+ /**
+ * Create input stream from given archive file. The archive format will be guessed
+ * from the firt couple of bytes in the file, which should work for most archive
+ * formats (see apache commons-compress docs for detailed information)
+ *
+ * @param fileName archive to open for reading
+ * @return ArchiveInputStream stream to read archive from
+ * @throws FileNotFoundException
+ * @throws ArchiveException
+ * @throws IllegalArgumentException
+ */
+ public static ArchiveInputStream getArchiveInputStream( String fileName )
+ throws FileNotFoundException, ArchiveException, IllegalArgumentException
+ {
+ // Get an input stream for the archive handler
+ // 1) Get input stream from file
+ InputStream fileStream = new BufferedInputStream( new FileInputStream( fileName ) );
+ // 2) Might be compressed, so try to get a decompressed stream
+ InputStream archiveStream;
+ try {
+ archiveStream = new BufferedInputStream( new CompressorStreamFactory().createCompressorInputStream( fileStream ) );
+ } catch ( Exception e ) {
+ // Might be uncompressed archive, like tar - just try file input stream
+ archiveStream = fileStream;
+ }
+ // 3) Try to create archive input stream - if it succeeds we can read the archive :)
+ return new ArchiveStreamFactory().createArchiveInputStream( archiveStream );
+ }
+
+ /**
+ * Create tar archive with the given name. Optional compression method is
+ * determined by the file extension.
+ *
+ * @param outputFile name of archive to create; can be relative or absolute path
+ * @return {@link TarArchiveOutputStream} to write archive entries to
+ * @throws IOException
+ */
+ @SuppressWarnings( "resource" )
+ public static TarArchiveOutputStream createTarArchive( String outputFile ) throws IOException
+ {
+ OutputStream outputStream = new BufferedOutputStream( new FileOutputStream( outputFile ) );
+ OutputStream compressedOutputStream;
+ if ( outputFile.endsWith( ".tar.gz" ) || outputFile.endsWith( ".tgz" ) ) {
+ compressedOutputStream = new BufferedOutputStream( new GzipCompressorOutputStream( outputStream ) );
+ } else if ( outputFile.endsWith( ".tar.bz2" ) || outputFile.endsWith( ".tbz" ) ) {
+ compressedOutputStream = new BufferedOutputStream( new BZip2CompressorOutputStream( outputStream ) );
+ } else if ( outputFile.endsWith( ".tar.xz" ) || outputFile.endsWith( ".txz" ) ) {
+ compressedOutputStream = new BufferedOutputStream( new XZCompressorOutputStream( outputStream ) );
+ } else if ( outputFile.endsWith( ".tar.Z" ) || outputFile.endsWith( ".tar.z" ) || outputFile.endsWith( ".tz" ) ) {
+ compressedOutputStream = new BufferedOutputStream( new XZCompressorOutputStream( outputStream ) );
+ } else if ( outputFile.endsWith( ".tar.lzma" ) ) {
+ compressedOutputStream = new BufferedOutputStream( new XZCompressorOutputStream( outputStream ) );
+ } else if ( outputFile.endsWith( ".tar" ) ) {
+ compressedOutputStream = outputStream;
+ } else {
+ outputStream.close();
+ return null;
+ }
+ try {
+ return new TarArchiveOutputStream( compressedOutputStream );
+ } catch ( Throwable t ) {
+ compressedOutputStream.close();
+ return null;
+ }
+ }
+
+ public static TarArchiveEntry createTarArchiveEntry( ArchiveEntry inEntry, int defaultUser, int defaultGroup, int defaultDirMode, int defaultFileMode )
+ {
+ if ( inEntry instanceof TarArchiveEntry ) {
+ // Source is tar - easy
+ return (TarArchiveEntry)inEntry;
+ }
+ final TarArchiveEntry outEntry = new TarArchiveEntry( inEntry.getName() );
+ outEntry.setSize( inEntry.getSize() );
+ if ( inEntry instanceof ArArchiveEntry ) {
+ // Source is ar - has most of the stuff tar supports; transform
+ outEntry.setIds( ( (ArArchiveEntry)inEntry ).getUserId(), ( (ArArchiveEntry)inEntry ).getGroupId() );
+ outEntry.setMode( ( (ArArchiveEntry)inEntry ).getMode() );
+ outEntry.setModTime( ( (ArArchiveEntry)inEntry ).getLastModifiedDate() );
+ } else if ( inEntry instanceof DumpArchiveEntry ) {
+ // Source is dump (whatever the fuck that is) - has most of the stuff tar supports; transform
+ outEntry.setIds( ( (DumpArchiveEntry)inEntry ).getUserId(), ( (DumpArchiveEntry)inEntry ).getGroupId() );
+ outEntry.setMode( ( (DumpArchiveEntry)inEntry ).getMode() );
+ outEntry.setModTime( ( (DumpArchiveEntry)inEntry ).getLastModifiedDate() );
+ } else if ( inEntry instanceof CpioArchiveEntry ) {
+ // Source is cpio - has most of the stuff tar supports; transform
+ outEntry.setIds( (int) ( (CpioArchiveEntry)inEntry ).getUID(), (int) ( (CpioArchiveEntry)inEntry ).getGID() );
+ outEntry.setMode( (int) ( ( (CpioArchiveEntry)inEntry ).getMode() & 07777 ) );
+ outEntry.setModTime( ( (CpioArchiveEntry)inEntry ).getLastModifiedDate() );
+ } else if ( inEntry instanceof ZipArchiveEntry ) {
+ // Source is ZIP - might not have unix stuff
+ outEntry.setIds( defaultUser, defaultGroup );
+ int mode = ( (ZipArchiveEntry)inEntry ).getUnixMode();
+ if ( mode != 0 ) { // Has unix permissions
+ outEntry.setMode( mode );
+ } else if ( inEntry.isDirectory() ) { // Use default passed in
+ outEntry.setMode( defaultDirMode );
+ } else { // Use default passed in
+ outEntry.setMode( defaultFileMode );
+ }
+ outEntry.setModTime( ( (ZipArchiveEntry)inEntry ).getLastModifiedDate() );
+ } else {
+ outEntry.setIds( defaultUser, defaultGroup );
+ if ( inEntry.isDirectory() ) {
+ outEntry.setMode( defaultDirMode );
+ } else {
+ outEntry.setMode( defaultFileMode );
+ Calendar cal = GregorianCalendar.getInstance();
+ cal.set( 2014, 4, 2, 13, 57 );
+ outEntry.setModTime( cal.getTime() );
+ }
+ }
+ return outEntry;
+ }
+
+ public static boolean tarAddFile( TarArchiveOutputStream tar, String inArchiveFileName, File sourceFile, int mode )
+ {
+ if ( !sourceFile.exists() || !sourceFile.isFile() )
+ return false;
+ InputStream in;
+ try {
+ in = new FileInputStream( sourceFile );
+ } catch ( Exception e ) {
+ e.printStackTrace();
+ return false;
+ }
+ TarArchiveEntry entry = new TarArchiveEntry( inArchiveFileName );
+ long size = FileUtils.sizeOf( sourceFile );
+ entry.setIds( 0, 0 );
+ entry.setMode( mode );
+ entry.setSize( size );
+ try {
+ tar.putArchiveEntry( entry );
+ Util.streamCopy( in, tar, size );
+ tar.closeArchiveEntry();
+ } catch ( IOException e ) {
+ e.printStackTrace();
+ return false;
+ } finally {
+ Util.multiClose( in );
+ }
+ return true;
+ }
+
+ public static boolean tarCreateFileFromString( TarArchiveOutputStream tar, String fileName, String contents, int mode )
+ {
+ return tarCreateFileFromString( tar, fileName, contents.getBytes( StandardCharsets.UTF_8 ), mode );
+ }
+
+ public static boolean tarCreateFileFromString( TarArchiveOutputStream tar, String fileName, byte[] contents, int mode )
+ {
+ TarArchiveEntry entry = new TarArchiveEntry( fileName );
+ entry.setIds( 0, 0 );
+ entry.setMode( mode );
+ entry.setSize( contents.length );
+ try {
+ tar.putArchiveEntry( entry );
+ tar.write( contents );
+ tar.closeArchiveEntry();
+ } catch ( IOException e ) {
+ e.printStackTrace();
+ return false;
+ }
+ return true;
+ }
+
+ public static boolean tarCreateSymlink( TarArchiveOutputStream tar, String target, String linkName )
+ {
+ TarArchiveEntry entry = new TarArchiveEntry( linkName, TarArchiveEntry.LF_SYMLINK );
+ entry.setIds( 0, 0 );
+ entry.setMode( 0777 );
+ entry.setLinkName( target );
+ try {
+ tar.putArchiveEntry( entry );
+ tar.closeArchiveEntry();
+ } catch ( IOException e ) {
+ e.printStackTrace();
+ return false;
+ }
+ return true;
+ }
+
+}
diff --git a/src/main/java/org/openslx/satserver/util/Constants.java b/src/main/java/org/openslx/satserver/util/Constants.java
new file mode 100644
index 0000000..b8394f0
--- /dev/null
+++ b/src/main/java/org/openslx/satserver/util/Constants.java
@@ -0,0 +1,8 @@
+package org.openslx.satserver.util;
+
+public class Constants
+{
+
+ public static final String BASEDIR = System.getProperty( "user.dir" );
+
+}
diff --git a/src/main/java/org/openslx/satserver/util/Util.java b/src/main/java/org/openslx/satserver/util/Util.java
new file mode 100644
index 0000000..a6d354a
--- /dev/null
+++ b/src/main/java/org/openslx/satserver/util/Util.java
@@ -0,0 +1,66 @@
+package org.openslx.satserver.util;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class Util
+{
+
+ /**
+ * Check if the given string starts with any of the additionally passed strings.
+ * Ugly boilerplate.
+ *
+ * @param stringToCheck a string we want to check the starting of
+ * @param compareTo list of strings we compare stringToCheck to
+ * @return true if stringToCheck starts with any of the strings in compareTo
+ */
+ public static boolean startsWith( String stringToCheck, String... compareTo )
+ {
+ for ( String check : compareTo ) {
+ if ( stringToCheck.startsWith( check ) )
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Close all given Closables. Can handle null references.
+ * @param streams one or more closables/streams
+ */
+ public static void multiClose( Closeable... streams )
+ {
+ if ( streams == null )
+ return;
+ for ( Closeable stream : streams ) {
+ if ( stream != null ) {
+ try {
+ stream.close();
+ } catch ( IOException e ) {
+ // Ignore - nothing meaningful to do
+ }
+ }
+ }
+ }
+
+ public static boolean streamCopy( InputStream in, OutputStream out, long bytes )
+ {
+ byte buffer[] = new byte[ 7900 ];
+ while ( bytes > 0 ) {
+ try {
+ int ret = in.read( buffer, 0, (int) ( bytes > buffer.length ? buffer.length : bytes ) );
+ if ( ret == -1 )
+ return false;
+ bytes -= ret;
+ out.write( buffer, 0, ret );
+ } catch ( IOException e ) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+ return true;
+ }
+
+
+}
diff --git a/src/main/java/org/openslx/taskmanager/tasks/CompileIPxe.java b/src/main/java/org/openslx/taskmanager/tasks/CompileIPxe.java
new file mode 100644
index 0000000..c0534c7
--- /dev/null
+++ b/src/main/java/org/openslx/taskmanager/tasks/CompileIPxe.java
@@ -0,0 +1,83 @@
+package org.openslx.taskmanager.tasks;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.commons.io.FileUtils;
+import org.openslx.taskmanager.api.AbstractTask;
+
+import com.google.gson.annotations.Expose;
+
+public class CompileIPxe extends AbstractTask
+{
+
+ @Expose
+ private String ip = null;
+ @Expose
+ private String defaultentry = null;
+ @Expose
+ private int timeout = 0;
+ @Expose
+ private String custom = null;
+
+ private Output status = new Output();
+
+ @Override
+ protected boolean initTask()
+ {
+ this.setStatusObject( this.status );
+ if ( this.ip == null ) {
+ status.error = "No IP address set.";
+ return false;
+ }
+ if ( this.defaultentry == null )
+ this.defaultentry = "net";
+ if ( this.custom == null )
+ this.custom = "";
+ return true;
+ }
+
+ @Override
+ protected boolean execute()
+ {
+ // Prepare menu
+ String template;
+ try {
+ template = FileUtils.readFileToString( new File( "./data/pxemenu.template" ), StandardCharsets.UTF_8 );
+ } catch ( IOException e ) {
+ status.error = e.toString();
+ return false;
+ }
+ // Substitutions
+ template = template.replaceAll( "%ip%", this.ip );
+ template = template.replaceAll( "%timeout%", Integer.toString( this.timeout * 10 ) );
+ template = template.replaceAll( "%totaltimeout%", Integer.toString( this.timeout * 40 ) );
+ template = template.replaceAll( "%default%", this.defaultentry );
+ template = template.replaceAll( "%custom%", this.custom );
+ // Default selection net
+ if ( this.defaultentry.equals( "net" ) )
+ template = template.replaceAll( "%default-net%", "MENU DEFAULT" );
+ else
+ template = template.replaceAll( "%default-net%", "" );
+ // Default selection hdd
+ if ( this.defaultentry.equals( "hdd" ) )
+ template = template.replaceAll( "%default-hdd%", "MENU DEFAULT" );
+ else
+ template = template.replaceAll( "%default-hdd%", "" );
+ // Write out
+ try {
+ FileUtils.writeStringToFile( new File( "/srv/openslx/tftp/pxelinux.cfg/default" ), template, StandardCharsets.UTF_8 );
+ } catch ( IOException e ) {
+ status.error = e.toString();
+ return false;
+ }
+ return true;
+ }
+
+ class Output
+ {
+ protected String error = null;
+ }
+
+}
diff --git a/src/main/java/org/openslx/taskmanager/tasks/CreateAdConfig.java b/src/main/java/org/openslx/taskmanager/tasks/CreateAdConfig.java
new file mode 100644
index 0000000..71ef249
--- /dev/null
+++ b/src/main/java/org/openslx/taskmanager/tasks/CreateAdConfig.java
@@ -0,0 +1,161 @@
+package org.openslx.taskmanager.tasks;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
+import org.apache.commons.io.FileUtils;
+import org.openslx.satserver.util.Archive;
+import org.openslx.satserver.util.Util;
+import org.openslx.taskmanager.api.AbstractTask;
+
+import com.google.gson.annotations.Expose;
+
+public class CreateAdConfig extends AbstractTask
+{
+ @Expose
+ private int moduleid = 0;
+ @Expose
+ private String filename = null;
+ @Expose
+ private String server = null;
+ @Expose
+ private String searchbase = null;
+ @Expose
+ private String binddn = null;
+ @Expose
+ private String bindpw = null;
+ @Expose
+ private String proxyip = null;
+ @Expose
+ private int proxyport = 0;
+ @Expose
+ private String home = null;
+
+ private Output status = new Output();
+
+ @Override
+ protected boolean initTask()
+ {
+ // TODO: Check path is allowed
+ this.setStatusObject( this.status );
+ if ( filename == null || server == null || searchbase == null || binddn == null || bindpw == null || proxyip == null || proxyport == 0 || moduleid == 0 ) {
+ status.error = "Missing argument to task";
+ return false;
+ }
+ if ( this.home == null )
+ this.home = "";
+ return true;
+ }
+
+ @Override
+ protected boolean execute()
+ {
+ TarArchiveOutputStream outArchive = null;
+ try {
+ // ldadp config
+ String ldadpConf = String.format(
+ "[%s]\n"
+ + "binddn=%s\n"
+ + "bindpw=%s\n"
+ + "base=%s\n"
+ + "port=%s\n"
+ + "home=%s\n"
+ + "\n",
+ this.server,
+ this.binddn,
+ this.bindpw,
+ this.searchbase,
+ this.proxyport,
+ this.home );
+ String fileName = "/opt/ldadp/configs/" + this.moduleid + ".cfg";
+ try {
+ Files.deleteIfExists( Paths.get( filename ) );
+ } catch ( IOException e1 ) {
+ }
+ try {
+ FileUtils.writeStringToFile( new File( fileName ), ldadpConf, StandardCharsets.UTF_8 );
+ } catch ( IOException e ) {
+ status.error = e.toString();
+ return false;
+ }
+ try {
+ outArchive = Archive.createTarArchive( this.filename );
+ } catch ( IOException e ) {
+ status.error = "Could not create archive at " + this.filename;
+ return false;
+ }
+ // Generic ldap config
+ String ldapConf = String
+ .format(
+ "URI ldap://%s:%d/\n"
+ + "BASE %s\n"
+ + "BIND_TIMELIMIT 10\n"
+ + "TIMELIMIT 30\n"
+ + "nss_base_passwd %s\n"
+ + "nss_base_group %s\n"
+ + "nss_initgroups_ignoreusers avahi,avahi-autoipd,backup,bin,colord,daemon,dnsmasq,games,gnats,hplip,irc,kernoops,libuuid,lightdm,list,lp,mail,man,messagebus,news,proxy,pulse,root,rtkit,saned,speech-dispatcher,sshd,sync,sys,syslog,usbmux,uucp,whoopsie,www-data\n",
+ this.proxyip, this.proxyport,
+ this.searchbase,
+ this.searchbase,
+ this.searchbase
+ );
+ // nslcd config
+ String nslcdConf = String
+ .format(
+ "URI ldap://%s:%d/\n"
+ + "BASE %s\n"
+ + "BIND_TIMELIMIT 10\n"
+ + "TIMELIMIT 30\n"
+ + "scope sub\n"
+ + "nss_initgroups_ignoreusers avahi,avahi-autoipd,backup,bin,colord,daemon,dnsmasq,games,gnats,hplip,irc,kernoops,libuuid,lightdm,list,lp,mail,man,messagebus,news,proxy,pulse,root,rtkit,saned,speech-dispatcher,sshd,sync,sys,syslog,usbmux,uucp,whoopsie,www-data\n",
+ this.proxyip, this.proxyport,
+ this.searchbase,
+ this.searchbase,
+ this.searchbase
+ );
+ // nsswitch.conf with ldap enabled
+ if ( !Archive.tarAddFile( outArchive, "/etc/nsswitch.conf", new File( "./data/ad/nsswitch.conf" ), 0644 ) ) {
+ status.error = "Could not add nsswitch.conf to module";
+ return false;
+ }
+ // All the pam.d common-XXXX files
+ for ( String file : new String[] { "common-auth", "common-account", "common-session", "common-session-noninteractive", "common-password" } ) {
+ if ( !Archive.tarAddFile( outArchive, "/etc/pam.d/" + file, new File( "./data/ad/" + file ), 0644 ) ) {
+ status.error = "Could not add " + file + " to module";
+ return false;
+ }
+ }
+ // Home if present
+ if ( this.home.length() != 0
+ && !Archive.tarAddFile( outArchive, "/opt/openslx/scripts/pam_script_mount_persistent", new File( "./data/ad/mountscript" ), 0644 ) ) {
+ status.error = "Could not add mount script to module";
+ return false;
+ }
+ boolean ret = Archive.tarCreateFileFromString( outArchive, "/etc/ldap.conf", ldapConf, 0644 )
+ && Archive.tarCreateFileFromString( outArchive, "/etc/nslcd.conf", nslcdConf, 0644 )
+ && Archive.tarCreateSymlink( outArchive, "/etc/ldap.conf", "/etc/ldap/ldap.conf" )
+ && Archive.tarCreateSymlink( outArchive, "/etc/ldap.conf", "/etc/openldap/ldap.conf" )
+ && Archive.tarCreateSymlink( outArchive, "../nslcd.service", "/etc/systemd/system/basic.target.wants/nslcd.service" );
+ if ( !ret ) {
+ status.error = "Could not add ldap configs to module";
+ }
+ return ret;
+ } finally {
+ Util.multiClose( outArchive );
+ }
+ }
+
+ /**
+ * Output - contains additional status data of this task
+ */
+ @SuppressWarnings( "unused" )
+ private static class Output
+ {
+ protected String error = null;
+ }
+
+}
diff --git a/src/main/java/org/openslx/taskmanager/tasks/DeleteFile.java b/src/main/java/org/openslx/taskmanager/tasks/DeleteFile.java
new file mode 100644
index 0000000..8c0f9a1
--- /dev/null
+++ b/src/main/java/org/openslx/taskmanager/tasks/DeleteFile.java
@@ -0,0 +1,57 @@
+package org.openslx.taskmanager.tasks;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+import org.apache.commons.io.FilenameUtils;
+import org.openslx.satserver.util.Util;
+import org.openslx.taskmanager.api.AbstractTask;
+
+import com.google.gson.annotations.Expose;
+
+public class DeleteFile extends AbstractTask
+{
+
+ protected static final String[] ALLOWED_DIRS =
+ { "/tmp/", "/opt/openslx/configs/" };
+
+ @Expose
+ private String file = null;
+
+ private Output status = new Output();
+
+ @Override
+ protected boolean initTask()
+ {
+ this.setStatusObject( status );
+ this.file = FilenameUtils.normalize( this.file );
+ if ( this.file == null || !Util.startsWith( this.file, ALLOWED_DIRS ) ) {
+ status.error = "File not in allowed directory";
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ protected boolean execute()
+ {
+ try {
+ Files.deleteIfExists( Paths.get( this.file ) );
+ } catch ( IOException e1 ) {
+ status.error = e1.toString();
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Output - contains additional status data of this task
+ */
+ @SuppressWarnings( "unused" )
+ private static class Output
+ {
+ protected String error = null;
+ }
+
+}
diff --git a/src/main/java/org/openslx/taskmanager/tasks/DownloadFile.java b/src/main/java/org/openslx/taskmanager/tasks/DownloadFile.java
new file mode 100644
index 0000000..d901c9f
--- /dev/null
+++ b/src/main/java/org/openslx/taskmanager/tasks/DownloadFile.java
@@ -0,0 +1,91 @@
+package org.openslx.taskmanager.tasks;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLConnection;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.FilenameUtils;
+import org.openslx.satserver.util.Util;
+import org.openslx.taskmanager.api.AbstractTask;
+
+import com.google.gson.annotations.Expose;
+
+public class DownloadFile extends AbstractTask
+{
+
+ @Expose
+ private String url = null;
+ @Expose
+ private String destination = null;
+
+ private Output status = new Output();
+
+ private static final String[] ALLOWED_DIRS =
+ { "/srv/openslx/www/boot/" };
+
+ @Override
+ protected boolean initTask()
+ {
+ this.setStatusObject( status );
+ if ( this.url == null ) {
+ status.error = "No URL given.";
+ return false;
+ }
+ this.destination = FilenameUtils.normalize( this.destination );
+ if ( this.destination == null || !Util.startsWith( this.destination, ALLOWED_DIRS ) || this.destination.endsWith( "/" ) ) {
+ status.error = "File not in allowed directory";
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ protected boolean execute()
+ {
+ URLConnection connection = null;
+ BufferedInputStream in = null;
+ FileOutputStream fout = null;
+ try {
+ File dest = new File( this.destination );
+ FileUtils.forceMkdir( new File( dest.getParent() ) );
+ FileUtils.deleteQuietly( dest );
+ connection = new URL( this.url ).openConnection();
+ in = new BufferedInputStream( connection.getInputStream() );
+ fout = new FileOutputStream( dest );
+ status.size = connection.getContentLengthLong();
+
+ final byte data[] = new byte[ 9000 ];
+ int count;
+ while ( ( count = in.read( data, 0, data.length ) ) != -1 ) {
+ fout.write( data, 0, count );
+ status.complete += count;
+ if ( status.size > 0 )
+ status.progress = (int) ( 100l * status.complete / status.size );
+ }
+ return true;
+ } catch ( IOException e ) {
+ status.error = "Download error: " + e.toString();
+ FileUtils.deleteQuietly( new File( this.destination ) );
+ return false;
+ } finally {
+ Util.multiClose( in, fout );
+ }
+ }
+
+ /**
+ * Output - contains additional status data of this task
+ */
+ @SuppressWarnings( "unused" )
+ private static class Output
+ {
+ protected String error = null;
+ protected long size = -1;
+ protected long complete = 0;
+ protected int progress = 0;
+ }
+
+}
diff --git a/src/main/java/org/openslx/taskmanager/tasks/DownloadText.java b/src/main/java/org/openslx/taskmanager/tasks/DownloadText.java
new file mode 100644
index 0000000..11b30cf
--- /dev/null
+++ b/src/main/java/org/openslx/taskmanager/tasks/DownloadText.java
@@ -0,0 +1,82 @@
+package org.openslx.taskmanager.tasks;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.nio.charset.StandardCharsets;
+
+import org.openslx.satserver.util.Util;
+import org.openslx.taskmanager.api.AbstractTask;
+
+import com.google.gson.annotations.Expose;
+
+public class DownloadText extends AbstractTask
+{
+
+ @Expose
+ private String url = null;
+
+ private Output status = new Output();
+
+ private static final long MAX_SIZE = 10000;
+
+ @Override
+ protected boolean initTask()
+ {
+ this.setStatusObject( status );
+ if ( this.url == null ) {
+ status.error = "No URL given.";
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ protected boolean execute()
+ {
+ URLConnection connection = null;
+ BufferedInputStream in = null;
+ try {
+ connection = new URL( this.url ).openConnection();
+ in = new BufferedInputStream( connection.getInputStream() );
+ status.size = connection.getContentLength();
+ if ( status.size > MAX_SIZE ) {
+ status.error = "Remote file too large: " + status.size + " bytes!";
+ return false;
+ }
+ StringBuilder sb = new StringBuilder( Math.max( 8, status.size ) );
+
+ final byte data[] = new byte[ 9000 ];
+ int count;
+ while ( ( count = in.read( data, 0, data.length ) ) != -1 ) {
+ sb.append( new String( data, 0, count, StandardCharsets.UTF_8 ) );
+ status.complete += count;
+ if ( status.complete > MAX_SIZE ) {
+ status.error = "Remote file too large: > " + status.size + " bytes!";
+ return false;
+ }
+ }
+ status.content = sb.toString();
+ return true;
+ } catch ( IOException e ) {
+ status.error = "Download error: " + e.toString();
+ return false;
+ } finally {
+ Util.multiClose( in );
+ }
+ }
+
+ /**
+ * Output - contains additional status data of this task
+ */
+ @SuppressWarnings( "unused" )
+ private static class Output
+ {
+ protected String error = null;
+ protected String content = null;
+ protected int size = -1;
+ protected int complete = 0;
+ }
+
+}
diff --git a/src/main/java/org/openslx/taskmanager/tasks/DummyTask.java b/src/main/java/org/openslx/taskmanager/tasks/DummyTask.java
new file mode 100644
index 0000000..9524f2f
--- /dev/null
+++ b/src/main/java/org/openslx/taskmanager/tasks/DummyTask.java
@@ -0,0 +1,58 @@
+package org.openslx.taskmanager.tasks;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+import org.apache.log4j.Logger;
+import org.openslx.taskmanager.api.AbstractTask;
+
+public class DummyTask extends AbstractTask
+{
+
+ private static final Logger log = Logger.getLogger( DummyTask.class );
+
+ private DummyTaskStatus status = new DummyTaskStatus();
+
+ @Override
+ protected boolean initTask()
+ {
+ log.debug( "Initialized DummyTask with ID " + this.getId() );
+ return true;
+ }
+
+ @Override
+ protected boolean execute()
+ {
+ setStatusObject( status );
+ Process process = null;
+ try {
+ // Create process
+ process = Runtime.getRuntime().exec( "dummy", null, new File("/") );
+
+ // Read its output
+ BufferedReader reader = new BufferedReader( new InputStreamReader( process.getInputStream() ) );
+ String line;
+ while ( ( line = reader.readLine() ) != null ) {
+ line = reader.readLine();
+ if ( line.matches( "^\\d+%$" ) )
+ this.status.progress = Integer.parseInt( line.substring( 0, line.length() - 1 ) );
+ }
+ } catch ( IOException e ) {
+ log.warn( "Process of task " + this.getId() + " died." );
+ } finally {
+ if ( process != null )
+ process.destroy();
+ }
+ return this.status.progress == 100;
+ }
+
+ class DummyTaskStatus
+ {
+
+ protected int progress = 0;
+
+ }
+
+}
diff --git a/src/main/java/org/openslx/taskmanager/tasks/LdadpLauncher.java b/src/main/java/org/openslx/taskmanager/tasks/LdadpLauncher.java
new file mode 100644
index 0000000..123ab1a
--- /dev/null
+++ b/src/main/java/org/openslx/taskmanager/tasks/LdadpLauncher.java
@@ -0,0 +1,79 @@
+package org.openslx.taskmanager.tasks;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.openslx.satserver.util.Constants;
+import org.openslx.taskmanager.api.SystemCommandTask;
+
+import com.google.gson.annotations.Expose;
+
+public class LdadpLauncher extends SystemCommandTask
+{
+ @Expose
+ private int[] ids = null;
+
+ private Output status = new Output();
+
+ @Override
+ protected boolean initTask()
+ {
+ this.setStatusObject( this.status );
+ if ( ids == null ) {
+ status.addMessage( "No ids passed to task." );
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ protected String[] initCommandLine()
+ {
+ List<String> args = new ArrayList<>();
+ args.addAll( Arrays.asList( new String[] {
+ "/usr/bin/sudo",
+ "-n",
+ "-u", "ldadp", Constants.BASEDIR + "/scripts/ldadp-launcher",
+ "/opt/ldadp",
+ "--"
+ } ) );
+ for ( int i = 0; i < ids.length; ++i ) {
+ args.add( Integer.toString( this.ids[i] ) );
+ }
+ return args.toArray( new String[ args.size() ] );
+ }
+
+ @Override
+ protected boolean processEnded( int exitCode )
+ {
+ return exitCode == 0;
+ }
+
+ @Override
+ protected void processStdOut( String line )
+ {
+ status.addMessage( line );
+ }
+
+ @Override
+ protected void processStdErr( String line )
+ {
+ status.addMessage( line );
+ }
+
+ class Output
+ {
+ private String messages = null;
+
+ private void addMessage( String str )
+ {
+ if ( messages == null ) {
+ messages = str;
+ } else {
+ messages += "\n" + str;
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/org/openslx/taskmanager/tasks/LdapSearch.java b/src/main/java/org/openslx/taskmanager/tasks/LdapSearch.java
new file mode 100644
index 0000000..f861b6a
--- /dev/null
+++ b/src/main/java/org/openslx/taskmanager/tasks/LdapSearch.java
@@ -0,0 +1,122 @@
+package org.openslx.taskmanager.tasks;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Random;
+
+import org.apache.commons.io.FileUtils;
+import org.openslx.taskmanager.api.SystemCommandTask;
+
+import com.google.gson.annotations.Expose;
+
+public class LdapSearch extends SystemCommandTask
+{
+
+ @Expose
+ private String server = null;
+ @Expose
+ private String searchbase = null;
+ @Expose
+ private String binddn = null;
+ @Expose
+ private String bindpw = null;
+
+ private String fifo = null;
+
+ private volatile int userCount = 0;
+
+ private Output status = new Output();
+
+ @Override
+ protected boolean initTask()
+ {
+ this.setStatusObject( this.status );
+ if ( this.server == null || this.searchbase == null || this.binddn == null ) {
+ status.messages = "Missing parameter";
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ protected String[] initCommandLine()
+ {
+ if ( this.bindpw == null )
+ this.bindpw = "";
+ this.fifo = String.format( "/tmp/bwlp-%s-%s.ldap", System.currentTimeMillis(), new Random().nextInt() );
+ File pwFile = new File( this.fifo );
+ FileUtils.deleteQuietly( pwFile );
+ try {
+ pwFile.createNewFile();
+ pwFile.setReadable( false, false );
+ pwFile.setReadable( true, true );
+ FileUtils.writeStringToFile( pwFile, this.bindpw, StandardCharsets.UTF_8 );
+ } catch ( IOException e ) {
+ FileUtils.deleteQuietly( pwFile );
+ status.messages = e.toString();
+ return null;
+ }
+ status.addMessage( "Trying to find 4 random AD users to verify everything is all right..." );
+
+ return new String[] {
+ "ldapsearch",
+ "-x", // Simple auth
+ "-LLL", // No additional stuff
+ "-y", this.fifo, // Password from file
+ "-H", "ldap://" + this.server + ":3268/", // Host
+ "-b", this.searchbase, // SB
+ "-D", this.binddn, // DN
+ "-l", "4", // Time limit in seconds
+ "-z", "4", // Max number of results
+ "-o", "ldif-wrap=no", // Turn off retarded line wrapping done by ldapsearch
+ "(&(objectClass=user)(objectClass=person)(sAMAccountName=*))",
+ "sAMAccountName" // Only one attribute
+ };
+ }
+
+ @Override
+ protected boolean processEnded( int exitCode )
+ {
+ FileUtils.deleteQuietly( new File( this.fifo ) );
+ if ( exitCode == 4 ) // Means size limit exceeded, ignore
+ exitCode = 0;
+ if ( exitCode != 0 )
+ status.addMessage( "Exit code is " + exitCode );
+ if ( exitCode == 0 && this.userCount < 4 )
+ status.addMessage( "Found less than 4 users. Are you sure you got the right credentials." );
+ return this.userCount >= 4;
+ }
+
+ @Override
+ protected void processStdOut( String line )
+ {
+ if ( line.contains( "sAMAccountName: " ) ) {
+ status.addMessage( "Found AD user " + line.substring( 16 ) + " :-)" );
+ this.userCount++;
+ }
+ }
+
+ @Override
+ protected void processStdErr( String line )
+ {
+ if ( line.contains( "Size limit exceeded" ) )
+ return;
+ status.addMessage( "Error: " + line );
+ }
+
+ class Output
+ {
+ private String messages = null;
+
+ private void addMessage( String str )
+ {
+ if ( messages == null ) {
+ messages = str;
+ } else {
+ messages += "\n" + str;
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/org/openslx/taskmanager/tasks/LinkConfigTgz.java b/src/main/java/org/openslx/taskmanager/tasks/LinkConfigTgz.java
new file mode 100644
index 0000000..686cb9b
--- /dev/null
+++ b/src/main/java/org/openslx/taskmanager/tasks/LinkConfigTgz.java
@@ -0,0 +1,61 @@
+package org.openslx.taskmanager.tasks;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+import org.apache.commons.io.FilenameUtils;
+import org.openslx.satserver.util.Util;
+import org.openslx.taskmanager.api.AbstractTask;
+
+import com.google.gson.annotations.Expose;
+
+public class LinkConfigTgz extends AbstractTask
+{
+
+ protected static final String[] ALLOWED_DIRS =
+ { "/tmp/", "/opt/openslx/configs/" };
+
+ @Expose
+ private String destination = null;
+
+ private Output status = new Output();
+
+ @Override
+ protected boolean initTask()
+ {
+ this.setStatusObject( status );
+ this.destination = FilenameUtils.normalize( this.destination );
+ if ( this.destination == null || !Util.startsWith( this.destination, ALLOWED_DIRS ) ) {
+ status.error = "File not in allowed directory";
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ protected boolean execute()
+ {
+ try {
+ Files.deleteIfExists( Paths.get( "/srv/openslx/www/boot/default/config.tgz" ) );
+ } catch ( IOException e1 ) {
+ }
+ try {
+ Files.createSymbolicLink( Paths.get( "/srv/openslx/www/boot/default/config.tgz" ), Paths.get( this.destination ) );
+ } catch ( IOException e ) {
+ status.error = e.toString();
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Output - contains additional status data of this task
+ */
+ @SuppressWarnings( "unused" )
+ private static class Output
+ {
+ protected String error = null;
+ }
+
+}
diff --git a/src/main/java/org/openslx/taskmanager/tasks/ListArchive.java b/src/main/java/org/openslx/taskmanager/tasks/ListArchive.java
new file mode 100644
index 0000000..33bb83a
--- /dev/null
+++ b/src/main/java/org/openslx/taskmanager/tasks/ListArchive.java
@@ -0,0 +1,116 @@
+package org.openslx.taskmanager.tasks;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.compress.archivers.ArchiveEntry;
+import org.apache.commons.compress.archivers.ArchiveException;
+import org.apache.commons.compress.archivers.ArchiveInputStream;
+import org.apache.commons.io.FilenameUtils;
+import org.openslx.satserver.util.Archive;
+import org.openslx.satserver.util.Util;
+import org.openslx.taskmanager.api.AbstractTask;
+
+import com.google.gson.annotations.Expose;
+
+public class ListArchive extends AbstractTask
+{
+
+ @Expose
+ private String file;
+
+ /*
+ * Own vars/constants not being deserialized
+ */
+
+ protected static final String[] ALLOWED_DIRS =
+ { "/tmp/", "/opt/openslx/" };
+
+ private Output status = new Output();
+
+ /*
+ * Code
+ */
+
+ @Override
+ protected boolean execute()
+ {
+ ArchiveInputStream archive;
+ try {
+ archive = Archive.getArchiveInputStream( this.file );
+ } catch ( FileNotFoundException e1 ) {
+ status.error = e1.getMessage();
+ status.errorCode = Output.ErrorCode.NOT_FOUND;
+ return false;
+ } catch ( IllegalArgumentException e1 ) {
+ status.error = e1.getMessage();
+ status.errorCode = Output.ErrorCode.UNKNOWN_ERROR;
+ return false;
+ } catch ( ArchiveException e1 ) {
+ status.error = e1.getMessage();
+ status.errorCode = Output.ErrorCode.UNKNOWN_FORMAT;
+ return false;
+ }
+
+ status.entries = new ArrayList<>();
+
+ ArchiveEntry archiveEntry;
+ try {
+ // Iterate over every entry
+ while ( ( archiveEntry = archive.getNextEntry() ) != null ) {
+ if ( !archive.canReadEntryData( archiveEntry ) )
+ continue;
+ Output.Entry entry = new Output.Entry();
+ entry.name = archiveEntry.getName();
+ entry.isdir = archiveEntry.isDirectory();
+ entry.size = archiveEntry.getSize();
+ status.entries.add( entry );
+ }
+ } catch ( IOException e ) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ protected boolean initTask()
+ {
+ this.setStatusObject( this.status );
+ if ( this.file == null || this.file.length() == 0 || this.file.charAt( 0 ) != '/' ) {
+ status.error = "Need absolute path.";
+ status.errorCode = Output.ErrorCode.INVALID_DIRECTORY;
+ return false;
+ }
+ this.file = FilenameUtils.normalize( this.file );
+ if ( this.file == null || !Util.startsWith( this.file, ALLOWED_DIRS ) ) {
+ status.error = "File not in allowed directory";
+ status.errorCode = Output.ErrorCode.INVALID_DIRECTORY;
+ return false;
+ }
+ return true;
+ }
+
+ @SuppressWarnings( "unused" )
+ private static class Output
+ {
+ protected String error = null;
+
+ protected enum ErrorCode
+ {
+ NOT_FOUND, UNKNOWN_FORMAT, UNKNOWN_ERROR, INVALID_DIRECTORY
+ };
+
+ protected ErrorCode errorCode = null;
+ protected List<Entry> entries = null;
+
+ public static class Entry
+ {
+ protected String name = null;
+ protected boolean isdir = false;
+ protected long size = -1;
+ }
+ }
+
+}
diff --git a/src/main/java/org/openslx/taskmanager/tasks/LocalAddressesList.java b/src/main/java/org/openslx/taskmanager/tasks/LocalAddressesList.java
new file mode 100644
index 0000000..2bfb20e
--- /dev/null
+++ b/src/main/java/org/openslx/taskmanager/tasks/LocalAddressesList.java
@@ -0,0 +1,76 @@
+package org.openslx.taskmanager.tasks;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.openslx.taskmanager.api.AbstractTask;
+
+public class LocalAddressesList extends AbstractTask
+{
+ private static final Logger LOG = Logger.getLogger( LocalAddressesList.class );
+
+ private Output status = new Output();
+
+ @Override
+ protected boolean initTask()
+ {
+ return true;
+ }
+
+ @Override
+ protected boolean execute()
+ {
+ this.setStatusObject( status );
+ List<Output.Entry> list = new ArrayList<>();
+ try {
+ for ( Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements(); ) {
+ NetworkInterface intf = en.nextElement();
+ for ( Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements(); ) {
+ InetAddress addr = enumIpAddr.nextElement();
+ Output.Entry entry = new Output.Entry();
+ entry.iface = intf.getName();
+ entry.ip = addr.getHostAddress();
+ if ( addr instanceof Inet4Address ) {
+ entry.type = "ipv4";
+ } else if ( addr instanceof Inet6Address ) {
+ entry.type = "ipv6";
+ } else {
+ entry.type = addr.getClass().getSimpleName();
+ }
+ list.add( entry );
+ }
+ }
+ } catch ( SocketException e ) {
+ LOG.info( " (error retrieving network interface list)" );
+ this.status.error = "Error retrieving network interface list (" + e.getMessage() + ")";
+ return false;
+ }
+ this.status.addresses = list;
+ return true;
+ }
+
+ /**
+ * Output - contains additional status data of this task
+ */
+ @SuppressWarnings( "unused" )
+ private static class Output
+ {
+ protected String error = null;
+ protected List<Entry> addresses = null;
+
+ protected static class Entry
+ {
+ protected String ip = null;
+ protected String iface = null;
+ protected String type = null;
+ }
+ }
+
+}
diff --git a/src/main/java/org/openslx/taskmanager/tasks/LsTask.java b/src/main/java/org/openslx/taskmanager/tasks/LsTask.java
new file mode 100644
index 0000000..2e01167
--- /dev/null
+++ b/src/main/java/org/openslx/taskmanager/tasks/LsTask.java
@@ -0,0 +1,80 @@
+package org.openslx.taskmanager.tasks;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.openslx.taskmanager.api.SystemCommandTask;
+
+import com.google.gson.annotations.Expose;
+
+public class LsTask extends SystemCommandTask
+{
+
+ /*
+ * Serialize
+ */
+ @Expose
+ private String directory = null;
+
+ /*
+ * Private, no serialization
+ */
+ private Output status = new Output();
+ private List<String> files = new ArrayList<>();
+
+ @Override
+ protected boolean initTask()
+ {
+ if ( directory == null ) {
+ status.error ="Missing directory argument";
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ protected String[] initCommandLine()
+ {
+ return new String[] { "/bin/ls", directory };
+ }
+
+ @Override
+ protected void processStarted()
+ {
+ setStatusObject( status );
+ }
+
+ @Override
+ protected boolean processEnded( int exitCode )
+ {
+ if ( exitCode != 0 ) {
+ status.files = null;
+ return false;
+ }
+ status.files = this.files;
+ return true;
+ }
+
+ @Override
+ protected void processStdOut( String line )
+ {
+ this.files.add( line );
+ }
+
+ @Override
+ protected void processStdErr( String line )
+ {
+ status.error = line;
+ }
+
+ /**
+ * Output - contains additional status data of this task
+ */
+ @SuppressWarnings( "unused" )
+ private static class Output
+ {
+ protected String error = null;
+ protected List<String> files = null;
+ }
+
+}
diff --git a/src/main/java/org/openslx/taskmanager/tasks/MountVmStore.java b/src/main/java/org/openslx/taskmanager/tasks/MountVmStore.java
new file mode 100644
index 0000000..a77387d
--- /dev/null
+++ b/src/main/java/org/openslx/taskmanager/tasks/MountVmStore.java
@@ -0,0 +1,84 @@
+package org.openslx.taskmanager.tasks;
+
+import org.openslx.satserver.util.Constants;
+import org.openslx.taskmanager.api.SystemCommandTask;
+
+import com.google.gson.annotations.Expose;
+
+public class MountVmStore extends SystemCommandTask
+{
+ @Expose
+ private String address = null;
+ @Expose
+ private String type = null;
+ @Expose
+ private String username = null;
+ @Expose
+ private String password = null;
+
+ private Output status = new Output();
+
+ @Override
+ protected boolean initTask()
+ {
+ this.setStatusObject( this.status );
+ if ( this.address == null || this.type == null ) {
+ status.addMessage( "Address or type not given." );
+ return false;
+ }
+ if ( this.username == null )
+ this.username = "";
+ if ( this.password == null )
+ this.password = "";
+ return true;
+ }
+
+ @Override
+ protected String[] initCommandLine()
+ {
+ return new String[] {
+ "/usr/bin/sudo",
+ "-n",
+ "-u", "root", Constants.BASEDIR + "/scripts/mount-store",
+ "images",
+ this.address,
+ this.username,
+ this.password
+ };
+ }
+
+ @Override
+ protected boolean processEnded( int exitCode )
+ {
+ if ( exitCode != 0 )
+ status.addMessage( "Failed with exit code " + exitCode );
+ return exitCode == 0;
+ }
+
+ @Override
+ protected void processStdOut( String line )
+ {
+ status.addMessage( line );
+ }
+
+ @Override
+ protected void processStdErr( String line )
+ {
+ status.addMessage( line );
+ }
+
+ class Output
+ {
+ private String messages = null;
+
+ private void addMessage( String str )
+ {
+ if ( messages == null ) {
+ messages = str;
+ } else {
+ messages += "\n" + str;
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/org/openslx/taskmanager/tasks/RecompressArchive.java b/src/main/java/org/openslx/taskmanager/tasks/RecompressArchive.java
new file mode 100644
index 0000000..d428f03
--- /dev/null
+++ b/src/main/java/org/openslx/taskmanager/tasks/RecompressArchive.java
@@ -0,0 +1,193 @@
+package org.openslx.taskmanager.tasks;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.commons.compress.archivers.ArchiveEntry;
+import org.apache.commons.compress.archivers.ArchiveException;
+import org.apache.commons.compress.archivers.ArchiveInputStream;
+import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
+import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.FilenameUtils;
+import org.openslx.satserver.util.Archive;
+import org.openslx.satserver.util.Util;
+import org.openslx.taskmanager.api.AbstractTask;
+
+import com.google.gson.annotations.Expose;
+
+public class RecompressArchive extends AbstractTask
+{
+
+ @Expose
+ private String[] inputFiles;
+
+ @Expose
+ private String outputFile;
+
+ /*
+ * Own vars/constants not being deserialized
+ */
+
+ protected static final String[] ALLOWED_DIRS =
+ { "/tmp/", "/opt/openslx/configs/" };
+
+ private Output status = new Output();
+
+ /*
+ * Code
+ */
+
+ @Override
+ protected boolean execute()
+ {
+ if ( execute2() ) {
+ return true;
+ }
+ FileUtils.deleteQuietly( new File( this.outputFile ) );
+ return false;
+ }
+
+ private boolean execute2()
+ {
+ // Open output file archive
+ TarArchiveOutputStream outArchive = null;
+ try {
+ try {
+ FileUtils.forceMkdir( new File( new File( this.outputFile ).getParent() ) );
+ outArchive = Archive.createTarArchive( this.outputFile );
+ } catch ( IOException e2 ) {
+ status.error = e2.getMessage();
+ status.errorCode = Output.ErrorCode.WRITE_FAILED;
+ return false;
+ }
+
+ // Open input file archives, one by one
+ for ( final String inputFile : this.inputFiles ) {
+ ArchiveInputStream inArchive = null;
+ try {
+ try {
+ inArchive = Archive.getArchiveInputStream( inputFile );
+ } catch ( FileNotFoundException e1 ) {
+ status.error = "(" + inputFile + ") " + e1.getMessage();
+ status.errorCode = Output.ErrorCode.NOT_FOUND;
+ return false;
+ } catch ( IllegalArgumentException e1 ) {
+ status.error = "(" + inputFile + ") " + e1.getMessage();
+ status.errorCode = Output.ErrorCode.UNKNOWN_ERROR;
+ return false;
+ } catch ( ArchiveException e1 ) {
+ status.error = "(" + inputFile + ") " + e1.getMessage();
+ status.errorCode = Output.ErrorCode.UNKNOWN_FORMAT;
+ return false;
+ }
+ // It's open
+ ArchiveEntry inEntry;
+
+ // Remember all files added, so we can issue warnings for duplicate entries
+ Set<String> entries = new HashSet<>();
+ // Iterate over every entry
+ while ( ( inEntry = inArchive.getNextEntry() ) != null ) {
+ if ( !inArchive.canReadEntryData( inEntry ) )
+ continue; // Skip unreadable entries
+ // Construct TarArchiveEntry - we want unix stuff like uid/gid, links, file/dir mode, so try to get from source archive
+ TarArchiveEntry outEntry = Archive.createTarArchiveEntry( inEntry, 0, 0, 0755, 0644 );
+ // Ask lib if the entry can be written
+ if ( !outArchive.canWriteEntryData( outEntry ) ) {
+ status.warnings.add( "Commons-compress says entry '" + outEntry.getName() + "' cannot be written." );
+ continue;
+ }
+ // Dupcheck
+ if ( entries.contains( outEntry.getName() ) ) {
+ status.warnings.add( "Duplicate entry: " + outEntry.getName() );
+ } else {
+ entries.add( outEntry.getName() );
+ }
+ // Actually add entry now
+ outArchive.putArchiveEntry( outEntry );
+ if ( !Util.streamCopy( inArchive, outArchive, outEntry.getSize() ) ) {
+ status.error = "(" + inputFile + ") Could not copy entry '" + inEntry.getName() + "' to destination archive.";
+ status.errorCode = Output.ErrorCode.WRITE_FAILED;
+ return false;
+ }
+ outArchive.closeArchiveEntry();
+ } // End entry
+ } catch ( IOException e ) {
+ return false;
+ } finally {
+ Util.multiClose( inArchive );
+ }
+ } // End of loop over input archives
+
+ return true;
+ } finally {
+ Util.multiClose( outArchive );
+ }
+ }
+
+ @Override
+ protected boolean initTask()
+ {
+ this.setStatusObject( this.status );
+ // Input files
+ if ( this.inputFiles == null ) {
+ status.error = "Input: No archives given";
+ status.errorCode = Output.ErrorCode.UNKNOWN_ERROR;
+ return false;
+ }
+ for ( int i = 0; i < this.inputFiles.length; ++i ) {
+ String inputFile = this.inputFiles[i];
+ if ( inputFile == null || inputFile.length() == 0 || inputFile.charAt( 0 ) != '/' ) {
+ status.error = "Input: Need absolute path (got: " + inputFile + ")";
+ status.errorCode = Output.ErrorCode.INVALID_DIRECTORY;
+ return false;
+ }
+ inputFile = FilenameUtils.normalize( inputFile );
+ if ( inputFile == null || !Util.startsWith( inputFile, ALLOWED_DIRS ) ) {
+ status.error = "Input: File not in allowed directory";
+ status.errorCode = Output.ErrorCode.INVALID_DIRECTORY;
+ return false;
+ }
+ this.inputFiles[i] = inputFile;
+ }
+ // Output file
+ if ( this.outputFile == null || this.outputFile.length() == 0 || this.outputFile.charAt( 0 ) != '/' ) {
+ status.error = "Output: Need absolute path.";
+ status.errorCode = Output.ErrorCode.INVALID_DIRECTORY;
+ return false;
+ }
+ this.outputFile = FilenameUtils.normalize( this.outputFile );
+ if ( !Util.startsWith( this.outputFile, ALLOWED_DIRS ) ) {
+ status.error = "Output: File not in allowed directory";
+ status.errorCode = Output.ErrorCode.INVALID_DIRECTORY;
+ return false;
+ }
+ return true;
+ }
+
+ @SuppressWarnings( "unused" )
+ private static class Output
+ {
+ protected String error = null;
+
+ protected enum ErrorCode
+ {
+ NOT_FOUND, UNKNOWN_FORMAT, UNKNOWN_ERROR, INVALID_DIRECTORY, WRITE_FAILED
+ };
+
+ protected ErrorCode errorCode = null;
+ protected List<String> warnings = null;
+
+ public static class Entry
+ {
+ protected String name = null;
+ protected boolean isdir = false;
+ protected long size = -1;
+ }
+ }
+
+}