public Channel launchChannel(
      String[] cmd, OutputStream out, FilePath _workDir, Map<String, String> envVars)
      throws IOException, InterruptedException {
    printCommandLine(cmd, _workDir);

    try {
      Process proc = launcher.launch(Util.join(asList(cmd), " "), _workDir.getRemote());

      return new Channel(
          "channel over named pipe to " + launcher.getHostName(),
          Computer.threadPoolForRemoting,
          proc.getInputStream(),
          new BufferedOutputStream(proc.getOutputStream()));
    } catch (JIException e) {
      throw new IOException(e);
    }
  }
  public Proc launch(ProcStarter ps) throws IOException {
    maskedPrintCommandLine(ps.cmds(), ps.masks(), ps.pwd());

    // TODO: environment variable handling

    String name = ps.cmds().toString();

    final Process proc;
    try {
      proc = launcher.launch(buildCommandLine(ps), ps.pwd().getRemote());
    } catch (JIException e) {
      throw new IOException(e);
    } catch (InterruptedException e) {
      throw new IOException(e);
    }
    final Thread t1 =
        new StreamCopyThread("stdout copier: " + name, proc.getInputStream(), ps.stdout(), false);
    t1.start();
    final Thread t2 =
        new StreamCopyThread("stdin copier: " + name, ps.stdin(), proc.getOutputStream(), true);
    t2.start();

    return new Proc() {
      public boolean isAlive() throws IOException, InterruptedException {
        try {
          proc.exitValue();
          return false;
        } catch (IllegalThreadStateException e) {
          return true;
        }
      }

      public void kill() throws IOException, InterruptedException {
        t1.interrupt();
        t2.interrupt();
        proc.destroy();
      }

      public int join() throws IOException, InterruptedException {
        try {
          t1.join();
          t2.join();
          return proc.waitFor();
        } finally {
          proc.destroy();
        }
      }

      @Override
      public InputStream getStdout() {
        throw new UnsupportedOperationException();
      }

      @Override
      public InputStream getStderr() {
        throw new UnsupportedOperationException();
      }

      @Override
      public OutputStream getStdin() {
        throw new UnsupportedOperationException();
      }
    };
  }
  private void wmiLaunch(final EC2Computer computer, final TaskListener listener)
      throws IOException, InterruptedException, JIException {
    try {
      final PrintStream logger = listener.getLogger();
      final String name = host;

      logger.println(Messages.ManagedWindowsServiceLauncher_ConnectingTo(name));

      InetAddress host = InetAddress.getByName(name);

      try {
        Socket s = new Socket();
        s.connect(new InetSocketAddress(host, 135), 5000);
        s.close();
      } catch (IOException e) {
        logger.println(
            "Failed to connect to port 135 of "
                + name
                + ". Is Windows firewall blocking this port? Or did you disable DCOM service?");
        // again, let it continue.
      }

      JIDefaultAuthInfoImpl auth = createAuth();
      JISession session = JISession.createSession(auth);
      session.setGlobalSocketTimeout(60000);
      SWbemServices services = WMI.connect(session, name);

      String path = computer.getNode().getRemoteFS();
      if (path.indexOf(':') == -1)
        throw new IOException(
            "Remote file system root path of the slave needs to be absolute: " + path);
      SmbFile remoteRoot =
          new SmbFile(
              "smb://" + name + "/" + path.replace('\\', '/').replace(':', '$') + "/",
              createSmbAuth());

      if (!remoteRoot.exists()) remoteRoot.mkdirs();

      try { // does Java exist?
        logger.println("Checking if Java exists");
        WindowsRemoteProcessLauncher wrpl = new WindowsRemoteProcessLauncher(name, auth);
        Process proc = wrpl.launch("%JAVA_HOME%\\bin\\java -fullversion", "c:\\");
        proc.getOutputStream().close();
        IOUtils.copy(proc.getInputStream(), logger);
        proc.getInputStream().close();
        int exitCode = proc.waitFor();
        if (exitCode == 1) { // we'll get this error code if Java is not found
          logger.println("No Java found. Downloading JDK");
          JDKInstaller jdki = new JDKInstaller("jdk-6u16-oth-JPR@CDS-CDS_Developer", true);
          URL jdk = jdki.locate(listener, Platform.WINDOWS, CPU.i386);

          listener.getLogger().println("Installing JDK");
          copyStreamAndClose(
              jdk.openStream(), new SmbFile(remoteRoot, "jdk.exe").getOutputStream());

          String javaDir = path + "\\jdk"; // this is where we install Java to

          WindowsRemoteFileSystem fs = new WindowsRemoteFileSystem(name, createSmbAuth());
          fs.mkdirs(javaDir);

          jdki.install(
              new WindowsRemoteLauncher(listener, wrpl),
              Platform.WINDOWS,
              fs,
              listener,
              javaDir,
              path + "\\jdk.exe");
        }
      } catch (Exception e) {
        e.printStackTrace(listener.error("Failed to prepare Java"));
      }

      String id = generateServiceId(path);
      Win32Service slaveService = services.getService(id);
      if (slaveService == null) {
        logger.println(Messages.ManagedWindowsServiceLauncher_InstallingSlaveService());
        if (!DotNet.isInstalled(2, 0, name, auth)) {
          // abort the launch
          logger.println(Messages.ManagedWindowsServiceLauncher_DotNetRequired());
          return;
        }

        // copy exe
        logger.println(Messages.ManagedWindowsServiceLauncher_CopyingSlaveExe());
        copyStreamAndClose(
            getClass().getResource("/windows-service/jenkins.exe").openStream(),
            new SmbFile(remoteRoot, "jenkins-slave.exe").getOutputStream());

        copySlaveJar(logger, remoteRoot);

        // copy jenkins-slave.xml
        logger.println(Messages.ManagedWindowsServiceLauncher_CopyingSlaveXml());
        String nodeNameEncoded = java.net.URLEncoder.encode(nodeName, "UTF-8").replace("+", "%20");
        String xml =
            generateSlaveXml(
                id,
                "\"%JAVA_HOME%\\bin\\java\"",
                " -jnlpUrl "
                    + Hudson.getInstance().getRootUrl()
                    + "computer/"
                    + nodeNameEncoded
                    + "/slave-agent.jnlp");
        copyStreamAndClose(
            new ByteArrayInputStream(xml.getBytes("UTF-8")),
            new SmbFile(remoteRoot, "jenkins-slave.xml").getOutputStream());

        // install it as a service
        logger.println(Messages.ManagedWindowsServiceLauncher_RegisteringService());
        Document dom = new SAXReader().read(new StringReader(xml));
        Win32Service svc = services.Get("Win32_Service").cast(Win32Service.class);
        int r =
            svc.Create(
                id,
                dom.selectSingleNode("/service/name").getText() + " at " + path,
                path + "\\jenkins-slave.exe",
                Win32OwnProcess,
                0,
                "Manual",
                true);
        if (r != 0) {
          throw new JIException(-1, ("Failed to create a service: " + svc.getErrorMessage(r)));
        }
        slaveService = services.getService(id);
      } else {
        copySlaveJar(logger, remoteRoot);
      }

      logger.println(Messages.ManagedWindowsServiceLauncher_StartingService());
      slaveService.start();

      // wait until we see the port.txt, but don't do so forever
      logger.println(Messages.ManagedWindowsServiceLauncher_WaitingForService());
      SmbFile portFile = new SmbFile(remoteRoot, "port.txt");
      for (int i = 0; !portFile.exists(); i++) {
        if (i >= 30) {
          throw new JIException(-1, Messages.ManagedWindowsServiceLauncher_ServiceDidntRespond());
        }
        Thread.sleep(1000);
      }
      int p = readSmbFile(portFile);

      // connect
      logger.println(Messages.ManagedWindowsServiceLauncher_ConnectingToPort(p));
      final Socket s = new Socket(name, p);

      // ready
      computer.setChannel(
          new BufferedInputStream(new SocketInputStream(s)),
          new BufferedOutputStream(new SocketOutputStream(s)),
          listener.getLogger(),
          new Listener() {
            @Override
            public void onClosed(Channel channel, IOException cause) {
              afterDisconnect(computer, listener);
            }
          });
      // destroy session to free the socket
      JISession.destroySession(session);
    } catch (SmbException e) {
      e.printStackTrace(listener.error(e.getMessage()));
    } catch (DocumentException e) {
      e.printStackTrace(listener.error(e.getMessage()));
    }
  }