//rpld.c:

/*
 *      Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2009-2014
 *
 *  This file is part of RoarAudio PlayList Daemon,
 *  a playlist management daemon for RoarAudio.
 *  See README for details.
 *
 *  This file is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 3
 *  as published by the Free Software Foundation.
 *
 *  RoarAudio 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this software; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 51 Franklin Street, Fifth Floor,
 *  Boston, MA 02110-1301, USA.
 *
 */

#include "rpld.h"

#if defined(ROAR_HAVE_SETGID) && defined(ROAR_HAVE_SETUID) && defined(ROAR_HAVE_IO_POSIX)
#include <grp.h>
#include <pwd.h>
#endif

#define DEFAULT_ROLE ROAR_ROLE_MUSIC

enum iodir {
 IODIR_IMPORT,
 IODIR_EXPORT
};

struct import_playlist {
 const char * playlist;
 const char * type;
 const char * filename;
 enum iodir   iodir;
};

#if defined(ROAR_HAVE_IO_POSIX) && defined(ROAR_HAVE_FS_POSIX)
#define SUPPORT_PIDFILE
static const char * pidfile = NULL;
#endif

static volatile int g_alive = 1;

#if defined(ROAR_HAVE_SETGID) && defined(ROAR_HAVE_IO_POSIX)
struct group   * g_grp  = NULL;
#endif
#if defined(ROAR_HAVE_SETUID) && defined(ROAR_HAVE_IO_POSIX)
struct passwd  * g_pwd  = NULL;
#endif

void usage (const char * progname) {
 printf("Usage: %s [OPTIONS]\n", progname);

 printf("\nMisc Options:\n\n");
 printf(
        " -h --help               - Show this help\n"
        "    --daemon             - Daemonize\n"
        "    --watchdog           - Enable watchdog\n"
        "    --server SERVER      - Connect to the RoarAudio server at SERVER\n"
        "    --server-queue SERV  - The server to use for the Main Queue.\n"
        "                           Normally --server should be used.\n"
        "    --server-info  SERV  - Copy server infos from this server.\n"
#ifdef SUPPORT_PIDFILE
        "    --pidfile PIDFILE    - Write a pidfile at PIDFILE\n"
#endif
        "    --histsize NUM       - Set history size to NUM\n"
        "    --location  LOC      - Set lion readable location of server\n"
        "    --description  DESC  - Set lion readable description of server\n"
        "    --contact CONTACT    - Set contact for this server\n"
        "    --serial SERIAL      - Set serial for this device or server\n"
        "                           (for embedded devices only)\n"
        "    --uiurl UIURL        - Set URL for userinterface of this device or server\n"
        "                           (for embedded devices only)\n"
#ifdef SUPPORT_PLUGIN
        "    --plugin-load PLUGIN - Load plugin PLUGIN\n"
        "    --plugin-args ARGS   - Arguments for the plugin\n"
        "                           (must be given before the --plugin-load)\n"
#endif
       );

 printf("\nSecurity Options:\n\n");
 printf(
        "    --user    USER       - Sets UID to USER\n"
        "    --group   GROUP      - Sets GID to GROUP\n"
        "    --allow-running-as-root\n"
        "                         - Allows rpld to run as root. NEVER USE THIS.\n"
       );

 printf("\nStore Options:\n\n");
 printf(
        "    --restore            - restore laste state\n"
        "    --no-restore         - do not restore laste state\n"
        "    --auto-restore       - restore last state if possible\n"
        "                           (a non-fatal version of --restore)\n"
        "    --store              - store state on exit\n"
        "    --no-store           - do not store state on exit\n"
        "    --store-path DIR     - location to store/restore from/to\n"
       );

 printf("\nPlayback Options:\n\n");
 printf(
        "    --play               - Start in playling state\n"
        "    --stopped            - Start in stopped state (default)\n"
        "    --no-default-queue   - Do not create a default queue\n"
        "    --role ROLE          - Set role for streams (default: music)\n"
        "    --codec-helper-rate RATE\n"
        "    --codec-helper-bits BITS\n"
        "    --codec-helper-channels CHANS\n"
        "    --codec-helper-codec CODEC\n"
        "                         - Set output format of codec-helper.\n"
       );

 printf("\nPlayList Options:\n\n");
 printf(
        "    --playlist-import PLAYLIST TYPE FILE\n"
        "                         - Load playlist PLAYLIST of type TYPE from file FILE\n"
        "    --playlist-export PLAYLIST TYPE FILE\n"
        "                         - export playlist PLAYLIST to file FILE in format TYPE\n"
       );

 printf("\nNetwork Options:\n\n");
 printf(
        "    --tcp                - Listen on TCP Socket\n"
        "    --unix               - Listen on UNIX Socket\n"
        "    --decnet             - Listen on DECnet Socket\n"
        "    --port PORT          - Listen on port PORT\n"
        "    --bind ADDR          - Listen on address ADDR\n"
        "    --proto PROTO        - Set protocol of listen socket\n"
        "    --acclev LEV         - Set the accesslevel for this socket to ACCLEV\n"
        "    --new-sock           - Opens an additional listen socket\n"
#ifdef HAVE_LIB_SLP
        "    --slp                - Enable OpenSLP support\n"
#endif
        "    --no-listen          - Do not listen for new clients\n"
        "                           (only useful with --client-fh)\n"
        "    --client-fh FH       - Comunicate with a client over this handle\n"
        "                           (only useful for relaing)\n"
        "    --close-fh  FH       - Closes the given fh\n"
       );

 printf("\nCodec Options:\n\n");
 printf(
        "    --bl-codec CODEC     - Use helper for codec CODEC\n"
       );
}

