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