diff options
author | Ryo Nakamura <upa@haeena.net> | 2022-10-16 22:12:03 +0900 |
---|---|---|
committer | Ryo Nakamura <upa@haeena.net> | 2022-10-16 22:12:03 +0900 |
commit | def9cfeba74357ca98fc25469d4285139ecd42ce (patch) | |
tree | 077c5de04728a1342392d37d95c3c0a7fbe455a3 /src | |
parent | 579629dea21dbbb26cc9c42c4f1f1022c7270144 (diff) |
implement fill_file
Diffstat (limited to 'src')
-rw-r--r-- | src/file.c | 281 | ||||
-rw-r--r-- | src/file.h | 26 | ||||
-rw-r--r-- | src/list.h | 517 | ||||
-rw-r--r-- | src/main.c | 109 | ||||
-rw-r--r-- | src/ssh.c | 8 | ||||
-rw-r--r-- | src/ssh.h | 3 | ||||
-rw-r--r-- | src/util.h | 11 |
7 files changed, 925 insertions, 30 deletions
diff --git a/src/file.c b/src/file.c new file mode 100644 index 0000000..88614ec --- /dev/null +++ b/src/file.c @@ -0,0 +1,281 @@ +#include <stdlib.h> +#include <stdbool.h> +#include <sys/stat.h> +#include <dirent.h> +#include <limits.h> + +#include <ssh.h> +#include <util.h> +#include <file.h> + +bool file_has_hostname(char *path) +{ + char *p; + + p = strchr(path, ':'); + if (p) { + if (p == path || ((p > path) && *(p - 1) == '\\')) { + /* first byte is colon or escaped colon, skip */ + return false; + } else { + return true; + } + } + + return false; +} + +char *file_find_hostname(char *path) +{ + char *dup, *p; + + dup = strdup(path); + if (!dup) { + pr_err("%s", strerrno()); + return NULL; + } + + p = strchr(dup, ':'); + if (p) { + if (p == dup || ((p > dup) && *(p - 1) == '\\')) { + /* first byte is colon or escaped colon, skip */ + free(dup); + } else { + /* handle this as remote hostname (with username) */ + *p = '\0'; + return dup; + } + } + + return NULL; +} + +static char *file_find_path(char *path) +{ + char *p; + + p = strchr(path, ':'); + if (p) { + if (p == path || ((p > path) && *(p - 1) == '\\')) { + /* first byte is colon or escaped colon, skip */ + return path; + } else { + return p + 1; + } + } + + return path; +} + +/* return 1 when path is directory, 0 is not directory, and -1 on error */ +int file_is_directory(char *path, sftp_session sftp) +{ + int ret = 0; + + if (sftp) { + char *remote_path = file_find_path(path); + sftp_attributes attr; + + char *p = *remote_path == '\0' ? "." : remote_path; + attr = sftp_stat(sftp, p); + if (!attr) { + pr_err("file %s: %s\n", p, + ssh_get_error(sftp_ssh(sftp))); + ret = -1; + } else if (attr->type == SSH_FILEXFER_TYPE_DIRECTORY) + ret = 1; + sftp_attributes_free(attr); + } else { + struct stat statbuf; + if (stat(path, &statbuf) < 0) { + pr_err("file %s: %s\n", path, strerrno()); + ret = -1; + } else if ((statbuf.st_mode & S_IFMT) == S_IFDIR) + ret = 1; + } + + return ret; +} + +static struct file *file_allocate(char *path, size_t size, bool remote) +{ + struct file *f; + + f = malloc(sizeof(*f)); + if (!f) { + pr_err("%s\n", strerrno()); + return NULL; + } + memset(f, 0, sizeof(*f)); + + f->path = strdup(path); + if (!f->path) { + pr_err("%s\n", strerrno()); + free(f); + return NULL; + } + + f->size = size; + f->remote = remote; + + return f; +} + +static bool file_should_skip(char *path) +{ + int len = strlen(path); + if ((len == 1 && strncmp(path, ".", 1) == 0) || + (len == 2 && strncmp(path, "..", 2) == 0)) { + return true; + } + return false; +} + + +static int file_fill_local_recursive(char *path, struct list_head *head) +{ + char child[PATH_MAX]; + struct stat statbuf; + struct dirent *de; + DIR *dir; + int ret; + + ret = file_is_directory(path, NULL); + if (ret < 0) + return -1; + + if (ret == 1) { + if ((dir = opendir(path)) == NULL) { + pr_err("opend to open dir %s: %s\n", path, strerrno()); + return -1; + } + + while ((de = readdir(dir)) != NULL) { + if (file_should_skip(de->d_name)) + continue; + snprintf(child, sizeof(child), "%s/%s", path, de->d_name); + ret = file_fill_local_recursive(child, head); + if (ret < 0) + return ret; + } + } else { + /* path is file */ + if (stat(path, &statbuf) < 0) { + pr_err("file %s: %s\n", path, strerrno()); + return -1; + } + + if ((statbuf.st_mode & S_IFMT) == S_IFREG || + (statbuf.st_mode & S_IFMT) == S_IFLNK) { + struct file *f = file_allocate(path, statbuf.st_size, false); + if (!f) { + pr_err("%s\n", strerrno()); + return -1; + } + list_add(&f->list, head); + } + } + + return 0; +} + +static int file_fill_remote_recursive(char *path, sftp_session sftp, + struct list_head *head) +{ + char child[PATH_MAX]; + sftp_attributes attr; + sftp_dir dir; + int ret; + + ret = file_is_directory(path, sftp); + if (ret < 0) + return -1; + + if (ret == 1) { + dir = sftp_opendir(sftp, path); + if (!dir) { + pr_err("failed to open dir %s: %s\n", path, + ssh_get_error(sftp_ssh(sftp))); + return -1; + } + + while ((attr = sftp_readdir(sftp, dir)) != NULL) { + if (file_should_skip(attr->name)) + continue; + + snprintf(child, sizeof(child), "%s/%s", path, attr->name); + ret = file_fill_remote_recursive(child, sftp, head); + if (ret < 0) + return ret; + sftp_attributes_free(attr); + } + + if (!sftp_dir_eof(dir)) { + pr_err("can't list directory %s: %s\n", path, + ssh_get_error(sftp_ssh(sftp))); + return -1; + } + + if (sftp_closedir(dir) != SSH_OK) { + pr_err("can't close directory %s: %s\n", path, + ssh_get_error(sftp_ssh(sftp))); + return -1; + } + + } else { + /* path is file */ + attr = sftp_stat(sftp, path); + if (!attr) { + pr_err("failed to get stat for %s: %s\n", + path, ssh_get_error(sftp_ssh(sftp))); + return -1; + } + + /* skip special and unknown files */ + if (attr->type == SSH_FILEXFER_TYPE_REGULAR || + attr->type == SSH_FILEXFER_TYPE_SYMLINK) { + struct file *f = file_allocate(path, attr->size, true); + if (!f) { + pr_err("%s\n", strerrno()); + return -1; + } + list_add(&f->list, head); + sftp_attributes_free(attr); + } + } + + return 0; +} + +int file_fill(sftp_session sftp, struct list_head *head, char **src_array, int count) +{ + char *src, *path; + int ret, n; + + for (n = 0; n < count; n++) { + src = *(src_array + n); + path = file_find_path(src); + path = *path == '\0' ? "." : path; + if (file_has_hostname(src)) + ret = file_fill_remote_recursive(path, sftp, head); + else + ret = file_fill_local_recursive(path, head); + if (ret < 0) + return -1; + } + + return 0; +} + + +#ifdef DEBUG +void file_dump(struct list_head *head) +{ + struct file *f; + + list_for_each_entry(f, head, list) { + pr_debug("%s %s %lu-byte\n", f->path, + f->remote ? "(remote)" : "(local)", f->size); + } +} +#endif @@ -1,14 +1,15 @@ #ifndef _FILE_H_ #define _FILE_H_ -struct path { - char *path; - bool remote; -}; +#include <libssh/libssh.h> +#include <libssh/sftp.h> +#include <list.h> struct file { - struct path src; /* copy source */ - struct path dst; /* copy desitnation */ + struct list_head list; /* sscp->file_list */ + + char *path; + bool remote; size_t size; /* size of this file */ }; @@ -18,11 +19,14 @@ struct chunk { size_t len; /* length of this chunk */ }; -struct file *file_expand(char **src_array, char *dst) -{ - /* return array of files expanded from sources and dst */ - return NULL; -} +char *file_find_hostname(char *path); +bool file_has_hostname(char *path); +int file_is_directory(char *path, sftp_session sftp); + +int file_fill(sftp_session sftp, struct list_head *head, char **src_array, int count); +#ifdef DEBUG +void file_dump(struct list_head *head); +#endif #endif /* _FILE_H_ */ diff --git a/src/list.h b/src/list.h new file mode 100644 index 0000000..81ad31e --- /dev/null +++ b/src/list.h @@ -0,0 +1,517 @@ +/** + * + * I grub it from linux kernel source code and fix it for user space + * program. Of course, this is a GPL licensed header file. + * + * Here is a recipe to cook list.h for user space program + * + * 1. copy list.h from linux/include/list.h + * 2. remove + * - #ifdef __KERNE__ and its #endif + * - all #include line + * - prefetch() and rcu related functions + * 3. add macro offsetof() and container_of + * + * - kazutomo@mcs.anl.gov + */ +#ifndef _LINUX_LIST_H +#define _LINUX_LIST_H + +/** + * @name from other kernel headers + */ +/*@{*/ + +/** + * Get offset of a member + */ +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) + +/** + * Casts a member of a structure out to the containing structure + * @param ptr the pointer to the member. + * @param type the type of the container struct this is embedded in. + * @param member the name of the member within the struct. + * + */ +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) +/*@}*/ + + +/* + * These are non-NULL pointers that will result in page faults + * under normal circumstances, used to verify that nobody uses + * non-initialized list entries. + */ +#define LIST_POISON1 ((void *) 0x00100100) +#define LIST_POISON2 ((void *) 0x00200200) + +/** + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +#define INIT_LIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head * prev, struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = LIST_POISON1; + entry->prev = LIST_POISON2; +} + + + +/** + * list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +static inline void list_del_init(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); +} + +/** + * list_move - delete from one list and add as another's head + * @list: the entry to move + * @head: the head that will precede our entry + */ +static inline void list_move(struct list_head *list, struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add(list, head); +} + +/** + * list_move_tail - delete from one list and add as another's tail + * @list: the entry to move + * @head: the head that will follow our entry + */ +static inline void list_move_tail(struct list_head *list, + struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add_tail(list, head); +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + +static inline void __list_splice(struct list_head *list, + struct list_head *head) +{ + struct list_head *first = list->next; + struct list_head *last = list->prev; + struct list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; +} + +/** + * list_splice - join two lists + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static inline void list_splice(struct list_head *list, struct list_head *head) +{ + if (!list_empty(list)) + __list_splice(list, head); +} + +/** + * list_splice_init - join two lists and reinitialise the emptied list. + * @list: the new list to add. + * @head: the place to add it in the first list. + * + * The list at @list is reinitialised + */ +static inline void list_splice_init(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) { + __list_splice(list, head); + INIT_LIST_HEAD(list); + } +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ + +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); \ + pos = pos->next) + +/** + * __list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + * + * This variant differs from list_for_each() in that it's the + * simplest possible list iteration code, no prefetching is done. + * Use this for code that knows the list to be very short (empty + * or 1 entry) most of the time. + */ +#define __list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * list_for_each_prev - iterate over a list backwards + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each_prev(pos, head) \ + for (pos = (head)->prev; prefetch(pos->prev), pos != (head); \ + pos = pos->prev) + +/** + * list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop counter. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/** + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_reverse - iterate backwards over list of given type. + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_reverse(pos, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.prev, typeof(*pos), member)) + +/** + * list_prepare_entry - prepare a pos entry for use as a start point in + * list_for_each_entry_continue + * @pos: the type * to use as a start point + * @head: the head of the list + * @member: the name of the list_struct within the struct. + */ +#define list_prepare_entry(pos, head, member) \ + ((pos) ? : list_entry(head, typeof(*pos), member)) + +/** + * list_for_each_entry_continue - iterate over list of given type + * continuing after existing point + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_continue(pos, head, member) \ + for (pos = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @pos: the type * to use as a loop counter. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_continue - iterate over list of given type + * continuing after existing point safe against removal of list entry + * @pos: the type * to use as a loop counter. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe_continue(pos, n, head, member) \ + for (pos = list_entry(pos->member.next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_reverse - iterate backwards over list of given type safe against + * removal of list entry + * @pos: the type * to use as a loop counter. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe_reverse(pos, n, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member), \ + n = list_entry(pos->member.prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.prev, typeof(*n), member)) + + + + +/* + * Double linked lists with a single pointer list head. + * Mostly useful for hash tables where the two pointer list head is + * too wasteful. + * You lose the ability to access the tail in O(1). + */ + +struct hlist_head { + struct hlist_node *first; +}; + +struct hlist_node { + struct hlist_node *next, **pprev; +}; + +#define HLIST_HEAD_INIT { .first = NULL } +#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL } +#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL) +#define INIT_HLIST_NODE(ptr) ((ptr)->next = NULL, (ptr)->pprev = NULL) + +static inline int hlist_unhashed(const struct hlist_node *h) +{ + return !h->pprev; +} + +static inline int hlist_empty(const struct hlist_head *h) +{ + return !h->first; +} + +static inline void __hlist_del(struct hlist_node *n) +{ + struct hlist_node *next = n->next; + struct hlist_node **pprev = n->pprev; + *pprev = next; + if (next) + next->pprev = pprev; +} + +static inline void hlist_del(struct hlist_node *n) +{ + __hlist_del(n); + n->next = LIST_POISON1; + n->pprev = LIST_POISON2; +} + + +static inline void hlist_del_init(struct hlist_node *n) +{ + if (n->pprev) { + __hlist_del(n); + INIT_HLIST_NODE(n); + } +} + +static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h) +{ + struct hlist_node *first = h->first; + n->next = first; + if (first) + first->pprev = &n->next; + h->first = n; + n->pprev = &h->first; +} + + + +/* next must be != NULL */ +static inline void hlist_add_before(struct hlist_node *n, + struct hlist_node *next) +{ + n->pprev = next->pprev; + n->next = next; + next->pprev = &n->next; + *(n->pprev) = n; +} + +static inline void hlist_add_after(struct hlist_node *n, + struct hlist_node *next) +{ + next->next = n->next; + n->next = next; + next->pprev = &n->next; + + if(next->next) + next->next->pprev = &next->next; +} + + + +#define hlist_entry(ptr, type, member) container_of(ptr,type,member) + +#define hlist_for_each(pos, head) \ + for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \ + pos = pos->next) + +#define hlist_for_each_safe(pos, n, head) \ + for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \ + pos = n) + +/** + * hlist_for_each_entry - iterate over list of given type + * @tpos: the type * to use as a loop counter. + * @pos: the &struct hlist_node to use as a loop counter. + * @head: the head for your list. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry(tpos, pos, head, member) \ + for (pos = (head)->first; \ + pos && ({ prefetch(pos->next); 1;}) && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = pos->next) + +/** + * hlist_for_each_entry_continue - iterate over a hlist continuing after existing point + * @tpos: the type * to use as a loop counter. + * @pos: the &struct hlist_node to use as a loop counter. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry_continue(tpos, pos, member) \ + for (pos = (pos)->next; \ + pos && ({ prefetch(pos->next); 1;}) && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = pos->next) + +/** + * hlist_for_each_entry_from - iterate over a hlist continuing from existing point + * @tpos: the type * to use as a loop counter. + * @pos: the &struct hlist_node to use as a loop counter. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry_from(tpos, pos, member) \ + for (; pos && ({ prefetch(pos->next); 1;}) && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = pos->next) + +/** + * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @tpos: the type * to use as a loop counter. + * @pos: the &struct hlist_node to use as a loop counter. + * @n: another &struct hlist_node to use as temporary storage + * @head: the head for your list. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry_safe(tpos, pos, n, head, member) \ + for (pos = (head)->first; \ + pos && ({ n = pos->next; 1; }) && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = n) + + +#endif @@ -3,14 +3,25 @@ #include <stdbool.h> #include <unistd.h> +#include <list.h> #include <util.h> #include <ssh.h> #include <file.h> #include <platform.h> +int verbose = 0; /* util.h */ #define DEFAULT_MIN_CHUNK_SZ (64 << 20) /* 64MB */ +struct sscp { + char *host; /* remote host (and username) */ + sftp_session ctrl; /* control sftp session */ + + struct list_head file_list; + char *target; + bool target_is_remote; +}; + void usage(bool print_help) { printf("sscp: super scp, copy files over multiple ssh connections\n" "\n" @@ -22,8 +33,7 @@ void usage(bool print_help) { if (!print_help) return; - printf(" -r expand directory recusrively\n" - " -n NR_CONNECTIONS max number of connections (default: # of cpu cores)\n" + printf(" -n NR_CONNECTIONS max number of connections (default: # of cpu cores)\n" " -s MIN_CHUNKSIZE min chunk size (default: 64MB)\n" " -S MAX_CHUNKSIZE max chunk size (default: filesize / nr_conn)\n" "\n" @@ -43,22 +53,65 @@ void usage(bool print_help) { "\n"); } +char *find_hostname(int ind, int argc, char **argv) +{ + char *h, *hostnames[argc]; + int n, cnt = 0; + + for (n = ind; n < argc; n++) { + h = file_find_hostname(argv[n]); + if (h) + hostnames[cnt++] = h; + } + + if (cnt == 0) + return NULL; + + /* check all hostnames are identical */ + for (n = 1; n < cnt; n++) { + int s1 = strlen(hostnames[n - 1]); + int s2 = strlen(hostnames[n]); + if (s1 != s2) { + pr_err("different hostnames: %s and %s\n", + hostnames[n - 1], hostnames[n]); + goto err_out; + } + if (strncmp(hostnames[n - 1], hostnames[n], s1) != 0) { + pr_err("different hostnames: %s and %s\n", + hostnames[n - 1], hostnames[n]); + goto err_out; + } + } + + for (n = 1; n < cnt; n++) { + free(hostnames[n]); + } + + return hostnames[0]; + +err_out: + for (n = 0; n < cnt; n++) { + free(hostnames[n]); + } + return NULL; +} + int main(int argc, char **argv) { + struct sscp sscp; struct ssh_opts opts; int nr_conn = nr_cpus(); - bool recursive = false; int min_chunk_sz = DEFAULT_MIN_CHUNK_SZ; int max_chunk_sz = 0; + int ret = 0; char ch; memset(&opts, 0, sizeof(opts)); + memset(&sscp, 0, sizeof(sscp)); + INIT_LIST_HEAD(&sscp.file_list); - while ((ch = getopt(argc, argv, "r:n:s:S:l:p:i:c:Cvh")) != -1) { + while ((ch = getopt(argc, argv, "n:s:S:l:p:i:c:Cvh")) != -1) { switch (ch) { - case 'r': - recursive = true; - break; case 'n': nr_conn = atoi(optarg); if (nr_conn < 1) { @@ -113,6 +166,7 @@ int main(int argc, char **argv) break; case 'v': opts.debuglevel++; + verbose++; break; case 'h': usage(true); @@ -129,13 +183,44 @@ int main(int argc, char **argv) return 1; } - printf("opts.port %s\n", opts.port); + if (argc - optind < 2) { + /* sscp needs at lease 2 (src and target) argument */ + usage(false); + return 1; + } + + sscp.target = argv[argc - 1]; + sscp.target_is_remote = file_has_hostname(sscp.target); + + /* create control session */ + sscp.host = find_hostname(optind, argc, argv); + if (!sscp.host) { + pr_err("no remote host given\n"); + return 1; + } + sscp.ctrl = ssh_make_sftp_session(sscp.host, &opts); + if (!sscp.ctrl) + return 1; + + /* check target is directory */ + ret = file_is_directory(sscp.target, sscp.target_is_remote ? sscp.ctrl : NULL); + if (ret < 0) + return 1; + if (ret == 0) { + pr_err("target must be directory\n"); + return 1; + } - int n; - for (n = 0; n < argc; n++) { - printf("%d %s\n", n, argv[n]); + ret = file_fill(sscp.ctrl, &sscp.file_list, &argv[optind], argc - optind - 1); + if (ret < 0) { + ssh_sftp_close(sscp.ctrl); + return 1; } - printf("optind %d", optind); +#ifdef DEBUG + file_dump(&sscp.file_list); +#endif + + ssh_sftp_close(sscp.ctrl); return 0; } @@ -235,3 +235,11 @@ static int ssh_verify_known_hosts(ssh_session session) ssh_clean_pubkey_hash(&hash); return 0; } + +void ssh_sftp_close(sftp_session sftp) +{ + ssh_session ssh = sftp_ssh(sftp); + sftp_free(sftp); + ssh_disconnect(ssh); + ssh_free(ssh); +} @@ -20,5 +20,8 @@ struct ssh_opts { * user@hostname and hostname notations (by libssh). */ sftp_session ssh_make_sftp_session(char *sshdst, struct ssh_opts *opts); +void ssh_sftp_close(sftp_session sftp); + +#define sftp_ssh(sftp) (sftp)->session #endif /* _SSH_H_ */ @@ -8,6 +8,8 @@ #define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0) +extern int verbose; + #define pr_v(level, fmt, ...) do { \ if (verbose >= level) { \ fprintf(stdout, "\x1b[1m\x1b[34m" \ @@ -32,14 +34,9 @@ "ERR:%s(): " fmt "\x1b[0m", \ __func__, ##__VA_ARGS__) -#define pr_debug(fmt, ...) \ - do { \ - if (unlikely(debug)) { \ - fprintf(stderr, "\x1b[1m\x1b[33m" \ +#define pr_debug(fmt, ...) fprintf(stderr, "\x1b[1m\x1b[33m" \ "DEBUG:%s(): " fmt "\x1b[0m", \ - __func__, ##__VA_ARGS__); \ - } \ - } while (0) + __func__, ##__VA_ARGS__); #define strerrno() strerror(errno) |