Example 2: using a named pipe with ABL
- Last Updated: January 17, 2024
- 4 minute read
- OpenEdge
- Version 12.8
- Documentation
Before working with the following procedures,
create a copy of the demo database with the PRODB
utility:
|
This example shows a simple user program that sends one line requests to an ABL message handler routine running in the background, and displays the results. The example consists of four files:
- A script called
i-pipex2that runs the example:I-pipex2
# Named Pipe Example 2. # # Create named pipes... mknod inpipe p mknod outpipe p # Start OpenEdge background session with i-pipex2.p running... bpro demo -1 -p i-pipex2.p # Run executable i-asksql... i-asksql # Terminate OpenEdge background session... echo "outpipe \"quit\"" > inpipe cat outpipe # Delete named pipes... rm inpipe rm outpipe - The message handler procedure,
i-pipex2.p:
i-pipex2.p
/* Target variable for the request:*/ DEFINE VARIABLE sql-stmt AS CHARACTER NO-UNDO FORMAT "x(220)". /* Holds the output file or FIFO: */ DEFINE VARIABLE out-pipe AS CHARACTER NO-UNDO FORMAT "x(32)". /* Do forever: */ REPEAT: /* Set up to read from in-FIFO named "inpipe". */ INPUT FROM inpipe NO-ECHO. /* For each request received: */ REPEAT: /* Get the output name and the request. */ IMPORT out-pipe sql-stmt. /* Set up to write results. */ OUTPUT TO VALUE(out-pipe) APPEND. /* Pass SQL request to sub-proc. */ RUN i-do-sql.p sql-stmt. OUTPUT CLOSE. /* This loop ends when the in-FIFO is empty. Just reopen it and wait for the next request. */ END. END. - A subprocedure, i-do-sql.p:
i-do-sql.p
/* This program consists of a single line of code. */ {1} - A C source file called i-asksql.c that implements the
requestor:
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:
- Use
ccto compile and link the requestor source i-asksql.c to produce the executablei-asksql, as shown:cc i-asksql.c -o i-asksql - Execute the
i-pipex2script to run the example:i-pipex2
The i-pipex2 script
performs the following actions:
- It uses
mknodto create two named pipes:inpipeandoutpipe. Named pipeinpipecarries requests from the requestor to the message handler routine. Named pipeoutpipecarries results back in the opposite direction, from the message handler to the requestor. The following figure illustrates this process.Figure 1. Named pipes and ABL 
- 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
inpipefor input:INPUT FROM inpipe NO-ECHO.Notice that ABL accesses named pipes in exactly the same way as UNIX text files. At this point, i-pipex2.p blocks until a requestor process opens
inpipefor output. - After starting the ABL message handler, the script starts the requestor,
i-asksql, which opensinpipefor output using the following statements:int fdi, fdo, nread; . . . fdi = open("inpipe", O_WRONLY); - As
i-asksqlopensinpipe, i-pipex2.p unblocks and blocks again as it attempts to read a message frominpipe. 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-nameSQL-statementEach 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 pipeoutpipecreated by the script. - 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
SELECTfrom the demo database:SELECT name FROM customer. - 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 toinpipe, as in the following statements from i-asksql.c:char request[LEN+8]; /* 8 for "outpipe " + punctuation */ . . . write(fdi, request, strlen(request)); - 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). - 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); - The ABL 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, ABL 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 fromi-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. - The example requestor,
i-asksql, continues to prompt for queries, repeating Actions 5 through 9, until you press RETURN with no additional input. - After the requestor terminates, the
i-pipex2script terminates the ABL background process with the following commands:echo "outpipe \"quit\"" > inpipe cat outpipeThe first command sends the ABL
QUITstatement 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 theQUITto terminate the ABL 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 Operational characteristics of named pipes). In this case, the message handler opens named pipeoutpipefor writing, and cannot executeQUITuntil thecatcommand opensoutpipefor reading. - The
i-pipex2script uses thermcommand to remove the named pipes that it created.