Release 10.1A: OpenEdge Development:
Programming Interfaces

> ## UNIX named pipe examples

> The following examples show different uses of named pipes. To provide a simple example of how named pipes operate, the first example shows how to use the shell to create a named pipe, send a message to it, and read the message back. The second example shows how to use named pipes with Progress.

> ##### Example 1 — creating and using a named pipe from the shell

> In this example, the `cat` command sets up a message handler routine and the `echo` command acts as the requestor:

> i-pipex1
>     # Named Pipe Example 1.
>     #
>     # Create named pipe...
>     mknod trypipe p
>     # Open named pipe and read message...
>     cat trypipe &
>     # Write message...
>     echo "This is a message!" > trypipe
>     # Delete pipe...
>     rm trypipe

> To try this example, run the shell script, `i-pipex1`.

> This script performs the following actions:

> 1. The `mknod` command creates a named pipe called `trypipe`.

> 2. The `cat` command opens `trypipe` for reading. It blocks because `trypipe` has not yet been opened for writing. Notice that an ampersand (`&`) is present at the end of the `cat` command; this runs the `cat` command as a background process.

> 3. The `echo` command opens `trypipe` for writing and writes a message. The `cat` command, blocked until now, resumes execution, and the message appears on the display.

> 4. The `rm` command deletes `trypipe`.

> ##### Example 2 — using a named pipe with Progress

> Before working with the following procedures, create a copy of the `demo` database with the PRODB utility:

>     prodb demo demo

> This example shows a simple user program that sends one line requests to a Progress message handler routine running in the background, and displays the results. The example consists of four files:

> - A script called `i-pipex2` that runs the example.

> - The message handler procedure, `i-pipex2.p`.

> - A subprocedure, `i-do-sql.p`.

> - A C source file called `i-asksql.c` that implements the requestor.

> A display of these files follows:

> i-pipex2
>     # Named Pipe Example 2.
>     #
>     # Create named pipes...
>     mknod inpipe p
>     mknod outpipe p
>     # Start Progress background session with i-pipex2.p running...
>     bpro demo -1 -p i-pipex2.p
>     # Run executable i-asksql...
>     i-asksql
>     # Terminate Progress background session...
>     echo "outpipe \"quit\"" > inpipe
>     cat outpipe
>     # Delete named pipes...
>     rm inpipe
>     rm outpipe

> i-pipex2.p
>     DEF VAR sql-stmt AS CHAR FORMAT "x(220)".  /* Target variable for the request*/
>     DEF VAR out-pipe AS CHAR FORMAT "x(32)".  /* Holds the output file or FIFO */

>     REPEAT:                /* Do forever: */
>       INPUT FROM inpipe NO-ECHO.  /* Set up to read from in-FIFO
>                              named "inpipe". */
>        REPEAT:              /* For each request received: */
>         IMPORT out-pipe sql-stmt.  /* Get the output name
>                              and the request. */
>         OUTPUT TO VALUE(out-pipe) APPEND.  /* Set up to write results. */
>         RUN i-do-sql.p sql-stmt.  /* Pass SQL request to sub-proc. */
>         OUTPUT CLOSE.
>       END.                  /* This loop ends when the in-FIFO
>                              is empty. Just reopen it and */
>     END.                    /*  wait for the next request. */

> i-do-sql.p
>     /* This program consists of a single line of code. */

>     {1}

> i-asksql.c
>     #include <stdio.h>
>     #include <fcntl.h>

>     main()

