static void rm_files_by_seqno(int loglvl, int seqno, const struct tm* tm) { char filename[128]; log_file_name(loglvl, seqno, filename, tm); DIR* dir = opendir(log_dir); if (!dir) { return; } struct dirent* dentry; while ((dentry = readdir(dir))) { if ( (seqno == get_logfile_seqno(dentry->d_name, loglvl)) && (strncmp(dentry->d_name, fds_info[loglvl].base_filename, fds_info[loglvl].base_filename_len) == 0) && (strcmp(filename, dentry->d_name) != 0) ) { char filepath[FILENAME_MAX]; snprintf(filepath, sizeof(filepath), "%s/%s", log_dir, dentry->d_name); remove(filepath); // if there are duplicated seqno caused by the '!dir' above and thus leads to some bugs, we should remove the 'break;' below break; } } closedir(dir); }
void log_write(Tox *tox, int fid, const uint8_t *message, uint16_t length, _Bool author, uint8_t msg_type) { if(!logging_enabled) { return; } uint8_t path[512], *p; uint8_t name[TOX_MAX_NAME_LENGTH]; int namelen; FILE *file; p = path + datapath(path); int len = log_file_name(p, sizeof(path) - (p - path), tox, fid); if (len == -1) return; p += len; file = fopen((char*)path, "ab"); if(file) { time_t rawtime; time(&rawtime); if(author) { namelen = tox_get_self_name(tox, name); } else if((namelen = tox_get_name(tox, fid, name)) == -1) { //error reading name namelen = 0; } LOG_FILE_MSG_HEADER header = { .time = rawtime, .namelen = namelen, .length = length, .flags = author, .msg_type = msg_type, }; fwrite(&header, sizeof(header), 1, file); fwrite(name, namelen, 1, file); fwrite(message, length, 1, file); fclose(file); } }
static int get_log_seq_recycle(int lvl) { char file_name[FILENAME_MAX] = { 0 }; DIR* dir = opendir(log_dir); if (!dir) { return -1; } struct dirent* dentry; while ((dentry = readdir(dir))) { if ( (strncmp(dentry->d_name, fds_info[lvl].base_filename, fds_info[lvl].base_filename_len) == 0) && (strcmp(dentry->d_name, file_name) > 0) ) { snprintf(file_name, sizeof(file_name), "%s", dentry->d_name); } } closedir(dir); struct tm tm; time_t now = time(0); localtime_r(&now, &tm); if (file_name[0] == '\0') { log_file_name(lvl, 0, file_name, &tm); } char* date = &file_name[fds_info[lvl].base_filename_len]; char today[9]; int seqno = get_logfile_seqno(file_name, lvl); snprintf(today, sizeof(today), "%4d%02d%02d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); if (strncmp(today, date, 8)) { ++seqno; } return seqno % max_log_files; }
void log_read(Tox *tox, int fid) { uint8_t path[512], *p; FILE *file; p = path + datapath(path); int len = log_file_name(p, sizeof(path) - (p - path), tox, fid); if (len == -1) { debug("Error getting log file name for friend %d\n", fid); return; } file = fopen((char*)path, "rb"); if(!file) { debug("File not found (%s)\n", path); p = path + datapath_old(path); len = log_file_name(p, sizeof(path) - (p - path), tox, fid); if (len == -1) { debug("Error getting log file name for friend %d\n", fid); return; } file = fopen((char*) path, "rb"); if (!file) { debug("File not found (%s)\n", path); return; } } LOG_FILE_MSG_HEADER header; off_t rewinds[MAX_BACKLOG_MESSAGES] = {}; size_t records_count = 0; /* todo: some checks to avoid crashes with corrupted log files */ /* first find the last MAX_BACKLOG_MESSAGES messages in the log */ while(1 == fread(&header, sizeof(LOG_FILE_MSG_HEADER), 1, file)) { fseeko(file, header.namelen + header.length, SEEK_CUR); rewinds[records_count % countof(rewinds)] = (off_t) sizeof(LOG_FILE_MSG_HEADER) + header.namelen + header.length; records_count++; } if(ferror(file) || !feof(file)) { // TODO: consider removing or truncating the log file. // If !feof() this means that the file has an incomplete record, // which would prevent it from loading forever, even though // new records will keep being appended as usual. debug("Log read error (%s)\n", path); fclose(file); return; } // Backtrack to read last MAX_BACKLOG_MESSAGES in full. off_t rewind = 0; MSG_IDX i; for(i = 0; (i < records_count) && (i < countof(rewinds)); i++) { rewind += rewinds[i]; } fseeko(file, -rewind, SEEK_CUR); MSG_DATA *m = &friend[fid].msg; m->data = malloc(sizeof(void*) * i); m->n = 0; /* add the messages */ while((0 < i) && (1 == fread(&header, sizeof(LOG_FILE_MSG_HEADER), 1, file))) { i--; // Skip unused friend name recorded at the time. fseeko(file, header.namelen, SEEK_CUR); MESSAGE *msg = NULL; switch(header.msg_type) { case LOG_FILE_MSG_TYPE_ACTION: { msg = malloc(sizeof(MESSAGE) + header.length); msg->msg_type = MSG_TYPE_ACTION_TEXT; break; } case LOG_FILE_MSG_TYPE_TEXT: { msg = malloc(sizeof(MESSAGE) + header.length); msg->msg_type = MSG_TYPE_TEXT; break; } default: { debug("Unknown backlog message type(%d), skipping.\n", (int)header.msg_type); fseeko(file, header.length, SEEK_CUR); continue; } } // Read text message. msg->author = header.flags & 1; msg->length = header.length; if(1 != fread(msg->msg, msg->length, 1, file)) { debug("Log read error (%s)\n", path); fclose(file); return; } struct tm *ti; time_t rawtime = header.time; ti = localtime(&rawtime); msg->time = ti->tm_hour * 60 + ti->tm_min; m->data[m->n++] = msg; debug("loaded backlog: %d: %.*s\n", fid, msg->length, msg->msg); } fclose(file); }