sapserver.cpp 9.46 KB
Newer Older
1
/*****************************************************************************
2
 * sapserver.cpp : SAP discovery service mini-server
3
 ****************************************************************************
4
 * Copyright (C) 1998-2004 VideoLAN
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
5
 * $Id$ 
6
 *
7
 * Authors: Arnaud Schauly <gitan@via.ecp.fr>
8
 *          Clément Stenac <zorglub@via.ecp.fr>
9
 *          Damien Lucas <nitrox@videolan.org>
10
 *          Rémi Denis-Courmont <rem # videolan.org>
11
 *
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
 * 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 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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

27 28 29
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
30
#include <inttypes.h>
31 32 33 34
#include <sys/types.h>
#include <sys/socket.h>
#include <time.h>
#include <string.h>
35
#include <stdlib.h>
36
#include <stdio.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
37
#include <signal.h>
38
#include <unistd.h>
39
#include <locale.h>
40 41
#include <string>
#include <vector>
42 43 44
#ifdef HAVE_GETOPT_H
# include <getopt.h>
#endif
45

46
using namespace std;
47

48
#include "sapserver.h"
49 50 51 52
#include "program.h"
#include "message.h"
#include "broadcast.h"
#include "parser.h"
53

54
#ifdef CONFIG_SLP
55
    #include <slp.h>
56
    #include "lslp.h"
57
#endif
58

59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
#ifndef HAVE_DAEMON
# include <fcntl.h>
/* daemon() is a BSD-specific function which SYSV doesn't have */
static int
daemon (int nochdir, int noclose)
{
    switch (fork ())
    {
        case -1:
             return -1;
        case 0:
             break;
        default:
             _exit (0);
    }

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

    if (!nochdir)
    {
        chdir ("/");
    }

    if (!noclose)
    {
        int nullfile = open ("/dev/null", O_RDWR);

        dup2 (nullfile, 0);
        dup2 (nullfile, 1);
        dup2 (nullfile, 2);

        close (nullfile);
    }
}
#endif

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
96 97 98 99 100 101 102 103 104 105 106 107 108
/*************************************************
 * Signal handler
 *************************************************/
static int should_exit = 0;

static void
exit_handler (int signum)
{
    signal (signum, exit_handler);
    should_exit = 1;
}


109 110 111
/*************************************************
 * Display the help
 *************************************************/
112 113
static void
help(void)
114
{
115
  printf(PACKAGE_NAME"\n"
116 117 118
  "Options:\n"
  "  -d      Use this to daemonize the process (run in the background)\n"
  "  -f      Use this to give a configuration file "
119
                "(default is %s)\n"
120 121 122 123 124
  "  -c      Same as -f\n"
  "  -h      Display this help\n"
  "  -t      \"slp\" or \"sap\". sap is default\n"
  "  -s      Display a dot for each packet sent\n"
  "  -u      Unregister services (SLP only)\n"
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
125
  "  -V      Display version information\n", DEFAULT_CONF);
126
}
127

128 129
static void
version(void)
130 131 132 133 134 135 136
{
    puts(PACKAGE_NAME" version "PACKAGE_VERSION"\n"
    "Copyright (C) 1999-2005 VideoLAN project\n"
    "This is free software; see the source for copying conditions.\n"
    "There is NO warranty; not even for MERCHANTABILITY or\n"
    "FITNESS FOR A PARTICULAR PURPOSE.");
}
137 138 139 140 141 142 143 144

typedef struct
{
    struct sockaddr_storage addr;
    socklen_t addrlen;
    Message *message;
} Announce;

145
#ifndef HAVE_CLEARENV
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
146 147
extern char **environ;

148 149 150 151 152 153 154 155
static int
clearenv (void)
{
	environ = NULL;
	return 0;
}
#endif

156 157 158
/*************************************************
 * The main function
 *************************************************/
