mirror of
https://github.com/jarun/nnn.git
synced 2024-12-01 02:49:44 +00:00
SSHFS support
This commit is contained in:
parent
bd29368d4c
commit
882a17a60f
36
README.md
36
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)
|
- [Navigate-as-you-type](#navigate-as-you-type)
|
||||||
- [File indicators](#file-indicators)
|
- [File indicators](#file-indicators)
|
||||||
- [Configuration](#configuration)
|
- [Configuration](#configuration)
|
||||||
|
- [SSHFS mounts](#sshfs-mounts)
|
||||||
- [Help](#help)
|
- [Help](#help)
|
||||||
- [Plugins](#plugins)
|
- [Plugins](#plugins)
|
||||||
- [Troubleshooting](#troubleshooting)
|
- [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)
|
- FreeDesktop compliant trash (needs trash-cli)
|
||||||
- Show copy, move progress on Linux (needs avdcpmv)
|
- Show copy, move progress on Linux (needs avdcpmv)
|
||||||
- Plugin repository
|
- Plugin repository
|
||||||
- Transfer files using lftp
|
- SSHFS mounts (needs sshfs)
|
||||||
- Batch rename (needs vidir)
|
- Batch rename (needs vidir)
|
||||||
- Per-context directory color (default: blue)
|
- Per-context directory color (default: blue)
|
||||||
- Spawn a shell in the current directory
|
- 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 |
|
| vidir (from moreutils) | batch rename dir entries |
|
||||||
| vlock (Linux), bashlock (macOS), lock(1) (BSD) | terminal locker |
|
| 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 |
|
| 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) |
|
| $EDITOR (overridden by $VISUAL, if defined) | edit files (fallback vi) |
|
||||||
| $PAGER (less, most) | page through files (fallback less) |
|
| $PAGER (less, most) | page through files (fallback less) |
|
||||||
| $SHELL | spawn a shell, run some commands (fallback sh) |
|
| $SHELL | spawn a shell, run some commands (fallback sh) |
|
||||||
|
@ -264,6 +267,7 @@ Press <kbd>?</kbd> in `nnn` to see the list anytime.
|
||||||
MISC
|
MISC
|
||||||
! ^] Spawn SHELL C Execute entry
|
! ^] Spawn SHELL C Execute entry
|
||||||
R ^V Pick plugin L Lock terminal
|
R ^V Pick plugin L Lock terminal
|
||||||
|
c SSHFS mount u Unmount
|
||||||
^P Prompt ^N Note = Launcher
|
^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_IDLE_TIMEOUT=300` | idle seconds before locking terminal [default: disabled] |
|
||||||
| `NNN_COPIER='/absolute/path/to/copier'` | system clipboard copier script [default: none] |
|
| `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_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_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_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_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> |
|
| `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_TRASH=1` | trash files to the desktop Trash [default: delete] |
|
||||||
| `NNN_OPS_PROG=1` | show copy, move progress on Linux |
|
| `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
|
#### Help
|
||||||
|
|
||||||
$ nnn -h
|
$ nnn -h
|
||||||
|
|
5
nnn.1
5
nnn.1
|
@ -189,6 +189,11 @@ when dealing with the !, e and p commands respectively. A single combination to
|
||||||
export NNN_TMPFILE=/tmp/nnn
|
export NNN_TMPFILE=/tmp/nnn
|
||||||
.Ed
|
.Ed
|
||||||
.Pp
|
.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
|
\fBNNN_USE_EDITOR:\fR use EDITOR (VISUAL takes preference, preferably CLI, fallback vi) to handle text
|
||||||
files.
|
files.
|
||||||
.Bd -literal
|
.Bd -literal
|
||||||
|
|
94
src/nnn.c
94
src/nnn.c
|
@ -279,6 +279,7 @@ static char *editor;
|
||||||
static char *pager;
|
static char *pager;
|
||||||
static char *shell;
|
static char *shell;
|
||||||
static char *home;
|
static char *home;
|
||||||
|
static char *sshfsmnt;
|
||||||
static blkcnt_t ent_blocks;
|
static blkcnt_t ent_blocks;
|
||||||
static blkcnt_t dir_blocks;
|
static blkcnt_t dir_blocks;
|
||||||
static ulong num_files;
|
static ulong num_files;
|
||||||
|
@ -388,15 +389,16 @@ static const char * const messages[] = {
|
||||||
#define NNN_PLUGIN_DIR 5
|
#define NNN_PLUGIN_DIR 5
|
||||||
#define NNN_NOTE 6
|
#define NNN_NOTE 6
|
||||||
#define NNN_TMPFILE 7
|
#define NNN_TMPFILE 7
|
||||||
#define NNNLVL 8 /* strings end here */
|
#define NNN_SSHFS_MNT_ROOT 8
|
||||||
#define NNN_USE_EDITOR 9 /* flags begin here */
|
#define NNNLVL 9 /* strings end here */
|
||||||
#define NNN_NO_AUTOSELECT 10
|
#define NNN_USE_EDITOR 10 /* flags begin here */
|
||||||
#define NNN_RESTRICT_NAV_OPEN 11
|
#define NNN_NO_AUTOSELECT 11
|
||||||
#define NNN_RESTRICT_0B 12
|
#define NNN_RESTRICT_NAV_OPEN 12
|
||||||
#define NNN_OPENER_DETACH 13
|
#define NNN_RESTRICT_0B 13
|
||||||
#define NNN_TRASH 14
|
#define NNN_OPENER_DETACH 14
|
||||||
|
#define NNN_TRASH 15
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
#define NNN_OPS_PROG 15
|
#define NNN_OPS_PROG 16
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static const char * const env_cfg[] = {
|
static const char * const env_cfg[] = {
|
||||||
|
@ -408,6 +410,7 @@ static const char * const env_cfg[] = {
|
||||||
"NNN_PLUGIN_DIR",
|
"NNN_PLUGIN_DIR",
|
||||||
"NNN_NOTE",
|
"NNN_NOTE",
|
||||||
"NNN_TMPFILE",
|
"NNN_TMPFILE",
|
||||||
|
"NNN_SSHFS_MNT_ROOT",
|
||||||
"NNNLVL",
|
"NNNLVL",
|
||||||
"NNN_USE_EDITOR",
|
"NNN_USE_EDITOR",
|
||||||
"NNN_NO_AUTOSELECT",
|
"NNN_NO_AUTOSELECT",
|
||||||
|
@ -2387,6 +2390,7 @@ static bool show_help(const char *path)
|
||||||
"1MISC\n"
|
"1MISC\n"
|
||||||
"9! ^] Spawn SHELL C Execute entry\n"
|
"9! ^] Spawn SHELL C Execute entry\n"
|
||||||
"9R ^V Pick plugin L Lock terminal\n"
|
"9R ^V Pick plugin L Lock terminal\n"
|
||||||
|
"cc SSHFS mount u Unmount\n"
|
||||||
"b^P Prompt ^N Note = Launcher\n"};
|
"b^P Prompt ^N Note = Launcher\n"};
|
||||||
|
|
||||||
if (g_tmpfpath[0])
|
if (g_tmpfpath[0])
|
||||||
|
@ -2888,10 +2892,8 @@ begin:
|
||||||
/* Can fail when permissions change while browsing.
|
/* Can fail when permissions change while browsing.
|
||||||
* It's assumed that path IS a directory when we are here.
|
* 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();
|
printwarn();
|
||||||
goto nochange;
|
|
||||||
}
|
|
||||||
|
|
||||||
populate(path, lastname);
|
populate(path, lastname);
|
||||||
if (interrupted) {
|
if (interrupted) {
|
||||||
|
@ -3862,6 +3864,73 @@ nochange:
|
||||||
|
|
||||||
/* Repopulate as directory content may have changed */
|
/* Repopulate as directory content may have changed */
|
||||||
goto begin;
|
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_QUITCD: // fallthrough
|
||||||
case SEL_QUIT:
|
case SEL_QUIT:
|
||||||
for (r = 0; r < CTX_MAX; ++r)
|
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);
|
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 */
|
/* Get the clipboard copier, if set */
|
||||||
copier = getenv(env_cfg[NNN_COPIER]);
|
copier = getenv(env_cfg[NNN_COPIER]);
|
||||||
|
|
||||||
|
|
|
@ -85,6 +85,8 @@ enum action {
|
||||||
SEL_NEW,
|
SEL_NEW,
|
||||||
SEL_RENAME,
|
SEL_RENAME,
|
||||||
SEL_RENAMEALL,
|
SEL_RENAMEALL,
|
||||||
|
SEL_SSHFS,
|
||||||
|
SEL_UMOUNT,
|
||||||
SEL_HELP,
|
SEL_HELP,
|
||||||
SEL_EXEC,
|
SEL_EXEC,
|
||||||
SEL_SHELL,
|
SEL_SHELL,
|
||||||
|
@ -218,6 +220,10 @@ static struct key bindings[] = {
|
||||||
{ CONTROL('R'), SEL_RENAME },
|
{ CONTROL('R'), SEL_RENAME },
|
||||||
/* Rename contents of current dir */
|
/* Rename contents of current dir */
|
||||||
{ 'r', SEL_RENAMEALL },
|
{ 'r', SEL_RENAMEALL },
|
||||||
|
/* Connect to server over SSHFS */
|
||||||
|
{ 'c', SEL_SSHFS },
|
||||||
|
/* Disconnect a SSHFS mount point */
|
||||||
|
{ 'u', SEL_UMOUNT },
|
||||||
/* Show help */
|
/* Show help */
|
||||||
{ '?', SEL_HELP },
|
{ '?', SEL_HELP },
|
||||||
/* Execute file */
|
/* Execute file */
|
||||||
|
|
Loading…
Reference in a new issue