diff options
Diffstat (limited to 'src/lib/common/watch.c')
-rw-r--r-- | src/lib/common/watch.c | 235 |
1 files changed, 235 insertions, 0 deletions
diff --git a/src/lib/common/watch.c b/src/lib/common/watch.c new file mode 100644 index 0000000..e6b5317 --- /dev/null +++ b/src/lib/common/watch.c | |||
@@ -0,0 +1,235 @@ | |||
1 | #include "watch.h" | ||
2 | |||
3 | #include <stdbool.h> | ||
4 | |||
5 | #define LOG(kind, ...) GNUNET_log_from (kind, "dbus-watch", __VA_ARGS__) | ||
6 | |||
7 | /* | ||
8 | * Wraps a file descriptor that needs to be watched | ||
9 | * for activity with select() | ||
10 | */ | ||
11 | struct Watch | ||
12 | { | ||
13 | /* | ||
14 | * DBus watch data. Contains the actual file descritor wrapped by libdbus | ||
15 | */ | ||
16 | DBusWatch *watch; | ||
17 | |||
18 | /* | ||
19 | * Have we asked the scheduler to watch this? | ||
20 | * Will be false if the associated task has not been | ||
21 | * re-scheduled yet after execution or because dbus has asked | ||
22 | * us to disable this watch. | ||
23 | */ | ||
24 | bool scheduled; | ||
25 | |||
26 | /* | ||
27 | * The task that is watching our file descriptor. | ||
28 | * Only valid if scheduled is true. | ||
29 | */ | ||
30 | struct GNUNET_SCHEDULER_Task *task; | ||
31 | |||
32 | struct GNUNET_NETWORK_Handle *net_handle; | ||
33 | struct GNUNET_DISK_FileHandle *file_handle; | ||
34 | |||
35 | unsigned ref_count; | ||
36 | }; | ||
37 | |||
38 | struct Watch * | ||
39 | watch_create ( | ||
40 | DBusWatch *watch) | ||
41 | { | ||
42 | struct Watch *w = GNUNET_new (struct Watch); | ||
43 | |||
44 | w->watch = watch; | ||
45 | w->scheduled = false; | ||
46 | w->net_handle = NULL; | ||
47 | w->file_handle = NULL; | ||
48 | w->ref_count = 1; | ||
49 | |||
50 | SOCKTYPE sock = dbus_watch_get_socket (watch); | ||
51 | if (-1 != sock) | ||
52 | { | ||
53 | w->net_handle = GNUNET_NETWORK_socket_box_native (sock); | ||
54 | if (NULL == w->net_handle) | ||
55 | { | ||
56 | LOG (GNUNET_ERROR_TYPE_ERROR, "Failed to box network socket passed in from dbus.\n"); | ||
57 | GNUNET_abort_ (); | ||
58 | }; | ||
59 | } | ||
60 | else { | ||
61 | int fd = dbus_watch_get_unix_fd (watch); | ||
62 | if (-1 != fd) | ||
63 | { | ||
64 | w->file_handle = GNUNET_DISK_get_handle_from_int_fd (fd); | ||
65 | if (NULL == w->file_handle) | ||
66 | { | ||
67 | LOG (GNUNET_ERROR_TYPE_ERROR, "Failed to box file handle passed in from dbus.\n"); | ||
68 | GNUNET_abort_ (); | ||
69 | }; | ||
70 | }; | ||
71 | }; | ||
72 | |||
73 | if (! w->net_handle && ! w->file_handle) | ||
74 | { | ||
75 | LOG (GNUNET_ERROR_TYPE_ERROR, "Failed to create watch. dbus_watch_get_socket returned %d\n", (int)sock); | ||
76 | GNUNET_abort_ (); | ||
77 | }; | ||
78 | |||
79 | return w; | ||
80 | }; | ||
81 | |||
82 | void | ||
83 | watch_ref ( | ||
84 | struct Watch *w) | ||
85 | { | ||
86 | w->ref_count++; | ||
87 | }; | ||
88 | |||
89 | void | ||
90 | watch_unref ( | ||
91 | struct Watch *w) | ||
92 | { | ||
93 | if (0 == w->ref_count) | ||
94 | { | ||
95 | LOG (GNUNET_ERROR_TYPE_ERROR, "Tried to unref watch with ref_count == 0\n"); | ||
96 | GNUNET_abort_ (); | ||
97 | }; | ||
98 | |||
99 | if (0 == --w->ref_count) | ||
100 | { | ||
101 | if (w->net_handle) | ||
102 | GNUNET_free (w->net_handle); | ||
103 | if (w->file_handle) | ||
104 | GNUNET_free (w->file_handle); | ||
105 | |||
106 | GNUNET_free (w); | ||
107 | } | ||
108 | }; | ||
109 | |||
110 | /* | ||
111 | * Callback called by the scheduler to tell libdbus that there is activity on | ||
112 | * one of its file descriptors. | ||
113 | * | ||
114 | * @param cls The watch | ||
115 | * @param tc the context given to us by the scheduler for this execution | ||
116 | */ | ||
117 | void | ||
118 | handle_watch ( | ||
119 | void *cls, | ||
120 | const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
121 | { | ||
122 | struct Watch *w = (struct Watch *)cls; | ||
123 | |||
124 | w->scheduled = false; | ||
125 | |||
126 | unsigned flags = 0; | ||
127 | if (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY) | ||
128 | flags |= DBUS_WATCH_READABLE; | ||
129 | if (tc->reason & GNUNET_SCHEDULER_REASON_WRITE_READY) | ||
130 | flags |= DBUS_WATCH_WRITABLE; | ||
131 | if (flags) | ||
132 | { | ||
133 | dbus_watch_handle(w->watch, flags); | ||
134 | }; | ||
135 | |||
136 | if(w->ref_count > 1 && ! (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) | ||
137 | watch_schedule (w); | ||
138 | watch_unref (w); | ||
139 | }; | ||
140 | |||
141 | /* | ||
142 | * Ask the scheduler to watch this watch for activity. | ||
143 | * | ||
144 | * @param w The watch | ||
145 | * @return GNUNET_OK or GNUNET_SYSERR | ||
146 | */ | ||
147 | void | ||
148 | watch_schedule ( | ||
149 | struct Watch *w) | ||
150 | { | ||
151 | unsigned flags = dbus_watch_get_flags (w->watch); | ||
152 | |||
153 | if (! dbus_watch_get_enabled (w->watch)) | ||
154 | { | ||
155 | LOG (GNUNET_ERROR_TYPE_WARNING, "Tried to schedule watch that is disabled!\n"); | ||
156 | return; | ||
157 | }; | ||
158 | |||
159 | if (w->scheduled) | ||
160 | return; | ||
161 | |||
162 | if (w->net_handle) | ||
163 | { | ||
164 | w->task = GNUNET_SCHEDULER_add_net_with_priority( | ||
165 | GNUNET_TIME_UNIT_FOREVER_REL, | ||
166 | GNUNET_SCHEDULER_PRIORITY_DEFAULT, | ||
167 | w->net_handle, | ||
168 | flags & DBUS_WATCH_READABLE, | ||
169 | flags & DBUS_WATCH_WRITABLE, | ||
170 | handle_watch, | ||
171 | w); | ||
172 | w->scheduled = true; | ||
173 | watch_ref (w); | ||
174 | return; | ||
175 | }; | ||
176 | |||
177 | if (w->file_handle) | ||
178 | { | ||
179 | w->task = GNUNET_SCHEDULER_add_file_with_priority( | ||
180 | GNUNET_TIME_UNIT_FOREVER_REL, | ||
181 | GNUNET_SCHEDULER_PRIORITY_DEFAULT, | ||
182 | w->file_handle, | ||
183 | flags & DBUS_WATCH_READABLE, | ||
184 | flags & DBUS_WATCH_WRITABLE, | ||
185 | handle_watch, | ||
186 | w); | ||
187 | w->scheduled = true; | ||
188 | watch_ref (w); | ||
189 | return; | ||
190 | }; | ||
191 | |||
192 | LOG (GNUNET_ERROR_TYPE_ERROR, "Failed to schedule watch.\n"); | ||
193 | GNUNET_abort_ (); | ||
194 | }; | ||
195 | |||
196 | /* | ||
197 | * Ask the scheduler to stop monitoring a watch either because we are shutting | ||
198 | * down or dbus has asked us to disable this watch. | ||
199 | * | ||
200 | * @param w The watch | ||
201 | * @return GNUNET_OK or GNUNET_SYSERR | ||
202 | */ | ||
203 | void | ||
204 | do_watch_unschedule ( | ||
205 | void *cls, | ||
206 | const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
207 | { | ||
208 | (void)tc; | ||
209 | struct Watch *w = (struct Watch *)cls; | ||
210 | |||
211 | if (! w->scheduled) | ||
212 | return; | ||
213 | |||
214 | void *ret = GNUNET_SCHEDULER_cancel (w->task); | ||
215 | if ((struct Watch *)ret != w) | ||
216 | LOG (GNUNET_ERROR_TYPE_WARNING, "Weird result unscheduling task. w == %p, GNUNET_SCHEDULER_cancel returned %p\n", w, ret); | ||
217 | |||
218 | watch_unref (w); | ||
219 | }; | ||
220 | |||
221 | void | ||
222 | watch_unschedule ( | ||
223 | struct Watch *w) | ||
224 | { | ||
225 | GNUNET_SCHEDULER_add_now (do_watch_unschedule, w); | ||
226 | }; | ||
227 | |||
228 | struct WatchIter * | ||
229 | watch_find (struct WatchIter *wi, DBusWatch *watch) | ||
230 | { | ||
231 | while (wi && wi->w->watch != watch) | ||
232 | wi = wi->next; | ||
233 | return wi; | ||
234 | }; | ||
235 | |||