1 /*
2  * fprint D-Bus daemon
3  * Copyright (C) 2008 Daniel Drake <dsd@gentoo.org>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 
20 #include "config.h"
21 
22 #include <glib.h>
23 #include <libfprint/fprint.h>
24 
25 #include <poll.h>
26 #include <stdlib.h>
27 
28 #include "loop.h"
29 
30 struct fdsource {
31 	GSource source;
32 	GSList *pollfds;
33 };
34 
source_prepare(GSource * source,gint * timeout)35 static gboolean source_prepare(GSource *source, gint *timeout)
36 {
37 	int r;
38 	struct timeval tv;
39 
40 	r = fp_get_next_timeout(&tv);
41 	if (r == 0) {
42 		*timeout = -1;
43 		return FALSE;
44 	}
45 
46 	if (!timerisset(&tv))
47 		return TRUE;
48 
49 	*timeout = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
50 	return FALSE;
51 }
52 
source_check(GSource * source)53 static gboolean source_check(GSource *source)
54 {
55 	struct fdsource *_fdsource = (struct fdsource *) source;
56 	GSList *l;
57 	struct timeval tv;
58 	int r;
59 
60 	if (!_fdsource->pollfds)
61 		return FALSE;
62 
63 	for (l = _fdsource->pollfds; l != NULL; l = l->next) {
64 		GPollFD *pollfd = l->data;
65 
66 		if (pollfd->revents)
67 			return TRUE;
68 	}
69 
70 	r = fp_get_next_timeout(&tv);
71 	if (r == 1 && !timerisset(&tv))
72 		return TRUE;
73 
74 	return FALSE;
75 }
76 
source_dispatch(GSource * source,GSourceFunc callback,gpointer data)77 static gboolean source_dispatch(GSource *source, GSourceFunc callback,
78 	gpointer data)
79 {
80 	struct timeval zerotimeout = {
81 		.tv_sec = 0,
82 		.tv_usec = 0,
83 	};
84 
85 	/* FIXME error handling */
86 	fp_handle_events_timeout(&zerotimeout);
87 
88 	/* FIXME whats the return value used for? */
89 	return TRUE;
90 }
91 
source_finalize(GSource * source)92 static void source_finalize(GSource *source)
93 {
94 	struct fdsource *_fdsource = (struct fdsource *) source;
95 	GSList *l;
96 
97 	if (!_fdsource->pollfds)
98 		return;
99 
100 	for (l = _fdsource->pollfds; l != NULL; l = l->next) {
101 		GPollFD *pollfd = l->data;
102 
103 		g_source_remove_poll((GSource *) _fdsource, pollfd);
104 		g_slice_free(GPollFD, pollfd);
105 		_fdsource->pollfds = g_slist_delete_link(_fdsource->pollfds, l);
106 	}
107 
108 	g_slist_free(_fdsource->pollfds);
109 }
110 
111 static GSourceFuncs sourcefuncs = {
112 	.prepare = source_prepare,
113 	.check = source_check,
114 	.dispatch = source_dispatch,
115 	.finalize = source_finalize,
116 };
117 
118 static struct fdsource *fdsource = NULL;
119 
pollfd_add(int fd,short events)120 static void pollfd_add(int fd, short events)
121 {
122 	GPollFD *pollfd;
123 
124 	pollfd = g_slice_new(GPollFD);
125 	pollfd->fd = fd;
126 	pollfd->events = 0;
127 	pollfd->revents = 0;
128 	if (events & POLLIN)
129 		pollfd->events |= G_IO_IN;
130 	if (events & POLLOUT)
131 		pollfd->events |= G_IO_OUT;
132 
133 	fdsource->pollfds = g_slist_prepend(fdsource->pollfds, pollfd);
134 	g_source_add_poll((GSource *) fdsource, pollfd);
135 }
136 
pollfd_added_cb(int fd,short events)137 static void pollfd_added_cb(int fd, short events)
138 {
139 	g_debug("now monitoring fd %d", fd);
140 	pollfd_add(fd, events);
141 }
142 
pollfd_removed_cb(int fd)143 static void pollfd_removed_cb(int fd)
144 {
145 	GSList *l;
146 
147 	g_debug("no longer monitoring fd %d", fd);
148 
149 	if (!fdsource->pollfds) {
150 		g_debug("cannot remove from list as list is empty?");
151 		return;
152 	}
153 
154 	for (l = fdsource->pollfds; l != NULL; l = l->next) {
155 		GPollFD *pollfd = l->data;
156 
157 		if (pollfd->fd != fd)
158 			continue;
159 
160 		g_source_remove_poll((GSource *) fdsource, pollfd);
161 		g_slice_free(GPollFD, pollfd);
162 		fdsource->pollfds = g_slist_delete_link(fdsource->pollfds, l);
163 		return;
164 	}
165 
166 	g_error("couldn't find fd %d in list\n", fd);
167 }
168 
setup_pollfds(void)169 int setup_pollfds(void)
170 {
171 	size_t numfds;
172 	size_t i;
173 	struct fp_pollfd *fpfds;
174 	GSource *gsource;
175 
176 	gsource = g_source_new(&sourcefuncs, sizeof(struct fdsource));
177 	fdsource = (struct fdsource *) gsource;
178 	fdsource->pollfds = NULL;
179 
180 	numfds = fp_get_pollfds(&fpfds);
181 	if (numfds < 0) {
182 		if (fpfds)
183 			free(fpfds);
184 		return (int) numfds;
185 	} else if (numfds > 0) {
186 		for (i = 0; i < numfds; i++) {
187 			struct fp_pollfd *fpfd = &fpfds[i];
188 			pollfd_add(fpfd->fd, fpfd->events);
189 		}
190 	}
191 
192 	free(fpfds);
193 	fp_set_pollfd_notifiers(pollfd_added_cb, pollfd_removed_cb);
194 	g_source_attach(gsource, NULL);
195 	return 0;
196 }
197