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