adada4573a6b8381c4682f79e79a4e0f6ba31eb4
[ircu2.10.12-pk.git] / ircd / os_generic.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 "config.h"
23
24 #define _XOPEN_SOURCE   500 /* make limits.h #define IOV_MAX */
25 #define __EXTENSIONS__  1   /* make Solaris netinet/in.h know IPv6 */
26
27 #include "ircd_osdep.h"
28 #include "msgq.h"
29 #include "res.h"
30 #include "s_bsd.h"
31 #include "sys.h"
32
33 /* Include file dependency notes:
34  * FreeBSD requires struct timeval from sys/time.h before struct
35  * rusage in sys/resource.h.
36  * Solaris requires sys/time.h before struct rusage (indirectly) in
37  * netinet/in.h.
38  */
39 #include <assert.h>
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <limits.h>
43 #include <stdio.h>
44 #include <string.h>
45 #include <sys/ioctl.h>
46 #include <sys/types.h>
47 #include <sys/time.h>
48 #include <netinet/in.h>
49 #include <sys/resource.h>
50 #include <sys/socket.h>
51 #include <sys/uio.h>
52
53 #if HAVE_SYS_PARAM_H
54 #include <sys/param.h>
55 #endif
56
57 #if HAVE_UNISTD_H
58 #include <unistd.h>
59 #endif
60
61 #ifndef IOV_MAX
62 #define IOV_MAX 16      /* minimum required */
63 #endif
64
65 #ifdef HPUX
66 #include <sys/syscall.h>
67 #define getrusage(a,b) syscall(SYS_GETRUSAGE, a, b)
68 #endif
69
70 #ifdef IPV6
71 #define sockaddr_native sockaddr_in6
72
73 void sockaddr_to_irc(const struct sockaddr_in6 *v6, struct irc_sockaddr *irc)
74 {
75     assert(v6->sin6_family == AF_INET6);
76     memcpy(&irc->addr.in6_16[0], &v6->sin6_addr, sizeof(v6->sin6_addr));
77     irc->port = ntohs(v6->sin6_port);
78 }
79
80 void sockaddr_from_irc(struct sockaddr_in6 *v6, const struct irc_sockaddr *irc, int persist)
81 {
82     memset(v6, 0, sizeof(*v6));
83     v6->sin6_family = AF_INET6;
84     memcpy(&v6->sin6_addr, &irc->addr.in6_16[0], sizeof(v6->sin6_addr));
85     if (persist && irc_in_addr_is_ipv4(&irc->addr))
86         v6->sin6_addr.s6_addr[10] = v6->sin6_addr.s6_addr[11] = '\xff';
87     v6->sin6_port = htons(irc->port);
88 }
89
90 #else
91 #define sockaddr_native sockaddr_in
92
93 void sockaddr_to_irc(const struct sockaddr_in *v4, struct irc_sockaddr *irc)
94 {
95     assert(v4->sin_family == AF_INET);
96     memset(&irc->addr, 0, 6*sizeof(irc->addr.in6_16[0]));
97     memcpy(&irc->addr.in6_16[6], &v4->sin_addr, sizeof(v4->sin_addr));
98     irc->port = ntohs(v4->sin_port);
99 }
100
101 void sockaddr_from_irc(struct sockaddr_in *v4, const struct irc_sockaddr *irc, int persist)
102 {
103     v4->sin_family = AF_INET;
104     assert(!irc->addr.in6_16[0] && !irc->addr.in6_16[1] && !irc->addr.in6_16[2] && !irc->addr.in6_16[3] && !irc->addr.in6_16[4] && (!irc->addr.in6_16[5] || irc->addr.in6_16[5] == 0xffff));
105     memcpy(&v4->sin_addr, &irc->addr.in6_16[6], sizeof(v4->sin_addr));
106     v4->sin_port = htons(irc->port);
107     (void)persist;
108 }
109
110 #endif
111
112 /*
113  * This is part of the STATS replies. There is no offical numeric for this
114  * since this isnt an official command, in much the same way as HASH isnt.
115  * It is also possible that some systems wont support this call or have
116  * different field names for "struct rusage".
117  * -avalon
118  */
119 int os_get_rusage(struct Client *cptr, int uptime, EnumFn enumerator)
120 {
121 #ifdef HAVE_GETRUSAGE
122   char buf[256];
123   struct rusage rus;
124   time_t secs;
125
126 #ifdef  hz
127 #  define hzz hz
128 #else
129 #  ifdef HZ
130 #    define hzz HZ
131 #  else
132   int hzz = 1;
133 #  ifdef HPUX
134   hzz = sysconf(_SC_CLK_TCK);
135 #  endif
136 #endif
137 #endif
138
139   assert(0 != enumerator);
140   if (getrusage(RUSAGE_SELF, &rus) == -1)
141     return 0;
142
143   secs = rus.ru_utime.tv_sec + rus.ru_stime.tv_sec;
144   if (secs == 0)
145     secs = 1;
146
147   sprintf(buf, "CPU Secs %ld:%ld User %ld:%ld System %ld:%ld",
148           (long)(secs / 60), (long)(secs % 60),
149           rus.ru_utime.tv_sec / 60, rus.ru_utime.tv_sec % 60,
150           rus.ru_stime.tv_sec / 60, rus.ru_stime.tv_sec % 60);
151   (*enumerator)(cptr, buf);
152
153   sprintf(buf, "RSS %ld ShMem %ld Data %ld Stack %ld",
154           rus.ru_maxrss,
155           rus.ru_ixrss / (uptime * hzz), rus.ru_idrss / (uptime * hzz),
156           rus.ru_isrss / (uptime * hzz));
157   (*enumerator)(cptr, buf);
158
159   sprintf(buf, "Swaps %ld Reclaims %ld Faults %ld",
160           rus.ru_nswap, rus.ru_minflt, rus.ru_majflt);
161   (*enumerator)(cptr, buf);
162
163   sprintf(buf, "Block in %ld out %ld", rus.ru_inblock, rus.ru_oublock);
164   (*enumerator)(cptr, buf);
165
166   sprintf(buf, "Msg Rcv %ld Send %ld", rus.ru_msgrcv, rus.ru_msgsnd);
167   (*enumerator)(cptr, buf);
168
169   sprintf(buf, "Signals %ld Context Vol. %ld Invol %ld",
170           rus.ru_nsignals, rus.ru_nvcsw, rus.ru_nivcsw);
171   (*enumerator)(cptr, buf);
172
173 #else /* HAVE_GETRUSAGE */
174 #if HAVE_TIMES
175   char buf[256];
176   struct tms tmsbuf;
177   time_t secs, mins;
178   int hzz = 1, ticpermin;
179   int umin, smin, usec, ssec;
180
181   assert(0 != enumerator);
182 #ifdef HPUX
183   hzz = sysconf(_SC_CLK_TCK);
184 #endif
185   ticpermin = hzz * 60;
186
187   umin = tmsbuf.tms_utime / ticpermin;
188   usec = (tmsbuf.tms_utime % ticpermin) / (float)hzz;
189   smin = tmsbuf.tms_stime / ticpermin;
190   ssec = (tmsbuf.tms_stime % ticpermin) / (float)hzz;
191   secs = usec + ssec;
192   mins = (secs / 60) + umin + smin;
193   secs %= hzz;
194
195   if (times(&tmsbuf) == -1)
196     return 0;
197   secs = tmsbuf.tms_utime + tmsbuf.tms_stime;
198
199   sprintf(buf, "CPU Secs %d:%d User %d:%d System %d:%d", 
200           mins, secs, umin, usec, smin, ssec);
201   (*enumerator)(cptr, buf);
202 #endif /* HAVE_TIMES */
203 #endif /* HAVE_GETRUSAGE */
204   return 1;
205 }
206
207 int os_get_sockerr(int fd)
208 {
209   int    err = 0;
210 #if defined(SO_ERROR)
211   unsigned int len = sizeof(err);
212   getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &len);
213 #endif
214   return err;
215 }
216
217 /*
218  * set_non_blocking
219  *
220  * Set the client connection into non-blocking mode. If your
221  * system doesn't support this, you can make this a dummy
222  * function (and get all the old problems that plagued the
223  * blocking version of IRC--not a problem if you are a
224  * lightly loaded node...)
225  */
226 int os_set_nonblocking(int fd)
227 {
228   int res;
229 #ifndef NBLOCK_SYSV
230   int nonb = 0;
231 #endif
232
233   /*
234    * NOTE: consult ALL your relevant manual pages *BEFORE* changing
235    * these ioctl's. There are quite a few variations on them,
236    * as can be seen by the PCS one. They are *NOT* all the same.
237    * Heed this well. - Avalon.
238    */
239 #ifdef  NBLOCK_POSIX
240   nonb |= O_NONBLOCK;
241 #endif
242 #ifdef  NBLOCK_BSD
243   nonb |= O_NDELAY;
244 #endif
245 #ifdef  NBLOCK_SYSV
246   /* This portion of code might also apply to NeXT. -LynX */
247   res = 1;
248
249   if (ioctl(fd, FIONBIO, &res) == -1)
250     return 0;
251 #else
252   if ((res = fcntl(fd, F_GETFL, 0)) == -1)
253     return 0;
254   else if (fcntl(fd, F_SETFL, res | nonb) == -1)
255     return 0;
256 #endif
257   return 1;
258 }
259
260
261 /*
262  *  set_sock_opts
263  */
264 int os_set_reuseaddr(int fd)
265 {
266   unsigned int opt = 1;
267   return (0 == setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
268                           (const char*) &opt, sizeof(opt)));
269 }
270
271 int os_set_sockbufs(int fd, unsigned int ssize, unsigned int rsize)
272 {
273   unsigned int sopt = ssize;
274   unsigned int ropt = rsize;
275   return (0 == setsockopt(fd, SOL_SOCKET, SO_RCVBUF,
276                           (const char*) &ropt, sizeof(ropt)) &&
277           0 == setsockopt(fd, SOL_SOCKET, SO_SNDBUF,
278                           (const char*) &sopt, sizeof(sopt)));
279 }
280
281 int os_set_tos(int fd,int tos)
282 {
283 #if defined(IP_TOS) && defined(IPPROTO_IP)
284   unsigned int opt = tos;
285   return (0 == setsockopt(fd, IPPROTO_IP, IP_TOS, &opt, sizeof(opt)));
286 #else
287   return 1;
288 #endif
289 }
290
291 int os_disable_options(int fd)
292 {
293 #if defined(IP_OPTIONS) && defined(IPPROTO_IP)
294   return (0 == setsockopt(fd, IPPROTO_IP, IP_OPTIONS, NULL, 0));
295 #else
296   return 1;
297 #endif
298 }
299
300 /*
301  * Try and find the correct name to use with getrlimit() for setting the max.
302  * number of files allowed to be open by this process.
303  */
304 #ifdef RLIMIT_FDMAX
305 #define RLIMIT_FD_MAX   RLIMIT_FDMAX
306 #else
307 #ifdef RLIMIT_NOFILE
308 #define RLIMIT_FD_MAX RLIMIT_NOFILE
309 #else
310 #ifdef RLIMIT_OPEN_MAX
311 #define RLIMIT_FD_MAX RLIMIT_OPEN_MAX
312 #else
313 #undef RLIMIT_FD_MAX
314 #endif
315 #endif
316 #endif
317
318 int os_set_fdlimit(unsigned int max_descriptors)
319 {
320 #if defined(HAVE_SETRLIMIT) && defined(RLIMIT_FD_MAX)
321   struct rlimit limit;
322
323   if (!getrlimit(RLIMIT_FD_MAX, &limit)) {
324     if (limit.rlim_max < MAXCONNECTIONS)
325       return limit.rlim_max;
326     limit.rlim_cur = limit.rlim_max;    /* make soft limit the max */
327     return setrlimit(RLIMIT_FD_MAX, &limit);
328   }
329 #endif /* defined(HAVE_SETRLIMIT) && defined(RLIMIT_FD_MAX) */
330   return 0;
331 }
332
333 /*
334  * os_recv_nonb - non blocking read of a connection
335  * returns:
336  *  1  if data was read or socket is blocked (recoverable error)
337  *    count_out > 0 if data was read
338  *
339  *  0  if socket closed from other end
340  *  -1 if an unrecoverable error occurred
341  */
342 IOResult os_recv_nonb(int fd, char* buf, unsigned int length,
343                  unsigned int* count_out)
344 {
345   int res;
346   assert(0 != buf);
347   assert(0 != count_out);
348   *count_out = 0;
349   errno = 0;
350
351   if (0 < (res = recv(fd, buf, length, 0))) {
352     *count_out = (unsigned) res;
353     return IO_SUCCESS;
354   }
355   else if (res < 0) {
356     if (EWOULDBLOCK == errno || EAGAIN == errno
357 #ifdef ENOMEM
358         || ENOMEM == errno
359 #endif
360 #ifdef ENOBUFS
361         || ENOBUFS == errno
362 #endif
363         )
364       return IO_BLOCKED;
365     else
366       return IO_FAILURE;
367   }
368   /*
369    * 0   == client closed the connection
370    * < 1 == error
371    */
372   return IO_FAILURE;
373 }
374
375 IOResult os_recvfrom_nonb(int fd, char* buf, unsigned int length,
376                           unsigned int* length_out,
377                           struct irc_sockaddr* addr_out)
378 {
379   struct sockaddr_native addr;
380   unsigned int len = sizeof(addr);
381   int    res;
382   assert(0 != buf);
383   assert(0 != length_out);
384   assert(0 != addr_out);
385   errno = 0;
386   *length_out = 0;
387
388   res = recvfrom(fd, buf, length, 0, (struct sockaddr*) &addr, &len);
389   if (-1 == res) {
390     if (EWOULDBLOCK == errno || ENOMEM == errno
391 #ifdef ENOMEM
392         || ENOMEM == errno
393 #endif
394 #ifdef ENOBUFS
395         || ENOBUFS == errno
396 #endif
397         )
398       return IO_BLOCKED;
399     return IO_FAILURE;
400   }
401   sockaddr_to_irc(&addr, addr_out);
402   *length_out = res;
403   return IO_SUCCESS;
404 }
405
406 /*
407  * os_sendto_nonb - non blocking send to a UDP socket
408  * returns:
409  *  1  if data was written, *count_out contains number of bytes
410  *  0  if sendto call blocked
411  *  -1 if an unrecoverable error occurred
412  */
413 IOResult os_sendto_nonb(int fd, const char* buf, unsigned int length,
414                         unsigned int* count_out, unsigned int flags,
415                         const struct irc_sockaddr* peer)
416 {
417   struct sockaddr_native addr;
418   int res;
419   assert(0 != buf);
420   if (count_out)
421     *count_out = 0;
422   errno = 0;
423
424   sockaddr_from_irc(&addr, peer, 1);
425   if (-1 < (res = sendto(fd, buf, length, flags, (struct sockaddr*)&addr, sizeof(addr)))) {
426     if (count_out)
427       *count_out = (unsigned) res;
428     return IO_SUCCESS;
429   }
430   else if (EWOULDBLOCK == errno || EAGAIN == errno
431 #ifdef ENOMEM
432            || ENOMEM == errno
433 #endif
434 #ifdef ENOBUFS
435            || ENOBUFS == errno
436 #endif
437       )
438     return IO_BLOCKED;
439   return IO_FAILURE;
440 }
441
442 /*
443  * os_send_nonb - non blocking read of a connection
444  * returns:
445  *  1  if data was written
446  *    count_out contains amount written
447  *
448  *  0  if write call blocked, recoverable error
449  *  -1 if an unrecoverable error occurred
450  */
451 IOResult os_send_nonb(int fd, const char* buf, unsigned int length, 
452                  unsigned int* count_out)
453 {
454   int res;
455   assert(0 != buf);
456   assert(0 != count_out);
457   *count_out = 0;
458   errno = 0;
459
460   if (-1 < (res = send(fd, buf, length, 0))) {
461     *count_out = (unsigned) res;
462     return IO_SUCCESS;
463   }
464   else if (EWOULDBLOCK == errno || EAGAIN == errno
465 #ifdef ENOMEM
466            || ENOMEM == errno
467 #endif
468 #ifdef ENOBUFS
469            || ENOBUFS == errno
470 #endif
471       )
472     return IO_BLOCKED;
473   return IO_FAILURE;
474 }
475
476 IOResult os_sendv_nonb(int fd, struct MsgQ* buf, unsigned int* count_in,
477                        unsigned int* count_out)
478 {
479   int res;
480   int count;
481   struct iovec iov[IOV_MAX];
482
483   assert(0 != buf);
484   assert(0 != count_in);
485   assert(0 != count_out);
486
487   *count_in = 0;
488   *count_out = 0;
489   errno = 0;
490
491   count = msgq_mapiov(buf, iov, IOV_MAX, count_in);
492
493   if (-1 < (res = writev(fd, iov, count))) {
494     *count_out = (unsigned) res;
495     return IO_SUCCESS;
496   }
497   else if (EWOULDBLOCK == errno || EAGAIN == errno
498 #ifdef ENOMEM
499            || ENOMEM == errno
500 #endif
501 #ifdef ENOBUFS
502            || ENOBUFS == errno
503 #endif
504       )
505     return IO_BLOCKED;
506
507   return IO_FAILURE;
508 }
509
510 int os_socket(const struct irc_sockaddr* local, int type, const char* port_name)
511 {
512   int family, fd;
513 #ifdef IPV6
514   family = AF_INET6;
515 #else
516   family = AF_INET;
517 #endif
518   fd = socket(family, type, 0);
519   if (fd < 0) {
520     report_error(SOCKET_ERROR_MSG, port_name, errno);
521     return -1;
522   }
523   if (fd > MAXCLIENTS - 1) {
524     report_error(CONNLIMIT_ERROR_MSG, port_name, 0);
525     close(fd);
526     return -1;
527   }
528   if (!os_set_reuseaddr(fd)) {
529     report_error(REUSEADDR_ERROR_MSG, port_name, errno);
530     close(fd);
531     return -1;
532   }
533   if (!os_set_nonblocking(fd)) {
534     report_error(NONB_ERROR_MSG, port_name, errno);
535     close(fd);
536     return -1;
537   }
538   if (local) {
539     struct sockaddr_native addr;
540     sockaddr_from_irc(&addr, local, 1);
541     if (bind(fd, (struct sockaddr*)&addr, sizeof(addr))) {
542       report_error(BIND_ERROR_MSG, port_name, errno);
543       close(fd);
544       return -1;
545     }
546   }
547   return fd;
548 }
549
550 int os_accept(int fd, struct irc_sockaddr* peer)
551 {
552   struct sockaddr_native addr;
553   socklen_t addrlen;
554   int new_fd;
555
556   addrlen = sizeof(addr);
557   new_fd = accept(fd, (struct sockaddr*)&addr, &addrlen);
558   if (new_fd < 0)
559     memset(peer, 0, sizeof(*peer));
560   else
561     sockaddr_to_irc(&addr, peer);
562   return new_fd;
563 }
564
565 IOResult os_connect_nonb(int fd, const struct irc_sockaddr* sin)
566 {
567   struct sockaddr_native addr;
568   sockaddr_from_irc(&addr, sin, 1);
569   if (connect(fd, (struct sockaddr*) &addr, sizeof(addr)))
570     return (errno == EINPROGRESS) ? IO_BLOCKED : IO_FAILURE;
571   return IO_SUCCESS;
572 }
573
574 int os_get_sockname(int fd, struct irc_sockaddr* sin_out)
575 {
576   struct sockaddr_native addr;
577   unsigned int len = sizeof(addr);
578
579   assert(0 != sin_out);
580   if (getsockname(fd, (struct sockaddr*) &addr, &len))
581     return 0;
582   sockaddr_to_irc(&addr, sin_out);
583   return 1;
584 }
585
586 int os_get_peername(int fd, struct irc_sockaddr* sin_out)
587 {
588   struct sockaddr_native addr;
589   unsigned int len = sizeof(addr);
590
591   assert(0 != sin_out);
592   if (getpeername(fd, (struct sockaddr*) &addr, &len))
593     return 0;
594   sockaddr_to_irc(&addr, sin_out);
595   return 1;
596 }
597
598 int os_set_listen(int fd, int backlog)
599 {
600   return (0 == listen(fd, backlog));
601 }