uncrustify as demanded.
[oweals/gnunet.git] / src / util / gnunet-helper-w32-console.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2014 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,
8      or (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-helper-w32-console.c
23  * @brief Does blocking reads from the console, writes the results
24  *        into stdout, turning blocking console I/O into non-blocking
25  *        pipe I/O. For W32 only.
26  * @author LRN
27  */
28 #include "platform.h"
29 #include "gnunet_crypto_lib.h"
30 #include "gnunet_common.h"
31 #include "gnunet-helper-w32-console.h"
32
33 static unsigned long buffer_size;
34
35 static int chars;
36
37 static HANDLE parent_handle;
38
39 /**
40  * Write @a size bytes from @a buf into @a output.
41  *
42  * @param output the descriptor to write into
43  * @param buf buffer with data to write
44  * @param size number of bytes to write
45  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
46  */
47 static int
48 write_all(int output,
49           const void *buf,
50           size_t size)
51 {
52   const char *cbuf = buf;
53   size_t total;
54   ssize_t wr;
55
56   total = 0;
57   do
58     {
59       wr = write(output,
60                  &cbuf[total],
61                  size - total);
62       if (wr > 0)
63         total += wr;
64     }
65   while ((wr > 0) && (total < size));
66   if (wr <= 0)
67     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
68                "Failed to write to stdout: %s\n",
69                strerror(errno));
70   return (total == size) ? GNUNET_OK : GNUNET_SYSERR;
71 }
72
73
74 /**
75  * Write message to the master process.
76  *
77  * @param output the descriptor to write into
78  * @param message_type message type to use
79  * @param data data to append, NULL for none
80  * @param data_length number of bytes in @a data
81  * @return #GNUNET_SYSERR to stop scanning (the pipe was broken somehow)
82  */
83 static int
84 write_message(int output,
85               uint16_t message_type,
86               const char *data,
87               size_t data_length)
88 {
89   struct GNUNET_MessageHeader hdr;
90
91 #if 0
92   fprintf(stderr,
93           "Helper sends %u-byte message of type %u\n",
94           (unsigned int)(sizeof(struct GNUNET_MessageHeader) + data_length),
95           (unsigned int)message_type);
96 #endif
97   hdr.type = htons(message_type);
98   hdr.size = htons(sizeof(struct GNUNET_MessageHeader) + data_length);
99   if (GNUNET_OK != write_all(output, &hdr, sizeof(hdr)))
100     return GNUNET_SYSERR;
101   if (GNUNET_OK != write_all(output, data, data_length))
102     return GNUNET_SYSERR;
103   return GNUNET_OK;
104 }
105
106
107 /**
108  * Main function of the helper process. Reads input events from console,
109  * writes messages, into stdout.
110  *
111  * @param console a handle to a console to read from
112  * @param output_stream a stream to write messages to
113  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
114  */
115 static int
116 read_events(HANDLE console, int output_stream)
117 {
118   DWORD rr;
119   BOOL b;
120   INPUT_RECORD *buf;
121   DWORD i;
122   int result;
123
124   result = GNUNET_SYSERR;
125   buf = malloc(sizeof(INPUT_RECORD) * buffer_size);
126   if (NULL == buf)
127     return result;
128   b = TRUE;
129   rr = 1;
130   while (TRUE == b && 0 < rr)
131     {
132       rr = 0;
133       b = ReadConsoleInput(console, buf, buffer_size, &rr);
134       if (FALSE == b && ERROR_SUCCESS != GetLastError())
135         break;
136       for (i = 0; i < rr; i++)
137         {
138           int r;
139           r = write_message(output_stream,
140                             GNUNET_MESSAGE_TYPE_W32_CONSOLE_HELPER_INPUT,
141                             (const char *)&buf[i],
142                             sizeof(INPUT_RECORD));
143           if (GNUNET_OK != r)
144             break;
145         }
146       if (rr + 1 != i)
147         break;
148     }
149   return result;
150 }
151
152
153 /**
154  * Main function of the helper process. Reads chars from console,
155  * writes messages, into stdout.
156  *
157  * @param console a handle to a console to read from
158  * @param output_stream a stream to write messages to
159  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
160  */
161 static int
162 read_chars(HANDLE console, int output_stream)
163 {
164   DWORD rr;
165   BOOL b;
166   wchar_t *buf;
167   char *small_ubuf;
168   char *large_ubuf;
169   char *ubuf;
170   int conv;
171   int r;
172   int result;
173
174   result = GNUNET_SYSERR;
175   buf = malloc(sizeof(wchar_t) * buffer_size);
176   if (NULL == buf)
177     return result;
178   small_ubuf = malloc(sizeof(char) * buffer_size * 2);
179   if (NULL == small_ubuf)
180     {
181       free(buf);
182       return result;
183     }
184   b = TRUE;
185   rr = 1;
186   while (TRUE == b)
187     {
188       large_ubuf = NULL;
189       rr = 0;
190       b = ReadConsoleW(console, buf, buffer_size, &rr, NULL);
191       if (FALSE == b && ERROR_SUCCESS != GetLastError())
192         break;
193       if (0 == rr)
194         continue;
195       /* Caveat: if the UTF-16-encoded string is longer than BUFFER_SIZE,
196        * there's a possibility that we will read up to a word that constitutes
197        * a part of a multi-byte UTF-16 codepoint. Converting that to UTF-8
198        * will either drop invalid word (flags == 0) or bail out because of it
199        * (flags == WC_ERR_INVALID_CHARS).
200        */
201       conv = WideCharToMultiByte(CP_UTF8, 0, buf, rr, small_ubuf, 0, NULL, FALSE);
202       if (0 == conv || 0xFFFD == conv)
203         continue;
204       if (conv <= buffer_size * 2 - 1)
205         {
206           memset(small_ubuf, 0, buffer_size * 2);
207           conv = WideCharToMultiByte(CP_UTF8, 0, buf, rr, small_ubuf, buffer_size * 2 - 1, NULL, FALSE);
208           if (0 == conv || 0xFFFD == conv)
209             continue;
210           ubuf = small_ubuf;
211         }
212       else
213         {
214           large_ubuf = malloc(conv + 1);
215           if (NULL == large_ubuf)
216             continue;
217           memset(large_ubuf, 0, conv + 1);
218           conv = WideCharToMultiByte(CP_UTF8, 0, buf, rr, large_ubuf, conv, NULL, FALSE);
219           if (0 == conv || 0xFFFD == conv)
220             {
221               free(large_ubuf);
222               large_ubuf = NULL;
223               continue;
224             }
225           ubuf = large_ubuf;
226         }
227       r = write_message(output_stream,
228                         GNUNET_MESSAGE_TYPE_W32_CONSOLE_HELPER_CHARS,
229                         ubuf,
230                         conv + 1);
231       if (large_ubuf)
232         free(large_ubuf);
233       if (GNUNET_OK != r)
234         break;
235     }
236   free(small_ubuf);
237   free(buf);
238   return result;
239 }
240
241
242 DWORD WINAPI
243 watch_parent(LPVOID param)
244 {
245   WaitForSingleObject(parent_handle, INFINITE);
246   ExitProcess(1);
247   return 0;
248 }
249
250 /**
251  * Main function of the helper process to extract meta data.
252  *
253  * @param argc should be 3
254  * @param argv [0] our binary name
255  *             [1] name of the file or directory to process
256  *             [2] "-" to disable extraction, NULL for defaults,
257  *                 otherwise custom plugins to load from LE
258  * @return 0 on success
259  */
260 int
261 main(int argc,
262      char *const *argv)
263 {
264   HANDLE os_stdin;
265   DWORD parent_pid;
266
267   /* We're using stdout to communicate binary data back to the parent; use
268    * binary mode.
269    */
270   _setmode(1, _O_BINARY);
271
272   if (argc != 4)
273     {
274       fprintf(stderr,
275               "Usage: gnunet-helper-w32-console <chars|events> <buffer size> <parent pid>\n");
276       return 2;
277     }
278
279   if (0 == strcmp(argv[1], "chars"))
280     chars = GNUNET_YES;
281   else if (0 == strcmp(argv[1], "events"))
282     chars = GNUNET_NO;
283   else
284     return 3;
285
286   buffer_size = strtoul(argv[2], NULL, 10);
287   if (buffer_size <= 0)
288     return 4;
289
290   parent_pid = (DWORD)strtoul(argv[3], NULL, 10);
291   if (parent_pid == 0)
292     return 5;
293   parent_handle = OpenProcess(SYNCHRONIZE, FALSE, parent_pid);
294   if (NULL == parent_handle)
295     return 6;
296
297   CreateThread(NULL, 0, watch_parent, NULL, 0, NULL);
298
299   if (0 == AttachConsole(ATTACH_PARENT_PROCESS))
300     {
301       if (ERROR_ACCESS_DENIED != GetLastError())
302         return 5;
303     }
304
305   /* Helper API overrides stdin, so we just attach to the console that we
306    * inherited. If we did.
307    */
308   os_stdin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE,
309                         FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
310   if (INVALID_HANDLE_VALUE == os_stdin)
311     return 1;
312
313   if (GNUNET_NO == chars)
314     return read_events(os_stdin, 1);
315   else
316     return read_chars(os_stdin, 1);
317 }
318
319 /* end of gnunet-helper-w32-console.c */