/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is the Netscape Portable Runtime (NSPR). * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998-2000 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /*********************************************************************** ** ** Name: forktest.c ** ** Description: UNIX test for fork functions. ** ** Modification History: ** 15-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. ** The debug mode will print all of the printfs associated with this test. ** The regress mode will be the default mode. Since the regress tool limits ** the output to a one line status:PASS or FAIL,all of the printf statements ** have been handled with an if (debug_mode) statement. ** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to ** recognize the return code from tha main program. ** 12-June-97 AGarcic - Revert to return code 0 and 1, remove debug option (obsolete). ***********************************************************************/ /*********************************************************************** ** Includes ***********************************************************************/ /* Used to get the command line option */ #include "plgetopt.h" #include "nspr.h" #include #include #include PRIntn failed_already=0; #ifdef XP_UNIX #include #include #include #include static char *message = "Hello world!"; static void ClientThreadFunc(void *arg) { PRNetAddr addr; PRFileDesc *sock = NULL; PRInt32 tmp = (PRInt32)arg; /* * Make sure the PR_Accept call will block */ printf("Wait one second before connect\n"); fflush(stdout); PR_Sleep(PR_SecondsToInterval(1)); addr.inet.family = AF_INET; addr.inet.ip = PR_htonl(INADDR_ANY); addr.inet.port = 0; if ((sock = PR_NewTCPSocket()) == NULL) { fprintf(stderr, "failed to create TCP socket: error code %d\n", PR_GetError()); failed_already = 1; goto finish; } if (PR_Bind(sock, &addr) != PR_SUCCESS) { fprintf(stderr, "PR_Bind failed: error code %d\n", PR_GetError()); failed_already = 1; goto finish; } addr.inet.ip = PR_htonl(INADDR_LOOPBACK); addr.inet.port = PR_htons((PRInt16)tmp); printf("Connecting to port %hu\n", PR_ntohs(addr.inet.port)); fflush(stdout); if (PR_Connect(sock, &addr, PR_SecondsToInterval(5)) != PR_SUCCESS) { fprintf(stderr, "PR_Connect failed: error code %d\n", PR_GetError()); failed_already = 1; goto finish; } printf("Writing message \"%s\"\n", message); fflush(stdout); if (PR_Send(sock, message, strlen(message) + 1, 0, PR_INTERVAL_NO_TIMEOUT) == -1) { fprintf(stderr, "PR_Send failed: error code %d\n", PR_GetError()); failed_already = 1; goto finish; } finish: if (sock) { PR_Close(sock); } return; } /* * DoIO -- * This function creates a thread that acts as a client and itself. * acts as a server. Then it joins the client thread. */ static void DoIO(void) { PRThread *clientThread; PRFileDesc *listenSock = NULL; PRFileDesc *sock = NULL; PRNetAddr addr; PRInt32 nBytes; char buf[128]; listenSock = PR_NewTCPSocket(); if (!listenSock) { fprintf(stderr, "failed to create a TCP socket: error code %d\n", PR_GetError()); failed_already = 1; goto finish; } addr.inet.family = AF_INET; addr.inet.ip = PR_htonl(INADDR_ANY); addr.inet.port = 0; if (PR_Bind(listenSock, &addr) == PR_FAILURE) { fprintf(stderr, "failed to bind socket: error code %d\n", PR_GetError()); failed_already = 1; goto finish; } if (PR_GetSockName(listenSock, &addr) == PR_FAILURE) { fprintf(stderr, "failed to get socket port number: error code %d\n", PR_GetError()); failed_already = 1; goto finish; } if (PR_Listen(listenSock, 5) == PR_FAILURE) { fprintf(stderr, "PR_Listen failed: error code %d\n", PR_GetError()); failed_already = 1; goto finish; } clientThread = PR_CreateThread( PR_USER_THREAD, ClientThreadFunc, (void *) PR_ntohs(addr.inet.port), PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); if (clientThread == NULL) { fprintf(stderr, "Cannot create client thread: (%d, %d)\n", PR_GetError(), PR_GetOSError()); failed_already = 1; goto finish; } printf("Accepting connection at port %hu\n", PR_ntohs(addr.inet.port)); fflush(stdout); sock = PR_Accept(listenSock, &addr, PR_SecondsToInterval(5)); if (!sock) { fprintf(stderr, "PR_Accept failed: error code %d\n", PR_GetError()); failed_already = 1; goto finish; } nBytes = PR_Recv(sock, buf, sizeof(buf), 0, PR_INTERVAL_NO_TIMEOUT); if (nBytes == -1) { fprintf(stderr, "PR_Recv failed: error code %d\n", PR_GetError()); failed_already = 1; goto finish; } /* * Make sure it has proper null byte to mark end of string */ buf[sizeof(buf) - 1] = '\0'; printf("Received \"%s\" from the client\n", buf); fflush(stdout); if (!strcmp(buf, message)) { PR_JoinThread(clientThread); printf("The message is received correctly\n"); fflush(stdout); } else { fprintf(stderr, "The message should be \"%s\"\n", message); failed_already = 1; } finish: if (listenSock) { PR_Close(listenSock); } if (sock) { PR_Close(sock); } return; } #ifdef _PR_DCETHREADS #include pid_t PR_UnixFork1(void) { pid_t parent = getpid(); int rv = syscall(SYS_fork); if (rv == -1) { return (pid_t) -1; } else { /* For each process, rv is the pid of the other process */ if (rv == parent) { /* the child */ return 0; } else { /* the parent */ return rv; } } } #elif defined(SOLARIS) /* * It seems like that in Solaris 2.4 one must call fork1() if the * the child process is going to use thread functions. Solaris 2.5 * doesn't have this problem. Calling fork() also works. */ pid_t PR_UnixFork1(void) { return fork1(); } #else pid_t PR_UnixFork1(void) { return fork(); } #endif /* PR_DCETHREADS */ int main( int argc, char *argv[] ) { pid_t pid; int rv; /* main test program */ DoIO(); pid = PR_UnixFork1(); if (pid == (pid_t) -1) { fprintf(stderr, "Fork failed: errno %d\n", errno); failed_already=1; return 1; } else if (pid > 0) { int childStatus; printf("Fork succeeded. Parent process continues.\n"); DoIO(); if ((rv = waitpid(pid, &childStatus, 0)) != pid) { #if defined(IRIX) && !defined(_PR_PTHREADS) /* * nspr may handle SIGCLD signal */ if ((rv < 0) && (errno == ECHILD)) { } else #endif { fprintf(stderr, "waitpid failed: %d\n", errno); failed_already = 1; } } else if (!WIFEXITED(childStatus) || WEXITSTATUS(childStatus) != 0) { failed_already = 1; } printf("Parent process exits.\n"); if (!failed_already) { printf("PASSED\n"); } else { printf("FAILED\n"); } return failed_already; } else { #if defined(IRIX) && !defined(_PR_PTHREADS) extern void _PR_IRIX_CHILD_PROCESS(void); _PR_IRIX_CHILD_PROCESS(); #endif printf("Fork succeeded. Child process continues.\n"); DoIO(); printf("Child process exits.\n"); return failed_already; } } #else /* XP_UNIX */ int main( int argc, char *argv[] ) { printf("The fork test is applicable to Unix only.\n"); return 0; } #endif /* XP_UNIX */