Back to index

radiance  4R0+20100331
persist.c
Go to the documentation of this file.
00001 #ifndef lint
00002 static const char    RCSid[] = "$Id: persist.c,v 2.41 2008/05/01 15:50:28 greg Exp $";
00003 #endif
00004 /*
00005  * Routines for persistent rtrace and rpict processes.
00006  *
00007  *  External symbols declared in ray.h
00008  */
00009 
00010 #include "copyright.h"
00011 
00012 #include <string.h>
00013 #include <signal.h>
00014 #include <sys/stat.h>
00015 #include <sys/types.h>
00016 
00017 #include "platform.h"
00018 #ifndef NON_POSIX /* XXX need abstraction for process management */
00019  #include <sys/wait.h>
00020 #endif
00021 
00022 #include "rtprocess.h" /* getpid() */
00023 #include "standard.h"
00024 #include "random.h"
00025 #include "ray.h"
00026 
00027 #ifdef F_SETLKW
00028 #include "paths.h"
00029 #include "selcall.h"
00030 
00031 #ifndef TIMELIM
00032 #define TIMELIM             (8*3600)      /* time limit for holding pattern */
00033 #endif
00034 
00035 extern int    headismine;   /* boolean true if header belongs to me */
00036 extern char   *progname;    /* global program name */
00037 extern char   *errfile;     /* global error file name */
00038 static char   *persistfname = NULL;       /* persist file name */
00039 static int    persistfd = -1;             /* persist file descriptor */
00040 static char   inpname[TEMPLEN+1], outpname[TEMPLEN+1], errname[TEMPLEN+1];
00041 
00042 typedef void (sighandler_t)(int);
00043 static sighandler_t sig_io;
00044 static sighandler_t sig_alrm;
00045 
00046 
00047 extern void
00048 pfdetach(void)              /* release persist (and header) resources */
00049 {
00050        if (persistfd >= 0)
00051               close(persistfd);
00052        persistfd = -1;
00053        persistfname = NULL;
00054        inpname[0] = '\0';
00055        outpname[0] = '\0';
00056        errname[0] = '\0';
00057        headismine = 0;
00058 }
00059 
00060 
00061 extern void
00062 pfclean(void)        /* clean up persist files */
00063 {
00064        if (persistfd >= 0)
00065               close(persistfd);
00066        if (persistfname != NULL)
00067               unlink(persistfname);
00068        if (inpname[0])
00069               unlink(inpname);
00070        if (outpname[0])
00071               unlink(outpname);
00072        if (errname[0])
00073               unlink(errname);
00074 }
00075 
00076 
00077 extern void
00078 pflock(              /* place or release exclusive lock on file */
00079        int    lf
00080 )
00081 {
00082        struct flock  fls;
00083 
00084        fls.l_type = lf ? F_WRLCK : F_UNLCK;
00085        fls.l_whence = 0;
00086        fls.l_start = 0L;
00087        fls.l_len = 0L;
00088        if (fcntl(persistfd, F_SETLKW, &fls) < 0)
00089               error(SYSTEM, "cannot (un)lock persist file");
00090 }
00091 
00092 
00093 extern void
00094 persistfile(  /* open persist file and lock it */
00095        char   *pfn
00096 )
00097 {
00098        persistfd = open(pfn, O_WRONLY|O_CREAT|O_EXCL, 0644);
00099        if (persistfd >= 0) {
00100               persistfname = pfn;
00101               pflock(1);
00102               return;
00103        }
00104                             /* file exists -- switch to i/o process */
00105        persistfd = open(pfn, O_RDWR);
00106        if (persistfd < 0) {
00107               sprintf(errmsg, "cannot open persist file \"%s\"", pfn);
00108               error(SYSTEM, errmsg);
00109        }
00110        pflock(1);
00111        io_process(); /* never returns */
00112 }
00113 
00114 
00115 static int    got_io;
00116 
00117 static void sig_io(int i) { got_io++; }
00118 
00119 static void sig_alrm(int i) { quit(0); }
00120 
00121 
00122 extern void
00123 pfhold(void)         /* holding pattern for idle rendering process */
00124 {
00125        sighandler_t  *oldalrm;
00126        char   buf[512];
00127        register int  n;
00128                             /* close input and output descriptors */
00129        close(0);
00130        close(1);
00131        if (errfile == NULL)
00132               close(2);
00133                             /* create named pipes for input and output */
00134        if (mkfifo(mktemp(strcpy(inpname,TEMPLATE)), 0600) < 0)
00135               goto createrr;
00136        if (mkfifo(mktemp(strcpy(outpname,TEMPLATE)), 0600) < 0)
00137               goto createrr;
00138        if (errfile == NULL &&
00139               mkfifo(mktemp(strcpy(errname,TEMPLATE)), 0600) < 0)
00140               goto createrr;
00141        sprintf(buf, "%s %d\n%s\n%s\n%s\n", progname, getpid(),
00142                      inpname, outpname, errname);
00143        n = strlen(buf);
00144        if (write(persistfd, buf, n) < n)
00145               error(SYSTEM, "error writing persist file");
00146        lseek(persistfd, (off_t)0, SEEK_SET);
00147                             /* wait TIMELIM for someone to signal us */
00148        got_io = 0;
00149        signal(SIGIO, sig_io);
00150        oldalrm = signal(SIGALRM, sig_alrm);
00151        alarm(TIMELIM);
00152        pflock(0);                  /* unlock persist file for attach */
00153        while (!got_io)
00154               pause();             /* wait for attach */
00155        alarm(0);                   /* turn off alarm */
00156        signal(SIGALRM, oldalrm);
00157        signal(SIGIO, SIG_DFL);
00158        pflock(1);                  /* grab persist file back */
00159                             /* someone wants us; reopen stdin and stdout */
00160        close(0);
00161        if (open(inpname, O_RDONLY) != 0)
00162               error(INTERNAL, "unexpected stdin file number");
00163        clearerr(stdin);
00164        close(1);
00165        if (open(outpname, O_WRONLY) != 1)
00166               error(INTERNAL, "unexpected stdout file number");
00167        sleep(3);            /* give them a chance to open their pipes */
00168        if (errname[0]) {
00169               close(2);
00170               if (open(errname, O_WRONLY) != 2)
00171                      error(INTERNAL, "unexpected stderr file number");
00172               unlink(errname);
00173               errname[0] = '\0';
00174        }
00175        unlink(inpname);
00176        inpname[0] = '\0';
00177        unlink(outpname);
00178        outpname[0] = '\0';
00179        return;
00180 createrr:
00181        error(SYSTEM, "cannot create named pipes in pfhold");
00182 }
00183 
00184 
00185 extern void
00186 io_process(void)            /* just act as go-between for actual process */
00187 {
00188        register char *cp;
00189        register int  nr, n;
00190        char   buf[BUFSIZ], *pfin, *pfout, *pferr;
00191        int    pid, nfds;
00192        int    fdout, fderr = -1;
00193        int    status = 0;
00194        fd_set readfds, excepfds;
00195                                    /* load persist file */
00196        n = 40;
00197        while ((nr = read(persistfd, buf, sizeof(buf)-1)) == 0) {
00198               if (!n--)
00199                      error(USER, "unattended persist file?");
00200               pflock(0);
00201               sleep(3+(3*getpid()+random())%13); /* wait until ready */
00202               pflock(1);
00203        }
00204        if (nr < 0)
00205               error(SYSTEM, "error reading persist file");
00206 #ifndef _WIN32 /* XXX we need a replacement for that one */
00207        ftruncate(persistfd, (off_t)0L);   /* truncate persist file */
00208 #endif
00209        pfdetach();                 /* close & release persist file */
00210        buf[nr] = '\0';                    /* parse what we got */
00211        if ((cp = strchr(buf, ' ')) == NULL)
00212               goto formerr;
00213        *cp++ = '\0';
00214        if ((pid = atoi(cp)) <= 0)
00215               goto formerr;
00216        if ((cp = strchr(cp, '\n')) == NULL)
00217               goto formerr;
00218        pfin = ++cp;
00219        if ((cp = strchr(cp, '\n')) == NULL)
00220               goto formerr;
00221        *cp++ = '\0';
00222        pfout = cp;
00223        if ((cp = strchr(cp, '\n')) == NULL)
00224               goto formerr;
00225        *cp++ = '\0';
00226        pferr = cp;
00227        if ((cp = strchr(cp, '\n')) == NULL)
00228               goto formerr;
00229        *cp++ = '\0';
00230        if (cp-buf != nr)
00231               goto formerr;
00232        if (strcmp(buf, progname)) {
00233               sprintf(errmsg, "persist file for %s, not %s", buf, progname);
00234               error(USER, errmsg);
00235        }
00236                                    /* wake up rendering process */
00237        if (kill(pid, SIGIO) < 0)
00238               error(SYSTEM, "cannot signal rendering process in io_process");
00239                                    /* fork child feeder process */
00240        pid = fork();
00241        if (pid < 0)
00242               error(SYSTEM, "fork failed in io_process");
00243        if (pid == 0) {                    /* feeder loop */
00244               int    fdin;
00245               close(1);            /* open input pipe */
00246               if ((fdin = open(pfin, O_WRONLY)) < 0)
00247                      error(SYSTEM, "cannot open feed pipe in io_process");
00248                                           /* renderer stdin */
00249               while ((nr = read(0, cp=buf, sizeof(buf))) > 0) {
00250                      do {
00251                             if ((n = write(fdin, cp, nr)) <= 0)
00252                                    goto writerr;
00253                             cp += n;
00254                      } while ((nr -= n) > 0);
00255               }
00256               if (nr < 0)
00257                      goto readerr;
00258               _exit(0);
00259        }
00260        close(0);
00261                                    /* open output pipes, in order */
00262        if ((fdout = open(pfout, O_RDONLY)) < 0)
00263               error(SYSTEM, "cannot open output pipe in io_process");
00264        if (pferr[0] && (fderr = open(pferr, O_RDONLY)) < 0)
00265               error(SYSTEM, "cannot open error pipe in io_process");
00266        for ( ; ; ) {               /* eater loop */
00267               FD_ZERO(&readfds);
00268               FD_ZERO(&excepfds);
00269               nfds = 0;
00270               if (fdout >= 0) {
00271                      FD_SET(fdout, &readfds);
00272                      FD_SET(fdout, &excepfds);
00273                      nfds = fdout+1;
00274               }
00275               if (fderr >= 0) {
00276                      FD_SET(fderr, &readfds);
00277                      FD_SET(fderr, &excepfds);
00278                      nfds = fderr+1;
00279               }
00280               if (nfds == 0)
00281                      break;               /* all done, exit */
00282               if (select(nfds, &readfds, NULL, &excepfds, NULL) < 0)
00283                      error(SYSTEM, "error in select call in io_process");
00284                                           /* renderer stderr */
00285               if (fderr >= 0 && (FD_ISSET(fderr, &readfds) ||
00286                             FD_ISSET(fderr, &excepfds))) {
00287                      nr = read(fderr, cp=buf, sizeof(buf));
00288                      if (nr < 0)
00289                             goto readerr;
00290                      if (nr == 0) {
00291                             close(fderr);
00292                             /* close(2);  don't close stderr! */
00293                             fderr = -1;
00294                      } else {
00295                             cp[nr] = '\0';       /* deduce status if we can */
00296                             n = strlen(progname);
00297                             if (!strncmp(cp, progname, n) &&
00298                                           cp[n++] == ':' &&
00299                                           cp[n++] == ' ') {
00300                                    register struct erract      *ep;
00301                                    for (ep = erract; ep < erract+NERRS;
00302                                                  ep++)
00303                                           if (ep->pre[0] &&
00304                                                  !strncmp(cp+n, ep->pre,
00305                                                      strlen(ep->pre))) {
00306                                                  status = ep->ec;
00307                                                  break;
00308                                           }
00309                             }
00310                             do {          /* write message */
00311                                    if ((n = write(2, cp, nr)) <= 0)
00312                                           goto writerr;
00313                                    cp += n;
00314                             } while ((nr -= n) > 0);
00315                      }
00316               }
00317                                           /* renderer stdout */
00318               if (fdout >= 0 && (FD_ISSET(fdout, &readfds) ||
00319                             FD_ISSET(fdout, &excepfds))) {
00320                      nr = read(fdout, cp=buf, sizeof(buf));
00321                      if (nr < 0)
00322                             goto readerr;
00323                      if (nr == 0) {              /* EOF */
00324                             close(fdout);
00325                             close(1);
00326                             fdout = -1;
00327                      } else
00328                             do {          /* write it all */
00329                                    if ((n = write(1, cp, nr)) <= 0)
00330                                           goto writerr;
00331                                    cp += n;
00332                             } while ((nr -= n) > 0);
00333               }
00334        }
00335        kill(pid, SIGTERM);  /* no more process to feed, so... */
00336        waitpid(pid, 0, 0);  /* wait for feeder process */
00337        _exit(status);
00338 formerr:
00339        error(USER, "format error in persist file");
00340 readerr:
00341        error(SYSTEM, "read error in io_process");
00342 writerr:
00343        error(SYSTEM, "write error in io_process");
00344 }
00345 
00346 #else
00347 
00348 extern void pfclean(void) {}
00349 
00350 #endif