]>
Commit | Line | Data |
---|---|---|
189935b1 | 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 | /** @file | |
20 | * @brief Implementation of OS-dependent operations. | |
9f8856e9 | 21 | * @version $Id: os_generic.c,v 1.23.2.4 2006/03/14 03:45:52 entrope Exp $ |
189935b1 | 22 | */ |
23 | #include "config.h" | |
24 | ||
052b069e | 25 | #ifdef IRCU_SOLARIS |
26 | /* Solaris requires C99 support for SUSv3, but C99 support breaks other | |
27 | * parts of the build. So fall back to SUSv2, but request IPv6 support | |
28 | * by defining __EXTENSIONS__. | |
29 | */ | |
30 | #define _XOPEN_SOURCE 500 | |
31 | #define __EXTENSIONS__ 1 | |
9f8856e9 | 32 | #elif defined(__FreeBSD__) && __FreeBSD__ >= 5 |
33 | /* FreeBSD 6.0 requires SUSv3 to support IPv6 -- but if you ask for | |
34 | * that specifically (by defining _XOPEN_SOURCE to anything at all), | |
35 | * they cleverly hide IPPROTO_IPV6. If you don't ask for anything, | |
36 | * they give you everything. | |
052b069e | 37 | */ |
9f8856e9 | 38 | #else |
052b069e | 39 | #define _XOPEN_SOURCE 600 |
40 | #endif | |
189935b1 | 41 | |
42 | #include "ircd_osdep.h" | |
43 | #include "msgq.h" | |
44 | #include "ircd_log.h" | |
45 | #include "res.h" | |
46 | #include "s_bsd.h" | |
47 | #include "sys.h" | |
48 | ||
49 | /* Include file dependency notes: | |
50 | * FreeBSD requires struct timeval from sys/time.h before struct | |
51 | * rusage in sys/resource.h. | |
52 | * Solaris requires sys/time.h before struct rusage (indirectly) in | |
53 | * netinet/in.h. | |
54 | */ | |
55 | /* #include <assert.h> -- Now using assert in ircd_log.h */ | |
56 | #include <errno.h> | |
57 | #include <fcntl.h> | |
58 | #include <limits.h> | |
59 | #include <stdio.h> | |
60 | #include <string.h> | |
61 | #include <sys/ioctl.h> | |
62 | #include <sys/types.h> | |
63 | #include <sys/time.h> | |
64 | #include <netinet/in.h> | |
65 | #include <sys/resource.h> | |
66 | #include <sys/socket.h> | |
67 | #include <sys/uio.h> | |
68 | ||
69 | #if HAVE_SYS_PARAM_H | |
70 | #include <sys/param.h> | |
71 | #endif | |
72 | ||
73 | #if HAVE_UNISTD_H | |
74 | #include <unistd.h> | |
75 | #endif | |
76 | ||
9f8856e9 | 77 | #if defined(IPV6_BINDV6ONLY) &&!defined(IPV6_V6ONLY) |
78 | # define IPV6_V6ONLY IPV6_BINDV6ONLY | |
79 | #endif | |
80 | ||
189935b1 | 81 | #ifndef IOV_MAX |
82 | #define IOV_MAX 16 /**< minimum required length of an iovec array */ | |
83 | #endif | |
84 | ||
85 | #ifdef HPUX | |
86 | #include <sys/syscall.h> | |
87 | #define getrusage(a,b) syscall(SYS_GETRUSAGE, a, b) | |
88 | #endif | |
89 | ||
9f8856e9 | 90 | static int is_blocked(int error) |
91 | { | |
92 | return EWOULDBLOCK == error | |
93 | #ifdef ENOMEM | |
94 | || ENOMEM == error | |
95 | #endif | |
96 | #ifdef ENOBUFS | |
97 | || ENOBUFS == error | |
98 | #endif | |
99 | || EAGAIN == error; | |
100 | } | |
189935b1 | 101 | |
102 | static void sockaddr_in_to_irc(const struct sockaddr_in *v4, | |
103 | struct irc_sockaddr *irc) | |
104 | { | |
105 | memset(&irc->addr, 0, 5*sizeof(int16_t)); | |
106 | irc->addr.in6_16[5] = 0xffff; | |
107 | memcpy(&irc->addr.in6_16[6], &v4->sin_addr, sizeof(v4->sin_addr)); | |
108 | irc->port = ntohs(v4->sin_port); | |
109 | } | |
110 | ||
111 | ||
112 | #ifdef IPV6 | |
113 | /** Native socket address type. */ | |
114 | #define sockaddr_native sockaddr_in6 | |
115 | /** Field name inside sockaddr_native to find address family. */ | |
116 | #define sn_family sin6_family | |
117 | ||
118 | /** Convert native socket address to IRC format. | |
119 | * @param[in] v6 Native socket address. | |
120 | * @param[out] irc IRC format socket address. | |
121 | */ | |
122 | void sockaddr_to_irc(const struct sockaddr_in6 *v6, struct irc_sockaddr *irc) | |
123 | { | |
124 | if (v6->sin6_family == AF_INET6) { | |
125 | memcpy(&irc->addr.in6_16[0], &v6->sin6_addr, sizeof(v6->sin6_addr)); | |
126 | irc->port = ntohs(v6->sin6_port); | |
127 | } | |
128 | else if (v6->sin6_family == AF_INET) { | |
129 | sockaddr_in_to_irc((struct sockaddr_in *)v6, irc); | |
130 | } | |
131 | else assert(0 && "Unhandled native address family"); | |
132 | } | |
133 | ||
134 | /** Convert IRC socket address to native format. | |
135 | * @param[out] v6 Native socket address. | |
136 | * @param[in] irc IRC socket address. | |
137 | * @param[in] compat_fd If non-negative, an FD specifying address family. | |
138 | * @return Length of address written to \a v6. | |
139 | */ | |
9f8856e9 | 140 | int sockaddr_from_irc(struct sockaddr_in6 *v6, const struct irc_sockaddr *irc, int compat_fd, int family) |
189935b1 | 141 | { |
142 | struct sockaddr_in6 sin6; | |
143 | socklen_t slen; | |
189935b1 | 144 | |
145 | assert(irc != 0); | |
146 | slen = sizeof(sin6); | |
9f8856e9 | 147 | if (family) { |
148 | /* accept whatever user specified */ | |
149 | } else if ((0 <= compat_fd) | |
189935b1 | 150 | && (0 == getsockname(compat_fd, (struct sockaddr*)&sin6, &slen))) |
151 | family = sin6.sin6_family; | |
152 | else if ((irc == &VirtualHost_v4) || irc_in_addr_is_ipv4(&irc->addr)) | |
153 | family = AF_INET; | |
154 | else | |
155 | family = AF_INET6; | |
156 | ||
157 | memset(v6, 0, sizeof(*v6)); | |
158 | if (family == AF_INET) { | |
159 | struct sockaddr_in *v4 = (struct sockaddr_in*)v6; | |
160 | v4->sin_family = AF_INET; | |
161 | memcpy(&v4->sin_addr, &irc->addr.in6_16[6], sizeof(v4->sin_addr)); | |
162 | v4->sin_port = htons(irc->port); | |
163 | return sizeof(*v4); | |
164 | } | |
165 | else { | |
166 | v6->sin6_family = AF_INET6; | |
167 | memcpy(&v6->sin6_addr, &irc->addr.in6_16[0], sizeof(v6->sin6_addr)); | |
168 | v6->sin6_port = htons(irc->port); | |
169 | return sizeof(*v6); | |
170 | } | |
171 | } | |
172 | ||
173 | #else | |
174 | #define sockaddr_native sockaddr_in | |
175 | #define sn_family sin_family | |
176 | #define sockaddr_to_irc sockaddr_in_to_irc | |
177 | ||
9f8856e9 | 178 | int sockaddr_from_irc(struct sockaddr_in *v4, const struct irc_sockaddr *irc, int compat_fd, int family) |
189935b1 | 179 | { |
180 | assert(irc != 0); | |
181 | v4->sin_family = AF_INET; | |
182 | if (irc) { | |
183 | 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)); | |
184 | memcpy(&v4->sin_addr, &irc->addr.in6_16[6], sizeof(v4->sin_addr)); | |
185 | v4->sin_port = htons(irc->port); | |
186 | } else{ | |
187 | memset(&v4, 0, sizeof(v4)); | |
188 | } | |
9f8856e9 | 189 | (void)compat_fd; (void)family; |
189935b1 | 190 | return sizeof(*v4); |
191 | } | |
192 | ||
193 | #endif | |
194 | ||
195 | #ifdef DEBUGMODE | |
196 | /** Send resource usage information to an enumerator function. | |
197 | * @param[in] cptr Client requesting information. | |
198 | * @param[in] uptime Wall time in seconds since the server started. | |
199 | * @param[in] enumerator Function to call to send a line to \a cptr. | |
200 | * @return Zero if some usage reports could not be sent, non-zero on success. | |
201 | */ | |
202 | int os_get_rusage(struct Client *cptr, int uptime, EnumFn enumerator) | |
203 | { | |
204 | #ifdef HAVE_GETRUSAGE | |
205 | char buf[256]; | |
206 | struct rusage rus; | |
207 | time_t secs; | |
208 | ||
209 | #ifdef hz | |
210 | # define hzz hz | |
211 | #else | |
212 | # ifdef HZ | |
213 | # define hzz HZ | |
214 | # else | |
215 | int hzz = 1; | |
216 | # ifdef HPUX | |
217 | hzz = sysconf(_SC_CLK_TCK); | |
218 | # endif | |
219 | #endif | |
220 | #endif | |
221 | ||
222 | assert(0 != enumerator); | |
223 | if (getrusage(RUSAGE_SELF, &rus) == -1) | |
224 | return 0; | |
225 | ||
226 | secs = rus.ru_utime.tv_sec + rus.ru_stime.tv_sec; | |
227 | if (secs == 0) | |
228 | secs = 1; | |
229 | ||
230 | sprintf(buf, "CPU Secs %ld:%ld User %ld:%ld System %ld:%ld", | |
231 | (long)(secs / 60), (long)(secs % 60), | |
232 | rus.ru_utime.tv_sec / 60, rus.ru_utime.tv_sec % 60, | |
233 | rus.ru_stime.tv_sec / 60, rus.ru_stime.tv_sec % 60); | |
234 | (*enumerator)(cptr, buf); | |
235 | ||
236 | sprintf(buf, "RSS %ld ShMem %ld Data %ld Stack %ld", | |
237 | rus.ru_maxrss, | |
238 | rus.ru_ixrss / (uptime * hzz), rus.ru_idrss / (uptime * hzz), | |
239 | rus.ru_isrss / (uptime * hzz)); | |
240 | (*enumerator)(cptr, buf); | |
241 | ||
242 | sprintf(buf, "Swaps %ld Reclaims %ld Faults %ld", | |
243 | rus.ru_nswap, rus.ru_minflt, rus.ru_majflt); | |
244 | (*enumerator)(cptr, buf); | |
245 | ||
246 | sprintf(buf, "Block in %ld out %ld", rus.ru_inblock, rus.ru_oublock); | |
247 | (*enumerator)(cptr, buf); | |
248 | ||
249 | sprintf(buf, "Msg Rcv %ld Send %ld", rus.ru_msgrcv, rus.ru_msgsnd); | |
250 | (*enumerator)(cptr, buf); | |
251 | ||
252 | sprintf(buf, "Signals %ld Context Vol. %ld Invol %ld", | |
253 | rus.ru_nsignals, rus.ru_nvcsw, rus.ru_nivcsw); | |
254 | (*enumerator)(cptr, buf); | |
255 | ||
256 | #else /* HAVE_GETRUSAGE */ | |
257 | #if HAVE_TIMES | |
258 | char buf[256]; | |
259 | struct tms tmsbuf; | |
260 | time_t secs, mins; | |
261 | int hzz = 1, ticpermin; | |
262 | int umin, smin, usec, ssec; | |
263 | ||
264 | assert(0 != enumerator); | |
265 | #ifdef HPUX | |
266 | hzz = sysconf(_SC_CLK_TCK); | |
267 | #endif | |
268 | ticpermin = hzz * 60; | |
269 | ||
270 | umin = tmsbuf.tms_utime / ticpermin; | |
271 | usec = (tmsbuf.tms_utime % ticpermin) / (float)hzz; | |
272 | smin = tmsbuf.tms_stime / ticpermin; | |
273 | ssec = (tmsbuf.tms_stime % ticpermin) / (float)hzz; | |
274 | secs = usec + ssec; | |
275 | mins = (secs / 60) + umin + smin; | |
276 | secs %= hzz; | |
277 | ||
278 | if (times(&tmsbuf) == -1) | |
279 | return 0; | |
280 | secs = tmsbuf.tms_utime + tmsbuf.tms_stime; | |
281 | ||
282 | sprintf(buf, "CPU Secs %d:%d User %d:%d System %d:%d", | |
283 | mins, secs, umin, usec, smin, ssec); | |
284 | (*enumerator)(cptr, buf); | |
285 | #endif /* HAVE_TIMES */ | |
286 | #endif /* HAVE_GETRUSAGE */ | |
287 | return 1; | |
288 | } | |
289 | #endif | |
290 | ||
291 | /** Look up the most recent socket error for a socket file descriptor. | |
292 | * @param[in] fd File descriptor to check. | |
293 | * @return Error code from the socket, or 0 if the OS does not support this. | |
294 | */ | |
295 | int os_get_sockerr(int fd) | |
296 | { | |
297 | int err = 0; | |
298 | #if defined(SO_ERROR) | |
299 | unsigned int len = sizeof(err); | |
300 | getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &len); | |
301 | #endif | |
302 | return err; | |
303 | } | |
304 | ||
305 | /** Set a file descriptor to non-blocking mode. | |
306 | * @param[in] fd %Socket file descriptor. | |
307 | * @return Non-zero on success, or zero on failure. | |
308 | */ | |
309 | int os_set_nonblocking(int fd) | |
310 | { | |
311 | int res; | |
312 | #ifndef NBLOCK_SYSV | |
313 | int nonb = 0; | |
314 | #endif | |
315 | ||
316 | /* | |
317 | * NOTE: consult ALL your relevant manual pages *BEFORE* changing | |
318 | * these ioctl's. There are quite a few variations on them, | |
319 | * as can be seen by the PCS one. They are *NOT* all the same. | |
320 | * Heed this well. - Avalon. | |
321 | */ | |
322 | #ifdef NBLOCK_POSIX | |
323 | nonb |= O_NONBLOCK; | |
324 | #endif | |
325 | #ifdef NBLOCK_BSD | |
326 | nonb |= O_NDELAY; | |
327 | #endif | |
328 | #ifdef NBLOCK_SYSV | |
329 | /* This portion of code might also apply to NeXT. -LynX */ | |
330 | res = 1; | |
331 | ||
332 | if (ioctl(fd, FIONBIO, &res) == -1) | |
333 | return 0; | |
334 | #else | |
335 | if ((res = fcntl(fd, F_GETFL, 0)) == -1) | |
336 | return 0; | |
337 | else if (fcntl(fd, F_SETFL, res | nonb) == -1) | |
338 | return 0; | |
339 | #endif | |
340 | return 1; | |
341 | } | |
342 | ||
343 | /** Mark a socket's address as reusable. | |
344 | * @param[in] fd %Socket file descriptor to manipulate. | |
345 | * @return Non-zero on success, or zero on failure. | |
346 | */ | |
347 | int os_set_reuseaddr(int fd) | |
348 | { | |
349 | unsigned int opt = 1; | |
350 | return (0 == setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, | |
351 | (const char*) &opt, sizeof(opt))); | |
352 | } | |
353 | ||
354 | /** Set a socket's send and receive buffer sizes. | |
355 | * @param[in] fd %Socket file descriptor to manipulate. | |
356 | * @param[in] ssize New send buffer size. | |
357 | * @param[in] rsize New receive buffer size. | |
358 | * @return Non-zero on success, or zero on failure. | |
359 | */ | |
360 | int os_set_sockbufs(int fd, unsigned int ssize, unsigned int rsize) | |
361 | { | |
362 | unsigned int sopt = ssize; | |
363 | unsigned int ropt = rsize; | |
364 | return (0 == setsockopt(fd, SOL_SOCKET, SO_RCVBUF, | |
365 | (const char*) &ropt, sizeof(ropt)) && | |
366 | 0 == setsockopt(fd, SOL_SOCKET, SO_SNDBUF, | |
367 | (const char*) &sopt, sizeof(sopt))); | |
368 | } | |
369 | ||
370 | /** Set a socket's "type of service" value. | |
371 | * @param[in] fd %Socket file descriptor to manipulate. | |
372 | * @param[in] tos New type of service value to use. | |
373 | * @return Non-zero on success, or zero on failure. | |
374 | */ | |
375 | int os_set_tos(int fd,int tos) | |
376 | { | |
377 | #if defined(IP_TOS) && defined(IPPROTO_IP) | |
378 | unsigned int opt = tos; | |
379 | return (0 == setsockopt(fd, IPPROTO_IP, IP_TOS, &opt, sizeof(opt))); | |
380 | #else | |
381 | return 1; | |
382 | #endif | |
383 | } | |
384 | ||
385 | /** Disable IP options on a socket. | |
386 | * @param[in] fd %Socket file descriptor to manipulate. | |
387 | * @return Non-zero on success, or zero on failure. | |
388 | */ | |
389 | int os_disable_options(int fd) | |
390 | { | |
391 | #if defined(IP_OPTIONS) && defined(IPPROTO_IP) | |
392 | return (0 == setsockopt(fd, IPPROTO_IP, IP_OPTIONS, NULL, 0)); | |
393 | #else | |
394 | return 1; | |
395 | #endif | |
396 | } | |
397 | ||
398 | /* | |
399 | * Try and find the correct name to use with getrlimit() for setting the max. | |
400 | * number of files allowed to be open by this process. | |
401 | */ | |
402 | #ifdef RLIMIT_FDMAX | |
403 | #define RLIMIT_FD_MAX RLIMIT_FDMAX | |
404 | #else | |
405 | #ifdef RLIMIT_NOFILE | |
406 | #define RLIMIT_FD_MAX RLIMIT_NOFILE | |
407 | #else | |
408 | #ifdef RLIMIT_OPEN_MAX | |
409 | #define RLIMIT_FD_MAX RLIMIT_OPEN_MAX | |
410 | #else | |
411 | #undef RLIMIT_FD_MAX | |
412 | #endif | |
413 | #endif | |
414 | #endif | |
415 | ||
416 | /** Set file descriptor limit for the process. | |
417 | * @param[in] max_descriptors Ideal number of file descriptors. | |
418 | * @return Zero on success; -1 on error; positive number of possible | |
419 | * file descriptors if \a max_descriptors is too high. | |
420 | */ | |
421 | int os_set_fdlimit(unsigned int max_descriptors) | |
422 | { | |
423 | #if defined(HAVE_SETRLIMIT) && defined(RLIMIT_FD_MAX) | |
424 | struct rlimit limit; | |
425 | ||
426 | if (!getrlimit(RLIMIT_FD_MAX, &limit)) { | |
427 | if (limit.rlim_max < max_descriptors) | |
428 | return limit.rlim_max; | |
429 | limit.rlim_cur = limit.rlim_max; /* make soft limit the max */ | |
430 | return setrlimit(RLIMIT_FD_MAX, &limit); | |
431 | } | |
432 | #endif /* defined(HAVE_SETRLIMIT) && defined(RLIMIT_FD_MAX) */ | |
433 | return 0; | |
434 | } | |
435 | ||
436 | /** Attempt to read from a non-blocking socket. | |
437 | * @param[in] fd File descriptor to read from. | |
438 | * @param[out] buf Output buffer to read into. | |
439 | * @param[in] length Number of bytes to read. | |
440 | * @param[out] count_out Receives number of bytes actually read. | |
441 | * @return An IOResult value indicating status. | |
442 | */ | |
443 | IOResult os_recv_nonb(int fd, char* buf, unsigned int length, | |
444 | unsigned int* count_out) | |
445 | { | |
446 | int res; | |
447 | assert(0 != buf); | |
448 | assert(0 != count_out); | |
189935b1 | 449 | |
450 | if (0 < (res = recv(fd, buf, length, 0))) { | |
451 | *count_out = (unsigned) res; | |
452 | return IO_SUCCESS; | |
9f8856e9 | 453 | } else { |
454 | *count_out = 0; | |
455 | return (res < 0) && is_blocked(errno) ? IO_BLOCKED : IO_FAILURE; | |
189935b1 | 456 | } |
189935b1 | 457 | } |
458 | ||
459 | /** Attempt to read from a non-blocking UDP socket. | |
460 | * @param[in] fd File descriptor to read from. | |
461 | * @param[out] buf Output buffer to read into. | |
462 | * @param[in] length Number of bytes to read. | |
463 | * @param[out] length_out Receives number of bytes actually read. | |
464 | * @param[out] addr_out Peer address that sent the message. | |
465 | * @return An IOResult value indicating status. | |
466 | */ | |
467 | IOResult os_recvfrom_nonb(int fd, char* buf, unsigned int length, | |
468 | unsigned int* length_out, | |
469 | struct irc_sockaddr* addr_out) | |
470 | { | |
471 | struct sockaddr_native addr; | |
472 | unsigned int len = sizeof(addr); | |
473 | int res; | |
474 | assert(0 != buf); | |
475 | assert(0 != length_out); | |
476 | assert(0 != addr_out); | |
189935b1 | 477 | |
478 | res = recvfrom(fd, buf, length, 0, (struct sockaddr*) &addr, &len); | |
9f8856e9 | 479 | if (-1 < res) { |
480 | sockaddr_to_irc(&addr, addr_out); | |
481 | *length_out = res; | |
482 | return IO_SUCCESS; | |
483 | } else { | |
484 | *length_out = 0; | |
485 | return is_blocked(errno) ? IO_BLOCKED : IO_FAILURE; | |
189935b1 | 486 | } |
189935b1 | 487 | } |
488 | ||
489 | /** Attempt to write on a non-blocking UDP socket. | |
490 | * @param[in] fd File descriptor to write to. | |
491 | * @param[in] buf Output buffer to send from. | |
492 | * @param[in] length Number of bytes to write. | |
493 | * @param[out] count_out Receives number of bytes actually written. | |
494 | * @param[in] flags Flags for call to sendto(). | |
495 | * @param[in] peer Destination address of the message. | |
496 | * @return An IOResult value indicating status. | |
497 | */ | |
498 | IOResult os_sendto_nonb(int fd, const char* buf, unsigned int length, | |
499 | unsigned int* count_out, unsigned int flags, | |
500 | const struct irc_sockaddr* peer) | |
501 | { | |
502 | struct sockaddr_native addr; | |
503 | int res, size; | |
504 | assert(0 != buf); | |
189935b1 | 505 | |
9f8856e9 | 506 | size = sockaddr_from_irc(&addr, peer, fd, 0); |
189935b1 | 507 | assert((addr.sn_family == AF_INET) == irc_in_addr_is_ipv4(&peer->addr)); |
508 | if (-1 < (res = sendto(fd, buf, length, flags, (struct sockaddr*)&addr, size))) { | |
509 | if (count_out) | |
510 | *count_out = (unsigned) res; | |
511 | return IO_SUCCESS; | |
9f8856e9 | 512 | } else { |
513 | if (count_out) | |
514 | *count_out = 0; | |
515 | return is_blocked(errno) ? IO_BLOCKED : IO_FAILURE; | |
189935b1 | 516 | } |
189935b1 | 517 | } |
518 | ||
519 | /** Attempt to write on a connected socket. | |
520 | * @param[in] fd File descriptor to write to. | |
521 | * @param[in] buf Output buffer to send from. | |
522 | * @param[in] length Number of bytes to write. | |
523 | * @param[out] count_out Receives number of bytes actually written. | |
524 | * @return An IOResult value indicating status. | |
525 | */ | |
526 | IOResult os_send_nonb(int fd, const char* buf, unsigned int length, | |
527 | unsigned int* count_out) | |
528 | { | |
529 | int res; | |
530 | assert(0 != buf); | |
531 | assert(0 != count_out); | |
189935b1 | 532 | |
533 | if (-1 < (res = send(fd, buf, length, 0))) { | |
534 | *count_out = (unsigned) res; | |
535 | return IO_SUCCESS; | |
9f8856e9 | 536 | } else { |
537 | *count_out = 0; | |
538 | return is_blocked(errno) ? IO_BLOCKED : IO_FAILURE; | |
189935b1 | 539 | } |
189935b1 | 540 | } |
541 | ||
542 | /** Attempt a vectored write on a connected socket. | |
543 | * @param[in] fd File descriptor to write to. | |
544 | * @param[in] buf Message queue to send from. | |
545 | * @param[out] count_in Number of bytes mapped from \a buf. | |
546 | * @param[out] count_out Receives number of bytes actually written. | |
547 | * @return An IOResult value indicating status. | |
548 | */ | |
549 | IOResult os_sendv_nonb(int fd, struct MsgQ* buf, unsigned int* count_in, | |
550 | unsigned int* count_out) | |
551 | { | |
552 | int res; | |
553 | int count; | |
554 | struct iovec iov[IOV_MAX]; | |
555 | ||
556 | assert(0 != buf); | |
557 | assert(0 != count_in); | |
558 | assert(0 != count_out); | |
559 | ||
560 | *count_in = 0; | |
189935b1 | 561 | count = msgq_mapiov(buf, iov, IOV_MAX, count_in); |
562 | ||
563 | if (-1 < (res = writev(fd, iov, count))) { | |
564 | *count_out = (unsigned) res; | |
565 | return IO_SUCCESS; | |
9f8856e9 | 566 | } else { |
567 | *count_out = 0; | |
568 | return is_blocked(errno) ? IO_BLOCKED : IO_FAILURE; | |
189935b1 | 569 | } |
189935b1 | 570 | } |
571 | ||
572 | /** Open a TCP or UDP socket on a particular address. | |
573 | * @param[in] local Local address to bind to. | |
574 | * @param[in] type SOCK_STREAM or SOCK_DGRAM. | |
575 | * @param[in] port_name Port name (used in error diagnostics). | |
9f8856e9 | 576 | * @param[in] family A specific address family to use, or 0 for automatic. |
189935b1 | 577 | * @return Bound descriptor, or -1 on error. |
578 | */ | |
9f8856e9 | 579 | int os_socket(const struct irc_sockaddr* local, int type, const char* port_name, int family) |
189935b1 | 580 | { |
581 | struct sockaddr_native addr; | |
582 | int size, fd; | |
583 | ||
584 | assert(local != 0); | |
9f8856e9 | 585 | size = sockaddr_from_irc(&addr, local, -1, family); |
189935b1 | 586 | fd = socket(addr.sn_family, type, 0); |
587 | if (fd < 0) { | |
588 | report_error(SOCKET_ERROR_MSG, port_name, errno); | |
589 | return -1; | |
590 | } | |
591 | if (fd > MAXCLIENTS - 1) { | |
592 | report_error(CONNLIMIT_ERROR_MSG, port_name, 0); | |
593 | close(fd); | |
594 | return -1; | |
595 | } | |
596 | if (!os_set_reuseaddr(fd)) { | |
597 | report_error(REUSEADDR_ERROR_MSG, port_name, errno); | |
598 | close(fd); | |
599 | return -1; | |
600 | } | |
601 | if (!os_set_nonblocking(fd)) { | |
602 | report_error(NONB_ERROR_MSG, port_name, errno); | |
603 | close(fd); | |
604 | return -1; | |
605 | } | |
606 | if (local) { | |
9f8856e9 | 607 | #if defined(IPV6_V6ONLY) |
608 | int on = 0; | |
609 | if (family == 0 && irc_in_addr_unspec(&local->addr)) | |
610 | setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); | |
611 | #endif | |
189935b1 | 612 | if (bind(fd, (struct sockaddr*)&addr, size)) { |
613 | report_error(BIND_ERROR_MSG, port_name, errno); | |
614 | close(fd); | |
615 | return -1; | |
616 | } | |
617 | } | |
618 | return fd; | |
619 | } | |
620 | ||
621 | /** Accept a connection on a socket. | |
622 | * @param[in] fd Listening file descriptor. | |
623 | * @param[out] peer Peer address of connection. | |
624 | * @return File descriptor for accepted connection. | |
625 | */ | |
626 | int os_accept(int fd, struct irc_sockaddr* peer) | |
627 | { | |
628 | struct sockaddr_native addr; | |
629 | socklen_t addrlen; | |
630 | int new_fd; | |
631 | ||
632 | addrlen = sizeof(addr); | |
633 | new_fd = accept(fd, (struct sockaddr*)&addr, &addrlen); | |
634 | if (new_fd < 0) | |
635 | memset(peer, 0, sizeof(*peer)); | |
636 | else | |
637 | sockaddr_to_irc(&addr, peer); | |
638 | return new_fd; | |
639 | } | |
640 | ||
641 | /** Start a non-blocking connection. | |
642 | * @param[in] fd Disconnected file descriptor. | |
643 | * @param[in] sin Target address for connection. | |
644 | * @return IOResult code indicating status. | |
645 | */ | |
646 | IOResult os_connect_nonb(int fd, const struct irc_sockaddr* sin) | |
647 | { | |
648 | struct sockaddr_native addr; | |
649 | int size; | |
650 | ||
9f8856e9 | 651 | size = sockaddr_from_irc(&addr, sin, fd, 0); |
652 | if (0 == connect(fd, (struct sockaddr*) &addr, size)) | |
653 | return IO_SUCCESS; | |
654 | else if (errno == EINPROGRESS) | |
655 | return IO_BLOCKED; | |
656 | else | |
657 | return IO_FAILURE; | |
189935b1 | 658 | } |
659 | ||
660 | /** Get local address of a socket. | |
661 | * @param[in] fd File descriptor to operate on. | |
662 | * @param[out] sin_out Receives local socket address. | |
663 | * @return Non-zero on success; zero on error. | |
664 | */ | |
665 | int os_get_sockname(int fd, struct irc_sockaddr* sin_out) | |
666 | { | |
667 | struct sockaddr_native addr; | |
668 | unsigned int len = sizeof(addr); | |
669 | ||
670 | assert(0 != sin_out); | |
671 | if (getsockname(fd, (struct sockaddr*) &addr, &len)) | |
672 | return 0; | |
673 | sockaddr_to_irc(&addr, sin_out); | |
674 | return 1; | |
675 | } | |
676 | ||
677 | /** Get remote address of a socket. | |
678 | * @param[in] fd File descriptor to operate on. | |
679 | * @param[out] sin_out Receives remote socket address. | |
680 | * @return Non-zero on success; zero on error. | |
681 | */ | |
682 | int os_get_peername(int fd, struct irc_sockaddr* sin_out) | |
683 | { | |
684 | struct sockaddr_native addr; | |
685 | unsigned int len = sizeof(addr); | |
686 | ||
687 | assert(0 != sin_out); | |
688 | if (getpeername(fd, (struct sockaddr*) &addr, &len)) | |
689 | return 0; | |
690 | sockaddr_to_irc(&addr, sin_out); | |
691 | return 1; | |
692 | } | |
693 | ||
694 | /** Start listening on a socket. | |
695 | * @param[in] fd Disconnected file descriptor. | |
696 | * @param[in] backlog Maximum number of un-accept()ed connections to keep. | |
697 | * @return Non-zero on success; zero on error. | |
698 | */ | |
699 | int os_set_listen(int fd, int backlog) | |
700 | { | |
701 | return (0 == listen(fd, backlog)); | |
702 | } | |
9f8856e9 | 703 | |
704 | /** Allocate a connected pair of local sockets. | |
705 | * @param[out] sv Array of two file descriptors. | |
706 | * @return Zero on success; non-zero number on error. | |
707 | */ | |
708 | int os_socketpair(int sv[2]) | |
709 | { | |
710 | return socketpair(AF_UNIX, SOCK_STREAM, 0, sv); | |
711 | } |