Coverage Report

Created: 2025-10-14 08:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fluent-bit/lib/monkey/mk_server/mk_server.c
Line
Count
Source
1
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*  Monkey HTTP Server
4
 *  ==================
5
 *  Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
6
 *
7
 *  Licensed under the Apache License, Version 2.0 (the "License");
8
 *  you may not use this file except in compliance with the License.
9
 *  You may obtain a copy of the License at
10
 *
11
 *      http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 *  Unless required by applicable law or agreed to in writing, software
14
 *  distributed under the License is distributed on an "AS IS" BASIS,
15
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 *  See the License for the specific language governing permissions and
17
 *  limitations under the License.
18
 */
19
20
#include <monkey/mk_info.h>
21
#include <monkey/monkey.h>
22
#include <monkey/mk_config.h>
23
#include <monkey/mk_scheduler.h>
24
#include <monkey/mk_plugin.h>
25
#include <monkey/mk_utils.h>
26
#include <monkey/mk_server.h>
27
#include <monkey/mk_server_tls.h>
28
#include <monkey/mk_scheduler.h>
29
#include <monkey/mk_core.h>
30
#include <monkey/mk_fifo.h>
31
#include <monkey/mk_http_thread.h>
32
33
#ifdef _WIN32
34
#include <winsock2.h>
35
#else
36
#include <sys/socket.h>
37
#include <netinet/in.h>
38
#endif
39
40
#ifndef _WIN32
41
#include <sys/time.h>
42
#include <sys/resource.h>
43
#endif
44
45
pthread_key_t mk_server_fifo_key;
46
47
static int mk_server_lib_notify_event_loop_break(struct mk_sched_worker *sched);
48
49
/* Return the number of clients that can be attended  */
50
unsigned int mk_server_capacity(struct mk_server *server)
51
0
{
52
0
    int ret;
53
0
    int cur;
54
55
0
#ifndef _WIN32
56
0
    struct rlimit lim;
57
58
    /* Limit by system */
59
0
    getrlimit(RLIMIT_NOFILE, &lim);
60
0
    cur = lim.rlim_cur;
61
62
0
    if (server->fd_limit > cur) {
63
0
        lim.rlim_cur = server->fd_limit;
64
0
        lim.rlim_max = server->fd_limit;
65
66
0
        ret = setrlimit(RLIMIT_NOFILE, &lim);
67
0
        if (ret == -1) {
68
0
            mk_warn("Could not increase FDLimit to %i.", server->fd_limit);
69
0
        }
70
0
        else {
71
0
            cur = server->fd_limit;
72
0
        }
73
0
    }
74
0
    else if (server->fd_limit > 0) {
75
0
        cur = server->fd_limit;
76
0
    }
77
78
#else
79
    ret = 0;
80
    cur = INT_MAX; 
81
82
    /* This is not the right way to plug this, according to raymond chen the only limit
83
     * to fd count is free memory in their winsock provider and there are no other limits
84
     * that I know of but I should still look for a more elegant solution. (even if it
85
     * was just ignoring the server_capacity limit in scheduler.c: _next_target)
86
    */
87
#endif
88
89
0
    return cur;
90
0
}
91
92
static inline
93
struct mk_sched_conn *mk_server_listen_handler(struct mk_sched_worker *sched,
94
                                               void *data,
95
                                               struct mk_server *server)
