+void dosockopts(int socket) {
+#ifndef sun
+ int yes=1;
+#else
+ char yes='1';
+#endif /* sun */
+ int sock_flags;
+
+ /* lose the pesky "Address already in use" error message */
+ if (setsockopt(socket,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1) {
+ err("setsockopt SO_REUSEADDR");
+ }
+ if (setsockopt(socket,SOL_SOCKET,SO_KEEPALIVE,&yes,sizeof(int)) == -1) {
+ err("setsockopt SO_KEEPALIVE");
+ }
+
+ /* make the listening socket non-blocking */
+ if ((sock_flags = fcntl(socket, F_GETFL, 0)) == -1) {
+ err("fcntl F_GETFL");
+ }
+ if (fcntl(socket, F_SETFL, sock_flags | O_NONBLOCK) == -1) {
+ err("fcntl F_SETFL O_NONBLOCK");
+ }
+}
+
+/**
+ * Connect a server's socket.
+ *
+ * @param serve the server we want to connect.
+ **/
+int setup_serve(SERVER *serve) {
+ struct addrinfo hints;
+ struct addrinfo *ai = NULL;
+ gchar *port = NULL;
+ int e;
+
+ if(!do_oldstyle) {
+ return serve->servename ? 1 : 0;
+ }
+ memset(&hints,'\0',sizeof(hints));
+ hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG | AI_NUMERICSERV;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_family = serve->socket_family;
+
+ port = g_strdup_printf ("%d", serve->port);
+ if (port == NULL)
+ return 0;
+
+ e = getaddrinfo(serve->listenaddr,port,&hints,&ai);
+
+ g_free(port);
+
+ if(e != 0) {
+ fprintf(stderr, "getaddrinfo failed: %s\n", gai_strerror(e));
+ serve->socket = -1;
+ freeaddrinfo(ai);
+ exit(EXIT_FAILURE);
+ }
+
+ if(serve->socket_family == AF_UNSPEC)
+ serve->socket_family = ai->ai_family;
+
+#ifdef WITH_SDP
+ if ((serve->flags) && F_SDP) {
+ if (ai->ai_family == AF_INET)
+ ai->ai_family = AF_INET_SDP;
+ else (ai->ai_family == AF_INET6)
+ ai->ai_family = AF_INET6_SDP;
+ }
+#endif
+ if ((serve->socket = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0)
+ err("socket: %m");
+
+ dosockopts(serve->socket);
+
+ DEBUG("Waiting for connections... bind, ");
+ e = bind(serve->socket, ai->ai_addr, ai->ai_addrlen);
+ if (e != 0 && errno != EADDRINUSE)
+ err("bind: %m");
+ DEBUG("listen, ");
+ if (listen(serve->socket, 1) < 0)
+ err("listen: %m");
+
+ freeaddrinfo (ai);
+ if(serve->servename) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+void open_modern(void) {
+ struct addrinfo hints;
+ struct addrinfo* ai = NULL;
+ struct sock_flags;
+ int e;
+
+ memset(&hints, '\0', sizeof(hints));
+ hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_protocol = IPPROTO_TCP;
+ e = getaddrinfo(modern_listen, modernport, &hints, &ai);
+ if(e != 0) {
+ fprintf(stderr, "getaddrinfo failed: %s\n", gai_strerror(e));
+ exit(EXIT_FAILURE);
+ }
+ if((modernsock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol))<0) {
+ err("socket: %m");
+ }
+
+ dosockopts(modernsock);
+
+ if(bind(modernsock, ai->ai_addr, ai->ai_addrlen)) {
+ err("bind: %m");
+ }
+ if(listen(modernsock, 10) <0) {
+ err("listen: %m");
+ }
+
+ freeaddrinfo(ai);
+}
+
+/**
+ * Connect our servers.
+ **/
+void setup_servers(GArray* servers) {
+ int i;
+ struct sigaction sa;
+ int want_modern=0;
+
+ for(i=0;i<servers->len;i++) {
+ want_modern |= setup_serve(&(g_array_index(servers, SERVER, i)));
+ }
+ if(want_modern) {
+ open_modern();
+ }
+ children=g_hash_table_new_full(g_int_hash, g_int_equal, NULL, destroy_pid_t);
+
+ sa.sa_handler = sigchld_handler;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+ if(sigaction(SIGCHLD, &sa, NULL) == -1)
+ err("sigaction: %m");
+ sa.sa_handler = sigterm_handler;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+ if(sigaction(SIGTERM, &sa, NULL) == -1)
+ err("sigaction: %m");
+}
+
+/**
+ * Go daemon (unless we specified at compile time that we didn't want this)
+ * @param serve the first server of our configuration. If its port is zero,
+ * then do not daemonize, because we're doing inetd then. This parameter
+ * is only used to create a PID file of the form
+ * /var/run/nbd-server.<port>.pid; it's not modified in any way.
+ **/
+#if !defined(NODAEMON)
+void daemonize(SERVER* serve) {
+ FILE*pidf;
+
+ if(serve && !(serve->port)) {
+ return;
+ }
+ if(daemon(0,0)<0) {
+ err("daemon");
+ }
+ if(!*pidftemplate) {
+ if(serve) {
+ strncpy(pidftemplate, "/var/run/nbd-server.%d.pid", 255);
+ } else {
+ strncpy(pidftemplate, "/var/run/nbd-server.pid", 255);
+ }
+ }
+ snprintf(pidfname, 255, pidftemplate, serve ? serve->port : 0);
+ pidf=fopen(pidfname, "w");
+ if(pidf) {
+ fprintf(pidf,"%d\n", (int)getpid());
+ fclose(pidf);
+ } else {
+ perror("fopen");
+ fprintf(stderr, "Not fatal; continuing");
+ }
+}
+#else
+#define daemonize(serve)
+#endif /* !defined(NODAEMON) */
+
+/*
+ * Everything beyond this point (in the file) is run in non-daemon mode.
+ * The stuff above daemonize() isn't.
+ */
+
+void serve_err(SERVER* serve, const char* msg) G_GNUC_NORETURN;
+
+void serve_err(SERVER* serve, const char* msg) {
+ g_message("Export of %s on port %d failed:", serve->exportname,
+ serve->port);
+ err(msg);
+}
+