(ctx context.Context, args []string)
| 763 | } |
| 764 | |
| 765 | func runFileGet(ctx context.Context, args []string) error { |
| 766 | if len(args) != 1 { |
| 767 | return errors.New("usage: tailscale file get <target-directory>") |
| 768 | } |
| 769 | log.SetFlags(0) |
| 770 | |
| 771 | dir := args[0] |
| 772 | if dir == "/dev/null" { |
| 773 | return wipeInbox(ctx) |
| 774 | } |
| 775 | |
| 776 | if fi, err := os.Stat(dir); err != nil || !fi.IsDir() { |
| 777 | return fmt.Errorf("%q is not a directory", dir) |
| 778 | } |
| 779 | if fileGetArgs.loop { |
| 780 | for { |
| 781 | errs := runFileGetOneBatch(ctx, dir) |
| 782 | for _, err := range errs { |
| 783 | outln(err) |
| 784 | } |
| 785 | if len(errs) > 0 { |
| 786 | // It's possible whatever caused the error(s) (e.g. conflicting target file, |
| 787 | // full disk, unwritable target directory) will re-occur if we try again so |
| 788 | // let's back off and not busy loop on error. |
| 789 | // |
| 790 | // If we've been invoked as: |
| 791 | // tailscale file get --conflict=skip ~/Downloads |
| 792 | // then any file coming in named the same as one in ~/Downloads will always |
| 793 | // appear as an "error" until the user clears it, but other incoming files |
| 794 | // should be receivable when they arrive, so let's not wait too long to |
| 795 | // check again. |
| 796 | time.Sleep(5 * time.Second) |
| 797 | } |
| 798 | } |
| 799 | } |
| 800 | errs := runFileGetOneBatch(ctx, dir) |
| 801 | if len(errs) == 0 { |
| 802 | return nil |
| 803 | } |
| 804 | for _, err := range errs[:len(errs)-1] { |
| 805 | outln(err) |
| 806 | } |
| 807 | return errs[len(errs)-1] |
| 808 | } |
| 809 | |
| 810 | func wipeInbox(ctx context.Context) error { |
| 811 | if fileGetArgs.wait { |
nothing calls this directly
no test coverage detected
searching dependent graphs…