>     {
>     #define  LEN     250
>             char   result[LEN];
>             int     fdi, fdo, nread;
>             char   request[LEN+8]; /* 8 for "outpipe " + punctuation */
>             char   *ptr;
>             int     validq, i;

>             fdi = open("inpipe", O_WRONLY);
>             if (fdi < 0)
>             { printf("Error on inpipe open\n"); exit(1);}

>             strcpy(request, "outpipe \""); /* request starts with ’outpipe "’ */

>             while (1)
>             {
>               printf("\n\nEnter your request (type [RETURN] to exit):\n");
>               ptr = request+9;
>               nread = read(0, ptr, LEN);
>               if (nread < 2)
>                   exit(0);
>               else
>               {
>                 validq = 1;             /* valid query? */
>                 for (i = 9; i<nread+9; i++)
>                     if (request[i] == ’\"’)
>                     { printf("Use only single quotes in queries.\n");
>                       validq = 0;
>                       break;
>                     }
>                 if (! validq) continue;
>                 ptr += nread-1;
>                 *ptr++ = ’\"’;
>                 *ptr++ = ’\n’;
>                 *ptr++ = ’\0’;
>                 write(fdi, request, strlen(request));
>                 sleep(1);
>                 fdo = open("outpipe", O_RDONLY);
>                 if (fdo < 0)
>                 { printf("Error on outpipe open\n"); exit(1);}

>                 while ((nread = read(fdo, result, LEN)) != 0)
>                 {
>                   result[nread] = ’\0’;
>                   printf("%s", result);
>                 }
>                 close(fdo);
>               }
>             }
>     }

> To prepare and run the example, follow these steps:

> 1. Use `cc` to compile and link the requestor source `i-asksql.c` to produce the executable `i-asksql`, as shown:

>     cc i-asksql.c -o i-asksql

> 2. Execute the `i-pipex2` script to run the example:

>     i-pipex2

> The `i-pipex2` script performs the following actions:

> 1. It uses `mknod` to create two named pipes: `inpipe` and `outpipe`. Named pipe `inpipe` carries requests from the requestor to the message handler routine. Named pipe `outpipe` carries results back in the opposite direction, from the message handler to the requestor.

> Figure 11–3 illustrates this process.

> Figure 11–3: Named pipes and Progress

> 2. It starts a background OpenEdge session that runs the message handler procedure, `i-pipex2.p`, with the demo database. The following line in `i-pipex2.p` opens the named pipe `inpipe` for input:

>     INPUT FROM inpipe NO-ECHO.

> Notice that Progress accesses named pipes in exactly the same way as UNIX text files. At this point, `i-pipex2.p` blocks until a requestor process opens `inpipe` for output.

> 3. After starting the Progress message handler, the script starts the requestor, `i-asksql`, which opens `inpipe` for output using the following statements:

>     int fdi, fdo, nread;
>                 .
>                 .
>                 .
>     fdi = open("inpipe", O_WRONLY);

> 4. As `i-asksql` opens `inpipe`, `i-pipex2.p` unblocks and blocks again as it attempts to read a message from `inpipe`. The message handler procedure, `i-pipex2.p`, expects single-line requests from requestors, and can handle more than one requestor. This is the syntax for message handler requests:

> Syntax
>     output-pipe-name SQL-statement

> Each request contains the name of the named (`output-named-pipe`) pipe from which the requestor expects to receive results and an SQL statement (`SQL-statement`) surrounded by double quotes (" "). The message handler procedure reads these messages with the following statement:

>     IMPORT out-pipe sql-stmt.

> Each requestor must specify a unique value for `output-pipe-name`, or results might be intermixed. (Using the requestor’s PID number as part of the name ensures uniqueness. However, for that to work, the requestor probably has to create its own named pipe using the `mknod()` system call.) Note that for this example, the requestor, `i-asksql`, uses the existing named pipe `outpipe` created by the script.

> 5. As the message handler waits for input, the requestor displays the following prompt:

>     Enter your request (type [RETURN] to exit):

> You can enter a one-line SQL query like the following `SELECT` from the demo database:

>     SELECT name FROM customer.

> 6. The requestor constructs a message from the name of the output pipe (`outpipe`, in the example) and the contents of your query, and writes the message to `inpipe`, as in the following statements from `i-asksql.c`:

>     char request[LEN+8]; /* 8 for "outpipe " + punctuation */
>                     .
>                     .
>                     .
>     write(fdi, request, strlen(request));

> 7. As the message handler receives (and removes) the message from `inpipe`, it unblocks and opens the output pipe named in the message with the following statement:

>     OUTPUT TO VALUE(out-pipe).

> 8. The message handler blocks again, waiting for the requestor to open the same pipe for input (to receive the query result), as in the following statements from `i-asksql.c`:

>     int fdi, fdo, nread;
>                 .
>                 .
>                 .
>     fdo = open("outpipe", O_RDONLY);

> 9. The Progress message handler then continues to compile and run the SQL query using the following statement:

>     RUN i-do-sql.p sql-stmt.

> As the query generates output, Progress writes it one line at a time to the named pipe specified by `outpipe`. The requestor reads each line as it is written to the output pipe, as in the following statements from `i-asksql`. In the example, the requestor also writes each line to its standard output:

>     char result[LEN];
>     int fdi, fdo, nread;int fdi, fdo, nread;
>                     .
>                     .
>                     .
>     while ((nread = read(fdo, result, LEN)) != 0)
>     {
>         result[nread] = ’\0’;
>         printf("%s", result);
>     }

> Note: If there is no output, you might have entered your SQL statement incorrectly. This causes `i-pipex2.p` to terminate. To trap this type of error, write the SQL statement to a file instead of to a named pipe, then compile the file. If the compilation is successful, run it.

> Note that although the query procedure, `i-do-sql.p`, contains only the single procedure parameter, `{1}`, you can extend it with formatting statements to avoid having to include these in each query, as in the following examples:

>     {1} WITH NO-LABELS.

>     {1} WITH EXPORT.

> 10. The example requestor, `i-asksql`, continues to prompt for queries, repeating Actions 5 through 9, until you press RETURN with no additional input.

> 11. After the requestor terminates, the `i-pipex2` script terminates the Progress background process with the following commands:

>     echo "outpipe \"quit\"" > inpipe
>     cat outpipe

> The first command sends the Progress `QUIT` statement to the message handler (instead of an SQL statement). The second command takes the place of Action 8, originally handled by the requestor. The requestor does not send the `QUIT` to terminate the Progress background process so that multiple copies of the requestor — each with its own output pipe — can run without affecting the message handler. It is necessary because a process blocks until a named pipe it opens for writing is opened for reading (see the "Operational characteristics of named pipes" section). In this case, the message handler opens named pipe `outpipe` for writing, and cannot execute `QUIT` until the `cat` command opens `outpipe` for reading.

> 12. The, `i-pipex2` script uses the `rm` command to remove the named pipes that it created.

Copyright © 2005 Progress Software Corporation www.progress.com Voice: (781) 280-4000 Fax: (781) 280-4095