Exemple #1
0
/** Reads the configuration file and then starts the main loop */
int main(int argc, char **argv) {

	s_config *config = config_get_config();
	config_init();

	parse_commandline(argc, argv);

	/* Initialize the config */
	config_read(config->configfile);
	config_validate();

	/* Initializes the linked list of connected clients */
	client_list_init();

	/* Init the signals to catch chld/quit/etc */
	init_signals();


	if (restart_orig_pid) {
		/*
		 * We were restarted and our parent is waiting for us to talk to it over the socket
		 */
		get_clients_from_parent();

		/*
		 * At this point the parent will start destroying itself and the firewall. Let it finish it's job before we continue
		 */
		while (kill(restart_orig_pid, 0) != -1) {
			debug(LOG_INFO, "Waiting for parent PID %d to die before continuing loading", restart_orig_pid);
			sleep(1);
		}

		debug(LOG_INFO, "Parent PID %d seems to be dead. Continuing loading.");
	}

	if (config->daemon) {

		debug(LOG_INFO, "Forking into background");

		switch(safe_fork()) {
			case 0: /* child */
				setsid();
				append_x_restartargv();
				main_loop();
				break;

			default: /* parent */
				exit(0);
				break;
		}
	}
	else {
		append_x_restartargv();
		main_loop();
	}

	return(0); /* never reached */
}
Exemple #2
0
int
setup(struct config_t *config, int argc, char **argv)
{
	char *home, *cfgfile = NULL;
	
	config_init(config);
	
	/* parse arguments */
	if (parse_args(config, argc, argv) != 0)
		return SETUP_ERROR;
	
	/* if the user did not supply an alternate config file,
	 * check if the default config file exists */
	if (!config->filename) {
		if ((home = getenv("HOME"))) {
			cfgfile = malloc(strlen(home) + sizeof(CONFIG_FILE) + 2);
			if (!cfgfile) {
				warn("malloc failed for $HOME + " CONFIG_FILE);
				return SETUP_ERROR;
			}
			stpcpy(stpcpy(stpcpy(cfgfile, home), "/"), CONFIG_FILE);
			if (access(cfgfile, F_OK) == 0) {
				/* file exists - keep malloc'ed data */
				config->filename = cfgfile;
			} else {
				/* file does not exist - free data */
				errno = 0;
				free(cfgfile);
				cfgfile = NULL;
			}
		}
	}
	
	/* parse config file - if provided or default exists */
	if (config->filename && parse_conf(config, config->filename) != 0)
		return SETUP_ERROR;
	
	/* put defaults for each undefined variable */
	config_finalize(config);
	
	/* check mandatory/conflicting settings */
	if (config_validate(config) != 0)
		return SETUP_ERROR;
	
	return SETUP_OK;
}
Exemple #3
0
/** Main entry point for nodogsplash.
 * Reads the configuration file and then starts the main loop.
 */
int main(int argc, char **argv) {

  s_config *config = config_get_config();
  config_init();

  parse_commandline(argc, argv);

  /* Initialize the config */
  debug(LOG_NOTICE,"Reading and validating configuration file %s", config->configfile);
  config_read(config->configfile);
  config_validate();

  /* Initializes the linked list of connected clients */
  client_list_init();

  /* Init the signals to catch chld/quit/etc */
  debug(LOG_NOTICE,"Initializing signal handlers");
  init_signals();

  if (config->daemon) {

    debug(LOG_NOTICE, "Starting as daemon, forking to background");

    switch(safe_fork()) {
    case 0: /* child */
      setsid();
      main_loop();
      break;

    default: /* parent */
      exit(0);
      break;
    }
  }
  else {
    main_loop();
  }

  return(0); /* never reached */
}
Exemple #4
0
void vUiTask( void * pvParameters)
{
	lcd_s_reset();
//	int_lcd_timer_dec = 1;

#ifdef DEBUG_BUILD
#else
   	led_update();
	printf_P(PSTR("DG1YFE"));
	lcd_fill();
	lcd_cpos(0);
	printf_P(PSTR("MCmega"));
	lcd_fill();
	lcd_cpos(0);
	vTaskDelay(150);
	printf_P(version_str);
	lcd_fill();
	vTaskDelay(150);
//	reset_ui(UI_RESET_WARM);
#endif
	reset_ui(UI_RESET_COLD);

    for(;;)
	{
    	pll_led(0);
    	led_update();
    	menu();
    	taskYIELD();
		
		// check if reset of control head was detected
		// (certain amount of 0x7e reset messages was received)
		if(!ch_reset_detected)
		{
			lcd_s_reset();
			reset_ui(UI_RESET_COLD);
		}
		config_validate();
	}
}
Exemple #5
0
/**
 * \brief Program main function.
 * \param argc Argument count from commandline
 * \param argv Argument list
 * \return Program exit state
 */
