Coverage Report - org.jscsi.target.TargetServer
 
Classes in this File Line Coverage Branch Coverage Complexity
TargetServer
0%
0/104
0%
0/26
0
 
 1  
 package org.jscsi.target;
 2  
 
 3  
 
 4  
 import java.io.BufferedReader;
 5  
 import java.io.File;
 6  
 import java.io.IOException;
 7  
 import java.io.InputStreamReader;
 8  
 import java.net.InetAddress;
 9  
 import java.net.InetSocketAddress;
 10  
 import java.net.NetworkInterface;
 11  
 import java.nio.channels.ServerSocketChannel;
 12  
 import java.nio.channels.SocketChannel;
 13  
 import java.security.DigestException;
 14  
 import java.util.ArrayList;
 15  
 import java.util.Collection;
 16  
 import java.util.Enumeration;
 17  
 import java.util.HashMap;
 18  
 import java.util.List;
 19  
 import java.util.Vector;
 20  
 import java.util.concurrent.Callable;
 21  
 import java.util.concurrent.atomic.AtomicInteger;
 22  
 
 23  
 import org.jscsi.exception.InternetSCSIException;
 24  
 import org.jscsi.parser.OperationCode;
 25  
 import org.jscsi.parser.ProtocolDataUnit;
 26  
 import org.jscsi.parser.login.ISID;
 27  
 import org.jscsi.parser.login.LoginRequestParser;
 28  
 import org.jscsi.target.connection.Connection;
 29  
 import org.jscsi.target.connection.Connection.TargetConnection;
 30  
 import org.jscsi.target.connection.TargetSession;
 31  
 import org.jscsi.target.scsi.inquiry.DeviceIdentificationVpdPage;
 32  
 import org.jscsi.target.settings.SettingsException;
 33  
 import org.slf4j.Logger;
 34  
 import org.slf4j.LoggerFactory;
 35  
 
 36  
 
 37  
 /**
 38  
  * The central class of the jSCSI Target, which keeps track of all active {@link TargetSession}s, stores target-wide
 39  
  * parameters and variables, and which contains the {@link #main(String[])} method for starting the program.
 40  
  * 
 41  
  * @author Andreas Ergenzinger, University of Konstanz
 42  
  * @author Sebastian Graf, University of Konstanz
 43  
  */
 44  0
 public final class TargetServer implements Callable<Void> {
 45  
 
 46  0
     private static final Logger LOGGER = LoggerFactory.getLogger(TargetServer.class);
 47  
 
 48  
     /**
 49  
      * A {@link SocketChannel} used for listening to incoming connections.
 50  
      */
 51  
     private ServerSocketChannel serverSocketChannel;
 52  
 
 53  
     /**
 54  
      * Contains all active {@link TargetSession}s.
 55  
      */
 56  0
     private Collection<TargetSession> sessions = new Vector<TargetSession>();
 57  
 
 58  
     /**
 59  
      * The jSCSI Target's global parameters.
 60  
      */
 61  
     private Configuration config;
 62  
 
 63  
     /**
 64  
      * 
 65  
      */
 66  
     private DeviceIdentificationVpdPage deviceIdentificationVpdPage;
 67  
 
 68  
     /**
 69  
      * The table of targets
 70  
      */
 71  0
     private HashMap<String , Target> targets = new HashMap<String , Target>();
 72  
 
 73  
     /**
 74  
      * A target-wide counter used for providing the value of sent {@link ProtocolDataUnit}s'
 75  
      * <code>Target Transfer Tag</code> field, unless that field is reserved.
 76  
      */
 77  0
     private static final AtomicInteger nextTargetTransferTag = new AtomicInteger();
 78  
 
 79  
     /**
 80  
      * The connection the target server is using.
 81  
      */
 82  
     private Connection connection;
 83  
     
 84  
     /**
 85  
      * while this value is true, the target is active.
 86  
      */
 87  0
     private boolean running = true;
 88  
 
 89  0
     public TargetServer (final Configuration conf) {
 90  0
         this.config = conf;
 91  
 
 92  0
         LOGGER.debug("Starting jSCSI-target: ");
 93  
 
 94  
         // read target settings from configuration file
 95  
 
 96  0
         LOGGER.debug("   port:           " + getConfig().getPort());
 97  0
         LOGGER.debug("   loading targets.");
 98  
         // open the storage medium
 99  0
         List<Target> targetInfo = getConfig().getTargets();
 100  0
         for (Target curTargetInfo : targetInfo) {
 101  
 
 102  0
             targets.put(curTargetInfo.getTargetName(), curTargetInfo);
 103  
             // print configuration and medium details
 104  0
             LOGGER.debug("   target name:    " + curTargetInfo.getTargetName() + " loaded.");
 105  0
         }
 106  
 
 107  0
         this.deviceIdentificationVpdPage = new DeviceIdentificationVpdPage(this);
 108  0
     }
 109  
 
 110  
     /**
 111  
      * Gets and increments the value to use in the next unreserved <code>Target Transfer Tag</code> field of the next
 112  
      * PDU to be sent by the jSCSI Target.
 113  
      * 
 114  
      * @see #nextTargetTransferTag
 115  
      * @return the value to use in the next unreserved <code>Target Transfer Tag
 116  
      * </code> field
 117  
      */
 118  
     public static int getNextTargetTransferTag () {
 119  
         // value 0xffffffff is reserved
 120  
         int tag;
 121  
         do {
 122  0
             tag = nextTargetTransferTag.getAndIncrement();
 123  0
         } while (tag == -1);
 124  0
         return tag;
 125  
     }
 126  
 
 127  
     /**
 128  
      * Starts the jSCSI target.
 129  
      * 
 130  
      * @param args all command line arguments are ignored
 131  
      * @throws IOException
 132  
      */
 133  
     public static void main (String[] args) throws Exception {
 134  
         TargetServer target;
 135  
 
 136  0
         System.out.println("This system provides more than one IP Address to advertise.\n");
 137  
 
 138  0
         Enumeration<NetworkInterface> interfaceEnum = NetworkInterface.getNetworkInterfaces();
 139  
         NetworkInterface i;
 140  0
         int addressCounter = 0;
 141  0
         List<InetAddress> addresses = new ArrayList<InetAddress>();
 142  0
         while (interfaceEnum.hasMoreElements()) {
 143  0
             i = interfaceEnum.nextElement();
 144  0
             Enumeration<InetAddress> addressEnum = i.getInetAddresses();
 145  
             InetAddress address;
 146  
 
 147  0
             while (addressEnum.hasMoreElements()) {
 148  0
                 address = addressEnum.nextElement();
 149  0
                 System.out.println("[" + addressCounter + "] " + address.getHostAddress());
 150  0
                 addresses.add(address);
 151  0
                 addressCounter++;
 152  
             }
 153  0
         }
 154  
 
 155  
         /*
 156  
          * Getting the desired address from the command line. You can't automatically make sure to always use the
 157  
          * correct host address.
 158  
          */
 159  0
         System.out.print("\nWhich one should be used?\nType in the number: ");
 160  0
         Integer chosenIndex = null;
 161  
 
 162  0
         while (chosenIndex == null) {
 163  0
             BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
 164  0
             String line = br.readLine();
 165  
             try {
 166  0
                 chosenIndex = Integer.parseInt(line);
 167  0
             } catch (NumberFormatException nfe) {
 168  0
                 chosenIndex = null;
 169  0
             }
 170  0
         }
 171  
 
 172  0
         String targetAddress = addresses.get(chosenIndex).getHostAddress();
 173  0
         System.out.println("Using ip address " + addresses.get(chosenIndex).getHostAddress());
 174  
         
 175  
         
 176  0
         switch (args.length) {
 177  
             case 0 :
 178  0
                 target = new TargetServer(Configuration.create(targetAddress));
 179  0
                 break;
 180  
             case 1 :
 181  
 
 182  
                 // Checking if the schema file is at the default location
 183  0
                 target = new TargetServer(Configuration.create(Configuration.CONFIGURATION_SCHEMA_FILE.exists() ? Configuration.CONFIGURATION_SCHEMA_FILE : new File("classpath://jscsi-target.xsd"), new File(args[0]), targetAddress));
 184  0
                 break;
 185  
             case 2 :
 186  0
                 target = new TargetServer(Configuration.create(new File(args[0]), new File(args[1]), targetAddress));
 187  0
                 break;
 188  
             default :
 189  0
                 throw new IllegalArgumentException("Only zero or one Parameter (Path to Configuration-File) allowed!");
 190  
         }
 191  
 
 192  0
         target.call();
 193  0
     }
 194  
 
 195  
     public Void call () throws Exception {
 196  
 
 197  
         // Create a blocking server socket and check for connections
 198  
         try {
 199  
             // Create a blocking server socket channel on the specified/default
 200  
             // port
 201  0
             serverSocketChannel = ServerSocketChannel.open();
 202  0
             serverSocketChannel.configureBlocking(true);
 203  
 
 204  
             // Making sure the socket is bound to the address used in the config.
 205  0
             serverSocketChannel.socket().bind(new InetSocketAddress(getConfig().getTargetAddress(), getConfig().getPort()));
 206  
 
 207  0
             while (running) {
 208  
                 // Accept the connection request.
 209  
                 // If serverSocketChannel is blocking, this method blocks.
 210  
                 // The returned channel is in blocking mode.
 211  0
                 final SocketChannel socketChannel = serverSocketChannel.accept();
 212  
 
 213  
                 // deactivate Nagle algorithm
 214  0
                 socketChannel.socket().setTcpNoDelay(true);
 215  
 
 216  0
                 connection = new TargetConnection(socketChannel, true);
 217  
                 try {
 218  0
                     final ProtocolDataUnit pdu = connection.receivePdu();
 219  
                     // confirm OpCode-
 220  0
                     if (pdu.getBasicHeaderSegment().getOpCode() != OperationCode.LOGIN_REQUEST) throw new InternetSCSIException();
 221  
                     // get initiatorSessionID
 222  
                     
 223  0
                     LoginRequestParser parser = (LoginRequestParser) pdu.getBasicHeaderSegment().getParser();
 224  0
                     ISID initiatorSessionID = parser.getInitiatorSessionID();
 225  
 
 226  
                     /*
 227  
                      * TODO get (new or existing) session based on TSIH But since we don't do session reinstatement and
 228  
                      * MaxConnections=1, we can just create a new one.
 229  
                      */
 230  0
                     TargetSession session = new TargetSession(this, connection, initiatorSessionID, parser.getCommandSequenceNumber(),// set
 231  
                                                                                                                                       // ExpCmdSN
 232  
                                                                                                                                       // (PDU
 233  
                                                                                                                                       // is
 234  
                                                                                                                                       // immediate,
 235  
                                                                                                                                       // hence
 236  
                                                                                                                                       // no
 237  
                                                                                                                                       // ++)
 238  
                     parser.getExpectedStatusSequenceNumber());
 239  
 
 240  0
                     sessions.add(session);
 241  
                     // threadPool.submit(connection);// ignore returned Future
 242  0
                     connection.call();
 243  0
                 } catch (DigestException | InternetSCSIException | SettingsException e) {
 244  0
                     LOGGER.info("Throws Exception", e);
 245  0
                     continue;
 246  0
                 }
 247  0
             }
 248  0
         } catch (IOException e) {
 249  
             // this block is entered if the desired port is already in use
 250  0
             LOGGER.error("Throws Exception", e);
 251  0
         }
 252  
 
 253  0
         System.out.println("Closing socket channel.");
 254  0
         serverSocketChannel.close();
 255  0
         for(TargetSession session: sessions){
 256  0
             System.out.println("Commiting uncommited changes.");
 257  0
             session.getStorageModule().close();
 258  0
         }
 259  0
         return null;
 260  
     }
 261  
 
 262  
     public Configuration getConfig () {
 263  0
         return config;
 264  
     }
 265  
 
 266  
     public DeviceIdentificationVpdPage getDeviceIdentificationVpdPage () {
 267  0
         return deviceIdentificationVpdPage;
 268  
     }
 269  
 
 270  
     public Target getTarget (String targetName) {
 271  0
         synchronized (targets) {
 272  0
             return targets.get(targetName);
 273  0
         }
 274  
     }
 275  
 
 276  
     /**
 277  
      * Removes a session from the jSCSI Target's list of active sessions.
 278  
      * 
 279  
      * @param session the session to remove from the list of active sessions
 280  
      */
 281  
     public synchronized void removeTargetSession (TargetSession session) {
 282  0
         sessions.remove(session);
 283  0
     }
 284  
 
 285  
     public String[] getTargetNames () {
 286  0
         String[] returnNames = new String[targets.size()];
 287  0
         returnNames = targets.keySet().toArray(returnNames);
 288  0
         return returnNames;
 289  
     }
 290  
 
 291  
     /**
 292  
      * Checks to see if this target name is valid.
 293  
      * 
 294  
      * @param checkTargetName
 295  
      * @return true if the the target name is configured
 296  
      */
 297  
     public boolean isValidTargetName (String checkTargetName) {
 298  0
         return targets.containsKey(checkTargetName);
 299  
     }
 300  
 
 301  
     /**
 302  
      * Using this connection mainly for test pruposes.
 303  
      * 
 304  
      * @return the connection the target server established.
 305  
      */
 306  
     public Connection getConnection () {
 307  0
         return this.connection;
 308  
     }
 309  
     
 310  
     /**
 311  
      * Stop this target server
 312  
      */
 313  
     public void stop(){
 314  0
         this.running = false;
 315  0
         for(TargetSession session : sessions){
 316  0
             if(!session.getConnection().stop()){
 317  0
                 this.running = true;
 318  0
                 LOGGER.error("Unable to stop session for " + session.getTargetName());
 319  
             }
 320  0
         }
 321  0
     }
 322  
 
 323  
 }