886 lines
20 KiB
C++
886 lines
20 KiB
C++
/*
|
|
* 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>
|
|
#include <string.h>
|
|
#include <stdio.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"
|
|
|
|
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 = ((struct in_addr*)&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);
|
|
}
|
|
}
|
|
|
|
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"));
|
|
}
|
|
|
|
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 ();
|
|
}
|
|
|
|
int
|
|
main (int argc, char **argv)
|
|
{
|
|
ut = ushare_new ();
|
|
if (!ut)
|
|
return EXIT_FAILURE;
|
|
|
|
setup_i18n ();
|
|
setup_iconv ();
|
|
|
|
/* Parse args before cfg file, as we may override the default file */
|
|
if (parse_command_line (ut, argc, argv) < 0)
|
|
{
|
|
ushare_free (ut);
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
if (ut->xbox360)
|
|
{
|
|
char *name;
|
|
|
|
name = (char*)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 EXIT_FAILURE;
|
|
}
|
|
|
|
if (!has_iface (ut->interface))
|
|
{
|
|
ushare_free (ut);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
ut->udn = create_udn (ut->interface);
|
|
if (!ut->udn)
|
|
{
|
|
ushare_free (ut);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
ut->ip = get_iface_address (ut->interface);
|
|
if (!ut->ip)
|
|
{
|
|
ushare_free (ut);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
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 EXIT_FAILURE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
display_headers ();
|
|
}
|
|
|
|
signal (SIGINT, UPnPBreak);
|
|
signal (SIGHUP, reload_config);
|
|
|
|
if (ut->use_telnet)
|
|
{
|
|
if (ctrl_telnet_start (ut->telnet_port) < 0)
|
|
{
|
|
ushare_free (ut);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
ctrl_telnet_register ("kill", ushare_kill,
|
|
_("Terminates the uShare server"));
|
|
}
|
|
|
|
if (init_upnp (ut) < 0)
|
|
{
|
|
finish_upnp (ut);
|
|
ushare_free (ut);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
build_metadata_list (ut);
|
|
|
|
/* Let main sleep until it's time to die... */
|
|
pthread_mutex_lock (&ut->termination_mutex);
|
|
pthread_cond_wait (&ut->termination_cond, &ut->termination_mutex);
|
|
pthread_mutex_unlock (&ut->termination_mutex);
|
|
|
|
if (ut->use_telnet)
|
|
ctrl_telnet_stop ();
|
|
finish_upnp (ut);
|
|
free_metadata_list (ut);
|
|
ushare_free (ut);
|
|
finish_iconv ();
|
|
|
|
/* it should never be executed */
|
|
return EXIT_SUCCESS;
|
|
}
|