├── lib ├── derby.jar └── Derby_Apache_2.0_License.txt ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitignore ├── .travis.yml ├── tools ├── run.sh ├── exclude.lst ├── ReadMe.txt └── CloneFilesystem.java ├── sql └── filesystem.sql ├── src └── main │ └── java │ └── me │ └── dilley │ ├── commands │ ├── Command.java │ └── Uname.java │ ├── Streams.java │ ├── Shell.java │ ├── Limits.java │ ├── Filesystem.java │ ├── Defaults.java │ ├── Network.java │ ├── services │ ├── DiscardServer.java │ ├── DaytimeServer.java │ ├── EchoServer.java │ ├── TimeServer.java │ ├── TelnetServer.java │ ├── HttpServer.java │ └── ChargenServer.java │ ├── OperatingSystem.java │ ├── Log.java │ ├── SystemCalls.java │ ├── User.java │ ├── TcpServer.java │ ├── UdpServer.java │ ├── Node.java │ ├── Database.java │ ├── File.java │ ├── Errors.java │ ├── Linulator.java │ └── Config.java ├── cfg ├── linulator.properties └── linulator.policy ├── pom.xml ├── gradlew.bat ├── gradlew └── README.md /lib/derby.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ldilley/linulator/HEAD/lib/derby.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ldilley/linulator/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Java class files 2 | *.class 3 | 4 | # Generated files 5 | .gradle/ 6 | build/ 7 | dist/ 8 | jdocs/ 9 | linulator/ 10 | log/ 11 | 12 | # Log files 13 | *.log 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | 3 | jdk: 4 | - oraclejdk7 5 | - openjdk7 6 | - oraclejdk8 7 | 8 | notifications: 9 | email: false 10 | irc: 11 | - "irc.dilley.me#sysadmininkudzu" 12 | - "chat.freenode.net#linulator" 13 | - "irc.oftc.net#linulator" 14 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed May 31 23:40:25 EDT 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.5-bin.zip 7 | -------------------------------------------------------------------------------- /tools/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | DERBY_JAR="../lib/derby.jar" 4 | 5 | if [ ! -f CloneFilesystem.class ]; then 6 | javac CloneFilesystem.java 7 | fi 8 | 9 | if [ ! -f $DERBY_JAR ]; then 10 | printf "${DERBY_JAR} not found! Please set the \$DERBY_JAR variable in run.sh appropriately.\n" 11 | exit 1 12 | fi 13 | 14 | java -cp .:$DERBY_JAR CloneFilesystem -c 15 | -------------------------------------------------------------------------------- /sql/filesystem.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE filesystem 2 | (id BIGINT NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1), 3 | path VARCHAR(4096) NOT NULL, 4 | name VARCHAR(255) NOT NULL, 5 | inode BIGINT NOT NULL, 6 | type SMALLINT NOT NULL, 7 | mode SMALLINT NOT NULL, 8 | linkcount INT NOT NULL DEFAULT 1, 9 | uid INT NOT NULL, 10 | gid INT NOT NULL, 11 | atime BIGINT NOT NULL, 12 | ctime BIGINT NOT NULL, 13 | mtime BIGINT NOT NULL, 14 | link VARCHAR(4096) DEFAULT NULL, 15 | bindata BLOB DEFAULT NULL, 16 | txtdata CLOB DEFAULT NULL, 17 | PRIMARY KEY(id)); 18 | -------------------------------------------------------------------------------- /src/main/java/me/dilley/commands/Command.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Linulator - The Linux Simulator 3 | * Copyright (C) 2014 Lloyd Dilley 4 | * http://www.linulator.org/ 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | 21 | package me.dilley.commands; 22 | 23 | public abstract class Command 24 | { 25 | protected String result; 26 | 27 | public abstract String execute(String[] args); 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/me/dilley/Streams.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Linulator - The Linux Simulator 3 | * Copyright (C) 2014 Lloyd Dilley 4 | * http://www.linulator.org/ 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | 21 | package me.dilley; 22 | 23 | class Streams 24 | { 25 | public static final byte STDIN = 0; // System.in 26 | public static final byte STDOUT = 1; // System.out 27 | public static final byte STDERR = 2; // System.err 28 | } 29 | -------------------------------------------------------------------------------- /tools/exclude.lst: -------------------------------------------------------------------------------- 1 | # Add the Linulator tools path here to avoid importing this file, CloneFilesystem.*, derby.jar, etc. 2 | #/path/to/linulator/tools 3 | 4 | # If using LVM, set your volume group and logical volume paths below. Otherwise, the import will hang. 5 | # Below is a sample. 6 | #/dev/vg_root/*.* 7 | 8 | # These files cause a hang 9 | /dev/block/*.* 10 | /dev/char/*.* 11 | /dev/console 12 | /dev/disk/*.* 13 | /dev/dm-[0-9]+ 14 | /dev/hd[a-z]?[0-9]* 15 | /dev/input/*.* 16 | /dev/mapper/*.* 17 | /dev/port 18 | /dev/ptmx 19 | /dev/pts/*.* 20 | /dev/random 21 | /dev/root 22 | /dev/rtc[0-9]? 23 | /dev/sd[a-z]?[0-9]* 24 | /dev/sg[0-9]+ 25 | /dev/stderr 26 | /dev/stdin 27 | /dev/stdout 28 | /dev/systty 29 | /dev/tty[0-9]* 30 | /dev/ttyS[0-9]+ 31 | /dev/urandom 32 | /dev/usbmon[0-9]? 33 | /dev/vga_arbiter 34 | /proc/acpi/event 35 | /proc/kallsyms 36 | /proc/kmsg 37 | /usr/lib/locale/locale-archive 38 | /var/spool/postfix/public/pickup 39 | /var/spool/postfix/public/qmgr 40 | 41 | # These files are not readable 42 | /proc/sys/net/ipv4/route/flush 43 | /proc/sys/net/ipv6/route/flush 44 | /proc/sys/vm/compact_memory 45 | 46 | # Ignore JDK and JRE due to size 47 | /usr/lib/jvm/*.* 48 | 49 | # These files are too large to read (128T on 64-bit systems) 50 | /dev/core 51 | /proc/kcore 52 | 53 | # Don't import any running process data; Linulator will manage this later 54 | /proc/[0-9]+/*.* 55 | -------------------------------------------------------------------------------- /src/main/java/me/dilley/Shell.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Linulator - The Linux Simulator 3 | * Copyright (C) 2014 Lloyd Dilley 4 | * http://www.linulator.org/ 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | 21 | package me.dilley; 22 | 23 | import me.dilley.commands.*; 24 | 25 | import java.util.HashMap; 26 | 27 | public class Shell 28 | { 29 | private static HashMap hashMap = new HashMap(); 30 | 31 | public static void populateCommands() 32 | { 33 | Uname uname = new Uname(); 34 | hashMap.put("uname", uname); 35 | } 36 | 37 | public static String execute(String[] args) 38 | { 39 | String result = null; 40 | 41 | Command command = hashMap.get(args[0]); 42 | 43 | if(command != null) 44 | result = command.execute(args); 45 | 46 | return result; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/me/dilley/Limits.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Linulator - The Linux Simulator 3 | * Copyright (C) 2014 Lloyd Dilley 4 | * http://www.linulator.org/ 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | 21 | package me.dilley; 22 | 23 | class Limits 24 | { 25 | public static final int MIN_PORT = 0; 26 | public static final int MAX_PORT = 65535; 27 | 28 | public static final byte MIN_PROCESSORS = 1; 29 | public static final byte MAX_PROCESSORS = 16; 30 | 31 | public static final int MIN_MEMORY = 1024; 32 | public static final int MAX_MEMORY = 32768; 33 | 34 | public static final short MAX_HOST_NAME_LENGTH = 255; // returned by "getconf HOST_NAME_MAX" 35 | 36 | public static final byte MAX_USER_NAME_LENGTH = 32; 37 | public static final byte MAX_PASSWORD_LENGTH = 32; 38 | 39 | public static final short MAX_SMALL_SERVER_PACKET_SIZE = 2048; 40 | } 41 | -------------------------------------------------------------------------------- /cfg/linulator.properties: -------------------------------------------------------------------------------- 1 | # IP address or hostname to use for network connections 2 | # Use all available interfaces by default if empty or commented 3 | listen_address= 4 | 5 | # Comment any of the services below to disable them. 6 | # You must run this program as root if any port numbers are <1024. 7 | # You can also use iptables/ipfw to redirect privileged ports to unprivileged ports. 8 | # It is recommended to keep the echo, discard, daytime, chargen, and time services 9 | # disabled. 10 | # SSH is not implemented yet. 11 | #echo_port=10007 12 | #discard_port=10009 13 | #daytime_port=10013 14 | #chargen_port=10019 15 | #time_port=10037 16 | #ftp_port=10021 17 | #ssh_port=10022 18 | telnet_port=10023 19 | #smtp_port=10025 20 | #dns_port=10059 21 | #http_port=10080 22 | #ntp_port=10123 23 | 24 | # Name of the host with optional domain name 25 | # The domain portion will be used in /etc/resolv.conf if appended. 26 | host_name=mirage.spoof.dom 27 | 28 | # Password for root account 29 | root_password=secret 30 | 31 | # Fake Linux distribution 32 | # Options are: 33 | # centos5 34 | # centos6 35 | # debian7 36 | # rhel5 37 | # rhel6 38 | # sles11 39 | fake_distro=centos5 40 | 41 | # Number of fake CPUs (keep it <=16) 42 | fake_processors=8 43 | 44 | # Amount of fake RAM in megabytes (keep it <=32768) 45 | fake_memory=8192 46 | 47 | # Hide console sessions from network users 48 | hide_console=false 49 | 50 | # Every time Linulator starts, a virtual filesystem is created in memory from the 51 | # database contents. If this option is set to true, only the virtual filesystem in 52 | # memory is mutable. This is typically desired so nobody can modify the filesystem 53 | # baseline. If you allow database writes, you may want to create a backup of the 54 | # database. Set this to false to allow the database to be modified. You can also 55 | # use Derby's "ij" to modify the filesystem table data if desired. 56 | readonly_database=true 57 | 58 | # Debug mode (extra logging) 59 | debug_mode=true 60 | -------------------------------------------------------------------------------- /src/main/java/me/dilley/Filesystem.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Linulator - The Linux Simulator 3 | * Copyright (C) 2014 Lloyd Dilley 4 | * http://www.linulator.org/ 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | 21 | package me.dilley; 22 | 23 | import java.math.BigInteger; 24 | 25 | /* ToDo: This class needs to be thread safe since Linulator is a multi-user environment 26 | and more than one user may be modifying the filesystem. */ 27 | class Filesystem 28 | { 29 | static long nextFreeInode; // gets set where it left off once filesystem data is loaded from Derby database 30 | static Node root; // filesystem root (/) 31 | 32 | private String mountPoint; 33 | private BigInteger size; // total filesystem size in bytes 34 | 35 | public Filesystem(Node root) 36 | { 37 | this.root = root; 38 | } 39 | 40 | public void add(File file) 41 | { 42 | root.add(file); 43 | } 44 | 45 | public String getMountPoint() 46 | { 47 | return mountPoint; 48 | } 49 | 50 | public void setMountPoint(String mountPoint) 51 | { 52 | this.mountPoint = mountPoint; 53 | } 54 | 55 | public BigInteger getSize() 56 | { 57 | return size; 58 | } 59 | 60 | public void setSize(BigInteger size) 61 | { 62 | this.size = size; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | me.dilley.linulator 4 | linulator 5 | 0.1.0 6 | 7 | yyyyMMdd 8 | 9 | 10 | 11 | 12 | org.apache.maven.plugins 13 | maven-compiler-plugin 14 | 3.6.1 15 | 16 | 1.7 17 | 1.7 18 | 19 | 20 | 21 | org.apache.maven.plugins 22 | maven-javadoc-plugin 23 | 2.10.4 24 | 25 | 26 | org.apache.maven.plugins 27 | maven-jar-plugin 28 | 3.0.2 29 | 30 | 31 | 32 | ${jdk.version} 33 | ${project.name} 34 | ${project.version} 35 | ${project.name} Library 36 | ${project.version} 37 | lib/derby.jar 38 | 39 | 40 | true 41 | me.dilley.Linulator 42 | lib/ 43 | 44 | 45 | ${project.artifactId}-${project.version}-${maven.build.timestamp} 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /src/main/java/me/dilley/Defaults.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Linulator - The Linux Simulator 3 | * Copyright (C) 2014 Lloyd Dilley 4 | * http://www.linulator.org/ 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | 21 | package me.dilley; 22 | 23 | class Defaults 24 | { 25 | public static final int DEFAULT_ECHO_PORT = 7; 26 | public static final int DEFAULT_DISCARD_PORT = 9; 27 | public static final int DEFAULT_DAYTIME_PORT = 13; 28 | public static final int DEFAULT_CHARGEN_PORT = 19; 29 | public static final int DEFAULT_TIME_PORT = 37; 30 | public static final int DEFAULT_FTP_PORT = 21; 31 | public static final int DEFAULT_SSH_PORT = 22; 32 | public static final int DEFAULT_TELNET_PORT = 23; 33 | public static final int DEFAULT_SMTP_PORT = 25; 34 | public static final int DEFAULT_DNS_PORT = 59; 35 | public static final int DEFAULT_HTTP_PORT = 80; 36 | public static final int DEFAULT_NTP_PORT = 123; 37 | public static final byte DEFAULT_PROCESSORS = 8; 38 | public static final int DEFAULT_MEMORY = 8192; 39 | public static final String DEFAULT_LISTEN_ADDRESS = "*"; 40 | public static final String DEFAULT_HOST_NAME = "mirage"; 41 | public static final String DEFAULT_DOMAIN_NAME = "spoof.dom"; 42 | public static final String DEFAULT_ROOT_PASSWORD = "secret"; 43 | public static final String DEFAULT_DISTRIBUTION = "centos5"; 44 | public static final boolean DEFAULT_HIDE_CONSOLE = false; 45 | public static final boolean DEFAULT_READONLY_DATABASE = true; 46 | public static final boolean DEFAULT_DEBUG_MODE = true; 47 | } 48 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /cfg/linulator.policy: -------------------------------------------------------------------------------- 1 | grant 2 | { 3 | // Uncomment the line below to effectively disable the security manager (not recommended) 4 | //permission java.security.AllPermission; 5 | 6 | // Needed for logging 7 | permission java.io.FilePermission "log${file.separator}linulator.log", "read, write"; 8 | 9 | // Needed to parse the configuration file 10 | permission java.io.FilePermission "cfg${file.separator}linulator.properties", "read"; 11 | 12 | // Since the address and ports are configurable, allow all addresses and ports for simplicity 13 | permission java.net.SocketPermission "*", "accept, listen, resolve"; 14 | 15 | // Create separate rules for each service and comment the wildcard line above for more control 16 | //permission java.net.SocketPermission "127.0.0.1:10023", "accept, listen, resolve"; 17 | 18 | // Uncomment the line below to allow outbound connections from Linulator (this is required for some services, but is less secure) 19 | //permission java.net.SocketPermission "*", "connect"; 20 | 21 | // Derby stuff 22 | permission java.io.FilePermission "derby.log", "read, write"; 23 | permission java.io.FilePermission "${user.dir}${file.separator}log${file.separator}database.log", "read, write"; 24 | permission java.io.FilePermission "${user.dir}", "read"; 25 | permission java.io.FilePermission "${user.dir}${file.separator}linulator", "read, write"; 26 | permission java.io.FilePermission "${user.dir}${file.separator}linulator${file.separator}-", "read, write, delete"; 27 | permission java.lang.RuntimePermission "createClassLoader"; 28 | permission java.lang.RuntimePermission "getProtectionDomain"; 29 | permission java.util.PropertyPermission "user.dir", "read"; 30 | permission java.util.PropertyPermission "derby.system.home", "read, write"; 31 | permission java.util.PropertyPermission "derby.stream.error.file", "read, write"; 32 | permission java.util.PropertyPermission "derby.language.logStatementText", "read, write"; 33 | permission java.util.PropertyPermission "derby.authentication.provider", "read"; 34 | permission java.util.PropertyPermission "derby.authentication.builtin.algorithm", "read"; 35 | permission java.util.PropertyPermission "derby.connection.requireAuthentication", "read"; 36 | permission java.util.PropertyPermission "derby.storage.useDefaultFilePermissions", "read"; 37 | permission java.util.PropertyPermission "derby.database.fullAccessUsers", "read"; 38 | permission java.util.PropertyPermission "derby.database.readOnlyAccessUsers", "read"; 39 | permission java.util.PropertyPermission "derby.database.defaultConnectionMode", "read"; 40 | permission java.util.PropertyPermission "derby.database.sqlAuthorization", "read"; 41 | }; 42 | -------------------------------------------------------------------------------- /tools/ReadMe.txt: -------------------------------------------------------------------------------- 1 | About 2 | ===== 3 | The CloneFilesystem program creates a replica of a Linux system and stores all the information in a database. 4 | This database serves as the virtual filesystem for a Linulator instance. 5 | 6 | Requirements 7 | ============ 8 | 1.) Java 7 or higher 9 | 2.) Run as root 10 | 3.) The operating system must be Linux 11 | 4.) Apache Derby (http://db.apache.org/derby/) 12 | 13 | Quick Start 14 | =========== 15 | You can safely avoid the compilation and manual database creation steps below by letting Linulator create 16 | the database and table information for you automatically. Just execute the "run.sh" script under the "tools/" 17 | directory as root: ./run.sh 18 | 19 | Compiling 20 | ========= 21 | The program can simply be compiled with: javac CloneFilesystem.java 22 | 23 | Usage 24 | ===== 25 | The program has two options: 26 | -c Create clone 27 | -h Display help 28 | 29 | Tips 30 | ==== 31 | 1.) Use the "exclude.lst" file to configure directory and file exclusions. Regex is supported. 32 | 2.) Exercise caution to avoid importing anything personal such as private keys, passwords, etc. 33 | 3.) This program should *only* be used on a fresh Linux install with no important data. 34 | 4.) A lightweight install of a distribution is recommended for a quicker cloning process. 35 | 5.) If a file appears to be hanging, use ctrl+c and add the file to "exclude.lst". 36 | 6.) During a re-run of the import process, existing directories and files will be skipped. 37 | 7.) If you want to start over with a new database, simply recursively remove the "linulator/" database directory. 38 | 39 | Database Initialization 40 | ======================= 41 | 1.) Download the latest Derby from http://db.apache.org/derby/ 42 | 43 | 2.) Unpack the tarball: tar -zxvf db-derby-x.x.x.x-bin.tar.gz 44 | 45 | 3.) Copy the filesystem SQL script to the "db-derby-x.x.x.x-bin/bin/" directory: 46 | cp sql/filesystem.sql /path/to/db-derby-x.x.x.x-bin/bin/ 47 | 48 | 4.) cd db-derby-x.x.x.x-bin/bin/ 49 | 50 | 5.) ./ij 51 | 52 | 6.) Create the database using ij: 53 | ij> CONNECT 'jdbc:derby:linulator;create=true'; 54 | 55 | 7.) Create the table using ij: 56 | ij> run 'filesystem.sql'; 57 | 58 | 8.) Quit ij: 59 | ij> quit; 60 | 61 | 9.) You should now have a "linulator/" directory containing data. 62 | 63 | 10.) Move this directory to the "tools/" directory where the CloneFilesystem program resides: 64 | mv linulator/ /path/to/linulator/tools/ 65 | 66 | 11.) Create a clone of the running operating system (*Note: This will take some time!): 67 | java -cp .:../lib/derby.jar CloneFilesystem -c 68 | 69 | 12.) Move the "linulator/" database directory to the "dist/" directory where the linulator.jar resides: 70 | mv linulator/ ../dist/ 71 | 72 | 13.) Fire up Linulator and enjoy your new instance: 73 | java -jar linulator-x.x.x-x.jar 74 | -------------------------------------------------------------------------------- /src/main/java/me/dilley/Network.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Linulator - The Linux Simulator 3 | * Copyright (C) 2014 Lloyd Dilley 4 | * http://www.linulator.org/ 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | 21 | package me.dilley; 22 | 23 | import java.io.BufferedReader; 24 | import java.io.BufferedWriter; 25 | import java.io.IOException; 26 | import java.net.DatagramPacket; 27 | import java.net.DatagramSocket; 28 | import java.net.SocketException; 29 | 30 | public class Network 31 | { 32 | public static String receive(BufferedReader input) 33 | { 34 | String data = null; 35 | try 36 | { 37 | data = input.readLine(); 38 | } 39 | catch(IOException ioe) 40 | { 41 | ioe.printStackTrace(); 42 | } 43 | 44 | return data; 45 | } 46 | 47 | public static int send(BufferedWriter output, String data, boolean isSeparate) 48 | { 49 | int bytesSent = 0; 50 | 51 | try 52 | { 53 | output.write(data); 54 | if(isSeparate) 55 | { 56 | output.newLine(); 57 | bytesSent = data.length() + System.getProperty("line.separator").length(); 58 | } 59 | else 60 | { 61 | bytesSent = data.length(); 62 | } 63 | output.flush(); 64 | } 65 | catch(NullPointerException npe) 66 | { 67 | return -1; // thread/client connection went away 68 | } 69 | catch(SocketException se) 70 | { 71 | se.getMessage(); 72 | } 73 | catch(IOException ioe) 74 | { 75 | ioe.printStackTrace(); 76 | } 77 | 78 | return bytesSent; 79 | } 80 | 81 | public static void receiveFrom(DatagramSocket serverSocket, DatagramPacket packet) 82 | { 83 | try 84 | { 85 | serverSocket.receive(packet); 86 | } 87 | catch(IOException ioe) 88 | { 89 | ioe.printStackTrace(); 90 | } 91 | } 92 | 93 | public static int sendTo(DatagramSocket serverSocket, DatagramPacket packet) 94 | { 95 | try 96 | { 97 | serverSocket.send(packet); 98 | } 99 | catch(IOException ioe) 100 | { 101 | ioe.printStackTrace(); 102 | } 103 | 104 | return packet.getLength(); // bytes sent 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/me/dilley/services/DiscardServer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Linulator - The Linux Simulator 3 | * Copyright (C) 2014 Lloyd Dilley 4 | * http://www.linulator.org/ 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | 21 | package me.dilley.services; 22 | 23 | import java.io.BufferedReader; 24 | import java.io.BufferedWriter; 25 | import java.io.InputStreamReader; 26 | import java.io.OutputStreamWriter; 27 | import java.io.IOException; 28 | import java.net.DatagramPacket; 29 | import java.net.DatagramSocket; 30 | import java.net.Socket; 31 | 32 | import me.dilley.Log; 33 | import me.dilley.Network; 34 | 35 | public class DiscardServer implements Runnable 36 | { 37 | protected byte[] buffer = null; 38 | protected boolean isUdp = false; 39 | protected Socket clientSocket = null; 40 | protected DatagramPacket packet = null; 41 | protected DatagramSocket serverSocket = null; 42 | protected BufferedReader input = null; 43 | protected BufferedWriter output = null; 44 | 45 | // TCP 46 | public DiscardServer(Socket clientSocket) 47 | { 48 | isUdp = false; 49 | this.clientSocket = clientSocket; 50 | } 51 | 52 | // UDP 53 | public DiscardServer(DatagramSocket serverSocket, byte[] buffer, DatagramPacket packet) 54 | { 55 | isUdp = true; 56 | this.serverSocket = serverSocket; 57 | this.buffer = buffer; 58 | this.packet = packet; 59 | } 60 | 61 | public void run() 62 | { 63 | try 64 | { 65 | if(!isUdp) 66 | { 67 | input = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); 68 | output = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream())); 69 | 70 | Log.write(0, "DiscardServer: TCP connection received from " + clientSocket.getInetAddress().getHostAddress() + ':' + clientSocket.getPort() + '.'); 71 | 72 | while(clientSocket != null && !clientSocket.isClosed()) 73 | { 74 | String message = Network.receive(input); 75 | if(message == null) // connection lost 76 | break; 77 | } 78 | 79 | input.close(); 80 | output.close(); 81 | Log.write(0, "DiscardServer: TCP connection to " + clientSocket.getInetAddress().getHostAddress() + ':' + clientSocket.getPort() + " closed."); 82 | } 83 | else 84 | { 85 | Log.write(0, "DiscardServer: UDP packet received from " + packet.getAddress().getHostAddress() + ':' + packet.getPort() + '.'); 86 | 87 | while(true) 88 | { 89 | Network.receiveFrom(serverSocket, packet); 90 | } 91 | } 92 | } 93 | catch(IOException ioe) 94 | { 95 | ioe.getMessage(); 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/me/dilley/OperatingSystem.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Linulator - The Linux Simulator 3 | * Copyright (C) 2014 Lloyd Dilley 4 | * http://www.linulator.org/ 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | 21 | package me.dilley; 22 | 23 | public class OperatingSystem 24 | { 25 | public static final String HARDWARE_ARCHITECTURE = "x86_64"; 26 | public static final String OPERATING_SYSTEM = "GNU/Linux"; 27 | public static final String KERNEL_RELEASE = "2.6.32-431.23.3.el6.x86_64"; 28 | public static final String KERNEL_VERSION = "#1 SMP Thu Jul 31 17:20:51 UTC 2014"; 29 | 30 | private static String hostName; 31 | private static String shortName; 32 | private static String domainName; 33 | private static int nextFreeUid; // next available user ID 34 | private static int nextFreeGid; // next available group ID 35 | private static int nextFreePid; // next available process ID 36 | 37 | public static synchronized String getHostName() 38 | { 39 | return hostName; 40 | } 41 | 42 | public static synchronized void setHostName(String newHostName) 43 | { 44 | hostName = newHostName; 45 | } 46 | 47 | public static synchronized String getShortName() 48 | { 49 | return shortName; 50 | } 51 | 52 | public static synchronized void setShortName(String newShortName) 53 | { 54 | shortName = newShortName; 55 | } 56 | 57 | public static synchronized String getDomainName() 58 | { 59 | return domainName; 60 | } 61 | 62 | public static synchronized void setDomainName(String newDomainName) 63 | { 64 | domainName = newDomainName; 65 | } 66 | 67 | public static synchronized int getNextFreeUid() 68 | { 69 | return nextFreeUid; 70 | } 71 | 72 | public static synchronized int assignNextFreeUid() 73 | { 74 | return nextFreeUid++; 75 | } 76 | 77 | public static synchronized void setNextFreeUid(int uid) 78 | { 79 | nextFreeUid = uid; 80 | } 81 | 82 | public static synchronized int getNextFreeGid() 83 | { 84 | return nextFreeGid; 85 | } 86 | 87 | public static synchronized int assignNextFreeGid() 88 | { 89 | return nextFreeGid++; 90 | } 91 | 92 | public static synchronized void setNextFreeGid(int gid) 93 | { 94 | nextFreeGid = gid; 95 | } 96 | 97 | public static synchronized int getNextFreePid() 98 | { 99 | return nextFreePid; 100 | } 101 | 102 | public static synchronized int assignNextFreePid() 103 | { 104 | return nextFreePid++; 105 | } 106 | 107 | public static synchronized void setNextFreePid(int pid) 108 | { 109 | nextFreePid = pid; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/main/java/me/dilley/Log.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Linulator - The Linux Simulator 3 | * Copyright (C) 2014 Lloyd Dilley 4 | * http://www.linulator.org/ 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | 21 | package me.dilley; 22 | 23 | import java.io.BufferedReader; 24 | import java.io.FileReader; 25 | import java.io.FileWriter; 26 | import java.io.IOException; 27 | import java.text.SimpleDateFormat; 28 | import java.util.Date; 29 | import java.util.Scanner; 30 | 31 | public class Log 32 | { 33 | public static final byte COLS = 80; 34 | public static final byte ROWS = 24; 35 | 36 | public static void write(int severity, String message) 37 | { 38 | SimpleDateFormat timestampFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss"); 39 | Date currentTime = new Date(); 40 | String timestamp = timestampFormat.format(currentTime); 41 | String logEntry = ""; 42 | FileWriter logFile; 43 | 44 | switch(severity) 45 | { 46 | case 0: 47 | logEntry = timestamp + " INFO: " + message + '\n'; 48 | break; 49 | case 1: 50 | logEntry = timestamp + " WARN: " + message + '\n'; 51 | break; 52 | case 2: 53 | logEntry = timestamp + " CRIT: " + message + '\n'; 54 | break; 55 | case 3: 56 | logEntry = timestamp + " DBUG: " + message + '\n'; 57 | break; 58 | default: 59 | logEntry = timestamp + " INFO: " + message + '\n'; 60 | break; 61 | } 62 | 63 | try 64 | { 65 | logFile = new FileWriter("log" + System.getProperty("file.separator") + "linulator.log", true); 66 | logFile.write(logEntry); 67 | logFile.close(); 68 | } 69 | catch(IOException ioe) 70 | { 71 | System.err.println("Unable to write to linulator.log file:"); 72 | System.err.println(ioe.getMessage()); 73 | System.exit(1); 74 | } 75 | } 76 | 77 | public static void viewLog() 78 | { 79 | BufferedReader logFile; 80 | Scanner scanner = null; 81 | String line; 82 | int rowCount = 0; 83 | 84 | try 85 | { 86 | logFile = new BufferedReader(new FileReader("log" + System.getProperty("file.separator") + "linulator.log")); 87 | while((line = logFile.readLine()) != null) 88 | { 89 | if(rowCount >= ROWS) 90 | { 91 | scanner = new Scanner(System.in); 92 | scanner.nextLine(); 93 | rowCount = 0; 94 | } 95 | System.out.println(line); 96 | rowCount += 1; 97 | } 98 | logFile.close(); 99 | } 100 | catch(IOException ioe) 101 | { 102 | System.err.println("Unable to read linulator.log file:"); 103 | System.err.println(ioe.getMessage()); 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/me/dilley/services/DaytimeServer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Linulator - The Linux Simulator 3 | * Copyright (C) 2014 Lloyd Dilley 4 | * http://www.linulator.org/ 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | 21 | package me.dilley.services; 22 | 23 | import java.io.BufferedReader; 24 | import java.io.BufferedWriter; 25 | import java.io.InputStreamReader; 26 | import java.io.OutputStreamWriter; 27 | import java.io.IOException; 28 | import java.net.DatagramPacket; 29 | import java.net.DatagramSocket; 30 | import java.net.Socket; 31 | import java.text.SimpleDateFormat; 32 | import java.util.Date; 33 | 34 | import me.dilley.Log; 35 | import me.dilley.Network; 36 | 37 | public class DaytimeServer implements Runnable 38 | { 39 | protected byte[] buffer = null; 40 | protected boolean isUdp = false; 41 | protected Socket clientSocket = null; 42 | protected DatagramPacket packet = null; 43 | protected DatagramSocket serverSocket = null; 44 | protected BufferedReader input = null; 45 | protected BufferedWriter output = null; 46 | protected SimpleDateFormat timestampFormat = null; 47 | protected Date currentTime = null; 48 | protected String timestamp = null; 49 | 50 | // TCP 51 | public DaytimeServer(Socket clientSocket) 52 | { 53 | isUdp = false; 54 | this.clientSocket = clientSocket; 55 | } 56 | 57 | // UDP 58 | public DaytimeServer(DatagramSocket serverSocket, byte[] buffer, DatagramPacket packet) 59 | { 60 | isUdp = true; 61 | this.serverSocket = serverSocket; 62 | this.buffer = buffer; 63 | this.packet = packet; 64 | } 65 | 66 | public void run() 67 | { 68 | timestampFormat = new SimpleDateFormat("EEEE, MMMM d, yyyy HH:mm:ss-zzz"); 69 | currentTime = new Date(); 70 | timestamp = timestampFormat.format(currentTime); 71 | 72 | try 73 | { 74 | if(!isUdp) 75 | { 76 | input = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); 77 | output = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream())); 78 | 79 | Log.write(0, "DaytimeServer: TCP connection received from " + clientSocket.getInetAddress().getHostAddress() + ':' + clientSocket.getPort() + '.'); 80 | Network.send(output, timestamp, true); 81 | input.close(); 82 | output.close(); 83 | Log.write(0, "DaytimeServer: TCP connection to " + clientSocket.getInetAddress().getHostAddress() + ':' + clientSocket.getPort() + " closed."); 84 | } 85 | else 86 | { 87 | Log.write(0, "DaytimeServer: UDP packet received from " + packet.getAddress().getHostAddress() + ':' + packet.getPort() + '.'); 88 | buffer = timestamp.getBytes(); 89 | packet.setData(buffer, 0, buffer.length); 90 | Network.sendTo(serverSocket, packet); 91 | } 92 | } 93 | catch(IOException ioe) 94 | { 95 | ioe.getMessage(); 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/me/dilley/services/EchoServer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Linulator - The Linux Simulator 3 | * Copyright (C) 2014 Lloyd Dilley 4 | * http://www.linulator.org/ 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | 21 | package me.dilley.services; 22 | 23 | import java.io.BufferedReader; 24 | import java.io.BufferedWriter; 25 | import java.io.InputStreamReader; 26 | import java.io.OutputStreamWriter; 27 | import java.io.IOException; 28 | import java.net.DatagramPacket; 29 | import java.net.DatagramSocket; 30 | import java.net.Socket; 31 | 32 | import me.dilley.Log; 33 | import me.dilley.Network; 34 | 35 | public class EchoServer implements Runnable 36 | { 37 | protected byte[] buffer = null; 38 | protected boolean isUdp = false; 39 | protected Socket clientSocket = null; 40 | protected DatagramPacket packet = null; 41 | protected DatagramSocket serverSocket = null; 42 | protected BufferedReader input = null; 43 | protected BufferedWriter output = null; 44 | 45 | // TCP 46 | public EchoServer(Socket clientSocket) 47 | { 48 | isUdp = false; 49 | this.clientSocket = clientSocket; 50 | } 51 | 52 | // UDP 53 | public EchoServer(DatagramSocket serverSocket, byte[] buffer, DatagramPacket packet) 54 | { 55 | isUdp = true; 56 | this.serverSocket = serverSocket; 57 | this.buffer = buffer; 58 | this.packet = packet; 59 | } 60 | 61 | public void run() 62 | { 63 | try 64 | { 65 | if(!isUdp) 66 | { 67 | input = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); 68 | output = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream())); 69 | 70 | Log.write(0, "EchoServer: TCP connection received from " + clientSocket.getInetAddress().getHostAddress() + ':' + clientSocket.getPort() + '.'); 71 | 72 | while(clientSocket != null && !clientSocket.isClosed()) 73 | { 74 | String message = Network.receive(input); 75 | if(message == null) // connection lost 76 | break; 77 | Network.send(output, message, true); 78 | } 79 | 80 | input.close(); 81 | output.close(); 82 | Log.write(0, "EchoServer: TCP connection to " + clientSocket.getInetAddress().getHostAddress() + ':' + clientSocket.getPort() + " closed."); 83 | } 84 | else 85 | { 86 | Log.write(0, "EchoServer: UDP packet received from " + packet.getAddress().getHostAddress() + ':' + packet.getPort() + '.'); 87 | Network.sendTo(serverSocket, packet); // send back initial echo from caller 88 | 89 | while(true) 90 | { 91 | Network.receiveFrom(serverSocket, packet); 92 | Network.sendTo(serverSocket, packet); 93 | } 94 | } 95 | } 96 | catch(IOException ioe) 97 | { 98 | ioe.getMessage(); 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/me/dilley/SystemCalls.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Linulator - The Linux Simulator 3 | * Copyright (C) 2014 Lloyd Dilley 4 | * http://www.linulator.org/ 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | 21 | package me.dilley; 22 | 23 | import java.util.Arrays; 24 | 25 | /* 26 | * This class emulates Linux syscalls for the x86-64 architecture. 27 | * I understand that the method names below are not consistent with other methods in Linulator (nor do they follow 28 | * Sun's recommendations.) I am attempting to preserve the look of the Linux API. I *may* standardize the names 29 | * at some point. For example, "sys_write" might become "sysWrite" or simply "write". 30 | */ 31 | 32 | class SystemCalls 33 | { 34 | public static String sys_call(int id, String[] args) 35 | { 36 | String returnValue = ""; 37 | 38 | try 39 | { 40 | switch(id) 41 | { 42 | case 0: 43 | returnValue = Integer.toString(sys_read(Integer.parseInt(args[0]), args[1], Integer.parseInt(args[2]))); 44 | break; 45 | case 1: 46 | returnValue = Integer.toString(sys_write(Integer.parseInt(args[0]), args[1], Integer.parseInt(args[2]))); 47 | break; 48 | case 2: 49 | returnValue = Integer.toString(sys_open(args[0], Integer.parseInt(args[1]), Integer.parseInt(args[2]))); 50 | break; 51 | case 3: 52 | returnValue = Integer.toString(sys_close(Integer.parseInt(args[0]))); 53 | break; 54 | default: 55 | Log.write(1, "Invalid system call: " + id); 56 | System.err.println("Warning: Invalid system call: " + id); 57 | break; 58 | } 59 | } 60 | catch(Exception e) 61 | { 62 | Log.write(1, "Unable to make system call: " + id); 63 | Log.write(1, "Arguments: " + Arrays.toString(args)); 64 | Log.write(1, e.getMessage()); 65 | System.err.println("Warning: Unable to make system call: " + id); 66 | System.err.println("Arguments: " + Arrays.toString(args)); 67 | System.err.println(e.getMessage()); 68 | } 69 | 70 | return returnValue; 71 | } 72 | 73 | // syscall placeholders for now 74 | public static int sys_read(int fileDescriptor, String buffer, int count) 75 | { 76 | return 0; 77 | } 78 | 79 | public static int sys_write(int fileDescriptor, String buffer, int count) 80 | { 81 | return 0; 82 | } 83 | 84 | public static int sys_open(String file, int flags, int mode) 85 | { 86 | return 0; 87 | } 88 | 89 | public static int sys_close(int fileDescriptor) 90 | { 91 | return 0; 92 | } 93 | 94 | /* 95 | * Unlike the standard sys_stat, this method takes an optional file descriptor 96 | * so that it can eliminate the need for sys_fstat and sys_lstat. 97 | */ 98 | public static int sys_stat(int fileDescriptor, File file) 99 | { 100 | //if(!fileDescriptor == -1) 101 | // stat by fd if defined 102 | 103 | //if(isLink(file)) 104 | // stat by link 105 | 106 | return 0; 107 | } 108 | 109 | // Resume work here... I got too tired adding all the error codes. 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/me/dilley/User.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Linulator - The Linux Simulator 3 | * Copyright (C) 2014 Lloyd Dilley 4 | * http://www.linulator.org/ 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | 21 | package me.dilley; 22 | 23 | class User 24 | { 25 | public static int SECONDS_IN_A_DAY = 86400; 26 | 27 | private String userName; 28 | private int uid; // user ID 29 | private int gid; // group ID 30 | private String gecos; // real name/comment 31 | private String homeDirectory; 32 | private String shell; 33 | private String password; 34 | private String passwordHash; 35 | private int lastChanged; // days elapsed since 01/01/1970 when password was last changed 36 | private String cwd; // current working directory 37 | 38 | // For use with useradd 39 | public User(String userName, int uid, int gid, String gecos, String homeDirectory, String shell) 40 | { 41 | this.userName = userName; 42 | this.uid = uid; 43 | this.gid = gid; 44 | this.gecos = gecos; 45 | this.homeDirectory = homeDirectory; 46 | this.shell = shell; 47 | password = null; 48 | passwordHash = null; 49 | lastChanged = (int)(System.currentTimeMillis() / 1000 / SECONDS_IN_A_DAY); 50 | } 51 | 52 | public String getUserName() 53 | { 54 | return userName; 55 | } 56 | 57 | public void setUserName(String userName) 58 | { 59 | this.userName = userName; 60 | } 61 | 62 | public int getUid() 63 | { 64 | return uid; 65 | } 66 | 67 | public void setUid(int uid) 68 | { 69 | this.uid = uid; 70 | } 71 | 72 | public int getGid() 73 | { 74 | return gid; 75 | } 76 | 77 | public void setGid(int gid) 78 | { 79 | this.gid = gid; 80 | } 81 | 82 | public String getGecos() 83 | { 84 | return gecos; 85 | } 86 | 87 | public void setGecos(String gecos) 88 | { 89 | this.gecos = gecos; 90 | } 91 | 92 | public String getHomeDirectory() 93 | { 94 | return homeDirectory; 95 | } 96 | 97 | public void setHomeDirectory(String homeDirectory) 98 | { 99 | this.homeDirectory = homeDirectory; 100 | } 101 | 102 | public String getShell() 103 | { 104 | return shell; 105 | } 106 | 107 | public void setShell(String shell) 108 | { 109 | this.shell = shell; 110 | } 111 | 112 | public String getPassword() 113 | { 114 | return password; 115 | } 116 | 117 | public void setPassword(String password) 118 | { 119 | this.password = password; 120 | } 121 | 122 | public String getPasswordHash() 123 | { 124 | return passwordHash; 125 | } 126 | 127 | public void setPasswordHash(String passwordHash) 128 | { 129 | this.passwordHash = passwordHash; 130 | } 131 | 132 | public int getLastChanged() 133 | { 134 | return lastChanged; 135 | } 136 | 137 | public void setLastChanged(int lastChanged) 138 | { 139 | this.lastChanged = lastChanged; 140 | } 141 | 142 | public String getCwd() 143 | { 144 | return cwd; 145 | } 146 | 147 | public void setCwd(String cwd) 148 | { 149 | this.cwd = cwd; 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/main/java/me/dilley/services/TimeServer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Linulator - The Linux Simulator 3 | * Copyright (C) 2014 Lloyd Dilley 4 | * http://www.linulator.org/ 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | 21 | package me.dilley.services; 22 | 23 | import java.io.BufferedReader; 24 | import java.io.BufferedWriter; 25 | import java.io.DataInputStream; 26 | import java.io.DataOutputStream; 27 | import java.io.InputStreamReader; 28 | import java.io.OutputStreamWriter; 29 | import java.io.IOException; 30 | import java.net.DatagramPacket; 31 | import java.net.DatagramSocket; 32 | import java.net.Socket; 33 | import java.nio.ByteBuffer; 34 | import java.util.Calendar; 35 | import java.util.TimeZone; 36 | 37 | import me.dilley.Log; 38 | import me.dilley.Network; 39 | 40 | public class TimeServer implements Runnable 41 | { 42 | protected byte[] buffer = null; 43 | protected boolean isUdp = false; 44 | protected Socket clientSocket = null; 45 | protected DatagramPacket packet = null; 46 | protected DatagramSocket serverSocket = null; 47 | protected BufferedReader input = null; 48 | protected BufferedWriter output = null; 49 | protected DataInputStream ios = null; // for raw data 50 | protected DataOutputStream dos = null; // for raw data 51 | protected Calendar calendar = null; 52 | 53 | // TCP 54 | public TimeServer(Socket clientSocket) 55 | { 56 | isUdp = false; 57 | this.clientSocket = clientSocket; 58 | } 59 | 60 | // UDP 61 | public TimeServer(DatagramSocket serverSocket, byte[] buffer, DatagramPacket packet) 62 | { 63 | isUdp = true; 64 | this.serverSocket = serverSocket; 65 | this.buffer = buffer; 66 | this.packet = packet; 67 | } 68 | 69 | public void run() 70 | { 71 | calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); 72 | Long long_timestamp = calendar.getTimeInMillis() / 1000L; 73 | int int_timestamp = long_timestamp.intValue(); // need 32-bit value per RFC (FixMe before February 7, 2036!) :) 74 | buffer = ByteBuffer.allocate(4).putInt(int_timestamp).array(); // 32-bit aligned network byte order 75 | //String timestamp = new String(buffer); // for use with send() (TCP connections) 76 | 77 | try 78 | { 79 | if(!isUdp) 80 | { 81 | input = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); 82 | output = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream())); 83 | ios = new DataInputStream(clientSocket.getInputStream()); 84 | dos = new DataOutputStream(clientSocket.getOutputStream()); 85 | 86 | Log.write(0, "TimeServer: TCP connection received from " + clientSocket.getInetAddress().getHostAddress() + ':' + clientSocket.getPort() + '.'); 87 | //Network.send(output, timestamp, true); 88 | dos.writeInt(int_timestamp); // writeInt() takes care of 4 byte big endian conversion 89 | input.close(); 90 | output.close(); 91 | Log.write(0, "TimeServer: TCP connection to " + clientSocket.getInetAddress().getHostAddress() + ':' + clientSocket.getPort() + " closed."); 92 | } 93 | else 94 | { 95 | Log.write(0, "TimeServer: UDP packet received from " + packet.getAddress().getHostAddress() + ':' + packet.getPort() + '.'); 96 | packet.setData(buffer, 0, buffer.length); 97 | Network.sendTo(serverSocket, packet); 98 | } 99 | } 100 | catch(IOException ioe) 101 | { 102 | ioe.getMessage(); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/me/dilley/TcpServer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Linulator - The Linux Simulator 3 | * Copyright (C) 2014 Lloyd Dilley 4 | * http://www.linulator.org/ 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | 21 | package me.dilley; 22 | 23 | import java.io.IOException; 24 | import java.net.InetAddress; 25 | import java.net.ServerSocket; 26 | import java.net.Socket; 27 | 28 | import me.dilley.services.*; 29 | 30 | class TcpServer implements Runnable 31 | { 32 | protected ServerSocket serverSocket = null; 33 | protected boolean isStopped = false; 34 | protected Thread runningThread = null; 35 | protected String address = null; 36 | protected int port = 0; 37 | protected String service = null; 38 | 39 | public TcpServer(String address, int port, String service) 40 | { 41 | this.address = address; 42 | this.port = port; 43 | this.service = service; 44 | } 45 | 46 | public void run() 47 | { 48 | synchronized(this) 49 | { 50 | this.runningThread = Thread.currentThread(); 51 | } 52 | 53 | try 54 | { 55 | if(this.address.equals("*")) 56 | this.serverSocket = new ServerSocket(this.port); 57 | else 58 | this.serverSocket = new ServerSocket(this.port, 0, InetAddress.getByName(this.address)); 59 | } 60 | catch(IOException ioe) 61 | { 62 | Log.write(2, this.service + " server unable to listen on port: " + this.port); 63 | Log.write(2, ioe.getMessage()); 64 | System.err.println("Critical: " + this.service + " server unable to listen on port: " + this.port); 65 | System.err.println(ioe.getMessage()); 66 | System.exit(1); 67 | } 68 | 69 | while(!isStopped) 70 | { 71 | Socket clientSocket = null; 72 | 73 | try 74 | { 75 | clientSocket = this.serverSocket.accept(); 76 | } 77 | catch(IOException ioe) 78 | { 79 | if(isStopped()) 80 | { 81 | Log.write(2, this.service + " server stopped."); 82 | Log.write(2, ioe.getMessage()); 83 | System.err.println("Critical: " + this.service + " server stopped."); 84 | System.err.println(ioe.getMessage()); 85 | return; 86 | } 87 | Log.write(1, "Error accepting client connection:"); 88 | Log.write(1, ioe.getMessage()); 89 | } 90 | 91 | if(this.service.equals("echo")) 92 | new Thread(new EchoServer(clientSocket)).start(); 93 | if(this.service.equals("discard")) 94 | new Thread(new DiscardServer(clientSocket)).start(); 95 | if(this.service.equals("daytime")) 96 | new Thread(new DaytimeServer(clientSocket)).start(); 97 | if(this.service.equals("chargen")) 98 | new Thread(new ChargenServer(clientSocket)).start(); 99 | if(this.service.equals("time")) 100 | new Thread(new TimeServer(clientSocket)).start(); 101 | if(this.service.equals("telnet")) 102 | new Thread(new TelnetServer(clientSocket)).start(); 103 | if(this.service.equals("http")) 104 | new Thread(new HttpServer(clientSocket)).start(); 105 | } 106 | 107 | Log.write(0, this.service + " server stopped."); 108 | } 109 | 110 | private synchronized boolean isStopped() 111 | { 112 | return this.isStopped; 113 | } 114 | 115 | public synchronized void stop() 116 | { 117 | this.isStopped = true; 118 | 119 | try 120 | { 121 | this.serverSocket.close(); 122 | } 123 | catch(IOException ioe) 124 | { 125 | Log.write(2, "Unable to close " + this.service + " server."); 126 | Log.write(2, ioe.getMessage()); 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/main/java/me/dilley/UdpServer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Linulator - The Linux Simulator 3 | * Copyright (C) 2014 Lloyd Dilley 4 | * http://www.linulator.org/ 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | 21 | package me.dilley; 22 | 23 | import java.io.IOException; 24 | import java.net.DatagramPacket; 25 | import java.net.DatagramSocket; 26 | import java.net.InetAddress; 27 | import java.net.Socket; 28 | 29 | import me.dilley.services.*; 30 | 31 | class UdpServer implements Runnable 32 | { 33 | protected DatagramSocket serverSocket = null; 34 | protected boolean isStopped = false; 35 | protected Thread runningThread = null; 36 | protected String address = null; 37 | protected int port = 0; 38 | protected String service = null; 39 | 40 | public UdpServer(String address, int port, String service) 41 | { 42 | this.address = address; 43 | this.port = port; 44 | this.service = service; 45 | } 46 | 47 | public void run() 48 | { 49 | synchronized(this) 50 | { 51 | this.runningThread = Thread.currentThread(); 52 | } 53 | 54 | try 55 | { 56 | if(this.address.equals("*")) 57 | this.serverSocket = new DatagramSocket(this.port); 58 | else 59 | this.serverSocket = new DatagramSocket(this.port, InetAddress.getByName(this.address)); 60 | } 61 | catch(IOException ioe) 62 | { 63 | Log.write(2, this.service + " server unable to listen on port: " + this.port); 64 | Log.write(2, ioe.getMessage()); 65 | System.err.println("Critical: " + this.service + " server unable to listen on port: " + this.port); 66 | System.err.println(ioe.getMessage()); 67 | System.exit(1); 68 | } 69 | 70 | while(!isStopped) 71 | { 72 | byte[] buffer = null; 73 | DatagramPacket packet = null; 74 | 75 | try 76 | { 77 | buffer = new byte[Limits.MAX_SMALL_SERVER_PACKET_SIZE]; 78 | packet = new DatagramPacket(buffer, buffer.length); 79 | this.serverSocket.receive(packet); 80 | } 81 | catch(IOException ioe) 82 | { 83 | if(isStopped()) 84 | { 85 | Log.write(2, this.service + " server stopped."); 86 | Log.write(2, ioe.getMessage()); 87 | System.err.println("Critical: " + this.service + " server stopped."); 88 | System.err.println(ioe.getMessage()); 89 | return; 90 | } 91 | Log.write(1, "Error receiving packet:"); 92 | Log.write(1, ioe.getMessage()); 93 | } 94 | 95 | if(this.service.equals("echo")) 96 | new Thread(new EchoServer(serverSocket, buffer, packet)).start(); 97 | if(this.service.equals("discard")) 98 | new Thread(new DiscardServer(serverSocket, buffer, packet)).start(); 99 | if(this.service.equals("daytime")) 100 | new Thread(new DaytimeServer(serverSocket, buffer, packet)).start(); 101 | if(this.service.equals("chargen")) 102 | new Thread(new ChargenServer(serverSocket, buffer, packet)).start(); 103 | if(this.service.equals("time")) 104 | new Thread(new TimeServer(serverSocket, buffer, packet)).start(); 105 | } 106 | 107 | Log.write(0, this.service + " server stopped."); 108 | } 109 | 110 | private synchronized boolean isStopped() 111 | { 112 | return this.isStopped; 113 | } 114 | 115 | public synchronized void stop() 116 | { 117 | this.isStopped = true; 118 | 119 | if(this.serverSocket == null || this.serverSocket.isClosed()) 120 | Log.write(2, "Unable to close " + this.service + " server."); 121 | else 122 | this.serverSocket.close(); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/main/java/me/dilley/Node.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Linulator - The Linux Simulator 3 | * Copyright (C) 2014 Lloyd Dilley 4 | * http://www.linulator.org/ 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | 21 | package me.dilley; 22 | 23 | import java.util.ArrayList; 24 | import java.util.Arrays; 25 | //import java.util.List; 26 | 27 | public class Node 28 | { 29 | Node parent = null; 30 | ArrayList children = null; 31 | //List leaves = null; 32 | //String data = null; 33 | File file = null; 34 | //String incrementalPath = null; 35 | 36 | //public Node(String nodeValue, String incrementalPath) 37 | public Node(File file) 38 | { 39 | this.file = file; 40 | children = new ArrayList(); 41 | //leaves = new ArrayList(); 42 | //data = nodeValue; 43 | //this.incrementalPath = incrementalPath; 44 | } 45 | 46 | public boolean doesExist(String path) 47 | { 48 | return false; 49 | } 50 | 51 | public boolean isLeaf() 52 | { 53 | //return children.isEmpty() && leaves.isEmpty(); 54 | return children.isEmpty(); 55 | } 56 | 57 | public void add(File file) 58 | { 59 | String[] path = file.getPath().split("/"); 60 | while(path[0] == null || path[0].equals("")) 61 | path = Arrays.copyOfRange(path, 1, path.length); 62 | 63 | //Node currentChild = new Node(path[0], currentPath + '/' + path[0]); 64 | Node currentChild = new Node(file); 65 | if(path.length == 1) 66 | { 67 | children.add(currentChild); 68 | return; 69 | } 70 | else 71 | { 72 | int index = children.indexOf(currentChild); 73 | if(index == -1) 74 | { 75 | children.add(currentChild); 76 | // ----currentChild.add(file.getPath(), Arrays.copyOfRange(path, 1, path.length)); 77 | //currentChild.add(currentChild.incrementalPath, Arrays.copyOfRange(path, 1, path.length)); 78 | } 79 | else 80 | { 81 | Node nextChild = children.get(index); 82 | // ----nextChild.add(file.getPath(), Arrays.copyOfRange(path, 1, path.length)); 83 | //nextChild.add(currentChild.incrementalPath, Arrays.copyOfRange(path, 1, path.length)); 84 | } 85 | } 86 | } 87 | // public void add(String currentPath, String[] path) 88 | // { 89 | // // Avoid first element that can be an empty string if you split a string that has a starting slash as /sd/card/ 90 | // while(path[0] == null || path[0].equals("")) 91 | // path = Arrays.copyOfRange(path, 1, path.length); 92 | 93 | // Node currentChild = new Node(path[0], currentPath + "/" + path[0]); 94 | // if(path.length == 1) 95 | // { 96 | // leaves.add(currentChild); 97 | // return; 98 | // } 99 | // else 100 | // { 101 | // int index = children.indexOf(currentChild); 102 | // if(index == -1) 103 | // { 104 | // children.add(currentChild); 105 | // currentChild.add(currentChild.incrementalPath, Arrays.copyOfRange(path, 1, path.length)); 106 | // } 107 | // else 108 | // { 109 | // Node nextChild = children.get(index); 110 | // nextChild.add(currentChild.incrementalPath, Arrays.copyOfRange(path, 1, path.length)); 111 | // } 112 | // } 113 | // } 114 | 115 | //@Override 116 | //public boolean equals(Object obj) 117 | //{ 118 | // Node cmpObj = (Node)obj; 119 | // return incrementalPath.equals(cmpObj.incrementalPath) && data.equals(cmpObj.data); 120 | //} 121 | 122 | public void showNode(int increment) 123 | { 124 | for(int i = 0; i < increment; i++) 125 | { 126 | System.out.print(" "); 127 | } 128 | // ------System.out.println(incrementalPath + (isLeaf() ? " -> " + file.getPath() : "")); 129 | //System.out.println(incrementalPath + (isLeaf() ? " -> " + data : "")); 130 | for(Node n: children) 131 | n.showNode(increment + 2); 132 | //for(Node n: leaves) 133 | // n.showNode(increment + 2); 134 | } 135 | 136 | //@Override 137 | //public String toString() 138 | //{ 139 | // return data; 140 | //} 141 | } 142 | -------------------------------------------------------------------------------- /src/main/java/me/dilley/Database.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Linulator - The Linux Simulator 3 | * Copyright (C) 2014 Lloyd Dilley 4 | * http://www.linulator.org/ 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | 21 | package me.dilley; 22 | 23 | import java.sql.Connection; 24 | import java.sql.DriverManager; 25 | import java.sql.PreparedStatement; 26 | import java.sql.ResultSet; 27 | import java.sql.SQLException; 28 | import java.sql.Statement; 29 | import java.util.Properties; 30 | 31 | // ToDo: This class needs to be thread safe 32 | class Database 33 | { 34 | private static String driver = "org.apache.derby.jdbc.EmbeddedDriver"; 35 | private static String protocol = "jdbc:derby:"; 36 | private static String databaseName = "linulator"; 37 | private static Connection connection = null; 38 | private static PreparedStatement preparedStatement = null; 39 | private static Statement statement = null; 40 | private static ResultSet resultSet = null; 41 | private static Properties properties = new Properties(); 42 | 43 | public static void connect() 44 | { 45 | try 46 | { 47 | System.setProperty("derby.system.home", System.getProperty("user.dir")); 48 | System.setProperty("derby.stream.error.file", "log" + System.getProperty("file.separator") + "database.log"); 49 | System.setProperty("derby.language.logStatementText", "true"); 50 | connection = DriverManager.getConnection(protocol + databaseName + ";create=true", properties); 51 | Log.write(0, "Connected to database successfully."); 52 | System.out.println("Connected to database successfully."); 53 | } 54 | catch(SQLException sqle) 55 | { 56 | Log.write(2, "Unable to connect to database."); 57 | Log.write(2, sqle.getMessage()); 58 | System.err.println("Critical: Unable to connect to database."); 59 | System.err.println(sqle.getMessage()); 60 | if(connection == null) 61 | System.exit(1); 62 | } 63 | } 64 | 65 | public static void disconnect() 66 | { 67 | try 68 | { 69 | connection.close(); 70 | DriverManager.getConnection("jdbc:derby:;shutdown=true"); 71 | } 72 | catch(SQLException sqle) 73 | { 74 | Log.write(2, "Unable to disconnect from database."); 75 | Log.write(2, sqle.getMessage()); 76 | System.err.println("Critical: Unable to disconnect from database."); 77 | System.err.println(sqle.getMessage()); 78 | } 79 | } 80 | 81 | public static ResultSet query(String query) 82 | { 83 | try 84 | { 85 | statement = connection.createStatement(); 86 | resultSet = statement.executeQuery(query); 87 | } 88 | catch(SQLException sqle) 89 | { 90 | Log.write(2, "Unable to query database."); 91 | Log.write(2, sqle.getMessage()); 92 | System.err.println("Critical: Unable to query database."); 93 | System.err.println(sqle.getMessage()); 94 | System.exit(1); 95 | } 96 | 97 | return resultSet; 98 | } 99 | 100 | public static void endQuery() 101 | { 102 | try 103 | { 104 | statement.close(); 105 | } 106 | catch(SQLException sqle) 107 | { 108 | Log.write(2, "Unable to close query."); 109 | Log.write(2, sqle.getMessage()); 110 | System.err.println("Critical: Unable to close query."); 111 | System.err.println(sqle.getMessage()); 112 | } 113 | } 114 | 115 | public static void shutdown() 116 | { 117 | try 118 | { 119 | DriverManager.getConnection("jdbc:derby:;shutdown=true"); 120 | } 121 | catch(SQLException sqle) 122 | { 123 | if(sqle.getErrorCode() == 50000 && sqle.getSQLState().equals("XJ015")) 124 | { 125 | Log.write(0, "Database was shut down properly."); 126 | System.out.println("Database was shut down properly."); 127 | } 128 | else 129 | { 130 | Log.write(2, "Database could not be shut down properly."); 131 | Log.write(2, sqle.getMessage()); 132 | System.err.println("Critical: Database could not be shut down properly."); 133 | System.err.println(sqle.getMessage()); 134 | } 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/main/java/me/dilley/services/TelnetServer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Linulator - The Linux Simulator 3 | * Copyright (C) 2014 Lloyd Dilley 4 | * http://www.linulator.org/ 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | 21 | package me.dilley.services; 22 | 23 | import java.io.BufferedReader; 24 | import java.io.BufferedWriter; 25 | import java.io.DataInputStream; 26 | import java.io.DataOutputStream; 27 | import java.io.InputStreamReader; 28 | import java.io.OutputStreamWriter; 29 | import java.io.IOException; 30 | import java.net.Socket; 31 | 32 | import me.dilley.Log; 33 | import me.dilley.Network; 34 | import me.dilley.OperatingSystem; 35 | import me.dilley.Shell; 36 | 37 | public class TelnetServer implements Runnable 38 | { 39 | protected Socket clientSocket = null; 40 | protected BufferedReader input = null; 41 | protected BufferedWriter output = null; 42 | protected DataInputStream ios = null; // for raw data 43 | protected DataOutputStream dos = null; // for raw data 44 | 45 | public final byte[] disableEcho = {(byte)0xFF, (byte)0xFB, (byte)0x01}; // IAC WILL ECHO 46 | public final byte[] enableEcho = {(byte)0xFF, (byte)0xFC, (byte)0x01}; // IAC WONT ECHO 47 | 48 | public TelnetServer(Socket clientSocket) 49 | { 50 | this.clientSocket = clientSocket; 51 | } 52 | 53 | public void run() 54 | { 55 | try 56 | { 57 | boolean isAuthenticated = false; 58 | String username = null; 59 | String password = null; 60 | String command = null; 61 | String result = null; 62 | byte attempts = 0; 63 | char sigil = '$'; 64 | input = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); 65 | output = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream())); 66 | ios = new DataInputStream(clientSocket.getInputStream()); 67 | dos = new DataOutputStream(clientSocket.getOutputStream()); 68 | 69 | Log.write(0, "TelnetServer: Connection received from " + clientSocket.getInetAddress().getHostAddress() + ':' + clientSocket.getPort() + '.'); 70 | 71 | // ToDo: Actually validate credentials later 72 | while(!isAuthenticated) 73 | { 74 | Network.send(output, "Login: ", false); 75 | username = Network.receive(input); 76 | if(username == null || username.length() == 0) 77 | continue; 78 | Network.send(output, "Password: ", false); 79 | dos.write(disableEcho, 0, disableEcho.length); 80 | password = Network.receive(input); 81 | dos.write(enableEcho, 0, enableEcho.length); 82 | Network.send(output, "", true); 83 | if(password == null || password.length() == 0) 84 | continue; 85 | isAuthenticated = true; 86 | } 87 | 88 | Log.write(0, "TelnetServer: " + username + " has logged in from " + clientSocket.getInetAddress().getHostAddress() + ':' + clientSocket.getPort() + '.'); 89 | 90 | // ToDo: display motd and last login 91 | ios.skipBytes(3); // ignore telnet negotiation 92 | if(username.equals("root")) 93 | sigil = '#'; 94 | while(true) 95 | { 96 | Network.send(output, username + "@" + OperatingSystem.getShortName() + sigil + " ", false); 97 | command = Network.receive(input); 98 | if(command == null || command.length() == 0 || command.trim().length() == 0) 99 | continue; 100 | if(command.trim().equals("exit") || command.trim().equals("logout")) 101 | { 102 | Log.write(0, "TelnetServer: " + username + " has logged out from " + clientSocket.getInetAddress().getHostAddress() + ':' + clientSocket.getPort() + '.'); 103 | break; 104 | } 105 | 106 | command = command.trim(); 107 | String[] args = command.split("\\s+"); 108 | result = Shell.execute(args); 109 | 110 | if(result == null || result.length() == 0) 111 | Network.send(output, "-bash: " + args[0] + ": command not found", true); 112 | else 113 | Network.send(output, result, true); 114 | } 115 | 116 | input.close(); 117 | output.close(); 118 | Log.write(0, "TelnetServer: Connection to " + clientSocket.getInetAddress().getHostAddress() + ':' + clientSocket.getPort() + " closed."); 119 | } 120 | catch(IOException ioe) 121 | { 122 | ioe.getMessage(); 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/main/java/me/dilley/services/HttpServer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Linulator - The Linux Simulator 3 | * Copyright (C) 2014 Lloyd Dilley 4 | * http://www.linulator.org/ 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | 21 | package me.dilley.services; 22 | 23 | import java.io.BufferedReader; 24 | import java.io.BufferedWriter; 25 | import java.io.DataInputStream; 26 | import java.io.DataOutputStream; 27 | import java.io.InputStreamReader; 28 | import java.io.OutputStreamWriter; 29 | import java.io.IOException; 30 | import java.net.Socket; 31 | import java.text.SimpleDateFormat; 32 | import java.util.Date; 33 | 34 | import me.dilley.Log; 35 | import me.dilley.Network; 36 | import me.dilley.OperatingSystem; 37 | 38 | public class HttpServer implements Runnable 39 | { 40 | public static final String version = "Apache/2.2.22"; 41 | protected Socket clientSocket = null; 42 | protected BufferedReader input = null; 43 | protected BufferedWriter output = null; 44 | 45 | public HttpServer(Socket clientSocket) 46 | { 47 | this.clientSocket = clientSocket; 48 | } 49 | 50 | public String getCode(int code) 51 | { 52 | String response = null; 53 | SimpleDateFormat timestampFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz"); 54 | Date currentTime = new Date(); 55 | String timestamp = timestampFormat.format(currentTime); 56 | 57 | switch(code) 58 | { 59 | case 404: 60 | break; 61 | case 500: 62 | response = "HTTP/1.1 500 Internal Server Error\r\n"; 63 | response += "Date: " + timestamp + "\r\n"; 64 | response += "Server: " + version + "\r\n"; 65 | response += "Connection: close\r\n"; 66 | response += "Content-Type: text/html; charset=iso-8859-1\r\n"; 67 | response += "\r\n"; 68 | response += "\r\n"; 69 | response += "\r\n"; 70 | response += "500 Internal Server Error\r\n"; 71 | response += "\r\n"; 72 | response += "

