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