source: roaraudio/libroar/basic.c @ 5369:dfc6cbfe8025

Last change on this file since 5369:dfc6cbfe8025 was 5369:dfc6cbfe8025, checked in by phi, 12 years ago

moved +fork out of socket.c into basic.c, now also support +fork=d:daemon and +fork=!daemon_command

File size: 16.0 KB
Line 
1//basic.c:
2
3/*
4 *      Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2008-2011
5 *
6 *  This file is part of libroar 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 *  libroar 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, 51 Franklin Street, Fifth Floor,
22 *  Boston, MA 02110-1301, USA.
23 *
24 *  NOTE for everyone want's to change something and send patches:
25 *  read README and HACKING! There a addition information on
26 *  the license of this document you need to read before you send
27 *  any patches.
28 *
29 *  NOTE for uses of non-GPL (LGPL,...) software using libesd, libartsc
30 *  or libpulse*:
31 *  The libs libroaresd, libroararts and libroarpulse link this lib
32 *  and are therefore GPL. Because of this it may be illigal to use
33 *  them with any software that uses libesd, libartsc or libpulse*.
34 */
35
36#include "libroar.h"
37
38static int _start_server(struct roar_connection * con, const char * server, int type, int flags, uint_least32_t timeout) {
39#if !defined(ROAR_TARGET_WIN32) && !defined(ROAR_TARGET_MICROCONTROLLER)
40 enum {
41  NORMAL = 0,
42  SYSTEM = 1
43 } mode = NORMAL;
44 const char * daemonimage = NULL;
45 int socks[2];
46 int r;
47 char fhstr[12];
48
49 if ( !strncmp(server, "+fork=", 6) ) {
50  server += 6;
51  if ( server[0] == 0 ) {
52   // no special case, we just ignore it.
53  } else if ( server[0] == 'd' && server[1] == ':' ) {
54   server += 2;
55   daemonimage = server;
56  } else if ( server[0] == '!' ) {
57   server += 1;
58   daemonimage = server;
59   mode = SYSTEM;
60  } else {
61   roar_err_set(ROAR_ERROR_ILLSEQ);
62   return -1;
63  }
64 }
65
66 // TODO: FIXME: we should move this into the config structure.
67 if ( daemonimage == NULL )
68  daemonimage = getenv("ROAR_DAEMONIMAGE");
69
70 if ( daemonimage == NULL || *daemonimage == 0 )
71  daemonimage = "roard";
72
73 if ( socketpair(AF_UNIX, SOCK_STREAM, 0, socks) == -1 ) {
74  roar_err_from_errno();
75  return -1;
76 }
77
78 r = fork();
79
80 if ( r == -1 ) { // error!
81  roar_err_from_errno();
82  ROAR_ERR("roar_socket_open_fork(*): Can not fork: %s", strerror(errno));
83  close(socks[0]);
84  close(socks[1]);
85  return -1;
86 } else if ( r == 0 ) { // we are the child
87  close(socks[0]);
88
89  close(ROAR_STDIN ); // we do not want roard to have any standard input
90  close(ROAR_STDOUT); // STDOUT is also not needed, so we close it,
91                      // but STDERR we keep open for error messages.
92
93  snprintf(fhstr, sizeof(fhstr), "%i", socks[1]);
94
95  switch (mode) {
96   case NORMAL:
97     execlp(daemonimage, daemonimage, "--no-listen", "--client-fh", fhstr, (_LIBROAR_GOOD_CAST char*)NULL);
98    break;
99   case SYSTEM:
100     dup2(socks[1], ROAR_STDIN );
101     dup2(socks[1], ROAR_STDOUT);
102     execl("/bin/sh", "/bin/sh", "-c", daemonimage, (_LIBROAR_GOOD_CAST char*)NULL);
103     execlp("sh", "sh", "-c", daemonimage, (_LIBROAR_GOOD_CAST char*)NULL);
104    break;
105  }
106
107  // we are still alive?
108  ROAR_ERR("roar_socket_open_fork(*): alive after exec(), that's bad!");
109  _exit(1);
110 } else { // we are the parent
111  close(socks[1]);
112  if ( roar_vio_open_fh_socket(con->viocon, socks[0]) == -1 ) {
113   close(socks[0]);
114   return -1;
115  } else {
116   con->flags |= ROAR_CON_FLAGS_VIO;
117  }
118  return 0;
119 }
120
121 return -1;
122#else
123 ROAR_ERR("roar_socket_open_fork(*): There is no UNIX Domain Socket support in win32, download a real OS.");
124 return -1;
125#endif
126}
127
128static int _connect_server(struct roar_connection * con, const char * server, int type, int flags, uint_least32_t timeout) {
129#if defined(ROAR_HAVE_STAT) && defined(ROAR_HAVE_H_SYS_STAT)
130 struct stat sockstat;
131#endif
132 const char * obj = NULL;
133 char user_sock[128];
134 int is_decnet = 0;
135 int port = 0;
136 int i = 0;
137 int fh = -1;
138 int err;
139
140 if ( con == NULL || server == NULL ) {
141  roar_err_set(ROAR_ERROR_FAULT);
142  return -1;
143 }
144
145 if ( !strcmp(server, "+invalid") ) {
146  roar_err_set(ROAR_ERROR_CANCELED);
147  return -1;
148 } else if ( !strncmp(server, "+dstr=", 6) ) {
149  if ( roar_vio_open_dstr_simple(con->viocon, server+6, ROAR_VIOF_READWRITE) == -1 )
150   return -1;
151  con->flags |= ROAR_CON_FLAGS_VIO;
152  return 0;
153 } else if ( !strcmp(server, "+fork") || !strncmp(server, "+fork=", 6) ) {
154  return _start_server(con, server, type, flags, timeout);
155 }
156
157 strncpy(user_sock, server, sizeof(user_sock)-1);
158 user_sock[sizeof(user_sock)-1] = 0;
159
160
161 if ( *user_sock != '/' ) { // don't test AF_UNIX sockets for ports
162  for (i = 0; user_sock[i] != 0; i++) {
163   if ( user_sock[i] == ':' ) {
164    if ( user_sock[i+1] == ':' ) { // DECnet, leave unchanged
165     is_decnet = 1;
166     obj = &user_sock[i+2];
167     break;
168    }
169
170    port = atoi(&(user_sock[i+1]));
171    user_sock[i] = 0;
172    break;
173   }
174  }
175 }
176
177 if ( is_decnet ) {
178  if ( *user_sock == ':' ) {
179   if ( roar_socket_get_local_nodename() != NULL ) {
180    strncpy(user_sock, roar_socket_get_local_nodename(), sizeof(user_sock)-1);
181    user_sock[sizeof(user_sock)-1] = 0;
182    roar_mm_strlcat(user_sock, server, sizeof(user_sock)-1);
183    user_sock[sizeof(user_sock)-1] = 0;
184    obj  = strstr(user_sock, "::");
185    obj += 2;
186   }
187  }
188
189  if ( *obj == 0 ) {
190#ifdef DN_MAXOBJL
191   roar_mm_strlcat(user_sock, ROAR_DEFAULT_OBJECT, sizeof(user_sock)-1);
192   user_sock[sizeof(user_sock)-1] = 0;
193#else
194   ROAR_ERR("roar_connect_raw(*): size of DECnet object unknown.");
195#endif
196  }
197   ROAR_DBG("roar_connect_raw(*): user_sock='%s'", user_sock);
198 }
199
200 if ( port || is_decnet ) {
201  fh = roar_socket_connect(user_sock, port);
202  // restore the original string
203  user_sock[i] = ':';
204 } else {
205#if defined(ROAR_HAVE_STAT) && defined(ROAR_HAVE_H_SYS_STAT)
206  if ( user_sock[0] == '/' ) {
207   if ( stat(user_sock, &sockstat) == 0 ) {
208    if ( S_ISCHR(sockstat.st_mode) ) {
209     return open(user_sock, O_RDWR|O_NOCTTY, 0666);
210    }
211   }
212  }
213#endif
214  fh = roar_socket_connect(user_sock, ROAR_DEFAULT_PORT);
215 }
216
217 if ( fh == -1 )
218  return -1;
219
220 if ( roar_vio_open_fh_socket(con->viocon, fh) == -1 ) {
221  err = roar_error;
222#ifdef ROAR_TARGET_WIN32
223  closesocket(fh);
224#else
225  close(fh);
226#endif
227  roar_error = err;
228  return -1;
229 } else {
230  con->flags |= ROAR_CON_FLAGS_VIO;
231 }
232
233 roar_err_set(ROAR_ERROR_NONE);
234 return 0;
235}
236
237static int roar_connect_raw (struct roar_connection * con, const char * server, int flags, uint_least32_t timeout) {
238#ifdef ROAR_HAVE_LIBSLP
239 struct roar_libroar_config * config = roar_libroar_get_config();
240#endif
241 char user_sock[128];
242 char * roar_server;
243 int i = 0;
244#if !defined(ROAR_TARGET_WIN32) && !defined(ROAR_TARGET_MICROCONTROLLER)
245 struct passwd * pwd;
246#endif
247#ifdef ROAR_HAVE_LIBDNET
248 struct stat decnet_stat;
249#endif
250#ifdef ROAR_HAVE_LIBX11
251 struct roar_x11_connection * x11con;
252#endif
253#ifdef ROAR_HAVE_LIBSLP
254 struct roar_server * list;
255 int workarounds_store;
256#endif
257
258 roar_err_set(ROAR_ERROR_UNKNOWN);
259
260 if ( timeout != 0 ) {
261  roar_err_set(ROAR_ERROR_INVAL);
262  return -1;
263 }
264
265 if ( flags & ROAR_ENUM_FLAG_HARDNONBLOCK )
266  flags |= ROAR_ENUM_FLAG_NONBLOCK;
267
268
269 if ( server == NULL || *server == 0 )
270  server = roar_libroar_get_server();
271
272 if ( server == NULL || *server == 0 )
273  server = getenv("ROAR_SERVER");
274
275#ifdef ROAR_HAVE_LIBX11
276 if ( server == NULL || *server == 0 ) {
277  if ( (x11con = roar_x11_connect(NULL)) != NULL ) {
278   server = roar_x11_get_prop(x11con, "ROAR_SERVER");
279   roar_x11_disconnect(x11con);
280  }
281 }
282#endif
283
284#if !defined(ROAR_TARGET_WIN32) && !defined(ROAR_TARGET_MICROCONTROLLER)
285 if ( (server == NULL || *server == 0) && (i = readlink("/etc/roarserver", user_sock, sizeof(user_sock)-1)) != -1 ) {
286   user_sock[i] = 0;
287   server = user_sock;
288 }
289#endif
290
291 if ( server != NULL && !strcasecmp(server, "+slp") ) {
292  server = roar_slp_find_roard(0);
293  if ( server == NULL ) {
294   return -1;
295  }
296 }
297
298 if ( server == NULL || *server == 0 ) {
299  /* connect via defaults */
300
301#ifdef ROAR_HAVE_UNIX
302#ifndef ROAR_TARGET_MICROCONTROLLER
303  roar_server = getenv("HOME");
304#else
305  roar_server = NULL;
306#endif
307
308  if ( roar_server == NULL ) {
309#if !defined(ROAR_TARGET_WIN32) && !defined(ROAR_TARGET_MICROCONTROLLER)
310   if ( (pwd = getpwuid(getuid())) == NULL ) {
311    roar_server = "/NX-HOME-DIR";
312   } else {
313    roar_server = pwd->pw_dir;
314   }
315#else
316   roar_server = "/WIN32-SUCKS";
317#endif
318  }
319
320  snprintf(user_sock, sizeof(user_sock)-1, "%s/%s", roar_server, ROAR_DEFAULT_SOCK_USER);
321  user_sock[sizeof(user_sock)-1] = 0;
322
323  if ( _connect_server(con, user_sock, ROAR_SOCKET_TYPE_UNIX, flags, timeout) == 0 )
324   return 0;
325
326  if ( _connect_server(con, ROAR_DEFAULT_SOCK_GLOBAL, ROAR_SOCKET_TYPE_UNIX, flags, timeout) == 0 )
327   return 0;
328#endif
329
330  if ( _connect_server(con, ROAR_DEFAULT_HOSTPORT, ROAR_SOCKET_TYPE_TCP, flags, timeout) == 0 )
331   return 0;
332
333#ifdef ROAR_HAVE_LIBDNET
334  if ( stat(ROAR_PROC_NET_DECNET, &decnet_stat) == 0 ) {
335   if ( roar_socket_get_local_nodename() ) {
336    snprintf(user_sock, 79, "%s::%s", roar_socket_get_local_nodename(), ROAR_DEFAULT_OBJECT);
337    if ( _connect_server(con, user_sock, ROAR_SOCKET_TYPE_DECNET, flags, timeout) == 0 )
338     return 0;
339   }
340  }
341#endif
342
343  if ( _connect_server(con, "+abstract", -1, flags, timeout) == 0 )
344   return 0;
345
346#ifdef ROAR_HAVE_LIBSLP
347 if ( !(config->workaround.workarounds & ROAR_LIBROAR_CONFIG_WAS_NO_SLP) &&
348      !(flags & ROAR_ENUM_FLAG_NONBLOCK)
349    ) {
350  if ( (server = roar_slp_find_roard(0)) != NULL ) {
351   if ( _connect_server(con, server, -1, 0, 0) == 0 )
352    return 0;
353
354   /* in case we can not connect to the server given this may be a cache problem,
355      we do a new lookup with the cache disabled in this case                     */
356   ROAR_WARN("roar_connect_raw(*): Can not connect to SLP located server, disabling cache");
357   if ( (server = roar_slp_find_roard(1)) != NULL )
358    if ( _connect_server(con, server, -1, 0, 0) == 0 )
359     return 0;
360  }
361 }
362
363 workarounds_store = config->workaround.workarounds;
364 config->workaround.workarounds |= ROAR_LIBROAR_CONFIG_WAS_NO_SLP;
365 list = roar_enum_servers(flags, -1, -1);
366 config->workaround.workarounds = workarounds_store;
367 if ( list != NULL ) {
368  for (i = 0; list[i].server != NULL; i++) {
369   if ( _connect_server(con, list[i].server, -1, 0, 0) == 0 ) {
370    roar_enum_servers_free(list);
371    return 0;
372   }
373  }
374  roar_enum_servers_free(list);
375 }
376#endif
377
378 return -1;
379
380 } else {
381  /* connect via (char*)server */
382  if ( _connect_server(con, server, -1, flags, timeout) != 0 )
383   return -1;
384  return 0;
385 }
386
387 roar_err_set(ROAR_ERROR_NODEV);
388 ROAR_DBG("roar_connect_raw(*) = -1 // error=NODEV");
389 return -1;
390}
391
392int roar_connect     (struct roar_connection * con, const char * server, int flags, uint_least32_t timeout) {
393 if ( con == NULL ) {
394  roar_err_set(ROAR_ERROR_FAULT);
395  return -1;
396 }
397
398 if ( roar_connect_none(con) == -1 )
399  return -1;
400
401 roar_err_set(ROAR_ERROR_UNKNOWN);
402 if ( roar_connect_raw(con, server, flags, timeout) == -1 )
403  return -1;
404
405 if ( server != NULL ) {
406  con->server_name = roar_mm_strdup(server);
407 }
408
409 return 0;
410}
411
412int roar_connect_none (struct roar_connection * con) {
413 if ( con == NULL ) {
414  roar_err_set(ROAR_ERROR_INVAL);
415  return -1;
416 }
417
418 memset(con, 0, sizeof(struct roar_connection));
419 con->refc        = 1;
420 con->flags       = ROAR_CON_FLAGS_NONE;
421 con->version     = 0;
422 con->cb_userdata = NULL;
423 con->cb          = NULL;
424 con->server_stds = NULL;
425 con->server_name = NULL;
426
427 roar_err_init(&(con->errorframe));
428
429 con->viocon = &(con->viocon_store);
430
431// con->flags |= ROAR_CON_FLAGS_VIO;
432
433 roar_err_set(ROAR_ERROR_NONE);
434 return 0;
435}
436
437int roar_connect_vio (struct roar_connection * con, struct roar_vio_calls * vio) {
438 if ( con == NULL || vio == NULL ) {
439  roar_err_set(ROAR_ERROR_INVAL);
440  return -1;
441 }
442
443 if ( roar_connect_none(con) == -1 )
444  return -1;
445
446 con->viocon = vio;
447 con->flags |= ROAR_CON_FLAGS_VIO;
448
449 return -1;
450}
451
452int roar_connect_fh (struct roar_connection * con, int fh) {
453
454 if ( con == NULL || fh == -1 ) {
455  roar_err_set(ROAR_ERROR_INVAL);
456  return -1;
457 }
458
459 if ( roar_connect_none(con) == -1 )
460  return -1;
461
462 // specal hack to set an ilegal value used internaly in libroar:
463 if ( fh == -2 )
464  fh = -1;
465
466 if ( roar_vio_open_fh_socket(con->viocon, fh) != -1 ) {
467  con->flags |= ROAR_CON_FLAGS_VIO;
468 }
469
470 roar_err_set(ROAR_ERROR_NONE);
471 return 0;
472}
473
474int roar_get_connection_fh (struct roar_connection * con) {
475 int fh;
476
477 ROAR_DBG("roar_get_connection_fh(con=%p) = ?", con);
478
479 roar_debug_warn_sysio("roar_get_connection_fh", "roar_get_connection_vio2", NULL);
480
481 if ( con == NULL )
482  return -1;
483
484 ROAR_DBG("roar_get_connection_fh(con=%p) = ?", con);
485
486 if ( roar_vio_ctl(con->viocon, ROAR_VIO_CTL_GET_FH, &fh) == -1 )
487  return -1;
488
489 ROAR_DBG("roar_get_connection_fh(con=%p) = %i", con, fh);
490
491 return fh;
492}
493
494struct roar_vio_calls * roar_get_connection_vio2 (struct roar_connection * con) {
495 if ( con == NULL )
496  return NULL;
497
498 if ( con->flags & ROAR_CON_FLAGS_VIO )
499  return con->viocon;
500
501// TODO: try to open the VIO.
502
503 return NULL;
504}
505
506const char * roar_get_connection_server(struct roar_connection * con) {
507 if ( con == NULL ) {
508  roar_err_set(ROAR_ERROR_FAULT);
509  return NULL;
510 }
511
512 return con->server_name;
513}
514
515int roar_connectionref(struct roar_connection * con) {
516 if ( con == NULL ) {
517  roar_err_set(ROAR_ERROR_FAULT);
518  return -1;
519 }
520
521 con->refc++;
522
523 return 0;
524}
525
526int roar_connectionunref(struct roar_connection * con) {
527 struct roar_vio_calls * vio;
528 struct roar_message m;
529
530 if ( con == NULL ) {
531  roar_err_set(ROAR_ERROR_FAULT);
532  return -1;
533 }
534
535 con->refc--;
536
537 if ( con->refc )
538  return 0;
539
540 memset(&m, 0, sizeof(m));
541
542 m.datalen =  0;
543 m.stream  = -1;
544 m.pos     =  0;
545 m.cmd     = ROAR_CMD_QUIT;
546
547 roar_req(con, &m, NULL);
548
549 if ( (vio = roar_get_connection_vio2(con)) != NULL ) {
550  roar_vio_close(vio);
551 }
552
553 if ( con->server_stds != NULL ) {
554  roar_stds_free(con->server_stds);
555  con->server_stds = NULL;
556 }
557
558 if ( con->server_name != NULL ) {
559  roar_mm_free(con->server_name);
560  con->server_name = NULL;
561 }
562
563 if ( con->flags & ROAR_CON_FLAGS_FREESELF ) {
564  roar_mm_free(con);
565 } else {
566  roar_connect_fh(con, -2);
567 }
568
569 return 0;
570}
571
572int roar_set_connection_callback(struct roar_connection * con,
573                                 void (*cb)(struct roar_connection * con,
574                                            struct roar_message    * mes,
575                                            void                   * data,
576                                            void                   * userdata),
577                                 void * userdata) {
578 if ( con == NULL )
579  return -1;
580
581 con->cb       = cb;
582 con->cb_userdata = userdata;
583
584 return 0;
585}
586
587int roar_sync         (struct roar_connection * con) {
588 // wait for any non-client reqs
589 return roar_wait_msg(con, 0x0000, 0x8000);
590}
591
592int roar_wait_msg     (struct roar_connection * con, int16_t seq, int16_t seqmask) {
593 roar_err_set(ROAR_ERROR_NOSYS);
594 return -1;
595}
596
597int roar_noop         (struct roar_connection * con) {
598 struct roar_message mes;
599
600 if ( con == NULL ) {
601  roar_err_set(ROAR_ERROR_FAULT);
602  return -1;
603 }
604
605 memset(&mes, 0, sizeof(mes));
606
607 mes.cmd = ROAR_CMD_NOOP;
608 mes.stream = -1;
609
610 return roar_req3(con, &mes, NULL);
611}
612
613int roar_identify   (struct roar_connection * con, const char * name) {
614 struct roar_message mes;
615 uint32_t pid;
616 int max_len;
617
618 roar_err_set(ROAR_ERROR_UNKNOWN);
619
620 ROAR_DBG("roar_identify(*): try to identify myself...");
621
622 memset(&mes, 0, sizeof(mes));
623
624 mes.cmd    = ROAR_CMD_IDENTIFY;
625 mes.stream = -1;
626 mes.pos    =  0;
627
628 ROAR_DBG("roar_identify(*): name=%p", name);
629
630 if ( name == NULL )
631  name = "libroar client";
632
633 ROAR_DBG("roar_identify(*): name=%p", name);
634
635 max_len = roar_mm_strlen(name);
636 ROAR_DBG("roar_identify(*): strlen(name) = %i", max_len);
637
638 if ( max_len > (LIBROAR_BUFFER_MSGDATA - 5) )
639  max_len = LIBROAR_BUFFER_MSGDATA - 5;
640
641 mes.datalen = 5 + max_len;
642 mes.data[0] = 1;
643
644 pid = getpid();
645 mes.data[1] = (pid & 0xFF000000UL) >> 24;
646 mes.data[2] = (pid & 0x00FF0000UL) >> 16;
647 mes.data[3] = (pid & 0x0000FF00UL) >>  8;
648 mes.data[4] = (pid & 0x000000FFUL) >>  0;
649 ROAR_DBG("roar_identify(*): pid = %i", (int)pid);
650
651 strncpy(mes.data+5, name, max_len);
652
653 return roar_req3(con, &mes, NULL);
654}
655
656//ll
Note: See TracBrowser for help on using the repository browser.