added files
[bcm963xx.git] / userapps / opensource / net-snmp / agent / mibgroup / ucd-snmp / pass_persist.c
1 #include <net-snmp/net-snmp-config.h>
2
3 #if HAVE_IO_H
4 #include <io.h>
5 #endif
6 #if HAVE_STDLIB_H
7 #include <stdlib.h>
8 #endif
9 #if HAVE_STRING_H
10 #include <string.h>
11 #else
12 #include <strings.h>
13 #endif
14 #if HAVE_UNISTD_H
15 #include <unistd.h>
16 #endif
17 #include <ctype.h>
18 #include <sys/types.h>
19 #if HAVE_NETINET_IN_H
20 #include <netinet/in.h>
21 #endif
22 #if HAVE_SYS_WAIT_H
23 # include <sys/wait.h>
24 #endif
25 #if HAVE_WINSOCK_H
26 #include <winsock.h>
27 #endif
28
29 #if HAVE_DMALLOC_H
30 #include <dmalloc.h>
31 #endif
32
33 #include <signal.h>
34 #include <errno.h>
35
36 #include <net-snmp/net-snmp-includes.h>
37 #include <net-snmp/agent/net-snmp-agent-includes.h>
38
39 #include "struct.h"
40 #include "pass_persist.h"
41 #include "extensible.h"
42 #include "util_funcs.h"
43
44 struct extensible *persistpassthrus = NULL;
45 int             numpersistpassthrus = 0;
46 struct persist_pipe_type {
47     FILE           *fIn, *fOut;
48     int             fdIn, fdOut;
49     int             pid;
50 }              *persist_pipes = (struct persist_pipe_type *) NULL;
51 static int      init_persist_pipes(void);
52 static void     close_persist_pipe(int iindex);
53 static int      open_persist_pipe(int iindex, char *command);
54 static void     destruct_persist_pipes(void);
55 static int      write_persist_pipe(int iindex, const char *data);
56
57 /*
58  * These are defined in pass.c 
59  */
60 extern int      asc2bin(char *p);
61 extern int      bin2asc(char *p, size_t n);
62 extern int      snmp_oid_min_compare(const oid *, size_t, const oid *,
63                                      size_t);
64
65 /*
66  * the relocatable extensible commands variables 
67  */
68 struct variable2 extensible_persist_passthru_variables[] = {
69     /*
70      * bogus entry.  Only some of it is actually used. 
71      */
72     {MIBINDEX, ASN_INTEGER, RWRITE, var_extensible_pass_persist, 0,
73      {MIBINDEX}},
74 };
75
76 void
77 init_pass_persist(void)
78 {
79     snmpd_register_config_handler("pass_persist",
80                                   pass_persist_parse_config,
81                                   pass_persist_free_config,
82                                   "miboid program");
83 }
84
85 void
86 pass_persist_parse_config(const char *token, char *cptr)
87 {
88     struct extensible **ppass = &persistpassthrus, **etmp, *ptmp;
89     char           *tcptr;
90     int             i;
91
92     if (*cptr == '.')
93         cptr++;
94     if (!isdigit(*cptr)) {
95         config_perror("second token is not a OID");
96         return;
97     }
98     numpersistpassthrus++;
99
100     while (*ppass != NULL)
101         ppass = &((*ppass)->next);
102     (*ppass) = (struct extensible *) malloc(sizeof(struct extensible));
103     if (*ppass == NULL)
104         return;
105     (*ppass)->type = PASSTHRU_PERSIST;
106
107     (*ppass)->miblen = parse_miboid(cptr, (*ppass)->miboid);
108     while (isdigit(*cptr) || *cptr == '.')
109         cptr++;
110     /*
111      * name 
112      */
113     cptr = skip_white(cptr);
114     if (cptr == NULL) {
115         config_perror("No command specified on pass_persist line");
116         (*ppass)->command[0] = 0;
117     } else {
118         for (tcptr = cptr; *tcptr != 0 && *tcptr != '#' && *tcptr != ';';
119              tcptr++);
120         strncpy((*ppass)->command, cptr, tcptr - cptr);
121         (*ppass)->command[tcptr - cptr] = 0;
122     }
123     strcpy((*ppass)->name, (*ppass)->command);
124     (*ppass)->next = NULL;
125
126     register_mib("pass_persist",
127                  (struct variable *) extensible_persist_passthru_variables,
128                  sizeof(struct variable2), 1, (*ppass)->miboid,
129                  (*ppass)->miblen);
130
131     /*
132      * argggg -- pasthrus must be sorted 
133      */
134     if (numpersistpassthrus > 0) {
135         etmp = (struct extensible **)
136             malloc(((sizeof(struct extensible *)) * numpersistpassthrus));
137         if (etmp == NULL)
138             return;
139         for (i = 0, ptmp = (struct extensible *) persistpassthrus;
140              i < numpersistpassthrus && ptmp != 0; i++, ptmp = ptmp->next)
141             etmp[i] = ptmp;
142         qsort(etmp, numpersistpassthrus, sizeof(struct extensible *),
143               pass_persist_compare);
144         persistpassthrus = (struct extensible *) etmp[0];
145         ptmp = (struct extensible *) etmp[0];
146
147         for (i = 0; i < numpersistpassthrus - 1; i++) {
148             ptmp->next = etmp[i + 1];
149             ptmp = ptmp->next;
150         }
151         ptmp->next = NULL;
152         free(etmp);
153     }
154 }
155
156 void
157 pass_persist_free_config(void)
158 {
159     struct extensible *etmp, *etmp2;
160
161     /*
162      * Close any open pipes to any programs 
163      */
164     destruct_persist_pipes();
165
166     for (etmp = persistpassthrus; etmp != NULL;) {
167         etmp2 = etmp;
168         etmp = etmp->next;
169         unregister_mib(etmp2->miboid, etmp2->miblen);
170         free(etmp2);
171     }
172     persistpassthrus = NULL;
173     numpersistpassthrus = 0;
174 }
175
176 u_char         *
177 var_extensible_pass_persist(struct variable *vp,
178                             oid * name,
179                             size_t * length,
180                             int exact,
181                             size_t * var_len, WriteMethod ** write_method)
182 {
183     oid             newname[MAX_OID_LEN];
184     int             i, rtest, newlen;
185     static long     long_ret;
186     char            buf[SNMP_MAXBUF];
187     static char     buf2[SNMP_MAXBUF];
188     static oid      objid[MAX_OID_LEN];
189     struct extensible *persistpassthru;
190     FILE           *file;
191
192     /*
193      * Make sure that our basic pipe structure is malloced 
194      */
195     init_persist_pipes();
196
197     long_ret = *length;
198     for (i = 1; i <= numpersistpassthrus; i++) {
199         persistpassthru = get_exten_instance(persistpassthrus, i);
200         rtest = snmp_oid_min_compare(name, *length,
201                                      persistpassthru->miboid,
202                                      persistpassthru->miblen);
203         if ((exact && rtest == 0) || (!exact && rtest <= 0)) {
204             /*
205              * setup args 
206              */
207             if (persistpassthru->miblen >= *length || rtest < 0)
208                 sprint_mib_oid(buf, persistpassthru->miboid,
209                                persistpassthru->miblen);
210             else
211                 sprint_mib_oid(buf, name, *length);
212
213             /*
214              * Open our pipe if necessary 
215              */
216             if (!open_persist_pipe(i, persistpassthru->name)) {
217                 return (NULL);
218             }
219
220             if (exact)
221                 snprintf(persistpassthru->command,
222                   sizeof(persistpassthru->command), "get\n%s\n", buf);
223             else
224                 snprintf(persistpassthru->command,
225                   sizeof(persistpassthru->command), "getnext\n%s\n", buf);
226             persistpassthru->command[ sizeof(persistpassthru->command)-1 ] = 0;
227
228             DEBUGMSGTL(("ucd-snmp/pass_persist",
229                         "persistpass-sending:\n%s",
230                         persistpassthru->command));
231             if (!write_persist_pipe(i, persistpassthru->command)) {
232                 *var_len = 0;
233                 /*
234                  * close_persist_pipes is called in write_persist_pipe 
235                  */
236                 return (NULL);
237             }
238
239             /*
240              * valid call.  Exec and get output 
241              */
242             if ((file = persist_pipes[i].fIn)) {
243                 if (fgets(buf, sizeof(buf), file) == NULL) {
244                     *var_len = 0;
245                     close_persist_pipe(i);
246                     return (NULL);
247                 }
248                 /*
249                  * persistant scripts return "NONE\n" on invalid items 
250                  */
251                 if (!strncmp(buf, "NONE", 4)) {
252                     *var_len = 0;
253                     return (NULL);
254                 }
255                 newlen = parse_miboid(buf, newname);
256
257                 /*
258                  * its good, so copy onto name/length 
259                  */
260                 memcpy((char *) name, (char *) newname,
261                        (int) newlen * sizeof(oid));
262                 *length = newlen;
263
264                 /*
265                  * set up return pointer for setable stuff 
266                  */
267                 *write_method = setPassPersist;
268
269                 if (newlen == 0 || fgets(buf, sizeof(buf), file) == NULL
270                     || fgets(buf2, sizeof(buf2), file) == NULL) {
271                     *var_len = 0;
272                     close_persist_pipe(i);
273                     return (NULL);
274                 }
275
276                 /*
277                  * buf contains the return type, and buf2 contains the data 
278                  */
279                 if (!strncasecmp(buf, "string", 6)) {
280                     buf2[strlen(buf2) - 1] = 0; /* zap the linefeed */
281                     *var_len = strlen(buf2);
282                     vp->type = ASN_OCTET_STR;
283                     return ((unsigned char *) buf2);
284                 } else if (!strncasecmp(buf, "integer", 7)) {
285                     *var_len = sizeof(long_ret);
286                     long_ret = strtol(buf2, NULL, 10);
287                     vp->type = ASN_INTEGER;
288                     return ((unsigned char *) &long_ret);
289                 } else if (!strncasecmp(buf, "unsigned", 7)) {
290                     *var_len = sizeof(long_ret);
291                     long_ret = strtoul(buf2, NULL, 10);
292                     vp->type = ASN_UNSIGNED;
293                     return ((unsigned char *) &long_ret);
294                 } else if (!strncasecmp(buf, "counter", 7)) {
295                     *var_len = sizeof(long_ret);
296                     long_ret = strtoul(buf2, NULL, 10);
297                     vp->type = ASN_COUNTER;
298                     return ((unsigned char *) &long_ret);
299                 } else if (!strncasecmp(buf, "octet", 5)) {
300                     *var_len = asc2bin(buf2);
301                     vp->type = ASN_OCTET_STR;
302                     return ((unsigned char *) buf2);
303                 } else if (!strncasecmp(buf, "opaque", 5)) {
304                     *var_len = asc2bin(buf2);
305                     vp->type = ASN_OPAQUE;
306                     return ((unsigned char *) buf2);
307                 } else if (!strncasecmp(buf, "gauge", 5)) {
308                     *var_len = sizeof(long_ret);
309                     long_ret = strtoul(buf2, NULL, 10);
310                     vp->type = ASN_GAUGE;
311                     return ((unsigned char *) &long_ret);
312                 } else if (!strncasecmp(buf, "objectid", 8)) {
313                     newlen = parse_miboid(buf2, objid);
314                     *var_len = newlen * sizeof(oid);
315                     vp->type = ASN_OBJECT_ID;
316                     return ((unsigned char *) objid);
317                 } else if (!strncasecmp(buf, "timetick", 8)) {
318                     *var_len = sizeof(long_ret);
319                     long_ret = strtoul(buf2, NULL, 10);
320                     vp->type = ASN_TIMETICKS;
321                     return ((unsigned char *) &long_ret);
322                 } else if (!strncasecmp(buf, "ipaddress", 9)) {
323                     newlen = parse_miboid(buf2, objid);
324                     if (newlen != 4) {
325                         snmp_log(LOG_ERR,
326                                  "invalid ipaddress returned:  %s\n",
327                                  buf2);
328                         *var_len = 0;
329                         return (NULL);
330                     }
331                     long_ret =
332                         (objid[0] << (8 * 3)) + (objid[1] << (8 * 2)) +
333                         (objid[2] << 8) + objid[3];
334                     long_ret = htonl(long_ret);
335                     *var_len = sizeof(long_ret);
336                     vp->type = ASN_IPADDRESS;
337                     return ((unsigned char *) &long_ret);
338                 }
339             }
340             *var_len = 0;
341             return (NULL);
342         }
343     }
344     if (var_len)
345         *var_len = 0;
346     *write_method = NULL;
347     return (NULL);
348 }
349
350 int
351 setPassPersist(int action,
352                u_char * var_val,
353                u_char var_val_type,
354                size_t var_val_len,
355                u_char * statP, oid * name, size_t name_len)
356 {
357     int             i, rtest;
358     struct extensible *persistpassthru;
359
360     char            buf[SNMP_MAXBUF], buf2[SNMP_MAXBUF];
361     long            tmp;
362     unsigned long   utmp;
363     int             itmp;
364
365     /*
366      * Make sure that our basic pipe structure is malloced 
367      */
368     init_persist_pipes();
369
370     for (i = 1; i <= numpersistpassthrus; i++) {
371         persistpassthru = get_exten_instance(persistpassthrus, i);
372         rtest = snmp_oid_min_compare(name, name_len,
373                                      persistpassthru->miboid,
374                                      persistpassthru->miblen);
375         if (rtest <= 0) {
376             if (action != COMMIT)
377                 return SNMP_ERR_NOERROR;
378             /*
379              * setup args 
380              */
381             if (persistpassthru->miblen >= name_len || rtest < 0)
382                 sprint_mib_oid(buf, persistpassthru->miboid,
383                                persistpassthru->miblen);
384             else
385                 sprint_mib_oid(buf, name, name_len);
386             snprintf(persistpassthru->command,
387                      sizeof(persistpassthru->command), "set\n%s\n", buf);
388             persistpassthru->command[ sizeof(persistpassthru->command)-1 ] = 0;
389             switch (var_val_type) {
390             case ASN_INTEGER:
391             case ASN_COUNTER:
392             case ASN_GAUGE:
393             case ASN_TIMETICKS:
394                 tmp = *((long *) var_val);
395                 switch (var_val_type) {
396                 case ASN_INTEGER:
397                     sprintf(buf, "integer %d\n", (int) tmp);
398                     break;
399                 case ASN_COUNTER:
400                     sprintf(buf, "counter %d\n", (int) tmp);
401                     break;
402                 case ASN_GAUGE:
403                     sprintf(buf, "gauge %d\n", (int) tmp);
404                     break;
405                 case ASN_TIMETICKS:
406                     sprintf(buf, "timeticks %d\n", (int) tmp);
407                     break;
408                 }
409                 break;
410             case ASN_IPADDRESS:
411                 utmp = *((u_long *) var_val);
412                 utmp = ntohl(utmp);
413                 sprintf(buf, "ipaddress %d.%d.%d.%d\n",
414                         (int) ((utmp & 0xff000000) >> (8 * 3)),
415                         (int) ((utmp & 0xff0000) >> (8 * 2)),
416                         (int) ((utmp & 0xff00) >> (8)),
417                         (int) ((utmp & 0xff)));
418                 break;
419             case ASN_OCTET_STR:
420                 itmp = sizeof(buf2);
421                 memcpy(buf2, var_val, var_val_len);
422                 if (var_val_len == 0)
423                     sprintf(buf, "string \"\"\n");
424                 else if (bin2asc(buf2, var_val_len) == (int) var_val_len)
425                     snprintf(buf, sizeof(buf), "string \"%s\"\n", buf2);
426                 else
427                     snprintf(buf, sizeof(buf), "octet \"%s\"\n", buf2);
428                 buf[ sizeof(buf)-1 ] = 0;
429                 break;
430             case ASN_OBJECT_ID:
431                 sprint_mib_oid(buf2, (oid *) var_val, var_val_len);
432                 snprintf(buf, sizeof(buf), "objectid \"%s\"\n", buf2);
433                 buf[ sizeof(buf)-1 ] = 0;
434                 break;
435             }
436             strncat(persistpassthru->command, buf,
437                     sizeof(persistpassthru->command) -
438                     strlen(persistpassthru->command) - 2);
439             persistpassthru->command[ sizeof(persistpassthru->command)-2 ] = '\n';
440             persistpassthru->command[ sizeof(persistpassthru->command)-1 ] = 0;
441
442             if (!open_persist_pipe(i, persistpassthru->name)) {
443                 return SNMP_ERR_NOTWRITABLE;
444             }
445
446             DEBUGMSGTL(("ucd-snmp/pass_persist",
447                         "persistpass-writing:  %s\n",
448                         persistpassthru->command));
449             if (!write_persist_pipe(i, persistpassthru->command)) {
450                 close_persist_pipe(i);
451                 return SNMP_ERR_NOTWRITABLE;
452             }
453
454             if (fgets(buf, sizeof(buf), persist_pipes[i].fIn) == NULL) {
455                 close_persist_pipe(i);
456                 return SNMP_ERR_NOTWRITABLE;
457             }
458
459             if (!strncasecmp(buf, "not-writable", 11)) {
460                 return SNMP_ERR_NOTWRITABLE;
461             } else if (!strncasecmp(buf, "wrong-type", 9)) {
462                 return SNMP_ERR_WRONGTYPE;
463             }
464             return SNMP_ERR_NOERROR;
465         }
466     }
467     if (snmp_get_do_debugging()) {
468         sprint_mib_oid(buf2, name, name_len);
469         DEBUGMSGTL(("ucd-snmp/pass_persist", "persistpass-notfound:  %s\n",
470                     buf2));
471     }
472     return SNMP_ERR_NOSUCHNAME;
473 }
474
475 int
476 pass_persist_compare(const void *a, const void *b)
477 {
478     const struct extensible *const *ap, *const *bp;
479     ap = (const struct extensible * const *) a;
480     bp = (const struct extensible * const *) b;
481     return snmp_oid_compare((*ap)->miboid, (*ap)->miblen, (*bp)->miboid,
482                             (*bp)->miblen);
483 }
484
485 /*
486  * Initialize our persistant pipes
487  *   - Returns 1 on success, 0 on failure.
488  *   - Initializes all FILE pointers to NULL to indicate "closed"
489  */
490 static int
491 init_persist_pipes(void)
492 {
493     int             i;
494
495     /*
496      * if we are already taken care of, just return 
497      */
498     if (persist_pipes) {
499         return persist_pipes ? 1 : 0;
500     }
501
502     /*
503      * Otherwise malloc and initialize 
504      */
505     persist_pipes = (struct persist_pipe_type *)
506         malloc(sizeof(struct persist_pipe_type) *
507                (numpersistpassthrus + 1));
508     if (persist_pipes) {
509         for (i = 0; i <= numpersistpassthrus; i++) {
510             persist_pipes[i].fIn = persist_pipes[i].fOut = (FILE *) 0;
511             persist_pipes[i].fdIn = persist_pipes[i].fdOut = -1;
512             persist_pipes[i].pid = -1;
513         }
514     }
515     return persist_pipes ? 1 : 0;
516 }
517
518 /*
519  * Destruct our persistant pipes
520  *
521  */
522 static void
523 destruct_persist_pipes(void)
524 {
525     int             i;
526
527     /*
528      * Return if there are no pipes 
529      */
530     if (!persist_pipes) {
531         return;
532     }
533
534     for (i = 0; i <= numpersistpassthrus; i++) {
535         close_persist_pipe(i);
536     }
537
538     free(persist_pipes);
539     persist_pipes = (struct persist_pipe_type *) 0;
540 }
541
542 /*
543  * returns 0 on failure, 1 on success 
544  */
545 static int
546 open_persist_pipe(int iindex, char *command)
547 {
548     static int      recurse = 0;        /* used to allow one level of recursion */
549
550     DEBUGMSGTL(("ucd-snmp/pass_persist", "open_persist_pipe(%d,'%s')\n",
551                 iindex, command));
552     /*
553      * Open if it's not already open 
554      */
555     if (persist_pipes[iindex].pid == -1) {
556         int             fdIn, fdOut, pid;
557         get_exec_pipes(command, &fdIn, &fdOut, &pid);
558
559         /*
560          * Did we fail? 
561          */
562         if (pid == -1) {
563             DEBUGMSGTL(("ucd-snmp/pass_persist",
564                         "open_persist_pipe: pid == -1\n"));
565             recurse = 0;
566             return 0;
567         }
568
569         /*
570          * If not, fill out our structure 
571          */
572         persist_pipes[iindex].pid = pid;
573         persist_pipes[iindex].fdIn = fdIn;
574         persist_pipes[iindex].fdOut = fdOut;
575         persist_pipes[iindex].fIn = fdopen(fdIn, "r");
576         persist_pipes[iindex].fOut = fdopen(fdOut, "w");
577
578         /*
579          * Setup our -non-buffered-io- 
580          */
581         setbuf(persist_pipes[iindex].fOut, (char *) 0);
582     }
583
584     /*
585      * Send test packet always so we can self-catch 
586      */
587     {
588         char            buf[SNMP_MAXBUF];
589         /*
590          * Should catch SIGPIPE around this call! 
591          */
592         if (!write_persist_pipe(iindex, "PING\n")) {
593             DEBUGMSGTL(("ucd-snmp/pass_persist",
594                         "open_persist_pipe: Error writing PING\n"));
595             close_persist_pipe(iindex);
596
597             /*
598              * Recurse one time if we get a SIGPIPE 
599              */
600             if (!recurse) {
601                 recurse = 1;
602                 return open_persist_pipe(iindex, command);
603             }
604             recurse = 0;
605             return 0;
606         }
607         if (fgets(buf, sizeof(buf), persist_pipes[iindex].fIn) == NULL) {
608             DEBUGMSGTL(("ucd-snmp/pass_persist",
609                         "open_persist_pipe: Error reading for PONG\n"));
610             close_persist_pipe(iindex);
611             recurse = 0;
612             return 0;
613         }
614         if (strncmp(buf, "PONG", 4)) {
615             DEBUGMSGTL(("ucd-snmp/pass_persist",
616                         "open_persist_pipe: PONG not received!\n"));
617             close_persist_pipe(iindex);
618             recurse = 0;
619             return 0;
620         }
621     }
622
623     recurse = 0;
624     return 1;
625 }
626
627 #if STRUCT_SIGACTION_HAS_SA_SIGACTION
628 /*
629  * Generic handler 
630  */
631 void
632 sigpipe_handler(int sig, siginfo_t * sip, void *uap)
633 {
634     return;
635 }
636 #endif
637
638 static int
639 write_persist_pipe(int iindex, const char *data)
640 {
641 #if HAVE_SIGNAL
642     struct sigaction sa, osa;
643     int             wret = 0, werrno = 0;
644
645     /*
646      * Don't write to a non-existant process 
647      */
648     if (persist_pipes[iindex].pid == -1) {
649         return 0;
650     }
651
652     /*
653      * Setup our signal action to catch SIGPIPEs 
654      */
655     sa.sa_handler = NULL;
656 #if STRUCT_SIGACTION_HAS_SA_SIGACTION
657     sa.sa_sigaction = &sigpipe_handler;
658 #endif
659     sigemptyset(&sa.sa_mask);
660     sa.sa_flags = 0;
661     if (sigaction(SIGPIPE, &sa, &osa)) {
662         DEBUGMSGTL(("ucd-snmp/pass_persist",
663                     "write_persist_pipe: sigaction failed: %d", errno));
664     }
665
666     /*
667      * Do the write 
668      */
669     wret = write(persist_pipes[iindex].fdOut, data, strlen(data));
670     werrno = errno;
671
672     /*
673      * Reset the signal handler 
674      */
675     sigaction(SIGPIPE, &osa, (struct sigaction *) 0);
676
677     if (wret < 0) {
678         if (werrno != EINTR) {
679             DEBUGMSGTL(("ucd-snmp/pass_persist",
680                         "write_persist_pipe: write returned unknown error %d\n",
681                         errno));
682         }
683         close_persist_pipe(iindex);
684         return 0;
685     }
686 #endif                          /* HAVE_SIGNAL */
687     return 1;
688 }
689
690 static void
691 close_persist_pipe(int iindex)
692 {
693
694     /*
695      * Check and nix every item 
696      */
697     if (persist_pipes[iindex].fOut) {
698         fclose(persist_pipes[iindex].fOut);
699         persist_pipes[iindex].fOut = (FILE *) 0;
700     }
701     if (persist_pipes[iindex].fdOut != -1) {
702         close(persist_pipes[iindex].fdOut);
703         persist_pipes[iindex].fdOut = -1;
704     }
705     if (persist_pipes[iindex].fIn) {
706         fclose(persist_pipes[iindex].fIn);
707         persist_pipes[iindex].fIn = (FILE *) 0;
708     }
709     if (persist_pipes[iindex].fdIn != -1) {
710         close(persist_pipes[iindex].fdIn);
711         persist_pipes[iindex].fdIn = -1;
712     }
713     if (persist_pipes[iindex].pid != -1) {
714 #if HAVE_SYS_WAIT_H
715         waitpid(persist_pipes[iindex].pid, 0, 0);
716 #endif
717         persist_pipes[iindex].pid = -1;
718     }
719
720 }