diff options
Diffstat (limited to 'dpi/file.c')
-rw-r--r-- | dpi/file.c | 975 |
1 files changed, 975 insertions, 0 deletions
diff --git a/dpi/file.c b/dpi/file.c new file mode 100644 index 00000000..e1547c66 --- /dev/null +++ b/dpi/file.c @@ -0,0 +1,975 @@ +/* + * File: file.c :) + * + * Copyright (C) 2000 - 2004 Jorge Arellano Cid <jcid@dillo.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + */ + +/* + * Directory scanning is no longer streamed, but it gets sorted instead! + * Directory entries on top, files next. + * With new HTML layout. + */ + +#include <pthread.h> + +#include <ctype.h> /* for tolower */ +#include <errno.h> /* for errno */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/un.h> +#include <dirent.h> +#include <fcntl.h> +#include <time.h> +#include <signal.h> + +#include "../dpip/dpip.h" +#include "dpiutil.h" +#include "d_size.h" + +/* + * Debugging macros + */ +#define _MSG(...) +#define MSG(...) printf("[file dpi]: " __VA_ARGS__) + + +#define MAXNAMESIZE 30 +#define HIDE_DOTFILES TRUE + +enum { + FILE_OK, + FILE_NOT_FOUND, + FILE_NO_ACCESS +}; + +typedef struct { + char *full_path; + const char *filename; + off_t size; + mode_t mode; + time_t mtime; +} FileInfo; + +typedef struct { + char *dirname; + Dlist *flist; /* List of files and subdirectories (for sorting) */ +} DilloDir; + +typedef struct { + SockHandler *sh; + int status; + int old_style; + pthread_t thrID; + int done; +} ClientInfo; + +/* + * Forward references + */ +static const char *File_content_type(const char *filename); +static int File_get_file(ClientInfo *Client, + const char *filename, + struct stat *sb, + const char *orig_url); +static int File_get_dir(ClientInfo *Client, + const char *DirName, + const char *orig_url); + +/* + * Global variables + */ +static volatile int DPIBYE = 0; +static volatile int ThreadRunning = 0; +static int OLD_STYLE = 0; +/* A list for the clients we are serving */ +static Dlist *Clients; +/* a mutex for operations on clients */ +static pthread_mutex_t ClMut; + +/* + * Close a file descriptor, but handling EINTR + */ +static void File_close(int fd) +{ + while (close(fd) < 0 && errno == EINTR) + ; +} + +/* + * Detects 'Content-Type' when the server does not supply one. + * It uses the magic(5) logic from file(1). Currently, it + * only checks the few mime types that Dillo supports. + * + * 'Data' is a pointer to the first bytes of the raw data. + * (this is based on a_Misc_get_content_type_from_data()) + */ +static const char *File_get_content_type_from_data(void *Data, size_t Size) +{ + static const char *Types[] = { + "application/octet-stream", + "text/html", "text/plain", + "image/gif", "image/png", "image/jpeg", + }; + int Type = 0; + char *p = Data; + size_t i, non_ascci; + + _MSG("File_get_content_type_from_data:: Size = %d\n", Size); + + /* HTML try */ + for (i = 0; i < Size && isspace(p[i]); ++i); + if ((Size - i >= 5 && !dStrncasecmp(p+i, "<html", 5)) || + (Size - i >= 5 && !dStrncasecmp(p+i, "<head", 5)) || + (Size - i >= 6 && !dStrncasecmp(p+i, "<title", 6)) || + (Size - i >= 14 && !dStrncasecmp(p+i, "<!doctype html", 14)) || + /* this line is workaround for FTP through the Squid proxy */ + (Size - i >= 17 && !dStrncasecmp(p+i, "<!-- HTML listing", 17))) { + + Type = 1; + + /* Images */ + } else if (Size >= 4 && !dStrncasecmp(p, "GIF8", 4)) { + Type = 3; + } else if (Size >= 4 && !dStrncasecmp(p, "\x89PNG", 4)) { + Type = 4; + } else if (Size >= 2 && !dStrncasecmp(p, "\xff\xd8", 2)) { + /* JPEG has the first 2 bytes set to 0xffd8 in BigEndian - looking + * at the character representation should be machine independent. */ + Type = 5; + + /* Text */ + } else { + /* We'll assume "text/plain" if the set of chars above 127 is <= 10 + * in a 256-bytes sample. Better heuristics are welcomed! :-) */ + non_ascci = 0; + Size = MIN (Size, 256); + for (i = 0; i < Size; i++) + if ((uchar_t) p[i] > 127) + ++non_ascci; + if (Size == 256) { + Type = (non_ascci > 10) ? 0 : 2; + } else { + Type = (non_ascci > 0) ? 0 : 2; + } + } + + return (Types[Type]); +} + +/* + * Compare two FileInfo pointers + * This function is used for sorting directories + */ +static int File_comp(const FileInfo *f1, const FileInfo *f2) +{ + if (S_ISDIR(f1->mode)) { + if (S_ISDIR(f2->mode)) { + return strcmp(f1->filename, f2->filename); + } else { + return -1; + } + } else { + if (S_ISDIR(f2->mode)) { + return 1; + } else { + return strcmp(f1->filename, f2->filename); + } + } +} + +/* + * Allocate a DilloDir structure, set safe values in it and sort the entries. + */ +static DilloDir *File_dillodir_new(char *dirname) +{ + struct stat sb; + struct dirent *de; + DIR *dir; + DilloDir *Ddir; + FileInfo *finfo; + char *fname; + int dirname_len; + + if (!(dir = opendir(dirname))) + return NULL; + + Ddir = dNew(DilloDir, 1); + Ddir->dirname = dStrdup(dirname); + Ddir->flist = dList_new(512); + + dirname_len = strlen(Ddir->dirname); + + /* Scan every name and sort them */ + while ((de = readdir(dir)) != 0) { + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) + continue; /* skip "." and ".." */ + + if (HIDE_DOTFILES) { + /* Don't add hidden files or backup files to the list */ + if (de->d_name[0] == '.' || + de->d_name[0] == '#' || + (de->d_name[0] != '\0' && + de->d_name[strlen(de->d_name) - 1] == '~')) + continue; + } + + fname = dStrconcat(Ddir->dirname, de->d_name, NULL); + if (stat(fname, &sb) == -1) { + dFree(fname); + continue; /* ignore files we can't stat */ + } + + finfo = dNew(FileInfo, 1); + finfo->full_path = fname; + finfo->filename = fname + dirname_len; + finfo->size = sb.st_size; + finfo->mode = sb.st_mode; + finfo->mtime = sb.st_mtime; + + dList_append(Ddir->flist, finfo); + } + + closedir(dir); + + /* sort the entries */ + dList_sort(Ddir->flist, (dCompareFunc)File_comp); + + return Ddir; +} + +/* + * Deallocate a DilloDir structure. + */ +static void File_dillodir_free(DilloDir *Ddir) +{ + int i; + FileInfo *finfo; + + for (i = 0; i < dList_length(Ddir->flist); ++i) { + finfo = dList_nth_data(Ddir->flist, i); + dFree(finfo->full_path); + dFree(finfo); + } + + dList_free(Ddir->flist); + dFree(Ddir->dirname); + dFree(Ddir); +} + +/* + * Output the string for parent directory + */ +static void File_print_parent_dir(ClientInfo *Client, const char *dirname) +{ + if (strcmp(dirname, "/") != 0) { /* Not the root dir */ + char *p, *parent, *HUparent, *Uparent; + + parent = dStrdup(dirname); + /* cut trailing slash */ + parent[strlen(parent) - 1] = '\0'; + /* make 'parent' have the parent dir path */ + if ((p = strrchr(parent, '/'))) + *(p + 1) = '\0'; + + Uparent = Escape_uri_str(parent, NULL); + HUparent = Escape_html_str(Uparent); + sock_handler_printf(Client->sh, 0, + "<a href='file:%s'>Parent directory</a>", HUparent); + dFree(HUparent); + dFree(Uparent); + dFree(parent); + } +} + +/* + * Given a timestamp, output an HTML-formatted date string. + */ +static void File_print_mtime(ClientInfo *Client, time_t mtime) +{ + char *ds = ctime(&mtime); + + /* Month, day and {hour or year} */ + if (Client->old_style) { + sock_handler_printf(Client->sh, 0, " %.3s %.2s", ds + 4, ds + 8); + if (time(NULL) - mtime > 15811200) { + sock_handler_printf(Client->sh, 0, " %.4s", ds + 20); + } else { + sock_handler_printf(Client->sh, 0, " %.5s", ds + 11); + } + } else { + sock_handler_printf(Client->sh, 0, + "<td>%.3s %.2s %.5s", ds + 4, ds + 8, + /* (more than 6 months old) ? year : hour; */ + (time(NULL) - mtime > 15811200) ? ds + 20 : ds + 11); + } +} + +/* + * Return a HTML-line from file info. + */ +static void File_info2html(ClientInfo *Client, + FileInfo *finfo, const char *dirname, int n) +{ + int size; + char *sizeunits; + char namebuf[MAXNAMESIZE + 1]; + char *Uref, *HUref, *Hname; + const char *ref, *filecont, *name = finfo->filename; + + if (finfo->size <= 9999) { + size = finfo->size; + sizeunits = "bytes"; + } else if (finfo->size / 1024 <= 9999) { + size = finfo->size / 1024 + (finfo->size % 1024 >= 1024 / 2); + sizeunits = "Kb"; + } else { + size = finfo->size / 1048576 + (finfo->size % 1048576 >= 1048576 / 2); + sizeunits = "Mb"; + } + + /* we could note if it's a symlink... */ + if S_ISDIR (finfo->mode) { + filecont = "Directory"; + } else if (finfo->mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { + filecont = "Executable"; + } else { + filecont = File_content_type(finfo->full_path); + if (!filecont || !strcmp(filecont, "application/octet-stream")) + filecont = "unknown"; + } + + ref = name; + + if (strlen(name) > MAXNAMESIZE) { + memcpy(namebuf, name, MAXNAMESIZE - 3); + strcpy(namebuf + (MAXNAMESIZE - 3), "..."); + name = namebuf; + } + + /* escape problematic filenames */ + Uref = Escape_uri_str(ref, NULL); + HUref = Escape_html_str(Uref); + Hname = Escape_html_str(name); + + if (Client->old_style) { + char *dots = ".. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .."; + int ndots = MAXNAMESIZE - strlen(name); + sock_handler_printf(Client->sh, 0, + "%s<a href='%s'>%s</a>" + " %s" + " %-11s%4d %-5s", + S_ISDIR (finfo->mode) ? ">" : " ", HUref, Hname, + dots + 50 - (ndots > 0 ? ndots : 0), + filecont, size, sizeunits); + + } else { + sock_handler_printf(Client->sh, 0, + "<tr align=center %s><td>%s<td align=left><a href='%s'>%s</a>" + "<td>%s<td>%d %s", + (n & 1) ? "bgcolor=#dcdcdc" : "", + S_ISDIR (finfo->mode) ? ">" : " ", HUref, Hname, + filecont, size, sizeunits); + } + File_print_mtime(Client, finfo->mtime); + sock_handler_write_str(Client->sh, 0, "\n"); + + dFree(Hname); + dFree(HUref); + dFree(Uref); +} + +/* + * Read a local directory and translate it to html. + */ +static void File_transfer_dir(ClientInfo *Client, + DilloDir *Ddir, const char *orig_url) +{ + int n; + char *d_cmd, *Hdirname, *Udirname, *HUdirname; + + /* Send DPI header */ + d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", orig_url); + sock_handler_write_str(Client->sh, 1, d_cmd); + dFree(d_cmd); + + /* Send page title */ + Udirname = Escape_uri_str(Ddir->dirname, NULL); + HUdirname = Escape_html_str(Udirname); + Hdirname = Escape_html_str(Ddir->dirname); + + sock_handler_printf(Client->sh, 0, + "Content-Type: text/html\n\n" + "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n" + "<HTML>\n<HEAD>\n <BASE href='file:%s'>\n" + " <TITLE>file:%s</TITLE>\n</HEAD>\n" + "<BODY><H1>Directory listing of %s</H1>\n", + HUdirname, Hdirname, Hdirname); + dFree(Hdirname); + dFree(HUdirname); + dFree(Udirname); + + if (Client->old_style) { + sock_handler_write_str(Client->sh, 0, "<pre>\n"); + } + + /* Output the parent directory */ + File_print_parent_dir(Client, Ddir->dirname); + + /* HTML style toggle */ + sock_handler_write_str(Client->sh, 0, + " <a href='dpi:/file/toggle'>%</a>\n"); + + if (dList_length(Ddir->flist)) { + if (Client->old_style) { + sock_handler_write_str(Client->sh, 0, "\n\n"); + } else { + sock_handler_write_str(Client->sh, 0, + "<br><br>\n" + "<table border=0 cellpadding=1 cellspacing=0" + " bgcolor=#E0E0E0 width=100%>\n" + "<tr align=center>\n" + "<td>\n" + "<td width=60%><b>Filename</b>" + "<td><b>Type</b>" + "<td><b>Size</b>" + "<td><b>Modified at</b>\n"); + } + } else { + sock_handler_write_str(Client->sh, 0, "<br><br>Directory is empty..."); + } + + /* Output entries */ + for (n = 0; n < dList_length(Ddir->flist); ++n) { + File_info2html(Client, dList_nth_data(Ddir->flist,n),Ddir->dirname,n+1); + } + + if (dList_length(Ddir->flist)) { + if (Client->old_style) { + sock_handler_write_str(Client->sh, 0, "</pre>\n"); + } else { + sock_handler_write_str(Client->sh, 0, "</table>\n"); + } + } + + sock_handler_write_str(Client->sh, 0, "</BODY></HTML>\n"); +} + +/* + * Return a content type based on the extension of the filename. + */ +static const char *File_ext(const char *filename) +{ + char *e; + + if (!(e = strrchr(filename, '.'))) + return NULL; + + e++; + + if (!dStrcasecmp(e, "gif")) { + return "image/gif"; + } else if (!dStrcasecmp(e, "jpg") || + !dStrcasecmp(e, "jpeg")) { + return "image/jpeg"; + } else if (!dStrcasecmp(e, "png")) { + return "image/png"; + } else if (!dStrcasecmp(e, "html") || + !dStrcasecmp(e, "htm") || + !dStrcasecmp(e, "shtml")) { + return "text/html"; + } else { + return NULL; + } +} + +/* + * Based on the extension, return the content_type for the file. + * (if there's no extension, analyze the data and try to figure it out) + */ +static const char *File_content_type(const char *filename) +{ + int fd; + struct stat sb; + const char *ct; + char buf[256]; + ssize_t buf_size; + + if (!(ct = File_ext(filename))) { + /* everything failed, let's analyze the data... */ + if ((fd = open(filename, O_RDONLY | O_NONBLOCK)) != -1) { + if ((buf_size = read(fd, buf, 256)) == 256 ) { + ct = File_get_content_type_from_data(buf, (size_t)buf_size); + + } else if (stat(filename, &sb) != -1 && + buf_size > 0 && buf_size == sb.st_size) { + ct = File_get_content_type_from_data(buf, (size_t)buf_size); + } + File_close(fd); + } + } + + return ct; +} + +/* + * Try to stat the file and determine if it's readable. + */ +static void File_get(ClientInfo *Client, const char *filename, + const char *orig_url) +{ + int res; + struct stat sb; + char *d_cmd; + Dstr *ds = NULL; + + if (stat(filename, &sb) != 0) { + /* stat failed, prepare a file-not-found error. */ + res = FILE_NOT_FOUND; + } else if (S_ISDIR(sb.st_mode)) { + /* set up for reading directory */ + res = File_get_dir(Client, filename, orig_url); + } else { + /* set up for reading a file */ + res = File_get_file(Client, filename, &sb, orig_url); + } + + if (res == FILE_NOT_FOUND) { + ds = dStr_sized_new(128); + dStr_sprintf(ds, "%s Not Found: %s", + S_ISDIR(sb.st_mode) ? "Directory" : "File", filename); + } else if (res == FILE_NO_ACCESS) { + ds = dStr_sized_new(128); + dStr_sprintf(ds, "Access denied to %s: %s", + S_ISDIR(sb.st_mode) ? "Directory" : "File", filename); + } + if (ds) { + d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s","send_status_message",ds->str); + sock_handler_write_str(Client->sh, 1, d_cmd); + dFree(d_cmd); + dStr_free(ds, 1); + } +} + +/* + * + */ +static int File_get_dir(ClientInfo *Client, + const char *DirName, const char *orig_url) +{ + Dstr *ds_dirname; + DilloDir *Ddir; + + /* Let's make sure this directory url has a trailing slash */ + ds_dirname = dStr_new(DirName); + if (ds_dirname->str[ds_dirname->len - 1] != '/') + dStr_append(ds_dirname, "/"); + + /* Let's get a structure ready for transfer */ + Ddir = File_dillodir_new(ds_dirname->str); + dStr_free(ds_dirname, TRUE); + if (Ddir) { + File_transfer_dir(Client, Ddir, orig_url); + File_dillodir_free(Ddir); + return FILE_OK; + } else + return FILE_NO_ACCESS; +} + +/* + * Send the MIME content/type and then send the file itself. + */ +static int File_get_file(ClientInfo *Client, + const char *filename, + struct stat *sb, + const char *orig_url) +{ +#define LBUF 16*1024 + + const char *ct; + char buf[LBUF], *d_cmd; + int fd, st; + + if ((fd = open(filename, O_RDONLY | O_NONBLOCK)) < 0) + return FILE_NO_ACCESS; + + /* Content-Type info is based on filename extension. If there's no + * known extension, then we do data sniffing. If this doesn't lead + * to a conclusion, "application/octet-stream" is sent. + */ + if (!(ct = File_content_type(filename))) + ct = "application/octet-stream"; + + /* Send DPI command */ + d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", orig_url); + sock_handler_write_str(Client->sh, 1, d_cmd); + dFree(d_cmd); + + /* Send HTTP stream */ + sock_handler_printf(Client->sh, 0, + "Content-Type: %s\n" + "Content-length: %ld\n\n", + ct, sb->st_size); + + /* Send raw file contents */ + do { + if ((st = read(fd, buf, LBUF)) > 0) { + if (sock_handler_write(Client->sh, 0, buf, (size_t)st) != 0) + break; + } else if (st < 0) { + perror("[read]"); + if (errno == EINTR || errno == EAGAIN) + continue; + } + } while (st > 0); + + /* todo: It may be better to send an error report to dillo instead of + * calling exit() */ + if (st == -1) { + MSG("ERROR while reading from file \"%s\", error was \"%s\"\n", + filename, strerror(errno)); + exit(1); + } + + File_close(fd); + return FILE_OK; +} + +/* + * Given an hex octet (e3, 2F, 20), return the corresponding + * character if the octet is valid, and -1 otherwise + */ +static int File_parse_hex_octet(const char *s) +{ + int hex_value; + char *tail, hex[3]; + + if ((hex[0] = s[0]) && (hex[1] = s[1])) { + hex[2] = 0; + hex_value = strtol(hex, &tail, 16); + if (tail - hex == 2) + return hex_value; + } + + return -1; +} + +/* + * Make a file URL into a human (and machine) readable path. + * The idea is to always have a path that starts with only one slash. + * Embedded slashes are ignored. + */ +static char *File_normalize_path(const char *orig) +{ + char *str = (char *) orig, *basename = NULL, *ret = NULL, *p; + + /* Make sure the string starts with "file:/" */ + if (strncmp(str, "file:/", 5) != 0) + return ret; + str += 5; + + /* Skip "localhost" */ + if (dStrncasecmp(str, "//localhost/", 12) == 0) + str += 11; + + /* Skip packed slashes, and leave just one */ + while (str[0] == '/' && str[1] == '/') + str++; + + { + int i, val; + Dstr *ds = dStr_sized_new(32); + dStr_sprintf(ds, "%s%s%s", + basename ? basename : "", + basename ? "/" : "", + str); + dFree(basename); + + /* Parse possible hexadecimal octets in the URI path */ + for (i = 0; ds->str[i]; ++i) { + if (ds->str[i] == '%' && + (val = File_parse_hex_octet(ds->str + i+1)) != -1) { + ds->str[i] = val; + dStr_erase(ds, i+1, 2); + } + } + /* Remove the fragment if not a part of the filename */ + if ((p = strrchr(ds->str, '#')) != NULL && access(ds->str, F_OK) != 0) + dStr_truncate(ds, p - ds->str); + ret = ds->str; + dStr_free(ds, 0); + } + + return ret; +} + +/* + * Set the style flag and ask for a reload, so it shows inmediatly. + */ +static void File_toggle_html_style(ClientInfo *Client) +{ + char *d_cmd; + + OLD_STYLE = !OLD_STYLE; + d_cmd = a_Dpip_build_cmd("cmd=%s", "reload_request"); + sock_handler_write_str(Client->sh, 1, d_cmd); + dFree(d_cmd); +} + +/* + * Perform any necessary cleanups upon abnormal termination + */ +static void termination_handler(int signum) +{ + exit(signum); +} + + +/* Client handling ----------------------------------------------------------*/ + +/* + * Add a new client to the list. + */ +static ClientInfo *File_add_client(int sock_fd) +{ + ClientInfo *NewClient; + + NewClient = dNew(ClientInfo, 1); + NewClient->sh = sock_handler_new(sock_fd, sock_fd, 8*1024); + NewClient->status = 0; + NewClient->done = 0; + NewClient->old_style = OLD_STYLE; + pthread_mutex_lock(&ClMut); + dList_append(Clients, NewClient); + pthread_mutex_unlock(&ClMut); + return NewClient; +} + +/* + * Get client record by number + */ +static void *File_get_client_n(uint_t n) +{ + void *client; + + pthread_mutex_lock(&ClMut); + client = dList_nth_data(Clients, n); + pthread_mutex_unlock(&ClMut); + + return client; +} + +/* + * Remove a client from the list. + */ +static void File_remove_client_n(uint_t n) +{ + ClientInfo *Client; + + pthread_mutex_lock(&ClMut); + Client = dList_nth_data(Clients, n); + dList_remove(Clients, (void *)Client); + pthread_mutex_unlock(&ClMut); + + _MSG("Closing Socket Handler\n"); + sock_handler_close(Client->sh); + sock_handler_free(Client->sh); + dFree(Client); +} + +/* + * Return the number of clients. + */ +static int File_num_clients(void) +{ + uint_t n; + + pthread_mutex_lock(&ClMut); + n = dList_length(Clients); + pthread_mutex_unlock(&ClMut); + + return n; +} + +/* + * Serve this client. + * (this function runs on its own thread) + */ +static void *File_serve_client(void *data) +{ + char *dpip_tag, *cmd = NULL, *url = NULL, *path; + ClientInfo *Client = data; + + /* Read the dpi command */ + dpip_tag = sock_handler_read(Client->sh); + MSG("dpip_tag={%s}\n", dpip_tag); + + if (dpip_tag) { + cmd = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "cmd"); + if (cmd) { + if (strcmp(cmd, "DpiBye") == 0) { + DPIBYE = 1; + } else { + url = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "url"); + if (!url) + MSG("file.dpi:: Failed to parse 'url'\n"); + } + } + } + dFree(cmd); + dFree(dpip_tag); + + if (!DPIBYE && url) { + _MSG("url = '%s'\n", url); + + path = File_normalize_path(url); + if (path) { + _MSG("path = '%s'\n", path); + File_get(Client, path, url); + } else if (strcmp(url, "dpi:/file/toggle") == 0) { + File_toggle_html_style(Client); + } else { + MSG("ERROR: URL path was %s\n", url); + } + dFree(path); + } + dFree(url); + + /* flag the the transfer finished */ + Client->done = 1; + + return NULL; +} + +/* + * Serve the client queue. + * (this function runs on its own thread) + */ +static void *File_serve_clients(void *client) +{ + /* switch to detached state */ + pthread_detach(pthread_self()); + + while (File_num_clients()) { + client = File_get_client_n((uint_t)0); + File_serve_client(client); + File_remove_client_n((uint_t)0); + } + ThreadRunning = 0; + + return NULL; +} + +/* --------------------------------------------------------------------------*/ + +/* + * Check a fd for activity, with a max timeout. + * return value: 0 if timeout, 1 if input available, -1 if error. + */ +int File_check_fd(int filedes, unsigned int seconds) +{ + int st; + fd_set set; + struct timeval timeout; + + /* Initialize the file descriptor set. */ + FD_ZERO (&set); + FD_SET (filedes, &set); + + /* Initialize the timeout data structure. */ + timeout.tv_sec = seconds; + timeout.tv_usec = 0; + + do { + st = select(FD_SETSIZE, &set, NULL, NULL, &timeout); + } while (st == -1 && errno == EINTR); + + return st; +} + + +int main(void) +{ + ClientInfo *NewClient; + struct sockaddr_un spun; + int temp_sock_descriptor; + socklen_t address_size; + int c_st, st = 1; + uint_t i; + + /* Arrange the cleanup function for abnormal terminations */ + if (signal (SIGINT, termination_handler) == SIG_IGN) + signal (SIGINT, SIG_IGN); + if (signal (SIGHUP, termination_handler) == SIG_IGN) + signal (SIGHUP, SIG_IGN); + if (signal (SIGTERM, termination_handler) == SIG_IGN) + signal (SIGTERM, SIG_IGN); + + MSG("(v.1) accepting connections...\n"); + + /* initialize mutex */ + pthread_mutex_init(&ClMut, NULL); + + /* initialize Clients list */ + Clients = dList_new(512); + + /* some OSes may need this... */ + address_size = sizeof(struct sockaddr_un); + + /* start the service loop */ + while (!DPIBYE) { + /* wait for a connection */ + do { + c_st = File_check_fd(STDIN_FILENO, 1); + } while (c_st == 0 && !DPIBYE); + if (c_st < 0) { + perror("[select]"); + break; + } + if (DPIBYE) + break; + + temp_sock_descriptor = + accept(STDIN_FILENO, (struct sockaddr *)&spun, &address_size); + + if (temp_sock_descriptor == -1) { + perror("[accept]"); + break; + } + + /* Create and initialize a new client */ + NewClient = File_add_client(temp_sock_descriptor); + + if (!ThreadRunning) { + ThreadRunning = 1; + /* Serve the client from a thread (avoids deadlocks) */ + if (pthread_create(&NewClient->thrID, NULL, + File_serve_clients, NewClient) != 0) { + perror("[pthread_create]"); + ThreadRunning = 0; + break; + } + } + } + + /* todo: handle a running thread better. */ + for (i = 0; i < 5 && ThreadRunning; ++i) { + MSG("sleep i=%u", i); + sleep(i); + } + + if (DPIBYE) + st = 0; + return st; +} + |