/**
   * Test {@link FileUtilities#generateSaveSetContent(SaveSetData)} and {@link
   * FileUtilities#readFromSaveSet(java.io.InputStream)}.
   *
   * @throws IOException
   */
  @Test
  public void testSaveSetData() throws IOException {
    SaveSet set =
        new SaveSet(
            new Branch(), Optional.empty(), new String[] {"first", "second", "third"}, "someId");
    SaveSetData bsd =
        new SaveSetData(
            set,
            Arrays.asList(
                new SaveSetEntry("pv1", "rb1", "d1", true),
                new SaveSetEntry("pv2", "rb2", "Math.pow(x,3)", false)),
            "some description");
    String content = FileUtilities.generateSaveSetContent(bsd);
    assertEquals(
        "# Description:\n# some description\n#\nPV,READBACK,DELTA,READ_ONLY\npv1,rb1,d1,true\npv2,rb2,\"Math.pow(x,3)\",false\n",
        content);

    SaveSetContent bsc =
        FileUtilities.readFromSaveSet(
            new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)));
    assertEquals("some description", bsc.getDescription());
    List<SaveSetEntry> entries = bsc.getEntries();
    assertArrayEquals(
        new String[] {"pv1", "pv2"},
        entries.stream().map(e -> e.getPVName()).toArray(String[]::new));
    assertArrayEquals(
        new String[] {"rb1", "rb2"},
        entries.stream().map(e -> e.getReadback()).toArray(String[]::new));
    assertArrayEquals(
        new String[] {"d1", "Math.pow(x,3)"},
        entries.stream().map(e -> e.getDelta()).toArray(String[]::new));
    assertArrayEquals(
        new Boolean[] {true, false},
        entries.stream().map(e -> e.isReadOnly()).toArray(Boolean[]::new));
  }
  /** Test {@link FileUtilities#split(String)} method */
  @Test
  public void testSplit() {
    String str =
        "TEST1,1,1454595641.463352838,HIHI_ALARM,MAJOR,double,\"1.2323212342342342E16\",,\"---\",";
    String[] result = FileUtilities.split(str);
    assertArrayEquals(
        result,
        new String[] {
          "TEST1",
          "1",
          "1454595641.463352838",
          "HIHI_ALARM",
          "MAJOR",
          "double",
          "1.2323212342342342E16",
          "",
          "---",
          ""
        });

    str = "foo,bar,blabla,,test,";
    result = FileUtilities.split(str);
    assertArrayEquals(result, new String[] {"foo", "bar", "blabla", "", "test", ""});

    str = "\"foo,test\",\"bar something\",blabla,,test,";
    result = FileUtilities.split(str);
    assertArrayEquals(result, new String[] {"foo,test", "bar something", "blabla", "", "test", ""});
  }
  /**
   * Publishes a hidden service
   *
   * @param hiddenServicePort The port that the hidden service will accept connections on
   * @param localPort The local port that the hidden service will relay connections to
   * @return The hidden service's onion address in the form X.onion.
   * @throws java.io.IOException - File errors
   */
  public synchronized String publishHiddenService(int hiddenServicePort, int localPort)
      throws IOException {
    if (controlConnection == null) {
      throw new RuntimeException("Service is not running.");
    }

    List<ConfigEntry> currentHiddenServices = controlConnection.getConf("HiddenServiceOptions");

    if ((currentHiddenServices.size() == 1
            && currentHiddenServices.get(0).key.compareTo("HiddenServiceOptions") == 0
            && currentHiddenServices.get(0).value.compareTo("") == 0)
        == false) {
      throw new RuntimeException(
          "Sorry, only one hidden service to a customer and we already have one. Please send complaints to https://github.com/thaliproject/Tor_Onion_Proxy_Library/issues/5 with your scenario so we can justify fixing this.");
    }

    LOG.info("Creating hidden service");
    File hostnameFile = onionProxyContext.getHostNameFile();

    if (hostnameFile.getParentFile().exists() == false
        && hostnameFile.getParentFile().mkdirs() == false) {
      throw new RuntimeException("Could not create hostnameFile parent directory");
    }

    if (hostnameFile.exists() == false && hostnameFile.createNewFile() == false) {
      throw new RuntimeException("Could not create hostnameFile");
    }

    // Watch for the hostname file being created/updated
    WriteObserver hostNameFileObserver = onionProxyContext.generateWriteObserver(hostnameFile);
    // Use the control connection to update the Tor config
    List<String> config =
        Arrays.asList(
            "HiddenServiceDir " + hostnameFile.getParentFile().getAbsolutePath(),
            "HiddenServicePort " + hiddenServicePort + " 127.0.0.1:" + localPort);
    controlConnection.setConf(config);
    controlConnection.saveConf();
    // Wait for the hostname file to be created/updated
    if (!hostNameFileObserver.poll(HOSTNAME_TIMEOUT, MILLISECONDS)) {
      FileUtilities.listFilesToLog(hostnameFile.getParentFile());
      throw new RuntimeException("Wait for hidden service hostname file to be created expired.");
    }

    // Publish the hidden service's onion hostname in transport properties
    String hostname = new String(FileUtilities.read(hostnameFile), "UTF-8").trim();
    LOG.info("Hidden service config has completed.");

    return hostname;
  }
