blob: 067823159bc9104619b4aa45b7739673408e6253 [file] [log] [blame]
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
// Do what 2 lines of shell script in couchspawnkillable does...
// * Create a new suspended process with the same (duplicated) standard
// handles as us.
// * Write a line to stdout, consisting of the path to ourselves, plus
// '--kill {pid}' where {pid} is the PID of the newly created process.
// * Un-suspend the new process.
// * Wait for the process to terminate.
// * Terminate with the child's exit-code.
// Later, couch will call us with --kill and the PID, so we dutifully
// terminate the specified PID.
#include <stdlib.h>
#include "windows.h"
char *get_child_cmdline(int argc, char **argv)
{
// make a new command-line, but skipping me.
// XXX - todo - spaces etc in args???
int i;
char *p, *cmdline;
int nchars = 0;
int nthis = 1;
for (i=1;i<argc;i++)
nchars += strlen(argv[i])+1;
cmdline = p = malloc(nchars+1);
if (!cmdline)
return NULL;
for (i=1;i<argc;i++) {
nthis = strlen(argv[i]);
strncpy(p, argv[i], nthis);
p[nthis] = ' ';
p += nthis+1;
}
// Replace the last space we added above with a '\0'
cmdline[nchars-1] = '\0';
return cmdline;
}
// create the child process, returning 0, or the exit-code we will
// terminate with.
int create_child(int argc, char **argv, PROCESS_INFORMATION *pi)
{
char buf[1024];
DWORD dwcreate;
STARTUPINFO si;
char *cmdline;
if (argc < 2)
return 1;
cmdline = get_child_cmdline(argc, argv);
if (!cmdline)
return 2;
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
// depending on how *our* parent is started, we may or may not have
// a valid stderr stream - so although we try and duplicate it, only
// failing to duplicate stdin and stdout are considered fatal.
if (!DuplicateHandle(GetCurrentProcess(),
GetStdHandle(STD_INPUT_HANDLE),
GetCurrentProcess(),
&si.hStdInput,
0,
TRUE, // inheritable
DUPLICATE_SAME_ACCESS) ||
!DuplicateHandle(GetCurrentProcess(),
GetStdHandle(STD_OUTPUT_HANDLE),
GetCurrentProcess(),
&si.hStdOutput,
0,
TRUE, // inheritable
DUPLICATE_SAME_ACCESS)) {
return 3;
}
DuplicateHandle(GetCurrentProcess(),
GetStdHandle(STD_ERROR_HANDLE),
GetCurrentProcess(),
&si.hStdError,
0,
TRUE, // inheritable
DUPLICATE_SAME_ACCESS);
si.dwFlags = STARTF_USESTDHANDLES;
dwcreate = CREATE_SUSPENDED;
if (!CreateProcess( NULL, cmdline,
NULL,
NULL,
TRUE, // inherit handles
dwcreate,
NULL, // environ
NULL, // cwd
&si,
pi))
return 4;
return 0;
}
// and here we go...
int main(int argc, char **argv)
{
char out_buf[1024];
int rc;
DWORD cbwritten;
DWORD exitcode;
PROCESS_INFORMATION pi;
if (argc==3 && strcmp(argv[1], "--kill")==0) {
HANDLE h = OpenProcess(PROCESS_TERMINATE, 0, atoi(argv[2]));
if (!h)
return 1;
if (!TerminateProcess(h, 0))
return 2;
CloseHandle(h);
return 0;
}
// spawn the new suspended process
rc = create_child(argc, argv, &pi);
if (rc)
return rc;
// Write the 'terminate' command, which includes this PID, back to couch.
// *sob* - what about spaces etc?
sprintf_s(out_buf, sizeof(out_buf), "%s --kill %d\n",
argv[0], pi.dwProcessId);
WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), out_buf, strlen(out_buf),
&cbwritten, NULL);
// Let the child process go...
ResumeThread(pi.hThread);
// Wait for the process to terminate so we can reflect the exit code
// back to couch.
WaitForSingleObject(pi.hProcess, INFINITE);
if (!GetExitCodeProcess(pi.hProcess, &exitcode))
return 6;
return exitcode;
}