Index: memcached.c
===================================================================
--- memcached.c	(revision 608)
+++ memcached.c	(working copy)
@@ -64,7 +64,7 @@
  */
 static void drive_machine(conn *c);
 static int new_socket(const bool is_udp);
-static int server_socket(const int port, const bool is_udp);
+static int server_socket(struct in_addr interf, const int port, const bool is_udp);
 static int try_read_command(conn *c);
 static int try_read_network(conn *c);
 static int try_read_udp(conn *c);
@@ -105,7 +105,8 @@
 static item **todelete = NULL;
 static int delcurr;
 static int deltotal;
-static conn *listen_conn;
+static conn *listen_conn[MAX_LISTEN_CONNS+1]; /* one for unix socket*/
+static int listen_conn_num=0;
 static struct event_base *main_base;
 
 #define TRANSMIT_COMPLETE   0
@@ -166,7 +167,7 @@
 static void settings_init(void) {
     settings.port = 11211;
     settings.udpport = 0;
-    settings.interf.s_addr = htonl(INADDR_ANY);
+    settings.interf_num = 0;
     settings.maxbytes = 64 * 1024 * 1024; /* default is 64MB */
     settings.maxconns = 1024;         /* to limit connections-related memory to about 5MB */
     settings.verbose = 0;
@@ -183,6 +184,7 @@
 #endif
     settings.prefix_delimiter = ':';
     settings.detail_enabled = 0;
+    settings.network_disabled = false;
 }
 
 /* returns true if a deleted item's delete-locked-time is over, and it
@@ -916,16 +918,16 @@
         char *pos = temp;
 
         info = mallinfo();
-        pos += sprintf(pos, "STAT arena_size %d\r\n", info.arena);
-        pos += sprintf(pos, "STAT free_chunks %d\r\n", info.ordblks);
-        pos += sprintf(pos, "STAT fastbin_blocks %d\r\n", info.smblks);
-        pos += sprintf(pos, "STAT mmapped_regions %d\r\n", info.hblks);
-        pos += sprintf(pos, "STAT mmapped_space %d\r\n", info.hblkhd);
-        pos += sprintf(pos, "STAT max_total_alloc %d\r\n", info.usmblks);
-        pos += sprintf(pos, "STAT fastbin_space %d\r\n", info.fsmblks);
-        pos += sprintf(pos, "STAT total_alloc %d\r\n", info.uordblks);
-        pos += sprintf(pos, "STAT total_free %d\r\n", info.fordblks);
-        pos += sprintf(pos, "STAT releasable_space %d\r\nEND", info.keepcost);
+        pos += sprintf(pos, "STAT arena_size %lu\r\n", info.arena);
+        pos += sprintf(pos, "STAT free_chunks %lu\r\n", info.ordblks);
+        pos += sprintf(pos, "STAT fastbin_blocks %lu\r\n", info.smblks);
+        pos += sprintf(pos, "STAT mmapped_regions %lu\r\n", info.hblks);
+        pos += sprintf(pos, "STAT mmapped_space %lu\r\n", info.hblkhd);
+        pos += sprintf(pos, "STAT max_total_alloc %lu\r\n", info.usmblks);
+        pos += sprintf(pos, "STAT fastbin_space %lu\r\n", info.fsmblks);
+        pos += sprintf(pos, "STAT total_alloc %lu\r\n", info.uordblks);
+        pos += sprintf(pos, "STAT total_free %lu\r\n", info.fordblks);
+        pos += sprintf(pos, "STAT releasable_space %lu\r\nEND", info.keepcost);
         out_string(c, temp);
         return;
     }
@@ -1731,20 +1733,15 @@
  * Sets whether we are listening for new connections or not.
  */
 void accept_new_conns(const bool do_accept) {
+    int i;
     if (! is_listen_thread())
         return;
-    if (do_accept) {
-        update_event(listen_conn, EV_READ | EV_PERSIST);
-        if (listen(listen_conn->sfd, 1024) != 0) {
-            perror("listen");
-        }
+    for (i=0; i<listen_conn_num; i++) {
+	    update_event(listen_conn[i], (do_accept)? (EV_READ | EV_PERSIST) : 0 );
+	    if (listen(listen_conn[i]->sfd, (do_accept) ? 1024 : 0 ) != 0) {
+		    perror("listen");
+	    }
     }
-    else {
-        update_event(listen_conn, 0);
-        if (listen(listen_conn->sfd, 0) != 0) {
-            perror("listen");
-        }
-    }
 }
 
 
@@ -2105,7 +2102,7 @@
 }
 
 
