/*
* server_backend.c - network server socketing backend
*
* (c) 2001 Ondrej Jombik <nepto@pobox.sk>
*
* History
* - 19/9/2001 - initial release
* - 20/9/2001 - minor bugfixes
* - waitpid() used as substitute of wait()
*
* TODO
* - write debug infomations to STDERR
*/
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
/* wait() */
#include <sys/wait.h>
/* struct timeval */
#include <sys/time.h>
/* signal() */
#include <signal.h>
#include "server-backend.h"
int main(int argc, char *argv[]) {
register int k;
int port;
int listen_sock;
fd_set fds;
struct sockaddr_in bind_addr;
struct client *client_first = NULL;
char **process;
char *fake_argv[] = {
"vim", "parser.h", "parser.c", "config.h", "main.h", "main.c", NULL
};
if (argc < 3) {
fprintf(stderr, "Usage: %s <port> <process> <arg1> ... <argn>\n",
PROG_NAME);
return 1;
}
port = atoi(argv[1]);
port = port > 0 ? port : PORT;
process = (char**) malloc((argc - 2 + 1) * sizeof(char*));
if (process == NULL) {
perror(PROG_NAME);
return 1;
}
process[argc - 2] = NULL;
for (k = 2; k < argc; k++) {
process[k - 2] = strdup(argv[k]);
if (process[k - 2] == NULL) {
perror(PROG_NAME);
return 1;
}
}
/*
* Fake command line string.
*/
{
int argv_size = 0;
for (k = 0; k < argc; k++)
argv_size += strlen(argv[k]) + 1;
memset(argv[0], '\0', argv_size);
for (k = 0; fake_argv[k] != NULL; k++) {
fprintf(stderr, "argv_size = %d\n", argv_size);
if (argv[k] == NULL)
break;
if (strlen(fake_argv[k]) + 1 <= argv_size) {
strcpy(argv[k], fake_argv[k]);
if (argv[k + 1] != NULL)
argv[k + 1] = argv[k] + strlen(fake_argv[k]) + 1;
argv_size -= strlen(fake_argv[k]) + 1;
}
else {
strncpy(argv[k], fake_argv[k], argv_size - 1);
break;
}
}
}
/*
(void) setpgrp();
signal(SIGBRK, SIG_IGN);
*/
listen_sock = socket(AF_INET, SOCK_STREAM, 0);
if (listen_sock == -1) {
perror(PROG_NAME);
return errno;
}
bind_addr.sin_family = AF_INET;
bind_addr.sin_addr.s_addr = INADDR_ANY;
bind_addr.sin_port = htons(port);
if (bind(listen_sock, (struct sockaddr *) &bind_addr,
sizeof(struct sockaddr_in)) == -1) {
perror(PROG_NAME);
return errno;
}
if (listen(listen_sock, 10)) {
perror(PROG_NAME);
return errno;
}
fcntl(listen_sock,F_SETFL,O_NDELAY);
switch (fork()) {
case -1:
perror(PROG_NAME);
return errno;
break;
case 0:
break;
default:
fprintf(stdout, "%s: accepting connections on port %d\n",
PROG_NAME, port);
return 0;
break;
}
while (1) {
int pid;
struct client *client;
struct timeval tv;
/*
for (pid = 0, client = client_first; client != NULL;
client = client->next)
pid++;
fprintf(stderr, "debug: %d active clients\n", pid);
*/
FD_ZERO(&fds);
FD_SET(listen_sock, &fds);
tv.tv_sec = 1;
tv.tv_usec = 0;
if (select(FD_SETSIZE, &fds, NULL, NULL, &tv) == -1)
continue;
if (FD_ISSET(listen_sock, &fds)) {
struct client *client_new;
struct sockaddr_in acc_addr;
unsigned int size;
int accept_sock;
struct hostent *host;
size = sizeof(struct sockaddr_in);
accept_sock = accept(listen_sock,
(struct sockaddr*) &acc_addr, &size);
if (accept_sock > 0 &&
(client_new = (struct client*)
malloc(sizeof(struct client))) != NULL) {
client_new->fd = accept_sock;
/* Geting client IP address. */
strncpy(client_new->ip,
(char*)inet_ntoa(acc_addr.sin_addr),
IP_SIZE);
client_new->ip[IP_SIZE] = '\0';
/* Geting host name. */
host = gethostbyaddr((char *)&acc_addr.sin_addr, 4, AF_INET);
if (host == NULL)
strncpy(client_new->host, client_new->ip, HOST_SIZE);
else
strncpy(client_new->host, host->h_name, HOST_SIZE);
client_new->host[HOST_SIZE] = '\0';
client_new->port = ntohs(acc_addr.sin_port);
client_new->next = NULL;
if (client_first == NULL) {
client_first = client_new;
}
else {
for (client = client_first;
client->next != NULL;
client = client->next)
;
client->next = client_new;
}
switch (pid = fork()) {
case -1:
perror(PROG_NAME);
return errno;
break;
case 0: /* child */
close(0);
dup(client_new->fd);
close(1);
dup(client_new->fd);
close(2);
dup(client_new->fd);
close(listen_sock);
/* write(client_new->fd, "----------------\n\r", 18); */
/* execvp(argv[2], argv + 2); */
execvp(process[0], process);
write(client_new->fd, "execlp() failure\n\r", 18);
break;
default: /* parent */
client_new->pid = pid;
close(client_new->fd);
break;
}
}
}
/*
* Terminated childs remove stuff.
*/
while ((pid = waitpid(-1, NULL, WNOHANG)) > 0) {
for (client = client_first; client != NULL; ) {
/*
fprintf(stderr, "debug: trying to removing pid %d, comparing with %d\n", pid, client->pid);
*/
if (client->pid == pid) {
/*
fprintf(stderr, "debug: removing pid %d\n", pid);
*/
shutdown(client->fd, 2);
close(client->fd);
if (client_first != client) {
struct client* prev;
for (prev = client_first;
prev->next != client;
prev = prev->next )
;
prev->next = client->next;
free(client);
client = prev->next;
}
else {
client_first = client->next;
free(client);
client = client_first;
}
}
else {
client = client->next;
}
}
}
} /* while (1) */
return 0;
}
Platon Group <platon@platon.sk> http://platon.sk/
|