d01dc2add07c6849afa972abdc2c2798aaed7ffa
[apps/livebox/data-provider-master.git] / src / client_life.c
1 /*
2  * Copyright 2013  Samsung Electronics Co., Ltd
3  *
4  * Licensed under the Flora License, Version 1.1 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://floralicense.org/license/
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <stdio.h>
18 #include <errno.h>
19
20 #include <Eina.h>
21 #include <Ecore.h>
22
23 #include <dlog.h>
24 #include <packet.h>
25 #include <livebox-errno.h>
26
27 #include "client_life.h"
28 #include "instance.h"
29 #include "client_rpc.h"
30 #include "debug.h"
31 #include "util.h"
32 #include "slave_life.h"
33 #include "xmonitor.h"
34 #include "conf.h"
35
36 int errno;
37
38 static struct {
39         Eina_List *client_list;
40         int nr_of_paused_clients;
41
42         enum global_event_process {
43                 GLOBAL_EVENT_PROCESS_IDLE = 0x00,
44                 GLOBAL_EVENT_PROCESS_CREATE = 0x01,
45                 GLOBAL_EVENT_PROCESS_DESTROY = 0x02
46         } in_event_process;
47
48         Eina_List *create_event_list;
49         Eina_List *destroy_event_list;
50
51 } s_info = {
52         .client_list = NULL,
53         .nr_of_paused_clients = 0,
54         .in_event_process = GLOBAL_EVENT_PROCESS_IDLE,
55         .create_event_list = NULL,
56         .destroy_event_list = NULL,
57 };
58
59 struct subscribe_item {
60         char *cluster;
61         char *category;
62 };
63
64 struct global_event_item {
65         void *cbdata;
66         int (*cb)(struct client_node *client, void *data);
67         int deleted;
68 };
69
70 struct event_item {
71         void *data;
72         int (*cb)(struct client_node *, void *);
73         int deleted;
74 };
75
76 struct data_item {
77         char *tag;
78         void *data;
79 };
80
81 struct client_node {
82         pid_t pid;
83         int refcnt;
84
85         int paused;
86
87         enum client_event_process {
88                 CLIENT_EVENT_PROCESS_IDLE = 0x00,
89                 CLIENT_EVENT_PROCESS_DEACTIVATE = 0x01,
90                 CLIENT_EVENT_PROCESS_ACTIVATE = 0x02
91         } in_event_process;
92         Eina_List *event_deactivate_list;
93         Eina_List *event_activate_list;
94
95         Eina_List *data_list;
96         Eina_List *subscribe_list;
97
98         int faulted;
99 };
100
101 static inline void invoke_global_destroyed_cb(struct client_node *client)
102 {
103         Eina_List *l;
104         Eina_List *n;
105         struct global_event_item *item;
106
107         s_info.in_event_process |= GLOBAL_EVENT_PROCESS_DESTROY;
108         EINA_LIST_FOREACH_SAFE(s_info.destroy_event_list, l, n, item) {
109                 /*!
110                  * The first,
111                  * item->deleted will be checked, so if it is deleted, remove item from the list
112                  * The second, if the first routine takes false path,
113                  * item->cb will be called, if it returns negative value, remove item from the list
114                  * The third, if the second routine takes false path,
115                  * Check the item->deleted again, so if it is turnned on, remove item from the list
116                  */
117                 if (item->deleted || item->cb(client, item->cbdata) < 0 || item->deleted) {
118                         s_info.destroy_event_list = eina_list_remove(s_info.destroy_event_list, item);
119                         DbgFree(item);
120                 }
121         }
122         s_info.in_event_process &= ~GLOBAL_EVENT_PROCESS_DESTROY;
123 }
124
125 static inline void invoke_global_created_cb(struct client_node *client)
126 {
127         Eina_List *l;
128         Eina_List *n;
129         struct global_event_item *item;
130
131         s_info.in_event_process |= GLOBAL_EVENT_PROCESS_CREATE;
132         EINA_LIST_FOREACH_SAFE(s_info.create_event_list, l, n, item) {
133                 /*!
134                  * The first,
135                  * item->deleted will be checked, so if it is deleted, remove item from the list
136                  * The second, if the first routine takes false path,
137                  * item->cb will be called, if it returns negative value, remove item from the list
138                  * The third, if the second routine takes false path,
139                  * Check the item->deleted again, so if it is turnned on, remove item from the list
140                  */
141
142                 if (item->deleted || item->cb(client, item->cbdata) < 0 || item->deleted) {
143                         s_info.create_event_list = eina_list_remove(s_info.create_event_list, item);
144                         DbgFree(item);
145                 }
146         }
147         s_info.in_event_process &= ~GLOBAL_EVENT_PROCESS_CREATE;
148 }
149
150 static inline void invoke_deactivated_cb(struct client_node *client)
151 {
152         struct event_item *item;
153         Eina_List *l;
154         Eina_List *n;
155
156         client->in_event_process |= CLIENT_EVENT_PROCESS_DEACTIVATE;
157         EINA_LIST_FOREACH_SAFE(client->event_deactivate_list, l, n, item) {
158                 /*!
159                  * The first,
160                  * item->deleted will be checked, so if it is deleted, remove item from the list
161                  * The second, if the first routine takes false path,
162                  * item->cb will be called, if it returns negative value, remove item from the list
163                  * The third, if the second routine takes false path,
164                  * Check the item->deleted again, so if it is turnned on, remove item from the list
165                  */
166
167                 if (item->deleted || item->cb(client, item->data) < 0 || item->deleted) {
168                         client->event_deactivate_list = eina_list_remove(client->event_deactivate_list, item);
169                         DbgFree(item);
170                 }
171         }
172         client->in_event_process &= ~CLIENT_EVENT_PROCESS_DEACTIVATE;
173 }
174
175 static inline void invoke_activated_cb(struct client_node *client)
176 {
177         struct event_item *item;
178         Eina_List *l;
179         Eina_List *n;
180
181         client->in_event_process |= CLIENT_EVENT_PROCESS_ACTIVATE;
182         EINA_LIST_FOREACH_SAFE(client->event_activate_list, l, n, item) {
183                 /*!
184                  * The first,
185                  * item->deleted will be checked, so if it is deleted, remove item from the list
186                  * The second, if the first routine takes false path,
187                  * item->cb will be called, if it returns negative value, remove item from the list
188                  * The third, if the second routine takes false path,
189                  * Check the item->deleted again, so if it is turnned on, remove item from the list
190                  */
191
192                 if (item->deleted || item->cb(client, item->data) < 0 || item->deleted) {
193                         client->event_activate_list = eina_list_remove(client->event_activate_list, item);
194                         DbgFree(item);
195                 }
196         }
197         client->in_event_process &= ~CLIENT_EVENT_PROCESS_ACTIVATE;
198 }
199
200 static inline void destroy_client_data(struct client_node *client)
201 {
202         struct event_item *event;
203         struct data_item *data;
204         struct subscribe_item *item;
205         Ecore_Timer *timer;
206
207         timer = client_del_data(client, "create,timer");
208         if (timer) {
209                 ecore_timer_del(timer);
210         }
211
212         DbgPrint("Destroy client: %p\n", client);
213
214         invoke_global_destroyed_cb(client);
215         client_rpc_fini(client); /*!< Finalize the RPC after invoke destroy callbacks */
216
217         EINA_LIST_FREE(client->data_list, data) {
218                 DbgPrint("Tag is not cleared (%s)\n", data->tag);
219                 DbgFree(data->tag);
220                 DbgFree(data);
221         }
222
223         EINA_LIST_FREE(client->event_deactivate_list, event) {
224                 DbgFree(event);
225         }
226
227         EINA_LIST_FREE(client->subscribe_list, item) {
228                 DbgFree(item->cluster);
229                 DbgFree(item->category);
230                 DbgFree(item);
231         }
232
233         if (client->paused) {
234                 s_info.nr_of_paused_clients--;
235         }
236
237         s_info.client_list = eina_list_remove(s_info.client_list, client);
238         DbgFree(client);
239
240         /*!
241          * \note
242          * If there is any changes of clients,
243          * We should check the pause/resume states again.
244          */
245         xmonitor_handle_state_changes();
246 }
247
248 static inline struct client_node *create_client_data(pid_t pid)
249 {
250         struct client_node *client;
251
252         client = calloc(1, sizeof(*client));
253         if (!client) {
254                 ErrPrint("Heap: %s\n", strerror(errno));
255                 return NULL;
256         }
257
258         client->pid = pid;
259         client->refcnt = 1;
260
261         s_info.client_list = eina_list_append(s_info.client_list, client);
262
263         /*!
264          * \note
265          * Right after create a client ADT,
266          * We assume that the client is paused.
267          */
268         client_paused(client);
269         xmonitor_handle_state_changes();
270         return client;
271 }
272
273 static Eina_Bool created_cb(void *data)
274 {
275         (void)client_del_data(data, "create,timer");
276
277         invoke_global_created_cb(data);
278         invoke_activated_cb(data);
279         /*!
280          * \note
281          * Client PAUSE/RESUME event must has to be sent after created event.
282          */
283         xmonitor_update_state(client_pid(data));
284
285         (void)client_unref(data);
286         return ECORE_CALLBACK_CANCEL;
287 }
288
289 /*!
290  * \note
291  * Noramlly, client ADT is created when it send the "acquire" packet.
292  * It means we have the handle for communicating with the client already,
293  * So we just create its ADT in this function.
294  * And invoke the global created event & activated event callbacks
295  */
296 HAPI struct client_node *client_create(pid_t pid, int handle)
297 {
298         struct client_node *client;
299         int ret;
300
301         client = client_find_by_rpc_handle(handle);
302         if (client) {
303                 ErrPrint("Client %d(%d) is already exists\n", pid, handle);
304                 return client;
305         }
306
307         client = create_client_data(pid);
308         if (!client) {
309                 ErrPrint("Failed to create a new client (%d)\n", pid);
310                 return NULL;
311         }
312
313         ret = client_rpc_init(client, handle);
314         if (ret < 0) {
315                 client = client_unref(client);
316                 ErrPrint("Failed to initialize the RPC for %d, Destroy client data %p(has to be 0x0)\n", pid, client);
317         } else {
318                 Ecore_Timer *create_timer;
319                 /*!
320                  * \note
321                  * To save the time to send reply packet to the client.
322                  */
323                 create_timer = ecore_timer_add(DELAY_TIME, created_cb, client_ref(client));
324                 if (create_timer == NULL) {
325                         ErrPrint("Failed to add a timer for client created event\n");
326                         client = client_unref(client); /* Decrease refcnt for argument */
327                         client = client_unref(client); /* Destroy client object */
328                         return NULL;
329                 }
330
331                 ret = client_set_data(client, "create,timer", create_timer);
332                 DbgPrint("Set data: %d\n", ret);
333         }
334
335         return client;
336 }
337
338 HAPI struct client_node *client_ref(struct client_node *client)
339 {
340         if (!client) {
341                 return NULL;
342         }
343
344         client->refcnt++;
345         return client;
346 }
347
348 HAPI struct client_node *client_unref(struct client_node *client)
349 {
350         if (!client) {
351                 return NULL;
352         }
353
354         if (client->refcnt == 0) {
355                 ErrPrint("Client refcnt is not managed correctly\n");
356                 return NULL;
357         }
358
359         client->refcnt--;
360         if (client->refcnt == 0) {
361                 destroy_client_data(client);
362                 client = NULL;
363         }
364
365         return client;
366 }
367
368 HAPI const int const client_refcnt(const struct client_node *client)
369 {
370         return client->refcnt;
371 }
372
373 HAPI const pid_t const client_pid(const struct client_node *client)
374 {
375         return client ? client->pid : (pid_t)-1;
376 }
377
378 HAPI struct client_node *client_find_by_pid(pid_t pid)
379 {
380         Eina_List *l;
381         struct client_node *client;
382
383         EINA_LIST_FOREACH(s_info.client_list, l, client) {
384                 if (client->pid == pid) {
385                         return client;
386                 }
387         }
388
389         return NULL;
390 }
391
392 HAPI struct client_node *client_find_by_rpc_handle(int handle)
393 {
394         Eina_List *l;
395         struct client_node *client;
396
397         if (handle <= 0) {
398                 ErrPrint("Invalid handle %d\n", handle);
399                 return NULL;
400         }
401
402         EINA_LIST_FOREACH(s_info.client_list, l, client) {
403                 if (client_rpc_handle(client) == handle) {
404                         return client;
405                 }
406         }
407
408         return NULL;
409 }
410
411 HAPI const int const client_count_paused(void)
412 {
413         return s_info.nr_of_paused_clients;
414 }
415
416 HAPI int client_is_all_paused(void)
417 {
418         DbgPrint("%d, %d\n", eina_list_count(s_info.client_list), s_info.nr_of_paused_clients);
419         return eina_list_count(s_info.client_list) == s_info.nr_of_paused_clients;
420 }
421
422 HAPI int client_count(void)
423 {
424         return eina_list_count(s_info.client_list);
425 }
426
427 HAPI struct client_node *client_deactivated_by_fault(struct client_node *client)
428 {
429         if (!client || client->faulted) {
430                 return client;
431         }
432
433         ErrPrint("Client[%p] is faulted(%d), pid(%d)\n", client, client->refcnt, client->pid);
434         client->faulted = 1;
435         client->pid = (pid_t)-1;
436
437         invoke_deactivated_cb(client);
438         client = client_destroy(client);
439         /*!
440          * \todo
441          * Who invokes this function has to care the reference counter of a client
442          * do I need to invoke the deactivated callback from here?
443          * client->pid = (pid_t)-1;
444          * slave_unref(client)
445          */
446         return client;
447 }
448
449 HAPI const int const client_is_faulted(const struct client_node *client)
450 {
451         /*!
452          * \note
453          * If the "client" is NIL, I assume that it is fault so returns TRUE(1)
454          */
455         return client ? client->faulted : 1;
456 }
457
458 HAPI void client_reset_fault(struct client_node *client)
459 {
460         if (client) {
461                 client->faulted = 0;
462         }
463 }
464
465 HAPI int client_event_callback_add(struct client_node *client, enum client_event event, int (*cb)(struct client_node *, void *), void *data)
466 {
467         struct event_item *item;
468
469         if (!cb) {
470                 ErrPrint("Invalid callback (cb == NULL)\n");
471                 return LB_STATUS_ERROR_INVALID;
472         }
473
474         item = malloc(sizeof(*item));
475         if (!item) {
476                 ErrPrint("Heap: %s\n", strerror(errno));
477                 return LB_STATUS_ERROR_MEMORY;
478         }
479
480         item->cb = cb;
481         item->data = data;
482         item->deleted = 0;
483
484         /*!
485          * \note
486          * Use the eina_list_prepend API.
487          * To keep the sequence of a callback invocation.
488          *
489          * Here is an example sequence.
490          *
491          * client_event_callback_add(CALLBACK_01);
492          * client_event_callback_add(CALLBACK_02);
493          * client_event_callback_add(CALLBACK_03);
494          *
495          * Then the invoke_event_callback function will call the CALLBACKS as below sequence
496          *
497          * invoke_CALLBACK_03
498          * invoke_CALLBACK_02
499          * invoke_CALLBACK_01
500          */
501
502         switch (event) {
503         case CLIENT_EVENT_DEACTIVATE:
504                 client->event_deactivate_list = eina_list_prepend(client->event_deactivate_list, item);
505                 break;
506         case CLIENT_EVENT_ACTIVATE:
507                 client->event_activate_list = eina_list_prepend(client->event_activate_list, item);
508                 break;
509         default:
510                 DbgFree(item);
511                 return LB_STATUS_ERROR_INVALID;
512         }
513
514         return LB_STATUS_SUCCESS;
515 }
516
517 HAPI int client_event_callback_del(struct client_node *client, enum client_event event, int (*cb)(struct client_node *, void *), void *data)
518 {
519         struct event_item *item;
520         Eina_List *l;
521         Eina_List *n;
522
523         if (!cb) {
524                 ErrPrint("Invalid callback (cb == NULL)\n");
525                 return LB_STATUS_ERROR_INVALID;
526         }
527
528         switch (event) {
529         case CLIENT_EVENT_DEACTIVATE:
530                 EINA_LIST_FOREACH_SAFE(client->event_deactivate_list, l, n, item) {
531                         if (item->cb == cb && item->data == data) {
532                                 if (client->in_event_process & CLIENT_EVENT_PROCESS_DEACTIVATE) {
533                                         item->deleted = 1;
534                                 } else {
535                                         client->event_deactivate_list = eina_list_remove(client->event_deactivate_list, item);
536                                         DbgFree(item);
537                                 }
538                                 return LB_STATUS_SUCCESS;
539                         }
540                 }
541                 break;
542
543         case CLIENT_EVENT_ACTIVATE:
544                 EINA_LIST_FOREACH_SAFE(client->event_activate_list, l, n, item) {
545                         if (item->cb == cb && item->data == data) {
546                                 if (client->in_event_process & CLIENT_EVENT_PROCESS_ACTIVATE) {
547                                         item->deleted = 1;
548                                 } else {
549                                         client->event_activate_list = eina_list_remove(client->event_activate_list, item);
550                                         DbgFree(item);
551                                 }
552                                 return LB_STATUS_SUCCESS;
553                         }
554                 }
555                 break;
556
557         default:
558                 ErrPrint("Invalid event\n");
559                 break;
560         }
561
562         return LB_STATUS_ERROR_NOT_EXIST;
563 }
564
565 HAPI int client_set_data(struct client_node *client, const char *tag, void *data)
566 {
567         struct data_item *item;
568
569         item = calloc(1, sizeof(*item));
570         if (!item) {
571                 ErrPrint("Heap: %s\n", strerror(errno));
572                 return LB_STATUS_ERROR_MEMORY;
573         }
574
575         item->tag = strdup(tag);
576         if (!item->tag) {
577                 ErrPrint("Heap: %s\n", strerror(errno));
578                 DbgFree(item);
579                 return LB_STATUS_ERROR_MEMORY;
580         }
581
582         item->data = data;
583
584         client->data_list = eina_list_append(client->data_list, item);
585         return LB_STATUS_SUCCESS;
586 }
587
588 HAPI void *client_data(struct client_node *client, const char *tag)
589 {
590         Eina_List *l;
591         struct data_item *item;
592
593         EINA_LIST_FOREACH(client->data_list, l, item) {
594                 if (!strcmp(item->tag, tag)) {
595                         return item->data;
596                 }
597         }
598
599         return NULL;
600 }
601
602 HAPI void *client_del_data(struct client_node *client, const char *tag)
603 {
604         Eina_List *l;
605         Eina_List *n;
606         struct data_item *item;
607
608         EINA_LIST_FOREACH_SAFE(client->data_list, l, n, item) {
609                 if (!strcmp(item->tag, tag)) {
610                         void *data;
611                         client->data_list = eina_list_remove(client->data_list, item);
612                         data = item->data;
613                         DbgFree(item->tag);
614                         DbgFree(item);
615                         return data;
616                 }
617         }
618
619         return NULL;
620 }
621
622 HAPI void client_paused(struct client_node *client)
623 {
624         if (client->paused) {
625                 return;
626         }
627
628         client->paused = 1;
629         s_info.nr_of_paused_clients++;
630 }
631
632 HAPI void client_resumed(struct client_node *client)
633 {
634         if (client->paused == 0) {
635                 return;
636         }
637
638         client->paused = 0;
639         s_info.nr_of_paused_clients--;
640 }
641
642 HAPI int client_init(void)
643 {
644         return LB_STATUS_SUCCESS;
645 }
646
647 HAPI void client_fini(void)
648 {
649         struct global_event_item *handler;
650         struct client_node *client;
651         Eina_List *l;
652         Eina_List *n;
653
654         EINA_LIST_FOREACH_SAFE(s_info.client_list, l, n, client) {
655                 (void)client_destroy(client);
656         }
657
658         EINA_LIST_FREE(s_info.create_event_list, handler) {
659                 DbgFree(handler);
660         }
661
662         EINA_LIST_FREE(s_info.destroy_event_list, handler) {
663                 DbgFree(handler);
664         }
665 }
666
667 HAPI int client_global_event_handler_add(enum client_global_event event_type, int (*cb)(struct client_node *client, void *data), void *data)
668 {
669         struct global_event_item *handler;
670
671         handler = malloc(sizeof(*handler));
672         if (!handler) {
673                 ErrPrint("Heap: %s\n", strerror(errno));
674                 return LB_STATUS_ERROR_MEMORY;
675         }
676
677         handler->cbdata = data;
678         handler->cb = cb;
679         handler->deleted = 0;
680
681         switch (event_type) {
682         case CLIENT_GLOBAL_EVENT_CREATE:
683                 s_info.create_event_list = eina_list_prepend(s_info.create_event_list, handler);
684                 break;
685         case CLIENT_GLOBAL_EVENT_DESTROY:
686                 s_info.destroy_event_list = eina_list_prepend(s_info.destroy_event_list, handler);
687                 break;
688         default:
689                 DbgFree(handler);
690                 return LB_STATUS_ERROR_INVALID;
691         }
692
693         return LB_STATUS_SUCCESS;
694 }
695
696 HAPI int client_global_event_handler_del(enum client_global_event event_type, int (*cb)(struct client_node *, void *), void *data)
697 {
698         Eina_List *l;
699         Eina_List *n;
700         struct global_event_item *item;
701
702         switch (event_type) {
703         case CLIENT_GLOBAL_EVENT_CREATE:
704                 EINA_LIST_FOREACH_SAFE(s_info.create_event_list, l, n, item) {
705                         if (item->cb == cb && item->cbdata == data) {
706                                 if (s_info.in_event_process & GLOBAL_EVENT_PROCESS_CREATE) {
707                                         item->deleted = 1;
708                                 } else {
709                                         s_info.create_event_list = eina_list_remove(s_info.create_event_list, item);
710                                         DbgFree(item);
711                                 }
712                                 return LB_STATUS_SUCCESS;
713                         }
714                 }
715                 break;
716         case CLIENT_GLOBAL_EVENT_DESTROY:
717                 EINA_LIST_FOREACH_SAFE(s_info.destroy_event_list, l, n, item) {
718                         if (item->cb == cb && item->cbdata == data) {
719                                 if (s_info.in_event_process & GLOBAL_EVENT_PROCESS_DESTROY) {
720                                         item->deleted = 1;
721                                 } else {
722                                         s_info.destroy_event_list = eina_list_remove(s_info.destroy_event_list, item);
723                                         DbgFree(item);
724                                 }
725                                 return LB_STATUS_SUCCESS;
726                         }
727                 }
728                 break;
729         default:
730                 break;
731         }
732
733         return LB_STATUS_ERROR_NOT_EXIST;
734 }
735
736 HAPI int client_subscribe(struct client_node *client, const char *cluster, const char *category)
737 {
738         struct subscribe_item *item;
739
740         item = malloc(sizeof(*item));
741         if (!item) {
742                 ErrPrint("Heap: %s\n", strerror(errno));
743                 return LB_STATUS_ERROR_MEMORY;
744         }
745
746         item->cluster = strdup(cluster);
747         if (!item->cluster) {
748                 ErrPrint("Heap: %s\n", strerror(errno));
749                 DbgFree(item);
750                 return LB_STATUS_ERROR_MEMORY;
751         }
752
753         item->category = strdup(category);
754         if (!item->category) {
755                 ErrPrint("Heap: %s\n", strerror(errno));
756                 DbgFree(item->cluster);
757                 DbgFree(item);
758                 return LB_STATUS_ERROR_MEMORY;
759         }
760
761         client->subscribe_list = eina_list_append(client->subscribe_list, item);
762         return LB_STATUS_SUCCESS;
763 }
764
765 HAPI int client_unsubscribe(struct client_node *client, const char *cluster, const char *category)
766 {
767         struct subscribe_item *item;
768         Eina_List *l;
769         Eina_List *n;
770
771         EINA_LIST_FOREACH_SAFE(client->subscribe_list, l, n, item) {
772                 if (!strcasecmp(cluster, item->cluster) && !strcasecmp(category, item->category)) {
773                         client->subscribe_list = eina_list_remove(client->subscribe_list, item);
774                         DbgFree(item->cluster);
775                         DbgFree(item->category);
776                         DbgFree(item);
777                         return LB_STATUS_SUCCESS;
778                 }
779         }
780
781         return LB_STATUS_ERROR_NOT_EXIST;
782 }
783
784 HAPI int client_is_subscribed(struct client_node *client, const char *cluster, const char *category)
785 {
786         struct subscribe_item *item;
787         Eina_List *l;
788
789         EINA_LIST_FOREACH(client->subscribe_list, l, item) {
790                 if (!strcmp(item->cluster, "*")) {
791                         return 1;
792                 }
793
794                 if (strcasecmp(item->cluster, cluster)) {
795                         continue;
796                 }
797
798                 if (!strcmp(item->category, "*")) {
799                         return 1;
800                 }
801
802                 if (!strcasecmp(item->category, category)) {
803                         return 1;
804                 }
805         }
806
807         return 0;
808 }
809
810 HAPI int client_browse_list(const char *cluster, const char *category, int (*cb)(struct client_node *client, void *data), void *data)
811 {
812         Eina_List *l;
813         struct client_node *client;
814         int cnt;
815
816         if (!cb || !cluster || !category) {
817                 return LB_STATUS_ERROR_INVALID;
818         }
819
820         cnt = 0;
821         EINA_LIST_FOREACH(s_info.client_list, l, client) {
822                 if (!client_is_subscribed(client, cluster, category)) {
823                         continue;
824                 }
825
826                 if (cb(client, data) < 0) {
827                         return LB_STATUS_ERROR_CANCEL;
828                 }
829
830                 cnt++;
831         }
832
833         return cnt;
834 }
835
836 HAPI int client_nr_of_subscriber(const char *cluster, const char *category)
837 {
838         Eina_List *l;
839         struct client_node *client;
840         int cnt;
841
842         cnt = 0;
843         EINA_LIST_FOREACH(s_info.client_list, l, client) {
844                 cnt += !!client_is_subscribed(client, cluster, category);
845         }
846
847         return cnt;
848 }
849
850 HAPI int client_broadcast(struct inst_info *inst, struct packet *packet)
851 {
852         Eina_List *l;
853         Eina_List *list;
854         struct client_node *client;
855
856         list = inst ? instance_client_list(inst) : s_info.client_list;
857         EINA_LIST_FOREACH(list, l, client) {
858                 if (client_pid(client) == -1) {
859                         ErrPrint("Client[%p] has PID[%d]\n", client, client_pid(client));
860                         continue;
861                 }
862
863                 (void)client_rpc_async_request(client, packet_ref(packet));
864         }
865
866         packet_unref(packet);
867         return LB_STATUS_SUCCESS;
868 }
869
870 /* End of a file */