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 **  1997 - Netscape Communications Corporation
8 **
9 ** Name: prselect_norm.c
10 **
11 ** Description: tests PR_Select with sockets - Normal operations.
12 **
13 ** Modification History:
14 ** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
15 **           The debug mode will print all of the printfs associated with this test.
16 **           The regress mode will be the default mode. Since the regress tool limits
17 **           the output to a one line status:PASS or FAIL,all of the printf statements
18 **           have been handled with an if (debug_mode) statement.
19 ** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
20 **          recognize the return code from tha main program.
21 ***********************************************************************/
22 
23 /***********************************************************************
24 ** Includes
25 ***********************************************************************/
26 /* Used to get the command line option */
27 #include "plgetopt.h"
28 
29 #include "prinit.h"
30 #include "prio.h"
31 #include "prlog.h"
32 #include "prprf.h"
33 #include "prerror.h"
34 #include "prnetdb.h"
35 
36 #include "obsolete/probslet.h"
37 
38 #include <stdio.h>
39 #include <string.h>
40 #include <stdlib.h>
41 
42 PRIntn failed_already=0;
43 PRIntn debug_mode;
44 
45 static void
clientThreadFunc(void * arg)46 clientThreadFunc(void *arg)
47 {
48     PRUintn port = (PRUintn)(uintptr_t)arg;
49     PRFileDesc *sock;
50     PRNetAddr addr;
51     char buf[128];
52     int i;
53 
54     addr.inet.family = PR_AF_INET;
55     addr.inet.port = PR_htons((PRUint16)port);
56     addr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK);
57     PR_snprintf(buf, sizeof(buf), "%hu", addr.inet.port);
58 
59     for (i = 0; i < 5; i++) {
60         sock = PR_NewTCPSocket();
61         PR_Connect(sock, &addr, PR_INTERVAL_NO_TIMEOUT);
62         PR_Write(sock, buf, sizeof(buf));
63         PR_Close(sock);
64     }
65 }
66 
main(int argc,char ** argv)67 int main(int argc, char **argv)
68 {
69     PRFileDesc *listenSock1, *listenSock2;
70     PRFileDesc *fds0[10], *fds1[10], **fds, **other_fds;
71     PRIntn nfds;
72     PRUint16 listenPort1, listenPort2;
73     PRNetAddr addr;
74     PR_fd_set readFdSet;
75     char buf[128];
76     PRThread *clientThread;
77     PRInt32 retVal;
78     PRIntn i, j;
79 
80     /* The command line argument: -d is used to determine if the test is being run
81     in debug mode. The regress tool requires only one line output:PASS or FAIL.
82     All of the printfs associated with this test has been handled with a if (debug_mode)
83     test.
84     Usage: test_name -d
85     */
86     PLOptStatus os;
87     PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
88     while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
89     {
90         if (PL_OPT_BAD == os) {
91             continue;
92         }
93         switch (opt->option)
94         {
95             case 'd':  /* debug mode */
96                 debug_mode = 1;
97                 break;
98             default:
99                 break;
100         }
101     }
102     PL_DestroyOptState(opt);
103 
104     /* main test */
105 
106     PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
107     PR_STDIO_INIT();
108 
109     if (debug_mode) {
110         printf("This program tests PR_Select with sockets.  \n");
111         printf(" Normal operation are tested.\n\n");
112     }
113 
114     /* Create two listening sockets */
115     if ((listenSock1 = PR_NewTCPSocket()) == NULL) {
116         fprintf(stderr, "Can't create a new TCP socket\n");
117         failed_already=1;
118         goto exit_now;
119     }
120     addr.inet.family = PR_AF_INET;
121     addr.inet.ip = PR_htonl(PR_INADDR_ANY);
122     addr.inet.port = PR_htons(0);
123     if (PR_Bind(listenSock1, &addr) == PR_FAILURE) {
124         fprintf(stderr, "Can't bind socket\n");
125         failed_already=1;
126         goto exit_now;
127     }
128     if (PR_GetSockName(listenSock1, &addr) == PR_FAILURE) {
129         fprintf(stderr, "PR_GetSockName failed\n");
130         failed_already=1;
131         goto exit_now;
132     }
133     listenPort1 = PR_ntohs(addr.inet.port);
134     if (PR_Listen(listenSock1, 5) == PR_FAILURE) {
135         fprintf(stderr, "Can't listen on a socket\n");
136         failed_already=1;
137         goto exit_now;
138     }
139 
140     if ((listenSock2  = PR_NewTCPSocket()) == NULL) {
141         fprintf(stderr, "Can't create a new TCP socket\n");
142         failed_already=1;
143         goto exit_now;
144     }
145     addr.inet.family = PR_AF_INET;
146     addr.inet.ip = PR_htonl(PR_INADDR_ANY);
147     addr.inet.port = PR_htons(0);
148     if (PR_Bind(listenSock2, &addr) == PR_FAILURE) {
149         fprintf(stderr, "Can't bind socket\n");
150         failed_already=1;
151         goto exit_now;
152     }
153     if (PR_GetSockName(listenSock2, &addr) == PR_FAILURE) {
154         fprintf(stderr, "PR_GetSockName failed\n");
155         failed_already=1;
156         goto exit_now;
157     }
158     listenPort2 = PR_ntohs(addr.inet.port);
159     if (PR_Listen(listenSock2, 5) == PR_FAILURE) {
160         fprintf(stderr, "Can't listen on a socket\n");
161         failed_already=1;
162         goto exit_now;
163     }
164     PR_snprintf(buf, sizeof(buf),
165                 "The server thread is listening on ports %hu and %hu\n\n",
166                 listenPort1, listenPort2);
167     if (debug_mode) {
168         printf("%s", buf);
169     }
170 
171     clientThread = PR_CreateThread(PR_USER_THREAD,
172                                    clientThreadFunc, (void *)(uintptr_t)listenPort1,
173                                    PR_PRIORITY_NORMAL, PR_LOCAL_THREAD,
174                                    PR_UNJOINABLE_THREAD, 0);
175     if (clientThread == NULL) {
176         fprintf(stderr, "can't create thread\n");
177         failed_already=1;
178         goto exit_now;
179     }
180 
181     clientThread = PR_CreateThread(PR_USER_THREAD,
182                                    clientThreadFunc, (void *)(uintptr_t)listenPort2,
183                                    PR_PRIORITY_NORMAL, PR_LOCAL_THREAD,
184                                    PR_UNJOINABLE_THREAD, 0);
185     if (clientThread == NULL) {
186         fprintf(stderr, "can't create thread\n");
187         failed_already=1;
188         goto exit_now;
189     }
190 
191     if (debug_mode) {
192         printf("Two client threads are created.  Each of them will\n");
193         printf("send data to one of the two ports the server is listening on.\n");
194         printf("The data they send is the port number.  Each of them send\n");
195         printf("the data five times, so you should see ten lines below,\n");
196         printf("interleaved in an arbitrary order.\n");
197     }
198     /* set up the fd array */
199     fds = fds0;
200     other_fds = fds1;
201     fds[0] = listenSock1;
202     fds[1] = listenSock2;
203     nfds = 2;
204     /* Set up the fd set */
205     PR_FD_ZERO(&readFdSet);
206     PR_FD_SET(listenSock1, &readFdSet);
207     PR_FD_SET(listenSock2, &readFdSet);
208 
209     /* 20 events total */
210     i = 0;
211     while (i < 20) {
212         PRFileDesc **tmp;
213         int nextIndex;
214         int nEvents = 0;
215 
216         retVal = PR_Select(0 /* unused */, &readFdSet, NULL, NULL,
217                            PR_INTERVAL_NO_TIMEOUT);
218         PR_ASSERT(retVal != 0);  /* no timeout */
219         if (retVal == -1) {
220             fprintf(stderr, "PR_Select failed (%d, %d)\n", PR_GetError(),
221                     PR_GetOSError());
222             failed_already=1;
223             goto exit_now;
224         }
225 
226         nextIndex = 2;
227         /* the two listening sockets */
228         for (j = 0; j < 2; j++) {
229             other_fds[j] = fds[j];
230             if (PR_FD_ISSET(fds[j], &readFdSet)) {
231                 PRFileDesc *sock;
232 
233                 nEvents++;
234                 sock = PR_Accept(fds[j], NULL, PR_INTERVAL_NO_TIMEOUT);
235                 if (sock == NULL) {
236                     fprintf(stderr, "PR_Accept() failed\n");
237                     failed_already=1;
238                     goto exit_now;
239                 }
240                 other_fds[nextIndex] = sock;
241                 PR_FD_SET(sock, &readFdSet);
242                 nextIndex++;
243             }
244             PR_FD_SET(fds[j], &readFdSet);
245         }
246 
247         for (j = 2; j < nfds; j++) {
248             if (PR_FD_ISSET(fds[j], &readFdSet)) {
249                 PRInt32 nBytes;
250 
251                 PR_FD_CLR(fds[j], &readFdSet);
252                 nEvents++;
253                 nBytes = PR_Read(fds[j], buf, sizeof(buf));
254                 if (nBytes == -1) {
255                     fprintf(stderr, "PR_Read() failed\n");
256                     failed_already=1;
257                     goto exit_now;
258                 }
259                 /* Just to be safe */
260                 buf[127] = '\0';
261                 PR_Close(fds[j]);
262                 if (debug_mode) {
263                     printf("The server received \"%s\" from a client\n", buf);
264                 }
265             } else {
266                 PR_FD_SET(fds[j], &readFdSet);
267                 other_fds[nextIndex] = fds[j];
268                 nextIndex++;
269             }
270         }
271 
272         PR_ASSERT(retVal == nEvents);
273         /* swap */
274         tmp = fds;
275         fds = other_fds;
276         other_fds = tmp;
277         nfds = nextIndex;
278         i += nEvents;
279     }
280 
281     if (debug_mode) {
282         printf("Test passed\n");
283     }
284 
285     PR_Cleanup();
286     goto exit_now;
287 exit_now:
288     if(failed_already) {
289         return 1;
290     }
291     else {
292         return 0;
293     }
294 }
295