--- libaitio/src/aitio.c 2010/02/23 22:54:52 1.1.1.1 +++ libaitio/src/aitio.c 2013/05/30 09:10:13 1.15 @@ -1,11 +1,48 @@ /************************************************************************* -* (C) 2010 AITNET ltd - Sofia/Bulgaria - -* by Michael Pounov +* (C) 2010 AITNET ltd - Sofia/Bulgaria - +* by Michael Pounov * * $Author: misho $ -* $Id: aitio.c,v 1.1.1.1 2010/02/23 22:54:52 misho Exp $ +* $Id: aitio.c,v 1.15 2013/05/30 09:10:13 misho Exp $ * -*************************************************************************/ +************************************************************************** +The ELWIX and AITNET software is distributed under the following +terms: + +All of the documentation and software included in the ELWIX and AITNET +Releases is copyrighted by ELWIX - Sofia/Bulgaria + +Copyright 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 + by Michael Pounov . All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. All advertising materials mentioning features or use of this software + must display the following acknowledgement: +This product includes software developed by Michael Pounov +ELWIX - Embedded LightWeight unIX and its contributors. +4. Neither the name of AITNET nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY AITNET AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. +*/ #include "global.h" @@ -18,39 +55,44 @@ char io_Error[STRSIZ]; // io_GetErrno() Get error code of last operation -inline int io_GetErrno() +int +io_GetErrno() { return io_Errno; } // io_GetError() Get error text of last operation -inline const char *io_GetError() +const char * +io_GetError() { return io_Error; } // io_SetErr() Set error to variables for internal use!!! -inline void io_SetErr(int eno, char *estr, ...) +void +io_SetErr(int eno, char *estr, ...) { va_list lst; io_Errno = eno; - memset(io_Error, 0, STRSIZ); + memset(io_Error, 0, sizeof io_Error); va_start(lst, estr); - vsnprintf(io_Error, STRSIZ, estr, lst); + vsnprintf(io_Error, sizeof io_Error, estr, lst); va_end(lst); } /* - * ioPromptRead() Read data from input h[0] with prompt to output h[1] + * ioPromptRead() - Read data from input h[0] with prompt to output h[1] + * * @h = file handles h[0] = input, h[1] = output, if NULL use stdin, stdout * @csPrompt = Prompt before input, may be NULL * @psData = Readed data * @dataLen = Length of data * return: 0 EOF; -1 error:: can`t read; >0 count of readed chars */ -int ioPromptRead(int *h, const char *csPrompt, char * __restrict psData, int dataLen) +int +ioPromptRead(int *h, const char *csPrompt, char * __restrict psData, int dataLen) { int ok = 0; FILE *inp, *out; @@ -95,7 +137,8 @@ int ioPromptRead(int *h, const char *csPrompt, char * } /* - * ioPromptPassword() Read password from input h[0] with prompt to output h[1] + * ioPromptPassword() - Read password from input h[0] with prompt to output h[1] + * * @h = file handles h[0] = input, h[1] = output, if NULL use stdin, stdout * @csPrompt = Prompt before input, may be NULL * @psPass = Readed password @@ -103,7 +146,8 @@ int ioPromptRead(int *h, const char *csPrompt, char * * @confirm = Confirm password, 0 - get password, !=0 Ask for confirmation * return: 0 EOF; -1 error:: can`t read; >0 count of readed chars */ -int ioPromptPassword(int *h, const char *csPrompt, char * __restrict psPass, int passLen, int confirm) +int +ioPromptPassword(int *h, const char *csPrompt, char * __restrict psPass, int passLen, int confirm) { int ret, ok = 0; FILE *inp, *out; @@ -180,125 +224,326 @@ next: } /* - * ioRegexVerify() Function for verify data match in regex expression - * @csRegex = Regulare expression pattern - * @csData = Data for check and verify - * @startPos = Return start positions - * @endPos = Return end positions - * return: NULL not match or error; !=NULL begin of matched data + * ioMkDir() - Function for racursive directory creation and validation + * + * @csDir = Full directory path + * @mode = Mode for directory creation if missing dir + * return: -1 error, 0 directory path exist, >0 created missing dirs */ -const char *ioRegexVerify(const char *csRegex, const char *csData, int *startPos, int *endPos) +int +ioMkDir(const char *csDir, int mode) { - regex_t re; - regmatch_t match; - char szErr[STRSIZ]; - int ret, flg; - const char *pos; + char *str, *s, *pbrk, szOld[MAXPATHLEN] = { 0 }; + register int cx = -1; - if (!csRegex || !csData) - return NULL; + if (!csDir) + return cx; - if ((ret = regcomp(&re, csRegex, REG_EXTENDED))) { - regerror(ret, &re, szErr, STRSIZ); - io_SetErr(ret, "Error:: %s\n", szErr); - regfree(&re); - return NULL; + str = e_strdup(csDir); + if (!str) { + LOGERR; + return cx; } - for (ret = flg = 0, pos = csData; !(ret = regexec(&re, pos, 1, &match, flg)); - pos += match.rm_eo, flg = REG_NOTBOL) { - if (startPos) - *startPos = match.rm_so; - if (endPos) - *endPos = match.rm_eo; + getcwd(szOld, MAXPATHLEN); + if (*str == '/') + chdir("/"); - pos += match.rm_so; - break; + for (cx = 0, s = strtok_r(str, "/", &pbrk); s; s = strtok_r(NULL, "/", &pbrk)) { + if (mkdir(s, mode) == -1) { + if (errno != EEXIST) { + LOGERR; + cx = -1; + goto end; + } + } else + cx++; + + if (chdir(s) == -1) { + LOGERR; + cx = -1; + goto end; + } } +end: + chdir(szOld); + e_free(str); + return cx; +} - if (ret) { - regerror(ret, &re, szErr, STRSIZ); - io_SetErr(ret, "Error:: %s\n", szErr); - pos = NULL; +/* + * ioWatchDirLoop() - Function for watching changes in directory and fire callback + * + * @csDir = Full directory path + * @callback = Callback if raise event! nOp -1 delete, 0 change/move, 1 create + * return: -1 error, !=-1 ok, number of total signaled events +*/ +int +ioWatchDirLoop(const char *csDir, int (*callback)(const char *csName, int nOp)) +{ + glob_t g[2] = {{ 0 }, { 0 }}; + int d, kq, n = 0; + register int j, i; + struct kevent req, chg; + char wrk[MAXPATHLEN * 2], str[MAXPATHLEN] = { 0 }; + + if (!csDir || !callback) + return 0; + + strlcpy(str, csDir, MAXPATHLEN); + strlcat(str, "/*", MAXPATHLEN); + + kq = kqueue(); + if (kq == -1) { + LOGERR; + return -1; } + d = open(csDir, O_RDONLY); + if (d == -1) { + LOGERR; + close(kq); + return -1; + } - regfree(&re); - return pos; + EV_SET(&req, d, EVFILT_VNODE, EV_ADD | EV_CLEAR, NOTE_WRITE, 0, 0); + + if ((n = glob(str, GLOB_NOCHECK, NULL, &g[0]))) { + LOGERR; + close(d); + close(kq); + return -1; + } /*else + ioDEBUG(3, "Start files %d in %s\n", g[0].gl_matchc, str);*/ + + while (kevent(kq, &req, 1, &chg, 1, NULL) > 0) { + /*ioDEBUG(1, "Event:: req=0x%x -> chg=0x%x data=%x\n", req.fflags, chg.fflags, chg.data);*/ + + if (!glob(str, GLOB_NOCHECK, NULL, &g[1])) { + /*ioDEBUG(3, "Diffs %d <> %d\n", g[0].gl_matchc, g[1].gl_matchc);*/ + + if (g[0].gl_matchc != g[1].gl_matchc) { + /* find new items */ + for (j = 0; j < g[1].gl_matchc; j++) { + for (i = 0; i < g[0].gl_matchc; i++) + if (!strcmp(g[0].gl_pathv[i], g[1].gl_pathv[j])) + break; + if (i == g[0].gl_matchc) { + if (callback(g[1].gl_pathv[j], 1) < 0) + break; + else + n++; + } + } + /* find del items */ + for (j = 0; j < g[0].gl_matchc; j++) { + for (i = 0; i < g[1].gl_matchc; i++) + if (!strcmp(g[1].gl_pathv[i], g[0].gl_pathv[j])) + break; + if (i == g[1].gl_matchc) { + if (callback(g[0].gl_pathv[j], -1) < 0) + break; + else + n++; + } + } + } else { + /* find chg from items */ + for (j = 0; j < g[0].gl_matchc; j++) { + for (i = 0; i < g[1].gl_matchc; i++) + if (!strcmp(g[1].gl_pathv[i], g[0].gl_pathv[j])) + break; + if (i == g[1].gl_matchc) { + strlcpy(wrk, g[0].gl_pathv[j], sizeof wrk); + strlcat(wrk, ":", sizeof wrk); + } + } + /* find chg to items */ + for (j = 0; j < g[1].gl_matchc; j++) { + for (i = 0; i < g[0].gl_matchc; i++) + if (!strcmp(g[0].gl_pathv[i], g[1].gl_pathv[j])) + break; + if (i == g[0].gl_matchc) { + strlcat(wrk, g[1].gl_pathv[j], sizeof wrk); + if (callback(wrk, 0) < 0) + break; + else + n++; + } + } + } + + globfree(&g[0]); + g[0] = g[1]; + } + } + + globfree(&g[0]); + close(d); + close(kq); + return n; } /* - * ioRegexGet() Function for get data match in regex expression - * @csRegex = Regulare expression pattern - * @csData = Data from get - * @psString = Returned string if match - * @strLen = Length of string - * return: 0 not match; >0 count of returned chars -*/ -int ioRegexGet(const char *csRegex, const char *csData, char * __restrict psString, int strLen) + * ioCreatePIDFile() - Create PID file + * + * @csName = PID filename + * @ifExists = !=0 if filename exists return error + * return: -1 error or 0 ok + */ +int +ioCreatePIDFile(const char *csName, int ifExists) { - int sp, ep, len; - const char *str; + int fd; + char str[STRSIZ] = { 0 }; - if (!csRegex || !csData) + if (!csName) return -1; - str = ioRegexVerify(csRegex, csData, &sp, &ep); - if (!str) + fd = open(csName, O_WRONLY | O_CREAT | (ifExists ? O_EXCL : 0), 0644); + if (fd == -1) { + LOGERR; + return -1; + } + snprintf(str, sizeof str, "%d", getpid()); + write(fd, str, strlen(str)); + close(fd); + return 0; +} + + +/* + * ioSendFile() - AITNET sendfile() userland implementation, not dependant from OS + * + * @s = socket + * @csFile = file for send + * @sendLen = bytes to send, if 0 send all data + * @offset = start file offset + * @sndbuf = SO_SNDBUF value, if 0 use default + * return: 0 error, >0 ok, sended bytes + */ +size_t +ioSendFile(int s, const char *csFile, size_t sendLen, off_t offset, int sndbuf) +{ + void *addr; + int fd; + size_t len = 0; + register size_t off = 0; + + if (!csFile) return 0; - len = ep - sp; - if (psString && strLen) { - memset(psString, 0, strLen); - strncpy(psString, str, strLen <= len ? strLen - 1 : len); + if (sndbuf) + if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof sndbuf) == -1) { + LOGERR; + return 0; + } + + fd = open(csFile, O_RDONLY); + if (fd == -1) { + LOGERR; + return 0; } + if (!sendLen) { + sendLen = lseek(fd, 0, SEEK_END); + if (sendLen == -1) { + LOGERR; + close(fd); + return 0; + } + } + addr = mmap(NULL, sendLen, PROT_READ, MAP_SHARED, fd, offset); + if (addr == MAP_FAILED) { + LOGERR; + close(fd); + return 0; + } else + close(fd); + while (off < sendLen && (len = write(s, addr + off, sendLen - off)) != -1) + off += len; + if (len == -1) { + LOGERR; + munmap(addr, sendLen); + return 0; + } else + len = off; + + if (len != sendLen) { + io_SetErr(ECANCELED, "Different sizes - request %u bytes, actually sended %u bytes\n", + sendLen, len); + len ^= len; + } + + munmap(addr, sendLen); return len; } /* - * ioRegexReplace() Function for replace data match in regex expression with newdata - * @csRegex = Regulare expression pattern - * @csData = Source data - * @csNew = Data for replace - * return: NULL not match or error; !=NULL allocated new string, must be free after use! -*/ -char *ioRegexReplace(const char *csRegex, const char *csData, const char *csNew) + * ioRecvFile() - Receive file from socket, fastest (zero-copy) way + * + * @s = socket + * @csFile = file for receive + * @recvLen = receive bytes + * @over = overwrite file if exists with mode like 0644 + * @rcvbuf = SO_RCVBUF value, if 0 use default + * return: 0 error, >0 ok, received bytes + */ +size_t +ioRecvFile(int s, const char *csFile, size_t recvLen, int over, int rcvbuf) { - int sp, ep, len; - char *str = NULL; + void *addr; + int fd; + size_t len = 0; + register size_t off = 0; + struct pollfd pfd = { s, POLLIN | POLLPRI, 0 }; - if (!csRegex || !csData) - return NULL; + if (!csFile || !recvLen) + return 0; + if (!over && !access(csFile, F_OK)) + return 0; - if (!ioRegexVerify(csRegex, csData, &sp, &ep)) - return NULL; + if (rcvbuf) + if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof rcvbuf) == -1) { + LOGERR; + return 0; + } - // ___ before match - len = sp + 1; - str = malloc(len); - if (!str) { + fd = open(csFile, O_WRONLY | O_CREAT | O_TRUNC, over); + if (fd == -1) { LOGERR; - return NULL; - } else - strlcpy(str, csData, len); - // * replace match * - if (csNew) { - len += strlen(csNew); - str = realloc(str, len); - if (!str) { - LOGERR; - return NULL; - } else - strlcat(str, csNew, len); + unlink(csFile); + return 0; } - // after match ___ - len += strlen(csData) - ep; - str = realloc(str, len); - if (!str) { + if (ftruncate(fd, recvLen) == -1) { LOGERR; - return NULL; + close(fd); + unlink(csFile); + return 0; + } + addr = mmap(NULL, recvLen, PROT_WRITE, MAP_SHARED, fd, 0); + if (addr == MAP_FAILED) { + LOGERR; + close(fd); + unlink(csFile); + return 0; } else - strlcat(str, csData + ep, len); + close(fd); - return str; + while (off < recvLen && poll(&pfd, 1, RECV_TIMEOUT) != -1) + while (off < recvLen && (len = read(s, addr + off, recvLen - off)) != -1) + off += len; + if (len == -1) { + LOGERR; + munmap(addr, recvLen); + unlink(csFile); + return 0; + } else + len = off; + + if (len != recvLen) + io_SetErr(EAGAIN, "Different sizes - request %u bytes, actually received %u bytes\n", + recvLen, len); + + munmap(addr, recvLen); + return len; }