[PATCH] Add examples/winexe

Jeremy Allison jra at samba.org
Mon Aug 27 21:07:40 UTC 2018


On Mon, Aug 27, 2018 at 04:32:37PM +0200, Volker Lendecke via samba-technical wrote:
> 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?

Oh I *love* patches 1-3, really nice addition to the libsmb/cli*.c
code ! :-).

As for patch #4, I'm good with adding this into our tree, even
if GPLv3-only. After all, you've marked it as such so I think
that's fine.

RB+ and pushed.

Cheers,

	Jeremy.

> -- 
> 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

> 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