/** parses and stores an option */ static error_t parse_opt (int key, char *arg, struct argp_state *state) { struct arguments *arguments = (struct arguments *) state->input; switch (key) { case 'T': arguments->tunnel = 1; arguments->has_work++; break; case 'R': arguments->route = 1; arguments->has_work++; break; case 'D': arguments->discover = 1; break; case 'S': { const char *serverip; const char *name = arguments->eibnetname; EIBnetServer *c; int port = 0; char *a = strdup (OPT_ARG(arg, state, "")); char *b; if (!a) die ("out of memory"); b = strchr (a, ':'); if (b) { *b++ = 0; port = atoi (b); } if (port <= 0) port = 3671; serverip = a; if (!*serverip) serverip = "224.0.23.12"; c = new EIBnetServer (serverip, port, arguments->tunnel, arguments->route, arguments->discover, arguments->l3(), arguments->tracer(), (name && *name) ? name : "knxd"); if (!c->init ()) die ("initialization of the EIBnet/IP server failed"); free (a); arguments->tunnel = false; arguments->route = false; arguments->discover = false; arguments->eibnetname = 0; } break; case 'u': { BaseServer *s; const char *name = OPT_ARG(arg,state,"/run/knx"); s = new LocalServer (arguments->l3(), arguments->tracer(), name); if (!s->init ()) die ("initialisation of the knxd unix protocol failed"); arguments->has_work++; } break; case 'i': { BaseServer *s = NULL; int port = atoi(OPT_ARG(arg,state,"6720")); if (port > 0) s = new InetServer (arguments->l3(), arguments->tracer(), port); if (!s || !s->init ()) die ("initialisation of the knxd inet protocol failed"); arguments->has_work++; } break; case 't': if (arg) { char *x; unsigned long level = strtoul(arg, &x, 0); if (*x) die ("Trace level: '%s' is not a number", arg); arguments->tracer(true)->SetTraceLevel (level); } else arguments->tracer(true)->SetTraceLevel (0); break; case 'f': arguments->tracer(true)->SetErrorLevel (arg ? atoi (arg) : 0); break; case 'e': if (arguments->has_l3 ()) { die ("You need to specify '-e' earlier"); } arguments->addr = readaddr (arg); break; case 'p': arguments->pidfile = arg; break; case 'd': arguments->daemon = OPT_ARG(arg,state,"/dev/null"); break; case 'c': if (!CreateGroupCache (arguments->l3(), arguments->tracer(), true)) die ("initialisation of the group cache failed"); break; case 'n': arguments->eibnetname = (char *)arg; if(arguments->eibnetname[0] == '=') arguments->eibnetname++; if(strlen(arguments->eibnetname) >= 30) die("EIBnetServer/IP name must be shorter than 30 bytes"); break; case OPT_FORCE_BROADCAST: arguments->force_broadcast = true; break; case OPT_STOP_NOW: arguments->stop_now = true; break; case OPT_BACK_TUNNEL_NOQUEUE: arguments->l2opts.flags |= FLAG_B_TUNNEL_NOQUEUE; break; case OPT_BACK_TPUARTS_ACKGROUP: arguments->l2opts.flags |= FLAG_B_TPUARTS_ACKGROUP; break; case OPT_BACK_TPUARTS_ACKINDIVIDUAL: arguments->l2opts.flags |= FLAG_B_TPUARTS_ACKINDIVIDUAL; break; case OPT_BACK_TPUARTS_DISCH_RESET: arguments->l2opts.flags |= FLAG_B_TPUARTS_DISCH_RESET; break; case OPT_BACK_EMI_NOQUEUE: arguments->l2opts.flags |= FLAG_B_EMI_NOQUEUE; break; case 'N': arguments->l2opts.flags |= FLAG_B_NO_MONITOR; break; case ARGP_KEY_ARG: case 'b': { arguments->l2opts.t = arguments->tracer (); Layer2 *l2 = Create (arg, &arguments->l2opts, arguments->l3 ()); if (!l2 || !l2->init ()) die ("initialisation of backend '%s' failed", arg); if (arguments->l2opts.flags) die ("You provided options which '%s' does not recognize", arg); memset(&arguments->l2opts, 0, sizeof(arguments->l2opts)); arguments->has_work++; break; } case ARGP_KEY_FINI: if (arguments->l2opts.flags) die ("You need to use backend flags in front of the affected backend"); #ifdef HAVE_SYSTEMD { BaseServer *s = NULL; const int num_fds = sd_listen_fds(0); if( num_fds < 0 ) die("Error getting fds from systemd."); // zero FDs from systemd is not a bug for( int fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START+num_fds; ++fd ) { if( sd_is_socket(fd, AF_UNSPEC, SOCK_STREAM, 1) <= 0 ) die("Error: socket not of expected type."); s = new SystemdServer(arguments->l3(), arguments->tracer(), fd); if (!s->init ()) die ("initialisation of the systemd socket failed"); arguments->has_work++; } } #endif errno = 0; if (arguments->tunnel || arguments->route || arguments->discover || arguments->eibnetname) die ("Option '-S' starts the multicast server.\n" "-T/-R/-D/-n after or without that option are useless."); if (arguments->l2opts.flags) die ("You provided L2 flags after specifying an L2 interface."); if (arguments->has_work == 0) die ("I know about no interface. Nothing to do. Giving up."); if (arguments->has_work == 1) die ("I only have one interface. Nothing to do. Giving up."); arguments->finish_l3(); break; default: return ARGP_ERR_UNKNOWN; } return 0; }
int main (int ac, char *ag[]) { int index; Queue < Server * >server; Server *s; Layer2Interface *l2; Layer3 *l3; #ifdef HAVE_EIBNETIPSERVER EIBnetServer *serv = 0; #endif memset (&arg, 0, sizeof (arg)); arg.addr = 0x0001; arg.errorlevel = LEVEL_WARNING; argp_parse (&argp, ac, ag, 0, &index, &arg); if (index > ac - 1) die ("url expected"); if (index < ac - 1) die ("unexpected parameter"); if (arg.port == 0 && arg.name == 0 && arg.serverip == 0) die ("No listen-address given"); signal (SIGPIPE, SIG_IGN); pth_init (); Trace t; t.SetTraceLevel (arg.tracelevel); t.SetErrorLevel (arg.errorlevel); /* if (getuid () == 0) ERRORPRINTF (&t, 0x37000001, 0, "EIBD should not run as root"); */ if(arg.eibnetname) { if(arg.eibnetname[0] == '=') arg.eibnetname++; if(strlen(arg.eibnetname) >= 30) die("EIBnetServer/IP name can't be longer then 30 char"); } if (arg.daemon) { int fd = open (arg.daemon, O_WRONLY | O_APPEND | O_CREAT, FILE_MODE); if (fd == -1) die ("Can not open file %s", arg.daemon); int i = fork (); if (i < 0) die ("fork failed"); if (i > 0) exit (0); close (1); close (2); close (0); dup2 (fd, 1); dup2 (fd, 2); close (fd); setsid (); } FILE *pidf; if (arg.pidfile) if ((pidf = fopen (arg.pidfile, "w")) != NULL) { fprintf (pidf, "%d", getpid ()); fclose (pidf); } l2 = Create (ag[index], arg.backendflags, &t); if (!l2 || !l2->init ()) die ("initialisation of the backend failed"); l3 = new Layer3 (l2, &t); if (arg.port) { s = new InetServer (l3, &t, arg.port); if (!s->init ()) die ("initialisation of the knxd inet protocol failed"); server.put (s); } if (arg.name) { s = new LocalServer (l3, &t, arg.name); if (!s->init ()) die ("initialisation of the knxd unix protocol failed"); server.put (s); } #ifdef HAVE_EIBNETIPSERVER serv = startServer (l3, &t, arg.eibnetname); #endif #ifdef HAVE_GROUPCACHE if (!CreateGroupCache (l3, &t, arg.groupcache)) die ("initialisation of the group cache failed"); #endif signal (SIGINT, SIG_IGN); signal (SIGTERM, SIG_IGN); int sig; do { sigset_t t1; sigemptyset (&t1); sigaddset (&t1, SIGINT); sigaddset (&t1, SIGHUP); sigaddset (&t1, SIGTERM); pth_sigwait (&t1, &sig); if (sig == SIGHUP && arg.daemon) { int fd = open (arg.daemon, O_WRONLY | O_APPEND | O_CREAT, FILE_MODE); if (fd == -1) { ERRORPRINTF (&t, 0x27000002, 0, "can't open log file %s", arg.daemon); continue; } close (1); close (2); dup2 (fd, 1); dup2 (fd, 2); close (fd); } } while (sig == SIGHUP); signal (SIGINT, SIG_DFL); signal (SIGTERM, SIG_DFL); while (!server.isempty ()) delete server.get (); #ifdef HAVE_EIBNETIPSERVER if (serv) delete serv; #endif #ifdef HAVE_GROUPCACHE DeleteGroupCache (); #endif delete l3; if (Cleanup) Cleanup (); if (arg.pidfile) unlink (arg.pidfile); pth_exit (0); return 0; }