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