]> Sergey Matveev's repositories - nnn.git/commitdiff
SSHFS support
authorArun Prakash Jana <engineerarun@gmail.com>
Sat, 20 Apr 2019 18:54:19 +0000 (00:24 +0530)
committerArun Prakash Jana <engineerarun@gmail.com>
Sat, 20 Apr 2019 19:17:11 +0000 (00:47 +0530)
README.md
nnn.1
src/nnn.c
src/nnn.h

index 9e8d478fca9c7d4a9c1fbba90c1d6cff10d2bb59..f98e3b41c1fdbed2a3e214a8ae9be9f64a489005 100644 (file)
--- a/README.md
+++ b/README.md
@@ -59,6 +59,7 @@ Have as many scripts as you want to extend the power of `nnn`! Pick from the ava
   - [Navigate-as-you-type](#navigate-as-you-type)
   - [File indicators](#file-indicators)
   - [Configuration](#configuration)
+  - [SSHFS mounts](#sshfs-mounts)
   - [Help](#help)
 - [Plugins](#plugins)
 - [Troubleshooting](#troubleshooting)
@@ -103,7 +104,7 @@ Have as many scripts as you want to extend the power of `nnn`! Pick from the ava
   - FreeDesktop compliant trash (needs trash-cli)
   - Show copy, move progress on Linux (needs avdcpmv)
   - Plugin repository
-  - Transfer files using lftp
+  - SSHFS mounts (needs sshfs)
   - Batch rename (needs vidir)
   - Per-context directory color (default: blue)
   - Spawn a shell in the current directory
@@ -140,6 +141,8 @@ Have as many scripts as you want to extend the power of `nnn`! Pick from the ava
 | vidir (from moreutils) | batch rename dir entries |
 | vlock (Linux), bashlock (macOS), lock(1) (BSD) | terminal locker |
 | advcpmv (Linux) ([integration](https://github.com/jarun/nnn/wiki/hacking-nnn#show-cp-mv-progress)) | copy, move progress |
+| sshfs | mount remote over SSHFS |
+| fusermount(3) | SSHFS unmount |
 | $EDITOR (overridden by $VISUAL, if defined) | edit files (fallback vi) |
 | $PAGER (less, most) | page through files (fallback less) |
 | $SHELL | spawn a shell, run some commands (fallback sh) |
@@ -264,6 +267,7 @@ Press <kbd>?</kbd> in `nnn` to see the list anytime.
  MISC
          ! ^]  Spawn SHELL       C  Execute entry
          R ^V  Pick plugin       L  Lock terminal
+            c  SSHFS mount       u  Unmount
            ^P  Prompt  ^N  Note  =  Launcher
 ```
 
@@ -380,8 +384,9 @@ The following indicators are used in the detail view:
 | `NNN_IDLE_TIMEOUT=300` | idle seconds before locking terminal [default: disabled] |
 | `NNN_COPIER='/absolute/path/to/copier'` | system clipboard copier script [default: none] |
 | `NNN_PLUGIN_DIR=/home/user/nnn-plugins` | absolute path to plugins dir |
-| `NNN_NOTE=/home/user/Dropbox/Public/notes` | path to note file [default: none] |
+| `NNN_NOTE=/home/user/Dropbox/notes` | path to note file [default: none] |
 | `NNN_TMPFILE=/tmp/nnn` | file to write current open dir path to for cd on quit |
+| `NNN_SSHFS_MNT_ROOT=/home/user/.netmnt` | absolute path to SSHFS mount point root |
 | `NNN_USE_EDITOR=1` | Open text files in `$EDITOR` (`$VISUAL`, if defined; fallback vi) |
 | `NNN_NO_AUTOSELECT=1` | do not auto-select matching dir in _nav-as-you-type_ mode |
 | `NNN_RESTRICT_NAV_OPEN=1` | open files on <kbd> ↵</kbd>, not <kbd>→</kbd> or <kbd>l</kbd> |
@@ -389,6 +394,33 @@ The following indicators are used in the detail view:
 | `NNN_TRASH=1` | trash files to the desktop Trash [default: delete] |
 | `NNN_OPS_PROG=1` | show copy, move progress on Linux |
 
+#### SSHFS mounts
+
+To connect to and mount remote shares using SSHFS, `nnn` requires the following:
+
+1. ssh configuration file `~/.ssh/config` should have the host entries. sshfs reads this file.
+2. `NNN_SSHFS_MNT_ROOT` should be set to the **absolute path** to the directory under which `nnn` creates the mount point for a host. The mount point is the same as the host name.
+
+Example host entry for a Termux environment on Android device:
+
+```
+Host phone
+    HostName 192.168.0.102
+    User u0_a117
+    Port 8022
+```
+
+If `NNN_SSHFS_MNT_ROOT` is set to `/home/user/remotes`, the above host `phone` will be mounted at `/home/user/remotes/phone`. `nnn` creates the directory `phone` if it doesn't exist.
+
+To unmount a mount point highlight it in `nnn` (so that it's the current entry) and press the relevant keybind to unmount. It might be a good idea to bookmark `NNN_SSHFS_MNT_ROOT`.
+
+Notes:
+
+1. `nnn` places you inside the mount point after both mount and unmount. This is done so you can ensure the operation completed successfully. To jump back to the last directory, press the usual <kbd>-</kbd>.
+2. `nnn` doesn't delete the mount point on unmount. This is to prevent accidental data loss.
+
+More information on [SSHFS](https://wiki.archlinux.org/index.php/SSHFS)
+
 #### Help
 
     $ nnn -h
diff --git a/nnn.1 b/nnn.1
index 8621776af337087f6407b2016b717be4605b5625..f21ea14a6ee624d5b7dffafd45a279d63629c376 100644 (file)
--- a/nnn.1
+++ b/nnn.1
@@ -189,6 +189,11 @@ when dealing with the !, e and p commands respectively. A single combination to
     export NNN_TMPFILE=/tmp/nnn
 .Ed
 .Pp
+\fBNNN_SSHFS_MNT_ROOT:\fR absolute path to SSHFS mount point root. Mount points are created at this location.
+.Bd -literal
+    export NNN_SSHFS_MNT_ROOT=/home/user/.netmnt`
+.Ed
+.Pp
 \fBNNN_USE_EDITOR:\fR use EDITOR (VISUAL takes preference, preferably CLI, fallback vi) to handle text
 files.
 .Bd -literal
index 05d1a266dd3cdb24b22a2b21eeb69a6cb63a57a4..2f10bb2205ee8d0092e034497da5f3508a51f7e5 100644 (file)
--- a/src/nnn.c
+++ b/src/nnn.c
@@ -279,6 +279,7 @@ static char *editor;
 static char *pager;
 static char *shell;
 static char *home;
+static char *sshfsmnt;
 static blkcnt_t ent_blocks;
 static blkcnt_t dir_blocks;
 static ulong num_files;
@@ -388,15 +389,16 @@ static const char * const messages[] = {
 #define NNN_PLUGIN_DIR 5
 #define NNN_NOTE 6
 #define NNN_TMPFILE 7
-#define NNNLVL 8 /* strings end here */
-#define NNN_USE_EDITOR 9 /* flags begin here */
-#define NNN_NO_AUTOSELECT 10
-#define NNN_RESTRICT_NAV_OPEN 11
-#define NNN_RESTRICT_0B 12
-#define NNN_OPENER_DETACH 13
-#define NNN_TRASH 14
+#define NNN_SSHFS_MNT_ROOT 8
+#define NNNLVL 9 /* strings end here */
+#define NNN_USE_EDITOR 10 /* flags begin here */
+#define NNN_NO_AUTOSELECT 11
+#define NNN_RESTRICT_NAV_OPEN 12
+#define NNN_RESTRICT_0B 13
+#define NNN_OPENER_DETACH 14
+#define NNN_TRASH 15
 #ifdef __linux__
-#define NNN_OPS_PROG 15
+#define NNN_OPS_PROG 16
 #endif
 
 static const char * const env_cfg[] = {
@@ -408,6 +410,7 @@ static const char * const env_cfg[] = {
        "NNN_PLUGIN_DIR",
        "NNN_NOTE",
        "NNN_TMPFILE",
+       "NNN_SSHFS_MNT_ROOT",
        "NNNLVL",
        "NNN_USE_EDITOR",
        "NNN_NO_AUTOSELECT",
@@ -2387,6 +2390,7 @@ static bool show_help(const char *path)
                "1MISC\n"
               "9! ^]  Spawn SHELL       C  Execute entry\n"
               "9R ^V  Pick plugin       L  Lock terminal\n"
+                 "cc  SSHFS mount       u  Unmount\n"
                 "b^P  Prompt  ^N  Note  =  Launcher\n"};
 
        if (g_tmpfpath[0])
@@ -2888,10 +2892,8 @@ begin:
        /* Can fail when permissions change while browsing.
         * It's assumed that path IS a directory when we are here.
         */
-       if (access(path, R_OK) == -1) {
+       if (access(path, R_OK) == -1)
                printwarn();
-               goto nochange;
-       }
 
        populate(path, lastname);
        if (interrupted) {
@@ -3862,6 +3864,73 @@ nochange:
 
                        /* Repopulate as directory content may have changed */
                        goto begin;
+               case SEL_SSHFS:
+                       if (!sshfsmnt) {
+                               printwait("set NNN_SSHFS_MNT_ROOT", &presel);
+                               goto nochange;
+                       }
+
+                       tmp = xreadline(NULL, "Host: ");
+                       if (!tmp[0])
+                               goto nochange;
+
+                       /* Create the mount point */
+                       mkpath(sshfsmnt, tmp, newpath);
+                       r = mkdir(newpath, 0777);
+                       if (r == -1 && errno != EEXIST) {
+                               printwait(strerror(errno), &presel);
+                               goto nochange;
+                       }
+
+                       /* Check if directory can be accessed */
+                       if (!xdiraccess(newpath)) {
+                               presel = MSGWAIT;
+                               goto nochange;
+                       }
+
+                       if (!getutil("sshfs")) {
+                               printwait("sshfs missing", &presel);
+                               goto nochange;
+                       }
+
+                       /* Convert "Host" to "Host:" */
+                       r = strlen(tmp);
+                       tmp[r] = ':';
+                       tmp[r + 1] = '\0';
+
+                       /* Connect to remote */
+                       spawn("sshfs", tmp, newpath, NULL, F_NORMAL); // fallthrough
+               case SEL_UMOUNT:
+                       if (sel == SEL_UMOUNT) {
+                               static char cmd[] = "fusermount3"; /* Arch Linux utility */
+                               static bool found = FALSE;
+
+                               /* On Ubuntu it's fusermount */
+                               if (!found && !getutil(cmd))
+                                       cmd[10] = '\0';
+
+                               if (!ndents)
+                                       goto nochange;
+
+                               mkpath(path, dents[cur].name, newpath);
+                               if (!xdiraccess(newpath)) {
+                                       presel = MSGWAIT;
+                                       goto nochange;
+                               }
+
+                               spawn(cmd, "-u", newpath, NULL, F_NORMAL);
+                       }
+
+                       lastname[0] = '\0';
+
+                       /* Save last working directory */
+                       xstrlcpy(lastdir, path, PATH_MAX);
+
+                       /* Switch to mount point */
+                       xstrlcpy(path, newpath, PATH_MAX);
+
+                       setdirwatch();
+                       goto begin;
                case SEL_QUITCD: // fallthrough
                case SEL_QUIT:
                        for (r = 0; r < CTX_MAX; ++r)
@@ -4169,6 +4238,9 @@ int main(int argc, char *argv[])
                xstrlcpy(g_cppath + g_tmpfplen - 1, "/.nnncp", PATH_MAX - g_tmpfplen);
        }
 
+       /* Get SSHFS mountpoint */
+       sshfsmnt = getenv("NNN_SSHFS_MNT_ROOT");
+
        /* Get the clipboard copier, if set */
        copier = getenv(env_cfg[NNN_COPIER]);
 
index dd21ef5b419a2e6217447c6b751c9209d74984cd..d47080b9fe7d3a56e1ca45af599ec82441d2fd3a 100644 (file)
--- a/src/nnn.h
+++ b/src/nnn.h
@@ -85,6 +85,8 @@ enum action {
        SEL_NEW,
        SEL_RENAME,
        SEL_RENAMEALL,
+       SEL_SSHFS,
+       SEL_UMOUNT,
        SEL_HELP,
        SEL_EXEC,
        SEL_SHELL,
@@ -218,6 +220,10 @@ static struct key bindings[] = {
        { CONTROL('R'),   SEL_RENAME },
        /* Rename contents of current dir */
        { 'r',            SEL_RENAMEALL },
+       /* Connect to server over SSHFS */
+       { 'c',            SEL_SSHFS },
+       /* Disconnect a SSHFS mount point */
+       { 'u',            SEL_UMOUNT },
        /* Show help */
        { '?',            SEL_HELP },
        /* Execute file */