Using named pipes

If the z/OS® UNIX XL C/C++ application program you are developing requires its active processes to communicate with other processes that are active but may not be from the same program, code your application program to create a named pipe (FIFO file). Named pipes allow transfer of data between processes in a FIFO manner and synchronization of process execution. Use of a named pipe allows processes to communicate even though they do not know what processes are on the other end of the pipe. Named pipes differ from standard unnamed pipes, created using the pipe() function, in that they involve the creation of a real file that is available for I/O operations to properly authorized processes.

Within the application program, you create a named pipe by coding a mkfifo() or mknod() function. You give the FIFO a name and an access mode when you create it. If the access mode allows all users read and write access to the named pipe, any process that knows its name can use it to send or receive data.

Processes can use the open() function to access named pipes and then use the regular I/O functions for files, such as read(), write(), and close(), when manipulating named pipes. Buffered I/O functions can also be used to access and manipulate named pipes. For more information on the mkfifo() and mknod() functions and the file I/O functions, see z/OS XL C/C++ Runtime Library Reference.

Restriction: If fopen() is used to open named pipes in a multi-threaded environment, a deadlock will occur. This deadlock is caused by a named pipe waiting for the other end of the pipe to be opened, while still holding the fopen() multi-thread mutex. To prevent this deadlock, use open() to open the named pipe, instead of fopen().

z/OS UNIX does security checks on named pipes.

The following steps outline how to use a named pipe from z/OS UNIX XL C/C++ application programs:

  1. Create a named pipe using the mkfifo() function. Only one of the processes that use the named pipe needs to do this.
  2. Access the named pipe using the appropriate I/O method.
  3. Communicate through the pipe with another process using file I/O functions:
    1. Write data to the named pipe.
    2. Read data from the named pipe.
  4. Close the named pipe.
  5. If the process created the named pipe and the named pipe is no longer needed, remove that named pipe using the unlink() function.

A process running the following simple example program creates a new named pipe with the file pathname pointed to by the path value coded in the mkfifo() function. The access mode of the new named pipe is initialized from the mode value coded in the mkfifo() function. The file permission bits of the mode argument are modified by the process file creation mask.

As an example, a process running the program code (CCNGHF2) in Figure 1 creates a child process and then creates a named pipe called fifo.test. The child process then writes a data string to the pipe file. The parent process reads from the pipe file and verifies that the data string it reads is the expected one.
Note: The two processes are related and have agreed to communicate through the named pipe. They need not be related, however. Other authorized users can run the same program and participate in (or interfere with) the process communication.
Figure 1. Named pipes example
/* this example shows how named pipes may be used */
 #define _OPEN_SYS
 #include <stdio.h>
 #include <unistd.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <sys/wait.h>
/*                                                                *
 *   Sample use of mkfifo()                                       *
 *                                                                */

main()

{                                    /* start of program          */

 int     flags, ret_value, c_status;
 pid_t   pid;
 size_t  n_elements;
 char    char_ptr[32];
 char    str[] = "string for fifo ";
 char    fifoname[] = "temp.fifo";
 FILE    *rd_stream,*wr_stream;

 if ((mkfifo(fifoname,S_IRWXU)) != 0) {
   printf("Unable to create a fifo; errno=%d\n",errno);
   exit(1);                     /* Print error message and return */
 }

 if ((pid = fork()) < 0) {
    perror("fork failed");
    exit(2);
 }

 if (pid == (pid_t)0) {        /* CHILD process                  */
    /* issue fopen for write end of the fifo                     */
        wr_stream = fopen(fifoname,"w");
           if (wr_stream == (FILE *) NULL)  {
              printf("In child process\n");
              printf("fopen returned a NULL, expected valid stream\n");
              exit(100);
           }

        /* perform a write                                        */
        n_elements = fwrite(str,1,strlen(str),wr_stream);
           if (n_elements != (size_t) strlen(str)) {
             printf("Fwrite returned %d, expected %d\n",
                (int)n_elements,strlen(str));
             exit(101);
            }
      exit(0);                 /* return success to parent        */
    }

    else  {                    /* PARENT  process                 */

        /* issue fopen for read                                   */
        rd_stream = fopen(fifoname,"r");
        if (rd_stream == (FILE *) NULL)  {
           printf("In parent process\n");
           printf("fopen returned a NULL, expected valid pointer\n");
           exit(2);
        }
        /* get current flag settings of file                      */
        if ((flags = fcntl(fileno(rd_stream),F_GETFL)) == -1) {
           printf("fcntl returned -1 for %s\n",fifoname);
           exit(3);
        }

        /* clear O_NONBLOCK  and reset file flags                 */
        flags &= (O_NONBLOCK);
        if ((fcntl(fileno(rd_stream),F_SETFL,flags)) == -1) {
           printf("\nfcntl returned -1 for %s",fifoname);
           exit(4);
        }

        /* try to read the string                              */
        ret_value = fread(char_ptr,sizeof(char),strlen(str),rd_stream);
        if (ret_value != strlen(str)) {
           printf("\nFread did not read %d elements as expected ",
              strlen(str));
           printf("\nret_value is %d ",ret_value);
           exit(6);
        }

        if (strncmp(char_ptr,str,strlen(str))) {
           printf("\ncontents of char_ptr are %s ",
              char_ptr);
           printf("\ncontents of str are %s ",
              str);
           printf("\nThese should be equal");
           exit(7);
        }

        ret_value = fclose(rd_stream);
        if (ret_value != 0)  {
            printf("\nFclose failed for %s",fifoname);
            printf("\nerrno is %d",errno);
            exit(8);
        }
        ret_value = remove(fifoname);
        if (ret_value != 0)  {
            printf("\nremove failed for %s",fifoname);
            printf("\nerrno is %d",errno);
            exit(9);
        }

      pid = wait(c_status);
      if ((WIFEXITED(c_status) !=0) &&; (WEXITSTATUS(c_status) !=0)) {
        printf("\nchild exited with code %d",WEXITSTATUS(c_status));
        exit(10);
      }
    }   /* end of else clause                                       */
    printf("About to issue exit(0),  \
            processing completed successfully\n");
    exit(0);
}