/* $Id: misc.c,v 1.3 2006/09/17 08:19:15 maxim Exp $ * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "configure.h" #include "misc.h" #include "settings.h" #include "str.h" #include "ptrinfo.h" #if DEBUGLOG int Debug = 1; #else int Debug = 0; #endif char* progName = "undef"; char* logName[TYPE_MASK+1] = {"debug.log", "message.log", "error.log", "action.log", "access.log"}; int myPID; time_t now; struct tm now_tm; short now_day; char now_str[SHORT_STR]; #define N_OF_YEARS 10 static short days[N_OF_YEARS]; short getDay(struct tm* tm) { if (!days[1]) { int i, y; days[0] = 0; for (i = 0, y = 2000; i < N_OF_YEARS - 1; i++, y++) days[i + 1] = days[i] + (isleap(y) ? 366 : 365); } return 100 <= tm->tm_year && tm->tm_year <= 100 + N_OF_YEARS - 1 ? days[tm->tm_year - 100] + tm->tm_yday : 0; } struct tm* getDate(short day) { static struct tm tm; static short mdays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; int y = 0, m = 0; if (!days[1]) { int i, y; days[0] = 0; for (i = 0, y = 2000; i < N_OF_YEARS - 1; i++, y++) days[i + 1] = days[i] + (isleap(y) ? 366 : 365); } while (y < N_OF_YEARS - 1 && day >= days[y + 1]) y++; day -= days[y]; y += 2000; memset(&tm, 0, sizeof(tm)); tm.tm_yday = day; mdays[1] = isleap(y) ? 29 : 28; while (m < 11 && day >= mdays[m]) day -= mdays[m++]; tm.tm_mday = day + 1; tm.tm_mon = m; tm.tm_year = y - 1900; return &tm; } time_t getTimeByWeek(short year, short week) { static struct tm tm; static short mdays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; int m = 0; int day = week * 7 - 1; mdays[1] = isleap(year) ? 29 : 28; while (m < 11 && day >= mdays[m]) day -= mdays[m++]; if (day > 31) day = 31; memset(&tm, 0, sizeof(tm)); tm.tm_mday = day + 1; tm.tm_mon = m; tm.tm_year = year - 1900; return mktime(&tm); } time_t getTime() { static time_t prev = 0; time(&now); if (now != prev) { now_tm = *(localtime(&now)); now_day = getDay(&now_tm); strftime(now_str, SHORT_STR, "%a, %d %b %Y %H:%M:%S GMT", gmtime(&now)); prev = now; } return now; } char* time2str(time_t t, char type) { static int call = 0; static char string[4][64]; struct tm* ts = type < HTTP_TIME ? localtime(&t) : gmtime(&t); char *s = string[++call % 4]; char* format; switch (type) { case LOCAL_TIME: format = "%d/%b/%Y:%H:%M:%S"; break; case LOCAL_DATE: format = "%d/%b/%Y"; break; case TODAY_TIME: format = "%H:%M:%S"; break; case SQL_DATE: format = "%Y-%m-%d"; break; case SQL_TIME: format = "%Y-%m-%d %H:%M:%S"; break; case HTTP_TIME: format = "%a, %d %b %Y %H:%M:%S GMT"; break; case COOKIE_TIME: format = "%a, %d-%b-%Y %H:%M:%S GMT"; break; default: format = ""; } strftime(s, 64, format, ts); return s; } unsigned biRound(unsigned value, unsigned n) { if (value >= 0x80000000) return 0x80000000; while (value > n) n <<= 1; return n; } short countBits(int v) { short bits; for(bits = 0; v; v<<=1) if (v < 0) bits++; return bits; } int fileSize(char* filename) { struct stat st; if (stat(filename, &st) != -1) { return st.st_size; } // message(SYS|ERROR, "cannot stat '%s'", filename); return -2048; } int fdSize(int fd) { struct stat st; if (fstat(fd, &st) != -1) { return st.st_size; } // message(SYS|ERROR, "cannot stat '%s'", filename); return -2048; } int fileSizeKb(char* filename) { return (fileSize(filename) + 512) / 1024; } time_t fileTime(char* filename) { struct stat st; if (stat(filename, &st) != -1) return st.st_mtime; message(SYS|ERROR, "cannot stat '%s'", filename); return 0; } char exists(char* filename) { struct stat st; return stat(filename, &st) == 0; } char makedir(char* dir) { char* slash = dir + strlen(dir) - 1; if (*slash == '/') *slash = 0; if (!exists(dir)) { char dir_[PATH_LEN]; char temp[PATH_LEN]; char* ptr; char* slash; strncpy_(dir_, dir, PATH_LEN); *temp = 0; ptr = dir_; do { (slash = strchr(ptr, '/')) && (*slash = 0); strcat(temp, ptr); if (*temp && !exists(temp)) { message(MESSAGE, "creating directory %s", temp); if (mkdir(temp, 0777)) { message(SYS|ERROR, "cannot make directory %s", temp); return FAILED; } } strcat(temp, "/"); if (slash) ptr = slash + 1; } while (slash); } if (*slash == 0) *slash = '/'; return OK; } char removedir(char* name) { DIR* dir; struct dirent* ent; char cwd[PATH_LEN]; if (!getcwd(cwd, PATH_LEN)) return message(SYS|ERROR, "cannot getcwd"); if (chdir(name)) return message(SYS|ERROR, "cannot chdir to %s", name); if (!(dir = opendir("."))) { message(SYS|ERROR, "cannot opendir %s", name); chdir(cwd); return FAILED; } readdir(dir); readdir(dir); while ((ent = readdir(dir))) { if (unlink(ent->d_name)) message(SYS|ERROR, "cannot unlink %s/%s", name, ent->d_name); } closedir(dir); if (chdir(cwd)) message(SYS|ERROR, "cannot chdir to %s", cwd); if (rmdir(name)) message(SYS|ERROR, "cannot rmdir %s", name); return OK; } char writePidFile(char* name, char bg, char exclusive) { int fd; char file[PATH_LEN]; char* p; if (chdir(ROOT)) message(SYS|ERROR, "cannot chdir to %s", ROOT); catstr(file, PATH_LEN, PIDS, "/", name, NULL); if ((fd = open(file, O_CREAT|O_RDWR, 0644)) < 0) return message(SYS|ERROR, "cannot open %s", file); if (bg) { int r; int null; if (flock(fd, LOCK_EX|LOCK_NB) < 0) { char pid[SHORT_STR]; int n = read(fd, pid, SHORT_STR - 1); if (n > 0) pid[n] = 0; else message(SYS|ERROR, "cannot read %s", file); if (exclusive) return message(ERROR, "another daemon is running [pid: %s]", pid); else message(MESSAGE, "warning: another daemon is running [pid: %s]", pid); } if ((r = fork()) < 0) return message(SYS|ERROR, "cannot fork"); if (r) exit(0); setsid(); if ((null = open("/dev/null", O_RDWR)) < 0) { message(SYS|ERROR, "cannot open %s", "/dev/null"); } else { dup2(null, STDIN_FILENO); dup2(null, STDOUT_FILENO); dup2(null, STDERR_FILENO); if (null > 2) close(null); } } ftruncate(fd, 0); p = itoa(myPID = getpid()); write(fd, p, strlen(p)); message(MESSAGE, "%s is starting to work", p); if (!bg && close(fd)) return message(SYS|ERROR, "cannot close %s", file); return OK; } char message__( #if DEBUGLOG char* file, int pos, #endif int flags, char *format, ...) { static int* log = NULL; static char* string = NULL; static int logs_day = -1; va_list ap; int l; int err = errno; int type = flags & TYPE_MASK; #if DEBUGLOG if (type == DEBUG && !Debug) return FAILED; #endif if (!string && !(string = (char*) calloc(LONG_STR, sizeof(char)))) return FAILED; if (!log) { if (!(log = calloc(TYPE_MASK + 1, sizeof(int)))) return FAILED; memset(log, -1, (TYPE_MASK + 1) * sizeof(int)); } getTime(); if (type == CLOSE_LOGS || logs_day != now_day) { int t; for (t = 0; t <= TYPE_MASK; t++) { if (log[t] >= 0 && log[t] != STDERR_FILENO) { close(log[t]); log[t] = -1; } } logs_day = now_day; if (type == CLOSE_LOGS) return FAILED; } if (log[type] < 0) { if (logName[type] && *logName[type] && log[type] < 0) { char str[PATH_LEN]; char* today = time2str(now, SQL_DATE); l = catstr(str, PATH_LEN, "logs/", today, NULL); if (mkdir(str, 0777) && errno != EEXIST) { logName[type] = NULL; log[type] = STDERR_FILENO; message(SYS|ERROR, "cannot create directory %s", str); } else { catstr(str + l, PATH_LEN - l, "/", logName[type], NULL); log[type] = open(str, O_CREAT|O_APPEND|O_WRONLY, 0644); if (log[type] < 0) { logName[type] = NULL; message(SYS|ERROR, "cannot open log file %s for appending", str); log[type] = STDERR_FILENO; } else { char tmp[PATH_LEN]; catstr(tmp, PATH_LEN, "logs/current.", itoa(getpid()), NULL); if (!symlink(today, tmp)) { if (rename(tmp, "logs/current")) message(SYS|ERROR, "cannot rename %s - %s", tmp, "logs/current"); } else { message(SYS|ERROR, "cannot link %s -> %s", tmp, today); } } } } else { log[type] = STDERR_FILENO; } } #if DEBUGLOG if (type == DEBUG) { struct timeval tv; gettimeofday(&tv, NULL); l = snprintf(string, LONG_STR, "%02d:%02d:%02d.%03d|%s|%s:%d| ", now_tm.tm_hour, now_tm.tm_min, now_tm.tm_sec, (int)(tv.tv_usec/1000), progName, file, pos); } else #endif l = snprintf(string, LONG_STR, "%02d:%02d:%02d|%s| ", now_tm.tm_hour, now_tm.tm_min, now_tm.tm_sec, progName); va_start(ap, format); l += vsnprintf(string + l, LONG_STR - l, format, ap); va_end(ap); if (l < LONG_STR) { if ((flags & SYS)) { l += snprintf(string + l, LONG_STR - l, ": %s\n", strerror(err)); if (l >= LONG_STR) { string[LONG_STR - 1] = '\n'; l = LONG_STR; } } else { string[l++] = '\n'; } } else { string[LONG_STR - 1] = '\n'; l = LONG_STR; } if (write(log[type], string, l) < l) { if (log[type] != STDERR_FILENO) close(log[type]); log[type] = STDERR_FILENO; message(SYS|ERROR, "cannot log to %s", logName[type]); } errno = err; return FAILED; } int addCount(char* file, char doFlush) { static FILE* log = NULL; static int count = 0; static int intercount = 0; static int error = 0; if (!doFlush) { count++; if ((++intercount) < 1000) return count; } if (!log && !error) { char name[PATH_LEN]; snprintf(name, PATH_LEN, "%s/%s-count", LOGS, file); if (!(log = fopen(name, "r+")) && !(log = fopen(name, "w+"))) { message(SYS|ERROR, "cannot open file %s", name); error = 1; } } if (!error) { int c = 0; char s[32]; fseek(log, 0, SEEK_SET); fgets(s, 32, log); if (*s) sscanf(s, "%d", &c); fseek(log, 0, SEEK_SET); if (c>=1000000000) c -= 1000000000; c += intercount; fprintf(log, "%-10d\n", c); fflush(log); } if (doFlush) { fclose(log); log = NULL; } intercount = 0; return count; } void* malloc_wrapper(size_t size, char* file, int line) { void* ptr = malloc(size); debug("allocating %d bytes on %s:%d", size, file, line); if (!ptr) message(ERROR, "cannot allocate %d bytes on %s:%d", size, file, line); #if DEBUGLOG else addPtr(ptr, size, file, line); #endif return ptr; } void* calloc_wrapper(size_t size, char* file, int line) { void* ptr = calloc(size, 1); debug("allocating %d bytes on %s:%d", size, file, line); if (!ptr) message(ERROR, "cannot allocate %d bytes on %s:%d", size, file, line); #if DEBUGLOG else addPtr(ptr, size, file, line); #endif return ptr; } void* realloc_wrapper(void* ptr, size_t size, char* file, int line) { #if DEBUGLOG if (ptr) delPtr(ptr, file, line); #endif ptr = realloc(ptr, size); debug("reallocating %d bytes on %s:%d", size, file, line); if (!ptr) message(ERROR, "cannot reallocate %d bytes on %s:%d", size, file, line); #if DEBUGLOG else addPtr(ptr, size, file, line); #endif return ptr; } void* free_wrapper(void* ptr #if DEBUGLOG ,char* file, int line #endif ) { if (ptr) { free(ptr); #if DEBUGLOG delPtr(ptr, file, line); #endif } debug("free on %s:%d", file, line); return NULL; } char* strdup_wrapper(char* src, char* file, int line) { int size = strlen(src) + 1; char* ptr = malloc(size); debug("allocating %d bytes on %s:%d", size, file, line); if (!ptr) { message(ERROR, "cannot allocate %d bytes on %s:%d", size, file, line); return NULL; } #if DEBUGLOG addPtr(ptr, size, file, line); #endif return memcpy(ptr, src, size); } char* readFile(char* fileName, int* size) { char* buf; int fd; int fs = fileSize(fileName); int readed; if (fs < 0) return NULL; if ((fd = open(fileName, O_RDONLY)) < 0) { message(SYS|ERROR, "cannot open %s", fileName); return NULL; } buf = CALLOC(fs + 1); buf[fs] = 0; if ((readed = read(fd, buf, fs)) < fs) message(ERROR, "short read %s (%d < %d)", fileName, readed, fs); if (close(fd)) message(SYS|ERROR, "cannot close %s", fileName); if (size) *size = fs; return buf; } char removeDir(char* name) { DIR* dir; struct dirent* ent; char result = OK; if (!(dir = opendir(name))) return message(SYS|ERROR, "cannot opendir %s", name); readdir(dir); readdir(dir); while((ent = readdir(dir))) { char filename[PATH_LEN]; struct stat s; snprintf(filename, PATH_LEN, "%s/%s", name, ent->d_name); if (lstat(filename, &s)) result = message(SYS|ERROR, "cannot stat %s", filename); else if (S_ISDIR(s.st_mode)) result &= removeDir(filename); else if (unlink(filename)) result = message(SYS|ERROR, "cannot unlink %s", filename); } closedir(dir); if (rmdir(name)) result = message(SYS|ERROR, "cannot rmdir %s", name); return result; } int rename2(char* from, char* to) { int result; if ((result = rename(from, to)) && errno == EXDEV) { int src, dest; char* buf; int n; debug("copying to other fs: %s -> %s", from, to); if ((src = open(from, O_RDONLY)) < 0) return -1; if ((dest = open(to, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) { close(src); return -1; } result = 0; buf = CALLOC(HUGE_TEXT); while((n = read(src, buf, HUGE_TEXT)) > 0) { if (write(dest, buf, n) < n) { message(SYS|ERROR, "cannot write to %s", to); result = -1; break; } } if (n < 0) { message(SYS|ERROR, "cannot read from %s", from); result = -1; } FREE(buf); close(src); close(dest); if (!result) result = unlink(from); } return result; }