fix assertion failure reported in #5578
[oweals/gnunet.git] / src / util / gnunet-timeout-w32.c
1 /*
2      This file is part of GNUnet
3      Copyright (C) 2010 GNUnet e.V.
4
5      GNUnet is free software: you can redistribute it and/or modify it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License, or
8      (at your option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      Affero General Public License for more details.
14
15      You should have received a copy of the GNU Affero General Public License
16      along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18      SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21 /**
22  * @file src/util/gnunet-timeout-w32.c
23  * @brief small tool starting a child process, waiting that it terminates or killing it after a given timeout period
24  * @author LRN
25  */
26
27 #include <windows.h>
28 #include <sys/types.h>
29 #include <stdio.h>
30
31 int
32 main (int argc, char *argv[])
33 {
34   int i;
35   DWORD wait_result;
36   wchar_t *commandline;
37   wchar_t **wargv;
38   wchar_t *arg;
39   unsigned int cmdlen;
40   STARTUPINFOW start;
41   PROCESS_INFORMATION proc;
42
43   wchar_t wpath[MAX_PATH + 1];
44
45   wchar_t *pathbuf;
46   DWORD pathbuf_len, alloc_len;
47   wchar_t *ptr;
48   wchar_t *non_const_filename;
49   wchar_t *wcmd;
50   int wargc;
51   int timeout = 0;
52   ssize_t wrote;
53
54   HANDLE job;
55
56   if (argc < 3)
57     {
58       printf
59         ("arg 1: timeout in sec., arg 2: executable, arg<n> arguments\n");
60       exit (1);
61     }
62
63   timeout = atoi (argv[1]);
64
65   if (timeout == 0)
66     timeout = 600;
67
68   commandline =  GetCommandLineW ();
69   if (commandline == NULL)
70   {
71     printf ("Failed to get commandline: %lu\n", GetLastError ());
72     exit (2);
73   }
74
75   wargv = CommandLineToArgvW (commandline, &wargc);
76   if (wargv == NULL || wargc <= 1)
77   {
78     printf ("Failed to get parse commandline: %lu\n", GetLastError ());
79     exit (3);
80   }
81
82   job = CreateJobObject (NULL, NULL);
83   if (job == NULL)
84   {
85     printf ("Failed to create a job: %lu\n", GetLastError ());
86     exit (4);
87   }
88
89   pathbuf_len = GetEnvironmentVariableW (L"PATH", (wchar_t *) &pathbuf, 0);
90
91   alloc_len = pathbuf_len + 1;
92
93   pathbuf = malloc (alloc_len * sizeof (wchar_t));
94
95   ptr = pathbuf;
96
97   alloc_len = GetEnvironmentVariableW (L"PATH", ptr, pathbuf_len);
98
99   cmdlen = wcslen (wargv[2]);
100   if (cmdlen < 5 || wcscmp (&wargv[2][cmdlen - 4], L".exe") != 0)
101   {
102     non_const_filename = malloc (sizeof (wchar_t) * (cmdlen + 5));
103     swprintf (non_const_filename, cmdlen + 5, L"%S.exe", wargv[2]);
104   }
105   else
106   {
107     non_const_filename = wcsdup (wargv[2]);
108   }
109
110   /* Check that this is the full path. If it isn't, search. */
111   if (non_const_filename[1] == L':')
112     swprintf (wpath, sizeof (wpath) / sizeof (wchar_t), L"%S", non_const_filename);
113   else if (!SearchPathW
114            (pathbuf, non_const_filename, NULL, sizeof (wpath) / sizeof (wchar_t),
115             wpath, NULL))
116   {
117     printf ("Failed to get find executable: %lu\n", GetLastError ());
118     exit (5);
119   }
120   free (pathbuf);
121   free (non_const_filename);
122
123   cmdlen = wcslen (wpath) + 4;
124   i = 3;
125   while (NULL != (arg = wargv[i++]))
126     cmdlen += wcslen (arg) + 4;
127
128   wcmd = malloc (sizeof (wchar_t) * (cmdlen + 1));
129   wrote = 0;
130   i = 2;
131   while (NULL != (arg = wargv[i++]))
132   {
133     /* This is to escape trailing slash */
134     wchar_t arg_lastchar = arg[wcslen (arg) - 1];
135     if (wrote == 0)
136     {
137       wrote += swprintf (&wcmd[wrote], cmdlen + 1 - wrote, L"\"%S%S\" ", wpath,
138           arg_lastchar == L'\\' ? L"\\" : L"");
139     }
140     else
141     {
142       if (wcschr (arg, L' ') != NULL)
143         wrote += swprintf (&wcmd[wrote], cmdlen + 1 - wrote, L"\"%S%S\"%S", arg,
144             arg_lastchar == L'\\' ? L"\\" : L"", i == wargc ? L"" : L" ");
145       else
146         wrote += swprintf (&wcmd[wrote], cmdlen + 1 - wrote, L"%S%S%S", arg,
147             arg_lastchar == L'\\' ? L"\\" : L"", i == wargc ? L"" : L" ");
148     }
149   }
150
151   LocalFree (wargv);
152
153   memset (&start, 0, sizeof (start));
154   start.cb = sizeof (start);
155
156   if (!CreateProcessW (wpath, wcmd, NULL, NULL, TRUE, CREATE_SUSPENDED,
157        NULL, NULL, &start, &proc))
158   {
159     wprintf (L"Failed to get spawn process `%S' with arguments `%S': %lu\n", wpath, wcmd, GetLastError ());
160     exit (6);
161   }
162
163   AssignProcessToJobObject (job, proc.hProcess);
164
165   ResumeThread (proc.hThread);
166   CloseHandle (proc.hThread);
167
168   free (wcmd);
169
170   wait_result = WaitForSingleObject (proc.hProcess, timeout * 1000);
171   if (wait_result == WAIT_OBJECT_0)
172   {
173     DWORD status;
174     wait_result = GetExitCodeProcess (proc.hProcess, &status);
175     CloseHandle (proc.hProcess);
176     if (wait_result != 0)
177     {
178       printf ("Test process exited with result %lu\n", status);
179       TerminateJobObject (job, status);
180       exit (status);
181     }
182     printf ("Test process exited (failed to obtain exit status)\n");
183     TerminateJobObject (job, 0);
184     exit (0);
185   }
186   printf ("Child processes were killed after timeout of %u seconds\n",
187           timeout);
188   TerminateJobObject (job, 1);
189   CloseHandle (proc.hProcess);
190   exit (1);
191 }
192
193 /* end of timeout_watchdog_w32.c */