fix possible crash on user deletion
[srvx.git] / src / ioset-win32.c
1 /* Win32 ioset backend for srvx
2  * Copyright 2006 srvx Development Team
3  *
4  * This file is part of srvx.
5  *
6  * srvx is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with srvx; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
19  */
20
21 #include "ioset-impl.h"
22 #include "common.h"
23 #include "log.h"
24
25 /* This is massively kludgy.  Unfortunately, the only performant I/O
26  * multiplexer with halfway decent semantics under Windows is
27  * WSAAsyncSelect() -- which requires a window that can receive
28  * messages.
29  *
30  * So ioset_win32_init() creates a hidden window and sets it up for
31  * asynchronous socket notifications.
32  */
33
34 #define IDT_TIMER1 1000
35 #define IDT_SOCKET 1001
36
37 static HWND ioset_window;
38 static struct io_fd **fds;
39 static unsigned int fds_used;
40 static unsigned int fds_size;
41
42 static unsigned int
43 io_fd_pos(int fd)
44 {
45     int lower = 0;
46     int upper = fds_used - 1;
47
48     while (lower <= upper)
49     {
50         int mid = (upper + lower) / 2;
51         if (fd < fds[mid]->fd)
52             upper = mid - 1;
53         else if (fd > fds[mid]->fd)
54             lower = mid + 1;
55         else
56             break;
57     }
58     return lower;
59 }
60
61 static struct io_fd *
62 io_fd_from_socket(int fd)
63 {
64     unsigned int ii;
65     ii = io_fd_pos(fd);
66     return ((ii < fds_used) && (fds[ii]->fd == fd)) ? fds[ii] : NULL;
67 }
68
69 static LRESULT CALLBACK
70 ioset_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
71 {
72     struct io_fd *fd;
73     int events;
74     int err;
75
76     if (hWnd == ioset_window) switch (uMsg)
77     {
78     case IDT_TIMER1:
79         return 0;
80     case IDT_SOCKET:
81         fd = io_fd_from_socket(wParam);
82         events = WSAGETSELECTEVENT(lParam);
83         err = WSAGETSELECTERROR(lParam);
84         ioset_events(fd, (events & (FD_READ | FD_ACCEPT | FD_CLOSE)) != 0, (events & (FD_WRITE | FD_CONNECT)) != 0);
85         return 0;
86     case WM_QUIT:
87         quit_services = wParam;
88         return 0;
89     }
90     return DefWindowProc(hWnd, uMsg, wParam, lParam);
91 }
92
93 static int
94 ioset_win32_init(void)
95 {
96     WSADATA wsadata;
97     WNDCLASSEX wcx;
98     HINSTANCE hinst;
99     int res;
100
101     // Start Windows Sockets.
102     res = WSAStartup(MAKEWORD(2, 0), &wsadata);
103     if (res)
104     {
105         log_module(MAIN_LOG, LOG_FATAL, "Unable to start Windows Sockets (%d)", res);
106         return 0;
107     }
108
109     // Get Windows HINSTANCE.
110     hinst = GetModuleHandle(NULL);
111
112     // Describe and register a window class.
113     memset(&wcx, 0, sizeof(wcx));
114     wcx.cbSize = sizeof(wcx);
115     wcx.lpfnWndProc = ioset_win32_wndproc;
116     wcx.hInstance = hinst;
117     wcx.lpszClassName = "srvxMainWindow";
118     if (!RegisterClassEx(&wcx))
119     {
120         log_module(MAIN_LOG, LOG_FATAL, "Unable to register window class (%lu)", GetLastError());
121         return 0;
122     }
123
124     ioset_window = CreateWindow("srvxMainWindow", PACKAGE_STRING, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
125     if (!ioset_window)
126     {
127         log_module(MAIN_LOG, LOG_FATAL, "Unable to create window (%lu)", GetLastError());
128         return 0;
129     }
130
131     return 1;
132 }
133
134 static long
135 ioset_win32_events(const struct io_fd *fd)
136 {
137     switch (fd->state)
138     {
139     case IO_CLOSED:
140         return 0;
141     case IO_LISTENING:
142         return FD_ACCEPT;
143     case IO_CONNECTING:
144         return FD_CONNECT;
145     case IO_CONNECTED:
146         return FD_READ | FD_CLOSE | (fd_wants_writes(fd) ? FD_WRITE : 0);
147     }
148 }
149
150 static void
151 ioset_win32_update(struct io_fd *fd)
152 {
153     int rc;
154     long events;
155
156     events = ioset_win32_events(fd);
157     rc = WSAAsyncSelect(fd->fd, ioset_window, IDT_SOCKET, events);
158     if (rc)
159     {
160         log_module(MAIN_LOG, LOG_ERROR, "Unable to add events %#lx for fd %#x (%d)", events, fd->fd, WSAGetLastError());
161     }
162 }
163
164 static void
165 ioset_win32_add(struct io_fd *fd)
166 {
167     unsigned int pos;
168     unsigned int ii;
169
170     // Make sure fds[] can hold a new entry.
171     if (fds_used + 1 >= fds_size)
172     {
173         struct io_fd **new_fds;
174         unsigned int new_size;
175
176         new_size = (fds_size < 8) ? 8 : fds_size * 2;
177         new_fds = calloc(new_size * 2, sizeof(new_fds[0]));
178         if (!new_fds)
179         {
180             log_module(MAIN_LOG, LOG_FATAL, "Unable to allocate %u-entry socket array.", new_size);
181         }
182         for (ii = 0; ii < fds_used; ++ii)
183             new_fds[ii] = fds[ii];
184         free(fds);
185         fds = new_fds;
186         fds_size = new_size;
187     }
188
189     // Insert fd into the appropriate spot in fds[].
190     pos = io_fd_pos(fd->fd);
191     for (ii = pos; ii < fds_used; ++ii)
192         fds[ii + 1] = fds[ii];
193     fds[pos] = fd;
194     ++fds_used;
195
196     // Ask the OS for future notifications.
197     ioset_win32_update(fd);
198 }
199
200 static void
201 ioset_win32_remove(struct io_fd *fd, int os_closed)
202 {
203     unsigned int pos;
204     int rc;
205
206     // Unregister from the OS.
207     if (!os_closed)
208     {
209         unsigned long ulong;
210
211         rc = WSAAsyncSelect(fd->fd, ioset_window, IDT_SOCKET, 0);
212         if (rc)
213         {
214             log_module(MAIN_LOG, LOG_ERROR, "Unable to remove events for fd %#x (%d)", fd->fd, WSAGetLastError());
215         }
216
217         ulong = 0;
218         ioctlsocket(fd->fd, FIONBIO, &ulong);
219     }
220
221     // Remove from the fds[] array.
222     pos = io_fd_pos(fd->fd);
223     for (--fds_used; pos < fds_used; ++pos)
224         fds[pos] = fds[pos + 1];
225 }
226
227 static void
228 ioset_win32_cleanup(void)
229 {
230     DestroyWindow(ioset_window);
231     ioset_window = NULL;
232     WSACleanup();
233 }
234
235 static int
236 ioset_win32_loop(struct timeval *timeout)
237 {
238     MSG msg;
239     BOOL not_really_bool;
240     int msec;
241
242     // Make sure we are woken up after the appropriate time.
243     msec = (timeout->tv_sec * 1000) + (timeout->tv_usec / 1000);
244     SetTimer(ioset_window, IDT_TIMER1, msec, NULL);
245     // Do a blocking read of the message queue.
246     not_really_bool = GetMessage(&msg, NULL, 0, 0);
247     KillTimer(ioset_window, IDT_TIMER1);
248     if (not_really_bool < 0)
249     {
250         return 1;
251     }
252     else if (not_really_bool == 0)
253     {
254         quit_services = 1;
255         return 0;
256     }
257     else
258     {
259         extern int clock_skew;
260
261         now = time(NULL) + clock_skew;
262         TranslateMessage(&msg);
263         DispatchMessage(&msg);
264     }
265     return 0;
266 }
267
268 struct io_engine io_engine_win32 = {
269     .name = "win32",
270     .init = ioset_win32_init,
271     .add = ioset_win32_add,
272     .remove = ioset_win32_remove,
273     .update = ioset_win32_update,
274     .loop = ioset_win32_loop,
275     .cleanup = ioset_win32_cleanup,
276 };