-static int server_socket(const int port, const bool is_udp) {
+static int server_socket(struct in_addr interf, const int port, const bool is_udp) {
     int sfd;
     struct linger ling = {0, 0};
     struct sockaddr_in addr;
@@ -2132,7 +2129,7 @@
 
     addr.sin_family = AF_INET;
     addr.sin_port = htons(port);
-    addr.sin_addr = settings.interf;
+    addr.sin_addr = interf;
     if (bind(sfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
         perror("bind()");
         close(sfd);
@@ -2213,7 +2210,8 @@
 }
 
 /* listening socket */
-static int l_socket = 0;
+static int l_socket[MAX_LISTEN_CONNS+1]; /* one for unix socket */
+static int l_socket_num=0;
 
 /* udp socket */
 static int u_socket = -1;
@@ -2221,7 +2219,9 @@
 /* invoke right before gdb is called, on assert */
 void pre_gdb(void) {
     int i;
-    if (l_socket > -1) close(l_socket);
+    for (i=0;i<l_socket_num;i++) {
+	    if (l_socket[i] > -1) close(l_socket[i]);
+    }
     if (u_socket > -1) close(u_socket);
     for (i = 3; i <= 500; i++) close(i); /* so lame */
     kill(getpid(), SIGABRT);
@@ -2304,7 +2304,7 @@
     printf(PACKAGE " " VERSION "\n");
     printf("-p <num>      TCP port number to listen on (default: 11211)\n"
            "-U <num>      UDP port number to listen on (default: 0, off)\n"
-           "-s <file>     unix socket path to listen on (disables network support)\n"
+           "-s <file>     unix socket path to listen on\n"
            "-l <ip_addr>  interface to listen on, default is INDRR_ANY\n"
            "-d            run as a daemon\n"
            "-r            maximize core file limit\n"
@@ -2320,7 +2320,8 @@
            "-b            run a managed instanced (mnemonic: buckets)\n"
            "-P <file>     save PID in <file>, only used with -d option\n"
            "-f <factor>   chunk size growth factor, default 1.25\n"
-           "-n <bytes>    minimum space allocated for key+value+flags, default 48\n");
+           "-n <bytes>    minimum space allocated for key+value+flags, default 48\n"
+           "-N            disable network support\n");
 #ifdef USE_THREADS
     printf("-t <num>      number of threads to use, default 4\n");
 #endif
@@ -2432,7 +2433,7 @@
 }
 
 int main (int argc, char **argv) {
-    int c;
+    int c,i;
     struct in_addr addr;
     bool lock_memory = false;
     bool daemonize = false;
@@ -2453,7 +2454,7 @@
     setbuf(stderr, NULL);
 
     /* process arguments */
-    while ((c = getopt(argc, argv, "bp:s:U:m:Mc:khirvdl:u:P:f:s:n:t:D:")) != -1) {
+    while ((c = getopt(argc, argv, "bp:s:U:m:Mc:khirvdl:u:P:f:s:n:t:D:N")) != -1) {
         switch (c) {
         case 'U':
             settings.udpport = atoi(optarg);
@@ -2489,12 +2490,23 @@
             settings.verbose++;
             break;
         case 'l':
-            if (inet_pton(AF_INET, optarg, &addr) <= 0) {
-                fprintf(stderr, "Illegal address: %s\n", optarg);
-                return 1;
-            } else {
-                settings.interf = addr;
-            }
+	    {
+		    char *port_cp = strchr(optarg,PORT_DELIMITER);
+		    if (settings.interf_num==MAX_LISTEN_CONNS) {
+			    fprintf(stderr, "Maximum number of listen socket reached\n");
+			    return 1;
+		    }
+		    if (port_cp) { /* cut ip on delimiter and move to port */
+			    *(port_cp++)=0;
+		    }
+		    if (inet_pton(AF_INET, optarg, &addr) <= 0) {
+			    fprintf(stderr, "Illegal address: %s\n", optarg);
+			    return 1;
+		    } else {
+			    settings.ports[settings.interf_num] = (port_cp) ? atoi(port_cp) : 0;
+			    settings.interf[settings.interf_num++] = addr;
+		    }
+	    }
             break;
         case 'd':
             daemonize = true;
@@ -2537,12 +2549,19 @@
             settings.prefix_delimiter = optarg[0];
             settings.detail_enabled = 1;
             break;
+	case 'N':
+		settings.network_disabled = true;
         default:
             fprintf(stderr, "Illegal argument \"%c\"\n", c);
             return 1;
         }
     }
 
+    if (settings.network_disabled && NULL==settings.socketpath) {
+		fprintf(stderr,"both unix socket and network disabled - memcache not operational - use -s or don't use -N option");
+		return 1;
+    }
+
     if (maxcore != 0) {
         struct rlimit rlim_new;
         /*
@@ -2597,21 +2616,30 @@
      */
 
     /* create the listening socket and bind it */
-    if (settings.socketpath == NULL) {
-        l_socket = server_socket(settings.port, 0);
-        if (l_socket == -1) {
-            fprintf(stderr, "failed to listen\n");
-            exit(EXIT_FAILURE);
-        }
-    }
+    if (!settings.network_disabled)  {
+	    struct in_addr any_interf;
+	    any_interf.s_addr = htonl(INADDR_ANY);
+	    if (settings.interf_num==0) { /*if no interfaces defined listen on INADDR_ANY*/
+		settings.ports[settings.interf_num]=0;
+		settings.interf[settings.interf_num++] = any_interf;
+	    }
+	    for (i=0;i<settings.interf_num;i++) {
+		    if ((l_socket[l_socket_num++] = server_socket(settings.interf[i],
+								(settings.ports[i])?settings.ports[i]:settings.port, 
+								0))==-1) {
+			    fprintf(stderr, "failed to listen\n");
+			    exit(EXIT_FAILURE);
+		    }
+	    }
 
-    if (settings.udpport > 0 && settings.socketpath == NULL) {
-        /* create the UDP listening socket and bind it */
-        u_socket = server_socket(settings.udpport, 1);
-        if (u_socket == -1) {
-            fprintf(stderr, "failed to listen on UDP port %d\n", settings.udpport);
-            exit(EXIT_FAILURE);
-        }
+	    if (settings.udpport > 0) {
+		    /* create the UDP listening socket and bind it */
+		    u_socket = server_socket(any_interf,settings.udpport, 1);
+		    if (u_socket == -1) {
+			    fprintf(stderr, "failed to listen on UDP port %d\n", settings.udpport);
+			    exit(EXIT_FAILURE);
+		    }
+	    }
     }
 
     /* lose root privileges if we have them */
@@ -2632,9 +2660,8 @@
 
     /* create unix mode sockets after dropping privileges */
     if (settings.socketpath != NULL) {
-        l_socket = server_socket_unix(settings.socketpath);
-        if (l_socket == -1) {
-            fprintf(stderr, "failed to listen\n");
+        if ((l_socket[l_socket_num++] = server_socket_unix(settings.socketpath))==-1) {
+            fprintf(stderr, "failed to listen on %s\n",settings.socketpath);
             exit(EXIT_FAILURE);
         }
     }
@@ -2692,10 +2719,12 @@
         exit(EXIT_FAILURE);
     }
     /* create the initial listening connection */
-    if (!(listen_conn = conn_new(l_socket, conn_listening,
-                                 EV_READ | EV_PERSIST, 1, false, main_base))) {
-        fprintf(stderr, "failed to create listening connection");
-        exit(EXIT_FAILURE);
+    for (i=0; i<l_socket_num;i++) {
+	    if (!(listen_conn[listen_conn_num++] = conn_new(l_socket[i], conn_listening,
+					    EV_READ | EV_PERSIST, 1, false, main_base))) {
+		    fprintf(stderr, "failed to create listening connection");
+		    exit(EXIT_FAILURE);
+	    }
     }
     /* start up worker threads if MT mode */
     thread_init(settings.num_threads, main_base);
Index: memcached.h
===================================================================
--- memcached.h	(revision 608)
+++ memcached.h	(working copy)
@@ -11,6 +11,8 @@
 #include <netinet/in.h>
 #include <event.h>
 
+#define MAX_LISTEN_CONNS 10
+#define PORT_DELIMITER ':'
 #define DATA_BUFFER_SIZE 2048
 #define UDP_READ_BUFFER_SIZE 65536
 #define UDP_MAX_PAYLOAD_SIZE 1400
@@ -77,7 +79,9 @@
     int maxconns;
     int port;
     int udpport;
-    struct in_addr interf;
+    struct in_addr interf[MAX_LISTEN_CONNS];
+    int ports[MAX_LISTEN_CONNS];
+    int interf_num;
     int verbose;
     rel_time_t oldest_live; /* ignore existing items older than this */
     bool managed;          /* if 1, a tracker manages virtual buckets */
@@ -88,6 +92,7 @@
     int num_threads;        /* number of libevent threads to run */
     char prefix_delimiter;  /* character that marks a key prefix (for stats) */
     int detail_enabled;     /* nonzero if we're collecting detailed stats */
+    bool network_disabled; /* if true network support is disabled (usefull with unix sockets) */
 };
 
 extern struct stats stats;
Index: doc/memcached.1
===================================================================
--- doc/memcached.1	(revision 608)
+++ doc/memcached.1	(working copy)
@@ -23,10 +23,12 @@
 .B \-s <file>
 Unix socket path to listen on (disables network support).
 .TP
-.B \-l <ip_addr>  
+.B \-l <ip_addr[:port]>  
 Listen on <ip_addr>; default to INDRR_ANY. This is an important option to 
 consider as there is no other way to secure the installation. Binding to an 
 internal or firewalled network interface is suggested.
+You can add multiple -l options to commandline if you wish to listen on multiple interfaces.
+If port number ommited, default or given with -p option will be used.
 .TP
 .B \-d
 Run memcached as a daemon.
@@ -102,6 +104,10 @@
 per-prefix stats reporting. The default is ":" (colon). If this option is
 specified, stats collection is turned on automatically; if not, then it may
 be turned on by sending the "stats detail on" command to the server.
+.TP
+.B \-N
+Disable network support. Memcache won't listen on any network inferface for
+incomming connections.
 .br
 .SH LICENSE
 The memcached daemon is copyright Danga Interactive and is distributed under 
Index: TODO
===================================================================
--- TODO	(revision 608)
+++ TODO	(working copy)
@@ -25,5 +25,3 @@
   out other objects with infinite expirations.
 
 * curr_items never decreases?  mailing list report.
-
-* memcached to listen on more than one IP.  mailing list request.
