Пример #1
0
/*
 * The server's core. Creates the configured listening sockets and then
 * enters the server event loop from which all clients are served.
 */
int
runserver(void)
{
    int i, ipv4fd, ipv6fd, unixfd, epfd, nfds, timeout, fd, childstatus;
    struct epoll_event events[MAX_EVENTS];
    struct client_data *client;
    struct timeval sigtime, curtime, tmp;

    fd_to_client_max = 64;      /* will be doubled every time it becomes too
                                   small */
    fd_to_client = malloc(fd_to_client_max * sizeof (struct client_data *));

    epfd = epoll_create1(EPOLL_CLOEXEC);
    if (epfd == -1) {
        log_msg("Error in epoll_create1");
        return FALSE;
    }

    if (!setup_server_sockets(&ipv4fd, &ipv6fd, &unixfd, epfd))
        return FALSE;

    /* 
     * server event loop
     */
    while (1) { /* loop exit via "goto finally" */
        timeout = 10 * 60 * 1000;
        if (termination_flag) {
            if (termination_flag == 1)  /* signal didn't interrupt epoll_wait */
                trigger_server_shutdown(&sigtime, &ipv4fd, &ipv6fd, &unixfd);
            gettimeofday(&curtime, NULL);
            /* calculate the elapsed time since the quit request */
            timersub(&curtime, &sigtime, &tmp);
            timeout = 5000 - (1000 * tmp.tv_sec) - (tmp.tv_usec / 1000);
            if (timeout < 0)
                timeout = 0;
        }

        /* make sure child processes are cleaned up */
        waitpid(-1, &childstatus, WNOHANG);

        nfds = epoll_wait(epfd, events, MAX_EVENTS, timeout);
        if (nfds == -1) {
            if (errno != EINTR) {       /* serious problem */
                log_msg("Error from epoll_wait in main event loop: %s",
                        strerror(errno));
                goto finally;
            }

            /* some signal - SIGTERM? */
            if (!termination_flag)
                continue;

            /* begin server shutdown sequence */
            if (!trigger_server_shutdown(&sigtime, &ipv4fd, &ipv6fd, &unixfd))
                continue;
            else
                goto finally;
        } else if (nfds == 0) { /* timeout */
            if (!termination_flag)
                log_msg(" -- mark (no activity for 10 minutes) --");
            else        /* shutdown timer has run out */
                goto finally;
            continue;
        }

        for (i = 0; i < nfds; i++) {
            fd = events[i].data.fd;

            if (fd == ipv4fd || fd == ipv6fd || fd == unixfd) {
                /* server socket ready for accept */
                server_socket_event(fd, epfd);
                continue;
            }

            /* activity on a client socket or pipe */
            client = fd_to_client[fd];
            /* was this fd closed while handling a prior event? */
            if (!client)
                continue;

            switch (client->state) {
            case NEW_CONNECTION:
                if (events[i].events & EPOLLERR ||      /* error */
                    events[i].events & EPOLLHUP ||      /* connection closed */
                    events[i].events & EPOLLRDHUP)      /* connection closed */
                    close(fd);
                else if (events[i].events & EPOLLIN)
                    handle_new_connection(fd, epfd);
                break;

            case CLIENT_DISCONNECTED:
                /* When the client is disconnected, activity usually only
                   happens on the pipes: either the game process is closing
                   them because the idle timeout expired or shutdown was
                   requested via a signal. */
                if (events[i].events & EPOLLERR ||      /* error */
                    events[i].events & EPOLLHUP ||      /* connection closed */
                    events[i].events & EPOLLRDHUP)      /* connection closed */
                    cleanup_game_process(client, epfd);
                else if (events[i].events & EPOLLIN) {
                    /* Perhaps the game process was just writing data to the
                       pipe when the client disconnected. There is nothing we
                       can do with this data here, but we don't want to kill
                       the game either, so just read and discard the data. */
                    char buf[2048];
                    int ret;

                    do {
                        ret = read(fd, buf, 2048);
                    } while (ret == 2048);
                }
                break;

            case CLIENT_CONNECTED:
                handle_communication(fd, epfd, events[i].events);
                break;
            }
            if (termination_flag && client_count == 0)
                goto finally;
        }       /* for */
    }   /* while(1) */

finally:
    while (disconnected_list_head.next)
        cleanup_game_process(disconnected_list_head.next, epfd);
    while (connected_list_head.next)
        cleanup_game_process(connected_list_head.next, epfd);

    close(epfd);
    if (ipv4fd != -1)
        close(ipv4fd);
    if (ipv6fd != -1)
        close(ipv6fd);
    if (unixfd != -1)
        close(unixfd);
    free(fd_to_client);

    return TRUE;
}
Пример #2
0
int
main(int argc, char **argv)
{
	TSS_RESULT result;
	int newsd, c, rv, option_index = 0;
	int i;
	socklen_t client_len;
	char *hostname = NULL;
	fd_set rdfd_set;
	int num_fds = 0;
	int nfds = 0;
	int stor_errno;
	sigset_t sigmask, termmask, oldsigmask;
	struct sockaddr_storage client_addr;
	struct srv_sock_info socks_info[MAX_IP_PROTO];
	struct passwd *pwd;
	struct option long_options[] = {
		{"help", 0, NULL, 'h'},
		{"foreground", 0, NULL, 'f'},
		{"config", 1, NULL, 'c'},
		{0, 0, 0, 0}
	};

	unsetenv("TCSD_USE_TCP_DEVICE");
	while ((c = getopt_long(argc, argv, "fhec:", long_options, &option_index)) != -1) {
		switch (c) {
			case 'f':
				setenv("TCSD_FOREGROUND", "1", 1);
				break;
			case 'c':
				tcsd_config_file = optarg;
				break;
			case 'e':
				setenv("TCSD_USE_TCP_DEVICE", "1", 1);
				break;
			case 'h':
				/* fall through */
			default:
				usage();
				return -1;
				break;
		}
	}

	if (!tcsd_config_file)
		tcsd_config_file = TCSD_DEFAULT_CONFIG_FILE;

	if ((result = tcsd_startup()))
		return (int)result;

#ifdef NOUSERCHECK
    LogWarn("will not switch user or check for file permissions. "
            "(Compiled with --disable-usercheck)");
#else
#ifndef SOLARIS
	pwd = getpwnam(TSS_USER_NAME);
	if (pwd == NULL) {
		if (errno == 0) {
			LogError("User \"%s\" not found, please add this user"
					" manually.", TSS_USER_NAME);
		} else {
			LogError("getpwnam(%s): %s", TSS_USER_NAME, strerror(errno));
		}
		return TCSERR(TSS_E_INTERNAL_ERROR);
	}
	setuid(pwd->pw_uid);
#endif
#endif

	if (setup_server_sockets(socks_info) == -1) {
		LogError("Could not create sockets to listen to connections. Aborting...");
		return -1;
	}

	if (getenv("TCSD_FOREGROUND") == NULL) {
		if (daemon(0, 0) == -1) {
			perror("daemon");
			tcsd_shutdown(socks_info);
			return -1;
		}
	}

	LogInfo("%s: TCSD up and running.", PACKAGE_STRING);

	sigemptyset(&sigmask);
	sigaddset(&sigmask, SIGTERM);
	sigaddset(&sigmask, SIGHUP);

	sigemptyset(&termmask);
	sigaddset(&termmask, SIGTERM);

	do {
		prepare_for_select(socks_info, &num_fds, &rdfd_set, &nfds);
		// Sanity check
		if (num_fds == 0) {
			LogError("No server sockets available to listen connections. Aborting...");
			return -1;
		}

		// Block TERM and HUP signals to prevent race condition
		if (sigprocmask(SIG_BLOCK, &sigmask, &oldsigmask) == -1) {
			LogError("Error setting interrupt mask before accept");
		}

		// TERM and HUP are blocked here, so its safe to test flags.
		if (hup) {
			// Config reading can be slow, so unmask SIGTERM.
			if (sigprocmask(SIG_UNBLOCK, &termmask, NULL) == -1) {
				LogError("Error unblocking SIGTERM before config reload");
			}
			if (reload_config() != TSS_SUCCESS)
				LogError("Failed reloading config");
			if (sigprocmask(SIG_BLOCK, &termmask, NULL) == -1) {
				LogError("Error blocking SIGTERM after config reload");
			}
		}
		if (term)
			break;

		// Select IPv4 and IPv6 socket descriptors with appropriate sigmask.
		LogDebug("Waiting for connections");
		rv = pselect(nfds+1, &rdfd_set, NULL, NULL, NULL, &oldsigmask);
		stor_errno = errno; // original mask must be set ASAP, so store errno.
		if (sigprocmask(SIG_SETMASK, &oldsigmask, NULL) == -1) {
			LogError("Error reseting signal mask to the original configuration.");
		}
		if (rv == -1) {
			if (stor_errno != EINTR) {
				LogError("Error monitoring server socket descriptors.");
				return -1;
			}
			continue;
		}

		for (i=0; i < num_fds; i++) { // accept connections from all IP versions (with valid sd)
			if (!FD_ISSET(socks_info[i].sd, &rdfd_set)) {
				continue;
			}
			client_len = socks_info[i].addr_len;
			newsd = accept(socks_info[i].sd, (struct sockaddr *) &client_addr, &client_len);
			if (newsd < 0) {
				if (errno != EINTR)
					LogError("Failed accept: %s", strerror(errno));
				continue;
			}
			LogDebug("accepted socket %i", newsd);

			hostname = fetch_hostname(&client_addr, client_len);
			if (hostname == NULL)
				hostname=INVALID_ADDR_STR;

			tcsd_thread_create(newsd, hostname);
			hostname = NULL;
		} // for (i=0; i < MAX_IP_PROTO; i++)
	} while (term ==0);

	/* To close correctly, we must receive a SIGTERM */
	tcsd_shutdown(socks_info);
	return 0;
}