1: /*************************************************************************
2: * (C) 2010 AITNET ltd - Sofia/Bulgaria - <misho@aitnet.org>
3: * by Michael Pounov <misho@elwix.org>
4: *
5: * $Author: misho $
6: * $Id: aitio.c,v 1.10.6.3 2012/05/23 12:16:13 misho Exp $
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
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:
49: int io_Debug;
50:
51:
52: #pragma GCC visibility push(hidden)
53:
54: int io_mpool;
55:
56: int io_Errno;
57: char io_Error[STRSIZ];
58:
59: #pragma GCC visibility pop
60:
61:
62: // io_GetErrno() Get error code of last operation
63: inline int
64: io_GetErrno()
65: {
66: return io_Errno;
67: }
68:
69: // io_GetError() Get error text of last operation
70: inline const char *
71: io_GetError()
72: {
73: return io_Error;
74: }
75:
76: // io_SetErr() Set error to variables for internal use!!!
77: inline void
78: io_SetErr(int eno, char *estr, ...)
79: {
80: va_list lst;
81:
82: io_Errno = eno;
83: memset(io_Error, 0, STRSIZ);
84: va_start(lst, estr);
85: vsnprintf(io_Error, STRSIZ, estr, lst);
86: va_end(lst);
87: }
88:
89: // mpool_inuse() Check for mpool usage
90: inline int
91: mpool_inuse()
92: {
93: return io_mpool;
94: }
95:
96:
97: // init libaitio routine
98: void
99: _init()
100: {
101: #ifdef USE_MPOOL
102: io_mpool = 42;
103: #else
104: io_mpool = 0;
105: #endif
106: }
107:
108: // fini libaitio routine
109: void
110: _fini()
111: {
112: }
113:
114:
115: /*
116: * ioPromptRead() - Read data from input h[0] with prompt to output h[1]
117: *
118: * @h = file handles h[0] = input, h[1] = output, if NULL use stdin, stdout
119: * @csPrompt = Prompt before input, may be NULL
120: * @psData = Readed data
121: * @dataLen = Length of data
122: * return: 0 EOF; -1 error:: can`t read; >0 count of readed chars
123: */
124: int
125: ioPromptRead(int *h, const char *csPrompt, char * __restrict psData, int dataLen)
126: {
127: int ok = 0;
128: FILE *inp, *out;
129: char szLine[BUFSIZ], *pos;
130:
131: if (!psData || !dataLen)
132: return -1;
133:
134: inp = fdopen(!h ? 0 : h[0], "r");
135: if (!inp) {
136: LOGERR;
137: return -1;
138: }
139: out = fdopen(!h ? 1 : h[1], "w");
140: if (!out) {
141: LOGERR;
142: return -1;
143: }
144:
145: while (!ok) {
146: if (csPrompt) {
147: fprintf(out, "%s", csPrompt);
148: fflush(out);
149: }
150:
151: memset(szLine, 0, BUFSIZ);
152: if (!fgets(szLine, BUFSIZ, inp)) {
153: clearerr(inp);
154: fpurge(out);
155: fflush(out);
156: return 0;
157: }
158:
159: if ((pos = strchr(szLine, '\n')))
160: *pos = 0;
161:
162: strlcpy(psData, szLine, dataLen);
163: ok = 1;
164: }
165:
166: return pos - szLine;
167: }
168:
169: /*
170: * ioPromptPassword() - Read password from input h[0] with prompt to output h[1]
171: *
172: * @h = file handles h[0] = input, h[1] = output, if NULL use stdin, stdout
173: * @csPrompt = Prompt before input, may be NULL
174: * @psPass = Readed password
175: * @passLen = Length of password
176: * @confirm = Confirm password, 0 - get password, !=0 Ask for confirmation
177: * return: 0 EOF; -1 error:: can`t read; >0 count of readed chars
178: */
179: int
180: ioPromptPassword(int *h, const char *csPrompt, char * __restrict psPass, int passLen, int confirm)
181: {
182: int ret, ok = 0;
183: FILE *inp, *out;
184: char szLine[2][STRSIZ];
185: struct sgttyb tty_state;
186:
187: if (!psPass || !passLen)
188: return -1;
189:
190: inp = fdopen(!h ? 0 : h[0], "r");
191: if (!inp) {
192: LOGERR;
193: return -1;
194: }
195: out = fdopen(!h ? 1 : h[1], "w");
196: if (!out) {
197: LOGERR;
198: return -1;
199: }
200:
201: if (ioctl(fileno(inp), TIOCGETP, &tty_state) == -1) {
202: LOGERR;
203: return -1;
204: } else {
205: tty_state.sg_flags &= ~ECHO;
206: if (ioctl(fileno(inp), TIOCSETP, &tty_state) == -1) {
207: LOGERR;
208: return -1;
209: }
210: }
211:
212: while (!ok) {
213: switch ((ret = ioPromptRead(h, (!csPrompt || !*csPrompt) ? "Password:" : csPrompt,
214: szLine[0], STRSIZ))) {
215: case -1:
216: LOGERR;
217: ok = -1;
218: case 0:
219: goto next;
220: }
221: if (confirm) {
222: fprintf(out, "\n");
223: fflush(out);
224:
225: switch (ioPromptRead(h, "Password confirm:", szLine[1], STRSIZ)) {
226: case -1:
227: LOGERR;
228: ok = -1;
229: goto next;
230: case 0:
231: default:
232: if (strcmp(szLine[0], szLine[1])) {
233: fprintf(out, "\n\07\07Mismatch - Try again!\n");
234: fflush(out);
235: continue;
236: }
237: }
238: }
239:
240: strlcpy(psPass, szLine[0], passLen);
241: ok = ret;
242: fprintf(out, "\n");
243: fflush(out);
244: }
245:
246: next:
247: tty_state.sg_flags |= ECHO;
248: if (ioctl(fileno(inp), TIOCSETP, &tty_state) == -1) {
249: LOGERR;
250: return -1;
251: }
252:
253: return ok;
254: }
255:
256: /*
257: * ioRegexVerify() - Function for verify data match in regex expression
258: *
259: * @csRegex = Regulare expression pattern
260: * @csData = Data for check and verify
261: * @startPos = Return start positions
262: * @endPos = Return end positions
263: * return: NULL not match or error; !=NULL begin of matched data
264: */
265: const char *
266: ioRegexVerify(const char *csRegex, const char *csData, int *startPos, int *endPos)
267: {
268: regex_t re;
269: regmatch_t match;
270: char szErr[STRSIZ];
271: int ret, flg;
272: const char *pos;
273:
274: if (!csRegex || !csData)
275: return NULL;
276:
277: if ((ret = regcomp(&re, csRegex, REG_EXTENDED))) {
278: regerror(ret, &re, szErr, STRSIZ);
279: io_SetErr(ret, "%s", szErr);
280: regfree(&re);
281: return NULL;
282: }
283:
284: for (ret = flg = 0, pos = csData; !(ret = regexec(&re, pos, 1, &match, flg));
285: pos += match.rm_eo, flg = REG_NOTBOL) {
286: if (startPos)
287: *startPos = match.rm_so;
288: if (endPos)
289: *endPos = match.rm_eo;
290:
291: pos += match.rm_so;
292: break;
293: }
294:
295: if (ret) {
296: regerror(ret, &re, szErr, STRSIZ);
297: io_SetErr(ret, "%s", szErr);
298: pos = NULL;
299: }
300:
301: regfree(&re);
302: return pos;
303: }
304:
305: /*
306: * ioRegexGet() - Function for get data match in regex expression
307: *
308: * @csRegex = Regulare expression pattern
309: * @csData = Data from get
310: * @psString = Returned string if match
311: * @strLen = Length of string
312: * return: 0 not match; >0 count of returned chars
313: */
314: int
315: ioRegexGet(const char *csRegex, const char *csData, char * __restrict psString, int strLen)
316: {
317: int sp, ep, len;
318: const char *str;
319:
320: if (!csRegex || !csData)
321: return -1;
322:
323: str = ioRegexVerify(csRegex, csData, &sp, &ep);
324: if (!str)
325: return 0;
326:
327: len = ep - sp;
328: if (psString && strLen) {
329: memset(psString, 0, strLen);
330: strncpy(psString, str, strLen <= len ? strLen - 1 : len);
331: }
332:
333: return len;
334: }
335:
336: /*
337: * ioRegexReplace() - Function for replace data match in regex expression with newdata
338: *
339: * @csRegex = Regulare expression pattern
340: * @csData = Source data
341: * @csNew = Data for replace
342: * return: NULL not match or error; !=NULL allocated new string, must be xfree after use!
343: */
344: char *
345: ioRegexReplace(const char *csRegex, const char *csData, const char *csNew)
346: {
347: int sp, ep, len;
348: char *str = NULL;
349:
350: if (!csRegex || !csData)
351: return NULL;
352:
353: if (!ioRegexVerify(csRegex, csData, &sp, &ep))
354: return NULL;
355:
356: // ___ before match
357: len = sp + 1;
358: str = xmalloc(len);
359: if (!str) {
360: LOGERR;
361: return NULL;
362: } else
363: strlcpy(str, csData, len);
364: // * replace match *
365: if (csNew) {
366: len += strlen(csNew);
367: str = xrealloc(str, len);
368: if (!str) {
369: LOGERR;
370: return NULL;
371: } else
372: strlcat(str, csNew, len);
373: }
374: // after match ___
375: len += strlen(csData) - ep;
376: str = xrealloc(str, len);
377: if (!str) {
378: LOGERR;
379: return NULL;
380: } else
381: strlcat(str, csData + ep, len);
382:
383: return str;
384: }
385:
386: /*
387: * ioStrAst() - Function for evaluate string like asterisk variable "{text[:[-]#[:#]]}"
388: *
389: * @csString = Input string
390: * return: NULL error, !=NULL Allocated new string evaluated from input string, must be xfree()
391: */
392: char *
393: ioStrAst(const char *csString)
394: {
395: char *ext, *str, *out = NULL;
396: int e[2] = { 0 };
397:
398: if (!csString)
399: return NULL;
400:
401: if (!strchr(csString, '{') || !strrchr(csString, '}')) {
402: memset(io_Error, 0, STRSIZ);
403: snprintf(io_Error, STRSIZ, "Invalid input string format ... "
404: "must be like {text[:[-]#[:#]]}");
405: io_Errno = EINVAL;
406: return NULL;
407: } else {
408: str = strdup(strchr(csString, '{') + 1);
409: *strrchr(str, '}') = 0;
410: }
411:
412: if ((ext = strchr(str, ':'))) {
413: *ext++ = 0;
414: e[0] = strtol(ext, NULL, 0);
415: if ((ext = strchr(ext, ':')))
416: e[1] = strtol(++ext, NULL, 0);
417:
418: /* make cut prefix */
419: if (e[0] >= 0)
420: ext = str + e[0];
421: else
422: ext = str + strlen(str) + e[0];
423: /* make cut suffix */
424: if (e[1] > 0)
425: *(ext + e[1]) = 0;
426: } else
427: /* ok, clear show */
428: ext = str;
429:
430: out = strdup(ext);
431: xfree(str);
432:
433: return out;
434: }
435:
436:
437: /*
438: * ioMkDir() - Function for racursive directory creation and validation
439: *
440: * @csDir = Full directory path
441: * @mode = Mode for directory creation if missing dir
442: * return: -1 error, 0 directory path exist, >0 created missing dirs
443: */
444: int
445: ioMkDir(const char *csDir, int mode)
446: {
447: char *str, *s, *pbrk, szOld[MAXPATHLEN] = { 0 };
448: register int cx = -1;
449:
450: if (!csDir)
451: return cx;
452:
453: str = strdup(csDir);
454: if (!str) {
455: LOGERR;
456: return cx;
457: }
458:
459: getcwd(szOld, MAXPATHLEN);
460: if (*str == '/')
461: chdir("/");
462:
463: for (cx = 0, s = strtok_r(str, "/", &pbrk); s; s = strtok_r(NULL, "/", &pbrk)) {
464: if (mkdir(s, mode) == -1) {
465: if (errno != EEXIST) {
466: LOGERR;
467: cx = -1;
468: goto end;
469: }
470: } else
471: cx++;
472:
473: if (chdir(s) == -1) {
474: LOGERR;
475: cx = -1;
476: goto end;
477: }
478: }
479: end:
480: chdir(szOld);
481: xfree(str);
482: return cx;
483: }
484:
485: /*
486: * ioWatchDirLoop() - Function for watching changes in directory and fire callback
487: *
488: * @csDir = Full directory path
489: * @callback = Callback if raise event! nOp -1 delete, 0 change/move, 1 create
490: * return: -1 error, !=-1 ok, number of total signaled events
491: */
492: int
493: ioWatchDirLoop(const char *csDir, int (*callback)(const char *csName, int nOp))
494: {
495: glob_t g[2] = {{ 0 }, { 0 }};
496: int d, kq, n = 0;
497: register int j, i;
498: struct kevent req, chg;
499: char wrk[MAXPATHLEN * 2], str[MAXPATHLEN] = { 0 };
500:
501: if (!csDir || !callback)
502: return 0;
503:
504: strlcpy(str, csDir, MAXPATHLEN);
505: strlcat(str, "/*", MAXPATHLEN);
506:
507: kq = kqueue();
508: if (kq == -1) {
509: LOGERR;
510: return -1;
511: }
512: d = open(csDir, O_RDONLY);
513: if (d == -1) {
514: LOGERR;
515: close(kq);
516: return -1;
517: }
518:
519: EV_SET(&req, d, EVFILT_VNODE, EV_ADD | EV_CLEAR, NOTE_WRITE, 0, 0);
520:
521: if ((n = glob(str, GLOB_NOCHECK, NULL, &g[0]))) {
522: LOGERR;
523: close(d);
524: close(kq);
525: return -1;
526: } /*else
527: ioDEBUG(3, "Start files %d in %s\n", g[0].gl_matchc, str);*/
528:
529: while (kevent(kq, &req, 1, &chg, 1, NULL) > 0) {
530: /*ioDEBUG(1, "Event:: req=0x%x -> chg=0x%x data=%x\n", req.fflags, chg.fflags, chg.data);*/
531:
532: if (!glob(str, GLOB_NOCHECK, NULL, &g[1])) {
533: /*ioDEBUG(3, "Diffs %d <> %d\n", g[0].gl_matchc, g[1].gl_matchc);*/
534:
535: if (g[0].gl_matchc != g[1].gl_matchc) {
536: /* find new items */
537: for (j = 0; j < g[1].gl_matchc; j++) {
538: for (i = 0; i < g[0].gl_matchc; i++)
539: if (!strcmp(g[0].gl_pathv[i], g[1].gl_pathv[j]))
540: break;
541: if (i == g[0].gl_matchc) {
542: if (callback(g[1].gl_pathv[j], 1) < 0)
543: break;
544: else
545: n++;
546: }
547: }
548: /* find del items */
549: for (j = 0; j < g[0].gl_matchc; j++) {
550: for (i = 0; i < g[1].gl_matchc; i++)
551: if (!strcmp(g[1].gl_pathv[i], g[0].gl_pathv[j]))
552: break;
553: if (i == g[1].gl_matchc) {
554: if (callback(g[0].gl_pathv[j], -1) < 0)
555: break;
556: else
557: n++;
558: }
559: }
560: } else {
561: /* find chg from items */
562: for (j = 0; j < g[0].gl_matchc; j++) {
563: for (i = 0; i < g[1].gl_matchc; i++)
564: if (!strcmp(g[1].gl_pathv[i], g[0].gl_pathv[j]))
565: break;
566: if (i == g[1].gl_matchc) {
567: strlcpy(wrk, g[0].gl_pathv[j], sizeof wrk);
568: strlcat(wrk, ":", sizeof wrk);
569: }
570: }
571: /* find chg to items */
572: for (j = 0; j < g[1].gl_matchc; j++) {
573: for (i = 0; i < g[0].gl_matchc; i++)
574: if (!strcmp(g[0].gl_pathv[i], g[1].gl_pathv[j]))
575: break;
576: if (i == g[0].gl_matchc) {
577: strlcat(wrk, g[1].gl_pathv[j], sizeof wrk);
578: if (callback(wrk, 0) < 0)
579: break;
580: else
581: n++;
582: }
583: }
584: }
585:
586: globfree(&g[0]);
587: g[0] = g[1];
588: }
589: }
590:
591: globfree(&g[0]);
592: close(d);
593: close(kq);
594: return n;
595: }
596:
597: /*
598: * ioCreatePIDFile() - Create PID file
599: *
600: * @csName = PID filename
601: * @ifExists = !=0 if filename exists return error
602: * return: -1 error or 0 ok
603: */
604: inline int
605: ioCreatePIDFile(const char *csName, int ifExists)
606: {
607: int fd;
608: char str[STRSIZ] = { 0 };
609:
610: if (!csName)
611: return -1;
612:
613: fd = open(csName, O_WRONLY | O_CREAT | (ifExists ? O_EXCL : 0), 0644);
614: if (fd == -1) {
615: LOGERR;
616: return -1;
617: }
618: snprintf(str, sizeof str, "%d", getpid());
619: write(fd, str, strlen(str));
620: close(fd);
621: return 0;
622: }
623:
624:
625: /*
626: * ioSendFile() - AITNET sendfile() userland implementation, not dependant from OS
627: *
628: * @s = socket
629: * @csFile = file for send
630: * @sendLen = bytes to send, if 0 send all data
631: * @offset = start file offset
632: * @sndbuf = SO_SNDBUF value, if 0 use default
633: * return: 0 error, >0 ok, sended bytes
634: */
635: size_t
636: ioSendFile(int s, const char *csFile, size_t sendLen, off_t offset, int sndbuf)
637: {
638: void *addr;
639: int fd;
640: size_t len = 0;
641: register size_t off = 0;
642:
643: if (!csFile)
644: return 0;
645:
646: if (sndbuf)
647: if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof sndbuf) == -1) {
648: LOGERR;
649: return 0;
650: }
651:
652: fd = open(csFile, O_RDONLY);
653: if (fd == -1) {
654: LOGERR;
655: return 0;
656: }
657: if (!sendLen) {
658: sendLen = lseek(fd, 0, SEEK_END);
659: if (sendLen == -1) {
660: LOGERR;
661: close(fd);
662: return 0;
663: }
664: }
665: addr = mmap(NULL, sendLen, PROT_READ, MAP_SHARED, fd, offset);
666: if (addr == MAP_FAILED) {
667: LOGERR;
668: close(fd);
669: return 0;
670: } else
671: close(fd);
672:
673: while (off < sendLen && (len = write(s, addr + off, sendLen - off)) != -1)
674: off += len;
675: if (len == -1) {
676: LOGERR;
677: munmap(addr, sendLen);
678: return 0;
679: } else
680: len = off;
681:
682: if (len != sendLen) {
683: io_SetErr(ECANCELED, "Different sizes - request %u bytes, actually sended %u bytes\n",
684: sendLen, len);
685: len ^= len;
686: }
687:
688: munmap(addr, sendLen);
689: return len;
690: }
691:
692: /*
693: * ioRecvFile() - Receive file from socket, fastest (zero-copy) way
694: *
695: * @s = socket
696: * @csFile = file for receive
697: * @recvLen = receive bytes
698: * @over = overwrite file if exists with mode like 0644
699: * @rcvbuf = SO_RCVBUF value, if 0 use default
700: * return: 0 error, >0 ok, received bytes
701: */
702: size_t
703: ioRecvFile(int s, const char *csFile, size_t recvLen, int over, int rcvbuf)
704: {
705: void *addr;
706: int fd;
707: size_t len = 0;
708: register size_t off = 0;
709: struct pollfd pfd = { s, POLLIN | POLLPRI, 0 };
710:
711: if (!csFile || !recvLen)
712: return 0;
713: if (!over && !access(csFile, F_OK))
714: return 0;
715:
716: if (rcvbuf)
717: if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof rcvbuf) == -1) {
718: LOGERR;
719: return 0;
720: }
721:
722: fd = open(csFile, O_WRONLY | O_CREAT | O_TRUNC, over);
723: if (fd == -1) {
724: LOGERR;
725: unlink(csFile);
726: return 0;
727: }
728: if (ftruncate(fd, recvLen) == -1) {
729: LOGERR;
730: close(fd);
731: unlink(csFile);
732: return 0;
733: }
734: addr = mmap(NULL, recvLen, PROT_WRITE, MAP_SHARED, fd, 0);
735: if (addr == MAP_FAILED) {
736: LOGERR;
737: close(fd);
738: unlink(csFile);
739: return 0;
740: } else
741: close(fd);
742:
743: while (off < recvLen && poll(&pfd, 1, RECV_TIMEOUT) != -1)
744: while (off < recvLen && (len = read(s, addr + off, recvLen - off)) != -1)
745: off += len;
746: if (len == -1) {
747: LOGERR;
748: munmap(addr, recvLen);
749: unlink(csFile);
750: return 0;
751: } else
752: len = off;
753:
754: if (len != recvLen)
755: io_SetErr(EAGAIN, "Different sizes - request %u bytes, actually received %u bytes\n",
756: recvLen, len);
757:
758: munmap(addr, recvLen);
759: return len;
760: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>