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