/* unixechoserver.c - Daemon using Unix sockets. It just echoes back what it receives. It is an on-demand concurrent server with clients handled by separate tasks. The command is given a file path parameter [ "/tmp" by default ]: Usage: % unixechoserver [] Assuming that the directory is /tmp and argv[0], the name of the server image, is unixechoserver: The name of the file associated with the socket is /tmp/unixechoserver The process id of the server is in the file /tmp/unixechoserver.pid The log file is /tmp/unixechoserver.log */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern int errno; #define PATHMAX 255 static char unixserver_path[PATHMAX+1] = "/tmp"; /* default */ static char unixsocket_path[PATHMAX+1]; static char unixlog_path[PATHMAX+1]; static char unixpid_path[PATHMAX+1]; /* If we are waiting reading from a pipe and the interlocutor dies abruptly (say because of ^C or kill -9), then we receive a SIGPIPE signal. Here we handle that. */ void sig_pipe(int n) { perror("Broken pipe signal"); } /* Handler for SIGCHLD signal */ void sig_chld(int n) { int status; fprintf(stderr, "Child terminated\n"); wait(&status); /* So no zombies */ } /* Initializes the current program as a daemon, by changing working directory, umask, and eliminating control terminal, setting signal handlers, saving pid, making sure that only one daemon is running. Modified from R.Stevens. */ void daemon_init(const char * const path, uint mask) { pid_t pid; char buff[256]; static FILE *log; /* for the log */ int fd; int k; /* put server in background (with init as parent) */ if ( ( pid = fork() ) < 0 ) { perror("daemon_init: cannot fork"); exit(0); } else if (pid > 0) /* The parent */ exit(0); /* the child */ /* Close all file descriptors that are open */ for (k = getdtablesize()-1; k>0; k--) close(k); /* Redirecting stdin and stdout to /dev/null */ if ( (fd = open("/dev/null", O_RDWR)) < 0) { perror("Open"); exit(0); } dup2(fd, STDIN_FILENO); /* detach stdin */ dup2(fd, STDOUT_FILENO); /* detach stdout */ close (fd); /* From this point on printf and scanf have no effect */ /* Redirecting stderr to unixlog_path */ log = fopen(unixlog_path, "aw"); /* attach stderr to unixlog_path */ fd = fileno(log); /* obtain file descriptor of the log */ dup2(fd, STDERR_FILENO); close (fd); /* From this point on printing to stderr will go to /tmp/mylog */ /* Establish handlers for signals */ if ( signal(SIGCHLD, sig_chld) < 0 ) { perror("Signal SIGCHLD"); exit(1); } if ( signal(SIGPIPE, sig_pipe) < 0 ) { perror("Signal SIGPIPE"); exit(1); } /* Change directory to specified directory */ chdir(path); /* Set umask to mask (usually 0) */ umask(mask); /* Detach controlling terminal by becoming sesion leader */ setsid(); /* Put self in a new process group */ pid = getpid(); setpgrp(); /* GPI: modified for linux */ /* Make sure only one server is running */ if ( ( k = open(unixpid_path, O_RDWR | O_CREAT, 0666) ) < 0 ) exit(1); if ( lockf(k, F_TLOCK, 0) != 0) exit(0); /* Save server's pid without closing file (so lock remains)*/ sprintf(buff, "%6d", pid); write(k, buff, strlen(buff)); return; } /*In an infinite loop, it reads characters from sockfd and writes them to the same socket */ void chr_echo(int sockfd) { for ( ; ; ) { ssize_t n; char c; n = read(sockfd, &c, 1); if (n > 0) { write(sockfd, &c, 1); } else if ( n < 0) { perror("Negative return from Read"); if (errno == EINTR) /* IO was interrupted by a signal */ continue; return; } else /* connection closed by other end */ return; } } /* Create and return a stream listening socket in the Unix domain using the filepath path. */ int unix_listening_socket(const char *path) { int fd; struct sockaddr_un servaddr; if ( ( fd = socket(AF_UNIX, SOCK_STREAM, 0) ) < 0 ) { exit(1); } bzero((void *)&servaddr, sizeof(servaddr)); servaddr.sun_family = AF_UNIX; strcpy(servaddr.sun_path, unixsocket_path); if ( bind(fd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0 ) { exit(1); } if ( listen(fd, 15) < 0 ) { exit(1); } return fd; } /* Return a connected socket obtain by an accept call on the listening socket lsd. It returns -1 if the accept was interrupted by a signal. */ int connected_socket(int lsd) { int csd; size_t clilen; struct sockaddr_in cliaddr; clilen = sizeof(cliaddr); if ( (csd = accept(lsd, (struct sockaddr *) &cliaddr, (int *)&clilen)) < 0) { if (errno == EINTR) return -1; else { exit(1); } } return csd; } int main(int argc, char *argv[]) { int listenfd; /* Initialize path variables */ if (argc > 1) strncpy(unixserver_path, argv[1], PATHMAX); /* use argv[1] */ strncat(unixserver_path, "/", PATHMAX-strlen(unixserver_path)); strncat(unixserver_path, argv[0], PATHMAX-strlen(unixserver_path)); strcpy(unixsocket_path, unixserver_path); strcpy(unixpid_path, unixserver_path); strncat(unixpid_path, ".pid", PATHMAX-strlen(unixpid_path)); strcpy(unixlog_path, unixserver_path); strncat(unixlog_path, ".log", PATHMAX-strlen(unixlog_path)); daemon_init(unixserver_path, 0); /* We stay in the unixserver_path directory and file creation is not restricted. */ unlink(unixsocket_path); /* delete the socket if already existing */ listenfd = unix_listening_socket(unixsocket_path); for ( ; ; ) { int connfd; pid_t childpid; if ( ( connfd = connected_socket(listenfd) ) < 0 ) continue; if ( ( childpid = fork() ) == 0) { /* child process */ close(listenfd); /* close listening socket */ chr_echo(connfd); /* process the request */ exit(0); /* terminate child */ } else if (childpid < 0) { perror("Fork"); exit(1); } close(connfd); /* parent closes connected socket */ } close(listenfd); return 0; }