source: roaraudio/roard/roard.c @ 3374:18a9494d6784

Last change on this file since 3374:18a9494d6784 was 3374:18a9494d6784, checked in by phi, 14 years ago

support --{x11-,}display

File size: 49.7 KB
Line 
1//roard.c:
2
3/*
4 *      Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2008-2010
5 *
6 *  This file is part of roard a part of RoarAudio,
7 *  a cross-platform sound system for both, home and professional use.
8 *  See README for details.
9 *
10 *  This file is free software; you can redistribute it and/or modify
11 *  it under the terms of the GNU General Public License version 3
12 *  as published by the Free Software Foundation.
13 *
14 *  RoarAudio is distributed in the hope that it will be useful,
15 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 *  GNU General Public License for more details.
18 *
19 *  You should have received a copy of the GNU General Public License
20 *  along with this software; see the file COPYING.  If not, write to
21 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
22 *
23 */
24
25#include "roard.h"
26
27#ifdef ROAR_SUPPORT_LISTEN
28char * server[ROAR_MAX_LISTEN_SOCKETS];
29#endif
30
31#if defined(ROAR_HAVE_IO_POSIX) && defined(ROAR_HAVE_FS_POSIX)
32#define SUPPORT_PIDFILE
33char * pidfile = NULL;
34#endif
35
36#if defined(ROAR_HAVE_SETGID) || defined(ROAR_HAVE_SETUID)
37int    setids    = 0;
38#endif
39
40#ifdef ROAR_HAVE_LIBX11
41char * x11display = NULL;
42#endif
43
44#ifdef ROAR_HAVE_MAIN_ARGS
45void usage (void) {
46 printf("Usage: roard [OPTIONS]...\n\n");
47
48 printf("Misc Options:\n\n");
49 printf(
50        " --daemon              - Bring the server into background after init\n"
51        " --verbose             - Be more verbose, can be used multiple times\n"
52        " --terminate           - Terminate after last client quited\n"
53        " --start               - No op parameter (starting roard is default operation)\n"
54        " --restart             - Trys to stop an old instance and start a new with new settings\n"
55        " --stop                - Stops a running roard (provide --pidfile!)\n"
56        " --shutdown            - Terminates a running roard (provide --pidfile!)\n"
57        " --realtime            - Trys to get realtime priority,\n"
58        "                         give multible times for being more realtime\n"
59        " --chroot DIR          - chroots to the given dir\n"
60        " --setgid              - GroupID to the audio group as specified via -G\n"
61        " --setuid              - UserID to the audio user as specified via -U\n"
62        " --sysclocksync        - calculate exact sample rate using the system clock\n"
63        " --location  LOC       - Set lion readable location of server\n"
64#ifdef SUPPORT_PIDFILE
65        " --pidfile PIDFILE     - Write a pidfile at PIDFILE\n"
66#endif
67       );
68
69 printf("\nPlugin Options:\n\n");
70 printf(
71        " --plugin-load FILE    - Load plugin FILE\n"
72       );
73
74 printf("\nAudio Options:\n\n");
75 printf(
76        " -R  --rate   RATE     - Set server rate\n"
77        " -B  --bits   BITS     - Set server bits\n"
78        " -C  --chans  CHANNELS - Set server channels\n"
79       );
80
81 printf("\nStream Options:\n\n");
82 printf(
83        " --stream-flags D=F    - Set default flags for stream directions\n"
84        "                         D is the stream direction and F is a comma seperated\n"
85        "                         list of flags in form +flag or -flag to set or unset\n"
86        "                         a flag as default or remove it from the default\n"
87       );
88
89 printf("\nDriver Options: (obsolete, do not use, Use Ouput Options)\n\n");
90 printf(" -d  --driver DRV      - Set the driver (default: %s)\n", ROAR_DRIVER_DEFAULT);
91 printf(" -D  --device DEV      - Set the device\n");
92 printf(" -dO OPTS              - Set output options\n");
93 printf(" --list-driver         - List all drivers\n");
94
95 printf("\nOutput Options:\n\n");
96 printf(" -o  --odriver DRV     - Set the driver, use '--list-driver' to get a list\n");
97 printf(" -O  --odevice DEV     - Set the device\n");
98 printf(" -oO OPTS              - Set output options\n");
99 printf(" -oN                   - Adds another output\n");
100 printf(" -oP                   - Mark output as primary\n");
101
102#ifndef ROAR_WITHOUT_DCOMP_SOURCES
103 printf("\nSource Options:\n\n");
104 printf(" -s  --source DRV      - Use DRV as input driver\n"
105        " -S           DEV      - Use DEV as input device\n"
106        " -sO          OPTS     - Use OPTS as input options\n"
107        " -sN                   - Adds another source\n"
108        " -sP                   - Make souce as primary\n"
109       );
110 printf(" --list-sources        - List all sources\n");
111#endif
112
113 printf("\nCodec Filter Options:\n\n");
114 printf(" --list-cf             - List all codec filter\n"
115       );
116
117#ifndef ROAR_WITHOUT_DCOMP_MIDI
118 printf("\nMIDI Options:\n\n");
119 printf(" --midi-no-console     - Disable console based MIDI synth\n"
120        " --midi-console-enable - Enables the console based MIDI synth\n"
121        " --midi-console DEV    - Set device for MIDI console\n"
122#ifndef ROAR_WITHOUT_DCOMP_SSYNTH
123        " --ssynth-enable       - Enable simple software synth\n"
124        " --ssynth-disable      - Disable simple software synth\n"
125#endif
126       );
127#endif
128
129#ifndef ROAR_WITHOUT_DCOMP_LIGHT
130 printf("\nLight Control Options:\n\n");
131 printf(" --light-channels NUM  - Sets the number of channels for Light control (default: %i)\n",
132                                  LIGHT_CHANNELS_DEFAULT
133       );
134#endif
135
136#ifndef ROAR_WITHOUT_DCOMP_RDTCS
137 printf("\nRadio Data and Transmitter Control System Options:\n\n");
138 printf(" --rds-pi   PI         - Sets the RDS Programme Identification (PI)\n"
139        " --rds-ps   PS         - Sets the RDS Programme Service Name (PS)\n"
140        " --rds-pty  PTY        - Sets the RDS Programme Type (PTY)\n"
141        " --rds-tp              - Sets the RDS Traffic Programme (TP) flag\n"
142        " --rds-ct              - Enables sending of RDS Clock Time (CT)\n"
143       );
144#endif
145
146#ifdef ROAR_HAVE_LIBX11
147 printf("\nX11 Options:\n\n");
148 printf(
149        " --x11-display DISPLAY - Set display for X11\n"
150        " --display DISPLAY     - Set display for X11\n"
151       );
152#endif
153
154 printf("\nServer Options:\n\n");
155 printf(" -t  --tcp             - Use TCP listen socket\n"
156        " -u  --unix            - Use UNIX Domain listen socket (default)\n"
157#ifdef ROAR_HAVE_LIBDNET
158        " -n  --decnet          - use DECnet listen socket\n"
159#endif
160        " -4                    - Use IPv4 connections (implies -t)\n"
161#ifdef PF_INET6
162        " -6                    - Use IPv6 connections (implies -t)\n"
163#endif
164#ifdef IPV6_ADDRFORM
165        " -64                   - Try to downgrade sockets from IPv6 into IPv4,\n"
166        "                         this is normaly not usefull.\n"
167#endif
168        " -p  --port            - TCP Port to bind to\n"
169        " -b  --bind            - IP/Hostname to bind to\n"
170        "     --sock            - Filename for UNIX Domain Socket\n"
171        "     --proto PROTO     - Use PROTO as protocol on Socket\n"
172        "     --proto-dir DIR   - Set direction parameter for protocol\n"
173        "     --proto-rate RATE - Set sample rate parameter for protocol\n"
174        "     --proto-bits BITS - Set bits per sample parameter for protocol\n"
175        "     --proto-codec E   - Set codec parameter for protocol\n"
176        "     --proto-chans C   - Set number of channels paramter for protocol\n"
177        "     --list-proto      - List supported protocols\n"
178        "     --new-sock        - Parameters for new socket follows\n"
179#ifdef ROAR_HAVE_LIBSLP
180        "     --slp             - Enable OpenSLP support\n"
181#endif
182#ifdef ROAR_HAVE_LIBX11
183        "     --x11             - Enable X11 support\n"
184#endif
185        " --jumbo-mtu MTU       - Sets the MTU for Jumbo Packets\n"
186        " -G  GROUP             - Sets the group for the UNIX Domain Socket, (default: %s)\n"
187        "                         You need the permissions to change the GID\n"
188        " -U  USER              - Sets the user for the UNIX Domain Socket, (default: do not set)\n"
189        "                         You need the permissions to change the UID (normaly only root has)\n"
190        " --no-listen           - Do not listen for new clients\n"
191        "                         (only usefull for relaing, impleys --terminate)\n"
192        " --client-fh           - Comunicate with a client over this handle\n"
193        "                         (only usefull for relaing)\n"
194        " --close-fh            - Closes the given fh\n"
195        " --standby             - Start in standby state\n"
196        " --auto-standby        - Automatical goes into standby if there are no streams\n",
197#ifdef ROAR_DEFAULT_SOCKGRP
198        ROAR_DEFAULT_SOCKGRP
199#else
200        "(none)"
201#endif
202       );
203// printf("\n Options:\n\n");
204 printf("\n");
205}
206
207static void list_proto (void) {
208 printf("  Protocol Flag Subsys - Description\n");
209 printf("------------------------------------------------------\n");
210 printf("  roar          WM LRX - RoarAudio native protocol\n");
211#if !defined(ROAR_WITHOUT_DCOMP_EMUL_ESD) && defined(ROAR_HAVE_H_ESD)
212 printf("  esd           W      - EsounD emulation\n");
213#endif
214 printf("  simple        WM LRX - PulseAudio simple protocol\n");
215}
216
217#endif
218
219int restart_server (char * server, int terminate) {
220 struct roar_connection con;
221#ifdef ROAR_HAVE_KILL
222 char buf[80];
223 ssize_t l;
224 struct roar_vio_calls fh;
225 pid_t pid;
226 int ok;
227
228 if ( pidfile != NULL ) {
229  if ( roar_vio_open_file(&fh, pidfile, O_RDONLY, 0644) == -1 ) {
230   ROAR_WARN("restart_server(*): Can not read pidfile: %s", pidfile);
231  } else {
232   l = roar_vio_read(&fh, buf, 80);
233   roar_vio_close(&fh);
234   if ( l > 0 ) {
235    buf[l-1] = 0;
236    buf[79]  = 0;
237    pid = atoi(buf);
238    if ( terminate ) {
239     ok = kill(pid, SIGUSR1);
240    } else {
241     ok = kill(pid, SIGINT);
242    }
243    if ( ok == 0 ) {
244     return 0;
245    } else {
246     ROAR_WARN("restart_server(*): Can not kill roard by pidfile");
247    }
248   } else {
249    ROAR_WARN("restart_server(*): Can not find a PID in the pidfile");
250   }
251  }
252 }
253#endif
254
255 if ( roar_connect(&con, server) == -1 ) {
256  return -1;
257 }
258
259 if ( roar_terminate(&con, terminate) == -1 ) {
260  return -1;
261 }
262
263 return roar_disconnect(&con);
264}
265
266#define R_SETUID 1
267#define R_SETGID 2
268
269int init_config (void) {
270 int i;
271
272 memset(g_config, 0, sizeof(struct roard_config));
273
274 for (i = 0; i < ROAR_DIR_DIRIDS; i++) {
275  g_config->streams[i].mixer_channels = 1;
276  g_config->streams[i].mixer.rpg_mul  = 1;
277  g_config->streams[i].mixer.rpg_div  = 1;
278  g_config->streams[i].mixer.scale    = 65535;
279  g_config->streams[i].mixer.mixer[0] = g_config->streams[i].mixer.scale;
280 }
281
282 g_config->streams[ROAR_DIR_PLAY    ].flags = ROAR_FLAG_META;
283 g_config->streams[ROAR_DIR_OUTPUT  ].flags = ROAR_FLAG_PASSMIXER;
284 g_config->streams[ROAR_DIR_FILTER  ].flags = ROAR_FLAG_SYNC;
285 g_config->streams[ROAR_DIR_MIDI_OUT].flags = ROAR_FLAG_SYNC;
286 g_config->streams[ROAR_DIR_BIDIR   ].flags = ROAR_FLAG_ANTIECHO;
287
288 g_config->location = "***default***";
289
290 return 0;
291}
292
293#ifdef ROAR_SUPPORT_LISTEN
294int init_listening (void) {
295 int i;
296
297 memset(g_listen, 0, sizeof(g_listen));
298
299 for (i = 0; i < ROAR_MAX_LISTEN_SOCKETS; i++) {
300  g_listen[i].socket = -1;
301  g_listen[i].proto  = ROAR_PROTO_ROARAUDIO;
302  server[i]          = NULL;
303 }
304
305 return 0;
306}
307
308int add_listen (char * addr, int port, int sock_type, char * user, char * group, int proto, int dir, struct roar_audio_info * info) {
309#if defined(ROAR_HAVE_SETGID) && defined(ROAR_HAVE_IO_POSIX)
310 struct group   * grp  = NULL;
311#endif
312#if defined(ROAR_HAVE_SETUID) && defined(ROAR_HAVE_IO_POSIX)
313 struct passwd  * pwd  = NULL;
314#endif
315#ifdef ROAR_HAVE_UNIX
316 char * env_roar_proxy_backup;
317#endif
318 int    sockid = -1;
319 int    sock;
320 int    i;
321
322 if ( *addr != 0 ) {
323  for (i = 0; i < ROAR_MAX_LISTEN_SOCKETS; i++) {
324   if ( g_listen[i].socket == -1 ) {
325    sockid = i;
326    break;
327   }
328  }
329
330  if ( sockid == -1 )
331   return -1;
332
333  g_listen[sockid].proto = proto;
334
335  ROAR_DBG("add_listen(*): proto=0x%.4x", proto);
336
337  if ( (g_listen[sockid].socket = roar_socket_listen(sock_type, addr, port)) == -1 ) {
338#ifdef ROAR_HAVE_UNIX
339   if ( *addr == '/' ) {
340    if ( (env_roar_proxy_backup = getenv("ROAR_PROXY")) != NULL ) {
341     env_roar_proxy_backup = strdup(env_roar_proxy_backup);
342     unsetenv("ROAR_PROXY");
343    }
344    if ( (sock = roar_socket_connect(addr, port)) != -1 ) {
345     close(sock);
346     ROAR_ERR("Can not open listen socket: Socket allready in use");
347     return 1;
348    } else {
349     unlink(addr);
350     if ( (g_listen[sockid].socket = roar_socket_listen(sock_type, addr, port)) == -1 ) {
351      ROAR_ERR("Can not open listen socket: %s", strerror(errno));
352      return 1;
353     }
354    }
355    if ( env_roar_proxy_backup != NULL ) {
356     setenv("ROAR_PROXY", env_roar_proxy_backup, 0);
357     free(env_roar_proxy_backup);
358    }
359#else
360   if (0) { // noop
361#endif
362   } else {
363    ROAR_ERR("Can not open listen socket: %s", strerror(errno));
364    return 1;
365   }
366  }
367
368#if defined(ROAR_HAVE_SETGID) && defined(ROAR_HAVE_IO_POSIX)
369  if ( group != NULL ) {
370   if ( (grp = getgrnam(group)) == NULL ) {
371    ROAR_ERR("Can not get GID for group %s: %s", group, strerror(errno));
372   }
373  }
374#endif
375#if defined(ROAR_HAVE_SETUID) && defined(ROAR_HAVE_IO_POSIX)
376  if ( user ) {
377   if ( (pwd = getpwnam(user)) == NULL ) {
378    ROAR_ERR("Can not get UID for user %s: %s", user, strerror(errno));
379   }
380  }
381#endif
382
383#if defined(ROAR_HAVE_IO_POSIX) && defined(ROAR_HAVE_UNIX)
384  if ( *addr == '/' ) {
385   if ( grp || pwd ) {
386     if ( chown(addr, pwd ? pwd->pw_uid : -1, grp ? grp->gr_gid : -1) == -1 )
387      return 1;
388   }
389#ifdef ROAR_HAVE_GETUID
390   if ( grp ) {
391    if ( getuid() == 0 )
392     if ( chmod(addr, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1 )
393      return 1;
394   }
395#endif
396  }
397#endif
398 }
399
400 // in case we opened the listening socket correctly.
401 if ( dir == -1 )
402  dir = ROAR_DIR_PLAY;
403
404 g_listen[sockid].inst.stpl.dir = dir;
405 memcpy(&(g_listen[sockid].inst.stpl.info), info, sizeof(struct roar_audio_info));
406
407 switch (dir) {
408  case ROAR_DIR_PLAY:
409  case ROAR_DIR_RECORD:
410  case ROAR_DIR_MONITOR:
411  case ROAR_DIR_FILTER:
412  case ROAR_DIR_BIDIR:
413    if ( !g_listen[sockid].inst.stpl.info.rate )
414     g_listen[sockid].inst.stpl.info.rate = g_sa->rate;
415
416    if ( !g_listen[sockid].inst.stpl.info.bits )
417     g_listen[sockid].inst.stpl.info.bits = g_sa->bits;
418
419    if ( !g_listen[sockid].inst.stpl.info.channels )
420     g_listen[sockid].inst.stpl.info.channels = g_sa->channels;
421
422    if ( !g_listen[sockid].inst.stpl.info.codec )
423     g_listen[sockid].inst.stpl.info.codec = g_sa->codec;
424   break;
425 }
426
427 server[sockid] = addr;
428 return 0;
429}
430#endif
431
432int update_stream_flags (char * str) {
433 int    dir;
434 char * flags;
435 char * k;
436 int    op;
437 int    flag;
438
439 if ( (flags = strstr(str, "=")) == NULL )
440  return -1;
441
442 *flags = 0;
443  flags++;
444
445 if ( (dir = roar_str2dir(str)) == -1 )
446  return -1;
447
448 while (flags != NULL) {
449  k = flags;
450  flags = strstr(flags, ",");
451
452  if ( flags != NULL )
453   *(flags++) = 0;
454
455  switch (*k) {
456   case '+': k++; op = ROAR_SET_FLAG;   break;
457   case '-': k++; op = ROAR_RESET_FLAG; break;
458   default:
459     op = ROAR_SET_FLAG;
460  }
461
462  flag = 0;
463
464  if ( !strcmp(k, "sync") ) {
465   flag = ROAR_FLAG_SYNC;
466  } else if ( !strcmp(k, "meta") ) {
467   flag = ROAR_FLAG_META;
468  } else if ( !strcmp(k, "cleanmeta") ) {
469   flag = ROAR_FLAG_CLEANMETA;
470  } else if ( !strcmp(k, "pause") ) {
471   flag = ROAR_FLAG_PAUSE;
472  } else if ( !strcmp(k, "mute") ) {
473   flag = ROAR_FLAG_MUTE;
474  } else if ( !strcmp(k, "antiecho") ) {
475   flag = ROAR_FLAG_ANTIECHO;
476  } else if ( !strcmp(k, "passmixer") ) {
477   flag = ROAR_FLAG_PASSMIXER;
478  } else {
479   return -1;
480  }
481
482  g_config->streams[dir].flags |= flag;
483
484  if ( op == ROAR_RESET_FLAG )
485   g_config->streams[dir].flags -= flag;
486 }
487
488 return 0;
489}
490
491int add_output (char * drv, char * dev, char * opts, int prim, int count) {
492 int stream;
493 struct roar_stream * s;
494 struct roar_stream_server * ss;
495 char * k, * v;
496#ifdef ROAR_DRIVER_CODEC
497 char * to_free = NULL;
498#endif
499 int sync = 0, f_mmap = 0;
500 int32_t blocks = -1, blocksize = -1;
501 int dir = ROAR_DIR_OUTPUT;
502 int error = 0;
503 // DMX:
504 int32_t channel  = -1;
505 int32_t universe = -1;
506 uint16_t tu16;
507 float q = -32e6;
508
509 ROAR_DBG("add_output(drv='%s', dev='%s', opts='%s') = ?", drv, dev, opts);
510
511 if ( drv == NULL && count == 0 ) {
512  drv  = ROAR_DRIVER_DEFAULT;
513  prim = 1;
514  sync = 1;
515
516#ifdef ROAR_DRIVER_CODEC
517  if ( opts == NULL ) {
518   opts = to_free = strdup("codec=" ROAR_DRIVER_CODEC);
519  }
520#endif
521 }
522
523 if ( opts == NULL && count == 0 ) {
524  sync = 1;
525  prim = 1; // if ( prim == 0 ) prim = 1; -> prim allways = 1
526 }
527
528 ROAR_DBG("add_output(drv='%s', dev='%s', opts='%s') = ?", drv, dev, opts);
529
530 if ( (stream = streams_new()) == -1 ) {
531  ROAR_DBG("add_output(drv='%s', dev='%s', opts='%s') = -1", drv, dev, opts);
532  if ( prim ) alive = 0;
533  return -1;
534 }
535
536 ROAR_DBG("add_output(drv='%s', dev='%s', opts='%s') = ?", drv, dev, opts);
537
538 streams_get(stream, &ss);
539 s = ROAR_STREAM(ss);
540
541 ROAR_DBG("add_output(drv='%s', dev='%s', opts='%s') = ?", drv, dev, opts);
542
543 memset(&(s->info), 0xFF, sizeof(struct roar_audio_info)); // set everything to -1
544
545 s->pos_rel_id = -1;
546// s->info.codec = codec;
547
548 ROAR_DBG("add_output(drv='%s', dev='%s', opts='%s') = ?", drv, dev, opts);
549
550 if ( opts == NULL ) {
551  k = NULL;
552 } else {
553  k = strtok(opts, ",");
554 }
555
556 ROAR_DBG("add_output(drv='%s', dev='%s', opts='%s'): initial k='%s'(%p)", drv, dev, opts, k, k);
557
558 while (k != NULL) {
559//  ROAR_WARN("add_output(*): opts: %s", k);
560
561  if ( (v = strstr(k, "=")) != NULL ) {
562   *v++ = 0;
563  }
564
565  ROAR_DBG("add_output(*): opts: k='%s', v='%s'", k, v);
566  if ( strcmp(k, "rate") == 0 ) {
567   s->info.rate = atoi(v);
568  } else if ( strcmp(k, "channels") == 0 ) {
569   s->info.channels = atoi(v);
570  } else if ( strcmp(k, "bits") == 0 ) {
571   s->info.bits = atoi(v);
572  } else if ( strcmp(k, "codec") == 0 ) {
573   if ( (s->info.codec = roar_str2codec(v)) == -1 ) {
574    ROAR_ERR("add_output(*): unknown codec '%s'", v);
575    error++;
576   }
577  } else if ( strcmp(k, "q") == 0 ) {
578   q = atof(v);
579  } else if ( strcmp(k, "blocks") == 0 ) {
580   blocks = atoi(v);
581  } else if ( strcmp(k, "blocksize") == 0 ) {
582   blocksize = atoi(v);
583  } else if ( strcmp(k, "mmap") == 0 ) {
584   f_mmap = 1;
585  } else if ( strcmp(k, "subsystem") == 0 ) {
586   if ( !strcasecmp(v, "wave") || !strcasecmp(v, "waveform") ) {
587    dir = ROAR_DIR_OUTPUT;
588#ifndef ROAR_WITHOUT_DCOMP_MIDI
589   } else if ( !strcasecmp(v, "midi") ) {
590    dir = ROAR_DIR_MIDI_OUT;
591#endif
592#ifndef ROAR_WITHOUT_DCOMP_LIGHT
593   } else if ( !strcasecmp(v, "light") ) {
594    dir = ROAR_DIR_LIGHT_OUT;
595#endif
596#ifndef ROAR_WITHOUT_DCOMP_RAW
597   } else if ( !strcasecmp(v, "raw") ) {
598    dir = ROAR_DIR_RAW_OUT;
599#endif
600   } else if ( !strcasecmp(v, "complex") ) {
601    dir = ROAR_DIR_COMPLEX_OUT;
602   } else {
603    ROAR_ERR("add_output(*): unknown/unsupported subsystem '%s'", k);
604    error++;
605   }
606  // DMX:
607  } else if ( strcmp(k, "channel") == 0 ) {
608   channel  = atoi(v);
609   if ( channel < 0 || channel > 65535 ) {
610    ROAR_ERR("add_output(*): Invalide channel (not within 0..65535): %i", channel);
611    channel = -1;
612    error++;
613   }
614  } else if ( strcmp(k, "universe") == 0 ) {
615   universe = atoi(v);
616   if ( universe < 0 || universe > 255 ) {
617    ROAR_ERR("add_output(*): Invalide universe (not within 0..255): %i", universe);
618    universe = -1;
619    error++;
620   }
621
622  } else if ( strcmp(k, "name") == 0 ) {
623   if ( streams_set_name(stream, v) == -1 ) {
624    ROAR_ERR("add_output(*): Can not set Stream name");
625    error++;
626   }
627
628  } else if ( strcmp(k, "meta") == 0 ) {
629   streams_set_flag(stream, ROAR_FLAG_META);
630  } else if ( strcmp(k, "sync") == 0 ) {
631   sync = 1;
632  } else if ( strcmp(k, "primary") == 0 ) {
633   prim = 1;
634
635  } else if ( strcmp(k, "cleanmeta") == 0 ) {
636   streams_set_flag(stream, ROAR_FLAG_CLEANMETA);
637  } else if ( strcmp(k, "autoconf") == 0 ) {
638   streams_set_flag(stream, ROAR_FLAG_AUTOCONF);
639  } else if ( strcmp(k, "recsource") == 0 ) {
640   streams_set_flag(stream, ROAR_FLAG_RECSOURCE);
641  } else if ( strcmp(k, "passmixer") == 0 ) {
642   streams_set_flag(stream, ROAR_FLAG_PASSMIXER);
643  } else {
644   ROAR_ERR("add_output(*): unknown option '%s'", k);
645   error++;
646  }
647
648  if ( error ) {
649   streams_delete(stream);
650   if ( prim ) alive = 0;
651#ifdef ROAR_DRIVER_CODEC
652   if ( to_free != NULL )
653    free(to_free);
654#endif
655   return -1;
656  }
657
658  k = strtok(NULL, ",");
659 }
660
661 ROAR_DBG("add_output(drv='%s', dev='%s', opts='%s') = ?", drv, dev, opts);
662
663 // set audio info...
664 switch (dir) {
665  case ROAR_DIR_LIGHT_OUT:
666    switch (s->info.codec) {
667     case ROAR_CODEC_DMX512:
668     case -1:
669       if ( s->info.rate == -1 )
670        s->info.rate = ROAR_OUTPUT_CFREQ;
671
672       s->info.channels =   0;
673       s->info.bits     =   8;
674       s->info.codec    = ROAR_CODEC_DMX512; // in case codec == -1
675      break;
676    }
677   break;
678  case ROAR_DIR_MIDI_OUT:
679    switch (s->info.codec) {
680     case ROAR_CODEC_MIDI:
681     case -1:
682       if ( s->info.rate == -1 )
683        s->info.rate    = ROAR_MIDI_TICKS_PER_BEAT;
684
685       s->info.channels = ROAR_MIDI_CHANNELS_DEFAULT;
686       s->info.bits     = ROAR_MIDI_BITS;
687       s->info.codec    = ROAR_CODEC_MIDI; // in case codec == -1
688      break;
689    }
690   break;
691  case ROAR_DIR_RAW_OUT:
692    if ( s->info.rate == -1 )
693     s->info.rate = 0;
694    if ( s->info.bits == -1 )
695     s->info.bits = 0;
696    if ( s->info.channels == -1 )
697     s->info.channels = 0;
698    if ( s->info.codec == -1 )
699     s->info.codec = 0;
700   break;
701 }
702
703 if ( s->info.rate == -1 )
704  s->info.rate = g_sa->rate;
705 if ( s->info.bits == -1 )
706  s->info.bits = g_sa->bits;
707 if ( s->info.channels == -1 )
708  s->info.channels = g_sa->channels;
709 if ( s->info.codec == -1 )
710  s->info.codec = g_sa->codec;
711
712 ROAR_DBG("add_output(*): s->info = {.rate=%i, .bits=%i, .channels=%i, .codec=%i}", s->info.rate, s->info.bits, s->info.channels, s->info.codec);
713
714 if ( streams_set_dir(stream, dir, 1) == -1 ) {
715  streams_delete(stream);
716  return -1;
717 }
718
719#ifdef ROAR_DRIVER_CODEC
720 if ( to_free != NULL )
721  free(to_free);
722#endif
723
724 if ( s->info.codec == ROAR_CODEC_ALAW || s->info.codec == ROAR_CODEC_MULAW )
725  s->info.bits = 8; // needed to open OSS driver, will be overriden by codecfilter
726
727 ROAR_STREAM_SERVER(s)->codec_orgi = s->info.codec;
728
729 if ( driver_openvio(&(ss->vio), &(ss->driver_id), drv, dev, &(s->info), -1, ss) == -1 ) {
730  ss->driver_id = -1; // don't close a driver not opened...
731  memset(&(ss->vio), 0, sizeof(struct roar_vio_calls));
732  streams_delete(stream);
733  ROAR_DBG("add_output(drv='%s', dev='%s', opts='%s') = -1", drv, dev, opts);
734  if ( prim ) alive = 0;
735  return -1;
736 }
737
738 roar_vio_ctl(&(ss->vio), ROAR_VIO_CTL_SET_SSTREAMID, &stream); // ignore errors here
739 roar_vio_ctl(&(ss->vio), ROAR_VIO_CTL_SET_SSTREAM,   s); // ignore errors here
740
741 if ( blocks != -1 )
742  roar_vio_ctl(&(ss->vio), ROAR_VIO_CTL_SET_DBLOCKS, &blocks);
743
744 if ( blocksize != -1 )
745  roar_vio_ctl(&(ss->vio), ROAR_VIO_CTL_SET_DBLKSIZE, &blocksize);
746
747 // TODO: we shoudld *really* check for errors here...
748 if ( channel != -1 ) {
749  tu16 = channel;
750  roar_vio_ctl(&(ss->vio), ROAR_VIO_CTL_SET_DMXSCHAN, &tu16);
751 }
752 if ( universe != -1 ) {
753  tu16 = universe;
754  roar_vio_ctl(&(ss->vio), ROAR_VIO_CTL_SET_DMXUNIV, &tu16);
755 }
756
757 ROAR_DBG("add_output(*): ss->driver_id=%i", ss->driver_id);
758
759 streams_set_fh(stream, -1); // update some internal structures
760
761 if ( q > -1e6 ) {
762  ROAR_DBG("add_output(*): setting q=%f", q);
763  streams_ctl(stream, ROAR_CODECFILTER_CTL_SET_Q|ROAR_STREAM_CTL_TYPE_FLOAT, &q);
764 }
765
766 client_stream_add(g_self_client, stream);
767
768 if ( prim ) {
769  streams_mark_primary(stream);
770  s->pos_rel_id = stream;
771 }
772
773 if ( sync ) {
774  streams_set_flag(stream, ROAR_FLAG_SYNC);
775 } else {
776  streams_reset_flag(stream, ROAR_FLAG_SYNC);
777 }
778
779 if ( f_mmap )
780  streams_set_flag(stream, ROAR_FLAG_MMAP);
781
782 ROAR_DBG("add_output(*): s->info = {.rate=%i, .bits=%i, .channels=%i, .codec=%i}", s->info.rate, s->info.bits, s->info.channels, s->info.codec);
783 return 0;
784}
785
786// X11:
787#ifdef ROAR_HAVE_LIBX11
788int register_x11 (int unreg, char * sockname) {
789 struct roar_x11_connection * x11con = NULL;
790 int ret = 0;
791
792 if ( (x11con = roar_x11_connect(x11display)) == NULL )
793  return -1;
794
795 if ( unreg ) {
796  if ( roar_x11_delete_prop(x11con, "ROAR_SERVER") == -1 )
797   ret = -1;
798 } else {
799  if ( roar_x11_set_prop(x11con, "ROAR_SERVER", sockname) == -1 )
800   ret = -1;
801 }
802
803 roar_x11_disconnect(x11con);
804
805 return ret;
806}
807#endif
808
809// SLP:
810void register_slp_callback(SLPHandle hslp, SLPError errcode, void * cookie) {
811 /* return the error code in the cookie */
812 *(SLPError*)cookie = errcode;
813}
814
815int register_slp (int unreg, char * sockname) {
816#ifdef ROAR_HAVE_LIBSLP
817 static int regged = 0;
818 static char * sn = NULL;
819 SLPError err;
820 SLPError callbackerr;
821 SLPHandle hslp;
822 char addr[1024];
823 char attr[1024] = "";
824 char * location;
825
826 if ( sockname != NULL )
827  sn = sockname;
828
829 snprintf(addr, sizeof(addr), ROAR_SLP_URL_TYPE_ROAR "://%s", sn);
830
831 err = SLPOpen("en", SLP_FALSE, &hslp);
832
833 if (err != SLP_OK) {
834  ROAR_ERR("Error opening slp handle: Error #%i", err);
835  return -1;
836 }
837
838 if (!unreg) {
839
840  if ( SLPEscape(g_config->location, &location, SLP_FALSE) != SLP_OK ) {
841   ROAR_ERR("Error using SLPEscape() on server location, really bad!");
842   SLPClose(hslp);
843   return -1;
844  }
845
846  snprintf(attr, sizeof(attr), "(wave-rate=%i),(wave-channels=%i),(wave-bits=%i),"
847#ifndef ROAR_WITHOUT_DCOMP_LIGHT
848                               "(light-channels=%i),"
849#endif
850                               "(location=%s)",
851           g_sa->rate, g_sa->channels, g_sa->bits,
852#ifndef ROAR_WITHOUT_DCOMP_LIGHT
853           g_light_state.channels,
854#endif
855           location
856          );
857
858  /* Register a service with SLP */
859  err = SLPReg(hslp,
860               addr,
861               SLP_LIFETIME_MAXIMUM,
862               0,
863               attr,
864               SLP_TRUE,
865               register_slp_callback,
866               &callbackerr);
867  regged = 1;
868 } else if ( unreg && regged ) {
869  err = SLPDereg(hslp, addr, register_slp_callback, &callbackerr);
870  regged = 0;
871 } else {
872  SLPClose(hslp);
873  return -1;
874 }
875
876 /* err may contain an error code that occurred as the slp library    */
877 /* _prepared_ to make the call.                                     */
878 if ( err != SLP_OK ) {
879  ROAR_ERR("Error (de)registering service with slp: Error #%i", err);
880  return -1;
881 }
882
883 /* callbackerr may contain an error code (that was assigned through */
884 /* the callback cookie) that occurred as slp packets were sent on    */
885 /* the wire */
886 if (callbackerr != SLP_OK) {
887  ROAR_ERR("Error (de)registering service with slp: Error #%i", callbackerr);
888  return -1;
889 }
890
891 SLPClose(hslp);
892 return 0;
893#else
894 return -1;
895#endif
896}
897
898
899// MAIN:
900
901#ifdef ROAR_HAVE_MAIN_ARGS
902int main (int argc, char * argv[]) {
903#else
904int main (void) {
905#endif
906#ifdef ROAR_HAVE_MAIN_ARGS
907 int i;
908 char * k;
909#endif
910#if defined(ROAR_SUPPORT_LISTEN) && defined(ROAR_HAVE_GETUID)
911 char user_sock[80]  = {0};
912#endif
913 struct roar_audio_info sa, max_sa;
914 struct roard_config config;
915#ifdef ROAR_HAVE_FORK
916 int    daemon       = 0;
917#endif
918 int    realtime     = 0;
919 int    sysclocksync = 0;
920 char * driver    = NULL;
921 char * device    = NULL;
922#ifdef ROAR_HAVE_MAIN_ARGS
923 char * opts      = NULL;
924#endif
925// char * server = ROAR_DEFAULT_SOCK_GLOBAL;
926#ifdef ROAR_SUPPORT_LISTEN
927 int    port       = ROAR_DEFAULT_PORT;
928 char * sock_addr  = NULL;
929 int    sock_proto = ROAR_PROTO_ROARAUDIO;
930 int    sock_dir   = -1;
931 struct roar_audio_info sock_info = {0, 0, 0, 0};
932#endif
933 int               drvid;
934#ifndef ROAR_WITHOUT_DCOMP_SOURCES
935 char * s_drv     = "cf";
936 char * s_dev     = NULL;
937 char * s_con     = NULL;
938 char * s_opt     = NULL;
939 int    s_prim    = 0;
940#endif
941 char * o_drv     = getenv("ROAR_DRIVER");
942 char * o_dev     = getenv("ROAR_DEVICE");
943 char * o_opts    = NULL;
944 int    o_prim    = 0;
945 int    o_count   = 0;
946#ifndef ROAR_WITHOUT_DCOMP_LIGHT
947 int    light_channels = LIGHT_CHANNELS_DEFAULT;
948#endif
949#ifdef ROAR_DEFAULT_SOCKGRP
950 char * sock_grp  = ROAR_DEFAULT_SOCKGRP;
951#else
952 char * sock_grp  = NULL;
953#endif
954 char * sock_user = NULL;
955#ifdef ROAR_SUPPORT_LISTEN
956 int    sock_type = ROAR_SOCKET_TYPE_UNKNOWN;
957#endif
958#ifdef ROAR_HAVE_LIBSLP
959 int    reg_slp   = 0;
960#endif
961#ifdef ROAR_HAVE_LIBX11
962 int    reg_x11   = 0;
963#endif
964#ifdef ROAR_HAVE_CHROOT
965 char * chrootdir = NULL;
966#endif
967#if defined(ROAR_HAVE_SETGID) && defined(ROAR_HAVE_IO_POSIX)
968 struct group   * grp  = NULL;
969#endif
970#if defined(ROAR_HAVE_SETUID) && defined(ROAR_HAVE_IO_POSIX)
971 struct passwd  * pwd  = NULL;
972#endif
973#ifdef ROAR_HAVE_GETSERVBYNAME
974 struct servent * serv = NULL;
975#endif
976 DRIVER_USERDATA_T drvinst;
977 struct roar_client * self = NULL;
978#ifdef ROAR_HAVE_LIBDNET
979 char decnethost[80];
980#endif
981#ifdef SUPPORT_PIDFILE
982 struct roar_vio_calls pidfile_vio;
983#endif
984
985 ROAR_DBG("main(*): starting roard...");
986
987 g_standby       =  0;
988 g_autostandby   =  0;
989 alive           =  1;
990#ifdef ROAR_SUPPORT_LISTEN
991 g_no_listen     =  0;
992#else
993 g_terminate     =  1;
994#endif
995
996 g_verbose       = ROAR_DBG_INFO_NONE;
997
998 sa.bits     = ROAR_BITS_DEFAULT;
999 sa.channels = ROAR_CHANNELS_DEFAULT;
1000 sa.rate     = ROAR_RATE_DEFAULT;
1001 sa.codec    = ROAR_CODEC_DEFAULT;
1002
1003 g_sa        = &sa;
1004 g_max_sa    = &max_sa;
1005
1006 memcpy(g_max_sa, g_sa, sizeof(max_sa));
1007
1008 g_config = &config;
1009
1010 if ( init_config() == -1 ) {
1011  ROAR_ERR("Can not init default config!");
1012  return 1;
1013 }
1014
1015 // load config
1016 roar_libroar_get_config();
1017
1018#ifdef ROAR_SUPPORT_LISTEN
1019 if ( init_listening() == -1 ) {
1020  ROAR_ERR("Can not init listening sockets!");
1021  return 1;
1022 }
1023#endif
1024
1025#ifndef ROAR_WITHOUT_DCOMP_MIDI
1026 if ( midi_init_config() == -1 ) {
1027  ROAR_ERR("Can not init MIDI config!");
1028  return 1;
1029 }
1030#endif
1031
1032#ifndef ROAR_WITHOUT_DCOMP_SSYNTH
1033 if ( ssynth_init_config() == -1 ) {
1034  ROAR_ERR("Can not init ssynth config!");
1035  return 1;
1036 }
1037#endif
1038
1039#ifndef ROAR_WITHOUT_DCOMP_RDTCS
1040 if ( rdtcs_init_config() == -1 ) {
1041  ROAR_ERR("Can not init RDTCS config!");
1042  return 1;
1043 }
1044#endif
1045
1046 if ( plugins_preinit() == -1 ) {
1047  ROAR_ERR("Can not pre-init plugins!");
1048  return 1;
1049 }
1050
1051#ifdef ROAR_SUPPORT_LISTEN
1052#ifndef ROAR_TARGET_WIN32
1053 sock_addr = ROAR_DEFAULT_SOCK_GLOBAL;
1054#else
1055 sock_addr = ROAR_DEFAULT_HOST;
1056#endif
1057
1058#ifdef ROAR_HAVE_GETUID
1059 if ( getuid() != 0 && getenv("HOME") != NULL ) {
1060  snprintf(user_sock, 79, "%s/%s", (char*)getenv("HOME"), ROAR_DEFAULT_SOCK_USER);
1061  sock_addr = user_sock;
1062 }
1063#endif
1064
1065 if ( getenv("ROAR_SERVER") != NULL )
1066  sock_addr = getenv("ROAR_SERVER");
1067#endif
1068
1069 if ( clients_init() == -1 ) {
1070  ROAR_ERR("Can not init clients!");
1071  return 1;
1072 }
1073
1074 if ( streams_init() == -1 ) {
1075  ROAR_ERR("Can not init streams!");
1076  return 1;
1077 }
1078
1079 if ( (g_self_client = clients_new()) == -1 ) {
1080  ROAR_ERR("Can not create self client!");
1081  return 1;
1082 }
1083
1084#ifndef ROAR_WITHOUT_DCOMP_SOURCES
1085 if ( sources_init() == -1 ) {
1086  ROAR_ERR("Can not init sources!");
1087  return 1;
1088 }
1089
1090 if ( (sources_set_client(g_self_client)) == -1 ) {
1091  ROAR_ERR("Can not init set source client!");
1092  return 1;
1093 }
1094#endif
1095
1096#ifdef ROAR_HAVE_MAIN_ARGS
1097 for (i = 1; i < argc; i++) {
1098  k = argv[i];
1099
1100  if ( strcmp(k, "-h") == 0 || strcmp(k, "--help") == 0 ) {
1101   usage();
1102   return 0;
1103
1104  } else if ( strcmp(k, "--start") == 0 ) {
1105   // this is a no op
1106  } else if ( strcmp(k, "--restart") == 0 ) {
1107#ifdef ROAR_SUPPORT_LISTEN
1108   if ( restart_server(sock_addr, 1) == -1 ) {
1109    ROAR_WARN("Can not terminate old server (not running at %s?), tring to continue anyway", sock_addr);
1110   }
1111#else
1112   ROAR_ERR("--restart not supported");
1113#endif
1114  } else if ( strcmp(k, "--shutdown") == 0 ) {
1115#ifdef ROAR_SUPPORT_LISTEN
1116   if ( restart_server(sock_addr, 1) == -1 ) {
1117    ROAR_WARN("Can not terminate old server (not running at %s?)", sock_addr);
1118    return 1;
1119   }
1120   return 0;
1121#else
1122   ROAR_ERR("--shutdown not supported");
1123   return 1;
1124#endif
1125  } else if ( strcmp(k, "--stop") == 0 ) {
1126#ifdef ROAR_SUPPORT_LISTEN
1127   if ( restart_server(sock_addr, 0) == -1 ) {
1128    ROAR_WARN("Can not stop old server (not running at %s?)", sock_addr);
1129    return 1;
1130   }
1131   return 0;
1132#else
1133   ROAR_ERR("--stop not supported");
1134   return 1;
1135#endif
1136
1137
1138  } else if ( strcmp(k, "--demon") == 0 || strcmp(k, "--daemon") == 0 ) {
1139#ifdef ROAR_HAVE_FORK
1140   daemon = 1;
1141#else
1142   ROAR_ERR("--daemon not supported");
1143#endif
1144  } else if ( strcmp(k, "--verbose") == 0 ) {
1145   g_verbose++;
1146  } else if ( strcmp(k, "--terminate") == 0 ) {
1147   g_terminate = 1;
1148  } else if ( strcmp(k, "--sysclocksync") == 0 ) {
1149   sysclocksync = 1000;
1150  } else if ( strcmp(k, "--realtime") == 0 ) {
1151   realtime++;
1152  } else if ( strcmp(k, "--chroot") == 0 ) {
1153#ifdef ROAR_HAVE_CHROOT
1154   chrootdir = argv[++i];
1155#else
1156   ROAR_ERR("--chroot not supported");
1157   i++;
1158#endif
1159  } else if ( strcmp(k, "--setgid") == 0 ) {
1160#ifdef ROAR_HAVE_SETGID
1161   setids |= R_SETGID;
1162#else
1163   ROAR_ERR("--setgid not supported");
1164#endif
1165  } else if ( strcmp(k, "--setuid") == 0 ) {
1166#ifdef ROAR_HAVE_SETUID
1167   setids |= R_SETUID;
1168#else
1169   ROAR_ERR("--setuid not supported");
1170#endif
1171  } else if ( strcmp(k, "--location") == 0 ) {
1172   g_config->location = argv[++i];
1173  } else if ( strcmp(k, "--pidfile") == 0 ) {
1174#ifdef SUPPORT_PIDFILE
1175   pidfile = argv[++i];
1176#else
1177   ROAR_ERR("--pidfile not supported");
1178   i++;
1179#endif
1180
1181  } else if ( strcmp(k, "--plugin-load") == 0 ) {
1182   if ( plugins_load(argv[++i]) == -1 ) {
1183    ROAR_ERR("Can not load plugin");
1184   }
1185
1186  } else if ( strcmp(k, "--list-cf") == 0 ) {
1187   print_codecfilterlist();
1188   return 0;
1189
1190  } else if ( strcmp(k, "-R") == 0 || strcmp(k, "--rate") == 0 ) {
1191   sa.rate = atoi(argv[++i]);
1192  } else if ( strcmp(k, "-B") == 0 || strcmp(k, "--bits") == 0 ) {
1193   sa.bits = atoi(argv[++i]);
1194  } else if ( strcmp(k, "-C") == 0 || strcmp(k, "--chans") == 0 ) {
1195   sa.channels = atoi(argv[++i]);
1196
1197  } else if ( strcmp(k, "--stream-flags") == 0 ) {
1198   if ( update_stream_flags(argv[++i]) == -1 ) {
1199    ROAR_ERR("Can not set stream flags");
1200    return 1;
1201   }
1202
1203  } else if ( strcmp(k, "-d") == 0 || strcmp(k, "--driver") == 0 ) {
1204   driver = argv[++i];
1205   if ( strcmp(driver, "list") == 0 ) {
1206    ROAR_WARN("The option is obsolete, use --list-driver!");
1207    print_driverlist();
1208    return 0;
1209   }
1210  } else if ( strcmp(k, "-D") == 0 || strcmp(k, "--device") == 0 ) {
1211   device = argv[++i];
1212  } else if ( strcmp(k, "-dO") == 0 ) {
1213   opts = argv[++i];
1214  } else if ( strcmp(k, "--list-driver") == 0 ) {
1215   print_driverlist();
1216   return 0;
1217
1218  } else if ( strcmp(k, "-o") == 0 || strcmp(k, "--odriver") == 0 ) {
1219   o_drv  = argv[++i];
1220  } else if ( strcmp(k, "-O") == 0 || strcmp(k, "--odevice") == 0 ) {
1221   o_dev  = argv[++i];
1222  } else if ( strcmp(k, "-oO") == 0 ) {
1223   o_opts = argv[++i];
1224  } else if ( strcmp(k, "-oP") == 0 ) {
1225   o_prim = 1;
1226  } else if ( strcmp(k, "-oN") == 0 ) {
1227   if ( add_output(o_drv, o_dev, o_opts, o_prim, o_count) != -1 )
1228    o_count++;
1229
1230   o_drv  = o_dev = o_opts = NULL;
1231   o_prim = 0;
1232
1233  } else if ( strcmp(k, "-s") == 0 || strcmp(k, "--source") == 0 ) {
1234#ifndef ROAR_WITHOUT_DCOMP_SOURCES
1235   s_drv = argv[++i];
1236#else
1237   ROAR_ERR("main(*): No support for sources compiled in");
1238#endif
1239  } else if ( strcmp(k, "-S") == 0 ) {
1240#ifndef ROAR_WITHOUT_DCOMP_SOURCES
1241   s_dev = argv[++i];
1242#else
1243   ROAR_ERR("main(*): No support for sources compiled in");
1244#endif
1245  } else if ( strcmp(k, "-sO") == 0 ) {
1246#ifndef ROAR_WITHOUT_DCOMP_SOURCES
1247   s_opt = argv[++i];
1248#else
1249   ROAR_ERR("main(*): No support for sources compiled in");
1250#endif
1251  } else if ( strcmp(k, "-sC") == 0 ) {
1252#ifndef ROAR_WITHOUT_DCOMP_SOURCES
1253   s_con = argv[++i];
1254#else
1255   ROAR_ERR("main(*): No support for sources compiled in");
1256#endif
1257  } else if ( strcmp(k, "-sP") == 0 ) {
1258#ifndef ROAR_WITHOUT_DCOMP_SOURCES
1259   s_prim = 1;
1260#else
1261   ROAR_ERR("main(*): No support for sources compiled in");
1262#endif
1263  } else if ( strcmp(k, "-sN") == 0 ) {
1264#ifndef ROAR_WITHOUT_DCOMP_SOURCES
1265   if ( sources_add(s_drv, s_dev, s_con, s_opt, s_prim) == -1 ) {
1266    ROAR_ERR("main(*): adding source '%s' via '%s' failed!", s_dev, s_drv);
1267   }
1268   s_opt = s_dev = s_con = NULL;
1269   s_drv = "cf";
1270   s_prim = 0;
1271#else
1272   ROAR_ERR("main(*): No support for sources compiled in");
1273#endif
1274  } else if ( strcmp(k, "--list-sources") == 0 ) {
1275#ifndef ROAR_WITHOUT_DCOMP_SOURCES
1276   print_sourcelist();
1277   return 0;
1278#else
1279   ROAR_ERR("main(*): No support for sources compiled in");
1280   return 1;
1281#endif
1282
1283  } else if ( strcmp(k, "--light-channels") == 0 ) {
1284#ifndef ROAR_WITHOUT_DCOMP_LIGHT
1285   light_channels = atoi(argv[++i]);
1286#else
1287   ROAR_WARN("main(*): no light subsystem compiled in");
1288#endif
1289
1290  } else if ( strcmp(k, "--rds-pi") == 0 ) {
1291#ifndef ROAR_WITHOUT_DCOMP_RDTCS
1292   g_rdtcs.rds.pi = atoi(argv[++i]);
1293#else
1294   ROAR_WARN("main(*): no RDTCS subsystem compiled in");
1295#endif
1296  } else if ( strcmp(k, "--rds-ps") == 0 ) {
1297#ifndef ROAR_WITHOUT_DCOMP_RDTCS
1298   if ( rdtcs_rds_set_ps(argv[++i]) == -1 ) {
1299    ROAR_ERR("Can not set RDS PS to '%s' (longer than 8 chars?)", argv[i]);
1300    return 1;
1301   }
1302#else
1303   ROAR_WARN("main(*): no RDTCS subsystem compiled in");
1304#endif
1305  } else if ( strcmp(k, "--rds-pty") == 0 ) {
1306#ifndef ROAR_WITHOUT_DCOMP_RDTCS
1307   if ( rdtcs_rds_set_pty(argv[++i]) == -1 ) {
1308    ROAR_ERR("Can not set RDS PTY to '%s'", argv[i]);
1309    return 1;
1310   }
1311#else
1312   ROAR_WARN("main(*): no RDTCS subsystem compiled in");
1313#endif
1314  } else if ( strcmp(k, "--rds-tp") == 0 ) {
1315#ifndef ROAR_WITHOUT_DCOMP_RDTCS
1316   if ( rdtcs_rds_set_flag(RDTCS_RDS_FLAG_TP, 0) == -1 ) {
1317    ROAR_ERR("Can not set RDS TP flag");
1318    return 1;
1319   }
1320#else
1321   ROAR_WARN("main(*): no RDTCS subsystem compiled in");
1322#endif
1323  } else if ( strcmp(k, "--rds-ct") == 0 ) {
1324#ifndef ROAR_WITHOUT_DCOMP_RDTCS
1325   if ( rdtcs_rds_set_flag(RDTCS_RDS_FLAG_CT, 0) == -1 ) {
1326    ROAR_ERR("Can not set RDS CT flag");
1327    return 1;
1328   }
1329#else
1330   ROAR_WARN("main(*): no RDTCS subsystem compiled in");
1331#endif
1332
1333
1334  } else if ( strcmp(k, "--midi-no-console") == 0 ) {
1335#ifndef ROAR_WITHOUT_DCOMP_CB
1336   midi_config.init_cb = 0;
1337#else
1338   // no warning here as this is the disable option
1339#endif
1340  } else if ( strcmp(k, "--midi-console-enable") == 0 ) {
1341#ifndef ROAR_WITHOUT_DCOMP_CB
1342   midi_config.init_cb = 1;
1343#else
1344   ROAR_ERR("main(*): No support for MIDI subsystem part CB compiled in");
1345#endif
1346  } else if ( strcmp(k, "--midi-console") == 0 ) {
1347#ifndef ROAR_WITHOUT_DCOMP_CB
1348   midi_config.console_dev = argv[++i];
1349   midi_config.init_cb = 1;
1350#else
1351   ROAR_ERR("main(*): No support for MIDI subsystem part CB compiled in");
1352#endif
1353
1354  } else if ( strcmp(k, "--ssynth-enable") == 0 ) {
1355#ifndef ROAR_WITHOUT_DCOMP_SSYNTH
1356   ssynth_conf.enable = 1;
1357#else
1358   ROAR_ERR("main(*): No support for ssynth compiled in");
1359#endif
1360  } else if ( strcmp(k, "--ssynth-disable") == 0 ) {
1361#ifndef ROAR_WITHOUT_DCOMP_SSYNTH
1362   ssynth_conf.enable = 0;
1363#else
1364   // we can safely ignore the disable
1365#endif
1366
1367  } else if ( strcmp(k, "--x11-display") == 0 || strcmp(k, "--display") == 0 ) {
1368#ifdef ROAR_HAVE_LIBX11
1369   x11display = argv[++i];
1370#else
1371   ROAR_ERR("No X11 support compiled in!");
1372   return 1;
1373#endif
1374
1375
1376  } else if ( strcmp(k, "-p") == 0 || strcmp(k, "--port") == 0 ) {
1377   // This is only usefull in INET not UNIX mode.
1378#ifdef ROAR_SUPPORT_LISTEN
1379   if ( *sock_addr == '/' )
1380    sock_addr = ROAR_DEFAULT_HOST;
1381
1382   errno = 0;
1383   if ( (port = atoi(argv[++i])) < 1 ) {
1384#ifdef ROAR_HAVE_GETSERVBYNAME
1385    if ( (serv = getservbyname(argv[i], "tcp")) == NULL ) {
1386     ROAR_ERR("Unknown service: %s: %s", argv[i], strerror(errno));
1387     return 1;
1388    }
1389    // NOTE: we need to use ROAR_NET2HOST16() here even if s_port is of type int!
1390    ROAR_DBG("main(*): serv = {s_name='%s', s_aliases={...}, s_port=%i, s_proto='%s'}",
1391            serv->s_name, ROAR_NET2HOST16(serv->s_port), serv->s_proto);
1392    port = ROAR_NET2HOST16(serv->s_port);
1393#else
1394    ROAR_ERR("invalite port number: %s", argv[i]);
1395    return 1;
1396#endif
1397   }
1398#endif
1399  } else if ( strcmp(k, "-b") == 0 || strcmp(k, "--bind") == 0 || strcmp(k, "--sock") == 0 ) {
1400#ifdef ROAR_SUPPORT_LISTEN
1401   sock_addr = argv[++i];
1402#endif
1403
1404  } else if ( strcmp(k, "--proto") == 0 ) {
1405#ifdef ROAR_SUPPORT_LISTEN
1406   if ( (sock_proto = roar_str2proto(argv[++i])) == -1 ) {
1407    ROAR_ERR("Unknown protocol: %s", argv[i]);
1408    return 1;
1409   }
1410#endif
1411  } else if ( strcmp(k, "--proto-dir") == 0 ) {
1412#ifdef ROAR_SUPPORT_LISTEN
1413   if ( (sock_dir = roar_str2dir(argv[++i])) == -1 ) {
1414    ROAR_ERR("Unknown stream direction: %s", argv[i]);
1415    return 1;
1416   }
1417#endif
1418  } else if ( strcmp(k, "--proto-rate") == 0 ) {
1419#ifdef ROAR_SUPPORT_LISTEN
1420   sock_info.rate = atoi(argv[++i]);
1421#endif
1422  } else if ( strcmp(k, "--proto-bits") == 0 ) {
1423#ifdef ROAR_SUPPORT_LISTEN
1424   sock_info.bits = atoi(argv[++i]);
1425#endif
1426  } else if ( strcmp(k, "--proto-chans") == 0 ) {
1427#ifdef ROAR_SUPPORT_LISTEN
1428   sock_info.channels = atoi(argv[++i]);
1429#endif
1430  } else if ( strcmp(k, "--proto-codec") == 0 ) {
1431#ifdef ROAR_SUPPORT_LISTEN
1432   if ( (sock_info.codec = roar_str2codec(argv[++i])) == -1 ) {
1433    ROAR_ERR("Unknown codec: %s", argv[i]);
1434    return 1;
1435   }
1436#endif
1437
1438
1439  } else if ( strcmp(k, "--list-proto") == 0 ) {
1440   list_proto();
1441   return 0;
1442
1443  } else if ( strcmp(k, "-t") == 0 || strcmp(k, "--tcp") == 0 ) {
1444#ifdef ROAR_SUPPORT_LISTEN
1445   if ( sock_type != ROAR_SOCKET_TYPE_TCP && sock_type != ROAR_SOCKET_TYPE_TCP6 )
1446    sock_type = ROAR_SOCKET_TYPE_TCP;
1447
1448   if ( *sock_addr == '/' )
1449    sock_addr = ROAR_DEFAULT_HOST;
1450#endif
1451
1452  } else if ( strcmp(k, "-4") == 0 ) {
1453#ifdef ROAR_SUPPORT_LISTEN
1454   sock_type = ROAR_SOCKET_TYPE_TCP;
1455   if ( *sock_addr == '/' )
1456    sock_addr = ROAR_DEFAULT_HOST;
1457#endif
1458  } else if ( strcmp(k, "-6") == 0 ) {
1459#ifdef ROAR_SUPPORT_LISTEN
1460#ifdef PF_INET6
1461   sock_type = ROAR_SOCKET_TYPE_TCP6;
1462   if ( *sock_addr == '/' )
1463    sock_addr = ROAR_DEFAULT_HOST;
1464#else
1465    ROAR_ERR("No IPv6 support compiled in!");
1466    return 1;
1467#endif
1468#endif
1469
1470  } else if ( strcmp(k, "-u") == 0 || strcmp(k, "--unix") == 0 ) {
1471#ifdef ROAR_SUPPORT_LISTEN
1472   // ignore this case as it is the default behavor.
1473   sock_type = ROAR_SOCKET_TYPE_UNIX;
1474#endif
1475
1476  } else if ( strcmp(k, "-n") == 0 || strcmp(k, "--decnet") == 0 ) {
1477#ifdef ROAR_SUPPORT_LISTEN
1478#ifdef ROAR_HAVE_LIBDNET
1479    port   = ROAR_DEFAULT_NUM;
1480    strcpy(decnethost, ROAR_DEFAULT_LISTEN_OBJECT);
1481    sock_addr = decnethost;
1482    sock_type = ROAR_SOCKET_TYPE_DECNET;
1483#else
1484    ROAR_ERR("No DECnet support compiled in!");
1485    return 1;
1486#endif
1487#endif
1488  } else if ( strcmp(k, "--new-sock") == 0 ) {
1489#ifdef ROAR_SUPPORT_LISTEN
1490   if ( add_listen(sock_addr, port, sock_type, sock_user, sock_grp, sock_proto, sock_dir, &sock_info) != 0 ) {
1491    ROAR_ERR("Can not open listen socket!");
1492    return 1;
1493   }
1494#endif
1495
1496  } else if ( strcmp(k, "--slp") == 0 ) {
1497#ifdef ROAR_HAVE_LIBSLP
1498   reg_slp = 1;
1499#else
1500   ROAR_ERR("No OpenSLP support compiled in!");
1501   return 1;
1502#endif
1503
1504  } else if ( strcmp(k, "--x11") == 0 ) {
1505#ifdef ROAR_HAVE_LIBX11
1506   reg_x11 = 1;
1507#else
1508   ROAR_ERR("No X11 support compiled in!");
1509   return 1;
1510#endif
1511
1512
1513  } else if ( strcmp(k, "--jumbo-mtu") == 0 ) {
1514   g_config->jumbo_mtu = atoi(argv[++i]);
1515
1516  } else if ( strcmp(k, "-G") == 0 ) {
1517   sock_grp  = argv[++i];
1518  } else if ( strcmp(k, "-U") == 0 ) {
1519   sock_user = argv[++i];
1520
1521  } else if ( strcmp(k, "--no-listen") == 0 ) {
1522#ifdef ROAR_SUPPORT_LISTEN
1523   sock_addr   = "";
1524   g_terminate = 1;
1525   g_no_listen = 1;
1526#endif
1527  } else if ( strcmp(k, "--client-fh") == 0 ) {
1528   if ( clients_set_fh(clients_new(), atoi(argv[++i])) == -1 ) {
1529    ROAR_ERR("main(*): Can not set client's fh");
1530    return 1;
1531   }
1532  } else if ( strcmp(k, "--close-fh") == 0 ) {
1533#ifdef ROAR_HAVE_IO_POSIX
1534   close(atoi(argv[++i]));
1535#else
1536   i++;
1537   ROAR_WARN("can not close file handle %s (closing not supported)", argv[i]);
1538#endif
1539
1540  } else if ( strcmp(k, "--standby") == 0 ) {
1541   g_standby = 1;
1542  } else if ( strcmp(k, "--auto-standby") == 0 ) {
1543   g_autostandby = 1;
1544  } else {
1545   usage();
1546   return 1;
1547  }
1548
1549 }
1550#endif
1551
1552#ifndef ROAR_WITHOUT_DCOMP_SOURCES
1553 if ( s_dev != NULL ) {
1554  if ( sources_add(s_drv, s_dev, s_con, s_opt, s_prim) == -1 ) {
1555   ROAR_ERR("main(*): adding source '%s' via '%s' failed!", s_dev, s_drv);
1556  }
1557 }
1558#endif
1559
1560 add_output(o_drv, o_dev, o_opts, o_prim, o_count);
1561
1562 ROAR_DBG("Server config: rate=%i, bits=%i, chans=%i", sa.rate, sa.bits, sa.channels);
1563
1564 if ( waveform_init() == -1 ) {
1565  ROAR_ERR("Can not initialize Waveform subsystem");
1566  return 1;
1567 }
1568
1569#ifndef ROAR_WITHOUT_DCOMP_MIDI
1570 if ( midi_init() == -1 ) {
1571  ROAR_ERR("Can not initialize MIDI subsystem");
1572 }
1573#endif
1574
1575#ifndef ROAR_WITHOUT_DCOMP_SSYNTH
1576 if ( ssynth_init() == -1 ) {
1577  ROAR_ERR("Can not initialize ssynth subsystem");
1578 }
1579#endif
1580
1581#ifndef ROAR_WITHOUT_DCOMP_LIGHT
1582 if ( light_init(light_channels) == -1 ) {
1583  ROAR_ERR("Can not initialize light control subsystem");
1584 }
1585#endif
1586
1587#ifndef ROAR_WITHOUT_DCOMP_RDTCS
1588 if ( rdtcs_init() == -1 ) {
1589  ROAR_ERR("Can not initialize RDTCS subsystem");
1590 }
1591#endif
1592
1593 if ( plugins_init() == -1 ) {
1594  ROAR_ERR("Can not initialize plugins");
1595 }
1596
1597#ifdef ROAR_SUPPORT_LISTEN
1598 if ( add_listen(sock_addr, port, sock_type, sock_user, sock_grp, sock_proto, sock_dir, &sock_info) != 0 ) {
1599  ROAR_ERR("Can not open listen socket!");
1600  return 1;
1601 }
1602#endif
1603
1604 if ( output_buffer_init(&sa) == -1 ) {
1605  ROAR_ERR("Can not init output buffer!");
1606  return 1;
1607 }
1608
1609 if ( driver == NULL ) {
1610  driver = "null";
1611 } else {
1612  ROAR_WARN("Usage of old driver interface. use -o not -d!");
1613 }
1614
1615 if ( driver_open(&drvinst, &drvid, driver, device, &sa) == -1 ) {
1616  ROAR_ERR("Can not open output driver!");
1617  return 1;
1618 }
1619
1620 if ( samples_init() == -1 ) {
1621  ROAR_ERR("Can not init samples!");
1622  return 1;
1623 }
1624
1625
1626 // we should handle this on microcontrollers, too.
1627#if !defined(ROAR_TARGET_MICROCONTROLLER) && !defined(ROAR_TARGET_WIN32)
1628 signal(SIGINT,  on_sig_int);
1629 signal(SIGTERM, on_sig_term);
1630 signal(SIGCHLD, on_sig_chld);
1631 signal(SIGUSR1, on_sig_usr1);
1632 signal(SIGPIPE, SIG_IGN);  // ignore broken pipes
1633#endif
1634
1635 if ( realtime ) {
1636#ifdef DEBUG
1637  ROAR_WARN("compiled with -DDEBUG but realtime is enabled: for real realtime support compiel without -DDEBUG");
1638#endif
1639
1640#ifdef ROAR_HAVE_NICE
1641  errno = 0;
1642  nice(-5*realtime); // -5 for each --realtime
1643  if ( errno ) {
1644   ROAR_WARN("Can not decrease nice value by %i: %s", 5*realtime, strerror(errno));
1645  }
1646#else
1647  ROAR_WARN("Can not decrease nice value by %i: %s", 5*realtime, strerror(errno));
1648#endif
1649/*
1650#ifdef __linux__
1651  if ( ioprio_set(IOPRIO_WHO_PROCESS, getpid(), IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 0)) == -1 )
1652   ROAR_WARN("Can not set io priority: %s", strerror(errno));
1653#endif
1654*/
1655 }
1656
1657#if defined(ROAR_HAVE_SETGID) && defined(ROAR_HAVE_IO_POSIX)
1658 if ( setids & R_SETGID ) {
1659  if ( sock_grp == NULL ) {
1660   ROAR_ERR("Can not set GID if no groupname is supplied");
1661   return 1;
1662  }
1663  if ( (grp = getgrnam(sock_grp)) == NULL ) {
1664   ROAR_ERR("Can not get GID for group %s: %s", sock_grp, strerror(errno));
1665   return 1;
1666  }
1667  if ( setgroups(0, (const gid_t *) NULL) == -1 ) {
1668   ROAR_ERR("Can not clear supplementary group IDs: %s", strerror(errno));
1669  }
1670  if ( !grp || setgid(grp->gr_gid) == -1 ) {
1671   ROAR_ERR("Can not set GroupID: %s", strerror(errno));
1672  }
1673 }
1674#endif
1675
1676
1677 clients_set_pid(g_self_client, getpid());
1678#ifdef ROAR_HAVE_GETUID
1679 clients_set_uid(g_self_client, getuid());
1680#endif
1681#ifdef ROAR_HAVE_GETGID
1682 clients_set_gid(g_self_client, getgid());
1683#endif
1684 clients_get(g_self_client, &self);
1685
1686 if ( self == NULL ) {
1687  ROAR_ERR("Can not get self client!");
1688  return 1;
1689 }
1690
1691 strcpy(self->name, "RoarAudio daemon internal");
1692
1693 if ( roar_nnode_free(&(self->nnode)) == -1 )
1694  return 1;
1695
1696 // not fully correct but ok as workaorund
1697 // so tools assume that roard runs on the same machine as
1698 // they in case they use AF_UNIX:
1699 if ( roar_nnode_new(&(self->nnode), ROAR_SOCKET_TYPE_UNIX) == -1 )
1700  return 1;
1701
1702#ifdef ROAR_HAVE_FORK
1703 if ( daemon ) {
1704  close(ROAR_STDIN );
1705  close(ROAR_STDOUT);
1706  close(ROAR_STDERR);
1707
1708  if ( fork() )
1709   ROAR_U_EXIT(0);
1710
1711#ifdef ROAR_HAVE_SETSID
1712  setsid();
1713#endif
1714  clients_set_pid(g_self_client, getpid()); // reset pid as it changed
1715 }
1716#endif
1717
1718#if defined(ROAR_HAVE_SETUID) && defined(ROAR_HAVE_IO_POSIX)
1719 // early test for UID as we need this for the pidfile and the setuid()
1720 if ( sock_user != NULL ) {
1721  if ( (pwd = getpwnam(sock_user)) == NULL ) {
1722   ROAR_ERR("Can not get UID for user %s: %s", sock_user, strerror(errno));
1723   return 1;
1724  }
1725 }
1726#endif
1727
1728#ifdef SUPPORT_PIDFILE
1729 if ( pidfile != NULL ) {
1730  if ( roar_vio_open_file(&pidfile_vio, pidfile, O_WRONLY|O_CREAT, 0644) == -1 ) {
1731   ROAR_ERR("Can not write pidfile: %s", pidfile);
1732  } else {
1733   roar_vio_printf(&pidfile_vio, "%i\n", getpid());
1734   roar_vio_close(&pidfile_vio);
1735  }
1736#if defined(ROAR_HAVE_SETGID) && defined(ROAR_HAVE_SETUID) && defined(ROAR_HAVE_IO_POSIX)
1737  if ( pwd || grp ) {
1738   if ( chown(pidfile, pwd ? pwd->pw_uid : -1, grp ? grp->gr_gid : -1) == -1 ) {
1739    ROAR_WARN("Can not change ownership of pidfile: %s: %s", pidfile, strerror(errno));
1740   }
1741  }
1742  if ( chmod(pidfile, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) == -1 ) {
1743   ROAR_WARN("Can not change permissions of pidfile: %s: %s", pidfile, strerror(errno));
1744  }
1745#endif
1746 }
1747#endif
1748
1749#ifdef ROAR_HAVE_CHROOT
1750 if (chrootdir) {
1751  if ( chroot(chrootdir) == -1 ) {
1752   ROAR_ERR("Can not chroot to %s: %s", chrootdir, strerror(errno));
1753   return 2;
1754  }
1755  if ( chdir("/") == -1 ) {
1756   ROAR_ERR("Can not chdir to /: %s", strerror(errno));
1757   return 2;
1758  }
1759 }
1760#endif
1761
1762#if defined(ROAR_HAVE_SETUID) && defined(ROAR_HAVE_IO_POSIX)
1763 if ( setids & R_SETUID ) {
1764  if ( sock_user == NULL ) {
1765   ROAR_ERR("Can not set UID if no username is supplied");
1766   return 1;
1767  }
1768  if ( !pwd || setuid(pwd->pw_uid) == -1 ) {
1769   ROAR_ERR("Can not set UserID: %s", strerror(errno));
1770   return 3;
1771  }
1772#ifdef ROAR_HAVE_GETUID
1773  clients_set_uid(g_self_client, getuid());
1774#endif
1775 }
1776#endif
1777
1778 // Register with OpenSLP:
1779#ifdef ROAR_HAVE_LIBSLP
1780 if ( reg_slp ) {
1781  register_slp(0, sock_addr);
1782 }
1783#endif
1784
1785#ifdef ROAR_HAVE_LIBX11
1786 if ( reg_x11 ) {
1787  register_x11(0, sock_addr);
1788 }
1789#endif
1790
1791 // start main loop...
1792 main_loop(drvid, drvinst, &sa, sysclocksync);
1793
1794 // clean up.
1795 clean_quit_prep();
1796 driver_close(drvinst, drvid);
1797 output_buffer_free();
1798
1799 return 0;
1800}
1801
1802void cleanup_listen_socket (int terminate) {
1803 int i;
1804
1805 // Deregister from SLP:
1806#ifdef ROAR_HAVE_LIBSLP
1807 register_slp(1, NULL);
1808#endif
1809
1810#ifdef ROAR_HAVE_LIBX11
1811 register_x11(1, NULL);
1812#endif
1813
1814#ifdef ROAR_SUPPORT_LISTEN
1815 for (i = 0; i < ROAR_MAX_LISTEN_SOCKETS; i++) {
1816  if ( g_listen[i].socket != -1 ) {
1817#ifdef ROAR_HAVE_IO_POSIX
1818   close(g_listen[i].socket);
1819#endif // #else is useless because we are in void context.
1820
1821   g_listen[i].socket = -1;
1822
1823#ifdef ROAR_HAVE_UNIX
1824   if ( server[i] != NULL )
1825    if ( *(server[i]) == '/' )
1826     unlink(server[i]);
1827#endif
1828  }
1829 }
1830
1831#endif
1832
1833 if ( terminate )
1834  g_terminate = 1;
1835}
1836
1837void clean_quit_prep (void) {
1838 cleanup_listen_socket(0);
1839
1840 plugins_free();
1841
1842#ifndef ROAR_WITHOUT_DCOMP_SOURCES
1843 sources_free();
1844#endif
1845 streams_free();
1846 clients_free();
1847#ifndef ROAR_WITHOUT_DCOMP_SSYNTH
1848 ssynth_free();
1849#endif
1850#ifndef ROAR_WITHOUT_DCOMP_CB
1851 midi_cb_stop(); // stop console beep
1852#endif
1853#ifndef ROAR_WITHOUT_DCOMP_MIDI
1854 midi_free();
1855#endif
1856#ifndef ROAR_WITHOUT_DCOMP_LIGHT
1857 light_free();
1858#endif
1859#ifndef ROAR_WITHOUT_DCOMP_RDTCS
1860 rdtcs_free();
1861#endif
1862
1863 waveform_free();
1864
1865#ifdef SUPPORT_PIDFILE
1866 if ( pidfile != NULL )
1867  unlink(pidfile);
1868#endif
1869}
1870
1871void clean_quit (void) {
1872 clean_quit_prep();
1873// driver_close(drvinst, drvid);
1874// output_buffer_free();
1875 exit(0);
1876}
1877
1878//ll
Note: See TracBrowser for help on using the repository browser.