[PATCH] Add examples/winexe

Volker Lendecke Volker.Lendecke at SerNet.DE
Mon Aug 27 14:32:37 UTC 2018


Hi!

A customer asked me to smb2-enable winexe from
https://sourceforge.net/projects/winexe/. That sourceforge project is
based on Samba code from 2012 and before. It was not feasible to fix
the code to modern internal Samba APIs, so I rewrote the core
Samba-side of things.

This has a few caveats:

It can't be tested in autobuild, we don't have Windows around

The Windows service files are GPLv3 only. Those are 1:1 taken from the
sourceforge project. The original author seems unreachable, so
relicensing looks difficult, and I don't feel capable of writing a
Windows service from scratch.

It requires mingw to build. We could add mingw to sn-devel, so at
least the build keeps working.

As winexe for some scenarious seems highly interesting, I would like
to get this into mainline Samba despite those caveats.

Comments?

Thanks, Volker

-- 
SerNet GmbH, Bahnhofsallee 1b, 37081 Göttingen
phone: +49-551-370000-0, fax: +49-551-370000-9
AG Göttingen, HRB 2816, GF: Dr. Johannes Loxen
http://www.sernet.de, mailto:kontakt at sernet.de

Meet us at Storage Developer Conference (SDC)
Santa Clara, CA USA, September 24th-27th 2018
-------------- next part --------------
From ef78b24bded8e2a2221963441b47adf572aca8a1 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Wed, 4 Apr 2018 16:18:28 +0200
Subject: [PATCH 1/4] libsmb: Add protocol-agnostic cli_read

So far only cli_pull could be called directly without looking at the
protocol. We did not have a simple read that did the right thing
depending on the protocol

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/libsmb/clireadwrite.c | 122 ++++++++++++++++++++++++++++++++++++++++++
 source3/libsmb/proto.h        |   9 ++++
 2 files changed, 131 insertions(+)

diff --git a/source3/libsmb/clireadwrite.c b/source3/libsmb/clireadwrite.c
index 67870d8c40b..a93f35727a0 100644
--- a/source3/libsmb/clireadwrite.c
+++ b/source3/libsmb/clireadwrite.c
@@ -700,6 +700,128 @@ NTSTATUS cli_pull(struct cli_state *cli, uint16_t fnum,
 	return status;
 }
 
+struct cli_read_state {
+	struct cli_state *cli;
+	char *buf;
+	size_t buflen;
+	size_t received;
+};
+
+static void cli_read_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_read_send(
+	TALLOC_CTX *mem_ctx,
+	struct tevent_context *ev,
+	struct cli_state *cli,
+	uint16_t fnum,
+	char *buf,
+	off_t offset,
+	size_t size)
+{
+	struct tevent_req *req, *subreq;
+	struct cli_read_state *state;
+
+	req = tevent_req_create(mem_ctx, &state, struct cli_read_state);
+	if (req == NULL) {
+		return NULL;
+	}
+	state->cli = cli;
+	state->buf = buf;
+	state->buflen = size;
+
+	if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
+		uint32_t max_size;
+		bool ok;
+
+		ok = smb2cli_conn_req_possible(state->cli->conn, &max_size);
+		if (!ok) {
+			tevent_req_nterror(
+				req,
+				NT_STATUS_INSUFFICIENT_RESOURCES);
+			return tevent_req_post(req, ev);
+		}
+
+		/*
+		 * downgrade depending on the available credits
+		 */
+		size = MIN(max_size, size);
+
+		subreq = cli_smb2_read_send(
+			state, ev, cli, fnum, offset, size);
+		if (tevent_req_nomem(subreq, req)) {
+			return tevent_req_post(req, ev);
+		}
+	} else {
+		bool ok;
+		ok = smb1cli_conn_req_possible(state->cli->conn);
+		if (!ok) {
+			tevent_req_nterror(
+				req,
+				NT_STATUS_INSUFFICIENT_RESOURCES);
+			return tevent_req_post(req, ev);
+		}
+
+		subreq = cli_read_andx_send(
+			state, ev, cli, fnum, offset, size);
+		if (tevent_req_nomem(subreq, req)) {
+			return tevent_req_post(req, ev);
+		}
+	}
+
+	tevent_req_set_callback(subreq, cli_read_done, req);
+
+	return req;
+}
+
+static void cli_read_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct cli_read_state *state = tevent_req_data(
+		req, struct cli_read_state);
+	NTSTATUS status;
+	ssize_t received;
+	uint8_t *buf;
+
+	if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
+		status = cli_smb2_read_recv(subreq, &received, &buf);
+	} else {
+		status = cli_read_andx_recv(subreq, &received, &buf);
+	}
+
+	if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE)) {
+		received = 0;
+		status = NT_STATUS_OK;
+	}
+	if (tevent_req_nterror(req, status)) {
+		return;
+	}
+	if ((received < 0) || (received > state->buflen)) {
+		state->received = 0;
+		tevent_req_nterror(req, NT_STATUS_UNEXPECTED_IO_ERROR);
+		return;
+	}
+
+	memcpy(state->buf, buf, received);
+	state->received = received;
+	tevent_req_done(req);
+}
+
+NTSTATUS cli_read_recv(struct tevent_req *req, size_t *received)
+{
+	struct cli_read_state *state = tevent_req_data(
+		req, struct cli_read_state);
+	NTSTATUS status;
+
+	if (tevent_req_is_nterror(req, &status)) {
+		return status;
+	}
+	if (received != NULL) {
+		*received = state->received;
+	}
+	return NT_STATUS_OK;
+}
+
 static NTSTATUS cli_read_sink(char *buf, size_t n, void *priv)
 {
 	char **pbuf = (char **)priv;
diff --git a/source3/libsmb/proto.h b/source3/libsmb/proto.h
index 2bd61b1d2c2..06d0a0c9e06 100644
--- a/source3/libsmb/proto.h
+++ b/source3/libsmb/proto.h
@@ -839,6 +839,15 @@ NTSTATUS cli_pull(struct cli_state *cli, uint16_t fnum,
 		  off_t start_offset, off_t size, size_t window_size,
 		  NTSTATUS (*sink)(char *buf, size_t n, void *priv),
 		  void *priv, off_t *received);
+struct tevent_req *cli_read_send(
+	TALLOC_CTX *mem_ctx,
+	struct tevent_context *ev,
+	struct cli_state *cli,
+	uint16_t fnum,
+	char *buf,
+	off_t offset,
+	size_t size);
+NTSTATUS cli_read_recv(struct tevent_req *req, size_t *received);
 NTSTATUS cli_read(struct cli_state *cli, uint16_t fnum,
 		  char *buf, off_t offset, size_t size,
 		  size_t *nread);
-- 
2.11.0


From 8892ceebe12fb3648a8348202ff3abf835c802ad Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Wed, 4 Apr 2018 16:19:52 +0200
Subject: [PATCH 2/4] libsmb: Rename cli_writeall_send/recv to
 cli_smb1_writeall_send/recv

Preparing a protocol agnostic writeall

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/libsmb/clireadwrite.c | 45 ++++++++++++++++++++++---------------------
 1 file changed, 23 insertions(+), 22 deletions(-)

diff --git a/source3/libsmb/clireadwrite.c b/source3/libsmb/clireadwrite.c
index a93f35727a0..844c0b345aa 100644
--- a/source3/libsmb/clireadwrite.c
+++ b/source3/libsmb/clireadwrite.c
@@ -1065,7 +1065,7 @@ NTSTATUS cli_write_andx_recv(struct tevent_req *req, size_t *pwritten)
 	return NT_STATUS_OK;
 }
 
