summaryrefslogtreecommitdiffstats
path: root/hw4/chttpd/chttpd-server.c
diff options
context:
space:
mode:
authorLAN-TW <lantw44@gmail.com>2014-01-14 01:15:02 +0800
committerLAN-TW <lantw44@gmail.com>2014-01-14 01:15:02 +0800
commit5873050be326f8b645b994d8c5e8b266573a6e40 (patch)
tree46ec76ae4fddc68974fee549cc6d530d408bd044 /hw4/chttpd/chttpd-server.c
parentd045ff1806eb429a2c06203dfda165a21dad14ab (diff)
downloadsp2013-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.c279
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;
+}