#ifdef ROAR_HAVE_H_SIGNAL
void on_sig_int (int signum) {
 (void)signum;

 rpld_exit();
}

#ifdef SIGUSR1
void on_sig_usr1 (int signum) {
 (void)signum;

 store();
}
#endif
#endif

void rpld_exit(void) {
 g_alive = 0;
}

#ifdef SUPPORT_PIDFILE
int pidfile_save(void) {
 struct roar_vio_calls pidfile_vio;

 if ( pidfile != NULL ) {
  if ( roar_vio_open_dstr_simple(&pidfile_vio, pidfile, O_WRONLY|O_CREAT) == -1 ) {
   ROAR_ERR("Can not write pidfile: %s", pidfile);
   return -1;
  } else {
   roar_vio_printf(&pidfile_vio, "%i\n", getpid());
   roar_vio_close(&pidfile_vio);
#if defined(ROAR_HAVE_SETGID) && defined(ROAR_HAVE_SETUID) && defined(ROAR_HAVE_IO_POSIX)
  if ( g_pwd != NULL || g_grp != NULL ) {
   if ( chown(pidfile, g_pwd != NULL ? g_pwd->pw_uid : (uid_t)-1, g_grp != NULL ? g_grp->gr_gid : (gid_t)-1) == -1 ) {
    ROAR_WARN("Can not change ownership of pidfile: %s: %s", pidfile, strerror(errno));
   }
  }
  if ( chmod(pidfile, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) == -1 ) {
   ROAR_WARN("Can not change permissions of pidfile: %s: %s", pidfile, strerror(errno));
  }
#endif
   return 0;
  }
 }

 return 0;
}

int pidfile_unlink(void) {
 if ( pidfile != NULL )
  return unlink(pidfile);

 return 0;
}
#endif

static void addstartup_to_queue(pli_t queue) {
 struct rpld_playlist_entry * plent;
 struct rpld_playlist_entry * copy;

 if ( (plent = rpld_pointer_search(POINTER_STARTUP, queue, -1)) == NULL )
  return;

 if ( (copy = rpld_ple_copy(plent)) == NULL )
  return;

 rpld_pl_add(g_playlists[queue], copy, -1);
}

static void addstartup(void) {
 size_t i;
 pli_t queue;

 for (i = 0; i < MAX_PLAYLISTS; i++) {
  if ( g_playlists[i] == NULL )
   continue;

  if ( g_playlists[i]->queue == NULL )
   continue;

  queue = g_playlists[i]->id;

  if ( rpld_pointer_is_set(POINTER_STARTUP, queue, -1) ) {
   addstartup_to_queue(queue);
  }
 }
}

