* new version 2.20.3
[alpine.git] / pith / osdep / pipe.c
blobed9eda0e77c2e2d971151c8bbe93c18c3b486859
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: pipe.c 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2006-2008 University of Washington
8 * Copyright 2013-2015 Eduardo Chappa
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 #include <system.h>
21 #include "err_desc.h"
23 #include "canaccess.h"
24 #include "temp_nam.h"
25 #include "forkwait.h"
26 #include "pipe.h"
27 #include "../charconv/utf8.h"
28 #include "../charconv/filesys.h"
29 #include "../debug.h"
31 #ifdef _WINDOWS
32 #include "../../pico/osdep/mswin.h"
33 #endif
37 /*======================================================================
38 pipe
40 Initiate I/O to and from a process. These functions used to be
41 similar to popen and pclose, but both an incoming stream and an
42 output file are provided.
44 ====*/
49 * Global's to helpsignal handler tell us child's status has changed...
51 static pid_t child_pid;
55 * Internal Protos
57 void zot_pipe(PIPE_S **);
58 #ifdef _WINDOWS
59 int pipe_mswin_exec_wrapper(char *, PIPE_S *, unsigned,
60 void (*)(PIPE_S *, int, void *),
61 void (*)(char *));
62 #else /* UNIX */
63 char *pipe_error_msg(char *, char *, char *);
64 RETSIGTYPE pipe_alarm(int);
65 #endif /* UNIX */
70 /*----------------------------------------------------------------------
71 Spawn a child process and optionally connect read/write pipes to it
73 Args: command -- string to hand the shell
74 outfile -- address of pointer containing file to receive output
75 errfile -- address of pointer containing file to receive error output
76 mode -- mode for type of shell, signal protection etc...
77 Returns: pointer to alloc'd PIPE_S on success, NULL otherwise
79 The outfile is either NULL, a pointer to a NULL value, or a pointer
80 to the requested name for the output file. In the pointer-to-NULL case
81 the caller doesn't care about the name, but wants to see the pipe's
82 results so we make one up. It's up to the caller to make sure the
83 free storage containing the name is cleaned up.
85 Mode bits serve several purposes.
86 PIPE_WRITE tells us we need to open a pipe to write the child's
87 stdin.
88 PIPE_READ tells us we need to open a pipe to read from the child's
89 stdout/stderr. *NOTE* Having neither of the above set means
90 we're not setting up any pipes, just forking the child and exec'ing
91 the command. Also, this takes precedence over any named outfile.
92 PIPE_STDERR means we're to tie the childs stderr to the same place
93 stdout is going. *NOTE* This only makes sense then if PIPE_READ
94 or an outfile is provided. Also, this takes precedence over any
95 named errfile.
96 PIPE_RESET means we reset the terminal mode to what it was before
97 we started pine and then exec the command. In PC-Pine, _RESET
98 was a shortcut for just executing a command. We'll try to pay
99 attention to the above flags to make sure we do the right thing.
100 PIPE_PROT means to protect the child from the usual nasty signals
101 that might cause premature death. Otherwise, the default signals are
102 set so the child can deal with the nasty signals in its own way.
103 NOT USED UNDER WINDOWS
104 PIPE_NOSHELL means we're to exec the command without the aid of
105 a system shell. *NOTE* This negates the affect of PIPE_USER.
106 NOT USED UNDER WINDOWS
107 PIPE_USER means we're to try executing the command in the user's
108 shell. Right now we only look in the environment, but that may get
109 more sophisticated later.
110 NOT USED UNDER WINDOWS
111 PIPE_RUNNOW was added for WINDOWS for the case pipe is called to run
112 a shell program (like for url viewing). This is the only option
113 where we don't wait for child termination, and is only obeyed if
114 PIPE_WRITE and PIPE_READ aren't set
115 ----*/
116 PIPE_S *
117 open_system_pipe(char *command, char **outfile, char **errfile, int mode,
118 int timeout, void (*pipecb_f)(PIPE_S *, int, void *),
119 void (*piperr_f)(char *))
121 PIPE_S *syspipe = NULL;
122 #ifdef _WINDOWS
123 int exit_code = 0;
124 char cmdbuf[1024];
125 unsigned flags = 0;
126 #else
127 char shellpath[MAXPATH+1], *shell;
128 int p[2], oparentd = -1, ochildd = -1, iparentd = -1, ichildd = -1;
129 #endif
131 #ifdef _WINDOWS
132 if(mode & PIPE_STDERR)
133 flags |= MSWIN_EAW_CAPT_STDERR;
135 * It'll be a lot more difficult to support READing and WRITing.
136 * This was never supported, and there don't look to be any cases
137 * that set both of these flags anymore for win32.
139 * errfile could probably be supported pretty easily
142 if(errfile){
143 if(piperr_f)
144 (*piperr_f)("Pipe arg not yet supported: Error File");
146 return(NULL);
150 if((mode & PIPE_RUNNOW)
151 && !(mode & (PIPE_WRITE | PIPE_READ | PIPE_STDERR))){
152 if(mswin_shell_exec(command, NULL) == 0
153 && (syspipe = (PIPE_S *) malloc(sizeof(PIPE_S))) != NULL){
154 memset(syspipe, 0, sizeof(PIPE_S));
155 return(syspipe);
158 return(NULL);
161 strncpy(cmdbuf, command, sizeof(cmdbuf));
162 cmdbuf[sizeof(cmdbuf)-1] = '\0';
164 if((syspipe = (PIPE_S *) malloc(sizeof(PIPE_S))) == NULL)
165 return(NULL);
167 memset(syspipe, 0, sizeof(PIPE_S));
168 syspipe->mode = mode;
169 if(!outfile){
170 syspipe->deloutfile = 1;
171 if(mode & PIPE_READ){
172 syspipe->outfile = temp_nam(NULL, "po");
173 our_unlink(syspipe->outfile);
176 else{
177 if(!*outfile) /* asked for, but not named? */
178 *outfile = temp_nam(NULL, "po");
180 our_unlink(*outfile);
181 syspipe->outfile = (char *) malloc((strlen(*outfile)+1)*sizeof(char));
182 snprintf(syspipe->outfile, strlen(*outfile)+1, "%s", *outfile);
185 if(mode & PIPE_WRITE){
187 * Create tmp file to write, spawn child in close_pipe
188 * after tmp file's written...
190 syspipe->infile = temp_nam(NULL, "pw");
191 syspipe->out.f = our_fopen(syspipe->infile, "wb");
192 syspipe->command = (char *) malloc((strlen(cmdbuf)+1)*sizeof(char));
193 snprintf(syspipe->command, strlen(cmdbuf)+1, "%s", cmdbuf);
194 dprint((1, "pipe write: %s", cmdbuf));
196 else if(mode & PIPE_READ){
198 * Create a tmp file for command result, exec the command
199 * here into temp file, and return file pointer to it...
201 syspipe->command = (char *) malloc((strlen(cmdbuf)+1)*sizeof(char));
202 snprintf(syspipe->command, strlen(cmdbuf)+1, "%s", cmdbuf);
203 dprint((1, "pipe read: %s", cmdbuf));
204 if(pipe_mswin_exec_wrapper("pipe command", syspipe,
205 flags, pipecb_f, piperr_f)){
206 if(syspipe->outfile){
207 free((void *) syspipe->outfile);
208 syspipe->outfile = NULL;
211 zot_pipe(&syspipe);
213 else{
214 syspipe->in.f = our_fopen(syspipe->outfile, "rb");
215 syspipe->exit_code = exit_code;
218 else{
219 /* we just run the command taking outfile into account */
220 syspipe->command = (char *) malloc((strlen(cmdbuf)+1)*sizeof(char));
221 snprintf(syspipe->command, strlen(cmdbuf)+1, "%s", cmdbuf);
222 if(pipe_mswin_exec_wrapper("pipe command", syspipe,
223 flags, pipecb_f, piperr_f)){
224 if(syspipe->outfile){
225 free((void *) syspipe->outfile);
226 syspipe->outfile = NULL;
229 zot_pipe(&syspipe);
231 else
232 syspipe->exit_code = exit_code;
235 #else /* !_WINDOWS */
237 if((syspipe = (PIPE_S *) malloc(sizeof(PIPE_S))) == NULL)
238 return(NULL);
240 memset(syspipe, 0, sizeof(PIPE_S));
242 syspipe->mode = mode;
245 * If we're not using the shell's command parsing smarts, build
246 * argv by hand...
248 if(mode & PIPE_NOSHELL){
249 char **ap, *p;
250 size_t n;
252 /* parse the arguments into argv */
253 for(p = command; *p && isspace((unsigned char)(*p)); p++)
254 ; /* swallow leading ws */
256 if(*p){
257 int l = strlen(p);
259 if((syspipe->args = (char *) malloc((l + 1) * sizeof(char))) != NULL){
260 strncpy(syspipe->args, p, l);
261 syspipe->args[l] = '\0';
263 else{
264 if(piperr_f)
265 (*piperr_f)(pipe_error_msg("<null>", "execute",
266 "Can't allocate command string"));
267 zot_pipe(&syspipe);
268 return(NULL);
271 else{
272 if(piperr_f)
273 (*piperr_f)(pipe_error_msg("<null>", "execute",
274 "No command name found"));
275 zot_pipe(&syspipe);
276 return(NULL);
279 for(p = syspipe->args, n = 2; *p; p++) /* count the args */
280 if(isspace((unsigned char)(*p))
281 && *(p+1) && !isspace((unsigned char)(*(p+1))))
282 n++;
284 if ((syspipe->argv = ap = (char **)malloc(n * sizeof(char *))) == NULL){
285 zot_pipe(&syspipe);
286 return(NULL);
289 memset(syspipe->argv, 0, n * sizeof(char *));
291 for(p = syspipe->args; *p; ){ /* collect args */
292 while(*p && isspace((unsigned char)(*p)))
293 *p++ = '\0';
295 *ap++ = (*p) ? p : NULL;
296 while(*p && !isspace((unsigned char)(*p)))
297 p++;
300 /* make sure argv[0] exists in $PATH */
301 if(can_access_in_path(getenv("PATH"), syspipe->argv[0],
302 EXECUTE_ACCESS) < 0){
303 if(piperr_f)
304 (*piperr_f)(pipe_error_msg(syspipe->argv[0], "access",
305 error_description(errno)));
306 zot_pipe(&syspipe);
307 return(NULL);
311 /* fill in any output filenames */
312 if(!(mode & PIPE_READ)){
313 if(outfile && !*outfile)
314 *outfile = temp_nam(NULL, "pine_p"); /* asked for, but not named? */
316 if(errfile && !*errfile)
317 *errfile = temp_nam(NULL, "pine_p"); /* ditto */
320 /* create pipes */
321 if(mode & (PIPE_WRITE | PIPE_READ)){
322 if(mode & PIPE_WRITE){
323 pipe(p); /* alloc pipe to write child */
324 oparentd = p[STDOUT_FILENO];
325 ichildd = p[STDIN_FILENO];
328 if(mode & PIPE_READ){
329 pipe(p); /* alloc pipe to read child */
330 iparentd = p[STDIN_FILENO];
331 ochildd = p[STDOUT_FILENO];
335 if(pipecb_f) /* let caller prep display */
336 (*pipecb_f)(syspipe, OSB_PRE_OPEN, NULL);
339 if((syspipe->pid = vfork()) == 0){
340 /* reset child's handlers in requested fashion... */
341 (void)signal(SIGINT, (mode & PIPE_PROT) ? SIG_IGN : SIG_DFL);
342 (void)signal(SIGQUIT, (mode & PIPE_PROT) ? SIG_IGN : SIG_DFL);
343 (void)signal(SIGHUP, (mode & PIPE_PROT) ? SIG_IGN : SIG_DFL);
344 #ifdef SIGCHLD
345 (void) signal(SIGCHLD, SIG_DFL);
346 #endif
348 /* if parent isn't reading, and we have a filename to write */
349 if(!(mode & PIPE_READ) && outfile){ /* connect output to file */
350 int output = our_creat(*outfile, 0600);
351 dup2(output, STDOUT_FILENO);
352 if(mode & PIPE_STDERR)
353 dup2(output, STDERR_FILENO);
354 else if(errfile)
355 dup2(our_creat(*errfile, 0600), STDERR_FILENO);
358 if(mode & PIPE_WRITE){ /* connect process input */
359 close(oparentd);
360 dup2(ichildd, STDIN_FILENO); /* tie stdin to pipe */
361 close(ichildd);
364 if(mode & PIPE_READ){ /* connect process output */
365 close(iparentd);
366 dup2(ochildd, STDOUT_FILENO); /* tie std{out,err} to pipe */
367 if(mode & PIPE_STDERR)
368 dup2(ochildd, STDERR_FILENO);
369 else if(errfile)
370 dup2(our_creat(*errfile, 0600), STDERR_FILENO);
372 close(ochildd);
375 if(mode & PIPE_NOSHELL){
376 execvp(syspipe->argv[0], syspipe->argv);
378 else{
379 if(mode & PIPE_USER){
380 char *env, *sh;
381 if((env = getenv("SHELL")) && (sh = strrchr(env, '/'))){
382 shell = sh + 1;
383 strncpy(shellpath, env, sizeof(shellpath)-1);
384 shellpath[sizeof(shellpath)-1] = '\0';
386 else{
387 shell = "csh";
388 strncpy(shellpath, "/bin/csh", sizeof(shellpath)-1);
389 shellpath[sizeof(shellpath)-1] = '\0';
392 else{
393 shell = "sh";
394 strncpy(shellpath, "/bin/sh", sizeof(shellpath)-1);
395 shellpath[sizeof(shellpath)-1] = '\0';
398 execl(shellpath, shell, command ? "-c" : (char *)NULL, fname_to_locale(command), (char *)NULL);
401 fprintf(stderr, "Can't exec %s\nReason: %s",
402 command, error_description(errno));
403 _exit(-1);
406 if((child_pid = syspipe->pid) > 0){
407 syspipe->isig = signal(SIGINT, SIG_IGN); /* Reset handlers to make */
408 syspipe->qsig = signal(SIGQUIT, SIG_IGN); /* sure we don't come to */
409 syspipe->hsig = signal(SIGHUP, SIG_IGN); /* a premature end... */
410 if((syspipe->timeout = timeout) != 0){
411 syspipe->alrm = signal(SIGALRM, pipe_alarm);
412 syspipe->old_timeo = alarm(timeout);
415 if(mode & PIPE_WRITE){
416 close(ichildd);
417 if(mode & PIPE_DESC)
418 syspipe->out.d = oparentd;
419 else
420 syspipe->out.f = fdopen(oparentd, "w");
423 if(mode & PIPE_READ){
424 close(ochildd);
425 if(mode & PIPE_DESC)
426 syspipe->in.d = iparentd;
427 else
428 syspipe->in.f = fdopen(iparentd, "r");
431 else{
432 if(mode & (PIPE_WRITE | PIPE_READ)){
433 if(mode & PIPE_WRITE){
434 close(oparentd);
435 close(ichildd);
438 if(mode & PIPE_READ){
439 close(iparentd);
440 close(ochildd);
444 if(pipecb_f) /* let caller fixup display */
445 (*pipecb_f)(syspipe, OSB_POST_OPEN, NULL);
447 if(outfile && *outfile){
448 our_unlink(*outfile);
449 free((void *) *outfile);
450 *outfile = NULL;
453 if(errfile && *errfile){
454 our_unlink(*errfile);
455 free((void *) *errfile);
456 *errfile = NULL;
459 if(piperr_f)
460 (*piperr_f)(pipe_error_msg(command, "fork",
461 error_description(errno)));
462 zot_pipe(&syspipe);
465 #endif /* UNIX */
467 return(syspipe);
472 #ifndef _WINDOWS
473 /*----------------------------------------------------------------------
474 Return appropriate error message
476 Args: cmd -- command we were trying to exec
477 op -- operation leading up to the exec
478 res -- result of that operation
480 ----*/
481 char *
482 pipe_error_msg(char *cmd, char *op, char *res)
484 static char ebuf[512];
486 snprintf(ebuf, 256, "Pipe can't %.256s \"%.32sb\": %.223s",
487 op ? op : "?", cmd ? cmd : "?", res ? res : "?");
489 return(ebuf);
491 #endif /* !_WINDOWS */
494 /*----------------------------------------------------------------------
495 Free resources associated with the given pipe struct
497 Args: syspipe -- address of pointer to struct to clean up
499 ----*/
500 void
501 zot_pipe(PIPE_S **syspipe)
503 if((*syspipe)->args){
504 free((void *) (*syspipe)->args);
505 (*syspipe)->args = NULL;
508 if((*syspipe)->argv){
509 free((void *) (*syspipe)->argv);
510 (*syspipe)->argv = NULL;
513 if((*syspipe)->tmp){
514 free((void *) (*syspipe)->tmp);
515 (*syspipe)->tmp = NULL;
518 #ifdef _WINDOWS
520 if((*syspipe)->outfile){
521 free((void *) (*syspipe)->outfile);
522 (*syspipe)->outfile = NULL;
525 if((*syspipe)->command){
526 free((void *) (*syspipe)->command);
527 (*syspipe)->command = NULL;
530 #endif /* _WINDOWS */
532 free((void *) *syspipe);
533 *syspipe = NULL;
539 * Returns: 0 if all went well, -1 otherwise
542 pipe_close_write(PIPE_S *syspipe)
544 int rv = 0;
546 if(!syspipe || !syspipe->out.f)
547 return -1;
549 #ifdef _WINDOWS
552 unsigned flags = 0;
554 if(syspipe->mode & PIPE_STDERR)
555 flags |= MSWIN_EAW_CAPT_STDERR;
557 rv = fclose(syspipe->out.f);
558 syspipe->out.f = NULL;
559 if(syspipe->mode & PIPE_WRITE){
561 * PIPE_WRITE should always be set if we're trying to close
562 * the write end.
563 * PIPE_WRITE can't start process till now, all the others
564 * will have already run
566 if(pipe_mswin_exec_wrapper("pipe command", syspipe,
567 flags, NULL, NULL))
568 /* some horrible error just occurred */
569 rv = -1;
570 else
571 syspipe->in.f = our_fopen(syspipe->outfile, "rb");
573 else
574 rv = -1;
577 #else /* UNIX */
579 rv = fclose(syspipe->out.f) ? -1 : 0;
580 syspipe->out.f = NULL;
582 #endif
583 return(rv);
588 /*----------------------------------------------------------------------
589 Close pipe previously allocated and wait for child's death
591 Args: syspipe -- address of pointer to struct returned by open_system_pipe
592 exitval -- return exit status here.
594 Returns:
595 Two modes of return values for backcompat.
596 If exitval == NULL
597 returns exit status of child or -1 if invalid syspipe
598 If exitval != NULL
599 returns -1 if invalid syspipe or 0 if ok. In that case, exitval
600 of child is returned in exitval
601 ----*/
603 close_system_pipe(PIPE_S **syspipe, int *exitval, void (*pipecb_f) (PIPE_S *, int, void *))
605 #ifdef _WINDOWS
606 int rv = 0;
607 unsigned flags = 0;
609 if(!(syspipe && *syspipe))
610 return(-1);
612 if((*syspipe)->mode & PIPE_STDERR)
613 flags |= MSWIN_EAW_CAPT_STDERR;
615 if(((*syspipe)->mode & PIPE_WRITE) && (*syspipe)->out.f){
616 fclose((*syspipe)->out.f);
618 * PIPE_WRITE can't start process till now, all the others
619 * will have already run
621 if(pipe_mswin_exec_wrapper("pipe command", (*syspipe),
622 flags, pipecb_f, NULL))
623 /* some horrible error just occurred */
624 rv = -1;
626 else if((*syspipe)->mode & PIPE_READ)
627 if((*syspipe)->in.f)
628 fclose((*syspipe)->in.f);
630 if(exitval){
631 *exitval = (*syspipe)->exit_code;
632 dprint((5, "Closed pipe: exitval=%d\n", *exitval));
635 if((*syspipe)->infile)
636 our_unlink((*syspipe)->infile);
638 if((*syspipe)->outfile && (*syspipe)->deloutfile)
639 our_unlink((*syspipe)->outfile);
641 if(rv != -1 && !exitval)
642 rv = (*syspipe)->exit_code;
644 zot_pipe(syspipe);
646 #ifdef DEBUG
647 if(!exitval){
648 dprint((5, "Closed pipe: rv=%d\n", rv));
650 #endif /* DEBUG */
652 return(rv);
654 #else /* UNIX */
655 int status;
657 if(!(syspipe && *syspipe))
658 return -1;
660 if(((*syspipe)->mode) & PIPE_WRITE){
661 if(((*syspipe)->mode) & PIPE_DESC){
662 if((*syspipe)->out.d >= 0)
663 close((*syspipe)->out.d);
665 else if((*syspipe)->out.f)
666 fclose((*syspipe)->out.f);
669 if(((*syspipe)->mode) & PIPE_READ){
670 if(((*syspipe)->mode) & PIPE_DESC){
671 if((*syspipe)->in.d >= 0)
672 close((*syspipe)->in.d);
674 else if((*syspipe)->in.f)
675 fclose((*syspipe)->in.f);
678 if(pipecb_f)
679 (*pipecb_f)(*syspipe, OSB_PRE_CLOSE, NULL);
681 /* wait on the child */
682 (void) process_reap((*syspipe)->pid, &status, PR_NONE);
684 /* restore original handlers... */
685 (void) signal(SIGINT, (*syspipe)->isig);
686 (void) signal(SIGHUP, (*syspipe)->hsig);
687 (void) signal(SIGQUIT, (*syspipe)->qsig);
689 if((*syspipe)->timeout){
690 (void)signal(SIGALRM, (*syspipe)->alrm);
691 alarm((*syspipe)->old_timeo);
692 child_pid = 0;
695 if(pipecb_f)
696 (*pipecb_f)(*syspipe, OSB_POST_CLOSE, NULL);
698 zot_pipe(syspipe);
700 if(exitval){
701 *exitval = status;
702 return 0;
704 else{
705 return(status);
707 #endif /* UNIX */
712 * process_reap - manage child demise and return exit status
714 * Args: pid -- id of process to reap
715 * esp -- pointer to exist status
716 * flags -- special reaping considerations
718 * Returns:
719 * < 0 -- if there's a problem
720 * 0 -- if no child to reap
721 * > 0 -- process id of the child
723 pid_t
724 process_reap(pid_t pid, int *esp, int flags)
726 #ifdef _WINDOWS
728 return 0;
730 #else /* UNIX */
731 WAITSTATUS_T wstatus;
732 pid_t rv;
733 int wflags;
735 #if HAVE_WAITPID
737 wflags = 0;
739 #ifdef WNOHANG
740 if(flags & PR_NOHANG)
741 wflags |= WNOHANG;
742 #endif
744 while (((rv = waitpid(pid, &wstatus, wflags)) < 0) && (errno != ECHILD));
746 #elif HAVE_WAIT4
748 wflags = 0;
750 #ifdef WNOHANG
751 if(flags & PR_NOHANG)
752 wflags |= WNOHANG;
753 #endif
755 while (((rv = wait4(pid,&wstatus,wflags,NULL)) < 0) && (errno != ECHILD));
757 #elif HAVE_WAIT
759 while (((rv = wait(&wstatus)) != pid) && ((rv > 0) || (errno != ECHILD)));
761 #else
763 /* BUG: BAIL */
765 #endif
767 if(rv > 0)
768 *esp = (WIFEXITED(wstatus)) ? (int) WEXITSTATUS(wstatus) : -1;
770 return(rv);
771 #endif /* UNIX */
775 #ifndef _WINDOWS
776 RETSIGTYPE
777 pipe_alarm(int sig)
779 if(child_pid)
780 kill(child_pid, SIGINT);
782 #endif /* !_WINDOWS */
785 #ifdef _WINDOWS
787 * Wrapper around mswin_exec_and_wait()
790 pipe_mswin_exec_wrapper(char *whatsit,
791 PIPE_S *syspipe, unsigned flags,
792 void (*pipecb_f)(PIPE_S *, int, void *),
793 void (*piperr_f)(char *))
795 int rv;
797 flags |= MSWIN_EAW_CTRL_C_CANCELS;
799 if(pipecb_f)
800 (*pipecb_f)(syspipe, OSB_PRE_OPEN, NULL);
802 rv = mswin_exec_and_wait(whatsit, syspipe->command,
803 syspipe->infile, syspipe->outfile,
804 &syspipe->exit_code, flags);
806 if(pipecb_f)
807 (*pipecb_f)(syspipe, OSB_POST_OPEN, (void *)rv);
809 return rv;
811 #endif