96
0
{
97
0
    int ret;
98
0
    int client_fd = -1;
99
0
    struct mk_sched_conn *conn;
100
0
    struct mk_server_listen *listener = data;
101
102
0
    client_fd = mk_socket_accept(listener->server_fd);
103
0
    if (mk_unlikely(client_fd == -1)) {
104
0
        MK_TRACE("[server] Accept connection failed: %s", strerror(errno));
105
0
        goto error;
106
0
    }
107
108
0
    conn = mk_sched_add_connection(client_fd, listener, sched, server);
109
0
    if (mk_unlikely(!conn)) {
110
0
        goto error;
111
0
    }
112
113
0
    ret = mk_event_add(sched->loop, client_fd,
114
0
                       MK_EVENT_CONNECTION, MK_EVENT_READ, conn);
115
0
    if (mk_unlikely(ret != 0)) {
116
0
        mk_err("[server] Error registering file descriptor: %s",
117
0
               strerror(errno));
118
0
        goto error;
119
0
    }
120
121
0
    sched->accepted_connections++;
122
0
    MK_TRACE("[server] New connection arrived: FD %i", client_fd);
123
0
    return conn;
124
125
0
error:
126
0
    if (client_fd != -1) {
127
0
        listener->network->network->close(listener->network, client_fd);
128
0
    }
129
130
0
    return NULL;
131
0
}
132
133
void mk_server_listen_free()
134
0
{
135
0
    struct mk_list *list;
136
0
    struct mk_list *tmp;
137
0
    struct mk_list *head;
138
0
    struct mk_server_listen *listener;
139
140
0
    list = MK_TLS_GET(mk_tls_server_listen);
141
0
    mk_list_foreach_safe(head, tmp, list) {
142
0
        listener = mk_list_entry(head, struct mk_server_listen, _head);
143
0
        mk_list_del(&listener->_head);
144
0
        mk_mem_free(listener);
145
0
    }
146
0
}
147
148
void mk_server_listen_exit(struct mk_list *list)
149
0
{
150
0
    struct mk_list *tmp;
151
0
    struct mk_list *head;
152
0
    struct mk_server_listen *listen;
153
154
0
    if (!list) {
155
0
        return;
156
0
    }
157
158
0
    mk_list_foreach_safe(head, tmp, list) {
159
0
        listen = mk_list_entry(head, struct mk_server_listen, _head);
160
0
        mk_event_closesocket(listen->server_fd);
161
0
        mk_list_del(&listen->_head);
162
0
        mk_mem_free(listen);
163
0
    }
164
165
0
    mk_mem_free(list);
166
0
}
167
168
struct mk_list *mk_server_listen_init(struct mk_server *server)
169
0
{
170
0
    int server_fd;
171
0
    int reuse_port = MK_FALSE;
172
0
    struct mk_list *head;
173
0
    struct mk_list *listeners;
174
0
    struct mk_event *event;
175
0
    struct mk_server_listen *listener;
176
0
    struct mk_sched_handler *protocol;
177
0
    struct mk_plugin *plugin;
178
0
    struct mk_config_listener *listen;
179
180
0
    if (!server) {
181
0
        goto error;
182
0
    }
183
184
0
    listeners = mk_mem_alloc(sizeof(struct mk_list));
185
0
    mk_list_init(listeners);
186
187
0
    if (server->scheduler_mode == MK_SCHEDULER_REUSEPORT) {
188
0
        reuse_port = MK_TRUE;
189
0
    }
190
191
0
    mk_list_foreach(head, &server->listeners) {
192
0
        listen = mk_list_entry(head, struct mk_config_listener, _head);
193
194
0
        server_fd = mk_socket_server(listen->port,
195
0
                                     listen->address,
196
0
                                     reuse_port,
197
0
                                     server);
198
0
        if (server_fd >= 0) {
199
0
            if (mk_socket_set_tcp_defer_accept(server_fd) != 0) {
200
0
#if defined (__linux__)
201
0
                mk_warn("[server] Could not set TCP_DEFER_ACCEPT");
202
0
#endif
203
0
            }
204
205
0
            listener = mk_mem_alloc_z(sizeof(struct mk_server_listen));
206
207
            /* configure the internal event_state */
208
0
            event = &listener->event;
209
0
            event->fd   = server_fd;
210
0
            event->type = MK_EVENT_LISTENER;
211
0
            event->mask = MK_EVENT_EMPTY;
212
0
            event->status = MK_EVENT_NONE;
213
214
            /* continue with listener setup and linking */
215
0
            listener->server_fd = server_fd;
216
0
            listener->listen    = listen;
217
218
0
            if (listen->flags & MK_CAP_HTTP) {
219
0
                protocol = mk_sched_handler_cap(MK_CAP_HTTP);
220
0
                if (!protocol) {
221
0
                    mk_err("HTTP protocol not supported");
222
0
                    exit(EXIT_FAILURE);
223
0
                }
224
0
                listener->protocol = protocol;
225
0
            }
226
227
#ifdef MK_HAVE_HTTP2
228
            if (listen->flags & MK_CAP_HTTP2) {
229
                protocol = mk_sched_handler_cap(MK_CAP_HTTP2);
230
                if (!protocol) {
231
                    mk_err("HTTP2 protocol not supported");
232
                    exit(EXIT_FAILURE);
233
                }
234
                listener->protocol = protocol;
235
            }
236
#endif
237
0
            listener->network = mk_plugin_cap(MK_CAP_SOCK_PLAIN, server);
238
239
0
            if (listen->flags & MK_CAP_SOCK_TLS) {
240
0
                plugin = mk_plugin_cap(MK_CAP_SOCK_TLS, server);
241
0
                if (!plugin) {
242
0
                    mk_err("SSL/TLS not supported");
243
0
                    exit(EXIT_FAILURE);
244
0
                }
245
0
                listener->network = plugin;
246
0
            }
247
248
0
            mk_list_add(&listener->_head, listeners);
249
0
        }
250
0
        else {
251
0
            mk_err("[server] Failed to bind server socket to %s:%s.",
252
0
                   listen->address,
253
0
                   listen->port);
254
0
            return NULL;
255
0
        }
256
0
    }
257
258
0
    if (reuse_port == MK_TRUE) {
259
0
        MK_TLS_SET(mk_tls_server_listen, listeners);
260
0
    }
261
262
0
    return listeners;
263
264
0
error:
265
0
    return NULL;
266
0
}
267
268
/* Here we launch the worker threads to attend clients */
269
void mk_server_launch_workers(struct mk_server *server)
270
0
{
271
0
    int i;
272
0
    pthread_t skip;
273
274
    /* Launch workers */
275
0
    for (i = 0; i < server->workers; i++) {
276
        /* Spawn the thread */
277
0
        mk_sched_launch_thread(server, &skip);
278
0
    }
279
0
}
280
281
/*
282
 * When using the FIFO interface, this function get's the FIFO worker
283
 * context and register the pipe file descriptor into the main event
284
 * loop.
285
 *
286
 * note: this function is invoked by each worker thread.
287
 */
