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