Internal Server Error

\r\n"; 73 | response += "

The server encountered an internal error or misconfiguration and was unable to complete your request.
\r\n"; 74 | response += "

\r\n"; 75 | response += "
\r\n"; 76 | response += "
" + version + " Server at " + OperatingSystem.getHostName() + " Port " + clientSocket.getLocalPort() + "
\r\n"; 77 | response += "\r\n"; 78 | break; 79 | default: 80 | // 400 bad request 81 | break; 82 | } 83 | 84 | return response; 85 | } 86 | 87 | public void run() 88 | { 89 | try 90 | { 91 | String request = null; 92 | input = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); 93 | output = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream())); 94 | 95 | Log.write(0, "HttpServer: Connection received from " + clientSocket.getInetAddress().getHostAddress() + ':' + clientSocket.getPort() + '.'); 96 | 97 | request = Network.receive(input); 98 | 99 | while(request == null || request.length() == 0) 100 | { 101 | request = Network.receive(input); 102 | continue; 103 | } 104 | 105 | //if(request.charAt(0) == ' ' || request.charAt(0) == '\t') 106 | //{ 107 | // Network.send(output, getCode(500), false); // send 500 instead of 400 for now 108 | //} 109 | 110 | request = request.replaceAll("\\s+$",""); // rtrim() 111 | String[] args = request.split("\\s+"); 112 | 113 | // Nothing is implemented yet, so just send 500 for everything for now 114 | // ToDo: Log all requests 115 | switch(args[0]) 116 | { 117 | case "GET": 118 | Network.send(output, getCode(500), false); 119 | break; 120 | case "PUT": 121 | Network.send(output, getCode(500), false); 122 | break; 123 | case "HEAD": 124 | Network.send(output, getCode(500), false); 125 | break; 126 | case "POST": 127 | Network.send(output, getCode(500), false); 128 | break; 129 | case "TRACE": 130 | Network.send(output, getCode(500), false); 131 | break; 132 | case "DELETE": 133 | Network.send(output, getCode(500), false); 134 | break; 135 | case "CONNECT": 136 | Network.send(output, getCode(500), false); 137 | break; 138 | case "OPTIONS": 139 | Network.send(output, getCode(500), false); 140 | break; 141 | default: 142 | Network.send(output, getCode(500), false); 143 | break; 144 | } 145 | 146 | input.close(); 147 | output.close(); 148 | Log.write(0, "HttpServer: Connection to " + clientSocket.getInetAddress().getHostAddress() + ':' + clientSocket.getPort() + " closed."); 149 | } 150 | catch(IOException ioe) 151 | { 152 | ioe.getMessage(); 153 | } 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/main/java/me/dilley/services/ChargenServer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Linulator - The Linux Simulator 3 | * Copyright (C) 2014 Lloyd Dilley 4 | * http://www.linulator.org/ 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | 21 | package me.dilley.services; 22 | 23 | import java.io.BufferedReader; 24 | import java.io.BufferedWriter; 25 | import java.io.DataInputStream; 26 | import java.io.DataOutputStream; 27 | import java.io.InputStreamReader; 28 | import java.io.OutputStreamWriter; 29 | import java.io.IOException; 30 | import java.net.DatagramPacket; 31 | import java.net.DatagramSocket; 32 | import java.net.Socket; 33 | import java.util.Random; 34 | 35 | import me.dilley.Log; 36 | import me.dilley.Network; 37 | 38 | public class ChargenServer implements Runnable 39 | { 40 | public static final int LINE_SIZE = 72; 41 | public static final int MAX_UDP_SIZE = 512; 42 | public static final int MAX_UDP_BUFFER_SIZE = 518; // evenly divisible by LINE_SIZE + 2 43 | protected byte[] buffer = null; 44 | protected boolean isUdp = false; 45 | protected Socket clientSocket = null; 46 | protected DatagramPacket packet = null; 47 | protected DatagramSocket serverSocket = null; 48 | protected BufferedReader input = null; 49 | protected BufferedWriter output = null; 50 | 51 | // TCP 52 | public ChargenServer(Socket clientSocket) 53 | { 54 | isUdp = false; 55 | this.clientSocket = clientSocket; 56 | } 57 | 58 | // UDP 59 | public ChargenServer(DatagramSocket serverSocket, byte[] buffer, DatagramPacket packet) 60 | { 61 | isUdp = true; 62 | this.serverSocket = serverSocket; 63 | this.buffer = buffer; 64 | this.packet = packet; 65 | } 66 | 67 | public void rotate(char[] pattern, int offset) 68 | { 69 | int i = 0; 70 | int j = 0; 71 | 72 | for(i = offset; j < LINE_SIZE; j++) 73 | { 74 | if(i > 126) // 126 = '~' (tilde ASCII code) 75 | i = 32; // reset to ' ' (space) 76 | 77 | pattern[j] = (char)i; 78 | i++; 79 | } 80 | 81 | // Append carriage return and newline 82 | pattern[LINE_SIZE] = '\r'; 83 | pattern[LINE_SIZE + 1] = '\n'; 84 | } 85 | 86 | // Returns random UDP buffer size per RFC (0 to 512 characters) 87 | public int populate(char[] pattern, char[] totalPattern, int offset) 88 | { 89 | int i = 0; 90 | int j = 0; 91 | Random random = new Random(); 92 | 93 | rotate(pattern, offset); // initialize pattern starting with '!' 94 | offset++; // proceed to next character in ASCII sequence 95 | 96 | // Initial array copy 97 | for(i = 0; i < (LINE_SIZE + 2); i++) 98 | totalPattern[i] = pattern[i]; 99 | 100 | // Complete the copy 101 | while(i < MAX_UDP_BUFFER_SIZE) 102 | { 103 | rotate(pattern, offset); 104 | offset++; 105 | for(j = 0; j < (LINE_SIZE + 2); j++) 106 | { 107 | totalPattern[i] = pattern[j]; 108 | i++; 109 | } 110 | } 111 | 112 | return random.nextInt(MAX_UDP_SIZE + 1); // rand(MAX_UDP_SIZE) is exclusive 113 | } 114 | 115 | public void run() 116 | { 117 | char[] pattern = new char[LINE_SIZE + 2]; // +2 for '\r' and '\n' 118 | int offset = 33; // ASCII character code -- starting with '!' 119 | 120 | try 121 | { 122 | if(!isUdp) 123 | { 124 | input = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); 125 | output = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream())); 126 | 127 | Log.write(0, "ChargenServer: TCP connection received from " + clientSocket.getInetAddress().getHostAddress() + ':' + clientSocket.getPort() + '.'); 128 | 129 | rotate(pattern, offset); // initialize pattern starting with '!' 130 | 131 | while(clientSocket != null && !clientSocket.isClosed()) 132 | { 133 | Network.send(output, new String(pattern), false); 134 | offset++; 135 | if(offset > 126) 136 | offset = 32; 137 | rotate(pattern, offset); 138 | //Thread.sleep(100); // sleep for 1/10th of a second if this service causes performance issues later 139 | } 140 | 141 | input.close(); 142 | output.close(); 143 | Log.write(0, "ChargenServer: TCP connection to " + clientSocket.getInetAddress().getHostAddress() + ':' + clientSocket.getPort() + " closed."); 144 | } 145 | else 146 | { 147 | char[] totalPattern = new char[MAX_UDP_BUFFER_SIZE]; 148 | Log.write(0, "ChargenServer: UDP packet received from " + packet.getAddress().getHostAddress() + ':' + packet.getPort() + '.'); 149 | int patternSize = populate(pattern, totalPattern, offset); 150 | totalPattern[patternSize] = '\r'; 151 | totalPattern[patternSize + 1] = '\n'; 152 | packet.setData(new String(totalPattern).getBytes(), 0, (patternSize + 2)); // +2 for '\r' and '\n' 153 | Network.sendTo(serverSocket, packet); 154 | } 155 | } 156 | // InterruptedException is required for Thread.sleep() 157 | //catch(InterruptedException ie) 158 | //{ 159 | // ie.getMessage(); 160 | //} 161 | catch(IOException ioe) 162 | { 163 | ioe.getMessage(); 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn ( ) { 37 | echo "$*" 38 | } 39 | 40 | die ( ) { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save ( ) { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /src/main/java/me/dilley/commands/Uname.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Linulator - The Linux Simulator 3 | * Copyright (C) 2014 Lloyd Dilley 4 | * http://www.linulator.org/ 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | 21 | package me.dilley.commands; 22 | 23 | import me.dilley.OperatingSystem; 24 | 25 | public class Uname extends Command 26 | { 27 | protected String result; 28 | 29 | public String execute(String[] args) 30 | { 31 | result = null; 32 | 33 | if(args.length == 1) 34 | result = "Linux"; 35 | 36 | if(args.length > 1) 37 | { 38 | if(findArg(args, "-a") || findArg(args, "--all")) 39 | { 40 | result = "Linux " + OperatingSystem.getShortName() + " " + OperatingSystem.KERNEL_RELEASE; 41 | result += " " + OperatingSystem.KERNEL_VERSION + " " + OperatingSystem.HARDWARE_ARCHITECTURE; 42 | result += " " + OperatingSystem.HARDWARE_ARCHITECTURE + " " + OperatingSystem.HARDWARE_ARCHITECTURE; 43 | result += " " + OperatingSystem.OPERATING_SYSTEM; 44 | return result; 45 | } 46 | if(findArg(args, "--help")) 47 | { 48 | result = "Usage: uname [OPTION]...\n"; 49 | result += "Print certain system information. With no OPTION, same as -s.\n\n"; 50 | result += " -a, --all print all information, in the following order,\n"; 51 | result += " except omit -p and -i if unknown:\n"; 52 | result += " -s, --kernel-name print the kernel name\n"; 53 | result += " -n, --nodename print the network node hostname\n"; 54 | result += " -r, --kernel-release print the kernel release\n"; 55 | result += " -v, --kernel-version print the kernel version\n"; 56 | result += " -m, --machine print the machine hardware name\n"; 57 | result += " -p, --processor print the processor type or \"unknown\"\n"; 58 | result += " -i, --hardware-platform print the hardware platform or \"unknown\"\n"; 59 | result += " -o, --operating-system print the operating system\n"; 60 | result += " --help display this help and exit\n"; 61 | result += " --version output version information and exit\n\n"; 62 | result += "Report uname bugs to bug-coreutils@gnu.org\n"; 63 | result += "GNU coreutils home page: \n"; 64 | result += "General help using GNU software: \n"; 65 | result += "For complete documentation, run: info coreutils 'uname invocation'\n"; 66 | return result; 67 | } 68 | if(findArg(args, "--version")) 69 | { 70 | result = "uname (GNU coreutils) 8.4\nCopyright (C) 2010 Free Software Foundation, Inc.\n"; 71 | result += "License GPLv3+: GNU GPL version 3 or later .\n"; 72 | result += "This is free software: you are free to change and redistribute it.\n"; 73 | result += "There is NO WARRANTY, to the extent permitted by law.\n\n"; 74 | result += "Written by David MacKenzie."; 75 | return result; 76 | } 77 | if(findArg(args, "-s") || findArg(args, "--kernel-name")) 78 | result = "Linux"; 79 | if(findArg(args, "-n") || findArg(args, "--nodename")) 80 | { 81 | if(result != null) 82 | result += " " + OperatingSystem.getShortName(); 83 | else 84 | result = OperatingSystem.getShortName(); 85 | } 86 | if(findArg(args, "-r") || findArg(args, "--kernel-release")) 87 | { 88 | if(result != null) 89 | result += " " + OperatingSystem.KERNEL_RELEASE; 90 | else 91 | result = OperatingSystem.KERNEL_RELEASE; 92 | } 93 | if(findArg(args, "-v") || findArg(args, "--kernel-version")) 94 | { 95 | if(result != null) 96 | result += " " + OperatingSystem.KERNEL_VERSION; 97 | else 98 | result = OperatingSystem.KERNEL_VERSION; 99 | } 100 | if(findArg(args, "-m") || findArg(args, "--machine")) 101 | { 102 | if(result != null) 103 | result += " " + OperatingSystem.HARDWARE_ARCHITECTURE; 104 | else 105 | result = OperatingSystem.HARDWARE_ARCHITECTURE; 106 | } 107 | if(findArg(args, "-p") || findArg(args, "--processor")) 108 | { 109 | if(result != null) 110 | result += " " + OperatingSystem.HARDWARE_ARCHITECTURE; 111 | else 112 | result = OperatingSystem.HARDWARE_ARCHITECTURE; 113 | } 114 | if(findArg(args, "-i") || findArg(args, "--hardware-platform")) 115 | { 116 | if(result != null) 117 | result += " " + OperatingSystem.HARDWARE_ARCHITECTURE; 118 | else 119 | result = OperatingSystem.HARDWARE_ARCHITECTURE; 120 | } 121 | if(findArg(args, "-o") || findArg(args, "--operating-system")) 122 | { 123 | if(result != null) 124 | result += " " + OperatingSystem.OPERATING_SYSTEM; 125 | else 126 | result = OperatingSystem.OPERATING_SYSTEM; 127 | } 128 | } 129 | 130 | return result; 131 | } 132 | 133 | public boolean findArg(String[] args, String arg) 134 | { 135 | boolean hasArg = false; 136 | 137 | for(int i = 1; i < args.length; i++) 138 | { 139 | if(args[i].equals(arg)) 140 | { 141 | hasArg = true; 142 | break; 143 | } 144 | if((args[i].length() > 2) && (args[i].charAt(0) == '-')) 145 | { 146 | for(int j = 1; j < args[i].length(); j++) 147 | { 148 | if(args[i].charAt(j) == arg.charAt(1)) 149 | { 150 | hasArg = true; 151 | break; 152 | } 153 | } 154 | } 155 | } 156 | 157 | return hasArg; 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Linulator 2 | ========= 3 | 4 | [![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](http://www.gnu.org/licenses/gpl-3.0) 5 | [![Build Status](https://travis-ci.org/ldilley/linulator.svg?branch=master)](https://travis-ci.org/ldilley/linulator) 6 | 7 | ### About 8 | 9 | The goal of this project is to create a fake Linux environment with configurable network ports that 10 | can be opened. Linulator can be used as a learning or training environment or even as a honeypot. 11 | While operating as a honeypot, it will hopefully attract potential attackers so that their methods 12 | can be learned. The environment can be frozen and saved to disk for forensic analysis. Extensive 13 | logging can also be employed. 14 | 15 | **Note:** This project is still very much a work in progress! There is no virtual filesystem in place yet, 16 | SSH is not implemented, and many commands still need to be added. 17 | 18 | Since the network services Linulator uses typically run on privileged ports and it is not recommended 19 | that you run Linulator as root, you should run the services on ports >1024 and configure your firewall 20 | to forward or redirect the traffic to the higher-numbered ports. You may also want to run the program 21 | within a virtual machine, container, zone, jail, or chroot environment for added security on the host 22 | system. 23 | 24 | ### Installation 25 | 26 | **Prerequisites:** [Apache Ant](http://ant.apache.org) and a [JDK](http://openjdk.java.net) 27 | 28 | 1.) Build the jar: 29 | ``` 30 | $ ant 31 | ``` 32 | 33 | 2.) Change to the dist directory: 34 | ``` 35 | $ cd dist 36 | ``` 37 | 38 | 3.) Modify the configuration file to your liking: 39 | ``` 40 | $ vi linulator.properties 41 | ``` 42 | 43 | 4.) Optionally modify the security policy to meet your needs (the default should suffice): 44 | ``` 45 | $ vi linulator.policy 46 | ``` 47 | 48 | 5.) Forward or redirect any ports you want to use with Linulator. If you have a firewall/NAT appliance 49 | or broadband router, you can simply have Linulator listen on higher-numbered ports and forward the 50 | standard port numbers. For example: outside:23 -> inside:10023 for telnet. If the host is reachable 51 | from the Internet and Linulator is listening on nonstandard unprivileged ports, you will want to 52 | redirect traffic from the standard ports. This can be done using iptables, ipfw, or similar. Replace 53 | the addresses, interfaces, and ports in the examples below with your own. 54 | 55 | **FreeBSD** 56 | Modify */etc/rc.conf* and add the following line: 57 | ``` 58 | pf_enable="YES" 59 | ``` 60 | 61 | Add similar lines for each service to */etc/pf.conf*: 62 | ``` 63 | rdr pass on em0 proto tcp from any to 192.168.1.7 port 10023 -> 192.168.1.7 port 23 64 | ``` 65 | 66 | Start the packet filter: 67 | ``` 68 | # /etc/rc.d/pf start 69 | ``` 70 | 71 | Confirm the rules are in memory: 72 | ``` 73 | # pfctl -sn 74 | ``` 75 | 76 | **Linux** 77 | Run similar commands for each service (see the documentation for your distribution to make the rule persistent): 78 | ``` 79 | # iptables -t nat -I PREROUTING --src 0/0 --dst 192.168.1.7 -p tcp --dport 23 -j REDIRECT --to-ports 10023 80 | ``` 81 | 82 | Confirm the rules are in memory: 83 | ``` 84 | # iptables -L 85 | ``` 86 | 87 | **OS X <= 10.6 (Snow Leopard)** 88 | Run similar commands for each service (and replace the rule numbers with your own): 89 | ``` 90 | $ sudo ipfw add 101 fwd 192.168.1.7,10023 tcp from any to me 23 91 | ``` 92 | 93 | Confirm the rules are in memory: 94 | ``` 95 | $ sudo ipfw list 96 | ``` 97 | 98 | To make the rules persistent, you can add them to */etc/ipfw.conf* and create a launch agent or daemon 99 | to load them automatically (use a search engine for details.) 100 | 101 | **OS X 10.7 (Lion), 10.8 (Mountain Lion), and 10.9 (Mavericks)** 102 | Note: *ipfw* was deprecated in 10.7 (Lion). Use *pf* instead. 103 | 104 | Add similar lines for each service to */etc/pf.conf*: 105 | ``` 106 | rdr on en0 inet proto tcp to 192.168.1.7 port 23 -> 192.168.1.7 port 10023 107 | ``` 108 | 109 | Load the changes: 110 | ``` 111 | $ sudo pfctl -f /etc/pf.conf 112 | ``` 113 | 114 | Enable the packet filter: 115 | ``` 116 | $ sudo pfctl -e 117 | ``` 118 | 119 | Confirm the rules are in memory: 120 | ``` 121 | $ sudo pfctl -sn 122 | ``` 123 | 124 | **Solaris** 125 | Add similar lines for each service to */etc/ipf/ipnat.conf*: 126 | ``` 127 | rdr e1000g0 from any to 192.168.1.7 port = 23 -> 192.168.1.7 port 10023 tcp 128 | ``` 129 | 130 | Enable the IP filter: 131 | ``` 132 | # svcadm enable ipfilter 133 | ``` 134 | 135 | Confirm the rules are in memory: 136 | ``` 137 | # ipnat -l 138 | ``` 139 | 140 | 6.) Launch Linulator (where X.X.X is the version and YYYYMMDD is the build date): 141 | ``` 142 | $ java -jar linulator-X.X.X-YYYYMMDD.jar 143 | ``` 144 | 145 | 7.) Enjoy! 146 | 147 | ### FAQ 148 | 149 | **Why create a fake Linux environment when you can have a real one?** 150 | 151 | This project was mainly created for learning purposes. The aim was to also create something even 152 | more disposable than a virtual machine with less setup time. 153 | 154 | **If used as a honeypot, how can an attacker gain access to the Linulator environment?** 155 | 156 | The idea is to allow the attacker access via FTP and telnet (and later SSH) after they discover a 157 | user/password combination with the password being intentionally weak. They can then navigate around 158 | the artificial system while Linulator logs their moves to the host system. They may attempt to remove 159 | log files, modify files, plant rootkits, and perform other malicious activities. All of this behavior 160 | can be tracked and does not harm the host operating system. 161 | 162 | **Why Java?** 163 | 164 | C was originally intended to be used for this project, but Java has some benefits worth taking 165 | advantage of. Java has a large API. As a result, there is unlikely a need to go in search of 166 | third-party libraries or re-invent the wheel. Java is also more portable and eliminates the 167 | requirement for things like #ifdef and cmake. 168 | 169 | Another very important and particularly applicable aspect for Linulator is security. Java performs 170 | bounds checking on arrays which can significantly reduce the risk of buffer overflows (unless there 171 | is a vulnerability in the JVM of course.) The SecurityManager class is additionally essential since 172 | it limits what users can do within Linulator. Lastly, a consequence of automatic memory management 173 | is that there are no explicit pointers. This increases safety by preventing dangling/wild pointers 174 | and defends against memory leaks. 175 | -------------------------------------------------------------------------------- /src/main/java/me/dilley/File.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Linulator - The Linux Simulator 3 | * Copyright (C) 2014 Lloyd Dilley 4 | * http://www.linulator.org/ 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | 21 | package me.dilley; 22 | 23 | import java.sql.Blob; 24 | import java.sql.Clob; 25 | import java.sql.SQLException; 26 | 27 | // ToDo: This class needs to be thread safe 28 | class File 29 | { 30 | // File types 31 | public static final int BLOCK = 0; 32 | public static final int CHARACTER = 1; 33 | public static final int DIRECTORY = 2; 34 | public static final int FIFO = 3; 35 | public static final int LINK = 4; 36 | public static final int REGULAR = 5; 37 | public static final int SOCKET = 6; 38 | 39 | private String path; // name of file prefixed with absolute path 40 | private String name; // base name of file 41 | private long inode; // unique ID 42 | private byte type; // file type 43 | private int mode; // octal permissions 44 | private int linkCount; // number of hard links 45 | private int uid; // user ID of owner 46 | private int gid; // group ID of owner 47 | private long size; // size of file in bytes 48 | private long atime; // last access time 49 | private long ctime; // create time 50 | private long mtime; // last modify time 51 | private String link; // link path 52 | private Blob binaryContents; // binary data 53 | private Clob textContents; // text data 54 | private boolean isBlock; // block device 55 | private boolean isCharacter; // character device 56 | private boolean isDirectory; 57 | private boolean isFifo; // queue 58 | private boolean isLink; 59 | private boolean isRegular; 60 | private boolean isSocket; 61 | 62 | public File() 63 | { 64 | // default constructor 65 | } 66 | 67 | // For use with sys_open, touch, mkdir, etc. 68 | public File(String path, String name, long inode, int mode, int uid, int gid, long size, int type) 69 | { 70 | this.path = path; 71 | this.name = name; 72 | this.inode = inode; 73 | this.mode = mode; 74 | this.uid = uid; 75 | this.gid = gid; 76 | this.size = size; 77 | this.linkCount = 0; 78 | atime = System.currentTimeMillis() / 1000; 79 | ctime = atime; 80 | mtime = atime; 81 | isBlock = false; 82 | isCharacter = false; 83 | isDirectory = false; 84 | isFifo = false; 85 | isLink = false; 86 | isRegular = false; 87 | isSocket = false; 88 | 89 | switch(type) 90 | { 91 | case 0: 92 | isBlock = true; 93 | break; 94 | case 1: 95 | isCharacter = true; 96 | break; 97 | case 2: 98 | isDirectory = true; 99 | break; 100 | case 3: 101 | isFifo = true; 102 | break; 103 | case 4: 104 | isLink = true; 105 | break; 106 | case 5: 107 | isRegular = true; 108 | break; 109 | case 6: 110 | isSocket = true; 111 | break; 112 | default: 113 | isRegular = true; 114 | break; 115 | } 116 | } 117 | 118 | public String getPath() 119 | { 120 | return path; 121 | } 122 | 123 | public void setPath(String path) 124 | { 125 | this.path = path; 126 | } 127 | 128 | public String getName() 129 | { 130 | return name; 131 | } 132 | 133 | public void setName(String name) 134 | { 135 | this.name = name; 136 | } 137 | 138 | public long getInode() 139 | { 140 | return inode; 141 | } 142 | 143 | public void setInode(long inode) 144 | { 145 | this.inode = inode; 146 | } 147 | 148 | public byte getType() 149 | { 150 | return type; 151 | } 152 | 153 | public void setType(byte type) 154 | { 155 | this.type = type; 156 | } 157 | 158 | public int getMode() 159 | { 160 | return mode; 161 | } 162 | 163 | public void setMode(int mode) 164 | { 165 | this.mode = mode; 166 | } 167 | 168 | public int getLinkCount() 169 | { 170 | return this.linkCount; 171 | } 172 | 173 | public void setLinkCount(int linkCount) 174 | { 175 | this.linkCount = linkCount; 176 | } 177 | 178 | public int getUid() 179 | { 180 | return uid; 181 | } 182 | 183 | public void setUid(int uid) 184 | { 185 | this.uid = uid; 186 | } 187 | 188 | public int getGid() 189 | { 190 | return gid; 191 | } 192 | 193 | public void setGid(int gid) 194 | { 195 | this.gid = gid; 196 | } 197 | 198 | public long getSize() 199 | { 200 | return size; 201 | } 202 | 203 | public void setSize(long size) 204 | { 205 | this.size = size; 206 | } 207 | 208 | public long getAtime() 209 | { 210 | return atime; 211 | } 212 | 213 | public void setAtime(long atime) 214 | { 215 | this.atime = atime; 216 | } 217 | 218 | public long getCtime() 219 | { 220 | return ctime; 221 | } 222 | 223 | public void setCtime(long ctime) 224 | { 225 | this.ctime = ctime; 226 | } 227 | 228 | public long getMtime() 229 | { 230 | return mtime; 231 | } 232 | 233 | public void setMtime(long mtime) 234 | { 235 | this.mtime = mtime; 236 | } 237 | 238 | public String getLink() 239 | { 240 | return link; 241 | } 242 | 243 | public void setLink(String link) 244 | { 245 | this.link = link; 246 | } 247 | 248 | public void setBinaryContents(Blob contents) 249 | { 250 | binaryContents = contents; 251 | } 252 | 253 | public void setTextContents(Clob contents) 254 | { 255 | textContents = contents; 256 | } 257 | 258 | public boolean isBlock() 259 | { 260 | return isBlock; 261 | } 262 | 263 | public boolean isCharacter() 264 | { 265 | return isCharacter; 266 | } 267 | 268 | public boolean isDirectory() 269 | { 270 | return isDirectory; 271 | } 272 | 273 | public boolean isFifo() 274 | { 275 | return isFifo; 276 | } 277 | 278 | public boolean isLink() 279 | { 280 | return isLink; 281 | } 282 | 283 | public boolean isRegular() 284 | { 285 | return isRegular; 286 | } 287 | 288 | public boolean isSocket() 289 | { 290 | return isSocket; 291 | } 292 | 293 | public long calculateSize() 294 | { 295 | long size = 0; 296 | try 297 | { 298 | if(binaryContents != null) 299 | size = binaryContents.length(); 300 | else if(textContents != null) 301 | size = textContents.length(); 302 | else 303 | size = 0; 304 | } 305 | catch(SQLException sqle) 306 | { 307 | Log.write(1, "Unable to determine size of: " + path); 308 | Log.write(1, sqle.getMessage()); 309 | Log.write(1, "Setting size to 0 bytes."); 310 | size = 0; 311 | } 312 | return size; 313 | } 314 | } 315 | -------------------------------------------------------------------------------- /src/main/java/me/dilley/Errors.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Linulator - The Linux Simulator 3 | * Copyright (C) 2014 Lloyd Dilley 4 | * http://www.linulator.org/ 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General public License for more details. 15 | * 16 | * You should have received a copy of the GNU General public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | 21 | package me.dilley; 22 | 23 | // These errors are taken from the Linux errno.h file 24 | 25 | public enum Errors 26 | { 27 | EPERM(1, "Operation not permitted"), 28 | ENOENT(2, "No such file or directory"), 29 | ESRCH(3, "No such process"), 30 | EINTR(4, "Interrupted system call"), 31 | EIO(5, "I/O error"), 32 | ENXIO(6, "No such device or address"), 33 | E2BIG(7, "Argument list too long"), 34 | ENOEXEC(8, "Exec format error"), 35 | EBADF(9, "Bad file number"), 36 | ECHILD(10, "No child processes"), 37 | EAGAIN(11, "Try again"), 38 | ENOMEM(12, "Out of memory"), 39 | EACCES(13, "Permission denied"), 40 | EFAULT(14, "Bad address"), 41 | ENOTBLK(15, "Block device required"), 42 | EBUSY(16, "Device or resource busy"), 43 | EEXIST(17, "File exists"), 44 | EXDEV(18, "Cross-device link"), 45 | ENODEV(19, "No such device"), 46 | ENOTDIR(20, "Not a directory"), 47 | EISDIR(21, "Is a directory"), 48 | EINVAL(22, "Invalid argument"), 49 | ENFILE(23, "File table overflow"), 50 | EMFILE(24, "Too many open files"), 51 | ENOTTY(25, "Not a typewriter"), 52 | ETXTBSY(26, "Text file busy"), 53 | EFBIG(27, "File too large"), 54 | ENOSPC(28, "No space left on device"), 55 | ESPIPE(29, "Illegal seek"), 56 | EROFS(30, "Read-only file system"), 57 | EMLINK(31, "Too many links"), 58 | EPIPE(32, "Broken pipe"), 59 | EDOM(33, "Math argument out of domain of func"), 60 | ERANGE(34, "Math result not representable"), 61 | EDEADLK(35, "Resource deadlock would occur"), 62 | ENAMETOOLONG(36, "File name too long"), 63 | ENOLCK(37, "No record locks available"), 64 | ENOSYS(38, "Function not implemented"), 65 | ENOTEMPTY(39, "Directory not empty"), 66 | ELOOP(40, "Too many symbolic links encountered"), 67 | EWOULDBLOCK(41, "Try again"), // EAGAIN 68 | ENOMSG(42, "No message of desired type"), 69 | EIDRM(43, "Identifier removed"), 70 | ECHRNG(44, "Channel number out of range"), 71 | EL2NSYNC(45, "Level 2 not synchronized"), 72 | EL3HLT(46, "Level 3 halted"), 73 | EL3RST(47, "Level 3 reset"), 74 | ELNRNG(48, "Link number out of range"), 75 | EUNATCH(49, "Protocol driver not attached"), 76 | ENOCSI(50, "No CSI structure available"), 77 | EL2HLT(51, "Level 2 halted"), 78 | EBADE(52, "Invalid exchange"), 79 | EBADR(53, "Invalid request descriptor"), 80 | EXFULL(54, "Exchange full"), 81 | ENOANO(55, "No anode"), 82 | EBADRQC(56, "Invalid request code"), 83 | EBADSLT(57, "Invalid slot"), 84 | EDEADLOCK(58, "Resource deadlock would occur"), // EDEADLK 85 | EBFONT(59, "Bad font file format"), 86 | ENOSTR(60, "Device not a stream"), 87 | ENODATA(61, "No data available"), 88 | ETIME(62, "Timer expired"), 89 | ENOSR(63, "Out of streams resources"), 90 | ENONET(64, "Machine is not on the network"), 91 | ENOPKG(65, "Package not installed"), 92 | EREMOTE(66, "Object is remote"), 93 | ENOLINK(67, "Link has been severed"), 94 | EADV(68, "Advertise error"), 95 | ESRMNT(69, "Srmount error"), 96 | ECOMM(70, "Communication error on send"), 97 | EPROTO(71, "Protocol error"), 98 | EMULTIHOP(72, "Multihop attempted"), 99 | EDOTDOT(73, "RFS specific error"), 100 | EBADMSG(74, "Not a data message"), 101 | EOVERFLOW(75, "Value too large for defined data type"), 102 | ENOTUNIQ(76, "Name not unique on network"), 103 | EBADFD(77, "File descriptor in bad state"), 104 | EREMCHG(78, "Remote address changed"), 105 | ELIBACC(79, "Cannot access a needed shared library"), 106 | ELIBBAD(80, "Accessing a corrupted shared library"), 107 | ELIBSCN(81, ".lib section in a.out corrupted"), 108 | ELIBMAX(82, "Attempted to link in too many shared libraries"), 109 | ELIBEXEC(83, "Cannot exec a shared library directly"), 110 | EILSEQ(84, "Illegal byte sequence"), 111 | ERESTART(85, "Interrupted system call should be restarted"), 112 | ESTRPIPE(86, "Streams pipe error"), 113 | EUSERS(87, "Too many users"), 114 | ENOTSOCK(88, "Socket operation on non-socket"), 115 | EDESTADDRREQ(89, "Destination address required"), 116 | EMSGSIZE(90, "Message too long"), 117 | EPROTOTYPE(91, "Protocol wrong type for socket"), 118 | ENOPROTOOPT(92, "Protocol not available"), 119 | EPROTONOSUPPORT(93, "Protocol not supported"), 120 | ESOCKTNOSUPPORT(94, "Socket type not supported"), 121 | EOPNOTSUPP(95, "Operation not supported on transport endpoint"), 122 | EPFNOSUPPORT(96, "Protocol family not supported"), 123 | EAFNOSUPPORT(97, "Address family not supported by protocol"), 124 | EADDRINUSE(98, "Address already in use"), 125 | EADDRNOTAVAIL(99, "Cannot assign requested address"), 126 | ENETDOWN(100, "Network is down"), 127 | ENETUNREACH(101, "Network is unreachable"), 128 | ENETRESET(102, "Network dropped connection because of reset"), 129 | ECONNABORTED(103, "Software caused connection abort"), 130 | ECONNRESET(104, "Connection reset by peer"), 131 | ENOBUFS(105, "No buffer space available"), 132 | EISCONN(106, "Transport endpoint is already connected"), 133 | ENOTCONN(107, "Transport endpoint is not connected"), 134 | ESHUTDOWN(108, "Cannot send after transport endpoint shutdown"), 135 | ETOOMANYREFS(109, "Too many references: cannot splice"), 136 | ETIMEDOUT(110, "Connection timed out"), 137 | ECONNREFUSED(111, "Connection refused"), 138 | EHOSTDOWN(112, "Host is down"), 139 | EHOSTUNREACH(113, "No route to host"), 140 | EALREADY(114, "Operation already in progress"), 141 | EINPROGRESS(115, "Operation now in progress"), 142 | ESTALE(116, "Stale NFS file handle"), 143 | EUCLEAN(117, "Structure needs cleaning"), 144 | ENOTNAM(118, "Not a XENIX named type file"), 145 | ENAVAIL(119, "No XENIX semaphores available"), 146 | EISNAM(120, "Is a named type file"), 147 | EREMOTEIO(121, "Remote I/O error"), 148 | EDQUOT(122, "Quota exceeded"), 149 | ENOMEDIUM(123, "No medium found"), 150 | EMEDIUMTYPE(124, "Wrong medium type"), 151 | ECANCELED(125, "Operation canceled"), 152 | ENOKEY(126, "Required key not available"), 153 | EKEYEXPIRED(127, "Key has expired"), 154 | EKEYREVOKED(128, "Key has been revoked"), 155 | EKEYREJECTED(129, "Key was rejected by service"), 156 | EOWNERDEAD(130, "Owner died"), 157 | ENOTRECOVERABLE(131, "State not recoverable"), 158 | ERFKILL(132, "Operation not possible due to RF kill"), 159 | EHWPOISON(133, "Memory page has hardware error"); 160 | 161 | private final int errorNumber; 162 | private final String errorMessage; 163 | 164 | private Errors(int errorNumber, String errorMessage) 165 | { 166 | this.errorNumber = errorNumber; 167 | this.errorMessage = errorMessage; 168 | } 169 | 170 | public int getErrorNumber() 171 | { 172 | return errorNumber; 173 | } 174 | 175 | public String getErrorMessage() 176 | { 177 | return errorMessage; 178 | } 179 | 180 | public String getErrorCode() 181 | { 182 | return this.toString(); 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /lib/Derby_Apache_2.0_License.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /src/main/java/me/dilley/Linulator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Linulator - The Linux Simulator 3 | * Copyright (C) 2014 Lloyd Dilley 4 | * http://www.linulator.org/ 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | 21 | package me.dilley; 22 | 23 | import java.sql.Blob; 24 | import java.sql.Clob; 25 | import java.sql.ResultSet; 26 | import java.sql.SQLException; 27 | import java.util.HashMap; 28 | import java.util.InputMismatchException; 29 | import java.util.Scanner; 30 | 31 | class Linulator 32 | { 33 | public static final String VERSION = "Linulator v0.1"; 34 | static Scanner scanner = null; 35 | static String input = null; 36 | static int option = 0; 37 | 38 | public static void main(String[] args) 39 | { 40 | System.out.println(VERSION); 41 | System.out.println("Enforcing security policy..."); 42 | System.setProperty("java.security.policy", "cfg" + System.getProperty("file.separator") + "linulator.policy"); 43 | SecurityManager securityManager = new SecurityManager(); 44 | System.setSecurityManager(securityManager); 45 | //securityManager.checkExec("/bin/ls"); // prove we cannot execute commands on the host OS 46 | Log.write(0, VERSION); 47 | Log.write(0, "Security policy enforced."); 48 | System.out.println("Parsing configuration file..."); 49 | Log.write(0, "Parsing configuration file..."); 50 | Config config = new Config(); 51 | config.parseConfig(); 52 | System.out.println("Setting hostname..."); 53 | Log.write(0, "Setting hostname..."); 54 | OperatingSystem.setHostName(config.getHostName()); 55 | OperatingSystem.setShortName(config.getShortName()); 56 | OperatingSystem.setDomainName(config.getDomainName()); 57 | System.out.println("Populating commands..."); 58 | Log.write(0, "Populating commands..."); 59 | Shell.populateCommands(); 60 | System.out.println("Connecting to database..."); 61 | Log.write(0, "Connecting to database..."); 62 | Database.connect(); 63 | System.out.println("Loading filesystem (this can take some time)..."); 64 | Log.write(0, "Loading filesystem (this can take some time)..."); 65 | loadFilesystem(); 66 | System.out.println("Starting network services..."); 67 | Log.write(0, "Starting network services..."); 68 | startServices(config); 69 | System.out.println("Linulator started successfully."); 70 | Log.write(0, "Linulator started successfully."); 71 | mainMenu(config); 72 | } 73 | 74 | public static void loadFilesystem() 75 | { 76 | try 77 | { 78 | int currentProgress = 0; 79 | int formerProgress = -1; 80 | int percentage = 0; 81 | long currentEntry = 0; 82 | long totalEntries = 0; 83 | double entryPerPercent = 0.00; 84 | File file = null; 85 | Filesystem filesystem = null; 86 | ResultSet resultSet = null; 87 | HashMap fileMap = new HashMap(); 88 | 89 | // Determine number of directories and files in database 90 | String query = "SELECT COUNT(*) FROM filesystem"; 91 | resultSet = Database.query(query); 92 | if(resultSet.next()) 93 | { 94 | totalEntries = resultSet.getInt(1); 95 | System.out.println(totalEntries + " entries detected."); 96 | } 97 | 98 | // Set root of filesystem 99 | query = "SELECT path, name, inode, type, mode, linkcount, uid, gid, atime, ctime, mtime, link, bindata, txtdata FROM filesystem WHERE path = '/'"; 100 | resultSet = Database.query(query); 101 | if(resultSet.next()) 102 | { 103 | file = new File(); 104 | file.setPath(resultSet.getString(1)); 105 | file.setName(resultSet.getString(2)); 106 | file.setInode(resultSet.getLong(3)); 107 | file.setType(resultSet.getByte(4)); 108 | file.setMode(resultSet.getInt(5)); 109 | file.setLinkCount(resultSet.getInt(6)); 110 | file.setUid(resultSet.getInt(7)); 111 | file.setGid(resultSet.getInt(8)); 112 | file.setAtime(resultSet.getLong(9)); 113 | file.setCtime(resultSet.getLong(10)); 114 | file.setMtime(resultSet.getLong(11)); 115 | file.setLink(resultSet.getString(12)); 116 | file.setBinaryContents(resultSet.getBlob(13)); 117 | file.setTextContents(resultSet.getClob(14)); 118 | file.setSize(file.calculateSize()); 119 | //filesystem = new Filesystem(new Node(file)); 120 | } 121 | else 122 | { 123 | System.err.println("Critical: Unable to set root of filesystem."); 124 | Log.write(2, "Unable to set root of filesystem."); 125 | } 126 | 127 | // Get everything else 128 | query = "SELECT path, name, inode, type, mode, linkcount, uid, gid, atime, ctime, mtime, link, bindata, txtdata FROM filesystem"; 129 | resultSet = Database.query(query); 130 | entryPerPercent = 100.00 / totalEntries; 131 | System.out.print("Progress: "); 132 | while(resultSet.next()) 133 | { 134 | currentEntry++; 135 | currentProgress = (int)Math.round(entryPerPercent * currentEntry); 136 | if((currentProgress % 5 == 0) && (currentProgress != formerProgress)) 137 | { 138 | if(percentage == 0) 139 | System.out.print("0%"); 140 | else 141 | System.out.print("..." + percentage + '%'); 142 | percentage += 5; 143 | formerProgress = currentProgress; 144 | } 145 | file = new File(); 146 | file.setPath(resultSet.getString(1)); 147 | file.setName(resultSet.getString(2)); 148 | file.setInode(resultSet.getLong(3)); 149 | file.setType(resultSet.getByte(4)); 150 | file.setMode(resultSet.getInt(5)); 151 | file.setLinkCount(resultSet.getInt(6)); 152 | file.setUid(resultSet.getInt(7)); 153 | file.setGid(resultSet.getInt(8)); 154 | file.setAtime(resultSet.getLong(9)); 155 | file.setCtime(resultSet.getLong(10)); 156 | file.setMtime(resultSet.getLong(11)); 157 | file.setLink(resultSet.getString(12)); 158 | file.setBinaryContents(resultSet.getBlob(13)); 159 | file.setTextContents(resultSet.getClob(14)); 160 | file.setSize(file.calculateSize()); 161 | // Do not hold file contents in memory. That could exhaust memory. When needed, file contents will be accessed from disk. 162 | Blob binJunk = null; 163 | file.setBinaryContents(binJunk); 164 | Clob txtJunk = null; 165 | file.setTextContents(txtJunk); 166 | if(!file.getPath().equals("/")) // ignore adding root since we did that first 167 | fileMap.put(file.getPath(), file); 168 | //filesystem.add(file); 169 | } 170 | Database.endQuery(); 171 | System.out.println(); 172 | System.out.println("Filesytem size: " + fileMap.size()); 173 | System.out.println(fileMap.get("/tmp").getPath()); 174 | System.out.println(fileMap.get("/usr/local/bin").getPath()); 175 | 176 | //filesystem.showTree(); 177 | } 178 | catch(SQLException sqle) 179 | { 180 | System.err.println("Critical: Unable to load filesystem."); 181 | Log.write(2, "Unable to load filesystem."); 182 | System.err.println(sqle.getMessage()); 183 | Log.write(2, sqle.getMessage()); 184 | System.exit(1); 185 | } 186 | } 187 | 188 | public static void startServices(Config config) 189 | { 190 | if(config.getEchoPort() != 0) 191 | { 192 | System.out.println("Starting echo server..."); 193 | Log.write(0, "Starting echo server..."); 194 | TcpServer TcpEchoServer = new TcpServer(config.getListenAddress(), config.getEchoPort(), "echo"); 195 | new Thread(TcpEchoServer).start(); 196 | UdpServer UdpEchoServer = new UdpServer(config.getListenAddress(), config.getEchoPort(), "echo"); 197 | new Thread(UdpEchoServer).start(); 198 | } 199 | if(config.getDiscardPort() != 0) 200 | { 201 | System.out.println("Starting discard server..."); 202 | Log.write(0, "Starting discard server..."); 203 | TcpServer TcpDiscardServer = new TcpServer(config.getListenAddress(), config.getDiscardPort(), "discard"); 204 | new Thread(TcpDiscardServer).start(); 205 | UdpServer UdpDiscardServer = new UdpServer(config.getListenAddress(), config.getDiscardPort(), "discard"); 206 | new Thread(UdpDiscardServer).start(); 207 | } 208 | if(config.getDaytimePort() != 0) 209 | { 210 | System.out.println("Starting daytime server..."); 211 | Log.write(0, "Starting daytime server..."); 212 | TcpServer TcpDaytimeServer = new TcpServer(config.getListenAddress(), config.getDaytimePort(), "daytime"); 213 | new Thread(TcpDaytimeServer).start(); 214 | UdpServer UdpDaytimeServer = new UdpServer(config.getListenAddress(), config.getDaytimePort(), "daytime"); 215 | new Thread(UdpDaytimeServer).start(); 216 | } 217 | if(config.getChargenPort() != 0) 218 | { 219 | System.out.println("Starting chargen server..."); 220 | Log.write(0, "Starting chargen server..."); 221 | TcpServer TcpChargenServer = new TcpServer(config.getListenAddress(), config.getChargenPort(), "chargen"); 222 | new Thread(TcpChargenServer).start(); 223 | UdpServer UdpChargenServer = new UdpServer(config.getListenAddress(), config.getChargenPort(), "chargen"); 224 | new Thread(UdpChargenServer).start(); 225 | } 226 | if(config.getTimePort() != 0) 227 | { 228 | System.out.println("Starting time server..."); 229 | Log.write(0, "Starting time server..."); 230 | TcpServer TcpTimeServer = new TcpServer(config.getListenAddress(), config.getTimePort(), "time"); 231 | new Thread(TcpTimeServer).start(); 232 | UdpServer UdpTimeServer = new UdpServer(config.getListenAddress(), config.getTimePort(), "time"); 233 | new Thread(UdpTimeServer).start(); 234 | } 235 | if(config.getTelnetPort() != 0) 236 | { 237 | System.out.println("Starting telnet server..."); 238 | Log.write(0, "Starting telnet server..."); 239 | TcpServer telnetServer = new TcpServer(config.getListenAddress(), config.getTelnetPort(), "telnet"); 240 | new Thread(telnetServer).start(); 241 | } 242 | if(config.getHttpPort() != 0) 243 | { 244 | System.out.println("Starting HTTP server..."); 245 | Log.write(0, "Starting HTTP server..."); 246 | TcpServer httpServer = new TcpServer(config.getListenAddress(), config.getHttpPort(), "http"); 247 | new Thread(httpServer).start(); 248 | } 249 | } 250 | 251 | public static void mainMenu(Config config) 252 | { 253 | while(true) 254 | { 255 | try 256 | { 257 | System.out.println("\nAdministrative Console"); 258 | System.out.println("======================\n"); 259 | System.out.println("1.) Open console"); 260 | System.out.println("2.) View logs"); 261 | System.out.println("3.) Freeze environment"); 262 | System.out.println("4.) Show configuration"); 263 | System.out.println("5.) Shutdown Linulator\n"); 264 | System.out.print("> "); 265 | 266 | scanner = new Scanner(System.in); 267 | input = scanner.nextLine(); 268 | 269 | if(input == null || input.trim().length() == 0) 270 | continue; 271 | 272 | input = input.trim(); 273 | option = Integer.parseInt(input); 274 | } 275 | catch(NumberFormatException nfe) 276 | { 277 | System.out.println("Invalid option"); 278 | continue; 279 | } 280 | catch(InputMismatchException ime) 281 | { 282 | System.out.println("Invalid option"); 283 | continue; 284 | } 285 | 286 | switch(option) 287 | { 288 | case 1: 289 | openConsole(); 290 | break; 291 | case 2: 292 | Log.viewLog(); 293 | break; 294 | case 3: 295 | System.out.println("Not implemented"); 296 | break; 297 | case 4: 298 | config.showConfig(); 299 | break; 300 | case 5: 301 | System.out.println("Shutting down..."); 302 | Log.write(0, "Shutting down..."); 303 | // ToDo: stop service threads and commit database changes 304 | Database.shutdown(); 305 | System.exit(0); 306 | break; 307 | default: 308 | System.out.println("Invalid option"); 309 | break; 310 | } 311 | } 312 | } 313 | 314 | public static void openConsole() 315 | { 316 | Scanner scanner = null; 317 | String command = null; 318 | String result = null; 319 | 320 | while(true) 321 | { 322 | scanner = new Scanner(System.in); 323 | System.out.print("root@" + OperatingSystem.getShortName() + "# "); 324 | command = scanner.nextLine(); 325 | 326 | if(command == null || command.length() == 0) 327 | continue; 328 | if(command.trim().equals("exit") || command.trim().equals("logout")) 329 | break; 330 | 331 | command = command.trim(); 332 | String[] args = command.split("\\s+"); 333 | result = Shell.execute(args); 334 | 335 | if(result == null || result.length() == 0) 336 | System.out.println("-bash: " + args[0] + ": command not found"); 337 | else 338 | System.out.println(result); 339 | } 340 | } 341 | } 342 | -------------------------------------------------------------------------------- /src/main/java/me/dilley/Config.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Linulator - The Linux Simulator 3 | * Copyright (C) 2014 Lloyd Dilley 4 | * http://www.linulator.org/ 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | 21 | package me.dilley; 22 | 23 | import java.io.FileInputStream; 24 | import java.io.InputStream; 25 | import java.io.IOException; 26 | import java.util.Properties; 27 | 28 | public class Config 29 | { 30 | private String listenAddress; 31 | private int echoPort; 32 | private int discardPort; 33 | private int daytimePort; 34 | private int chargenPort; 35 | private int timePort; 36 | private int ftpPort; 37 | private int sshPort; 38 | private int telnetPort; 39 | private int smtpPort; 40 | private int dnsPort; 41 | private int httpPort; 42 | private int ntpPort; 43 | private String hostName; 44 | private String shortName; 45 | private String domainName; 46 | private String rootPassword; 47 | private String fakeDistro; 48 | private byte fakeProcessors; 49 | private int fakeMemory; 50 | private boolean hideConsole; 51 | private boolean readonlyDatabase; 52 | private boolean debugMode; 53 | 54 | public String getListenAddress() 55 | { 56 | return listenAddress; 57 | } 58 | 59 | public void setListenAddress(String listenAddress) 60 | { 61 | if(listenAddress.length() > 0) 62 | this.listenAddress = listenAddress; 63 | else 64 | { 65 | Log.write(0, "listen_address is not set. Listening on all available interfaces..."); 66 | this.listenAddress = Defaults.DEFAULT_LISTEN_ADDRESS; // listen on all available interfaces 67 | } 68 | } 69 | 70 | public int getEchoPort() 71 | { 72 | return echoPort; 73 | } 74 | 75 | public void setEchoPort(String echoPort) 76 | { 77 | this.echoPort = validatePort(echoPort, this.echoPort, "echo", "echo_port"); 78 | } 79 | 80 | public int getDiscardPort() 81 | { 82 | return discardPort; 83 | } 84 | 85 | public void setDiscardPort(String discardPort) 86 | { 87 | this.discardPort = validatePort(discardPort, this.discardPort, "discard", "discard_port"); 88 | } 89 | 90 | public int getDaytimePort() 91 | { 92 | return daytimePort; 93 | } 94 | 95 | public void setDaytimePort(String daytimePort) 96 | { 97 | this.daytimePort = validatePort(daytimePort, this.daytimePort, "daytime", "daytime_port"); 98 | } 99 | 100 | public int getChargenPort() 101 | { 102 | return chargenPort; 103 | } 104 | 105 | public void setChargenPort(String chargenPort) 106 | { 107 | this.chargenPort = validatePort(chargenPort, this.chargenPort, "chargen", "chargen_port"); 108 | } 109 | 110 | public int getTimePort() 111 | { 112 | return timePort; 113 | } 114 | 115 | public void setTimePort(String timePort) 116 | { 117 | this.timePort = validatePort(timePort, this.timePort, "time", "time_port"); 118 | } 119 | 120 | public int getFtpPort() 121 | { 122 | return ftpPort; 123 | } 124 | 125 | public void setFtpPort(String ftpPort) 126 | { 127 | this.ftpPort = validatePort(ftpPort, this.ftpPort, "FTP", "ftp_port"); 128 | } 129 | 130 | public int getSshPort() 131 | { 132 | return sshPort; 133 | } 134 | 135 | public void setSshPort(String sshPort) 136 | { 137 | this.sshPort = validatePort(sshPort, this.sshPort, "SSH", "ssh_port"); 138 | } 139 | 140 | public int getTelnetPort() 141 | { 142 | return telnetPort; 143 | } 144 | 145 | public void setTelnetPort(String telnetPort) 146 | { 147 | this.telnetPort = validatePort(telnetPort, this.telnetPort, "telnet", "telnet_port"); 148 | } 149 | 150 | public int getSmtpPort() 151 | { 152 | return smtpPort; 153 | } 154 | 155 | public void setSmtpPort(String smtpPort) 156 | { 157 | this.smtpPort = validatePort(smtpPort, this.smtpPort, "SMTP", "smtp_port"); 158 | } 159 | 160 | public int getDnsPort() 161 | { 162 | return dnsPort; 163 | } 164 | 165 | public void setDnsPort(String dnsPort) 166 | { 167 | this.dnsPort = validatePort(dnsPort, this.dnsPort, "DNS", "dns_port"); 168 | } 169 | 170 | public int getHttpPort() 171 | { 172 | return httpPort; 173 | } 174 | 175 | public void setHttpPort(String httpPort) 176 | { 177 | this.httpPort = validatePort(httpPort, this.httpPort, "HTTP", "http_port"); 178 | } 179 | 180 | public int getNtpPort() 181 | { 182 | return ntpPort; 183 | } 184 | 185 | public void setNtpPort(String ntpPort) 186 | { 187 | this.ntpPort = validatePort(ntpPort, this.ntpPort, "NTP", "ntp_port"); 188 | } 189 | 190 | public String getHostName() 191 | { 192 | return hostName; 193 | } 194 | 195 | public void setHostName(String hostName) 196 | { 197 | int domainIndex = 0; 198 | String domainName = ""; 199 | 200 | if(hostName.length() > 0) 201 | this.hostName = hostName; 202 | else 203 | { 204 | Log.write(1, "host_name value is not set. Setting host name to: " + Defaults.DEFAULT_HOST_NAME); 205 | System.out.println("Warning: host_name value is not set. Setting host name to: " + Defaults.DEFAULT_HOST_NAME); 206 | this.hostName = Defaults.DEFAULT_HOST_NAME; 207 | } 208 | 209 | // Is it a valid host name? 210 | if(!this.hostName.matches("^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])$") || this.hostName.length() > Limits.MAX_HOST_NAME_LENGTH) 211 | { 212 | Log.write(1, "host_name value is invalid. Setting host name to: " + Defaults.DEFAULT_HOST_NAME); 213 | System.out.println("Warning: host_name value is invalid. Setting host name to: " + Defaults.DEFAULT_HOST_NAME); 214 | this.hostName = Defaults.DEFAULT_HOST_NAME; 215 | } 216 | 217 | domainIndex = this.hostName.indexOf('.'); 218 | if(domainIndex > 0) 219 | { 220 | setShortName(this.hostName.substring(0, domainIndex)); 221 | domainName = this.hostName.substring(domainIndex + 1); 222 | } 223 | else 224 | setShortName(this.hostName); 225 | 226 | setDomainName(domainName); 227 | } 228 | 229 | public String getShortName() 230 | { 231 | return shortName; 232 | } 233 | 234 | public void setShortName(String shortName) 235 | { 236 | this.shortName = shortName; 237 | } 238 | 239 | public String getDomainName() 240 | { 241 | return domainName; 242 | } 243 | 244 | public void setDomainName(String domainName) 245 | { 246 | this.domainName = domainName; 247 | // domainName is already set to empty in validateOption() 248 | //if(this.domainName.length() == 0) 249 | //{ 250 | // Log.write(1, "domain_name value is not set. Setting domain name to: " + Defaults.DEFAULT_DOMAIN_NAME); 251 | // System.out.println("Warning: domain_name value is not set. Setting domain name to: " + Defaults.DEFAULT_DOMAIN_NAME); 252 | // this.domainName = Defaults.DEFAULT_DOMAIN_NAME; 253 | //} 254 | } 255 | 256 | public String getRootPassword() 257 | { 258 | return rootPassword; 259 | } 260 | 261 | public String getMaskedRootPassword() 262 | { 263 | String maskedRootPassword = ""; 264 | 265 | for(byte b = 0; b < rootPassword.length(); b++) 266 | maskedRootPassword += '*'; 267 | 268 | return maskedRootPassword; 269 | } 270 | 271 | public void setRootPassword(String rootPassword) 272 | { 273 | if(rootPassword.length() > 0 || rootPassword.length() < Limits.MAX_PASSWORD_LENGTH) 274 | this.rootPassword = rootPassword; 275 | else 276 | { 277 | Log.write(1, "root_password value is invalid. Setting root password to: " + Defaults.DEFAULT_ROOT_PASSWORD); 278 | System.out.println("Warning: root_password value is invalid. Setting root password to: " + Defaults.DEFAULT_ROOT_PASSWORD); 279 | this.rootPassword = Defaults.DEFAULT_ROOT_PASSWORD; 280 | } 281 | } 282 | 283 | public String getFakeDistro() 284 | { 285 | return fakeDistro; 286 | } 287 | 288 | public void setFakeDistro(String fakeDistro) 289 | { 290 | if(fakeDistro.length() > 0) 291 | this.fakeDistro = fakeDistro; 292 | else 293 | { 294 | Log.write(1, "fake_distro value is not set. Setting Linux distribution to: " + Defaults.DEFAULT_DISTRIBUTION); 295 | System.out.println("Warning: fake_distro value is not set. Setting Linux distribution to: " + Defaults.DEFAULT_DISTRIBUTION); 296 | this.fakeDistro = Defaults.DEFAULT_DISTRIBUTION; 297 | } 298 | 299 | if(!this.fakeDistro.equals("centos5") && !this.fakeDistro.equals("centos6") && !this.fakeDistro.equals("debian7") && !this.fakeDistro.equals("rhel5") && !this.fakeDistro.equals("rhel6") && !this.fakeDistro.equals("sles11")) 300 | { 301 | Log.write(1, "fake_distro value is invalid. Setting Linux distribution to: " + Defaults.DEFAULT_DISTRIBUTION); 302 | System.out.println("Warning: fake_distro value is invalid. Setting Linux distribution to: " + Defaults.DEFAULT_DISTRIBUTION); 303 | this.fakeDistro = Defaults.DEFAULT_DISTRIBUTION; 304 | } 305 | } 306 | 307 | public byte getFakeProcessors() 308 | { 309 | return fakeProcessors; 310 | } 311 | 312 | public void setFakeProcessors(String fakeProcessors) 313 | { 314 | try 315 | { 316 | if(fakeProcessors.length() > 0) 317 | this.fakeProcessors = Byte.parseByte(fakeProcessors); 318 | else 319 | { 320 | Log.write(1, "fake_processors value is not set. Setting number of CPUs to: " + Defaults.DEFAULT_PROCESSORS); 321 | System.out.println("Warning: fake_processors value is not set. Setting number of CPUs to: " + Defaults.DEFAULT_PROCESSORS); 322 | this.fakeProcessors = Defaults.DEFAULT_PROCESSORS; 323 | } 324 | } 325 | catch(NumberFormatException nfe) 326 | { 327 | Log.write(1, "fake_processors value is invalid. Setting number of CPUs to: " + Defaults.DEFAULT_PROCESSORS); 328 | System.out.println("Warning: fake_processors value is invalid. Setting number of CPUs to: " + Defaults.DEFAULT_PROCESSORS); 329 | this.fakeProcessors = Defaults.DEFAULT_PROCESSORS; 330 | } 331 | 332 | if(this.fakeProcessors < Limits.MIN_PROCESSORS || this.fakeProcessors > Limits.MAX_PROCESSORS) 333 | { 334 | Log.write(1, "fake_processors value is out of range. Setting number of CPUs to: " + Defaults.DEFAULT_PROCESSORS); 335 | System.out.println("Warning: fake_processors value is out of range. Setting number of CPUs to: " + Defaults.DEFAULT_PROCESSORS); 336 | this.fakeProcessors = Defaults.DEFAULT_PROCESSORS; 337 | } 338 | } 339 | 340 | public int getFakeMemory() 341 | { 342 | return fakeMemory; 343 | } 344 | 345 | public void setFakeMemory(String fakeMemory) 346 | { 347 | try 348 | { 349 | if(fakeMemory.length() > 0) 350 | this.fakeMemory = Integer.parseInt(fakeMemory); 351 | else 352 | { 353 | Log.write(1, "fake_memory value is not set. Setting amount of RAM to: " + Defaults.DEFAULT_MEMORY); 354 | System.out.println("Warning: fake_memory value is not set. Setting amount of RAM to: " + Defaults.DEFAULT_MEMORY); 355 | this.fakeMemory = Defaults.DEFAULT_MEMORY; 356 | } 357 | } 358 | catch(NumberFormatException nfe) 359 | { 360 | Log.write(1, "fake_memory value is invalid. Setting amount of RAM to: " + Defaults.DEFAULT_MEMORY); 361 | System.out.println("Warning: fake_memory value is invalid. Setting amount of RAM to: " + Defaults.DEFAULT_MEMORY); 362 | this.fakeMemory = Defaults.DEFAULT_MEMORY; 363 | } 364 | 365 | if(this.fakeMemory < Limits.MIN_MEMORY || this.fakeMemory > Limits.MAX_MEMORY) 366 | { 367 | Log.write(1, "fake_memory value is out of range. Setting amount of RAM to: " + Defaults.DEFAULT_MEMORY); 368 | System.out.println("Warning: fake_memory value is out of range. Setting amount of RAM to: " + Defaults.DEFAULT_MEMORY); 369 | this.fakeMemory = Defaults.DEFAULT_MEMORY; 370 | } 371 | } 372 | 373 | public boolean getHideConsole() 374 | { 375 | return hideConsole; 376 | } 377 | 378 | public void setHideConsole(String hideConsole) 379 | { 380 | try 381 | { 382 | if(hideConsole.length() > 0) 383 | { 384 | if(hideConsole.equals("1") || hideConsole.equalsIgnoreCase("on") || hideConsole.equalsIgnoreCase("enabled")) 385 | this.hideConsole = true; 386 | else 387 | this.hideConsole = Boolean.parseBoolean(hideConsole); 388 | } 389 | else 390 | { 391 | Log.write(1, "hide_console value is not set. Setting hidden console mode to: Disabled"); 392 | System.out.println("Warning: hide_console value is not set. Setting hidden console mode to: Disabled"); 393 | this.hideConsole = Defaults.DEFAULT_HIDE_CONSOLE; 394 | } 395 | } 396 | catch(NumberFormatException nfe) 397 | { 398 | Log.write(1, "hide_console value is invalid. Setting hidden console mode to: Disabled"); 399 | System.out.println("Warning: hide_console value is invalid. Setting hidden console mode to: Disabled"); 400 | this.hideConsole = Defaults.DEFAULT_HIDE_CONSOLE; 401 | } 402 | } 403 | 404 | public boolean getReadonlyDatabase() 405 | { 406 | return readonlyDatabase; 407 | } 408 | 409 | public void setReadonlyDatabase(String readonlyDatabase) 410 | { 411 | try 412 | { 413 | if(readonlyDatabase.length() > 0) 414 | { 415 | if(readonlyDatabase.equals("1") || readonlyDatabase.equalsIgnoreCase("on") || readonlyDatabase.equalsIgnoreCase("enabled")) 416 | this.readonlyDatabase = true; 417 | else 418 | this.readonlyDatabase = Boolean.parseBoolean(readonlyDatabase); 419 | } 420 | else 421 | { 422 | Log.write(1, "readonly_database value is not set. Setting read-only database mode to: Enabled"); 423 | System.out.println("Warning: readonly_database value is not set. Setting read-only database mode to: Enabled"); 424 | this.debugMode = Defaults.DEFAULT_READONLY_DATABASE; 425 | } 426 | } 427 | catch(NumberFormatException nfe) 428 | { 429 | Log.write(1, "readonly_database value is invalid. Setting read-only database mode to: Enabled"); 430 | System.out.println("Warning: readonly_database value is invalid. Setting read-only database mode to: Enabled"); 431 | this.readonlyDatabase = Defaults.DEFAULT_READONLY_DATABASE; 432 | } 433 | } 434 | 435 | public boolean getDebugMode() 436 | { 437 | return debugMode; 438 | } 439 | 440 | public void setDebugMode(String debugMode) 441 | { 442 | try 443 | { 444 | if(debugMode.length() > 0) 445 | { 446 | if(debugMode.equals("1") || debugMode.equalsIgnoreCase("on") || debugMode.equalsIgnoreCase("enabled")) 447 | this.debugMode = true; 448 | else 449 | this.debugMode = Boolean.parseBoolean(debugMode); 450 | } 451 | else 452 | { 453 | Log.write(1, "debug_mode value is not set. Setting debug mode to: Enabled"); 454 | System.out.println("Warning: debug_mode value is not set. Setting debug mode to: Enabled"); 455 | this.debugMode = Defaults.DEFAULT_DEBUG_MODE; 456 | } 457 | } 458 | catch(NumberFormatException nfe) 459 | { 460 | Log.write(1, "debug_mode value is invalid. Setting debug mode to: Enabled"); 461 | System.out.println("Warning: debug_mode value is invalid. Setting debug mode to: Enabled"); 462 | this.debugMode = Defaults.DEFAULT_DEBUG_MODE; 463 | } 464 | } 465 | 466 | public void parseConfig() 467 | { 468 | InputStream inFile = null; 469 | Properties config = new Properties(); 470 | 471 | try 472 | { 473 | inFile = new FileInputStream("cfg" + System.getProperty("file.separator") + "linulator.properties"); 474 | config.load(inFile); 475 | 476 | setListenAddress(validateOption(config, "listen_address")); 477 | setEchoPort(validateOption(config, "echo_port")); 478 | setDiscardPort(validateOption(config, "discard_port")); 479 | setDaytimePort(validateOption(config, "daytime_port")); 480 | setChargenPort(validateOption(config, "chargen_port")); 481 | setTimePort(validateOption(config, "time_port")); 482 | setFtpPort(validateOption(config, "ftp_port")); 483 | setSshPort(validateOption(config, "ssh_port")); 484 | setTelnetPort(validateOption(config, "telnet_port")); 485 | setSmtpPort(validateOption(config, "smtp_port")); 486 | setDnsPort(validateOption(config, "dns_port")); 487 | setHttpPort(validateOption(config, "http_port")); 488 | setNtpPort(validateOption(config, "ntp_port")); 489 | setHostName(validateOption(config, "host_name")); 490 | setRootPassword(validateOption(config, "root_password")); 491 | setFakeDistro(validateOption(config, "fake_distro")); 492 | setFakeProcessors(validateOption(config, "fake_processors")); 493 | setFakeMemory(validateOption(config, "fake_memory")); 494 | setHideConsole(validateOption(config, "hide_console")); 495 | setReadonlyDatabase(validateOption(config, "readonly_database")); 496 | setDebugMode(validateOption(config, "debug_mode")); 497 | } 498 | catch(IOException ioe) 499 | { 500 | System.err.println("Critical: Unable to parse linulator.properties:"); 501 | System.err.println(ioe.getMessage()); 502 | System.exit(1); 503 | } 504 | finally 505 | { 506 | if(inFile != null) 507 | { 508 | try 509 | { 510 | inFile.close(); 511 | } 512 | catch(IOException ioe) 513 | { 514 | ioe.printStackTrace(); 515 | } 516 | } 517 | } 518 | 519 | validateUniquePorts(echoPort, "echo_port"); 520 | validateUniquePorts(discardPort, "discard_port"); 521 | validateUniquePorts(daytimePort, "daytime_port"); 522 | validateUniquePorts(chargenPort, "chargen_port"); 523 | validateUniquePorts(timePort, "time_port"); 524 | validateUniquePorts(ftpPort, "ftp_port"); 525 | validateUniquePorts(sshPort, "ssh_port"); 526 | validateUniquePorts(telnetPort, "telnet_port"); 527 | validateUniquePorts(smtpPort, "smtp_port"); 528 | validateUniquePorts(dnsPort, "dns_port"); 529 | validateUniquePorts(httpPort, "http_port"); 530 | validateUniquePorts(ntpPort, "ntp_port"); 531 | } 532 | 533 | public void showConfig() 534 | { 535 | System.out.println("Listen Address: " + getListenAddress()); 536 | System.out.println("Echo Port: " + getEchoPort()); 537 | System.out.println("Discard Port: " + getDiscardPort()); 538 | System.out.println("Daytime Port: " + getDaytimePort()); 539 | System.out.println("Chargen Port: " + getChargenPort()); 540 | System.out.println("Time Port: " + getTimePort()); 541 | System.out.println("FTP Port: " + getFtpPort()); 542 | System.out.println("SSH Port: " + getSshPort()); 543 | System.out.println("Telnet Port: " + getTelnetPort()); 544 | System.out.println("SMTP Port: " + getSmtpPort()); 545 | System.out.println("DNS Port: " + getDnsPort()); 546 | System.out.println("HTTP Port: " + getHttpPort()); 547 | System.out.println("NTP Port: " + getNtpPort()); 548 | System.out.println("Host Name: " + getShortName()); 549 | System.out.println("Domain Name: " + getDomainName()); 550 | System.out.println("Root Password: " + getMaskedRootPassword() + " (masked)"); 551 | System.out.println("Fake Distro: " + getFakeDistro()); 552 | System.out.println("Fake Processors: " + getFakeProcessors()); 553 | System.out.println("Fake Memory: " + getFakeMemory()); 554 | System.out.println("Hide Console: " + getHideConsole()); 555 | System.out.println("Read-only Database: " + getReadonlyDatabase()); 556 | System.out.println("Debug Mode: " + getDebugMode()); 557 | } 558 | 559 | public String validateOption(Properties config, String option) 560 | { 561 | if(config.getProperty(option) == null || config.getProperty(option).length() == 0) 562 | return ""; 563 | else 564 | return config.getProperty(option); 565 | } 566 | 567 | public int validatePort(String port1, int port2, String serviceName, String option) 568 | { 569 | try 570 | { 571 | if(port1.length() > 0) 572 | port2 = Integer.parseInt(port1); 573 | else 574 | port2 = 0; 575 | } 576 | catch(NumberFormatException nfe) 577 | { 578 | Log.write(1, "The " + serviceName + " service has been disabled since " + option + " value is invalid: " + port1); 579 | System.out.println("Warning: The " + serviceName + " service has been disabled since " + option + " value is invalid: " + port1); 580 | port2 = 0; // disable service 581 | return port2; 582 | } 583 | 584 | if(port2 < Limits.MIN_PORT || port2 > Limits.MAX_PORT) 585 | { 586 | Log.write(1, "The " + serviceName + " service has been disabled since " + option + " value is out of range: " + port2); 587 | System.out.println("Warning: The " + serviceName + " service has been disabled since " + option + " value is out of range: " + port2); 588 | port2 = 0; 589 | } 590 | 591 | if(port2 == 0) 592 | { 593 | Log.write(0, "The " + serviceName + " service has been disabled since " + option + " value is not set."); 594 | System.out.println("The " + serviceName + " service has been disabled since " + option + " value is not set."); 595 | } 596 | 597 | return port2; 598 | } 599 | 600 | public void validateUniquePorts(int port, String option) 601 | { 602 | byte matchCount = 0; 603 | 604 | if(port > 0) 605 | { 606 | if(port == echoPort) 607 | matchCount++; 608 | if(port == discardPort) 609 | matchCount++; 610 | if(port == daytimePort) 611 | matchCount++; 612 | if(port == chargenPort) 613 | matchCount++; 614 | if(port == timePort) 615 | matchCount++; 616 | if(port == ftpPort) 617 | matchCount++; 618 | if(port == sshPort) 619 | matchCount++; 620 | if(port == telnetPort) 621 | matchCount++; 622 | if(port == smtpPort) 623 | matchCount++; 624 | if(port == dnsPort) 625 | matchCount++; 626 | if(port == httpPort) 627 | matchCount++; 628 | if(port == ntpPort) 629 | matchCount++; 630 | } 631 | 632 | if(matchCount > 1) 633 | { 634 | Log.write(2, "Modify the " + option + " value in linulator.properties. The port number is not unique: " + port); 635 | System.err.println("Critical: Modify the " + option + " value in linulator.properties. The port number is not unique: " + port); 636 | System.exit(1); 637 | } 638 | } 639 | } 640 | -------------------------------------------------------------------------------- /tools/CloneFilesystem.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Linulator - The Linux Simulator 3 | * Copyright (C) 2014 Lloyd Dilley 4 | * http://www.linulator.org/ 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | 21 | import java.io.BufferedReader; 22 | import java.io.File; 23 | import java.io.FileInputStream; 24 | import java.io.FileReader; 25 | import java.io.InputStream; 26 | import java.io.InputStreamReader; 27 | import java.io.IOException; 28 | import java.nio.file.FileVisitResult; 29 | import java.nio.file.FileVisitor; 30 | import java.nio.file.Files; 31 | import java.nio.file.LinkOption; 32 | import java.nio.file.Path; 33 | import java.nio.file.Paths; 34 | import java.nio.file.SimpleFileVisitor; 35 | import java.nio.file.attribute.BasicFileAttributes; 36 | import java.nio.file.attribute.PosixFileAttributes; 37 | import java.nio.file.attribute.PosixFilePermission; 38 | import java.nio.file.attribute.PosixFilePermissions; 39 | import java.sql.Blob; 40 | import java.sql.Connection; 41 | import java.sql.DriverManager; 42 | import java.sql.PreparedStatement; 43 | import java.sql.ResultSet; 44 | import java.sql.SQLException; 45 | import java.sql.Statement; 46 | import java.sql.Types; 47 | import java.text.SimpleDateFormat; 48 | import java.util.ArrayList; 49 | import java.util.Date; 50 | import java.util.HashMap; 51 | import java.util.Properties; 52 | import java.util.Scanner; 53 | import java.util.Set; 54 | import java.util.regex.Matcher; 55 | import java.util.regex.Pattern; 56 | 57 | // This class creates the initial system baseline from an existing Linux filesystem 58 | class CloneFilesystem 59 | { 60 | public static final String VERSION = "1.0"; 61 | public static final long SECONDS_IN_A_YEAR = 31556926; 62 | private static String driver = "org.apache.derby.jdbc.EmbeddedDriver"; 63 | private static String protocol = "jdbc:derby:"; 64 | private static String databaseName = "linulator"; 65 | private static Connection connection = null; 66 | private static PreparedStatement preparedStatement = null; 67 | private static Statement statement = null; 68 | private static ResultSet resultSet = null; 69 | private static Properties properties = new Properties(); 70 | private static HashMap users = new HashMap(); 71 | private static HashMap groups = new HashMap(); 72 | private static ArrayList excludes = new ArrayList(); 73 | 74 | public static class Record 75 | { 76 | String path; 77 | String name; 78 | long inode; 79 | byte type; 80 | int mode; 81 | int linkCount; 82 | int uid; 83 | int gid; 84 | long atime; 85 | long ctime; 86 | long mtime; 87 | String link; 88 | String data; 89 | } 90 | 91 | public static void main(String[] args) 92 | { 93 | System.out.println("\nCloneFilesystem " + VERSION + '\n'); 94 | 95 | // Due to use of NIO 96 | double javaVersion = Double.valueOf(System.getProperty("java.specification.version")); 97 | if(javaVersion < 1.7) 98 | { 99 | System.err.println("Java >=7 is required."); 100 | System.exit(1); 101 | } 102 | 103 | if(!System.getProperty("os.name").equals("Linux")) 104 | { 105 | System.err.println("This must be run on a Linux system."); 106 | System.exit(1); 107 | } 108 | 109 | if(!System.getProperty("user.name").equals("root")) 110 | { 111 | System.err.println("This must be run as root."); 112 | System.exit(1); 113 | } 114 | 115 | if(args.length == 0) 116 | { 117 | System.err.println("Not enough arguments.\n"); 118 | showUsage(); 119 | System.exit(1); 120 | } 121 | 122 | if(args.length > 1) 123 | { 124 | System.err.println("Too many arguments.\n"); 125 | showUsage(); 126 | System.exit(1); 127 | } 128 | 129 | if(args[0].equals("-c")) 130 | { 131 | try 132 | { 133 | System.out.println("Connecting to database..."); 134 | connectDatabase(); 135 | System.out.println("Reading /etc/groups..."); 136 | readGroup(); 137 | System.out.println("Read " + groups.size() + " groups."); 138 | System.out.println("Reading /etc/passwd..."); 139 | readPasswd(); 140 | System.out.println("Read " + users.size() + " users."); 141 | System.out.println("Reading exclude.lst..."); 142 | readExcludes(); 143 | System.out.println("Read " + excludes.size() + " exclusions."); 144 | System.out.println("Cloning filesystem..."); 145 | createClone(); 146 | System.out.println("Shutting down database..."); 147 | shutdownDatabase(); 148 | System.exit(0); 149 | } 150 | catch(IOException ioe) 151 | { 152 | System.err.println(ioe.getMessage()); 153 | } 154 | } 155 | else if(args[0].equals("-h")) 156 | showUsage(); 157 | else 158 | { 159 | System.err.println("Invalid argument.\n"); 160 | showUsage(); 161 | System.exit(1); 162 | } 163 | } 164 | 165 | public static void createClone() throws IOException 166 | { 167 | Scanner scanner = new Scanner(System.in); 168 | String input = null; 169 | 170 | System.out.println("\n!!! This program _may_ not function as intended. Please report any hangs or bugs to the developers. !!!"); 171 | System.out.println("Known issues:"); 172 | System.out.println("1. Link counts are not currently supported. A default of '1' link (file itself) is recorded."); 173 | System.out.println("2. Setuid, setgid, and sticky bits are not currently supported.\n"); 174 | System.out.println("Warning: This should only be performed on a fresh install to avoid importing personal data!"); 175 | System.out.print("Type \"yes\" and press enter if you understand this and want to proceed: "); 176 | input = scanner.nextLine(); 177 | 178 | if(!input.equalsIgnoreCase("yes")) 179 | { 180 | System.out.println("Process aborted per user request."); 181 | shutdownDatabase(); 182 | System.exit(0); 183 | } 184 | 185 | FileVisitor fileProcessor = new ProcessFile(); 186 | Files.walkFileTree(Paths.get("/"), fileProcessor); // get everything under / 187 | } 188 | 189 | private static final class ProcessFile extends SimpleFileVisitor 190 | { 191 | @Override public FileVisitResult visitFile(Path file, BasicFileAttributes basicAttribs) throws IOException 192 | { 193 | boolean skip = false; 194 | for(int i = 0; i < excludes.size(); i++) 195 | { 196 | if(file.toString().matches(excludes.get(i))) 197 | skip = true; 198 | } 199 | if(skip) 200 | { 201 | System.out.println("Ignored excluded file: " + file); 202 | return FileVisitResult.CONTINUE; 203 | } 204 | 205 | if(pathExists(file)) 206 | { 207 | System.out.println("Skipping existing file: " + file); 208 | return FileVisitResult.CONTINUE; 209 | } 210 | 211 | System.out.print("Importing file: " + file); 212 | 213 | boolean isBinary = false; 214 | PosixFileAttributes posixAttribs = Files.readAttributes(file, PosixFileAttributes.class, LinkOption.NOFOLLOW_LINKS); 215 | SimpleDateFormat timestampFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss"); 216 | Date currentTime = new Date(); 217 | String timestamp = timestampFormat.format(currentTime); 218 | System.out.println(" (" + posixAttribs.size() + " bytes) " + timestamp); 219 | Set posixPerms = posixAttribs.permissions(); 220 | String perms = PosixFilePermissions.toString(posixPerms); // convert to symbolic format 221 | 222 | String inode = posixAttribs.fileKey().toString(); 223 | Pattern p = Pattern.compile("\\=(\\d+)\\)"); // regex to acquire just the inode 224 | Matcher m = p.matcher(inode); 225 | m.find(); 226 | inode = m.group(1); 227 | 228 | // Only files have a type (not directories) 229 | String contents = null; 230 | String type = null; 231 | // The check below prevents a null pointer exception 232 | type = Files.probeContentType(file); 233 | if(type == null) 234 | contents = readText(file); 235 | else 236 | { 237 | type = type.substring(0, 4); 238 | if(type.equals("text")) 239 | contents = readText(file); 240 | else if(type.equals("inod")) 241 | contents = ""; // is a hard link 242 | else 243 | { 244 | isBinary = true; 245 | contents = readBin(file); 246 | } 247 | } 248 | 249 | // Log this to file once in production 250 | //System.out.println("Properties of file: " + file); 251 | //System.out.println(inode + " " + perms + " " + posixAttribs.owner().getName() + ":" + posixAttribs.group().getName() + " " + posixAttribs.size() + " " + calcMtime(mtimeStamp) + "\n"); 252 | 253 | CloneFilesystem.Record entry = new CloneFilesystem.Record(); 254 | entry.path = file.toString(); 255 | entry.name = new File(entry.path).getName(); 256 | entry.inode = Long.parseLong(inode); 257 | entry.type = determineType(file, posixAttribs); 258 | entry.mode = calculateOctalPerms(perms); 259 | entry.linkCount = 1; // link count is currently broken; set a default of one (itself) 260 | entry.uid = users.get(posixAttribs.owner().getName()); // use containsKey() if this causes a null pointer exception 261 | entry.gid = groups.get(posixAttribs.group().getName()); // same as above 262 | entry.atime = posixAttribs.lastAccessTime().toMillis(); 263 | entry.ctime = posixAttribs.creationTime().toMillis(); 264 | entry.mtime = posixAttribs.lastModifiedTime().toMillis(); 265 | if(posixAttribs.isSymbolicLink()) 266 | entry.link = Files.readSymbolicLink(file).toString(); 267 | else 268 | entry.link = null; 269 | entry.data = contents; 270 | if(isBinary) 271 | writeDatabase(entry, true, false); 272 | else 273 | writeDatabase(entry, false, false); 274 | 275 | return FileVisitResult.CONTINUE; 276 | } 277 | 278 | @Override public FileVisitResult preVisitDirectory(Path directory, BasicFileAttributes basicAttribs) throws IOException 279 | { 280 | boolean skip = false; 281 | for(int i = 0; i < excludes.size(); i++) 282 | { 283 | if(directory.toString().matches(excludes.get(i))) 284 | skip = true; 285 | } 286 | if(skip) 287 | { 288 | System.out.println("Ignored excluded directory: " + directory); 289 | return FileVisitResult.CONTINUE; 290 | } 291 | 292 | if(pathExists(directory)) 293 | { 294 | System.out.println("Skipping existing directory: " + directory); 295 | return FileVisitResult.CONTINUE; 296 | } 297 | 298 | System.out.print("Importing directory: " + directory); 299 | 300 | PosixFileAttributes posixAttribs = Files.readAttributes(directory, PosixFileAttributes.class, LinkOption.NOFOLLOW_LINKS); 301 | SimpleDateFormat timestampFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss"); 302 | Date currentTime = new Date(); 303 | String timestamp = timestampFormat.format(currentTime); 304 | System.out.println(" " + timestamp); 305 | Set posixPerms = posixAttribs.permissions(); 306 | String perms = PosixFilePermissions.toString(posixPerms); 307 | 308 | String inode = posixAttribs.fileKey().toString(); 309 | Pattern p = Pattern.compile("\\=(\\d+)\\)"); 310 | Matcher m = p.matcher(inode); 311 | m.find(); 312 | inode = m.group(1); 313 | 314 | long mtimeStamp = posixAttribs.lastModifiedTime().toMillis(); 315 | 316 | if(posixAttribs.isSymbolicLink()) 317 | System.out.println(Files.readSymbolicLink(directory).toString()); 318 | 319 | // Log this to file once in production 320 | //System.out.println("Properties of directory: " + directory); 321 | //System.out.println(inode + " " + perms + " " + posixAttribs.owner().getName() + ":" + posixAttribs.group().getName() + " " + posixAttribs.size() + " " + calcMtime(mtimeStamp) + "\n"); 322 | 323 | CloneFilesystem.Record entry = new CloneFilesystem.Record(); 324 | entry.path = directory.toString(); 325 | entry.name = new File(entry.path).getName(); 326 | entry.inode = Long.parseLong(inode); 327 | entry.type = determineType(directory, posixAttribs); 328 | entry.mode = calculateOctalPerms(perms); 329 | entry.linkCount = 1; // link count is currently broken; set a default of one (itself) 330 | entry.uid = users.get(posixAttribs.owner().getName()); // use containsKey() if this causes a null pointer exception 331 | entry.gid = groups.get(posixAttribs.group().getName()); // same as above 332 | entry.atime = posixAttribs.lastAccessTime().toMillis(); 333 | entry.ctime = posixAttribs.creationTime().toMillis(); 334 | entry.mtime = posixAttribs.lastModifiedTime().toMillis(); 335 | if(posixAttribs.isSymbolicLink()) 336 | entry.link = Files.readSymbolicLink(directory).toString(); 337 | else 338 | entry.link = null; 339 | entry.data = ""; 340 | writeDatabase(entry, false, true); 341 | 342 | return FileVisitResult.CONTINUE; 343 | } 344 | } 345 | 346 | public static void readGroup() 347 | { 348 | try 349 | { 350 | BufferedReader groupFile = new BufferedReader(new FileReader("/etc/group")); 351 | String line = null; 352 | 353 | while((line = groupFile.readLine()) != null) 354 | { 355 | String[] entry = line.split(":"); 356 | groups.put(entry[0], Integer.parseInt(entry[2])); // store group name associated with GID for lookup later 357 | } 358 | groupFile.close(); 359 | } 360 | catch(IOException ioe) 361 | { 362 | System.err.println("Unable to read /etc/group."); 363 | System.err.println(ioe.getMessage()); 364 | System.exit(1); 365 | } 366 | } 367 | 368 | public static void readPasswd() 369 | { 370 | try 371 | { 372 | BufferedReader passwdFile = new BufferedReader(new FileReader("/etc/passwd")); 373 | String line = null; 374 | 375 | while((line = passwdFile.readLine()) != null) 376 | { 377 | String[] entry = line.split(":"); 378 | users.put(entry[0], Integer.parseInt(entry[2])); // store username associated with UID for lookup later 379 | } 380 | passwdFile.close(); 381 | } 382 | catch(IOException ioe) 383 | { 384 | System.err.println("Unable to read /etc/passwd."); 385 | System.err.println(ioe.getMessage()); 386 | System.exit(1); 387 | } 388 | } 389 | 390 | public static void readExcludes() 391 | { 392 | try 393 | { 394 | BufferedReader excludeFile = new BufferedReader(new FileReader("exclude.lst")); 395 | String line = excludeFile.readLine(); 396 | while(line != null) 397 | { 398 | if(line.trim().length() == 0 || line.trim().charAt(0) == '#') 399 | { 400 | line = excludeFile.readLine(); 401 | continue; 402 | } 403 | excludes.add(line.trim()); 404 | line = excludeFile.readLine(); 405 | } 406 | excludeFile.close(); 407 | } 408 | catch(IOException ioe) 409 | { 410 | System.err.println("Critical: Unable to parse excludes.lst:"); 411 | System.err.println(ioe.getMessage()); 412 | System.exit(1); 413 | } 414 | } 415 | 416 | public static String calcMtime(long mtimeStamp) 417 | { 418 | Date currentDate = new Date(); 419 | Date mdate = new Date(mtimeStamp); 420 | long delta = currentDate.getTime() / 1000 - mdate.getTime() / 1000; 421 | SimpleDateFormat timestampFormat; 422 | 423 | // If mtime is >1 year, show MMM d YYYY instead 424 | if(delta >= SECONDS_IN_A_YEAR) 425 | timestampFormat = new SimpleDateFormat("MMM d YYYY"); 426 | else 427 | timestampFormat = new SimpleDateFormat("MMM d HH:mm"); 428 | 429 | return timestampFormat.format(mdate); 430 | } 431 | 432 | public static String readBin(Path passedFile) 433 | { 434 | String data = null; 435 | File file = new File(passedFile.toString()); 436 | FileInputStream targetFile; 437 | 438 | try 439 | { 440 | { 441 | targetFile = new FileInputStream(file); 442 | byte[] content = new byte[(int)file.length()]; // may cause issues for large files 443 | targetFile.read(content); 444 | data = new String(content); 445 | targetFile.close(); 446 | } 447 | } 448 | catch(IOException ioe) 449 | { 450 | System.err.println("Unable to read " + passedFile.toString() + ":"); 451 | System.err.println(ioe.getMessage()); 452 | data = ""; 453 | } 454 | catch(OutOfMemoryError oome) 455 | { 456 | System.err.println("Skipping: " + passedFile.toString() + " (Out of memory while reading file)"); 457 | data = ""; 458 | } 459 | 460 | return data; 461 | } 462 | 463 | public static String readText(Path passedFile) 464 | { 465 | String data = null; 466 | BufferedReader targetFile; 467 | String line; 468 | 469 | try 470 | { 471 | targetFile = new BufferedReader(new FileReader(passedFile.toString())); 472 | while((line = targetFile.readLine()) != null) 473 | { 474 | data += line; 475 | } 476 | targetFile.close(); 477 | } 478 | catch(IOException ioe) 479 | { 480 | System.err.println("Unable to read " + passedFile.toString() + ":"); 481 | System.err.println(ioe.getMessage()); 482 | data = ""; 483 | } 484 | catch(OutOfMemoryError oome) 485 | { 486 | System.err.println("Skipping: " + passedFile.toString() + " (Out of memory while reading file)"); 487 | data = ""; 488 | } 489 | 490 | return data; 491 | } 492 | 493 | public static byte determineType(Path file, PosixFileAttributes posixAttribs) 494 | { 495 | byte type = 5; 496 | 497 | if(posixAttribs.isRegularFile()) 498 | type = 5; 499 | else if(posixAttribs.isDirectory()) 500 | type = 2; 501 | else if(posixAttribs.isSymbolicLink()) 502 | type = 4; 503 | else if(posixAttribs.isOther()) 504 | { 505 | try 506 | { 507 | Process process = new ProcessBuilder("/bin/ls", "-la", file.toString()).start(); 508 | InputStream inputStream = process.getInputStream(); 509 | InputStreamReader inputStreamReader = new InputStreamReader(inputStream); 510 | BufferedReader bufferedReader = new BufferedReader(inputStreamReader); 511 | String line = null; 512 | line = bufferedReader.readLine(); 513 | if(line.charAt(0) == 'b') 514 | type = 0; // block 515 | else if(line.charAt(0) == 'c') 516 | type = 1; // character 517 | else if(line.charAt(0) == 'p') 518 | type = 3; // pipe/FIFO 519 | else if(line.charAt(0) == 's') 520 | type = 6; // socket 521 | } 522 | catch(IOException ioe) 523 | { 524 | System.err.println("Unable to run process to determine file type."); 525 | System.err.println(ioe.getMessage()); 526 | } 527 | } 528 | 529 | return type; 530 | } 531 | 532 | public static int calculateOctalPerms(String symbolicPerms) 533 | { 534 | String octalPerms = ""; 535 | String subPerms = null; 536 | int start = 0; 537 | int end = 3; 538 | 539 | if(symbolicPerms != null && symbolicPerms.length() == 9) 540 | { 541 | for(int i = 0; i < 3; i++) // 3 chunks (user, group, other) 542 | { 543 | subPerms = symbolicPerms.substring(start, end); 544 | if(subPerms.equals("---")) 545 | octalPerms += "0"; 546 | else if(subPerms.equals("r--")) 547 | octalPerms += "4"; 548 | else if(subPerms.equals("-w-")) 549 | octalPerms += "2"; 550 | else if(subPerms.equals("--x")) 551 | octalPerms += "1"; 552 | else if(subPerms.equals("rw-")) 553 | octalPerms += "6"; 554 | else if(subPerms.equals("r-x")) 555 | octalPerms += "5"; 556 | else if(subPerms.equals("-wx")) 557 | octalPerms += "3"; 558 | else if(subPerms.equals("rwx")) 559 | octalPerms += "7"; 560 | else 561 | octalPerms += "0"; 562 | start += 3; 563 | end += 3; 564 | } 565 | } 566 | else 567 | octalPerms = "000"; 568 | 569 | int result = 0; 570 | try 571 | { 572 | result = Integer.parseInt(octalPerms); 573 | } 574 | catch(NumberFormatException nfe) 575 | { 576 | result = 000; 577 | } 578 | 579 | return result; 580 | } 581 | 582 | public static boolean pathExists(Path path) 583 | { 584 | boolean result = false; 585 | 586 | try 587 | { 588 | String query = "SELECT * FROM filesystem WHERE path = \'" + path.toString() + "\'"; 589 | statement = connection.createStatement(); 590 | resultSet = statement.executeQuery(query); 591 | if(resultSet.next()) 592 | result = true; 593 | else 594 | result = false; 595 | statement.close(); 596 | } 597 | catch(SQLException sqle) 598 | { 599 | System.err.println("Critical: Unable to query database."); 600 | System.err.println(sqle.getMessage()); 601 | System.exit(1); 602 | } 603 | 604 | return result; 605 | } 606 | 607 | public static void createTable() 608 | { 609 | try 610 | { 611 | statement = connection.createStatement(); 612 | String query = "CREATE TABLE filesystem " + 613 | "(id BIGINT NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1), " + 614 | "path VARCHAR(4096) NOT NULL, " + 615 | "name VARCHAR(255) NOT NULL, " + 616 | "inode BIGINT NOT NULL, " + 617 | "type SMALLINT NOT NULL, " + 618 | "mode SMALLINT NOT NULL, " + 619 | "linkcount INT NOT NULL DEFAULT 1, " + 620 | "uid INT NOT NULL, " + 621 | "gid INT NOT NULL, " + 622 | "atime BIGINT NOT NULL, " + 623 | "ctime BIGINT NOT NULL, " + 624 | "mtime BIGINT NOT NULL, " + 625 | "link VARCHAR(4096) DEFAULT NULL, " + 626 | "bindata BLOB DEFAULT NULL, " + 627 | "txtdata CLOB DEFAULT NULL, " + 628 | "PRIMARY KEY(id))"; 629 | statement.execute(query); 630 | statement.close(); 631 | connection.commit(); 632 | System.out.println("\"filesystem\" table created successfully."); 633 | System.out.println("Reconnecting to database..."); 634 | connectDatabase(); 635 | } 636 | catch(SQLException sqle) 637 | { 638 | System.err.println("Critical: Unable to create database table."); 639 | System.err.println(sqle.getMessage()); 640 | System.exit(1); 641 | } 642 | } 643 | 644 | public static void connectDatabase() 645 | { 646 | try 647 | { 648 | System.setProperty("derby.system.home", System.getProperty("user.dir")); 649 | //System.setProperty("derby.stream.error.file", "log" + System.getProperty("file.separator") + "database.log"); 650 | System.setProperty("derby.language.logStatementText", "false"); // set to true for increased verbosity 651 | connection = DriverManager.getConnection(protocol + databaseName + ";create=true", properties); 652 | connection.setAutoCommit(false); 653 | System.out.println("Connected to database successfully."); 654 | preparedStatement = connection.prepareStatement("INSERT INTO filesystem (path, name, inode, type, mode, linkcount, uid, gid, atime, " + 655 | "ctime, mtime, link, bindata, txtdata) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); 656 | } 657 | catch(SQLException sqle) 658 | { 659 | // Table does not exist, so create it... 660 | if(sqle.getSQLState().equals("42X05") || sqle.getSQLState().equals("X0X05")) 661 | { 662 | System.out.println("\"filesystem\" table does not exist. Creating it..."); 663 | createTable(); 664 | } 665 | else 666 | { 667 | System.err.println("Critical: Unable to connect to database."); 668 | System.err.println(sqle.getMessage()); 669 | //if(connection == null) 670 | System.exit(1); 671 | } 672 | } 673 | } 674 | 675 | public static void disconnectDatabase() 676 | { 677 | try 678 | { 679 | connection.close(); 680 | DriverManager.getConnection("jdbc:derby:;shutdown=true"); 681 | } 682 | catch(SQLException sqle) 683 | { 684 | System.err.println("Critical: Unable to disconnect from database."); 685 | System.err.println(sqle.getMessage()); 686 | } 687 | } 688 | 689 | public static void shutdownDatabase() 690 | { 691 | try 692 | { 693 | DriverManager.getConnection("jdbc:derby:;shutdown=true"); 694 | } 695 | catch(SQLException sqle) 696 | { 697 | if(sqle.getErrorCode() == 50000 && sqle.getSQLState().equals("XJ015")) 698 | { 699 | System.out.println("Database was shut down properly."); 700 | } 701 | else 702 | { 703 | System.err.println("Critical: Database could not be shut down properly."); 704 | System.err.println(sqle.getMessage()); 705 | } 706 | } 707 | } 708 | 709 | public static void writeDatabase(Record file, boolean isBinary, boolean isDirectory) 710 | { 711 | Blob blob = null; 712 | 713 | try 714 | { 715 | preparedStatement.setString(1, file.path); 716 | preparedStatement.setString(2, file.name); 717 | preparedStatement.setLong(3, file.inode); 718 | preparedStatement.setByte(4, file.type); 719 | preparedStatement.setInt(5, file.mode); 720 | preparedStatement.setInt(6, file.linkCount); 721 | preparedStatement.setInt(7, file.uid); 722 | preparedStatement.setInt(8, file.gid); 723 | preparedStatement.setLong(9, file.atime); // a/c/mtime may need setTimestamp() 724 | preparedStatement.setLong(10, file.ctime); 725 | preparedStatement.setLong(11, file.mtime); 726 | preparedStatement.setString(12, file.link); 727 | if(isBinary) 728 | { 729 | blob = connection.createBlob(); 730 | blob.setBytes(1, file.data.getBytes()); 731 | preparedStatement.setBlob(13, blob); 732 | //preparedStatement.setString(13, file.data); // this may need setBlob() 733 | preparedStatement.setNull(14, Types.CLOB); 734 | } 735 | else if(isDirectory) 736 | { 737 | preparedStatement.setNull(13, Types.BLOB); 738 | preparedStatement.setNull(14, Types.CLOB); 739 | } 740 | else // is text 741 | { 742 | preparedStatement.setNull(13, Types.BLOB); 743 | preparedStatement.setString(14, file.data); // this may need setClob() 744 | } 745 | preparedStatement.executeUpdate(); 746 | connection.commit(); 747 | } 748 | catch(SQLException sqle) 749 | { 750 | System.err.println("Critical: Unable to write to database."); 751 | System.err.println(sqle.getMessage()); 752 | if(connection == null) 753 | System.exit(1); 754 | } 755 | } 756 | 757 | public static void showUsage() 758 | { 759 | System.out.println("CloneFilesystem Usage"); 760 | System.out.println("====================="); 761 | System.out.println("-c\tCreate clone"); 762 | System.out.println("-h\tDisplay help\n"); 763 | } 764 | } 765 | --------------------------------------------------------------------------------