int main(int argc, char *argv[])
{
  config *cfg  = NULL; //!< Global configuration
  FILE *input  = NULL; //!< input file (YUV)
  FILE *output = NULL; //!< output file (HEVC NAL stream)
  encoder_control encoder;
  double psnr[3] = { 0.0, 0.0, 0.0 };
  uint32_t stat_frames = 0;
  uint64_t curpos = 0;
  FILE *recout = NULL; //!< reconstructed YUV output, --debug
  clock_t start_time = clock();
  clock_t encoding_start_cpu_time;
  CLOCK_T encoding_start_real_time;
  
  clock_t encoding_end_cpu_time;
  CLOCK_T encoding_end_real_time;

  // Stdin and stdout need to be binary for input and output to work.
  // Stderr needs to be text mode to convert \n to \r\n in Windows.
  #ifdef _WIN32
      _setmode( _fileno( stdin ),  _O_BINARY );
      _setmode( _fileno( stdout ), _O_BINARY );
      _setmode( _fileno( stderr ), _O_TEXT );
  #endif
      
  CHECKPOINTS_INIT();

  // Handle configuration
  cfg = config_alloc();

  // If problem with configuration, print banner and shutdown
  if (!cfg || !config_init(cfg) || !config_read(cfg,argc,argv)) {
    fprintf(stderr,
            "/***********************************************/\n"
            " *   Kvazaar HEVC Encoder v. " VERSION_STRING "             *\n"
            " *     Tampere University of Technology 2014   *\n"
            "/***********************************************/\n\n");

    fprintf(stderr,
            "Usage:\n"
            "kvazaar -i <input> --input-res <width>x<height> -o <output>\n"
            "\n"
            "Optional parameters:\n"
            "      -n, --frames <integer>     : Number of frames to code [all]\n"
            "      --seek <integer>           : First frame to code [0]\n"
            "      --input-res <int>x<int>    : Input resolution (width x height)\n"
            "      -q, --qp <integer>         : Quantization Parameter [32]\n"
            "      -p, --period <integer>     : Period of intra pictures [0]\n"
            "                                     0: only first picture is intra\n"
            "                                     1: all pictures are intra\n"
            "                                     2-N: every Nth picture is intra\n"
            "      -r, --ref <integer>        : Reference frames, range 1..15 [3]\n"
            "          --no-deblock           : Disable deblocking filter\n"
            "          --deblock <beta:tc>    : Deblocking filter parameters\n"
            "                                   beta and tc range is -6..6 [0:0]\n"
            "          --no-sao               : Disable sample adaptive offset\n"
            "          --no-rdoq              : Disable RDO quantization\n"
            "          --rd <integer>         : Rate-Distortion Optimization level [1]\n"
            "                                     0: no RDO\n"
            "                                     1: estimated RDO\n"
            "                                     2: full RDO\n"
            "          --full-intra-search    : Try all intra modes.\n"
            "          --no-transform-skip    : Disable transform skip\n"
            "          --aud                  : Use access unit delimiters\n"
            "          --cqmfile <string>     : Custom Quantization Matrices from a file\n"
            "          --debug <string>       : Output encoders reconstruction.\n"
            "          --cpuid <integer>      : Disable runtime cpu optimizations with value 0.\n"
            "\n"
            "  Video Usability Information:\n"
            "          --sar <width:height>   : Specify Sample Aspect Ratio\n"
            "          --overscan <string>    : Specify crop overscan setting [\"undef\"]\n"
            "                                     - undef, show, crop\n"
            "          --videoformat <string> : Specify video format [\"undef\"]\n"
            "                                     - component, pal, ntsc, secam, mac, undef\n"
            "          --range <string>       : Specify color range [\"tv\"]\n"
            "                                     - tv, pc\n"
            "          --colorprim <string>   : Specify color primaries [\"undef\"]\n"
            "                                     - undef, bt709, bt470m, bt470bg,\n"
            "                                       smpte170m, smpte240m, film, bt2020\n"
            "          --transfer <string>    : Specify transfer characteristics [\"undef\"]\n"
            "                                     - undef, bt709, bt470m, bt470bg,\n"
            "                                       smpte170m, smpte240m, linear, log100,\n"
            "                                       log316, iec61966-2-4, bt1361e,\n"
            "                                       iec61966-2-1, bt2020-10, bt2020-12\n"
            "          --colormatrix <string> : Specify color matrix setting [\"undef\"]\n"
            "                                     - undef, bt709, fcc, bt470bg, smpte170m,\n"
            "                                       smpte240m, GBR, YCgCo, bt2020nc, bt2020c\n"
            "          --chromaloc <integer>  : Specify chroma sample location (0 to 5) [0]\n"
            "\n"
            "  Parallel processing :\n"
            "          --threads <integer>    : Maximum number of threads to use.\n"
            "                                   Disable threads if set to 0.\n"
            "\n"
            "  Tiles:\n"
            "          --tiles-width-split <string>|u<int> : \n"
            "                                   Specifies a comma separated list of pixel\n"
            "                                   positions of tiles columns separation coordinates.\n"
            "                                   Can also be u followed by and a single int n,\n"
            "                                   in which case it produces columns of uniform width.\n"
            "          --tiles-height-split <string>|u<int> : \n"
            "                                   Specifies a comma separated list of pixel\n"
            "                                   positions of tiles rows separation coordinates.\n"
            "                                   Can also be u followed by and a single int n,\n"
            "                                   in which case it produces rows of uniform height.\n"
            "\n"
            "  Wpp:\n"
            "          --wpp                  : Enable wavefront parallel processing\n"
            "          --owf <integer>        : Enable parallel processing of multiple frames\n"
            "\n"
            "  Slices:\n"
            "          --slice-addresses <string>|u<int>: \n"
            "                                   Specifies a comma separated list of LCU\n"
            "                                   positions in tile scan order of tile separations.\n"
            "                                   Can also be u followed by and a single int n,\n"
            "                                   in which case it produces uniform slice length.\n"
            "\n"
            "  Deprecated parameters: (might be removed at some point)\n"
            "     Use --input-res:\n"
            "       -w, --width               : Width of input in pixels\n"
            "       -h, --height              : Height of input in pixels\n");

    goto exit_failure;
  }

  // Add dimensions to the reconstructions file name.
  if (cfg->debug != NULL) {
    char dim_str[50]; // log10(2^64) < 20, so this should suffice. I hate C.
    size_t left_len, right_len;
    sprintf(dim_str, "_%dx%d.yuv", cfg->width, cfg->height);
    left_len = strlen(cfg->debug);
    right_len = strlen(dim_str);
    cfg->debug = realloc(cfg->debug, left_len + right_len + 1);
    if (!cfg->debug) {
      fprintf(stderr, "realloc failed!\n");
      goto exit_failure;
    }
    strcpy(cfg->debug + left_len, dim_str);
  }

  // Do more validation to make sure the parameters we have make sense.
  if (!config_validate(cfg)) {
    goto exit_failure;
  }

  //Initialize strategies
  if (!strategyselector_init(cfg->cpuid)) {
    fprintf(stderr, "Failed to initialize strategies.\n");
    goto exit_failure;
  }

  // Check if the input file name is a dash, this means stdin
  if (!strcmp(cfg->input, "-")) {
    input = stdin;
  } else {
    // Otherwise we try to open the input file
    input = fopen(cfg->input, "rb");
  }

  // Check that input was opened correctly
  if (input == NULL) {
    fprintf(stderr, "Could not open input file, shutting down!\n");
    goto exit_failure;
  }

  // Open output file and check that it was opened correctly
  output = fopen(cfg->output, "wb");
  if (output == NULL) {
    fprintf(stderr, "Could not open output file, shutting down!\n");
    goto exit_failure;
  }

  if (cfg->debug != NULL) {
    recout = fopen(cfg->debug, "wb");
    if (recout == NULL) {
      fprintf(stderr, "Could not open reconstruction file (%s), shutting down!\n", cfg->debug);
      goto exit_failure;
    }
  }
  
  //Allocate and init exp golomb table
  if (!init_exp_golomb(4096*8)) {
    fprintf(stderr, "Failed to allocate the exp golomb code table, shutting down!\n");
    goto exit_failure;
  }

  if (!encoder_control_init(&encoder, cfg)) {
    goto exit_failure;
  }
  
  // Set output file
  encoder.out.file = output;
  
  // input init (TODO: read from commandline / config)
  encoder.bitdepth = 8;
  encoder.in.video_format = FORMAT_420;
  
  // deblocking filter
  encoder.deblock_enable   = (int8_t)encoder.cfg->deblock_enable;
  encoder.beta_offset_div2 = (int8_t)encoder.cfg->deblock_beta;
  encoder.tc_offset_div2   = (int8_t)encoder.cfg->deblock_tc;
  // SAO
  encoder.sao_enable = (int8_t)encoder.cfg->sao_enable;
  // RDO
  encoder.rdoq_enable = (int8_t)encoder.cfg->rdoq_enable;
  encoder.rdo         = (int8_t)encoder.cfg->rdo;
  encoder.full_intra_search = (int8_t)encoder.cfg->full_intra_search;
  // TR SKIP
  encoder.trskip_enable = (int8_t)encoder.cfg->trskip_enable;
  encoder.tr_depth_intra = (int8_t)encoder.cfg->tr_depth_intra;
  // VUI
  encoder.vui.sar_width   = (int16_t)encoder.cfg->vui.sar_width;
  encoder.vui.sar_height  = (int16_t)encoder.cfg->vui.sar_height;
  encoder.vui.overscan    = encoder.cfg->vui.overscan;
  encoder.vui.videoformat = encoder.cfg->vui.videoformat;
  encoder.vui.fullrange   = encoder.cfg->vui.fullrange;
  encoder.vui.colorprim   = encoder.cfg->vui.colorprim;
  encoder.vui.transfer    = encoder.cfg->vui.transfer;
  encoder.vui.colormatrix = encoder.cfg->vui.colormatrix;
  encoder.vui.chroma_loc  = (int8_t)encoder.cfg->vui.chroma_loc;
  // AUD
  encoder.aud_enable = (int8_t)encoder.cfg->aud_enable;

  encoder.in.file = input;

  fprintf(stderr, "Input: %s, output: %s\n", cfg->input, cfg->output);
  fprintf(stderr, "  Video size: %dx%d (input=%dx%d)\n",
         encoder.in.width, encoder.in.height,
         encoder.in.real_width, encoder.in.real_height);
  
  //Now, do the real stuff
  {
    encoder_state *encoder_states = malloc((encoder.owf + 1) * sizeof(encoder_state));
    if (encoder_states == NULL) {
      fprintf(stderr, "Failed to allocate memory.");
      goto exit_failure;
    }

    int i;
    int current_encoder_state = 0;
    for (i = 0; i <= encoder.owf; ++i) {
      encoder_states[i].encoder_control = &encoder;
      if (i > 0) {
        encoder_states[i].previous_encoder_state = &encoder_states[i-1];
      } else { 
        //i == 0, use last encoder as the previous one
        encoder_states[i].previous_encoder_state = &encoder_states[encoder.owf];
      }
      if (!encoder_state_init(&encoder_states[i], NULL)) {
        goto exit_failure;
      }
      encoder_states[i].global->QP = (int8_t)encoder.cfg->qp;
    }
    
    for (i = 0; i <= encoder.owf; ++i) {
      encoder_state_match_children_of_previous_frame(&encoder_states[i]);
    }
    
    //Initial frame
    encoder_states[current_encoder_state].global->frame    = -1;

    // Only the code that handles conformance window coding needs to know
    // the real dimensions. As a quick fix for broken non-multiple of 8 videos,
    // change the input values here to be the real values. For a real fix
    // encoder.in probably needs to be merged into cfg.
    // The real fix would be: never go dig in cfg
    //cfg->width = encoder.in.width;
    //cfg->height = encoder.in.height;
    
    GET_TIME(&encoding_start_real_time);
    encoding_start_cpu_time = clock();

    // Start coding cycle while data on input and not on the last frame
    while(!cfg->frames || encoder_states[current_encoder_state].global->frame < cfg->frames - 1) {
      // Skip '--seek' frames before input.
      // This block can be moved outside this while loop when there is a
      // mechanism to skip the while loop on error.
      if (encoder_states[current_encoder_state].global->frame == 0 && cfg->seek > 0) {
        int frame_bytes = cfg->width * cfg->height * 3 / 2;
        int error = 0;

        if (!strcmp(cfg->input, "-")) {
          // Input is stdin.
          int i;
          for (i = 0; !error && i < cfg->seek; ++i) {
            error = !read_one_frame(input, &encoder_states[current_encoder_state]);
          }
        } else {
          // input is a file. We hope. Proper detection is OS dependent.
          error = fseek(input, cfg->seek * frame_bytes, SEEK_CUR);
        }
        if (error && !feof(input)) {
          fprintf(stderr, "Failed to seek %d frames.\n", cfg->seek);
          break;
        }
        GET_TIME(&encoding_start_real_time);
        encoding_start_cpu_time = clock();
      }
      
      //Compute stats
      encoder_compute_stats(&encoder_states[current_encoder_state], recout, &stat_frames, psnr);
      
      //Clear encoder
      encoder_next_frame(&encoder_states[current_encoder_state]);
      
      //Abort if enough frames
      if (cfg->frames && encoder_states[current_encoder_state].global->frame >= cfg->frames) {
        //Ignore this frame, which is not valid...
        encoder_states[current_encoder_state].stats_done = 1;
        break;
      }
      
      CHECKPOINT_MARK("read source frame: %d", encoder_states[current_encoder_state].global->frame + cfg->seek);

      // Read one frame from the input
      if (!read_one_frame(input, &encoder_states[current_encoder_state])) {
        if (!feof(input))
          fprintf(stderr, "Failed to read a frame %d\n", encoder_states[current_encoder_state].global->frame);
        
        //Ignore this frame, which is not valid...
        encoder_states[current_encoder_state].stats_done = 1;
        break;
      }

      // The actual coding happens here, after this function we have a coded frame
      encode_one_frame(&encoder_states[current_encoder_state]);
      
      //Switch to the next encoder
      current_encoder_state = (current_encoder_state + 1) % (encoder.owf + 1);
    }
    
    //Compute stats for the remaining encoders
    {
      int first_enc = current_encoder_state;
      do {
        current_encoder_state = (current_encoder_state + 1) % (encoder.owf + 1);
        encoder_compute_stats(&encoder_states[current_encoder_state], recout, &stat_frames, psnr);
      } while (current_encoder_state != first_enc);
    }
    
    GET_TIME(&encoding_end_real_time);
    encoding_end_cpu_time = clock();
    
    threadqueue_flush(encoder.threadqueue);
    
    
    // Coding finished
    fgetpos(output,(fpos_t*)&curpos);

    // Print statistics of the coding
    fprintf(stderr, " Processed %d frames, %10llu bits AVG PSNR: %2.4f %2.4f %2.4f\n", stat_frames, (long long unsigned int) curpos<<3,
          psnr[0] / stat_frames, psnr[1] / stat_frames, psnr[2] / stat_frames);
    fprintf(stderr, " Total CPU time: %.3f s.\n", ((float)(clock() - start_time)) / CLOCKS_PER_SEC);
    
    {
      double encoding_time = ((double)(encoding_end_cpu_time - encoding_start_cpu_time)) / CLOCKS_PER_SEC;
      double wall_time = CLOCK_T_AS_DOUBLE(encoding_end_real_time) - CLOCK_T_AS_DOUBLE(encoding_start_real_time);
      fprintf(stderr, " Encoding time: %.3lf s.\n", encoding_time);
      fprintf(stderr, " Encoding wall time: %.3lf s.\n", wall_time);
      fprintf(stderr, " Encoding CPU usage: %.2lf%%\n", encoding_time/wall_time*100.f);
      fprintf(stderr, " FPS: %.2lf\n", ((double)stat_frames)/wall_time);
    }

    fclose(input);
    fclose(output);
    if(recout != NULL) fclose(recout);
    
    for (i = 0; i <= encoder.owf; ++i) {
      encoder_state_finalize(&encoder_states[i]);
    }

    free(encoder_states);
  }

  // Deallocating
  config_destroy(cfg);
  encoder_control_finalize(&encoder);

  free_exp_golomb();
  
  strategyselector_free();
  
  CHECKPOINTS_FINALIZE();

  return EXIT_SUCCESS;

exit_failure:
  if (cfg) config_destroy(cfg);
  if (input) fclose(input);
  if (output) fclose(output);
  if (recout) fclose(recout);
  strategyselector_free();
  CHECKPOINTS_FINALIZE();
  return EXIT_FAILURE;
}
Exemple #6
0
/*************************************************************
** MAIN
**	Reads the config file. Binds to a port. Launches the
**	worker and other threads. Listens for requests.
*/
int 
main(int argc, char **argv)
{
	char *		conf	= NULL;
	int	 	nthreads;
	int	 	c, ret;
	char**	 	ary;
	char*	 	prog;
	u_int		numfds		= 0;
	int	 	xerror;
	int	 	bg_flag	= TRUE;
	int	 	only_check_config;
	char		ebuf[BUFSIZ];
	char		nbuf[64];
	u_char *	gifimage	= NULL;
	u_char *	favicon		= NULL;
	int 		gifimagelen	= 0;
	int 		faviconlen	= 0;
	TPOOL_CTX *	tpool_ctx	= NULL;
	DEBUGS *	dp;
	LISTEN_CTX	*gctx		= NULL;
	LISTEN_CTX	*dctx		= NULL;
	LISTEN_CTX	*jctx		= NULL;
	pthread_t	gif_tid;
	pthread_t	data_tid;
	pthread_t	upload_tid;
#if HAVE_RESOURCE_H || HAVE_SYS_RESOURCE_H
	struct rlimit	rl;
#endif
	time_t		servertimeout;
	 
	prog = basename(argv[0]);
	if (prog == NULL)
		prog = argv[0];

	only_check_config = FALSE;
	servertimeout = 20;

	while ((c = getopt(argc, argv, "C:d:c:f")) != -1)
	{
		switch (c)
		{
			case 'f':
				bg_flag = FALSE;
				break;
			case 'C':
				only_check_config = TRUE;
				/*FALLTHROUGH*/
			case 'c':
				conf = optarg;
				break;
			case 'd':
				/*
				 * Debugging turned of if in background 
				 */
				if (bg_flag == TRUE)
					break;
				for (dp = DebugNames; dp->name != NULL; dp++)
				{
					if (strncasecmp(dp->name, optarg, strlen(optarg)) == 0)
					{
						setdebug(dp->flag);
						if (dp->flag == BUG_THREADS
							    || dp->flag == BUG_ALL)
							tpool_debug_set(TRUE);
						break;
					}
				}
				if (dp->name != NULL)
					break;
				printf("Unknown debug flag: %s, select from:\n", optarg);
				for (dp = DebugNames; dp->name != NULL; dp++)
					printf("\t%s\n", dp->name);
				return 0;
			case '?':
			default:
			usage:
				printf("Usage: %s "
					"[-c /path/onepixd.conf "
					"or "
					"-C /path/onepixd.conf] "
					"-d what "
					"[-f]\n",
					prog);
				return 0;
		}
	}
	if (argc != optind)
		goto usage;

	if (conf == NULL || strlen(conf) == 0)
	{
		(void) fprintf(stderr,
			"Required Parameter \"-c configfile\" missing.\n");
		goto usage;
	}
	ret = config_read_file(conf);
	if (ret != 0)
		return 0;

	/*
	 * All errors are logged, so we need to set up
	 * logging before checking the config file.
	 */
	ary = config_lookup(CONF_LOG_FACILITY);
	if (ary == NULL)
		ret = log_init(DEFAULT_LOG_FACILITY, prog);
	else
		ret = log_init(ary[0], prog);
	if (ret != 0)
		return 0;

	ret = config_validate();
	if (ret != 0)
		return 0;
	if (ret == 0 && only_check_config == TRUE)
		(void) printf("%s: FILE IS OKAY TO USE\n", conf);
	if (only_check_config == TRUE)
		return 0;


	ary = config_lookup(CONF_HOME_DIR);
	if (ary != NULL)
	{
		if (chdir(ary[0]) != 0)
		{
			xerror = errno;
			(void) snprintf(ebuf, sizeof ebuf,
				"Attempt to chdir(\"%s\"): %s",
				ary[0], strerror(xerror));
			log_emit(LOG_ERR, NULL, __FILE__, __LINE__, ebuf);
			goto shutdown_server;
		}
	}

	if (util_bg(bg_flag) != 0)
	{
		(void) snprintf(ebuf, sizeof ebuf, "fork(): %s\n", strerror(errno));
		log_emit(LOG_ERR, NULL, __FILE__, __LINE__, ebuf);
		goto shutdown_server;
	}


#if HAVE_RESOURCE_H || HAVE_SYS_RESOURCE_H
#ifdef RLIMIT_AS
	rl.rlim_cur = RLIM_INFINITY;
	rl.rlim_max = RLIM_INFINITY;
	(void) setrlimit(RLIMIT_AS, &rl); 	/* max size virtual memory */
#endif
#ifdef RLIMIT_CPU
	rl.rlim_cur = RLIM_INFINITY;
	rl.rlim_max = RLIM_INFINITY;
	(void) setrlimit(RLIMIT_CPU, &rl);	/* unlimited CPU usage */
#endif
#ifdef RLIMIT_DATA
	rl.rlim_cur = RLIM_INFINITY;
	rl.rlim_max = RLIM_INFINITY;
	(void) setrlimit(RLIMIT_DATA, &rl);	/* unlimited memory */
#endif
#ifdef RLIMIT_FSIZE
	rl.rlim_cur = RLIM_INFINITY;
	rl.rlim_max = RLIM_INFINITY;
	(void) setrlimit(RLIMIT_FSIZE, &rl);	/* unlimited file sizes */
#endif
#ifdef RLIMIT_STACK
	rl.rlim_cur = RLIM_INFINITY;
	rl.rlim_max = RLIM_INFINITY;
	(void) setrlimit(RLIMIT_STACK, &rl);	/* unlimited stack */
#endif
#ifdef RLIMIT_CORE
	rl.rlim_cur = RLIM_INFINITY;
	rl.rlim_max = RLIM_INFINITY;
	(void) setrlimit(RLIMIT_CORE, &rl);	/* allow core dumps */
#endif
#ifdef RLIMIT_NOFILE
	rl.rlim_cur = RLIM_INFINITY;
	rl.rlim_max = RLIM_INFINITY;
	(void) setrlimit(RLIMIT_NOFILE, &rl);	/* maximum file descriptors */
	(void) getrlimit(RLIMIT_NOFILE, &rl);
	numfds = (rl.rlim_cur);

#endif
#endif /* HAVE_RESOURCE_H */

	ary = config_lookup(CONF_BECOME_USER);
	if (ary != NULL)
	{
		/*
		 * Note that setrunasuser() logs its own errors 
		 */
		if (setrunasuser(ary[0]) != 0)
			goto shutdown_server;
	}

	ary = config_lookup(CONF_NUMTHREADS);
	if (ary == NULL)
		nthreads = DEFAULT_THREADS;
	else
	{
		nthreads = strtoul(ary[0], NULL, 10);
		if (nthreads <= 0)
		{
			(void) fprintf(stderr,
				"Configuration item \"%s\" illegal value: %d.\n", 
					CONF_NUMTHREADS, nthreads);
			return 0;
		}
	}
	if (numfds == 0 || numfds > (nthreads * 3))
		numfds = nthreads * 3;
	(void) io_init(numfds);

	gifimage = gif_get1x1gif(&gifimagelen);
	if (gifimage == NULL)
	{
		(void) snprintf(ebuf, sizeof ebuf, "Load gif image: %s\n", strerror(errno));
		log_emit(LOG_ERR, NULL, __FILE__, __LINE__, ebuf);
		goto shutdown_server;
	}
	favicon  = gif_getfavicon_ico(&faviconlen);

	/*
	 * Prepare to launch the thread to listen for inbound
	 * gif requests.
	 */
	gctx = str_alloc(sizeof(LISTEN_CTX), 1, __FILE__, __LINE__);
	if (gctx == NULL)
	{
		(void) snprintf(ebuf, sizeof ebuf, "alloc(): %s\n", strerror(errno));
		log_emit(LOG_ERR, NULL, __FILE__, __LINE__, ebuf);
		goto shutdown_server;
	}
	gctx->type = LISTEN_TYPE_GIF;
	/* gif image only, no favicon */
	gctx->gifimage = gifimage;
	gctx->gifimagelen = gifimagelen;
	gctx->favicon  = NULL;
	gctx->faviconlen = 0;
	ary = config_lookup(CONF_GIF_PORT);
	if (ary == NULL)
		(void) strlcpy(gctx->port, "80", sizeof gctx->port);
	else
		(void) strlcpy(gctx->port, ary[0], sizeof gctx->port);

	ary = config_lookup(CONF_GIF_INTERFACE);
	if (ary == NULL)
		(void) strlcpy(gctx->interface, "INADDR_ANY", sizeof gctx->interface);
	else
		(void) strlcpy(gctx->interface, ary[0], sizeof gctx->interface);

	/*
	 * Everything from here down must be thread safe.
	 */

	tpool_ctx = tpool_init(nthreads, ebuf, sizeof ebuf);
	if (tpool_ctx == NULL)
		goto shutdown_server;
	gctx->tpool_ctx = tpool_ctx;


	/*
	 * The GIF server may have to bind to a privaliged port, such
	 * as port 80, so we launch it and expect it to change the
	 * user id after its bind.
	 */
	ret = pthread_create(&gif_tid, NULL, thread_listener, (void *)gctx);
	if (ret != 0)
	{
	}

	/*
	 * Prepare to launch the thread to listen for inbound
	 * data requests.
	 */
	dctx = str_alloc(sizeof(LISTEN_CTX), 1, __FILE__, __LINE__);
	if (dctx == NULL)
	{
		(void) snprintf(ebuf, sizeof ebuf, "alloc(): %s\n", strerror(errno));
		log_emit(LOG_ERR, NULL, __FILE__, __LINE__, ebuf);
		goto shutdown_server;
	}
	dctx->type = LISTEN_TYPE_DATA;
	/* a favicon only, no gif */
	dctx->gifimage = NULL;
	dctx->favicon  = favicon;
	dctx->gifimagelen = 0;
	dctx->faviconlen  = faviconlen;
	ary = config_lookup(CONF_DATA_PORT);
	if (ary == NULL)
		(void) strlcpy(dctx->port, "8100", sizeof dctx->port);
	else
		(void) strlcpy(dctx->port, ary[0], sizeof dctx->port);

	ary = config_lookup(CONF_DATA_INTERFACE);
	if (ary == NULL)
		(void) strlcpy(dctx->interface, "INADDR_ANY", sizeof dctx->interface);
	else
		(void) strlcpy(dctx->interface, ary[0], sizeof dctx->interface);

	dctx->tpool_ctx = tpool_ctx;

	ret = pthread_create(&data_tid, NULL, thread_listener, (void *)dctx);
	if (ret != 0)
	{
	}

	/*
	 * Prepare to launch the thread to listen for uploads of more.
	 */
	(void) verify_threads_locking_init();
	jctx = str_alloc(sizeof(LISTEN_CTX), 1, __FILE__, __LINE__);
	if (jctx == NULL)
	{
		(void) snprintf(ebuf, sizeof ebuf, "alloc(): %s\n", strerror(errno));
		log_emit(LOG_ERR, NULL, __FILE__, __LINE__, ebuf);
		goto shutdown_server;
	}
	jctx->type = LISTEN_TYPE_UPLOAD;
	/* a favicon only, no gif */
	jctx->gifimage = NULL;
	jctx->favicon  = favicon;
	jctx->gifimagelen = 0;
	jctx->faviconlen  = faviconlen;
	ary = config_lookup(CONF_UPLOAD_PORT);
	if (ary == NULL)
		(void) strlcpy(jctx->port, "8101", sizeof jctx->port);
	else
		(void) strlcpy(jctx->port, ary[0], sizeof jctx->port);

	ary = config_lookup(CONF_UPLOAD_INTERFACE);
	if (ary == NULL)
		(void) strlcpy(jctx->interface, "INADDR_ANY", sizeof jctx->interface);
	else
		(void) strlcpy(jctx->interface, ary[0], sizeof jctx->interface);

	jctx->tpool_ctx = tpool_ctx;

	ret = pthread_create(&upload_tid, NULL, thread_listener, (void *)jctx);
	if (ret != 0)
	{
	}

	ary = config_lookup(CONF_PIDFILE);
	if (ary != NULL)
	{
		/*
		 * Note that write_pid_file() logs its own errors 
		 */
		if (write_pid_file(ary[0]) != 0)
			goto shutdown_server;
	}

	(void) strlcpy(ebuf, "Startup: version=", sizeof ebuf);
	(void) strlcat(ebuf, VERSION, sizeof ebuf);
	(void) strlcat(ebuf, ", gif_interface=", sizeof ebuf);
	(void) strlcat(ebuf, gctx->interface, sizeof ebuf);
	(void) strlcat(ebuf, ", gif_port=", sizeof ebuf);
	(void) strlcat(ebuf, gctx->port, sizeof ebuf);
	(void) strlcat(ebuf, ", data_interface=", sizeof ebuf);
	(void) strlcat(ebuf, dctx->interface, sizeof ebuf);
	(void) strlcat(ebuf, ", data_port=", sizeof ebuf);
	(void) strlcat(ebuf, dctx->port, sizeof ebuf);
	(void) strlcat(ebuf, ", threads=", sizeof ebuf);
	(void) str_ultoa(nthreads, nbuf, sizeof nbuf);
	(void) strlcat(ebuf, nbuf, sizeof ebuf);
	log_emit(LOG_INFO, NULL, __FILE__, __LINE__, ebuf);


	Global_Die = FALSE;

	(void) signal(SIGINT,  catch_sig);
	(void) signal(SIGQUIT, catch_sig);
	(void) signal(SIGKILL, catch_sig);
	(void) signal(SIGTERM, catch_sig);
	(void) signal(SIGPIPE, SIG_IGN);

	if (Global_Die == TRUE)
		goto shutdown_server;
	(void) file_prune_garbage(&Global_Die);


shutdown_server:
	Global_Die = TRUE;

	(void) pthread_join(data_tid, NULL);
	(void) pthread_join(gif_tid, NULL);
	(void) pthread_join(upload_tid, NULL);

	(void) verify_threads_locking_shutdown();

	if (tpool_ctx != NULL)
		tpool_ctx = tpool_shutdown(tpool_ctx, ebuf, sizeof ebuf);

	if (gctx != NULL)
		gctx = str_free(gctx, __FILE__, __LINE__);
	if (dctx != NULL)
		dctx = str_free(dctx, __FILE__, __LINE__);
	if (jctx != NULL)
		jctx = str_free(jctx, __FILE__, __LINE__);
	if (gifimage != NULL)
		gifimage = str_free(gifimage, __FILE__, __LINE__);
	if (favicon != NULL)
		favicon = str_free(favicon, __FILE__, __LINE__);

	ary = config_lookup(CONF_PIDFILE);
	if (ary != NULL)
		(void) unlink(ary[0]);

	io_shutdown();
	config_shutdown();

	(void) strlcpy(ebuf, "Shutdown: version=", sizeof ebuf);
	(void) strlcat(ebuf, VERSION, sizeof ebuf);
	log_emit(LOG_INFO, NULL, __FILE__, __LINE__, ebuf);
	/*
	 * This str_shutdown must always be last.
	 */
	str_shutdown();

	return 0;
}