1 //////////////////////////////////////////////////////////////////////
2 //
3 // Pixie
4 //
5 // Copyright � 1999 - 2003, Okan Arikan
6 //
7 // Contact: okan@cs.utexas.edu
8 //
9 // This library is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU Lesser General Public
11 // License as published by the Free Software Foundation; either
12 // version 2.1 of the License, or (at your option) any later version.
13 //
14 // This library is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 // Lesser General Public License for more details.
18 //
19 // You should have received a copy of the GNU Lesser General Public
20 // License along with this library; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 //
23 ///////////////////////////////////////////////////////////////////////
24 ///////////////////////////////////////////////////////////////////////
25 //
26 // File : rndr.cpp
27 // Classes : -
28 // Description : rib parser
29 //
30 ////////////////////////////////////////////////////////////////////////
31 #include <stdlib.h>
32
33 #include "common/global.h"
34 #include "common/os.h"
35 #include "common/containers.h"
36 #include "ri/ri.h"
37
38
39 #ifdef _WINDOWS
40 #include <process.h>
41 #else
42 #include <sys/select.h>
43 #include <netinet/tcp.h>
44 #include <signal.h>
45 #endif
46
47 #define BUFFERSIZE 1024
48 #define DEFAULT_DAEMON_PORT 24666
49 #define MAX_LOCALSERVERS 8
50
51 // Whether non Windows implementations should exec after fork
52 // Windows doesn't have / use fork anyway
53
54 #define USE_PURE_FORK 0
55
56 // Prototype for main
57 int main(int argc, char* argv[]);
58 static int gargc;
59 static char **gargv;
60 static int isDaemon;
61 static int silent;
62 static int noRestart;
63 static SOCKET listenSock;
64 static int numLocalServers;
65 static SOCKET localServerSockets[MAX_LOCALSERVERS];
66
67
68 // Stats signal handling
69 extern "C" {
70 void printStatsHandler(int);
71 }
72
73
74 ///////////////////////////////////////////////////////////////////////
75 // Function : exitFunction
76 // Description : This function is called before exitting so that if
77 // we're running a server, it can spawn another one
78 // Return Value : -
79 // Comments :
exitFunction()80 void exitFunction() {
81 if (isDaemon == TRUE) {
82 // Close socket before respawning
83 closesocket(listenSock);
84
85 if (noRestart == FALSE) {
86 char **argv = new char*[gargc+2];
87 int i;
88
89 for (i=0;i<gargc;++i) argv[i] = gargv[i];
90 argv[i++] = (char *) "-q";
91 argv[i] = NULL;
92
93 // use execvp to search PATH, incase pixie
94 // isn't on the default search path
95 #ifdef _WINDOWS
96 _execvp(argv[0],argv);
97 #else
98 execvp(argv[0],argv);
99 #endif
100 delete [] argv;
101 }
102 } else if (numLocalServers > 0) {
103 // we have open local sockets, close them
104 int i = closesocket(listenSock);
105 for (int j=0;j<numLocalServers;j++) {
106 i = closesocket(localServerSockets[j]);
107 }
108 // we may wish to kill subprocesses here
109 }
110 }
111
112 ///////////////////////////////////////////////////////////////////////
113 // Function : printVersion
114 // Description : Print the version
115 // Return Value : -
116 // Comments :
printVersion()117 void printVersion() {
118 printf("Pixie RenderMan Renderer (rndr) v%d.%d.%d\n",VERSION_RELEASE,VERSION_BETA,VERSION_ALPHA);
119 printf("\nCopyright 1999-2008 Okan Arikan. http://renderpixie.com/\n");
120 printf("Pixie is free software. There is NO warranty; not even for\n");
121 printf("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
122 }
123
124 ///////////////////////////////////////////////////////////////////////
125 // Function : printUsage
126 // Description : Print the stinking usage
127 // Return Value : -
128 // Comments :
printUsage()129 void printUsage() {
130 printf("Usage: rndr <options> file.rib [file.rib ...]\n");
131 printf("Listing several RIB files concatenates them before rendering.\n");
132 printf("\nOptions:\n");
133 printf(" -f <range> Render only a subsequence of frames\n");
134 printf(" -f 43 = Render only the 43rd frame\n");
135 printf(" -f 5:15 = Render frames 5 thru 15\n");
136 printf(" -f 5:2:15 = Render every second frame from 5 thru 15\n");
137 printf(" -q Quiet mode; errors and warnings are ignored\n");
138 printf(" -d Ignore the display drivers and use framebuffer\n");
139 printf(" -t Print renderer statistics after every frame\n");
140 printf(" -p Display rendering progress\n");
141 printf(" -P:<n> Render using <n> processes; default is using one process\n");
142 printf(" -t:<n> Render using <n> threads; default is one thread per CPU core\n");
143 printf(" -r [port] Start a network server. If given, use port <port>\n");
144 printf(" -k <serverlist> Stop network servers in <serverlist>\n");
145 printf(" -s <serverlist> Render on network servers in <serverlist>\n");
146 printf(" <serverlist> specified as <IP[:port],IP[:port],...>\n");
147 printf(" -v Display version information\n");
148 printf(" -h Display this help\n");
149 printf("\nEnvironment variables:\n");
150 printf(" PIXIEHOME Pixie installation path\n");
151 printf(" SHADERS Shader search path\n");
152 }
153
154
155 ///////////////////////////////////////////////////////////////////////
156 // Function : riThread
157 // Description : The main rendering thread
158 // Return Value :
159 // Comments :
riThread(void * w)160 void riThread(void *w) {
161 T32 *buffer = (T32 *) w;
162 char managerString[1024];
163 T32 a;
164
165 a.integer = 0;
166 send((SOCKET) buffer[0].integer,(char *) &a,sizeof(T32),0);
167
168 sprintf(managerString,"#rib:%s net:client=%d",(char *) &buffer[1].character,buffer[0].integer);
169
170 // I may want to do this in a seperate process
171 RiBegin(managerString);
172
173 #ifndef _WINDOWS
174 signal(SIGHUP,printStatsHandler);
175 #ifdef SIGINFO
176 signal(SIGINFO,printStatsHandler);
177 #endif
178 #endif
179
180 char *source = strdup((char *) &buffer[1].character);
181 char *pstart = source;
182 char *pend;
183 do {
184 pend = strchr(pstart,':');
185 if (pend) *pend = '\0';
186 RiReadArchive(pstart,NULL,NULL);
187 pstart = pend + 1;
188 } while(pend);
189 free(source);
190 RiEnd();
191 }
192
193
194
195 ///////////////////////////////////////////////////////////////////////
196 // Function : rndrc
197 // Description : run as a local server and connect to client
198 // Return Value : -
199 // Comments : Servers connect back to client to avoid race
rndrc(char * ribFile,int port)200 void rndrc(char *ribFile,int port) {
201 char managerString[1024];
202 SOCKET sock;
203 struct sockaddr_in client;
204
205 #ifdef _WINDOWS
206 WSADATA wsaData;
207
208 // Init the winsock
209 if (WSAStartup(0x202,&wsaData) == SOCKET_ERROR) {
210 WSACleanup();
211 if (silent == FALSE) fprintf(stderr,"Winsock init error\n");
212 return;
213 }
214 #else
215 // For platforms where we can't disable sigpipe per socket, do it globally
216 #ifndef SO_NOSIGPIPE
217 signal(SIGPIPE,SIG_IGN);
218 #endif
219 #endif
220
221 unsigned int attemptAddress = INADDR_ANY;
222
223 // Here we include robustness for Windows not allowing bind / connect to ANY
224 retryBind:
225
226 sock = socket(AF_INET, SOCK_STREAM, 0);
227 if (sock == INVALID_SOCKET) {
228 if (silent == FALSE) fprintf(stderr,"Socket error\n");
229 return;
230 }
231
232 // Ensure there's no delay on network transactions
233 int val = 1;
234 setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(const char *) &val,sizeof(int));
235 val = 1;
236 setsockopt(sock,IPPROTO_TCP,TCP_NODELAY,(const char *) &val,sizeof(int));
237
238 // connect to server
239
240 client.sin_family = AF_INET;
241 client.sin_addr.s_addr = htonl(attemptAddress);
242 client.sin_port = htons(port);
243
244 if (connect(sock, (struct sockaddr *) &client, sizeof(client)) < 0) {
245 // Retry with loopback
246 if (attemptAddress != INADDR_LOOPBACK) {
247 closesocket(sock);
248 attemptAddress = INADDR_LOOPBACK;
249 goto retryBind;
250 }
251 if (silent == FALSE) fprintf(stderr,"Connection error\n");
252 closesocket(sock);
253 return;
254 }
255
256 // Run the rib
257
258 sprintf(managerString,"#rib:%s net:locclient=%d",ribFile,sock);
259
260 RiBegin(managerString);
261
262 #ifndef _WINDOWS
263 signal(SIGHUP,printStatsHandler);
264 #ifdef SIGINFO
265 signal(SIGINFO,printStatsHandler);
266 #endif
267 #endif
268
269 char *source = strdup(ribFile);
270 char *pstart = source;
271 char *pend;
272 do {
273 pend = strchr(pstart,':');
274 if (pend) *pend = '\0';
275 RiReadArchive(pstart,NULL,NULL);
276 pstart = pend + 1;
277 } while(pend);
278 free(source);
279 RiEnd();
280 }
281
282 ///////////////////////////////////////////////////////////////////////
283 // Function : runLocalServers
284 // Description : Run a set of subprocess and pre-accept connects
285 // Return Value : -
286 // Comments : the accepted sockets are handed back in managerString
runLocalServers(int numChildren,char * ribFile,char * managerString)287 int runLocalServers(int numChildren,char *ribFile,char *managerString) {
288 SOCKET sock;
289 struct sockaddr_in me;
290 int listenPort;
291 char *tmp = managerString + strlen(managerString);
292 int i,j;
293
294 #ifdef _WINDOWS
295 WSADATA wsaData;
296
297 // Init the winsock
298 if (WSAStartup(0x202,&wsaData) == SOCKET_ERROR) {
299 WSACleanup();
300 if (silent == FALSE) fprintf(stderr,"Winsock init error\n");
301 return FALSE;
302 }
303 #else
304 // For platforms where we can't disable sigpipe per socket, do it globally
305 #ifndef SO_NOSIGPIPE
306 signal(SIGPIPE,SIG_IGN);
307 #endif
308 #endif
309
310 // Create the socket
311 sock = socket(AF_INET, SOCK_STREAM, 0);
312 if (sock == INVALID_SOCKET) {
313 if (silent == FALSE) fprintf(stderr,"Socket error\n");
314 return FALSE;
315 }
316
317 // Ensure there's no delay on network transactions
318 int val = 1;
319 setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(const char *) &val,sizeof(int));
320 val = 1;
321 setsockopt(sock,IPPROTO_TCP,TCP_NODELAY,(const char *) &val,sizeof(int));
322
323 // Bind to a port (let the os decide which)
324 me.sin_family = AF_INET;
325 me.sin_addr.s_addr = htonl(INADDR_ANY);
326 me.sin_port = htons(0);
327 if (bind(sock, (struct sockaddr *) &me, sizeof(me)) < 0) {
328 if (silent == FALSE) fprintf(stderr,"Bind error\n");
329 j = closesocket(sock);
330 return FALSE;
331 }
332
333 // work out which port we bound to
334 socklen_t namelen = sizeof(me);
335 if (getsockname(sock,(struct sockaddr *) &me,&namelen) < 0) {
336 fprintf(stderr,"Could not determine port for bound socket\n");
337 j = closesocket(sock);
338 return FALSE;
339 }
340 listenPort = ntohs(me.sin_port);
341
342
343 // Save socket so we can close it on fatal errors
344 listenSock = sock;
345
346 // Listen to incoming connections
347 if (listen(sock,SOMAXCONN) < 0) {
348 if (silent == FALSE) fprintf(stderr,"Socket error\n");
349 j = closesocket(sock);
350 return FALSE;
351 }
352
353 // fork or launch the children
354 {
355 char portbuf[20];
356
357 // prepare the args for child process
358 sprintf(portbuf,"%d",listenPort);
359
360 // The command line arguments
361 char * const argv[] = {gargv[0],(char *) "-q",(char *) "-c",portbuf,ribFile,NULL};
362
363 for(int k=0;k<numChildren;k++){
364 #ifdef _WINDOWS
365 // use _spawnvp to search PATH, incase pixie
366 // isn't on the default search path
367 intptr_t pid = _spawnvp(_P_NOWAIT,argv[0],argv);
368 if (pid <= 0) {
369 if (silent == FALSE) fprintf(stderr,"Failed to launch subprocess\n");
370 return FALSE;
371 }
372 #else
373 long pid = fork();
374 if (pid < 0) {
375 // We failed to fork
376 if (silent == FALSE) fprintf(stderr,"Failed to launch subprocess\n");
377 return FALSE;
378 } else if (pid == 0) {
379 // We are now the child server
380 #if USE_PURE_FORK
381 // We can actually skip the exec alltogether
382 // which is useful if true local sockets were used
383 rndrc(ribFile,listenPort);
384 exit(0);
385 #else
386 // use execvp to search PATH, incase pixie
387 // isn't on the default search path
388 execvp(argv[0],argv);
389 exit(0);
390 #endif
391 }
392 #endif
393 }
394 }
395
396 // pre-accept the local servers
397
398 sprintf(tmp,"locservers=");
399 tmp += strlen(tmp);
400
401 // accept the connections
402 for(i=0;i<numChildren;i++) {
403 SOCKET peer;
404 socklen_t servLen = sizeof(sockaddr_in);
405 sockaddr_in serv;
406 fd_set fds;
407 timeval timeout;
408
409 // implement a timeout for sockets (so we don't wait for ever for subprocesses)
410 FD_ZERO(&fds);
411 FD_SET(sock,&fds);
412 timeout.tv_sec = 4;
413 timeout.tv_usec = 0;
414 if ( select((int) sock+1,&fds,NULL,NULL,&timeout) <= 0) {
415 if (silent == FALSE) fprintf(stderr,"Timeout waiting for socket\n");
416 j = closesocket(sock);
417 return FALSE;
418 }
419
420 // finally if we didn't time out, accept the connection
421 peer = accept(sock,(sockaddr *) &serv,&servLen);
422
423 #ifdef SO_NOSIGPIPE
424 val = 1;
425 setsockopt(peer,SOL_SOCKET,SO_NOSIGPIPE,(const char *) &val,sizeof(int));
426 #endif
427
428 if (peer != INVALID_SOCKET) {
429 // record peer socket
430 localServerSockets[numLocalServers++] = peer;
431
432 if (i < numChildren-1) sprintf(tmp,"%d,",peer);
433 else sprintf(tmp,"%d",peer);
434 tmp += strlen(tmp);
435 } else {
436 if (silent == FALSE) fprintf(stderr,"Socket error\n");
437 j = closesocket(sock);
438 return FALSE;
439 }
440 }
441
442 // Close socket before exiting
443 j = closesocket(sock);
444
445 return TRUE;
446 }
447
448 ///////////////////////////////////////////////////////////////////////
449 // Function : rndrd
450 // Description : Run the network daemon
451 // Return Value : -
452 // Comments :
rndrd(int port)453 void rndrd(int port) {
454 SOCKET sock;
455 struct sockaddr_in me;
456 T32 buffer[BUFFERSIZE];
457 int running = TRUE;
458
459 #ifdef _WINDOWS
460 WSADATA wsaData;
461
462 // Init the winsock
463 if (WSAStartup(0x202,&wsaData) == SOCKET_ERROR) {
464 WSACleanup();
465 if (silent == FALSE) fprintf(stderr,"Winsock init error\n");
466 }
467 #else
468 // For platforms where we can't disable sigpipe per socket, do it globally
469 #ifndef SO_NOSIGPIPE
470 signal(SIGPIPE,SIG_IGN);
471 #endif
472 #endif
473
474 // Create the socket
475 sock = socket(AF_INET, SOCK_STREAM, 0);
476 if (sock == INVALID_SOCKET) {
477 if (silent == FALSE) fprintf(stderr,"Socket error\n");
478 return;
479 }
480
481 // Ensure there's no delay on network transactions
482 int val = 1;
483 setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(const char *) &val,sizeof(int));
484 val = 1;
485 setsockopt(sock,IPPROTO_TCP,TCP_NODELAY,(const char *) &val,sizeof(int));
486
487 // Bind to the port
488 me.sin_family = AF_INET;
489 me.sin_addr.s_addr = htonl(INADDR_ANY);
490 me.sin_port = htons(port);
491 if (bind(sock, (struct sockaddr *) &me, sizeof(me)) < 0) {
492 if (silent == FALSE) fprintf(stderr,"Bind error\n");
493 return;
494 }
495
496 // Display the information
497 {
498 char hostName[128];
499 hostent *hostinfo;
500
501 if(gethostname(hostName,sizeof(hostName)) == 0) {
502 if((hostinfo = gethostbyname(hostName)) != NULL) {
503 if (silent == FALSE) { printf("Active at %s:%d\n",inet_ntoa (*(struct in_addr *)*hostinfo->h_addr_list),ntohs(me.sin_port)); fflush(stdout); }
504 }
505 }
506 }
507
508 // We're running a daemon
509 isDaemon = TRUE;
510
511 // Save socket so we can close it on fatal errors
512 listenSock = sock;
513
514 // Listen to incoming connections
515 listen(sock,SOMAXCONN);
516 while(running == TRUE) {
517 SOCKET peer;
518 socklen_t servLen = sizeof(sockaddr_in);
519 sockaddr_in serv;
520
521 peer = accept(sock,(sockaddr *) &serv,&servLen);
522
523 if (peer != INVALID_SOCKET) {
524
525 // If supported / needed, disable sigpipe (needs to be an connected socket)
526 #ifdef SO_NOSIGPIPE
527 val = 1;
528 setsockopt(peer,SOL_SOCKET,SO_NOSIGPIPE,(const char *) &val,sizeof(int));
529 #endif
530
531 // Handle the message
532 recv(peer,(char *) &buffer[1].character,(BUFFERSIZE-1)*sizeof(T32),0);
533 if (strncmp((char *) &buffer[1].character," quit",5) == 0) {
534 noRestart = TRUE;
535 running = FALSE;
536 } else {
537 buffer[0].integer = (int) peer;
538
539 assert(sizeof(SOCKET) == sizeof(int));
540
541 riThread((void *) buffer);
542
543 running = TRUE;
544 }
545
546 // Close peer socket
547 closesocket(peer);
548 }
549 }
550
551 // Close socket before exiting
552 closesocket(sock);
553 }
554
555
556 ///////////////////////////////////////////////////////////////////////
557 // Function : main
558 // Description : The god
559 // Return Value : -
560 // Comments :
main(int argc,char * argv[])561 int main(int argc, char* argv[]) {
562 int i;
563 char managerString[1024];
564 char managerString2[1024];
565 int server = FALSE;
566 int client = FALSE;
567 int localserver = FALSE;
568 int killservers = FALSE;
569 char *source = NULL;
570 int port = 0;
571 int clientport = 0;
572 const char *frameRange = NULL;
573 int frameBufferOnly = FALSE;
574 int displayStats = FALSE;
575 int displayProgress = FALSE;
576 int numThreads = -1;
577 int localChildren = 0;
578
579 // Enable memory leak detection/report
580 #ifdef _WINDOWS
581 #ifdef _DEBUG
582 _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
583 #endif
584 #endif
585
586 // Register the exit stuff
587 sprintf(managerString,"");
588 gargc = argc;
589 gargv = argv;
590 isDaemon = FALSE;
591 silent = FALSE;
592 numLocalServers = 0;
593 atexit(exitFunction);
594
595 for (i=1;i<argc;i++) {
596 if (strcmp(argv[i],"-h") == 0
597 || strcmp(argv[i],"-help") == 0
598 || strcmp(argv[i],"--help") == 0) {
599 printUsage();
600 if (source) free(source);
601 exit(0);
602 } else if (strcmp(argv[i],"-v") == 0
603 || strcmp(argv[i],"-version") == 0
604 || strcmp(argv[i],"--version") == 0) {
605 printVersion();
606 if (source) free(source);
607 exit(0);
608 } else if (strcmp(argv[i],"-r") == 0) {
609 i++;
610
611 if ((i < argc) && (sscanf(argv[i],"%d",&port) == 1)) {
612 } else {
613 port = DEFAULT_DAEMON_PORT;
614 i--;
615 }
616 } else if (strcmp(argv[i],"-q") == 0) {
617 silent = TRUE;
618 } else if (strcmp(argv[i],"-c") == 0) {
619 client = TRUE;
620
621 i++;
622 if (i >= argc) {
623 if (silent == FALSE) fprintf(stderr,"Was expecting client port\n");
624 exit(1);
625 }
626
627 if(sscanf(argv[i],"%d",&clientport) != 1) {
628 if (silent == FALSE) fprintf(stderr,"Unrecognized client port\n");
629 exit(1);
630 }
631 } else if (strcmp(argv[i],"-sizereport") == 0) {
632 printf("Size Report:\n");
633 printf(" sizeof(char): %d\n",sizeof(char));
634 printf(" sizeof(short): %d\n",sizeof(short));
635 printf(" sizeof(int): %d\n",sizeof(int));
636 printf(" sizeof(long): %d\n",sizeof(long));
637 printf(" sizeof(long long): %d\n",sizeof(long long));
638 printf(" sizeof(void *): %d\n",sizeof(void *));
639 printf(" sizeof(T32): %d\n",sizeof(T32));
640 printf(" sizeof(T64): %d\n",sizeof(T64));
641 printf(" sizeof(TMutex): %d\n",sizeof(TMutex));
642 exit(0);
643 } else if (strcmp(argv[i],"-s") == 0) {
644 server = TRUE;
645
646 i++;
647 if (i >= argc) {
648 if (silent == FALSE) fprintf(stderr,"Was expecting list of servers\n");
649 exit(1);
650 }
651 sprintf(managerString,"servers=%s",argv[i]);
652 } else if (strcmp(argv[i],"-k") == 0) {
653 server = TRUE;
654 killservers = TRUE;
655
656 i++;
657 if (i >= argc) {
658 if (silent == FALSE) fprintf(stderr,"Was expecting list of servers\n");
659 exit(1);
660 }
661 sprintf(managerString,"killservers=%s",argv[i]);
662 } else if (strcmp(argv[i],"-f") == 0) {
663 i++;
664 if (i >= argc) {
665 if (silent == FALSE) fprintf(stderr,"Was expecting the frame range\n");
666 exit(1);
667 }
668
669 frameRange = argv[i];
670 } else if (strncmp(argv[i],"-P:",3) == 0) {
671 localChildren = atoi(argv[i]+3);
672 localserver = TRUE;
673
674 if (localChildren > MAX_LOCALSERVERS) {
675
676 if (silent == FALSE) fprintf(stderr,"Cannot run more than %d local processes\n",MAX_LOCALSERVERS);
677 localChildren = MAX_LOCALSERVERS;
678 }
679 } else if (strcmp(argv[i],"-d") == 0) {
680 frameBufferOnly = TRUE;
681 } else if (strncmp(argv[i],"-t:",3) == 0) {
682 numThreads = atoi(argv[i]+3);
683 } else if (strcmp(argv[i],"-t") == 0) {
684 displayStats = TRUE;
685 } else if (strcmp(argv[i],"-p") == 0) {
686 displayProgress = TRUE;
687 } else if (argv[i][0] == '-' && argv[i][1] != 0) {
688 // Starts with '-' but not matched any option
689 if (silent == FALSE)
690 fprintf(stderr,"Unknown option '%s'\n", argv[i]);
691 } else {
692 // Create colon-separated list of source RIBs
693 if (!source) {
694 source = strdup(argv[i]);
695 } else {
696 char* tmp = (char*)malloc(strlen(source)+1+strlen(argv[i])+1);
697 strcpy(tmp,source);
698 strcat(tmp,":");
699 strcat(tmp,argv[i]);
700 free(source);
701 source = tmp;
702 }
703 }
704 }
705
706 // Read from STDIN if no source given
707 if (source == NULL)
708 source = strdup("-");
709
710 // Validate option combinations
711 if ((client | server | localserver) && (client ^ server ^ localserver) == 0) {
712 fprintf(stderr,"Invalid combination of client and server options\n");
713 free(source);
714 exit(1);
715 }
716
717 // FIXME: remove once multithreaded netrenders are working
718 if ((client | server | localserver) && numThreads > 0) {
719 fprintf(stderr,"Using threads is currently not possible for network or multiprocess renders; turning off threads.\n");
720 numThreads = 0;
721 }
722
723 // Launch into daemon mode if appropriate
724 if (port != 0) {
725 if ((localChildren != 0) | server) {
726 fprintf(stderr,"Using multiple processes is not possible for network renders\n");
727 free(source);
728 exit(1);
729 }
730
731 noRestart = FALSE;
732 rndrd(port);
733 free(source);
734 exit(0);
735
736 }
737
738 // Deal with stdin renders
739 if (strcmp(source,"-") == 0) {
740 if ((client | server | localserver) && (silent == FALSE) && (killservers != TRUE)) {
741 fprintf(stderr,"Using STDIN pipe is not possible for network or multiprocess renders\n");
742 free(source);
743 exit(1);
744 }
745 }
746
747 // Launch local servers if needed
748 if (localChildren != 0) {
749 noRestart = TRUE;
750
751 // verify RIBs exists
752 char *pstart = source;
753 char *pend;
754 do {
755 pend = strchr(pstart,':');
756 if (pend) *pend = '\0';
757 if (osFileExists(pstart) == FALSE) {
758 if (silent == FALSE) fprintf(stderr,"Cannot find RIB file '%s'\n",pstart);
759 free(source);
760 exit(1);
761 }
762 if (pend) *pend = ':';
763 pstart = pend + 1;
764 } while(pend);
765
766 // run teh servers
767 if (runLocalServers(localChildren,source,managerString) == FALSE) {
768 if (silent == FALSE) fprintf(stderr,"Failed to launch subprocesses\n");
769 free(source);
770 exit(1);
771 }
772 }
773
774 // Launch as spawned local server if needed
775 if (clientport != 0) {
776 rndrc(source,clientport);
777 free(source);
778 exit(0);
779 }
780
781 // Create the command line for the ri
782 if (client | server | localserver) {
783 sprintf(managerString2,"#rib:%s net:%s",source,managerString);
784 } else {
785 sprintf(managerString2,"#");
786 }
787
788 if (frameRange != NULL) {
789 strcat(managerString2," frames:");
790 strcat(managerString2,frameRange);
791 }
792
793 if (frameBufferOnly) {
794 strcat(managerString2," fbonly:");
795 }
796
797 RiBegin(managerString2);
798
799 #ifndef _WINDOWS
800 signal(SIGHUP,printStatsHandler);
801 #ifdef SIGINFO
802 signal(SIGINFO,printStatsHandler);
803 #endif
804 #endif
805
806 if (silent == TRUE) RiErrorHandler(RiErrorIgnore);
807 if (displayStats == TRUE) {
808 RtInt level = 3;
809 RiOption(RI_STATISTICS,RI_ENDOFFRAME,&level,RI_NULL);
810 }
811
812 if (displayProgress == TRUE) {
813 RtInt progress = 1;
814 RiOption(RI_STATISTICS,RI_PROGRESS,&progress,RI_NULL);
815 }
816
817 if (numThreads > 0) {
818 RiOption(RI_LIMITS,RI_NUMTHREADS,&numThreads,RI_NULL);
819 }
820
821 if (!killservers) {
822 char *pstart = source;
823 char *pend;
824 do {
825 pend = strchr(pstart,':');
826 if (pend) *pend = '\0';
827 RiReadArchive(pstart,NULL,NULL);
828 if (pend) *pend = ':';
829 pstart = pend + 1;
830 } while(pend);
831 }
832
833 RiEnd();
834 free(source);
835
836 return (RiLastError != RIE_NOERROR) ? -1 : 0;
837 }
838
839