Annotation of libaitio/src/aitio.c, revision 1.17.8.3
1.1 misho 1: /*************************************************************************
1.5 misho 2: * (C) 2010 AITNET ltd - Sofia/Bulgaria - <misho@aitnet.org>
3: * by Michael Pounov <misho@elwix.org>
1.1 misho 4: *
5: * $Author: misho $
1.17.8.3! misho 6: * $Id: aitio.c,v 1.17.8.2 2016/08/10 14:01:51 misho Exp $
1.1 misho 7: *
1.5 misho 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:
1.17.8.1 misho 15: Copyright 2004 - 2016
1.5 misho 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: */
1.1 misho 46: #include "global.h"
47:
48:
49: #pragma GCC visibility push(hidden)
50:
51: int io_Errno;
52: char io_Error[STRSIZ];
53:
54: #pragma GCC visibility pop
55:
56:
57: // io_GetErrno() Get error code of last operation
1.15 misho 58: int
1.6 misho 59: io_GetErrno()
1.1 misho 60: {
61: return io_Errno;
62: }
63:
64: // io_GetError() Get error text of last operation
1.15 misho 65: const char *
1.6 misho 66: io_GetError()
1.1 misho 67: {
68: return io_Error;
69: }
70:
71: // io_SetErr() Set error to variables for internal use!!!
1.15 misho 72: void
1.6 misho 73: io_SetErr(int eno, char *estr, ...)
1.1 misho 74: {
75: va_list lst;
76:
77: io_Errno = eno;
1.14 misho 78: memset(io_Error, 0, sizeof io_Error);
1.1 misho 79: va_start(lst, estr);
1.14 misho 80: vsnprintf(io_Error, sizeof io_Error, estr, lst);
1.1 misho 81: va_end(lst);
82: }
83:
84:
85: /*
1.10 misho 86: * ioPromptRead() - Read data from input h[0] with prompt to output h[1]
1.9 misho 87: *
1.1 misho 88: * @h = file handles h[0] = input, h[1] = output, if NULL use stdin, stdout
89: * @csPrompt = Prompt before input, may be NULL
90: * @psData = Readed data
91: * @dataLen = Length of data
92: * return: 0 EOF; -1 error:: can`t read; >0 count of readed chars
93: */
1.6 misho 94: int
95: ioPromptRead(int *h, const char *csPrompt, char * __restrict psData, int dataLen)
1.1 misho 96: {
97: int ok = 0;
98: FILE *inp, *out;
99: char szLine[BUFSIZ], *pos;
100:
101: if (!psData || !dataLen)
102: return -1;
103:
104: inp = fdopen(!h ? 0 : h[0], "r");
105: if (!inp) {
106: LOGERR;
107: return -1;
108: }
109: out = fdopen(!h ? 1 : h[1], "w");
110: if (!out) {
111: LOGERR;
112: return -1;
113: }
114:
115: while (!ok) {
116: if (csPrompt) {
117: fprintf(out, "%s", csPrompt);
118: fflush(out);
119: }
120:
121: memset(szLine, 0, BUFSIZ);
122: if (!fgets(szLine, BUFSIZ, inp)) {
123: clearerr(inp);
1.17.8.2 misho 124: #ifdef HAVE_FPURGE
1.1 misho 125: fpurge(out);
1.17.8.2 misho 126: #else
127: __fpurge(out);
128: #endif
1.1 misho 129: fflush(out);
130: return 0;
131: }
132:
133: if ((pos = strchr(szLine, '\n')))
134: *pos = 0;
135:
136: strlcpy(psData, szLine, dataLen);
137: ok = 1;
138: }
139:
140: return pos - szLine;
141: }
142:
143: /*
1.10 misho 144: * ioPromptPassword() - Read password from input h[0] with prompt to output h[1]
1.9 misho 145: *
1.1 misho 146: * @h = file handles h[0] = input, h[1] = output, if NULL use stdin, stdout
147: * @csPrompt = Prompt before input, may be NULL
148: * @psPass = Readed password
149: * @passLen = Length of password
150: * @confirm = Confirm password, 0 - get password, !=0 Ask for confirmation
151: * return: 0 EOF; -1 error:: can`t read; >0 count of readed chars
152: */
1.6 misho 153: int
154: ioPromptPassword(int *h, const char *csPrompt, char * __restrict psPass, int passLen, int confirm)
1.1 misho 155: {
156: int ret, ok = 0;
157: FILE *inp, *out;
158: char szLine[2][STRSIZ];
1.17.8.3! misho 159: #ifndef __linux__
1.1 misho 160: struct sgttyb tty_state;
1.17.8.3! misho 161: #else
! 162: struct termios o;
! 163: #endif
1.1 misho 164:
165: if (!psPass || !passLen)
166: return -1;
167:
168: inp = fdopen(!h ? 0 : h[0], "r");
169: if (!inp) {
170: LOGERR;
171: return -1;
172: }
173: out = fdopen(!h ? 1 : h[1], "w");
174: if (!out) {
175: LOGERR;
176: return -1;
177: }
178:
1.17.8.3! misho 179: #ifndef __linux__
1.1 misho 180: if (ioctl(fileno(inp), TIOCGETP, &tty_state) == -1) {
181: LOGERR;
182: return -1;
183: } else {
184: tty_state.sg_flags &= ~ECHO;
185: if (ioctl(fileno(inp), TIOCSETP, &tty_state) == -1) {
186: LOGERR;
187: return -1;
188: }
189: }
1.17.8.3! misho 190: #else
! 191: if (tcgetattr(fileno(inp), &o) == -1) {
! 192: LOGERR;
! 193: return -1;
! 194: } else {
! 195: o.c_lflag &= ~ECHO;
! 196: if (tcsetattr(fileno(inp), TCSANOW, &o) == -1) {
! 197: LOGERR;
! 198: return -1;
! 199: }
! 200: }
! 201: #endif
1.1 misho 202:
203: while (!ok) {
204: switch ((ret = ioPromptRead(h, (!csPrompt || !*csPrompt) ? "Password:" : csPrompt,
205: szLine[0], STRSIZ))) {
206: case -1:
207: LOGERR;
208: ok = -1;
209: case 0:
210: goto next;
211: }
212: if (confirm) {
213: fprintf(out, "\n");
214: fflush(out);
215:
216: switch (ioPromptRead(h, "Password confirm:", szLine[1], STRSIZ)) {
217: case -1:
218: LOGERR;
219: ok = -1;
220: goto next;
221: case 0:
222: default:
223: if (strcmp(szLine[0], szLine[1])) {
224: fprintf(out, "\n\07\07Mismatch - Try again!\n");
225: fflush(out);
226: continue;
227: }
228: }
229: }
230:
231: strlcpy(psPass, szLine[0], passLen);
232: ok = ret;
233: fprintf(out, "\n");
234: fflush(out);
235: }
236:
237: next:
1.17.8.3! misho 238: #ifndef __linux__
1.1 misho 239: tty_state.sg_flags |= ECHO;
240: if (ioctl(fileno(inp), TIOCSETP, &tty_state) == -1) {
241: LOGERR;
242: return -1;
243: }
1.17.8.3! misho 244: #else
! 245: o.c_lflag |= ECHO;
! 246: if (tcsetattr(fileno(inp), TCSANOW, &o) == -1) {
! 247: LOGERR;
! 248: return -1;
! 249: }
! 250: #endif
1.1 misho 251:
252: return ok;
253: }
254:
255: /*
1.10 misho 256: * ioMkDir() - Function for racursive directory creation and validation
1.9 misho 257: *
1.2 misho 258: * @csDir = Full directory path
259: * @mode = Mode for directory creation if missing dir
260: * return: -1 error, 0 directory path exist, >0 created missing dirs
261: */
262: int
263: ioMkDir(const char *csDir, int mode)
264: {
265: char *str, *s, *pbrk, szOld[MAXPATHLEN] = { 0 };
266: register int cx = -1;
267:
268: if (!csDir)
269: return cx;
270:
1.14 misho 271: str = e_strdup(csDir);
1.2 misho 272: if (!str) {
273: LOGERR;
274: return cx;
275: }
276:
277: getcwd(szOld, MAXPATHLEN);
278: if (*str == '/')
279: chdir("/");
280:
281: for (cx = 0, s = strtok_r(str, "/", &pbrk); s; s = strtok_r(NULL, "/", &pbrk)) {
282: if (mkdir(s, mode) == -1) {
283: if (errno != EEXIST) {
284: LOGERR;
285: cx = -1;
286: goto end;
287: }
288: } else
289: cx++;
290:
291: if (chdir(s) == -1) {
292: LOGERR;
293: cx = -1;
294: goto end;
295: }
296: }
297: end:
298: chdir(szOld);
1.14 misho 299: e_free(str);
1.2 misho 300: return cx;
301: }
302:
1.3 misho 303: /*
1.10 misho 304: * ioWatchDirLoop() - Function for watching changes in directory and fire callback
1.9 misho 305: *
1.3 misho 306: * @csDir = Full directory path
307: * @callback = Callback if raise event! nOp -1 delete, 0 change/move, 1 create
308: * return: -1 error, !=-1 ok, number of total signaled events
309: */
310: int
311: ioWatchDirLoop(const char *csDir, int (*callback)(const char *csName, int nOp))
312: {
313: glob_t g[2] = {{ 0 }, { 0 }};
1.17.8.3! misho 314: int d, n = 0;
1.3 misho 315: register int j, i;
1.17.8.3! misho 316: #ifndef __linux__
! 317: int kq;
1.3 misho 318: struct kevent req, chg;
1.17.8.3! misho 319: #endif
1.3 misho 320: char wrk[MAXPATHLEN * 2], str[MAXPATHLEN] = { 0 };
321:
322: if (!csDir || !callback)
323: return 0;
324:
325: strlcpy(str, csDir, MAXPATHLEN);
326: strlcat(str, "/*", MAXPATHLEN);
327:
1.17.8.3! misho 328: d = open(csDir, O_RDONLY);
! 329: if (d == -1) {
1.3 misho 330: LOGERR;
331: return -1;
332: }
1.17.8.3! misho 333:
! 334: #ifndef __linux__
! 335: kq = kqueue();
! 336: if (kq == -1) {
1.3 misho 337: LOGERR;
1.17.8.3! misho 338: close(d);
1.3 misho 339: return -1;
340: }
341:
1.4 misho 342: EV_SET(&req, d, EVFILT_VNODE, EV_ADD | EV_CLEAR, NOTE_WRITE, 0, 0);
1.17.8.3! misho 343: #endif
1.3 misho 344:
345: if ((n = glob(str, GLOB_NOCHECK, NULL, &g[0]))) {
346: LOGERR;
347: close(d);
1.17.8.3! misho 348: #ifndef __linux__
1.3 misho 349: close(kq);
1.17.8.3! misho 350: #endif
1.3 misho 351: return -1;
352: } /*else
353: ioDEBUG(3, "Start files %d in %s\n", g[0].gl_matchc, str);*/
354:
1.17.8.3! misho 355: #ifndef __linux__
1.3 misho 356: while (kevent(kq, &req, 1, &chg, 1, NULL) > 0) {
357: /*ioDEBUG(1, "Event:: req=0x%x -> chg=0x%x data=%x\n", req.fflags, chg.fflags, chg.data);*/
358:
359: if (!glob(str, GLOB_NOCHECK, NULL, &g[1])) {
360: /*ioDEBUG(3, "Diffs %d <> %d\n", g[0].gl_matchc, g[1].gl_matchc);*/
361:
362: if (g[0].gl_matchc != g[1].gl_matchc) {
363: /* find new items */
364: for (j = 0; j < g[1].gl_matchc; j++) {
365: for (i = 0; i < g[0].gl_matchc; i++)
366: if (!strcmp(g[0].gl_pathv[i], g[1].gl_pathv[j]))
367: break;
368: if (i == g[0].gl_matchc) {
369: if (callback(g[1].gl_pathv[j], 1) < 0)
370: break;
371: else
372: n++;
373: }
374: }
375: /* find del items */
376: for (j = 0; j < g[0].gl_matchc; j++) {
377: for (i = 0; i < g[1].gl_matchc; i++)
378: if (!strcmp(g[1].gl_pathv[i], g[0].gl_pathv[j]))
379: break;
380: if (i == g[1].gl_matchc) {
381: if (callback(g[0].gl_pathv[j], -1) < 0)
382: break;
383: else
384: n++;
385: }
386: }
387: } else {
388: /* find chg from items */
389: for (j = 0; j < g[0].gl_matchc; j++) {
390: for (i = 0; i < g[1].gl_matchc; i++)
391: if (!strcmp(g[1].gl_pathv[i], g[0].gl_pathv[j]))
392: break;
393: if (i == g[1].gl_matchc) {
394: strlcpy(wrk, g[0].gl_pathv[j], sizeof wrk);
395: strlcat(wrk, ":", sizeof wrk);
396: }
397: }
398: /* find chg to items */
399: for (j = 0; j < g[1].gl_matchc; j++) {
400: for (i = 0; i < g[0].gl_matchc; i++)
401: if (!strcmp(g[0].gl_pathv[i], g[1].gl_pathv[j]))
402: break;
403: if (i == g[0].gl_matchc) {
404: strlcat(wrk, g[1].gl_pathv[j], sizeof wrk);
405: if (callback(wrk, 0) < 0)
406: break;
407: else
408: n++;
409: }
410: }
411: }
412:
413: globfree(&g[0]);
414: g[0] = g[1];
415: }
416: }
1.17.8.3! misho 417: #endif
1.3 misho 418:
419: globfree(&g[0]);
1.17.8.3! misho 420: #ifndef __linux__
1.3 misho 421: close(kq);
1.17.8.3! misho 422: #endif
! 423: close(d);
1.3 misho 424: return n;
425: }
1.7 misho 426:
427: /*
1.10 misho 428: * ioCreatePIDFile() - Create PID file
1.9 misho 429: *
1.7 misho 430: * @csName = PID filename
431: * @ifExists = !=0 if filename exists return error
432: * return: -1 error or 0 ok
433: */
1.15 misho 434: int
1.7 misho 435: ioCreatePIDFile(const char *csName, int ifExists)
436: {
437: int fd;
438: char str[STRSIZ] = { 0 };
439:
440: if (!csName)
441: return -1;
442:
443: fd = open(csName, O_WRONLY | O_CREAT | (ifExists ? O_EXCL : 0), 0644);
444: if (fd == -1) {
445: LOGERR;
446: return -1;
447: }
448: snprintf(str, sizeof str, "%d", getpid());
449: write(fd, str, strlen(str));
450: close(fd);
451: return 0;
452: }
1.8 misho 453:
454:
455: /*
1.10 misho 456: * ioSendFile() - AITNET sendfile() userland implementation, not dependant from OS
1.9 misho 457: *
1.8 misho 458: * @s = socket
459: * @csFile = file for send
460: * @sendLen = bytes to send, if 0 send all data
461: * @offset = start file offset
462: * @sndbuf = SO_SNDBUF value, if 0 use default
463: * return: 0 error, >0 ok, sended bytes
464: */
465: size_t
466: ioSendFile(int s, const char *csFile, size_t sendLen, off_t offset, int sndbuf)
467: {
468: void *addr;
469: int fd;
470: size_t len = 0;
471: register size_t off = 0;
472:
473: if (!csFile)
474: return 0;
475:
476: if (sndbuf)
477: if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof sndbuf) == -1) {
478: LOGERR;
479: return 0;
480: }
481:
482: fd = open(csFile, O_RDONLY);
483: if (fd == -1) {
484: LOGERR;
485: return 0;
486: }
487: if (!sendLen) {
488: sendLen = lseek(fd, 0, SEEK_END);
489: if (sendLen == -1) {
490: LOGERR;
491: close(fd);
492: return 0;
493: }
494: }
495: addr = mmap(NULL, sendLen, PROT_READ, MAP_SHARED, fd, offset);
496: if (addr == MAP_FAILED) {
497: LOGERR;
498: close(fd);
499: return 0;
500: } else
501: close(fd);
502:
503: while (off < sendLen && (len = write(s, addr + off, sendLen - off)) != -1)
504: off += len;
505: if (len == -1) {
506: LOGERR;
507: munmap(addr, sendLen);
508: return 0;
509: } else
510: len = off;
511:
512: if (len != sendLen) {
513: io_SetErr(ECANCELED, "Different sizes - request %u bytes, actually sended %u bytes\n",
514: sendLen, len);
515: len ^= len;
516: }
517:
518: munmap(addr, sendLen);
519: return len;
520: }
521:
522: /*
1.10 misho 523: * ioRecvFile() - Receive file from socket, fastest (zero-copy) way
1.9 misho 524: *
1.8 misho 525: * @s = socket
526: * @csFile = file for receive
527: * @recvLen = receive bytes
528: * @over = overwrite file if exists with mode like 0644
529: * @rcvbuf = SO_RCVBUF value, if 0 use default
530: * return: 0 error, >0 ok, received bytes
531: */
532: size_t
533: ioRecvFile(int s, const char *csFile, size_t recvLen, int over, int rcvbuf)
534: {
535: void *addr;
536: int fd;
537: size_t len = 0;
538: register size_t off = 0;
539: struct pollfd pfd = { s, POLLIN | POLLPRI, 0 };
540:
541: if (!csFile || !recvLen)
542: return 0;
543: if (!over && !access(csFile, F_OK))
544: return 0;
545:
546: if (rcvbuf)
547: if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof rcvbuf) == -1) {
548: LOGERR;
549: return 0;
550: }
551:
552: fd = open(csFile, O_WRONLY | O_CREAT | O_TRUNC, over);
553: if (fd == -1) {
554: LOGERR;
555: unlink(csFile);
556: return 0;
557: }
1.10 misho 558: if (ftruncate(fd, recvLen) == -1) {
1.8 misho 559: LOGERR;
560: close(fd);
561: unlink(csFile);
562: return 0;
563: }
564: addr = mmap(NULL, recvLen, PROT_WRITE, MAP_SHARED, fd, 0);
565: if (addr == MAP_FAILED) {
566: LOGERR;
567: close(fd);
568: unlink(csFile);
569: return 0;
570: } else
571: close(fd);
572:
573: while (off < recvLen && poll(&pfd, 1, RECV_TIMEOUT) != -1)
574: while (off < recvLen && (len = read(s, addr + off, recvLen - off)) != -1)
575: off += len;
576: if (len == -1) {
577: LOGERR;
578: munmap(addr, recvLen);
579: unlink(csFile);
580: return 0;
581: } else
582: len = off;
583:
584: if (len != recvLen)
585: io_SetErr(EAGAIN, "Different sizes - request %u bytes, actually received %u bytes\n",
586: recvLen, len);
587:
588: munmap(addr, recvLen);
589: return len;
590: }
1.16 misho 591:
592: /*
593: * ioRealFileName() - Get real file name
594: *
595: * @fname = filename
596: * return: =NULL error or !=NULL real filename, should be free with e_free()
597: */
598: char *
599: ioRealFileName(const char *fname)
600: {
601: char *str = NULL;
602: struct stat sb;
603:
604: if (!fname)
605: return NULL;
606:
607: str = e_malloc(MAXPATHLEN);
608: if (!str) {
609: io_SetErr(elwix_GetErrno(), "%s", elwix_GetError());
610: return NULL;
611: } else
612: memset(str, 0, MAXPATHLEN);
613: if (readlink(fname, str, MAXPATHLEN) == -1) {
614: if (stat(fname, &sb) == -1) {
615: LOGERR;
616: e_free(str);
617: return NULL;
618: } else
619: strlcpy(str, fname, MAXPATHLEN);
620: }
621:
622: return str;
623: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>