Esempio n. 4
0
 /**
  * Copy the content in the directory with the given name located in the <code>test_data</code>
  * directory in the plug-in with the given id into the specified target directory.
  *
  * @param pluginId the id of the plug-in containing the project
  * @param projectName the name of the directory containing the project
  * @param targetDirectory the directory into which the content is copied. This directory is
  *     created if it does not already exist
  * @throws IOException if a required file cannot be accessed
  */
 public static void copyPluginRelativeContent(
     String pluginId, String projectName, File targetDirectory) throws IOException {
   URL pluginInstallUri = PluginUtilities.getInstallUrl(pluginId);
   URL sourceUrl = new URL(pluginInstallUri, PROJECT_DIRECTORY_NAME + "/" + projectName);
   IPath sourcePath = new Path(FileLocator.toFileURL(sourceUrl).getPath());
   FileUtilities.copyDirectoryContents(sourcePath.toFile(), targetDirectory);
 }
Esempio n. 5
0
  /**
   * 'handler' can be of any type that implements 'exportedInterface', but only methods declared by
   * the interface (and its superinterfaces) will be invocable.
   */
  public <T> InAppServer(
      String name,
      String portFilename,
      InetAddress inetAddress,
      Class<T> exportedInterface,
      T handler) {
    this.fullName = name + "Server";
    this.exportedInterface = exportedInterface;
    this.handler = handler;

    // In the absence of authentication, we shouldn't risk starting a server as root.
    if (System.getProperty("user.name").equals("root")) {
      Log.warn(
          "InAppServer: refusing to start unauthenticated server \"" + fullName + "\" as root!");
      return;
    }

    try {
      File portFile = FileUtilities.fileFromString(portFilename);
      secretFile = new File(portFile.getPath() + ".secret");
      Thread serverThread = new Thread(new ConnectionAccepter(portFile, inetAddress), fullName);
      // If there are no other threads left, the InApp server shouldn't keep us alive.
      serverThread.setDaemon(true);
      serverThread.start();
    } catch (Throwable th) {
      Log.warn("InAppServer: couldn't start \"" + fullName + "\".", th);
    }
    writeNewSecret();
  }
Esempio n. 6
0
 /**
  * Run the given operation, passing in a temporary directory that is newly created (implying that
  * it exists and is empty), and delete the directory after the operation has completed.
  *
  * @param operation the operation to be run
  * @throws Exception if the operation throws an Exception or if the directory either cannot be
  *     created or cannot be deleted
  */
 public static void runWithTempDirectory(FileOperation operation) throws Exception {
   File tempDir = createTempDirectory();
   try {
     operation.run(tempDir);
   } finally {
     FileUtilities.delete(tempDir);
   }
 }
 /**
  * Reads all the lines from the named file into a string array. Throws a RuntimeException on
  * failure.
  */
 public static String[] readLinesFromFile(String filename) {
   ArrayList<String> result = new ArrayList<String>();
   LineNumberReader in = null;
   try {
     File file = FileUtilities.fileFromString(filename);
     in = new LineNumberReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));
     String line;
     while ((line = in.readLine()) != null) {
       result.add(line);
     }
     return result.toArray(new String[result.size()]);
   } catch (IOException ex) {
     throw new RuntimeException(ex);
   } finally {
     FileUtilities.close(in);
   }
 }
 public CheckInChangesAction() {
   super(
       "Check in _Changes...",
       ToolInputDisposition.NO_INPUT,
       ToolOutputDisposition.ERRORS_WINDOW,
       "SCM_EDITOR=$EVERGREEN_LAUNCHER "
           + FileUtilities.findScriptFromBundle("checkintool", "org.jessies.SCM"));
   setCheckEverythingSaved(true);
 }
 private static String writeFile(File file, String content, boolean append) {
   PrintWriter out = null;
   try {
     out = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file, append), "UTF-8"));
     out.print(content);
     return null;
   } catch (IOException ex) {
     return ex.getMessage();
   } finally {
     FileUtilities.close(out);
   }
 }
