#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <pthread.h>
#include <libsmbclient.h>
#include <fuse/fuse.h>
#include <glib.h>
#include "common.h"
#include "smbitem.h"
#include "auth.h"
#include "process.h"
#include "samba.h"
#include "stat_workaround.h"
#include "reconfigure.h"
static int event_query_browser_flag = 1;
static int event_time_step = 10;
static int event_smb_tree_scan_period = 300;
static int event_smb_tree_elements_ttl = 900;
static int event_config_update_period = 300;
static time_t event_last_smb_tree_scan = (time_t) 0;
static time_t event_last_config_update = (time_t) 0;
static pthread_mutex_t m_evthread = PTHREAD_MUTEX_INITIALIZER;
static pthread_t event_ev_thread_id;
static pthread_t event_smb_thread_id;
int event_set_query_browser_flag(int flag){
DPRINTF(7, "flag=%d\n", flag);
g_atomic_int_set(&event_query_browser_flag, flag);
return 1;
}
static inline int event_get_query_browser_flag(void){
return g_atomic_int_get(&event_query_browser_flag);
}
int event_set_time_step(int step){
if (step < 1) return 0;
DPRINTF(7, "step=%d\n", step);
g_atomic_int_set(&event_time_step, step);
return 1;
}
static inline int event_get_time_step(void){
return g_atomic_int_get(&event_time_step);
}
int event_set_smb_tree_scan_period(int period){
if (period < event_get_time_step()) return 0;
DPRINTF(7, "period=%d\n", period);
pthread_mutex_lock(&m_evthread);
event_smb_tree_scan_period = period;
pthread_mutex_unlock(&m_evthread);
return 1;
}
static inline int event_get_smb_tree_scan_period(void){
int period;
pthread_mutex_lock(&m_evthread);
period = event_smb_tree_scan_period;
pthread_mutex_unlock(&m_evthread);
return period;
}
int event_set_smb_tree_elements_ttl(int ttl){
if (ttl < event_get_smb_tree_scan_period()) return 0;
DPRINTF(7, "ttl=%d\n", ttl);
g_atomic_int_set(&event_smb_tree_elements_ttl, ttl);
return 1;
}
static inline int event_get_smb_tree_elements_ttl(void){
return g_atomic_int_get(&event_smb_tree_elements_ttl);
}
int event_set_config_update_period(int period){
if ((period != 0) && (period < event_get_time_step())) return 0;
DPRINTF(7, "period=%d\n", period);
pthread_mutex_lock(&m_evthread);
event_config_update_period = period;
pthread_mutex_unlock(&m_evthread);
return 1;
}
static void event_set_last_smb_tree_scan(time_t scan_time){
pthread_mutex_lock(&m_evthread);
event_last_smb_tree_scan = scan_time;
pthread_mutex_unlock(&m_evthread);
}
static int event_is_time_for_smb_tree_scan(void){
int flag;
pthread_mutex_lock(&m_evthread);
flag = (time(NULL) >= event_last_smb_tree_scan +
event_smb_tree_scan_period) ? 1 : 0;
pthread_mutex_unlock(&m_evthread);
return flag;
}
static void event_set_last_config_update(time_t update_time){
pthread_mutex_lock(&m_evthread);
event_last_config_update = update_time;
pthread_mutex_unlock(&m_evthread);
}
static int event_is_time_for_config_update(void){
int flag;
pthread_mutex_lock(&m_evthread);
flag = ((event_config_update_period > 0) &&
(time(NULL) >= event_last_config_update +
event_config_update_period)) ? 1 : 0;
pthread_mutex_unlock(&m_evthread);
return flag;
}
static void event_scan_samba_group(const char *group){
char buf[4096], name[256], link[256];
int count;
samba_fd fd;
DPRINTF(5, "group=%s\n", group);
snprintf(name, sizeof(name), "/%s", group);
fd = samba_opendir(name);
while(1){
struct smb_conn_dirent_rec *rec;
count = samba_readdir(fd, buf, sizeof(buf));
if (count <= 0) break;
rec = (struct smb_conn_dirent_rec *) buf;
for( ; count >= (int) sizeof(struct smb_conn_dirent_rec);
count -= sizeof(struct smb_conn_dirent_rec)){
switch(rec->smbc_type){
case SMBC_SERVER:
smbitem_mkhost(rec->d_name, group, 1, SMBITEM_SAMBA_TREE);
snprintf(name, sizeof(name), "%s/%s", group, rec->d_name);
snprintf(link, sizeof(link), "../%s", rec->d_name);
smbitem_mklink(name, link, SMBITEM_SAMBA_TREE);
break;
default:
DPRINTF(6, "ups..., smbc_type=%d, d_name=%s\n",
rec->smbc_type, rec->d_name);
}
rec++;
}
}
samba_closedir(fd);
}
static void event_scan_smb_root(void){
char buf[4096];
int count;
samba_fd fd;
DPRINTF(5, "reading group list\n");
fd = samba_opendir("/");
while(1){
struct smb_conn_dirent_rec *rec;
count = samba_readdir(fd, buf, sizeof(buf));
if (count <= 0) break;
rec = (struct smb_conn_dirent_rec *) buf;
for( ; count >= (int) sizeof(struct smb_conn_dirent_rec);
count -= sizeof(struct smb_conn_dirent_rec)){
switch(rec->smbc_type){
case SMBC_WORKGROUP:
smbitem_mkgroup(rec->d_name, SMBITEM_SAMBA_TREE);
break;
default:
DPRINTF(6, "ups..., smbc_type=%d, d_name=%s\n",
rec->smbc_type, rec->d_name);
}
rec++;
}
}
samba_closedir(fd);
}
void event_scan_smb_tree(void){
int i;
struct smbitem *dir;
if (event_get_query_browser_flag()) event_scan_smb_root();
dir = smbitem_get_samba_groups();
for(i = 0; i < dir->child_cnt; i++){
if (dir->childs[i]->type != SMBITEM_GROUP) continue;
event_scan_samba_group(dir->childs[i]->name);
}
smbitem_release_dir(dir);
}
static void* event_update_smb_tree_thread(void *data){
time_t scan_time;
time_t die_threshold;
int time_step;
(void)data;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
scan_time = time(NULL);
event_scan_smb_tree();
event_set_last_smb_tree_scan(scan_time);
while(1){
time_step = event_get_time_step();
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
sleep(time_step);
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
if (event_is_time_for_smb_tree_scan()){
scan_time = time(NULL);
die_threshold = scan_time - event_get_smb_tree_elements_ttl();
DPRINTF(5, "start at timestamp=%u, die_threshold=%u\n",
(unsigned) scan_time, (unsigned) die_threshold);
event_scan_smb_tree();
smbitem_delete_obsolete(die_threshold, SMBITEM_SAMBA_TREE);
event_set_last_smb_tree_scan(scan_time);
}
}
return NULL;
}
static void event_reread_config(void){
time_t reread_time;
reread_time = time(NULL);
DPRINTF(5, "start at timestamp=%u\n", (unsigned) reread_time);
reconfigure_read_config(0);
smbitem_delete_obsolete(reread_time, SMBITEM_USER_TREE);
auth_delete_obsolete(reread_time);
stat_workaround_delete_obsolete(reread_time);
event_set_last_config_update(reread_time);
}
static void* event_thread(void *data){
siginfo_t siginfo;
sigset_t signal_set;
time_t start_time;
struct timespec timeout;
int sigret;
(void)data;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
start_time = time(NULL);
event_set_last_config_update(start_time);
/* set signals to watch */
sigemptyset(&signal_set);
sigaddset(&signal_set, SIGHUP);
sigaddset(&signal_set, SIGCHLD);
while(1){
timeout.tv_sec = event_get_time_step();
timeout.tv_nsec = 0;
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
sigret = sigtimedwait(&signal_set, &siginfo, &timeout);
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
process_cleanup_from_zombies();
if ((sigret != -1 && siginfo.si_signo == SIGHUP)
|| event_is_time_for_config_update())
event_reread_config();
}
return NULL;
}
static void* event_init(struct fuse_conn_info *conn){
(void) conn;
if (pthread_create(&event_smb_thread_id, NULL,
event_update_smb_tree_thread, NULL) != 0){
fprintf(stderr, "Could not create smb_tree thread\n");
exit(1);
}
if (pthread_create(&event_ev_thread_id, NULL,
event_thread, NULL) != 0){
fprintf(stderr, "Could not create event thread\n");
exit(1);
}
return NULL;
}
static void event_destroy(void *private_data){
(void)private_data;
DPRINTF(1, "Destroy cfg and smb_tree threads\n");
process_disable_new_smb_conn_starting();
process_kill_all();
pthread_cancel(event_ev_thread_id);
pthread_cancel(event_smb_thread_id);
pthread_join(event_ev_thread_id, NULL);
pthread_join(event_smb_thread_id, NULL);
}
void event_set_event_handler(struct fuse_operations *file_oper){
file_oper->init = event_init;
file_oper->destroy = event_destroy;
}