1 /* Gamin
2  * Copyright (C) 2003 James Willcox, Corey Bowers
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free
16  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18 
19 #include "server_config.h"
20 
21 #include <string.h>
22 #include <glib.h>
23 #include "gam_listener.h"
24 #include "gam_subscription.h"
25 #include "gam_server.h"
26 #include "gam_error.h"
27 #include "gam_pidname.h"
28 #ifdef ENABLE_INOTIFY
29 #include "gam_inotify.h"
30 #endif
31 
32 //#define GAM_LISTENER_VERBOSE
33 /* private struct representing a single listener */
34 struct _GamListener {
35     void *service;
36     int pid;
37     char *pidname;
38     GList *subs;
39 };
40 
41 /**
42  * @defgroup GamListener GamListener
43  * @ingroup Daemon
44  * @brief GamListener API.
45  *
46  * @{
47  */
48 
49 /**
50  * gam_listener_new:
51  *
52  * @service: service structure used to communicate with #GamListener
53  * @pid: the unique ID for this listener
54  *
55  * Creates a #GamListener
56  *
57  * Returns a new #GamListener on success, NULL otherwise
58  */
59 GamListener *
gam_listener_new(void * service,int pid)60 gam_listener_new(void *service, int pid)
61 {
62     GamListener *listener;
63 
64     g_assert(service);
65     g_assert(pid != 0);
66 
67     listener = g_new0(GamListener, 1);
68     listener->service = service;
69     listener->pid = pid;
70     listener->pidname = gam_get_pidname (pid);
71     listener->subs = NULL;
72 
73 #ifdef GAM_LISTENER_VERBOSE
74     GAM_DEBUG(DEBUG_INFO, "Created listener for %d\n", pid);
75 #endif
76 
77     return listener;
78 }
79 
80 /**
81  * gam_listener_free_subscription:
82  *
83  * @listener: the #GamListener
84  * @sub: the subscription to remove
85  *
86  * Frees a listener's subscription
87  */
88 static void
gam_listener_free_subscription(GamListener * listener,GamSubscription * sub)89 gam_listener_free_subscription(GamListener *listener,
90 			       GamSubscription *sub)
91 {
92     char *path;
93 
94     g_assert(listener);
95     g_assert(sub);
96     g_assert(g_list_find(listener->subs, sub));
97     path = g_strdup(gam_subscription_get_path(sub));
98 
99     gam_remove_subscription(sub);
100 #ifdef ENABLE_INOTIFY
101     if (gam_inotify_is_running() && (!gam_exclude_check(path))) {
102 	gam_fs_mon_type type;
103 
104 	type = gam_fs_get_mon_type (path);
105 	if (type != GFS_MT_POLL)
106 	    gam_subscription_free(sub);
107     }
108 #endif
109     g_free(path);
110 }
111 
112 /**
113  * gam_listener_free:
114  *
115  * @listener: the #GamListener to free
116  *
117  * Frees a #GamListener returned by #gam_listener_new
118  */
119 void
gam_listener_free(GamListener * listener)120 gam_listener_free(GamListener *listener)
121 {
122     GList *cur;
123 
124     g_assert(listener);
125 
126     while ((cur = g_list_first(listener->subs)) != NULL) {
127         GamSubscription * sub = cur->data;
128 	gam_listener_free_subscription(listener, sub);
129 	listener->subs = g_list_delete_link(listener->subs, cur);
130     }
131 	g_free(listener->pidname);
132     g_free(listener);
133 }
134 
135 /**
136  * gam_listener_get_service:
137  *
138  * @listener: the #GamListener
139  *
140  * Gets the service associated with a #GamListener
141  *
142  * Returns the service associated with the #GamListener.  The result
143  * is owned by the #GamListener and must not be freed by the caller.
144  */
145 void *
gam_listener_get_service(GamListener * listener)146 gam_listener_get_service(GamListener *listener)
147 {
148     return listener->service;
149 }
150 
151 /**
152  * gam_listener_get_pid:
153  *
154  * @listener: the #GamListener
155  *
156  * Gets the unique process ID associated with a #GamListener
157  *
158  * Returns the pid associated with the #GamListener.
159  */
160 int
gam_listener_get_pid(GamListener * listener)161 gam_listener_get_pid(GamListener *listener)
162 {
163     return listener->pid;
164 }
165 
166 /**
167  * gam_listener_get_pidname:
168  *
169  * @listener: the #GamListener
170  *
171  * Gets the process name associated with a #GamListener
172  *
173  * Returns the process name associated with the #GamListener.
174  */
175 const char *
gam_listener_get_pidname(GamListener * listener)176 gam_listener_get_pidname(GamListener *listener)
177 {
178     return listener->pidname;
179 }
180 
181 /**
182  * gam_listener_get_subscription:
183  *
184  * @listener: the #GamListener
185  * @path: a path to a file or directory
186  *
187  * Gets the subscription to a path
188  *
189  * Returns the #GamSubscription to path, or NULL if there is none
190  */
191 GamSubscription *
gam_listener_get_subscription(GamListener * listener,const char * path)192 gam_listener_get_subscription(GamListener *listener, const char *path)
193 {
194     GList *l;
195 
196     for (l = listener->subs; l; l = l->next) {
197         GamSubscription *sub = l->data;
198 
199         if (strcmp(gam_subscription_get_path(sub), path) == 0)
200             return sub;
201     }
202 
203     return NULL;
204 }
205 
206 /**
207  * gam_listener_get_subscription_by_reqno:
208  *
209  * @listener: the #GamListener
210  * @reqno: a subscription request number
211  *
212  * Gets the subscription represented by the given reqno
213  *
214  * Returns a #GamSubscription, or NULL if it wasn't found
215  */
216 GamSubscription *
gam_listener_get_subscription_by_reqno(GamListener * listener,int reqno)217 gam_listener_get_subscription_by_reqno(GamListener * listener, int reqno)
218 {
219     GList *l;
220 
221     for (l = listener->subs; l; l = l->next) {
222         GamSubscription *sub = l->data;
223 
224         if (gam_subscription_get_reqno(sub) == reqno)
225             return sub;
226     }
227 
228     return NULL;
229 }
230 
231 /**
232  * gam_listener_is_subscribed:
233  *
234  * @listener: the #GamListener
235  * @path: the path to the file or directory
236  *
237  * Returns whether a #GamListener is subscribed to a file or directory
238  *
239  * Returns TRUE if listener has a subscription to the path, FALSE
240  * otherwise.
241  */
242 gboolean
gam_listener_is_subscribed(GamListener * listener,const char * path)243 gam_listener_is_subscribed(GamListener *listener, const char *path)
244 {
245     return gam_listener_get_subscription(listener, path) != NULL;
246 }
247 
248 /**
249  * gam_listener_add_subscription:
250  *
251  * @listener: the #GamListener
252  * @sub: the #GamSubscription to add
253  *
254  * Adds a subscription to the #GamListener
255  */
256 void
gam_listener_add_subscription(GamListener * listener,GamSubscription * sub)257 gam_listener_add_subscription(GamListener *listener,
258                               GamSubscription *sub)
259 {
260     g_assert(listener);
261     g_assert(sub);
262     g_assert(!g_list_find(listener->subs, sub));
263 
264     listener->subs = g_list_prepend(listener->subs, sub);
265     GAM_DEBUG(DEBUG_INFO, "Adding sub %s to listener %s\n", gam_subscription_get_path (sub), listener->pidname);
266 }
267 
268 /**
269  * gam_listener_remove_subscription:
270  *
271  * @listener: the #GamListener
272  * @sub: the #GamSubscription to remove
273  *
274  * Removes a subscription from the #GamListener.
275  */
276 void
gam_listener_remove_subscription(GamListener * listener,GamSubscription * sub)277 gam_listener_remove_subscription(GamListener *listener,
278                                  GamSubscription *sub)
279 {
280     g_assert(listener);
281     g_assert(sub);
282     g_assert(g_list_find(listener->subs, sub));
283 
284     listener->subs = g_list_remove(listener->subs, sub);
285     GAM_DEBUG(DEBUG_INFO, "Removing sub %s from listener %s\n", gam_subscription_get_path (sub), listener->pidname);
286     /* There should only be one.  */
287     g_assert(!g_list_find(listener->subs, sub));
288 }
289 
290 /**
291  * gam_listener_get_subscriptions:
292  *
293  * @listener: the #GamListener
294  *
295  * Gets all the subscriptions a given listener has
296  *
297  * Returns a new list containing all of listener's subscriptions.  It
298  * is the responsibility of the caller to free the list.
299  */
300 GList *
gam_listener_get_subscriptions(GamListener * listener)301 gam_listener_get_subscriptions(GamListener *listener)
302 {
303     g_assert(listener);
304     return g_list_copy(listener->subs);
305 }
306 
307 /**
308  * gam_listener_debug:
309  *
310  * @listener: the #GamListener
311  *
312  * Print debugging information about a listener
313  */
314 void
gam_listener_debug(GamListener * listener)315 gam_listener_debug(GamListener *listener)
316 {
317 #ifdef GAM_DEBUG_ENABLED
318     GList *cur;
319 
320     if (listener == NULL) {
321 	GAM_DEBUG(DEBUG_INFO, "  Listener is NULL\n");
322         return;
323     }
324 
325     GAM_DEBUG(DEBUG_INFO, "  Listener %s has %d subscriptions registered\n", listener->pidname,
326               g_list_length(listener->subs));
327     for (cur = listener->subs; cur; cur = g_list_next(cur)) {
328 	gam_subscription_debug((GamSubscription *) cur->data);
329     }
330 #endif
331 }
332 
333 /** @} */
334