Annotation of libaitio/src/aitio.c, revision 1.17.8.2
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.2! misho 6: * $Id: aitio.c,v 1.17.8.1 2016/08/10 13:59:44 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];
159: struct sgttyb tty_state;
160:
161: if (!psPass || !passLen)
162: return -1;
163:
164: inp = fdopen(!h ? 0 : h[0], "r");
165: if (!inp) {
166: LOGERR;
167: return -1;
168: }
169: out = fdopen(!h ? 1 : h[1], "w");
170: if (!out) {
171: LOGERR;
172: return -1;
173: }
174:
175: if (ioctl(fileno(inp), TIOCGETP, &tty_state) == -1) {
176: LOGERR;
177: return -1;
178: } else {
179: tty_state.sg_flags &= ~ECHO;
180: if (ioctl(fileno(inp), TIOCSETP, &tty_state) == -1) {
181: LOGERR;
182: return -1;
183: }
184: }
185:
186: while (!ok) {
187: switch ((ret = ioPromptRead(h, (!csPrompt || !*csPrompt) ? "Password:" : csPrompt,
188: szLine[0], STRSIZ))) {
189: case -1:
190: LOGERR;
191: ok = -1;
192: case 0:
193: goto next;
194: }
195: if (confirm) {
196: fprintf(out, "\n");
197: fflush(out);
198:
199: switch (ioPromptRead(h, "Password confirm:", szLine[1], STRSIZ)) {
200: case -1:
201: LOGERR;
202: ok = -1;
203: goto next;
204: case 0:
205: default:
206: if (strcmp(szLine[0], szLine[1])) {
207: fprintf(out, "\n\07\07Mismatch - Try again!\n");
208: fflush(out);
209: continue;
210: }
211: }
212: }
213:
214: strlcpy(psPass, szLine[0], passLen);
215: ok = ret;
216: fprintf(out, "\n");
217: fflush(out);
218: }
219:
220: next:
221: tty_state.sg_flags |= ECHO;
222: if (ioctl(fileno(inp), TIOCSETP, &tty_state) == -1) {
223: LOGERR;
224: return -1;
225: }
226:
227: return ok;
228: }
229:
230: /*
1.10 misho 231: * ioMkDir() - Function for racursive directory creation and validation
1.9 misho 232: *
1.2 misho 233: * @csDir = Full directory path
234: * @mode = Mode for directory creation if missing dir
235: * return: -1 error, 0 directory path exist, >0 created missing dirs
236: */
237: int
238: ioMkDir(const char *csDir, int mode)
239: {
240: char *str, *s, *pbrk, szOld[MAXPATHLEN] = { 0 };
241: register int cx = -1;
242:
243: if (!csDir)
244: return cx;
245:
1.14 misho 246: str = e_strdup(csDir);
1.2 misho 247: if (!str) {
248: LOGERR;
249: return cx;
250: }
251:
252: getcwd(szOld, MAXPATHLEN);
253: if (*str == '/')
254: chdir("/");
255:
256: for (cx = 0, s = strtok_r(str, "/", &pbrk); s; s = strtok_r(NULL, "/", &pbrk)) {
257: if (mkdir(s, mode) == -1) {
258: if (errno != EEXIST) {
259: LOGERR;
260: cx = -1;
261: goto end;
262: }
263: } else
264: cx++;
265:
266: if (chdir(s) == -1) {
267: LOGERR;
268: cx = -1;
269: goto end;
270: }
271: }
272: end:
273: chdir(szOld);
1.14 misho 274: e_free(str);
1.2 misho 275: return cx;
276: }
277:
1.3 misho 278: /*
1.10 misho 279: * ioWatchDirLoop() - Function for watching changes in directory and fire callback
1.9 misho 280: *
1.3 misho 281: * @csDir = Full directory path
282: * @callback = Callback if raise event! nOp -1 delete, 0 change/move, 1 create
283: * return: -1 error, !=-1 ok, number of total signaled events
284: */
285: int
286: ioWatchDirLoop(const char *csDir, int (*callback)(const char *csName, int nOp))
287: {
288: glob_t g[2] = {{ 0 }, { 0 }};
289: int d, kq, n = 0;
290: register int j, i;
291: struct kevent req, chg;
292: char wrk[MAXPATHLEN * 2], str[MAXPATHLEN] = { 0 };
293:
294: if (!csDir || !callback)
295: return 0;
296:
297: strlcpy(str, csDir, MAXPATHLEN);
298: strlcat(str, "/*", MAXPATHLEN);
299:
300: kq = kqueue();
301: if (kq == -1) {
302: LOGERR;
303: return -1;
304: }
305: d = open(csDir, O_RDONLY);
306: if (d == -1) {
307: LOGERR;
308: close(kq);
309: return -1;
310: }
311:
1.4 misho 312: EV_SET(&req, d, EVFILT_VNODE, EV_ADD | EV_CLEAR, NOTE_WRITE, 0, 0);
1.3 misho 313:
314: if ((n = glob(str, GLOB_NOCHECK, NULL, &g[0]))) {
315: LOGERR;
316: close(d);
317: close(kq);
318: return -1;
319: } /*else
320: ioDEBUG(3, "Start files %d in %s\n", g[0].gl_matchc, str);*/
321:
322: while (kevent(kq, &req, 1, &chg, 1, NULL) > 0) {
323: /*ioDEBUG(1, "Event:: req=0x%x -> chg=0x%x data=%x\n", req.fflags, chg.fflags, chg.data);*/
324:
325: if (!glob(str, GLOB_NOCHECK, NULL, &g[1])) {
326: /*ioDEBUG(3, "Diffs %d <> %d\n", g[0].gl_matchc, g[1].gl_matchc);*/
327:
328: if (g[0].gl_matchc != g[1].gl_matchc) {
329: /* find new items */
330: for (j = 0; j < g[1].gl_matchc; j++) {
331: for (i = 0; i < g[0].gl_matchc; i++)
332: if (!strcmp(g[0].gl_pathv[i], g[1].gl_pathv[j]))
333: break;
334: if (i == g[0].gl_matchc) {
335: if (callback(g[1].gl_pathv[j], 1) < 0)
336: break;
337: else
338: n++;
339: }
340: }
341: /* find del items */
342: for (j = 0; j < g[0].gl_matchc; j++) {
343: for (i = 0; i < g[1].gl_matchc; i++)
344: if (!strcmp(g[1].gl_pathv[i], g[0].gl_pathv[j]))
345: break;
346: if (i == g[1].gl_matchc) {
347: if (callback(g[0].gl_pathv[j], -1) < 0)
348: break;
349: else
350: n++;
351: }
352: }
353: } else {
354: /* find chg from items */
355: for (j = 0; j < g[0].gl_matchc; j++) {
356: for (i = 0; i < g[1].gl_matchc; i++)
357: if (!strcmp(g[1].gl_pathv[i], g[0].gl_pathv[j]))
358: break;
359: if (i == g[1].gl_matchc) {
360: strlcpy(wrk, g[0].gl_pathv[j], sizeof wrk);
361: strlcat(wrk, ":", sizeof wrk);
362: }
363: }
364: /* find chg to items */
365: for (j = 0; j < g[1].gl_matchc; j++) {
366: for (i = 0; i < g[0].gl_matchc; i++)
367: if (!strcmp(g[0].gl_pathv[i], g[1].gl_pathv[j]))
368: break;
369: if (i == g[0].gl_matchc) {
370: strlcat(wrk, g[1].gl_pathv[j], sizeof wrk);
371: if (callback(wrk, 0) < 0)
372: break;
373: else
374: n++;
375: }
376: }
377: }
378:
379: globfree(&g[0]);
380: g[0] = g[1];
381: }
382: }
383:
384: globfree(&g[0]);
385: close(d);
386: close(kq);
387: return n;
388: }
1.7 misho 389:
390: /*
1.10 misho 391: * ioCreatePIDFile() - Create PID file
1.9 misho 392: *
1.7 misho 393: * @csName = PID filename
394: * @ifExists = !=0 if filename exists return error
395: * return: -1 error or 0 ok
396: */
1.15 misho 397: int
1.7 misho 398: ioCreatePIDFile(const char *csName, int ifExists)
399: {
400: int fd;
401: char str[STRSIZ] = { 0 };
402:
403: if (!csName)
404: return -1;
405:
406: fd = open(csName, O_WRONLY | O_CREAT | (ifExists ? O_EXCL : 0), 0644);
407: if (fd == -1) {
408: LOGERR;
409: return -1;
410: }
411: snprintf(str, sizeof str, "%d", getpid());
412: write(fd, str, strlen(str));
413: close(fd);
414: return 0;
415: }
1.8 misho 416:
417:
418: /*
1.10 misho 419: * ioSendFile() - AITNET sendfile() userland implementation, not dependant from OS
1.9 misho 420: *
1.8 misho 421: * @s = socket
422: * @csFile = file for send
423: * @sendLen = bytes to send, if 0 send all data
424: * @offset = start file offset
425: * @sndbuf = SO_SNDBUF value, if 0 use default
426: * return: 0 error, >0 ok, sended bytes
427: */
428: size_t
429: ioSendFile(int s, const char *csFile, size_t sendLen, off_t offset, int sndbuf)
430: {
431: void *addr;
432: int fd;
433: size_t len = 0;
434: register size_t off = 0;
435:
436: if (!csFile)
437: return 0;
438:
439: if (sndbuf)
440: if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof sndbuf) == -1) {
441: LOGERR;
442: return 0;
443: }
444:
445: fd = open(csFile, O_RDONLY);
446: if (fd == -1) {
447: LOGERR;
448: return 0;
449: }
450: if (!sendLen) {
451: sendLen = lseek(fd, 0, SEEK_END);
452: if (sendLen == -1) {
453: LOGERR;
454: close(fd);
455: return 0;
456: }
457: }
458: addr = mmap(NULL, sendLen, PROT_READ, MAP_SHARED, fd, offset);
459: if (addr == MAP_FAILED) {
460: LOGERR;
461: close(fd);
462: return 0;
463: } else
464: close(fd);
465:
466: while (off < sendLen && (len = write(s, addr + off, sendLen - off)) != -1)
467: off += len;
468: if (len == -1) {
469: LOGERR;
470: munmap(addr, sendLen);
471: return 0;
472: } else
473: len = off;
474:
475: if (len != sendLen) {
476: io_SetErr(ECANCELED, "Different sizes - request %u bytes, actually sended %u bytes\n",
477: sendLen, len);
478: len ^= len;
479: }
480:
481: munmap(addr, sendLen);
482: return len;
483: }
484:
485: /*
1.10 misho 486: * ioRecvFile() - Receive file from socket, fastest (zero-copy) way
1.9 misho 487: *
1.8 misho 488: * @s = socket
489: * @csFile = file for receive
490: * @recvLen = receive bytes
491: * @over = overwrite file if exists with mode like 0644
492: * @rcvbuf = SO_RCVBUF value, if 0 use default
493: * return: 0 error, >0 ok, received bytes
494: */
495: size_t
496: ioRecvFile(int s, const char *csFile, size_t recvLen, int over, int rcvbuf)
497: {
498: void *addr;
499: int fd;
500: size_t len = 0;
501: register size_t off = 0;
502: struct pollfd pfd = { s, POLLIN | POLLPRI, 0 };
503:
504: if (!csFile || !recvLen)
505: return 0;
506: if (!over && !access(csFile, F_OK))
507: return 0;
508:
509: if (rcvbuf)
510: if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof rcvbuf) == -1) {
511: LOGERR;
512: return 0;
513: }
514:
515: fd = open(csFile, O_WRONLY | O_CREAT | O_TRUNC, over);
516: if (fd == -1) {
517: LOGERR;
518: unlink(csFile);
519: return 0;
520: }
1.10 misho 521: if (ftruncate(fd, recvLen) == -1) {
1.8 misho 522: LOGERR;
523: close(fd);
524: unlink(csFile);
525: return 0;
526: }
527: addr = mmap(NULL, recvLen, PROT_WRITE, MAP_SHARED, fd, 0);
528: if (addr == MAP_FAILED) {
529: LOGERR;
530: close(fd);
531: unlink(csFile);
532: return 0;
533: } else
534: close(fd);
535:
536: while (off < recvLen && poll(&pfd, 1, RECV_TIMEOUT) != -1)
537: while (off < recvLen && (len = read(s, addr + off, recvLen - off)) != -1)
538: off += len;
539: if (len == -1) {
540: LOGERR;
541: munmap(addr, recvLen);
542: unlink(csFile);
543: return 0;
544: } else
545: len = off;
546:
547: if (len != recvLen)
548: io_SetErr(EAGAIN, "Different sizes - request %u bytes, actually received %u bytes\n",
549: recvLen, len);
550:
551: munmap(addr, recvLen);
552: return len;
553: }
1.16 misho 554:
555: /*
556: * ioRealFileName() - Get real file name
557: *
558: * @fname = filename
559: * return: =NULL error or !=NULL real filename, should be free with e_free()
560: */
561: char *
562: ioRealFileName(const char *fname)
563: {
564: char *str = NULL;
565: struct stat sb;
566:
567: if (!fname)
568: return NULL;
569:
570: str = e_malloc(MAXPATHLEN);
571: if (!str) {
572: io_SetErr(elwix_GetErrno(), "%s", elwix_GetError());
573: return NULL;
574: } else
575: memset(str, 0, MAXPATHLEN);
576: if (readlink(fname, str, MAXPATHLEN) == -1) {
577: if (stat(fname, &sb) == -1) {
578: LOGERR;
579: e_free(str);
580: return NULL;
581: } else
582: strlcpy(str, fname, MAXPATHLEN);
583: }
584:
585: return str;
586: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>