コード例 #1
0
ファイル: att.c プロジェクト: Drakey83/steamlink-sdk
static void handle_rsp(struct bt_att *att, uint8_t opcode, uint8_t *pdu,
								ssize_t pdu_len)
{
	struct att_send_op *op = att->pending_req;
	uint8_t req_opcode;
	uint8_t rsp_opcode;
	uint8_t *rsp_pdu = NULL;
	uint16_t rsp_pdu_len = 0;

	/*
	 * If no request is pending, then the response is unexpected. Disconnect
	 * the bearer.
	 */
	if (!op) {
		util_debug(att->debug_callback, att->debug_data,
					"Received unexpected ATT response");
		io_shutdown(att->io);
		return;
	}

	/*
	 * If the received response doesn't match the pending request, or if
	 * the request is malformed, end the current request with failure.
	 */
	if (opcode == BT_ATT_OP_ERROR_RSP) {
		/* Return if error response cause a retry */
		if (handle_error_rsp(att, pdu, pdu_len, &req_opcode)) {
			wakeup_writer(att);
			return;
		}
	} else if (!(req_opcode = get_req_opcode(opcode)))
		goto fail;

	if (req_opcode != op->opcode)
		goto fail;

	rsp_opcode = opcode;

	if (pdu_len > 0) {
		rsp_pdu = pdu;
		rsp_pdu_len = pdu_len;
	}

	goto done;

fail:
	util_debug(att->debug_callback, att->debug_data,
			"Failed to handle response PDU; opcode: 0x%02x", opcode);

	rsp_opcode = BT_ATT_OP_ERROR_RSP;

done:
	if (op->callback)
		op->callback(rsp_opcode, rsp_pdu, rsp_pdu_len, op->user_data);

	destroy_att_send_op(op);
	att->pending_req = NULL;

	wakeup_writer(att);
}
コード例 #2
0
ファイル: hfp.c プロジェクト: AlanApter/steamlink-sdk
bool hfp_gw_disconnect(struct hfp_gw *hfp)
{
	if (!hfp)
		return false;

	return io_shutdown(hfp->io);
}
コード例 #3
0
ファイル: att.c プロジェクト: Drakey83/steamlink-sdk
static bool timeout_cb(void *user_data)
{
	struct timeout_data *timeout = user_data;
	struct bt_att *att = timeout->att;
	struct att_send_op *op = NULL;

	if (att->pending_req && att->pending_req->id == timeout->id) {
		op = att->pending_req;
		att->pending_req = NULL;
	} else if (att->pending_ind && att->pending_ind->id == timeout->id) {
		op = att->pending_ind;
		att->pending_ind = NULL;
	}

	if (!op)
		return false;

	util_debug(att->debug_callback, att->debug_data,
				"Operation timed out: 0x%02x", op->opcode);

	if (att->timeout_callback)
		att->timeout_callback(op->id, op->opcode, att->timeout_data);

	op->timeout_id = 0;
	destroy_att_send_op(op);

	/*
	 * Directly terminate the connection as required by the ATT protocol.
	 * This should trigger an io disconnect event which will clean up the
	 * io and notify the upper layer.
	 */
	io_shutdown(att->io);

	return false;
}
コード例 #4
0
ファイル: sploit.c プロジェクト: darcyg/chaosircd
void sploit_shutdown(void)
{
  ssl_shutdown();
  net_shutdown();
  str_shutdown();
  io_shutdown();
  dlink_shutdown();
  queue_shutdown();
  log_shutdown();
  timer_shutdown();
  mem_shutdown();
  
  syscall_exit(1);
}
コード例 #5
0
ファイル: ttftest.c プロジェクト: rsenn/tichu
int main(int argc, char **argv)
{
#ifdef HAVE_FT2
  const char *text = "Test text!";
  const char *font = "arial.ttf";
  const char *file = "ttftest.gif";
  const char *bg = "heading-yellow.gif";
  const char *color = "#000000";
  
  log_init(STDOUT_FILENO, LOG_ALL, L_warning);
  io_init_except(STDOUT_FILENO, STDOUT_FILENO, STDOUT_FILENO);
  mem_init();
  dlink_init();
  gif_init();
  image_init();
  ttf_init();
  
  ttftest_log = log_source_register("ttftest");
  
  log_level(LOG_ALL, L_verbose);
  
  if(argc > 1)
    text = argv[1];
  if(argc > 2)
    font = argv[2];
  if(argc > 3)
    file = argv[3];
  if(argc > 4)
    bg = argv[4];
  if(argc > 5)
    color = argv[5];

  ttftest_write(text, font, file, bg, color);

  log_level(LOG_ALL, L_warning);
  
  log_source_unregister(ttftest_log);
  
  ttf_shutdown();
  image_shutdown();
  gif_shutdown();
  dlink_shutdown();
  mem_shutdown();
  log_shutdown();
  io_shutdown();
#endif  
  return 0;
}
コード例 #6
0
ファイル: servauth.c プロジェクト: darcyg/chaosircd
/* -------------------------------------------------------------------------- *
 * Clean things up.                                                           *
 * -------------------------------------------------------------------------- */