159 160
int main(int argc, char *argv[])
{
161
    int result;
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
    const struct option opts[] =
        {
                { "conf",       required_argument, NULL, 'c' },
                { "config",     required_argument, NULL, 'c' },
                { "daemon",     no_argument,       NULL, 'd' },
                { "daemonize",  no_argument,       NULL, 'd' },
                { "file",       required_argument, NULL, 'f' },
                { "help",       no_argument,       NULL, 'h' },
                { "dotmode",    no_argument,       NULL, 's' },
                { "type",       required_argument, NULL, 't' },
                { "unregister", no_argument,       NULL, 'u' },
                { "version",    no_argument,       NULL, 'V' },
                { NULL,         no_argument,       NULL, '\0'}
        };

177

178 179
    setvbuf (stdout, NULL, _IOLBF, 0);
    setvbuf (stderr, NULL, _IOLBF, 0);
180
    setlocale(LC_ALL, "");
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
181
    Config config(DEFAULT_CONF);
182 183

    /* Parse the command line */
184
    while((result = getopt_long(argc,argv,"c:df:hst:uV", opts, NULL))>0)
185
    {
186
        switch(result)
187
        {
188 189
            case 'c':
            case 'f':
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
190
                config.SetFile(optarg);
191 192
                break;
            case 's':
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
193
                config.SetDotMode(true);
194 195
                break;
            case 'd':
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
196 197 198 199 200 201 202
                config.SetDaemonMode(true);
                break;
            case 't':
                config.SetType(optarg);
                break;
            case 'u':
                config.SetReverse(true);
203 204 205
                break;
            case 'h':
                help();
206
                return 0;
207 208 209
            case 'V':
                version();
                return 0;
210
            default:
211
                return -1;
212
        }
213 214
    }

215
    /* clean up environment */
216
    clearenv();
217 218 219 220
    /* make sure stdin, stdout and stderr exists */
    if (dup (2) < 3)
        return 1;

221
    /* Get the programs */
222
    puts("+ Parsing configuration file\n");
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
223
    if(config.Parse())
224
    {
225
        fputs("+ Parsing failed\n", stderr);
226
        return 1;
227
    }
228

229
    /* TODO check configuration */
230
    printf("+ %u programs loaded\n",config.Programs.size());
231 232 233 234 235
    if (config.Programs.size() == 0)
    {
        puts ("+ Nothing to do. Exiting.");
        return 0;
    }
236

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
237
    if(config.GetType() == TYPE_SAP)
238
    {
239
        printf("+ Packet TTL set to %u\n",config.GetTTL());
240
        printf("+ Running as %s.\n",
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
241
               config.GetDaemonMode() ? "daemon" : "program");
242 243

        /* Create the broadcast object */
244
        Broadcast broadcast (config.GetTTL (), config.GetInterface ());
245

246 247 248
        vector<Announce *> announces;

        for (unsigned int i = 0; i < config.Programs.size(); i++)
249
        {
250
            Program *program = config.Programs[i];
251

252 253 254 255 256 257 258 259 260 261
            Announce *announce = new Announce;
            if (broadcast.GuessDestination (program->GetAddress ().c_str (),
                                            &announce->addr,
                                            &announce->addrlen))
            {
                fputs("- Network initialization failed. Aborting\n", stderr);
                delete announce;
                /* FIXME memory leak in announces */
                return 1;
            }
262

263
            /* Create a new message */
264
            Message *message = new Message(0x4212+i, "1.2.3.4");
265 266
            /* Add the program */
            message->AddProgram(config.Programs[i]);
267

268 269
            announce->message = message;
            announces.push_back( announce );
270
        }
271

272
        /* Forking if necessary */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
273
        if(config.GetDaemonMode())
274
        {
275
            puts("+ Forking ... \n");
276 277 278
            daemon(0,0);
        }

279
        unsigned n = config.Programs.size();
280
        lldiv_t d = lldiv (1000000000LL * config.GetDelay() / n, 1000000000);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
281
        struct timespec delay;
282 283
        delay.tv_sec = d.quot;
        delay.tv_nsec = d.rem;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
284 285 286 287 288

        signal (SIGINT, exit_handler);
        signal (SIGTERM, exit_handler);
        signal (SIGHUP, exit_handler);
        signal (SIGQUIT, exit_handler);
289
        setvbuf (stdout, NULL, _IONBF, 0);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
290 291

        while(!should_exit)
292
        {
293
            for( unsigned int i = 0; i< announces.size() ; i ++ )
294
            {
295
                Announce *announce = announces[i];
296
                /* Send the message */
297 298 299 300 301 302 303
                if (broadcast.Send (announce->message ,
                                    (struct sockaddr *)&announce->addr,
                                    announce->addrlen))
                {
                    fputs ("- Message send failed\n", stderr);
                }
                else
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
304
                if(config.GetDotMode())
305
                    fputc('.', stdout);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
306

307
                /* Wait for next sending */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
308 309 310
                if(should_exit)
                    break;
                nanosleep( &delay, NULL );
311
            }
312
        }
313

314 315 316 317 318 319
        for( unsigned int i = 0; i < announces.size() ; i ++ )
        {
            Announce *announce = announces[i];
            delete announce->message;
            delete announce;
        }
320
    }
321
    else
322
    {
323
#ifndef CONFIG_SLP
324
        fputs("- SLP not compiled in...Aborting\n", stderr);
325
        return 1;
326 327
#endif
#ifdef CONFIG_SLP
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
        SLP slp;

        for(unsigned int i=0 ; i<config.Programs.size(); i++)
        {
            if(!config.GetReverse())
            {
                slp.Register(config.Programs[i]);
            }
            else
            {
                slp.UnRegister(config.Programs[i]);
            }
        }
        if(!config.GetReverse())
        {
343
            puts("+ Programs registered. To unregister them, run me with -u");
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
344 345 346
        }
        else
        {
347
            puts("+ Programs unregistered");
348 349
        }
#endif
350
    }
351

352
    puts("Done.");
353 354

    return 0;
355
}