Пример #1
0
int main(int argc, char *argv[]){

	char *service = SERVER_PORT;
	if (daemon_init() == -1){
		printf("can't fork self\n");
		exit(0);
	}

    if(is_already_running(argv[0]) != 0){
        fprintf(stdout, "%s process is already exists.\n", argv[0]);
        exit(-1);
    }

	int servSock = SetupTcpServerSocket(service);
	if(servSock < 0){
		fprintf(stderr, "SetupTCPServerSocket() failed");
	}

	for(; ;){
		int clntSock = AcceptTcpConnection(servSock);

		HandleTcpClient(clntSock);
		close(clntSock);
	}
}
Пример #2
0
static bool
is_already_running()
{
    int			 fd	   = -1;
    const char		*file_path = "/var/run/educ_noip.pid";
    const mode_t	 mode	   = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;	/* -rw-r--r-- */
    struct flock lock_ctx = {
	.l_type	  = F_WRLCK,
	.l_whence = SEEK_SET,
	.l_start  = 0,
	.l_len	  = 0,
	.l_pid	  = -1,
    };
    const int	OBTAIN_LOCK_ERR = -1;
    int		errno_save	= 0;

    if ((fd = open(file_path, O_RDWR | O_CREAT, mode)) == -1)
	log_die(errno, "is_already_running: can't open %s", file_path);
    if (fcntl(fd, F_SETLK, &lock_ctx) == OBTAIN_LOCK_ERR) {
	errno_save = errno;
	close(fd);
	if (errno_save == EACCES || errno_save == EAGAIN)
	    return (true);
	else
	    log_die(errno_save, "is_already_running: can't lock %s", file_path);
    }
    ftruncate(fd, 0);
    dprintf(fd, "%ld\n", (long int) getpid());
    g_lockfile_fd = fd;
    return (false);
}

/**
 * @brief	Run in the background
 * @return	void
 *
 * Detach the program from the controlling terminal and continue
 * execution...
 */
void
Daemonize()
{
    switch (fork()) {
    case FORK_FAILED:
	log_die(errno, "Daemonize: Cannot fork");
    case VALUE_CHILD_PROCESS:
	if (setsid() == -1)
	    log_die(errno, "Daemonize: Trouble in becoming the session leader");
	break;
    default:
	_exit(0);
    }

    log_init(); /* Calls to the log functions before this log to stderr/stdout. */

    switch (redirect_standard_streams()) {
    case REDIR_STDERR_FAIL:
	log_warn(0, "Daemonize: Error redirecting stderr");
	break;
    case REDIR_STDIN_FAIL:
	log_warn(0, "Daemonize: Error redirecting stdin");
	break;
    case REDIR_STDOUT_FAIL:
	log_warn(0, "Daemonize: Error redirecting stdout");
	break;
    default:
    case REDIR_OK:
	log_debug("Daemonize: All standard IO-streams successfully redirected");
	break;
    }

    if (is_already_running())
	log_die(0, "Daemonize: FATAL: A copy of the daemon is already running!");
}
Пример #3
0
/*
 *  Respond to commands requests and perform the commands:
 *  For a list of commands see command_process().
 *
 *  Will fork() for longer running operations such as fullcompare and quickcompare.
 *
 *  Will load all PPMs into RAM before listening for commands.
 *
 *  Args:
 *
 * orig_socketfh    : A file handle where text is sent, typically for any operational errors.
 * sql_info         : A string with SQL connection information.
 * portno           : Which network port to listen on.
 * compare_size     : The height (and width) of the PPMs.
 * maxerr           : For images to be considered similar the difference must be below this amount.
 *
 *  Return: non-zero on error.
 */
