xref: /unit/src/nxt_process.c (revision 1488:6976d36be926)
1 
2 /*
3  * Copyright (C) Igor Sysoev
4  * Copyright (C) NGINX, Inc.
5  */
6 
7 #include <nxt_main.h>
8 #include <nxt_main_process.h>
9 
10 #if (NXT_HAVE_CLONE)
11 #include <nxt_clone.h>
12 #endif
13 
14 #include <signal.h>
15 
16 static nxt_int_t nxt_process_setup(nxt_task_t *task, nxt_process_t *process);
17 static nxt_int_t nxt_process_child_fixup(nxt_task_t *task,
18     nxt_process_t *process);
19 static nxt_int_t nxt_process_send_created(nxt_task_t *task,
20     nxt_process_t *process);
21 static nxt_int_t nxt_process_send_ready(nxt_task_t *task,
22     nxt_process_t *process);
23 static void nxt_process_created_ok(nxt_task_t *task, nxt_port_recv_msg_t *msg,
24     void *data);
25 static void nxt_process_created_error(nxt_task_t *task,
26     nxt_port_recv_msg_t *msg, void *data);
27 
28 
29 /* A cached process pid. */
30 nxt_pid_t  nxt_pid;
31 
32 /* An original parent process pid. */
33 nxt_pid_t  nxt_ppid;
34 
35 /* A cached process effective uid */
36 nxt_uid_t  nxt_euid;
37 
38 /* A cached process effective gid */
39 nxt_gid_t  nxt_egid;
40 
41 nxt_bool_t  nxt_proc_conn_matrix[NXT_PROCESS_MAX][NXT_PROCESS_MAX] = {
42     { 1, 1, 1, 1, 1 },
43     { 1, 0, 0, 0, 0 },
44     { 1, 0, 0, 1, 0 },
45     { 1, 0, 1, 0, 1 },
46     { 1, 0, 0, 0, 0 },
47 };
48 
49 nxt_bool_t  nxt_proc_remove_notify_matrix[NXT_PROCESS_MAX][NXT_PROCESS_MAX] = {
50     { 0, 0, 0, 0, 0 },
51     { 0, 0, 0, 0, 0 },
52     { 0, 0, 0, 1, 0 },
53     { 0, 0, 1, 0, 1 },
54     { 0, 0, 0, 1, 0 },
55 };
56 
57 
58 static nxt_int_t
59 nxt_process_child_fixup(nxt_task_t *task, nxt_process_t *process)
60 {
61     nxt_process_t       *p;
62     nxt_runtime_t       *rt;
63     nxt_process_init_t  *init;
64     nxt_process_type_t  ptype;
65 
66     init = nxt_process_init(process);
67 
68     nxt_pid = nxt_getpid();
69 
70     process->pid = nxt_pid;
71 
72     /* Clean inherited cached thread tid. */
73     task->thread->tid = 0;
74 
75 #if (NXT_HAVE_CLONE && NXT_HAVE_CLONE_NEWPID)
76     if (nxt_is_clone_flag_set(process->isolation.clone.flags, NEWPID)) {
77         ssize_t  pidsz;
78         char     procpid[10];
79 
80         nxt_debug(task, "%s isolated pid is %d", process->name, nxt_pid);
81 
82         pidsz = readlink("/proc/self", procpid, sizeof(procpid));
83 
84         if (nxt_slow_path(pidsz < 0 || pidsz >= (ssize_t) sizeof(procpid))) {
85             nxt_alert(task, "failed to read real pid from /proc/self");
86             return NXT_ERROR;
87         }
88 
89         procpid[pidsz] = '\0';
90 
91         nxt_pid = (nxt_pid_t) strtol(procpid, NULL, 10);
92 
93         nxt_assert(nxt_pid >  0 && nxt_errno != ERANGE);
94 
95         process->pid = nxt_pid;
96         task->thread->tid = nxt_pid;
97 
98         nxt_debug(task, "%s real pid is %d", process->name, nxt_pid);
99     }
100 
101 #endif
102 
103     ptype = init->type;
104 
105     nxt_port_reset_next_id();
106 
107     nxt_event_engine_thread_adopt(task->thread->engine);
108 
109     rt = task->thread->runtime;
110 
111     /* Remove not ready processes. */
112     nxt_runtime_process_each(rt, p) {
113 
114         if (nxt_proc_conn_matrix[ptype][nxt_process_type(p)] == 0) {
115             nxt_debug(task, "remove not required process %PI", p->pid);
116 
117             nxt_process_close_ports(task, p);
118 
119             continue;
120         }
121 
122         if (p->state != NXT_PROCESS_STATE_READY) {
123             nxt_debug(task, "remove not ready process %PI", p->pid);
124 
125             nxt_process_close_ports(task, p);
126 
127             continue;
128         }
129 
130         nxt_port_mmaps_destroy(&p->incoming, 0);
131         nxt_port_mmaps_destroy(&p->outgoing, 0);
132 
133     } nxt_runtime_process_loop;
134 
135     return NXT_OK;
136 }
137 
138 
139 nxt_pid_t
140 nxt_process_create(nxt_task_t *task, nxt_process_t *process)
141 {
142     nxt_int_t           ret;
143     nxt_pid_t           pid;
144 
145 #if (NXT_HAVE_CLONE)
146     pid = nxt_clone(SIGCHLD | process->isolation.clone.flags);
147     if (nxt_slow_path(pid < 0)) {
148         nxt_alert(task, "clone() failed for %s %E", process->name, nxt_errno);
149         return pid;
150     }
151 #else
152     pid = fork();
153     if (nxt_slow_path(pid < 0)) {
154         nxt_alert(task, "fork() failed for %s %E", process->name, nxt_errno);
155         return pid;
156     }
157 #endif
158 
159     if (pid == 0) {
160         /* Child. */
161 
162         ret = nxt_process_child_fixup(task, process);
163         if (nxt_slow_path(ret != NXT_OK)) {
164             nxt_process_quit(task, 1);
165             return -1;
166         }
167 
168         nxt_runtime_process_add(task, process);
169 
170         if (nxt_slow_path(nxt_process_setup(task, process) != NXT_OK)) {
171             nxt_process_quit(task, 1);
172         }
173 
174         /*
175          * Explicitly return 0 to notice the caller function this is the child.
176          * The caller must return to the event engine work queue loop.
177          */
178         return 0;
179     }
180 
181     /* Parent. */
182 
183 #if (NXT_HAVE_CLONE)
184     nxt_debug(task, "clone(%s): %PI", process->name, pid);
185 #else
186     nxt_debug(task, "fork(%s): %PI", process->name, pid);
187 #endif
188 
189     process->pid = pid;
190 
191     nxt_runtime_process_add(task, process);
192 
193     return pid;
194 }
195 
196 
197 static nxt_int_t
198 nxt_process_setup(nxt_task_t *task, nxt_process_t *process)
199 {
200     nxt_int_t                    ret;
201     nxt_port_t                   *port, *main_port;
202     nxt_thread_t                 *thread;
203     nxt_runtime_t                *rt;
204     nxt_process_init_t           *init;
205     nxt_event_engine_t           *engine;
206     const nxt_event_interface_t  *interface;
207 
208     init = nxt_process_init(process);
209 
210     nxt_debug(task, "%s setup", process->name);
211 
212     nxt_process_title(task, "unit: %s", process->name);
213 
214     thread = task->thread;
215     rt     = thread->runtime;
216 
217     nxt_random_init(&thread->random);
218 
219     rt->type = init->type;
220 
221     engine = thread->engine;
222 
223     /* Update inherited main process event engine and signals processing. */
224     engine->signals->sigev = init->signals;
225 
226     interface = nxt_service_get(rt->services, "engine", rt->engine);
227     if (nxt_slow_path(interface == NULL)) {
228         return NXT_ERROR;
229     }
230 
231     if (nxt_event_engine_change(engine, interface, rt->batch) != NXT_OK) {
232         return NXT_ERROR;
233     }
234 
235     ret = nxt_runtime_thread_pool_create(thread, rt, rt->auxiliary_threads,
236                                          60000 * 1000000LL);
237     if (nxt_slow_path(ret != NXT_OK)) {
238         return NXT_ERROR;
239     }
240 
241     main_port = rt->port_by_type[NXT_PROCESS_MAIN];
242 
243     nxt_port_read_close(main_port);
244     nxt_port_write_enable(task, main_port);
245 
246     port = nxt_process_port_first(process);
247 
248     nxt_port_write_close(port);
249 
250     nxt_port_enable(task, port, init->port_handlers);
251 
252     ret = init->setup(task, process);
253 
254     if (nxt_slow_path(ret != NXT_OK)) {
255         return NXT_ERROR;
256     }
257 
258     switch (process->state) {
259 
260     case NXT_PROCESS_STATE_CREATED:
261         ret = nxt_process_send_created(task, process);
262         break;
263 
264     case NXT_PROCESS_STATE_READY:
265         ret = nxt_process_send_ready(task, process);
266 
267         if (nxt_slow_path(ret != NXT_OK)) {
268             break;
269         }
270 
271         ret = init->start(task, &process->data);
272         break;
273 
274     default:
275         nxt_assert(0);
276     }
277 
278     if (nxt_slow_path(ret != NXT_OK)) {
279         nxt_alert(task, "%s failed to start", process->name);
280     }
281 
282     return ret;
283 }
284 
285 
286 static nxt_int_t
287 nxt_process_send_created(nxt_task_t *task, nxt_process_t *process)
288 {
289     uint32_t            stream;
290     nxt_int_t           ret;
291     nxt_port_t          *my_port, *main_port;
292     nxt_runtime_t       *rt;
293 
294     nxt_assert(process->state == NXT_PROCESS_STATE_CREATED);
295 
296     rt = task->thread->runtime;
297 
298     my_port = nxt_process_port_first(process);
299     main_port = rt->port_by_type[NXT_PROCESS_MAIN];
300 
301     nxt_assert(my_port != NULL && main_port != NULL);
302 
303     stream = nxt_port_rpc_register_handler(task, my_port,
304                                            nxt_process_created_ok,
305                                            nxt_process_created_error,
306                                            main_port->pid, process);
307 
308     if (nxt_slow_path(stream == 0)) {
309         return NXT_ERROR;
310     }
311 
312     ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_PROCESS_CREATED,
313                                 -1, stream, my_port->id, NULL);
314 
315     if (nxt_slow_path(ret != NXT_OK)) {
316         nxt_alert(task, "%s failed to send CREATED message", process->name);
317         nxt_port_rpc_cancel(task, my_port, stream);
318         return NXT_ERROR;
319     }
320 
321     nxt_debug(task, "%s created", process->name);
322 
323     return NXT_OK;
324 }
325 
326 
327 static void
328 nxt_process_created_ok(nxt_task_t *task, nxt_port_recv_msg_t *msg, void *data)
329 {
330     nxt_int_t           ret;
331     nxt_process_t       *process;
332     nxt_process_init_t  *init;
333 
334     process = data;
335     init = nxt_process_init(process);
336 
337     ret = nxt_process_apply_creds(task, process);
338     if (nxt_slow_path(ret != NXT_OK)) {
339         goto fail;
340     }
341 
342     nxt_log(task, NXT_LOG_INFO, "%s started", process->name);
343 
344     ret = init->start(task, &process->data);
345 
346 fail:
347 
348     nxt_process_quit(task, ret == NXT_OK ? 0 : 1);
349 }
350 
351 
352 static void
353 nxt_process_created_error(nxt_task_t *task, nxt_port_recv_msg_t *msg,
354     void *data)
355 {
356     nxt_process_t       *process;
357     nxt_process_init_t  *init;
358 
359     process = data;
360     init = nxt_process_init(process);
361 
362     nxt_alert(task, "%s failed to start", init->name);
363 
364     nxt_process_quit(task, 1);
365 }
366 
367 
368 nxt_int_t
369 nxt_process_core_setup(nxt_task_t *task, nxt_process_t *process)
370 {
371     nxt_int_t  ret;
372 
373     ret = nxt_process_apply_creds(task, process);
374     if (nxt_slow_path(ret != NXT_OK)) {
375         return NXT_ERROR;
376     }
377 
378     process->state = NXT_PROCESS_STATE_READY;
379 
380     return NXT_OK;
381 }
382 
383 
384 #if (NXT_HAVE_CLONE_NEWUSER)
385 
386 nxt_int_t
387 nxt_process_vldt_isolation_creds(nxt_task_t *task, nxt_process_t *process)
388 {
389     nxt_int_t         ret;
390     nxt_clone_t       *clone;
391     nxt_credential_t  *creds;
392 
393     clone = &process->isolation.clone;
394     creds = process->user_cred;
395 
396     if (clone->uidmap.size == 0 && clone->gidmap.size == 0) {
397         return NXT_OK;
398     }
399 
400     if (!nxt_is_clone_flag_set(clone->flags, NEWUSER)) {
401         if (nxt_slow_path(clone->uidmap.size > 0)) {
402             nxt_log(task, NXT_LOG_ERR, "\"uidmap\" is set but "
403                     "\"isolation.namespaces.credential\" is false or unset");
404 
405             return NXT_ERROR;
406         }
407 
408         if (nxt_slow_path(clone->gidmap.size > 0)) {
409             nxt_log(task, NXT_LOG_ERR, "\"gidmap\" is set but "
410                     "\"isolation.namespaces.credential\" is false or unset");
411 
412             return NXT_ERROR;
413         }
414 
415         return NXT_OK;
416     }
417 
418     ret = nxt_clone_vldt_credential_uidmap(task, &clone->uidmap, creds);
419     if (nxt_slow_path(ret != NXT_OK)) {
420         return NXT_ERROR;
421     }
422 
423     return nxt_clone_vldt_credential_gidmap(task, &clone->gidmap, creds);
424 }
425 
426 #endif
427 
428 
429 nxt_int_t
430 nxt_process_creds_set(nxt_task_t *task, nxt_process_t *process, nxt_str_t *user,
431     nxt_str_t *group)
432 {
433     char  *str;
434 
435     process->user_cred = nxt_mp_zalloc(process->mem_pool,
436                                        sizeof(nxt_credential_t));
437 
438     if (nxt_slow_path(process->user_cred == NULL)) {
439         return NXT_ERROR;
440     }
441 
442     str = nxt_mp_zalloc(process->mem_pool, user->length + 1);
443     if (nxt_slow_path(str == NULL)) {
444         return NXT_ERROR;
445     }
446 
447     nxt_memcpy(str, user->start, user->length);
448     str[user->length] = '\0';
449 
450     process->user_cred->user = str;
451 
452     if (group->start != NULL) {
453         str = nxt_mp_zalloc(process->mem_pool, group->length + 1);
454         if (nxt_slow_path(str == NULL)) {
455             return NXT_ERROR;
456         }
457 
458         nxt_memcpy(str, group->start, group->length);
459         str[group->length] = '\0';
460 
461     } else {
462         str = NULL;
463     }
464 
465     return nxt_credential_get(task, process->mem_pool, process->user_cred, str);
466 }
467 
468 
469 nxt_int_t
470 nxt_process_apply_creds(nxt_task_t *task, nxt_process_t *process)
471 {
472     nxt_int_t      ret, cap_setid;
473     nxt_runtime_t  *rt;
474 
475     rt = task->thread->runtime;
476 
477     cap_setid = rt->capabilities.setid;
478 
479 #if (NXT_HAVE_CLONE && NXT_HAVE_CLONE_NEWUSER)
480     if (!cap_setid
481         && nxt_is_clone_flag_set(process->isolation.clone.flags, NEWUSER)) {
482         cap_setid = 1;
483     }
484 #endif
485 
486     if (cap_setid) {
487         ret = nxt_credential_setgids(task, process->user_cred);
488         if (nxt_slow_path(ret != NXT_OK)) {
489             return NXT_ERROR;
490         }
491 
492         ret = nxt_credential_setuid(task, process->user_cred);
493         if (nxt_slow_path(ret != NXT_OK)) {
494             return NXT_ERROR;
495         }
496     }
497 
498     return NXT_OK;
499 }
500 
501 
502 static nxt_int_t
503 nxt_process_send_ready(nxt_task_t *task, nxt_process_t *process)
504 {
505     nxt_int_t           ret;
506     nxt_port_t          *main_port;
507     nxt_runtime_t       *rt;
508 
509     rt = task->thread->runtime;
510 
511     main_port = rt->port_by_type[NXT_PROCESS_MAIN];
512 
513     nxt_assert(main_port != NULL);
514 
515     ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_PROCESS_READY,
516                                 -1, process->stream, 0, NULL);
517 
518     if (nxt_slow_path(ret != NXT_OK)) {
519         nxt_alert(task, "%s failed to send READY message", process->name);
520         return NXT_ERROR;
521     }
522 
523     nxt_debug(task, "%s sent ready", process->name);
524 
525     return NXT_OK;
526 }
527 
528 
529 #if (NXT_HAVE_POSIX_SPAWN)
530 
531 /*
532  * Linux glibc 2.2 posix_spawn() is implemented via fork()/execve().
533  * Linux glibc 2.4 posix_spawn() without file actions and spawn
534  * attributes uses vfork()/execve().
535  *
536  * On FreeBSD 8.0 posix_spawn() is implemented via vfork()/execve().
537  *
538  * Solaris 10:
539  *   In the Solaris 10 OS, posix_spawn() is currently implemented using
540  *   private-to-libc vfork(), execve(), and exit() functions.  They are
541  *   identical to regular vfork(), execve(), and exit() in functionality,
542  *   but they are not exported from libc and therefore don't cause the
543  *   deadlock-in-the-dynamic-linker problem that any multithreaded code
544  *   outside of libc that calls vfork() can cause.
545  *
546  * On MacOSX 10.5 (Leoprad) and NetBSD 6.0 posix_spawn() is implemented
547  * as syscall.
548  */
549 
550 nxt_pid_t
551 nxt_process_execute(nxt_task_t *task, char *name, char **argv, char **envp)
552 {
553     nxt_pid_t  pid;
554 
555     nxt_debug(task, "posix_spawn(\"%s\")", name);
556 
557     if (posix_spawn(&pid, name, NULL, NULL, argv, envp) != 0) {
558         nxt_alert(task, "posix_spawn(\"%s\") failed %E", name, nxt_errno);
559         return -1;
560     }
561 
562     return pid;
563 }
564 
565 #else
566 
567 nxt_pid_t
568 nxt_process_execute(nxt_task_t *task, char *name, char **argv, char **envp)
569 {
570     nxt_pid_t  pid;
571 
572     /*
573      * vfork() is better than fork() because:
574      *   it is faster several times;
575      *   its execution time does not depend on private memory mapping size;
576      *   it has lesser chances to fail due to the ENOMEM error.
577      */
578 
579     pid = vfork();
580 
581     switch (pid) {
582 
583     case -1:
584         nxt_alert(task, "vfork() failed while executing \"%s\" %E",
585                   name, nxt_errno);
586         break;
587 
588     case 0:
589         /* A child. */
590         nxt_debug(task, "execve(\"%s\")", name);
591 
592         (void) execve(name, argv, envp);
593 
594         nxt_alert(task, "execve(\"%s\") failed %E", name, nxt_errno);
595 
596         exit(1);
597         nxt_unreachable();
598         break;
599 
600     default:
601         /* A parent. */
602         nxt_debug(task, "vfork(): %PI", pid);
603         break;
604     }
605 
606     return pid;
607 }
608 
609 #endif
610 
611 
612 nxt_int_t
613 nxt_process_daemon(nxt_task_t *task)
614 {
615     nxt_fd_t      fd;
616     nxt_pid_t     pid;
617     const char    *msg;
618 
619     fd = -1;
620 
621     /*
622      * fork() followed by a parent process's exit() detaches a child process
623      * from an init script or terminal shell process which has started the
624      * parent process and allows the child process to run in background.
625      */
626 
627     pid = fork();
628 
629     switch (pid) {
630 
631     case -1:
632         msg = "fork() failed %E";
633         goto fail;
634 
635     case 0:
636         /* A child. */
637         break;
638 
639     default:
640         /* A parent. */
641         nxt_debug(task, "fork(): %PI", pid);
642         exit(0);
643         nxt_unreachable();
644     }
645 
646     nxt_pid = getpid();
647 
648     /* Clean inherited cached thread tid. */
649     task->thread->tid = 0;
650 
651     nxt_debug(task, "daemon");
652 
653     /* Detach from controlling terminal. */
654 
655     if (setsid() == -1) {
656         nxt_alert(task, "setsid() failed %E", nxt_errno);
657         return NXT_ERROR;
658     }
659 
660     /*
661      * Reset file mode creation mask: any access
662      * rights can be set on file creation.
663      */
664     umask(0);
665 
666     /* Redirect STDIN and STDOUT to the "/dev/null". */
667 
668     fd = open("/dev/null", O_RDWR);
669     if (fd == -1) {
670         msg = "open(\"/dev/null\") failed %E";
671         goto fail;
672     }
673 
674     if (dup2(fd, STDIN_FILENO) == -1) {
675         msg = "dup2(\"/dev/null\", STDIN) failed %E";
676         goto fail;
677     }
678 
679     if (dup2(fd, STDOUT_FILENO) == -1) {
680         msg = "dup2(\"/dev/null\", STDOUT) failed %E";
681         goto fail;
682     }
683 
684     if (fd > STDERR_FILENO) {
685         nxt_fd_close(fd);
686     }
687 
688     return NXT_OK;
689 
690 fail:
691 
692     nxt_alert(task, msg, nxt_errno);
693 
694     if (fd != -1) {
695         nxt_fd_close(fd);
696     }
697 
698     return NXT_ERROR;
699 }
700 
701 
702 void
703 nxt_nanosleep(nxt_nsec_t ns)
704 {
705     struct timespec  ts;
706 
707     ts.tv_sec = ns / 1000000000;
708     ts.tv_nsec = ns % 1000000000;
709 
710     (void) nanosleep(&ts, NULL);
711 }
712 
713 
714 void
715 nxt_process_use(nxt_task_t *task, nxt_process_t *process, int i)
716 {
717     process->use_count += i;
718 
719     if (process->use_count == 0) {
720         nxt_runtime_process_release(task->thread->runtime, process);
721     }
722 }
723 
724 
725 void
726 nxt_process_port_add(nxt_task_t *task, nxt_process_t *process, nxt_port_t *port)
727 {
728     nxt_assert(port->process == NULL);
729 
730     port->process = process;
731     nxt_queue_insert_tail(&process->ports, &port->link);
732 
733     nxt_process_use(task, process, 1);
734 }
735 
736 
737 nxt_process_type_t
738 nxt_process_type(nxt_process_t *process)
739 {
740     return nxt_queue_is_empty(&process->ports) ? 0 :
741         (nxt_process_port_first(process))->type;
742 }
743 
744 
745 void
746 nxt_process_close_ports(nxt_task_t *task, nxt_process_t *process)
747 {
748     nxt_port_t  *port;
749 
750     nxt_process_port_each(process, port) {
751 
752         nxt_port_close(task, port);
753 
754         nxt_runtime_port_remove(task, port);
755 
756     } nxt_process_port_loop;
757 }
758 
759 
760 void
761 nxt_process_connected_port_add(nxt_process_t *process, nxt_port_t *port)
762 {
763     nxt_thread_mutex_lock(&process->cp_mutex);
764 
765     nxt_port_hash_add(&process->connected_ports, port);
766 
767     nxt_thread_mutex_unlock(&process->cp_mutex);
768 }
769 
770 
771 void
772 nxt_process_connected_port_remove(nxt_process_t *process, nxt_port_t *port)
773 {
774     nxt_thread_mutex_lock(&process->cp_mutex);
775 
776     nxt_port_hash_remove(&process->connected_ports, port);
777 
778     nxt_thread_mutex_unlock(&process->cp_mutex);
779 }
780 
781 
782 nxt_port_t *
783 nxt_process_connected_port_find(nxt_process_t *process, nxt_port_t *port)
784 {
785     nxt_port_t  *res;
786 
787     nxt_thread_mutex_lock(&process->cp_mutex);
788 
789     res = nxt_port_hash_find(&process->connected_ports, port->pid, port->id);
790 
791     nxt_thread_mutex_unlock(&process->cp_mutex);
792 
793     return res;
794 }
795 
796 
797 void
798 nxt_process_quit(nxt_task_t *task, nxt_uint_t exit_status)
799 {
800     nxt_uint_t           n;
801     nxt_queue_t          *listen;
802     nxt_runtime_t        *rt;
803     nxt_queue_link_t     *link, *next;
804     nxt_listen_event_t   *lev;
805     nxt_listen_socket_t  *ls;
806 
807     rt = task->thread->runtime;
808 
809     nxt_debug(task, "close listen connections");
810 
811     listen = &task->thread->engine->listen_connections;
812 
813     for (link = nxt_queue_first(listen);
814          link != nxt_queue_tail(listen);
815          link = next)
816     {
817         next = nxt_queue_next(link);
818         lev = nxt_queue_link_data(link, nxt_listen_event_t, link);
819         nxt_queue_remove(link);
820 
821         nxt_fd_event_close(task->thread->engine, &lev->socket);
822     }
823 
824     if (rt->listen_sockets != NULL) {
825 
826         ls = rt->listen_sockets->elts;
827         n = rt->listen_sockets->nelts;
828 
829         while (n != 0) {
830             nxt_socket_close(task, ls->socket);
831             ls->socket = -1;
832 
833             ls++;
834             n--;
835         }
836 
837         rt->listen_sockets->nelts = 0;
838     }
839 
840     nxt_runtime_quit(task, exit_status);
841 }
842