formlink2.c 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885
  1. /*
  2. Copyright (C) Mikhail Tentyukov.
  3. This file is part of FORMLINK interface to the Symbolic Manipulation
  4. System FORM (http://www.nikhef.nl/~form).
  5. Redistribution and use in source and binary forms, with or without
  6. modification, are permitted provided that the following conditions are met:
  7. 1. Redistributions of source code must retain the above copyright notice,
  8. this list of conditions and the following disclaimer.
  9. 2. Redistributions of modified source code should refer to its origin as
  10. follows: "This code is based on the source code of FORMLINK interface to
  11. the Symbolic Manipulation System FORM".
  12. 3. Provided this code was used in any form in scientific work that
  13. leads to publication in a scientific journal, this work should refer
  14. to the paper M. Tentyukov and J.A.M.Vermaseren "Extension of the
  15. functionality of the symbolic program FORM by external software",
  16. cs.SC/0604052
  17. THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY; WITHOUT EVEN
  18. THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR
  19. PURPOSE.
  20. */
  21. /*
  22. This is the FORMLINK interface which can be used for embedding FORM
  23. in other applications, see http://www.arxiv.org/abs/cs.SC/0604052
  24. Appendix D. There are two public functions available, the low-level
  25. function flnk_do_runcmd() and the high-level (and restricted)
  26. function flnk_runcmd(), see comments below. The functions return PID
  27. (Process IDentifier) of a program starting FORM, or an error code <0.
  28. In the latter case the error code may be checked by means of the
  29. macro FLNK_ISERR, see comments in the file flnk_startform.h.
  30. To compile this module, use the command
  31. cc -c flnk_startform.c
  32. this produces the object file flnk_startform.o which can be used
  33. in order to link programs which uses flnk_runcmd() and
  34. flnk_do_runcmd() (do not forget to #include "flnk_startform.h" in
  35. such programs).
  36. At the end of this file there is the demo program. In order to compile
  37. it, the macro DEMO_CALC should be defined, see comments below (just
  38. after the line #ifdef DEMO_CALC).
  39. */
  40. #include <stdlib.h>
  41. #include <stdio.h>
  42. #include <unistd.h>
  43. #include <fcntl.h>
  44. #include <sys/types.h>
  45. #include <sys/time.h>
  46. #include <sys/wait.h>
  47. #include <errno.h>
  48. #include <signal.h>
  49. #include <string.h>
  50. #include "flnk_startform.h"
  51. #include "psl.h"
  52. /*First argument for the function signal:*/
  53. #ifndef INTSIGHANDLER
  54. typedef void (*mysighandler_t)(int);
  55. #else
  56. /* Sometimes, this nonsense may occurs:*/
  57. /*typedef int (*mysighandler_t)(int);*/
  58. #endif
  59. typedef int FLNK_APIPE[2];
  60. /*
  61. This function converts the second argument (n) into the string representation,
  62. and returns the pointer to the end of the string str with _added_ string:
  63. */
  64. static char *addInt2str(char *str, long int n)
  65. { char buf[21];
  66. char *c=buf;
  67. int i;
  68. buf[0]='\0';
  69. do
  70. { *c++ = '0' + n % 10;
  71. n /= 10;
  72. }
  73. while (n);
  74. for (i=c-buf-1; !(i<0); i--)*str++=buf[i];
  75. return str;
  76. }/*addInt2str*/
  77. /*
  78. The default behaviuor of syscalls under non-blocked signals is
  79. changed time to time and depends on the UNIX flavour. Try here to
  80. make some universal wrappers.
  81. */
  82. /*Wrapper to the read() syscall, to handle possible
  83. interrupts by unblocked signals:*/
  84. static ssize_t saferead(int fd, char *buf, size_t count)
  85. { ssize_t res;
  86. if ( (res=read(fd,buf,count)) <1 )
  87. /*EOF or read is interrupted by a signal?:*/
  88. while ( (errno == EINTR)&&(res <1) )
  89. /*The call was interrupted by a signal
  90. before any data was read, try again:*/
  91. res=read(fd,buf,count);
  92. return (res);
  93. }/*saferead*/
  94. /*Wrapper to the write() syscall, to handle possible
  95. interrupts by unblocked signals:*/
  96. static ssize_t safewrite(int fd, char *buf, size_t count)
  97. { ssize_t res;
  98. /*Is write interrupted by a signal?:*/
  99. if ( (res=write(fd,buf,count)) <1 )
  100. while ( (errno == EINTR)&&(res <1) )
  101. /*The call was interrupted by a signal
  102. before any data were written, try again:*/
  103. res=write(fd,buf,count);
  104. return (res);
  105. }/*safewrite*/
  106. /* Read one (binary) PID from the file descriptor fd:*/
  107. static pid_t readpid(int fd)
  108. { pid_t tmp;
  109. if (saferead(fd,(char*)&tmp,sizeof(pid_t))!=sizeof(pid_t))
  110. return (pid_t)-1;
  111. return tmp;
  112. }/*readpid*/
  113. /* Writeone (binary) PID to the file descriptor fd:*/
  114. static pid_t writepid(int fd, pid_t thepid)
  115. { if (safewrite(fd,(char*)&thepid,sizeof(pid_t))!=sizeof(pid_t))
  116. return (pid_t)-1;
  117. return (pid_t)0;
  118. }/*readpid*/
  119. /* Adds the integer fd to the array fifo of length top+1 so that
  120. the array is ascendantly ordered. It is supposed that all 0 -- top-1
  121. elements in the array are already ordered:*/
  122. static void pushDescriptor(int *fifo, int top, int fd)
  123. { int ins=top-1;
  124. if ( fifo[ins]<=fd )
  125. fifo[top]=fd;
  126. else
  127. { /*Find the position:*/
  128. while ( (ins>=0)&&(fifo[ins]>fd) )ins--;
  129. /*Move all elements starting from the position to the right:*/
  130. for (ins++; top>ins; top--)
  131. fifo[top]=fifo[top-1];
  132. /*Put the element:*/
  133. fifo[ins]=fd;
  134. }
  135. }/*pushDescriptor*/
  136. /* Set the FD_CLOEXEC flag of desc if value is nonzero,
  137. or clear the flag if value is 0.
  138. Return 0 on success, or -1 on error with errno set. */
  139. static int set_cloexec_flag (int desc, int value)
  140. { int oldflags = fcntl (desc, F_GETFD, 0);
  141. /* If reading the flags failed, return error indication now.*/
  142. if (oldflags < 0)
  143. return (oldflags);
  144. /* Set just the flag we want to set. */
  145. if (value != 0)
  146. oldflags |= FD_CLOEXEC;
  147. else
  148. oldflags &= ~FD_CLOEXEC;
  149. /* Store modified flag word in the descriptor. */
  150. return (fcntl(desc, F_SETFD, oldflags));
  151. }/*set_cloexec_flag*/
  152. /*CLose all descriptors greate or equal than startFrom except those
  153. listed in the ascendantly ordered array usedFd of length top:*/
  154. static void closeAllDescriptors(int startFrom,int *usedFd, int top)
  155. { int n,maxfd;
  156. for (n=0; n<top; n++)
  157. { maxfd=usedFd[n];
  158. for (; startFrom<maxfd; startFrom++) /*Close all less than maxfd*/
  159. close(startFrom);
  160. startFrom++;/*skip maxfd*/
  161. }/*for (;startFrom<maxfd;startFrom++)*/
  162. /*Close all the rest:*/
  163. maxfd=sysconf(_SC_OPEN_MAX);
  164. for (; startFrom<maxfd; startFrom++)
  165. close(startFrom);
  166. }/*closeAllDescriptors*/
  167. /*Closes both pipe descriptors if not -1:*/
  168. static void closepipe(FLNK_APIPE *thepipe)
  169. { if ( (*thepipe)[0] != -1) close ((*thepipe)[0]);
  170. if ( (*thepipe)[1] != -1) close ((*thepipe)[1]);
  171. }/*closepipe*/
  172. /*The following function initializes opened channels.
  173. Upon startup, FORM sends its PID in ASCII decimal format with an
  174. appended newline character to the descriptor writable end of each the
  175. channel descriptor and then FORM will wait for the answer from the
  176. readable channel descriptor. The answer must be two comma-separated
  177. integers in ASCII decimal format followed by a newline character. The
  178. first integer corresponds to the FORM PID while the second one is the
  179. parent process PID. If the answer is not obtained after some timeout,
  180. or if it is not correct (i.e. it is not a list of two integers or the
  181. first integer is not the FORM PID) then FORM fails.
  182. The function returns the FORM pid (which may differ from chldpid!)
  183. of error code < 0.
  184. */
  185. static INLINE
  186. int initFrm(int nchannels, int *fdwrite, int *fdread, pid_t chldpid)
  187. { char chldpidtxt[22];/*FORM pid as ASCII decimal with trailing \n*/
  188. /*Comma-separated FORM pid and my pid as ASCII decimal with trailing \n:*/
  189. char bothpidtxt[45];
  190. int bothpidlen;/*The length of bothpidtxt*/
  191. fd_set rfds;
  192. struct timeval tv;
  193. int i,flags,nrep;
  194. pid_t formPID=0;
  195. long int fpid=(long int)chldpid,mypid= (long int)getpid();
  196. char buf[22];
  197. printf("%ld,%ld\n",fpid,mypid);
  198. sprintf(chldpidtxt,"%ld\n",fpid);
  199. sprintf(bothpidtxt,"%ld,%ld\n",fpid,mypid);
  200. bothpidlen=strlen(bothpidtxt);
  201. for (i=0; i<nchannels; i++)
  202. { /*wait PID from FORM:*/
  203. /* Watch fdread[i] to see when it has input:*/
  204. nrep=5;/*only (5-1) interruptions from non-blocked signals*/
  205. do
  206. { FD_ZERO(&rfds);
  207. FD_SET(fdread[i], &rfds);
  208. /* Wait up to timeout. */
  209. /*FLNK_TIMEOUT is in millisecs.*/
  210. tv.tv_sec = FLNK_TIMEOUT/1000;
  211. tv.tv_usec = (FLNK_TIMEOUT % 1000)*1000;
  212. nrep--;
  213. switch(select(fdread[i]+1, &rfds, NULL, NULL, &tv))
  214. { case -1:
  215. if ((nrep == 0)||( errno != EINTR) )
  216. { perror("select()");
  217. return FLNK_INITERR;
  218. }/*else -- A non blocked signal was caught, just repeat*/
  219. break;
  220. case 0:/*timeout*/
  221. return FLNK_INITTMERR;
  222. default:
  223. /*read from FORM its PID:*/
  224. nrep=saferead(fdread[i], buf, 21);
  225. if ( (nrep<2)||(nrep>20)||(buf[nrep-1]!='\n') )
  226. return FLNK_INITERR;
  227. buf[nrep]='\0';
  228. /*compare obtained PID with what we know:*/
  229. if (strcmp(buf,chldpidtxt)!=0)
  230. { char *b;
  231. /*The FORM PID is not necessarily coincide with
  232. chldpid! Supposre, we start FORM from a script, of
  233. inn a subshell...*/
  234. /*Check the validity of the PID:*/
  235. if (formPID != 0) /*There cannot be two different PIDs!*/
  236. return FLNK_INITPIDERR;
  237. fpid=strtol(buf, &b, 10);
  238. if (
  239. (fpid<2)||
  240. (b==buf)||/*buf is not a number*/
  241. (*b!='\n')||/*number is not terminated by \n*/
  242. kill((pid_t)fpid,0)/*there is no such PID*/
  243. )
  244. return FLNK_INITERR;
  245. /*And now reset FORM PID:*/
  246. sprintf(chldpidtxt,"%ld\n",fpid);
  247. sprintf(bothpidtxt,"%ld,%ld\n",fpid,mypid);
  248. formPID=(pid_t)fpid;
  249. }/*if (strcmp(buf,chldpidtxt)!=0)*/
  250. nrep=0;
  251. /*Send the reply (bothpidtxt):*/
  252. /* make the descriptor non-blocking:*/
  253. flags = fcntl(fdwrite[i], F_GETFL,0);/*First, save the original mode*/
  254. /*Add O_NONBLOCK:*/
  255. fcntl(fdwrite[i],F_SETFL, flags | O_NONBLOCK);
  256. /*write bothpidtxt to fdwrite[i]*/
  257. if (safewrite(fdwrite[i],bothpidtxt,bothpidlen)!=bothpidlen)
  258. return FLNK_INITERR;
  259. /*restore the flags:*/
  260. fcntl(fdwrite[i],F_SETFL, flags);
  261. }/*switch*/
  262. }
  263. while (nrep);
  264. }/*for (i=0; i<nchannels;i++)*/
  265. if (formPID == 0)
  266. formPID=chldpid;
  267. return formPID;
  268. }/*initFrm*/
  269. /*
  270. The function creates "nchannels" communication channels and starts a command
  271. "cmd" providing it these channels.
  272. The function opens nchannels*2 pipes and executes a command "cmd" passing
  273. corresponding descriptors as a comma separated list of (r,w) pairs (in ASCII
  274. decimal format) via a command line argument replacing arg[nopt] by this list.
  275. arg[] is a NULL-terminated array of cmd arguments. The other ends of pipes
  276. are placed into arrays fdread and fdwrite so that for the channel # the main
  277. program reads from the descriptor fdread[#] and writs to the descriptor
  278. fdwrite[#]. Both arrays fdread and fdwrite must be allocated for nchannels
  279. integers. The argument arg[nopt] is just replaced by the list without
  280. any cleanup.
  281. Example:
  282. cmd="./form",argv={"form","-pipe",NULL,NULL},
  283. nopt=2,
  284. nchannels=2.
  285. The function creates nchannels*2=4 pipes.
  286. Lets us suppose the first pipe was created with the descriptors 5,6;
  287. the second pipe has the descriptors 7,8;
  288. the third pipe has the descriptors 9,10
  289. and the fourth pipe has the descriptors 11,12.
  290. The function executes the command:
  291. form -pipe 5,8,9,12
  292. and returns in the
  293. array fdread the following:
  294. {7,11}
  295. array fwrite the following:
  296. {6,10};
  297. the zero channel:
  298. the program "form" reads from 5 and writes to 8 while
  299. the main program reads from 7 and writes to 6;
  300. the first channel:
  301. the program "form" reads from 9 and writes to 12 while
  302. the main program reads from 11 and writes to 10.
  303. All unuised descriptors except of first "fdstart" (usually three
  304. "standard" stdin, stdout and stderr) are closed before execute
  305. the specified command.
  306. The function returns PID of the started command, or -1*/
  307. pid_t flnk_do_runcmd(
  308. /*--Input parameters:--*/
  309. int nchannels,/*number of i/o cahnnels*/
  310. int ttymode,/*0 - nothing,
  311. &1 - stdin</dev/null
  312. &2 - stdout>/dev/null
  313. &4 - stderr>/dev/null
  314. &8 - daemonizeing
  315. &16 - setsid()*/
  316. char *cmd,/*filename to execute*/
  317. char *argv[],/*arguments, argv[0] is the name of the started process*/
  318. int nopt,/*if >0, order number of the option to be replaced by pipe info*/
  319. char *envnam,/* if not NULL, export the pipe info into this env. variable*/
  320. int fdstart,/*How many standard descriptors are opened?
  321. (usually 3 -- stdin, stdout and stderr*/
  322. /*--Output parameters:--*/
  323. int *fdread,/*array of read descriptors*/
  324. int *fdwrite,/*array of write descriptors*/
  325. int *formPID/*if !NULL, FORM PID obtained during initialization*/
  326. )
  327. { int i, ret;
  328. FLNK_APIPE *fdin=NULL, *fdout=NULL,/*arrays of input-output pipes*/
  329. fdsig= {-1,-1}; /*Signal pipe*/
  330. pid_t childpid=(pid_t)-1,fatherchildpid = (pid_t)0;
  331. mysighandler_t oldPIPE=NULL;
  332. ret=FLINK_ALLOCERR;
  333. fdin=malloc(sizeof(FLNK_APIPE)*nchannels);
  334. if (fdin==NULL)
  335. goto fail_flnk_do_runcmd;
  336. /*For possible rollback:*/
  337. for (i=0; i<nchannels; i++)
  338. fdin[i][0]=fdin[i][1]=-1;
  339. fdout=malloc(sizeof(FLNK_APIPE)*nchannels);
  340. if (fdout==NULL)
  341. goto fail_flnk_do_runcmd;
  342. /*For possible rollback:*/
  343. for (i=0; i<nchannels; i++)
  344. fdout[i][0]=fdout[i][1]=-1;
  345. ret=FLINK_PIPEERR;
  346. /*Att! The order of pipe() calls is important! Do not change the order:
  347. pipe(fdsig), pipe(fdin), pipe(fdout)!*/
  348. /*This pipe will be used by a child to tell the father if fail:*/
  349. if (pipe(fdsig)!=0)
  350. goto fail_flnk_do_runcmd;
  351. /*Without daemonizing, the father expects failure readig this pipe
  352. since close-on-exec flag will be set.
  353. With deamonizing, the (grand)father expects the (grand)child
  354. PID from this pipe, or <0 if the (grand)child
  355. fails on exec().*/
  356. for (i=0; i<nchannels; i++)
  357. if ( /* Open pipes for input-output:*/
  358. (pipe(fdin[i])!=0)
  359. ||(pipe(fdout[i])!=0)
  360. )
  361. goto fail_flnk_do_runcmd;
  362. if ((childpid = fork()) == -1)
  363. { perror("fork");
  364. ret=FLINK_FORKEERR;
  365. goto fail_flnk_do_runcmd;
  366. }
  367. if (childpid == 0) /*Child.*/
  368. { int *fifo, top=0;
  369. char *bufin=NULL,*args=NULL;/*bufin will be a comma-separated list of
  370. descriptors, args is a working pointer*/
  371. /*
  372. To be thread safely, we can't rely on ascendant order of opened
  373. file descriptors. So we put each of descriptor we have to
  374. preserve into the array fifo. But first we have to allocate fifo.
  375. */
  376. /*
  377. Note, allocated arrays will never be freed since this process
  378. either exec()'ed or exited in this function.
  379. */
  380. if (
  381. ( (args=bufin=malloc(nchannels*2*21)) ==NULL )
  382. /* 64/Log_2[10] = 19.3, so 21 (20+'\0' or ',') is
  383. enough forever for a single argument.*/
  384. ||( (fifo=malloc((nchannels*2+2)*sizeof(int))) ==NULL)
  385. /*The fifo size -- fdin[i][0]+fdout[i][1]+fdsig[0]+fdsig[1]*/
  386. )
  387. { /*Signal to the parent process:*/
  388. writepid(fdsig[1],FLINK_ALLOCERR);
  389. _exit(1);/*The child, just exit, not return*/
  390. }
  391. /*Mark descriptors which will NOT be closed:*/
  392. pushDescriptor(fifo,top++,fdsig[1]);
  393. /*Process all channel descriptors which will be passeed to
  394. FORM:*/
  395. for (i=0; i<nchannels; i++)
  396. { /*This is extra, files have no close-on-exec flag by
  397. default, but just to be on the safe side clean it:*/
  398. set_cloexec_flag(fdin[i][0], 0);
  399. set_cloexec_flag(fdout[i][1], 0);
  400. /*Mark descriptors which will NOT be closed:*/
  401. pushDescriptor(fifo,top++,fdin[i][0]);
  402. pushDescriptor(fifo,top++,fdout[i][1]);
  403. /*build the argument list:*/
  404. /*add 'r' descriptor to the list:*/
  405. args=addInt2str(args,fdin[i][0]);
  406. *args++=',';
  407. /*add 'w' descriptor to the list:*/
  408. args=addInt2str(args,fdout[i][1]);
  409. *args++=',';
  410. }/*for (i=0; i<nchannels;i++)*/
  411. *--args='\0';/*terminate the string overridding the latest ','*/
  412. /*Now in bufin there is the comma-separated list of descriptors
  413. to be pased to FORM. Store the list:*/
  414. if (nopt>0)
  415. argv[nopt]=bufin;
  416. if (envnam!=NULL)
  417. setenv(envnam,bufin,1);
  418. /*Close all descriptors except those listed in fifo:*/
  419. closeAllDescriptors(fdstart,fifo, top);
  420. if (ttymode & 1) /* stdin </dev/null*/
  421. { if (close(0) == 0)
  422. open("/dev/null",O_RDONLY);
  423. }/*if (ttymode & 1)*/
  424. if (ttymode & 2) /* stdout > /dev/null*/
  425. { if (close(1) == 0 )
  426. open("/dev/null",O_WRONLY);
  427. }/*if (ttymode & 2)*/
  428. if (ttymode & 4) /* stder > /dev/null*/
  429. { if (close(2) == 0 )
  430. open("/dev/null",O_WRONLY);
  431. }/*if (ttymode & 4)*/
  432. if ( ttymode & 16 )/* create a session and sets the process group ID */
  433. setsid();
  434. if (set_cloexec_flag (fdsig[1], 1)!=0) /*Error?*/
  435. { /*Signal to parent:*/
  436. writepid(fdsig[1],FLINK_PIPEERR);
  437. _exit(1);
  438. }
  439. if ( ttymode & 8 ) /*Daemonize*/
  440. { int fdsig2[2];/*To check exec() success*/
  441. if (
  442. pipe(fdsig2)||
  443. (set_cloexec_flag (fdsig2[1], 1)!=0)
  444. )
  445. { /*Error?*/
  446. /*Signal to parent:*/
  447. writepid(fdsig[1],FLINK_DAEMONERR|FLINK_PIPEERR);
  448. _exit(1);
  449. }
  450. set_cloexec_flag (fdsig2[0], 1);
  451. switch(childpid=fork())
  452. { case 0:/*grandchild*/
  453. /*Execute external command:*/
  454. execvp(cmd, argv);
  455. /* Control can reach this point only on error!*/
  456. writepid(fdsig2[1],(pid_t)-2);
  457. break;
  458. case -1:
  459. /* Control can reach this point only on error!*/
  460. /*Inform the father about the failure*/
  461. writepid(fdsig[1],FLINK_DAEMONERR|FLINK_FORKEERR);
  462. _exit(1);/*The child, just exit, not return*/
  463. default:/*Son of his father*/
  464. close(fdsig2[1]);
  465. /*Ignore SIGPIPE (up to the end of the process):*/
  466. signal(SIGPIPE,SIG_IGN);
  467. /*Wait on read() while the granchild close the pipe
  468. (on success) or send -2 (if exec() fails).*/
  469. /*There are two possibilities:
  470. -1 -- this is ok, the pipe was closed on exec,
  471. the program was successfully executed;
  472. -2 -- something is wrong, exec failed since the
  473. grandchild sends -2 after exec.
  474. */
  475. if ( readpid(fdsig2[0]) != (pid_t)-1 )/*something is wrong*/
  476. writepid(fdsig[1],FLINK_DAEMONERR|FLINK_EXECEERR);
  477. else/*ok, send PID of the granchild to the father:*/
  478. writepid(fdsig[1],childpid);
  479. /*Die and free the life space for the grandchild:*/
  480. _exit(0);/*The child, just exit, not return*/
  481. }/*switch(childpid=fork())*/
  482. }
  483. else /*if ( ttymode & 8 )*/
  484. { execvp(cmd, argv);
  485. /* Control can reach this point only on error!*/
  486. writepid(fdsig[1],FLINK_EXECEERR);
  487. _exit(2);/*The child, just exit, not return*/
  488. }/*if ( ttymode & 8 )...else*/
  489. }
  490. else /* The (grand)father*/
  491. { close(fdsig[1]);
  492. /*To prevent closing fdsig in rollback after
  493. goto fail_flnk_do_runcmd:*/
  494. fdsig[1]=-1;
  495. for (i=0; i<nchannels; i++)
  496. { /*No rollback here -- what could be the reason?:*/
  497. close(fdin[i][0]);
  498. close(fdout[i][1]);
  499. fdread[i]=fdout[i][0];
  500. fdwrite[i]=fdin[i][1];
  501. }/*for (i=0; i<nchannels;i++)*/
  502. free(fdin); fdin=NULL;
  503. free(fdout); fdout=NULL;
  504. /* Temporary ignore this signal:*/
  505. /* if compiler fails here, try to change the definition of
  506. mysighandler_t on the beginning of this file
  507. (just define INTSIGHANDLER)*/
  508. oldPIPE=signal(SIGPIPE,SIG_IGN);
  509. if ( ttymode & 8 ) /*Daemonize*/
  510. { /*Read the grandchild PID from the son.*/
  511. fatherchildpid=childpid;
  512. if ( (childpid=readpid(fdsig[0]))<0 )
  513. { /*Daemoniization process fails for some reasons!*/
  514. ret=childpid;
  515. childpid=fatherchildpid;/*for rollback*/
  516. goto fail_flnk_do_runcmd;
  517. }
  518. }
  519. else
  520. { /*fdsig[1] should be closed on exec and this read operation
  521. must fail on success:*/
  522. ret=readpid(fdsig[0]);
  523. if ( ret != (pid_t)-1 )
  524. goto fail_flnk_do_runcmd;
  525. }/*if ( ttymode & 8 ) ... else*/
  526. }/*The (grand)father*/
  527. /*Here can be ONLY the father*/
  528. close(fdsig[0]);
  529. /*To prevent closing fdsig in rollback after goto fail_flnk_do_runcmd:*/
  530. fdsig[0]=-1;
  531. if ( ttymode & 8 )/*Daemonize*/
  532. /*Wait while the father of a grandchild dies:*/
  533. waitpid(fatherchildpid,&i,0);
  534. if ( (ret=initFrm(nchannels,fdwrite,fdread,childpid))<0)
  535. goto fail_flnk_do_runcmd;
  536. if (formPID!=NULL)
  537. *formPID=ret;
  538. /*Restore the signal:*/
  539. signal(SIGPIPE,oldPIPE);
  540. return(childpid);/*The normal return. The father must return here.*/
  541. /*rollback:*/
  542. fail_flnk_do_runcmd:
  543. if (oldPIPE!=NULL)/*restore the signal:*/
  544. signal(SIGPIPE,oldPIPE);
  545. closepipe(&fdsig);
  546. if (fdout)
  547. { for (i=0; i<nchannels; i++)
  548. closepipe(fdout+i);
  549. free(fdout);
  550. }
  551. if (fdin)
  552. { for (i=0; i<nchannels; i++)
  553. closepipe(fdin+i);
  554. free(fdin);
  555. }
  556. if (childpid>0)
  557. { for (i=0; i<nchannels; i++)
  558. { close(fdread[i]);
  559. close(fdwrite[i]);
  560. }/*for (i=0; i<nchannels;i++)*/
  561. kill(childpid,SIGKILL);
  562. waitpid(childpid,fdsig,0);
  563. }/*if (childpid>0)*/
  564. if (formPID!=NULL)
  565. *formPID=0;
  566. return ret;
  567. }/*flnk_do_runcmd*/
  568. static int parseline(char **argv, char *cmd)
  569. { int n=0,nopt=0;
  570. while (*cmd != '\0')
  571. { for (; (*cmd <= ' ') && (*cmd != '\0') ; cmd++)
  572. ;
  573. if (*cmd != '\0')
  574. { argv[n]=cmd;
  575. while (*++cmd > ' ');
  576. if (*cmd != '\0')
  577. *cmd++ = '\0';
  578. if ( (nopt==0) && (strcmp(argv[n],"-pipe")==0) )
  579. argv[nopt=++n]=NULL;
  580. n++;
  581. }/*if (*cmd != '\0')*/
  582. }/*while (*cmd != '\0')*/
  583. argv[n]=NULL;
  584. if (n==0)return -1;
  585. return nopt;
  586. }/*parseline*/
  587. /*The high-level wrapper to flnk_do_runcmd(). Creates one preset
  588. channel and starts a command "cmd" providing it this channel.
  589. The output of the running command can be received from the stream
  590. freceive, the input can be send by the stream fsend. The buffering
  591. mode of these streams is DEFAULT one provided by stdio, i.e.,
  592. after the data are sent to fsend, fflush(fsend); is needed.
  593. If cmd containes the token -pipe, then the descriptor list
  594. will be inserted after this token, otherwise, the environment
  595. variable FORM_PIPES with this list will be exported.
  596. If daemonize == 0, the command is started directly in the
  597. current process group and its stahdard input, output and error
  598. streams are kept as they are. If daemonize != 0, the command
  599. is started in a new session without the leader, it is passed
  600. to the init process and its stahdard input, output and error
  601. streams are redirected to /dev/null.
  602. */
  603. pid_t flnk_runcmd(
  604. FILE **freceive,/*get from running program*/
  605. FILE **fsend,/*send to running program*/
  606. int daemonize,/*0 - not, !=0 - yes*/
  607. char *cmd/*command to execute, may contain '-pipe'; if not,
  608. FORM_PIPES environment variable will be used*/
  609. )
  610. { pid_t pid;
  611. int fdread, fdwrite, nopt;
  612. char *envname,**argvc;
  613. if (cmd==NULL)return FLINK_INVCMD;
  614. /*Temporarily use nopt and envname to detouch cmd and allocate argvc.*/
  615. nopt=strlen(cmd);
  616. if ( (envname=malloc(nopt+1))==NULL )
  617. return FLINK_ALLOCERR;
  618. cmd=strcpy(envname,cmd);/*detouch cmd*/
  619. /*Number of options can't be more than the length of a line:*/
  620. argvc=malloc( (nopt+2)*sizeof(char*) );
  621. if (argvc == NULL)
  622. { free(cmd);
  623. return FLINK_ALLOCERR;
  624. }
  625. nopt=parseline(argvc,cmd);
  626. if (nopt<0)
  627. { free(cmd);
  628. free(argvc);
  629. return FLINK_INVCMD;
  630. }
  631. if (nopt == 0)
  632. envname=FORM_PIPES;
  633. else
  634. envname=NULL;
  635. pid=flnk_do_runcmd(1, (daemonize)?(1|2|4|8|16):0,
  636. argvc[0],argvc,nopt,envname,3,&fdread, &fdwrite,NULL);
  637. free(cmd);
  638. free(argvc);
  639. if (pid>0)
  640. { *freceive=fdopen(fdread,"r");
  641. *fsend=fdopen(fdwrite,"w");
  642. if ( ( (*freceive)==NULL )||( (*fsend)==NULL ) )
  643. { kill(pid,SIGKILL);
  644. waitpid(pid,&fdread,0);
  645. pid=FLNK_DOPENERR;
  646. }/*if ( ( (*freceive)==NULL )||( (*fsend)==NULL ) )*/
  647. }/*if (pid>0)*/
  648. return pid;
  649. }/*flnk_runcmd*/
  650. /*
  651. This simple demo is an algebraic "calculator" based on embedded FORM.
  652. To compile it, one can use the following command line:
  653. cc -o demo_calc -DDEMO_CALC flnk_startform.c
  654. The executable file demo_calc must appear in the current directory.
  655. Simply start it:
  656. ./demo_calc
  657. and enter something like
  658. (a+b)^100
  659. If the error occures:
  660. Error executing program
  661. this means the program cannot start the FORM executable, correct the path
  662. FORMPATH.
  663. If the error occures:
  664. Initialization error
  665. this means the program starts the old FORM executable, which has no external
  666. interface. The interface used in this demo appears in FORM 3.2
  667. To quit the program, press ctrl-d
  668. */
  669. /*
  670. This simple demo is rather useless, it is only to demonstrate of how
  671. flnk_runcmd() could be used, and also how to handle errors.
  672. */
  673. /*
  674. The program reads line by line from the keyboard. Each line is parsed
  675. in order to pick up identifiers. Identifiers are started by a letter
  676. and are formed by letters and digits. All ID's are assumed to be just
  677. symbols.
  678. Each identifier is sent to FORM as
  679. s ID;
  680. Then the string (without trailing newline or ';') is sent to FORM as
  681. l tempExpr = <the string>;\n
  682. and the module is ended by the .sort instruction. In the next module
  683. the expression is sent back to the program and dropped, all symbols
  684. are discarded by the .store instruction. The program prints the answer
  685. and reads the next line from the keyboard.
  686. If the program fails reading the answer, it simply restarts FORM.
  687. The program creates the temporary file "tmp.frm" (the name could be
  688. changed by changing the macro TMPFRMNAME) with the following FORM
  689. program:
  690. #setexternal `PIPE1_'
  691. #do i=1,1
  692. #fromexternal
  693. #toexternal "%e\n", tempExpr
  694. .store
  695. #redefine i "0"
  696. #enddo
  697. E.g., if the user enters such a line:
  698. (a1+a2+b)^23
  699. the following two modules will be executed:
  700. s a1;
  701. s a2;
  702. s b;
  703. drop tempExpr;
  704. l tempExpr = (a1+a2+b)^23;
  705. .sort
  706. #toexternal "%e\n", tempExpr
  707. .store
  708. */
  709. /*Path to the FORM executable file:*/
  710. #define FORMPATH "/home/neun/form/form -pipe"
  711. /*Alternatively, the following variant can be used:*/
  712. /*#define FORMPATH "./form -pipe"*/
  713. char FormCommand [40];
  714. /*The temporary file name:*/
  715. #define TMPFRMNAME "/home/neun/form/tmp.frm"
  716. /*This function demonstrates how one can use the macro FLNK_ISERR
  717. in order to check the error code returned by functions flnk_do_runcmd()
  718. and flnk_runcmd() (note, errors may be OR'ed!):*/
  719. static int errmessage(pid_t errcode)
  720. { int n=0;
  721. if (FLNK_ISERR(errcode,FLINK_ALLOCERR))
  722. { puts("Memory allocation error");
  723. n++;
  724. }
  725. if (FLNK_ISERR(errcode,FLINK_PIPEERR))
  726. { puts("Error processing a pipe");
  727. n++;
  728. }
  729. if (FLNK_ISERR(errcode,FLINK_FORKEERR))
  730. { puts("Fork error");
  731. n++;
  732. }
  733. if (FLNK_ISERR(errcode,FLINK_EXECEERR))
  734. { puts("Error executing program");
  735. n++;
  736. }
  737. if (FLNK_ISERR(errcode,FLINK_DAEMONERR))
  738. { puts("Error during daemonizing");
  739. n++;
  740. }
  741. if (FLNK_ISERR(errcode,FLNK_DOPENERR))
  742. { puts("Error opening stream");
  743. n++;
  744. }
  745. if (FLNK_ISERR(errcode,FLINK_INVCMD))
  746. { puts("Invalid command");
  747. n++;
  748. }
  749. if (FLNK_ISERR(errcode,FLNK_INITERR))
  750. { puts("Initialization error");
  751. n++;
  752. }
  753. if (FLNK_ISERR(errcode,FLNK_INITTMERR))
  754. { puts("Timeout");
  755. n++;
  756. }
  757. if (FLNK_ISERR(errcode,FLNK_INITPIDERR))
  758. { puts("More than one FORM PID");
  759. n++;
  760. }
  761. return n;
  762. }/*errmessage*/
  763. /*The function just (re)starts FORM:*/
  764. int forminit(FILE **freceive,FILE **fsend)
  765. { pid_t fpid;
  766. sprintf(FormCommand,"%s/form/form -pipe %s/form/tmp.frm",
  767. getenv("reduce"),getenv("reduce"));
  768. if ( (*freceive) !=NULL)
  769. fclose(*freceive);
  770. if ( (*fsend) !=NULL)
  771. fclose(*fsend);
  772. *freceive=*fsend=NULL;
  773. fpid=flnk_runcmd(
  774. freceive,
  775. fsend,
  776. 1,/*daemon mode. Try 0, the FORM output will be visible on the screen.*/
  777. // FORMPATH" "TMPFRMNAME
  778. FormCommand
  779. );
  780. if (fpid<0)
  781. { printf("Total %d errors\n",errmessage(fpid));
  782. return -1;
  783. }
  784. return 0;
  785. }/*forminit*/
  786. /* end of formlink2.c */