fix possible crash on user deletion
[srvx.git] / src / ioset-win32.c
index 018206a45143a2938de9fb9464b55955218496dc..15b3cbdc0ae54d998fef4693f02051c2e1e6a15b 100644 (file)
  * asynchronous socket notifications.
  */
 
+#define IDT_TIMER1 1000
+#define IDT_SOCKET 1001
+
+static HWND ioset_window;
+static struct io_fd **fds;
+static unsigned int fds_used;
+static unsigned int fds_size;
+
+static unsigned int
+io_fd_pos(int fd)
+{
+    int lower = 0;
+    int upper = fds_used - 1;
+
+    while (lower <= upper)
+    {
+        int mid = (upper + lower) / 2;
+        if (fd < fds[mid]->fd)
+            upper = mid - 1;
+        else if (fd > fds[mid]->fd)
+            lower = mid + 1;
+        else
+            break;
+    }
+    return lower;
+}
+
+static struct io_fd *
+io_fd_from_socket(int fd)
+{
+    unsigned int ii;
+    ii = io_fd_pos(fd);
+    return ((ii < fds_used) && (fds[ii]->fd == fd)) ? fds[ii] : NULL;
+}
+
+static LRESULT CALLBACK
+ioset_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+    struct io_fd *fd;
+    int events;
+    int err;
+
+    if (hWnd == ioset_window) switch (uMsg)
+    {
+    case IDT_TIMER1:
+        return 0;
+    case IDT_SOCKET:
+        fd = io_fd_from_socket(wParam);
+        events = WSAGETSELECTEVENT(lParam);
+        err = WSAGETSELECTERROR(lParam);
+        ioset_events(fd, (events & (FD_READ | FD_ACCEPT | FD_CLOSE)) != 0, (events & (FD_WRITE | FD_CONNECT)) != 0);
+        return 0;
+    case WM_QUIT:
+        quit_services = wParam;
+        return 0;
+    }
+    return DefWindowProc(hWnd, uMsg, wParam, lParam);
+}
+
 static int
 ioset_win32_init(void)
 {
     WSADATA wsadata;
+    WNDCLASSEX wcx;
+    HINSTANCE hinst;
     int res;
 
+    // Start Windows Sockets.
     res = WSAStartup(MAKEWORD(2, 0), &wsadata);
-    // TODO: finish implementing ioset_win32_init()
-    return 0;
+    if (res)
+    {
+        log_module(MAIN_LOG, LOG_FATAL, "Unable to start Windows Sockets (%d)", res);
+        return 0;
+    }
+
+    // Get Windows HINSTANCE.
+    hinst = GetModuleHandle(NULL);
+
+    // Describe and register a window class.
+    memset(&wcx, 0, sizeof(wcx));
+    wcx.cbSize = sizeof(wcx);
+    wcx.lpfnWndProc = ioset_win32_wndproc;
+    wcx.hInstance = hinst;
+    wcx.lpszClassName = "srvxMainWindow";
+    if (!RegisterClassEx(&wcx))
+    {
+        log_module(MAIN_LOG, LOG_FATAL, "Unable to register window class (%lu)", GetLastError());
+        return 0;
+    }
+
+    ioset_window = CreateWindow("srvxMainWindow", PACKAGE_STRING, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
+    if (!ioset_window)
+    {
+        log_module(MAIN_LOG, LOG_FATAL, "Unable to create window (%lu)", GetLastError());
+        return 0;
+    }
+
+    return 1;
+}
+
+static long
+ioset_win32_events(const struct io_fd *fd)
+{
+    switch (fd->state)
+    {
+    case IO_CLOSED:
+        return 0;
+    case IO_LISTENING:
+        return FD_ACCEPT;
+    case IO_CONNECTING:
+        return FD_CONNECT;
+    case IO_CONNECTED:
+        return FD_READ | FD_CLOSE | (fd_wants_writes(fd) ? FD_WRITE : 0);
+    }
 }
 
 static void
-ioset_win32_add(struct io_fd *fd)
+ioset_win32_update(struct io_fd *fd)
 {
-    // TODO: implement ioset_win32_add()
+    int rc;
+    long events;
+
+    events = ioset_win32_events(fd);
+    rc = WSAAsyncSelect(fd->fd, ioset_window, IDT_SOCKET, events);
+    if (rc)
+    {
+        log_module(MAIN_LOG, LOG_ERROR, "Unable to add events %#lx for fd %#x (%d)", events, fd->fd, WSAGetLastError());
+    }
 }
 
 static void
-ioset_win32_remove(struct io_fd *fd)
+ioset_win32_add(struct io_fd *fd)
 {
-    // TODO: implement ioset_win32_remove()
+    unsigned int pos;
+    unsigned int ii;
+
+    // Make sure fds[] can hold a new entry.
+    if (fds_used + 1 >= fds_size)
+    {
+        struct io_fd **new_fds;
+        unsigned int new_size;
+
+        new_size = (fds_size < 8) ? 8 : fds_size * 2;
+        new_fds = calloc(new_size * 2, sizeof(new_fds[0]));
+        if (!new_fds)
+        {
+            log_module(MAIN_LOG, LOG_FATAL, "Unable to allocate %u-entry socket array.", new_size);
+        }
+        for (ii = 0; ii < fds_used; ++ii)
+            new_fds[ii] = fds[ii];
+        free(fds);
+        fds = new_fds;
+        fds_size = new_size;
+    }
+
+    // Insert fd into the appropriate spot in fds[].
+    pos = io_fd_pos(fd->fd);
+    for (ii = pos; ii < fds_used; ++ii)
+        fds[ii + 1] = fds[ii];
+    fds[pos] = fd;
+    ++fds_used;
+
+    // Ask the OS for future notifications.
+    ioset_win32_update(fd);
 }
 
 static void
-ioset_win32_update(struct io_fd *fd)
+ioset_win32_remove(struct io_fd *fd, int os_closed)
 {
-    // TODO: implement ioset_win32_update()
+    unsigned int pos;
+    int rc;
+
+    // Unregister from the OS.
+    if (!os_closed)
+    {
+        unsigned long ulong;
+
+        rc = WSAAsyncSelect(fd->fd, ioset_window, IDT_SOCKET, 0);
+        if (rc)
+        {
+            log_module(MAIN_LOG, LOG_ERROR, "Unable to remove events for fd %#x (%d)", fd->fd, WSAGetLastError());
+        }
+
+        ulong = 0;
+        ioctlsocket(fd->fd, FIONBIO, &ulong);
+    }
+
+    // Remove from the fds[] array.
+    pos = io_fd_pos(fd->fd);
+    for (--fds_used; pos < fds_used; ++pos)
+        fds[pos] = fds[pos + 1];
 }
 
 static void
 ioset_win32_cleanup(void)
 {
-    // TODO: finish implementing ioset_win32_cleanup()
+    DestroyWindow(ioset_window);
+    ioset_window = NULL;
     WSACleanup();
 }
 
 static int
 ioset_win32_loop(struct timeval *timeout)
 {
-    // TODO: implement ioset_win32_loop()
+    MSG msg;
+    BOOL not_really_bool;
+    int msec;
+
+    // Make sure we are woken up after the appropriate time.
+    msec = (timeout->tv_sec * 1000) + (timeout->tv_usec / 1000);
+    SetTimer(ioset_window, IDT_TIMER1, msec, NULL);
+    // Do a blocking read of the message queue.
+    not_really_bool = GetMessage(&msg, NULL, 0, 0);
+    KillTimer(ioset_window, IDT_TIMER1);
+    if (not_really_bool < 0)
+    {
+        return 1;
+    }
+    else if (not_really_bool == 0)
+    {
+        quit_services = 1;
+        return 0;
+    }
+    else
+    {
+        extern int clock_skew;
+
+        now = time(NULL) + clock_skew;
+        TranslateMessage(&msg);
+        DispatchMessage(&msg);
+    }
     return 0;
 }