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