1 #include "server_config.h"
2 #include <string.h>             /* for memmove */
3 #include <stdlib.h>             /* for exit() */
4 #include <time.h>
5 #include "gam_connection.h"
6 #include "gam_subscription.h"
7 #include "gam_listener.h"
8 #include "gam_server.h"
9 #include "gam_event.h"
10 #include "gam_protocol.h"
11 #include "gam_channel.h"
12 #include "gam_error.h"
13 #include "gam_pidname.h"
14 #include "gam_eq.h"
15 #ifdef GAMIN_DEBUG_API
16 #include "gam_debugging.h"
17 #endif
18 #ifdef ENABLE_INOTIFY
19 #include "gam_inotify.h"
20 #endif
21 #include "fam.h"
22 
23 /************************************************************************
24  *									*
25  *			Connection data handling			*
26  *									*
27  ************************************************************************/
28 
29 static GList *gamConnList;
30 
31 struct GamConnData {
32     GamConnState state;         /* the state for the connection */
33     int fd;                     /* the file descriptor */
34     int pid;                    /* the PID of the remote process */
35     gchar *pidname;		/* The name of the process */
36     GMainLoop *loop;            /* the Glib loop used */
37     GIOChannel *source;         /* the Glib I/O Channel used */
38     int request_len;            /* how many bytes of request are valid */
39     GAMPacket request;          /* the next request being read */
40     GamListener *listener;      /* the listener associated with the connection */
41     gam_eq_t *eq;               /* the event queue */
42     guint eq_source;            /* the event queue GSource id */
43 };
44 
45 static void gam_cancel_server_timeout (void);
46 
47 
48 static const char *
gam_reqtype_to_string(GAMReqType type)49 gam_reqtype_to_string (GAMReqType type)
50 {
51 	switch (type)
52 	{
53 	case GAM_REQ_FILE:
54 		return "MONFILE";
55 	case GAM_REQ_DIR:
56 		return "MONDIR";
57 	case GAM_REQ_CANCEL:
58 		return "CANCEL";
59 	case GAM_REQ_DEBUG:
60 		return "4";
61 	}
62 
63 	return "";
64 }
65 
66 /**
67  * gam_connections_init:
68  *
69  * Initialize the connections data layer
70  *
71  * Returns 0 on success; -1 on failure
72  */
73 int
gam_connections_init(void)74 gam_connections_init(void)
75 {
76     return (0);
77 }
78 
79 /**
80  * gam_connection_exists:
81  * @conn: the connection
82  *
83  * Routine to chech whether a connection still exists
84  *
85  * Returns 1 if still registered, 0 otherwise
86  */
87 int
gam_connection_exists(GamConnDataPtr conn)88 gam_connection_exists(GamConnDataPtr conn)
89 {
90     g_assert(conn);
91     return g_list_find(gamConnList, (gconstpointer) conn) != NULL;
92 }
93 
94 /**
95  * gam_connection_close:
96  * @conn: the connection
97  *
98  * Routine to close a connection and discard the associated data
99  *
100  * Returns 0 on success; -1 on error
101  */
102 int
gam_connection_close(GamConnDataPtr conn)103 gam_connection_close(GamConnDataPtr conn)
104 {
105     g_assert (conn);
106     /* A valid connection is on gamConnList.  */
107     g_assert(g_list_find(gamConnList, (gconstpointer) conn));
108     g_assert(conn->source);
109 
110     /* Kill the queue event source */
111     if (conn->eq_source != 0)
112       g_source_remove (conn->eq_source);
113     /* Flush the event queue */
114     gam_eq_flush (conn->eq, conn);
115     /* Kill the event queue */
116     gam_eq_free (conn->eq);
117 
118     if (conn->listener != NULL) {
119         gam_listener_free(conn->listener);
120     }
121 
122 #ifdef GAMIN_DEBUG_API
123     gam_debug_release(conn);
124 #endif
125     GAM_DEBUG(DEBUG_INFO, "Closing connection %d\n", conn->fd);
126 
127     g_io_channel_unref(conn->source);
128     gamConnList = g_list_remove(gamConnList, conn);
129     g_assert (!g_list_find(gamConnList, conn));
130     g_free(conn->pidname);
131     g_free(conn);
132 
133     if (gamConnList == NULL && gam_server_use_timeout ())
134       gam_schedule_server_timeout ();
135 
136     return (0);
137 }
138 
139 /**
140  * gam_connections_close:
141  *
142  * Close all the registered connections
143  *
144  * Returns 0 on success; -1 if at least one connection failed to close
145  */
146 int
gam_connections_close(void)147 gam_connections_close(void)
148 {
149     int ret = 0;
150     GList *cur;
151 
152     while ((cur = g_list_first(gamConnList)) != NULL) {
153         if (gam_connection_close((GamConnDataPtr) cur->data) < 0)
154 	    ret = -1;
155     }
156     return (ret);
157 }
158 
159 /**
160  * gam_connection_eq_flush:
161  *
162  * Flushes the connections event queue
163  *
164  * returns TRUE
165  */
166 static gboolean
gam_connection_eq_flush(gpointer data)167 gam_connection_eq_flush (gpointer data)
168 {
169 	gboolean work;
170 	GamConnDataPtr conn = (GamConnDataPtr)data;
171 	if (!conn)
172 		return FALSE;
173 
174 	work = gam_eq_flush (conn->eq, conn);
175 	if (!work)
176 		conn->eq_source = 0;
177 	return work;
178 }
179 
180 /**
181  * gam_connection_new:
182  * @loop: the Glib loop
183  * @source: the  Glib I/O Channel
184  *
185  * Create a new connection data structure.
186  *
187  * Returns a new connection structure on success; NULL on error.
188  */
189 GamConnDataPtr
gam_connection_new(GMainLoop * loop,GIOChannel * source)190 gam_connection_new(GMainLoop *loop, GIOChannel *source)
191 {
192     GamConnDataPtr ret;
193 
194     g_assert(loop);
195     g_assert(source);
196 
197     ret = g_malloc0(sizeof(GamConnData));
198     if (ret == NULL)
199         return (NULL);
200 
201     ret->state = GAM_STATE_AUTH;
202     ret->fd = g_io_channel_unix_get_fd(source);
203     ret->loop = loop;
204     ret->source = source;
205     ret->eq = gam_eq_new ();
206     ret->eq_source = g_timeout_add (100 /* 100 milisecond */, gam_connection_eq_flush, ret);
207     gamConnList = g_list_prepend(gamConnList, ret);
208 
209     gam_cancel_server_timeout ();
210 
211     GAM_DEBUG(DEBUG_INFO, "Created connection %d\n", ret->fd);
212 
213     return (ret);
214 }
215 
216 /**
217  * gam_connection_get_fd:
218  * @conn: a connection data structure.
219  *
220  * Get the file descriptor associated with a connection
221  *
222  * Returns the file descriptor or -1 in case of error.
223  */
224 int
gam_connection_get_fd(GamConnDataPtr conn)225 gam_connection_get_fd(GamConnDataPtr conn)
226 {
227     g_assert(conn);
228     return (conn->fd);
229 }
230 
231 /**
232  * gam_connection_get_pid:
233  * @conn: a connection data structure.
234  *
235  * accessor for the pid associated to the connection
236  *
237  * Returns the process identifier or -1 in case of error.
238  */
239 int
gam_connection_get_pid(GamConnDataPtr conn)240 gam_connection_get_pid(GamConnDataPtr conn)
241 {
242     g_assert(conn);
243     return (conn->pid);
244 }
245 
246 gchar *
gam_connection_get_pidname(GamConnDataPtr conn)247 gam_connection_get_pidname(GamConnDataPtr conn)
248 {
249 	g_assert (conn);
250 	return conn->pidname;
251 }
252 
253 /**
254  * gam_connection_set_pid:
255  * @conn: a connection data structure.
256  * @pid: the client process id
257  *
258  * Set the client process id, this also indicate that authentication was done.
259  *
260  * Returns 0 in case of success or -1 in case of error.
261  */
262 int
gam_connection_set_pid(GamConnDataPtr conn,int pid)263 gam_connection_set_pid(GamConnDataPtr conn, int pid)
264 {
265     g_assert(conn);
266 
267     if (conn->state != GAM_STATE_AUTH) {
268         GAM_DEBUG(DEBUG_INFO, "Connection in unexpected state: "
269 		  "not waiting for authentication\n");
270         conn->state = GAM_STATE_ERROR;
271         return (-1);
272     }
273 
274     conn->state = GAM_STATE_OKAY;
275     conn->pid = pid;
276     conn->pidname = gam_get_pidname (pid);
277     conn->listener = gam_listener_new(conn, pid);
278     if (conn->listener == NULL) {
279         GAM_DEBUG(DEBUG_INFO, "Failed to create listener\n");
280         conn->state = GAM_STATE_ERROR;
281         return (-1);
282     }
283     return (0);
284 }
285 
286 /**
287  * gam_connection_get_state:
288  * @conn: a connection
289  *
290  * Accessor for the connection state
291  *
292  * Returns the connection's connection state
293  */
294 GamConnState
gam_connection_get_state(GamConnDataPtr conn)295 gam_connection_get_state(GamConnDataPtr conn)
296 {
297     g_assert(conn);
298     return (conn->state);
299 }
300 
301 /**
302  * gam_connection_get_data:
303  * @conn: a connection
304  * @data: address to store pointer to data
305  * @size: amount of data available
306  *
307  * Get the address and length of the data store for the connection
308  *
309  * Returns 0 on success; -1 on failure
310  */
311 int
gam_connection_get_data(GamConnDataPtr conn,char ** data,int * size)312 gam_connection_get_data(GamConnDataPtr conn, char **data, int *size)
313 {
314     g_assert(conn);
315     g_assert(data);
316     g_assert(size);
317 
318     *data = (char *) &conn->request + conn->request_len;
319     *size = sizeof(GAMPacket) - conn->request_len;
320 
321     return (0);
322 }
323 
324 /**
325  * gam_connection_request:
326  *
327  * @conn: connection data structure.
328  * @req: the request
329  *
330  * Process a complete request.
331  *
332  * Returns 0 on success; -1 on error
333  */
334 static int
gam_connection_request(GamConnDataPtr conn,GAMPacketPtr req)335 gam_connection_request(GamConnDataPtr conn, GAMPacketPtr req)
336 {
337     GamSubscription *sub;
338     int events;
339     gboolean is_dir = TRUE;
340     char byte_save;
341     int type;
342     int options;
343 
344     g_assert(conn);
345     g_assert(req);
346     g_assert(conn->state == GAM_STATE_OKAY);
347     g_assert(conn->fd >= 0);
348     g_assert(conn->listener);
349 
350     type = req->type & 0xF;
351     options = req->type & 0xFFF0;
352     GAM_DEBUG(DEBUG_INFO, "%s request: from %s, seq %d, type %x options %x\n",
353               gam_reqtype_to_string (type), conn->pidname, req->seq, type, options);
354 
355     if (req->pathlen >= MAXPATHLEN)
356         return (-1);
357 
358     /*
359      * zero-terminate the string in the buffer, but keep the byte as
360      * it may be the first one of the next request.
361      */
362     byte_save = req->path[req->pathlen];
363     req->path[req->pathlen] = 0;
364 
365     switch (type) {
366         case GAM_REQ_FILE:
367         case GAM_REQ_DIR:
368             events = GAMIN_EVENT_CHANGED | GAMIN_EVENT_CREATED |
369                 GAMIN_EVENT_DELETED | GAMIN_EVENT_MOVED |
370                 GAMIN_EVENT_EXISTS;
371 
372 	    is_dir = (type == GAM_REQ_DIR);
373             sub = gam_subscription_new(req->path, events, req->seq,
374 	                               is_dir, options);
375             gam_subscription_set_listener(sub, conn->listener);
376             gam_add_subscription(sub);
377             break;
378         case GAM_REQ_CANCEL: {
379             char *path;
380             int pathlen;
381 
382             sub = gam_listener_get_subscription_by_reqno(conn->listener,
383 							 req->seq);
384             if (sub == NULL) {
385                 GAM_DEBUG(DEBUG_INFO,
386                           "Cancel: no subscription with reqno %d found\n",
387                           req->seq);
388 		goto error;
389 	    }
390 
391 	    GAM_DEBUG(DEBUG_INFO, "Cancelling subscription with reqno %d\n",
392 		      req->seq);
393 	    /* We need to make a copy of sub's path as gam_send_ack
394 	       needs it but gam_listener_remove_subscription frees
395 	       it.  */
396 	    path = g_strdup(gam_subscription_get_path(sub));
397 	    pathlen = gam_subscription_pathlen(sub);
398 
399 	    gam_listener_remove_subscription(conn->listener, sub);
400 	    gam_remove_subscription(sub);
401 #ifdef ENABLE_INOTIFY
402 	    if ((gam_inotify_is_running()) && (!gam_exclude_check(path))) {
403 		gam_fs_mon_type type;
404 
405                 type = gam_fs_get_mon_type (path);
406 		if (type != GFS_MT_POLL)
407 		    gam_subscription_free(sub);
408 	    }
409 #endif
410 
411 	    if (gam_send_ack(conn, req->seq, path, pathlen) < 0) {
412 		GAM_DEBUG(DEBUG_INFO, "Failed to send cancel ack to PID %d\n",
413 			  gam_connection_get_pid(conn));
414 	    }
415 	    g_free(path);
416 	    break;
417         }
418         case GAM_REQ_DEBUG:
419 #ifdef GAMIN_DEBUG_API
420 	    gam_debug_add(conn, req->path, options);
421 #else
422             GAM_DEBUG(DEBUG_INFO, "Unhandled debug request for %s\n",
423 		      req->path);
424 #endif
425             break;
426         default:
427             GAM_DEBUG(DEBUG_INFO, "Unknown request type %d for %s\n",
428                       type, req->path);
429             goto error;
430     }
431 
432     req->path[req->pathlen] = byte_save;
433     return (0);
434 error:
435     req->path[req->pathlen] = byte_save;
436     return (-1);
437 }
438 
439 /**
440  * gam_connection_data:
441  * @conn: the connection data structure
442  * @len: the amount of data added to the request buffer
443  *
444  * When receiving data, it should be read into an internal buffer
445  * retrieved using gam_connection_get_data.  After receiving some
446  * incoming data, call this to process the data.
447  *
448  * Returns 0 in case of success, -1 in case of error
449  */
450 int
gam_connection_data(GamConnDataPtr conn,int len)451 gam_connection_data(GamConnDataPtr conn, int len)
452 {
453     GAMPacketPtr req;
454 
455     g_assert(conn);
456     g_assert(len >= 0);
457     g_assert(conn->request_len >= 0);
458     g_assert(len + conn->request_len <= (int) sizeof(GAMPacket));
459 
460     conn->request_len += len;
461     req = &conn->request;
462 
463     /*
464      * loop processing all complete requests available in conn->request
465      */
466     while (1) {
467         if (conn->request_len < (int) GAM_PACKET_HEADER_LEN) {
468             /*
469              * we don't have enough data to check the current request
470              * keep it as a pending incomplete request and wait for more.
471              */
472             break;
473         }
474         /* check the packet total length */
475         if (req->len > sizeof(GAMPacket)) {
476             GAM_DEBUG(DEBUG_INFO, "malformed request: invalid length %d\n",
477 		      req->len);
478             return (-1);
479         }
480         /* check the version */
481         if (req->version != GAM_PROTO_VERSION) {
482             GAM_DEBUG(DEBUG_INFO, "unsupported version %d\n", req->version);
483             return (-1);
484         }
485 	if (GAM_REQ_CANCEL != req->type) {
486     	    /* double check pathlen and total length */
487     	    if ((req->pathlen <= 0) || (req->pathlen > MAXPATHLEN)) {
488         	GAM_DEBUG(DEBUG_INFO,
489 			  "malformed request: invalid path length %d\n",
490                 	  req->pathlen);
491         	return (-1);
492     	    }
493 	}
494         if (req->pathlen + GAM_PACKET_HEADER_LEN != req->len) {
495             GAM_DEBUG(DEBUG_INFO,
496 		      "malformed request: invalid packet sizes: %d %d\n",
497                       req->len, req->pathlen);
498             return (-1);
499         }
500         /* Check the type of the request: TODO !!! */
501 
502         if (conn->request_len < req->len) {
503             /*
504              * the current request is incomplete, wait for the rest.
505              */
506             break;
507         }
508 
509         if (gam_connection_request(conn, req) < 0) {
510             GAM_DEBUG(DEBUG_INFO, "gam_connection_request() failed\n");
511             return (-1);
512         }
513 
514         /*
515          * process any remaining request piggy-back'ed on the same packet
516          */
517         conn->request_len -= req->len;
518         if (conn->request_len == 0)
519             break;
520 
521 #if defined(__i386__) || defined(__x86_64__)
522 	req = (void *) req + req->len;
523 #else
524         memmove(&conn->request, (void *)req + req->len, conn->request_len);
525 #endif
526     }
527 
528     if ((conn->request_len > 0) && (req != &conn->request))
529 	memmove(&conn->request, req, conn->request_len);
530 
531     return (0);
532 }
533 
534 
535 /**
536  * gam_send_event:
537  * @conn: the connection
538  * @event: the event type
539  * @path: the path
540  *
541  * Send an event over a connection
542  *
543  * Returns 0 on success; -1 on failure
544  */
545 int
gam_send_event(GamConnDataPtr conn,int reqno,int event,const char * path,int len)546 gam_send_event(GamConnDataPtr conn, int reqno, int event,
547                const char *path, int len)
548 {
549     GAMPacket req;
550     size_t tlen;
551     int ret;
552     int type;
553 
554     g_assert(conn);
555     g_assert(conn->fd >= 0);
556     g_assert(path);
557     g_assert(path[len] == '\0');
558 
559     if (len >= MAXPATHLEN) {
560         GAM_DEBUG(DEBUG_INFO, "File path too long %s\n", path);
561         return (-1);
562     }
563 
564     /*
565      * Convert between Gamin/Marmot internal values and FAM ones.
566      */
567     switch (event) {
568         case GAMIN_EVENT_CHANGED:
569             type = FAMChanged;
570             break;
571         case GAMIN_EVENT_CREATED:
572             type = FAMCreated;
573             break;
574         case GAMIN_EVENT_DELETED:
575             type = FAMDeleted;
576             break;
577         case GAMIN_EVENT_MOVED:
578             type = FAMMoved;
579             break;
580         case GAMIN_EVENT_EXISTS:
581             type = FAMExists;
582             break;
583         case GAMIN_EVENT_ENDEXISTS:
584             type = FAMEndExist;
585             break;
586 #ifdef GAMIN_DEBUG_API
587 	case 50:
588 	    type = 50 + reqno;
589 	    break;
590 #endif
591         default:
592             GAM_DEBUG(DEBUG_INFO, "Unknown event type %d\n", event);
593             return (-1);
594     }
595 
596     GAM_DEBUG(DEBUG_INFO, "Event to %s : %d, %d, %s %s\n", conn->pidname,
597               reqno, type, path, gam_event_to_string(event));
598     /*
599      * prepare the packet
600      */
601     tlen = GAM_PACKET_HEADER_LEN + len;
602     /* We use only local socket so no need for network byte order conversion */
603     req.len = (unsigned short) tlen;
604     req.version = GAM_PROTO_VERSION;
605     req.seq = reqno;
606     req.type = (unsigned short) type;
607     req.pathlen = len;
608     memcpy(req.path, path, len);
609     ret = gam_client_conn_write(conn->source, conn->fd, (gpointer) &req, tlen);
610     if (!ret) {
611         GAM_DEBUG(DEBUG_INFO, "Failed to send event to %s\n", conn->pidname);
612         return (-1);
613     }
614     return (0);
615 }
616 
617 /**
618  * gam_queue_event:
619  * @conn: the connection
620  * @event: the event type
621  * @path: the path
622  *
623  * Queue an event to be sent over a connection within the next second.
624  * If an identical event is found at the tail of the event queue
625  * no event will be queued.
626  */
627 void
gam_queue_event(GamConnDataPtr conn,int reqno,int event,const char * path,int len)628 gam_queue_event(GamConnDataPtr conn, int reqno, int event,
629                 const char *path, int len)
630 {
631 	g_assert (conn);
632 	g_assert (conn->eq);
633 
634 	gam_eq_queue (conn->eq, reqno, event, path, len);
635 	if (!conn->eq_source)
636 	    conn->eq_source = g_timeout_add (100 /* 100 milisecond */, gam_connection_eq_flush, conn);
637 }
638 
639 
640 /**
641  * gam_send_ack:
642  * @conn: the connection data
643  * @path: the file/directory path
644  *
645  * Emit an acknowledge event on the connection
646  *
647  * Returns 0 on success; -1 on failure
648  */
649 int
gam_send_ack(GamConnDataPtr conn,int reqno,const char * path,int len)650 gam_send_ack(GamConnDataPtr conn, int reqno,
651              const char *path, int len)
652 {
653     GAMPacket req;
654     size_t tlen;
655     int ret;
656 
657     g_assert(conn);
658     g_assert(conn->fd >= 0);
659     g_assert(path);
660     g_assert(len > 0);
661     g_assert(path[len] == '\0');
662 
663     if (len >= MAXPATHLEN) {
664         GAM_DEBUG(DEBUG_INFO,
665 		  "path (%s)'s length (%d) exceeds MAXPATHLEN (%d)\n",
666 		  path, len, MAXPATHLEN);
667         return (-1);
668     }
669 
670     GAM_DEBUG(DEBUG_INFO, "Event to %s: %d, %d, %s\n", conn->pidname,
671               reqno, FAMAcknowledge, path);
672 
673     /*
674      * prepare the packet
675      */
676     tlen = GAM_PACKET_HEADER_LEN + len;
677     /* We only use local sockets so no need for network byte order
678        conversion */
679     req.len = (unsigned short) tlen;
680     req.version = GAM_PROTO_VERSION;
681     req.seq = reqno;
682     req.type = FAMAcknowledge;
683     req.pathlen = len;
684     memcpy(req.path, path, len);
685 
686     ret = gam_client_conn_write(conn->source, conn->fd, (gpointer) &req, tlen);
687     if (!ret) {
688         GAM_DEBUG(DEBUG_INFO, "Failed to send event to %s\n", conn->pidname);
689         return (-1);
690     }
691     return (0);
692 }
693 
694 /************************************************************************
695  *									*
696  *			Automatic exit handling				*
697  *									*
698  ************************************************************************/
699 
700 #define MAX_IDLE_TIMEOUT_MSEC (30*1000) /* 30 seconds */
701 
702 static guint server_timeout_id = 0;
703 
704 /**
705  * gam_connections_check:
706  *
707  * This function can be called periodically by e.g. g_timeout_add and
708  * shuts the server down if there have been no outstanding connections
709  * for a while.
710  */
711 static gboolean
gam_connections_check(void)712 gam_connections_check(void)
713 {
714     server_timeout_id = 0;
715 
716     if (gamConnList == NULL) {
717         GAM_DEBUG(DEBUG_INFO, "Exiting on timeout\n");
718 	gam_shutdown();
719         exit(0);
720     }
721     return (FALSE);
722 }
723 
724 static void
gam_cancel_server_timeout(void)725 gam_cancel_server_timeout (void)
726 {
727   if (server_timeout_id)
728     g_source_remove (server_timeout_id);
729   server_timeout_id = 0;
730 }
731 
732 void
gam_schedule_server_timeout(void)733 gam_schedule_server_timeout (void)
734 {
735   gam_cancel_server_timeout ();
736   server_timeout_id =
737     g_timeout_add(MAX_IDLE_TIMEOUT_MSEC, (GSourceFunc) gam_connections_check, NULL);
738 }
739 
740 /**
741  * gam_connections_debug:
742  *
743  * Calling this function generate debugging informations about the set
744  * of existing connections.
745  */
746 void
gam_connections_debug(void)747 gam_connections_debug(void)
748 {
749 #ifdef GAM_DEBUG_ENABLED
750     GamConnDataPtr conn;
751     GList *cur;
752 
753     if (!gam_debug_active)
754 	return;
755     if (gamConnList == NULL) {
756 	GAM_DEBUG(DEBUG_INFO, "No active connections\n");
757 	return;
758     }
759 
760     for (cur = gamConnList; cur; cur = g_list_next(cur)) {
761         conn = (GamConnDataPtr) cur->data;
762 	if (conn == NULL) {
763 	    GAM_DEBUG(DEBUG_INFO, "Error: connection with no data\n");
764 	} else {
765 	    const char *state = "unknown";
766 
767 	    switch (conn->state) {
768 	        case GAM_STATE_ERROR:
769 		    state = "error";
770 		    break;
771 	        case GAM_STATE_AUTH:
772 		    state = "need auth";
773 		    break;
774 	        case GAM_STATE_OKAY:
775 		    state = "okay";
776 		    break;
777 	        case GAM_STATE_CLOSED:
778 		    state = "closed";
779 		    break;
780 	    }
781 	    GAM_DEBUG(DEBUG_INFO,
782 	              "Connection fd %d to %s: state %s, %d read\n",
783 		      conn->fd, conn->pidname, state, conn->request_len);
784 	    gam_listener_debug(conn->listener);
785 	}
786     }
787 #endif
788 }
789