Author: Bleep <tomh@inxpress.net>
[ircu2.10.12-pk.git] / ircd / os_bsd.c
1 /*
2  * IRC - Internet Relay Chat, ircd/os_generic.c
3  * Copyright (C) 1999 Thomas Helvey
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 1, or (at your option)
8  * any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  *
19  * $Id$
20  *
21  */
22 #include "ircd_osdep.h"
23 #include "config.h"
24
25 #include <assert.h>
26 #include <sys/types.h>
27 #include <sys/time.h>
28 #include <sys/resource.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <netinet/in.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <sys/ioctl.h>
35 #include <sys/socket.h>
36 #include <unistd.h>
37
38 #ifdef HPUX
39 #include <sys/syscall.h>
40 #define getrusage(a,b) syscall(SYS_GETRUSAGE, a, b)
41 #endif
42
43 /*
44  * This is part of the STATS replies. There is no offical numeric for this
45  * since this isnt an official command, in much the same way as HASH isnt.
46  * It is also possible that some systems wont support this call or have
47  * different field names for "struct rusage".
48  * -avalon
49  */
50 int os_get_rusage(struct Client *cptr, int uptime, EnumFn enumerator)
51 {
52   char buf[256];
53 #ifdef HAVE_GETRUSAGE
54   struct rusage rus;
55   time_t secs;
56
57 #ifdef  hz
58 #  define hzz hz
59 #else
60 #  ifdef HZ
61 #    define hzz HZ
62 #  else
63   int hzz = 1;
64 #  ifdef HPUX
65   hzz = sysconf(_SC_CLK_TCK);
66 #  endif
67 #endif
68 #endif
69
70   assert(0 != enumerator);
71   if (getrusage(RUSAGE_SELF, &rus) == -1)
72     return 0;
73
74   secs = rus.ru_utime.tv_sec + rus.ru_stime.tv_sec;
75   if (secs == 0)
76     secs = 1;
77
78   sprintf(buf, "CPU Secs %ld:%ld User %ld:%ld System %ld:%ld",
79           secs / 60, secs % 60,
80           rus.ru_utime.tv_sec / 60, rus.ru_utime.tv_sec % 60,
81           rus.ru_stime.tv_sec / 60, rus.ru_stime.tv_sec % 60);
82   (*enumerator)(cptr, buf);
83
84   sprintf(buf, "RSS %ld ShMem %ld Data %ld Stack %ld",
85           rus.ru_maxrss,
86           rus.ru_ixrss / (uptime * hzz), rus.ru_idrss / (uptime * hzz),
87           rus.ru_isrss / (uptime * hzz));
88   (*enumerator)(cptr, buf);
89
90   sprintf(buf, "Swaps %ld Reclaims %ld Faults %ld",
91           rus.ru_nswap, rus.ru_minflt, rus.ru_majflt);
92   (*enumerator)(cptr, buf);
93
94   sprintf(buf, "Block in %ld out %ld", rus.ru_inblock, rus.ru_oublock);
95   (*enumerator)(cptr, buf);
96   
97   sprintf(buf, "Msg Rcv %ld Send %ld", rus.ru_msgrcv, rus.ru_msgsnd);
98   (*enumerator)(cptr, buf);
99
100   sprintf(buf, "Signals %ld Context Vol. %ld Invol %ld",
101           rus.ru_nsignals, rus.ru_nvcsw, rus.ru_nivcsw);
102   (*enumerator)(cptr, buf);
103
104 #else /* HAVE_GETRUSAGE */
105 #if HAVE_TIMES
106   struct tms tmsbuf;
107   time_t secs, mins;
108   int hzz = 1, ticpermin;
109   int umin, smin, usec, ssec;
110
111   assert(0 != enumerator);
112 #ifdef HPUX
113   hzz = sysconf(_SC_CLK_TCK);
114 #endif
115   ticpermin = hzz * 60;
116
117   umin = tmsbuf.tms_utime / ticpermin;
118   usec = (tmsbuf.tms_utime % ticpermin) / (float)hzz;
119   smin = tmsbuf.tms_stime / ticpermin;
120   ssec = (tmsbuf.tms_stime % ticpermin) / (float)hzz;
121   secs = usec + ssec;
122   mins = (secs / 60) + umin + smin;
123   secs %= hzz;
124
125   if (times(&tmsbuf) == -1)
126     return 0;
127   secs = tmsbuf.tms_utime + tmsbuf.tms_stime;
128
129   sprintf(buf, "CPU Secs %d:%d User %d:%d System %d:%d", 
130           mins, secs, umin, usec, smin, ssec);
131   (*enumerator)(cptr, buf);
132 #endif /* HAVE_TIMES */
133 #endif /* HAVE_GETRUSAGE */
134   return 1;
135 }
136
137 int os_get_sockerr(int fd)
138 {
139   int    err = 0;
140 #if defined(SO_ERROR)
141   size_t len = sizeof(err);
142   getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &len);
143 #endif
144   return err;
145 }
146
147 /*
148  * set_non_blocking
149  *
150  * Set the client connection into non-blocking mode. If your
151  * system doesn't support this, you can make this a dummy
152  * function (and get all the old problems that plagued the
153  * blocking version of IRC--not a problem if you are a
154  * lightly loaded node...)
155  */
156 int os_set_nonblocking(int fd)
157 {
158   int res;
159 #ifndef NBLOCK_SYSV
160   int nonb = 0;
161 #endif
162
163   /*
164    * NOTE: consult ALL your relevant manual pages *BEFORE* changing
165    * these ioctl's. There are quite a few variations on them,
166    * as can be seen by the PCS one. They are *NOT* all the same.
167    * Heed this well. - Avalon.
168    */
169 #ifdef  NBLOCK_POSIX
170   nonb |= O_NONBLOCK;
171 #endif
172 #ifdef  NBLOCK_BSD
173   nonb |= O_NDELAY;
174 #endif
175 #ifdef  NBLOCK_SYSV
176   /* This portion of code might also apply to NeXT. -LynX */
177   res = 1;
178
179   if (ioctl(fd, FIONBIO, &res) == -1)
180     return 0;
181 #else
182   if ((res = fcntl(fd, F_GETFL, 0)) == -1)
183     return 0;
184   else if (fcntl(fd, F_SETFL, res | nonb) == -1)
185     return 0;
186 #endif
187   return 1;
188 }
189
190
191 /*
192  *  set_sock_opts
193  */
194 int os_set_reuseaddr(int fd)
195 {
196   size_t opt = 1;
197   return (0 == setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, 
198                           (const char*) &opt, sizeof(opt)));
199 }
200
201 int os_set_sockbufs(int fd, unsigned int size)
202 {
203   size_t opt = size;
204   return (0 == setsockopt(fd, SOL_SOCKET, SO_RCVBUF, 
205                           (const char*) &opt, sizeof(opt)) &&
206           0 == setsockopt(fd, SOL_SOCKET, SO_SNDBUF, 
207                           (const char*) &opt, sizeof(opt)));
208 }
209
210 int os_disable_options(int fd)
211 {
212 #if defined(IP_OPTIONS) && defined(IPPROTO_IP)
213   return (0 == setsockopt(fd, IPPROTO_IP, IP_OPTIONS, NULL, 0));
214 #else
215   return 1;
216 #endif
217 }
218
219 /*
220  * Try and find the correct name to use with getrlimit() for setting the max.
221  * number of files allowed to be open by this process.
222  */
223 #ifdef RLIMIT_FDMAX
224 #define RLIMIT_FD_MAX   RLIMIT_FDMAX
225 #else
226 #ifdef RLIMIT_NOFILE
227 #define RLIMIT_FD_MAX RLIMIT_NOFILE
228 #else
229 #ifdef RLIMIT_OPEN_MAX
230 #define RLIMIT_FD_MAX RLIMIT_OPEN_MAX
231 #else
232 #undef RLIMIT_FD_MAX
233 #endif
234 #endif
235 #endif
236
237 int os_set_fdlimit(unsigned int max_descriptors)
238 {
239 #if defined(HAVE_SETRLIMIT) && defined(RLIMIT_FD_MAX)
240   struct rlimit limit;
241
242   if (!getrlimit(RLIMIT_FD_MAX, &limit)) {
243     if (limit.rlim_max < MAXCONNECTIONS)
244       return limit.rlim_max;
245     limit.rlim_cur = limit.rlim_max;    /* make soft limit the max */
246     return setrlimit(RLIMIT_FD_MAX, &limit);
247   }
248 #endif /* defined(HAVE_SETRLIMIT) && defined(RLIMIT_FD_MAX) */
249   return 0;
250 }
251
252 IOResult os_recv_nonb(int fd, char* buf, unsigned int length, 
253                  unsigned int* count_out)
254 {
255   int res;
256   assert(0 != buf);
257   assert(0 != count_out);
258   *count_out = 0;
259   errno = 0;
260
261   if (0 < (res = recv(fd, buf, length, 0))) {
262     *count_out = (unsigned) res;
263     return IO_SUCCESS;
264   }
265   else if (res < 0) {
266     if (EWOULDBLOCK == errno || EAGAIN == errno)
267       return IO_BLOCKED;
268     else
269       return IO_FAILURE;
270   } 
271   /*
272    * 0   == client closed the connection
273    * < 1 == error
274    */
275   return IO_FAILURE;
276 }
277
278 IOResult os_recvfrom_nonb(int fd, char* buf, unsigned int length, 
279                           unsigned int* length_out, struct sockaddr_in* sin_out)
280 {
281   int    res;
282   size_t len = sizeof(struct sockaddr_in);
283   assert(0 != buf);
284   assert(0 != length_out);
285   assert(0 != sin_out);
286   errno = 0;
287   *length_out = 0;
288
289   res = recvfrom(fd, buf, length, 0, (struct sockaddr*) sin_out, &len);
290   if (-1 == res) {
291     if (EWOULDBLOCK == errno || ENOMEM == errno)
292       return IO_BLOCKED;
293     return IO_FAILURE;
294   }
295   *length_out = res;
296   return IO_SUCCESS;
297 }
298
299 IOResult os_send_nonb(int fd, const char* buf, unsigned int length, 
300                  unsigned int* count_out)
301 {
302   int res;
303   assert(0 != buf);
304   assert(0 != count_out);
305   *count_out = 0;
306   errno = 0;
307
308   if (-1 < (res = send(fd, buf, length, 0))) {
309     *count_out = (unsigned) res;
310     return IO_SUCCESS;
311   }
312   else if (EWOULDBLOCK == errno || EAGAIN == errno || 
313            ENOMEM == errno || ENOBUFS == errno)
314     return IO_BLOCKED;
315   return IO_FAILURE;
316 }
317
318 int os_connect_nonb(int fd, const struct sockaddr_in* sin)
319 {
320   if (connect(fd, (struct sockaddr*) sin, sizeof(struct sockaddr_in))) {
321     if (errno != EINPROGRESS)
322       return 0;
323   }
324   return 1;
325 }
326       
327 int os_get_sockname(int fd, struct sockaddr_in* sin_out)
328 {
329   size_t len = sizeof(struct sockaddr_in);
330   assert(0 != sin_out);
331   return (0 == getsockname(fd, (struct sockaddr*) sin_out, &len));
332 }
333
334 int os_get_peername(int fd, struct sockaddr_in* sin_out)
335 {
336   size_t len = sizeof(struct sockaddr_in);
337   assert(0 != sin_out);
338   return (0 == getpeername(fd, (struct sockaddr*) sin_out, &len));
339 }
340
341 int os_set_listen(int fd, int backlog)
342 {
343   return (0 == listen(fd, backlog));
344 }
345
346