|
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 /*********************************************************************** |
|
7 ** |
|
8 ** Name: forktest.c |
|
9 ** |
|
10 ** Description: UNIX test for fork functions. |
|
11 ** |
|
12 ** Modification History: |
|
13 ** 15-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. |
|
14 ** The debug mode will print all of the printfs associated with this test. |
|
15 ** The regress mode will be the default mode. Since the regress tool limits |
|
16 ** the output to a one line status:PASS or FAIL,all of the printf statements |
|
17 ** have been handled with an if (debug_mode) statement. |
|
18 ** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to |
|
19 ** recognize the return code from tha main program. |
|
20 ** 12-June-97 AGarcic - Revert to return code 0 and 1, remove debug option (obsolete). |
|
21 ***********************************************************************/ |
|
22 |
|
23 /*********************************************************************** |
|
24 ** Includes |
|
25 ***********************************************************************/ |
|
26 /* Used to get the command line option */ |
|
27 #include "plgetopt.h" |
|
28 |
|
29 #include "nspr.h" |
|
30 #include <string.h> |
|
31 #include <stdio.h> |
|
32 #include <stdlib.h> |
|
33 |
|
34 PRIntn failed_already=0; |
|
35 |
|
36 #ifdef XP_UNIX |
|
37 |
|
38 #include <sys/types.h> |
|
39 #include <sys/wait.h> |
|
40 #include <unistd.h> |
|
41 #include <errno.h> |
|
42 |
|
43 static char *message = "Hello world!"; |
|
44 |
|
45 static void |
|
46 ClientThreadFunc(void *arg) |
|
47 { |
|
48 PRNetAddr addr; |
|
49 PRFileDesc *sock = NULL; |
|
50 PRInt32 tmp = (PRInt32)arg; |
|
51 |
|
52 /* |
|
53 * Make sure the PR_Accept call will block |
|
54 */ |
|
55 |
|
56 printf("Wait one second before connect\n"); |
|
57 fflush(stdout); |
|
58 PR_Sleep(PR_SecondsToInterval(1)); |
|
59 |
|
60 addr.inet.family = AF_INET; |
|
61 addr.inet.ip = PR_htonl(INADDR_ANY); |
|
62 addr.inet.port = 0; |
|
63 if ((sock = PR_NewTCPSocket()) == NULL) { |
|
64 fprintf(stderr, "failed to create TCP socket: error code %d\n", |
|
65 PR_GetError()); |
|
66 failed_already = 1; |
|
67 goto finish; |
|
68 } |
|
69 if (PR_Bind(sock, &addr) != PR_SUCCESS) { |
|
70 fprintf(stderr, "PR_Bind failed: error code %d\n", |
|
71 PR_GetError()); |
|
72 failed_already = 1; |
|
73 goto finish; |
|
74 } |
|
75 addr.inet.ip = PR_htonl(INADDR_LOOPBACK); |
|
76 addr.inet.port = PR_htons((PRInt16)tmp); |
|
77 printf("Connecting to port %hu\n", PR_ntohs(addr.inet.port)); |
|
78 fflush(stdout); |
|
79 if (PR_Connect(sock, &addr, PR_SecondsToInterval(5)) != |
|
80 PR_SUCCESS) { |
|
81 fprintf(stderr, "PR_Connect failed: error code %d\n", |
|
82 PR_GetError()); |
|
83 failed_already = 1; |
|
84 goto finish; |
|
85 } |
|
86 printf("Writing message \"%s\"\n", message); |
|
87 fflush(stdout); |
|
88 if (PR_Send(sock, message, strlen(message) + 1, 0, PR_INTERVAL_NO_TIMEOUT) == |
|
89 -1) { |
|
90 fprintf(stderr, "PR_Send failed: error code %d\n", |
|
91 PR_GetError()); |
|
92 failed_already = 1; |
|
93 goto finish; |
|
94 } |
|
95 finish: |
|
96 if (sock) { |
|
97 PR_Close(sock); |
|
98 } |
|
99 return; |
|
100 } |
|
101 |
|
102 /* |
|
103 * DoIO -- |
|
104 * This function creates a thread that acts as a client and itself. |
|
105 * acts as a server. Then it joins the client thread. |
|
106 */ |
|
107 static void |
|
108 DoIO(void) |
|
109 { |
|
110 PRThread *clientThread; |
|
111 PRFileDesc *listenSock = NULL; |
|
112 PRFileDesc *sock = NULL; |
|
113 PRNetAddr addr; |
|
114 PRInt32 nBytes; |
|
115 char buf[128]; |
|
116 |
|
117 listenSock = PR_NewTCPSocket(); |
|
118 if (!listenSock) { |
|
119 fprintf(stderr, "failed to create a TCP socket: error code %d\n", |
|
120 PR_GetError()); |
|
121 failed_already = 1; |
|
122 goto finish; |
|
123 } |
|
124 addr.inet.family = AF_INET; |
|
125 addr.inet.ip = PR_htonl(INADDR_ANY); |
|
126 addr.inet.port = 0; |
|
127 if (PR_Bind(listenSock, &addr) == PR_FAILURE) { |
|
128 fprintf(stderr, "failed to bind socket: error code %d\n", |
|
129 PR_GetError()); |
|
130 failed_already = 1; |
|
131 goto finish; |
|
132 } |
|
133 if (PR_GetSockName(listenSock, &addr) == PR_FAILURE) { |
|
134 fprintf(stderr, "failed to get socket port number: error code %d\n", |
|
135 PR_GetError()); |
|
136 failed_already = 1; |
|
137 goto finish; |
|
138 } |
|
139 if (PR_Listen(listenSock, 5) == PR_FAILURE) { |
|
140 fprintf(stderr, "PR_Listen failed: error code %d\n", |
|
141 PR_GetError()); |
|
142 failed_already = 1; |
|
143 goto finish; |
|
144 } |
|
145 clientThread = PR_CreateThread( PR_USER_THREAD, ClientThreadFunc, |
|
146 (void *) PR_ntohs(addr.inet.port), PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, |
|
147 PR_JOINABLE_THREAD, 0); |
|
148 if (clientThread == NULL) { |
|
149 fprintf(stderr, "Cannot create client thread: (%d, %d)\n", |
|
150 PR_GetError(), PR_GetOSError()); |
|
151 failed_already = 1; |
|
152 goto finish; |
|
153 } |
|
154 printf("Accepting connection at port %hu\n", PR_ntohs(addr.inet.port)); |
|
155 fflush(stdout); |
|
156 sock = PR_Accept(listenSock, &addr, PR_SecondsToInterval(5)); |
|
157 if (!sock) { |
|
158 fprintf(stderr, "PR_Accept failed: error code %d\n", |
|
159 PR_GetError()); |
|
160 failed_already = 1; |
|
161 goto finish; |
|
162 } |
|
163 nBytes = PR_Recv(sock, buf, sizeof(buf), 0, PR_INTERVAL_NO_TIMEOUT); |
|
164 if (nBytes == -1) { |
|
165 fprintf(stderr, "PR_Recv failed: error code %d\n", |
|
166 PR_GetError()); |
|
167 failed_already = 1; |
|
168 goto finish; |
|
169 } |
|
170 |
|
171 /* |
|
172 * Make sure it has proper null byte to mark end of string |
|
173 */ |
|
174 |
|
175 buf[sizeof(buf) - 1] = '\0'; |
|
176 printf("Received \"%s\" from the client\n", buf); |
|
177 fflush(stdout); |
|
178 if (!strcmp(buf, message)) { |
|
179 PR_JoinThread(clientThread); |
|
180 |
|
181 printf("The message is received correctly\n"); |
|
182 fflush(stdout); |
|
183 } else { |
|
184 fprintf(stderr, "The message should be \"%s\"\n", |
|
185 message); |
|
186 failed_already = 1; |
|
187 } |
|
188 |
|
189 finish: |
|
190 if (listenSock) { |
|
191 PR_Close(listenSock); |
|
192 } |
|
193 if (sock) { |
|
194 PR_Close(sock); |
|
195 } |
|
196 return; |
|
197 } |
|
198 |
|
199 #ifdef _PR_DCETHREADS |
|
200 |
|
201 #include <syscall.h> |
|
202 |
|
203 pid_t PR_UnixFork1(void) |
|
204 { |
|
205 pid_t parent = getpid(); |
|
206 int rv = syscall(SYS_fork); |
|
207 |
|
208 if (rv == -1) { |
|
209 return (pid_t) -1; |
|
210 } else { |
|
211 /* For each process, rv is the pid of the other process */ |
|
212 if (rv == parent) { |
|
213 /* the child */ |
|
214 return 0; |
|
215 } else { |
|
216 /* the parent */ |
|
217 return rv; |
|
218 } |
|
219 } |
|
220 } |
|
221 |
|
222 #elif defined(SOLARIS) |
|
223 |
|
224 /* |
|
225 * It seems like that in Solaris 2.4 one must call fork1() if the |
|
226 * the child process is going to use thread functions. Solaris 2.5 |
|
227 * doesn't have this problem. Calling fork() also works. |
|
228 */ |
|
229 |
|
230 pid_t PR_UnixFork1(void) |
|
231 { |
|
232 return fork1(); |
|
233 } |
|
234 |
|
235 #else |
|
236 |
|
237 pid_t PR_UnixFork1(void) |
|
238 { |
|
239 return fork(); |
|
240 } |
|
241 |
|
242 #endif /* PR_DCETHREADS */ |
|
243 |
|
244 int main(int argc, char **argv) |
|
245 { |
|
246 pid_t pid; |
|
247 int rv; |
|
248 |
|
249 /* main test program */ |
|
250 |
|
251 DoIO(); |
|
252 |
|
253 pid = PR_UnixFork1(); |
|
254 |
|
255 if (pid == (pid_t) -1) { |
|
256 fprintf(stderr, "Fork failed: errno %d\n", errno); |
|
257 failed_already=1; |
|
258 return 1; |
|
259 } else if (pid > 0) { |
|
260 int childStatus; |
|
261 |
|
262 printf("Fork succeeded. Parent process continues.\n"); |
|
263 DoIO(); |
|
264 if ((rv = waitpid(pid, &childStatus, 0)) != pid) { |
|
265 #if defined(IRIX) && !defined(_PR_PTHREADS) |
|
266 /* |
|
267 * nspr may handle SIGCLD signal |
|
268 */ |
|
269 if ((rv < 0) && (errno == ECHILD)) { |
|
270 } else |
|
271 #endif |
|
272 { |
|
273 fprintf(stderr, "waitpid failed: %d\n", errno); |
|
274 failed_already = 1; |
|
275 } |
|
276 } else if (!WIFEXITED(childStatus) |
|
277 || WEXITSTATUS(childStatus) != 0) { |
|
278 failed_already = 1; |
|
279 } |
|
280 printf("Parent process exits.\n"); |
|
281 if (!failed_already) { |
|
282 printf("PASSED\n"); |
|
283 } else { |
|
284 printf("FAILED\n"); |
|
285 } |
|
286 return failed_already; |
|
287 } else { |
|
288 #if defined(IRIX) && !defined(_PR_PTHREADS) |
|
289 extern void _PR_IRIX_CHILD_PROCESS(void); |
|
290 _PR_IRIX_CHILD_PROCESS(); |
|
291 #endif |
|
292 printf("Fork succeeded. Child process continues.\n"); |
|
293 DoIO(); |
|
294 printf("Child process exits.\n"); |
|
295 return failed_already; |
|
296 } |
|
297 } |
|
298 |
|
299 #else /* XP_UNIX */ |
|
300 |
|
301 int main( int argc, |
|
302 char *argv[] |
|
303 ) |
|
304 { |
|
305 |
|
306 printf("The fork test is applicable to Unix only.\n"); |
|
307 return 0; |
|
308 |
|
309 } |
|
310 |
|
311 #endif /* XP_UNIX */ |