diff options
author | ng0 <ng0@n0.is> | 2019-04-13 16:43:08 +0000 |
---|---|---|
committer | ng0 <ng0@n0.is> | 2019-04-13 16:43:08 +0000 |
commit | 83cf7d019c8421f50637e6ac1bfc60682a101c01 (patch) | |
tree | 88cb22e93e97aadd893037afe413a26cdadead62 /src/test_pty.c | |
download | msh-83cf7d019c8421f50637e6ac1bfc60682a101c01.tar.gz msh-83cf7d019c8421f50637e6ac1bfc60682a101c01.zip |
No history is preserved as our svn server is long gone.
Diffstat (limited to 'src/test_pty.c')
-rw-r--r-- | src/test_pty.c | 666 |
1 files changed, 666 insertions, 0 deletions
diff --git a/src/test_pty.c b/src/test_pty.c new file mode 100644 index 0000000..b1efae1 --- /dev/null +++ b/src/test_pty.c | |||
@@ -0,0 +1,666 @@ | |||
1 | #include "common.h" | ||
2 | #include <gnunet/gnunet_util_lib.h> | ||
3 | #include <termios.h> | ||
4 | #include "mtypes.h" | ||
5 | |||
6 | /****************************************************************************/ | ||
7 | /* Implemented after http://rachid.koucha.free.fr/tech_corner/pty_pdip.html */ | ||
8 | /****************************************************************************/ | ||
9 | |||
10 | #define LOG(kind,...) \ | ||
11 | GNUNET_log (kind, __VA_ARGS__) | ||
12 | |||
13 | #define LOG_DEBUG(...) LOG(GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__) | ||
14 | |||
15 | #define LOG_ERROR(...) LOG(GNUNET_ERROR_TYPE_ERROR, __VA_ARGS__) | ||
16 | |||
17 | #define TIMEOUT(secs) \ | ||
18 | GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, secs) | ||
19 | |||
20 | #define DEFAULT_TIMEOUT TIMEOUT(30) | ||
21 | |||
22 | #define BUF_SIZE 512 | ||
23 | |||
24 | /** | ||
25 | * handler for SIGCHLD.l Register it just before forking | ||
26 | */ | ||
27 | static struct GNUNET_SIGNAL_Context *shc_chld; | ||
28 | |||
29 | /** | ||
30 | * Task to kill the child | ||
31 | */ | ||
32 | static GNUNET_SCHEDULER_TaskIdentifier child_death_task; | ||
33 | |||
34 | /** | ||
35 | * Task to read output from child | ||
36 | */ | ||
37 | static GNUNET_SCHEDULER_TaskIdentifier read_child_task; | ||
38 | |||
39 | /** | ||
40 | * Pipe used to communicate shutdown via signal. | ||
41 | */ | ||
42 | static struct GNUNET_DISK_PipeHandle *sigpipe; | ||
43 | |||
44 | static struct GNUNET_DISK_FileHandle *chld_io; | ||
45 | |||
46 | static struct GNUNET_CONNECTION_Handle *conn; | ||
47 | |||
48 | static struct GNUNET_CONNECTION_TransmitHandle *tx; | ||
49 | |||
50 | static char **chld_argv; | ||
51 | |||
52 | static char *cp; | ||
53 | static struct termios tio; | ||
54 | static struct winsize ws; | ||
55 | |||
56 | static int in_receive; | ||
57 | |||
58 | static char buf[BUF_SIZE]; | ||
59 | static size_t buf_off; | ||
60 | |||
61 | /** | ||
62 | * The process id of the child | ||
63 | */ | ||
64 | static pid_t child_pid; | ||
65 | |||
66 | /** | ||
67 | * Function to copy NULL terminated list of arguments | ||
68 | * | ||
69 | * @param argv the NULL terminated list of arguments. Cannot be NULL. | ||
70 | * @return the copied NULL terminated arguments | ||
71 | */ | ||
72 | static char ** | ||
73 | copy_argv (char *const *argv) | ||
74 | { | ||
75 | char **argv_dup; | ||
76 | unsigned int argp; | ||
77 | |||
78 | GNUNET_assert (NULL != argv); | ||
79 | for (argp = 0; NULL != argv[argp]; argp++) ; | ||
80 | argv_dup = GNUNET_malloc (sizeof (char *) * (argp + 1)); | ||
81 | for (argp = 0; NULL != argv[argp]; argp++) | ||
82 | argv_dup[argp] = strdup (argv[argp]); | ||
83 | return argv_dup; | ||
84 | } | ||
85 | |||
86 | |||
87 | /** | ||
88 | * Frees the given NULL terminated arguments | ||
89 | * | ||
90 | * @param argv the NULL terminated list of arguments | ||
91 | */ | ||
92 | static void | ||
93 | free_argv (char **argv) | ||
94 | { | ||
95 | unsigned int argp; | ||
96 | |||
97 | for (argp = 0; NULL != argv[argp]; argp++) | ||
98 | GNUNET_free (argv[argp]); | ||
99 | GNUNET_free (argv); | ||
100 | } | ||
101 | |||
102 | |||
103 | /** | ||
104 | * shutdown task | ||
105 | */ | ||
106 | static void | ||
107 | do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
108 | { | ||
109 | if (NULL != chld_argv) | ||
110 | { | ||
111 | free_argv (chld_argv); | ||
112 | chld_argv = NULL; | ||
113 | } | ||
114 | if (NULL != conn) | ||
115 | { | ||
116 | if (in_receive) | ||
117 | GNUNET_CONNECTION_receive_cancel (conn); | ||
118 | if (NULL != tx) | ||
119 | GNUNET_CONNECTION_notify_transmit_ready_cancel (tx); | ||
120 | GNUNET_CONNECTION_destroy (conn); | ||
121 | conn = NULL; | ||
122 | } | ||
123 | if (0 != child_pid) | ||
124 | { | ||
125 | GNUNET_break (0 == kill (child_pid, SIGTERM)); | ||
126 | LOG_DEBUG ("Child still running. Delaying shutdown\n"); | ||
127 | (void) GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, | ||
128 | &do_shutdown, NULL); | ||
129 | return; | ||
130 | } | ||
131 | if (GNUNET_SCHEDULER_NO_TASK != child_death_task) | ||
132 | { | ||
133 | GNUNET_SCHEDULER_cancel (child_death_task); | ||
134 | child_death_task = GNUNET_SCHEDULER_NO_TASK; | ||
135 | } | ||
136 | if (NULL != chld_io) | ||
137 | GNUNET_DISK_file_close (chld_io); | ||
138 | } | ||
139 | |||
140 | |||
141 | /** | ||
142 | * Read the child processes output and send it to over network connection to waiter | ||
143 | */ | ||
144 | static void | ||
145 | read_child (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); | ||
146 | |||
147 | |||
148 | /** | ||
149 | * Function called to notify a client about the connection | ||
150 | * begin ready to queue more data. "buf" will be | ||
151 | * NULL and "size" zero if the connection was closed for | ||
152 | * writing in the meantime. | ||
153 | * | ||
154 | * @param cls closure | ||
155 | * @param size number of bytes available in buf | ||
156 | * @param netbuf where the callee should write the message | ||
157 | * @return number of bytes written to buf | ||
158 | */ | ||
159 | static size_t | ||
160 | do_tx (void *cls, size_t size, void *netbuf) | ||
161 | { | ||
162 | tx = NULL; | ||
163 | if ((NULL == buf) || (0 == size)) | ||
164 | { | ||
165 | LOG_ERROR ("Failure in connectivity\n"); | ||
166 | GNUNET_SCHEDULER_shutdown (); | ||
167 | return 0; | ||
168 | } | ||
169 | GNUNET_assert (buf_off <= size); | ||
170 | (void) memcpy (netbuf, buf, buf_off); | ||
171 | size = buf_off; | ||
172 | buf_off = 0; | ||
173 | if (GNUNET_SCHEDULER_NO_TASK == read_child_task) | ||
174 | read_child_task = | ||
175 | GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, chld_io, | ||
176 | &read_child, NULL); | ||
177 | return size; | ||
178 | } | ||
179 | |||
180 | |||
181 | /** | ||
182 | * Read the child processes output and send it to over network connection to waiter | ||
183 | */ | ||
184 | static void | ||
185 | read_child (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
186 | { | ||
187 | ssize_t rsize; | ||
188 | |||
189 | read_child_task = GNUNET_SCHEDULER_NO_TASK; | ||
190 | if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) | ||
191 | return; | ||
192 | if (BUF_SIZE == buf_off) | ||
193 | { | ||
194 | GNUNET_assert (NULL != tx); | ||
195 | return; | ||
196 | } | ||
197 | rsize = GNUNET_DISK_file_read (chld_io, buf + buf_off, BUF_SIZE - buf_off); | ||
198 | if (rsize <= 0) | ||
199 | { | ||
200 | LOG_DEBUG ("Child stdout closed\n"); | ||
201 | return; | ||
202 | } | ||
203 | buf_off += rsize; | ||
204 | if (NULL != tx) | ||
205 | GNUNET_CONNECTION_notify_transmit_ready_cancel (tx); | ||
206 | tx = GNUNET_CONNECTION_notify_transmit_ready (conn, buf_off, DEFAULT_TIMEOUT, | ||
207 | &do_tx, NULL); | ||
208 | if (BUF_SIZE == buf_off) | ||
209 | return; | ||
210 | read_child_task = | ||
211 | GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, chld_io, | ||
212 | &read_child, NULL); | ||
213 | } | ||
214 | |||
215 | |||
216 | /** | ||
217 | * Task triggered whenever we receive a SIGCHLD (child | ||
218 | * process died). | ||
219 | * | ||
220 | * @param cls closure, NULL if we need to self-restart | ||
221 | * @param tc context | ||
222 | */ | ||
223 | static void | ||
224 | child_died (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
225 | { | ||
226 | const struct GNUNET_DISK_FileHandle *pr; | ||
227 | char c[16]; | ||
228 | |||
229 | pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ); | ||
230 | child_death_task = GNUNET_SCHEDULER_NO_TASK; | ||
231 | if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY)) | ||
232 | { | ||
233 | child_death_task = | ||
234 | GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, | ||
235 | pr, &child_died, NULL); | ||
236 | return; | ||
237 | } | ||
238 | /* consume the signal */ | ||
239 | GNUNET_break (0 < GNUNET_DISK_file_read (pr, &c, sizeof (c))); | ||
240 | LOG_DEBUG ("Child died\n"); | ||
241 | child_pid = 0; | ||
242 | GNUNET_SCHEDULER_shutdown (); | ||
243 | /* FIXME: read any of the left over output from child? */ | ||
244 | } | ||
245 | |||
246 | |||
247 | /** | ||
248 | * Signal handler called for SIGCHLD. | ||
249 | */ | ||
250 | static void | ||
251 | sighandler_child_death () | ||
252 | { | ||
253 | static char c; | ||
254 | int old_errno = errno; /* back-up errno */ | ||
255 | |||
256 | GNUNET_break (1 == | ||
257 | GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle | ||
258 | (sigpipe, GNUNET_DISK_PIPE_END_WRITE), | ||
259 | &c, sizeof (c))); | ||
260 | errno = old_errno; /* restore errno */ | ||
261 | } | ||
262 | |||
263 | |||
264 | /** | ||
265 | * Configure the terminal for the child | ||
266 | */ | ||
267 | static int | ||
268 | parse_term_mode (const struct MSH_MSG_PtyMode *msg) | ||
269 | { | ||
270 | uint16_t *params; | ||
271 | unsigned int cnt; | ||
272 | unsigned int ns; | ||
273 | uint16_t msize; | ||
274 | size_t expected; | ||
275 | speed_t ospeed; | ||
276 | speed_t ispeed; | ||
277 | |||
278 | msize = ntohs (msg->header.size); | ||
279 | ns = ntohs (msg->nsettings); | ||
280 | expected = (sizeof (struct MSH_MSG_PtyMode) + (sizeof (uint16_t) * ns * 2)); | ||
281 | if (msize < expected) | ||
282 | { | ||
283 | GNUNET_break (0); | ||
284 | return GNUNET_SYSERR; | ||
285 | } | ||
286 | #define PARSE_WIN_SIZE(field) \ | ||
287 | ws.field = ntohs (msg->field); | ||
288 | PARSE_WIN_SIZE (ws_row); | ||
289 | PARSE_WIN_SIZE (ws_col); | ||
290 | PARSE_WIN_SIZE (ws_xpixel); | ||
291 | PARSE_WIN_SIZE (ws_ypixel); | ||
292 | #undef PARSE_WIN_SIZE | ||
293 | ospeed = baud_to_speed (ntohl (msg->ospeed)); | ||
294 | ispeed = baud_to_speed (ntohl (msg->ispeed)); | ||
295 | if (0 != cfsetospeed (&tio, ospeed)) | ||
296 | { | ||
297 | GNUNET_break (0); | ||
298 | return GNUNET_SYSERR; | ||
299 | } | ||
300 | if (0 != cfsetispeed (&tio, ispeed)) | ||
301 | { | ||
302 | GNUNET_break (0); | ||
303 | return GNUNET_SYSERR; | ||
304 | } | ||
305 | params = (uint16_t *) &msg[1]; | ||
306 | for (cnt = 0; cnt < ns; cnt++) | ||
307 | { | ||
308 | switch (ntohs (params[2 * cnt])) | ||
309 | { | ||
310 | #define TTYCHAR(NAME,OP) \ | ||
311 | case OP: \ | ||
312 | tio.c_cc[NAME] = ntohs (params[(2 * cnt)+1]); \ | ||
313 | break; | ||
314 | #define TTYMODE(NAME, FIELD, OP) \ | ||
315 | case OP: \ | ||
316 | if (1 == ntohs (params[(2 * cnt)+1])) \ | ||
317 | tio.FIELD |= NAME; \ | ||
318 | break; | ||
319 | #include "ttymodes.h" | ||
320 | #undef TTYCHAR | ||
321 | #undef TTYMODE | ||
322 | |||
323 | default: | ||
324 | GNUNET_assert (0); | ||
325 | } | ||
326 | } | ||
327 | if (0 == (msize - expected)) | ||
328 | return GNUNET_OK; | ||
329 | cp = ((void *) msg) + expected; | ||
330 | if ('\0' != cp[(msize - expected) - 1]) | ||
331 | { | ||
332 | GNUNET_break (0); | ||
333 | cp = NULL; | ||
334 | return GNUNET_OK; | ||
335 | } | ||
336 | cp = GNUNET_strdup (cp); | ||
337 | return GNUNET_OK; | ||
338 | } | ||
339 | |||
340 | |||
341 | /** | ||
342 | * Create pty, fork, setup the tty modes and exec the given binary | ||
343 | */ | ||
344 | static int | ||
345 | spawn_child (const struct MSH_MSG_PtyMode *msg) | ||
346 | { | ||
347 | const char *fn; | ||
348 | int master; | ||
349 | int ret; | ||
350 | |||
351 | LOG_DEBUG ("Creating PTY\n"); | ||
352 | master = posix_openpt (O_RDWR); | ||
353 | if (-1 == master) | ||
354 | { | ||
355 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "posix_openpt"); | ||
356 | goto err_ret; | ||
357 | } | ||
358 | if (-1 == grantpt (master)) | ||
359 | { | ||
360 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "grantpt"); | ||
361 | goto err_ret; | ||
362 | } | ||
363 | if (-1 == unlockpt (master)) | ||
364 | { | ||
365 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "unlockpt"); | ||
366 | goto err_ret; | ||
367 | } | ||
368 | if (NULL == (fn = ptsname (master))) | ||
369 | { | ||
370 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "ptsname"); | ||
371 | goto err_ret; | ||
372 | } | ||
373 | shc_chld = | ||
374 | GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD, &sighandler_child_death); | ||
375 | ret = fork(); | ||
376 | if (-1 == ret) | ||
377 | { | ||
378 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork"); | ||
379 | goto err_ret; | ||
380 | } | ||
381 | if (0 != ret) | ||
382 | { | ||
383 | LOG_DEBUG ("Forked child successfully\n"); | ||
384 | child_pid = ret; | ||
385 | chld_io = GNUNET_DISK_get_handle_from_int_fd (master); | ||
386 | read_child_task = | ||
387 | GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, chld_io, | ||
388 | &read_child, NULL); | ||
389 | return GNUNET_OK; | ||
390 | } | ||
391 | /* child process */ | ||
392 | { | ||
393 | int slave; | ||
394 | |||
395 | close (master); | ||
396 | LOG_DEBUG ("Opening slave PTY %s\n", fn); | ||
397 | slave = open (fn, O_RDWR); | ||
398 | if (-1 == slave) | ||
399 | { | ||
400 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "open"); | ||
401 | _exit (1); | ||
402 | } | ||
403 | if (-1 == tcgetattr (slave, &tio)) | ||
404 | { | ||
405 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "tcsetattr"); | ||
406 | _exit (1); | ||
407 | } | ||
408 | parse_term_mode (msg); | ||
409 | if (-1 == tcsetattr (slave, TCSANOW, &tio)) | ||
410 | { | ||
411 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "tcsetattr"); | ||
412 | //_exit (1); /* Ignore for now */ | ||
413 | } | ||
414 | if (-1 == ioctl (slave, TIOCSWINSZ, &ws)) | ||
415 | { | ||
416 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "ioctl"); | ||
417 | _exit (1); | ||
418 | } | ||
419 | if (-1 == setsid ()) | ||
420 | { | ||
421 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "setsid"); | ||
422 | _exit (1); | ||
423 | } | ||
424 | close (0); | ||
425 | close (1); | ||
426 | close (2); | ||
427 | if ( (-1 == dup2 (slave, 0)) || | ||
428 | (-1 == dup2 (slave, 1)) || | ||
429 | (-1 == dup2 (slave, 2)) ) | ||
430 | { | ||
431 | //GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "dup"); //we won't get it | ||
432 | //at this point | ||
433 | _exit (2); | ||
434 | } | ||
435 | close (slave); | ||
436 | LOG_DEBUG ("Execing %s\n", chld_argv[0]); | ||
437 | if (-1 == execvp (chld_argv[0], chld_argv)) | ||
438 | { | ||
439 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "execvp"); | ||
440 | _exit (1); | ||
441 | } | ||
442 | } | ||
443 | |||
444 | err_ret: | ||
445 | GNUNET_SCHEDULER_shutdown (); | ||
446 | return GNUNET_SYSERR; | ||
447 | } | ||
448 | |||
449 | |||
450 | /** | ||
451 | * Callback function for data received from the network. Note that | ||
452 | * both "available" and "err" would be 0 if the read simply timed out. | ||
453 | * | ||
454 | * @param cls closure | ||
455 | * @param buf pointer to received data | ||
456 | * @param available number of bytes availabe in "buf", | ||
457 | * possibly 0 (on errors) | ||
458 | * @param addr address of the sender | ||
459 | * @param addrlen size of addr | ||
460 | * @param errCode value of errno (on errors receiving) | ||
461 | */ | ||
462 | static void | ||
463 | net_receive (void *cls, const void *buf, size_t available, | ||
464 | const struct sockaddr * addr, socklen_t addrlen, int errCode) | ||
465 | { | ||
466 | static enum { | ||
467 | STATE_HEADER = 0, | ||
468 | STATE_MSG, | ||
469 | STATE_FORWARD | ||
470 | } state; | ||
471 | static size_t rb; | ||
472 | struct GNUNET_TIME_Relative timeout; | ||
473 | static struct MSH_MSG_PtyMode *msg; | ||
474 | size_t rsize; | ||
475 | |||
476 | in_receive = 0; | ||
477 | if (0 == available) | ||
478 | { | ||
479 | GNUNET_SCHEDULER_shutdown (); | ||
480 | return; | ||
481 | } | ||
482 | switch (state) | ||
483 | { | ||
484 | case STATE_HEADER: | ||
485 | { | ||
486 | static struct GNUNET_MessageHeader hdr; | ||
487 | uint16_t msize; | ||
488 | |||
489 | GNUNET_assert (available <= sizeof (hdr)); | ||
490 | memcpy (((void *) &hdr) + rb, buf, available); | ||
491 | rb += available; | ||
492 | timeout = DEFAULT_TIMEOUT; | ||
493 | rsize = sizeof (hdr) - rb; | ||
494 | if (0 != rsize) | ||
495 | goto read_again; | ||
496 | if (MSH_MTYPE_PTY_MODE != ntohs (hdr.type)) | ||
497 | { | ||
498 | GNUNET_break_op (0); | ||
499 | GNUNET_SCHEDULER_shutdown (); | ||
500 | return; | ||
501 | } | ||
502 | msize = ntohs (hdr.size); | ||
503 | msg = GNUNET_malloc (msize); | ||
504 | memcpy (msg, &hdr, sizeof (hdr)); | ||
505 | rsize = msize - sizeof (hdr); | ||
506 | state = STATE_MSG; | ||
507 | goto read_again; | ||
508 | } | ||
509 | case STATE_MSG: | ||
510 | { | ||
511 | uint16_t msize; | ||
512 | |||
513 | msize = ntohs (msg->header.size); | ||
514 | GNUNET_assert (available <= (msize - rb)); | ||
515 | memcpy (((void *) msg) + rb, buf, available); | ||
516 | rb += available; | ||
517 | timeout = DEFAULT_TIMEOUT; | ||
518 | rsize = msize - rb; | ||
519 | if (0 != rsize) | ||
520 | goto read_again; | ||
521 | spawn_child (msg); | ||
522 | GNUNET_free (msg); | ||
523 | msg = NULL; | ||
524 | state = STATE_FORWARD; | ||
525 | } | ||
526 | break; | ||
527 | case STATE_FORWARD: | ||
528 | /* receive from net and write to child pty */ | ||
529 | { | ||
530 | size_t wb; | ||
531 | ssize_t ret; | ||
532 | |||
533 | wb = 0; | ||
534 | do { | ||
535 | ret = GNUNET_DISK_file_write (chld_io, buf + wb, available - wb); | ||
536 | if (GNUNET_SYSERR == ret) | ||
537 | { | ||
538 | GNUNET_break (0); | ||
539 | GNUNET_SCHEDULER_shutdown (); | ||
540 | return; | ||
541 | } | ||
542 | wb += ret; | ||
543 | } while (wb < available); | ||
544 | } | ||
545 | break; | ||
546 | } | ||
547 | rsize = BUF_SIZE; | ||
548 | timeout = GNUNET_TIME_UNIT_FOREVER_REL; | ||
549 | |||
550 | read_again: | ||
551 | GNUNET_CONNECTION_receive (conn, rsize, timeout, &net_receive, NULL); | ||
552 | in_receive = 1; | ||
553 | } | ||
554 | |||
555 | |||
556 | /** | ||
557 | * Main function that will be run by the scheduler. | ||
558 | * | ||
559 | * @param cls closure | ||
560 | * @param args remaining command-line arguments | ||
561 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
562 | * @param c configuration | ||
563 | */ | ||
564 | static void | ||
565 | run (void *cls, | ||
566 | char *const *args, | ||
567 | const char *cfgfile, | ||
568 | const struct GNUNET_CONFIGURATION_Handle *c) | ||
569 | { | ||
570 | unsigned int argc; | ||
571 | const char *hostname; | ||
572 | const char *portstr; | ||
573 | struct sockaddr *addr; | ||
574 | struct addrinfo *res; | ||
575 | struct addrinfo hints; | ||
576 | int af_family; | ||
577 | socklen_t addrlen; | ||
578 | int ret; | ||
579 | |||
580 | argc = 0; | ||
581 | hostname = args[argc++]; | ||
582 | if (NULL == hostname) | ||
583 | { | ||
584 | GNUNET_break (0); | ||
585 | return; | ||
586 | } | ||
587 | portstr = args[argc++]; | ||
588 | if (NULL == portstr) | ||
589 | { | ||
590 | GNUNET_break (0); | ||
591 | return; | ||
592 | } | ||
593 | if (NULL == args[argc]) | ||
594 | { | ||
595 | GNUNET_break (0); | ||
596 | return; | ||
597 | } | ||
598 | (void) memset (&hints, 0, sizeof (hints)); | ||
599 | hints.ai_flags = AI_NUMERICSERV; | ||
600 | hints.ai_family = AF_INET; | ||
601 | hints.ai_socktype = SOCK_STREAM; | ||
602 | ret = getaddrinfo (hostname, portstr, &hints, &res); | ||
603 | if (0 != ret) | ||
604 | { | ||
605 | LOG_ERROR ("getaddrinfo() failed: %s\n", gai_strerror (ret)); | ||
606 | return; | ||
607 | } | ||
608 | if (NULL == res) | ||
609 | { | ||
610 | GNUNET_break (0); | ||
611 | return; | ||
612 | } | ||
613 | addrlen = res->ai_addrlen; | ||
614 | addr = GNUNET_malloc (addrlen); | ||
615 | memcpy (addr, res->ai_addr, addrlen); | ||
616 | af_family = res->ai_family; | ||
617 | freeaddrinfo (res); | ||
618 | conn = GNUNET_CONNECTION_create_from_sockaddr (af_family, addr, addrlen); | ||
619 | GNUNET_free (addr); | ||
620 | if (NULL == conn) | ||
621 | { | ||
622 | GNUNET_break (0); | ||
623 | return; | ||
624 | } | ||
625 | GNUNET_CONNECTION_receive (conn, sizeof (struct GNUNET_MessageHeader), | ||
626 | DEFAULT_TIMEOUT, &net_receive, NULL); | ||
627 | in_receive = 1; | ||
628 | child_death_task = | ||
629 | GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, | ||
630 | GNUNET_DISK_pipe_handle (sigpipe, | ||
631 | GNUNET_DISK_PIPE_END_READ), | ||
632 | &child_died, NULL); | ||
633 | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, | ||
634 | &do_shutdown, NULL); | ||
635 | chld_argv = copy_argv (&args[argc]); | ||
636 | return; | ||
637 | } | ||
638 | |||
639 | |||
640 | int main(int argc, char *const argv[]) | ||
641 | { | ||
642 | static const struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
643 | GNUNET_GETOPT_OPTION_END | ||
644 | }; | ||
645 | int ret; | ||
646 | |||
647 | if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) | ||
648 | return 2; | ||
649 | if (NULL == (sigpipe = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO, | ||
650 | GNUNET_NO, GNUNET_NO))) | ||
651 | { | ||
652 | GNUNET_break (0); | ||
653 | return 1; | ||
654 | } | ||
655 | ret = | ||
656 | GNUNET_PROGRAM_run (argc, argv, "test-pty", | ||
657 | gettext_noop | ||
658 | ("msh-waiter test program"), | ||
659 | options, &run, NULL); | ||
660 | if (NULL != shc_chld) | ||
661 | GNUNET_SIGNAL_handler_uninstall (shc_chld); | ||
662 | GNUNET_DISK_pipe_close (sigpipe); | ||
663 | GNUNET_free ((void *) argv); | ||
664 | LOG_DEBUG ("Exiting\n"); | ||
665 | return ret; | ||
666 | } | ||