Esempio n. 10
0
 /** Assert that the Eclipse .log file does not exist. */
 public static void assertNoLogFile() {
   File logFile = getLogFile();
   if (logFile.exists()) {
     PrintStringWriter writer = new PrintStringWriter();
     try {
       String contents = FileUtilities.getContents(logFile);
       writer.println("Non-empty log file. Log file contents:");
       writer.println();
       writer.print(contents);
     } catch (IOException exception) {
       writer.println("Non-empty log file. Could not access contents of log file.");
       writer.println();
       exception.printStackTrace(writer);
     }
     Assert.fail(writer.toString());
   }
 }
Esempio n. 11
0
  /**
   * Opens a FileInputStream and detects its encoding. If no encoding is detected, the default
   * system encoding will be returned. {@link http://code.google.com/p/juniversalchardet/} {@link
   * http://www-archive.mozilla.org/projects/intl/UniversalCharsetDetection.html}
   *
   * @param filename case-insensitive absolute filename path.
   * @return String, the file encoding, it might be null.
   * @see FileUtilities#detectFileEncoding(String)
   */
  public static String detectFileEncoding(String filename) {
    byte[] buf = new byte[4096];
    int nread = 0;
    int totalReadBytes = 0;
    String encoding = null;
    FileInputStream fis = null;

    try {
      fis = new FileInputStream(new CaseInsensitiveFile(filename).toFile());
      UniversalDetector detector = new UniversalDetector(null);

      // Feed detector with bytes until it detect the encoding or reach the max bytes to read
      // if the detector can't get the encoding after reading some bytes, we should stop to save
      // time.
      while ((nread = fis.read(buf)) > 0
          && !detector.isDone()
          && totalReadBytes < MAX_BTYES_TO_READ) {
        totalReadBytes += nread;
        detector.handleData(buf, 0, nread);
      }
      if (!detector.isDone()) {
        // IndependantLog.warn("Didn't detect file encoding after reading "+totalReadBytes+"
        // bytes.");
      }
      detector.dataEnd();

      // Get the file encoding string (defined in org.mozilla.universalchardet.Constants)
      encoding = detector.getDetectedCharset();
      detector.reset();

    } catch (Exception e) {
      IndependantLog.warn(StringUtils.debugmsg(false) + StringUtils.debugmsg(e));
    } finally {
      if (fis != null)
        try {
          fis.close();
        } catch (Exception e) {
        }
    }

    // If no encoding is detected, test if it is "utf-8"
    try {
      if (encoding == null || encoding.trim().isEmpty())
        if (FileUtilities.isFileUTF8(filename)) encoding = "UTF-8";
    } catch (Exception e) {
      IndependantLog.warn(StringUtils.debugmsg(false) + StringUtils.debugmsg(e));
    }

    // If no encoding is detected, get the default file encoding
    try {
      if (encoding == null || encoding.trim().isEmpty())
        encoding = System.getProperty("file.encoding");
      if (!Charset.isSupported(encoding)) {
        String error =
            "The detected encoding is '"
                + encoding
                + "', it is not supported by java.nio.charset.Charset";
        IndependantLog.warn(
            error); // We need to map org.mozilla.universalchardet.Constants and
                    // java.nio.charset.Charset ?
        throw new IOException(error);
      }
    } catch (Exception e) {
      IndependantLog.warn(StringUtils.debugmsg(false) + StringUtils.debugmsg(e));
    }

    return encoding;
  }
 public void addFile(String path) throws IOException {
   if (path.contains(includeDir)) {
     FileUtilities.append(outputFile, new File(path));
   }
 }
  public View getView(int position, View convertView, ViewGroup parent) {
    LayoutInflater inflater =
        (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

    View gridView;

    if (convertView == null) {
      gridView = new View(mContext);
    } else {
      gridView = (View) convertView;
    }

    // get layout from mobile.xml
    gridView = inflater.inflate(R.layout.file_explorer_grid_item, null);

    // set value into textview
    TextView textView = (TextView) gridView.findViewById(R.id.grid_item_label);
    textView.setText(filePaths[position].getName());
    // set image based on selected text
    ImageView imageView = (ImageView) gridView.findViewById(R.id.grid_item_image);
    if (filePaths[position].isDirectory()) // Is a folder
    {
      // Default view is a generic folder icon.
      imageView.setImageResource(R.drawable.folder);
      // How should we handle empty folders / folders with no thumbnails? -> new files
      gridView = inflater.inflate(R.layout.file_explorer_folder_icon, null);
      org.libreoffice.ui.FolderIconView icon =
          (org.libreoffice.ui.FolderIconView) gridView.findViewById(R.id.folder_icon);
      icon.setDir(filePaths[position]);
      textView = (TextView) gridView.findViewById(R.id.grid_item_label);
      textView.setText(filePaths[position].getName());
      return gridView;
    } else {
      File thumbnailFile =
          new File(
              filePaths[position].getParent(),
              "." + filePaths[position].getName().split("[.]")[0] + ".png");
      BitmapFactory factory = new BitmapFactory();
      Bitmap thumb = factory.decodeFile(thumbnailFile.getAbsolutePath());
      if (thumb != null) {
        Log.i("GRID", "true");
      } else {
        Log.i("GRID", thumbnailFile.getAbsolutePath());
      }
      switch (FileUtilities.getType(filePaths[position].getName())) {
        case FileUtilities.DOC:
          if (thumb != null) {
            imageView.setImageBitmap(thumb);
            break;
          }
          imageView.setImageResource(R.drawable.writer);
          break;
        case FileUtilities.CALC:
          imageView.setImageResource(R.drawable.calc);
          break;
        case FileUtilities.DRAWING: // FIXME: only for now ...
        case FileUtilities.IMPRESS:
          imageView.setImageResource(R.drawable.impress);
          break;
        case FileUtilities.UNKNOWN:
        default:
          break; // FIXME something prettier ?
      }
    }
    return gridView;
  }
  /**
   * Test {@link FileUtilities#generateSnapshotFileContent(VSnapshot)} and {@link
   * FileUtilities#readFromSnapshot(java.io.InputStream)}.
   *
   * @throws IOException
   * @throws ParseException
   */
  @Test
  public void testSnapshotData() throws IOException, ParseException {
    SaveSet set =
        new SaveSet(
            new Branch(), Optional.empty(), new String[] {"first", "second", "third"}, "someId");
    Snapshot snapshot = new Snapshot(set, Instant.now(), "comment", "owner");
    Date d = new Date(1455296909369L);
    Date d2 = new Date(1455296909379L);
    Alarm alarmNone = ValueFactory.alarmNone();
    Alarm alarm = ValueFactory.newAlarm(AlarmSeverity.MINOR, "HIGH");
    Display display = ValueFactory.displayNone();
    Time time = ValueFactory.newTime(d.toInstant());
    Time time2 = ValueFactory.newTime(d2.toInstant());

    VDouble val1 = ValueFactory.newVDouble(5d, alarm, time, display);
    VDoubleArray val2 =
        ValueFactory.newVDoubleArray(new ArrayDouble(1, 2, 3), alarmNone, time2, display);
    VDouble rval1 = ValueFactory.newVDouble(6d, alarmNone, time, display);
    VDoubleArray rval2 =
        ValueFactory.newVDoubleArray(new ArrayDouble(1, 1, 1), alarmNone, time, display);

    VSnapshot vs =
        new VSnapshot(
            snapshot,
            Arrays.asList(
                new SnapshotEntry("pv1", val1, true, "rb1", rval1, "50", true),
                new SnapshotEntry("pv2", val2, false, "rb2", rval2, "Math.min(x,3)", false)),
            time.getTimestamp());

    String content = FileUtilities.generateSnapshotFileContent(vs);
    String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(d);
    String CONTENT =
        "# Date: "
            + date
            + "\n"
            + "PV,SELECTED,TIMESTAMP,STATUS,SEVERITY,VALUE_TYPE,VALUE,READBACK,READBACK_VALUE,DELTA,READ_ONLY\n"
            + "pv1,1,1455296909.369000000,HIGH,MINOR,double,\"5.0\",rb1,\"6.0\",50,true\n"
            + "pv2,0,1455296909.379000000,NONE,NONE,double_array,\"[1.0;2.0;3.0]\",rb2,\"[1.0;1.0;1.0]\",\"Math.min(x,3)\",false\n";
    assertEquals(CONTENT, content);

    SnapshotContent sc =
        FileUtilities.readFromSnapshot(
            new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)));
    assertEquals(time.getTimestamp(), sc.getDate());
    List<SnapshotEntry> entries = sc.getEntries();
    assertArrayEquals(
        new String[] {"pv1", "pv2"},
        entries.stream().map(e -> e.getPVName()).toArray(String[]::new));
    assertArrayEquals(
        new String[] {"rb1", "rb2"},
        entries.stream().map(e -> e.getReadbackName()).toArray(String[]::new));
    assertArrayEquals(
        new String[] {"50", "Math.min(x,3)"},
        entries.stream().map(e -> e.getDelta()).toArray(String[]::new));
    assertArrayEquals(
        new Boolean[] {true, false},
        entries.stream().map(e -> e.isSelected()).toArray(Boolean[]::new));
    assertArrayEquals(
        new Boolean[] {true, false},
        entries.stream().map(e -> e.isReadOnly()).toArray(Boolean[]::new));

    // compare values
    List<VType> data = sc.getEntries().stream().map(e -> e.getValue()).collect(Collectors.toList());
    assertEquals(2, data.size());
    VDouble v1 = (VDouble) data.get(0);
    assertEquals(val1.getValue(), v1.getValue());
    assertEquals(val1.getTimestamp(), v1.getTimestamp());
    assertEquals(val1.getAlarmSeverity(), v1.getAlarmSeverity());
    assertEquals(val1.getAlarmName(), v1.getAlarmName());
    VDoubleArray v2 = (VDoubleArray) data.get(1);
    ListDouble ld1 = val2.getData();
    ListDouble ld2 = v2.getData();
    assertEquals(ld1.size(), ld2.size());
    for (int i = 0; i < ld1.size(); i++) {
      assertEquals(ld1.getDouble(i), ld2.getDouble(i), 0);
    }
    assertEquals(val2.getTimestamp(), v2.getTimestamp());
    assertEquals(val2.getAlarmSeverity(), v2.getAlarmSeverity());
    assertEquals(val2.getAlarmName(), v2.getAlarmName());

    // compare readbacks
    data = sc.getEntries().stream().map(e -> e.getReadbackValue()).collect(Collectors.toList());
    assertEquals(2, data.size());
    v1 = (VDouble) data.get(0);
    assertEquals(rval1.getValue(), v1.getValue());
    v2 = (VDoubleArray) data.get(1);
    ld1 = rval2.getData();
    ld2 = v2.getData();
    assertEquals(ld1.size(), ld2.size());
    for (int i = 0; i < ld1.size(); i++) {
      assertEquals(ld1.getDouble(i), ld2.getDouble(i), 0);
    }
  }
  /**
   * Installs all necessary files and starts the Tor OP in offline mode (e.g.
   * networkEnabled(false)). This would only be used if you wanted to start the Tor OP so that the
   * install and related is all done but aren't ready to actually connect it to the network.
   *
   * @return True if all files installed and Tor OP successfully started
   * @throws java.io.IOException - IO Exceptions
   * @throws java.lang.InterruptedException - If we are, well, interrupted
   */
  public synchronized boolean installAndStartTorOp() throws IOException, InterruptedException {
    // The Tor OP will die if it looses the connection to its socket so if there is no controlSocket
    // defined
    // then Tor is dead. This assumes, of course, that takeOwnership works and we can't end up with
    // Zombies.
    if (controlConnection != null) {
      LOG.info("Tor is already running");
      return true;
    }

    // The code below is why this method is synchronized, we don't want two instances of it running
    // at once
    // as the result would be a mess of screwed up files and connections.
    LOG.info("Tor is not running");

    installAndConfigureFiles();

    LOG.info("Starting Tor");
    File cookieFile = onionProxyContext.getCookieFile();
    if (cookieFile.getParentFile().exists() == false
        && cookieFile.getParentFile().mkdirs() == false) {
      throw new RuntimeException("Could not create cookieFile parent directory");
    }

    // The original code from Briar watches individual files, not a directory and Android's file
    // observer
    // won't work on files that don't exist. Rather than take 5 seconds to rewrite Briar's code I
    // instead
    // just make sure the file exists
    if (cookieFile.exists() == false && cookieFile.createNewFile() == false) {
      throw new RuntimeException("Could not create cookieFile");
    }

    File workingDirectory = onionProxyContext.getWorkingDirectory();
    // Watch for the auth cookie file being created/updated
    WriteObserver cookieObserver = onionProxyContext.generateWriteObserver(cookieFile);
    // Start a new Tor process
    String torPath = onionProxyContext.getTorExecutableFile().getAbsolutePath();
    String configPath = onionProxyContext.getTorrcFile().getAbsolutePath();
    String pid = onionProxyContext.getProcessId();
    String[] cmd = {torPath, "-f", configPath, OWNER, pid};
    String[] env = onionProxyContext.getEnvironmentArgsForExec();
    ProcessBuilder processBuilder = new ProcessBuilder(cmd);
    onionProxyContext.setEnvironmentArgsAndWorkingDirectoryForStart(processBuilder);
    Process torProcess = null;
    try {
      //            torProcess = Runtime.getRuntime().exec(cmd, env, workingDirectory);
      torProcess = processBuilder.start();
      CountDownLatch controlPortCountDownLatch = new CountDownLatch(1);
      eatStream(torProcess.getInputStream(), false, controlPortCountDownLatch);
      eatStream(torProcess.getErrorStream(), true, null);

      // On platforms other than Windows we run as a daemon and so we need to wait for the process
      // to detach
      // or exit. In the case of Windows the equivalent is running as a service and unfortunately
      // that requires
      // managing the service, such as turning it off or uninstalling it when it's time to move on.
      // Any number
      // of errors can prevent us from doing the cleanup and so we would leave the process running
      // around. Rather
      // than do that on Windows we just let the process run on the exec and hence don't look for an
      // exit code.
      // This does create a condition where the process has exited due to a problem but we should
      // hopefully
      // detect that when we try to use the control connection.
      if (OsData.getOsType() != OsData.OsType.Windows) {
        int exit = torProcess.waitFor();
        torProcess = null;
        if (exit != 0) {
          LOG.warn("Tor exited with value " + exit);
          return false;
        }
      }

      // Wait for the auth cookie file to be created/updated
      if (!cookieObserver.poll(COOKIE_TIMEOUT, MILLISECONDS)) {
        LOG.warn("Auth cookie not created");
        FileUtilities.listFilesToLog(workingDirectory);
        return false;
      }

      // Now we should be able to connect to the new process
      controlPortCountDownLatch.await();
      controlSocket = new Socket("127.0.0.1", control_port);

      // Open a control connection and authenticate using the cookie file
      TorControlConnection controlConnection = new TorControlConnection(controlSocket);
      controlConnection.authenticate(FileUtilities.read(cookieFile));
      // Tell Tor to exit when the control connection is closed
      controlConnection.takeOwnership();
      controlConnection.resetConf(Collections.singletonList(OWNER));
      // Register to receive events from the Tor process
      controlConnection.setEventHandler(new OnionProxyManagerEventHandler());
      controlConnection.setEvents(Arrays.asList(EVENTS));

      // We only set the class property once the connection is in a known good state
      this.controlConnection = controlConnection;
      return true;
    } catch (SecurityException e) {
      LOG.warn(e.toString(), e);
      return false;
    } catch (InterruptedException e) {
      LOG.warn("Interrupted while starting Tor", e);
      Thread.currentThread().interrupt();
      return false;
    } finally {
      if (controlConnection == null && torProcess != null) {
        // It's possible that something 'bad' could happen after we executed exec but before we
        // takeOwnership()
        // in which case the Tor OP will hang out as a zombie until this process is killed. This is
        // problematic
        // when we want to do things like
        torProcess.destroy();
      }
    }
  }