#include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include <string.h> #include <unistd.h> #include <signal.h> #include <sys/param.h> #include <sys/socket.h> #include <sys/stat.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <fcntl.h> #include <errno.h> #include <time.h> #include <pwd.h>
#define _GNU_SOURCE #include <getopt.h>
#define DEFAULT_PORT 1987 #define DEFAULT_USER "nikolay" #define LOGFILE "/home/nikolay/access_log" #define BACKLOG 5
int sockfd, sockfd_new; /* дескриптори на гнезда */
void sisd_log(char *fmt, ...) { va_list args; char buffer[128]; FILE *logfd;
if((logfd = fopen(LOGFILE, "a")) == 0) { fclose(logfd); exit(1); }
va_start(args, fmt); vsprintf(buffer, fmt, args); va_end(args);
fprintf(logfd, buffer);
fclose(logfd);
}
void p_error(char error[], int errnum, int how) { sisd_log("%s error #%d\n", errno, errnum);
if(how == 0) exit(2); if(how == 1) { close(sockfd); exit(3); } if(how == 2) return; if(how == 3) { shutdown(sockfd_new, 2); close(sockfd_new); return; } if(how == 4) { shutdown(sockfd_new, 2); close(sockfd_new); usleep(100); exit(4); } }
void process(FILE *f, char *cl) { time_t now; char timebuf[128];
fprintf(f, "HTTP/1.1 200 OK\r\n"); fprintf(f, "Server: SIS\r\n"); now = time(NULL); strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&now)); fprintf(f, "Date: %s\r\n", timebuf); fprintf(f, "Content-Type: text/html\r\n"); fprintf(f, "Connection: close\r\n"); fprintf(f, "\r\n"); fprintf(f, "<html><head><title>SISD</title></head><body>");
FILE *read_fp; char buf[BUFSIZ];
memset(buf, '\0', sizeof(buf)); read_fp = popen("fortune", "r"); if(read_fp != NULL) { fread(buf, sizeof(buf), 1, read_fp); pclose(read_fp); fprintf(f, buf); } else { fprintf(f, "Error!"); } memset(buf, '\0', sizeof(buf));
fprintf(f, "</body></html>");
sisd_log("Connection from %s at %s\n", cl, timebuf); }
int main(int argc, char *argv[]) { int opt; int port = -1; char user[21] = "\n", logfile[256] = "\n"; struct option opts[] = { {"port", 2, NULL, 'p'}, {"username", 2, NULL, 'u'}, {"logfile", 2, NULL, 'l'}, {"version", 0, NULL, 'v'}, {"help", 0, NULL, 'h'}};
/*************************************************/ /* обработване на входните параметри */ /************************************************/ while((opt = getopt_long(argc, argv, "p:u:l:vh", opts, NULL)) != -1) { switch(opt) { case 'p': { if(optarg) { port = (int)optarg; } else { port = DEFAULT_PORT; } break; } case 'u': { if(optarg) { strncpy(user, optarg, 20); } else { strcpy(user, DEFAULT_USER); } break; } case 'l': { if(optarg) { strncpy(logfile, optarg, 255); } else { strcpy(logfile, LOGFILE); } break; } case 'v': { printf("Simple Info Server deamon (SISD) 1.0\n"); return 0; } case 'h': { printf("Simple Info Server deamon (SISD) 1.0\n"); printf("Server provides system information in text/html format.\n"); printf("Nikolay Stefanov 2007\n"); return 1; } case ':': { printf("Option %s needs a value\n", optarg); return 1; } case '?': { printf("Unknown option: %c\n", optopt); return 2; } } }
/* ако не са зададени опции p,u,l */ if(port == -1) { port = DEFAULT_PORT; }
if(strcmp(user, "\n") == 0) { strcpy(user, DEFAULT_USER); }
if(strcmp(logfile, "\n") == 0) { strcpy(logfile, LOGFILE); }
/****************************************************************/ /* Настройване на сървъра за работа като deamon */ /***************************************************************/ struct passwd *usr = getpwnam(user);
printf("Starting deamon as %s(%d:%d) at port: %d ...\n", user, usr->pw_uid, usr->pw_gid, htons(port)); /* затварят се всички отворени файлове */ int i; for(i=0; i<NOFILE; i++) close(i); /* стартира се нова сесия */ switch(fork()) { case -1: p_error("First fork()", errno, 0); default: exit(0); case 0: { /* първо дете стартира нова сесия */ if(setsid() == -1) /* детето става лидер на новата сесия */ p_error("setsid()", errno, 0); if(setuid(usr->pw_uid) == -1) p_error("setuid()", errno, 0); if(setgid(usr->pw_gid) == -1) p_error("setgid()", errno, 0); switch(fork()) { /* второ пораждане на дете */ case -1: p_error("Second fork()", errno, 0); case 0: umask(0); break; /* второто дете не е лидер на сесия */ default: exit(0); /* завършва втория родител (първо дете) */ } } }
sigset(SIGCHLD, SIG_IGN); /* сигнала се игнорира за да няма зомбита */
/* създава се потоково гнездо */ if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) p_error("Could not create socket", errno, 0);
struct sockaddr_in addr, client_addr; socklen_t addrlen; int flags;
memset(&addr, 0, sizeof(addr)); memset(&client_addr, 0, sizeof(client_addr));
addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = INADDR_ANY;
/* присвоява се адрес на гнездото */ if(bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) p_error("bind()", errno, 1);
/* подготовка на гнездото за приемане на заявки */ if(listen(sockfd, BACKLOG) == -1) p_error("listen()", errno, 1); if((flags = fcntl(sockfd, F_GETFL)) == -1 || fcntl(sockfd, F_SETFL, flags & ~O_NONBLOCK) == -1) /* блокира до пристигане на заявката */ p_error("fcntl()", errno, 1);
/* получаване и обслужване на клиентски заявки */ for(;;) { addrlen = sizeof(client_addr); /* ново гнездо и установяване на връзка с гнездо на клиент */ if((sockfd_new = accept(sockfd, (struct sockaddr*)&client_addr, &addrlen)) < 0) { p_error("accept()", errno, 2); continue; } switch(fork()) { case -1: p_error("fork() client", errno, 2); continue; case 0: { FILE *f; if(!(f = fdopen(sockfd_new, "r+"))) { p_error("fdopen", errno, 2); continue; } process(f, inet_ntoa(client_addr.sin_addr)); fclose(f);
shutdown(sockfd_new, 2); /* закрива се връзката */ close(sockfd_new); /* новото гнездо се унищожава */ sleep(1); /* дава шанс на клиента да завърши успешно */ exit(0); /* детето завършва */ } } close(sockfd_new); /* родител - затваря новия дескриптор */ } return 0; }
|