Mercurial > pt1.oyama
diff src/ushare.c @ 125:e413158cae13
Add ushare project files.
author | naoyan@johnstown.minaminoshima.org |
---|---|
date | Sun, 03 Oct 2010 11:35:19 +0900 |
parents | |
children | 5dcaf3785ebe |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ushare.c Sun Oct 03 11:35:19 2010 +0900 @@ -0,0 +1,911 @@ +/* + * ushare.c : GeeXboX uShare UPnP Media Server. + * Originally developped for the GeeXboX project. + * Parts of the code are originated from GMediaServer from Oskar Liljeblad. + * Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <stdio.h> +#include <signal.h> +#include <string.h> +#include <stdlib.h> +#include <stdarg.h> +#include <unistd.h> +#include <errno.h> +#include <getopt.h> + +#if (defined(BSD) || defined(__FreeBSD__) || defined(__APPLE__)) +#include <sys/socket.h> +#include <sys/sysctl.h> +#include <net/if_dl.h> +#endif + +#if (defined(__APPLE__)) +#include <net/route.h> +#endif + +#include <net/if.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <stdbool.h> +#include <fcntl.h> + +#ifdef HAVE_IFADDRS_H +#include <ifaddrs.h> +#endif + +#if (defined(__unix__) || defined(unix)) && !defined(USG) +#include <sys/param.h> +#endif + +#include <upnp/upnp.h> +#include <upnp/upnptools.h> + +#if (defined(HAVE_SETLOCALE) && defined(CONFIG_NLS)) +# include <locale.h> +#endif + +#include "config.h" +#include "ushare.h" +#include "services.h" +#include "http.h" +#include "metadata.h" +#include "util_iconv.h" +#include "content.h" +#include "cfgparser.h" +#include "gettext.h" +#include "trace.h" +#include "buffer.h" +#include "ctrl_telnet.h" +#include "recpt1.h" + +struct ushare_t *ut = NULL; + +static struct ushare_t * ushare_new (void) + __attribute__ ((malloc)); + +static struct ushare_t * +ushare_new (void) +{ + struct ushare_t *ut = (struct ushare_t *) malloc (sizeof (struct ushare_t)); + if (!ut) + return NULL; + + ut->name = strdup (DEFAULT_USHARE_NAME); + ut->interface = strdup (DEFAULT_USHARE_IFACE); + ut->model_name = strdup (DEFAULT_USHARE_NAME); + ut->contentlist = NULL; + ut->rb = rbinit (rb_compare, NULL); + ut->root_entry = NULL; + ut->nr_entries = 0; + ut->starting_id = STARTING_ENTRY_ID_DEFAULT; + ut->init = 0; + ut->dev = 0; + ut->udn = NULL; + ut->ip = NULL; + ut->port = 0; /* Randomly attributed by libupnp */ + ut->telnet_port = CTRL_TELNET_PORT; + ut->presentation = NULL; + ut->use_presentation = true; + ut->use_telnet = true; +#ifdef HAVE_DLNA + ut->dlna_enabled = false; + ut->dlna = NULL; + ut->dlna_flags = DLNA_ORG_FLAG_STREAMING_TRANSFER_MODE | + DLNA_ORG_FLAG_BACKGROUND_TRANSFERT_MODE | + DLNA_ORG_FLAG_CONNECTION_STALL | + DLNA_ORG_FLAG_DLNA_V15; +#endif /* HAVE_DLNA */ + ut->xbox360 = false; + ut->verbose = false; + ut->daemon = false; + ut->override_iconv_err = false; + ut->cfg_file = NULL; + + pthread_mutex_init (&ut->termination_mutex, NULL); + pthread_cond_init (&ut->termination_cond, NULL); + + return ut; +} + +static void +ushare_free (struct ushare_t *ut) +{ + if (!ut) + return; + + if (ut->name) + free (ut->name); + if (ut->interface) + free (ut->interface); + if (ut->model_name) + free (ut->model_name); + if (ut->contentlist) + content_free (ut->contentlist); + if (ut->rb) + rbdestroy (ut->rb); + if (ut->root_entry) + upnp_entry_free (ut, ut->root_entry); + if (ut->udn) + free (ut->udn); + if (ut->ip) + free (ut->ip); + if (ut->presentation) + buffer_free (ut->presentation); +#ifdef HAVE_DLNA + if (ut->dlna_enabled) + { + if (ut->dlna) + dlna_uninit (ut->dlna); + ut->dlna = NULL; + } +#endif /* HAVE_DLNA */ + if (ut->cfg_file) + free (ut->cfg_file); + + pthread_cond_destroy (&ut->termination_cond); + pthread_mutex_destroy (&ut->termination_mutex); + + free (ut); +} + +static void +ushare_signal_exit (void) +{ + pthread_mutex_lock (&ut->termination_mutex); + pthread_cond_signal (&ut->termination_cond); + pthread_mutex_unlock (&ut->termination_mutex); +} + +static void +handle_action_request (struct Upnp_Action_Request *request) +{ + struct service_t *service; + struct service_action_t *action; + char val[256]; + uint32_t ip; + + if (!request || !ut) + return; + + if (request->ErrCode != UPNP_E_SUCCESS) + return; + + if (strcmp (request->DevUDN + 5, ut->udn)) + return; + + ip = request->CtrlPtIPAddr.s_addr; + ip = ntohl (ip); + sprintf (val, "%d.%d.%d.%d", + (ip >> 24) & 0xFF, (ip >> 16) & 0xFF, (ip >> 8) & 0xFF, ip & 0xFF); + + if (ut->verbose) + { + DOMString str = ixmlPrintDocument (request->ActionRequest); + log_verbose ("***************************************************\n"); + log_verbose ("** New Action Request **\n"); + log_verbose ("***************************************************\n"); + log_verbose ("ServiceID: %s\n", request->ServiceID); + log_verbose ("ActionName: %s\n", request->ActionName); + log_verbose ("CtrlPtIP: %s\n", val); + log_verbose ("Action Request:\n%s\n", str); + ixmlFreeDOMString (str); + } + + if (find_service_action (request, &service, &action)) + { + struct action_event_t event; + + event.request = request; + event.status = true; + event.service = service; + + if (action->function (&event) && event.status) + request->ErrCode = UPNP_E_SUCCESS; + + if (ut->verbose) + { + DOMString str = ixmlPrintDocument (request->ActionResult); + log_verbose ("Action Result:\n%s", str); + log_verbose ("***************************************************\n"); + log_verbose ("\n"); + ixmlFreeDOMString (str); + } + + return; + } + + if (service) /* Invalid Action name */ + strcpy (request->ErrStr, "Unknown Service Action"); + else /* Invalid Service name */ + strcpy (request->ErrStr, "Unknown Service ID"); + + request->ActionResult = NULL; + request->ErrCode = UPNP_SOAP_E_INVALID_ACTION; +} + +static int +device_callback_event_handler (Upnp_EventType type, void *event, + void *cookie __attribute__((unused))) +{ + switch (type) + { + case UPNP_CONTROL_ACTION_REQUEST: + handle_action_request ((struct Upnp_Action_Request *) event); + break; + case UPNP_CONTROL_ACTION_COMPLETE: + case UPNP_EVENT_SUBSCRIPTION_REQUEST: + case UPNP_CONTROL_GET_VAR_REQUEST: + break; + default: + break; + } + + return 0; +} + +static int +finish_upnp (struct ushare_t *ut) +{ + if (!ut) + return -1; + + log_info (_("Stopping UPnP Service ...\n")); + UpnpUnRegisterRootDevice (ut->dev); + UpnpFinish (); + + return UPNP_E_SUCCESS; +} + +static int +init_upnp (struct ushare_t *ut) +{ + char *description = NULL; + int res; + size_t len; + + if (!ut || !ut->name || !ut->udn || !ut->ip) + return -1; + +#ifdef HAVE_DLNA + if (ut->dlna_enabled) + { + len = 0; + description = + dlna_dms_description_get (ut->name, + "GeeXboX Team", + "http://ushare.geexbox.org/", + "uShare : DLNA Media Server", + ut->model_name, + "001", + "http://ushare.geexbox.org/", + "USHARE-01", + ut->udn, + "/web/ushare.html", + "/web/cms.xml", + "/web/cms_control", + "/web/cms_event", + "/web/cds.xml", + "/web/cds_control", + "/web/cds_event"); + if (!description) + return -1; + } + else + { +#endif /* HAVE_DLNA */ + len = strlen (UPNP_DESCRIPTION) + strlen (ut->name) + + strlen (ut->model_name) + strlen (ut->udn) + 1; + description = (char *) malloc (len * sizeof (char)); + memset (description, 0, len); + sprintf (description, UPNP_DESCRIPTION, ut->name, ut->model_name, ut->udn); +#ifdef HAVE_DLNA + } +#endif /* HAVE_DLNA */ + + log_info (_("Initializing UPnP subsystem ...\n")); + res = UpnpInit (ut->ip, ut->port); + if (res != UPNP_E_SUCCESS) + { + log_error (_("Cannot initialize UPnP subsystem\n")); + return -1; + } + + if (UpnpSetMaxContentLength (UPNP_MAX_CONTENT_LENGTH) != UPNP_E_SUCCESS) + log_info (_("Could not set Max content UPnP\n")); + + if (ut->xbox360) + log_info (_("Starting in XboX 360 compliant profile ...\n")); + +#ifdef HAVE_DLNA + if (ut->dlna_enabled) + { + log_info (_("Starting in DLNA compliant profile ...\n")); + ut->dlna = dlna_init (); + dlna_set_verbosity (ut->dlna, ut->verbose ? 1 : 0); + dlna_set_extension_check (ut->dlna, 1); + dlna_register_all_media_profiles (ut->dlna); + } +#endif /* HAVE_DLNA */ + + ut->port = UpnpGetServerPort(); + log_info (_("UPnP MediaServer listening on %s:%d\n"), + UpnpGetServerIpAddress (), ut->port); + + UpnpEnableWebserver (TRUE); + + res = UpnpSetVirtualDirCallbacks (&virtual_dir_callbacks); + if (res != UPNP_E_SUCCESS) + { + log_error (_("Cannot set virtual directory callbacks\n")); + free (description); + return -1; + } + + res = UpnpAddVirtualDir (VIRTUAL_DIR); + if (res != UPNP_E_SUCCESS) + { + log_error (_("Cannot add virtual directory for web server\n")); + free (description); + return -1; + } + + res = UpnpRegisterRootDevice2 (UPNPREG_BUF_DESC, description, 0, 1, + device_callback_event_handler, + NULL, &(ut->dev)); + if (res != UPNP_E_SUCCESS) + { + log_error (_("Cannot register UPnP device\n")); + free (description); + return -1; + } + + res = UpnpUnRegisterRootDevice (ut->dev); + if (res != UPNP_E_SUCCESS) + { + log_error (_("Cannot unregister UPnP device\n")); + free (description); + return -1; + } + + res = UpnpRegisterRootDevice2 (UPNPREG_BUF_DESC, description, 0, 1, + device_callback_event_handler, + NULL, &(ut->dev)); + if (res != UPNP_E_SUCCESS) + { + log_error (_("Cannot register UPnP device\n")); + free (description); + return -1; + } + + log_info (_("Sending UPnP advertisement for device ...\n")); + UpnpSendAdvertisement (ut->dev, 1800); + + log_info (_("Listening for control point connections ...\n")); + + if (description) + free (description); + + return 0; +} + +static bool +has_iface (char *interface) +{ +#ifdef HAVE_IFADDRS_H + struct ifaddrs *itflist, *itf; + + if (!interface) + return false; + + if (getifaddrs (&itflist) < 0) + { + perror ("getifaddrs"); + return false; + } + + itf = itflist; + while (itf) + { + if ((itf->ifa_flags & IFF_UP) + && !strncmp (itf->ifa_name, interface, IFNAMSIZ)) + { + log_error (_("Interface %s is down.\n"), interface); + log_error (_("Recheck uShare's configuration and try again !\n")); + freeifaddrs (itflist); + return true; + } + itf = itf->ifa_next; + } + + freeifaddrs (itf); +#else + int sock, i, n; + struct ifconf ifc; + struct ifreq ifr; + char buff[8192]; + + if (!interface) + return false; + + /* determine UDN according to MAC address */ + sock = socket (AF_INET, SOCK_STREAM, 0); + if (sock < 0) + { + perror ("socket"); + return false; + } + + /* get list of available interfaces */ + ifc.ifc_len = sizeof (buff); + ifc.ifc_buf = buff; + + if (ioctl (sock, SIOCGIFCONF, &ifc) < 0) + { + perror ("ioctl"); + close (sock); + return false; + } + + n = ifc.ifc_len / sizeof (struct ifreq); + for (i = n - 1 ; i >= 0 ; i--) + { + ifr = ifc.ifc_req[i]; + + if (strncmp (ifr.ifr_name, interface, IFNAMSIZ)) + continue; + + if (ioctl (sock, SIOCGIFFLAGS, &ifr) < 0) + { + perror ("ioctl"); + close (sock); + return false; + } + + if (!(ifr.ifr_flags & IFF_UP)) + { + /* interface is down */ + log_error (_("Interface %s is down.\n"), interface); + log_error (_("Recheck uShare's configuration and try again !\n")); + close (sock); + return false; + } + + /* found right interface */ + close (sock); + return true; + } + close (sock); +#endif + + log_error (_("Can't find interface %s.\n"),interface); + log_error (_("Recheck uShare's configuration and try again !\n")); + + return false; +} + +static char * +create_udn (char *interface) +{ + int sock = -1; + char *buf; + unsigned char *ptr; + +#if (defined(BSD) || defined(__FreeBSD__) || defined(__APPLE__)) + int mib[6]; + size_t len; + struct if_msghdr *ifm; + struct sockaddr_dl *sdl; +#else /* Linux */ + struct ifreq ifr; +#endif + + if (!interface) + return NULL; + +#if (defined(BSD) || defined(__FreeBSD__) || defined(__APPLE__)) + mib[0] = CTL_NET; + mib[1] = AF_ROUTE; + mib[2] = 0; + mib[3] = AF_LINK; + mib[4] = NET_RT_IFLIST; + + mib[5] = if_nametoindex (interface); + if (mib[5] == 0) + { + perror ("if_nametoindex"); + return NULL; + } + + if (sysctl (mib, 6, NULL, &len, NULL, 0) < 0) + { + perror ("sysctl"); + return NULL; + } + + buf = malloc (len); + if (sysctl (mib, 6, buf, &len, NULL, 0) < 0) + { + perror ("sysctl"); + return NULL; + } + + ifm = (struct if_msghdr *) buf; + sdl = (struct sockaddr_dl*) (ifm + 1); + ptr = (unsigned char *) LLADDR (sdl); +#else /* Linux */ + /* determine UDN according to MAC address */ + sock = socket (AF_INET, SOCK_STREAM, 0); + if (sock < 0) + { + perror ("socket"); + return NULL; + } + + strcpy (ifr.ifr_name, interface); + strcpy (ifr.ifr_hwaddr.sa_data, ""); + + if (ioctl (sock, SIOCGIFHWADDR, &ifr) < 0) + { + perror ("ioctl"); + return NULL; + } + + buf = (char *) malloc (64 * sizeof (char)); + memset (buf, 0, 64); + ptr = (unsigned char *) ifr.ifr_hwaddr.sa_data; +#endif /* (defined(BSD) || defined(__FreeBSD__)) */ + + snprintf (buf, 64, "%s-%02x%02x%02x%02x%02x%02x", DEFAULT_UUID, + (ptr[0] & 0377), (ptr[1] & 0377), (ptr[2] & 0377), + (ptr[3] & 0377), (ptr[4] & 0377), (ptr[5] & 0377)); + + if (sock) + close (sock); + + return buf; +} + +static char * +get_iface_address (char *interface) +{ + int sock; + uint32_t ip; + struct ifreq ifr; + char *val; + + if (!interface) + return NULL; + + /* determine UDN according to MAC address */ + sock = socket (AF_INET, SOCK_STREAM, 0); + if (sock < 0) + { + perror ("socket"); + return NULL; + } + + strcpy (ifr.ifr_name, interface); + ifr.ifr_addr.sa_family = AF_INET; + + if (ioctl (sock, SIOCGIFADDR, &ifr) < 0) + { + perror ("ioctl"); + close (sock); + return NULL; + } + + val = (char *) malloc (16 * sizeof (char)); + ip = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr; + ip = ntohl (ip); + sprintf (val, "%d.%d.%d.%d", + (ip >> 24) & 0xFF, (ip >> 16) & 0xFF, (ip >> 8) & 0xFF, ip & 0xFF); + + close (sock); + + return val; +} + +static int +restart_upnp (struct ushare_t *ut) +{ + finish_upnp (ut); + + if (ut->udn) + free (ut->udn); + ut->udn = create_udn (ut->interface); + if (!ut->udn) + return -1; + + if (ut->ip) + free (ut->ip); + ut->ip = get_iface_address (ut->interface); + if (!ut->ip) + return -1; + + return (init_upnp (ut)); +} + +static void +UPnPBreak (int s __attribute__ ((unused))) +{ + ushare_signal_exit (); +} + +static void +reload_config (int s __attribute__ ((unused))) +{ + struct ushare_t *ut2; + bool reload = false; + + log_info (_("Reloading configuration...\n")); + + ut2 = ushare_new (); + if (!ut || !ut2) + return; + + if (parse_config_file (ut2) < 0) + return; + + if (ut->name && strcmp (ut->name, ut2->name)) + { + free (ut->name); + ut->name = ut2->name; + ut2->name = NULL; + reload = true; + } + + if (ut->interface && strcmp (ut->interface, ut2->interface)) + { + if (!has_iface (ut2->interface)) + { + ushare_free (ut2); + raise (SIGINT); + } + else + { + free (ut->interface); + ut->interface = ut2->interface; + ut2->interface = NULL; + reload = true; + } + } + + if (ut->port != ut2->port) + { + ut->port = ut2->port; + reload = true; + } + + if (reload) + { + if (restart_upnp (ut) < 0) + { + ushare_free (ut2); + raise (SIGINT); + } + } + + if (ut->contentlist) + content_free (ut->contentlist); + ut->contentlist = ut2->contentlist; + ut2->contentlist = NULL; + ushare_free (ut2); + + if (ut->contentlist) + { + free_metadata_list (ut); + build_metadata_list (ut); + } + else + { + log_error (_("Error: no content directory to be shared.\n")); + raise (SIGINT); + } +} + +inline void +display_headers (void) +{ + printf (_("%s (version %s), a lightweight UPnP A/V and DLNA Media Server.\n"), + PACKAGE_NAME, VERSION); + printf (_("Benjamin Zores (C) 2005-2007, for GeeXboX Team.\n")); + printf (_("See http://ushare.geexbox.org/ for updates.\n")); +} + +inline static void +setup_i18n(void) +{ +#ifdef CONFIG_NLS +#ifdef HAVE_SETLOCALE + setlocale (LC_ALL, ""); +#endif +#if (!defined(BSD) && !defined(__FreeBSD__)) + bindtextdomain (PACKAGE, LOCALEDIR); +#endif + textdomain (PACKAGE); +#endif +} + +#define SHUTDOWN_MSG _("Server is shutting down: other clients will be notified soon, Bye bye ...\n") + +static void +ushare_kill (ctrl_telnet_client *client, + int argc __attribute__((unused)), + char **argv __attribute__((unused))) +{ + if (ut->use_telnet) + { + ctrl_telnet_client_send (client, SHUTDOWN_MSG); + client->exiting = true; + } + ushare_signal_exit (); +} + +//main (int argc, char **argv) +void * +dlna_startup (void *p) +{ + ut = ushare_new (); + log_verbose ("dlna_startup() start\n"); + + if (!ut) + return NULL; + + setup_i18n (); + setup_iconv (); + +#if 0 + /* Parse args before cfg file, as we may override the default file */ + if (parse_command_line (ut, argc, argv) < 0) + { + ushare_free (ut); + return NULL; + } +#endif + + if (parse_config_file (ut) < 0) + { + /* fprintf here, because syslog not yet ready */ + fprintf (stderr, _("Warning: can't parse file \"%s\".\n"), + ut->cfg_file ? ut->cfg_file : SYSCONFDIR "/" USHARE_CONFIG_FILE); + } + ut->verbose = true; + ut->port = 0; + ut->use_presentation = false; + ut->use_telnet = false; + ut->dlna_enabled = true; + ut->override_iconv_err = false; + ut->xbox360 = true; + ut->daemon = false; + //ut->interface = "192.168.1.34"; + ut->contentlist = "/tmp"; + + if (ut->xbox360) + { + char *name; + + name = malloc (strlen (XBOX_MODEL_NAME) + strlen (ut->model_name) + 4); + sprintf (name, "%s (%s)", XBOX_MODEL_NAME, ut->model_name); + free (ut->model_name); + ut->model_name = strdup (name); + free (name); + + ut->starting_id = STARTING_ENTRY_ID_XBOX360; + } + + if (ut->daemon) + { + /* starting syslog feature as soon as possible */ + start_log (); + } + + if (!ut->contentlist) + { + log_error (_("Error: no content directory to be shared.\n")); + ushare_free (ut); + return NULL; + } + + if (!has_iface (ut->interface)) + { + ushare_free (ut); + return NULL; + } + + ut->udn = create_udn (ut->interface); + if (!ut->udn) + { + ushare_free (ut); + return NULL; + } + + ut->ip = get_iface_address (ut->interface); + if (!ut->ip) + { + ushare_free (ut); + return NULL; + } + + if (ut->daemon) + { + int err; + err = daemon (0, 0); + if (err == -1) + { + log_error (_("Error: failed to daemonize program : %s\n"), + strerror (err)); + ushare_free (ut); + return NULL; + } + } + else + { + display_headers (); + } + +#if 0 + signal (SIGINT, UPnPBreak); + signal (SIGTERM, UPnPBreak); + signal (SIGUSR1, UPnPBreak); + signal (SIGUSR2, UPnPBreak); + signal (SIGPIPE, UPnPBreak); + signal (SIGHUP, reload_config); +#endif + + if (ut->use_telnet) + { + if (ctrl_telnet_start (ut->telnet_port) < 0) + { + ushare_free (ut); + return NULL; + } + + ctrl_telnet_register ("kill", ushare_kill, + _("Terminates the uShare server")); + } + log_verbose ("init_upnp() start\n"); + + if (init_upnp (ut) < 0) + { + finish_upnp (ut); + ushare_free (ut); + return NULL; + } + + build_metadata_list (ut); + + log_verbose ("uShare mutex lock.\n"); + /* Let main sleep until it's time to die... */ + pthread_mutex_lock (&ut->termination_mutex); + log_verbose ("uShare cond wait.\n"); + pthread_cond_wait (&ut->termination_cond, &ut->termination_mutex); + pthread_mutex_unlock (&ut->termination_mutex); + log_verbose ("uShare finish.\n"); + + if (ut->use_telnet) + ctrl_telnet_stop (); + finish_upnp (ut); + free_metadata_list (ut); + ushare_free (ut); + finish_iconv (); + + log_verbose ("dlna_start() finish\n"); + /* it should never be executed */ + return NULL; +}