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 | |
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')
-rw-r--r-- | hw4/chttpd/chttpd-log.c | 117 | ||||
-rw-r--r-- | hw4/chttpd/chttpd-log.h | 34 | ||||
-rw-r--r-- | hw4/chttpd/chttpd-server.c | 279 | ||||
-rw-r--r-- | hw4/chttpd/chttpd-server.h | 29 | ||||
-rw-r--r-- | hw4/chttpd/chttpd-socket.c | 120 | ||||
-rw-r--r-- | hw4/chttpd/chttpd-socket.h | 22 |
6 files changed, 601 insertions, 0 deletions
diff --git a/hw4/chttpd/chttpd-log.c b/hw4/chttpd/chttpd-log.c new file mode 100644 index 0000000..7214a29 --- /dev/null +++ b/hw4/chttpd/chttpd-log.c @@ -0,0 +1,117 @@ +/* b01902062 藍挺瑋 */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "chttpd-log.h" +#include "memwrap.h" +#include <l4list.h> +#include <l4posix.h> + +#include <fcntl.h> +#include <pthread.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +ChttpdLog* chttpd_log_new (const char* name, int log_fd) { + char* name_start = strrchr (name, '/'); + name = name_start == NULL ? name : name_start + 1; + size_t name_len = (name == NULL) ? 0 : strlen (name); + char* mem = xmalloc (sizeof (ChttpdLog) + name_len + 1); + void* mem_generic = mem; + + ChttpdLog* hlog = mem_generic; + hlog->name = mem + sizeof (ChttpdLog); + if (name != NULL) { + strncpy (hlog->name, name, name_len + 1); + } + hlog->name[name_len] = '\0'; + + hlog->ref_count = 1; + hlog->pid = getpid (); + hlog->log_fd = log_fd; + hlog->log_data = lbs_list_meta_new (free); + pthread_cond_init (&hlog->ref_change, NULL); + pthread_mutex_init (&hlog->ref_mutex, NULL); + pthread_rwlock_init (&hlog->log_lock, NULL); + + return hlog; +} + +ChttpdLog* chttpd_log_new_file (const char* name, const char* file) { + int log_fd = open (file, O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC, 0600); + if (log_fd < 0) { + return NULL; + } + + return chttpd_log_new (name, log_fd); +} + +ChttpdLog* chttpd_log_ref (ChttpdLog* hlog) { + pthread_mutex_lock (&hlog->ref_mutex); + hlog->ref_count++; + pthread_mutex_unlock (&hlog->ref_mutex); + pthread_cond_broadcast (&hlog->ref_change); + return hlog; +} + +void chttpd_log_unref (ChttpdLog* hlog) { + pthread_mutex_lock (&hlog->ref_mutex); + hlog->ref_count--; + if (hlog->ref_count <= 0) { + /* No thread has a reference on hlog, so we are going to destory it */ + pthread_mutex_unlock (&hlog->ref_mutex); + pthread_mutex_destroy (&hlog->ref_mutex); + pthread_cond_destroy (&hlog->ref_change); + pthread_rwlock_destroy (&hlog->log_lock); + lbs_list_meta_free (hlog->log_data); + free (hlog); + } else { + pthread_mutex_unlock (&hlog->ref_mutex); + pthread_cond_broadcast (&hlog->ref_change); + } +} + +int chttpd_log_read_start (ChttpdLog* hlog) { + return pthread_rwlock_tryrdlock (&hlog->log_lock); +} + +void chttpd_log_read_stop (ChttpdLog* hlog) { + pthread_rwlock_unlock (&hlog->log_lock); +} + +size_t chttpd_log_write (ChttpdLog* hlog, const char* format, ...) { + va_list ap; + char* str; + size_t r; + + va_start (ap, format); + str = lbs_str_vprintf (format, ap); + va_end (ap); + + r = chttpd_log_write_str (hlog, str); + free (str); + return r; +} + +size_t chttpd_log_write_str (ChttpdLog* hlog, const char* string) { + time_t t; + struct tm tmd; + char* msg; + size_t r; + + time (&t); + localtime_r (&t, &tmd); + msg = lbs_str_printf ("%04d-%02d-%02d %02d:%02d:%02d %s[%ld]: %s\n", + tmd.tm_year + 1900, tmd.tm_mon + 1, tmd.tm_mday, + tmd.tm_hour, tmd.tm_min, tmd.tm_sec, + hlog->name, (long)(hlog->pid), string); + + pthread_rwlock_wrlock (&hlog->log_lock); + lbs_list_push_back (hlog->log_data, msg, NULL); + r = lbs_posix_write_all (hlog->log_fd, msg, 0); + pthread_rwlock_unlock (&hlog->log_lock); + + return r; +} diff --git a/hw4/chttpd/chttpd-log.h b/hw4/chttpd/chttpd-log.h new file mode 100644 index 0000000..ff57ad0 --- /dev/null +++ b/hw4/chttpd/chttpd-log.h @@ -0,0 +1,34 @@ +#ifndef CHTTPD_LOG_H +#define CHTTPD_LOG_H + +#include <l4list.h> + +#include <stddef.h> +#include <pthread.h> +#include <sys/types.h> + +typedef struct { + /* read-only */ + char* name; + pid_t pid; + int log_fd; + + /* read-write and lock */ + unsigned ref_count; + pthread_cond_t ref_change; + pthread_mutex_t ref_mutex; + LbsListMeta* log_data; + pthread_rwlock_t log_lock; +} ChttpdLog; + +ChttpdLog* chttpd_log_new (const char* name, int log_fd); +ChttpdLog* chttpd_log_new_file (const char* name, const char* file); +ChttpdLog* chttpd_log_ref (ChttpdLog* hlog); +void chttpd_log_unref (ChttpdLog* hlog); + +int chttpd_log_read_start (ChttpdLog* hlog); +void chttpd_log_read_stop (ChttpdLog* hlog); +size_t chttpd_log_write (ChttpdLog* hlog, const char* format, ...); +size_t chttpd_log_write_str (ChttpdLog* hlog, const char* string); + +#endif /* CHTTPD_LOG_H */ 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; +} diff --git a/hw4/chttpd/chttpd-server.h b/hw4/chttpd/chttpd-server.h new file mode 100644 index 0000000..1fd6f7c --- /dev/null +++ b/hw4/chttpd/chttpd-server.h @@ -0,0 +1,29 @@ +#ifndef CHTTPD_SERVER_H +#define CHTTPD_SERVER_H + +#include <l4list.h> +#include <pthread.h> +#include <stdbool.h> +#include <sys/socket.h> + +typedef struct { + /* read-only */ + int sockfd; + struct sockaddr_storage addr; + socklen_t addrlen; + int attr_admin : 1; + + /* read-write and lock */ + pthread_rwlock_t lock; + ChttpdLog* hlog; + LbsListMeta* conn; + int attr_close : 1; +} ChttpdServer; + +extern int chttpd_server_notify; +extern pthread_mutex_t chttpd_server_notify_mutex; + +void chttpd_server_ctor (void* server_generic, ChttpdLog* hlog, bool is_admin); +void chttpd_server_dtor (void* server_generic); + +#endif /* CHTTPD_SERVER_H */ diff --git a/hw4/chttpd/chttpd-socket.c b/hw4/chttpd/chttpd-socket.c new file mode 100644 index 0000000..cd1ff2e --- /dev/null +++ b/hw4/chttpd/chttpd-socket.c @@ -0,0 +1,120 @@ +/* b01902062 藍挺瑋 */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "chttpd-socket.h" + +#include <netdb.h> +#include <netinet/in.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/un.h> +#include <unistd.h> + +#define SOCKADDR(x) ((struct sockaddr*)(x)) +#define SOCKLEN(x) ((socklen_t)(x)) + +int chttpd_socket_new_unix (const char* path, + struct sockaddr_un* saddr, socklen_t* saddrlen) { + + struct sockaddr_un addr; + socklen_t addrlen = sizeof (addr); + size_t pathlen; + + memset (&addr, 0, sizeof (addr)); + pathlen = sizeof (addr) - offsetof (struct sockaddr_un, sun_path) - 1; + addr.sun_family = AF_UNIX; + strncpy (addr.sun_path, path, pathlen); + + int sockfd = socket (AF_UNIX, SOCK_STREAM, 0); + if (sockfd < 0) { + return CHTTPD_SOCKET_NEW_ERROR_SOCKET; + } + if (bind (sockfd, SOCKADDR (&addr), addrlen) < 0) { + close (sockfd); + return CHTTPD_SOCKET_NEW_ERROR_BIND; + } + + if (saddr != NULL) { + *saddr = addr; + } + if (saddrlen != NULL) { + *saddrlen = addrlen; + } + + return sockfd; +} + +int chttpd_socket_new_inet (const char* host, const char* service, int domain, + struct sockaddr* saddr, socklen_t* saddrlen, int* error) { + + if (service == NULL) { + service = "http"; + } + if (domain != AF_INET && domain != AF_INET6) { + domain = AF_UNSPEC; + } + + struct addrinfo hints, *result; + memset (&hints, 0, sizeof (hints)); + hints.ai_family = domain; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; + + int gai_error = getaddrinfo (host, service, &hints, &result); + if (gai_error != 0) { + if (error != NULL) { + *error = gai_error; + } + return CHTTPD_SOCKET_NEW_ERROR_GETADDRINFO; + } + + int sockfd = -1; + int bindrv = -1; + for (struct addrinfo* iter = result; iter != NULL; iter = iter->ai_next) { + sockfd = socket (iter->ai_family, SOCK_STREAM, 0); + if (sockfd < 0) { + continue; + } + + if (iter->ai_family == AF_INET6) { + setsockopt (sockfd, IPPROTO_IPV6, IPV6_V6ONLY, + &(int){ 1 }, sizeof (int)); + } + + bindrv = bind (sockfd, iter->ai_addr, iter->ai_addrlen); + if (bindrv < 0) { + close (sockfd); + } else { + if (saddr != NULL) { + switch (iter->ai_family) { + case AF_INET: + *(struct sockaddr_in*)(saddr) = + *(struct sockaddr_in*)(iter->ai_addr); + break; + case AF_INET6: + *(struct sockaddr_in6*)(saddr) = + *(struct sockaddr_in6*)(iter->ai_addr); + break; + default: + freeaddrinfo (result); + return CHTTPD_SOCKET_NEW_ERROR_UNEXPECTED; + } + } + if (saddrlen != NULL) { + *saddrlen = iter->ai_addrlen; + } + freeaddrinfo (result); + return sockfd; + } + } + + freeaddrinfo (result); + if (sockfd < 0) { + return CHTTPD_SOCKET_NEW_ERROR_SOCKET; + } + + return CHTTPD_SOCKET_NEW_ERROR_BIND; +} diff --git a/hw4/chttpd/chttpd-socket.h b/hw4/chttpd/chttpd-socket.h new file mode 100644 index 0000000..2ac05b6 --- /dev/null +++ b/hw4/chttpd/chttpd-socket.h @@ -0,0 +1,22 @@ +#ifndef CHTTPD_SOCKET_H +#define CHTTPD_SOCKET_H + +#include <l4common.h> + +#include <netinet/in.h> +#include <sys/un.h> + +#define CHTTPD_SOCKET_NEW_ERROR_GETADDRINFO -1 +#define CHTTPD_SOCKET_NEW_ERROR_SOCKET -2 +#define CHTTPD_SOCKET_NEW_ERROR_BIND -3 +#define CHTTPD_SOCKET_NEW_ERROR_UNEXPECTED -100 +int chttpd_socket_new_unix (const char* path, + struct sockaddr_un* saddr, socklen_t* saddrlen); +int chttpd_socket_new_inet (const char* host, const char* service, int domain, + struct sockaddr* saddr, socklen_t* saddrlen, int *e); + +#define CHTTPD_SOCKET_SOCKADDR_UN_SET_NULL(x) \ + (LBS_COMMON_CHECK_TYPE (x, struct sockaddr_un*)->sun_path \ + [sizeof (struct sockaddr_un) - offsetof (struct sockaddr_un, sun_path) - 1]) + +#endif /* CHTTPD_SOCKET_H */ |