Annotation of libaitio/src/aitio.c, revision 1.17.8.4
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.4! misho 6: * $Id: aitio.c,v 1.17.8.3 2016/08/10 14:19:50 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.4! misho 319: #else
! 320: int in;
! 321: struct inotify_event evt;
1.17.8.3 misho 322: #endif
1.3 misho 323: char wrk[MAXPATHLEN * 2], str[MAXPATHLEN] = { 0 };
324:
325: if (!csDir || !callback)
326: return 0;
327:
328: strlcpy(str, csDir, MAXPATHLEN);
329: strlcat(str, "/*", MAXPATHLEN);
330:
1.17.8.4! misho 331: #ifndef __linux__
1.17.8.3 misho 332: d = open(csDir, O_RDONLY);
333: if (d == -1) {
1.3 misho 334: LOGERR;
335: return -1;
336: }
1.17.8.3 misho 337:
338: kq = kqueue();
339: if (kq == -1) {
1.3 misho 340: LOGERR;
1.17.8.3 misho 341: close(d);
1.3 misho 342: return -1;
343: }
344:
1.4 misho 345: EV_SET(&req, d, EVFILT_VNODE, EV_ADD | EV_CLEAR, NOTE_WRITE, 0, 0);
1.17.8.4! misho 346: #else
! 347: in = inotify_init();
! 348: if (in == -1) {
! 349: LOGERR;
! 350: return -1;
! 351: }
! 352:
! 353: d = inotify_add_watch(in, csDir, IN_CREATE | IN_DELETE);
1.17.8.3 misho 354: #endif
1.3 misho 355:
356: if ((n = glob(str, GLOB_NOCHECK, NULL, &g[0]))) {
357: LOGERR;
1.17.8.3 misho 358: #ifndef __linux__
1.3 misho 359: close(kq);
1.17.8.4! misho 360: close(d);
! 361: #else
! 362: inotify_rm_watch(in, d);
! 363: close(in);
1.17.8.3 misho 364: #endif
1.3 misho 365: return -1;
366: } /*else
367: ioDEBUG(3, "Start files %d in %s\n", g[0].gl_matchc, str);*/
368:
1.17.8.3 misho 369: #ifndef __linux__
1.3 misho 370: while (kevent(kq, &req, 1, &chg, 1, NULL) > 0) {
371: /*ioDEBUG(1, "Event:: req=0x%x -> chg=0x%x data=%x\n", req.fflags, chg.fflags, chg.data);*/
372:
373: if (!glob(str, GLOB_NOCHECK, NULL, &g[1])) {
374: /*ioDEBUG(3, "Diffs %d <> %d\n", g[0].gl_matchc, g[1].gl_matchc);*/
375:
376: if (g[0].gl_matchc != g[1].gl_matchc) {
377: /* find new items */
378: for (j = 0; j < g[1].gl_matchc; j++) {
379: for (i = 0; i < g[0].gl_matchc; i++)
380: if (!strcmp(g[0].gl_pathv[i], g[1].gl_pathv[j]))
381: break;
382: if (i == g[0].gl_matchc) {
383: if (callback(g[1].gl_pathv[j], 1) < 0)
384: break;
385: else
386: n++;
387: }
388: }
389: /* find del items */
390: for (j = 0; j < g[0].gl_matchc; j++) {
391: for (i = 0; i < g[1].gl_matchc; i++)
392: if (!strcmp(g[1].gl_pathv[i], g[0].gl_pathv[j]))
393: break;
394: if (i == g[1].gl_matchc) {
395: if (callback(g[0].gl_pathv[j], -1) < 0)
396: break;
397: else
398: n++;
399: }
400: }
401: } else {
402: /* find chg from items */
403: for (j = 0; j < g[0].gl_matchc; j++) {
404: for (i = 0; i < g[1].gl_matchc; i++)
405: if (!strcmp(g[1].gl_pathv[i], g[0].gl_pathv[j]))
406: break;
407: if (i == g[1].gl_matchc) {
408: strlcpy(wrk, g[0].gl_pathv[j], sizeof wrk);
409: strlcat(wrk, ":", sizeof wrk);
410: }
411: }
412: /* find chg to items */
413: for (j = 0; j < g[1].gl_matchc; j++) {
414: for (i = 0; i < g[0].gl_matchc; i++)
415: if (!strcmp(g[0].gl_pathv[i], g[1].gl_pathv[j]))
416: break;
417: if (i == g[0].gl_matchc) {
418: strlcat(wrk, g[1].gl_pathv[j], sizeof wrk);
419: if (callback(wrk, 0) < 0)
420: break;
421: else
422: n++;
423: }
424: }
425: }
426:
427: globfree(&g[0]);
428: g[0] = g[1];
429: }
430: }
1.17.8.3 misho 431: #endif
1.3 misho 432:
433: globfree(&g[0]);
1.17.8.3 misho 434: #ifndef __linux__
1.3 misho 435: close(kq);
1.17.8.3 misho 436: close(d);
1.17.8.4! misho 437: #else
! 438: inotify_rm_watch(in, d);
! 439: close(in);
! 440: #endif
1.3 misho 441: return n;
442: }
1.7 misho 443:
444: /*
1.10 misho 445: * ioCreatePIDFile() - Create PID file
1.9 misho 446: *
1.7 misho 447: * @csName = PID filename
448: * @ifExists = !=0 if filename exists return error
449: * return: -1 error or 0 ok
450: */
1.15 misho 451: int
1.7 misho 452: ioCreatePIDFile(const char *csName, int ifExists)
453: {
454: int fd;
455: char str[STRSIZ] = { 0 };
456:
457: if (!csName)
458: return -1;
459:
460: fd = open(csName, O_WRONLY | O_CREAT | (ifExists ? O_EXCL : 0), 0644);
461: if (fd == -1) {
462: LOGERR;
463: return -1;
464: }
465: snprintf(str, sizeof str, "%d", getpid());
466: write(fd, str, strlen(str));
467: close(fd);
468: return 0;
469: }
1.8 misho 470:
471:
472: /*
1.10 misho 473: * ioSendFile() - AITNET sendfile() userland implementation, not dependant from OS
1.9 misho 474: *
1.8 misho 475: * @s = socket
476: * @csFile = file for send
477: * @sendLen = bytes to send, if 0 send all data
478: * @offset = start file offset
479: * @sndbuf = SO_SNDBUF value, if 0 use default
480: * return: 0 error, >0 ok, sended bytes
481: */
482: size_t
483: ioSendFile(int s, const char *csFile, size_t sendLen, off_t offset, int sndbuf)
484: {
485: void *addr;
486: int fd;
487: size_t len = 0;
488: register size_t off = 0;
489:
490: if (!csFile)
491: return 0;
492:
493: if (sndbuf)
494: if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof sndbuf) == -1) {
495: LOGERR;
496: return 0;
497: }
498:
499: fd = open(csFile, O_RDONLY);
500: if (fd == -1) {
501: LOGERR;
502: return 0;
503: }
504: if (!sendLen) {
505: sendLen = lseek(fd, 0, SEEK_END);
506: if (sendLen == -1) {
507: LOGERR;
508: close(fd);
509: return 0;
510: }
511: }
512: addr = mmap(NULL, sendLen, PROT_READ, MAP_SHARED, fd, offset);
513: if (addr == MAP_FAILED) {
514: LOGERR;
515: close(fd);
516: return 0;
517: } else
518: close(fd);
519:
520: while (off < sendLen && (len = write(s, addr + off, sendLen - off)) != -1)
521: off += len;
522: if (len == -1) {
523: LOGERR;
524: munmap(addr, sendLen);
525: return 0;
526: } else
527: len = off;
528:
529: if (len != sendLen) {
530: io_SetErr(ECANCELED, "Different sizes - request %u bytes, actually sended %u bytes\n",
531: sendLen, len);
532: len ^= len;
533: }
534:
535: munmap(addr, sendLen);
536: return len;
537: }
538:
539: /*
1.10 misho 540: * ioRecvFile() - Receive file from socket, fastest (zero-copy) way
1.9 misho 541: *
1.8 misho 542: * @s = socket
543: * @csFile = file for receive
544: * @recvLen = receive bytes
545: * @over = overwrite file if exists with mode like 0644
546: * @rcvbuf = SO_RCVBUF value, if 0 use default
547: * return: 0 error, >0 ok, received bytes
548: */
549: size_t
550: ioRecvFile(int s, const char *csFile, size_t recvLen, int over, int rcvbuf)
551: {
552: void *addr;
553: int fd;
554: size_t len = 0;
555: register size_t off = 0;
556: struct pollfd pfd = { s, POLLIN | POLLPRI, 0 };
557:
558: if (!csFile || !recvLen)
559: return 0;
560: if (!over && !access(csFile, F_OK))
561: return 0;
562:
563: if (rcvbuf)
564: if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof rcvbuf) == -1) {
565: LOGERR;
566: return 0;
567: }
568:
569: fd = open(csFile, O_WRONLY | O_CREAT | O_TRUNC, over);
570: if (fd == -1) {
571: LOGERR;
572: unlink(csFile);
573: return 0;
574: }
1.10 misho 575: if (ftruncate(fd, recvLen) == -1) {
1.8 misho 576: LOGERR;
577: close(fd);
578: unlink(csFile);
579: return 0;
580: }
581: addr = mmap(NULL, recvLen, PROT_WRITE, MAP_SHARED, fd, 0);
582: if (addr == MAP_FAILED) {
583: LOGERR;
584: close(fd);
585: unlink(csFile);
586: return 0;
587: } else
588: close(fd);
589:
590: while (off < recvLen && poll(&pfd, 1, RECV_TIMEOUT) != -1)
591: while (off < recvLen && (len = read(s, addr + off, recvLen - off)) != -1)
592: off += len;
593: if (len == -1) {
594: LOGERR;
595: munmap(addr, recvLen);
596: unlink(csFile);
597: return 0;
598: } else
599: len = off;
600:
601: if (len != recvLen)
602: io_SetErr(EAGAIN, "Different sizes - request %u bytes, actually received %u bytes\n",
603: recvLen, len);
604:
605: munmap(addr, recvLen);
606: return len;
607: }
1.16 misho 608:
609: /*
610: * ioRealFileName() - Get real file name
611: *
612: * @fname = filename
613: * return: =NULL error or !=NULL real filename, should be free with e_free()
614: */
615: char *
616: ioRealFileName(const char *fname)
617: {
618: char *str = NULL;
619: struct stat sb;
620:
621: if (!fname)
622: return NULL;
623:
624: str = e_malloc(MAXPATHLEN);
625: if (!str) {
626: io_SetErr(elwix_GetErrno(), "%s", elwix_GetError());
627: return NULL;
628: } else
629: memset(str, 0, MAXPATHLEN);
630: if (readlink(fname, str, MAXPATHLEN) == -1) {
631: if (stat(fname, &sb) == -1) {
632: LOGERR;
633: e_free(str);
634: return NULL;
635: } else
636: strlcpy(str, fname, MAXPATHLEN);
637: }
638:
639: return str;
640: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>