Fix test_os_startprocess
[oweals/gnunet.git] / src / util / test_os_start_process.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      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      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20 /**
21  * @file util/test_os_start_process.c
22  * @brief testcase for os start process code
23  *
24  * This testcase simply calls the os start process code
25  * giving a file descriptor to write stdout to.  If the
26  * correct data "HELLO" is read then all is well.
27  */
28 #include "platform.h"
29 #include "gnunet_common.h"
30 #include "gnunet_getopt_lib.h"
31 #include "gnunet_os_lib.h"
32 #include "gnunet_program_lib.h"
33 #include "gnunet_scheduler_lib.h"
34 #include "disk.h"
35
36
37 static const char *test_phrase = "HELLO WORLD";
38
39 static int ok;
40
41 static struct GNUNET_OS_Process *proc;
42
43 /**
44  * Pipe to write to started processes stdin (on write end) 
45  */
46 static struct GNUNET_DISK_PipeHandle *hello_pipe_stdin;
47
48 /**
49  * Pipe to read from started processes stdout (on read end) 
50  */
51 static struct GNUNET_DISK_PipeHandle *hello_pipe_stdout;
52
53 static GNUNET_SCHEDULER_TaskIdentifier die_task;
54
55 struct read_context
56 {
57   char buf[16];
58   int buf_offset;
59   const struct GNUNET_DISK_FileHandle *stdout_read_handle;
60 };
61
62 struct read_context rc;
63
64 static void
65 end_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
66 {
67   if (0 != GNUNET_OS_process_kill (proc, SIGTERM))
68   {
69     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
70   }
71   GNUNET_assert (GNUNET_OK == GNUNET_OS_process_wait (proc));
72   GNUNET_OS_process_destroy (proc);
73   proc = NULL;
74   GNUNET_DISK_pipe_close (hello_pipe_stdout);
75   GNUNET_DISK_pipe_close (hello_pipe_stdin);
76 }
77
78
79 static void
80 read_call (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
81 {
82   int bytes;
83
84   bytes = GNUNET_DISK_file_read (rc.stdout_read_handle, &rc.buf[rc.buf_offset], \
85       sizeof (rc.buf) - rc.buf_offset);
86
87   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "bytes is %d\n", bytes);
88
89   if (bytes < 1)
90   {
91     GNUNET_break (0);
92     ok = 1;
93     GNUNET_SCHEDULER_cancel (die_task);
94     GNUNET_SCHEDULER_add_now (&end_task, NULL);
95     return;
96   }
97
98   ok = strncmp (rc.buf, test_phrase, strlen (test_phrase));
99   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "read %s\n", &rc.buf[rc.buf_offset]);
100   rc.buf_offset += bytes;
101
102   if (0 == ok)
103   {
104     GNUNET_SCHEDULER_cancel (die_task);
105     GNUNET_SCHEDULER_add_now (&end_task, NULL);
106     return;
107   }
108
109   GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
110                                   rc.stdout_read_handle, &read_call,
111                                   NULL);
112
113 }
114
115
116 static void
117 run_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
118 {
119   char *fn;
120   const struct GNUNET_DISK_FileHandle *stdout_read_handle;
121   const struct GNUNET_DISK_FileHandle *wh;
122
123 #if !WINDOWS
124   GNUNET_asprintf (&fn, "cat");
125 #else
126   GNUNET_asprintf (&fn, "w32cat");
127 #endif
128
129   hello_pipe_stdin = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_YES, GNUNET_NO);
130   hello_pipe_stdout = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO, GNUNET_YES);
131
132   if ((hello_pipe_stdout == NULL) || (hello_pipe_stdin == NULL))
133   {
134     GNUNET_break (0);
135     ok = 1;
136     GNUNET_free (fn);
137     return;
138   }
139
140   proc =
141       GNUNET_OS_start_process (GNUNET_NO, GNUNET_OS_INHERIT_STD_ERR,
142                                hello_pipe_stdin, hello_pipe_stdout, fn,
143                                "test_gnunet_echo_hello", "-", NULL);
144   GNUNET_free (fn);
145
146   /* Close the write end of the read pipe */
147   GNUNET_DISK_pipe_close_end (hello_pipe_stdout, GNUNET_DISK_PIPE_END_WRITE);
148   /* Close the read end of the write pipe */
149   GNUNET_DISK_pipe_close_end (hello_pipe_stdin, GNUNET_DISK_PIPE_END_READ);
150
151   wh = GNUNET_DISK_pipe_handle (hello_pipe_stdin, GNUNET_DISK_PIPE_END_WRITE);
152
153   /* Write the test_phrase to the cat process */
154   if (GNUNET_DISK_file_write (wh, test_phrase, strlen (test_phrase) + 1) !=
155       strlen (test_phrase) + 1)
156   {
157     GNUNET_break (0);
158     ok = 1;
159     return;
160   }
161
162   /* Close the write end to end the cycle! */
163   GNUNET_DISK_pipe_close_end (hello_pipe_stdin, GNUNET_DISK_PIPE_END_WRITE);
164
165   stdout_read_handle =
166       GNUNET_DISK_pipe_handle (hello_pipe_stdout, GNUNET_DISK_PIPE_END_READ);
167
168   die_task =
169       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
170                                     (GNUNET_TIME_UNIT_MINUTES, 1), &end_task,
171                                     NULL);
172
173   memset (&rc, 0, sizeof (rc));
174   rc.stdout_read_handle = stdout_read_handle;
175   GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
176                                   stdout_read_handle, &read_call,
177                                   NULL);
178 }
179
180
181 /**
182  * Main method, starts scheduler with task1,
183  * checks that "ok" is correct at the end.
184  */
185 static int
186 check_run ()
187 {
188   ok = 1;
189   GNUNET_SCHEDULER_run (&run_task, &ok);
190   return ok;
191 }
192
193
194 /**
195  * Test killing via pipe.
196  */
197 static int
198 check_kill ()
199 {
200   char *fn;
201
202   hello_pipe_stdin = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_YES, GNUNET_NO);
203   hello_pipe_stdout = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO, GNUNET_YES);
204   if ((hello_pipe_stdout == NULL) || (hello_pipe_stdin == NULL))
205   {
206     return 1;
207   }
208   fn = GNUNET_OS_get_libexec_binary_path ("gnunet-service-resolver");
209   proc =
210     GNUNET_OS_start_process (GNUNET_YES, GNUNET_OS_INHERIT_STD_ERR, hello_pipe_stdin, hello_pipe_stdout, fn,
211                              "gnunet-service-resolver", "-", NULL); 
212   sleep (1); /* give process time to start, so we actually use the pipe-kill mechanism! */
213   GNUNET_free (fn);
214   if (0 != GNUNET_OS_process_kill (proc, SIGTERM))
215     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
216   GNUNET_assert (GNUNET_OK == GNUNET_OS_process_wait (proc));
217   GNUNET_OS_process_destroy (proc);
218   proc = NULL;
219   GNUNET_DISK_pipe_close (hello_pipe_stdout);
220   GNUNET_DISK_pipe_close (hello_pipe_stdin);
221   return 0;
222 }
223
224
225 /**
226  * Test killing via pipe.
227  */
228 static int
229 check_instant_kill ()
230 {
231   char *fn;
232
233   hello_pipe_stdin = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_YES, GNUNET_NO);
234   hello_pipe_stdout = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO, GNUNET_YES);
235   if ((hello_pipe_stdout == NULL) || (hello_pipe_stdin == NULL))
236   {
237     return 1;
238   }
239   fn = GNUNET_OS_get_libexec_binary_path ("gnunet-service-resolver");
240   proc =
241     GNUNET_OS_start_process (GNUNET_YES, GNUNET_OS_INHERIT_STD_ERR, hello_pipe_stdin, hello_pipe_stdout, fn,
242                              "gnunet-service-resolver", "-", NULL); 
243   if (0 != GNUNET_OS_process_kill (proc, SIGTERM))
244   {
245     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
246   }
247   GNUNET_free (fn);
248   GNUNET_assert (GNUNET_OK == GNUNET_OS_process_wait (proc));
249   GNUNET_OS_process_destroy (proc);
250   proc = NULL;
251   GNUNET_DISK_pipe_close (hello_pipe_stdout);
252   GNUNET_DISK_pipe_close (hello_pipe_stdin);
253   return 0;
254 }
255
256
257 int
258 main (int argc, char *argv[])
259 {
260   int ret;
261
262   GNUNET_log_setup ("test-os-start-process",
263                     "WARNING",
264                     NULL);
265   ret = 0;
266   ret |= check_run ();
267   ret |= check_kill ();
268   ret |= check_instant_kill ();
269   return ret;
270 }
271
272 /* end of test_os_start_process.c */