diff options
author | LAN-TW <lantw44@gmail.com> | 2014-01-14 01:15:02 +0800 |
---|---|---|
committer | LAN-TW <lantw44@gmail.com> | 2014-01-14 01:15:02 +0800 |
commit | 5873050be326f8b645b994d8c5e8b266573a6e40 (patch) | |
tree | 46ec76ae4fddc68974fee549cc6d530d408bd044 /hw4/chttpd/chttpd-server.c | |
parent | d045ff1806eb429a2c06203dfda165a21dad14ab (diff) | |
download | sp2013-5873050be326f8b645b994d8c5e8b266573a6e40.tar sp2013-5873050be326f8b645b994d8c5e8b266573a6e40.tar.gz sp2013-5873050be326f8b645b994d8c5e8b266573a6e40.tar.bz2 sp2013-5873050be326f8b645b994d8c5e8b266573a6e40.tar.lz sp2013-5873050be326f8b645b994d8c5e8b266573a6e40.tar.xz sp2013-5873050be326f8b645b994d8c5e8b266573a6e40.tar.zst sp2013-5873050be326f8b645b994d8c5e8b266573a6e40.zip |
HW4: 基本的接聽連線功能
Diffstat (limited to 'hw4/chttpd/chttpd-server.c')
-rw-r--r-- | hw4/chttpd/chttpd-server.c | 279 |
1 files changed, 279 insertions, 0 deletions
diff --git a/hw4/chttpd/chttpd-server.c b/hw4/chttpd/chttpd-server.c new file mode 100644 index 0000000..d5c0337 --- /dev/null +++ b/hw4/chttpd/chttpd-server.c @@ -0,0 +1,279 @@ +/* b01902062 藍挺瑋 */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "memwrap.h" +#include "chttpd-log.h" +#include "chttpd-server.h" +#include "chttpd-socket.h" +#include <l4list.h> +#include <l4str.h> +#include <l4posix.h> + +#include <errno.h> +#include <fcntl.h> +#include <locale.h> +#include <netdb.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <time.h> +#include <unistd.h> + +#define NULLPTR ((void*)0) +#define SOCKADDR(x) ((struct sockaddr*)(x)) +#define SOCKADDR_UN(x) ((struct sockaddr_un*)(x)) +#define SOCKADDR_IN(x) ((struct sockaddr_in*)(x)) +#define SOCKADDR_IN6(x) ((struct sockaddr_in6*)(x)) + +static inline void report_unix_error (int rval) { + int errno_backup = errno; + switch (rval) { + case CHTTPD_SOCKET_NEW_ERROR_SOCKET: + fputs ("socket: ", stdout); + break; + case CHTTPD_SOCKET_NEW_ERROR_BIND: + fputs ("bind: ", stdout); + break; + } + puts (strerror (errno_backup)); + errno_backup = errno; +} + +static inline bool init_runtime_dir (ChttpdServer* server, + const char* dir, const char* ctrl, const char* pid) { + + printf ("Trying %s ... ", ctrl); + fflush (stdout); + + mkdir (dir, 0700); + + struct stat st; + char* chttpd_force = getenv ("CGISH_HTTPD_FORCE"); + if (chttpd_force != NULL && *chttpd_force != '\0') { + if (stat (ctrl, &st) >= 0) { + if (S_ISSOCK (st.st_mode)) { + unlink (ctrl); + } + } + } + + server->sockfd = chttpd_socket_new_unix (ctrl, + SOCKADDR_UN (&server->addr), &server->addrlen); + if (server->sockfd < 0) { + report_unix_error (server->sockfd); + return false; + } + + int pidfd = open (pid, O_WRONLY | O_TRUNC | O_CREAT, 0666); + if (pidfd >= 0) { + dprintf (pidfd, "%ld", ((long)getpid ())); + close (pidfd); + } + + puts ("OK"); + return true; +} + +static inline void report_inet_error (int rval, int gai_error) { + int errno_backup = errno; + switch (rval) { + case CHTTPD_SOCKET_NEW_ERROR_GETADDRINFO: + fputs ("getaddrinfo: ", stdout); + puts (gai_strerror (gai_error)); + break; + case CHTTPD_SOCKET_NEW_ERROR_SOCKET: + fputs ("socket: ", stdout); + puts (strerror (errno_backup)); + break; + case CHTTPD_SOCKET_NEW_ERROR_BIND: + fputs ("bind: ", stdout); + puts (strerror (errno_backup)); + break; + case CHTTPD_SOCKET_NEW_ERROR_UNEXPECTED: + puts ("Unexpected error! Is your system broken?"); + break; + } + errno_backup = errno; +} + +static LbsListMeta* chttpd_main_init (ChttpdLog* hlog, const char* service) { + ChttpdServer* server; + LbsListMeta* slist = lbs_list_meta_new (chttpd_server_dtor); + int gai_error; + + /* Default: Unix socket (ADMIN) */ + bool unix_ok = false; + uid_t uid = getuid (); + server = xmalloc (sizeof (ChttpdServer)); + if (uid == 0) { + /* System runtime */ + char dir[] = "/var/run/" PACKAGE_NAME; + char ctrl[] = "/var/run/" PACKAGE_NAME "/control"; + char pid[] = "/var/run/" PACKAGE_NAME "/pid"; + unix_ok = init_runtime_dir (server, dir, ctrl, pid); + } + + if (!unix_ok) { + /* User XDG runtime */ + char* xdg_dir = getenv ("XDG_RUNTIME_DIR"); + if (xdg_dir != NULL && *xdg_dir != '\0') { + char* dir = lbs_posix_strcat (xdg_dir, "/" PACKAGE_NAME, NULLPTR); + char* ctrl = lbs_posix_strcat (dir, "/control", NULLPTR); + char* pid = lbs_posix_strcat (dir, "/pid", NULLPTR); + unix_ok = init_runtime_dir (server, dir, ctrl, pid); + free (dir); + free (ctrl); + free (pid); + } + } + + if (!unix_ok) { + /* Just guess user XDG dir ! */ + char* xdg_dir = lbs_str_printf ("/run/user/%lu", (unsigned long)uid); + char* dir = lbs_posix_strcat (xdg_dir, "/" PACKAGE_NAME, NULLPTR); + char* ctrl = lbs_posix_strcat (dir, "/control", NULLPTR); + char* pid = lbs_posix_strcat (dir, "/pid", NULLPTR); + unix_ok = init_runtime_dir (server, dir, ctrl, pid); + free (xdg_dir); + free (dir); + free (ctrl); + free (pid); + } + + if (!unix_ok) { + /* Fallback to XDG_CONFIG_HOME and current working directory */ + char* xdg_dir = getenv ("XDG_CONFIG_HOME"); + bool xdg_dir_alloc = false; + if (xdg_dir == NULL || *xdg_dir == '\0') { + char* home = getenv ("HOME"); + if (home == NULL || *home == '\0') { + home = "."; + } + + xdg_dir = lbs_posix_strcat (home, "/.config", LBS_COMMON_NULL_PTR); + xdg_dir_alloc = true; + } + + char* dir = lbs_posix_strcat (xdg_dir, "/" PACKAGE_NAME, NULLPTR); + char* ctrl = lbs_posix_strcat (dir, "/control", NULLPTR); + char* pid = lbs_posix_strcat (dir, "/pid", NULLPTR); + unix_ok = init_runtime_dir (server, dir, ctrl, pid); + if (xdg_dir_alloc) { + free (xdg_dir); + } + free (dir); + free (ctrl); + free (pid); + } + + if (unix_ok) { + chttpd_server_ctor (server, hlog, true); + lbs_list_push_back (slist, server, NULL); + } else { + free (server); + } + + /* Default: IPv4 socket (HTTP) */ + server = xmalloc (sizeof (ChttpdServer)); + printf ("Trying IPv4 any host, service %s ... ", service); + server->sockfd = chttpd_socket_new_inet (NULL, service, AF_INET, + SOCKADDR (&server->addr), &server->addrlen, &gai_error); + if (server->sockfd >= 0) { + puts ("OK"); + chttpd_server_ctor (server, hlog, false); + lbs_list_push_back (slist, server, NULL); + } else { + report_inet_error (server->sockfd, gai_error); + free (server); + } + + /* Default: IPv6 socket (HTTP) */ + server = xmalloc (sizeof (ChttpdServer)); + printf ("Trying IPv6 any host, service %s ... ", service); + server->sockfd = chttpd_socket_new_inet (NULL, service, AF_INET6, + SOCKADDR (&server->addr), &server->addrlen, &gai_error); + if (server->sockfd >= 0) { + puts ("OK"); + chttpd_server_ctor (server, hlog, false); + lbs_list_push_back (slist, server, NULL); + } else { + report_inet_error (server->sockfd, gai_error); + free (server); + } + + return slist; +} + +int chttpd_server_notify; +pthread_mutex_t chttpd_server_notify_mutex; + +static void chttpd_main_loop (ChttpdLog* hlog, LbsListMeta* slist) { + chttpd_server_notify = 1; + pthread_mutex_init (&chttpd_server_notify_mutex, NULL); + + +} + +void chttpd_server_ctor (void* server_generic, ChttpdLog* hlog, bool is_admin) { + ChttpdServer* server = server_generic; + server->attr_admin = is_admin; + server->attr_close = false; + server->hlog = chttpd_log_ref (hlog); + server->conn = lbs_list_meta_new (NULL); + pthread_rwlock_init (&server->lock, NULL); +} + +void chttpd_server_dtor (void* server_generic) { + ChttpdServer* server = server_generic; + if (server->addr.ss_family == AF_UNIX) { + CHTTPD_SOCKET_SOCKADDR_UN_SET_NULL (SOCKADDR_UN (&server->addr)); + unlink (SOCKADDR_UN (&server->addr)->sun_path); + } + close (server->sockfd); + pthread_rwlock_destroy (&server->lock); + lbs_list_meta_free (server->conn); + free (server); +} + +int main (int argc, char* argv[]) { + setlocale (LC_ALL, ""); + tzset (); + + if (argc < 3) { + fprintf (stderr, "Usage: %s port logfile\n", argv[0]); + return 1; + } + + ChttpdLog* hlog = chttpd_log_new_file (argv[0], argv[2]); + if (hlog == NULL) { + fprintf (stderr, "Error while opening %s: %s\n", + argv[2], strerror (errno)); + return 2; + } + + LbsListMeta* slist = chttpd_main_init (hlog, argv[1]); + if (lbs_list_meta_get_len (slist) > 0) { + chttpd_main_loop (hlog, slist); + } else { + puts ("No server is active! Exiting ..."); + } + lbs_list_meta_free (slist); + + chttpd_log_write_str (hlog, "Waiting for all threads to terminate"); + + pthread_mutex_lock (&hlog->ref_mutex); + while (hlog->ref_count > 1) { + pthread_cond_wait (&hlog->ref_change, &hlog->ref_mutex); + } + pthread_mutex_unlock (&hlog->ref_mutex); + + chttpd_log_write_str (hlog, "All threads terminated. Exit now!"); + chttpd_log_unref (hlog); + + return 0; +} |