| 1 | greg | 2.1 | #ifndef lint | 
| 2 | greg | 2.32 | static const char       RCSid[] = "$Id: persist.c,v 2.31 2003/08/30 09:03:31 schorsch Exp $"; | 
| 3 | greg | 2.1 | #endif | 
| 4 |  |  | /* | 
| 5 |  |  | * Routines for persistent rtrace and rpict processes. | 
| 6 | greg | 2.26 | * | 
| 7 |  |  | *  External symbols declared in ray.h | 
| 8 |  |  | */ | 
| 9 |  |  |  | 
| 10 | greg | 2.27 | #include "copyright.h" | 
| 11 | greg | 2.1 |  | 
| 12 | schorsch | 2.29 | #include <string.h> | 
| 13 |  |  | #include <signal.h> | 
| 14 |  |  | #include <sys/stat.h> | 
| 15 |  |  |  | 
| 16 | schorsch | 2.30 | #include "rtprocess.h" /* getpid() */ | 
| 17 | greg | 2.1 | #include "standard.h" | 
| 18 | greg | 2.32 | #include "platform.h" | 
| 19 | gregl | 2.25 | #include "random.h" | 
| 20 | greg | 2.6 |  | 
| 21 |  |  | #ifdef F_SETLKW | 
| 22 | greg | 2.1 | #include "paths.h" | 
| 23 | gregl | 2.21 | #include "selcall.h" | 
| 24 | greg | 2.1 |  | 
| 25 | greg | 2.2 | #ifndef TIMELIM | 
| 26 |  |  | #define TIMELIM         (8*3600)        /* time limit for holding pattern */ | 
| 27 | greg | 2.26 | #endif | 
| 28 |  |  |  | 
| 29 |  |  | extern void     io_process(); | 
| 30 | greg | 2.1 |  | 
| 31 |  |  | extern int      headismine;     /* boolean true if header belongs to me */ | 
| 32 |  |  |  | 
| 33 | greg | 2.12 | extern char     *progname;      /* global program name */ | 
| 34 |  |  |  | 
| 35 | greg | 2.14 | extern char     *errfile;       /* global error file name */ | 
| 36 |  |  |  | 
| 37 | greg | 2.1 | static char     *persistfname = NULL;   /* persist file name */ | 
| 38 |  |  | static int      persistfd = -1;         /* persist file descriptor */ | 
| 39 |  |  |  | 
| 40 | greg | 2.14 | static char     inpname[TEMPLEN+1], outpname[TEMPLEN+1], errname[TEMPLEN+1]; | 
| 41 | greg | 2.1 |  | 
| 42 |  |  |  | 
| 43 | greg | 2.26 | void | 
| 44 | greg | 2.1 | pfdetach()              /* release persist (and header) resources */ | 
| 45 |  |  | { | 
| 46 |  |  | if (persistfd >= 0) | 
| 47 |  |  | close(persistfd); | 
| 48 |  |  | persistfd = -1; | 
| 49 |  |  | persistfname = NULL; | 
| 50 |  |  | inpname[0] = '\0'; | 
| 51 |  |  | outpname[0] = '\0'; | 
| 52 | greg | 2.14 | errname[0] = '\0'; | 
| 53 | greg | 2.1 | headismine = 0; | 
| 54 |  |  | } | 
| 55 |  |  |  | 
| 56 |  |  |  | 
| 57 | greg | 2.26 | void | 
| 58 | greg | 2.1 | pfclean()               /* clean up persist files */ | 
| 59 |  |  | { | 
| 60 |  |  | if (persistfd >= 0) | 
| 61 |  |  | close(persistfd); | 
| 62 |  |  | if (persistfname != NULL) | 
| 63 |  |  | unlink(persistfname); | 
| 64 |  |  | if (inpname[0]) | 
| 65 |  |  | unlink(inpname); | 
| 66 |  |  | if (outpname[0]) | 
| 67 |  |  | unlink(outpname); | 
| 68 | greg | 2.14 | if (errname[0]) | 
| 69 |  |  | unlink(errname); | 
| 70 | greg | 2.1 | } | 
| 71 |  |  |  | 
| 72 |  |  |  | 
| 73 | greg | 2.26 | void | 
| 74 | greg | 2.1 | pflock(lf)              /* place or release exclusive lock on file */ | 
| 75 |  |  | int     lf; | 
| 76 |  |  | { | 
| 77 |  |  | struct flock    fls; | 
| 78 |  |  |  | 
| 79 |  |  | fls.l_type = lf ? F_WRLCK : F_UNLCK; | 
| 80 |  |  | fls.l_whence = 0; | 
| 81 |  |  | fls.l_start = 0L; | 
| 82 |  |  | fls.l_len = 0L; | 
| 83 |  |  | if (fcntl(persistfd, F_SETLKW, &fls) < 0) | 
| 84 |  |  | error(SYSTEM, "cannot (un)lock persist file"); | 
| 85 |  |  | } | 
| 86 |  |  |  | 
| 87 |  |  |  | 
| 88 | schorsch | 2.30 | void | 
| 89 | greg | 2.1 | persistfile(pfn)        /* open persist file and lock it */ | 
| 90 |  |  | char    *pfn; | 
| 91 |  |  | { | 
| 92 | greg | 2.13 | persistfd = open(pfn, O_WRONLY|O_CREAT|O_EXCL, 0644); | 
| 93 | greg | 2.1 | if (persistfd >= 0) { | 
| 94 |  |  | persistfname = pfn; | 
| 95 |  |  | pflock(1); | 
| 96 |  |  | return; | 
| 97 |  |  | } | 
| 98 |  |  | /* file exists -- switch to i/o process */ | 
| 99 |  |  | persistfd = open(pfn, O_RDWR); | 
| 100 |  |  | if (persistfd < 0) { | 
| 101 |  |  | sprintf(errmsg, "cannot open persist file \"%s\"", pfn); | 
| 102 |  |  | error(SYSTEM, errmsg); | 
| 103 |  |  | } | 
| 104 |  |  | pflock(1); | 
| 105 |  |  | io_process();   /* never returns */ | 
| 106 |  |  | } | 
| 107 |  |  |  | 
| 108 |  |  |  | 
| 109 | greg | 2.13 | static int      got_io; | 
| 110 | greg | 2.1 |  | 
| 111 | greg | 2.26 | static void sig_io() { got_io++; } | 
| 112 | greg | 2.1 |  | 
| 113 | greg | 2.26 | static void sig_alrm() { quit(0); } | 
| 114 | greg | 2.13 |  | 
| 115 |  |  |  | 
| 116 | greg | 2.26 | void | 
| 117 | greg | 2.1 | pfhold()                /* holding pattern for idle rendering process */ | 
| 118 |  |  | { | 
| 119 | greg | 2.26 | void    (*oldalrm)(); | 
| 120 | greg | 2.12 | char    buf[512]; | 
| 121 | greg | 2.1 | register int    n; | 
| 122 |  |  | /* close input and output descriptors */ | 
| 123 |  |  | close(fileno(stdin)); | 
| 124 |  |  | close(fileno(stdout)); | 
| 125 | greg | 2.16 | if (errfile == NULL) | 
| 126 |  |  | close(fileno(stderr)); | 
| 127 | greg | 2.1 | /* create named pipes for input and output */ | 
| 128 | greg | 2.26 | if (mkfifo(mktemp(strcpy(inpname,TEMPLATE)), 0600) < 0) | 
| 129 | greg | 2.1 | goto createrr; | 
| 130 | greg | 2.26 | if (mkfifo(mktemp(strcpy(outpname,TEMPLATE)), 0600) < 0) | 
| 131 | greg | 2.1 | goto createrr; | 
| 132 | greg | 2.14 | if (errfile == NULL && | 
| 133 | greg | 2.26 | mkfifo(mktemp(strcpy(errname,TEMPLATE)), 0600) < 0) | 
| 134 | greg | 2.14 | goto createrr; | 
| 135 |  |  | sprintf(buf, "%s %d\n%s\n%s\n%s\n", progname, getpid(), | 
| 136 |  |  | inpname, outpname, errname); | 
| 137 | greg | 2.1 | n = strlen(buf); | 
| 138 |  |  | if (write(persistfd, buf, n) < n) | 
| 139 | greg | 2.10 | error(SYSTEM, "error writing persist file"); | 
| 140 | greg | 2.32 | lseek(persistfd, (off_t)0, SEEK_SET); | 
| 141 | greg | 2.2 | /* wait TIMELIM for someone to signal us */ | 
| 142 | greg | 2.13 | got_io = 0; | 
| 143 |  |  | signal(SIGIO, sig_io); | 
| 144 | greg | 2.26 | oldalrm = (void (*)())signal(SIGALRM, sig_alrm); | 
| 145 | greg | 2.2 | alarm(TIMELIM); | 
| 146 | greg | 2.15 | pflock(0);                      /* unlock persist file for attach */ | 
| 147 | greg | 2.13 | while (!got_io) | 
| 148 | greg | 2.15 | pause();                /* wait for attach */ | 
| 149 |  |  | alarm(0);                       /* turn off alarm */ | 
| 150 | greg | 2.13 | signal(SIGALRM, oldalrm); | 
| 151 | greg | 2.2 | signal(SIGIO, SIG_DFL); | 
| 152 | greg | 2.17 | pflock(1);                      /* grab persist file back */ | 
| 153 | greg | 2.1 | /* someone wants us; reopen stdin and stdout */ | 
| 154 | greg | 2.26 | /* | 
| 155 | greg | 2.1 | if (freopen(inpname, "r", stdin) == NULL) | 
| 156 |  |  | goto openerr; | 
| 157 |  |  | if (freopen(outpname, "w", stdout) == NULL) | 
| 158 |  |  | goto openerr; | 
| 159 | greg | 2.26 | */ | 
| 160 |  |  | close(0); | 
| 161 |  |  | if (open(inpname, O_RDONLY) != 0) | 
| 162 |  |  | error(INTERNAL, "unexpected stdin file number"); | 
| 163 |  |  | clearerr(stdin); | 
| 164 |  |  | close(1); | 
| 165 |  |  | if (open(outpname, O_WRONLY) != 1) | 
| 166 |  |  | error(INTERNAL, "unexpected stdout file number"); | 
| 167 | gregl | 2.22 | sleep(3);               /* give them a chance to open their pipes */ | 
| 168 | greg | 2.14 | if (errname[0]) { | 
| 169 | greg | 2.26 | close(2); | 
| 170 |  |  | if (open(errname, O_WRONLY) != 2) | 
| 171 |  |  | error(INTERNAL, "unexpected stderr file number"); | 
| 172 | greg | 2.14 | unlink(errname); | 
| 173 |  |  | errname[0] = '\0'; | 
| 174 |  |  | } | 
| 175 | greg | 2.1 | unlink(inpname); | 
| 176 |  |  | inpname[0] = '\0'; | 
| 177 |  |  | unlink(outpname); | 
| 178 |  |  | outpname[0] = '\0'; | 
| 179 |  |  | return; | 
| 180 |  |  | createrr: | 
| 181 |  |  | error(SYSTEM, "cannot create named pipes in pfhold"); | 
| 182 |  |  | openerr: | 
| 183 |  |  | error(SYSTEM, "cannot open named pipes in pfhold"); | 
| 184 |  |  | } | 
| 185 |  |  |  | 
| 186 |  |  |  | 
| 187 | greg | 2.26 | void | 
| 188 | greg | 2.16 | io_process()            /* just act as go-between for actual process */ | 
| 189 | greg | 2.1 | { | 
| 190 |  |  | register char   *cp; | 
| 191 |  |  | register int    nr, n; | 
| 192 | greg | 2.16 | char    buf[BUFSIZ], *pfin, *pfout, *pferr; | 
| 193 |  |  | int     pid, nfds; | 
| 194 | gregl | 2.22 | int     fdout, fderr = -1; | 
| 195 | gregl | 2.23 | int     status = 0; | 
| 196 | gregl | 2.22 | fd_set  readfds, excepfds; | 
| 197 | greg | 2.15 | /* load persist file */ | 
| 198 | greg | 2.17 | n = 40; | 
| 199 | greg | 2.15 | while ((nr = read(persistfd, buf, sizeof(buf)-1)) == 0) { | 
| 200 | greg | 2.17 | if (!n--) | 
| 201 |  |  | error(USER, "unattended persist file?"); | 
| 202 | greg | 2.15 | pflock(0); | 
| 203 | gregl | 2.25 | sleep(3+(3*getpid()+random())%13);      /* wait until ready */ | 
| 204 | greg | 2.15 | pflock(1); | 
| 205 |  |  | } | 
| 206 |  |  | if (nr < 0) | 
| 207 | greg | 2.14 | error(SYSTEM, "error reading persist file"); | 
| 208 | schorsch | 2.28 | #ifndef _WIN32 /* XXX we need a replacement for that one */ | 
| 209 | greg | 2.26 | ftruncate(persistfd, (off_t)0L);        /* truncate persist file */ | 
| 210 | schorsch | 2.28 | #endif | 
| 211 | greg | 2.17 | pfdetach();                     /* close & release persist file */ | 
| 212 |  |  | buf[nr] = '\0';                 /* parse what we got */ | 
| 213 | schorsch | 2.29 | if ((cp = strchr(buf, ' ')) == NULL) | 
| 214 | greg | 2.1 | goto formerr; | 
| 215 |  |  | *cp++ = '\0'; | 
| 216 | greg | 2.12 | if ((pid = atoi(cp)) <= 0) | 
| 217 | greg | 2.1 | goto formerr; | 
| 218 | schorsch | 2.29 | if ((cp = strchr(cp, '\n')) == NULL) | 
| 219 | greg | 2.1 | goto formerr; | 
| 220 | greg | 2.12 | pfin = ++cp; | 
| 221 | schorsch | 2.29 | if ((cp = strchr(cp, '\n')) == NULL) | 
| 222 | greg | 2.12 | goto formerr; | 
| 223 | greg | 2.1 | *cp++ = '\0'; | 
| 224 |  |  | pfout = cp; | 
| 225 | schorsch | 2.29 | if ((cp = strchr(cp, '\n')) == NULL) | 
| 226 | greg | 2.1 | goto formerr; | 
| 227 |  |  | *cp++ = '\0'; | 
| 228 | greg | 2.14 | pferr = cp; | 
| 229 | schorsch | 2.29 | if ((cp = strchr(cp, '\n')) == NULL) | 
| 230 | greg | 2.14 | goto formerr; | 
| 231 |  |  | *cp++ = '\0'; | 
| 232 | greg | 2.1 | if (cp-buf != nr) | 
| 233 |  |  | goto formerr; | 
| 234 | greg | 2.12 | if (strcmp(buf, progname)) { | 
| 235 |  |  | sprintf(errmsg, "persist file for %s, not %s", buf, progname); | 
| 236 |  |  | error(USER, errmsg); | 
| 237 |  |  | } | 
| 238 | greg | 2.16 | /* wake up rendering process */ | 
| 239 | greg | 2.2 | if (kill(pid, SIGIO) < 0) | 
| 240 | greg | 2.1 | error(SYSTEM, "cannot signal rendering process in io_process"); | 
| 241 | gregl | 2.22 | /* fork child feeder process */ | 
| 242 |  |  | pid = fork(); | 
| 243 |  |  | if (pid < 0) | 
| 244 |  |  | error(SYSTEM, "fork failed in io_process"); | 
| 245 |  |  | if (pid == 0) {                 /* feeder loop */ | 
| 246 |  |  | int     fdin; | 
| 247 |  |  | close(1);               /* open input pipe */ | 
| 248 |  |  | if ((fdin = open(pfin, O_WRONLY)) < 0) | 
| 249 |  |  | error(SYSTEM, "cannot open feed pipe in io_process"); | 
| 250 |  |  | /* renderer stdin */ | 
| 251 |  |  | while ((nr = read(0, cp=buf, sizeof(buf))) > 0) { | 
| 252 |  |  | do { | 
| 253 |  |  | if ((n = write(fdin, cp, nr)) <= 0) | 
| 254 |  |  | goto writerr; | 
| 255 |  |  | cp += n; | 
| 256 |  |  | } while ((nr -= n) > 0); | 
| 257 |  |  | } | 
| 258 |  |  | if (nr < 0) | 
| 259 |  |  | goto readerr; | 
| 260 |  |  | _exit(0); | 
| 261 |  |  | } | 
| 262 |  |  | close(0); | 
| 263 |  |  | /* open output pipes, in order */ | 
| 264 | greg | 2.16 | if ((fdout = open(pfout, O_RDONLY)) < 0) | 
| 265 |  |  | error(SYSTEM, "cannot open output pipe in io_process"); | 
| 266 |  |  | if (pferr[0] && (fderr = open(pferr, O_RDONLY)) < 0) | 
| 267 |  |  | error(SYSTEM, "cannot open error pipe in io_process"); | 
| 268 | gregl | 2.22 | for ( ; ; ) {                   /* eater loop */ | 
| 269 | greg | 2.16 | FD_ZERO(&readfds); | 
| 270 |  |  | FD_ZERO(&excepfds); | 
| 271 |  |  | nfds = 0; | 
| 272 |  |  | if (fdout >= 0) { | 
| 273 |  |  | FD_SET(fdout, &readfds); | 
| 274 |  |  | FD_SET(fdout, &excepfds); | 
| 275 |  |  | nfds = fdout+1; | 
| 276 | greg | 2.14 | } | 
| 277 | greg | 2.16 | if (fderr >= 0) { | 
| 278 |  |  | FD_SET(fderr, &readfds); | 
| 279 |  |  | FD_SET(fderr, &excepfds); | 
| 280 |  |  | nfds = fderr+1; | 
| 281 |  |  | } | 
| 282 |  |  | if (nfds == 0) | 
| 283 |  |  | break;                  /* all done, exit */ | 
| 284 | gregl | 2.22 | if (select(nfds, &readfds, NULL, &excepfds, NULL) < 0) | 
| 285 | greg | 2.16 | error(SYSTEM, "error in select call in io_process"); | 
| 286 |  |  | /* renderer stderr */ | 
| 287 |  |  | if (fderr >= 0 && (FD_ISSET(fderr, &readfds) || | 
| 288 |  |  | FD_ISSET(fderr, &excepfds))) { | 
| 289 |  |  | nr = read(fderr, cp=buf, sizeof(buf)); | 
| 290 |  |  | if (nr < 0) | 
| 291 |  |  | goto readerr; | 
| 292 |  |  | if (nr == 0) { | 
| 293 |  |  | close(fderr); | 
| 294 | greg | 2.18 | /* close(2);    don't close stderr! */ | 
| 295 | greg | 2.16 | fderr = -1; | 
| 296 | gregl | 2.24 | } else { | 
| 297 | gregl | 2.23 | cp[nr] = '\0';  /* deduce status if we can */ | 
| 298 |  |  | n = strlen(progname); | 
| 299 |  |  | if (!strncmp(cp, progname, n) && | 
| 300 |  |  | cp[n++] == ':' && | 
| 301 |  |  | cp[n++] == ' ') { | 
| 302 |  |  | register struct erract  *ep; | 
| 303 |  |  | for (ep = erract; ep < erract+NERRS; | 
| 304 |  |  | ep++) | 
| 305 |  |  | if (ep->pre[0] && | 
| 306 |  |  | !strncmp(cp+n, ep->pre, | 
| 307 |  |  | strlen(ep->pre))) { | 
| 308 |  |  | status = ep->ec; | 
| 309 |  |  | break; | 
| 310 |  |  | } | 
| 311 |  |  | } | 
| 312 |  |  | do {            /* write message */ | 
| 313 | greg | 2.16 | if ((n = write(2, cp, nr)) <= 0) | 
| 314 |  |  | goto writerr; | 
| 315 |  |  | cp += n; | 
| 316 |  |  | } while ((nr -= n) > 0); | 
| 317 | gregl | 2.24 | } | 
| 318 | greg | 2.16 | } | 
| 319 |  |  | /* renderer stdout */ | 
| 320 |  |  | if (fdout >= 0 && (FD_ISSET(fdout, &readfds) || | 
| 321 |  |  | FD_ISSET(fdout, &excepfds))) { | 
| 322 |  |  | nr = read(fdout, cp=buf, sizeof(buf)); | 
| 323 |  |  | if (nr < 0) | 
| 324 |  |  | goto readerr; | 
| 325 |  |  | if (nr == 0) {          /* EOF */ | 
| 326 |  |  | close(fdout); | 
| 327 |  |  | close(1); | 
| 328 |  |  | fdout = -1; | 
| 329 |  |  | } else | 
| 330 |  |  | do {            /* write it all */ | 
| 331 |  |  | if ((n = write(1, cp, nr)) <= 0) | 
| 332 |  |  | goto writerr; | 
| 333 |  |  | cp += n; | 
| 334 |  |  | } while ((nr -= n) > 0); | 
| 335 |  |  | } | 
| 336 | greg | 2.1 | } | 
| 337 | gregl | 2.23 | wait(0);                /* wait for feeder process */ | 
| 338 |  |  | _exit(status); | 
| 339 | greg | 2.1 | formerr: | 
| 340 |  |  | error(USER, "format error in persist file"); | 
| 341 | greg | 2.16 | readerr: | 
| 342 |  |  | error(SYSTEM, "read error in io_process"); | 
| 343 |  |  | writerr: | 
| 344 |  |  | error(SYSTEM, "write error in io_process"); | 
| 345 | greg | 2.1 | } | 
| 346 | greg | 2.6 |  | 
| 347 | greg | 2.8 | #else | 
| 348 |  |  |  | 
| 349 | greg | 2.26 | void pfclean() {} | 
| 350 | greg | 2.8 |  | 
| 351 | greg | 2.6 | #endif |