| /* Tests for fork. | 
 |    Copyright (C) 2000-2016 Free Software Foundation, Inc. | 
 |    This file is part of the GNU C Library. | 
 |    Contributed by Ulrich Drepper <drepper@cygnus.com>, 2000. | 
 |  | 
 |    The GNU C Library is free software; you can redistribute it and/or | 
 |    modify it under the terms of the GNU Lesser General Public | 
 |    License as published by the Free Software Foundation; either | 
 |    version 2.1 of the License, or (at your option) any later version. | 
 |  | 
 |    The GNU C Library is distributed in the hope that it will be useful, | 
 |    but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
 |    Lesser General Public License for more details. | 
 |  | 
 |    You should have received a copy of the GNU Lesser General Public | 
 |    License along with the GNU C Library; if not, see | 
 |    <http://www.gnu.org/licenses/>.  */ | 
 |  | 
 | #include <errno.h> | 
 | #include <error.h> | 
 | #include <stdlib.h> | 
 | #include <string.h> | 
 | #include <unistd.h> | 
 | #include <wait.h> | 
 |  | 
 |  | 
 | static const char testdata[] = "This is a test"; | 
 | static const char testdata2[] = "And here we go again"; | 
 |  | 
 |  | 
 | int | 
 | main (void) | 
 | { | 
 |   const char *tmpdir = getenv ("TMPDIR"); | 
 |   char buf[100]; | 
 |   size_t tmpdirlen; | 
 |   char *name; | 
 |   int fd; | 
 |   pid_t pid; | 
 |   pid_t ppid; | 
 |   off_t off; | 
 |   int status; | 
 |  | 
 |   if (tmpdir == NULL || *tmpdir == '\0') | 
 |     tmpdir = "/tmp"; | 
 |   tmpdirlen = strlen (tmpdir); | 
 |  | 
 |   name = (char *) malloc (tmpdirlen + strlen ("/forkXXXXXX") + 1); | 
 |   if (name == NULL) | 
 |     error (EXIT_FAILURE, errno, "cannot allocate file name"); | 
 |  | 
 |   mempcpy (mempcpy (name, tmpdir, tmpdirlen), | 
 | 	   "/forkXXXXXX", sizeof ("/forkXXXXXX")); | 
 |  | 
 |   /* Open our test file.   */ | 
 |   fd = mkstemp (name); | 
 |   if (fd == -1) | 
 |      error (EXIT_FAILURE, errno, "cannot open test file `%s'", name); | 
 |  | 
 |   /* Make sure it gets removed.  */ | 
 |   unlink (name); | 
 |  | 
 |   /* Write some data.  */ | 
 |   if (write (fd, testdata, strlen (testdata)) != strlen (testdata)) | 
 |     error (EXIT_FAILURE, errno, "cannot write test data"); | 
 |  | 
 |   /* Get the position in the stream.  */ | 
 |   off = lseek (fd, 0, SEEK_CUR); | 
 |   if (off == (off_t) -1 || off != strlen (testdata)) | 
 |     error (EXIT_FAILURE, errno, "wrong file position"); | 
 |  | 
 |   /* Get the parent PID.  */ | 
 |   ppid = getpid (); | 
 |  | 
 |   /* Now fork of the process.  */ | 
 |   pid = fork (); | 
 |   if (pid == 0) | 
 |     { | 
 |       /* One little test first: the PID must have changed.  */ | 
 |       if (getpid () == ppid) | 
 | 	error (EXIT_FAILURE, 0, "child and parent have same PID"); | 
 |  | 
 |       /* Test the `getppid' function.  */ | 
 |       pid = getppid (); | 
 |       if (pid == (pid_t) -1 ? errno != ENOSYS : pid != ppid) | 
 | 	error (EXIT_FAILURE, 0, | 
 | 	       "getppid returned wrong PID (%ld, should be %ld)", | 
 | 	       (long int) pid, (long int) ppid); | 
 |  | 
 |       /* This is the child.  First get the position of the descriptor.  */ | 
 |       off = lseek (fd, 0, SEEK_CUR); | 
 |       if (off == (off_t) -1 || off != strlen (testdata)) | 
 | 	error (EXIT_FAILURE, errno, "wrong file position in child"); | 
 |  | 
 |       /* Reset the position.  */ | 
 |       if (lseek (fd, 0, SEEK_SET) != 0) | 
 | 	error (EXIT_FAILURE, errno, "cannot reset position in child"); | 
 |  | 
 |       /* Read the data.  */ | 
 |       if (read (fd, buf, sizeof buf) != strlen (testdata)) | 
 | 	error (EXIT_FAILURE, errno, "cannot read data in child"); | 
 |  | 
 |       /* Compare the data.  */ | 
 |       if (memcmp (buf, testdata, strlen (testdata)) != 0) | 
 | 	error (EXIT_FAILURE, 0, "data comparison failed in child"); | 
 |  | 
 |       /* Reset position again.  */ | 
 |       if (lseek (fd, 0, SEEK_SET) != 0) | 
 | 	error (EXIT_FAILURE, errno, "cannot reset position again in child"); | 
 |  | 
 |       /* Write new data.  */ | 
 |       if (write (fd, testdata2, strlen (testdata2)) != strlen (testdata2)) | 
 | 	error (EXIT_FAILURE, errno, "cannot write new data in child"); | 
 |  | 
 |       /* Close the file.  This must not remove it.  */ | 
 |       close (fd); | 
 |  | 
 |       _exit (0); | 
 |     } | 
 |   else if (pid < 0) | 
 |     /* Something went wrong.  */ | 
 |     error (EXIT_FAILURE, errno, "cannot fork"); | 
 |  | 
 |   /* Wait for the child.  */ | 
 |   if (waitpid (pid, &status, 0) != pid) | 
 |     error (EXIT_FAILURE, 0, "Oops, wrong test program terminated"); | 
 |  | 
 |   if (WTERMSIG (status) != 0) | 
 |     error (EXIT_FAILURE, 0, "Child terminated incorrectly"); | 
 |   status = WEXITSTATUS (status); | 
 |  | 
 |   if (status == 0) | 
 |     { | 
 |       /* Test whether the child wrote the right data.  First test the | 
 | 	 position.  It must be the same as in the child.  */ | 
 |       if (lseek (fd, 0, SEEK_CUR) != strlen (testdata2)) | 
 | 	error (EXIT_FAILURE, 0, "file position not changed"); | 
 |  | 
 |       if (lseek (fd, 0, SEEK_SET) != 0) | 
 | 	error (EXIT_FAILURE, errno, "cannot reset file position"); | 
 |  | 
 |       if (read (fd, buf, sizeof buf) != strlen (testdata2)) | 
 | 	error (EXIT_FAILURE, errno, "cannot read new data"); | 
 |  | 
 |       if (memcmp (buf, testdata2, strlen (testdata2)) != 0) | 
 | 	error (EXIT_FAILURE, 0, "new data not read correctly"); | 
 |     } | 
 |  | 
 |   return status; | 
 | } |