FORM v5.0.0-35-g6318119
extcmd.c
Go to the documentation of this file.
1
5/* #[ License : */
6/*
7 * Copyright (C) 1984-2026 J.A.M. Vermaseren
8 * When using this file you are requested to refer to the publication
9 * J.A.M.Vermaseren "New features of FORM" math-ph/0010025
10 * This is considered a matter of courtesy as the development was paid
11 * for by FOM the Dutch physics granting agency and we would like to
12 * be able to track its scientific use to convince FOM of its value
13 * for the community.
14 *
15 * This file is part of FORM.
16 *
17 * FORM is free software: you can redistribute it and/or modify it under the
18 * terms of the GNU General Public License as published by the Free Software
19 * Foundation, either version 3 of the License, or (at your option) any later
20 * version.
21 *
22 * FORM is distributed in the hope that it will be useful, but WITHOUT ANY
23 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
24 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
25 * details.
26 *
27 * You should have received a copy of the GNU General Public License along
28 * with FORM. If not, see <http://www.gnu.org/licenses/>.
29 */
30/* #] License : */
31/*
32 #[ Documentation :
33
34 This module is written by M.Tentyukov as a part of implementation of
35 interaction between FORM and external processes, first release
36 09.04.2004. A part of this code is copied from the DIANA project, which was
37 written by M. Tentyukov and published under the GPL version 2 as
38 published by the Free Software Foundation.
39
40 This file is completely re-written by M.Tentyukov in May 2006.
41 Since the interface was changed, the public function were changed,
42 also. A new public functions were added: initPresetExternalChannels()
43 (see comments just before this function in the present file) and
44 setKillModeForExternalChannel (a pointer, not a function).
45
46 If a macro WITHEXTERNALCHANNEL is not defined, all public functions
47 are stubs returning failure.
48
49 The idea is to start an external command swallowing
50 its stdin and stdout. This can be done by means of the function
51 int openExternalChannel(cmd,daemonize,shellname,stderrname), where
52 cmd is a command to run,
53 daemonize: if !=0 then start the command in the "daemon" mode,
54 shellname: if !=NULL, execute the command in a subshell,
55 stderrname: if != NULL, redirect stderr of the command to this file.
56 The function returns some small positive integer number (the
57 descriptor of a newly created external channel), or -1 on failure.
58
59 After the command is started, it becomes a _current_ opened external
60 channel. The buffer can be sent to its stdin by a function
61 int writeBufToExtChannel(buf, n)
62 (here buf is a pointer to the buffer, n is the length in bytes; the
63 function returns 0 in success, or -1 on failure),
64 or one character can be read from its stdout by
65 means of the function
66 int getcFromExtChannel().
67
68 The latter returns the
69 character casted to integer, or something <0. This can be -2 (if there
70 is no current external channel) or EOF, if the external program closes
71 its stdout, or if the external program outputs a string coinciding
72 with a _terminator_.
73
74 By default, the terminator if an empty line. For the current external
75 channel it can be set by means of the function
76 int setTerminatorForExternalChannel(newterminator).
77 The function returns 0 in success, or !0 if something is wrong (no
78 current channel, too long terminator).
79
80 After getcFromExtChannel() returns EOF, the current channel becomes
81 undefined. Any further attempts to read information by
82 getcFromExtChannel() result in -2. To set (re-set) a current channel,
83 the function
84 int selectExternalChannel(n)
85 can be used. This function accepts the valid external channel
86 descriptor (returned by openExternalChannel) and returns the
87 descriptor of a previous current channel (0, if there was no current
88 channel, or -1, if the external channel descriptor is invalid).
89 If n == 0, the function undefine the current external channel.
90
91 The function
92 int closeExternalChannel(n)
93 destroys the opened external channel with the descriptor n. It returns
94 0 in success, or -1 on failure. If the corresponding external channel
95 was the current one, the current channel becomes undefined. If n==0,
96 the function closes the current external channel.
97
98 The function
99 int getCurrentExternalChannel(void)
100 returns the descriptor if the current external channel, or 0 , if
101 there is no current external channel.
102
103 The function
104 void closeAllExternalChannels(void)
105
106 destroys all opened external channels.
107
108 List of all public functions:
109 int openExternalChannel(UBYTE *cmd,int daemonize,UBYTE *shellname, UBYTE * stderrname);
110 int initPresetExternalChannels(UBYTE *theline, int thetimeout);
111 int setTerminatorForExternalChannel(char *newterminator);
112 int setKillModeForExternalChannel(int signum, int sentToWholeGroup);
113 int closeExternalChannel(int n);
114 int selectExternalChannel(int n);
115 int writeBufToExtChannel(char *buf,int n);
116 int getcFromExtChannel(void);
117 int getCurrentExternalChannel(void);
118 void closeAllExternalChannels(void);
119
120 ATTENTION!
121
122 Four of them:
123 1 setTerminatorForExternalChannel
124 2 setKillModeForExternalChannel
125 3 writeBufToExtChannel
126 4 getcFromExtChannel
127
128 are NOT functions, but variables (pointers) of a corrsponding type.
129 They are initialised by proper values to avoid repeated error checking.
130
131 All public functions are independent of realization hidden in this module.
132 All other functions may have a returned type/parameters type local w.r.t.
133 this module; they are not declared outside of this file.
134
135 #] Documentation :
136 #[ Selftest initializing:
137*/
138
139/*
140Uncomment to get a self-consistent program:
141#define SELFTEST 1
142*/
143
144
145#ifdef SELFTEST
146#define WITHEXTERNALCHANNEL 1
147#ifdef _MSC_VER
148#define FORM_INLINE __inline
149#else
150#define FORM_INLINE inline
151#endif
152
153/*
154 From form3.h:
155*/
156typedef unsigned char UBYTE;
157
158/*The following variables should be defined in variable.h:*/
159extern int (*writeBufToExtChannel)(char *buffer, size_t n);
160extern int (*getcFromExtChannel)();
161extern int (*setTerminatorForExternalChannel)(char *buffer);
162extern int (*setKillModeForExternalChannel)(int signum, int sentToWholeGroup);
163
164#else /*ifdef SELFTEST*/
165#include "form3.h"
166#endif /*ifdef SELFTEST ... else*/
167/*
168pid_t getExternalChannelPid(void);
169*/
170/*
171 #] Selftest initializing:
172 #[ Includes :
173*/
174#ifdef WITHEXTERNALCHANNEL
175#include <stdio.h>
176#ifndef _MSC_VER
177#include <unistd.h>
178#endif
179#include <fcntl.h>
180#include <sys/types.h>
181#ifndef _MSC_VER
182#include <sys/time.h>
183#include <sys/wait.h>
184#endif
185#include <errno.h>
186#include <signal.h>
187#include <limits.h>
188/*
189 #] Includes :
190 #[ FailureFunctions:
191*/
192
193/*Non-initialized variant of public functions:*/
194int writeBufToExtChannelFailure(char *buf, size_t count)
195{
196 DUMMYUSE(buf); DUMMYUSE(count);
197 return(-1);
198}/*writeBufToExtChannelFailure*/
199
200int setTerminatorForExternalChannelFailure(char *newTerminator)
201{
202 DUMMYUSE(newTerminator);
203 return(-1);
204}/*setTerminatorForExternalChannelFailure*/
205
206int setKillModeForExternalChannelFailure(int signum, int sentToWholeGroup)
207{
208 DUMMYUSE(signum); DUMMYUSE(sentToWholeGroup);
209 return(-1);
210}/*setKillModeForExternalChannelFailure*/
211
212int getcFromExtChannelFailure(void)
213{
214 return(-2);
215}/*getcFromExtChannelFailure*/
216
217int (*writeBufToExtChannel)(char *buffer, size_t n) = &writeBufToExtChannelFailure;
218int (*setTerminatorForExternalChannel)(char *buffer) =
219 &setTerminatorForExternalChannelFailure;
220int (*setKillModeForExternalChannel)(int signum, int sentToWholeGroup) =
221 &setKillModeForExternalChannelFailure;
222int (*getcFromExtChannel)(void) = &getcFromExtChannelFailure;
223#endif
224/*
225 #] FailureFunctions:
226 #[ Stubs :
227*/
228#ifndef WITHEXTERNALCHANNEL
229/*Stubs for public functions:*/
230int openExternalChannel(UBYTE *cmd, int daemonize, UBYTE *shellname, UBYTE *stderrname)
231{ DUMMYUSE(cmd); DUMMYUSE(daemonize); DUMMYUSE(shellname); DUMMYUSE(stderrname); return(-1); };
232int initPresetExternalChannels(UBYTE *theline, int thetimeout) { DUMMYUSE(theline); DUMMYUSE(thetimeout); return(-1); };
233int closeExternalChannel(int n) { DUMMYUSE(n); return(-1); };
234int selectExternalChannel(int n) { DUMMYUSE(n); return(-1); };
235int getCurrentExternalChannel(void) { return(0); };
236void closeAllExternalChannels(void) {};
237#else /*ifndef WITHEXTERNALCHANNEL*/
238/*
239 #] Stubs :
240 #[ Local types :
241*/
242/*First argument for the function signal:*/
243#ifndef INTSIGHANDLER
244typedef void (*mysighandler_t)(int);
245#else
246/* Sometimes, this nonsense may occurs:*/
247/*typedef int (*mysighandler_t)(int);*/
248#endif
249
250/*Input IO buffer size increment -- each time the buffer
251is expired it will be increased by this value (in bytes):*/
252#define DELTA_EXT_BUF 128
253
254/*Re-allocatable array containing External Channel
255handlers increased each time by this value:*/
256#define DELTA_EXT_LIST 8
257
258/*How many times I/O routines may attempt to continue their work
259in some failures:*/
260#define MAX_FAILS_IO 2
261
262/*The external channel handler structure:*/
263typedef struct ExternalChannel {
264 pid_t pid; /*PID of the external process*/
265 pid_t gpid; /*process group ID of the external process.
266 If <=0, not used, if >0, the kill signals
267 is sent to the whole group */
268 FILE *frec; /*stdout of the external process*/
269 char *INbuf; /*External channel buffer*/
270 char *IBfill; /*Position in INbuf from which the next input character will be read*/
271 char *IBfull; /*End of read INbuf*/
272 char *IBstop; /*end of allocated space for INbuf*/
273 char *terminator;/* Terminator - when extern. program outputs ONLY this string,
274 it is assumed that the answer is ready, and getcFromExtChannel
275 returns EOF. Should not be longer then the minimal buffer!*/
276 /*Info fields, not changable after creating a channel:*/
277 char *cmd; /*the command*/
278 char *shellname;
279 char *stderrname;/*filename to redirect stderr, or NULL*/
280 int fsend; /*stdin of the external process*/
281 int killSignal; /*signal to kill*/
282 int daemonize;/*0 --neither setsid nor daemonize, !=0 -- full daemonization*/
283} EXTHANDLE;
284
285
286static EXTHANDLE *externalChannelsList=0;
287/*Here integers are better than pointers: */
288static int externalChannelsListStop=0;
289static int externalChannelsListFill=0;
290
291/*"current" external channel:*/
292static EXTHANDLE *externalChannelsListTop=0;
293/*
294 #] Local types :
295 #[ Selftest functions :
296*/
297#ifdef SELFTEST
298
299/*For malloc prototype:*/
300#include <stdlib.h>
301
302/*StrLen, Malloc1, M_free and strDup1 are defined in tools.c -- here only emulation:*/
303int StrLen(char *pattern)
304{
305register char *p=(char*)pattern;
306 while(*p)p++;
307 return((int) ((p-(char*)pattern)) );
308}/*StrLen*/
309void *Malloc1(int l, char *c)
310{
311 return(malloc(l));
312}
313void M_free(void *p,char *c)
314{
315 return(free(p));
316}
317
318char *strDup1(UBYTE *instring, char *ifwrong)
319{
320 UBYTE *s = instring, *to;
321 while ( *s ) s++;
322 to = s = (UBYTE *)Malloc1((s-instring)+1,ifwrong);
323 while ( *instring ) *to++ = *instring++;
324 *to = 0;
325 return(s);
326}
327
328/*PutPreVar from pre.c -- just ths stub:*/
329int PutPreVar(UBYTE *a,UBYTE *b,UBYTE *c,int i)
330{
331 return(0);
332}
333
334#endif
335/*
336 #] Selftest functions :
337 #[ Local functions :
338*/
339
340/*Initialize one cell of handler:*/
341static FORM_INLINE void extHandlerInit(EXTHANDLE *h)
342{
343 h->pid=-1;
344 h->gpid=-1;
345 h->fsend=0;
346 h->killSignal=SIGKILL;
347 h->daemonize=1;
348 h->frec=NULL;
349 h->INbuf=h->IBfill=h->IBfull=h->IBstop=
350 h->terminator=h->cmd=h->shellname=h->stderrname=NULL;
351}/*extHandlerInit*/
352
353/* Copies each field of handler:*/
354static FORM_INLINE void extHandlerSwallowCopy(EXTHANDLE *to, EXTHANDLE *from)
355{
356 to->pid=from->pid;
357 to->gpid=from->gpid;
358 to->fsend=from->fsend;
359 to->killSignal=from->killSignal;
360 to->daemonize=from->daemonize;
361 to->frec=from->frec;
362 to->INbuf=from->INbuf;
363 to->IBfill=from->IBfill;
364 to->IBfull=from->IBfull;
365 to->IBstop=from->IBstop;
366 to->terminator=from->terminator;
367 to->cmd=from->cmd;
368 to->shellname=from->shellname;
369 to->stderrname=from->stderrname;
370}/*extHandlerSwallow*/
371
372/*Allocates memory for fields of handler which have no fixed
373 storage size and initializes some fields:*/
374static FORM_INLINE void
375extHandlerAlloc(EXTHANDLE *h, char *cmd, char *shellname, char *stderrname)
376{
377 h->IBfill=h->IBfull=h->INbuf=
378 Malloc1(DELTA_EXT_BUF,"External channel buffer");
379 h->IBstop=h->INbuf+DELTA_EXT_BUF;
380 /*Initialize a terminator:*/
381 *(h->terminator=Malloc1(DELTA_EXT_BUF,"External channel terminator"))='\n';
382 (h->terminator)[1]='\0';/*By default the terminator is '\n'*/
383 /*Deep copy all strings:*/
384 if(cmd!=NULL)
385 h->cmd=(char *)strDup1((UBYTE *)cmd,"External channel command");
386 else/*cmd cannot be NULL! If this is NULL then force it to be something special*/
387 h->cmd=(char *)strDup1((UBYTE *)"/","External channel command");
388 if(shellname!=NULL)
389 h->shellname=
390 (char *)strDup1((UBYTE *)shellname,"External channel shell name");
391 if(stderrname!=NULL)
392 h->stderrname=
393 (char *)strDup1((UBYTE *)stderrname,"External channel stderr name");
394}/*extHandlerAlloc*/
395
396/*Disallocates dynamically allocated fields of a handler:*/
397static FORM_INLINE void extHandlerFree(EXTHANDLE *h)
398{
399 if(h->stderrname) M_free(h->stderrname,"External channel stderr name");
400 if(h->shellname) M_free(h->shellname,"External channel shell name");
401 if(h->cmd) M_free(h->cmd,"External channel command");
402 if(h->terminator)M_free(h->terminator,"External channel terminator");
403 if(h->INbuf)M_free(h->INbuf,"External channel buffer");
404 extHandlerInit(h);
405}/*extHandlerFree*/
406/* Closes all descriptors, kills the external process, frees all internal fields,
407BUT does NOT free the main container:*/
408static void destroyExternalChannel(EXTHANDLE *h)
409{
410 /*Note, this function works in parallel mode correctly, see comments below.*/
411
412 /*Note, for slaves in a parallel mode h->pid == 0:*/
413 if( (h->pid > 0) && (h->killSignal > 0) ){
414 int chstatus;
415 if( h->gpid > 0)
416 chstatus=kill(-h->gpid,h->killSignal);
417 else
418 chstatus=kill(h->pid,h->killSignal);
419 if(chstatus==0)
420 /*If the process will not be killed by this signal, FORM hangs up here!:*/
421 waitpid(h->pid, &chstatus, 0);
422 }/*if( (h->pid > 0) && (h->killSignal > 0) )*/
423
424 /*Note, for slaves in a parallel mode h->frec == h->fsend == 0:*/
425 if(h->frec) fclose(h->frec);
426 if( h->fsend > 0) close(h->fsend);
427
428 extHandlerFree(h);
429 /*Does not do "free(h)"!*/
430}/*destroyExternalChannel*/
431
432/*Wrapper to the read() syscall, to handle possible interrupts by unblocked signals:*/
433static FORM_INLINE ssize_t read2b(int fd, char *buf, size_t count)
434{
435ssize_t res;
436
437 if( (res=read(fd,buf,count)) <1 )/*EOF or read is interrupted by a signal?:*/
438 while( (errno == EINTR)&&(res <1) )
439 /*The call was interrupted by a signal before any data was read, try again:*/
440 res=read(fd,buf,count);
441 return (res);
442}/*read2b*/
443
444/*Wrapper to the write() syscall, to handle possible interrupts by unblocked signals:*/
445static FORM_INLINE ssize_t writeFromb(int fd, char *buf, size_t count)
446{
447ssize_t res;
448 if( (res=write(fd,buf,count)) <1 )/*Is write interrupted by a signal?:*/
449 while( (errno == EINTR)&&(res <1) )
450 /*The call was interrupted by a signal before any data was written, try again:*/
451 res=write(fd,buf,count);
452 return (res);
453}/*writeFromb*/
454
455/* Read one (binary) PID from the file descriptor fd:*/
456static FORM_INLINE pid_t readpid(int fd)
457{
458pid_t tmp;
459 if(read2b(fd,(char*)&tmp,sizeof(pid_t))!=sizeof(pid_t))
460 return (pid_t)-1;
461 return tmp;
462}/*readpid*/
463
464/* Writeone (binary) PID to the file descriptor fd:*/
465static FORM_INLINE pid_t writepid(int fd, pid_t thepid)
466{
467 if(writeFromb(fd,(char*)&thepid,sizeof(pid_t))!=sizeof(pid_t))
468 return (pid_t)-1;
469 return (pid_t)0;
470}/*readpid*/
471
472/*Wrtites exactly count bytes from the buffer buf into the descriptor fd, independently on
473 nonblocked signals and the MPU/buffer hits. Returns 0 or -1:
474*/
475static FORM_INLINE int writexactly(int fd, char *buf, size_t count)
476{
477ssize_t i;
478int j=0,n=0;
479
480 for(;;){
481 if( (i=writeFromb(fd, buf+j, count-j)) < 0 ) return(-1);
482 j+=i;
483 if ( ((size_t)j) == count ) break;
484 if(i==0)n++;
485 else n=0;
486 if(n>MAX_FAILS_IO)return (-1);
487 }/*for(;;)*/
488 return (0);
489}/*writexactly*/
490
491/* Set the FD_CLOEXEC flag of desc if value is nonzero,
492 or clear the flag if value is 0.
493 Return 0 on success, or -1 on error with errno set. */
494static int set_cloexec_flag(int desc, int value)
495{
496int oldflags = fcntl (desc, F_GETFD, 0);
497 /* If reading the flags failed, return error indication now.*/
498 if (oldflags < 0)
499 return (oldflags);
500 /* Set just the flag we want to set. */
501 if (value != 0)
502 oldflags |= FD_CLOEXEC;
503 else
504 oldflags &= ~FD_CLOEXEC;
505 /* Store modified flag word in the descriptor. */
506 return (fcntl(desc, F_SETFD, oldflags));
507}/*set_cloexec_flag*/
508
509/* Adds the integer fd to the array fifo of length top+1 so that
510the array is ascendantly ordered. It is supposed that all 0 -- top-1
511elements in the array are already ordered:*/
512static void pushDescriptor(int *fifo, int top, int fd)
513{
514 if ( top == 0 ) {
515 fifo[top] = fd;
516 } else {
517 int ins=top-1;
518 if( fifo[ins]<=fd )
519 fifo[top]=fd;
520 else{
521 /*Find the position:*/
522 while( (ins>=0)&&(fifo[ins]>fd) )ins--;
523 /*Move all elements starting from the position to the right:*/
524 for(ins++;top>ins; top--)
525 fifo[top]=fifo[top-1];
526 /*Put the element:*/
527 fifo[ins]=fd;
528 }
529 }
530}/*pushDescriptor*/
531
532/*Close all descriptors greater or equal than startFrom except those
533 listed in the ascendantly ordered array usedFd of length top:*/
534static FORM_INLINE void closeAllDescriptors(int startFrom, int *usedFd, int top)
535{
536int n,maxfd;
537 for(n=0;n<top; n++){
538 maxfd=usedFd[n];
539 for(;startFrom<maxfd;startFrom++)/*Close all less than maxfd*/
540 close(startFrom);
541 startFrom++;/*skip maxfd*/
542 }/*for(;startFrom<maxfd;startFrom++)*/
543 /*Close all the rest:*/
544 maxfd=sysconf(_SC_OPEN_MAX);
545 for(;startFrom<maxfd;startFrom++)
546 close(startFrom);
547}/*closeAllDescriptors*/
548
549typedef int L_APIPE[2];
550/*Closes both pipe descriptors if not -1:*/
551static void closepipe(L_APIPE *thepipe)
552{
553 if( (*thepipe)[0] != -1) close ((*thepipe)[0]);
554 if( (*thepipe)[1] != -1) close ((*thepipe)[1]);
555}/*closepipe*/
556
557/*Parses the cmd line like "sh -c myprg", passes each option to the
558 corresponding element of argv, ends agrv by NULL. Returns the
559 number of stored argv elements, or -1 if fails:*/
560static FORM_INLINE int parseline(char **argv, char *cmd)
561{
562int n=0;
563 while(*cmd != '\0'){
564 for(; (*cmd <= ' ') && (*cmd != '\0') ;cmd++);
565 if(*cmd != '\0'){
566 argv[n]=cmd;
567 while(*++cmd > ' ');
568 if(*cmd != '\0')
569 *cmd++ = '\0';
570 n++;
571 }/*if(*cmd != '\0')*/
572 }/*while(*cmd != '\0')*/
573 argv[n]=NULL;
574 if(n==0)return -1;
575 return n;
576}/*parseline*/
577
578/*Reads positive decimal number (not bigger than maxnum)
579 from the string and returns it;
580 the pointer *b is set to the next non-converted character:*/
581static LONG str2i(char *str, char **b, LONG maxnum)
582{
583 LONG n=0;
584 /*Eat trailing spaces:*/
585 while(*str<=' ')if(*str++ == '\0')return(-1);
586 (*b)=str;
587 while (*str>='0'&&*str<='9')
588 if( (n=10*n + *str++ - '0')>maxnum )
589 return(-1);
590 if((*b)==str)/*No single number!*/
591 return(-1);
592 (*b)=str;
593 return(n);
594}
595
596/*Converts long integer to a decimal representation.
597 For portability reasons we cannot use LongCopy from tools.c
598 since theoretically LONG may be smaller than pid_t:*/
599static char *l2s(LONG x, char *to)
600{
601 char *s;
602 int i = 0, j;
603 s = to;
604 do { *s++ = (x % 10)+'0'; i++; } while ( ( x /= 10 ) != 0 );
605 *s-- = '\0';
606 j = ( i - 1 ) >> 1;
607 while ( j >= 0 ) {
608 i = to[j]; to[j] = s[-j]; s[-j] = (char)i; j--;
609 }
610 return(s+1);
611}
612
613/*like strcat() but returns the pointer to the end of the
614resulting string:*/
615static FORM_INLINE char *addStr(char *to, char *from)
616{
617 while( (*to++ = *from++)!='\0' );
618 return(to-1);
619}/*addStr*/
620
621
622/*Try to write (atomically) short buffer (of length count) to fd.
623 timeout is a timeout in millisecs. Returns number of written bytes or -1:*/
624static FORM_INLINE ssize_t writeSome(int fd, char *buf, size_t count, int timeout)
625{
626 ssize_t res = 0;
627 fd_set wfds;
628 struct timeval tv;
629 int nrep=5;/*five attempts it interrupted by a non-blocking signal*/
630 int flags = fcntl(fd, F_GETFL,0);
631
632 /*Add O_NONBLOCK:*/
633 fcntl(fd,F_SETFL, flags | O_NONBLOCK);
634 /* important -- in order to avoid blocking of short receiver buffer*/
635
636 do{
637 FD_ZERO(&wfds);
638 FD_SET(fd, &wfds);
639 /* Wait up to timeout. */
640
641 tv.tv_sec =timeout /1000;
642 tv.tv_usec = (timeout % 1000)*1000;
643 nrep--;
644
645 switch(select(fd+1, NULL, &wfds, NULL, &tv)){
646 case -1:
647 if((nrep == 0)||( errno != EINTR) ){
648 perror("select()");
649 res=-1;
650 nrep=0;
651 }/*else -- A non blocked signal was caught, just repeat*/
652 break;
653 case 0:/*timeout*/
654 res=-1;
655 nrep=0;
656 break;
657 default:
658 if( (res=write(fd,buf,count)) <0 )/*Signal?*/
659 while( (errno == EINTR)&&(res <0) )
660 res=write(fd,buf,count);
661 nrep=0;
662 }/*switch*/
663 }while(nrep);
664
665 /*restore the flags:*/
666 fcntl(fd,F_SETFL, flags);
667 return (res);
668}/*writeSome*/
669
670/*Try to read short buffer (of length not more than count)
671 from fd. timeout is a timeout in millisecs. Returns number
672 of written bytes or -1: */
673static FORM_INLINE ssize_t readSome(int fd, char *buf, size_t count, int timeout)
674{
675 ssize_t res = 0;
676 fd_set rfds;
677 struct timeval tv;
678 int nrep=5;/*five attempts it interrupted by a non-blocking signal*/
679
680 do{
681 FD_ZERO(&rfds);
682 FD_SET(fd, &rfds);
683 /* Wait up to timeout. */
684
685 tv.tv_sec = timeout/1000;
686 tv.tv_usec = (timeout % 1000)*1000;
687 nrep--;
688
689 switch(select(fd+1, &rfds, NULL, NULL, &tv)){
690 case -1:
691 if((nrep == 0)||( errno != EINTR) ){
692 perror("select()");
693 res=-1;
694 nrep=0;
695 }/*else -- A non blocked signal was caught, just repeat*/
696 break;
697 case 0:/*timeout*/
698 res=-1;
699 nrep=0;
700 break;
701 default:
702 if( (res=read(fd,buf,count)) <0 )/*Signal?*/
703 while( (errno == EINTR)&&(res <0) )
704 res=read(fd,buf,count);
705 nrep=0;
706 }/*switch*/
707 }while(nrep);
708 return (res);
709}/*readSome*/
710
711/*
712 #] Local functions :
713 #[ Ok functions:
714*/
715
716/*Copies (deep copy) newTerminator to thehandler->terminator. Returns 0 if
717 newTerminator fits to the buffer, or !0 if it does not fit. ATT! In the
718 latter case thehandler->terminator is NOT '\0' terminated! */
719int setTerminatorForExternalChannelOk(char *newTerminator)
720{
721int i=DELTA_EXT_BUF;
722/*
723 No problems with externalChannelsListTop are
724 possible since this function may be invoked only
725 when the current channel is defined and externalChannelsListTop
726 is set properly
727*/
728char *t=externalChannelsListTop->terminator;
729
730 for(; i>1; i--)
731 if( (*t++ = *newTerminator++)=='\0' )
732 break;
733 /*Add trailing '\n', if absent:*/
734 if( (i == DELTA_EXT_BUF)/*newTerminator == '\0'*/
735 ||(*(t-2)!='\n') ){
736 *(t-1)='\n';*t='\0';
737 }
738 return(i==1);
739}/*setTerminatorForExternalChannelOk*/
740
741/*Interface to change handler fields "killSignal" and "gpid"*/
742int setKillModeForExternalChannelOk(int signum, int sentToWholeGroup)
743{
744 if(signum<0)
745 return(-1);
746 /*
747 No problems with externalChannelsListTop are
748 possible since this function may be invoked only
749 when the current channel is defined and externalChannelsListTop
750 is set properly
751 */
752 externalChannelsListTop->killSignal=signum;
753 if(sentToWholeGroup){/*gpid must be >0*/
754 if(externalChannelsListTop->gpid <= 0)
755 externalChannelsListTop->gpid=-externalChannelsListTop->gpid;
756 }else{/*gpid must be <=0*/
757 if(externalChannelsListTop->gpid>0)
758 externalChannelsListTop->gpid=-externalChannelsListTop->gpid;
759 }
760 return(0);
761}/*setKillModeForExternalChannelOk*/
762
763/*
764 #[ getcFromExtChannelOk
765*/
766/*Returns one character from the external channel. It the input is expired,
767returns EOF. If the external process is finished completely, the function closes
768the channel (and returns EOF). If the external process was finished, the function
769returns EOF:*/
770int getcFromExtChannelOk(void)
771{
772mysighandler_t oldPIPE = 0;
773EXTHANDLE *h;
774int ret;
775
776 if (externalChannelsListTop->IBfill < externalChannelsListTop->IBfull)
777 /*in buffer*/
778 return( *(externalChannelsListTop->IBfill++) );
779 /*else -- the buffer is empty*/
780 ret=EOF;
781 h= externalChannelsListTop;
782#ifdef WITHMPI
783 if ( PF.me == MASTER ){
784#endif
785 /* Temporary ignore this signal:*/
786 /* if compiler fails here, try to change the definition of
787 mysighandler_t on the beginning of this file
788 (just define INTSIGHANDLER).*/
789 oldPIPE=signal(SIGPIPE,SIG_IGN);
790#ifdef WITHMPI
791 if( fgets(h->INbuf,h->IBstop - h->INbuf, h->frec) == 0 )/*Fail! EOF?*/
792 *(h->INbuf)='\0';/*Empty line may not appear here!*/
793#else
794 if( (fgets(h->INbuf,h->IBstop - h->INbuf, h->frec) == 0)/*Fail! EOF?*/
795 ||( *(h->INbuf) == '\0')/*Empty line? This shouldn't be!*/
796 ){
797 closeExternalChannel(externalChannelsListTop-externalChannelsList+1);
798 /*Note, this code is only for the sequential mode! */
799 goto getcFromExtChannelReady;
800 /*Here we assume that fgets is never interrupted by singals*/
801 }/*if( fgets(h->INbuf,h->IBstop - h->INbuf, h->frec) == 0 )*/
802#endif
803#ifdef WITHMPI
804 }/*if ( PF.me == MASTER */
805
806 /*Master broadcasts result to slaves, slaves read it from the master:*/
807 if( PF_BroadcastString((UBYTE *)h->INbuf) ){/*Fail!*/
808 MesPrint("Fail broadcasting external channel results");
809 Terminate(-1);
810 }/*if( PF_BroadcastString((UBYTE *)h->INbuf) )*/
811
812 if( *(h->INbuf) == '\0'){/*Empty line? This shouldn't be!*/
813 closeExternalChannel(externalChannelsListTop-externalChannelsList+1);
814 goto getcFromExtChannelReady;
815 }/*if( *(h->INbuf) == '\0')*/
816#endif
817
818 {/*Block*/
819 char *t=h->terminator;
820 /*Move IBfull to the end of read line and compare the line with the terminator.
821 Note, by construction the terminator fits to the first read line, see
822 the function setTerminatorForExternalChannel.*/
823 for(h->IBfull=h->INbuf; *(h->IBfull)!='\0'; (h->IBfull)++)
824 if( *t== *(h->IBfull) )
825 t++;
826 else
827 break;/*not a terminator*/
828 /*Continue moving IBfullto the end of read line:*/
829 while(*(h->IBfull)!='\0')(h->IBfull)++;
830
831 if( (t-h->terminator) == (h->IBfull-h->INbuf) ){
832 /*Terminator!*/
833 /*Reset the channel*/
834 h->IBfull=h->IBfill=h->INbuf;
835 externalChannelsListTop=0;/*Undefine the current channel*/
836 writeBufToExtChannel=&writeBufToExtChannelFailure;
837 getcFromExtChannel=&getcFromExtChannelFailure;
838 setTerminatorForExternalChannel=&setTerminatorForExternalChannelFailure;
839 setKillModeForExternalChannel=&setKillModeForExternalChannelFailure;
840 goto getcFromExtChannelReady;
841 }/*if(t == (h->IBfull-h->INbuf) )*/
842 }/*Block*/
843 /*Does the buffer have enough capacity?*/
844 while( *(h->IBfull - 1) != '\n' ){/*Buffer is not enough!*/
845 /*Extend the buffer:*/
846 int l= (h->IBstop - h->INbuf)+DELTA_EXT_BUF;
847 char *newbuf=Malloc1(l,"External channel buffer");
848 /*We wouldn't like to use realloc.*/
849 /*Copy the buffer:*/
850 char *n=newbuf,*o=h->INbuf;
851 while( (*n++ = *o++)!='\0' );
852 /*Att! The order of the following operators is important!:*/
853 h->IBfull= newbuf+(h->IBfull-h->INbuf);
854 M_free(h->INbuf,"External channel buffer");
855 h->INbuf = newbuf;
856 h->IBstop = h->INbuf+l;
857#ifdef WITHMPI
858 if ( PF.me == MASTER ){
859 (h->IBfull)[1]='\0';/*Will mark (h->IBfull)[1] as '!' for failure*/
860 if( fgets(h->IBfull,h->IBstop - h->IBfull, h->frec) == 0 ){
861 /*EOF! No trailing '\n'?*/
862 /*Mark:*/
863 (h->IBfull)[0]='\0';
864 (h->IBfull)[1]='!';
865 (h->IBfull)[2]='\0';
866 /*The string "\0!\0" is used as an image of NULL.*/
867 }/*if( fgets(h->IBfull,h->IBstop - h->IBfull, h->frec) == 0 )*/
868 }/*if ( PF.me == MASTER )*/
869
870 /*Master broadcasts results to slaves, slaves read it from the master:*/
871 if( PF_BroadcastString((UBYTE *)h->IBfull) ){/*Fail!*/
872 MesPrint("Fail broadcasting external channel results");
873 Terminate(-1);
874 }/*if( PF_BroadcastString(h->IBfull) )*/
875
876 /*The string "\0!\0" is used as the image of NULL.*/
877 if(
878 ( (h->IBfull)[0]=='\0' )
879 &&( (h->IBfull)[1]=='!' )
880 &&( (h->IBfull)[2]=='\0' )
881 )/*EOF! No trailing '\n'?*/
882 break;
883#else
884 if( fgets(h->IBfull,h->IBstop - h->IBfull, h->frec) == 0 )
885 /*EOF! No trailing '\n'?*/
886 break;
887#endif
888 while( *(h->IBfull)!='\0' )(h->IBfull)++;
889 }/*while( *(h->IBfull - 1) != '\n' )*/
890 /*In h->INbuf we have a fresh string.*/
891 ret=*(h->IBfill=h->INbuf);
892 h->IBfill++;/*Next time a new, isn't it?*/
893
894 getcFromExtChannelReady:
895#ifdef WITHMPI
896 if ( PF.me == MASTER ){
897#endif
898 signal(SIGPIPE,oldPIPE);
899#ifdef WITHMPI
900 }/*if ( PF.me == MASTER )*/
901#endif
902 return(ret);
903}/*getcFromExtChannelOk*/
904/*
905 #] getcFromExtChannelOk
906*/
907/*Writes exactly count bytes from the buffer buf to the external channel thehandler
908 Returns 0 (on success) or -1:
909*/
910int writeBufToExtChannelOk(char *buf, size_t count)
911{
912
913int ret;
914mysighandler_t oldPIPE;
915#ifdef WITHMPI
916 /*Only master communicates with the external program:*/
917 if ( PF.me == MASTER ){
918#endif
919 /* Temporary ignore this signal:*/
920 /* if compiler fails here, try to change the definition of
921 mysighandler_t on the beginning of this file
922 (just define INTSIGHANDLER)*/
923 oldPIPE=signal(SIGPIPE,SIG_IGN);
924 ret=writexactly( externalChannelsListTop->fsend, buf, count);
925 signal(SIGPIPE,oldPIPE);
926#ifdef WITHMPI
927 }else{
928 /*Do not wait the master status: this would be too slow!*/
929 ret=0;
930 }
931#endif
932 return(ret);
933}/*writeBufToExtChannel*/
934/*
935 #] Ok functions:
936 #[ do_run_cmd :
937*/
938/*The function returns PID of the started command*/
939static FORM_INLINE pid_t do_run_cmd(
940 int *fdsend,
941 int *fdreceive,
942 int *gpid, /*returns group process ID*/
943 int ttymode,
944 /*
945 &8 - daemonizeing
946 &16 - setsid()*/
947 char *cmd,
948 char *argv[],
949 char *stderrname
950 )
951{
952int fdin[2]={-1,-1}, fdout[2]={-1,-1}, fdsig[2]={-1,-1};
953/*initialised by -1 for possible rollback at failure, see closepipe() above*/
954
955pid_t childpid,fatherchildpid = (pid_t)0;
956mysighandler_t oldPIPE=NULL;
957
958 if(
959 (pipe(fdsig)!=0)/*This pipe will be used by a child to tell the father if fail.*/
960 ||(pipe(fdin)!=0)
961 ||(pipe(fdout)!=0)
962 )goto fail_do_run_cmd;
963
964 if((childpid = fork()) == -1){
965 perror("fork");
966 goto fail_do_run_cmd;
967 }/*if((childpid = fork()) == -1)*/
968
969 if(childpid == 0){/*Child.*/
970 int fifo[3], top=0;
971 /*
972 To be thread safely we can't rely on ascendant order of opened
973 file descriptors. So we put each of descriptor we have to
974 preserve into the array fifo.
975 Note, in _this_ process there are no any threads but descriptors
976 were created in frame of the parent process which may have
977 multiple threads.
978 */
979 /*Mark descriptors which will NOT be closed:*/
980 pushDescriptor(fifo,top++,fdsig[1]);
981 pushDescriptor(fifo,top++,fdin[0]);
982 pushDescriptor(fifo,top++,fdout[1]);
983 /*Close all except stdin, stdout, stderr and placed into fifo:*/
984 closeAllDescriptors(3,fifo, top);
985
986 /*Now reopen stdin and stdout.*/
987 /*thread-safety is not a problem here since there are no any threads up to now:*/
988 if(
989 (close(0) == -1 )||/* Use fdin as stdin :*/
990 (dup(fdin[0]) == -1 )||
991 (close(1)==-1)||/* Use fdout as stdout:*/
992 (dup(fdout[1]) == -1 )
993 )
994 {/*Fail!*/
995 /*Signal to parent:*/
996 writepid(fdsig[1],(pid_t)-2);
997 _exit(1);
998 }
999
1000 if(stderrname != NULL){
1001 if(
1002 (close(2) != 0 )||
1003 (open(stderrname,O_WRONLY)<0)
1004 )
1005 {/*Fail!*/
1006 writepid(fdsig[1],(pid_t)-2);
1007 _exit(1);
1008 }
1009 }/*if(stderrname != NULL)*/
1010
1011 if( ttymode & 16 )/* create a session and sets the process group ID */
1012 setsid();
1013
1014 /* */
1015 if(set_cloexec_flag (fdsig[1], 1)!=0){/*Error?*/
1016 /*Signal to parent:*/
1017 writepid(fdsig[1],(pid_t)-2);
1018 _exit(1);
1019 }/*if(set_cloexec_flag (fdsig[1], 1)!=0)*/
1020
1021 if( ttymode & 8 ){/*Daemonize*/
1022 int fdsig2[2];/*To check exec() success*/
1023 if(
1024 pipe(fdsig2)||
1025 (set_cloexec_flag (fdsig2[1], 1)!=0)
1026 )
1027 {/*Error?*/
1028 /*Signal to parent:*/
1029 writepid(fdsig[1],(pid_t)-2);
1030 _exit(1);
1031 }
1032 set_cloexec_flag (fdsig2[0], 1);
1033 switch(childpid=fork()){
1034 case 0:/*grandchild*/
1035 /*Execute external command:*/
1036 execvp(cmd, argv);
1037 /* Control can reach this point only on error!*/
1038 writepid(fdsig2[1],(pid_t)-2);
1039 break;
1040 case -1:
1041 /* Control can reach this point only on error!*/
1042 /*Inform the father about the failure*/
1043 writepid(fdsig[1],(pid_t)-2);
1044 _exit(1);/*The child, just exit, not return*/
1045 default:/*Son of his father*/
1046 close(fdsig2[1]);
1047 /*Ignore SIGPIPE (up to the end of the process):*/
1048 signal(SIGPIPE,SIG_IGN);
1049
1050 /*Wait on read() while the grandchild close the pipe
1051 (on success) or send -2 (if exec() fails).*/
1052 /*There are two possibilities:
1053 -1 -- this is ok, the pipe was closed on exec,
1054 the program was successfully executed;
1055 -2 -- something is wrong, exec failed since the
1056 grandchild sends -2 after exec.
1057 */
1058 if( readpid(fdsig2[0]) != (pid_t)-1 )/*something is wrong*/
1059 writepid(fdsig[1],(pid_t)-1);
1060 else/*ok, send PID of the grandchild to the father:*/
1061 writepid(fdsig[1],childpid);
1062 /*Die and free the life space for the grandchild:*/
1063 _exit(0);/*The child, just exit, not return*/
1064 }/*switch(childpid=fork())*/
1065 }else{/*if( ttymode & 8 )*/
1066 execvp(cmd, argv);
1067 /* Control can reach this point only on error!*/
1068 writepid(fdsig[1],(pid_t)-2);
1069 _exit(2);/*The child, just exit, not return*/
1070 }/*if( ttymode & 8 )...else*/
1071 }else{/* The (grand)father*/
1072 close(fdsig[1]);
1073 /*To prevent closing fdsig in rollback:*/
1074 fdsig[1]=-1;
1075 close(fdin[0]);
1076 close(fdout[1]);
1077 *fdsend = fdin[1];
1078 *fdreceive = fdout[0];
1079
1080 /*Get the process group ID.*/
1081 /*Avoid to use getpgid() which is non-standard.*/
1082 if( ttymode & 16)/*setsid() was invoked, the child is a group leader:*/
1083 *gpid=childpid;
1084 else/*the child belongs to the same process group as the this process:*/
1085 *gpid=getpgrp();/*if compiler fails here, try getpgrp(0) instead!*/
1086 /*
1087 Rationale: getpgrp conform to POSIX.1 while 4.3BSD provides a
1088 getpgrp() function that returns the process group ID for a
1089 specified process.
1090 */
1091
1092 /* Temporary ignore this signal:*/
1093 /* if compiler fails here, try to change the definition of
1094 mysighandler_t on the beginning of this file
1095 (just define INTSIGHANDLER)*/
1096 oldPIPE=signal(SIGPIPE,SIG_IGN);
1097
1098 if( ttymode & 8 ){/*Daemonize*/
1099 /*Read the grandchild PID from the son.*/
1100 fatherchildpid=childpid;
1101 if( (childpid=readpid(fdsig[0]))<0 ){
1102 /*Daemonization process fails for some reasons!*/
1103 childpid=fatherchildpid;/*for rollback*/
1104 goto fail_do_run_cmd;
1105 }
1106 }else{
1107 /*fdsig[1] should be closed on exec and this read operation
1108 must fail on success:*/
1109 if( readpid(fdsig[0])!= (pid_t)-1 )
1110 goto fail_do_run_cmd;
1111 }/*if( ttymode & 8 ) ... else*/
1112 }/*if(childpid == 0)...else*/
1113
1114 /*Here can be ONLY the father*/
1115 close(fdsig[0]);
1116 /*To prevent closing fdsig in rollback after goto fail_flnk_do_runcmd:*/
1117 fdsig[0]=-1;
1118
1119 if( ttymode & 8 ){/*Daemonize*/
1120 int i;
1121 /*Wait while the father of a grandchild dies:*/
1122 waitpid(fatherchildpid,&i,0);
1123 }
1124
1125 /*Restore the signal:*/
1126 signal(SIGPIPE,oldPIPE);
1127
1128 return(childpid);
1129
1130 fail_do_run_cmd:
1131 closepipe(&fdout);
1132 closepipe(&fdin);
1133 closepipe(&fdsig);
1134 return((pid_t)-1);
1135}/*do_run_cmd*/
1136/*
1137 #] do_run_cmd :
1138 #[ run_cmd :
1139*/
1140/*Starts the command cmd (directly, if shellpath is NULL, or in a subshell),
1141 swallowing its stdin and stdout;
1142 stderr will be re-directed to stderrname (if !=NULL). Returns PID of
1143 the started process. Stdin will be available as fdsend, and stdout
1144 will be available as fdreceive:*/
1145static FORM_INLINE pid_t run_cmd(char *cmd,
1146 int *fdsend,
1147 int *fdreceive,
1148 int *gpid,
1149 int daemonize,
1150 char *shellpath,
1151 char *stderrname )
1152{
1153char **argv;
1154pid_t thepid;
1155
1156 cmd=(char*)strDup1((UBYTE*)cmd, "run_cmd: cmd");/*detouch cmd*/
1157
1158
1159 /* Prepare arguments for execvp:*/
1160 if(shellpath != NULL){/*Run in a subshell.*/
1161 int nopt;
1162 /*Allocate space which is definitely enough:*/
1163 argv=Malloc1(StrLen((UBYTE*)shellpath)*sizeof(char*)+2,"run_cmd:argv");
1164 shellpath=(char*)strDup1((UBYTE*)shellpath, "run_cmd: shellpath");/*detouch shellpath*/
1165 /*Parse a shell (e.g., "/bin/sh -c"):*/
1166 nopt=parseline(argv, shellpath);
1167 /* and add the command as a shell argument:*/
1168 argv[nopt]=cmd;
1169 argv[nopt+1]=NULL;
1170 }else{/*Run the command directly:*/
1171 /*Allocate space which is definitely enough:*/
1172 argv=Malloc1(StrLen((UBYTE*)cmd)*sizeof(char*)+1,"run_cmd:argv");
1173 parseline(argv, cmd);
1174 }
1175
1176 thepid=do_run_cmd(
1177 fdsend,
1178 fdreceive,
1179 gpid,
1180 (daemonize)?(8|16):0,
1181 argv[0],
1182 argv,
1183 stderrname
1184 );
1185
1186 M_free(argv,"run_cmd:argv");
1187 if(shellpath)
1188 M_free(shellpath,"run_cmd:argv");
1189 M_free(cmd, "run_cmd: cmd");
1190
1191 return(thepid);
1192}/*run_cmd*/
1193/*
1194 #] run_cmd :
1195 #[ createExternalChannel :
1196*/
1197/*The structure to pass parameters to createExternalChannel
1198 and openExternalChannel in case of preset channel (instead of
1199 shellname):*/
1200typedef struct{
1201 int fdin;
1202 int fdout;
1203 pid_t theppid;
1204}ECINFOSTRUCT;
1205
1206/* Creates a new external channel starting the command cmd (if cmd !=NULL)
1207 or using information from (ECINFOSTRUCT *)shellname, if cmd ==NULL:*/
1208static FORM_INLINE void *createExternalChannel(
1209 EXTHANDLE *h,
1210 char *cmd, /*Command to run or NULL*/
1211 /*0 --neither setsid nor daemonize, !=0 -- full daemonization:*/
1212 int daemonize,
1213 char *shellname,/* The shell (like "/bin/sh -c") or NULL*/
1214 char *stderrname/*filename to redirect stderr or NULL*/
1215 )
1216{
1217 int fdreceive=0;
1218 int gpid = 0;
1219 ECINFOSTRUCT *psetInfo;
1220#ifdef WITHMPI
1221 char statusbuf[2]={'\0','\0'};/*'\0' if run_cmd retuns ok, '!' otherwise.*/
1222#endif
1223 extHandlerInit(h);
1224
1225 h->pid=0;
1226
1227 if( cmd==NULL ){/*Instead of starting a new command, use preset channel:*/
1228 psetInfo=(ECINFOSTRUCT *)shellname;
1229 shellname=NULL;
1230 h->killSignal=0;
1231 h->daemonize=0;
1232 }
1233
1234 /*Create a channel:*/
1235#ifdef WITHMPI
1236 if ( PF.me == MASTER ){
1237#endif
1238 if(cmd!=NULL)
1239 h->pid=run_cmd (cmd, &(h->fsend),
1240 &fdreceive,&gpid,daemonize,shellname,stderrname);
1241 else{
1242 gpid=-psetInfo->theppid;
1243 h->pid=psetInfo->theppid;
1244 h->fsend=psetInfo->fdout;
1245 fdreceive=psetInfo->fdin;
1246 }
1247#ifdef WITHMPI
1248 if(h->pid<0)
1249 statusbuf[0]='!';/*Broadcast fail to slaves*/
1250 }
1251 /*else: Keep h->pid = 0 and h->fsend = 0 for slaves in parallel mode!*/
1252
1253 /*Master broadcasts status to slaves, slaves read it from the master:*/
1254 if( PF_BroadcastString((UBYTE *)statusbuf) ){/*Fail!*/
1255 h->pid=-1;
1256 }else if( statusbuf[0]=='!')/*Master fails*/
1257 h->pid=-1;
1258#endif
1259
1260 if(h->pid<0)goto createExternalChannelFails;
1261#ifdef WITHMPI
1262 if ( PF.me == MASTER ){
1263#endif
1264 h->gpid=gpid;
1265 /*Open stdout of a newly created program as FILE* :*/
1266 if( (h->frec=fdopen(fdreceive,"r")) == 0 )goto createExternalChannelFails;
1267
1268#ifdef WITHMPI
1269 }
1270#endif
1271 /*Initialize buffers:*/
1272 extHandlerAlloc(h,cmd,shellname,stderrname);
1273 return(h);
1274 /*Something is wrong?*/
1275 createExternalChannelFails:
1276
1277 destroyExternalChannel(h);
1278 return(NULL);
1279}/*createExternalChannel*/
1280
1281/*
1282 #] createExternalChannel :
1283 #[ openExternalChannel :
1284*/
1285int openExternalChannel(UBYTE *cmd, int daemonize, UBYTE *shellname, UBYTE *stderrname)
1286{
1287EXTHANDLE *h=externalChannelsListTop;
1288int i=0;
1289
1290 for(externalChannelsListTop=0;i<externalChannelsListFill;i++)
1291 if(externalChannelsList[i].cmd==0){
1292 externalChannelsListTop=externalChannelsList+i;
1293 break;
1294 }/*if(externalChannelsList[i].cmd!=0)*/
1295
1296 if(externalChannelsListTop==0){
1297 /*No free cells, create the new one:*/
1298 if(externalChannelsListFill == externalChannelsListStop){
1299 /*The list is full, increase and reallocate it:*/
1300 EXTHANDLE *newbuf=Malloc1(
1301 (externalChannelsListStop+=DELTA_EXT_LIST)*sizeof(EXTHANDLE),
1302 "External channel list");
1303 for(i=0;i<externalChannelsListFill;i++)
1304 extHandlerSwallowCopy(newbuf+i,externalChannelsList+i);
1305 if(externalChannelsList!=0)
1306 M_free(externalChannelsList,"External channel list");
1307 for(;i<externalChannelsListStop;i++)
1308 extHandlerInit(newbuf+i);
1309 externalChannelsList=newbuf;
1310 }/*if(externalChannelsListFill == externalChannelsListStop)*/
1311 externalChannelsListTop=externalChannelsList+externalChannelsListFill;
1312 externalChannelsListFill++;
1313 }/*if(externalChannelsListTop==0)*/
1314
1315 if(createExternalChannel(
1316 externalChannelsListTop,
1317 (char*) cmd,
1318 daemonize,
1319 (char*)shellname,
1320 (char*)stderrname )!=NULL){
1321 writeBufToExtChannel=&writeBufToExtChannelOk;
1322 getcFromExtChannel=&getcFromExtChannelOk;
1323 setTerminatorForExternalChannel=&setTerminatorForExternalChannelOk;
1324 setKillModeForExternalChannel=&setKillModeForExternalChannelOk;
1325 return(externalChannelsListTop-externalChannelsList+1);
1326 }
1327 /*else - failure!*/
1328 externalChannelsListTop=h;
1329 return(-1);
1330}/*openExternalChannel*/
1331/*
1332 #] openExternalChannel :
1333 #[ initPresetExternalChannels :
1334*/
1335/*Just simple wrapper to invoke openExternalChannel()
1336 from initPresetExternalChannels():*/
1337static FORM_INLINE int openPresetExternalChannel(int fdin, int fdout, pid_t theppid)
1338{
1339ECINFOSTRUCT inf;
1340 inf.fdin=fdin; inf.fdout=fdout;inf.theppid=theppid;
1341 return( openExternalChannel(NULL,0,(UBYTE *)&inf,NULL) );
1342}/*openPresetExternalChannel*/
1343
1344#define PIDTXTSIZE 23
1345#define BOTHPIDSIZE 45
1346
1347#ifndef LONG_MAX
1348#define LONG_MAX 0x7FFFFFFFL
1349#endif
1350
1351/*There is a possibility to start FORM from another program providing
1352one (or more) communication channels. These channels will be
1353visible from a FORM program as ``pre-opened'' external channels existing
1354after FORM starts.
1355Before starting FORM, the parent application must create one or more
1356pairs of pipes The read-only descriptor of the first pipe in the pair
1357and the write-only descriptor of the second pipe must be passed to
1358FORM as an argument of a command line option -pipe in ASCII decimal
1359format. The argument of the option is a comma-separated list of pairs
1360r#,w# where r# is a read-only descriptor and w# is a write-only
1361descriptor. Alternatively, the environment variable FORM_PIPES can be
1362used.
1363 The following function expects as the first argument
1364this comma-separated list of the descriptor pairs and tries to
1365initialize each of channel during thetimeout milliseconds:*/
1366
1367int initPresetExternalChannels(UBYTE *theline, int thetimeout)
1368{
1369 int i, nchannels = 0;
1370 int pidln; /*The length of FORM PID in pidtxt*/
1371 char pidtxt[PIDTXTSIZE], /*64/Log_2[10] = 19.3, this is enough for any integer*/
1372 chdescriptor[PIDTXTSIZE],
1373 bothpidtxt[BOTHPIDSIZE], /*"#,#\n\0"*/
1374 *c,*b = 0;
1375 int pip[2];
1376 pid_t ppid;
1377 if ( theline == NULL ) return(-1);
1378 /*Put into pidtxt PID\n:*/
1379 c = l2s((LONG)getpid(),pidtxt);
1380 *c++='\n';
1381 *c = '\0';
1382 pidln = c-pidtxt;
1383
1384 do {
1385 pip[0] = (int)str2i((char*)theline,&c,0xFFFF);
1386 if( ( pip[0] < 0 ) || ( *c != ',' ) ) goto presetFails;
1387
1388 theline = (UBYTE*)c + 1;
1389 pip[1] = (int)str2i((char*)theline,&c,0xFFFF);
1390 if ( (pip[1] < 0 ) || ( ( *c != ',' ) && ( *c != '\0' ) ) ) goto presetFails;
1391 theline = (UBYTE *)c + 1;
1392 /*Now we have two descriptors.
1393 According to the protocol, FORM must send to external channel
1394 it's PID with added '\n' and read back two comma-separated
1395 decimals with added '\n'. The first must be repeated FORM PID,
1396 the second must be the parent PID
1397 */
1398 if ( writeSome(pip[1],pidtxt,pidln,thetimeout) != pidln ) goto presetFails;
1399 i = readSome(pip[0],bothpidtxt,BOTHPIDSIZE,thetimeout);
1400 if( ( i < 4 ) /*at least 1,2\n*/
1401 || ( i == BOTHPIDSIZE ) /*No space for trailing '\0'*/
1402 ) goto presetFails;
1403 /*read the FORM PID:*/
1404 ppid = (pid_t)str2i(bothpidtxt,&b,getpid());
1405 if( ( *b != ',' ) || ( ppid != getpid() ) )goto presetFails;
1406 /*read the parent PID:*/
1407 /*The problem is that we do not know the the real type of pid_t.
1408 But long should be enough. On obsolete systems (when LONG_MAX
1409 is not defined) we assume pid_t is 32-bit integer.
1410 This can lead to problem with portability: */
1411 ppid = (pid_t)str2i(b+1,&b,LONG_MAX);
1412 if ( (*b != '\n') || (ppid<2) ) goto presetFails;
1413 i = openPresetExternalChannel(pip[0],pip[1],ppid);
1414 if ( i < 0 ) goto presetFails;
1415 nchannels++;
1416 /*Now use bothpidtxt as a buffer for preprovar, the space is enough:*/
1417 /*"PIPE#_" where # is ne order number of the channel:*/
1418 b = l2s(nchannels,addStr(bothpidtxt,"PIPE"));
1419 *b = '_';
1420 b[1] = '\0';
1421 *l2s(i,chdescriptor) = '\0';
1422 PutPreVar((UBYTE*)bothpidtxt,(UBYTE*)chdescriptor,0,0);
1423 } while ( *c != '\0' );
1424 /*Export proprovar "PIPES_":*/
1425 *l2s(nchannels,chdescriptor)='\0';
1426 PutPreVar((UBYTE*)"PIPES_",(UBYTE*)chdescriptor,0,0);
1427
1428 /*success:*/
1429 return (nchannels);
1430
1431presetFails:
1432 /*Here we assume the descriptors the beginning of the list!*/
1433 for(i=0; i<nchannels; i++)
1434 destroyExternalChannel(externalChannelsList+i);
1435 return(-1);
1436} /*initPresetExternalChannels*/
1437/*
1438 #] initPresetExternalChannels :
1439 #[ selectExternalChannel :
1440*/
1441/*
1442Accepts the valid external channel descriptor (returned by
1443openExternalChannel) and returns the descriptor of a previous current
1444channel (0, if there was no current channel, or -1, if the external
1445channel descriptor is invalid). If n == 0, the function undefine the
1446current external channel:
1447*/
1448int selectExternalChannel(int n)
1449{
1450int ret=0;
1451 if(externalChannelsListTop!=0)
1452 ret=externalChannelsListTop-externalChannelsList+1;
1453
1454 if(--n<0){
1455 if(n!=-1)
1456 return(-1);
1457 externalChannelsListTop=0;
1458 writeBufToExtChannel=&writeBufToExtChannelFailure;
1459 getcFromExtChannel=&getcFromExtChannelFailure;
1460 setTerminatorForExternalChannel=&setTerminatorForExternalChannelFailure;
1461 setKillModeForExternalChannel=&setKillModeForExternalChannelFailure;
1462 return(ret);
1463 }
1464 if(
1465 (n>=externalChannelsListFill)||
1466 (externalChannelsList[n].cmd==0)
1467 )
1468 return(-1);
1469
1470 externalChannelsListTop=externalChannelsList+n;
1471 writeBufToExtChannel=&writeBufToExtChannelOk;
1472 getcFromExtChannel=&getcFromExtChannelOk;
1473 setTerminatorForExternalChannel=&setTerminatorForExternalChannelOk;
1474 setKillModeForExternalChannel=&setKillModeForExternalChannelOk;
1475 return(ret);
1476}/*selectExternalChannel*/
1477/*
1478 #] selectExternalChannel :
1479 #[ closeExternalChannel :
1480*/
1481
1482/*
1483Destroys the opened external channel with the descriptor n. It returns
14840 in success, or -1 on failure. If the corresponding external channel
1485was the current one, the current channel becomes undefined. If n==0,
1486the function closes the current external channel.
1487*/
1488int closeExternalChannel(int n)
1489{
1490 if(n==0)
1491 n=externalChannelsListTop-externalChannelsList;
1492 else
1493 n--;/*Count from 0*/
1494
1495 if(
1496 (n<0)||
1497 (n>=externalChannelsListFill)||
1498 (externalChannelsList[n].cmd==0)
1499 )/*No shuch a channel*/
1500 return(-1);
1501
1502 destroyExternalChannel(externalChannelsList+n);
1503 /*If the current external channel was destroyed, undefine current channel:*/
1504 if(externalChannelsListTop==externalChannelsList+n){
1505 externalChannelsListTop=NULL;
1506 writeBufToExtChannel=&writeBufToExtChannelFailure;
1507 getcFromExtChannel=&getcFromExtChannelFailure;
1508 setTerminatorForExternalChannel=&setTerminatorForExternalChannelFailure;
1509 setKillModeForExternalChannel=&setKillModeForExternalChannelFailure;
1510 }/*if(externalChannelsListTop==externalChannelsList+n)*/
1511 return(0);
1512}/*closeExternalChannel*/
1513/*
1514 #] closeExternalChannel :
1515 #[ closeAllExternalChannels :
1516*/
1517void closeAllExternalChannels(void)
1518{
1519int i;
1520 for(i=0; i<externalChannelsListFill; i++)
1521 destroyExternalChannel(externalChannelsList+i);
1522 externalChannelsListFill=externalChannelsListStop=0;
1523 externalChannelsListTop=NULL;
1524
1525 writeBufToExtChannel=&writeBufToExtChannelFailure;
1526 getcFromExtChannel=&getcFromExtChannelFailure;
1527 setTerminatorForExternalChannel=&setTerminatorForExternalChannelFailure;
1528 setKillModeForExternalChannel=&setKillModeForExternalChannelFailure;
1529
1530 if(externalChannelsList!=NULL){
1531 M_free(externalChannelsList,"External channel list");
1532 externalChannelsList=NULL;
1533 }
1534}/*closeAllExternalChannels*/
1535/*
1536 #] closeAllExternalChannels :
1537 #[ getExternalChannelPid :
1538*/
1539#ifdef SELFTEST
1540pid_t getExternalChannelPid(void)
1541{
1542 if(externalChannelsListTop!=0)
1543 return(externalChannelsListTop ->pid);
1544 return(-1);
1545}/*getExternalChannelPid*/
1546#endif
1547/*
1548 #] getExternalChannelPid :
1549 #[ getCurrentExternalChannel :
1550*/
1551
1552int getCurrentExternalChannel(void)
1553{
1554
1555 if ( externalChannelsListTop != 0 )
1556 return(externalChannelsListTop-externalChannelsList+1);
1557 return(0);
1558}/*getCurrentExternalChannel*/
1559/*
1560 #] getCurrentExternalChannel :
1561 #[ Selftest main :
1562*/
1563
1564#ifdef SELFTEST
1565
1566/*
1567 This is the example of how all these public functions may be used:
1568*/
1569
1570char buf[1024];
1571char buf2[1024];
1572
1573void help(void)
1574{
1575 printf("String started with a special letter is a command\n");
1576 printf("Known commands are:\n");
1577 printf("H or ? -- this help\n");
1578 printf("Nn<command> -- start a new command\n");
1579 printf("S<command> -- start a new command in a subshell,daemon,stderr>/dev/null\n");
1580 printf("C# -- destroy channel #\n");
1581 printf("R# -- set a new cahhel(number#) as a current one\n");
1582 printf("K#1 #2 -- set signal for kill and kill mode (0 or !=0)\n");
1583 printf(" ^d to quit\n");
1584}/*help*/
1585
1586int main (void)
1587{
1588 int i, j, k,last;
1589 long long sum = 0;
1590
1591 /*openExternalChannel(UBYTE *cmd, int daemonize, UBYTE *shellname, UBYTE *stderrname)*/
1592
1593 help();
1594
1595 printf("Initial channel:%d\n",last=openExternalChannel((UBYTE*)"cat",0,NULL,NULL));
1596
1597 if( ( i = setTerminatorForExternalChannel("qu") ) != 0 ) return 1;
1598 printf("Terminator is 'qu'\n");
1599
1600 while ( fgets(buf, 1024, stdin) != NULL ) {
1601 if ( *buf == 'N' ) {
1602 printf("New channel:%d\n",j=openExternalChannel((UBYTE*)buf+1,0,NULL,NULL));
1603 continue;
1604 }
1605 else if ( *buf == 'C' ) {
1606 int n;
1607 sscanf(buf+1,"%d",&n);
1608 printf("Destroy last channel:%d\n",closeExternalChannel(n));
1609 continue;
1610 }
1611 else if ( *buf == 'R' ) {
1612 int n = 0;
1613 sscanf(buf+1,"%d",&n);
1614 printf("Reopen channel %d:%d\n",n,selectExternalChannel(n));
1615 continue;
1616 }else if( *buf == 'K' ) {
1617 int n=0,g = 0;
1618 sscanf(buf+1,"%d %d",&n,&g);
1619 printf("setKillMode %d\n",setKillModeForExternalChannel(n,g));
1620 continue;
1621 }else if( *buf == 'S' ) {
1622 printf("New channel with sh&d&stderr:%d\n",
1623 j=openExternalChannel((UBYTE*)buf+1,1,(UBYTE*)"/bin/sh -c",(UBYTE*)"/dev/null"));
1624 continue;
1625 }
1626 else if( ( *buf == 'H' )|| ( *buf == '?' ) ){
1627 help();
1628 continue;
1629 }
1630
1631 writeBufToExtChannel(buf,k=StrLen(buf));
1632 sum += k;
1633 for ( j = 0; ( i = getcFromExtChannel() ) != '\n'; j++) {
1634 if ( i == EOF ) {
1635 printf("EOF!\n");
1636 break;
1637 }
1638 buf2[j] = i;
1639 }
1640 buf2[j] = '\0';
1641 printf("I got:'%s'; pid=%d\n",buf2,getExternalChannelPid());
1642 }
1643 printf("Total:%lld bytes\n",sum);
1644 closeAllExternalChannels();
1645 return 0;
1646}
1647#endif /*ifdef SELFTEST*/
1648/*
1649 #] Selftest main :
1650*/
1651
1652#endif /*ifndef WITHEXTERNALCHANNEL ... else*/
int PutPreVar(UBYTE *, UBYTE *, UBYTE *, int)
Definition pre.c:724
int PF_BroadcastString(UBYTE *str)
Definition parallel.c:2167