#ifdef HAVE_CONFIG_H # include "config.h" #endif #include "server.h" #include "session.h" #include "socktool.h" #include "basic-list.h" #include #include #include #include #include #include #include #include #include #include #include #include #include static volatile sig_atomic_t quit_request = false; static void server_quit_request_setter (int signo) { quit_request = true; } static void server_cleanup_child (int signo) { while (waitpid (-1, NULL, WUNTRACED | WNOHANG) > 0); } static void svrlist_fill_rset (List* svrlist, fd_set* rset, int* maxfd) { ListNode* iter; for (iter = list_node_front (svrlist); iter != NULL; iter = list_next (iter)) { RasServer* server = RAS_SERVER (list_data (iter)); RasConn* conn = RAS_CONN (server); if (conn->fd_is_set) { FD_SET (conn->fd, rset); *maxfd = xmax (*maxfd, conn->fd) + 1; } } } static RasServer* svrlist_find_server (List* svrlist, int fd) { ListNode* iter; for (iter = list_node_front (svrlist); iter != NULL; iter = list_next (iter)) { RasServer* server = RAS_SERVER (list_data (iter)); RasConn* conn = RAS_CONN (server); if (conn->fd_is_set && conn->fd == fd) { return server; } } return NULL; } int main (int argc, char* argv[]) { setlocale (LC_ALL, ""); tzset (); if (argc < 2) { fprintf (stderr, "Usage: %s port\n", argv[0]); return 1; } struct sigaction action = { .sa_handler = SIG_IGN, .sa_flags = 0 }; sigemptyset (&action.sa_mask); sigaction (SIGPIPE, &action, NULL); sigaction (SIGHUP, &action, NULL); action.sa_handler = server_cleanup_child; sigaction (SIGCHLD, &action, NULL); action.sa_handler = server_quit_request_setter; sigaction (SIGINT, &action, NULL); sigaction (SIGQUIT, &action, NULL); sigaction (SIGTERM, &action, NULL); /* XXX 設定 standard I/O buffer mode,為後面的 shell 做準備 */ setvbuf (stdin, NULL, _IOLBF, 0); setvbuf (stdout, NULL, _IOLBF, 0); setvbuf (stderr, NULL, _IONBF, 0); List* svrlist = list_create (); int sid = 0, cid = 0; RasServer svr; /* XXX Check before unlink */ unlink ("/tmp/ras-server.sock"); ras_server_init (&svr, RAS_CONN_PERM_ADMIN, AF_LOCAL, NULL, sid), sid++; if (ras_server_listen (&svr, "/tmp/ras-server.sock", 0600) < 0) { return 1; } else { list_pushback (svrlist, &svr, sizeof (RasServer)); } ras_server_init (&svr, RAS_CONN_PERM_RESTRICTED, AF_INET, NULL, sid), sid++; if (ras_server_listen (&svr, NULL, atoi (argv[1])) < 0) { return 1; } else { list_pushback (svrlist, &svr, sizeof (RasServer)); } ras_server_init (&svr, RAS_CONN_PERM_RESTRICTED, AF_INET6, NULL, sid), sid++; if (ras_server_listen (&svr, NULL, atoi (argv[1])) < 0) { return 1; } else { list_pushback (svrlist, &svr, sizeof (RasServer)); } int maxfd; fd_set rset, wset; while (!quit_request) { int rval; maxfd = 0; FD_ZERO (&rset); FD_ZERO (&wset); svrlist_fill_rset (svrlist, &rset, &maxfd); rval = select (maxfd, &rset, &wset, NULL, NULL); if (rval < 0) { continue; } for (int i = 0; i < maxfd; i++) { if (FD_ISSET (i, &rset)) { RasServer* server = svrlist_find_server (svrlist, i); if (server == NULL) { continue; } RasSession session; int session_fd; char* peername; pid_t cpid; session_fd = accept (i, NULL, NULL); if (session_fd < 0) { continue; } peername = ras_socktool_get_peername (session_fd); ras_server_log (server, "client %d from %s is accepted", cid, peername); free (peername); cpid = fork (); if (cpid < 0) { ras_server_log (server, "client %d cannot continue: fork error: %s", cid, strerror (errno)); close (session_fd); ras_server_log (server, "client %d connection closed", cid); continue; } else if (cpid > 0) { /* parent process: just close the accepted fd */ ras_server_log (server, "client %d is handled by process %u", cid, cpid); cid++; close (session_fd); } else { /* child process: we need to reset signal handlers */ struct sigaction session_action = { .sa_handler = SIG_DFL, .sa_flags = 0 }; sigemptyset (&session_action.sa_mask); sigaction (SIGINT, &action, NULL); sigaction (SIGQUIT, &action, NULL); sigaction (SIGTERM, &action, NULL); /* detach from the controlling terminal */ if (setsid () < 0) { exit (1); } if (ras_session_init (&session, server, session_fd, cid) < 0) { ras_session_destroy (&session); exit (1); } if (ras_session_read_header (&session) < 0) { ras_session_destroy (&session); exit (1); } if (ras_session_start_shell (&session) < 0) { ras_session_destroy (&session); exit (1); } ras_session_destroy (&session); exit (0); } } } } puts ("Shutting down all servers ..."); for (ListNode* iter = list_node_front (svrlist); iter != NULL; iter = list_next (iter)) { ras_server_destroy (RAS_SERVER (list_data (iter))); } list_free (svrlist); puts ("Exited"); return 0; }