void servauth_shutdown(void)
{
  log(servauth_log, L_status, "Shutting down servauth...");

  syscall_exit(0);

  connect_shutdown();
  io_shutdown();
  queue_shutdown();
  dlink_shutdown();
  mem_shutdown();
  log_shutdown();
  timer_shutdown();

  log_source_unregister(servauth_log);
}
コード例 #7
0
ファイル: giftest.c プロジェクト: darcyg/chaosircd
int main()
{
  printf("log_init\n");

  log_init(STDOUT_FILENO, LOG_ALL, L_status);
  io_init_except(STDOUT_FILENO, STDOUT_FILENO, STDOUT_FILENO);
  mem_init();
  dlink_init();
  gif_init();

  giftest_write();

  gif_shutdown();
  dlink_shutdown();
  mem_shutdown();
  log_shutdown();
  io_shutdown();

  return 0;
}
コード例 #8
0
ファイル: att.c プロジェクト: Drakey83/steamlink-sdk
static void handle_conf(struct bt_att *att, uint8_t *pdu, ssize_t pdu_len)
{
	struct att_send_op *op = att->pending_ind;

	/*
	 * Disconnect the bearer if the confirmation is unexpected or the PDU is
	 * invalid.
	 */
	if (!op || pdu_len) {
		util_debug(att->debug_callback, att->debug_data,
				"Received unexpected/invalid ATT confirmation");
		io_shutdown(att->io);
		return;
	}

	if (op->callback)
		op->callback(BT_ATT_OP_HANDLE_VAL_CONF, NULL, 0, op->user_data);

	destroy_att_send_op(op);
	att->pending_ind = NULL;

	wakeup_writer(att);
}
コード例 #9
0
ファイル: graphtest.c プロジェクト: rsenn/tichu
int main()
{
  log_init(STDOUT_FILENO, LOG_ALL, L_status);
  io_init_except(STDOUT_FILENO, STDOUT_FILENO, STDOUT_FILENO);
  mem_init();
  dlink_init();
  gif_init();
  image_init();
  timer_init();
  graph_init();
  
  graphtest_write();

  graph_shutdown();
  timer_shutdown();
  image_shutdown();
  gif_shutdown();
  dlink_shutdown();
  mem_shutdown();
  log_shutdown();
  io_shutdown();
  
  return 0;
}
コード例 #10
0
ファイル: grilio.c プロジェクト: saukko/ofono
void g_ril_io_unref(GRilIO *io)
{
	gboolean is_zero;

	if (io == NULL)
		return;

	is_zero = g_atomic_int_dec_and_test(&io->ref_count);

	if (is_zero == FALSE)
		return;

	io_shutdown(io);

	/* glib delays the destruction of the watcher until it exits, this
	 * means we can't free the data just yet, even though we've been
	 * destroyed already.  We have to wait until the read_watcher
	 * destroy function gets called
	 */
	if (io->read_watch > 0)
		io->destroyed = TRUE;
	else
		g_free(io);
}
コード例 #11
0
ファイル: shell.c プロジェクト: powertang/yard-ice
int cmd_xflash(FILE * f, int argc, char ** argv)
{
	uint32_t offs = 0x00000;
	uint32_t size = 0x00000;

	if (argc < 2)
		return SHELL_ERR_ARG_MISSING;

	if (argc > 2)
		return SHELL_ERR_EXTRA_ARGS;

	do {
		if ( (strcmp(argv[1], "firm") == 0) ||
			 (strcmp(argv[1], "f") == 0)) {
			offs = 0;
			size = 56 * 1024;
			break;;
		} 
		return SHELL_ERR_ARG_INVALID;
	} while (0);

	fprintf(f, "Shutdown...\n");
	fflush(f);

	slcdev_sim_stop();
	slcdev_sleep();
	isink_sleep();
	io_shutdown();

	fprintf(f, "Firmware update %d bytes...\n", size);
	fflush(f);

	uart_xflash(STM32_USART2, offs, size);

	return 0;
}
コード例 #12
0
ファイル: att.c プロジェクト: Drakey83/steamlink-sdk
static bool can_read_data(struct io *io, void *user_data)
{
	struct bt_att *att = user_data;
	uint8_t opcode;
	uint8_t *pdu;
	ssize_t bytes_read;

	bytes_read = read(att->fd, att->buf, att->mtu);
	if (bytes_read < 0)
		return false;

	util_hexdump('>', att->buf, bytes_read,
					att->debug_callback, att->debug_data);

	if (bytes_read < ATT_MIN_PDU_LEN)
		return true;

	pdu = att->buf;
	opcode = pdu[0];

	bt_att_ref(att);

	/* Act on the received PDU based on the opcode type */
	switch (get_op_type(opcode)) {
	case ATT_OP_TYPE_RSP:
		util_debug(att->debug_callback, att->debug_data,
				"ATT response received: 0x%02x", opcode);
		handle_rsp(att, opcode, pdu + 1, bytes_read - 1);
		break;
	case ATT_OP_TYPE_CONF:
		util_debug(att->debug_callback, att->debug_data,
				"ATT confirmation received: 0x%02x", opcode);
		handle_conf(att, pdu + 1, bytes_read - 1);
		break;
	case ATT_OP_TYPE_REQ:
		/*
		 * If a request is currently pending, then the sequential
		 * protocol was violated. Disconnect the bearer, which will
		 * promptly notify the upper layer via disconnect handlers.
		 */
		if (att->in_req) {
			util_debug(att->debug_callback, att->debug_data,
					"Received request while another is "
					"pending: 0x%02x", opcode);
			io_shutdown(att->io);
			bt_att_unref(att);

			return false;
		}

		att->in_req = true;

		/* Fall through to the next case */
	case ATT_OP_TYPE_CMD:
	case ATT_OP_TYPE_NOT:
	case ATT_OP_TYPE_UNKNOWN:
	case ATT_OP_TYPE_IND:
	default:
		/* For all other opcodes notify the upper layer of the PDU and
		 * let them act on it.
		 */
		util_debug(att->debug_callback, att->debug_data,
					"ATT PDU received: 0x%02x", opcode);
		handle_notify(att, opcode, pdu + 1, bytes_read - 1);
		break;
	}

	bt_att_unref(att);

	return true;
}
コード例 #13
0
ファイル: onepixd.c プロジェクト: bcxorg/onepixd
/*************************************************************
** 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;
}