From 28495b3bb29df1ae87967f208d008182a794f37e Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Sat, 13 Aug 2005 21:17:11 +0000 Subject: reconnection --- ChangeLog | 8 +++ cache.c | 3 + cache.h | 4 ++ sshfs.c | 229 +++++++++++++++++++++++++++++++++++++++++++++++--------------- 4 files changed, 188 insertions(+), 56 deletions(-) diff --git a/ChangeLog b/ChangeLog index 46fb46c..d6837e4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2005-08-13 Miklos Szeredi + + * Add 'reconnect' option, which tries to reconnect to the server + when the connection is broken. If a password is required for + connection, it is recommended that you install ssh-askpass, and + set the SSH_ASKPASS environment variable (see 'man ssh' for more + details). + 2005-05-05 Miklos Szeredi * Work around missing truncate() support in some older sftp diff --git a/cache.c b/cache.c index 2ccf115..733a451 100644 --- a/cache.c +++ b/cache.c @@ -406,6 +406,9 @@ static int cache_write(const char *path, const char *buf, size_t size, static void cache_unity_fill(struct fuse_cache_operations *oper, struct fuse_operations *cache_oper) { +#if FUSE_VERSION >= 23 + cache_oper->init = oper->oper.init; +#endif cache_oper->getattr = oper->oper.getattr; cache_oper->readlink = oper->oper.readlink; cache_oper->getdir = cache_unity_getdir; diff --git a/cache.h b/cache.h index 0f9d4f4..b0b93b1 100644 --- a/cache.h +++ b/cache.h @@ -8,6 +8,10 @@ #include +#ifndef FUSE_VERSION +#define FUSE_VERSION (FUSE_MAJOR_VERSION * 10 + FUSE_MINOR_VERSION) +#endif + typedef struct fuse_cache_dirhandle *fuse_cache_dirh_t; typedef int (*fuse_cache_dirfil_t) (fuse_cache_dirh_t h, const char *name, const struct stat *stbuf); diff --git a/sshfs.c b/sshfs.c index 9a33d9a..f7f57fc 100644 --- a/sshfs.c +++ b/sshfs.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -86,11 +87,14 @@ static int infd; static int outfd; +static int connver; static int server_version; static int debug = 0; +static int reconnect = 0; static int sync_write = 0; static int sync_read = 0; static char *base_path; +static char *host; struct buffer { uint8_t *p; @@ -111,6 +115,7 @@ struct request { sem_t ready; uint8_t reply_type; int replied; + int error; struct buffer reply; struct timeval start; void *data; @@ -135,6 +140,7 @@ struct sshfs_file { struct read_chunk *readahead; off_t next_pos; int is_seq; + int connver; }; static GHashTable *reqtab; @@ -192,6 +198,7 @@ enum { SOPT_SYNC_READ, SOPT_MAX_READ, SOPT_DEBUG, + SOPT_RECONNECT, SOPT_LAST /* Last entry in this list! */ }; @@ -202,6 +209,7 @@ static struct opt sshfs_opts[] = { [SOPT_SYNC_READ] = { .optname = "no_readahead" }, [SOPT_MAX_READ] = { .optname = "max_read" }, [SOPT_DEBUG] = { .optname = "sshfs_debug" }, + [SOPT_RECONNECT] = { .optname = "reconnect" }, [SOPT_LAST] = { .optname = NULL } }; @@ -545,9 +553,13 @@ static int start_ssh(char *host) perror("failed to fork"); return -1; } else if (pid == 0) { + int devnull; int argctr = 0; char *ssh_args[sizeof(ssh_opts)/sizeof(struct opt) + 32]; char *ssh_cmd; + + devnull = open("/dev/null", O_WRONLY); + if (sshfs_opts[SOPT_SSHCMD].present) ssh_cmd = sshfs_opts[SOPT_SSHCMD].value; else @@ -555,13 +567,27 @@ static int start_ssh(char *host) if (dup2(outpipe[0], 0) == -1 || dup2(inpipe[1], 1) == -1) { perror("failed to redirect input/output"); - exit(1); + _exit(1); } + if (devnull != -1) + dup2(devnull, 2); + close(inpipe[0]); close(inpipe[1]); close(outpipe[0]); close(outpipe[1]); + switch (fork()) { + case -1: + perror("failed to fork"); + _exit(1); + case 0: + break; + default: + _exit(0); + } + chdir("/"); + ssh_args[argctr++] = ssh_cmd; ssh_args[argctr++] = "-2"; ssh_args[argctr++] = "-x"; @@ -581,8 +607,9 @@ static int start_ssh(char *host) ssh_args[argctr++] = NULL; execvp(ssh_cmd, ssh_args); - exit(1); + _exit(1); } + waitpid(pid, NULL, 0); close(inpipe[1]); close(outpipe[0]); return 0; @@ -654,11 +681,9 @@ static int sftp_send(uint8_t type, struct buffer *buf) buf_init(&buf2, 5); buf_add_uint32(&buf2, buf->len + 1); buf_add_uint8(&buf2, type); - pthread_mutex_lock(&lock); res = do_write(&buf2); if (res != -1) res = do_write(buf); - pthread_mutex_unlock(&lock); buf_free(&buf2); return res; } @@ -674,7 +699,7 @@ static int do_read(struct buffer *buf) perror("read"); return -1; } else if (res == 0) { - fprintf(stderr, "end of file read\n"); + fprintf(stderr, "remote host has disconnected\n"); return -1; } size -= res; @@ -734,12 +759,27 @@ static void chunk_put_locked(struct read_chunk *chunk) pthread_mutex_unlock(&lock); } -static void *process_requests(void *_data) +static int clean_req(void *key_, struct request *req) +{ + (void) key_; + + req->error = -EIO; + if (req->want_reply) + sem_post(&req->ready); + else { + if (req->end_func) + req->end_func(req); + request_free(req); + } + return TRUE; +} + +static void *process_requests(void *data_) { - (void) _data; + int res; + (void) data_; while (1) { - int res; struct buffer buf; uint8_t type; struct request *req; @@ -773,17 +813,80 @@ static void *process_requests(void *_data) if (req->want_reply) sem_post(&req->ready); else { - if (req->end_func) + if (req->end_func) { + pthread_mutex_lock(&lock); req->end_func(req); + pthread_mutex_unlock(&lock); + } request_free(req); } } else buf_free(&buf); } - kill(getpid(), SIGTERM); + if (!reconnect) { + /* harakiri */ + kill(getpid(), SIGTERM); + } else { + pthread_mutex_lock(&lock); + processing_thread_started = 0; + close(infd); + infd = -1; + close(outfd); + outfd = -1; + g_hash_table_foreach_remove(reqtab, (GHRFunc) clean_req, NULL); + connver ++; + pthread_mutex_unlock(&lock); + + } return NULL; } +static int sftp_init() +{ + int res = -1; + uint8_t type; + uint32_t version; + struct buffer buf; + buf_init(&buf, 4); + buf_add_uint32(&buf, PROTO_VERSION); + if (sftp_send(SSH_FXP_INIT, &buf) == -1) + goto out; + buf_clear(&buf); + if (sftp_read(&type, &buf) == -1) + goto out; + if (type != SSH_FXP_VERSION) { + fprintf(stderr, "protocol error\n"); + goto out; + } + if (buf_get_uint32(&buf, &version) == -1) + goto out; + + server_version = version; + DEBUG("Server version: %i\n", server_version); + if (version > PROTO_VERSION) + fprintf(stderr, "Warning: server uses version: %i, we support: %i\n", + version, PROTO_VERSION); + res = 0; + + out: + buf_free(&buf); + return res; +} + +static int connect_remote(void) +{ + int err; + + if (sshfs_opts[SOPT_DIRECTPORT].present) + err = connect_to(host, sshfs_opts[SOPT_DIRECTPORT].value); + else + err = start_ssh(host); + if (!err) + err = sftp_init(); + + return err; +} + static int start_processing_thread(void) { int err; @@ -791,16 +894,30 @@ static int start_processing_thread(void) if (processing_thread_started) return 0; + if (outfd == -1) { + err = connect_remote(); + if (err) + return -EIO; + } + err = pthread_create(&thread_id, NULL, process_requests, NULL); if (err) { fprintf(stderr, "failed to create thread: %s\n", strerror(err)); - return -EPERM; + return -EIO; } pthread_detach(thread_id); processing_thread_started = 1; return 0; } +#if FUSE_VERSION >= 23 +static void *sshfs_init(void) +{ + start_processing_thread(); + return NULL; +} +#endif + static int sftp_request_common(uint8_t type, const struct buffer *buf, uint8_t expect_type, struct buffer *outbuf, request_func begin_func, request_func end_func, @@ -824,26 +941,32 @@ static int sftp_request_common(uint8_t type, const struct buffer *buf, begin_func(req); pthread_mutex_lock(&lock); err = start_processing_thread(); + if (err) { + pthread_mutex_unlock(&lock); + goto out; + } g_hash_table_insert(reqtab, (gpointer) id, req); gettimeofday(&req->start, NULL); DEBUG("[%05i] %s\n", id, type_name(type)); - pthread_mutex_unlock(&lock); - if (err) - goto out; err = -EIO; if (sftp_send(type, &buf2) == -1) { - pthread_mutex_lock(&lock); g_hash_table_remove(reqtab, (gpointer) id); pthread_mutex_unlock(&lock); goto out; } + pthread_mutex_unlock(&lock); + if (expect_type == 0) { buf_free(&buf2); return 0; } sem_wait(&req->ready); + if (req->error) { + err = req->error; + goto out; + } err = -EPROTO; if (req->reply_type != expect_type && req->reply_type != SSH_FXP_STATUS) { fprintf(stderr, "protocol error\n"); @@ -1146,6 +1269,11 @@ static int sshfs_utime(const char *path, struct utimbuf *ubuf) return err; } +static inline int sshfs_file_is_conn(struct sshfs_file *sf) +{ + return sf->connver == connver; +} + static int sshfs_open(const char *path, struct fuse_file_info *fi) { int err; @@ -1167,6 +1295,7 @@ static int sshfs_open(const char *path, struct fuse_file_info *fi) /* Assume random read after open */ sf->is_seq = 0; sf->next_pos = 0; + sf->connver = connver; buf_init(&buf, 0); buf_add_path(&buf, path); buf_add_uint32(&buf, pflags); @@ -1188,6 +1317,9 @@ static int sshfs_flush(const char *path, struct fuse_file_info *fi) struct list_head write_reqs; struct list_head *curr_list; + if (!sshfs_file_is_conn(sf)) + return -EIO; + if (sync_write) return 0; @@ -1218,8 +1350,10 @@ static int sshfs_release(const char *path, struct fuse_file_info *fi) { struct sshfs_file *sf = (struct sshfs_file *) fi->fh; struct buffer *handle = &sf->handle; - sshfs_flush(path, fi); - sftp_request(SSH_FXP_CLOSE, handle, 0, NULL); + if (sshfs_file_is_conn(sf)) { + sshfs_flush(path, fi); + sftp_request(SSH_FXP_CLOSE, handle, 0, NULL); + } buf_free(handle); chunk_put_locked(sf->readahead); g_free(sf); @@ -1259,7 +1393,9 @@ static int sshfs_sync_read(struct sshfs_file *sf, char *rbuf, size_t size, static void sshfs_read_end(struct request *req) { struct read_chunk *chunk = (struct read_chunk *) req->data; - if (req->replied) { + if (req->error) + chunk->res = req->error; + else if (req->replied) { chunk->res = -EPROTO; if (req->reply_type == SSH_FXP_STATUS) { @@ -1285,8 +1421,9 @@ static void sshfs_read_end(struct request *req) chunk->res = -EIO; sem_post(&chunk->ready); - chunk_put_locked(chunk); + chunk_put(chunk); } + static void sshfs_read_begin(struct request *req) { struct read_chunk *chunk = (struct read_chunk *) req->data; @@ -1416,6 +1553,10 @@ static int sshfs_read(const char *path, char *rbuf, size_t size, off_t offset, { struct sshfs_file *sf = (struct sshfs_file *) fi->fh; (void) path; + + if (!sshfs_file_is_conn(sf)) + return -EIO; + if (sync_read) return sshfs_sync_read(sf, rbuf, size, offset); else @@ -1435,8 +1576,9 @@ static void sshfs_write_end(struct request *req) uint32_t serr; struct sshfs_file *sf = (struct sshfs_file *) req->data; - pthread_mutex_lock(&lock); - if (req->replied) { + if (req->error) + sf->write_error = req->error; + else if (req->replied) { if (req->reply_type != SSH_FXP_STATUS) fprintf(stderr, "protocol error\n"); else if (buf_get_uint32(&req->reply, &serr) != -1 && serr != SSH_FX_OK) @@ -1444,7 +1586,6 @@ static void sshfs_write_end(struct request *req) } list_del(&req->list); pthread_cond_broadcast(&sf->write_finished); - pthread_mutex_unlock(&lock); } static int sshfs_write(const char *path, const char *wbuf, size_t size, @@ -1457,6 +1598,10 @@ static int sshfs_write(const char *path, const char *wbuf, size_t size, struct buffer *handle = &sf->handle; (void) path; + + if (!sshfs_file_is_conn(sf)) + return -EIO; + data.p = (uint8_t *) wbuf; data.len = size; buf_init(&buf, 0); @@ -1472,38 +1617,6 @@ static int sshfs_write(const char *path, const char *wbuf, size_t size, return err ? err : (int) size; } -static int sftp_init() -{ - int res = -1; - uint8_t type; - uint32_t version; - struct buffer buf; - buf_init(&buf, 4); - buf_add_uint32(&buf, PROTO_VERSION); - if (sftp_send(SSH_FXP_INIT, &buf) == -1) - goto out; - buf_clear(&buf); - if (sftp_read(&type, &buf) == -1) - goto out; - if (type != SSH_FXP_VERSION) { - fprintf(stderr, "protocol error\n"); - goto out; - } - if (buf_get_uint32(&buf, &version) == -1) - goto out; - - server_version = version; - DEBUG("Server version: %i\n", server_version); - if (version > PROTO_VERSION) - fprintf(stderr, "Warning: server uses version: %i, we support: %i\n", - version, PROTO_VERSION); - res = 0; - - out: - buf_free(&buf); - return res; -} - static int processing_init(void) { pthread_mutex_init(&lock, NULL); @@ -1517,6 +1630,9 @@ static int processing_init(void) static struct fuse_cache_operations sshfs_oper = { .oper = { +#if FUSE_VERSION >= 23 + .init = sshfs_init, +#endif .getattr = sshfs_getattr, .readlink = sshfs_readlink, .mknod = sshfs_mknod, @@ -1550,6 +1666,7 @@ static void usage(const char *progname) " -V show version information\n" " -p PORT equivalent to '-o port=PORT'\n" " -C equivalent to '-o compression=yes'\n" +" -o reconnect reconnect to server" " -o sshfs_sync synchronous writes\n" " -o no_readahead synchronous reads (no speculative readahead)\n" " -o sshfs_debug print some debugging information\n" @@ -1566,7 +1683,6 @@ static void usage(const char *progname) int main(int argc, char *argv[]) { - char *host = NULL; char *fsname; int res; int argctr; @@ -1642,6 +1758,8 @@ int main(int argc, char *argv[]) sync_read = 1; if (sshfs_opts[SOPT_DEBUG].present) debug = 1; + if (sshfs_opts[SOPT_RECONNECT].present) + reconnect = 1; if (sshfs_opts[SOPT_MAX_READ].present) { unsigned val; if (opt_get_unsigned(&sshfs_opts[SOPT_MAX_READ], &val) == -1) @@ -1659,7 +1777,6 @@ int main(int argc, char *argv[]) if (res == -1) exit(1); - g_free(host); res = sftp_init(); if (res == -1) exit(1); -- cgit v1.2.3