Back to index

radiance  4R0+20100331
rholo.c
Go to the documentation of this file.
00001 #ifndef lint
00002 static const char    RCSid[] = "$Id: rholo.c,v 3.74 2008/12/05 00:22:33 greg Exp $";
00003 #endif
00004 /*
00005  * Radiance holodeck generation controller
00006  */
00007 
00008 #include <stdio.h>
00009 #include <time.h>
00010 #include <signal.h>
00011 #include <sys/stat.h>
00012 #include <string.h>
00013 
00014 #include "platform.h"
00015 #include "rterror.h"
00016 #include "resolu.h"
00017 #include "rholo.h"
00018 #include "random.h"
00019 
00020 #ifndef FRAGWARN
00021 #define FRAGWARN     20            /* fragmentation for warning (%) */
00022 #endif
00023 #ifndef MAXQTIME
00024 #define MAXQTIME     3             /* target maximum seconds in queue */
00025 #endif
00026                                    /* manual cache flushing frequency */
00027 #ifndef RTFLUSH
00028 #if MAXQTIME
00029 #define RTFLUSH             (300/MAXQTIME*totqlen)      /* <= 5 minutes */
00030 #else
00031 #define RTFLUSH             (50*totqlen)         /* just guess */
00032 #endif
00033 #endif
00034                      /* the following must be consistent with rholo.h */
00035 int    NVARS = NRHVARS;            /* total number of variables */
00036 
00037 VARIABLE      vv[] = RHVINIT;             /* variable-value pairs */
00038 
00039 char   *progname;           /* our program name */
00040 char   *hdkfile;            /* holodeck file name */
00041 char   froot[256];          /* root file name */
00042 
00043 int    ncprocs = 0;         /* desired number of compute processes */
00044 
00045 char   *outdev = NULL;             /* output device name */
00046 
00047 int    readinp = 0;         /* read commands from stdin */
00048 
00049 int    force = 0;           /* allow overwrite of holodeck (-1 == read-only) */
00050 
00051 time_t starttime;           /* time we got started */
00052 time_t endtime;             /* time we should end by */
00053 time_t reporttime;          /* time for next report */
00054 
00055 off_t  maxdisk;             /* maximum file space (bytes) */
00056 
00057 int    rtargc = 1;          /* rtrace command */
00058 char   *rtargv[128] = {"rtrace", NULL};
00059 
00060 int    orig_mode = -1;             /* original file mode (-1 if unchanged) */
00061 
00062 long   nraysdone = 0L;             /* number of rays done */
00063 long   npacksdone = 0L;     /* number of packets done */
00064 
00065 PACKET *freepacks;          /* available packets */
00066 int    totqlen;             /* maximum queue length (number of packets) */
00067 
00068 char  *sigerr[NSIG];        /* signal error messages */
00069 
00070 extern int    nowarn;              /* turn warnings off? */
00071 
00072 static void onsig(int  signo);
00073 static void sigdie(int  signo, char  *msg);
00074 static int resfmode(int     fd, int       mod);
00075 static void initrholo(void);
00076 static int rholo(void);
00077 static void setdefaults(HDGRID     *gp);
00078 static void creatholo(HDGRID       *gp);
00079 static gethfunc headline;
00080 static void loadholo(void);
00081 static void rootname(char   *rn, char     *fn);
00082 static void badvalue(int    vc);
00083 
00084 
00085 int
00086 main(
00087        int    argc,
00088        char   *argv[]
00089 )
00090 {
00091        int    i;
00092 
00093        progname = argv[0];                /* get arguments */
00094        for (i = 1; i < argc && argv[i][0] == '-'; i++)
00095               switch (argv[i][1]) {
00096               case 'w':                   /* turn off warnings */
00097                      nowarn++;
00098                      break;
00099               case 'f':                   /* force overwrite */
00100                      force = 1;
00101                      break;
00102               case 'r':                   /* read-only mode */
00103                      force = -1;
00104                      break;
00105               case 'i':                   /* read input from stdin */
00106                      readinp++;
00107                      break;
00108               case 'n':                   /* compute processes */
00109                      if (i >= argc-2)
00110                             goto userr;
00111                      ncprocs = atoi(argv[++i]);
00112                      break;
00113               case 'o':                   /* output display */
00114                      if (i >= argc-2)
00115                             goto userr;
00116                      outdev = argv[++i];
00117                      break;
00118               default:
00119                      goto userr;
00120               }
00121                                           /* get root file name */
00122        if (i >= argc)
00123               goto userr;
00124        rootname(froot, hdkfile=argv[i++]);
00125                                           /* load variables? */
00126        if (i < argc)
00127               if (argv[i][0] != '-' && argv[i][0] != '+')
00128                      loadvars(argv[i]);   /* load variables from file */
00129 
00130        if (i >= argc || argv[i][0] == '+')
00131               loadholo();                 /* load existing holodeck */
00132 
00133        while (++i < argc)                 /* get command line settings */
00134               if (setvariable(argv[i], matchvar) < 0) {
00135                      sprintf(errmsg, "unknown variable: %s", argv[i]);
00136                      error(USER, errmsg);
00137               }
00138                                           /* check settings */
00139        checkvalues();
00140                                           /* load rad input file */
00141        getradfile();
00142 
00143        if (hdlist[0] == NULL) {           /* create new holodeck */
00144               HDGRID hdg[HDMAX];
00145                                                  /* set defaults */
00146               setdefaults(hdg);
00147                                                  /* check read-only */
00148               if (force < 0)
00149                      error(USER, "cannot create read-only holodeck");
00150                                                  /* holodeck exists? */
00151               if (!force && access(hdkfile, R_OK|W_OK) == 0)
00152                      error(USER,
00153                             "holodeck file exists -- use -f to overwrite");
00154                                                  /* create holodeck */
00155               creatholo(hdg);
00156        } else                             /* else just set defaults */
00157               setdefaults(NULL);
00158                                           /* initialize */
00159        initrholo();
00160                                           /* main loop */
00161        while (rholo())
00162               ;
00163                                           /* done */
00164        quit(0);
00165 userr:
00166        fprintf(stderr,
00167 "Usage: %s [-n nprocs][-o disp][-i][-w][-r|-f] output.hdk [control.hif|+|- [VAR=val ..]]\n",
00168                      progname);
00169        quit(1);
00170        return 1; /* pro forma return */
00171 }
00172 
00173 
00174 static void
00175 onsig(                      /* fatal signal */
00176        int  signo
00177 )
00178 {
00179        static int  gotsig = 0;
00180 
00181        if (gotsig > 1)                    /* we're going as fast as we can! */
00182               return;
00183        if (gotsig++) {                    /* two signals and we split */
00184               hdsync(NULL, 0);     /* don't leave w/o saying goodbye */
00185               _exit(signo);
00186        }
00187        alarm(300);                 /* allow 5 minutes to clean up */
00188        eputs("signal - ");
00189        eputs(sigerr[signo]);
00190        eputs("\n");
00191        quit(3);
00192 }
00193 
00194 
00195 static void
00196 sigdie(                     /* set fatal signal */
00197        int  signo,
00198        char  *msg
00199 )
00200 {
00201        if (signal(signo, onsig) == SIG_IGN)
00202               signal(signo, SIG_IGN);
00203        sigerr[signo] = msg;
00204 }
00205 
00206 
00207 static int
00208 resfmode(            /* restrict open file access mode */
00209        int    fd,
00210        int    mod
00211 )
00212 {
00213        struct stat   stbuf;
00214                                    /* get original mode */
00215        if (fstat(fd, &stbuf) < 0)
00216               error(SYSTEM, "cannot stat open holodeck file");
00217        mod &= stbuf.st_mode;              /* always more restrictive */
00218        if (mod == (stbuf.st_mode & 0777))
00219               return(-1);          /* already set */
00220                                    /* else change it */
00221        if (fchmod(fd, mod) < 0) {
00222               error(WARNING, "cannot change holodeck file access mode");
00223               return(-1);
00224        }
00225        return(stbuf.st_mode);             /* return original mode */
00226 }
00227 
00228 
00229 static void
00230 initrholo(void)                    /* get our holodeck running */
00231 {
00232        extern int    global_packet();
00233        register int  i;
00234                                           /* close holodeck on exec() */
00235        fcntl(hdlist[0]->fd, F_SETFD, FD_CLOEXEC);
00236 
00237        if (outdev != NULL)                /* open output device */
00238               disp_open(outdev);
00239        else if (ncprocs > 0)                     /* else use global ray feed */
00240               init_global();
00241                                           /* record disk space limit */
00242        if (!vdef(DISKSPACE))
00243               maxdisk = ((off_t)1<<(sizeof(off_t)*8-2)) - 1024;
00244        else
00245               maxdisk = 1024.*1024.*vflt(DISKSPACE);
00246                                           /* set up memory cache */
00247        if (outdev == NULL)
00248               hdcachesize = 0;            /* manual flushing */
00249        else if (vdef(CACHE))
00250               hdcachesize = 1024.*1024.*vflt(CACHE);
00251                                           /* open report file */
00252        if (vdef(REPORT)) {
00253               register char *s = sskip2(vval(REPORT), 1);
00254               if (*s && freopen(s, "a", stderr) == NULL)
00255                      quit(2);
00256        }
00257                                           /* mark the starting time */
00258        starttime = time(NULL);
00259                                           /* compute end time */
00260        if (!vdef(TIME) || vflt(TIME) <= FTINY)
00261               endtime = 0;
00262        else
00263               endtime = starttime + vflt(TIME)*3600. + .5;
00264                                           /* start rtrace */
00265        if (ncprocs > 0) {
00266               totqlen = i = start_rtrace();
00267               if (i < 1)
00268                      error(USER, "cannot start rtrace process(es)");
00269               if (vdef(REPORT)) {         /* make first report */
00270                      printargs(rtargc, rtargv, stderr);
00271                      report(0);
00272               }
00273                                           /* allocate packets */
00274               freepacks = (PACKET *)bmalloc(i*sizeof(PACKET));
00275               if (freepacks == NULL)
00276                      goto memerr;
00277               freepacks[--i].nr = 0;
00278               freepacks[i].next = NULL;
00279               if (!vdef(OBSTRUCTIONS) || !vbool(OBSTRUCTIONS)) {
00280                      freepacks[i].offset = (float *)bmalloc(
00281                                    RPACKSIZ*sizeof(float)*(i+1) );
00282                      if (freepacks[i].offset == NULL)
00283                             goto memerr;
00284               } else
00285                      freepacks[i].offset = NULL;
00286               while (i--) {
00287                      freepacks[i].nr = 0;
00288                      freepacks[i].offset = freepacks[i+1].offset == NULL ?
00289                                    NULL : freepacks[i+1].offset+RPACKSIZ ;
00290                      freepacks[i].next = &freepacks[i+1];
00291               }
00292        }
00293                                    /* set up signal handling */
00294        sigdie(SIGINT, "Interrupt");
00295        sigdie(SIGTERM, "Terminate");
00296 #ifdef SIGHUP
00297        sigdie(SIGHUP, "Hangup");
00298 #endif
00299 #ifdef SIGPIPE
00300        sigdie(SIGPIPE, "Broken pipe");
00301 #endif
00302 #ifdef SIGALRM
00303        sigdie(SIGALRM, "Alarm clock");
00304 #endif
00305 #ifdef SIGXCPU
00306        sigdie(SIGXCPU, "CPU limit exceeded");
00307 #endif
00308 #ifdef SIGXFSZ
00309        sigdie(SIGXFSZ, "File size exceeded");
00310 #endif
00311                                    /* protect holodeck file */
00312        orig_mode = resfmode(hdlist[0]->fd, (ncprocs>0) & (force>=0) ? 0 : 0444);
00313        return;
00314 memerr:
00315        error(SYSTEM, "out of memory in initrholo");
00316 }
00317 
00318 
00319 static int
00320 rholo(void)                        /* holodeck main loop */
00321 {
00322        static off_t  nextfragwarn = 100L<<20;
00323        static int    idle = 0;
00324        PACKET *pl = NULL, *plend;
00325        off_t  fsiz;
00326        int    pksiz;
00327        register PACKET      *p;
00328        time_t t;
00329                                    /* check display */
00330        if (nprocs <= 0)
00331               idle = 1;
00332        if (outdev != NULL) {
00333               if (!disp_check(idle))
00334                      return(0);    /* quit request */
00335               if (nprocs <= 0)
00336                      return(1);
00337        } else if (idle)
00338               return(0);           /* all done */
00339        fsiz = hdfilen(hdlist[0]->fd);     /* check file size */
00340        if (maxdisk > 0 && fsiz >= maxdisk) {
00341               error(USER, "file limit exceeded");
00342               done_rtrace();
00343               return(1);    /* comes back */
00344        }
00345 #if FRAGWARN
00346        if (fsiz >= nextfragwarn) {
00347               double pctfrag = 100.*(fsiz-hdfiluse(hdlist[0]->fd))/fsiz;
00348                      if (pctfrag >= (double)FRAGWARN) {
00349                      sprintf(errmsg, "holodeck file fragmentation is %.0f%%",
00350                                    pctfrag);
00351                      error(WARNING, errmsg);
00352                      nextfragwarn = fsiz + (fsiz>>2);
00353               } else
00354                      nextfragwarn = fsiz + (10L<<20);
00355        }
00356 #endif
00357        t = time(NULL);                    /* check time */
00358        if (endtime > 0 && t >= endtime) {
00359               error(USER, "time limit exceeded");
00360               done_rtrace();
00361               return(1);    /* comes back */
00362        }
00363        if (reporttime > 0 && t >= reporttime)
00364               report(t);
00365                                    /* figure out good packet size */
00366        pksiz = RPACKSIZ;
00367 #if MAXQTIME
00368        if (!chunkycmp) {
00369               pksiz = nraysdone*MAXQTIME/(totqlen*(t - starttime + 1L));
00370               if (pksiz < 1) pksiz = 1;
00371               else if (pksiz > RPACKSIZ) pksiz = RPACKSIZ;
00372        }
00373 #endif
00374        idle = 0;                   /* get packets to process */
00375        while (freepacks != NULL) {
00376               p = freepacks; freepacks = p->next; p->next = NULL;
00377               if (!next_packet(p, pksiz)) {
00378                      p->next = freepacks; freepacks = p;
00379                      idle = 1;
00380                      break;
00381               }
00382               if (pl == NULL) pl = p;
00383               else plend->next = p;
00384               plend = p;
00385        }
00386                                    /* process packets */
00387        done_packets(do_packets(pl));
00388        return(1);                  /* and continue */
00389 }
00390 
00391 
00392 static void
00393 setdefaults(                /* set default values */
00394        register HDGRID      *gp
00395 )
00396 {
00397        extern char   *atos();
00398        register int  i;
00399        int    n;
00400        double len[3], d;
00401 
00402        if (!vdef(SECTION)) {
00403               sprintf(errmsg, "%s must be defined", vnam(SECTION));
00404               error(USER, errmsg);
00405        }
00406        if (!vdef(OCTREE)) {
00407               if ((vval(OCTREE) = bmalloc(strlen(froot)+5)) == NULL)
00408                      error(SYSTEM, "out of memory");
00409               sprintf(vval(OCTREE), "%s.oct", froot);
00410               vdef(OCTREE)++;
00411        }
00412        if (!vdef(VDIST)) {
00413               vval(VDIST) = "F";
00414               vdef(VDIST)++;
00415        }
00416                             /* append rendering options */
00417        if (vdef(RENDER))
00418               rtargc += wordstring(rtargv+rtargc, vval(RENDER));
00419        
00420        if (gp == NULL)             /* already initialized? */
00421               return;
00422                             /* set grid parameters */
00423        for (n = 0; n < vdef(SECTION); n++, gp++) {
00424               gp->grid[0] = gp->grid[1] = gp->grid[2] = 0;
00425               if (sscanf(nvalue(SECTION, n),
00426               "%lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %hd %hd %hd",
00427                             &gp->orig[0], &gp->orig[1], &gp->orig[2],
00428                             &gp->xv[0][0], &gp->xv[0][1], &gp->xv[0][2],
00429                             &gp->xv[1][0], &gp->xv[1][1], &gp->xv[1][2],
00430                             &gp->xv[2][0], &gp->xv[2][1], &gp->xv[2][2],
00431                             &gp->grid[0], &gp->grid[1], &gp->grid[2]) < 12)
00432                      badvalue(SECTION);
00433               for (i = 0; i < 3; i++)
00434                      len[i] = VLEN(gp->xv[i]);
00435               if (!vdef(GRID)) {
00436                      d = 2/2e5*( len[0]*len[0]*(len[1]*len[1] +
00437                                    len[2]*len[2] + 4*len[1]*len[2])
00438                             + len[1]*len[1]*len[2]*(len[2] + 4*len[0])
00439                             + 4*len[0]*len[1]*len[2]*len[2] );
00440                      d = sqrt(sqrt(d));
00441               } else if ((d = vflt(GRID)) <= FTINY)
00442                      badvalue(GRID);
00443               for (i = 0; i < 3; i++)
00444                      if (gp->grid[i] <= 0)
00445                             gp->grid[i] = len[i]/d + (1.-FTINY);
00446        }
00447 }
00448 
00449 
00450 static void
00451 creatholo(                  /* create a holodeck output file */
00452        HDGRID *gp
00453 )
00454 {
00455        extern char   VersionID[];
00456        int32  lastloc, nextloc;
00457        int    n;
00458        int    fd;
00459        FILE   *fp;
00460                                    /* open & truncate file */
00461        if ((fp = fopen(hdkfile, "w+")) == NULL) {
00462               sprintf(errmsg, "cannot open \"%s\" for writing", hdkfile);
00463               error(SYSTEM, errmsg);
00464        }
00465                                    /* write information header */
00466        newheader("RADIANCE", fp);
00467        fprintf(fp, "SOFTWARE= %s\n", VersionID);
00468        printvars(fp);
00469        fputformat(HOLOFMT, fp);
00470        fputc('\n', fp);
00471        putw(HOLOMAGIC, fp);        /* put magic number */
00472        fd = dup(fileno(fp));
00473        fclose(fp);                 /* flush and close stdio stream */
00474        lastloc = lseek(fd, (off_t)0, SEEK_END);
00475        for (n = vdef(SECTION); n--; gp++) {      /* initialize each section */
00476               nextloc = 0L;
00477               write(fd, (char *)&nextloc, sizeof(nextloc));
00478               hdinit(fd, gp);                    /* writes beam index */
00479               if (!n)
00480                      break;
00481               nextloc = hdfilen(fd);             /* write section pointer */
00482               if (lseek(fd, (off_t)lastloc, SEEK_SET) < 0)
00483                      error(SYSTEM,
00484                             "cannot seek on holodeck file in creatholo");
00485               write(fd, (char *)&nextloc, sizeof(nextloc));
00486               lseek(fd, (off_t)(lastloc=nextloc), SEEK_SET);
00487        }
00488 }
00489 
00490 
00491 static int
00492 headline(                   /* process information header line */
00493        char   *s,
00494        void   *p
00495 )
00496 {
00497        extern char   FMTSTR[];
00498        register char *cp;
00499        char   fmt[32];
00500 
00501        if (formatval(fmt, s)) {
00502               if (strcmp(fmt, HOLOFMT)) {
00503                      sprintf(errmsg, "%s file \"%s\" has %s%s",
00504                                    HOLOFMT, hdkfile, FMTSTR, fmt);
00505                      error(USER, errmsg);
00506               }
00507               return(0);
00508        }
00509        for (cp = s; *cp; cp++)            /* take off any comments */
00510               if (*cp == '#') {
00511                      *cp = '\0';
00512                      break;
00513               }
00514        setvariable(s, matchvar);   /* don't flag errors */
00515        return(0);
00516 }
00517 
00518 
00519 static void
00520 loadholo(void)                     /* start loading a holodeck from fname */
00521 {
00522        FILE   *fp;
00523        int    fd;
00524        int    n;
00525        int32  nextloc;
00526        
00527        if ((ncprocs > 0) & (force >= 0))
00528               fp = fopen(hdkfile, "r+");
00529        else
00530               fp = NULL;
00531        if (fp == NULL) {
00532               if ((fp = fopen(hdkfile, "r")) == NULL) {
00533                      sprintf(errmsg, "cannot open \"%s\"", hdkfile);
00534                      error(SYSTEM, errmsg);
00535               }
00536               if (ncprocs > 0) {
00537                      sprintf(errmsg, "\"%s\" is read-only", hdkfile);
00538                      if (outdev == NULL)
00539                             error(USER, errmsg);
00540                      strcat(errmsg, "; new rays will be discarded");
00541                      error(WARNING, errmsg);
00542                      force = -1;
00543               }
00544        }
00545                                    /* load variables from header */
00546        getheader(fp, headline, NULL);
00547                                    /* check magic number */
00548        if (getw(fp) != HOLOMAGIC) {
00549               sprintf(errmsg, "bad magic number in holodeck file \"%s\"",
00550                             hdkfile);
00551               error(USER, errmsg);
00552        }
00553        nextloc = ftell(fp);               /* get stdio position */
00554        fd = dup(fileno(fp));
00555        fclose(fp);                        /* done with stdio */
00556        for (n = 0; nextloc > 0L; n++) {   /* initialize each section */
00557               lseek(fd, (off_t)nextloc, SEEK_SET);
00558               read(fd, (char *)&nextloc, sizeof(nextloc));
00559               hdinit(fd, NULL);
00560        }
00561        if (n != vdef(SECTION)) {
00562               sprintf(errmsg, "number of sections does not match %s setting",
00563                             vnam(SECTION));
00564               error(WARNING, errmsg);
00565        }
00566 }
00567 
00568 
00569 extern void
00570 done_packets(        /* handle finished packets */
00571        PACKET *pl
00572 )
00573 {
00574        static int    n2flush = 0;
00575        register PACKET      *p;
00576 
00577        while (pl != NULL) {
00578               p = pl; pl = p->next; p->next = NULL;
00579               if (p->nr > 0) {            /* add to holodeck */
00580                      memcpy( (void *)hdnewrays(hdlist[p->hd],p->bi,p->nr),
00581                             (void *)p->ra,
00582                             p->nr*sizeof(RAYVAL));
00583                      if (outdev != NULL)  /* display it */
00584                             disp_packet((PACKHEAD *)p);
00585                      if (hdcachesize <= 0)
00586                             n2flush++;
00587                      nraysdone += p->nr;
00588                      npacksdone++;
00589                      p->nr = 0;
00590               }
00591               p->next = freepacks;        /* push onto free list */
00592               freepacks = p;
00593        }
00594        if (n2flush >= RTFLUSH) {
00595               if (outdev != NULL)
00596                      hdsync(NULL, 1);
00597               else
00598                      hdflush(NULL);
00599               n2flush = 0;
00600        }
00601 }
00602 
00603 
00604 static void
00605 rootname(            /* remove tail from end of fn */
00606        register char *rn,
00607        register char *fn
00608 )
00609 {
00610        char   *tp, *dp;
00611 
00612        for (tp = NULL, dp = rn; (*rn = *fn++); rn++) {
00613               if (*rn == '/')
00614                      dp = rn;
00615               else if (*rn == '.')
00616                      tp = rn;
00617        }
00618        if (tp != NULL && tp > dp)
00619               *tp = '\0';
00620 }
00621 
00622 
00623 static void
00624 badvalue(                   /* report bad variable value and exit */
00625        int    vc
00626 )
00627 {
00628        sprintf(errmsg, "bad value for variable '%s'", vnam(vc));
00629        error(USER, errmsg);
00630 }
00631 
00632 
00633 void
00634 eputs(s)                    /* put error message to stderr */
00635 register char  *s;
00636 {
00637        static int  midline = 0;
00638 
00639        if (!*s)
00640               return;
00641        if (!midline++) {    /* prepend line with program name */
00642               fputs(progname, stderr);
00643               fputs(": ", stderr);
00644        }
00645        fputs(s, stderr);
00646        if (s[strlen(s)-1] == '\n') {
00647               fflush(stderr);
00648               midline = 0;
00649        }
00650 }
00651 
00652 
00653 void
00654 quit(ec)                    /* exit program gracefully */
00655 int    ec;
00656 {
00657        int    status = 0;
00658 
00659        if (hdlist[0] != NULL) {    /* close holodeck */
00660               if (nprocs > 0)
00661                      status = done_rtrace();            /* calls hdsync() */
00662               if ((ncprocs > 0) & (force >= 0) && vdef(REPORT)) {
00663                      off_t  fsiz, fuse;
00664                      fsiz = hdfilen(hdlist[0]->fd);
00665                      fuse = hdfiluse(hdlist[0]->fd);
00666                      fprintf(stderr,
00667                      "%s: %.1f Mbyte holodeck file, %.1f%% fragmentation\n",
00668                                    hdkfile, fsiz/(1024.*1024.),
00669                                    100.*(fsiz-fuse)/fsiz);
00670               }
00671        }
00672        if (orig_mode >= 0)         /* reset holodeck access mode */
00673               fchmod(hdlist[0]->fd, orig_mode);
00674        if (outdev != NULL)         /* close display */
00675               disp_close();
00676        exit(ec ? ec : status);            /* exit */
00677 }