• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim:set expandtab ts=4 shiftwidth=4: */
3 /*
4  * Copyright (C) 2008 Sun Microsystems, Inc. All rights reserved.
5  * Use is subject to license terms.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General
18  * Public License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
20  * Boston, MA 02111-1307, USA.
21  *
22  * Authors: Lin Ma <lin.ma@sun.com>
23  */
24 
25 #include "config.h"
26 #include <port.h>
27 #include <sys/types.h>
28 #include <sys/time.h>
29 #include <sys/stat.h>
30 #include <errno.h>
31 #include <glib.h>
32 #include "fen-data.h"
33 #include "fen-kernel.h"
34 #include "fen-missing.h"
35 #include "fen-dump.h"
36 
37 #define	PROCESS_EVENTQ_TIME	10	/* in milliseconds */
38 #define PAIR_EVENTS_TIMEVAL	00000	/* in microseconds */
39 #define	PAIR_EVENTS_INC_TIMEVAL	0000	/* in microseconds */
40 #define SCAN_CHANGINGS_TIME	50	/* in milliseconds */
41 #define	SCAN_CHANGINGS_MAX_TIME	(4*100)	/* in milliseconds */
42 #define	SCAN_CHANGINGS_MIN_TIME	(4*100)	/* in milliseconds */
43 #define	INIT_CHANGES_NUM	2
44 #define	BASE_NUM	2
45 
46 #ifdef GIO_COMPILATION
47 #define FD_W if (fd_debug_enabled) g_warning
48 static gboolean fd_debug_enabled = FALSE;
49 #else
50 #include "gam_error.h"
51 #define FD_W(...) GAM_DEBUG(DEBUG_INFO, __VA_ARGS__)
52 #endif
53 
54 G_LOCK_EXTERN (fen_lock);
55 static GList *deleting_data = NULL;
56 static guint deleting_data_id = 0;
57 
58 static void (*emit_once_cb) (fdata *f, int events, gpointer sub);
59 static void (*emit_cb) (fdata *f, int events);
60 static int (*_event_converter) (int event);
61 
62 static gboolean fdata_delete (fdata* f);
63 static gint fdata_sub_find (gpointer a, gpointer b);
64 static void scan_children (node_t *f);
65 static void scan_known_children (node_t* f);
66 
67 node_t*
add_missing_cb(node_t * parent,gpointer user_data)68 add_missing_cb (node_t* parent, gpointer user_data)
69 {
70     g_assert (parent);
71     FD_W ("%s p:0x%p %s\n", __func__, parent, (gchar*)user_data);
72     return add_node (parent, (gchar*)user_data);
73 }
74 
75 gboolean
pre_del_cb(node_t * node,gpointer user_data)76 pre_del_cb (node_t* node, gpointer user_data)
77 {
78     fdata* data;
79 
80     g_assert (node);
81     data = node_get_data (node);
82     FD_W ("%s node:0x%p %s\n", __func__, node, NODE_NAME(node));
83     if (data != NULL) {
84         if (!FN_IS_PASSIVE(data)) {
85             return FALSE;
86         }
87         fdata_delete (data);
88     }
89     return TRUE;
90 }
91 
92 static guint
_pow(guint x,guint y)93 _pow (guint x, guint y)
94 {
95     guint z = 1;
96     g_assert (x >= 0 && y >= 0);
97     for (; y > 0; y--) {
98         z *= x;
99     }
100     return z;
101 }
102 
103 static guint
get_scalable_scan_time(fdata * data)104 get_scalable_scan_time (fdata* data)
105 {
106     guint sleep_time;
107     /* Caculate from num = 0 */
108     sleep_time = _pow (BASE_NUM, data->changed_event_num) * SCAN_CHANGINGS_TIME;
109     if (sleep_time < SCAN_CHANGINGS_MIN_TIME) {
110         sleep_time = SCAN_CHANGINGS_MIN_TIME;
111     } else if (sleep_time > SCAN_CHANGINGS_MAX_TIME) {
112         sleep_time = SCAN_CHANGINGS_MAX_TIME;
113         data->change_update_id = INIT_CHANGES_NUM;
114     }
115     FD_W ("SCALABE SCAN num:time [ %4u : %4u ] %s\n", data->changed_event_num, sleep_time, FN_NAME(data));
116     return sleep_time;
117 }
118 
119 static gboolean
g_timeval_lt(GTimeVal * val1,GTimeVal * val2)120 g_timeval_lt (GTimeVal *val1, GTimeVal *val2)
121 {
122     if (val1->tv_sec < val2->tv_sec)
123         return TRUE;
124 
125     if (val1->tv_sec > val2->tv_sec)
126         return FALSE;
127 
128     /* val1->tv_sec == val2->tv_sec */
129     if (val1->tv_usec < val2->tv_usec)
130         return TRUE;
131 
132     return FALSE;
133 }
134 
135 /**
136  * If all active children nodes are ported, then cancel monitor the parent node
137  *
138  * Unsafe, need lock.
139  */
140 static void
scan_known_children(node_t * f)141 scan_known_children (node_t* f)
142 {
143 	GDir *dir;
144 	GError *err = NULL;
145     fdata* pdata;
146 
147     FD_W ("%s %s [0x%p]\n", __func__, NODE_NAME(f), f);
148     pdata = node_get_data (f);
149     /*
150      * Currect fdata must is directly monitored. Be sure it is 1 level monitor.
151      */
152 	dir = g_dir_open (NODE_NAME(f), 0, &err);
153 	if (dir) {
154 		const char *basename;
155 
156 		while ((basename = g_dir_read_name (dir)))
157 		{
158             node_t* childf = NULL;
159             fdata* data;
160 			GList *idx;
161 			/*
162              * If the node is existed, and isn't ported, then emit created
163              * event. Ignore others.
164              */
165             childf = children_find (f, basename);
166             if (childf &&
167               (data = node_get_data (childf)) != NULL &&
168               !FN_IS_PASSIVE (data)) {
169                 if (!is_monitoring (data) &&
170                   port_add (&data->fobj, &data->len, data)) {
171                     fdata_emit_events (data, FN_EVENT_CREATED);
172                 }
173             }
174         }
175 		g_dir_close (dir);
176     } else {
177         FD_W (err->message);
178         g_error_free (err);
179     }
180 }
181 
182 static void
scan_children(node_t * f)183 scan_children (node_t *f)
184 {
185 	GDir *dir;
186 	GError *err = NULL;
187     fdata* pdata;
188 
189     FD_W ("%s %s [0x%p]\n", __func__, NODE_NAME(f), f);
190     pdata = node_get_data (f);
191     /*
192      * Currect fdata must is directly monitored. Be sure it is 1 level monitor.
193      */
194 	dir = g_dir_open (NODE_NAME(f), 0, &err);
195 	if (dir) {
196 		const char *basename;
197 
198 		while ((basename = g_dir_read_name (dir)))
199 		{
200             node_t* childf = NULL;
201             fdata* data;
202 			GList *idx;
203 
204             childf = children_find (f, basename);
205             if (childf == NULL) {
206                 gchar *filename;
207 
208                 filename = g_build_filename (NODE_NAME(f), basename, NULL);
209                 childf = add_node (f, filename);
210                 g_assert (childf);
211                 data = fdata_new (childf, FALSE);
212                 g_free (filename);
213             }
214             if ((data = node_get_data (childf)) == NULL) {
215                 data = fdata_new (childf, FALSE);
216             }
217             /* Be sure data isn't ported and add to port successfully */
218             /* Don't need delete it, it will be deleted by the parent */
219             if (is_monitoring (data)) {
220                 /* Ignored */
221             } else if (/* !is_ported (data) && */
222                 port_add (&data->fobj, &data->len, data)) {
223                 fdata_emit_events (data, FN_EVENT_CREATED);
224             }
225         }
226 		g_dir_close (dir);
227     } else {
228         FD_W (err->message);
229         g_error_free (err);
230     }
231 }
232 
233 static gboolean
scan_deleting_data(gpointer data)234 scan_deleting_data (gpointer data)
235 {
236     fdata *f;
237     GList* i;
238     GList* deleted_list = NULL;
239     gboolean ret = TRUE;
240 
241     if (G_TRYLOCK (fen_lock)) {
242         for (i = deleting_data; i; i = i->next) {
243             f = (fdata*)i->data;
244             if (fdata_delete (f)) {
245                 deleted_list = g_list_prepend (deleted_list, i);
246             }
247         }
248 
249         for (i = deleted_list; i; i = i->next) {
250             deleting_data = g_list_remove_link (deleting_data,
251               (GList *)i->data);
252             g_list_free_1 ((GList *)i->data);
253         }
254         g_list_free (deleted_list);
255 
256         if (deleting_data == NULL) {
257             deleting_data_id = 0;
258             ret = FALSE;
259         }
260         G_UNLOCK (fen_lock);
261     }
262     return ret;
263 }
264 
265 gboolean
is_monitoring(fdata * data)266 is_monitoring (fdata* data)
267 {
268     return is_ported (data) || data->change_update_id > 0;
269 }
270 
271 fdata*
get_parent_data(fdata * data)272 get_parent_data (fdata* data)
273 {
274     if (FN_NODE(data) && !IS_TOPNODE(FN_NODE(data))) {
275         return node_get_data (FN_NODE(data)->parent);
276     }
277     return NULL;
278 }
279 
280 node_t*
get_parent_node(fdata * data)281 get_parent_node (fdata* data)
282 {
283     if (FN_NODE(data)) {
284         return (FN_NODE(data)->parent);
285     }
286     return NULL;
287 }
288 
289 fdata *
fdata_new(node_t * node,gboolean is_mondir)290 fdata_new (node_t* node, gboolean is_mondir)
291 {
292 	fdata *f = NULL;
293 
294     g_assert (node);
295 	if ((f = g_new0 (fdata, 1)) != NULL) {
296         FN_NODE(f) = node;
297 		FN_NAME(f) = g_strdup (NODE_NAME(node));
298         f->is_dir = is_mondir;
299         f->eventq = g_queue_new ();
300         FD_W ("[ %s ] 0x%p %s\n", __func__, f, FN_NAME(f));
301         node_set_data (node, f);
302 	}
303 	return f;
304 }
305 
306 static gboolean
fdata_delete(fdata * f)307 fdata_delete (fdata *f)
308 {
309     fnode_event_t *ev;
310 
311     FD_W ("[ TRY %s ] 0x%p id[%4d:%4d] %s\n", __func__, f, f->eventq_id, f->change_update_id, FN_NAME(f));
312     g_assert (FN_IS_PASSIVE(f));
313 
314     port_remove (f);
315     /* missing_remove (f); */
316 
317     if (f->node != NULL) {
318         node_set_data (f->node, NULL);
319         f->node = NULL;
320     }
321 
322     if (f->change_update_id > 0 || f->eventq_id > 0) {
323         if (FN_IS_LIVING(f)) {
324             f->is_cancelled = TRUE;
325             deleting_data = g_list_prepend (deleting_data, f);
326             if (deleting_data_id == 0) {
327                 deleting_data_id = g_idle_add (scan_deleting_data, NULL);
328                 g_assert (deleting_data_id > 0);
329             }
330         }
331         return FALSE;
332     }
333     FD_W ("[ %s ] 0x%p %s\n", __func__, f, FN_NAME(f));
334 
335     while ((ev = g_queue_pop_head (f->eventq)) != NULL) {
336         fnode_event_delete (ev);
337     }
338 
339     g_queue_free (f->eventq);
340     g_free (FN_NAME(f));
341     g_free (f);
342     return TRUE;
343 }
344 
345 void
fdata_reset(fdata * data)346 fdata_reset (fdata* data)
347 {
348     fnode_event_t *ev;
349 
350     g_assert (data);
351 
352     while ((ev = g_queue_pop_head (data->eventq)) != NULL) {
353         fnode_event_delete (ev);
354     }
355 }
356 
357 static gint
fdata_sub_find(gpointer a,gpointer b)358 fdata_sub_find (gpointer a, gpointer b)
359 {
360     if (a != b) {
361         return 1;
362     } else {
363         return 0;
364     }
365 }
366 
367 void
fdata_sub_add(fdata * f,gpointer sub)368 fdata_sub_add (fdata *f, gpointer sub)
369 {
370     FD_W ("[%s] [data: 0x%p ] [s: 0x%p ] %s\n", __func__, f, sub, FN_NAME(f));
371     g_assert (g_list_find_custom (f->subs, sub, (GCompareFunc)fdata_sub_find) == NULL);
372     f->subs = g_list_prepend (f->subs, sub);
373 }
374 
375 void
fdata_sub_remove(fdata * f,gpointer sub)376 fdata_sub_remove (fdata *f, gpointer sub)
377 {
378     GList *l;
379     FD_W ("[%s] [data: 0x%p ] [s: 0x%p ] %s\n", __func__, f, sub, FN_NAME(f));
380     g_assert (g_list_find_custom (f->subs, sub, (GCompareFunc)fdata_sub_find) != NULL);
381     l = g_list_find_custom (f->subs, sub, (GCompareFunc)fdata_sub_find);
382     g_assert (l);
383     g_assert (sub == l->data);
384     f->subs = g_list_delete_link (f->subs, l);
385 }
386 
387 /**
388  * Adjust self on failing to Port
389  */
390 void
fdata_adjust_deleted(fdata * f)391 fdata_adjust_deleted (fdata* f)
392 {
393     node_t* parent;
394     fdata* pdata;
395     node_op_t op = {NULL, NULL, pre_del_cb, NULL};
396 
397     /*
398      * It's a top node. We move it to missing list.
399      */
400     parent = get_parent_node (f);
401     pdata = get_parent_data (f);
402     if (!FN_IS_PASSIVE(f) ||
403       children_num (FN_NODE(f)) > 0 ||
404       (pdata && !FN_IS_PASSIVE(pdata))) {
405         if (parent) {
406             if (pdata == NULL) {
407                 pdata = fdata_new (parent, FALSE);
408             }
409             g_assert (pdata);
410             if (!port_add (&pdata->fobj, &pdata->len, pdata)) {
411                 fdata_adjust_deleted (pdata);
412             }
413         } else {
414             /* f is root */
415             g_assert (IS_TOPNODE(FN_NODE(f)));
416             missing_add (f);
417         }
418     } else {
419 #ifdef GIO_COMPILATION
420         pending_remove_node (FN_NODE(f), &op);
421 #else
422         remove_node (FN_NODE(f), &op);
423 #endif
424     }
425 }
426 
427 static gboolean
fdata_adjust_changed(fdata * f)428 fdata_adjust_changed (fdata *f)
429 {
430     fnode_event_t *ev;
431     struct stat buf;
432     node_t* parent;
433     fdata* pdata;
434 
435     G_LOCK (fen_lock);
436     parent = get_parent_node (f);
437     pdata = get_parent_data (f);
438 
439     if (!FN_IS_LIVING(f) ||
440       (children_num (FN_NODE(f)) == 0 &&
441         FN_IS_PASSIVE(f) &&
442         pdata && FN_IS_PASSIVE(pdata))) {
443         f->change_update_id = 0;
444         G_UNLOCK (fen_lock);
445         return FALSE;
446     }
447 
448     FD_W ("[ %s ] %s\n", __func__, FN_NAME(f));
449     if (FN_STAT (FN_NAME(f), &buf) != 0) {
450         FD_W ("LSTAT [%-20s] %s\n", FN_NAME(f), g_strerror (errno));
451         goto L_delete;
452     }
453     f->is_dir = S_ISDIR (buf.st_mode) ? TRUE : FALSE;
454     if (f->len != buf.st_size) {
455         /* FD_W ("LEN [%lld:%lld] %s\n", f->len, buf.st_size, FN_NAME(f)); */
456         f->len = buf.st_size;
457         ev = fnode_event_new (FILE_MODIFIED, TRUE, f);
458         if (ev != NULL) {
459             ev->is_pending = TRUE;
460             fdata_add_event (f, ev);
461         }
462         /* Fdata is still changing, so scalable scan */
463         f->change_update_id = g_timeout_add (get_scalable_scan_time (f),
464           (GSourceFunc)fdata_adjust_changed,
465           (gpointer)f);
466         G_UNLOCK (fen_lock);
467         return FALSE;
468     } else {
469         f->changed_event_num = 0;
470         f->fobj.fo_atime = buf.st_atim;
471         f->fobj.fo_mtime = buf.st_mtim;
472         f->fobj.fo_ctime = buf.st_ctim;
473         if (FN_IS_DIR(f)) {
474             if (FN_IS_MONDIR(f)) {
475                 scan_children (FN_NODE(f));
476             } else {
477                 scan_known_children (FN_NODE(f));
478                 if ((children_num (FN_NODE(f)) == 0 &&
479                       FN_IS_PASSIVE(f) &&
480                       pdata && FN_IS_PASSIVE(pdata))) {
481                     port_remove (f);
482                     goto L_exit;
483                 }
484             }
485         }
486         if (!port_add_simple (&f->fobj, f)) {
487         L_delete:
488             ev = fnode_event_new (FILE_DELETE, FALSE, f);
489             if (ev != NULL) {
490                 fdata_add_event (f, ev);
491             }
492         }
493     }
494 L_exit:
495     f->change_update_id = 0;
496     G_UNLOCK (fen_lock);
497     return FALSE;
498 }
499 
500 void
fdata_emit_events_once(fdata * f,int event,gpointer sub)501 fdata_emit_events_once (fdata *f, int event, gpointer sub)
502 {
503     emit_once_cb (f, _event_converter (event), sub);
504 }
505 
506 void
fdata_emit_events(fdata * f,int event)507 fdata_emit_events (fdata *f, int event)
508 {
509     emit_cb (f, _event_converter (event));
510 }
511 
512 static gboolean
process_events(gpointer udata)513 process_events (gpointer udata)
514 {
515     node_op_t op = {NULL, NULL, pre_del_cb, NULL};
516     fdata* f;
517     fnode_event_t* ev;
518     int e;
519 
520     /* FD_W ("IN <======== %s\n", __func__); */
521 
522     f = (fdata*)udata;
523     FD_W ("%s 0x%p id:%-4d %s\n", __func__, f, f->eventq_id, FN_NAME(f));
524 
525     G_LOCK (fen_lock);
526 
527     if (!FN_IS_LIVING(f)) {
528         f->eventq_id = 0;
529         G_UNLOCK (fen_lock);
530         return FALSE;
531     }
532 
533     if ((ev = (fnode_event_t*)g_queue_pop_head (f->eventq)) != NULL) {
534         /* Send events to clients. */
535         e = ev->e;
536         if (!ev->is_pending) {
537 #ifdef GIO_COMPILATION
538             if (ev->has_twin) {
539                 fdata_emit_events (f, FILE_ATTRIB);
540             }
541 #endif
542             fdata_emit_events (f, ev->e);
543         }
544 
545         fnode_event_delete (ev);
546         ev = NULL;
547 
548         /* Adjust node state. */
549         /*
550          * Node the node has been created, so we can delete create event in
551          * optimizing. To reduce the statings, we add it to Port on discoving
552          * it then emit CREATED event. So we don't need to do anything here.
553          */
554         switch (e) {
555         case FILE_MODIFIED:
556         case MOUNTEDOVER:
557         case UNMOUNTED:
558             /* If the event is a changed event, then pending process it */
559             if (f->change_update_id == 0) {
560                 f->change_update_id = g_timeout_add (get_scalable_scan_time(f),
561                   (GSourceFunc)fdata_adjust_changed,
562                   (gpointer)f);
563                 g_assert (f->change_update_id > 0);
564             }
565             break;
566         case FILE_ATTRIB:
567             g_assert (f->change_update_id == 0);
568             if (!port_add (&f->fobj, &f->len, f)) {
569                 ev = fnode_event_new (FILE_DELETE, FALSE, f);
570                 if (ev != NULL) {
571                     fdata_add_event (f, ev);
572                 }
573             }
574             break;
575         case FILE_DELETE: /* Ignored */
576             break;
577         default:
578             g_assert_not_reached ();
579             break;
580         }
581         /* Process one event a time */
582         G_UNLOCK (fen_lock);
583         return TRUE;
584     }
585     f->eventq_id = 0;
586     G_UNLOCK (fen_lock);
587     /* FD_W ("OUT ========> %s\n", __func__); */
588     return FALSE;
589 }
590 
591 /**
592  * fdata_add_event:
593  *
594  */
595 void
fdata_add_event(fdata * f,fnode_event_t * ev)596 fdata_add_event (fdata *f, fnode_event_t *ev)
597 {
598     node_op_t op = {NULL, NULL, pre_del_cb, NULL};
599     fnode_event_t *tail;
600 
601     if (!FN_IS_LIVING(f)) {
602         fnode_event_delete (ev);
603         return;
604     }
605 
606     FD_W ("%s %d\n", __func__, ev->e);
607     g_get_current_time (&ev->t);
608     /*
609      * If created/deleted events of child node happened, then we use parent
610      * event queue to handle.
611      * If child node emits deleted event, it seems no changes for the parent
612      * node, but the attr is changed. So we may try to cancel processing the
613      * coming changed events of the parent node.
614      */
615     tail = (fnode_event_t*)g_queue_peek_tail (f->eventq);
616     switch (ev->e) {
617     case FILE_RENAME_FROM:
618     case FILE_RENAME_TO:
619     case FILE_ACCESS:
620         fnode_event_delete (ev);
621         g_assert_not_reached ();
622         return;
623     case FILE_DELETE:
624         /* clear changed event number */
625         f->changed_event_num = 0;
626         /*
627          * We will cancel all previous events.
628          */
629         if (tail) {
630             g_queue_pop_tail (f->eventq);
631             do {
632                 fnode_event_delete (tail);
633             } while ((tail = (fnode_event_t*)g_queue_pop_tail (f->eventq)) != NULL);
634         }
635         /*
636          * Given a node "f" is deleted, process it ASAP.
637          */
638         fdata_emit_events (f, ev->e);
639         fnode_event_delete (ev);
640         fdata_adjust_deleted (f);
641         return;
642     case FILE_MODIFIED:
643     case UNMOUNTED:
644     case MOUNTEDOVER:
645         /* clear changed event number */
646         f->changed_event_num ++;
647     case FILE_ATTRIB:
648     default:
649         /*
650          * If in the time range, we will try optimizing
651          * (changed+) to (changed)
652          * (attrchanged changed) to ([changed, attrchanged])
653          * (event attrchanged) to ([event, attrchanged])
654          */
655         if (tail) {
656             do {
657                 if (tail->e == ev->e) {
658                     if (g_timeval_lt (&ev->t, &tail->t)) {
659                         g_queue_peek_tail (f->eventq);
660                         /* Add the increment */
661                         g_time_val_add (&ev->t, PAIR_EVENTS_INC_TIMEVAL);
662                         /* skip the previous event */
663                         FD_W ("SKIPPED -- %s\n", _event_string (tail->e));
664                         fnode_event_delete (tail);
665                     } else {
666                         break;
667                     }
668                 } else if (ev->e == FILE_MODIFIED && tail->e == FILE_ATTRIB) {
669                     ev->has_twin = TRUE;
670                     fnode_event_delete (tail);
671                 } else if (ev->e == FILE_ATTRIB && f->change_update_id > 0) {
672                     tail->has_twin = TRUE;
673                     /* skip the current event */
674                     fnode_event_delete (ev);
675                     return;
676                 } else {
677                     break;
678                 }
679             } while ((tail = (fnode_event_t*)g_queue_peek_tail (f->eventq)) != NULL);
680         }
681     }
682 
683     /* must add the threshold time */
684     g_time_val_add (&ev->t, PAIR_EVENTS_TIMEVAL);
685 
686     g_queue_push_tail (f->eventq, ev);
687 
688     /* starting process_events */
689     if (f->eventq_id == 0) {
690         f->eventq_id = g_timeout_add (PROCESS_EVENTQ_TIME,
691           process_events,
692           (gpointer)f);
693         g_assert (f->eventq_id > 0);
694     }
695     FD_W ("%s 0x%p id:%-4d %s\n", __func__, f, f->eventq_id, FN_NAME(f));
696 }
697 
698 gboolean
fdata_class_init(void (* user_emit_cb)(fdata *,int),void (* user_emit_once_cb)(fdata *,int,gpointer),int (* user_event_converter)(int event))699 fdata_class_init (void (*user_emit_cb) (fdata*, int),
700   void (*user_emit_once_cb) (fdata*, int,  gpointer),
701   int (*user_event_converter) (int event))
702 {
703     FD_W ("%s\n", __func__);
704     if (user_emit_cb == NULL) {
705         return FALSE;
706     }
707     if (user_emit_once_cb == NULL) {
708         return FALSE;
709     }
710     if (user_event_converter == NULL) {
711         return FALSE;
712     }
713     emit_cb = user_emit_cb;
714     emit_once_cb = user_emit_once_cb;
715     _event_converter = user_event_converter;
716 
717     if (!port_class_init (fdata_add_event)) {
718         FD_W ("port_class_init failed.");
719         return FALSE;
720     }
721     return TRUE;
722 }
723