/* -------------------------------------------------------------------- */ /* SMS Client, send messages to mobile phones and pagers */ /* */ /* server.c */ /* */ /* Copyright (C) 1997,1998,1999 Angelo Masci */ /* */ /* This library is free software; you can redistribute it and/or */ /* modify it under the terms of the GNU Library General Public */ /* License as published by the Free Software Foundation; either */ /* version 2 of the License, or (at your option) any later version. */ /* */ /* This library is distributed in the hope that it will be useful, */ /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ /* Library General Public License for more details. */ /* */ /* You should have received a copy of the GNU Library General Public */ /* License along with this library; if not, write to the Free */ /* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* */ /* You can contact the author at this e-mail address: */ /* */ /* angelo@styx.demon.co.uk */ /* */ /* -------------------------------------------------------------------- $Id$ -------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lock/lock.h" #include "server.h" #include "logfile/logfile.h" #include "common/common.h" /* -------------------------------------------------------------------- */ #if !defined(WNOHANG) #define WNOHANG 0 #endif /* -------------------------------------------------------------------- */ /* Read a complete '\n' terminated string from socket */ /* upto maxlen characters will be read and a '\0' is appended */ /* -------------------------------------------------------------------- */ char *hgets(char *buf, int maxlen, int fd) { int result, i; /* NOTE - */ /* As this function now uses recv instead of read */ /* it probably makes sense to use the MSG_PEEK functionality */ /* to read a block of data find the newline and then */ /* make another call to receive with the correct length */ /* of the line. */ i = 0; buf[0] = '\0'; while (i < (maxlen -1)) { result = read(fd, &buf[i], 1); if (result < 0) { if (errno != EINTR) { lprintf(LOG_ERROR, "read() failed\n"); exit(-1); } } else if (result == 0) { buf[i+1] = '\0'; if (i == 0) { return NULL; } break; } else { if (buf[i] == '\n') { i++; break; } i++; } } buf[i] = '\0'; return buf; } /* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */ void hprintf(int fd, char *fmt, ...) { va_list args; int line_len, len; static char line[MAX_STRING_LEN]; char *line_ptr; /* ---------------------------- */ va_start(args, fmt); #if !defined(LINUX) vsprintf(line, fmt, args); #else vsnprintf(line, MAX_STRING_LEN, fmt, args); #endif va_end(args); line_len = strlen(line); line_ptr = line; while ((len = write(fd, line_ptr, line_len)) != line_len) { if (len < 0) { if (errno == EINTR) { continue; } else { lprintf(LOG_ERROR, "write() failed\n"); exit(-1); } } line_ptr += len; line_len -= len; } } /* -------------------------------------------------------------------- */ /* Reap child processes on receipt of SIGCHLD */ /* -------------------------------------------------------------------- */ #if defined(LINUX) || defined(SOLARIS) || defined(OSF1) void sigchld_handler(int signum) { while (waitpid((pid_t)-1, NULL, WNOHANG) > 0) { /* Reap children */ } signal(SIGCHLD, sigchld_handler); /* Re-establish signal */ /* handler */ } #endif /* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */ int server_init(int server_port) { int sockfd; /* listen on sock_fd */ struct sockaddr_in server_addr; /* my address information */ if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { lprintf(LOG_ERROR, "socket() failed\n"); return(-1); } server_addr.sin_family = AF_INET; /* host byte order */ server_addr.sin_port = htons(server_port); /* short, network byte order */ server_addr.sin_addr.s_addr = INADDR_ANY; /* auto-fill with my IP */ memset(&(server_addr.sin_zero), 0, 8); /* zero rest of struct */ if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) < 0) { switch (errno) { case EADDRINUSE: lprintf(LOG_ERROR, "bind() failed - Socket %d allready in use\n", server_port); break; case EACCES: lprintf(LOG_ERROR, "bind() failed - Socket %d protected port and not superuser\n", server_port); break; default: lprintf(LOG_ERROR, "bind() failed\n"); } return(-1); } if (listen(sockfd, MAX_BACKLOG) < 0) { lprintf(LOG_ERROR, "listen() failed\n"); return(-1); } lprintf(LOG_STANDARD, "Listening on port %d\n", server_port); return sockfd; } /* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */ extern void release_lock(void); int server_quit; void signal_term(int sig) { server_quit = TRUE; } void init_server_quit_handler(void) { struct sigaction act; act.sa_flags = 0; act.sa_handler = signal_term; sigemptyset(&act.sa_mask); #if defined(SA_INTERRUPT) act.sa_flags |= SA_INTERRUPT; #endif server_quit = FALSE; sigaction(SIGTERM, &act, NULL); } int server_main(int server_port, void (*handle_child)(int new_fd), char *lockfile) { pid_t pid; int sockfd, sin_size, new_fd; struct sockaddr_in client_addr; /* connector's address information */ #if 0 if (deamon_init() != 0) { return(-11); } #endif if (resource_test_lockdir(lockfile) == -1) { return -1; } if (resource_lock(lockfile)) { lprintf(LOG_ERROR, "smsd aborted, lockfile held by another smsd daemon\n"); return(-1); } sockfd = server_init(server_port); if (sockfd == -1) { release_lock(); return(-1); } #if defined(LINUX) || defined(SOLARIS) signal(SIGCHLD, sigchld_handler); #else signal(SIGCHLD, SIG_IGN); /* Automagically reap zombies */ /* under SYSV */ #endif set_consolelog(FALSE); /* No more login output */ /* to be sent to the console */ init_server_quit_handler(); while(! server_quit) { /* main accept() loop */ sin_size = sizeof(struct sockaddr_in); if ((new_fd = accept(sockfd, (struct sockaddr *)&client_addr, &sin_size)) < 0) { if (errno == EINTR) { continue; } else { lprintf(LOG_ERROR, "accept() failed\n"); release_lock(); exit(-1); } } pid = fork(); if (pid == 0) { (*handle_child)(new_fd); close(new_fd); exit(0); } else if (pid < 0) { lprintf(LOG_ERROR, "Trying to fork child process\n"); release_lock(); exit(-1); } close(new_fd); } release_lock(); return 0; } /* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */ void default_echo(int new_fd) { char buf[MAX_STRING_LEN]; while (hgets(buf, MAX_STRING_LEN, new_fd) != NULL) { printf("Received Data +%s+\n", buf); hprintf(new_fd, "Echo: +%s+\n", buf); } } /* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */ int deamon_init(void) { pid_t pid; pid = fork(); if (pid == -1) { lprintf(LOG_ERROR, "fork() Failed with errno %d\n", errno); return(-1); } else if (pid != 0) { exit(0); /* Parent Exits */ } /* ------------------------------------ */ /* Child continues */ /* ------------------------------------ */ if (setsid() < 0) /* Become session leader */ { lprintf(LOG_ERROR, "setsid() Failed with errno %d\n", errno); return(-1); } if (chdir("/") < 0) /* Change working directory */ { lprintf(LOG_ERROR, "chdir() Failed with errno %d\n", errno); return(-1); } return 0; } /* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */ int get_client_information(int fd, char *host_name, char *ip_address) { int sin_size; struct sockaddr_in client_addr; /* connector's address information */ struct hostent *host; u_long addr; sin_size = sizeof(struct sockaddr_in); if ((getpeername(fd, (struct sockaddr *)&client_addr, &sin_size)) < 0) { return -1; } strcpy(ip_address, inet_ntoa(client_addr.sin_addr)); addr = inet_addr(ip_address); host = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET); if (host != NULL) { strcpy(host_name, host->h_name); } else { strcpy(host_name, ""); } return 0; }