Added SIGUSR1 option for cache file integrity test
This commit is contained in:
committed by
Andrew Gaul
parent
e0a38adaf6
commit
ad1961417d
@ -340,6 +340,12 @@ Put the debug message from libcurl when this option is specified.
|
||||
Specify "normal" or "body" for the parameter.
|
||||
If the parameter is omitted, it is the same as "normal".
|
||||
If "body" is specified, some API communication body data will be output in addition to the debug message output as "normal".
|
||||
.TP
|
||||
\fB\-o\fR set_check_cache_sigusr1 (default is stdout)
|
||||
If the cache is enabled, you can check the integrity of the cache file and the cache file's stats info file.
|
||||
This option is specified and when sending the SIGUSR1 signal to the s3fs process checks the cache status at that time.
|
||||
This option can take a file path as parameter to output the check result to that file.
|
||||
The file path parameter can be omitted. If omitted, the result will be output to stdout or syslog.
|
||||
.SS "utility mode options"
|
||||
.TP
|
||||
\fB\-u\fR or \fB\-\-incomplete\-mpu\-list\fR
|
||||
|
||||
@ -32,7 +32,8 @@ s3fs_SOURCES = \
|
||||
s3fs_util.cpp \
|
||||
fdcache.cpp \
|
||||
common_auth.cpp \
|
||||
addhead.cpp
|
||||
addhead.cpp \
|
||||
sighandlers.cpp
|
||||
if USE_SSL_OPENSSL
|
||||
s3fs_SOURCES += openssl_auth.cpp
|
||||
endif
|
||||
|
||||
11
src/common.h
11
src/common.h
@ -126,6 +126,16 @@ enum s3fs_log_level{
|
||||
} \
|
||||
}while(0)
|
||||
|
||||
// Special macro for checking cache files
|
||||
#define S3FS_LOW_CACHE(fp, fmt, ...) \
|
||||
do{ \
|
||||
if(foreground){ \
|
||||
fprintf(fp, fmt "%s\n", __VA_ARGS__); \
|
||||
}else{ \
|
||||
syslog(S3FS_LOG_LEVEL_TO_SYSLOG(S3FS_LOG_INFO), "%s: " fmt "%s", instance_name.c_str(), __VA_ARGS__); \
|
||||
} \
|
||||
}while(0)
|
||||
|
||||
// [NOTE]
|
||||
// small trick for VA_ARGS
|
||||
//
|
||||
@ -140,6 +150,7 @@ enum s3fs_log_level{
|
||||
#define S3FS_PRN_INFO2(fmt, ...) S3FS_LOW_LOGPRN2(S3FS_LOG_INFO, 2, fmt, ##__VA_ARGS__, "")
|
||||
#define S3FS_PRN_INFO3(fmt, ...) S3FS_LOW_LOGPRN2(S3FS_LOG_INFO, 3, fmt, ##__VA_ARGS__, "")
|
||||
#define S3FS_PRN_CURL(fmt, ...) S3FS_LOW_CURLDBG(fmt, ##__VA_ARGS__, "")
|
||||
#define S3FS_PRN_CACHE(fp, ...) S3FS_LOW_CACHE(fp, ##__VA_ARGS__, "")
|
||||
|
||||
//
|
||||
// Typedef
|
||||
|
||||
501
src/fdcache.cpp
501
src/fdcache.cpp
@ -31,6 +31,7 @@
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <dirent.h>
|
||||
#include <stddef.h>
|
||||
#include <curl/curl.h>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
@ -51,7 +52,8 @@ using namespace std;
|
||||
//------------------------------------------------
|
||||
// Symbols
|
||||
//------------------------------------------------
|
||||
static const int MAX_MULTIPART_CNT = 10 * 1000; // S3 multipart max count
|
||||
static const int MAX_MULTIPART_CNT = 10 * 1000; // S3 multipart max count
|
||||
static const int CHECK_CACHEFILE_PART_SIZE = 1024 * 16; // Buffer size in PageList::CheckZeroAreaInFile()
|
||||
|
||||
//
|
||||
// For cache directory top path
|
||||
@ -62,6 +64,28 @@ static const int MAX_MULTIPART_CNT = 10 * 1000; // S3 multipart max count
|
||||
#define TMPFILE_DIR_0PATH "/tmp"
|
||||
#endif
|
||||
|
||||
//
|
||||
// The following symbols are used by FdManager::RawCheckAllCache().
|
||||
//
|
||||
#define CACHEDBG_FMT_DIR_PROB "Directory: %s"
|
||||
#define CACHEDBG_FMT_HEAD "------------------------------------------------------------\n" \
|
||||
"Check cache file and its stats file consistency\n" \
|
||||
"------------------------------------------------------------"
|
||||
#define CACHEDBG_FMT_FOOT "------------------------------------------------------------\n" \
|
||||
"Summary - Total files: %d\n" \
|
||||
" Detected error files: %d\n" \
|
||||
" Detected error directories: %d\n" \
|
||||
"------------------------------------------------------------"
|
||||
#define CACHEDBG_FMT_FILE_OK "File: %s%s -> [OK] no problem"
|
||||
#define CACHEDBG_FMT_FILE_PROB "File: %s%s"
|
||||
#define CACHEDBG_FMT_DIR_PROB "Directory: %s"
|
||||
#define CACHEDBG_FMT_ERR_HEAD " -> [E] there is a mark that data exists in stats, but there is no data in the cache file."
|
||||
#define CACHEDBG_FMT_WARN_HEAD " -> [W] These show no data in stats, but there is evidence of data in the cache file(no problem)."
|
||||
#define CACHEDBG_FMT_WARN_OPEN "\n -> [W] This file is currently open and may not provide accurate analysis results."
|
||||
#define CACHEDBG_FMT_CRIT_HEAD " -> [C] %s"
|
||||
#define CACHEDBG_FMT_CRIT_HEAD2 " -> [C] "
|
||||
#define CACHEDBG_FMT_PROB_BLOCK " 0x%016jx(0x%016jx bytes)"
|
||||
|
||||
//------------------------------------------------
|
||||
// CacheFileStat class methods
|
||||
//------------------------------------------------
|
||||
@ -223,7 +247,7 @@ bool CacheFileStat::SetPath(const char* tpath, bool is_open)
|
||||
return Open();
|
||||
}
|
||||
|
||||
bool CacheFileStat::Open()
|
||||
bool CacheFileStat::RawOpen(bool readonly)
|
||||
{
|
||||
if(path.empty()){
|
||||
return false;
|
||||
@ -239,9 +263,16 @@ bool CacheFileStat::Open()
|
||||
return false;
|
||||
}
|
||||
// open
|
||||
if(-1 == (fd = open(sfile_path.c_str(), O_CREAT|O_RDWR, 0600))){
|
||||
S3FS_PRN_ERR("failed to open cache stat file path(%s) - errno(%d)", path.c_str(), errno);
|
||||
return false;
|
||||
if(readonly){
|
||||
if(-1 == (fd = open(sfile_path.c_str(), O_RDONLY))){
|
||||
S3FS_PRN_ERR("failed to read only open cache stat file path(%s) - errno(%d)", path.c_str(), errno);
|
||||
return false;
|
||||
}
|
||||
}else{
|
||||
if(-1 == (fd = open(sfile_path.c_str(), O_CREAT|O_RDWR, 0600))){
|
||||
S3FS_PRN_ERR("failed to open cache stat file path(%s) - errno(%d)", path.c_str(), errno);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// lock
|
||||
if(-1 == flock(fd, LOCK_EX)){
|
||||
@ -263,6 +294,16 @@ bool CacheFileStat::Open()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CacheFileStat::Open()
|
||||
{
|
||||
return RawOpen(false);
|
||||
}
|
||||
|
||||
bool CacheFileStat::ReadOnlyOpen()
|
||||
{
|
||||
return RawOpen(true);
|
||||
}
|
||||
|
||||
bool CacheFileStat::Release()
|
||||
{
|
||||
if(-1 == fd){
|
||||
@ -410,6 +451,181 @@ static fdpage_list_t parse_partsize_fdpage_list(const fdpage_list_t& pages, off_
|
||||
return parsed_pages;
|
||||
}
|
||||
|
||||
//------------------------------------------------
|
||||
// PageList class methods
|
||||
//------------------------------------------------
|
||||
//
|
||||
// Examine and return the status of each block in the file.
|
||||
//
|
||||
// Assuming the file is a sparse file, check the HOLE and DATA areas
|
||||
// and return it in fdpage_list_t. The loaded flag of each fdpage is
|
||||
// set to false for HOLE blocks and true for DATA blocks.
|
||||
//
|
||||
bool PageList::GetSparseFilePages(int fd, size_t file_size, fdpage_list_t& sparse_list)
|
||||
{
|
||||
// [NOTE]
|
||||
// Express the status of the cache file using fdpage_list_t.
|
||||
// There is a hole in the cache file(sparse file), and the
|
||||
// state of this hole is expressed by the "loaded" member of
|
||||
// struct fdpage. (the "modified" member is not used)
|
||||
//
|
||||
if(0 == file_size){
|
||||
// file is empty
|
||||
return true;
|
||||
}
|
||||
|
||||
bool is_hole = false;
|
||||
int hole_pos = lseek(fd, 0, SEEK_HOLE);
|
||||
int data_pos = lseek(fd, 0, SEEK_DATA);
|
||||
if(-1 == hole_pos && -1 == data_pos){
|
||||
S3FS_PRN_ERR("Could not find the first position both HOLE and DATA in the file(fd=%d).", fd);
|
||||
return false;
|
||||
}else if(-1 == hole_pos){
|
||||
is_hole = false;
|
||||
}else if(-1 == data_pos){
|
||||
is_hole = true;
|
||||
}else if(hole_pos < data_pos){
|
||||
is_hole = true;
|
||||
}else{
|
||||
is_hole = false;
|
||||
}
|
||||
|
||||
for(int cur_pos = 0, next_pos = 0; 0 <= cur_pos; cur_pos = next_pos, is_hole = !is_hole){
|
||||
fdpage page;
|
||||
page.offset = cur_pos;
|
||||
page.loaded = !is_hole;
|
||||
page.modified = false;
|
||||
|
||||
next_pos = lseek(fd, cur_pos, (is_hole ? SEEK_DATA : SEEK_HOLE));
|
||||
if(-1 == next_pos){
|
||||
page.bytes = static_cast<off_t>(file_size - cur_pos);
|
||||
}else{
|
||||
page.bytes = next_pos - cur_pos;
|
||||
}
|
||||
sparse_list.push_back(page);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Confirm that the specified area is ZERO
|
||||
//
|
||||
bool PageList::CheckZeroAreaInFile(int fd, off_t start, size_t bytes)
|
||||
{
|
||||
char* readbuff = new char[CHECK_CACHEFILE_PART_SIZE];
|
||||
|
||||
for(size_t comp_bytes = 0, check_bytes = 0; comp_bytes < bytes; comp_bytes += check_bytes){
|
||||
if(CHECK_CACHEFILE_PART_SIZE < (bytes - comp_bytes)){
|
||||
check_bytes = CHECK_CACHEFILE_PART_SIZE;
|
||||
}else{
|
||||
check_bytes = bytes - comp_bytes;
|
||||
}
|
||||
bool found_bad_data = false;
|
||||
ssize_t read_bytes;
|
||||
if(-1 == (read_bytes = pread(fd, readbuff, check_bytes, (start + comp_bytes)))){
|
||||
S3FS_PRN_ERR("Something error is occurred in reading %zu bytes at %lld from file(%d).", check_bytes, static_cast<long long int>(start + comp_bytes), fd);
|
||||
found_bad_data = true;
|
||||
}else{
|
||||
check_bytes = static_cast<size_t>(read_bytes);
|
||||
for(size_t tmppos = 0; tmppos < check_bytes; ++tmppos){
|
||||
if('\0' != readbuff[tmppos]){
|
||||
// found not ZERO data.
|
||||
found_bad_data = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(found_bad_data){
|
||||
delete[] readbuff;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
delete[] readbuff;
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Checks that the specified area matches the state of the sparse file.
|
||||
//
|
||||
// [Parameters]
|
||||
// checkpage: This is one state of the cache file, it is loaded from the stats file.
|
||||
// sparse_list: This is a list of the results of directly checking the cache file status(HOLE/DATA).
|
||||
// In the HOLE area, the "loaded" flag of fdpage is false. The DATA area has it set to true.
|
||||
// fd: opened file discriptor to target cache file.
|
||||
//
|
||||
bool PageList::CheckAreaInSparseFile(const struct fdpage& checkpage, const fdpage_list_t& sparse_list, int fd, fdpage_list_t& err_area_list, fdpage_list_t& warn_area_list)
|
||||
{
|
||||
// Check the block status of a part(Check Area: checkpage) of the target file.
|
||||
// The elements of sparse_list have 5 patterns that overlap this block area.
|
||||
//
|
||||
// File |<---...--------------------------------------...--->|
|
||||
// Check Area (offset)<-------------------->(offset + bytes - 1)
|
||||
// Area case(0) <------->
|
||||
// Area case(1) <------->
|
||||
// Area case(2) <-------->
|
||||
// Area case(3) <---------->
|
||||
// Area case(4) <----------->
|
||||
// Area case(5) <----------------------------->
|
||||
//
|
||||
bool result = true;
|
||||
|
||||
for(fdpage_list_t::const_iterator iter = sparse_list.begin(); iter != sparse_list.end(); ++iter){
|
||||
off_t check_start = 0;
|
||||
off_t check_bytes = 0;
|
||||
if((iter->offset + iter->bytes) <= checkpage.offset){
|
||||
// case 0
|
||||
continue; // next
|
||||
|
||||
}else if((checkpage.offset + checkpage.bytes) <= iter->offset){
|
||||
// case 1
|
||||
break; // finish
|
||||
|
||||
}else if(iter->offset < checkpage.offset && (iter->offset + iter->bytes) < (checkpage.offset + checkpage.bytes)){
|
||||
// case 2
|
||||
check_start = checkpage.offset;
|
||||
check_bytes = iter->bytes - (checkpage.offset - iter->offset);
|
||||
|
||||
}else if(iter->offset < (checkpage.offset + checkpage.bytes) && (checkpage.offset + checkpage.bytes) < (iter->offset + iter->bytes)){
|
||||
// case 3
|
||||
check_start = iter->offset;
|
||||
check_bytes = checkpage.bytes - (iter->offset - checkpage.offset);
|
||||
|
||||
}else if(checkpage.offset < iter->offset && (iter->offset + iter->bytes) < (checkpage.offset + checkpage.bytes)){
|
||||
// case 4
|
||||
check_start = iter->offset;
|
||||
check_bytes = iter->bytes;
|
||||
|
||||
}else{ // (iter->offset <= checkpage.offset && (checkpage.offset + checkpage.bytes) <= (iter->offset + iter->bytes))
|
||||
// case 5
|
||||
check_start = checkpage.offset;
|
||||
check_bytes = checkpage.bytes;
|
||||
}
|
||||
|
||||
// check target area type
|
||||
if(checkpage.loaded || checkpage.modified){
|
||||
// target area must be not HOLE(DATA) area.
|
||||
if(!iter->loaded){
|
||||
// Found bad area, it is HOLE area.
|
||||
fdpage page(check_start, check_bytes, false, false);
|
||||
err_area_list.push_back(page);
|
||||
result = false;
|
||||
}
|
||||
}else{
|
||||
// target area should be HOLE area.(If it is not a block boundary, it may be a DATA area.)
|
||||
if(iter->loaded){
|
||||
// need to check this area's each data, it should be ZERO.
|
||||
if(!PageList::CheckZeroAreaInFile(fd, check_start, static_cast<size_t>(check_bytes))){
|
||||
// Discovered an area that has un-initial status data but it probably does not effect bad.
|
||||
fdpage page(check_start, check_bytes, true, false);
|
||||
warn_area_list.push_back(page);
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//------------------------------------------------
|
||||
// PageList methods
|
||||
//------------------------------------------------
|
||||
@ -946,6 +1162,49 @@ void PageList::Dump() const
|
||||
S3FS_PRN_DBG("}");
|
||||
}
|
||||
|
||||
//
|
||||
// Compare the fdpage_list_t pages of the object with the state of the file.
|
||||
//
|
||||
// The loaded=true or modified=true area of pages must be a DATA block
|
||||
// (not a HOLE block) in the file.
|
||||
// The other area is a HOLE block in the file or is a DATA block(but the
|
||||
// data of the target area in that block should be ZERO).
|
||||
// If it is a bad area in the previous case, it will be reported as an error.
|
||||
// If the latter case does not match, it will be reported as a warning.
|
||||
//
|
||||
bool PageList::CompareSparseFile(int fd, size_t file_size, fdpage_list_t& err_area_list, fdpage_list_t& warn_area_list)
|
||||
{
|
||||
err_area_list.clear();
|
||||
warn_area_list.clear();
|
||||
|
||||
// First, list the block disk allocation area of the cache file.
|
||||
// The cache file has holes(sparse file) and no disk block areas
|
||||
// are assigned to any holes.
|
||||
fdpage_list_t sparse_list;
|
||||
if(!PageList::GetSparseFilePages(fd, file_size, sparse_list)){
|
||||
S3FS_PRN_ERR("Something error is occurred in parsing hole/data of the cache file(%d).", fd);
|
||||
|
||||
fdpage page(0, static_cast<off_t>(file_size), false, false);
|
||||
err_area_list.push_back(page);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if(sparse_list.empty() && pages.empty()){
|
||||
// both file and stats information are empty, it means cache file size is ZERO.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Compare each pages and sparse_list
|
||||
bool result = true;
|
||||
for(fdpage_list_t::const_iterator iter = pages.begin(); iter != pages.end(); ++iter){
|
||||
if(!PageList::CheckAreaInSparseFile(*iter, sparse_list, fd, err_area_list, warn_area_list)){
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//------------------------------------------------
|
||||
// FdEntity class methods
|
||||
//------------------------------------------------
|
||||
@ -2347,6 +2606,7 @@ bool FdManager::is_lock_init(false);
|
||||
string FdManager::cache_dir;
|
||||
bool FdManager::check_cache_dir_exist(false);
|
||||
off_t FdManager::free_disk_space = 0;
|
||||
std::string FdManager::check_cache_output;
|
||||
|
||||
//------------------------------------------------
|
||||
// FdManager class methods
|
||||
@ -2361,6 +2621,16 @@ bool FdManager::SetCacheDir(const char* dir)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FdManager::SetCacheCheckOutput(const char* path)
|
||||
{
|
||||
if(!path || '\0' == path[0]){
|
||||
check_cache_output.erase();
|
||||
}else{
|
||||
check_cache_output = path;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FdManager::DeleteCacheDirectory()
|
||||
{
|
||||
if(FdManager::cache_dir.empty()){
|
||||
@ -2915,6 +3185,227 @@ void FdManager::FreeReservedDiskSpace(off_t size)
|
||||
free_disk_space -= size;
|
||||
}
|
||||
|
||||
//
|
||||
// Inspect all files for stats file for cache file
|
||||
//
|
||||
// [NOTE]
|
||||
// The minimum sub_path parameter is "/".
|
||||
// The sub_path is a directory path starting from "/" and ending with "/".
|
||||
//
|
||||
// This method produces the following output.
|
||||
//
|
||||
// * Header
|
||||
// ------------------------------------------------------------
|
||||
// Check cache file and its stats file consistency
|
||||
// ------------------------------------------------------------
|
||||
// * When the cache file and its stats information match
|
||||
// File path: <file path> -> [OK] no problem
|
||||
//
|
||||
// * If there is a problem with the cache file and its stats information
|
||||
// File path: <file path>
|
||||
// -> [P] <If the problem is that parsing is not possible in the first place, the message is output here with this prefix.>
|
||||
// -> [E] there is a mark that data exists in stats, but there is no data in the cache file.
|
||||
// <offset address>(bytes)
|
||||
// ...
|
||||
// ...
|
||||
// -> [W] These show no data in stats, but there is evidence of data in the cache file.(no problem.)
|
||||
// <offset address>(bytes)
|
||||
// ...
|
||||
// ...
|
||||
//
|
||||
bool FdManager::RawCheckAllCache(FILE* fp, const char* cache_stat_top_dir, const char* sub_path, int& total_file_cnt, int& err_file_cnt, int& err_dir_cnt)
|
||||
{
|
||||
if(!cache_stat_top_dir || '\0' == cache_stat_top_dir[0] || !sub_path || '\0' == sub_path[0]){
|
||||
S3FS_PRN_ERR("Parameter cache_stat_top_dir is empty.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// allocate dirent buffer
|
||||
long name_max = pathconf(cache_stat_top_dir, _PC_NAME_MAX);
|
||||
if(-1 == name_max){
|
||||
name_max = 255; // [NOTE] Is PATH_MAX better?
|
||||
}
|
||||
size_t structlen = offsetof(struct dirent, d_name) + name_max + 1;
|
||||
struct dirent* pdirent;
|
||||
if(NULL == (pdirent = reinterpret_cast<struct dirent*>(malloc(structlen)))){
|
||||
S3FS_PRN_ERR("Could not allocate memory for dirent(length = %zu) by errno(%d)", structlen, errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
// open directory of cache file's stats
|
||||
DIR* statsdir;
|
||||
string target_dir = cache_stat_top_dir;
|
||||
target_dir += sub_path;
|
||||
if(NULL == (statsdir = opendir(target_dir.c_str()))){
|
||||
S3FS_PRN_ERR("Could not open directory(%s) by errno(%d)", target_dir.c_str(), errno);
|
||||
free(pdirent);
|
||||
return false;
|
||||
}
|
||||
|
||||
// loop in directory of cache file's stats
|
||||
struct dirent* pdirent_res = NULL;
|
||||
int result;
|
||||
for(result = readdir_r(statsdir, pdirent, &pdirent_res); 0 == result && pdirent_res; result = readdir_r(statsdir, pdirent, &pdirent_res)){
|
||||
if(DT_DIR == pdirent_res->d_type){
|
||||
// found directory
|
||||
if(0 == strcmp(pdirent_res->d_name, ".") || 0 == strcmp(pdirent_res->d_name, "..")){
|
||||
continue;
|
||||
}
|
||||
|
||||
// reentrant for sub directory
|
||||
string subdir_path = sub_path;
|
||||
subdir_path += pdirent_res->d_name;
|
||||
subdir_path += '/';
|
||||
if(!RawCheckAllCache(fp, cache_stat_top_dir, subdir_path.c_str(), total_file_cnt, err_file_cnt, err_dir_cnt)){
|
||||
// put error message for this dir.
|
||||
++err_dir_cnt;
|
||||
S3FS_PRN_CACHE(fp, CACHEDBG_FMT_DIR_PROB, subdir_path.c_str());
|
||||
S3FS_PRN_CACHE(fp, CACHEDBG_FMT_CRIT_HEAD, "Something error is occurred in checking this directory");
|
||||
}
|
||||
|
||||
}else{
|
||||
++total_file_cnt;
|
||||
|
||||
// make cache file path
|
||||
string strOpenedWarn;
|
||||
string cache_path;
|
||||
string object_file_path = sub_path;
|
||||
object_file_path += pdirent_res->d_name;
|
||||
if(!FdManager::MakeCachePath(object_file_path.c_str(), cache_path, false, false) || cache_path.empty()){
|
||||
++err_file_cnt;
|
||||
S3FS_PRN_CACHE(fp, CACHEDBG_FMT_FILE_PROB, object_file_path.c_str(), strOpenedWarn.c_str());
|
||||
S3FS_PRN_CACHE(fp, CACHEDBG_FMT_CRIT_HEAD, "Could not make cache file path");
|
||||
continue;
|
||||
}
|
||||
|
||||
// check if the target file is currently in operation.
|
||||
{
|
||||
AutoLock auto_lock(&FdManager::fd_manager_lock);
|
||||
|
||||
fdent_map_t::iterator iter = fent.find(object_file_path);
|
||||
if(fent.end() != iter){
|
||||
// This file is opened now, then we need to put warning message.
|
||||
strOpenedWarn = CACHEDBG_FMT_WARN_OPEN;
|
||||
}
|
||||
}
|
||||
|
||||
// open cache file
|
||||
int cache_file_fd;
|
||||
if(-1 == (cache_file_fd = open(cache_path.c_str(), O_RDONLY))){
|
||||
++err_file_cnt;
|
||||
S3FS_PRN_CACHE(fp, CACHEDBG_FMT_FILE_PROB, object_file_path.c_str(), strOpenedWarn.c_str());
|
||||
S3FS_PRN_CACHE(fp, CACHEDBG_FMT_CRIT_HEAD, "Could not open cache file");
|
||||
continue;
|
||||
}
|
||||
|
||||
// get inode number for cache file
|
||||
struct stat st;
|
||||
if(0 != fstat(cache_file_fd, &st)){
|
||||
++err_file_cnt;
|
||||
S3FS_PRN_CACHE(fp, CACHEDBG_FMT_FILE_PROB, object_file_path.c_str(), strOpenedWarn.c_str());
|
||||
S3FS_PRN_CACHE(fp, CACHEDBG_FMT_CRIT_HEAD, "Could not get file inode number for cache file");
|
||||
|
||||
close(cache_file_fd);
|
||||
continue;
|
||||
}
|
||||
ino_t cache_file_inode = st.st_ino;
|
||||
|
||||
// open cache stat file and load page info.
|
||||
PageList pagelist;
|
||||
CacheFileStat cfstat(object_file_path.c_str());
|
||||
if(!cfstat.ReadOnlyOpen() || !pagelist.Serialize(cfstat, false, cache_file_inode)){
|
||||
++err_file_cnt;
|
||||
S3FS_PRN_CACHE(fp, CACHEDBG_FMT_FILE_PROB, object_file_path.c_str(), strOpenedWarn.c_str());
|
||||
S3FS_PRN_CACHE(fp, CACHEDBG_FMT_CRIT_HEAD, "Could not load cache file stats information");
|
||||
|
||||
close(cache_file_fd);
|
||||
continue;
|
||||
}
|
||||
cfstat.Release();
|
||||
|
||||
// compare cache file size and stats information
|
||||
if(st.st_size != pagelist.Size()){
|
||||
++err_file_cnt;
|
||||
S3FS_PRN_CACHE(fp, CACHEDBG_FMT_FILE_PROB, object_file_path.c_str(), strOpenedWarn.c_str());
|
||||
S3FS_PRN_CACHE(fp, CACHEDBG_FMT_CRIT_HEAD2 "The cache file size(%lld) and the value(%lld) from cache file stats are different", static_cast<long long int>(st.st_size), static_cast<long long int>(pagelist.Size()));
|
||||
|
||||
close(cache_file_fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
// compare cache file stats and cache file blocks
|
||||
fdpage_list_t err_area_list;
|
||||
fdpage_list_t warn_area_list;
|
||||
if(!pagelist.CompareSparseFile(cache_file_fd, st.st_size, err_area_list, warn_area_list)){
|
||||
// Found some error or warning
|
||||
S3FS_PRN_CACHE(fp, CACHEDBG_FMT_FILE_PROB, object_file_path.c_str(), strOpenedWarn.c_str());
|
||||
if(!warn_area_list.empty()){
|
||||
S3FS_PRN_CACHE(fp, CACHEDBG_FMT_WARN_HEAD);
|
||||
for(fdpage_list_t::const_iterator witer = warn_area_list.begin(); witer != warn_area_list.end(); ++witer){
|
||||
S3FS_PRN_CACHE(fp, CACHEDBG_FMT_PROB_BLOCK, (intmax_t)(witer->offset), (intmax_t)(witer->bytes));
|
||||
}
|
||||
}
|
||||
if(!err_area_list.empty()){
|
||||
++err_file_cnt;
|
||||
S3FS_PRN_CACHE(fp, CACHEDBG_FMT_ERR_HEAD);
|
||||
for(fdpage_list_t::const_iterator eiter = err_area_list.begin(); eiter != err_area_list.end(); ++eiter){
|
||||
S3FS_PRN_CACHE(fp, CACHEDBG_FMT_PROB_BLOCK, (intmax_t)(eiter->offset), (intmax_t)(eiter->bytes));
|
||||
}
|
||||
}
|
||||
}else{
|
||||
// There is no problem!
|
||||
if(!strOpenedWarn.empty()){
|
||||
strOpenedWarn += "\n ";
|
||||
}
|
||||
S3FS_PRN_CACHE(fp, CACHEDBG_FMT_FILE_OK, object_file_path.c_str(), strOpenedWarn.c_str());
|
||||
}
|
||||
err_area_list.clear();
|
||||
warn_area_list.clear();
|
||||
close(cache_file_fd);
|
||||
}
|
||||
}
|
||||
|
||||
closedir(statsdir);
|
||||
free(pdirent);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FdManager::CheckAllCache()
|
||||
{
|
||||
FILE* fp;
|
||||
if(FdManager::check_cache_output.empty()){
|
||||
fp = stdout;
|
||||
}else{
|
||||
if(NULL == (fp = fopen(FdManager::check_cache_output.c_str(), "a+"))){
|
||||
S3FS_PRN_ERR("Could not open(create) output file(%s) for checking all cache by errno(%d)", FdManager::check_cache_output.c_str(), errno);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// print head message
|
||||
S3FS_PRN_CACHE(fp, CACHEDBG_FMT_HEAD);
|
||||
|
||||
// Loop in directory of cache file's stats
|
||||
string top_path = CacheFileStat::GetCacheFileStatTopDir();
|
||||
int total_file_cnt = 0;
|
||||
int err_file_cnt = 0;
|
||||
int err_dir_cnt = 0;
|
||||
bool result = RawCheckAllCache(fp, top_path.c_str(), "/", total_file_cnt, err_file_cnt, err_dir_cnt);
|
||||
if(!result){
|
||||
S3FS_PRN_ERR("Processing failed due to some problem.");
|
||||
}
|
||||
|
||||
// print foot message
|
||||
S3FS_PRN_CACHE(fp, CACHEDBG_FMT_FOOT, total_file_cnt, err_file_cnt, err_dir_cnt);
|
||||
|
||||
if(stdout != fp){
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* tab-width: 4
|
||||
|
||||
@ -34,6 +34,8 @@ class CacheFileStat
|
||||
private:
|
||||
static bool MakeCacheFileStatPath(const char* path, std::string& sfile_path, bool is_create_dir = true);
|
||||
|
||||
bool RawOpen(bool readonly);
|
||||
|
||||
public:
|
||||
static std::string GetCacheFileStatTopDir(void);
|
||||
static bool DeleteCacheFileStat(const char* path);
|
||||
@ -45,6 +47,7 @@ class CacheFileStat
|
||||
~CacheFileStat();
|
||||
|
||||
bool Open(void);
|
||||
bool ReadOnlyOpen(void);
|
||||
bool Release(void);
|
||||
bool SetPath(const char* tpath, bool is_open = true);
|
||||
int GetFd(void) const { return fd; }
|
||||
@ -91,6 +94,10 @@ class PageList
|
||||
};
|
||||
|
||||
private:
|
||||
static bool GetSparseFilePages(int fd, size_t file_size, fdpage_list_t& sparse_list);
|
||||
static bool CheckZeroAreaInFile(int fd, off_t start, size_t bytes);
|
||||
static bool CheckAreaInSparseFile(const struct fdpage& checkpage, const fdpage_list_t& sparse_list, int fd, fdpage_list_t& err_area_list, fdpage_list_t& warn_area_list);
|
||||
|
||||
void Clear(void);
|
||||
bool Compress();
|
||||
bool Parse(off_t new_pos);
|
||||
@ -118,6 +125,7 @@ class PageList
|
||||
|
||||
bool Serialize(CacheFileStat& file, bool is_output, ino_t inode);
|
||||
void Dump(void) const;
|
||||
bool CompareSparseFile(int fd, size_t file_size, fdpage_list_t& err_area_list, fdpage_list_t& warn_area_list);
|
||||
};
|
||||
|
||||
//------------------------------------------------
|
||||
@ -217,12 +225,14 @@ class FdManager
|
||||
static std::string cache_dir;
|
||||
static bool check_cache_dir_exist;
|
||||
static off_t free_disk_space; // limit free disk space
|
||||
static std::string check_cache_output;
|
||||
|
||||
fdent_map_t fent;
|
||||
|
||||
private:
|
||||
static off_t GetFreeDiskSpace(const char* path);
|
||||
void CleanupCacheDirInternal(const std::string &path = "");
|
||||
bool RawCheckAllCache(FILE* fp, const char* cache_stat_top_dir, const char* sub_path, int& total_file_cnt, int& err_file_cnt, int& err_dir_cnt);
|
||||
|
||||
public:
|
||||
FdManager();
|
||||
@ -234,8 +244,10 @@ class FdManager
|
||||
static bool DeleteCacheDirectory(void);
|
||||
static int DeleteCacheFile(const char* path);
|
||||
static bool SetCacheDir(const char* dir);
|
||||
static bool IsCacheDir(void) { return (0 < FdManager::cache_dir.size()); }
|
||||
static bool IsCacheDir(void) { return !FdManager::cache_dir.empty(); }
|
||||
static const char* GetCacheDir(void) { return FdManager::cache_dir.c_str(); }
|
||||
static bool SetCacheCheckOutput(const char* path);
|
||||
static const char* GetCacheCheckOutput(void) { return FdManager::check_cache_output.c_str(); }
|
||||
static bool MakeCachePath(const char* path, std::string& cache_path, bool is_create_dir = true, bool is_mirror_path = false);
|
||||
static bool CheckCacheTopDir(void);
|
||||
static bool MakeRandomTempPath(const char* path, std::string& tmppath);
|
||||
@ -256,6 +268,8 @@ class FdManager
|
||||
bool Close(FdEntity* ent);
|
||||
bool ChangeEntityToTempPath(FdEntity* ent, const char* path);
|
||||
void CleanupCacheDir();
|
||||
|
||||
bool CheckAllCache(void);
|
||||
};
|
||||
|
||||
#endif // FD_CACHE_H_
|
||||
|
||||
28
src/s3fs.cpp
28
src/s3fs.cpp
@ -52,6 +52,7 @@
|
||||
#include "fdcache.h"
|
||||
#include "s3fs_auth.h"
|
||||
#include "addhead.h"
|
||||
#include "sighandlers.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@ -3549,6 +3550,11 @@ static void* s3fs_init(struct fuse_conn_info* conn)
|
||||
conn->want |= FUSE_CAP_BIG_WRITES;
|
||||
}
|
||||
|
||||
// Signal object
|
||||
if(S3fsSignals::Initialize()){
|
||||
S3FS_PRN_ERR("Failed to initialize signal object, but continue...");
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -3556,6 +3562,11 @@ static void s3fs_destroy(void*)
|
||||
{
|
||||
S3FS_PRN_INFO("destroy");
|
||||
|
||||
// Signal object
|
||||
if(S3fsSignals::Destroy()){
|
||||
S3FS_PRN_WARN("Failed to clean up signal object.");
|
||||
}
|
||||
|
||||
// cache(remove at last)
|
||||
if(is_remove_cache && (!CacheFileStat::DeleteCacheFileStatDirectory() || !FdManager::DeleteCacheDirectory())){
|
||||
S3FS_PRN_WARN("Could not remove cache directory.");
|
||||
@ -5102,6 +5113,23 @@ static int my_fuse_opt_proc(void* data, const char* arg, int key, struct fuse_ar
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
//
|
||||
// Check cache file, using SIGUSR1
|
||||
//
|
||||
if(0 == strcmp(arg, "set_check_cache_sigusr1")){
|
||||
if(!S3fsSignals::SetUsr1Handler(NULL)){
|
||||
S3FS_PRN_EXIT("could not set sigusr1 for checking cache.");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}else if(0 == STR2NCMP(arg, "set_check_cache_sigusr1=")){
|
||||
const char* strfilepath = strchr(arg, '=') + sizeof(char);
|
||||
if(!S3fsSignals::SetUsr1Handler(strfilepath)){
|
||||
S3FS_PRN_EXIT("could not set sigusr1 for checking cache and output file(%s).", strfilepath);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(0 == STR2NCMP(arg, "accessKeyId=")){
|
||||
S3FS_PRN_EXIT("option accessKeyId is no longer supported.");
|
||||
|
||||
@ -1506,6 +1506,15 @@ void show_help ()
|
||||
" If \"body\" is specified, some API communication body data will be\n"
|
||||
" output in addition to the debug message output as \"normal\".\n"
|
||||
"\n"
|
||||
" set_check_cache_sigusr1 (default is stdout)\n"
|
||||
" If the cache is enabled, you can check the integrity of the\n"
|
||||
" cache file and the cache file's stats info file.\n"
|
||||
" This option is specified and when sending the SIGUSR1 signal\n"
|
||||
" to the s3fs process checks the cache status at that time.\n"
|
||||
" This option can take a file path as parameter to output the\n"
|
||||
" check result to that file. The file path parameter can be omitted.\n"
|
||||
" If omitted, the result will be output to stdout or syslog.\n"
|
||||
"\n"
|
||||
"FUSE/mount Options:\n"
|
||||
"\n"
|
||||
" Most of the generic mount options described in 'man mount' are\n"
|
||||
|
||||
230
src/sighandlers.cpp
Normal file
230
src/sighandlers.cpp
Normal file
@ -0,0 +1,230 @@
|
||||
/*
|
||||
* s3fs - FUSE-based file system backed by Amazon S3
|
||||
*
|
||||
* Copyright(C) 2007 Randy Rizun <rrizun@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cerrno>
|
||||
#include <syslog.h>
|
||||
#include <pthread.h>
|
||||
#include <curl/curl.h>
|
||||
#include <csignal>
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
#include "common.h"
|
||||
#include "sighandlers.h"
|
||||
#include "curl.h"
|
||||
#include "fdcache.h"
|
||||
#include "psemaphore.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Class S3fsSignals
|
||||
//-------------------------------------------------------------------
|
||||
S3fsSignals* S3fsSignals::pSingleton = NULL;
|
||||
bool S3fsSignals::enableUsr1 = false;
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Class methods
|
||||
//-------------------------------------------------------------------
|
||||
bool S3fsSignals::Initialize()
|
||||
{
|
||||
if(!S3fsSignals::pSingleton){
|
||||
S3fsSignals::pSingleton = new S3fsSignals;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool S3fsSignals::Destroy()
|
||||
{
|
||||
if(S3fsSignals::pSingleton){
|
||||
delete S3fsSignals::pSingleton;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void S3fsSignals::HandlerUSR1(int sig)
|
||||
{
|
||||
if(SIGUSR1 != sig){
|
||||
S3FS_PRN_ERR("The handler for SIGUSR1 received signal(%d)", sig);
|
||||
return;
|
||||
}
|
||||
|
||||
S3fsSignals* pSigobj = S3fsSignals::get();
|
||||
if(!pSigobj){
|
||||
S3FS_PRN_ERR("S3fsSignals object is not initialized.");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!pSigobj->WakeupUsr1Thread()){
|
||||
S3FS_PRN_ERR("Failed to wakeup the thread for SIGUSR1.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool S3fsSignals::SetUsr1Handler(const char* path)
|
||||
{
|
||||
// set output file
|
||||
if(!FdManager::SetCacheCheckOutput(path)){
|
||||
S3FS_PRN_ERR("Could not set output file(%s) for checking cache.", path ? path : "null(stdout)");
|
||||
return false;
|
||||
}
|
||||
|
||||
S3fsSignals::enableUsr1 = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void* S3fsSignals::CheckCacheWorker(void* arg)
|
||||
{
|
||||
Semaphore* pSem = static_cast<Semaphore*>(arg);
|
||||
if(!pSem){
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
if(!S3fsSignals::enableUsr1){
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
// wait and loop
|
||||
while(S3fsSignals::enableUsr1){
|
||||
// wait
|
||||
pSem->wait();
|
||||
if(!S3fsSignals::enableUsr1){
|
||||
break; // assap
|
||||
}
|
||||
|
||||
// check all cache
|
||||
if(!FdManager::get()->CheckAllCache()){
|
||||
S3FS_PRN_ERR("Processing failed due to some problem.");
|
||||
}
|
||||
|
||||
// do not allow request queuing
|
||||
for(int value = pSem->get_value(); 0 < value; value = pSem->get_value()){
|
||||
pSem->wait();
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Methods
|
||||
//-------------------------------------------------------------------
|
||||
S3fsSignals::S3fsSignals() : pThreadUsr1(NULL), pSemUsr1(NULL)
|
||||
{
|
||||
if(S3fsSignals::enableUsr1){
|
||||
if(!InitUsr1Handler()){
|
||||
S3FS_PRN_ERR("failed creating thread for SIGUSR1 handler, but continue...");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
S3fsSignals::~S3fsSignals()
|
||||
{
|
||||
if(S3fsSignals::enableUsr1){
|
||||
if(!DestroyUsr1Handler()){
|
||||
S3FS_PRN_ERR("failed stopping thread for SIGUSR1 handler, but continue...");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool S3fsSignals::InitUsr1Handler()
|
||||
{
|
||||
if(pThreadUsr1 || pSemUsr1){
|
||||
S3FS_PRN_ERR("Already run thread for SIGUSR1");
|
||||
return false;
|
||||
}
|
||||
|
||||
// create thread
|
||||
int result;
|
||||
pSemUsr1 = new Semaphore(0);
|
||||
pThreadUsr1 = new pthread_t;
|
||||
if(0 != (result = pthread_create(pThreadUsr1, NULL, S3fsSignals::CheckCacheWorker, static_cast<void*>(pSemUsr1)))){
|
||||
S3FS_PRN_ERR("Could not create thread for SIGUSR1 by %d", result);
|
||||
delete pSemUsr1;
|
||||
delete pThreadUsr1;
|
||||
pSemUsr1 = NULL;
|
||||
pThreadUsr1 = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
// set handler
|
||||
struct sigaction sa;
|
||||
memset(&sa, 0, sizeof(struct sigaction));
|
||||
sa.sa_handler = S3fsSignals::HandlerUSR1;
|
||||
sa.sa_flags = SA_RESTART;
|
||||
if(0 != sigaction(SIGUSR1, &sa, NULL)){
|
||||
S3FS_PRN_ERR("Could not set signal handler for SIGUSR1");
|
||||
DestroyUsr1Handler();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool S3fsSignals::DestroyUsr1Handler()
|
||||
{
|
||||
if(!pThreadUsr1 || !pSemUsr1){
|
||||
return false;
|
||||
}
|
||||
// for thread exit
|
||||
S3fsSignals::enableUsr1 = false;
|
||||
|
||||
// wakeup thread
|
||||
pSemUsr1->post();
|
||||
|
||||
// wait for thread exiting
|
||||
void* retval = NULL;
|
||||
int result;
|
||||
if(0 != (result = pthread_join(*pThreadUsr1, &retval))){
|
||||
S3FS_PRN_ERR("Could not stop thread for SIGUSR1 by %d", result);
|
||||
return false;
|
||||
}
|
||||
delete pSemUsr1;
|
||||
delete pThreadUsr1;
|
||||
pSemUsr1 = NULL;
|
||||
pThreadUsr1 = NULL;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool S3fsSignals::WakeupUsr1Thread()
|
||||
{
|
||||
if(!pThreadUsr1 || !pSemUsr1){
|
||||
S3FS_PRN_ERR("The thread for SIGUSR1 is not setup.");
|
||||
return false;
|
||||
}
|
||||
pSemUsr1->post();
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* tab-width: 4
|
||||
* c-basic-offset: 4
|
||||
* End:
|
||||
* vim600: noet sw=4 ts=4 fdm=marker
|
||||
* vim<600: noet sw=4 ts=4
|
||||
*/
|
||||
67
src/sighandlers.h
Normal file
67
src/sighandlers.h
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* s3fs - FUSE-based file system backed by Amazon S3
|
||||
*
|
||||
* Copyright(C) 2007 Randy Rizun <rrizun@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef S3FS_SIGHANDLERS_H_
|
||||
#define S3FS_SIGHANDLERS_H_
|
||||
|
||||
#include "psemaphore.h"
|
||||
|
||||
//----------------------------------------------
|
||||
// class S3fsSignals
|
||||
//----------------------------------------------
|
||||
class S3fsSignals
|
||||
{
|
||||
private:
|
||||
static S3fsSignals* pSingleton;
|
||||
static bool enableUsr1;
|
||||
|
||||
pthread_t* pThreadUsr1;
|
||||
Semaphore* pSemUsr1;
|
||||
|
||||
protected:
|
||||
static S3fsSignals* get(void) { return pSingleton; }
|
||||
|
||||
static void HandlerUSR1(int sig);
|
||||
static void* CheckCacheWorker(void* arg);
|
||||
|
||||
S3fsSignals();
|
||||
~S3fsSignals();
|
||||
|
||||
bool InitUsr1Handler(void);
|
||||
bool DestroyUsr1Handler(void);
|
||||
bool WakeupUsr1Thread(void);
|
||||
|
||||
public:
|
||||
static bool Initialize(void);
|
||||
static bool Destroy(void);
|
||||
|
||||
static bool SetUsr1Handler(const char* path);
|
||||
};
|
||||
|
||||
#endif // S3FS_SIGHANDLERS_H_
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* tab-width: 4
|
||||
* c-basic-offset: 4
|
||||
* End:
|
||||
* vim600: noet sw=4 ts=4 fdm=marker
|
||||
* vim<600: noet sw=4 ts=4
|
||||
*/
|
||||
Reference in New Issue
Block a user