288
static int mk_server_fifo_worker_setup(struct mk_event_loop *evl)
289
0
{
290
0
    int ret;
291
0
    struct mk_fifo_worker *fw;
292
293
0
    fw = pthread_getspecific(mk_server_fifo_key);
294
0
    if (!fw) {
295
0
        return -1;
296
0
    }
297
298
0
    ret = mk_event_add(evl, fw->channel[0],
299
0
                       MK_EVENT_FIFO, MK_EVENT_READ,
300
0
                       fw);
301
0
    if (ret != 0) {
302
0
        mk_err("[server] Error registering fifo worker channel: %s",
303
0
               strerror(errno));
304
0
        return -1;
305
0
    }
306
307
0
    return 0;
308
0
}
309
310
/*
311
 * The loop_balancer() runs in the main process context and is considered
312
 * the old-fashion way to handle connections. It have an event queue waiting
313
 * for connections, once one arrives, it decides which worker (thread) may
314
 * handle it registering the accept(2)ed file descriptor on the worker
315
 * event monitored queue.
316
 */
317
void mk_server_loop_balancer(struct mk_server *server)
318
0
{
319
0
    size_t bytes;
320
0
    uint64_t val;
321
0
    int operation_flag;
322
0
    struct mk_list *head;
323
0
    struct mk_list *listeners;
324
0
    struct mk_server_listen *listener;
325
0
    struct mk_event *event;
326
0
    struct mk_event_loop *evl;
327
0
    struct mk_sched_worker *sched;
328
0
    struct mk_event management_event;
329
330
    /* Init the listeners */
331
0
    listeners = mk_server_listen_init(server);
332
0
    if (!listeners) {
333
0
        mk_err("Failed to initialize listen sockets.");
334
0
        return;
335
0
    }
336
337
    /* Create an event loop context */
338
0
    evl = mk_event_loop_create(MK_EVENT_QUEUE_SIZE);
339
0
    if (!evl) {
340
0
        mk_err("Could not initialize event loop");
341
0
        exit(EXIT_FAILURE);
342
0
    }
343
344
    /* Register the listeners */
345
0
    mk_list_foreach(head, listeners) {
346
0
        listener = mk_list_entry(head, struct mk_server_listen, _head);
347
0
        mk_event_add(evl, listener->server_fd,
348
0
                     MK_EVENT_LISTENER, MK_EVENT_READ,
349
0
                     listener);
350
0
    }
351
352
0
    memset(&management_event, 0, sizeof(struct mk_event));
353
354
0
    mk_event_add(evl,
355
0
                 server->lib_ch_manager[0],
356
0
                 MK_EVENT_NOTIFICATION,
357
0
                 MK_EVENT_READ,
358
0
                 &management_event);
359
360
0
    operation_flag = MK_TRUE;
361
0
    while (operation_flag) {
362
0
        mk_event_wait(evl);
363
0
        mk_event_foreach(event, evl) {
364
0
            if (event->mask & MK_EVENT_READ) {
365
                /* This signal is sent by mk_stop and both this and
366
                 * mk_lib_worker are expecting it.
367
                 */
368
0
                if (server->lib_ch_manager[0] == event->fd) {
369
#ifdef _WIN32
370
        bytes = recv(event->fd, &val, sizeof(uint64_t), MSG_WAITALL);
371
#else
372
0
        bytes = read(event->fd, &val, sizeof(uint64_t));
373
0
#endif
374
375
0
                    if (bytes <= 0) {
376
0
                        return;
377
0
                    }
378
379
0
                    if (val == MK_SERVER_SIGNAL_STOP) {
380
0
                        operation_flag = MK_FALSE;
381
382
0
                        break;
383
0
                    }
384
385
0
                    continue;
386
0
                }
387
388
                /*
389
                 * Accept connection: determinate which thread may work on this
390
                 * new connection.
391
                 */
392
0
                sched = mk_sched_next_target(server);
393
0
                if (sched != NULL) {
394
0
                    mk_server_listen_handler(sched, event, server);
395
396
0
                    mk_server_lib_notify_event_loop_break(sched);
397
398
#ifdef MK_HAVE_TRACE
399
                    int i;
400
                    struct mk_sched_ctx *ctx = server->sched_ctx;
401
402
                    for (i = 0; i < server->workers; i++) {
403
                        MK_TRACE("Worker Status");
404
                        MK_TRACE(" WID %i / conx = %llu",
405
                                 ctx->workers[i].idx,
406
                                 ctx->workers[i].accepted_connections -
407
                                 ctx->workers[i].closed_connections);
408
                    }
409
#endif
410
0
                }
411
0
                else {
412
0
                    mk_warn("[server] Over capacity.");
413
0
                }
414
0
            }
415
0
            else if (event->mask & MK_EVENT_CLOSE) {
416
0
                mk_err("[server] Error on socket %d: %s",
417
0
                       event->fd, strerror(errno));
418
0
            }
419
0
        }
420
0
    }
421
0
    mk_event_loop_destroy(evl);
422
0
    mk_server_listen_exit(listeners);
423
0
}
424
425
/*
426
 * This function is called when the scheduler is running in the REUSEPORT
427
 * mode. That means that each worker is listening on shared TCP ports.
428
 *
429
 * When using shared TCP ports the Kernel decides to which worker the
430
 * connection will be assigned.
431
 */
