Example 20-4. The ntfy_pipe.c program (continued)
if (!*++argv)
puts("specify a program [w/args]"), exit(1);
pipe(pipe_io[0 ]); /* set up input pipe */
pipe(pipe_io[1 ]); /* set up output pipe */
switch (pid = fork()) {
case -1:
close(pipe_io[0][0]);
close(pipe_io[0][1]);
close(pipe_io[1][0]);
close(pipe_io[1][1]);
perror("fork failed");
exit(1);
case 0: /* child */
/* redirect child’s stdin (0), stdout (1) and stderr(2) */
dup2(pipe_io[0 ][0 ], 0);
dup2(pipe_io[1 ][1 ], 1);
dup2(pipe_io[1 ][1 ], 2);
for (i = getdtablesize(); i > 2; i--)
(void) close(i);
for (i = 0; i < NSIG; i++)
(void) signal(i, SIG_DFL);
execvp(*argv, argv);
if (errno == ENOENT)
printf("%s: command not found.0, *argv);
else
perror(*argv);
perror("execvp");
_exit(-1);
default: /* parent */
close(pipe_io[0][0]); /* close unused portions of pipes */
close(pipe_io[1][1]);
}
/* when the process outputs data, read it */
notify_set_input_func(client1, read_it, pipe_io[1 ][0]);
notify_set_wait3_func(client1, sigchldcatcher, pid);
/* wait for user input -- then write data to pipe */
notify_set_input_func(client2, write_it, 0);
notify_set_wait3_func(client2, sigchldcatcher, pid);
notify_start();
}
/*
* callback routine for when there is data on the parent’s stdin to
* read. Read it and then write the data to the child process via
* the pipe.
*/
Notify_value
write_it(client, fd)
Notify_client client;
int fd;
{
484 XView Programming Manual
Example 20-4. The ntfy_pipe.c program (continued)
char buf[BUFSIZ];
int bytes, i;
/* only write to pipe (child’s stdin) if user typed anything */
if (ioctl(fd, FIONREAD, &bytes) == -1 || bytes == 0) {
notify_set_input_func(client, NOTIFY_FUNC_NULL, pipe_io[0 ][1 ]);
close(pipe_io[0][1]);
} else
while (bytes > 0) {
if ((i = read(fd, buf, sizeof buf)) > 0) {
printf("[Sending %d bytes to pipe (fd=%d)]0,
i, pipe_io[0 ][1 ]);
write(pipe_io[0][1], buf, i);
} else if (i == -1)
break;
bytes -= i;
}
return NOTIFY_DONE;
}
/*
* callback routine for when there is data on the child’s stdout to
* read. Read, then write the data to stdout (owned by the parent).
*/
Notify_value
read_it(client, fd)
Notify_client client;
register int fd;
{
char buf[BUFSIZ];
int bytes, i;
if (ioctl(fd, FIONREAD, &bytes) == 0)
while (bytes > 0) {
if ((i = read(fd, buf, sizeof buf)) > 0) {
printf("[Reading %d bytes from pipe (fd=%d)]0,
i, fd);
(void) write(1, buf, i);
bytes -= i;
}
}
return NOTIFY_DONE;
}
/*
* handle the death of the child. If the process dies, the child
* dies and generates a SIGCHLD signal. Capture it and disable the
* functions that talk to the pipes.
*/
Notify_value
sigchldcatcher(client, pid, status, rusage)
Notify_client client; /* the client noted in main() */
int pid; /* the pid that died */
union wait *status; /* the status of the process (unused here) */
struct rusage *rusage; /* resources used by this process (unused) */
{
Notifier
The Notifier 485
Example 20-4. The ntfy_pipe.c program (continued)
if (WIFEXITED(*status)) {
printf("Process termined with status %d0, status–>w_retcode);
/* unregister input func with appropriate file descriptor */
notify_set_input_func(client, NOTIFY_FUNC_NULL,
(client == client1)? pipe_io[1][0] : 0);
return NOTIFY_DONE;
}
puts("SIGCHLD not handled");
return NOTIFY_IGNORED;
}
Assuming that the program was run with cat (1) as described earlier, input to the child pro-
cess comes from the function write_it(). That is, write_it() is the function that
writes to the other side of the pipe that the child is reading from. This function was set up as
the input_func for the parent’s stdin. The function gets its input from whatever you type in
the window in which you ran the program. It could have gotten its input from a text panel
item or a selection using the selection service. It reads whatever data is sent through the pipe
and ends up as the stdin for the child process.
Similarly, when the child process writes anything to its stdout, the data is redirected
through the pipe. The parent reads from the other end of the pipe in read_it(). Whatever
the parent reads is written to the parent’s stdout, which is the user’s tty window. It does
this by calling write(1, buf, i). This line of the program can easily be replaced by:
textsw_insert(textsw, buf, i);
This shows how you can redirect the output (including stderr output) from a child process
to a text subwindow.
The last thing to note about the program is its treatment of extraneous file descriptors and of
signals handled by the child process after forking. When using fork(), the child process
inherits all the file descriptors and signal handling set up by the parent.* This could seriously
affect your program if the child process gets certain signals. In this case the best thing for the
child to do is loop through all the signals that are dealt with by the parent and reset them to
SIG_DFL using signal (3). Then it can close all file descriptors except for the child’s
stdin (0), stdout (1), and stderr (2) descriptors.
We use the signal() system call here because it is assumed that the child is not going to
execute any of the XView code in the parent’s program. Therefore, the child does not need
to use the notify_set_signal_func() routine to unregister the signals. Releasing
these signals in this way completely disassociates the program from the parent.
This very general program is used as an example, but it is useful in all applications that need
to run external processes. The only modifications it needs are alternate sources for the input
and output of the parent. main() could be replaced by a general function that simply gets
an argv parameter containing the program to execute, including arguments.
*Unless the file descriptors were set to close on exec using ioctl(fd, FIOCLEX, 0).
486 XView Programming Manual
The program could be modified to be used as a replacement for the system (3) call. Even if
you do not expect to read the child’s stdout or send data to its stdin, the child process still
needs to be handled differently from system (3) because that function calls wait() and
attempts to do its own signal handling.
20.8.3 Exception Occurred Events
Exception occurred notifications are similar to input pending notifications. The only known
devices that generate exceptions are stream-based socket connections when an out-of-band
byte is available. Thus a SIGURG signal catcher is set up by the Notifier, much like a SIGIO
for asynchronous input.
Notify_func
notify_set_exception_func(nclient, func, fd)
Notify_client nclient;
Notify_func func;
int fd;
20.8.4 Getting an Event Handler
This section contains a list of the notify_get_*_func() routines. These functions
allow you to retrieve the value of a client’s event handler. Once you have the value of a
client’s event handler, you could modify or replace the event handler. It is recommended that
you use the Notifier’s interposition mechanism instead of using these functions. The argu-
ments for these functions parallel the associated notify_set_*_func() functions
described previously, except for the absence of the event handler function pointer. Refer to
the XView Reference Manual for a description of the arguments.
For all of the notify_get_*_func() functions in the following list, a return value of
NOTIFY_FUNC_NULL indicates an error. If the client is unknown, then notify_errno is
set to NOTIFY_UKNOWN_CLIENT. If no event handler is registered for the specified event,
then notify_errno is set to NOTIFY_NO_CONDITION. Other values on notify_errno
are possible, depending on the event; for example, NOTIFY_BAD_FD if an invalid file descrip-
tor is specified.
Here is a list of the event handler retrieval routines:
• notify_get_input_func(client, fd)
• notify_get_event_func(client, when)
• notify_get_output_func(client, fd)
• notify_get_exception_func(client, fd)
• notify_get_signal_func(client, which)
• notify_get_itimer_func(client, signal, mode)
Notifier
The Notifier 487
• notify_get_wait3_func(client, pid)
• notify_get_destroy_func(client)
20.9 Interposition
The Notifier provides a mechanism called interposition, with which you can intercept control
of the internal communications within XView. Interposition is a powerful way to both moni-
tor and modify window behavior in ways that extend the functionality of a window object.
Interposition allows a client to intercept an event before it reaches the base event handler.
The base event handler is the one set originally by a client. The client can call the base event
handler before and/or after its own handling of the event or not at all. This allows the appli-
cation to override the handling provided by XView’s base event handler, or to add special
event handling. The notifier supports interposition by keeping track of how interposition
functions are ordered for each type of event for each client.
There may be more than one interposer for a Notifier client. As each interposer is added, it is
inserted ahead of the last interposer installed. Interposes may also be removed from the
interposer list. When an event arrives, the Notifier calls the function at the top of the inter-
poser list for that client.
Keeping track of which function to call when running down the list of interposers is done by
maintaining an interposition stack. The number of interpositions allowed on the stack is lim-
ited to a maximum size (about six levels deep). Since the base event handler is placed on the
interposition stack, there are five usable levels, although few applications should require
more than two levels of interposition. Figure 20-2 illustrates the flow of control with interpo-
sition. Note that the interposer could have stopped the flow of control to the next event han-
dler.
Event
Dispatched
Notifier
Interposer Base Event Handler
Figure 20-2. Flow of control in interposition
488 XView Programming Manual
Get Volume 7A: XView Programming Manual now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.