|
SASC-NG
DVBSoftCAM
|
00001 /* 00002 * tools.c: Various tools 00003 * 00004 * See the main source file 'vdr.c' for copyright information and 00005 * how to reach the author. 00006 * 00007 * $Id: tools.c 1.97 2005/08/27 14:43:55 kls Exp $ 00008 */ 00009 00010 #include "include/vdr/tools.h" 00011 #include <ctype.h> 00012 #include <dirent.h> 00013 #include <errno.h> 00014 #include <stdarg.h> 00015 #include <stdlib.h> 00016 #include <sys/time.h> 00017 #include <sys/vfs.h> 00018 #include <time.h> 00019 #include <unistd.h> 00020 #include "include/vdr/i18n.h" 00021 #include "include/vdr/thread.h" 00022 00023 int SysLogLevel = 3; 00024 00025 #define MAXSYSLOGBUF 256 00026 00027 void syslog_with_tid(int priority, const char *format, ...) 00028 { 00029 va_list ap; 00030 char fmt[MAXSYSLOGBUF]; 00031 snprintf(fmt, sizeof(fmt), "[%d] %s", cThread::ThreadId(), format); 00032 va_start(ap, format); 00033 vsyslog(priority, fmt, ap); 00034 va_end(ap); 00035 } 00036 00037 int BCD2INT(int x) 00038 { 00039 return ((1000000 * BCDCHARTOINT((x >> 24) & 0xFF)) + 00040 (10000 * BCDCHARTOINT((x >> 16) & 0xFF)) + 00041 (100 * BCDCHARTOINT((x >> 8) & 0xFF)) + 00042 BCDCHARTOINT( x & 0xFF)); 00043 } 00044 00045 ssize_t safe_read(int filedes, void *buffer, size_t size) 00046 { 00047 for (;;) { 00048 ssize_t p = read(filedes, buffer, size); 00049 if (p < 0 && errno == EINTR) { 00050 dsyslog("EINTR while reading from file handle %d - retrying", filedes); 00051 continue; 00052 } 00053 return p; 00054 } 00055 } 00056 00057 ssize_t safe_write(int filedes, const void *buffer, size_t size) 00058 { 00059 ssize_t p = 0; 00060 ssize_t written = size; 00061 const unsigned char *ptr = (const unsigned char *)buffer; 00062 while (size > 0) { 00063 p = write(filedes, ptr, size); 00064 if (p < 0) { 00065 if (errno == EINTR) { 00066 dsyslog("EINTR while writing to file handle %d - retrying", filedes); 00067 continue; 00068 } 00069 break; 00070 } 00071 ptr += p; 00072 size -= p; 00073 } 00074 return p < 0 ? p : written; 00075 } 00076 00077 void writechar(int filedes, char c) 00078 { 00079 safe_write(filedes, &c, sizeof(c)); 00080 } 00081 00082 int WriteAllOrNothing(int fd, const uchar *Data, int Length, int TimeoutMs, int RetryMs) 00083 { 00084 int written = 0; 00085 while (Length > 0) { 00086 int w = write(fd, Data + written, Length); 00087 if (w > 0) { 00088 Length -= w; 00089 written += w; 00090 } 00091 else if (written > 0 && !FATALERRNO) { 00092 // we've started writing, so we must finish it! 00093 cTimeMs t; 00094 cPoller Poller(fd, true); 00095 Poller.Poll(RetryMs); 00096 if (TimeoutMs > 0 && (TimeoutMs -= t.Elapsed()) <= 0) 00097 break; 00098 } 00099 else 00100 // nothing written yet (or fatal error), so we can just return the error code: 00101 return w; 00102 } 00103 return written; 00104 } 00105 00106 char *strcpyrealloc(char *dest, const char *src) 00107 { 00108 if (src) { 00109 int l = max(dest ? strlen(dest) : 0, strlen(src)) + 1; // don't let the block get smaller! 00110 dest = (char *)realloc(dest, l); 00111 if (dest) 00112 strcpy(dest, src); 00113 else 00114 esyslog("ERROR: out of memory"); 00115 } 00116 else { 00117 free(dest); 00118 dest = NULL; 00119 } 00120 return dest; 00121 } 00122 00123 char *strn0cpy(char *dest, const char *src, size_t n) 00124 { 00125 char *s = dest; 00126 for ( ; --n && (*dest = *src) != 0; dest++, src++) ; 00127 *dest = 0; 00128 return s; 00129 } 00130 00131 char *strreplace(char *s, char c1, char c2) 00132 { 00133 char *p = s; 00134 00135 while (p && *p) { 00136 if (*p == c1) 00137 *p = c2; 00138 p++; 00139 } 00140 return s; 00141 } 00142 00143 char *strreplace(char *s, const char *s1, const char *s2) 00144 { 00145 char *p = strstr(s, s1); 00146 if (p) { 00147 int of = p - s; 00148 int l = strlen(s); 00149 int l1 = strlen(s1); 00150 int l2 = strlen(s2); 00151 if (l2 > l1) 00152 s = (char *)realloc(s, strlen(s) + l2 - l1 + 1); 00153 if (l2 != l1) 00154 memmove(s + of + l2, s + of + l1, l - of - l1 + 1); 00155 strncpy(s + of, s2, l2); 00156 } 00157 return s; 00158 } 00159 00160 char *skipspace(const char *s) 00161 { 00162 while (*s && isspace(*s)) 00163 s++; 00164 return (char *)s; 00165 } 00166 00167 char *stripspace(char *s) 00168 { 00169 if (s && *s) { 00170 for (char *p = s + strlen(s) - 1; p >= s; p--) { 00171 if (!isspace(*p)) 00172 break; 00173 *p = 0; 00174 } 00175 } 00176 return s; 00177 } 00178 00179 char *compactspace(char *s) 00180 { 00181 if (s && *s) { 00182 char *t = stripspace(skipspace(s)); 00183 char *p = t; 00184 while (p && *p) { 00185 char *q = skipspace(p); 00186 if (q - p > 1) 00187 memmove(p + 1, q, strlen(q) + 1); 00188 p++; 00189 } 00190 if (t != s) 00191 memmove(s, t, strlen(t) + 1); 00192 } 00193 return s; 00194 } 00195 00196 cString strescape(const char *s, const char *chars) 00197 { 00198 char *buffer; 00199 const char *p = s; 00200 char *t = NULL; 00201 while (*p) { 00202 if (strchr(chars, *p)) { 00203 if (!t) { 00204 buffer = MALLOC(char, 2 * strlen(s) + 1); 00205 t = buffer + (p - s); 00206 s = strcpy(buffer, s); 00207 } 00208 *t++ = '\\'; 00209 } 00210 if (t) 00211 *t++ = *p; 00212 p++; 00213 } 00214 if (t) 00215 *t = 0; 00216 return cString(s, t != NULL); 00217 } 00218 00219 bool startswith(const char *s, const char *p) 00220 { 00221 while (*p) { 00222 if (*p++ != *s++) 00223 return false; 00224 } 00225 return true; 00226 } 00227 00228 bool endswith(const char *s, const char *p) 00229 { 00230 const char *se = s + strlen(s) - 1; 00231 const char *pe = p + strlen(p) - 1; 00232 while (pe >= p) { 00233 if (*pe-- != *se-- || (se < s && pe >= p)) 00234 return false; 00235 } 00236 return true; 00237 } 00238 00239 bool isempty(const char *s) 00240 { 00241 return !(s && *skipspace(s)); 00242 } 00243 00244 int numdigits(int n) 00245 { 00246 char buf[16]; 00247 snprintf(buf, sizeof(buf), "%d", n); 00248 return strlen(buf); 00249 } 00250 00251 bool isnumber(const char *s) 00252 { 00253 if (!*s) 00254 return false; 00255 while (*s) { 00256 if (!isdigit(*s)) 00257 return false; 00258 s++; 00259 } 00260 return true; 00261 } 00262 00263 cString AddDirectory(const char *DirName, const char *FileName) 00264 { 00265 char *buf; 00266 asprintf(&buf, "%s/%s", DirName && *DirName ? DirName : ".", FileName); 00267 return cString(buf, true); 00268 } 00269 00270 cString itoa(int n) 00271 { 00272 char buf[16]; 00273 snprintf(buf, sizeof(buf), "%d", n); 00274 return buf; 00275 } 00276 00277 int FreeDiskSpaceMB(const char *Directory, int *UsedMB) 00278 { 00279 if (UsedMB) 00280 *UsedMB = 0; 00281 int Free = 0; 00282 struct statfs statFs; 00283 if (statfs(Directory, &statFs) == 0) { 00284 double blocksPerMeg = 1024.0 * 1024.0 / statFs.f_bsize; 00285 if (UsedMB) 00286 *UsedMB = int((statFs.f_blocks - statFs.f_bfree) / blocksPerMeg); 00287 Free = int(statFs.f_bavail / blocksPerMeg); 00288 } 00289 else 00290 LOG_ERROR_STR(Directory); 00291 return Free; 00292 } 00293 00294 bool DirectoryOk(const char *DirName, bool LogErrors) 00295 { 00296 struct stat ds; 00297 if (stat(DirName, &ds) == 0) { 00298 if (S_ISDIR(ds.st_mode)) { 00299 if (access(DirName, R_OK | W_OK | X_OK) == 0) 00300 return true; 00301 else if (LogErrors) 00302 esyslog("ERROR: can't access %s", DirName); 00303 } 00304 else if (LogErrors) 00305 esyslog("ERROR: %s is not a directory", DirName); 00306 } 00307 else if (LogErrors) 00308 LOG_ERROR_STR(DirName); 00309 return false; 00310 } 00311 00312 bool MakeDirs(const char *FileName, bool IsDirectory) 00313 { 00314 bool result = true; 00315 char *s = strdup(FileName); 00316 char *p = s; 00317 if (*p == '/') 00318 p++; 00319 while ((p = strchr(p, '/')) != NULL || IsDirectory) { 00320 if (p) 00321 *p = 0; 00322 struct stat fs; 00323 if (stat(s, &fs) != 0 || !S_ISDIR(fs.st_mode)) { 00324 dsyslog("creating directory %s", s); 00325 if (mkdir(s, ACCESSPERMS) == -1) { 00326 LOG_ERROR_STR(s); 00327 result = false; 00328 break; 00329 } 00330 } 00331 if (p) 00332 *p++ = '/'; 00333 else 00334 break; 00335 } 00336 free(s); 00337 return result; 00338 } 00339 00340 bool RemoveFileOrDir(const char *FileName, bool FollowSymlinks) 00341 { 00342 struct stat st; 00343 if (stat(FileName, &st) == 0) { 00344 if (S_ISDIR(st.st_mode)) { 00345 cReadDir d(FileName); 00346 if (d.Ok()) { 00347 struct dirent *e; 00348 while ((e = d.Next()) != NULL) { 00349 if (strcmp(e->d_name, ".") && strcmp(e->d_name, "..")) { 00350 char *buffer; 00351 asprintf(&buffer, "%s/%s", FileName, e->d_name); 00352 if (FollowSymlinks) { 00353 int size = strlen(buffer) * 2; // should be large enough 00354 char *l = MALLOC(char, size); 00355 int n = readlink(buffer, l, size); 00356 if (n < 0) { 00357 if (errno != EINVAL) 00358 LOG_ERROR_STR(buffer); 00359 } 00360 else if (n < size) { 00361 l[n] = 0; 00362 dsyslog("removing %s", l); 00363 if (remove(l) < 0) 00364 LOG_ERROR_STR(l); 00365 } 00366 else 00367 esyslog("ERROR: symlink name length (%d) exceeded anticipated buffer size (%d)", n, size); 00368 free(l); 00369 } 00370 dsyslog("removing %s", buffer); 00371 if (remove(buffer) < 0) 00372 LOG_ERROR_STR(buffer); 00373 free(buffer); 00374 } 00375 } 00376 } 00377 else { 00378 LOG_ERROR_STR(FileName); 00379 return false; 00380 } 00381 } 00382 dsyslog("removing %s", FileName); 00383 if (remove(FileName) < 0) { 00384 LOG_ERROR_STR(FileName); 00385 return false; 00386 } 00387 } 00388 else if (errno != ENOENT) { 00389 LOG_ERROR_STR(FileName); 00390 return false; 00391 } 00392 return true; 00393 } 00394 00395 bool RemoveEmptyDirectories(const char *DirName, bool RemoveThis) 00396 { 00397 cReadDir d(DirName); 00398 if (d.Ok()) { 00399 bool empty = true; 00400 struct dirent *e; 00401 while ((e = d.Next()) != NULL) { 00402 if (strcmp(e->d_name, ".") && strcmp(e->d_name, "..") && strcmp(e->d_name, "lost+found")) { 00403 char *buffer; 00404 asprintf(&buffer, "%s/%s", DirName, e->d_name); 00405 struct stat st; 00406 if (stat(buffer, &st) == 0) { 00407 if (S_ISDIR(st.st_mode)) { 00408 if (!RemoveEmptyDirectories(buffer, true)) 00409 empty = false; 00410 } 00411 else 00412 empty = false; 00413 } 00414 else { 00415 LOG_ERROR_STR(buffer); 00416 free(buffer); 00417 return false; 00418 } 00419 free(buffer); 00420 } 00421 } 00422 if (RemoveThis && empty) { 00423 dsyslog("removing %s", DirName); 00424 if (remove(DirName) < 0) { 00425 LOG_ERROR_STR(DirName); 00426 return false; 00427 } 00428 } 00429 return empty; 00430 } 00431 else 00432 LOG_ERROR_STR(DirName); 00433 return false; 00434 } 00435 00436 char *ReadLink(const char *FileName) 00437 { 00438 char RealName[PATH_MAX]; 00439 const char *TargetName = NULL; 00440 int n = readlink(FileName, RealName, sizeof(RealName) - 1); 00441 if (n < 0) { 00442 if (errno == ENOENT || errno == EINVAL) // file doesn't exist or is not a symlink 00443 TargetName = FileName; 00444 else // some other error occurred 00445 LOG_ERROR_STR(FileName); 00446 } 00447 else if (n < int(sizeof(RealName))) { // got it! 00448 RealName[n] = 0; 00449 TargetName = RealName; 00450 } 00451 else 00452 esyslog("ERROR: symlink's target name too long: %s", FileName); 00453 return TargetName ? strdup(TargetName) : NULL; 00454 } 00455 00456 bool SpinUpDisk(const char *FileName) 00457 { 00458 char *buf = NULL; 00459 for (int n = 0; n < 10; n++) { 00460 free(buf); 00461 if (DirectoryOk(FileName)) 00462 asprintf(&buf, "%s/vdr-%06d", *FileName ? FileName : ".", n); 00463 else 00464 asprintf(&buf, "%s.vdr-%06d", FileName, n); 00465 if (access(buf, F_OK) != 0) { // the file does not exist 00466 timeval tp1, tp2; 00467 gettimeofday(&tp1, NULL); 00468 int f = open(buf, O_WRONLY | O_CREAT, DEFFILEMODE); 00469 // O_SYNC doesn't work on all file systems 00470 if (f >= 0) { 00471 if (fdatasync(f) < 0) 00472 LOG_ERROR_STR(buf); 00473 close(f); 00474 remove(buf); 00475 gettimeofday(&tp2, NULL); 00476 double seconds = (((long long)tp2.tv_sec * 1000000 + tp2.tv_usec) - ((long long)tp1.tv_sec * 1000000 + tp1.tv_usec)) / 1000000.0; 00477 if (seconds > 0.5) 00478 dsyslog("SpinUpDisk took %.2f seconds\n", seconds); 00479 free(buf); 00480 return true; 00481 } 00482 else 00483 LOG_ERROR_STR(buf); 00484 } 00485 } 00486 free(buf); 00487 esyslog("ERROR: SpinUpDisk failed"); 00488 return false; 00489 } 00490 00491 time_t LastModifiedTime(const char *FileName) 00492 { 00493 struct stat fs; 00494 if (stat(FileName, &fs) == 0) 00495 return fs.st_mtime; 00496 return 0; 00497 } 00498 00499 // --- cTimeMs --------------------------------------------------------------- 00500 00501 cTimeMs::cTimeMs(int Ms) 00502 { 00503 Set(Ms); 00504 } 00505 00506 uint64_t cTimeMs::Now(void) 00507 { 00508 struct timeval t; 00509 if (gettimeofday(&t, NULL) == 0) 00510 return (uint64_t(t.tv_sec)) * 1000 + t.tv_usec / 1000; 00511 return 0; 00512 } 00513 00514 void cTimeMs::Set(int Ms) 00515 { 00516 begin = Now() + Ms; 00517 } 00518 00519 bool cTimeMs::TimedOut(void) 00520 { 00521 return Now() >= begin; 00522 } 00523 00524 uint64_t cTimeMs::Elapsed(void) 00525 { 00526 return Now() - begin; 00527 } 00528 00529 // --- cString --------------------------------------------------------------- 00530 00531 cString::cString(const char *S, bool TakePointer) 00532 { 00533 s = TakePointer ? (char *)S : S ? strdup(S) : NULL; 00534 } 00535 00536 cString::~cString() 00537 { 00538 free(s); 00539 } 00540 00541 cString &cString::operator=(const cString &String) 00542 { 00543 s = String.s ? strdup(String.s) : NULL; 00544 return *this; 00545 } 00546 00547 cString cString::sprintf(const char *fmt, ...) 00548 { 00549 va_list ap; 00550 va_start(ap, fmt); 00551 char *buffer; 00552 vasprintf(&buffer, fmt, ap); 00553 return cString(buffer, true); 00554 } 00555 00556 cString WeekDayName(int WeekDay) 00557 { 00558 char buffer[4]; 00559 WeekDay = WeekDay == 0 ? 6 : WeekDay - 1; // we start with monday==0! 00560 if (0 <= WeekDay && WeekDay <= 6) { 00561 const char *day = "MonTueWedThuFriSatSun"; 00562 day += WeekDay * 3; 00563 strn0cpy(buffer, day, sizeof(buffer)); 00564 return buffer; 00565 } 00566 else 00567 return "???"; 00568 } 00569 00570 cString WeekDayName(time_t t) 00571 { 00572 struct tm tm_r; 00573 return WeekDayName(localtime_r(&t, &tm_r)->tm_wday); 00574 } 00575 00576 cString DayDateTime(time_t t) 00577 { 00578 char buffer[32]; 00579 if (t == 0) 00580 time(&t); 00581 struct tm tm_r; 00582 tm *tm = localtime_r(&t, &tm_r); 00583 snprintf(buffer, sizeof(buffer), "%s %2d.%02d %02d:%02d", *WeekDayName(tm->tm_wday), tm->tm_mday, tm->tm_mon + 1, tm->tm_hour, tm->tm_min); 00584 return buffer; 00585 } 00586 00587 cString TimeToString(time_t t) 00588 { 00589 char buffer[32]; 00590 if (ctime_r(&t, buffer)) { 00591 buffer[strlen(buffer) - 1] = 0; // strip trailing newline 00592 return buffer; 00593 } 00594 return "???"; 00595 } 00596 00597 cString DateString(time_t t) 00598 { 00599 char buf[32]; 00600 struct tm tm_r; 00601 tm *tm = localtime_r(&t, &tm_r); 00602 char *p = stpcpy(buf, WeekDayName(tm->tm_wday)); 00603 *p++ = ' '; 00604 strftime(p, sizeof(buf) - (p - buf), "%d.%m.%Y", tm); 00605 return buf; 00606 } 00607 00608 cString TimeString(time_t t) 00609 { 00610 char buf[25]; 00611 struct tm tm_r; 00612 strftime(buf, sizeof(buf), "%R", localtime_r(&t, &tm_r)); 00613 return buf; 00614 } 00615 00616 // --- cReadLine ------------------------------------------------------------- 00617 00618 char *cReadLine::Read(FILE *f) 00619 { 00620 if (fgets(buffer, sizeof(buffer), f) > 0) { 00621 int l = strlen(buffer) - 1; 00622 if (l >= 0 && buffer[l] == '\n') 00623 buffer[l] = 0; 00624 return buffer; 00625 } 00626 return NULL; 00627 } 00628 00629 // --- cPoller --------------------------------------------------------------- 00630 00631 cPoller::cPoller(int FileHandle, bool Out) 00632 { 00633 numFileHandles = 0; 00634 Add(FileHandle, Out); 00635 } 00636 00637 bool cPoller::Add(int FileHandle, bool Out) 00638 { 00639 if (FileHandle >= 0) { 00640 for (int i = 0; i < numFileHandles; i++) { 00641 if (pfd[i].fd == FileHandle && pfd[i].events == (Out ? POLLOUT : POLLIN)) 00642 return true; 00643 } 00644 if (numFileHandles < MaxPollFiles) { 00645 pfd[numFileHandles].fd = FileHandle; 00646 pfd[numFileHandles].events = Out ? POLLOUT : POLLIN; 00647 pfd[numFileHandles].revents = 0; 00648 numFileHandles++; 00649 return true; 00650 } 00651 esyslog("ERROR: too many file handles in cPoller"); 00652 } 00653 return false; 00654 } 00655 00656 bool cPoller::Poll(int TimeoutMs) 00657 { 00658 if (numFileHandles) { 00659 if (poll(pfd, numFileHandles, TimeoutMs) != 0) 00660 return true; // returns true even in case of an error, to let the caller 00661 // access the file and thus see the error code 00662 } 00663 return false; 00664 } 00665 00666 // --- cReadDir -------------------------------------------------------------- 00667 00668 cReadDir::cReadDir(const char *Directory) 00669 { 00670 directory = opendir(Directory); 00671 } 00672 00673 cReadDir::~cReadDir() 00674 { 00675 if (directory) 00676 closedir(directory); 00677 } 00678 00679 struct dirent *cReadDir::Next(void) 00680 { 00681 return directory && readdir_r(directory, &u.d, &result) == 0 ? result : NULL; 00682 } 00683 00684 // --- cFile ----------------------------------------------------------------- 00685 00686 bool cFile::files[FD_SETSIZE] = { false }; 00687 int cFile::maxFiles = 0; 00688 00689 cFile::cFile(void) 00690 { 00691 f = -1; 00692 } 00693 00694 cFile::~cFile() 00695 { 00696 Close(); 00697 } 00698 00699 bool cFile::Open(const char *FileName, int Flags, mode_t Mode) 00700 { 00701 if (!IsOpen()) 00702 return Open(open(FileName, Flags, Mode)); 00703 esyslog("ERROR: attempt to re-open %s", FileName); 00704 return false; 00705 } 00706 00707 bool cFile::Open(int FileDes) 00708 { 00709 if (FileDes >= 0) { 00710 if (!IsOpen()) { 00711 f = FileDes; 00712 if (f >= 0) { 00713 if (f < FD_SETSIZE) { 00714 if (f >= maxFiles) 00715 maxFiles = f + 1; 00716 if (!files[f]) 00717 files[f] = true; 00718 else 00719 esyslog("ERROR: file descriptor %d already in files[]", f); 00720 return true; 00721 } 00722 else 00723 esyslog("ERROR: file descriptor %d is larger than FD_SETSIZE (%d)", f, FD_SETSIZE); 00724 } 00725 } 00726 else 00727 esyslog("ERROR: attempt to re-open file descriptor %d", FileDes); 00728 } 00729 return false; 00730 } 00731 00732 void cFile::Close(void) 00733 { 00734 if (f >= 0) { 00735 close(f); 00736 files[f] = false; 00737 f = -1; 00738 } 00739 } 00740 00741 bool cFile::Ready(bool Wait) 00742 { 00743 return f >= 0 && AnyFileReady(f, Wait ? 1000 : 0); 00744 } 00745 00746 bool cFile::AnyFileReady(int FileDes, int TimeoutMs) 00747 { 00748 fd_set set; 00749 FD_ZERO(&set); 00750 for (int i = 0; i < maxFiles; i++) { 00751 if (files[i]) 00752 FD_SET(i, &set); 00753 } 00754 if (0 <= FileDes && FileDes < FD_SETSIZE && !files[FileDes]) 00755 FD_SET(FileDes, &set); // in case we come in with an arbitrary descriptor 00756 if (TimeoutMs == 0) 00757 TimeoutMs = 10; // load gets too heavy with 0 00758 struct timeval timeout; 00759 timeout.tv_sec = TimeoutMs / 1000; 00760 timeout.tv_usec = (TimeoutMs % 1000) * 1000; 00761 return select(FD_SETSIZE, &set, NULL, NULL, &timeout) > 0 && (FileDes < 0 || FD_ISSET(FileDes, &set)); 00762 } 00763 00764 bool cFile::FileReady(int FileDes, int TimeoutMs) 00765 { 00766 fd_set set; 00767 struct timeval timeout; 00768 FD_ZERO(&set); 00769 FD_SET(FileDes, &set); 00770 if (TimeoutMs >= 0) { 00771 if (TimeoutMs < 100) 00772 TimeoutMs = 100; 00773 timeout.tv_sec = TimeoutMs / 1000; 00774 timeout.tv_usec = (TimeoutMs % 1000) * 1000; 00775 } 00776 return select(FD_SETSIZE, &set, NULL, NULL, (TimeoutMs >= 0) ? &timeout : NULL) > 0 && FD_ISSET(FileDes, &set); 00777 } 00778 00779 bool cFile::FileReadyForWriting(int FileDes, int TimeoutMs) 00780 { 00781 fd_set set; 00782 struct timeval timeout; 00783 FD_ZERO(&set); 00784 FD_SET(FileDes, &set); 00785 if (TimeoutMs < 100) 00786 TimeoutMs = 100; 00787 timeout.tv_sec = 0; 00788 timeout.tv_usec = TimeoutMs * 1000; 00789 return select(FD_SETSIZE, NULL, &set, NULL, &timeout) > 0 && FD_ISSET(FileDes, &set); 00790 } 00791 00792 // --- cSafeFile ------------------------------------------------------------- 00793 00794 cSafeFile::cSafeFile(const char *FileName) 00795 { 00796 f = NULL; 00797 fileName = ReadLink(FileName); 00798 tempName = fileName ? MALLOC(char, strlen(fileName) + 5) : NULL; 00799 if (tempName) 00800 strcat(strcpy(tempName, fileName), ".$$$"); 00801 } 00802 00803 cSafeFile::~cSafeFile() 00804 { 00805 if (f) 00806 fclose(f); 00807 unlink(tempName); 00808 free(fileName); 00809 free(tempName); 00810 } 00811 00812 bool cSafeFile::Open(void) 00813 { 00814 if (!f && fileName && tempName) { 00815 f = fopen(tempName, "w"); 00816 if (!f) 00817 LOG_ERROR_STR(tempName); 00818 } 00819 return f != NULL; 00820 } 00821 00822 bool cSafeFile::Close(void) 00823 { 00824 bool result = true; 00825 if (f) { 00826 if (ferror(f) != 0) { 00827 LOG_ERROR_STR(tempName); 00828 result = false; 00829 } 00830 if (fclose(f) < 0) { 00831 LOG_ERROR_STR(tempName); 00832 result = false; 00833 } 00834 f = NULL; 00835 if (result && rename(tempName, fileName) < 0) { 00836 LOG_ERROR_STR(fileName); 00837 result = false; 00838 } 00839 } 00840 else 00841 result = false; 00842 return result; 00843 } 00844 00845 // --- cLockFile ------------------------------------------------------------- 00846 00847 #define LOCKFILENAME ".lock-vdr" 00848 #define LOCKFILESTALETIME 600 // seconds before considering a lock file "stale" 00849 00850 cLockFile::cLockFile(const char *Directory) 00851 { 00852 fileName = NULL; 00853 f = -1; 00854 if (DirectoryOk(Directory)) 00855 asprintf(&fileName, "%s/%s", Directory, LOCKFILENAME); 00856 } 00857 00858 cLockFile::~cLockFile() 00859 { 00860 Unlock(); 00861 free(fileName); 00862 } 00863 00864 bool cLockFile::Lock(int WaitSeconds) 00865 { 00866 if (f < 0 && fileName) { 00867 time_t Timeout = time(NULL) + WaitSeconds; 00868 do { 00869 f = open(fileName, O_WRONLY | O_CREAT | O_EXCL, DEFFILEMODE); 00870 if (f < 0) { 00871 if (errno == EEXIST) { 00872 struct stat fs; 00873 if (stat(fileName, &fs) == 0) { 00874 if (abs(time(NULL) - fs.st_mtime) > LOCKFILESTALETIME) { 00875 esyslog("ERROR: removing stale lock file '%s'", fileName); 00876 if (remove(fileName) < 0) { 00877 LOG_ERROR_STR(fileName); 00878 break; 00879 } 00880 continue; 00881 } 00882 } 00883 else if (errno != ENOENT) { 00884 LOG_ERROR_STR(fileName); 00885 break; 00886 } 00887 } 00888 else { 00889 LOG_ERROR_STR(fileName); 00890 break; 00891 } 00892 if (WaitSeconds) 00893 sleep(1); 00894 } 00895 } while (f < 0 && time(NULL) < Timeout); 00896 } 00897 return f >= 0; 00898 } 00899 00900 void cLockFile::Unlock(void) 00901 { 00902 if (f >= 0) { 00903 close(f); 00904 remove(fileName); 00905 f = -1; 00906 } 00907 } 00908 00909 // --- cListObject ----------------------------------------------------------- 00910 00911 cListObject::cListObject(void) 00912 { 00913 prev = next = NULL; 00914 } 00915 00916 cListObject::~cListObject() 00917 { 00918 } 00919 00920 void cListObject::Append(cListObject *Object) 00921 { 00922 next = Object; 00923 Object->prev = this; 00924 } 00925 00926 void cListObject::Insert(cListObject *Object) 00927 { 00928 prev = Object; 00929 Object->next = this; 00930 } 00931 00932 void cListObject::Unlink(void) 00933 { 00934 if (next) 00935 next->prev = prev; 00936 if (prev) 00937 prev->next = next; 00938 next = prev = NULL; 00939 } 00940 00941 int cListObject::Index(void) const 00942 { 00943 cListObject *p = prev; 00944 int i = 0; 00945 00946 while (p) { 00947 i++; 00948 p = p->prev; 00949 } 00950 return i; 00951 } 00952 00953 // --- cListBase ------------------------------------------------------------- 00954 00955 cListBase::cListBase(void) 00956 { 00957 objects = lastObject = NULL; 00958 count = 0; 00959 } 00960 00961 cListBase::~cListBase() 00962 { 00963 Clear(); 00964 } 00965 00966 void cListBase::Add(cListObject *Object, cListObject *After) 00967 { 00968 if (After && After != lastObject) { 00969 After->Next()->Insert(Object); 00970 After->Append(Object); 00971 } 00972 else { 00973 if (lastObject) 00974 lastObject->Append(Object); 00975 else 00976 objects = Object; 00977 lastObject = Object; 00978 } 00979 count++; 00980 } 00981 00982 void cListBase::Ins(cListObject *Object, cListObject *Before) 00983 { 00984 if (Before && Before != objects) { 00985 Before->Prev()->Append(Object); 00986 Before->Insert(Object); 00987 } 00988 else { 00989 if (objects) 00990 objects->Insert(Object); 00991 else 00992 lastObject = Object; 00993 objects = Object; 00994 } 00995 count++; 00996 } 00997 00998 void cListBase::Del(cListObject *Object, bool DeleteObject) 00999 { 01000 if (Object == objects) 01001 objects = Object->Next(); 01002 if (Object == lastObject) 01003 lastObject = Object->Prev(); 01004 Object->Unlink(); 01005 if (DeleteObject) 01006 delete Object; 01007 count--; 01008 } 01009 01010 void cListBase::Move(int From, int To) 01011 { 01012 Move(Get(From), Get(To)); 01013 } 01014 01015 void cListBase::Move(cListObject *From, cListObject *To) 01016 { 01017 if (From && To) { 01018 if (From->Index() < To->Index()) 01019 To = To->Next(); 01020 if (From == objects) 01021 objects = From->Next(); 01022 if (From == lastObject) 01023 lastObject = From->Prev(); 01024 From->Unlink(); 01025 if (To) { 01026 if (To->Prev()) 01027 To->Prev()->Append(From); 01028 From->Append(To); 01029 } 01030 else { 01031 lastObject->Append(From); 01032 lastObject = From; 01033 } 01034 if (!From->Prev()) 01035 objects = From; 01036 } 01037 } 01038 01039 void cListBase::Clear(void) 01040 { 01041 while (objects) { 01042 cListObject *object = objects->Next(); 01043 delete objects; 01044 objects = object; 01045 } 01046 objects = lastObject = NULL; 01047 count = 0; 01048 } 01049 01050 cListObject *cListBase::Get(int Index) const 01051 { 01052 if (Index < 0) 01053 return NULL; 01054 cListObject *object = objects; 01055 while (object && Index-- > 0) 01056 object = object->Next(); 01057 return object; 01058 } 01059 01060 static int CompareListObjects(const void *a, const void *b) 01061 { 01062 const cListObject *la = *(const cListObject **)a; 01063 const cListObject *lb = *(const cListObject **)b; 01064 return la->Compare(*lb); 01065 } 01066 01067 void cListBase::Sort(void) 01068 { 01069 int n = Count(); 01070 cListObject *a[n]; 01071 cListObject *object = objects; 01072 int i = 0; 01073 while (object && i < n) { 01074 a[i++] = object; 01075 object = object->Next(); 01076 } 01077 qsort(a, n, sizeof(cListObject *), CompareListObjects); 01078 objects = lastObject = NULL; 01079 for (i = 0; i < n; i++) { 01080 a[i]->Unlink(); 01081 count--; 01082 Add(a[i]); 01083 } 01084 } 01085 01086 // --- cHashBase ------------------------------------------------------------- 01087 01088 cHashBase::cHashBase(int Size) 01089 { 01090 size = Size; 01091 hashTable = (cList<cHashObject>**)calloc(size, sizeof(cList<cHashObject>*)); 01092 } 01093 01094 cHashBase::~cHashBase(void) 01095 { 01096 for (int i = 0; i < size; i++) 01097 delete hashTable[i]; 01098 free(hashTable); 01099 } 01100 01101 void cHashBase::Add(cListObject *Object, unsigned int Id) 01102 { 01103 unsigned int hash = hashfn(Id); 01104 if (!hashTable[hash]) 01105 hashTable[hash] = new cList<cHashObject>; 01106 hashTable[hash]->Add(new cHashObject(Object, Id)); 01107 } 01108 01109 void cHashBase::Del(cListObject *Object, unsigned int Id) 01110 { 01111 cList<cHashObject> *list = hashTable[hashfn(Id)]; 01112 if (list) { 01113 for (cHashObject *hob = list->First(); hob; hob = list->Next(hob)) { 01114 if (hob->object == Object) { 01115 list->Del(hob); 01116 break; 01117 } 01118 } 01119 } 01120 } 01121 01122 cListObject *cHashBase::Get(unsigned int Id) const 01123 { 01124 cList<cHashObject> *list = hashTable[hashfn(Id)]; 01125 if (list) { 01126 for (cHashObject *hob = list->First(); hob; hob = list->Next(hob)) { 01127 if (hob->id == Id) 01128 return hob->object; 01129 } 01130 } 01131 return NULL; 01132 } 01133 01134 cList<cHashObject> *cHashBase::GetList(unsigned int Id) const 01135 { 01136 return hashTable[hashfn(Id)]; 01137 }