432
void mk_server_worker_loop(struct mk_server *server)
433
0
{
434
0
    int ret = -1;
435
0
    int timeout_fd;
436
0
    uint64_t val;
437
0
    struct mk_event *event;
438
0
    struct mk_event_loop *evl;
439
0
    struct mk_list *list;
440
0
    struct mk_list *head;
441
0
    struct mk_sched_conn *conn;
442
0
    struct mk_sched_worker *sched;
443
0
    struct mk_server_listen *listener;
444
0
    struct mk_server_timeout *server_timeout;
445
446
    /* Get thread conf */
447
0
    sched = mk_sched_get_thread_conf();
448
0
    evl = sched->loop;
449
450
    /*
451
     * The worker will NOT process any connection until the master
452
     * process through mk_server_loop() send us the green light
453
     * signal MK_SERVER_SIGNAL_START.
454
     */
455
0
    mk_event_wait(evl);
456
0
    mk_event_foreach(event, evl) {
457
0
        if ((event->mask & MK_EVENT_READ) &&
458
0
            event->type == MK_EVENT_NOTIFICATION) {
459
0
            if (event->fd == sched->signal_channel_r) {
460
        /* When using libevent _mk_event_channel_create creates a unix socket
461
         * instead of a pipe and windows doesn't us calling read / write on a
462
         * socket instead of recv / send
463
         */
464
#ifdef _WIN32
465
                ret = recv(event->fd, &val, sizeof(val), MSG_WAITALL);
466
#else
467
0
                ret = read(event->fd, &val, sizeof(val));
468
0
#endif
469
0
                if (ret < 0) {
470
0
                    mk_libc_error("read");
471
0
                    continue;
472
0
                }
473
0
                if (val == MK_SERVER_SIGNAL_START) {
474
0
                    MK_TRACE("Worker %i started (SIGNAL_START)", sched->idx);
475
0
                    break;
476
0
                }
477
0
            }
478
0
        }
479
0
    }
480
481
0
    if (server->scheduler_mode == MK_SCHEDULER_REUSEPORT) {
482
        /* Register listeners */
483
0
        list = MK_TLS_GET(mk_tls_server_listen);
484
0
        mk_list_foreach(head, list) {
485
0
            listener = mk_list_entry(head, struct mk_server_listen, _head);
486
0
            mk_event_add(sched->loop, listener->server_fd,
487
0
                         MK_EVENT_LISTENER, MK_EVENT_READ,
488
0
                         listener);
489
0
        }
490
0
    }
491
492
    /*
493
     * If running in library mode, register the FIFO pipe file descriptors
494
     * into the main event loop.
495
     */
496
0
    if (server->lib_mode == MK_TRUE) {
497
0
        mk_server_fifo_worker_setup(evl);
498
0
    }
499
500
    /* create a new timeout file descriptor */
501
0
    server_timeout = mk_mem_alloc_z(sizeof(struct mk_server_timeout));
502
0
    MK_TLS_SET(mk_tls_server_timeout, server_timeout);
503
0
    timeout_fd = mk_event_timeout_create(evl, server->timeout, 0, server_timeout);
504
505
0
    while (1) {
506
0
        mk_event_wait(evl);
507
0
        mk_event_foreach(event, evl) {
508
0
            ret = 0;
509
0
            if (event->type & MK_EVENT_IDLE) {
510
0
                continue;
511
0
            }
512
513
0
            if (event->type == MK_EVENT_CONNECTION) {
514
0
                conn = (struct mk_sched_conn *) event;
515
516
0
                if (event->mask & MK_EVENT_WRITE) {
517
0
                    MK_TRACE("[FD %i] Event WRITE", event->fd);
518
0
                    ret = mk_sched_event_write(conn, sched, server);
519
0
                }
520
521
0
                if (event->mask & MK_EVENT_READ) {
522
0
                    MK_TRACE("[FD %i] Event READ", event->fd);
523
0
                    ret = mk_sched_event_read(conn, sched, server);
524
0
                }
525
526
527
0
                if (event->mask & MK_EVENT_CLOSE && ret != -1) {
528
0
                    MK_TRACE("[FD %i] Event CLOSE", event->fd);
529
0
                    ret = -1;
530
0
                }
531
532
0
                if (ret < 0 && conn->status != MK_SCHED_CONN_CLOSED) {
533
0
                    MK_TRACE("[FD %i] Event FORCE CLOSE | ret = %i",
534
0
                             event->fd, ret);
535
0
                    mk_sched_event_close(conn, sched, MK_EP_SOCKET_CLOSED,
536
0
                                         server);
537
0
                }
538
0
            }
539
0
            else if (event->type == MK_EVENT_LISTENER) {
540
                /*
541
                 * A new connection have been accepted..or failed, despite
542
                 * the result, we let the loop continue processing the other
543
                 * events triggered.
544
                 */
545
0
                conn = mk_server_listen_handler(sched, event, server);
546
0
                if (conn) {
547
                    //conn->event.mask = MK_EVENT_READ
548
                    //goto speed;
549
0
                }
550
0
                continue;
551
0
            }
552
0
            else if (event->type == MK_EVENT_CUSTOM) {
553
                /*
554
                 * We got an event associated to a custom interface, that
555
                 * means a plugin registered some file descriptor on this
556
                 * event loop and an event was triggered. We pass the control
557
                 * to the defined event handler.
558
                 */
559
0
                event->handler(event);
560
0
            }
561
0
            else if (event->type == MK_EVENT_NOTIFICATION) {
562
#ifdef _WIN32
563
                ret = recv(event->fd, &val, sizeof(val), MSG_WAITALL);
564
#else
565
0
                ret = read(event->fd, &val, sizeof(val));
566
0
#endif
567
0
                if (ret < 0) {
568
0
                    mk_libc_error("read");
569
0
                    continue;
570
0
                }
571
572
0
                if (event->fd == sched->signal_channel_r) {
573
0
                    if (val == MK_SCHED_SIGNAL_DEADBEEF) {
574
                        //FIXME:mk_sched_sync_counters();
575
0
                        continue;
576
0
                    }
577
0
                    else if (val == MK_SCHED_SIGNAL_FREE_ALL) {
578
0
                        if (timeout_fd > 0) {
579
0
                            mk_event_timeout_destroy(evl, server_timeout);
580
0
                        }
581
0
                        mk_mem_free(MK_TLS_GET(mk_tls_server_timeout));
582
0
                        mk_server_listen_exit(sched->listeners);
583
0
                        mk_event_loop_destroy(evl);
584
0
                        mk_sched_worker_free(server);
585
0
                        return;
586
0
                    }
587
0
                    else if (val == MK_SCHED_SIGNAL_EVENT_LOOP_BREAK) {
588
                        /* NOTE: This is just a notification that's sent to break out
589
                         *       of the libevent loop in windows after accepting a new
590
                         *       client
591
                        */
592
0
                        MK_TRACE("New client accepted, awesome!");
593
0
                    }
594
0
                }
595
0
                else if (event->fd == timeout_fd) {
596
0
                    mk_sched_check_timeouts(sched, server);
597
0
                }
598
0
                continue;
599
0
            }
600
0
            else if (event->type == MK_EVENT_THREAD) {
601
0
                mk_http_thread_event(event);
602
0
                continue;
603
0
            }
604
0
            else if (event->type == MK_EVENT_FIFO) {
605
0
                mk_fifo_worker_read(event);
606
0
                continue;
607
0
            }
608
0
        }
609
0
        mk_sched_threads_purge(sched);
610
0
        mk_sched_event_free_all(sched);
611
0
    }
612
0
}
613
614
static int mk_server_lib_notify_event_loop_break(struct mk_sched_worker *sched)
615
0
{
616
0
    uint64_t val;
617
618
    /* Check the channel is valid (enabled by library mode) */
619
0
    if (sched->signal_channel_w <= 0) {
620
0
        return -1;
621
0
    }
622
623
0
    val = MK_SCHED_SIGNAL_EVENT_LOOP_BREAK;
624
625
#ifdef _WIN32
626
    return send(sched->signal_channel_w, &val, sizeof(uint64_t), 0);
627
#else
628
0
    return write(sched->signal_channel_w, &val, sizeof(uint64_t));
629
0
#endif
630
0
}
631
632
static int mk_server_lib_notify_started(struct mk_server *server)
633
0
{
634
0
    uint64_t val;
635
636
    /* Check the channel is valid (enabled by library mode) */
637
0
    if (server->lib_ch_start[1] <= 0) {
638
0
        return -1;
639
0
    }
640
641
0
    val = MK_SERVER_SIGNAL_START;
642
643
#ifdef _WIN32
644
    return send(server->lib_ch_start[1], &val, sizeof(uint64_t), 0);
645
#else
646
0
    return write(server->lib_ch_start[1], &val, sizeof(uint64_t));
647
0
#endif
648
0
}
649
650
void mk_server_loop(struct mk_server *server)
651
0
{
652
0
    uint64_t val;
653
654
    /* Rename worker */
655
0
    mk_utils_worker_rename("monkey: server");
656
657
0
    if (server->lib_mode == MK_FALSE) {
658
0
        mk_info("HTTP Server started");
659
0
    }
660
661
    /* Wake up workers */
662
0
    val = MK_SERVER_SIGNAL_START;
663
0
    mk_sched_broadcast_signal(server, val);
664
665
    /* Signal lib caller (if any) */
666
0
    mk_server_lib_notify_started(server);
667
668
    /*
669
     * When using REUSEPORT mode on the Scheduler, we need to signal
670
     * them so they can start processing connections.
671
     */
672
0
    if (server->scheduler_mode == MK_SCHEDULER_REUSEPORT) {
673
        /* do thing :) */
674
0
    }
675
0
    else {
676
        /* FIXME!: this old mode needs some checks on library mode */
677
0
        mk_server_loop_balancer(server);
678
0
    }
679
0
}