michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /*********************************************************************** michael@0: ** michael@0: ** Name: forktest.c michael@0: ** michael@0: ** Description: UNIX test for fork functions. michael@0: ** michael@0: ** Modification History: michael@0: ** 15-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. michael@0: ** The debug mode will print all of the printfs associated with this test. michael@0: ** The regress mode will be the default mode. Since the regress tool limits michael@0: ** the output to a one line status:PASS or FAIL,all of the printf statements michael@0: ** have been handled with an if (debug_mode) statement. michael@0: ** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to michael@0: ** recognize the return code from tha main program. michael@0: ** 12-June-97 AGarcic - Revert to return code 0 and 1, remove debug option (obsolete). michael@0: ***********************************************************************/ michael@0: michael@0: /*********************************************************************** michael@0: ** Includes michael@0: ***********************************************************************/ michael@0: /* Used to get the command line option */ michael@0: #include "plgetopt.h" michael@0: michael@0: #include "nspr.h" michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: PRIntn failed_already=0; michael@0: michael@0: #ifdef XP_UNIX michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: static char *message = "Hello world!"; michael@0: michael@0: static void michael@0: ClientThreadFunc(void *arg) michael@0: { michael@0: PRNetAddr addr; michael@0: PRFileDesc *sock = NULL; michael@0: PRInt32 tmp = (PRInt32)arg; michael@0: michael@0: /* michael@0: * Make sure the PR_Accept call will block michael@0: */ michael@0: michael@0: printf("Wait one second before connect\n"); michael@0: fflush(stdout); michael@0: PR_Sleep(PR_SecondsToInterval(1)); michael@0: michael@0: addr.inet.family = AF_INET; michael@0: addr.inet.ip = PR_htonl(INADDR_ANY); michael@0: addr.inet.port = 0; michael@0: if ((sock = PR_NewTCPSocket()) == NULL) { michael@0: fprintf(stderr, "failed to create TCP socket: error code %d\n", michael@0: PR_GetError()); michael@0: failed_already = 1; michael@0: goto finish; michael@0: } michael@0: if (PR_Bind(sock, &addr) != PR_SUCCESS) { michael@0: fprintf(stderr, "PR_Bind failed: error code %d\n", michael@0: PR_GetError()); michael@0: failed_already = 1; michael@0: goto finish; michael@0: } michael@0: addr.inet.ip = PR_htonl(INADDR_LOOPBACK); michael@0: addr.inet.port = PR_htons((PRInt16)tmp); michael@0: printf("Connecting to port %hu\n", PR_ntohs(addr.inet.port)); michael@0: fflush(stdout); michael@0: if (PR_Connect(sock, &addr, PR_SecondsToInterval(5)) != michael@0: PR_SUCCESS) { michael@0: fprintf(stderr, "PR_Connect failed: error code %d\n", michael@0: PR_GetError()); michael@0: failed_already = 1; michael@0: goto finish; michael@0: } michael@0: printf("Writing message \"%s\"\n", message); michael@0: fflush(stdout); michael@0: if (PR_Send(sock, message, strlen(message) + 1, 0, PR_INTERVAL_NO_TIMEOUT) == michael@0: -1) { michael@0: fprintf(stderr, "PR_Send failed: error code %d\n", michael@0: PR_GetError()); michael@0: failed_already = 1; michael@0: goto finish; michael@0: } michael@0: finish: michael@0: if (sock) { michael@0: PR_Close(sock); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: /* michael@0: * DoIO -- michael@0: * This function creates a thread that acts as a client and itself. michael@0: * acts as a server. Then it joins the client thread. michael@0: */ michael@0: static void michael@0: DoIO(void) michael@0: { michael@0: PRThread *clientThread; michael@0: PRFileDesc *listenSock = NULL; michael@0: PRFileDesc *sock = NULL; michael@0: PRNetAddr addr; michael@0: PRInt32 nBytes; michael@0: char buf[128]; michael@0: michael@0: listenSock = PR_NewTCPSocket(); michael@0: if (!listenSock) { michael@0: fprintf(stderr, "failed to create a TCP socket: error code %d\n", michael@0: PR_GetError()); michael@0: failed_already = 1; michael@0: goto finish; michael@0: } michael@0: addr.inet.family = AF_INET; michael@0: addr.inet.ip = PR_htonl(INADDR_ANY); michael@0: addr.inet.port = 0; michael@0: if (PR_Bind(listenSock, &addr) == PR_FAILURE) { michael@0: fprintf(stderr, "failed to bind socket: error code %d\n", michael@0: PR_GetError()); michael@0: failed_already = 1; michael@0: goto finish; michael@0: } michael@0: if (PR_GetSockName(listenSock, &addr) == PR_FAILURE) { michael@0: fprintf(stderr, "failed to get socket port number: error code %d\n", michael@0: PR_GetError()); michael@0: failed_already = 1; michael@0: goto finish; michael@0: } michael@0: if (PR_Listen(listenSock, 5) == PR_FAILURE) { michael@0: fprintf(stderr, "PR_Listen failed: error code %d\n", michael@0: PR_GetError()); michael@0: failed_already = 1; michael@0: goto finish; michael@0: } michael@0: clientThread = PR_CreateThread( PR_USER_THREAD, ClientThreadFunc, michael@0: (void *) PR_ntohs(addr.inet.port), PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, michael@0: PR_JOINABLE_THREAD, 0); michael@0: if (clientThread == NULL) { michael@0: fprintf(stderr, "Cannot create client thread: (%d, %d)\n", michael@0: PR_GetError(), PR_GetOSError()); michael@0: failed_already = 1; michael@0: goto finish; michael@0: } michael@0: printf("Accepting connection at port %hu\n", PR_ntohs(addr.inet.port)); michael@0: fflush(stdout); michael@0: sock = PR_Accept(listenSock, &addr, PR_SecondsToInterval(5)); michael@0: if (!sock) { michael@0: fprintf(stderr, "PR_Accept failed: error code %d\n", michael@0: PR_GetError()); michael@0: failed_already = 1; michael@0: goto finish; michael@0: } michael@0: nBytes = PR_Recv(sock, buf, sizeof(buf), 0, PR_INTERVAL_NO_TIMEOUT); michael@0: if (nBytes == -1) { michael@0: fprintf(stderr, "PR_Recv failed: error code %d\n", michael@0: PR_GetError()); michael@0: failed_already = 1; michael@0: goto finish; michael@0: } michael@0: michael@0: /* michael@0: * Make sure it has proper null byte to mark end of string michael@0: */ michael@0: michael@0: buf[sizeof(buf) - 1] = '\0'; michael@0: printf("Received \"%s\" from the client\n", buf); michael@0: fflush(stdout); michael@0: if (!strcmp(buf, message)) { michael@0: PR_JoinThread(clientThread); michael@0: michael@0: printf("The message is received correctly\n"); michael@0: fflush(stdout); michael@0: } else { michael@0: fprintf(stderr, "The message should be \"%s\"\n", michael@0: message); michael@0: failed_already = 1; michael@0: } michael@0: michael@0: finish: michael@0: if (listenSock) { michael@0: PR_Close(listenSock); michael@0: } michael@0: if (sock) { michael@0: PR_Close(sock); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: #ifdef _PR_DCETHREADS michael@0: michael@0: #include michael@0: michael@0: pid_t PR_UnixFork1(void) michael@0: { michael@0: pid_t parent = getpid(); michael@0: int rv = syscall(SYS_fork); michael@0: michael@0: if (rv == -1) { michael@0: return (pid_t) -1; michael@0: } else { michael@0: /* For each process, rv is the pid of the other process */ michael@0: if (rv == parent) { michael@0: /* the child */ michael@0: return 0; michael@0: } else { michael@0: /* the parent */ michael@0: return rv; michael@0: } michael@0: } michael@0: } michael@0: michael@0: #elif defined(SOLARIS) michael@0: michael@0: /* michael@0: * It seems like that in Solaris 2.4 one must call fork1() if the michael@0: * the child process is going to use thread functions. Solaris 2.5 michael@0: * doesn't have this problem. Calling fork() also works. michael@0: */ michael@0: michael@0: pid_t PR_UnixFork1(void) michael@0: { michael@0: return fork1(); michael@0: } michael@0: michael@0: #else michael@0: michael@0: pid_t PR_UnixFork1(void) michael@0: { michael@0: return fork(); michael@0: } michael@0: michael@0: #endif /* PR_DCETHREADS */ michael@0: michael@0: int main(int argc, char **argv) michael@0: { michael@0: pid_t pid; michael@0: int rv; michael@0: michael@0: /* main test program */ michael@0: michael@0: DoIO(); michael@0: michael@0: pid = PR_UnixFork1(); michael@0: michael@0: if (pid == (pid_t) -1) { michael@0: fprintf(stderr, "Fork failed: errno %d\n", errno); michael@0: failed_already=1; michael@0: return 1; michael@0: } else if (pid > 0) { michael@0: int childStatus; michael@0: michael@0: printf("Fork succeeded. Parent process continues.\n"); michael@0: DoIO(); michael@0: if ((rv = waitpid(pid, &childStatus, 0)) != pid) { michael@0: #if defined(IRIX) && !defined(_PR_PTHREADS) michael@0: /* michael@0: * nspr may handle SIGCLD signal michael@0: */ michael@0: if ((rv < 0) && (errno == ECHILD)) { michael@0: } else michael@0: #endif michael@0: { michael@0: fprintf(stderr, "waitpid failed: %d\n", errno); michael@0: failed_already = 1; michael@0: } michael@0: } else if (!WIFEXITED(childStatus) michael@0: || WEXITSTATUS(childStatus) != 0) { michael@0: failed_already = 1; michael@0: } michael@0: printf("Parent process exits.\n"); michael@0: if (!failed_already) { michael@0: printf("PASSED\n"); michael@0: } else { michael@0: printf("FAILED\n"); michael@0: } michael@0: return failed_already; michael@0: } else { michael@0: #if defined(IRIX) && !defined(_PR_PTHREADS) michael@0: extern void _PR_IRIX_CHILD_PROCESS(void); michael@0: _PR_IRIX_CHILD_PROCESS(); michael@0: #endif michael@0: printf("Fork succeeded. Child process continues.\n"); michael@0: DoIO(); michael@0: printf("Child process exits.\n"); michael@0: return failed_already; michael@0: } michael@0: } michael@0: michael@0: #else /* XP_UNIX */ michael@0: michael@0: int main( int argc, michael@0: char *argv[] michael@0: ) michael@0: { michael@0: michael@0: printf("The fork test is applicable to Unix only.\n"); michael@0: return 0; michael@0: michael@0: } michael@0: michael@0: #endif /* XP_UNIX */