int server_loop(FILE *log_fh, char *sql_info, int portno, int compare_size,
		unsigned int maxerr) {

	if(is_already_running(log_fh, portno)){
		error(log_fh, "Quitting.");
		return 3;
	}

	// Setup IPv4 and/or IPv6 ports to listen on.
	// Ports below first_real_client_index are for listening for new connections.
	int first_real_client_index = 0;
	int listening_socket = create_port_listen_v4(log_fh, portno);
	if (listening_socket > 0) {
		global_client_detail[first_real_client_index].fd = listening_socket;
		first_real_client_index++;
	}

	// Setup a IPV6 network socket to listen on
	listening_socket = create_port_listen_v6(log_fh, portno);
	if (listening_socket > 0) {
		global_client_detail[first_real_client_index].fd = listening_socket;
		first_real_client_index++;
	}
	if (first_real_client_index == 0) {
		error(log_fh, "Failed to start listening on network. Quitting.");
		return 2;
	}
	// Done setting up IPv4 and/or IPv6 listening ports.

	// Connect to SQL database
	PGconn *psql = ppm_sql_connect(log_fh, sql_info);
	if (!psql) {
		error(log_fh,
				"libpq error: PQconnectdb returned NULL.\nSQL details: %s",
				sql_info);
		return 1;
	}

	// All PPMs in RAM. Loaded from SQL.
	PicInfo *picinfo_list = NULL;

	// Load all PPMs from SQL into RAM.
	int rc = load(log_fh, psql, &picinfo_list);
	if (rc) {
		error(log_fh, "LOAD failed with code %d", rc);
		ppm_sql_disconnect(log_fh, psql);
		return 1;
	}

	// Setup an array of incoming file descriptors.
	int index;
	for (index = first_real_client_index; index < CLIENT_MAX; index++) {
		global_client_detail[index].fd = CLIENT_SLOT_FREE;
		global_client_detail[index].command_buffer[0] = 0;
		global_client_detail[index].pid = 0;
		// TODO global_client_detail[index].connection_timeout = TODO ;
	}
	global_active_connection_count = first_real_client_index;

	// Listening Sockets have been created.
	int server_loop = 1;

	// Setup a timeout waiting for commands.
	// We time out so that we can reap children without needing to wait for commands.
	fd_set my_fd_set;
	struct timeval timeout;

	// listen for commands
	while (server_loop) {

		/* Initialize the file descriptor set we will block on. */
		FD_ZERO(&my_fd_set);

		// Setup FD set to block on. Also maximum FD.
		int fd_max = 0;
		for (index = 0; index < CLIENT_MAX; index++) {
			// Don't add closed FDs.
			if (global_client_detail[index].fd == CLIENT_SLOT_FREE)
				continue;
			// Stop listening for new connections if we have too many.
			if ((index < first_real_client_index)
					&& (global_active_connection_count >= CLIENT_MAX))
				continue;

			FD_SET(global_client_detail[index].fd, &my_fd_set);
			fd_max = max(fd_max, global_client_detail[index].fd);
		}

		/* Initialize the timeout data structure. */
		// We do timeout so we can do housekeeping without need to wait for client input to trigger the loop.
		timeout.tv_sec = COMMAND_LISTEN_TIMEOUT;
		timeout.tv_usec = 0;

		/* select() returns the count of FDs with waiting input, or -1 if error. */
		int fd_count_with_input = TEMP_FAILURE_RETRY(
				select(fd_max + 1, &my_fd_set, NULL, NULL, &timeout));
		if (fd_count_with_input < 0) {
			error(log_fh, "select() failed. errno=%d, error=%s", errno,
					strerror(errno));
			continue;
		}

		// Housekeeping
		// Reaper: Clean up any child processes which have exited.
		pid_t late_pid;
		while ((late_pid = waitpid(-1, NULL, WNOHANG)) > 0) {
			global_child_process_count--;
			// Find the late client by pid then record it as dead.
			for (index = first_real_client_index; index < CLIENT_MAX; index++){
				if ((global_client_detail[index].fd != CLIENT_SLOT_FREE)
						 && (global_client_detail[index].pid == late_pid)) {
					global_client_detail[index].pid = 0;
					break;
				}
			}
		}

		// Housekeeping
		// TODO check for any stale client connections
		// TODO add a mechanism so that we expire connections (other than socked for new v4 and v6)
		// if they wait too long to send data.
		// Don't expire connections if the server is the one that hasn't responded.

		// There was a select() timeout, no FDs with input available.
		if (fd_count_with_input == 0)
			continue;

		// Read data from FDs.
		for (index = 0; index < CLIENT_MAX; index++) {
			// Check for valid FD.
			if (global_client_detail[index].fd == CLIENT_SLOT_FREE)
				continue;
			// Check for waiting data on FD.
			int fd = global_client_detail[index].fd;
			if (!FD_ISSET(fd, &my_fd_set))
				continue;
			// We have a new IPv4 or IPv6 connection.
			if (index < first_real_client_index) {
				int new_sockfd = accept(fd, NULL, NULL);
				if (new_sockfd < 0) {
					error(log_fh, "accept()");
					continue;
				}
				int free_slot = first_real_client_index; // lower connections are for new IPv4 and IPv6 connections.
				for (; free_slot < CLIENT_MAX; free_slot++)
					if (global_client_detail[free_slot].fd == CLIENT_SLOT_FREE)
						break;
				// Are we are out of free slots?
				if (free_slot >= CLIENT_MAX) {
					error(log_fh,
							"Internal Error: we somehow got more connections than we can handle.");
					char *mesg = "BUSY: Please come back later";
					int write_rc = write(new_sockfd, mesg, strlen(mesg));
					if (write_rc == -1) {
						error(log_fh, "Failed to tell client to go away");
					}
					close(new_sockfd);
				}
				// Register new connection.
				else {
					global_client_detail[free_slot].fd = new_sockfd;
					global_client_detail[free_slot].pid = 0;
					bzero(global_client_detail[free_slot].command_buffer,
					BUFFER_SIZE);
					global_client_detail[free_slot].cmd_offset = 0;
					global_active_connection_count++;
					// TODO anything else?
					// TODO set timeout.
				}
			} else {
				// We have data on existing connection that needs to be read.
				int client_fd = global_client_detail[index].fd;
				char *cmd_buffer = global_client_detail[index].command_buffer;
				int cmd_offset = global_client_detail[index].cmd_offset;
				// Read data.
				int read_bytes = read(client_fd, &cmd_buffer[cmd_offset], BUFFER_SIZE - cmd_offset);
				if (read_bytes < 0) {
					// kill the connection as client has most likely gone away.
					error(log_fh,
							"Failed to read from client, closing the FD.");
					close(client_fd);
					global_client_detail[index].fd = CLIENT_SLOT_FREE;
					continue;
				}
				global_client_detail[index].cmd_offset += read_bytes;
				// TODO update timeout.
				// Check if a command has been completed.
				int command_end = strcspn(cmd_buffer, "\r\n");
				if (command_end > 0) {
					cmd_buffer[command_end] = 0; // Strip trailing LF, CR, CRLF, LFCR, ...
					command_process(client_fd, cmd_buffer, &picinfo_list, psql,
							&server_loop, compare_size, maxerr);
					close(client_fd);
					global_client_detail[index].fd = CLIENT_SLOT_FREE;
					global_active_connection_count--;
				}
			}
		}
	}
	if (picinfo_list) {
		unload(&picinfo_list);
	}

	// close all sockets, including for new IPv4 and IPv6 connections.
	for (index = 0; index < CLIENT_MAX; index++)
		if (global_client_detail[index].fd != CLIENT_SLOT_FREE)
			close(global_client_detail[index].fd);

	// End of server loop
	ppm_sql_disconnect(log_fh, psql);
	return 0;
}