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