Annotation of libaitio/src/sock.c, revision 1.9.2.1
1.2 misho 1: /*************************************************************************
2: * (C) 2013 AITNET ltd - Sofia/Bulgaria - <misho@aitnet.org>
3: * by Michael Pounov <misho@elwix.org>
4: *
5: * $Author: misho $
1.9.2.1 ! misho 6: * $Id: sock.c,v 1.9 2013/11/25 11:46:17 misho Exp $
1.2 misho 7: *
8: **************************************************************************
9: The ELWIX and AITNET software is distributed under the following
10: terms:
11:
12: All of the documentation and software included in the ELWIX and AITNET
13: Releases is copyrighted by ELWIX - Sofia/Bulgaria <info@elwix.org>
14:
15: Copyright 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013
16: by Michael Pounov <misho@elwix.org>. All rights reserved.
17:
18: Redistribution and use in source and binary forms, with or without
19: modification, are permitted provided that the following conditions
20: are met:
21: 1. Redistributions of source code must retain the above copyright
22: notice, this list of conditions and the following disclaimer.
23: 2. Redistributions in binary form must reproduce the above copyright
24: notice, this list of conditions and the following disclaimer in the
25: documentation and/or other materials provided with the distribution.
26: 3. All advertising materials mentioning features or use of this software
27: must display the following acknowledgement:
28: This product includes software developed by Michael Pounov <misho@elwix.org>
29: ELWIX - Embedded LightWeight unIX and its contributors.
30: 4. Neither the name of AITNET nor the names of its contributors
31: may be used to endorse or promote products derived from this software
32: without specific prior written permission.
33:
34: THIS SOFTWARE IS PROVIDED BY AITNET AND CONTRIBUTORS ``AS IS'' AND
35: ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
36: IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
37: ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
38: FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
39: DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
40: OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
41: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
42: LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
43: OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44: SUCH DAMAGE.
45: */
46: #include "global.h"
47:
48:
1.5 misho 49: static void *
50: io_closeClient(sched_task_t *task)
51: {
52: sock_cli_t *cli = (sock_cli_t*) TASK_ARG(task);
53: sock_t *s = (sock_t*) cli->cli_parent;
1.7 misho 54: int stat;
1.5 misho 55:
56: pthread_mutex_lock(&s->sock_mtx);
57: TAILQ_REMOVE(&s->sock_cli, cli, cli_node);
58: pthread_mutex_unlock(&s->sock_mtx);
59:
60: schedCancelby(s->sock_root, taskMAX, CRITERIA_ARG, cli, NULL);
61:
1.9 misho 62: if (*cli->cli_name)
63: ioFreePTY(cli->cli_pty, cli->cli_name);
1.8 misho 64:
1.5 misho 65: if (s->sock_type == SOCK_STREAM) {
1.7 misho 66: shutdown(cli->cli_fd, SHUT_RDWR);
67: close(cli->cli_fd);
1.5 misho 68: }
69: AIT_FREE_VAL(&cli->cli_buf[1]);
70: AIT_FREE_VAL(&cli->cli_buf[0]);
71:
72: if (cli->cli_pid > 0) {
1.7 misho 73: kill(cli->cli_pid, SIGKILL);
1.5 misho 74: while (waitpid(cli->cli_pid, &stat, WNOHANG) > 0) {
75: usleep(1000);
1.7 misho 76: kill(cli->cli_pid, SIGKILL);
1.5 misho 77: }
78: }
79:
80: e_free(cli);
81: taskExit(task, NULL);
82: }
83:
84: static void *
85: io_acceptClient(sched_task_t *task)
86: {
87: int c, rlen;
88: sockaddr_t sa;
89: socklen_t salen = sizeof sa.ss;
90: sock_cli_t *cli = NULL;
91: sock_t *s = (sock_t*) TASK_ARG(task);
92:
93: if (s->sock_type == SOCK_STREAM) {
94: if ((c = accept(TASK_FD(task), &sa.sa, &salen)) == -1) {
95: LOGERR;
96: goto end;
97: }
98: } else {
99: if ((rlen = recvfrom(TASK_FD(task),
100: AIT_GET_BUF(&s->sock_buf), AIT_LEN(&s->sock_buf),
101: MSG_PEEK, &sa.sa, &salen)) == -1) {
102: LOGERR;
103: goto end;
104: } else
105: c = TASK_FD(task);
106: }
107:
108: cli = e_malloc(sizeof(sock_cli_t));
109: if (!cli) {
110: io_SetErr(elwix_GetErrno(), "%s", elwix_GetError());
111: if (s->sock_type == SOCK_STREAM)
112: close(c);
113: goto end;
114: } else {
115: memset(cli, 0, sizeof(sock_cli_t));
116: pthread_mutex_lock(&s->sock_mtx);
117: TAILQ_INSERT_TAIL(&s->sock_cli, cli, cli_node);
118: pthread_mutex_unlock(&s->sock_mtx);
119: }
120:
121: cli->cli_parent = TASK_ARG(task);
122: cli->cli_fd = c;
123: cli->cli_func = TASK_DATA(task);
124: memcpy(&cli->cli_addr, &sa, sizeof cli->cli_addr);
125: AIT_SET_BUFSIZ(&cli->cli_buf[0], 0, AIT_LEN(&s->sock_buf));
126: AIT_SET_BUFSIZ(&cli->cli_buf[1], 0, AIT_LEN(&s->sock_buf));
127:
128: schedRead(TASK_ROOT(task), cli->cli_func, cli, cli->cli_fd, TASK_ARG(task), 0);
1.7 misho 129: ioUpdTimerSocket(cli);
1.5 misho 130: end:
131: schedReadSelf(task);
132: taskExit(task, NULL);
133: }
134:
135: static void *
136: io_txNet(sched_task_t *task)
137: {
1.9.2.1 ! misho 138: int wlen, ret, len = TASK_DATLEN(task);
1.5 misho 139: sock_cli_t *cli = TASK_ARG(task);
140: sock_t *s = (sock_t*) cli->cli_parent;
1.9.2.1 ! misho 141: u_char *buf = TASK_DATA(task);
! 142: struct pollfd pfd[1];
1.5 misho 143:
1.9.2.1 ! misho 144: pfd->fd = TASK_FD(task);
! 145: pfd->events = POLLOUT;
! 146: pfd->revents = 0;
! 147: for(; len > 0; len -= wlen, buf += wlen) {
! 148: ioUpdTimerSocket(cli);
! 149:
! 150: if ((ret = poll(pfd, 1, s->sock_timeout.tv_sec * 1000)) < 1 ||
! 151: pfd->revents & (POLLNVAL | POLLERR | POLLHUP)) {
! 152: if (!ret)
! 153: continue;
! 154: schedEvent(TASK_ROOT(task), io_closeClient, cli, 0, NULL, 0);
! 155: break;
! 156: }
1.5 misho 157:
1.9.2.1 ! misho 158: if (s->sock_type == SOCK_STREAM)
! 159: wlen = send(TASK_FD(task), buf, len, 0);
! 160: else
! 161: wlen = sendto(TASK_FD(task), buf, len, 0,
! 162: &cli->cli_addr.sa, cli->cli_addr.sa.sa_len);
! 163: if (wlen < 1) {
! 164: schedEvent(TASK_ROOT(task), io_closeClient, cli, 0, NULL, 0);
! 165: break;
! 166: }
! 167: }
1.5 misho 168:
169: taskExit(task, NULL);
170: }
171:
172: static void *
173: io_txPty(sched_task_t *task)
174: {
175: int wlen;
176: sock_cli_t *cli = TASK_ARG(task);
177:
1.7 misho 178: ioUpdTimerSocket(cli);
1.5 misho 179:
180: wlen = write(TASK_FD(task), TASK_DATA(task), TASK_DATLEN(task));
181: if (wlen < 1)
1.7 misho 182: schedEvent(TASK_ROOT(task), io_closeClient, cli, 0, NULL, 0);
1.5 misho 183:
184: taskExit(task, NULL);
185: }
186:
187: static void *
188: io_rxNet(sched_task_t *task)
189: {
190: int rlen;
191: sock_cli_t *cli = TASK_ARG(task);
192: sock_t *s = (sock_t*) cli->cli_parent;
193: sockaddr_t sa;
194: socklen_t salen = sizeof sa.ss;
195:
1.7 misho 196: ioUpdTimerSocket(cli);
1.5 misho 197:
198: if (s->sock_type == SOCK_STREAM)
199: rlen = recv(TASK_FD(task), AIT_GET_BUF(&cli->cli_buf[0]),
200: AIT_LEN(&cli->cli_buf[0]), 0);
201: else {
202: rlen = recvfrom(TASK_FD(task), AIT_GET_BUF(&cli->cli_buf[0]),
203: AIT_LEN(&cli->cli_buf[0]), 0, &sa.sa, &salen);
204: if (e_addrcmp(&cli->cli_addr, &sa, 42))
205: goto end;
206: }
207: if (rlen < 1)
1.7 misho 208: schedEvent(TASK_ROOT(task), io_closeClient, cli, 0, NULL, 0);
1.5 misho 209: else
210: schedEvent(TASK_ROOT(task), io_txPty, cli, cli->cli_pty,
211: AIT_GET_BUF(&cli->cli_buf[0]), rlen);
212: end:
213: schedReadSelf(task);
214: taskExit(task, NULL);
215: }
216:
217: static void *
218: io_rxPty(sched_task_t *task)
219: {
220: int rlen;
221: sock_cli_t *cli = TASK_ARG(task);
222:
1.7 misho 223: ioUpdTimerSocket(cli);
1.5 misho 224:
225: rlen = read(TASK_FD(task), AIT_GET_BUF(&cli->cli_buf[1]), AIT_LEN(&cli->cli_buf[1]));
226: if (rlen < 1)
1.7 misho 227: schedEvent(TASK_ROOT(task), io_closeClient, cli, 0, NULL, 0);
1.5 misho 228: else
229: schedEvent(TASK_ROOT(task), io_txNet, cli, cli->cli_fd,
230: AIT_GET_BUF(&cli->cli_buf[1]), rlen);
231:
232: schedReadSelf(task);
233: taskExit(task, NULL);
234: }
235:
236: static void *
237: io_bridgeClient(sched_task_t *task)
238: {
239: int c, rlen;
240: pid_t pid;
241: sockaddr_t sa;
242: socklen_t salen = sizeof sa.ss;
243: sock_cli_t *cli = NULL;
244: sock_t *s = (sock_t*) TASK_ARG(task);
245: array_t *args = NULL;
246: char **argv = NULL;
247:
248: if (s->sock_type == SOCK_STREAM) {
249: if ((c = accept(TASK_FD(task), &sa.sa, &salen)) == -1) {
250: LOGERR;
251: goto end;
252: }
253: } else {
254: if ((rlen = recvfrom(TASK_FD(task),
255: AIT_GET_BUF(&s->sock_buf), AIT_LEN(&s->sock_buf),
256: MSG_PEEK, &sa.sa, &salen)) == -1) {
257: LOGERR;
258: goto end;
259: } else
260: c = TASK_FD(task);
261: }
262:
263: cli = e_malloc(sizeof(sock_cli_t));
264: if (!cli) {
265: io_SetErr(elwix_GetErrno(), "%s", elwix_GetError());
266: if (s->sock_type == SOCK_STREAM)
267: close(c);
268: goto end;
269: } else {
270: memset(cli, 0, sizeof(sock_cli_t));
271: pthread_mutex_lock(&s->sock_mtx);
272: TAILQ_INSERT_TAIL(&s->sock_cli, cli, cli_node);
273: pthread_mutex_unlock(&s->sock_mtx);
274: }
275:
276: cli->cli_parent = TASK_ARG(task);
277: cli->cli_fd = c;
278: strlcpy(cli->cli_cmdline, TASK_DATA(task), sizeof cli->cli_cmdline);
279: memcpy(&cli->cli_addr, &sa, sizeof cli->cli_addr);
280: AIT_SET_BUFSIZ(&cli->cli_buf[0], 0, AIT_LEN(&s->sock_buf));
281: AIT_SET_BUFSIZ(&cli->cli_buf[1], 0, AIT_LEN(&s->sock_buf));
282:
283: switch ((pid = ioForkPTY(&cli->cli_pty, cli->cli_name, sizeof cli->cli_name,
284: NULL, NULL, NULL))) {
285: case -1:
286: ELIBERR(io);
287: break;
288: case 0:
289: array_Args(cli->cli_cmdline, 0, " \t", &args);
290: argv = array_To(args);
291: array_Destroy(&args);
292:
293: printf("Console %s\n", cli->cli_name);
1.6 misho 294: rlen = execv(*argv, argv);
295: _exit(rlen);
1.5 misho 296: break;
297: default:
298: cli->cli_pid = pid;
299:
300: schedRead(TASK_ROOT(task), io_rxPty, cli, cli->cli_pty,
301: TASK_ARG(task), 0);
302: schedRead(TASK_ROOT(task), io_rxNet, cli, cli->cli_fd,
303: TASK_ARG(task), 0);
1.7 misho 304: ioUpdTimerSocket(cli);
1.5 misho 305: break;
306: }
307: end:
308: schedReadSelf(task);
309: taskExit(task, NULL);
310: }
311:
312:
1.2 misho 313: /*
314: * ioInitSocket() - Init socket and allocate resources
315: *
316: * @role = Socket role
317: * @type = Socket type
318: * @proto = Socket protocol
319: * @addr = Bind to address
320: * @port = Bind to port
321: * @buflen = Socket buffer, optional if =0 == BUFSIZ
322: * return: NULL error or !=NULL created socket
323: */
324: sock_t *
325: ioInitSocket(int role, int type, int proto, const char *addr, u_short port, size_t buflen)
326: {
327: sock_t *s = NULL;
328: int n = 1;
329:
330: if (!addr)
331: return NULL;
332:
333: s = e_malloc(sizeof(sock_t));
334: if (!s) {
335: io_SetErr(elwix_GetErrno(), "%s", elwix_GetError());
336: return NULL;
337: } else
338: memset(s, 0, sizeof(sock_t));
339:
1.3 misho 340: TAILQ_INIT(&s->sock_cli);
341:
1.2 misho 342: s->sock_role = role;
343: s->sock_type = type;
344: s->sock_proto = proto;
345: if (!e_gethostbyname(addr, port, &s->sock_addr)) {
346: io_SetErr(elwix_GetErrno(), "%s", elwix_GetError());
347: e_free(s);
348: return NULL;
349: } else {
350: buflen = buflen ? buflen : BUFSIZ;
1.5 misho 351: buflen = E_ALIGN(buflen, 2); /* align buflen length */
1.2 misho 352: AIT_SET_BUFSIZ(&s->sock_buf, 0, buflen);
353: }
354:
355: s->sock_fd = socket(s->sock_addr.sa.sa_family, s->sock_type, s->sock_proto);
356: if (s->sock_fd == -1) {
357: LOGERR;
358: AIT_FREE_VAL(&s->sock_buf);
359: e_free(s);
360: return NULL;
361: }
362: if (setsockopt(s->sock_fd, SOL_SOCKET, SO_SNDBUF, &buflen, sizeof buflen) == -1) {
363: LOGERR;
364: AIT_FREE_VAL(&s->sock_buf);
365: e_free(s);
366: return NULL;
367: }
368: if (setsockopt(s->sock_fd, SOL_SOCKET, SO_RCVBUF, &buflen, sizeof buflen) == -1) {
369: LOGERR;
370: AIT_FREE_VAL(&s->sock_buf);
371: e_free(s);
372: return NULL;
373: }
374: if (setsockopt(s->sock_fd, SOL_SOCKET, SO_REUSEADDR, &n, sizeof n) == -1) {
375: LOGERR;
376: AIT_FREE_VAL(&s->sock_buf);
377: e_free(s);
378: return NULL;
379: }
380: if (bind(s->sock_fd, &s->sock_addr.sa, s->sock_addr.sa.sa_len) == -1) {
381: LOGERR;
382: AIT_FREE_VAL(&s->sock_buf);
383: e_free(s);
384: return NULL;
385: }
386:
1.5 misho 387: s->sock_root = schedBegin();
388: if (!s->sock_root) {
389: io_SetErr(sched_GetErrno(), "%s", sched_GetError());
390: AIT_FREE_VAL(&s->sock_buf);
391: e_free(s);
392: return NULL;
393: }
394:
1.3 misho 395: pthread_mutex_init(&s->sock_mtx, NULL);
1.2 misho 396: return s;
397: }
398:
399: /*
400: * ioCloseSocket() - Close socket and free resources
401: *
402: * @s = Socket
403: * return: none
404: */
405: void
406: ioCloseSocket(sock_t ** __restrict s)
407: {
1.5 misho 408: sock_cli_t *cli;
409: int stat;
1.3 misho 410:
1.2 misho 411: if (s && *s) {
1.3 misho 412: pthread_mutex_lock(&(*s)->sock_mtx);
413: while ((cli = TAILQ_FIRST(&(*s)->sock_cli))) {
414: TAILQ_REMOVE(&(*s)->sock_cli, cli, cli_node);
1.5 misho 415:
416: schedCancelby((*s)->sock_root, taskMAX, CRITERIA_ARG, cli, NULL);
417:
418: if ((*s)->sock_type == SOCK_STREAM) {
419: shutdown(cli->cli_fd, SHUT_RDWR);
420: close(cli->cli_fd);
421: }
422: AIT_FREE_VAL(&cli->cli_buf[1]);
423: AIT_FREE_VAL(&cli->cli_buf[0]);
424:
425: if (cli->cli_pid > 0) {
1.7 misho 426: kill(cli->cli_pid, SIGKILL);
1.5 misho 427: while (waitpid(cli->cli_pid, &stat, WNOHANG) > 0) {
428: usleep(1000);
1.7 misho 429: kill(cli->cli_pid, SIGKILL);
1.5 misho 430: }
431: }
432:
1.3 misho 433: e_free(cli);
434: }
435: pthread_mutex_unlock(&(*s)->sock_mtx);
436:
1.2 misho 437: shutdown((*s)->sock_fd, SHUT_RDWR);
438: close((*s)->sock_fd);
439:
440: AIT_FREE_VAL(&(*s)->sock_buf);
1.3 misho 441:
1.5 misho 442: schedEnd(&(*s)->sock_root);
443:
1.3 misho 444: pthread_mutex_destroy(&(*s)->sock_mtx);
1.2 misho 445: e_free(*s);
446: *s = NULL;
447: }
448: }
449:
450: /*
451: * ioUpSocket() - Setup socket for use
452: *
453: * @s = Socket
454: * @arg = Server role = listen backlog queue and Client role = peer address
1.5 misho 455: * @timeout = Socket timeout in sec (default -1 infinit)
1.2 misho 456: * return: -1 error or 0 ok
457: */
458: int
1.5 misho 459: ioUpSocket(sock_t * __restrict s, void *arg, int timeout)
1.2 misho 460: {
461: int ret = 0;
462: sockaddr_t *peer = (sockaddr_t*) arg;
463: uintptr_t backlog = (uintptr_t) arg;
464:
465: if (!s || !arg)
466: return -1;
1.5 misho 467: else {
468: s->sock_timeout.tv_sec = timeout;
469: s->sock_timeout.tv_nsec = (timeout < 1) ? timeout : 0;
470: schedPolling(s->sock_root, &s->sock_timeout, NULL);
471: }
1.2 misho 472:
473: switch (s->sock_role) {
474: case IO_SOCK_ROLE_CLIENT:
475: memcpy(&s->sock_peer, peer, sizeof s->sock_peer);
476:
477: if (connect(s->sock_fd, &s->sock_peer.sa,
478: s->sock_peer.sa.sa_len) == -1) {
479: LOGERR;
480: return -1;
481: }
482: break;
483: case IO_SOCK_ROLE_SERVER:
484: if (s->sock_type == SOCK_STREAM) {
485: s->sock_backq = backlog;
486:
487: if (listen(s->sock_fd, s->sock_backq) == -1) {
488: LOGERR;
489: return -1;
490: }
491: }
492: break;
493: default:
494: io_SetErr(EINVAL, "Unsupported socket type");
495: return -1;
496: }
497:
498: fcntl(s->sock_fd, F_SETFL, fcntl(s->sock_fd, F_GETFL) | O_NONBLOCK);
499: return ret;
500: }
1.3 misho 501:
1.5 misho 502: /*
503: * ioUpdTimerSocket() - Update timeout of socket
504: *
505: * @c = Client socket
506: * return: none
507: */
508: void
1.7 misho 509: ioUpdTimerSocket(sock_cli_t * __restrict c)
1.3 misho 510: {
1.5 misho 511: sock_t *s;
1.3 misho 512:
1.5 misho 513: if (!c)
514: return;
515: else
516: s = c->cli_parent;
1.3 misho 517:
1.7 misho 518: schedCancelby(s->sock_root, taskTIMER, CRITERIA_ARG, c, NULL);
519: schedTimer(s->sock_root, io_closeClient, c, s->sock_timeout, NULL, 0);
1.3 misho 520: }
521:
1.5 misho 522: /*
523: * ioCloseClient() - Close client socket
524: *
525: * @c = Client socket
526: * return: 0 ok or !=0 error
527: */
528: int
529: ioCloseClient(sock_cli_t * __restrict c)
1.3 misho 530: {
1.5 misho 531: sock_t *s;
1.3 misho 532:
1.5 misho 533: if (!c)
534: return -1;
535: else
536: s = c->cli_parent;
1.3 misho 537:
1.7 misho 538: return !schedEvent(s->sock_root, io_closeClient, c, 0, NULL, 0);
1.3 misho 539: }
540:
541: /*
1.5 misho 542: * ioLoopSocket() - Start socket scheduler
1.3 misho 543: *
544: * @s = Socket
1.5 misho 545: * @rcb = Read callback
546: * return: -1 error or return result from scheduler
1.3 misho 547: */
548: int
1.5 misho 549: ioLoopSocket(sock_t * __restrict s, sched_task_func_t rcb)
1.3 misho 550: {
1.5 misho 551: if (!s || !rcb || s->sock_kill)
1.3 misho 552: return -1;
553:
1.5 misho 554: schedRead(s->sock_root, io_acceptClient, s, s->sock_fd, rcb, 0);
555: return schedRun(s->sock_root, &s->sock_kill);
556: }
1.3 misho 557:
1.5 misho 558: /*
559: * ioBridgeProg2Socket() - Start socket scheduler and bridge program to socket
560: *
561: * @s = Socket
562: * @prgname = Program name
563: * return: 0 ok or !=0 error
564: */
565: int
566: ioBridgeProg2Socket(sock_t * __restrict s, const char *prgname)
567: {
568: if (!s || !prgname || s->sock_kill)
569: return -1;
1.3 misho 570:
1.5 misho 571: schedRead(s->sock_root, io_bridgeClient, s, s->sock_fd, (void*) prgname, 0);
572: return schedRun(s->sock_root, &s->sock_kill);
1.3 misho 573: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>