static int imexportplaylist(const struct import_playlist * playlist) {
 struct roar_vio_defaults def;
 struct fformat_handle  * handle;
 struct roar_vio_calls    vio;
 struct rpld_playlist   * pl = NULL;
 int format;
 int ret;

 if ( playlist->playlist == NULL || playlist->playlist[0] == 0 ||
      playlist->type     == NULL || playlist->type[0]     == 0 ||
      playlist->filename == NULL || playlist->filename[0] == 0 ) {
  ROAR_ERR("imexportplaylist(playlist='%s', type='%s', file='%s'): Invalid argument", playlist->playlist, playlist->type, playlist->filename);
  return -1;
 }

 if ( roar_vio_dstr_init_defaults(&def, ROAR_VIO_DEF_TYPE_NONE, playlist->iodir == IODIR_IMPORT ? O_RDONLY : O_WRONLY|O_CREAT|O_TRUNC, 0644) == -1 ) {
  ROAR_ERR("imexportplaylist(playlist='%s', type='%s', file='%s'): Can not create DSTR defaults", playlist->playlist, playlist->type, playlist->filename);
  return -1;
 }

 format = fformat_str2plf(playlist->type);
 if ( format == -1 ) {
  ROAR_ERR("imexportplaylist(playlist='%s', type='%s', file='%s'): Unknown playlist type", playlist->playlist, playlist->type, playlist->filename);
  return -1;
 }

 handle = fformat_handle_new(format);
 if ( handle == NULL ) {
  ROAR_ERR("imexportplaylist(playlist='%s', type='%s', file='%s'): Can not open file format handle for playlist type", playlist->playlist, playlist->type, playlist->filename);
  return -1;
 }

 if ( roar_vio_open_dstr(&vio, playlist->filename, &def, 1) == -1 ) {
  ROAR_ERR("imexportplaylist(playlist='%s', type='%s', file='%s'): Can not open file: %s", playlist->playlist, playlist->type, playlist->filename, roar_error2str(roar_error));
  fformat_handle_close(handle);
  return -1;
 }

 pl = rpld_pl_get_by_name(playlist->playlist);
 if ( pl == NULL ) {
  pl = rpld_pl_new();
  if ( pl != NULL ) {
   rpld_pl_set_name(pl, playlist->playlist);
   rpld_pl_register(pl);
  } 
 }

 if ( pl == NULL ) {
  ROAR_ERR("imexportplaylist(playlist='%s', type='%s', file='%s'): Playlist not found and playlist creation failed", playlist->playlist, playlist->type, playlist->filename);
  roar_vio_close(&vio);
  fformat_handle_close(handle);
  return -1;
 }

 if ( playlist->iodir == IODIR_IMPORT ) {
  ret = fformat_playlist_import(handle, &vio, pl);
 } else {
  ret = fformat_playlist_export(handle, &vio, pl);
 }

 roar_vio_close(&vio);

 fformat_handle_close(handle);

 rpld_pl_unref(pl);

 if ( ret == -1 ) {
  ROAR_ERR("imexportplaylist(playlist='%s', type='%s', file='%s'): Can not import playlist", playlist->playlist, playlist->type, playlist->filename);
 }
 return ret;
}

