Repository: borgbackup/borg

Issue:
giving env var doesn't work for forced cmd
From IRC / bket:
```
macbook:~/bin$ borg-wrapper list --debug
using builtin fallback logging configuration
35 self tests completed in 0.23 seconds
SSH command line: ['ssh', '-i', '/Users/bket/.snapshot/keys/snapshot_ssh', '-o', 'IdentitiesOnly=yes', 'snapshot@aurora.lan', 'BORG_HOSTNAME_IS_UNIQUE=yes', 'borg', 'serve', '--umask=077', '--debug']
Remote: usage: borg [-h] [-V] <command> ...
Remote: borg: error: argument <command>: invalid choice: 'borg' (choose from 'serve', 'init', 'check', 'change-passphrase', 'key', 'migrate-to-repokey', 'create', 'extract', 'rename', 'delete', 'list', 'mount', 'umount', 'info', 'break-lock', 'prune', 'upgrade', 'help', 'debug', 'debug-info', 'debug-dump-archive-items', 'debug-dump-repo-objs', 'debug-get-obj', 'debug-put-obj', 'debug-delete-obj', 'debug-refcount-obj')
Connection closed by remote host. Is borg working on the server?
Traceback (most recent call last):
  File "borg/archiver.py", line 3476, in main
  File "borg/archiver.py", line 3404, in run
  File "borg/archiver.py", line 102, in wrapper
  File "borg/remote.py", line 553, in __init__
borg.remote.ConnectionClosedWithHint: Connection closed by remote host. Is borg working on the server?

Platform: Darwin macbook 16.4.0 Darwin Kernel Version 16.4.0: Thu Dec 22 22:53:21 PST 2016; root:xnu-3789.41.3~3/RELEASE_X86_64 x86_64 i386
Borg: 1.1.0b4  Python: CPython 3.5.3
PID: 3279  CWD: /Users/bket/bin
sys.argv: ['borg', 'list', '--debug']
SSH_ORIGINAL_COMMAND: None
```


---

:moneybag: [there is a bounty for this](https://www.bountysource.com/issues/43561139-giving-env-var-doesn-t-work-for-forced-cmd)


Discussion:
codecov-io: # [Codecov](https://codecov.io/gh/borgbackup/borg/pull/2374?src=pr&el=h1) Report
> Merging [#2374](https://codecov.io/gh/borgbackup/borg/pull/2374?src=pr&el=desc) into [master](https://codecov.io/gh/borgbackup/borg/commit/d79da81d2244c328387cde3bbc1a83e36883a2d6?src=pr&el=desc) will **decrease** coverage by `0.11%`.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/borgbackup/borg/pull/2374/graphs/tree.svg?src=pr&token=LssEpmW3o4&width=650&height=150)](https://codecov.io/gh/borgbackup/borg/pull/2374?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master    #2374      +/-   ##
==========================================
- Coverage   83.08%   82.96%   -0.12%     
==========================================
  Files          20       20              
  Lines        7613     7615       +2     
  Branches     1294     1295       +1     
==========================================
- Hits         6325     6318       -7     
- Misses        929      936       +7     
- Partials      359      361       +2
```


| [Impacted Files](https://codecov.io/gh/borgbackup/borg/pull/2374?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/borg/archiver.py](https://codecov.io/gh/borgbackup/borg/pull/2374?src=pr&el=tree#diff-c3JjL2JvcmcvYXJjaGl2ZXIucHk=) | `82.12% <100%> (-0.11%)` | :arrow_down: |
| [src/borg/archive.py](https://codecov.io/gh/borgbackup/borg/pull/2374?src=pr&el=tree#diff-c3JjL2JvcmcvYXJjaGl2ZS5weQ==) | `81.86% <0%> (-0.56%)` | :arrow_down: |
| [src/borg/remote.py](https://codecov.io/gh/borgbackup/borg/pull/2374?src=pr&el=tree#diff-c3JjL2JvcmcvcmVtb3RlLnB5) | `76.78% <0%> (-0.21%)` | :arrow_down: |
| [src/borg/helpers.py](https://codecov.io/gh/borgbackup/borg/pull/2374?src=pr&el=tree#diff-c3JjL2JvcmcvaGVscGVycy5weQ==) | `87.61% <0%> (+0.07%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/borgbackup/borg/pull/2374?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/borgbackup/borg/pull/2374?src=pr&el=footer). Last update [d79da81...b43abf1](https://codecov.io/gh/borgbackup/borg/pull/2374?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

Patch:
diff --git a/src/borg/archiver.py b/src/borg/archiver.py
index 7dccd55a..e73aaf2d 100644
--- a/src/borg/archiver.py
+++ b/src/borg/archiver.py
@@ -4,6 +4,7 @@
 import functools
 import hashlib
 import inspect
+import itertools
 import json
 import logging
 import os
@@ -3306,6 +3307,9 @@ def get_args(self, argv, cmd):
         if cmd is not None and result.func == self.do_serve:
             forced_result = result
             argv = shlex.split(cmd)
+            # Drop environment variables (do *not* interpret them) before trying to parse
+            # the borg command line.
+            argv = list(itertools.dropwhile(lambda arg: '=' in arg, argv))
             result = self.parse_args(argv[1:])
             if result.func != forced_result.func:
                 # someone is trying to execute a different borg subcommand, don't do that!


Test patch:
diff --git a/src/borg/testsuite/archiver.py b/src/borg/testsuite/archiver.py
index 287dfe2c..f2608f13 100644
--- a/src/borg/testsuite/archiver.py
+++ b/src/borg/testsuite/archiver.py
@@ -2841,6 +2841,13 @@ def test_get_args():
                              'borg init --encryption=repokey /')
     assert args.func == archiver.do_serve
 
+    # Check that environment variables in the forced command don't cause issues. If the command
+    # were not forced, environment variables would be interpreted by the shell, but this does not
+    # happen for forced commands - we get the verbatim command line and need to deal with env vars.
+    args = archiver.get_args(['borg', 'serve', ],
+                             'BORG_HOSTNAME_IS_UNIQUE=yes borg serve --info')
+    assert args.func == archiver.do_serve
+
 
 def test_compare_chunk_contents():
     def ccc(a, b):