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