1 /*
2 * ircd-hybrid: an advanced, lightweight Internet Relay Chat Daemon (ircd)
3 *
4 * Copyright (c) 1997-2021 ircd-hybrid development team
5 *
6 * This program 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 this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
19 * USA
20 */
21
22 /*! \file fdlist.c
23 * \brief Maintains a list of file descriptors.
24 * \version $Id: fdlist.c 9858 2021-01-01 04:43:42Z michael $
25 */
26
27 #include "stdinc.h"
28 #include "fdlist.h"
29 #include "irc_string.h"
30 #include "s_bsd.h" /* comm_setselect */
31 #include "memory.h"
32 #include "misc.h"
33
34
35 fde_t *fd_table;
36 int number_fd = LEAKED_FDS;
37 int hard_fdlimit = 0;
38 int highest_fd = -1;
39
40
41 void
fdlist_init(void)42 fdlist_init(void)
43 {
44 /*
45 * Allow MAXCLIENTS_MIN clients even at the cost of MAX_BUFFER and
46 * some not really LEAKED_FDS
47 */
48 hard_fdlimit = IRCD_MAX(hard_fdlimit, LEAKED_FDS + MAX_BUFFER + MAXCLIENTS_MIN);
49 fd_table = xcalloc(sizeof(*fd_table) * hard_fdlimit);
50 }
51
52 static void
fdlist_update_highest_fd(int fd,bool opening)53 fdlist_update_highest_fd(int fd, bool opening)
54 {
55 if (fd < highest_fd)
56 return;
57
58 assert(fd < hard_fdlimit);
59
60 if (fd > highest_fd)
61 {
62 /*
63 * assert() that we are not closing a FD bigger than our known highest FD.
64 */
65 assert(opening == true);
66 highest_fd = fd;
67 return;
68 }
69
70 /* If we are here, then fd == highest_fd */
71 /*
72 * assert() that we are closing the highest FD; we can't be re-opening it.
73 */
74 assert(opening == false);
75
76 while (highest_fd >= 0 && fd_table[highest_fd].flags.open == false)
77 --highest_fd;
78 }
79
80 /* Called to open a given filedescriptor */
81 fde_t *
fd_open(int fd,bool is_socket,const char * desc)82 fd_open(int fd, bool is_socket, const char *desc)
83 {
84 fde_t *F = &fd_table[fd];
85
86 assert(fd >= 0);
87 assert(F->fd == 0);
88 assert(F->flags.open == false);
89
90 /*
91 * Note: normally we'd have to clear the other flags, but currently F
92 * is always cleared before calling us.
93 */
94 F->fd = fd;
95 F->comm_index = -1;
96 F->flags.open = true;
97 F->flags.is_socket = is_socket;
98
99 if (desc)
100 F->desc = xstrndup(desc, FD_DESC_SIZE);
101
102 fdlist_update_highest_fd(F->fd, true);
103 ++number_fd;
104
105 return F;
106 }
107
108 /* Called to close a given filedescriptor */
109 fde_t *
fd_close(fde_t * F)110 fd_close(fde_t *F)
111 {
112 assert(F->fd >= 0);
113 assert(F->flags.open == true);
114
115 if (F->flags.is_socket == true)
116 comm_setselect(F, COMM_SELECT_WRITE | COMM_SELECT_READ, NULL, NULL, 0);
117
118 if (tls_isusing(&F->tls))
119 tls_free(&F->tls);
120
121 xfree(F->desc);
122 /* Unlike squid, we're actually closing the FD here! -- adrian */
123 close(F->fd);
124 F->flags.open = false; /* Must set F->flags.open == false before fdlist_update_highest_fd() */
125
126 fdlist_update_highest_fd(F->fd, false);
127 --number_fd;
128
129 memset(F, 0, sizeof(*F));
130
131 return F;
132 }
133
134 /*
135 * fd_note() - set the fd note
136 *
137 * Note: must be careful not to overflow fd_table[fd].desc when
138 * calling.
139 */
140 void
fd_note(fde_t * F,const char * format,...)141 fd_note(fde_t *F, const char *format, ...)
142 {
143 if (format)
144 {
145 char buf[FD_DESC_SIZE + 1];
146 va_list args;
147
148 va_start(args, format);
149 vsnprintf(buf, sizeof(buf), format, args);
150 va_end(args);
151
152 xfree(F->desc);
153 F->desc = xstrdup(buf);
154 }
155 else
156 {
157 xfree(F->desc);
158 F->desc = NULL;
159 }
160 }
161
162 /* Make sure stdio descriptors (0-2) and profiler descriptor (3)
163 * always go somewhere harmless. Use -foreground for profiling
164 * or executing from gdb */
165 void
close_standard_fds(void)166 close_standard_fds(void)
167 {
168 for (int i = 0; i < LOWEST_SAFE_FD; ++i)
169 {
170 close(i);
171
172 if (open("/dev/null", O_RDWR) < 0)
173 exit(EXIT_FAILURE); /* we're hosed if we can't even open /dev/null */
174 }
175 }
176
177 void
close_fds(void)178 close_fds(void)
179 {
180 for (int fd = 0; fd <= highest_fd; ++fd)
181 close(fd);
182 }
183