2 * This file is part of the OpenPTS project.
4 * The Initial Developer of the Original Code is International
5 * Business Machines Corporation. Portions created by IBM
6 * Corporation are Copyright (C) 2010 International Business
7 * Machines Corporation. All Rights Reserved.
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the Common Public License as published by
11 * IBM Corporation; either version 1 of the License, or (at your option)
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * Common Public License for more details.
19 * You should have received a copy of the Common Public License
20 * along with this program; if not, a copy can be viewed at
21 * http://www.opensource.org/licenses/cpl1.0.php.
26 * \brief misc functions
27 * @author Seiji Munetoh <munetoh@users.sourceforge.jp>
29 * cleanup 2012-01-05 SM (remains one lint error)
30 * src/misc.c:448: If you can, use sizeof(ptr) instead of 3 as the 2nd arg to snprintf. [runtime/printf] [3]
42 #include <search.h> // hash table
44 #include <sys/types.h>
45 #include <sys/socket.h>
47 #include <netinet/in.h>
54 Due to the frequent use of malloc/free in the code base (as opposed to
55 stack based allocation) these wrapper routines were added for easier debugging
56 - after their introduction several asserts fired that found genuine bugs. In
57 theory, for most programs that are not daemons we never really need to free memory
58 since it gets freed on program exit anyway. In addition, malloc should never
59 really fail - if so it usually indicates a programming error.
60 NOTE: On AIX the address 0x00000000 is a valid address, corresponding to a
61 read-only map present in the address space of all running programs.
64 #ifndef ALWAYS_ASSERT_ON_BAD_ALLOC
65 void *xmalloc(size_t size) {
66 char *result = malloc(size);
68 LOG(LOG_ERR, "Failed to allocate %d bytes of memory\n", size);
69 ERROR(NLS(MS_OPENPTS, OPENPTS_MALLOC_FAIL,
76 void *xmalloc_assert(size_t size) {
77 char *result = malloc(size);
79 LOG(LOG_ERR, "Failed to allocate %d bytes of memory\n", size);
80 ERROR(NLS(MS_OPENPTS, OPENPTS_ABORT,
81 "Abort to return NULL pointer - cannot continue\n"));
87 void xfree(void *buf) {
89 LOG(LOG_ERR, "Freeing a NULL pointer is bad");
92 #ifndef NEVER_FREE_MEMORY
98 #ifndef ALWAYS_ASSERT_ON_BAD_ALLOC
100 * malloc and copy string
102 char *smalloc(char *str) {
106 DEBUG("null input\n");
110 /* check string length */
113 LOG(LOG_ERR, "Failed to duplicate string '%s'\n", str);
122 * malloc and copy string
124 char *smalloc_assert(char *str) {
128 DEBUG("smalloc - string is NULL\n");
132 /* check string length */
135 LOG(LOG_ERR, "Failed to duplicate string '%s'\n", str);
136 ERROR(NLS(MS_OPENPTS, OPENPTS_ABORT,
137 "Abort to return NULL pointer - cannot continue\n"));
145 * malloc and copy string with length
153 char *snmalloc(char *str, int len) {
158 LOG(LOG_ERR, "smalloc - string is NULL\n");
167 out = xmalloc_assert(len);
168 strncpy(out, str, len);
169 /* ensure always NULL-terminated */
172 out = strndup(str, len);
178 * get NEW string buffer
180 * snmalloc2("ABCDEF", 2,3) => "CDE"
187 BYTE *snmalloc2(BYTE *buf, int offset, int len) {
190 LOG(LOG_ERR, "null input");
194 LOG(LOG_ERR, "offset < 0");
198 LOG(LOG_ERR, "len < 0");
203 BYTE *output = (BYTE *) xmalloc(len + 1);
204 if (output == NULL) {
208 memcpy((void *) output, (void *) &buf[offset], len);
218 void sfree(char *str) {
221 LOG(LOG_ERR, "null input");
232 * get fullpathname of file
233 * This malloc new buf for the fullpathname.
236 * basepath must be start from /
238 * UnitTest : check_conf
241 char *getFullpathName(char *basepath, char *filename) {
242 char *fullpath = NULL;
248 if (basepath == NULL) {
249 LOG(LOG_ERR, "null input");
252 if (filename == NULL) {
253 LOG(LOG_ERR, "null input");
257 /* start from root */
258 if (filename[0] == '/') {
259 /* seems fullpath, copy the filename to new buf */
260 return smalloc(filename);
263 /* basepath + filename */
264 if (basepath[0] != '/') {
265 /* relative path -> error when it run as daemon */
266 LOG(LOG_TODO, "getFullpathName() - basepath, '%s' is not started from root\n", basepath);
273 0x00 /AAA/ + BBB => /AAA/BBB
274 0x01 /AAA/ + ./BBB => /AAA/BBB
275 0x10 /AAA + BBB => /AAA/BBB
276 0x11 /AAA + ./BBB => /AAA/BBB
278 basepath_len = strlen(basepath);
279 filename_len = strlen(filename);
281 if (filename_len < 2) {
282 LOG(LOG_ERR, "ilename len < 2\n");
286 /* basepath has "/" at end. else add "/" */
287 if (basepath[basepath_len - 1] != '/') {
290 /* filename has "./" at start ? remove */
291 if ((filename[0] == '.') && (filename[1] == '/')) {
298 /* /AAA/ + BBB => /AAA/BBB */
299 fullpath = xmalloc_assert(basepath_len + filename_len + 1);
300 memcpy(fullpath, basepath, basepath_len);
301 memcpy(&fullpath[basepath_len], filename, filename_len);
302 fullpath[basepath_len + filename_len] = 0;
305 /* /AAA/ + ./BBB => /AAA/BBB */
306 fullpath = xmalloc_assert(basepath_len + filename_len + 1 - 2);
307 memcpy(fullpath, basepath, basepath_len);
308 memcpy(&fullpath[basepath_len], filename + 2, filename_len - 2);
309 fullpath[basepath_len + filename_len - 2] = 0;
312 /* /AAA + BBB => /AAA/BBB */
313 fullpath = xmalloc_assert(basepath_len + 1 + filename_len + 1);
314 memcpy(fullpath, basepath, basepath_len);
315 fullpath[basepath_len] = '/';
316 memcpy(&fullpath[basepath_len + 1], filename, filename_len);
317 fullpath[basepath_len + filename_len + 1] = 0;
320 /* /AAA + ./BBB => /AAA/BBB */
321 fullpath = xmalloc_assert(basepath_len + 1 + filename_len + 1 - 2);
322 memcpy(fullpath, basepath, basepath_len);
323 fullpath[basepath_len] = '/';
324 memcpy(&fullpath[basepath_len + 1], filename + 2, filename_len - 2);
325 fullpath[basepath_len + filename_len - 1] = 0;
328 LOG(LOG_ERR, "internal error\n");
336 * Get dirname from fullpath filename
338 * this malloc new string
340 * /AAA/BBB/CCC/DDD => /AAA/BBB/CCC/
343 char *getFullpathDir(char *filename) {
344 char *fullpath = NULL;
349 if (filename == NULL) {
350 LOG(LOG_ERR, "null input");
354 filename_len = strlen(filename);
356 for (i = filename_len; i > 0; i--) {
357 if (filename[i] == '/') {
358 // slash = &filename[i];
363 fullpath = xmalloc_assert(i+2); // check/abort
364 memcpy(fullpath, filename, i+1);
373 * Little Endian (Intel)
375 UINT32 byte2uint32(BYTE *b) {
379 LOG(LOG_ERR, "byte2uint32 - NULL");
380 OUTPUT("About to return NULL pointer - cannot continue\n"); // TODO
401 char * trim(char *str) {
407 LOG(LOG_ERR, "null input");
412 strLen = strlen(str);
418 end = str + strLen - 1;
420 /* skip space at start */
421 while (*str == ' ') {
426 /* remove space at tail */
428 while (*end == ' ') {
437 * BYTE* -> Hex string (malloc)
440 char *getHexString(BYTE *bin, int size) {
448 LOG(LOG_ERR, "null input");
452 buf = xmalloc_assert(size * 2 + 1); // check/abort
454 for (i = 0; i < size; i++) {
455 // len = snprintf(ptr, sizeof(ptr), "%02x", bin[i]);
456 len = snprintf(ptr, 3, "%02x", bin[i]);
458 LOG(LOG_ERR, "FATAL");
473 char *outBuf, int outBufLen, char *head, BYTE *data, int num, char *tail) {
478 if (outBuf == NULL) {
479 LOG(LOG_ERR, "null input");
483 LOG(LOG_ERR, "null input");
487 LOG(LOG_ERR, "null input");
491 LOG(LOG_ERR, "null input");
496 outSoFar += snprintf(outBuf, outBufLen, "%s[%d]=", head, num);
498 for (i = 0; i < num; i++) {
499 if ( outSoFar < outBufLen ) {
500 outSoFar += snprintf(&outBuf[outSoFar], outBufLen - outSoFar, "%02X", data[i]);
503 if ( outSoFar < outBufLen ) {
504 snprintf(&outBuf[outSoFar], outBufLen - outSoFar, "%s", tail);
508 void printHex(char *head, BYTE *data, int num, char *tail) {
511 snprintHex(outBuf, 1023, head, data, num, tail);
512 /* I could just use OUTPUT(outBuf), but since warnings are errors
513 I have to use this less efficient form */
514 OUTPUT("%s", outBuf);
517 void debugHex(char *head, BYTE *data, int num, char *tail) {
519 snprintHex(outBuf, 1023, head, data, num, tail);
520 writeLog(LOG_DEBUG, outBuf);
523 void fprintHex(FILE *fp, BYTE *data, int num) {
528 LOG(LOG_ERR, "null input");
532 LOG(LOG_ERR, "null input");
537 for (i = 0; i < num; i++) {
538 fprintf(fp, "%02X", data[i]);
545 UINT32 b2l(UINT32 in) {
565 * @param filename output filename
566 * @param len message length
572 char *filename, int len, BYTE * msg) {
574 const int max_retries = 10;
580 LOG(LOG_ERR, "len <0 \n");
584 LOG(LOG_ERR, "msg is NULL \n");
587 if (filename == NULL) {
588 LOG(LOG_ERR, "filename is NULL \n");
592 if ((fp = fopen(filename, "w+b")) == NULL) {
593 LOG(LOG_ERR, "File open failed, %s \n", filename);
594 return PTS_FATAL; // TODO(munetoh): set PTS error code.
597 /* If the filesystem is full, we shouldn't hang whilst trying to
598 write the file to disk -> we only allow so many attempts. */
599 while (n_tries < max_retries) {
600 int bytes_written = fwrite(&msg[ptr], 1, len, fp);
601 /* DEBUG_IFM(" %s %d %d\n", filename, rc, len); */
602 ptr += bytes_written;
603 len -= bytes_written;
610 /* DEBUG_IFM(" %s %d \n", filename, len); */
615 LOG(LOG_ERR, "After %d retries still have %d bytes unwritten to '%s'\n", max_retries, len, filename);
625 UINT32 getUint32(BYTE *buf) {
630 LOG(LOG_ERR, "null input");
633 // TODO check the size?
635 data = (buf[0] << 24) |
647 int makeDir(char *dirname) {
648 int rc = PTS_SUCCESS;
652 if (dirname == NULL) {
653 LOG(LOG_ERR, "null input");
658 rc = mkdir(dirname, S_IRUSR | S_IWUSR | S_IXUSR |
659 S_IRGRP | S_IWGRP | S_IXGRP);
663 LOG(LOG_ERR, "mkdir %s failed, EACCES", dirname);
668 rc = lstat(dirname, &st);
670 if ((st.st_mode & S_IFMT) != S_IFDIR) {
671 LOG(LOG_ERR, "directory, %s is not a directory %x %x\n",
672 dirname, (st.st_mode & S_IFMT), S_IFDIR);
673 rc = PTS_INTERNAL_ERROR;
679 LOG(LOG_ERR, "lstat(%s) failed, errno=%d\n", dirname, errno);
684 LOG(LOG_ERR, "mkdir %s failed, EFAULT", dirname);
687 // TODO add others :-)
689 LOG(LOG_ERR, "mkdir %s failed, errono = 0x%X", dirname, errno);
702 * PTS_SUCCESS - exist
703 * PTS_INTERNAL_ERROR - not exist or not a dir
706 int checkDir(char *dirname) {
710 if (dirname == NULL) {
711 LOG(LOG_ERR, "null input");
715 if (lstat(dirname, &st) == -1) {
717 return PTS_INTERNAL_ERROR; // TODO OPENPTS_DIR_MISSING;
718 } else if ((st.st_mode & S_IFMT) != S_IFDIR) {
720 return PTS_INTERNAL_ERROR;
723 return PTS_SUCCESS; // TODO OPENPTS_DIR_EXIST;
727 * Check file (reguler file)
729 int checkFile(char *filename) {
733 if (filename == NULL) {
734 LOG(LOG_ERR, "null input");
738 if (lstat(filename, &st) == -1) {
740 return OPENPTS_FILE_MISSING;
741 } else if ((st.st_mode & S_IFMT) != S_IFREG) {
743 return PTS_INTERNAL_ERROR;
746 return OPENPTS_FILE_EXISTS;
752 ssize_t wrapRead(int fd, void *buf, size_t count) {
757 LOG(LOG_ERR, "null input");
762 len = read(fd, buf, count);
763 if ((len < 0) && (errno == EAGAIN || errno == EINTR)) {
773 ssize_t wrapWrite(int fd, const void *buf, size_t count) {
778 LOG(LOG_ERR, "null input");
783 len = write(fd, buf, count);
784 if ((len < 0) && (errno == EAGAIN || errno == EINTR)) {
792 * recursive part of unlinkDir()
794 static int unlinkDir_(char *dirPath) {
796 struct dirent *entry;
797 char path[PATH_MAX + 1];
802 if (dirPath == NULL) {
803 LOG(LOG_ERR, "null input");
807 dirHandle = opendir(dirPath);
808 if (dirHandle == NULL) {
809 LOG(LOG_ERR, "opendir(%s) fail", dirPath);
816 rc = readdir_r(dirHandle, &dr, &entry);
818 if (entry == NULL) break;
820 if (strcmp(".", entry->d_name) == 0) continue;
821 if (strcmp("..", entry->d_name) == 0) continue;
823 snprintf(path, sizeof(path), "%s/%s", dirPath, entry->d_name);
824 if (stat(path, &st) != 0) {
825 LOG(LOG_ERR, "stat(%s) fail", path);
830 if (S_ISDIR(st.st_mode)) {
831 if (unlinkDir_(path) != 0) {
835 } else if (S_ISREG(st.st_mode)) {
836 if (unlink(path) != 0) {
837 LOG(LOG_ERR, "unlink(%s) fail", path);
845 if (rmdir(dirPath) != 0) {
846 LOG(LOG_ERR, "rmdir(%s) fail", dirPath);
860 * Recursively destroy the content of a directory
862 int unlinkDir(const char *dirPath) {
863 char path[PATH_MAX + 1];
866 if (dirPath == NULL) {
867 LOG(LOG_ERR, "null input");
870 if (dirPath[0] == '\0' || strlen(dirPath) >= PATH_MAX) {
871 LOG(LOG_ERR, "bad dirPath, %s", dirPath);
875 strncpy(path, dirPath, sizeof(path));
876 // there is at least one byte free before path[PATH_MAX]
878 return unlinkDir_(path);