-struct cli_writeall_state {
+struct cli_smb1_writeall_state {
 	struct tevent_context *ev;
 	struct cli_state *cli;
 	uint16_t fnum;
@@ -1076,20 +1076,21 @@ struct cli_writeall_state {
 	size_t written;
 };
 
-static void cli_writeall_written(struct tevent_req *req);
+static void cli_smb1_writeall_written(struct tevent_req *req);
 
-static struct tevent_req *cli_writeall_send(TALLOC_CTX *mem_ctx,
-					    struct tevent_context *ev,
-					    struct cli_state *cli,
-					    uint16_t fnum,
-					    uint16_t mode,
-					    const uint8_t *buf,
-					    off_t offset, size_t size)
+static struct tevent_req *cli_smb1_writeall_send(TALLOC_CTX *mem_ctx,
+						 struct tevent_context *ev,
+						 struct cli_state *cli,
+						 uint16_t fnum,
+						 uint16_t mode,
+						 const uint8_t *buf,
+						 off_t offset, size_t size)
 {
 	struct tevent_req *req, *subreq;
-	struct cli_writeall_state *state;
+	struct cli_smb1_writeall_state *state;
 
-	req = tevent_req_create(mem_ctx, &state, struct cli_writeall_state);
+	req = tevent_req_create(mem_ctx, &state,
+				struct cli_smb1_writeall_state);
 	if (req == NULL) {
 		return NULL;
 	}
@@ -1108,16 +1109,16 @@ static struct tevent_req *cli_writeall_send(TALLOC_CTX *mem_ctx,
 	if (tevent_req_nomem(subreq, req)) {
 		return tevent_req_post(req, ev);
 	}
-	tevent_req_set_callback(subreq, cli_writeall_written, req);
+	tevent_req_set_callback(subreq, cli_smb1_writeall_written, req);
 	return req;
 }
 
-static void cli_writeall_written(struct tevent_req *subreq)
+static void cli_smb1_writeall_written(struct tevent_req *subreq)
 {
 	struct tevent_req *req = tevent_req_callback_data(
 		subreq, struct tevent_req);
-	struct cli_writeall_state *state = tevent_req_data(
-		req, struct cli_writeall_state);
+	struct cli_smb1_writeall_state *state = tevent_req_data(
+		req, struct cli_smb1_writeall_state);
 	NTSTATUS status;
 	size_t written, to_write;
 
@@ -1148,14 +1149,14 @@ static void cli_writeall_written(struct tevent_req *subreq)
 	if (tevent_req_nomem(subreq, req)) {
 		return;
 	}
-	tevent_req_set_callback(subreq, cli_writeall_written, req);
+	tevent_req_set_callback(subreq, cli_smb1_writeall_written, req);
 }
 
-static NTSTATUS cli_writeall_recv(struct tevent_req *req,
-				  size_t *pwritten)
+static NTSTATUS cli_smb1_writeall_recv(struct tevent_req *req,
+				       size_t *pwritten)
 {
-	struct cli_writeall_state *state = tevent_req_data(
-		req, struct cli_writeall_state);
+	struct cli_smb1_writeall_state *state = tevent_req_data(
+		req, struct cli_smb1_writeall_state);
 	NTSTATUS status;
 
 	if (tevent_req_is_nterror(req, &status)) {
@@ -1191,7 +1192,7 @@ NTSTATUS cli_writeall(struct cli_state *cli, uint16_t fnum, uint16_t mode,
 		req = cli_smb2_writeall_send(frame, ev, cli, fnum, mode,
 					     buf, offset, size);
 	} else {
-		req = cli_writeall_send(frame, ev, cli, fnum, mode,
+		req = cli_smb1_writeall_send(frame, ev, cli, fnum, mode,
 					buf, offset, size);
 	}
 	if (req == NULL) {
@@ -1203,7 +1204,7 @@ NTSTATUS cli_writeall(struct cli_state *cli, uint16_t fnum, uint16_t mode,
 	if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
 		status = cli_smb2_writeall_recv(req, pwritten);
 	} else {
-		status = cli_writeall_recv(req, pwritten);
+		status = cli_smb1_writeall_recv(req, pwritten);
 	}
  fail:
 	TALLOC_FREE(frame);
-- 
2.11.0


From 461cc80d3c1c08b54b042d8f29c9d2e3e3621893 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Wed, 4 Apr 2018 16:32:01 +0200
Subject: [PATCH 3/4] libsmb: Expose protocol-agnostic cli_writeall_send/recv

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/libsmb/clireadwrite.c | 106 +++++++++++++++++++++++++++++++++++++-----
 source3/libsmb/proto.h        |  10 ++++
 2 files changed, 104 insertions(+), 12 deletions(-)

diff --git a/source3/libsmb/clireadwrite.c b/source3/libsmb/clireadwrite.c
index 844c0b345aa..6bf3df6913f 100644
--- a/source3/libsmb/clireadwrite.c
+++ b/source3/libsmb/clireadwrite.c
@@ -1168,6 +1168,98 @@ static NTSTATUS cli_smb1_writeall_recv(struct tevent_req *req,
 	return NT_STATUS_OK;
 }
 
+struct cli_writeall_state {
+	struct cli_state *cli;
+	size_t written;
+};
+
+static void cli_writeall_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_writeall_send(
+	TALLOC_CTX *mem_ctx,
+	struct tevent_context *ev,
+	struct cli_state *cli,
+	uint16_t fnum,
+	uint16_t mode,
+	const uint8_t *buf,
+	off_t offset,
+	size_t size)
+{
+	struct tevent_req *req, *subreq;
+	struct cli_writeall_state *state;
+
+	req = tevent_req_create(mem_ctx, &state, struct cli_writeall_state);
+	if (req == NULL) {
+		return NULL;
+	}
+	state->cli = cli;
+
+	if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+		subreq = cli_smb2_writeall_send(
+			state,
+			ev,
+			cli,
+			fnum,
+			mode,
+			buf,
+			offset,
+			size);
+	} else {
+		subreq = cli_smb1_writeall_send(
+			state,
+			ev,
+			cli,
+			fnum,
+			mode,
+			buf,
+			offset,
+			size);
+	}
+
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq, cli_writeall_done, req);
+
+	return req;
+}
+
+static void cli_writeall_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct cli_writeall_state *state = tevent_req_data(
+		req, struct cli_writeall_state);
+	NTSTATUS status;
+
+	if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
+		status = cli_smb2_writeall_recv(subreq, &state->written);
+	} else {
+		status = cli_smb1_writeall_recv(subreq, &state->written);
+	}
+	TALLOC_FREE(subreq);
+	if (tevent_req_nterror(req, status)) {
+		return;
+	}
+	tevent_req_done(req);
+}
+
+NTSTATUS cli_writeall_recv(struct tevent_req *req, size_t *pwritten)
+{
+	struct cli_writeall_state *state = tevent_req_data(
+		req, struct cli_writeall_state);
+	NTSTATUS status;
+
+	if (tevent_req_is_nterror(req, &status)) {
+		return status;
+	}
+	if (pwritten != NULL) {
+		*pwritten = state->written;
+	}
+	return NT_STATUS_OK;
+}
+
+
 NTSTATUS cli_writeall(struct cli_state *cli, uint16_t fnum, uint16_t mode,
 		      const uint8_t *buf, off_t offset, size_t size,
 		      size_t *pwritten)
@@ -1188,24 +1280,14 @@ NTSTATUS cli_writeall(struct cli_state *cli, uint16_t fnum, uint16_t mode,
 	if (ev == NULL) {
 		goto fail;
 	}
-	if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
-		req = cli_smb2_writeall_send(frame, ev, cli, fnum, mode,
-					     buf, offset, size);
-	} else {
-		req = cli_smb1_writeall_send(frame, ev, cli, fnum, mode,
-					buf, offset, size);
-	}
+	req = cli_writeall_send(frame, ev, cli, fnum, mode, buf, offset, size);
 	if (req == NULL) {
 		goto fail;
 	}
 	if (!tevent_req_poll_ntstatus(req, ev, &status)) {
 		goto fail;
 	}
-	if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
-		status = cli_smb2_writeall_recv(req, pwritten);
-	} else {
-		status = cli_smb1_writeall_recv(req, pwritten);
-	}
+	status = cli_writeall_recv(req, pwritten);
  fail:
 	TALLOC_FREE(frame);
 	return status;
diff --git a/source3/libsmb/proto.h b/source3/libsmb/proto.h
index 06d0a0c9e06..d0bc3e78771 100644
--- a/source3/libsmb/proto.h
+++ b/source3/libsmb/proto.h
@@ -868,6 +868,16 @@ struct tevent_req *cli_write_andx_send(TALLOC_CTX *mem_ctx,
 				       off_t offset, size_t size);
 NTSTATUS cli_write_andx_recv(struct tevent_req *req, size_t *pwritten);
 
+struct tevent_req *cli_writeall_send(
+	TALLOC_CTX *mem_ctx,
+	struct tevent_context *ev,
+	struct cli_state *cli,
+	uint16_t fnum,
+	uint16_t mode,
+	const uint8_t *buf,
+	off_t offset,
+	size_t size);
+NTSTATUS cli_writeall_recv(struct tevent_req *req, size_t *pwritten);
 NTSTATUS cli_writeall(struct cli_state *cli, uint16_t fnum, uint16_t mode,
 		      const uint8_t *buf, off_t offset, size_t size,
 		      size_t *pwritten);
-- 
2.11.0


From 1baaf4356640378e76b674525b4757d4d0a36e36 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 10 Apr 2018 17:18:18 +0200
Subject: [PATCH 4/4] examples: Add winexe re-implemented on current Samba libs

winexe from https://sourceforge.net/projects/winexe/ is a project
based on Samba libraries from 2012. According to the winexe git
repository the last Samba commit winexe was updated to is 47bbf9886f0c
from November 6, 2012. As winexe uses unpublished Samba internal
libraries, it broke over time.

This is a port of the winexe functionality to more modern Samba
versions. It still uses internal APIs, but it being part of the tree
means that it is much easier to keep up to date.

The Windows service files were taken literally from the original
winexe from the sourceforge git. Andrzej Hajda chose GPLv3 only and
not GPLv3+. As GPL evolves very slowly, this should not be a practical
problem for quite some time.

To build it under Linux, you need mingw binaries on your build
system. Under Debian stretch, the package names are gcc-mingw-w64 and
friends.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 examples/winexe/README        |   18 +
 examples/winexe/winexe.c      | 1867 +++++++++++++++++++++++++++++++++++++++++
 examples/winexe/winexesvc.c   |  745 ++++++++++++++++
 examples/winexe/winexesvc.h   |   42 +
 examples/winexe/wscript       |   29 +
 examples/winexe/wscript_build |   87 ++
 source3/wscript_build         |    1 +
 wscript                       |    1 +
 8 files changed, 2790 insertions(+)
 create mode 100644 examples/winexe/README
 create mode 100644 examples/winexe/winexe.c
 create mode 100644 examples/winexe/winexesvc.c
 create mode 100644 examples/winexe/winexesvc.h
 create mode 100644 examples/winexe/wscript
 create mode 100644 examples/winexe/wscript_build

diff --git a/examples/winexe/README b/examples/winexe/README
new file mode 100644
index 00000000000..c688e5d1ba4
--- /dev/null
+++ b/examples/winexe/README
@@ -0,0 +1,18 @@
+winexe from https://sourceforge.net/projects/winexe/ is a project
+based on Samba libraries from 2012. According to the winexe git
+repository the last Samba commit winexe was updated to is 47bbf9886f0c
+from November 6, 2012. As winexe uses unpublished Samba internal
+libraries, it broke over time.
+
+This is a port of the winexe functionality to more modern Samba
+versions. It still uses internal APIs, but it being part of the tree
+means that it is much easier to keep up to date.
+
+The Windows service files were taken literally from the original
+winexe from the sourceforge git. Andrzej Hajda chose GPLv3 only and
+not GPLv3+. As GPL evolves very slowly, this should not be a practical
+problem for quite some time.
+
+To build it under Linux, you need mingw binaries on your build
+system. Under Debian stretch, the package names are gcc-mingw-w64 and
+friends.
diff --git a/examples/winexe/winexe.c b/examples/winexe/winexe.c
new file mode 100644
index 00000000000..cf667a64ebc
--- /dev/null
+++ b/examples/winexe/winexe.c
@@ -0,0 +1,1867 @@
+/*
+ * Samba Unix/Linux CIFS implementation
+ *
+ * winexe
+ *
+ * Copyright (C) 2018 Volker Lendecke <vl at samba.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include <tevent.h>
+#include <popt.h>
+#include "version.h"
+#include "lib/param/param.h"
+#include "auth/credentials/credentials.h"
+#include "lib/util/talloc_stack.h"
+#include "lib/util/tevent_ntstatus.h"
+#include "lib/util/sys_rw.h"
+#include "libsmb/proto.h"
+#include "librpc/gen_ndr/ndr_svcctl_c.h"
+#include "rpc_client/cli_pipe.h"
+#include "libcli/smb/smbXcli_base.h"
+#include "libcli/util/werror.h"
+#include "lib/async_req/async_sock.h"
+#include "client.h"
+
+#define SVC_INTERACTIVE 1
+#define SVC_IGNORE_INTERACTIVE 2
+#define SVC_INTERACTIVE_MASK 3
+#define SVC_FORCE_UPLOAD 4
+#define SVC_OS64BIT 8
+#define SVC_OSCHOOSE 16
+#define SVC_UNINSTALL 32
+#define SVC_SYSTEM 64
+
+#define SERVICE_NAME "winexesvc"
+
+#define PIPE_NAME "ahexec"
+#define PIPE_NAME_IN "ahexec_stdin%08X"
+#define PIPE_NAME_OUT "ahexec_stdout%08X"
+#define PIPE_NAME_ERR "ahexec_stderr%08X"
+
+static const char version_message_fmt[] = "winexe version %d.%d\n"
+	"This program may be freely redistributed under the terms of the "
+	"GNU GPLv3\n";
+
+struct program_options {
+	char *hostname;
+	char *cmd;
+	struct cli_credentials *credentials;
+	char *runas;
+	char *runas_file;
+	int flags;
+};
+
+static void parse_args(int argc, const char *argv[],
+		       TALLOC_CTX *mem_ctx,
+		       struct program_options *options,
+		       struct loadparm_context *lp_ctx)
+{
+	poptContext pc;
+	int opt, i;
+	struct cli_credentials *cred;
+
+	int argc_new;
+	char **argv_new;
+
+	int flag_interactive = SVC_IGNORE_INTERACTIVE;
+	int flag_ostype = 2;
+	int flag_reinstall = 0;
+	int flag_uninstall = 0;
+	int flag_help = 0;
+	int flag_version = 0;
+	int flag_nopass = 0;
+	char *opt_user = NULL;
+	char *opt_kerberos = NULL;
+	char *opt_auth_file = NULL;
+	char *opt_debuglevel = NULL;
+
+	struct poptOption long_options[] = {
+		{ "help", 'h', POPT_ARG_NONE, &flag_help, 0,
+		  "Display help message" },
+		{ "version", 'V', POPT_ARG_NONE, &flag_version, 0,
+		  "Display version number" },
+		{ "user", 'U', POPT_ARG_STRING, &opt_user, 0,
+		  "Set the network username", "[DOMAIN/]USERNAME[%PASSWORD]" },
+		{ "authentication-file", 'A',
+		  POPT_ARG_STRING, &opt_auth_file, 0,
+		  "Get the credentials from a file", "FILE" },
+		{ "no-pass", 'N', POPT_ARG_NONE, &flag_nopass, 0,
+		  "Do not ask for a password", NULL },
+		{ "kerberos", 'k', POPT_ARG_STRING, &opt_kerberos, 0,
+		  "Use Kerberos, -k [yes|no]" },
+		{ "debuglevel", 'd', POPT_ARG_STRING, &opt_debuglevel, 0,
+		  "Set debug level", "DEBUGLEVEL" },
+		{ "uninstall", 0, POPT_ARG_NONE, &flag_uninstall, 0,
+		  "Uninstall winexe service after remote execution", NULL},
+		{ "reinstall", 0, POPT_ARG_NONE, &flag_reinstall, 0,
+		  "Reinstall winexe service before remote execution", NULL},
+		{ "runas", 0, POPT_ARG_STRING, &options->runas, 0,
+		  "Run as the given user (BEWARE: this password is sent "
+		  "in cleartext over the network!)",
+		  "[DOMAIN\\]USERNAME%PASSWORD"},
+		{ "runas-file", 0, POPT_ARG_STRING, &options->runas_file, 0,
+		  "Run as user options defined in a file", "FILE"},
+		{ "interactive", 0, POPT_ARG_INT, &flag_interactive, 0,
+		  "Desktop interaction: 0 - disallow, 1 - allow. If allow, "
+		  "also use the --system switch (Windows requirement). Vista "
+		  "does not support this option.", "0|1"},
+		{ "ostype", 0, POPT_ARG_INT, &flag_ostype, 0,
+		  "OS type: 0 - 32-bit, 1 - 64-bit, 2 - winexe will decide. "
+		  "Determines which version (32-bit or 64-bit) of service "
+		  "will be installed.", "0|1|2"},
+		POPT_TABLEEND
+	};
+
+	ZERO_STRUCTP(options);
+
+	pc = poptGetContext(argv[0], argc, (const char **) argv, long_options,
+			    0);
+
+	poptSetOtherOptionHelp(pc, "[OPTION]... //HOST COMMAND\nOptions:");
+
+	if (((opt = poptGetNextOpt(pc)) != -1) || flag_help || flag_version) {
+		fprintf(stderr, version_message_fmt, SAMBA_VERSION_MAJOR,
+			SAMBA_VERSION_MINOR);
+		if (flag_version) {
+			exit(0);
+		}
+		poptPrintHelp(pc, stdout, 0);
+		if (flag_help) {
+			exit(0);
+		}
+		exit(1);
+	}
+
+	argv_new = discard_const_p(char *, poptGetArgs(pc));
+
+	argc_new = argc;
+	for (i = 0; i < argc; i++) {
+		if (!argv_new || argv_new[i] == NULL) {
+			argc_new = i;
+			break;
+		}
+	}
+
+	if (argc_new != 2 || argv_new[0][0] != '/' || argv_new[0][1] != '/') {
+		fprintf(stderr, version_message_fmt, SAMBA_VERSION_MAJOR,
+			SAMBA_VERSION_MINOR);
+		poptPrintHelp(pc, stdout, 0);
+		exit(1);
+	}
+
+	if (opt_debuglevel) {
+		lp_set_cmdline("log level", opt_debuglevel);
+	}
+
+	cred = cli_credentials_init(mem_ctx);
+
+	if (opt_user) {
+		cli_credentials_parse_string(cred, opt_user, CRED_SPECIFIED);
+	} else if (opt_auth_file) {
+		cli_credentials_parse_file(cred, opt_auth_file,
+					   CRED_SPECIFIED);
+	}
+
+	cli_credentials_guess(cred, lp_ctx);
+	if (!cli_credentials_get_password(cred) && !flag_nopass) {
+		char *p = getpass("Enter password: ");
+		if (*p) {
+			cli_credentials_set_password(cred, p, CRED_SPECIFIED);
+		}
+	}
+
+	if (opt_kerberos) {
+		cli_credentials_set_kerberos_state(cred,
+		                                   strcmp(opt_kerberos, "yes")
+		                                   ? CRED_MUST_USE_KERBEROS
+		                                   : CRED_DONT_USE_KERBEROS);
+	}
+
+	if (options->runas == NULL && options->runas_file != NULL) {
+		struct cli_credentials *runas_cred;
+		const char *user;
+		const char *pass;
+
+		runas_cred = cli_credentials_init(mem_ctx);
+		cli_credentials_parse_file(runas_cred, options->runas_file,
+					   CRED_SPECIFIED);
+
+		user = cli_credentials_get_username(runas_cred);
+		pass = cli_credentials_get_password(runas_cred);
+
+		if (user && pass) {
+			char buffer[1024];
+			const char *dom;
+
+			dom = cli_credentials_get_domain(runas_cred);
+			if (dom) {
+				snprintf(buffer, sizeof(buffer), "%s\\%s%%%s",
+					 dom, user, pass);
+			} else {
+				snprintf(buffer, sizeof(buffer), "%s%%%s",
+					 user, pass);
+			}
+			buffer[sizeof(buffer)-1] = '\0';
+			options->runas = talloc_strdup(mem_ctx, buffer);
+		}
+	}
+
+	options->credentials = cred;
+
+	options->hostname = argv_new[0] + 2;
+	options->cmd = argv_new[1];
+
+	options->flags = flag_interactive;
+	if (flag_reinstall) {
+		options->flags |= SVC_FORCE_UPLOAD;
+	}
+	if (flag_ostype == 1) {
+		options->flags |= SVC_OS64BIT;
+	}
+	if (flag_ostype == 2) {
+		options->flags |= SVC_OSCHOOSE;
+	}
+	if (flag_uninstall) {
+		options->flags |= SVC_UNINSTALL;
+	}
+}
+
+static NTSTATUS winexe_svc_upload(
+	const char *hostname,
+	const char *service_filename,
+	const DATA_BLOB *svc32_exe,
+	const DATA_BLOB *svc64_exe,
+	struct cli_credentials *credentials,
+	int flags)
+{
+	struct cli_state *cli;
+	uint16_t fnum;
+	NTSTATUS status;
+	const DATA_BLOB *binary = NULL;
+
+	status = cli_full_connection_creds(
+		&cli,
+		NULL,
+		hostname,
+		NULL,
+		445,
+		"ADMIN$",
+		"?????",
+		credentials,
+		0,
+		0);
+	if (!NT_STATUS_IS_OK(status)) {
+		DBG_WARNING("cli_full_connection_creds failed: %s\n",
+			    nt_errstr(status));
+		return status;
+	}
+
+	if (flags & SVC_FORCE_UPLOAD) {
+		status = cli_unlink(cli, service_filename, 0);
+		if (!NT_STATUS_IS_OK(status)) {
+			DBG_WARNING("cli_unlink failed: %s\n",
+				    nt_errstr(status));
+		}
+	}
+
+	if (flags & SVC_OSCHOOSE) {
+		status = cli_chkpath(cli, "SysWoW64");
+		if (NT_STATUS_IS_OK(status)) {
+			flags |= SVC_OS64BIT;
+		}
+	}
+
+	if (flags & SVC_OS64BIT) {
+		binary = svc64_exe;
+	} else {
+		binary = svc32_exe;
+	}
+
+	if (binary == NULL) {
+		//TODO
+	}
+
+	status = cli_ntcreate(
+		cli,
+		service_filename,
+		0,			/* CreatFlags */
+		SEC_FILE_WRITE_DATA,    /* DesiredAccess */
+		FILE_ATTRIBUTE_NORMAL,  /* FileAttributes */
+		FILE_SHARE_WRITE|FILE_SHARE_READ, /* ShareAccess */
+		FILE_OPEN_IF,		 /* CreateDisposition */
+		FILE_NON_DIRECTORY_FILE, /* CreateOptions */
+		0,			 /* SecurityFlags */
+		&fnum,
+		NULL);		/* CreateReturns */
+	if (!NT_STATUS_IS_OK(status)) {
+		DBG_WARNING("Could not create %s: %s\n", service_filename,
+			    nt_errstr(status));
+		goto done;
+	}
+
+	status = cli_writeall(
+		cli,
+		fnum,
+		0,
+		binary->data,
+		0,
+		binary->length,
+		NULL);
+	if (!NT_STATUS_IS_OK(status)) {
+		DBG_WARNING("Could not write file: %s\n", nt_errstr(status));
+		goto close_done;
+	}
+
+close_done:
+	status = cli_close(cli, fnum);
+	if (!NT_STATUS_IS_OK(status)) {
+		DBG_WARNING("Close(%"PRIu16") failed for %s: %s\n", fnum,
+			    service_filename, nt_errstr(status));
+	}
+done:
+	TALLOC_FREE(cli);
+	return status;
+}
+
+static NTSTATUS winexe_svc_install(
+	struct cli_state *cli,
+	const char *hostname,
+	const char *service_name,
+	const char *service_filename,
+	const DATA_BLOB *svc32_exe,
+	const DATA_BLOB *svc64_exe,
+	struct cli_credentials *credentials,
+	int flags)
+{
+	TALLOC_CTX *frame = talloc_stackframe();
+	struct rpc_pipe_client *rpccli;
+	struct policy_handle scmanager_handle;
+	struct policy_handle service_handle;
+	struct SERVICE_STATUS service_status;
+	bool need_start = false;
+	bool need_conf = false;
+	NTSTATUS status;
+	WERROR werr;
+
+	status = cli_rpc_pipe_open_noauth_transport(
+		cli,
+		NCACN_NP,
+		&ndr_table_svcctl,
+		&rpccli);
+	if (!NT_STATUS_IS_OK(status)) {
+		DBG_WARNING("cli_rpc_pipe_open_noauth_transport failed: %s\n",
+			    nt_errstr(status));
+		goto done;
+	}
+
+	status = dcerpc_svcctl_OpenSCManagerW(
+		rpccli->binding_handle,
+		frame,
+		smbXcli_conn_remote_name(cli->conn),
+		NULL,
+		SEC_FLAG_MAXIMUM_ALLOWED,
+		&scmanager_handle,
+		&werr);
+	if (!NT_STATUS_IS_OK(status)) {
+		DBG_WARNING("dcerpc_svcctl_OpenSCManagerW failed: %s\n",
+			    nt_errstr(status));
+		goto done;
+	}
+	if (!W_ERROR_IS_OK(werr)) {
+		DBG_WARNING("dcerpc_svcctl_OpenSCManagerW failed: %s\n",
+			    win_errstr(werr));
+		goto done;
+	}
+
+	status = dcerpc_svcctl_OpenServiceW(
+		rpccli->binding_handle,
+		frame,
+		&scmanager_handle,
+		service_name,
+		SERVICE_ALL_ACCESS,
+		&service_handle,
+		&werr);
+	if (!NT_STATUS_IS_OK(status)) {
+		DBG_WARNING("dcerpc_svcctl_OpenServiceW failed: %s\n",
+			    nt_errstr(status));
+		goto close_scmanager;
+	}
+
+	if (W_ERROR_EQUAL(werr,  WERR_SERVICE_DOES_NOT_EXIST)) {
+		status = dcerpc_svcctl_CreateServiceW(
+			rpccli->binding_handle,
+			frame,
+			&scmanager_handle,
+			service_name,
+			NULL,
+			SERVICE_ALL_ACCESS,
+			SERVICE_TYPE_WIN32_OWN_PROCESS |
+			((flags & SVC_INTERACTIVE) ?
+			 SERVICE_TYPE_INTERACTIVE_PROCESS : 0),
+			SVCCTL_DEMAND_START,
+			SVCCTL_SVC_ERROR_NORMAL,
+			service_filename,
+			NULL,
+			NULL,
+			NULL,
+			0,
+			NULL,
+			NULL,
+			0,
+			&service_handle,
+			&werr);
+		if (!NT_STATUS_IS_OK(status)) {
+			DBG_WARNING("dcerpc_svcctl_CreateServiceW "
+				    "failed: %s\n", nt_errstr(status));
+			goto close_scmanager;
+		}
+		if (!W_ERROR_IS_OK(werr)) {
+			DBG_WARNING("dcerpc_svcctl_CreateServiceW "
+				    "failed: %s\n", win_errstr(werr));
+			status = werror_to_ntstatus(werr);
+			goto close_scmanager;
+		}
+	}
+
+	status = dcerpc_svcctl_QueryServiceStatus(
+		rpccli->binding_handle,
+		frame,
+		&service_handle,
+		&service_status,
+		&werr);
+
+	if (!NT_STATUS_IS_OK(status)) {
+		DBG_WARNING("dcerpc_svcctl_QueryServiceStatus "
+			    "failed: %s\n", nt_errstr(status));
+		goto close_service;
+	}
+	if (!W_ERROR_IS_OK(werr)) {
+		DBG_WARNING("dcerpc_svcctl_QueryServiceStatus "
+			    "failed: %s\n", win_errstr(werr));
+		status = werror_to_ntstatus(werr);
+		goto close_service;
+	}
+
+	if (!(flags & SVC_IGNORE_INTERACTIVE)) {
+		need_conf =
+			!(service_status.type &
+			  SERVICE_TYPE_INTERACTIVE_PROCESS) ^
+			!(flags & SVC_INTERACTIVE);
+	}
+
+	if (service_status.state == SVCCTL_STOPPED) {
+		need_start = true;
+	} else if (need_conf) {
+		status = dcerpc_svcctl_ControlService(
+			rpccli->binding_handle,
+			frame,
+			&service_handle,
+			SVCCTL_CONTROL_STOP,
+			&service_status,
+			&werr);
+
+		if (!NT_STATUS_IS_OK(status)) {
+			DBG_WARNING("dcerpc_svcctl_ControlServiceStatus "
+			    "failed: %s\n", nt_errstr(status));
+			goto close_service;
+		}
+		if (!W_ERROR_IS_OK(werr)) {
+			DBG_WARNING("dcerpc_svcctl_ControlServiceStatus "
+				    "failed: %s\n", win_errstr(werr));
+			status = werror_to_ntstatus(werr);
+			goto close_service;
+		}
+
+		do {
+			smb_msleep(100);
+
+			status = dcerpc_svcctl_QueryServiceStatus(
+				rpccli->binding_handle,
+				frame,
+				&service_handle,
+				&service_status,
+				&werr);
+
+			if (!NT_STATUS_IS_OK(status)) {
+				DBG_WARNING("dcerpc_svcctl_QueryServiceStatus "
+					    "failed: %s\n", nt_errstr(status));
+				goto close_service;
+			}
+			if (!W_ERROR_IS_OK(werr)) {
+				DBG_WARNING("dcerpc_svcctl_QueryServiceStatus "
+					    "failed: %s\n", win_errstr(werr));
+				status = werror_to_ntstatus(werr);
+				goto close_service;
+			}
+		} while (service_status.state == SVCCTL_STOP_PENDING);
+
+		need_start = 1;
+	}
+
+	if (need_conf) {
+		status = dcerpc_svcctl_ChangeServiceConfigW(
+			rpccli->binding_handle,
+			frame,
+			&service_handle,
+			SERVICE_TYPE_WIN32_OWN_PROCESS |
+			((flags & SVC_INTERACTIVE) ?
+			 SERVICE_TYPE_INTERACTIVE_PROCESS : 0), /* type */
+			UINT32_MAX, /* start_type, SERVICE_NO_CHANGE */
+			UINT32_MAX, /* error_control, SERVICE_NO_CHANGE */
+			NULL,	    /* binary_path */
+			NULL,	    /* load_order_group */
+			NULL,	    /* tag_id */
+			NULL,	    /* dependencies */
+			NULL,	    /* service_start_name */
+			NULL,	    /* password */
+			NULL,	    /* display_name */
+			&werr);
+
+		if (!NT_STATUS_IS_OK(status)) {
+			DBG_WARNING("dcerpc_svcctl_ChangeServiceConfigW "
+				    "failed: %s\n", nt_errstr(status));
+			goto close_service;
+		}
+		if (!W_ERROR_IS_OK(werr)) {
+			DBG_WARNING("dcerpc_svcctl_ChangeServiceConfigW "
+				    "failed: %s\n", win_errstr(werr));
+			status = werror_to_ntstatus(werr);
+			goto close_service;
+		}
+	}
+
+	if (need_start) {
+		status = winexe_svc_upload(
+			hostname,
+			service_filename,
+			svc32_exe,
+			svc64_exe,
+			credentials,
+			flags);
+		if (!NT_STATUS_IS_OK(status)) {
+			DBG_WARNING("winexe_svc_upload failed: %s\n",
+				    nt_errstr(status));
+			goto close_service;
+		}
+
+		status = dcerpc_svcctl_StartServiceW(
+			rpccli->binding_handle,
+			frame,
+			&service_handle,
+			0,	/* num_args */
+			NULL,	/* arguments */
+			&werr);
+
+		if (!NT_STATUS_IS_OK(status)) {
+			DBG_WARNING("dcerpc_svcctl_StartServiceW "
+				    "failed: %s\n", nt_errstr(status));
+			goto close_service;
+		}
+		if (!W_ERROR_IS_OK(werr)) {
+			DBG_WARNING("dcerpc_svcctl_StartServiceW "
+				    "failed: %s\n", win_errstr(werr));
+			status = werror_to_ntstatus(werr);
+			goto close_service;
+		}
+
+		do {
+			smb_msleep(100);
+
+			status = dcerpc_svcctl_QueryServiceStatus(
+				rpccli->binding_handle,
+				frame,
+				&service_handle,
+				&service_status,
+				&werr);
+
+			if (!NT_STATUS_IS_OK(status)) {
+				DBG_WARNING("dcerpc_svcctl_QueryServiceStatus "
+					    "failed: %s\n", nt_errstr(status));
+				goto close_service;
+			}
+			if (!W_ERROR_IS_OK(werr)) {
+				DBG_WARNING("dcerpc_svcctl_QueryServiceStatus "
+					    "failed: %s\n", win_errstr(werr));
+				status = werror_to_ntstatus(werr);
+				goto close_service;
+			}
+		} while (service_status.state == SVCCTL_START_PENDING);
+
+		if (service_status.state != SVCCTL_RUNNING) {
+			DBG_WARNING("Failed to start service\n");
+			status = NT_STATUS_UNSUCCESSFUL;
+			goto close_service;
+		}
+	}
+
+close_service:
+	{
+		NTSTATUS close_status;
+		WERROR close_werr;
+
+		close_status = dcerpc_svcctl_CloseServiceHandle(
+			rpccli->binding_handle,
+			frame,
+			&service_handle,
+			&close_werr);
+		if (!NT_STATUS_IS_OK(close_status)) {
+			DBG_WARNING("dcerpc_svcctl_CloseServiceHandle "
+				    "failed: %s\n", nt_errstr(close_status));
+			goto done;
+		}
+		if (!W_ERROR_IS_OK(close_werr)) {
+			DBG_WARNING("dcerpc_svcctl_CloseServiceHandle "
+				    " failed: %s\n", win_errstr(close_werr));
+			goto done;
+		}
+	}
+
+close_scmanager:
+	{
+		NTSTATUS close_status;
+		WERROR close_werr;
+
+		close_status = dcerpc_svcctl_CloseServiceHandle(
+			rpccli->binding_handle,
+			frame,
+			&scmanager_handle,
+			&close_werr);
+		if (!NT_STATUS_IS_OK(close_status)) {
+			DBG_WARNING("dcerpc_svcctl_CloseServiceHandle "
+				    "failed: %s\n", nt_errstr(close_status));
+			goto done;
+		}
+		if (!W_ERROR_IS_OK(close_werr)) {
+			DBG_WARNING("dcerpc_svcctl_CloseServiceHandle "
+				    " failed: %s\n", win_errstr(close_werr));
+			goto done;
+		}
+	}
+
+done:
+	TALLOC_FREE(rpccli);
+	TALLOC_FREE(frame);
+	return status;
+}
+
+static NTSTATUS winexe_svc_uninstall(
+	struct cli_state *cli,
+	const char *service_name)
+{
+	TALLOC_CTX *frame = talloc_stackframe();
+	struct rpc_pipe_client *rpccli;
+	struct policy_handle scmanager_handle;
+	struct policy_handle service_handle;
+	struct SERVICE_STATUS service_status;
+	NTSTATUS status;
+	WERROR werr;
+
+	status = cli_rpc_pipe_open_noauth_transport(
+		cli,
+		NCACN_NP,
+		&ndr_table_svcctl,
+		&rpccli);
+	if (!NT_STATUS_IS_OK(status)) {
+		DBG_WARNING("cli_rpc_pipe_open_noauth_transport failed: %s\n",
+			    nt_errstr(status));
+		goto done;
+	}
+
+	status = dcerpc_svcctl_OpenSCManagerW(
+		rpccli->binding_handle,
+		frame,
+		smbXcli_conn_remote_name(cli->conn),
+		NULL,
+		SEC_FLAG_MAXIMUM_ALLOWED,
+		&scmanager_handle,
+		&werr);
+	if (!NT_STATUS_IS_OK(status)) {
+		DBG_WARNING("dcerpc_svcctl_OpenSCManagerW failed: %s\n",
+			    nt_errstr(status));
+		goto done;
+	}
+	if (!W_ERROR_IS_OK(werr)) {
+		DBG_WARNING("dcerpc_svcctl_OpenSCManagerW failed: %s\n",
+			    win_errstr(werr));
+		goto done;
+	}
+
+	status = dcerpc_svcctl_OpenServiceW(
+		rpccli->binding_handle,
+		frame,
+		&scmanager_handle,
+		service_name,
+		SERVICE_ALL_ACCESS,
+		&service_handle,
+		&werr);
+	if (!NT_STATUS_IS_OK(status)) {
+		DBG_WARNING("dcerpc_svcctl_OpenServiceW failed: %s\n",
+			    nt_errstr(status));
+		goto close_scmanager;
+	}
+	if (!W_ERROR_IS_OK(werr)) {
+		DBG_WARNING("dcerpc_svcctl_OpenServiceW failed: %s\n",
+			    win_errstr(werr));
+		status = werror_to_ntstatus(werr);
+		goto close_scmanager;
+	}
+
+	status = dcerpc_svcctl_ControlService(
+		rpccli->binding_handle,
+		frame,
+		&service_handle,
+		SVCCTL_CONTROL_STOP,
+		&service_status,
+		&werr);
+	if (!NT_STATUS_IS_OK(status)) {
+		DBG_WARNING("dcerpc_svcctl_ControlServiceStatus "
+			    "failed: %s\n", nt_errstr(status));
+		goto close_service;
+	}
+	if (!W_ERROR_IS_OK(werr)) {
+		DBG_WARNING("dcerpc_svcctl_ControlServiceStatus "
+			    "failed: %s\n", win_errstr(werr));
+		status = werror_to_ntstatus(werr);
+		goto close_service;
+	}
+
+	do {
+		smb_msleep(100);
+
+		status = dcerpc_svcctl_QueryServiceStatus(
+			rpccli->binding_handle,
+			frame,
+			&service_handle,
+			&service_status,
+			&werr);
+
+		if (!NT_STATUS_IS_OK(status)) {
+			DBG_WARNING("dcerpc_svcctl_QueryServiceStatus "
+				    "failed: %s\n", nt_errstr(status));
+			goto close_service;
+		}
+		if (!W_ERROR_IS_OK(werr)) {
+			DBG_WARNING("dcerpc_svcctl_QueryServiceStatus "
+				    "failed: %s\n", win_errstr(werr));
+			status = werror_to_ntstatus(werr);
+			goto close_service;
+		}
+	} while (service_status.state != SVCCTL_STOPPED);
+
+	status = dcerpc_svcctl_DeleteService(
+		rpccli->binding_handle,
+		frame,
+		&service_handle,
+		&werr);
+	if (!NT_STATUS_IS_OK(status)) {
+		DBG_WARNING("dcerpc_svcctl_DeleteService "
+			    "failed: %s\n", nt_errstr(status));
+		goto close_service;
+	}
+	if (!W_ERROR_IS_OK(werr)) {
+		DBG_WARNING("dcerpc_svcctl_DeleteService "
+			    "failed: %s\n", win_errstr(werr));
+		status = werror_to_ntstatus(werr);
+		goto close_service;
+	}
+
+close_service:
+	{
+		NTSTATUS close_status;
+		WERROR close_werr;
+
+		close_status = dcerpc_svcctl_CloseServiceHandle(
+			rpccli->binding_handle,
+			frame,
+			&service_handle,
+			&close_werr);
+		if (!NT_STATUS_IS_OK(close_status)) {
+			DBG_WARNING("dcerpc_svcctl_CloseServiceHandle "
+				    "failed: %s\n", nt_errstr(close_status));
+			goto done;
+		}
+		if (!W_ERROR_IS_OK(close_werr)) {
+			DBG_WARNING("dcerpc_svcctl_CloseServiceHandle "
+				    " failed: %s\n", win_errstr(close_werr));
+			goto done;
+		}
+	}
+
+close_scmanager:
+	{
+		NTSTATUS close_status;
+		WERROR close_werr;
+
+		close_status = dcerpc_svcctl_CloseServiceHandle(
+			rpccli->binding_handle,
+			frame,
+			&scmanager_handle,
+			&close_werr);
+		if (!NT_STATUS_IS_OK(close_status)) {
+			DBG_WARNING("dcerpc_svcctl_CloseServiceHandle "
+				    "failed: %s\n", nt_errstr(close_status));
+			goto done;
+		}
+		if (!W_ERROR_IS_OK(close_werr)) {
+			DBG_WARNING("dcerpc_svcctl_CloseServiceHandle "
+				    " failed: %s\n", win_errstr(close_werr));
+			goto done;
+		}
+	}
+
+done:
+	TALLOC_FREE(rpccli);
+	TALLOC_FREE(frame);
+	return status;
+}
+
+struct winexe_out_pipe_state {
+	struct tevent_context *ev;
+	struct cli_state *cli;
+	uint16_t out_pipe;
+	int out_fd;
+	char out_inbuf[256];
+};
+
+static void winexe_out_pipe_opened(struct tevent_req *subreq);
+static void winexe_out_pipe_got_data(struct tevent_req *subreq);
+static void winexe_out_pipe_closed(struct tevent_req *subreq);
+
+static struct tevent_req *winexe_out_pipe_send(
+	TALLOC_CTX *mem_ctx,
+	struct tevent_context *ev,
+	struct cli_state *cli,
+	const char *pipe_name,
+	int out_fd)
+{
+	struct tevent_req *req, *subreq;
+	struct winexe_out_pipe_state *state;
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct winexe_out_pipe_state);
+	if (req == NULL) {
+		return NULL;
+	}
+	state->ev = ev;
+	state->cli = cli;
+	state->out_fd = out_fd;
+
+	subreq = cli_ntcreate_send(
+		state,
+		state->ev,
+		state->cli,
+		pipe_name,
+		0,
+		SEC_RIGHTS_FILE_READ|SEC_RIGHTS_FILE_WRITE|
+		SEC_RIGHTS_FILE_EXECUTE,
+		0,		/* FileAttributes */
+		FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+		FILE_OPEN,	/* CreateDisposition */
+		0,		/* CreateOptions */
+		0);		/* SecurityFlags */
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq, winexe_out_pipe_opened, req);
+	return req;
+}
+
+static void winexe_out_pipe_opened(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct winexe_out_pipe_state *state = tevent_req_data(
+		req, struct winexe_out_pipe_state);
+	int timeout;
+	NTSTATUS status;
+
+	status = cli_ntcreate_recv(subreq, &state->out_pipe, NULL);
+	TALLOC_FREE(subreq);
+	if (tevent_req_nterror(req, status)) {
+		return;
+	}
+
+	timeout = state->cli->timeout;
+	state->cli->timeout = 0;
+
+	subreq = cli_read_send(
+		state,
+		state->ev,
+		state->cli,
+		state->out_pipe,
+		state->out_inbuf,
+		0,
+		sizeof(state->out_inbuf));
+
+	state->cli->timeout = timeout;
+
+	if (tevent_req_nomem(subreq, req)) {
+		return;
+	}
+	tevent_req_set_callback(subreq, winexe_out_pipe_got_data, req);
+}
+
+static void winexe_out_pipe_got_data(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct winexe_out_pipe_state *state = tevent_req_data(
+		req, struct winexe_out_pipe_state);
+	NTSTATUS status;
+	int timeout;
+	size_t received;
+	ssize_t written;
+
+	status = cli_read_recv(subreq, &received);
+	TALLOC_FREE(subreq);
+
+	DBG_DEBUG("cli_read for %d gave %s\n",
+		  state->out_fd,
+		  nt_errstr(status));
+
+	if (NT_STATUS_EQUAL(status, NT_STATUS_PIPE_DISCONNECTED)) {
+		subreq = cli_close_send(
+			state,
+			state->ev,
+			state->cli,
+			state->out_pipe);
+		if (tevent_req_nomem(subreq, req)) {
+			return;
+		}
+		tevent_req_set_callback(subreq, winexe_out_pipe_closed, req);
+		return;
+	}
+
+	if (tevent_req_nterror(req, status)) {
+		return;
+	}
+
+	if (received > 0) {
+		written = sys_write(state->out_fd, state->out_inbuf, received);
+		if (written == -1) {
+			tevent_req_nterror(req, map_nt_error_from_unix(errno));
+			return;
+		}
+	}
+
+	timeout = state->cli->timeout;
+	state->cli->timeout = 0;
+
+	subreq = cli_read_send(
+		state,
+		state->ev,
+		state->cli,
+		state->out_pipe,
+		state->out_inbuf,
+		0,
+		sizeof(state->out_inbuf));
+
+	state->cli->timeout = timeout;
+
+	if (tevent_req_nomem(subreq, req)) {
+		return;
+	}
+	tevent_req_set_callback(subreq, winexe_out_pipe_got_data, req);
+}
+
+static void winexe_out_pipe_closed(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	NTSTATUS status;
+
+	status = cli_close_recv(subreq);
+	TALLOC_FREE(subreq);
+	if (tevent_req_nterror(req, status)) {
+		return;
+	}
+	tevent_req_done(req);
+}
+
+static NTSTATUS winexe_out_pipe_recv(struct tevent_req *req)
+{
+	return tevent_req_simple_recv_ntstatus(req);
+}
+
+struct winexe_in_pipe_state {
+	struct tevent_context *ev;
+	struct cli_state *cli;
+	struct tevent_req *fd_read_req;
+	bool close_requested;
+	bool closing;
+	uint16_t in_pipe;
+	int in_fd;
+	char inbuf[256];
+};
+
+static void winexe_in_pipe_opened(struct tevent_req *subreq);
+static void winexe_in_pipe_got_data(struct tevent_req *subreq);
+static void winexe_in_pipe_written(struct tevent_req *subreq);
+static void winexe_in_pipe_closed(struct tevent_req *subreq);
+
+static struct tevent_req *winexe_in_pipe_send(
+	TALLOC_CTX *mem_ctx,
+	struct tevent_context *ev,
+	struct cli_state *cli,
+	const char *pipe_name,
+	int in_fd)
+{
+	struct tevent_req *req, *subreq;
+	struct winexe_in_pipe_state *state;
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct winexe_in_pipe_state);
+	if (req == NULL) {
+		return NULL;
+	}
+	state->ev = ev;
+	state->cli = cli;
+	state->in_fd = in_fd;
+
+	subreq = cli_ntcreate_send(
+		state,
+		state->ev,
+		state->cli,
+		pipe_name,
+		0,
+		SEC_RIGHTS_FILE_READ|SEC_RIGHTS_FILE_WRITE|
+		SEC_RIGHTS_FILE_EXECUTE,
+		0,		/* FileAttributes */
+		FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+		FILE_OPEN,	/* CreateDisposition */
+		0,		/* CreateOptions */
+		0);		/* SecurityFlags */
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq, winexe_in_pipe_opened, req);
+	return req;
+}
+
+static void winexe_in_pipe_opened(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct winexe_in_pipe_state *state = tevent_req_data(
+		req, struct winexe_in_pipe_state);
+	NTSTATUS status;
+
+	status = cli_ntcreate_recv(subreq, &state->in_pipe, NULL);
+	TALLOC_FREE(subreq);
+	if (tevent_req_nterror(req, status)) {
+		return;
+	}
+
+	subreq = wait_for_read_send(
+		state,
+		state->ev,
+		state->in_fd,
+		true);
+	if (tevent_req_nomem(subreq, req)) {
+		return;
+	}
+	tevent_req_set_callback(subreq, winexe_in_pipe_got_data, req);
+
+	state->fd_read_req = subreq;
+}
+
+static void winexe_in_pipe_got_data(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct winexe_in_pipe_state *state = tevent_req_data(
+		req, struct winexe_in_pipe_state);
+	int err;
+	bool ok;
+	int timeout;
+	ssize_t nread;
+
+	ok = wait_for_read_recv(subreq, &err);
+	TALLOC_FREE(subreq);
+	if (!ok) {
+		tevent_req_nterror(req, map_nt_error_from_unix(err));
+		return;
+	}
+	state->fd_read_req = NULL;
+
+	nread = sys_read(state->in_fd, &state->inbuf, sizeof(state->inbuf));
+	if (nread == -1) {
+		tevent_req_nterror(req, map_nt_error_from_unix(errno));
+		return;
+	}
+	if (nread == 0) {
+		tevent_req_nterror(req, NT_STATUS_CONNECTION_DISCONNECTED);
+		return;
+	}
+
+	timeout = state->cli->timeout;
+	state->cli->timeout = 0;
+
+	subreq = cli_writeall_send(
+		state,
+		state->ev,
+		state->cli,
+		state->in_pipe,
+		0,
+		(uint8_t *)state->inbuf,
+		0,
+		nread);
+
+	state->cli->timeout = timeout;
+
+	if (tevent_req_nomem(subreq, req)) {
+		return;
+	}
+	tevent_req_set_callback(subreq, winexe_in_pipe_written, req);
+}
+
+static void winexe_in_pipe_written(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct winexe_in_pipe_state *state = tevent_req_data(
+		req, struct winexe_in_pipe_state);
+	NTSTATUS status;
+
+	status = cli_writeall_recv(subreq, NULL);
+	TALLOC_FREE(subreq);
+
+	DBG_DEBUG("cli_writeall for %d gave %s\n",
+		  state->in_fd,
+		  nt_errstr(status));
+
+	if (NT_STATUS_EQUAL(status, NT_STATUS_PIPE_DISCONNECTED) ||
+	    state->close_requested) {
+		subreq = cli_close_send(
+			state,
+			state->ev,
+			state->cli,
+			state->in_pipe);
+		if (tevent_req_nomem(subreq, req)) {
+			return;
+		}
+		tevent_req_set_callback(subreq, winexe_in_pipe_closed, req);
+		state->closing = true;
+		return;
+	}
+
+	if (tevent_req_nterror(req, status)) {
+		return;
+	}
+
+	subreq = wait_for_read_send(
+		state,
+		state->ev,
+		state->in_fd,
+		true);
+	if (tevent_req_nomem(subreq, req)) {
+		return;
+	}
+	tevent_req_set_callback(subreq, winexe_in_pipe_got_data, req);
+
+	state->fd_read_req = subreq;
+}
+
+static void winexe_in_pipe_closed(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	NTSTATUS status;
+
+	status = cli_close_recv(subreq);
+	TALLOC_FREE(subreq);
+	if (tevent_req_nterror(req, status)) {
+		return;
+	}
+	return tevent_req_done(req);
+}
+
+static NTSTATUS winexe_in_pipe_recv(struct tevent_req *req)
+{
+	return tevent_req_simple_recv_ntstatus(req);
+}
+
+static bool winexe_in_pipe_close(struct tevent_req *req)
+{
+	struct winexe_in_pipe_state *state = tevent_req_data(
+		req, struct winexe_in_pipe_state);
+	struct tevent_req *subreq;
+
+	if (state->closing) {
+		return true;
+	}
+
+	if (state->fd_read_req == NULL) {
+		/*
+		 * cli_writeall active, wait for it to return
+		 */
+		state->close_requested = true;
+		return true;
+	}
+
+	TALLOC_FREE(state->fd_read_req);
+
+	subreq = cli_close_send(
+		state,
+		state->ev,
+		state->cli,
+		state->in_pipe);
+	if (subreq == NULL) {
+		return false;
+	}
+	tevent_req_set_callback(subreq, winexe_in_pipe_closed, req);
+	state->closing = true;
+
+	return true;
+}
+
+struct winexe_pipes_state {
+	struct tevent_req *pipes[3];
+};
+
+static void winexe_pipes_stdin_done(struct tevent_req *subreq);
+static void winexe_pipes_stdout_done(struct tevent_req *subreq);
+static void winexe_pipes_stderr_done(struct tevent_req *subreq);
+
+static struct tevent_req *winexe_pipes_send(
+	TALLOC_CTX *mem_ctx,
+	struct tevent_context *ev,
+	struct cli_state *cli,
+	const char *pipe_postfix)
+{
+	struct tevent_req *req;
+	struct winexe_pipes_state *state;
+	char *pipe_name;
+
+	req = tevent_req_create(mem_ctx, &state, struct winexe_pipes_state);
+	if (req == NULL) {
+		return NULL;
+	}
+
+	pipe_name = talloc_asprintf(state, "\\ahexec_stdin%s", pipe_postfix);
+	if (tevent_req_nomem(pipe_name, req)) {
+		return tevent_req_post(req, ev);
+	}
+	state->pipes[0] = winexe_in_pipe_send(
+		state,
+		ev,
+		cli,
+		pipe_name,
+		0);
+	if (tevent_req_nomem(state->pipes[0], req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(state->pipes[0], winexe_pipes_stdin_done, req);
+
+	pipe_name = talloc_asprintf(state, "\\ahexec_stdout%s", pipe_postfix);
+	if (tevent_req_nomem(pipe_name, req)) {
+		return tevent_req_post(req, ev);
+	}
+	state->pipes[1] = winexe_out_pipe_send(
+		state,
+		ev,
+		cli,
+		pipe_name,
+		1);
+	if (tevent_req_nomem(state->pipes[1], req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(state->pipes[1], winexe_pipes_stdout_done,
+				req);
+
+	pipe_name = talloc_asprintf(state, "\\ahexec_stderr%s", pipe_postfix);
+	if (tevent_req_nomem(pipe_name, req)) {
+		return tevent_req_post(req, ev);
+	}
+	state->pipes[2] = winexe_out_pipe_send(
+		state,
+		ev,
+		cli,
+		pipe_name,
+		2);
+	if (tevent_req_nomem(state->pipes[2], req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(state->pipes[2], winexe_pipes_stderr_done,
+				req);
+
+	DBG_DEBUG("pipes = %p %p %p\n",
+		  state->pipes[0],
+		  state->pipes[1],
+		  state->pipes[2]);
+
+	return req;
+}
+
+static void winexe_pipes_stdin_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct winexe_pipes_state *state = tevent_req_data(
+		req, struct winexe_pipes_state);
+	NTSTATUS status;
+
+	status = winexe_in_pipe_recv(subreq);
+	TALLOC_FREE(subreq);
+
+	DBG_DEBUG("stdin returned %s\n", nt_errstr(status));
+
+	if (tevent_req_nterror(req, status)) {
+		return;
+	}
+
+	state->pipes[0] = NULL;
+
+	DBG_DEBUG("pipes = %p %p %p\n",
+		  state->pipes[0],
+		  state->pipes[1],
+		  state->pipes[2]);
+
+	if ((state->pipes[1] == NULL) && (state->pipes[2] == NULL)) {
+		tevent_req_done(req);
+	}
+}
+
+static void winexe_pipes_stdout_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct winexe_pipes_state *state = tevent_req_data(
+		req, struct winexe_pipes_state);
+	NTSTATUS status;
+
+	status = winexe_out_pipe_recv(subreq);
+	TALLOC_FREE(subreq);
+
+	DBG_DEBUG("stdout returned %s\n", nt_errstr(status));
+
+	if (tevent_req_nterror(req, status)) {
+		return;
+	}
+
+	if (state->pipes[0] != NULL) {
+		winexe_in_pipe_close(state->pipes[0]);
+	}
+
+	state->pipes[1] = NULL;
+
+	DBG_DEBUG("pipes = %p %p %p\n",
+		  state->pipes[0],
+		  state->pipes[1],
+		  state->pipes[2]);
+
+	if ((state->pipes[0] == NULL) && (state->pipes[2] == NULL)) {
+		tevent_req_done(req);
+	}
+}
+
+static void winexe_pipes_stderr_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct winexe_pipes_state *state = tevent_req_data(
+		req, struct winexe_pipes_state);
+	NTSTATUS status;
+
+	status = winexe_out_pipe_recv(subreq);
+	TALLOC_FREE(subreq);
+
+	DBG_DEBUG("stderr returned %s\n", nt_errstr(status));
+
+	if (tevent_req_nterror(req, status)) {
+		return;
+	}
+
+	if (state->pipes[0] != NULL) {
+		winexe_in_pipe_close(state->pipes[0]);
+	}
+
+	state->pipes[2] = NULL;
+
+	DBG_DEBUG("pipes = %p %p %p\n",
+		  state->pipes[0],
+		  state->pipes[1],
+		  state->pipes[2]);
+
+	if ((state->pipes[0] == NULL) && (state->pipes[1] == NULL)) {
+		tevent_req_done(req);
+	}
+}
+
+static NTSTATUS winexe_pipes_recv(struct tevent_req *req)
+{
+	return tevent_req_simple_recv_ntstatus(req);
+}
+
+struct winexe_ctrl_state {
+	struct tevent_context *ev;
+	struct cli_state *cli;
+
+	uint16_t ctrl_pipe;
+	bool ctrl_pipe_done;
+
+	char ctrl_inbuf[256];
+	char *cmd;
+	int return_code;
+
+	struct tevent_req *pipes_req;
+};
+
+static void winexe_ctrl_opened(struct tevent_req *subreq);
+static void winexe_ctrl_got_read(struct tevent_req *subreq);
+static void winexe_ctrl_wrote_version(struct tevent_req *subreq);
+static void winexe_ctrl_wrote_cmd(struct tevent_req *subreq);
+static void winexe_ctrl_pipes_done(struct tevent_req *subreq);
+static void winexe_ctrl_pipe_closed(struct tevent_req *subreq);
+
+static struct tevent_req *winexe_ctrl_send(
+	TALLOC_CTX *mem_ctx,
+	struct tevent_context *ev,
+	struct cli_state *cli,
+	const char *cmd)
+{
+	struct tevent_req *req, *subreq;
+	struct winexe_ctrl_state *state;
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct winexe_ctrl_state);
+	if (req == NULL) {
+		return NULL;
+	}
+	state->ev = ev;
+	state->cli = cli;
+
+	state->cmd = talloc_asprintf(state, "run %s\n", cmd);
+	if (tevent_req_nomem(state->cmd, req)) {
+		return tevent_req_post(req, ev);
+	}
+
+	subreq = cli_ntcreate_send(
+		state,
+		state->ev,
+		state->cli,
+		"\\" PIPE_NAME,
+		0,
+		SEC_RIGHTS_FILE_READ|SEC_RIGHTS_FILE_WRITE|
+		SEC_RIGHTS_FILE_EXECUTE,
+		0,		/* FileAttributes */
+		FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+		FILE_OPEN,	/* CreateDisposition */
+		0,		/* CreateOptions */
+		0);		/* SecurityFlags */
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq, winexe_ctrl_opened, req);
+	return req;
+}
+
+static void winexe_ctrl_opened(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct winexe_ctrl_state *state = tevent_req_data(
+		req, struct winexe_ctrl_state);
+	int timeout;
+	NTSTATUS status;
+	static const char cmd[] = "get codepage\nget version\n";
+
+	status = cli_ntcreate_recv(subreq, &state->ctrl_pipe, NULL);
+	TALLOC_FREE(subreq);
+	if (tevent_req_nterror(req, status)) {
+		return;
+	}
+
+	timeout = state->cli->timeout;
+	state->cli->timeout = 0;
+
+	subreq = cli_read_send(
+		state,
+		state->ev,
+		state->cli,
+		state->ctrl_pipe,
+		state->ctrl_inbuf,
+		0,
+		sizeof(state->ctrl_inbuf)-1);
+
+	state->cli->timeout = timeout;
+
+	if (tevent_req_nomem(subreq, req)) {
+		return;
+	}
+	tevent_req_set_callback(subreq, winexe_ctrl_got_read, req);
+
+	subreq = cli_writeall_send(
+		state,
+		state->ev,
+		state->cli,
+		state->ctrl_pipe,
+		0,
+		(const uint8_t *)cmd,
+		0,
+		strlen(cmd));
+	if (tevent_req_nomem(subreq, req)) {
+		return;
+	}
+	tevent_req_set_callback(subreq, winexe_ctrl_wrote_version, req);
+}
+
+static void winexe_ctrl_got_read(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct winexe_ctrl_state *state = tevent_req_data(
+		req, struct winexe_ctrl_state);
+	NTSTATUS status;
+	int timeout;
+	size_t received;
+	unsigned int version, return_code;
+	int ret;
+
+	status = cli_read_recv(subreq, &received);
+	TALLOC_FREE(subreq);
+
+	if (NT_STATUS_EQUAL(status, NT_STATUS_PIPE_DISCONNECTED)) {
+		subreq = cli_close_send(
+			state,
+			state->ev,
+			state->cli,
+			state->ctrl_pipe);
+		if (tevent_req_nomem(subreq, req)) {
+			return;
+		}
+		tevent_req_set_callback(subreq, winexe_ctrl_pipe_closed, req);
+		return;
+	}
+	if (tevent_req_nterror(req, status)) {
+		return;
+	}
+
+	DBG_DEBUG("Got %zu bytes\n", received);
+
+	timeout = state->cli->timeout;
+	state->cli->timeout = 0;
+
+	subreq = cli_read_send(
+		state,
+		state->ev,
+		state->cli,
+		state->ctrl_pipe,
+		state->ctrl_inbuf,
+		0,
+		sizeof(state->ctrl_inbuf)-1);
+
+	state->cli->timeout = timeout;
+
+	if (tevent_req_nomem(subreq, req)) {
+		return;
+	}
+	tevent_req_set_callback(subreq, winexe_ctrl_got_read, req);
+
+	ret = sscanf(state->ctrl_inbuf, "version 0x%x\n", &version);
+	if (ret == 1) {
+		DBG_DEBUG("Got version %x\n", version);
+
+		subreq = cli_writeall_send(
+			state,
+			state->ev,
+			state->cli,
+			state->ctrl_pipe,
+			0,
+			(const uint8_t *)state->cmd,
+			0,
+			strlen(state->cmd));
+		if (tevent_req_nomem(subreq, req)) {
+			return;
+		}
+		tevent_req_set_callback(subreq, winexe_ctrl_wrote_cmd, req);
+		return;
+	}
+
+	ret = strncmp(state->ctrl_inbuf, "std_io_err ", strlen("std_io_err "));
+	if (ret == 0) {
+		char *p = state->ctrl_inbuf + 11;
+		char *q = strchr(state->ctrl_inbuf, '\n');
+		char *postfix;
+		size_t postfix_len;
+
+		if (q == NULL) {
+			DBG_DEBUG("Got invalid pipe postfix\n");
+			return;
+		}
+
+		postfix_len = q - p;
+
+		postfix = talloc_strndup(state, p, postfix_len);
+		if (tevent_req_nomem(postfix, req)) {
+			return;
+		}
+
+		DBG_DEBUG("Got pipe postfix %s\n", postfix);
+
+		subreq = winexe_pipes_send(
+			state,
+			state->ev,
+			state->cli,
+			postfix);
+		if (tevent_req_nomem(subreq, req)) {
+			return;
+		}
+		tevent_req_set_callback(subreq, winexe_ctrl_pipes_done, req);
+
+		state->pipes_req = subreq;
+
+		return;
+	}
+
+	ret = strncmp(state->ctrl_inbuf, "error ", strlen("error "));
+	if (ret == 0) {
+		printf("Error: %s", state->ctrl_inbuf);
+		return;
+	}
+
+	ret = sscanf(state->ctrl_inbuf, "version 0x%x\n", &return_code);
+	if (ret == 1) {
+		state->return_code = return_code;
+		return;
+	}
+}
+
+static void winexe_ctrl_wrote_version(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	NTSTATUS status;
+
+	status = cli_writeall_recv(subreq, NULL);
+	TALLOC_FREE(subreq);
+	if (tevent_req_nterror(req, status)) {
+		return;
+	}
+}
+
+static void winexe_ctrl_wrote_cmd(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	NTSTATUS status;
+
+	status = cli_writeall_recv(subreq, NULL);
+	TALLOC_FREE(subreq);
+	if (tevent_req_nterror(req, status)) {
+		return;
+	}
+}
+
+static void winexe_ctrl_pipe_closed(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct winexe_ctrl_state *state = tevent_req_data(
+		req, struct winexe_ctrl_state);
+	NTSTATUS status;
+
+	status = cli_close_recv(subreq);
+	TALLOC_FREE(subreq);
+	if (tevent_req_nterror(req, status)) {
+		return;
+	}
+
+	state->ctrl_pipe_done = true;
+	if (state->pipes_req == NULL) {
+		tevent_req_done(req);
+	}
+}
+
+static void winexe_ctrl_pipes_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct winexe_ctrl_state *state = tevent_req_data(
+		req, struct winexe_ctrl_state);
+	NTSTATUS status;
+
+	status = winexe_pipes_recv(subreq);
+	TALLOC_FREE(subreq);
+	if (tevent_req_nterror(req, status)) {
+		return;
+	}
+
+	state->pipes_req = NULL;
+	if (state->ctrl_pipe_done) {
+		tevent_req_done(req);
+	}
+}
+
+static NTSTATUS winexe_ctrl_recv(struct tevent_req *req,
+				 int *preturn_code)
+{
+	struct winexe_ctrl_state *state = tevent_req_data(
+		req, struct winexe_ctrl_state);
+	NTSTATUS status;
+
+	if (tevent_req_is_nterror(req, &status)) {
+		return status;
+	}
+	if (preturn_code != NULL) {
+		*preturn_code = state->return_code;
+	}
+	return NT_STATUS_OK;
+}
+
+static NTSTATUS winexe_ctrl(struct cli_state *cli,
+			    const char *cmd,
+			    int *preturn_code)
+{
+	struct tevent_context *ev = NULL;
+	struct tevent_req *req = NULL;
+	NTSTATUS status = NT_STATUS_NO_MEMORY;
+	bool ok;
+
+	ev = samba_tevent_context_init(cli);
+	if (ev == NULL) {
+		goto done;
+	}
+	req = winexe_ctrl_send(ev, ev, cli, cmd);
+	if (req == NULL) {
+		goto done;
+	}
+	ok = tevent_req_poll_ntstatus(req, ev, &status);
+	if (!ok) {
+		goto done;
+	}
+	status = winexe_ctrl_recv(req, preturn_code);
+done:
+	TALLOC_FREE(req);
+	TALLOC_FREE(ev);
+	return status;
+}
+
+#ifdef HAVE_WINEXE_CC_WIN32
+const DATA_BLOB *winexesvc32_exe_binary(void);
+#endif
+
+#ifdef HAVE_WINEXE_CC_WIN64
+const DATA_BLOB *winexesvc64_exe_binary(void);
+#endif
+
+int main(int argc, const char *argv[])
+{
+	TALLOC_CTX *frame = talloc_stackframe();
+	struct program_options options = {0};
+	struct loadparm_context *lp_ctx;
+	struct cli_state *cli;
+	const char *service_name = SERVICE_NAME;
+	char *service_filename = NULL;
+#ifdef HAVE_WINEXE_CC_WIN32
+	const DATA_BLOB *winexesvc32_exe = winexesvc32_exe_binary();
+#else
+	const DATA_BLOB *winexesvc32_exe = NULL;
+#endif
+#ifdef HAVE_WINEXE_CC_WIN64
+	const DATA_BLOB *winexesvc64_exe = winexesvc64_exe_binary();
+#else
+	const DATA_BLOB *winexesvc64_exe = NULL;
+#endif
+	NTSTATUS status;
+	int ret = 1;
+	int return_code = 0;
+
+	lp_ctx = loadparm_init_s3(frame, loadparm_s3_helpers());
+	if (lp_ctx == NULL) {
+		fprintf(stderr, "loadparm_init_s3 failed\n");
+		goto done;
+	}
+
+	smb_init_locale();
+	setup_logging("winexe", DEBUG_STDOUT);
+
+	lp_load_global(get_dyn_CONFIGFILE());
+
+	parse_args(argc, argv, frame, &options, lp_ctx);
+
+	if (options.cmd == NULL) {
+		fprintf(stderr, "no cmd given\n");
+		goto done;
+	}
+
+	service_filename = talloc_asprintf(frame, "%s.exe", service_name);
+	if (service_filename == NULL) {
+		DBG_WARNING("talloc_asprintf failed\n");
+		goto done;
+	}
+
+	status = cli_full_connection_creds(
+		&cli,
+		NULL,
+		options.hostname,
+		NULL,
+		445,
+		"IPC$",
+		"?????",
+		options.credentials,
+		0,
+		0);
+
+	if (!NT_STATUS_IS_OK(status)) {
+		DBG_WARNING("cli_full_connection_creds failed: %s\n",
+			    nt_errstr(status));
+		goto done;
+	}
+
+	status = winexe_svc_install(
+		cli,
+		options.hostname,
+		service_name,
+		service_filename,
+		winexesvc32_exe,
+		winexesvc64_exe,
+		options.credentials,
+		options.flags);
+	if (!NT_STATUS_IS_OK(status)) {
+		DBG_WARNING("winexe_svc_install failed: %s\n",
+			    nt_errstr(status));
+		goto done;
+	}
+
+	status = winexe_ctrl(cli, options.cmd, &return_code);
+	if (NT_STATUS_EQUAL(status, NT_STATUS_PIPE_DISCONNECTED)) {
+		/* Normal finish */
+		status = NT_STATUS_OK;
+	}
+	if (!NT_STATUS_IS_OK(status)) {
+		DBG_WARNING("cli_ctrl failed: %s\n",
+			    nt_errstr(status));
+		goto done;
+	}
+
+	if (options.flags & SVC_UNINSTALL) {
+		status = winexe_svc_uninstall(
+			cli,
+			service_name);
+		if (!NT_STATUS_IS_OK(status)) {
+			DBG_WARNING("winexe_svc_uninstall failed: %s\n",
+				    nt_errstr(status));
+			goto done;
+		}
+	}
+
+	ret = return_code;
+done:
+	TALLOC_FREE(frame);
+	return ret;
+}
diff --git a/examples/winexe/winexesvc.c b/examples/winexe/winexesvc.c
new file mode 100644
index 00000000000..02aa9df4b3d
--- /dev/null
+++ b/examples/winexe/winexesvc.c
@@ -0,0 +1,745 @@
+/*
+ * Copyright (C) Andrzej Hajda 2009-2013
+ * Contact: andrzej.hajda at wp.pl
+ *
+ * Source of this file: https://git.code.sf.net/p/winexe/winexe-waf
+ * commit b787d2a2c4b1abc3653bad10aec943b8efcd7aab.
+ *
+ * ** NOTE! The following "GPLv3 only" license applies to the winexe
+ * ** service files.  This does NOT imply that all of Samba is released
+ * ** under the "GPLv3 only" license.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 3 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <windows.h>
+#include <aclapi.h>
+#include <userenv.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+#include "winexesvc.h"
+
+#define BUFSIZE 256
+
+#if 0
+#define dbg(arg...) \
+({\
+	FILE *f = fopen("C:\\" SERVICE_NAME ".log", "at");\
+	if (f) {\
+		fprintf(f, arg);\
+		fclose(f);\
+	}\
+})
+#else
+#define dbg(arg...)
+#endif
+
+static SECURITY_ATTRIBUTES sa;
+
+/* Creates SECURITY_ATTRIBUTES sa with full access for BUILTIN\Administrators */
+static int CreatePipesSA()
+{
+	DWORD dwRes;
+	PSID pAdminSID = NULL;
+	PACL pACL = NULL;
+	PSECURITY_DESCRIPTOR pSD = NULL;
+	EXPLICIT_ACCESS ea;
+	SID_IDENTIFIER_AUTHORITY SIDAuthNT = {SECURITY_NT_AUTHORITY};
+
+	/* Create a SID for the BUILTIN\Administrators group. */
+	if (
+		!AllocateAndInitializeSid(
+			&SIDAuthNT, 2,
+			SECURITY_BUILTIN_DOMAIN_RID,
+			DOMAIN_ALIAS_RID_ADMINS,
+			0, 0, 0, 0, 0, 0, &pAdminSID
+		)
+	) {
+		dbg("AllocateAndInitializeSid Error %lu\n", GetLastError());
+		return 0;
+	}
+	/* Initialize an EXPLICIT_ACCESS structure for an ACE.
+	   The ACE will allow the Administrators group full access to the key.
+	*/
+	ea.grfAccessPermissions = FILE_ALL_ACCESS;
+	ea.grfAccessMode = SET_ACCESS;
+	ea.grfInheritance = NO_INHERITANCE;
+	ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
+	ea.Trustee.TrusteeType = TRUSTEE_IS_GROUP;
+	ea.Trustee.ptstrName = (LPTSTR) pAdminSID;
+
+	/* Create a new ACL that contains the new ACEs */
+	dwRes = SetEntriesInAcl(1, &ea, NULL, &pACL);
+	if (ERROR_SUCCESS != dwRes) {
+		dbg("SetEntriesInAcl Error %lu\n", GetLastError());
+		return 0;
+	}
+	/* Initialize a security descriptor */
+	pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
+	if (NULL == pSD) {
+		dbg("LocalAlloc Error %lu\n", GetLastError());
+		return 0;
+	}
+
+	if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION))
+	{
+		dbg("InitializeSecurityDescriptor Error %lu\n", GetLastError());
+		return 0;
+	}
+	/* Add the ACL to the security descriptor */
+	if (
+		!SetSecurityDescriptorDacl(
+			pSD, TRUE,  /* bDaclPresent flag */
+			pACL, FALSE  /* not a default DACL */
+		)
+	) {
+		dbg("SetSecurityDescriptorDacl Error %lu\n", GetLastError());
+		return 0;
+	}
+	/* Initialize a security attributes structure */
+	sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+	sa.lpSecurityDescriptor = pSD;
+	sa.bInheritHandle = FALSE;
+	return 1;
+}
+
+typedef struct {
+	HANDLE h;
+	OVERLAPPED o;
+} OV_HANDLE;
+
+static int hgets(char *str, int n, OV_HANDLE *pipe)
+{
+	DWORD res;
+	DWORD count = 0;
+	--n;
+	while (--n >= 0) {
+		if (!ReadFile(pipe->h, str, 1, NULL, &pipe->o) && GetLastError() != ERROR_IO_PENDING)
+			goto finish;
+		if (!GetOverlappedResult(pipe->h, &pipe->o, &res, TRUE) || !res)
+			goto finish;
+		if (*str == '\n')
+			goto finish;
+		++count;
+		++str;
+	}
+finish:
+	*str = 0;
+	return count;
+}
+
+static int hprintf(OV_HANDLE *pipe, const char *fmt, ...)
+{
+	int res;
+	char buf[1024];
+	va_list ap;
+	va_start(ap, fmt);
+	vsnprintf(buf, sizeof(buf), fmt, ap);
+	va_end(ap);
+	if (!WriteFile(pipe->h, buf, strlen(buf), NULL, &pipe->o) && GetLastError() == ERROR_IO_PENDING)
+		GetOverlappedResult(pipe->h, &pipe->o, (LPDWORD)&res, TRUE);
+	FlushFileBuffers(pipe->h);
+	return res;
+}
+
+typedef struct {
+	OV_HANDLE *pipe;
+	const char *cmd;
+	HANDLE pin;
+	HANDLE pout;
+	HANDLE perr;
+	HANDLE token;
+	int implevel;
+	int system;
+	int profile;
+	char *runas;
+	int conn_number;
+} connection_context;
+
+typedef int CMD_FUNC(connection_context *);
+
+typedef struct {
+	const char *name;
+	CMD_FUNC *func;
+} CMD_ITEM;
+
+static int cmd_set(connection_context *c)
+{
+	static const char* var_system = "system";
+	static const char* var_implevel = "implevel";
+	static const char* var_runas = "runas";
+	static const char* var_profile = "profile";
+	char *cmdline;
+	int res = 0;
+
+	cmdline = strchr(c->cmd, ' ');
+	if (!cmdline) {
+		goto finish;
+	}
+	++cmdline;
+	int l;
+	if ((strstr(cmdline, var_system) == cmdline) && (cmdline[l = strlen(var_system)] == ' ')) {
+		c->system = atoi(cmdline + l + 1);
+	} else if ((strstr(cmdline, var_implevel) == cmdline) && (cmdline[l = strlen(var_implevel)] == ' ')) {
+		c->implevel = atoi(cmdline + l + 1);
+	} else if ((strstr(cmdline, var_profile) == cmdline) && (cmdline[l = strlen(var_profile)] == ' ')) {
+		c->profile = atoi(cmdline + l + 1);
+	} else if ((strstr(cmdline, var_runas) == cmdline) && (cmdline[l = strlen(var_runas)] == ' ')) {
+		c->runas = strdup(cmdline + l + 1);
+	} else {
+		hprintf(c->pipe, "error Unknown commad (%s)\n", c->cmd);
+		goto finish;
+	}
+	res = 1;
+finish:
+	return res;
+}
+
+static int cmd_get(connection_context *c)
+{
+	static const char* var_version = "version";
+	static const char* var_codepage = "codepage";
+	char *cmdline;
+	int res = 0;
+
+	cmdline = strchr(c->cmd, ' ');
+	if (!cmdline) {
+		goto finish;
+	}
+	++cmdline;
+	int l;
+	if ((strstr(cmdline, var_version) == cmdline)
+	    && (cmdline[l = strlen(var_version)] == 0)) {
+		hprintf(c->pipe, "version 0x%04X\n", VERSION);
+	} else if ((strstr(cmdline, var_codepage) == cmdline)
+	           && (cmdline[l = strlen(var_codepage)] == 0)) {
+		hprintf(c->pipe, "codepage %d\n", GetOEMCP());
+	} else {
+		hprintf(c->pipe, "error Unknown argument (%s)\n", c->cmd);
+		goto finish;
+	}
+	res = 1;
+finish:
+	return res;
+}
+
+typedef struct {
+	char *user;
+	char *domain;
+	char *password;
+} credentials;
+
+static int prepare_credentials(char *str, credentials *crd)
+{
+	char *p;
+	p = strchr(str, '/');
+	if (!p) p = strchr(str, '\\');
+	if (p) {
+		*p++ = 0;
+		crd->domain = str;
+	} else {
+		p = str;
+		crd->domain = ".";
+	}
+	crd->user = p;
+	p = strchr(p, '%');
+	if (p)
+		*p++ = 0;
+	crd->password = p;
+	return 1;
+}
+
+static int get_token(connection_context *c)
+{
+	int res = 0;
+	int wres;
+	HANDLE token;
+
+	if (c->runas) {
+		credentials crd;
+		if (!prepare_credentials(c->runas, &crd)) {
+			hprintf(c->pipe, "error Incorrect runas credentials\n");
+			goto finish;
+		}
+		wres = LogonUser(crd.user, crd.domain, crd.password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, &c->token);
+		if (!wres) {
+			hprintf(c->pipe, "error Cannot LogonUser(%s,%s,%s) %d\n",
+			        crd.user, crd.domain, crd.password, GetLastError());
+			goto finish;
+		}
+		res = 1;
+		goto finish;
+	} else if (c->system) {
+		if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &token)) {
+			hprintf(c->pipe, "error Cannot OpenProcessToken %d\n", GetLastError());
+			goto finish;
+		}
+	} else {
+		if (!ImpersonateNamedPipeClient(c->pipe->h)) {
+			hprintf(c->pipe, "error Cannot ImpersonateNamedPipeClient %d\n", GetLastError());
+			goto finish;
+		}
+		if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, FALSE, &token)) {
+			hprintf(c->pipe, "error Cannot OpenThreadToken %d\n", GetLastError());
+			goto finishRevertToSelf;
+		}
+	}
+	if (!DuplicateTokenEx(token, MAXIMUM_ALLOWED, 0, c->implevel, TokenPrimary, &c->token)) {
+		hprintf(c->pipe, "error Cannot Duplicate Token %d\n", GetLastError());
+		goto finishCloseToken;
+	}
+	res = 1;
+finishCloseToken:
+	CloseHandle(token);
+finishRevertToSelf:
+	if (!c->system) {
+		if (!RevertToSelf()) {
+			hprintf(c->pipe, "error Cannot RevertToSelf %d\n", GetLastError());
+			res = 0;
+		}
+	}
+finish:
+	return res;
+}
+
+static int load_user_profile(connection_context *c)
+{
+	PROFILEINFO pi = { .dwSize = sizeof(PROFILEINFO) };
+	DWORD ulen = 256;
+	TCHAR username[ulen];
+
+	GetUserName(username, &ulen);
+	pi.lpUserName = username;
+
+	return LoadUserProfile(c->token, &pi);
+}
+
+static int cmd_run(connection_context *c)
+{
+	char buf[256];
+	int res = 0;
+	char *cmdline;
+	DWORD pipe_nr;
+
+	cmdline = strchr(c->cmd, ' ');
+	if (!cmdline) {
+		goto finish;
+	}
+	++cmdline;
+
+	if (!get_token(c))
+		return 0;
+
+	pipe_nr = (GetCurrentProcessId() << 16) + (DWORD) c->conn_number;
+
+	sprintf(buf, "\\\\.\\pipe\\" PIPE_NAME_IN, (unsigned int) pipe_nr);
+	c->pin = CreateNamedPipe(buf,
+	                         PIPE_ACCESS_DUPLEX,
+	                         PIPE_WAIT,
+	                         1,
+	                         BUFSIZE,
+	                         BUFSIZE,
+	                         NMPWAIT_USE_DEFAULT_WAIT,
+	                         &sa);
+	if (c->pin == INVALID_HANDLE_VALUE) {
+		hprintf(c->pipe, "error Cannot create in pipe(%s), error 0x%08X\n", buf, GetLastError());
+		goto finishCloseToken;
+	}
+
+	sprintf(buf, "\\\\.\\pipe\\" PIPE_NAME_OUT, (unsigned int) pipe_nr);
+	c->pout = CreateNamedPipe(buf,
+	                          PIPE_ACCESS_DUPLEX,
+	                          PIPE_WAIT,
+	                          1,
+	                          BUFSIZE,
+	                          BUFSIZE,
+	                          NMPWAIT_USE_DEFAULT_WAIT,
+	                          &sa);
+	if (c->pout == INVALID_HANDLE_VALUE) {
+		hprintf(c->pipe, "error Cannot create out pipe(%s), error 0x%08X\n", buf, GetLastError());
+		goto finishClosePin;
+	}
+
+	sprintf(buf, "\\\\.\\pipe\\" PIPE_NAME_ERR, (unsigned int) pipe_nr);
+	c->perr = CreateNamedPipe(buf,
+	                          PIPE_ACCESS_DUPLEX,
+	                          PIPE_WAIT,
+	                          1,
+	                          BUFSIZE,
+	                          BUFSIZE,
+	                          NMPWAIT_USE_DEFAULT_WAIT,
+	                          &sa);
+	if (c->perr == INVALID_HANDLE_VALUE) {
+		hprintf(c->pipe, "error Cannot create err pipe(%s), error 0x%08x\n", buf, GetLastError());
+		goto finishClosePout;
+	}
+
+	/* Send handle to client (it will use it to connect pipes) */
+	hprintf(c->pipe, CMD_STD_IO_ERR " %08X\n", pipe_nr);
+
+	HANDLE ph[] = { c->pin, c->pout, c->perr };
+	int i;
+
+	for (i = 0; i < 3; ++i) {
+		if (ConnectNamedPipe(ph[i], NULL))
+			continue;
+		int err = GetLastError();
+		if (err != ERROR_PIPE_CONNECTED) {
+			hprintf(c->pipe, "error ConnectNamedPipe(pin) %d\n", err);
+			while (--i >= 0)
+				DisconnectNamedPipe(ph[i]);
+			goto finishClosePerr;
+		}
+	}
+
+	SetHandleInformation(c->pin, HANDLE_FLAG_INHERIT, 1);
+	SetHandleInformation(c->pout, HANDLE_FLAG_INHERIT, 1);
+	SetHandleInformation(c->perr, HANDLE_FLAG_INHERIT, 1);
+
+	if (c->profile)
+		load_user_profile(c);
+
+	PROCESS_INFORMATION pi;
+	ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
+
+	STARTUPINFO si;
+	ZeroMemory(&si, sizeof(STARTUPINFO));
+	si.cb = sizeof(STARTUPINFO);
+	si.hStdInput = c->pin;
+	si.hStdOutput = c->pout;
+	si.hStdError = c->perr;
+	si.dwFlags |= STARTF_USESTDHANDLES;
+
+	if (CreateProcessAsUser(
+		c->token,
+		NULL,
+		cmdline,	/* command line */
+		NULL,	/* process security attributes */
+		NULL,	/* primary thread security attributes */
+		TRUE,	/* handles are inherited */
+		0,	/* creation flags */
+		NULL,	/* use parent's environment */
+		NULL,	/* use parent's current directory */
+		&si,	/* STARTUPINFO pointer */
+		&pi) 	/* receives PROCESS_INFORMATION */
+	) {
+		HANDLE hlist[2] = {c->pipe->o.hEvent, pi.hProcess};
+		DWORD ec;
+		char str[1];
+
+		if (!ResetEvent(c->pipe->o.hEvent))
+			dbg("ResetEvent error - %lu\n", GetLastError());
+		if (!ReadFile(c->pipe->h, str, 1, NULL, &c->pipe->o) && GetLastError() != ERROR_IO_PENDING)
+			dbg("ReadFile(control_pipe) error - %lu\n", GetLastError());
+		ec = WaitForMultipleObjects(2, hlist, FALSE, INFINITE);
+		dbg("WaitForMultipleObjects=%lu\n", ec - WAIT_OBJECT_0);
+		if (ec != WAIT_OBJECT_0)
+			GetExitCodeProcess(pi.hProcess, &ec);
+		else
+			TerminateProcess(pi.hProcess, ec = 0x1234);
+		FlushFileBuffers(c->pout);
+		FlushFileBuffers(c->perr);
+		CloseHandle(pi.hProcess);
+		CloseHandle(pi.hThread);
+		hprintf(c->pipe, CMD_RETURN_CODE " %08X\n", ec);
+	} else {
+		hprintf(c->pipe, "error Creating process(%s) %d\n", cmdline, GetLastError());
+	}
+
+	DisconnectNamedPipe(c->perr);
+	DisconnectNamedPipe(c->pout);
+	DisconnectNamedPipe(c->pin);
+finishClosePerr:
+	CloseHandle(c->perr);
+finishClosePout:
+	CloseHandle(c->pout);
+finishClosePin:
+	CloseHandle(c->pin);
+finishCloseToken:
+	CloseHandle(c->token);
+finish:
+	return res;
+}
+
+static CMD_ITEM cmd_table[] = {
+	{"run", cmd_run},
+	{"set", cmd_set},
+	{"get", cmd_get},
+	{NULL, NULL}
+};
+
+typedef struct {
+	OV_HANDLE *pipe;
+	int conn_number;
+} connection_data;
+
+#define MAX_COMMAND_LENGTH (32768)
+
+static VOID handle_connection(connection_data *data)
+{
+	char *cmd = 0;
+	int res;
+	connection_context _c, *c = &_c;
+	cmd = malloc(MAX_COMMAND_LENGTH);
+	if (!cmd) {
+		hprintf(data->pipe,
+		        "error: unable to allocate buffer for command\n");
+		return;
+	}
+	ZeroMemory(cmd, MAX_COMMAND_LENGTH);
+	ZeroMemory(c, sizeof(connection_context));
+	c->pipe = data->pipe;
+	c->cmd = cmd;
+	c->conn_number = data->conn_number;
+	free(data);
+	/* FIXME make wait for end of process or ctrl_pipe input */
+	while (1) {
+		res = hgets(cmd, MAX_COMMAND_LENGTH, c->pipe);
+		if (res <= 0) {
+			dbg("Error reading from pipe(%p)\n", c->pipe->h);
+			goto finish;
+		}
+		dbg("Retrieved line: \"%s\"\n", cmd);
+		CMD_ITEM *ci;
+		for (ci = cmd_table; ci->name; ++ci) {
+			if (strstr(cmd, ci->name) != cmd)
+				continue;
+			char c = cmd[strlen(ci->name)];
+			if (!c || (c == ' '))
+				break;
+		}
+		if (ci->name) {
+			if (!ci->func(c))
+				goto finish;
+		} else {
+			hprintf(c->pipe, "error Ignoring unknown command (%s)\n", cmd);
+		}
+	}
+finish:
+	FlushFileBuffers(c->pipe->h);
+	DisconnectNamedPipe(c->pipe->h);
+	CloseHandle(c->pipe->h);
+	CloseHandle(c->pipe->o.hEvent);
+	free(c->pipe);
+	free(cmd);
+}
+
+static int conn_number = 0;
+
+DWORD WINAPI winexesvc_loop(LPVOID lpParameter)
+{
+	BOOL res;
+
+	dbg("server_loop: alive\n");
+	if (!CreatePipesSA()) {
+		dbg("CreatePipesSA failed (%08lX)\n", GetLastError());
+		return -1;
+	}
+	dbg("server_loop: CreatePipesSA done\n");
+	for (;;) {
+		dbg("server_loop: Create Pipe\n");
+		OV_HANDLE *pipe;
+		pipe = (OV_HANDLE *)malloc(sizeof(OV_HANDLE));
+		ZeroMemory(&pipe->o, sizeof(OVERLAPPED));
+		pipe->o.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
+		pipe->h = CreateNamedPipe("\\\\.\\pipe\\" PIPE_NAME,
+		                          PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
+		                          PIPE_WAIT,
+		                          PIPE_UNLIMITED_INSTANCES,
+		                          BUFSIZE,
+		                          BUFSIZE,
+		                          NMPWAIT_USE_DEFAULT_WAIT,
+		                          &sa);
+		if (pipe->h == INVALID_HANDLE_VALUE) {
+			dbg("CreatePipe failed(%08lX)\n",
+			            GetLastError());
+			CloseHandle(pipe->o.hEvent);
+			free(pipe);
+			return 0;
+		}
+
+		dbg("server_loop: Connect Pipe\n");
+		if (ConnectNamedPipe(pipe->h, &pipe->o)) {
+			dbg("server_loop: Connect Pipe err %08lX\n", GetLastError());
+			res = FALSE;
+		} else {
+			switch (GetLastError()) {
+			  case ERROR_IO_PENDING:
+				dbg("server_loop: Connect Pipe(0) pending\n");
+				DWORD t;
+				res = GetOverlappedResult(pipe->h, &pipe->o, &t, TRUE);
+				break;
+			  case ERROR_PIPE_CONNECTED:
+				dbg("server_loop: Connect Pipe(0) connected\n");
+				res = TRUE;
+				break;
+			  default:
+				dbg("server_loop: Connect Pipe(0) err %08lX\n", GetLastError());
+				res = FALSE;
+			}
+		}
+
+		if (res) {
+			connection_data *cd = malloc(sizeof(connection_data));
+			cd->pipe = pipe;
+			cd->conn_number = ++conn_number;
+			dbg("server_loop: CreateThread\n");
+			HANDLE th = CreateThread(NULL,	/* no security attribute */
+			                         0,	/* default stack size */
+			                         (LPTHREAD_START_ROUTINE)
+			                         handle_connection,
+			                         (LPVOID) cd,	/* thread parameter */
+			                         0,	/* not suspended */
+			                         NULL);	/* returns thread ID */
+			if (!th) {
+				dbg("Cannot create thread\n");
+				CloseHandle(pipe->h);
+				CloseHandle(pipe->o.hEvent);
+				free(pipe);
+			} else {
+				CloseHandle(th);
+				dbg("server_loop: Thread created\n");
+			}
+		} else {
+			dbg("server_loop: Pipe not connected\n");
+			CloseHandle(pipe->h);
+			CloseHandle(pipe->o.hEvent);
+			free(pipe);
+		}
+	}
+	dbg("server_loop: STH wrong\n");
+	return 0;
+}
+
+static SERVICE_STATUS winexesvcStatus;
+static SERVICE_STATUS_HANDLE winexesvcStatusHandle;
+
+static VOID WINAPI winexesvcCtrlHandler(DWORD Opcode)
+{
+	switch (Opcode) {
+	  case SERVICE_CONTROL_PAUSE:
+		dbg(SERVICE_NAME ": winexesvcCtrlHandler: pause\n", 0);
+		winexesvcStatus.dwCurrentState = SERVICE_PAUSED;
+		break;
+
+	  case SERVICE_CONTROL_CONTINUE:
+		dbg(SERVICE_NAME ": winexesvcCtrlHandler: continue\n", 0);
+		winexesvcStatus.dwCurrentState = SERVICE_RUNNING;
+		break;
+
+	  case SERVICE_CONTROL_STOP:
+		dbg(SERVICE_NAME ": winexesvcCtrlHandler: stop\n", 0);
+		winexesvcStatus.dwWin32ExitCode = 0;
+		winexesvcStatus.dwCurrentState = SERVICE_STOPPED;
+		winexesvcStatus.dwCheckPoint = 0;
+		winexesvcStatus.dwWaitHint = 0;
+
+		if (!SetServiceStatus (winexesvcStatusHandle, &winexesvcStatus))
+			dbg(SERVICE_NAME ": SetServiceStatus error %ld\n", GetLastError());
+
+		dbg(SERVICE_NAME ": Leaving winexesvc\n", 0);
+		return;
+
+	  case SERVICE_CONTROL_INTERROGATE:
+		dbg(SERVICE_NAME ": winexesvcCtrlHandler: interrogate\n", 0);
+		break;
+
+	  default:
+		dbg(SERVICE_NAME ": Unrecognized opcode %ld\n", Opcode);
+	}
+
+	if (!SetServiceStatus(winexesvcStatusHandle, &winexesvcStatus))
+		dbg(SERVICE_NAME ": SetServiceStatus error 0x%08X\n", GetLastError());
+
+	return;
+}
+
+static DWORD winexesvcInitialization(DWORD argc, LPTSTR * argv, DWORD * specificError)
+{
+	HANDLE th = CreateThread(NULL, 0, winexesvc_loop, NULL, 0, NULL);
+	if (th) {
+		CloseHandle(th);
+		return NO_ERROR;
+	}
+	return !NO_ERROR;
+}
+
+static void WINAPI winexesvcStart(DWORD argc, LPTSTR * argv)
+{
+	DWORD status;
+	DWORD specificError;
+
+	winexesvcStatus.dwServiceType = SERVICE_WIN32;
+	winexesvcStatus.dwCurrentState = SERVICE_START_PENDING;
+	winexesvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;
+	winexesvcStatus.dwWin32ExitCode = 0;
+	winexesvcStatus.dwServiceSpecificExitCode = 0;
+	winexesvcStatus.dwCheckPoint = 0;
+	winexesvcStatus.dwWaitHint = 0;
+
+	dbg(SERVICE_NAME ": RegisterServiceCtrlHandler\n", 0);
+
+	winexesvcStatusHandle = RegisterServiceCtrlHandler(SERVICE_NAME, winexesvcCtrlHandler);
+
+	if (winexesvcStatusHandle == (SERVICE_STATUS_HANDLE) 0) {
+		dbg(SERVICE_NAME
+		            ": RegisterServiceCtrlHandler failed %d\n",
+		            GetLastError());
+		return;
+	}
+	status = winexesvcInitialization(argc, argv, &specificError);
+
+	if (status != NO_ERROR) {
+		winexesvcStatus.dwCurrentState = SERVICE_STOPPED;
+		winexesvcStatus.dwCheckPoint = 0;
+		winexesvcStatus.dwWaitHint = 0;
+		winexesvcStatus.dwWin32ExitCode = status;
+		winexesvcStatus.dwServiceSpecificExitCode = specificError;
+
+		SetServiceStatus(winexesvcStatusHandle, &winexesvcStatus);
+		return;
+	}
+
+	winexesvcStatus.dwCurrentState = SERVICE_RUNNING;
+	winexesvcStatus.dwCheckPoint = 0;
+	winexesvcStatus.dwWaitHint = 0;
+
+	if (!SetServiceStatus(winexesvcStatusHandle, &winexesvcStatus)) {
+		status = GetLastError();
+		dbg(SERVICE_NAME ": SetServiceStatus error %ld\n", status);
+	}
+
+	dbg(SERVICE_NAME ": Returning the Main Thread \n", 0);
+
+	return;
+}
+
+int main(int argc, char *argv[])
+{
+	SERVICE_TABLE_ENTRY DispatchTable[] = {
+		{SERVICE_NAME, winexesvcStart},
+		{NULL, NULL}
+	};
+
+	dbg(SERVICE_NAME ": StartServiceCtrlDispatcher %d\n", GetLastError());
+	if (!StartServiceCtrlDispatcher(DispatchTable)) {
+		dbg(SERVICE_NAME
+		": StartServiceCtrlDispatcher (%d)\n",
+		GetLastError());
+	}
+	return 0;
+}
diff --git a/examples/winexe/winexesvc.h b/examples/winexe/winexesvc.h
new file mode 100644
index 00000000000..92b83750b70
--- /dev/null
+++ b/examples/winexe/winexesvc.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) Andrzej Hajda 2009-2013
+ * Contact: andrzej.hajda at wp.pl
+ *
+ * Source of this file: https://git.code.sf.net/p/winexe/winexe-waf
+ * commit b787d2a2c4b1abc3653bad10aec943b8efcd7aab.
+ *
+ * ** NOTE! The following "GPLv3 only" license applies to the winexe
+ * ** service files.  This does NOT imply that all of Samba is released
+ * ** under the "GPLv3 only" license.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 3 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Shared by winexe and winexesvc
+ */
+
+#define VERSION_MAJOR 1
+#define VERSION_MINOR 1
+
+#define VERSION ((VERSION_MAJOR * 100) + VERSION_MINOR)
+
+#define SERVICE_NAME "winexesvc"
+
+#define PIPE_NAME "ahexec"
+#define PIPE_NAME_IN "ahexec_stdin%08X"
+#define PIPE_NAME_OUT "ahexec_stdout%08X"
+#define PIPE_NAME_ERR "ahexec_stderr%08X"
+
+#define CMD_STD_IO_ERR "std_io_err"
+#define CMD_RETURN_CODE "return_code"
diff --git a/examples/winexe/wscript b/examples/winexe/wscript
new file mode 100644
index 00000000000..3380b9794f6
--- /dev/null
+++ b/examples/winexe/wscript
@@ -0,0 +1,29 @@
+#!/usr/bin/env python
+
+def configure(conf):
+    AR32 = ['i386', 'i586', 'i686']
+    AR64 = ['x86_64', 'amd64']
+    TC = ['mingw32', 'mingw32msvc', 'w64-mingw32']
+
+    found = False
+
+    for a in AR32:
+        for t in TC:
+	    if conf.find_program(a + '-' + t + '-gcc', var='WINEXE_CC_WIN32'):
+	        found = True
+		break
+	if found:
+            conf.DEFINE('HAVE_WINEXE_CC_WIN32', 1);
+	    break
+
+    for a in AR64:
+        for t in TC:
+	    if conf.find_program(a + '-' + t + '-gcc', var='WINEXE_CC_WIN64'):
+	        found = True
+		break
+	if found:
+            conf.DEFINE('HAVE_WINEXE_CC_WIN64', 1);
+	    break
+
+    conf.DEFINE("WINEXE_LDFLAGS",
+                "-s -Wall -Wl,-Bstatic -Wl,-Bdynamic -luserenv")
diff --git a/examples/winexe/wscript_build b/examples/winexe/wscript_build
new file mode 100644
index 00000000000..ecad3772461
--- /dev/null
+++ b/examples/winexe/wscript_build
@@ -0,0 +1,87 @@
+#!/usr/bin/env python
+
+import samba_utils
+
+def generate_winexesvc_c_from_exe(t):
+    src = t.inputs[0].bldpath(t.env)
+    tgt = t.outputs[0].bldpath(t.env)
+    fn = t.env.SAMBA_GENERATOR_VARS['WINEXE_FN']
+    src_blob = samba_utils.load_file(src)
+
+    def c_array(src):
+        N = 0
+        result = ''
+        while src:
+            l = src[:8]
+            src = src[8:]
+            h = ' '.join(["0x%02X," % ord(x) for x in l])
+            result += "\t\t%s\n" % (h)
+        return result
+
+    src_array = c_array(src_blob)
+
+    contents = '''
+#include "replace.h"
+#include "lib/util/data_blob.h"
+
+const DATA_BLOB *%s(void);
+const DATA_BLOB *%s(void)
+{
+\tstatic const uint8_t array[] = {
+%s
+\t};
+\tstatic const DATA_BLOB blob = {
+\t\t.data = discard_const_p(uint8_t, array),
+\t\t.length = ARRAY_SIZE(array),
+\t};
+\treturn &blob;
+}
+''' % (fn, fn, src_array)
+
+    ret = samba_utils.save_file(tgt, contents)
+    assert(ret == True)
+
+winexesvc_binaries = ''
+
+if bld.env.WINEXE_CC_WIN32:
+    bld.SAMBA_GENERATOR(
+        'winexesvc32_exe',
+        source='winexesvc.c',
+        target='winexesvc32.exe',
+        rule='${WINEXE_CC_WIN32} ${SRC} -o ${TGT} ${WINEXE_LDFLAGS}')
+    vars = {"WINEXE_FN": "winexesvc32_exe_binary"}
+    bld.SAMBA_GENERATOR(
+        'winexesvc32_exe_binary',
+        source='winexesvc32.exe',
+        target='winexesvc32_exe_binary.c',
+        group='build_source',
+        vars=vars,
+        rule=generate_winexesvc_c_from_exe)
+    winexesvc_binaries += ' winexesvc32_exe_binary.c'
+
+if bld.env.WINEXE_CC_WIN64:
+    bld.SAMBA_GENERATOR(
+        'winexesvc64_exe',
+        source='winexesvc.c',
+        target='winexesvc64.exe',
+        rule='${WINEXE_CC_WIN64} ${SRC} -o ${TGT} ${WINEXE_LDFLAGS}')
+    vars = {"WINEXE_FN": "winexesvc64_exe_binary"}
+    bld.SAMBA_GENERATOR(
+        'winexesvc64_exe_binary',
+        source='winexesvc64.exe',
+        target='winexesvc64_exe_binary.c',
+        group='build_source',
+        vars=vars,
+        rule=generate_winexesvc_c_from_exe)
+    winexesvc_binaries += ' winexesvc64_exe_binary.c'
+
+if winexesvc_binaries != '':
+    bld.SAMBA3_BINARY('winexe',
+                      source='winexe.c ' + winexesvc_binaries,
+                      deps='''
+                          popt
+                          samba-credentials
+                          LOADPARM_CTX
+                          libsmb
+                          msrpc3
+                      ''')
diff --git a/source3/wscript_build b/source3/wscript_build
index 6e34bfaecf0..f463a8c4261 100644
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -1345,6 +1345,7 @@ bld.RECURSE('../examples/libsmbclient')
 bld.RECURSE('../examples/pdb')
 bld.RECURSE('../examples/VFS')
 bld.RECURSE('../examples/fuse')
+bld.RECURSE('../examples/winexe')
 bld.RECURSE('lib/netapi/tests')
 bld.RECURSE('lib/netapi/examples')
 bld.RECURSE('smbd/notifyd')
diff --git a/wscript b/wscript
index f98d731e537..619b4923813 100644
--- a/wscript
+++ b/wscript
@@ -128,6 +128,7 @@ def configure(conf):
     conf.RECURSE('lib/replace')
 
     conf.RECURSE('examples/fuse')
+    conf.RECURSE('examples/winexe')
 
     conf.SAMBA_CHECK_PERL(mandatory=True)
     conf.find_program('xsltproc', var='XSLTPROC')
-- 
2.11.0



More information about the samba-technical mailing list