int main (int argc, char * argv[]) {
 struct roar_server_info serverinfo;
 const char * info_server = NULL;
 struct roar_audio_info * codechelper_info = playback_get_codechelper_info();
 struct roar_vio_calls * clientvio;
 struct rpld_playlist * queue, * history;
 int allow_root  = 0;
 int do_restore  = 0;
 int do_store    = 0;
 int i;
 const char * k;
 const char * n_server = NULL;
 int    n_listen =  1;
 int    n_port   = -1;
 int    n_type   = ROAR_SOCKET_TYPE_UNKNOWN;
 int    n_proto  = ROAR_PROTO_RPLD;
 enum rpld_client_acclev n_acclev = ACCLEV_ALL;
 int    playing  = 0;
#ifdef HAVE_LIB_SLP
 int    slp      = 0;
#endif
 int    daemon   = 0;
 int    watchdog = 0;
 int is_playing  = 1;
 const char * queue_server = NULL;
 int role = DEFAULT_ROLE;
 int create_default_queue = 1;
#ifdef SUPPORT_PLUGIN
 const char * plugin_args = NULL;
#endif
 struct import_playlist import_playlist[8];
 int    import_num = 0;

 ROAR_DBG("main(*) = ?");

 memset(&serverinfo, 0, sizeof(serverinfo));

 if ( roar_notify_core_new_global(-1) == -1 ) {
  ROAR_ERR("main(*): Can not create new global notify bus: %s", roar_error2str(roar_error));
  return 1;
 }

 if ( roar_notify_core_register_proxy(NULL, roar_notify_proxy_std, NULL) == -1 ) {
  ROAR_ERR("main(*): Can not register global notify proxy: %s", roar_error2str(roar_error));
  return 1;
 }

 if ( backend_init() == -1 ) {
  ROAR_ERR("main(*): Can not start backend module: %s", roar_error2str(roar_error));
  return 1;
 }

#ifdef SUPPORT_PLUGIN
 if ( rpld_plugin_init() == -1 ) {
  ROAR_ERR("main(*): Can not allocate plugin container: %s", roar_error2str(roar_error));
  return 1;
 }
#endif

 if ( (queue = rpld_pl_new()) == NULL )
  return 1;

 ROAR_DBG("main(*) = ?");

 if ( (history = rpld_pl_new()) == NULL )
  return 1;

 memset(g_playlists, 0, sizeof(g_playlists));
 rpld_pl_register_id(queue, 0);
 rpld_pl_register_id(history, 1);

 rpld_pl_set_name(queue,   "Main Queue");
 rpld_pl_set_name(history, "Main History");
 rpld_pl_set_histsize(history, 42);

 ROAR_DBG("main(*) = ?");

 if ( playback_preinit() == -1 )
  return 1;

 ROAR_DBG("main(*) = ?");

 if ( network_preinit() == -1 )
  return 1;

 ROAR_DBG("main(*) = ?");

 client_init();

 for (i = 1; i < argc; i++) {
  k = argv[i];

  if ( !strcmp(k, "-h") || !strcmp(k, "--help") ) {
   usage(argv[0]);
   return 0;
  } else if ( !strcmp(k, "--daemon") ) {
   daemon = 1;
  } else if ( !strcmp(k, "--watchdog") ) {
   watchdog = 1;
  } else if ( !strcmp(k, "--server") ) {
   roar_libroar_set_server(argv[++i]);
  } else if ( !strcmp(k, "--server-queue") ) {
   queue_server = argv[++i];
  } else if ( !strcmp(k, "--server-info") ) {
   info_server = argv[++i];
  } else if ( strcmp(k, "--pidfile") == 0 ) {
#ifdef SUPPORT_PIDFILE
   pidfile = argv[++i];
#else
   ROAR_ERR("--pidfile not supported");
   i++;
#endif

  } else if ( strcmp(k, "--location") == 0 ) {
   serverinfo.location = argv[++i];
  } else if ( strcmp(k, "--description") == 0 ) {
   serverinfo.description = argv[++i];
  } else if ( strcmp(k, "--contact") == 0 ) {
   serverinfo.contact = argv[++i];
  } else if ( strcmp(k, "--serial") == 0 ) {
   serverinfo.serial = argv[++i];
  } else if ( strcmp(k, "--uiurl") == 0 ) {
   serverinfo.uiurl = argv[++i];

  } else if ( strcmp(k, "--plugin-args") == 0 ) {
#ifdef SUPPORT_PLUGIN
   plugin_args = argv[++i];
#else
   i++;
#endif
  } else if ( strcmp(k, "--plugin-load") == 0 ) {
#ifdef SUPPORT_PLUGIN
   rpld_plugin_load(argv[++i], plugin_args);
   plugin_args = NULL;
#else
   ROAR_ERR("--plugin-load not supported");
   i++;
#endif

  } else if ( strcmp(k, "--no-listen") == 0 ) {
   n_listen = 0;
  } else if ( strcmp(k, "--client-fh") == 0 ) {
   if ( client_new(&clientvio) == -1 ) {
    ROAR_ERR("Can not add client from fh %i", atoi(argv[++i]));
    return 1;
   }

   if ( roar_vio_open_fh(clientvio, atoi(argv[++i])) == -1 ) {
    ROAR_ERR("Can not add client from fh %i", atoi(argv[i]));
    return 1;
   }
  } else if ( strcmp(k, "--close-fh") == 0 ) {
#ifdef ROAR_HAVE_IO_POSIX
   close(atoi(argv[++i]));
#else
   i++;
   ROAR_WARN("can not close file handle %s (closing not supported)", argv[i]);
#endif

  } else if ( !strcmp(k, "--histsize") ) {
   i++;
   if ( !strcmp(argv[i], "default") ) {
    // noop. Used by rpld-storemgr to simplify the argument generator.
   } else {
    rpld_pl_set_histsize(history, atol(argv[i]));
   }

  } else if ( !strcmp(k, "--user") ) {
#if defined(ROAR_HAVE_SETUID) && defined(ROAR_HAVE_IO_POSIX)
  // early test for UID as we need this for the pidfile and the setuid()
  if ( (g_pwd = getpwnam(argv[++i])) == NULL ) {
   ROAR_ERR("Can not get UID for user %s: %s", argv[i], strerror(errno));
   return 1;
  }
#else
   ROAR_ERR("No support for setuid()");
   return 1;
#endif
  } else if ( !strcmp(k, "--group") ) {
#if defined(ROAR_HAVE_SETGID) && defined(ROAR_HAVE_IO_POSIX)
  if ( (g_grp = getgrnam(argv[++i])) == NULL ) {
   ROAR_ERR("Can not get GID for group %s: %s", argv[i], strerror(errno));
   return 1;
  }
  if ( setgroups(0, (const gid_t *) NULL) == -1 ) {
   ROAR_ERR("Can not clear supplementary group IDs: %s", strerror(errno));
  }
  if ( g_grp == NULL || setgid(g_grp->gr_gid) == -1 ) {
   ROAR_ERR("Can not set GroupID: %s", strerror(errno));
  }
#else
   ROAR_ERR("No support for setgid()");
   return 1;
#endif
  } else if ( !strcmp(k, "--allow-running-as-root") ) {
   allow_root = 1;

  } else if ( !strcmp(k, "--store-path") ) {
   do_restore = 1;
   if ( setstorepath(argv[++i]) == -1 ) {
    fprintf(stderr, "Error: can not set restore path\n");
    return 1;
   }
  } else if ( !strcmp(k, "--restore") ) {
   do_restore = 1;
  } else if ( !strcmp(k, "--no-restore") ) {
   do_restore = 0;
  } else if ( !strcmp(k, "--auto-restore") ) {
   do_restore = 2;
  } else if ( !strcmp(k, "--store") ) {
   do_store = 1;
  } else if ( !strcmp(k, "--no-store") ) {
   do_store = 0;

  } else if ( !strcmp(k, "--play") ) {
   playing = 1;
  } else if ( !strcmp(k, "--stopped") ) {
   playing = 0;
  } else if ( !strcmp(k, "--no-default-queue") ) {
   create_default_queue = 0;
  } else if ( !strcmp(k, "--role") ) {
   i++;
   if ( !strcmp(argv[i], "default") ) {
    role = DEFAULT_ROLE;
   } else {
    if ( (role = roar_str2role(argv[i])) == -1 ) {
     fprintf(stderr, "Error: can not set role (unknown role name?) to: %s\n", argv[i]);
     return 1;
    }
   }

  } else if ( !strcmp(k, "--codec-helper-rate") ) {
   codechelper_info->rate = roar_str2rate(argv[++i]);
  } else if ( !strcmp(k, "--codec-helper-bits") ) {
   codechelper_info->bits = roar_str2bits(argv[++i]);
  } else if ( !strcmp(k, "--codec-helper-channels") ) {
   codechelper_info->channels = roar_str2channels(argv[++i]);
  } else if ( !strcmp(k, "--codec-helper-codec") ) {
   codechelper_info->codec = roar_str2codec(argv[++i]);

  // --load-playlist is old parameter name and should no longer be used.
  } else if ( !strcmp(k, "--load-playlist") || !strcmp(k, "--playlist-import") || !strcmp(k, "--playlist-export") ) {
   if ( !strcmp(k, "--load-playlist") ) {
    roar_debug_option_obsolete(argv[0], k, "--playlist-import", NULL);
   }

   if ( import_num == (sizeof(import_playlist)/sizeof(*import_playlist)) ) {
    ROAR_WARN("Can not import more playlists: maximum number of playlists to import reached.");
    i += 3;
    continue;
   }

   import_playlist[import_num].playlist = argv[i+1];
   import_playlist[import_num].type     = argv[i+2];
   import_playlist[import_num].filename = argv[i+3];
   import_playlist[import_num].iodir    = !strcmp(k, "--playlist-export") ? IODIR_EXPORT : IODIR_IMPORT;
   import_num++;
   i += 3;

  } else if ( !strcmp(k, "--tcp") ) {
   n_type = ROAR_SOCKET_TYPE_TCP;
  } else if ( !strcmp(k, "--unix") ) {
   n_type = ROAR_SOCKET_TYPE_UNIX;
  } else if ( !strcmp(k, "--decnet") ) {
   n_type = ROAR_SOCKET_TYPE_DECNET;
  } else if ( !strcmp(k, "--bind") ) {
   n_server = argv[++i];
  } else if ( !strcmp(k, "--port") ) {
   n_port = atoi(argv[++i]);
  } else if ( !strcmp(k, "--proto") ) {
   n_proto = client_str2proto(argv[++i]);
  } else if ( !strcmp(k, "--acclev") ) {
   n_acclev = client_str2acclev(argv[++i]);
  } else if ( !strcmp(k, "--new-sock") ) {
   if ( network_add_server(n_server, n_port, n_type, n_proto, n_acclev) == -1 )
    return 1;
  } else if ( !strcmp(k, "--slp") ) {
#ifdef HAVE_LIB_SLP
   slp = 1;
#else
   fprintf(stderr, "Warning: No OpenSLP support compiled in.\n");
#endif

  } else if ( !strcmp(k, "--bl-codec") ) {
   backend_blacklist_codec_global(roar_str2codec(argv[++i]));
  } else {
   fprintf(stderr, "Error: Invalid option: %s\n", k);
   usage(argv[0]);
   return 1;
  }
 }

#ifdef ROAR_HAVE_GETUID
 if ( getgid() == ROOT_UID ) {
  if ( !allow_root ) {
   fprintf(stderr, "Error: Running as superuser. THIS IS A VERY BAD THING TO DO.\n");
   fprintf(stderr, "Error: Rejecting to run as superuser. Terminating.\n");
   return 1;
  }
 }
#else
 if ( !allow_root ) {
  fprintf(stderr, "Warning: Can not check if running as superuser\n");
 }
#endif

 // set up the main queue.
 if ( create_default_queue ) {
  playback_add_queue(queue, queue_server, -1 /* FIXME */, history);
  playback_set_role(rpld_pl_get_id(queue), role);
 }

 rpld_pl_unref(queue);
 rpld_pl_unref(history);

 // setting up server info:
 if ( info_server != NULL )
  rpld_serverinfo_update(RPLD_SERVERINFO_SOURCE_SERVER, info_server);
 rpld_serverinfo_update(RPLD_SERVERINFO_SOURCE_OBJECT, &serverinfo);

 if ( playback_init() == -1 )
  return 1;

 // init network
 if ( do_restore ) {
  if ( restore() == -1 ) {
   if ( do_restore == 2 ) {
    fprintf(stderr, "Warning: Restore failed.\n");
   } else {
    fprintf(stderr, "Error: Restore failed.\n");
    return 1;
   }
  }
 }

 for (i = 0; i < import_num; i++) {
  if ( imexportplaylist(&(import_playlist[i])) == -1 ) {
   ROAR_WARN("Failed to import/export playlist: %s: %s", import_playlist[i].filename, roar_error2str(roar_error));
  }
 }

// We need to fork before the init the playback backend
// so the PID does not change after the init.
// We will close the STD* handles later.
 if ( daemon ) {
  switch (roar_fork(NULL)) {
   case -1:
     ROAR_ERR("Fork failed: %s", roar_error2str(roar_error));
     ROAR_U_EXIT(1);
    break;
   case 0:
    break;
   default:
     ROAR_U_EXIT(0);
  }

#ifdef ROAR_HAVE_SETSID
  setsid();
#endif
 }

 yield_watchdog_active(watchdog);
 yield_watchdog_start();

 ROAR_DBG("main(*) = ?");

 if ( n_listen )
  if ( network_add_server(n_server, n_port, n_type, n_proto, n_acclev) == -1 )
   return 1;

 ROAR_DBG("main(*) = ?");

 if ( network_init() == -1 )
  return 1;

 ROAR_DBG("main(*) = ?");

#ifdef HAVE_LIB_SLP
 if ( slp ) {
  if ( n_type == ROAR_SOCKET_TYPE_UNIX ) {
   ROAR_WARN("SLP not supported on UNIX Sockets, ignoring --slp");
   slp = 0;
  } else {
   if ( register_slp(0, n_server) == -1 ) {
    return 1;
   }
  }
 }
#endif

 if ( pidfile != NULL ) {
  if ( pidfile_save() == -1 ) {
   ROAR_WARN("Can not save pidfile: %s", pidfile);
  }
 }

 ROAR_DBG("main(*) = ?");

#if defined(ROAR_HAVE_SETUID) && defined(ROAR_HAVE_IO_POSIX)
 if ( g_pwd != NULL ) {
  if ( setuid(g_pwd->pw_uid) == -1 ) {
   ROAR_ERR("Can not set UserID: %s", strerror(errno));
   return 1;
  }
 }
#endif

 ROAR_DBG("main(*) = ?");

#ifdef ROAR_HAVE_H_SIGNAL
#ifdef SIGINT
 signal(SIGINT,  on_sig_int);
#endif
#ifdef SIGTERM
 signal(SIGTERM,  on_sig_int);
#endif
#ifdef SIGPIPE
 signal(SIGPIPE, SIG_IGN);  // ignore broken pipes
#endif
#ifdef SIGUSR1
 signal(SIGUSR1,  on_sig_usr1);
#endif
#endif

 addstartup();

// Now we should have everything up and running, we close the STD* handles.
 if ( daemon ) {
  roar_debug_set_stderr_mode(ROAR_DEBUG_MODE_SYSLOG);
  close(ROAR_STDIN );
  close(ROAR_STDOUT);
  close(ROAR_STDERR);
 }

 ROAR_DBG("main(*) = ?");

#ifdef SUPPORT_PLUGIN
  rpld_plugin_appsched(ROAR_DL_APPSCHED_INIT);
#endif

 if ( playing )
  playback_play_all();

 while (g_alive) {
  // main loop, check clients, play files
  yield_watchdog_trigger();
  network_check(is_playing ? RPLD_YIELD_BLOCK : RPLD_YIELD_LONGBLOCK);
  playback_check(RPLD_YIELD_BLOCK);
  is_playing = playback_is_playing_any();
#ifdef SUPPORT_PLUGIN
  rpld_plugin_appsched(ROAR_DL_APPSCHED_UPDATE);
#endif
 }

 ROAR_DBG("main(*) = ?");

 playback_stop_all(0, 1);

#ifdef HAVE_LIB_SLP
 if ( slp ) {
  if ( register_slp(1, n_server) == -1 ) {
   ROAR_WARN("Can not unregister SLP service");
  }
 }
#endif

 if ( do_store ) {
  store();
 }

#ifdef SUPPORT_PLUGIN
 rpld_plugin_free();
#endif

 // deinit network
 network_uninit();

 playback_uninit();

 backend_free();

 if ( pidfile != NULL )
  pidfile_unlink();

 return 0;
}

//ll
