2018-10-26 13:51:28 +00:00
package operations
import (
2019-06-17 08:34:30 +00:00
"context"
2021-11-04 10:12:57 +00:00
"fmt"
2020-06-12 06:04:30 +00:00
"io"
"mime"
"mime/multipart"
"net/http"
2020-08-10 16:09:46 +00:00
"path"
2018-10-26 13:51:28 +00:00
"strings"
2020-06-12 06:04:30 +00:00
"time"
2018-10-26 13:51:28 +00:00
2019-07-28 17:47:38 +00:00
"github.com/rclone/rclone/fs"
"github.com/rclone/rclone/fs/rc"
2018-10-26 13:51:28 +00:00
)
func init ( ) {
rc . Add ( rc . Call {
Path : "operations/list" ,
AuthRequired : true ,
Fn : rcList ,
Title : "List the given remote and path in JSON format" ,
2021-08-16 09:30:01 +00:00
Help : ` This takes the following parameters :
2018-10-26 13:51:28 +00:00
2020-10-13 21:49:58 +00:00
- fs - a remote name string e . g . "drive:"
- remote - a path within that remote e . g . "dir"
2018-10-26 13:51:28 +00:00
- opt - a dictionary of options to control the listing ( optional )
- recurse - If set recurse directories
- noModTime - If set return modification time
- showEncrypted - If set show decrypted names
- showOrigIDs - If set show the IDs for each item if known
- showHash - If set return a dictionary of hashes
2020-09-23 16:20:28 +00:00
- noMimeType - If set don ' t show mime types
- dirsOnly - If set only show directories
- filesOnly - If set only show files
2022-05-24 10:16:29 +00:00
- metadata - If set return metadata of objects also
2020-09-23 16:20:28 +00:00
- hashTypes - array of strings of hash types to show if showHash set
2018-10-26 13:51:28 +00:00
2021-08-16 09:30:01 +00:00
Returns :
2018-10-26 13:51:28 +00:00
- list
- This is an array of objects as described in the lsjson command
2022-06-19 13:51:37 +00:00
See the [ lsjson ] ( / commands / rclone_lsjson / ) command for more information on the above and examples .
2018-10-26 13:51:28 +00:00
` ,
} )
}
// List the directory
2019-06-17 08:34:30 +00:00
func rcList ( ctx context . Context , in rc . Params ) ( out rc . Params , err error ) {
2020-11-05 15:18:51 +00:00
f , remote , err := rc . GetFsAndRemote ( ctx , in )
2018-10-26 13:51:28 +00:00
if err != nil {
return nil , err
}
var opt ListJSONOpt
err = in . GetStruct ( "opt" , & opt )
if rc . NotErrParamNotFound ( err ) {
return nil , err
}
var list = [ ] * ListJSONItem { }
2019-06-17 08:34:30 +00:00
err = ListJSON ( ctx , f , remote , & opt , func ( item * ListJSONItem ) error {
2018-10-26 13:51:28 +00:00
list = append ( list , item )
return nil
} )
if err != nil {
return nil , err
}
out = make ( rc . Params )
out [ "list" ] = list
return out , nil
}
2020-09-23 16:20:28 +00:00
func init ( ) {
rc . Add ( rc . Call {
Path : "operations/stat" ,
AuthRequired : true ,
Fn : rcStat ,
Title : "Give information about the supplied file or directory" ,
Help : ` This takes the following parameters
- fs - a remote name string eg "drive:"
- remote - a path within that remote eg "dir"
- opt - a dictionary of options to control the listing ( optional )
- see operations / list for the options
The result is
- item - an object as described in the lsjson command . Will be null if not found .
Note that if you are only interested in files then it is much more
efficient to set the filesOnly flag in the options .
2022-06-19 13:51:37 +00:00
See the [ lsjson ] ( / commands / rclone_lsjson / ) command for more information on the above and examples .
2020-09-23 16:20:28 +00:00
` ,
} )
}
// List the directory
func rcStat ( ctx context . Context , in rc . Params ) ( out rc . Params , err error ) {
f , remote , err := rc . GetFsAndRemote ( ctx , in )
if err != nil {
return nil , err
}
var opt ListJSONOpt
err = in . GetStruct ( "opt" , & opt )
if rc . NotErrParamNotFound ( err ) {
return nil , err
}
item , err := StatJSON ( ctx , f , remote , & opt )
if err != nil {
return nil , err
}
out = make ( rc . Params )
out [ "item" ] = item
return out , nil
}
2018-10-26 13:51:28 +00:00
func init ( ) {
rc . Add ( rc . Call {
Path : "operations/about" ,
AuthRequired : true ,
Fn : rcAbout ,
Title : "Return the space used on the remote" ,
2021-08-16 09:30:01 +00:00
Help : ` This takes the following parameters :
2018-10-26 13:51:28 +00:00
2020-10-13 21:49:58 +00:00
- fs - a remote name string e . g . "drive:"
2018-10-26 13:51:28 +00:00
The result is as returned from rclone about -- json
2019-06-08 08:19:07 +00:00
2022-06-19 13:51:37 +00:00
See the [ about ] ( / commands / rclone_about / ) command for more information on the above .
2018-10-26 13:51:28 +00:00
` ,
} )
}
// About the remote
2019-06-17 08:34:30 +00:00
func rcAbout ( ctx context . Context , in rc . Params ) ( out rc . Params , err error ) {
2020-11-05 15:18:51 +00:00
f , err := rc . GetFs ( ctx , in )
2018-10-26 13:51:28 +00:00
if err != nil {
return nil , err
}
doAbout := f . Features ( ) . About
if doAbout == nil {
2021-11-04 10:12:57 +00:00
return nil , fmt . Errorf ( "%v doesn't support about" , f )
2018-10-26 13:51:28 +00:00
}
2019-06-17 08:34:30 +00:00
u , err := doAbout ( ctx )
2018-10-26 13:51:28 +00:00
if err != nil {
2021-11-04 10:12:57 +00:00
return nil , fmt . Errorf ( "about call failed: %w" , err )
2018-10-26 13:51:28 +00:00
}
err = rc . Reshape ( & out , u )
if err != nil {
2021-11-04 10:12:57 +00:00
return nil , fmt . Errorf ( "about Reshape failed: %w" , err )
2018-10-26 13:51:28 +00:00
}
return out , nil
}
func init ( ) {
for _ , copy := range [ ] bool { false , true } {
copy := copy
name := "Move"
if copy {
name = "Copy"
}
rc . Add ( rc . Call {
Path : "operations/" + strings . ToLower ( name ) + "file" ,
AuthRequired : true ,
2019-06-17 08:34:30 +00:00
Fn : func ( ctx context . Context , in rc . Params ) ( rc . Params , error ) {
return rcMoveOrCopyFile ( ctx , in , copy )
2018-10-26 13:51:28 +00:00
} ,
Title : name + " a file from source remote to destination remote" ,
2021-08-16 09:30:01 +00:00
Help : ` This takes the following parameters :
2018-10-26 13:51:28 +00:00
2023-03-24 11:25:39 +00:00
- srcFs - a remote name string e . g . "drive:" for the source , "/" for local filesystem
2020-10-13 21:49:58 +00:00
- srcRemote - a path within that remote e . g . "file.txt" for the source
2023-03-24 11:25:39 +00:00
- dstFs - a remote name string e . g . "drive2:" for the destination , "/" for local filesystem
2020-10-13 21:49:58 +00:00
- dstRemote - a path within that remote e . g . "file2.txt" for the destination
2018-10-26 13:51:28 +00:00
` ,
} )
}
}
// Copy a file
2019-06-17 08:34:30 +00:00
func rcMoveOrCopyFile ( ctx context . Context , in rc . Params , cp bool ) ( out rc . Params , err error ) {
2020-11-05 15:18:51 +00:00
srcFs , srcRemote , err := rc . GetFsAndRemoteNamed ( ctx , in , "srcFs" , "srcRemote" )
2018-10-26 13:51:28 +00:00
if err != nil {
return nil , err
}
2020-11-05 15:18:51 +00:00
dstFs , dstRemote , err := rc . GetFsAndRemoteNamed ( ctx , in , "dstFs" , "dstRemote" )
2018-10-26 13:51:28 +00:00
if err != nil {
return nil , err
}
2019-06-17 08:34:30 +00:00
return nil , moveOrCopyFile ( ctx , dstFs , srcFs , dstRemote , srcRemote , cp )
2018-10-26 13:51:28 +00:00
}
func init ( ) {
for _ , op := range [ ] struct {
2020-06-05 14:26:46 +00:00
name string
title string
help string
noRemote bool
needsRequest bool
2018-10-26 13:51:28 +00:00
} {
{ name : "mkdir" , title : "Make a destination directory or container" } ,
{ name : "rmdir" , title : "Remove an empty directory or container" } ,
{ name : "purge" , title : "Remove a directory or container and all of its contents" } ,
2022-06-19 13:51:37 +00:00
{ name : "rmdirs" , title : "Remove all the empty directories in the path" , help : "- leaveRoot - boolean, set to true not to delete the root\n" } ,
2018-10-26 13:51:28 +00:00
{ name : "delete" , title : "Remove files in the path" , noRemote : true } ,
{ name : "deletefile" , title : "Remove the single file pointed to" } ,
2022-06-19 13:51:37 +00:00
{ name : "copyurl" , title : "Copy the URL to the object" , help : "- url - string, URL to read from\n - autoFilename - boolean, set to true to retrieve destination file name from url\n" } ,
{ name : "uploadfile" , title : "Upload file using multiform/form-data" , help : "- each part in body represents a file to be uploaded\n" , needsRequest : true } ,
2018-10-26 13:51:28 +00:00
{ name : "cleanup" , title : "Remove trashed files in the remote or path" , noRemote : true } ,
} {
op := op
2020-10-13 21:49:58 +00:00
remote := "- remote - a path within that remote e.g. \"dir\"\n"
2018-10-26 13:51:28 +00:00
if op . noRemote {
remote = ""
}
rc . Add ( rc . Call {
Path : "operations/" + op . name ,
AuthRequired : true ,
2020-06-05 14:26:46 +00:00
NeedsRequest : op . needsRequest ,
2019-06-17 08:34:30 +00:00
Fn : func ( ctx context . Context , in rc . Params ) ( rc . Params , error ) {
return rcSingleCommand ( ctx , in , op . name , op . noRemote )
2018-10-26 13:51:28 +00:00
} ,
Title : op . title ,
2021-08-16 09:30:01 +00:00
Help : ` This takes the following parameters :
2018-10-26 13:51:28 +00:00
2020-10-13 21:49:58 +00:00
- fs - a remote name string e . g . "drive:"
2018-10-26 13:51:28 +00:00
` + remote + op.help + `
2022-06-19 13:51:37 +00:00
See the [ ` + op.name + ` ] ( / commands / rclone_ ` + op.name + ` / ) command for more information on the above .
2018-10-26 13:51:28 +00:00
` ,
} )
}
}
2020-10-13 21:49:58 +00:00
// Run a single command, e.g. Mkdir
2019-06-17 08:34:30 +00:00
func rcSingleCommand ( ctx context . Context , in rc . Params , name string , noRemote bool ) ( out rc . Params , err error ) {
2018-10-26 13:51:28 +00:00
var (
f fs . Fs
remote string
)
if noRemote {
2020-11-05 15:18:51 +00:00
f , err = rc . GetFs ( ctx , in )
2018-10-26 13:51:28 +00:00
} else {
2020-11-05 15:18:51 +00:00
f , remote , err = rc . GetFsAndRemote ( ctx , in )
2018-10-26 13:51:28 +00:00
}
if err != nil {
return nil , err
}
switch name {
case "mkdir" :
2019-06-17 08:34:30 +00:00
return nil , Mkdir ( ctx , f , remote )
2018-10-26 13:51:28 +00:00
case "rmdir" :
2019-06-17 08:34:30 +00:00
return nil , Rmdir ( ctx , f , remote )
2018-10-26 13:51:28 +00:00
case "purge" :
2019-06-17 08:34:30 +00:00
return nil , Purge ( ctx , f , remote )
2018-10-26 13:51:28 +00:00
case "rmdirs" :
leaveRoot , err := in . GetBool ( "leaveRoot" )
if rc . NotErrParamNotFound ( err ) {
return nil , err
}
2019-06-17 08:34:30 +00:00
return nil , Rmdirs ( ctx , f , remote , leaveRoot )
2018-10-26 13:51:28 +00:00
case "delete" :
2019-06-17 08:34:30 +00:00
return nil , Delete ( ctx , f )
2018-10-26 13:51:28 +00:00
case "deletefile" :
2019-06-17 08:34:30 +00:00
o , err := f . NewObject ( ctx , remote )
2018-10-26 13:51:28 +00:00
if err != nil {
return nil , err
}
2019-06-17 08:34:30 +00:00
return nil , DeleteFile ( ctx , o )
2018-10-26 13:51:28 +00:00
case "copyurl" :
url , err := in . GetString ( "url" )
if err != nil {
return nil , err
}
2019-09-03 16:25:19 +00:00
autoFilename , _ := in . GetBool ( "autoFilename" )
2020-04-19 11:40:17 +00:00
noClobber , _ := in . GetBool ( "noClobber" )
2021-07-28 16:05:21 +00:00
headerFilename , _ := in . GetBool ( "headerFilename" )
2019-09-03 16:25:19 +00:00
2021-07-28 16:05:21 +00:00
_ , err = CopyURL ( ctx , f , remote , url , autoFilename , headerFilename , noClobber )
2018-10-26 13:51:28 +00:00
return nil , err
2020-06-12 06:04:30 +00:00
case "uploadfile" :
var request * http . Request
request , err := in . GetHTTPRequest ( )
if err != nil {
return nil , err
}
contentType := request . Header . Get ( "Content-Type" )
mediaType , params , err := mime . ParseMediaType ( contentType )
if err != nil {
return nil , err
}
if strings . HasPrefix ( mediaType , "multipart/" ) {
mr := multipart . NewReader ( request . Body , params [ "boundary" ] )
for {
p , err := mr . NextPart ( )
if err == io . EOF {
return nil , nil
}
if err != nil {
return nil , err
}
if p . FileName ( ) != "" {
2022-11-08 17:42:18 +00:00
obj , err := Rcat ( ctx , f , path . Join ( remote , p . FileName ( ) ) , p , time . Now ( ) , nil )
2020-06-12 06:04:30 +00:00
if err != nil {
return nil , err
}
fs . Debugf ( obj , "Upload Succeeded" )
}
}
}
return nil , nil
2018-10-26 13:51:28 +00:00
case "cleanup" :
2019-06-17 08:34:30 +00:00
return nil , CleanUp ( ctx , f )
2018-10-26 13:51:28 +00:00
}
panic ( "unknown rcSingleCommand type" )
}
func init ( ) {
rc . Add ( rc . Call {
Path : "operations/size" ,
AuthRequired : true ,
Fn : rcSize ,
Title : "Count the number of bytes and files in remote" ,
2021-08-16 09:30:01 +00:00
Help : ` This takes the following parameters :
2018-10-26 13:51:28 +00:00
2020-10-13 21:49:58 +00:00
- fs - a remote name string e . g . "drive:path/to/dir"
2018-10-26 13:51:28 +00:00
2021-08-16 09:30:01 +00:00
Returns :
2018-10-26 13:51:28 +00:00
- count - number of files
- bytes - number of bytes in those files
2022-06-19 13:51:37 +00:00
See the [ size ] ( / commands / rclone_size / ) command for more information on the above .
2018-10-26 13:51:28 +00:00
` ,
} )
}
2019-03-11 16:59:27 +00:00
// Size a directory
2019-06-17 08:34:30 +00:00
func rcSize ( ctx context . Context , in rc . Params ) ( out rc . Params , err error ) {
2020-11-05 15:18:51 +00:00
f , err := rc . GetFs ( ctx , in )
2018-10-26 13:51:28 +00:00
if err != nil {
return nil , err
}
2022-04-06 12:15:07 +00:00
count , bytes , sizeless , err := Count ( ctx , f )
2018-10-26 13:51:28 +00:00
if err != nil {
return nil , err
}
out = make ( rc . Params )
out [ "count" ] = count
out [ "bytes" ] = bytes
2022-04-06 12:15:07 +00:00
out [ "sizeless" ] = sizeless
2018-10-26 13:51:28 +00:00
return out , nil
}
2019-03-11 16:59:27 +00:00
func init ( ) {
rc . Add ( rc . Call {
Path : "operations/publiclink" ,
AuthRequired : true ,
Fn : rcPublicLink ,
Title : "Create or retrieve a public link to the given file or folder." ,
2021-08-16 09:30:01 +00:00
Help : ` This takes the following parameters :
2019-03-11 16:59:27 +00:00
2020-10-13 21:49:58 +00:00
- fs - a remote name string e . g . "drive:"
- remote - a path within that remote e . g . "dir"
2020-05-31 21:18:01 +00:00
- unlink - boolean - if set removes the link rather than adding it ( optional )
2020-10-13 21:49:58 +00:00
- expire - string - the expiry time of the link e . g . "1d" ( optional )
2019-03-11 16:59:27 +00:00
2021-08-16 09:30:01 +00:00
Returns :
2019-03-11 16:59:27 +00:00
- url - URL of the resource
2022-06-19 13:51:37 +00:00
See the [ link ] ( / commands / rclone_link / ) command for more information on the above .
2019-03-11 16:59:27 +00:00
` ,
} )
}
// Make a public link
2019-06-17 08:34:30 +00:00
func rcPublicLink ( ctx context . Context , in rc . Params ) ( out rc . Params , err error ) {
2020-11-05 15:18:51 +00:00
f , remote , err := rc . GetFsAndRemote ( ctx , in )
2019-03-11 16:59:27 +00:00
if err != nil {
return nil , err
}
2020-05-31 21:18:01 +00:00
unlink , _ := in . GetBool ( "unlink" )
expire , err := in . GetDuration ( "expire" )
2021-11-09 10:18:49 +00:00
if rc . IsErrParamNotFound ( err ) {
expire = time . Duration ( fs . DurationOff )
} else if err != nil {
2020-05-31 21:18:01 +00:00
return nil , err
}
url , err := PublicLink ( ctx , f , remote , fs . Duration ( expire ) , unlink )
2019-03-11 16:59:27 +00:00
if err != nil {
return nil , err
}
out = make ( rc . Params )
out [ "url" ] = url
return out , nil
}
2019-06-08 08:19:07 +00:00
func init ( ) {
rc . Add ( rc . Call {
Path : "operations/fsinfo" ,
Fn : rcFsInfo ,
Title : "Return information about the remote" ,
2021-08-16 09:30:01 +00:00
Help : ` This takes the following parameters :
2019-06-08 08:19:07 +00:00
2020-10-13 21:49:58 +00:00
- fs - a remote name string e . g . "drive:"
2019-06-08 08:19:07 +00:00
This returns info about the remote passed in ;
` + " ` ` ` " + `
{
2022-06-22 14:56:41 +00:00
// optional features and whether they are available or not
"Features" : {
"About" : true ,
"BucketBased" : false ,
"BucketBasedRootOK" : false ,
"CanHaveEmptyDirectories" : true ,
"CaseInsensitive" : false ,
"ChangeNotify" : false ,
"CleanUp" : false ,
"Command" : true ,
"Copy" : false ,
"DirCacheFlush" : false ,
"DirMove" : true ,
"Disconnect" : false ,
"DuplicateFiles" : false ,
"GetTier" : false ,
"IsLocal" : true ,
"ListR" : false ,
"MergeDirs" : false ,
"MetadataInfo" : true ,
"Move" : true ,
"OpenWriterAt" : true ,
"PublicLink" : false ,
"Purge" : true ,
"PutStream" : true ,
"PutUnchecked" : false ,
"ReadMetadata" : true ,
"ReadMimeType" : false ,
"ServerSideAcrossConfigs" : false ,
"SetTier" : false ,
"SetWrapper" : false ,
"Shutdown" : false ,
"SlowHash" : true ,
"SlowModTime" : false ,
"UnWrap" : false ,
"UserInfo" : false ,
"UserMetadata" : true ,
"WrapFs" : false ,
"WriteMetadata" : true ,
"WriteMimeType" : false
} ,
// Names of hashes available
"Hashes" : [
"md5" ,
"sha1" ,
"whirlpool" ,
"crc32" ,
"sha256" ,
"dropbox" ,
"mailru" ,
"quickxor"
] ,
"Name" : "local" , // Name as created
"Precision" : 1 , // Precision of timestamps in ns
"Root" : "/" , // Path as created
"String" : "Local file system at /" , // how the remote will appear in logs
// Information about the system metadata for this backend
"MetadataInfo" : {
"System" : {
"atime" : {
"Help" : "Time of last access" ,
"Type" : "RFC 3339" ,
"Example" : "2006-01-02T15:04:05.999999999Z07:00"
} ,
"btime" : {
"Help" : "Time of file birth (creation)" ,
"Type" : "RFC 3339" ,
"Example" : "2006-01-02T15:04:05.999999999Z07:00"
} ,
"gid" : {
"Help" : "Group ID of owner" ,
"Type" : "decimal number" ,
"Example" : "500"
} ,
"mode" : {
"Help" : "File type and mode" ,
"Type" : "octal, unix style" ,
"Example" : "0100664"
} ,
"mtime" : {
"Help" : "Time of last modification" ,
"Type" : "RFC 3339" ,
"Example" : "2006-01-02T15:04:05.999999999Z07:00"
} ,
"rdev" : {
"Help" : "Device ID (if special file)" ,
"Type" : "hexadecimal" ,
"Example" : "1abc"
} ,
"uid" : {
"Help" : "User ID of owner" ,
"Type" : "decimal number" ,
"Example" : "500"
}
} ,
"Help" : "Textual help string\n"
}
2019-06-08 08:19:07 +00:00
}
` + " ` ` ` " + `
This command does not have a command line equivalent so use this instead :
rclone rc -- loopback operations / fsinfo fs = remote :
` ,
} )
}
// Fsinfo the remote
2019-06-17 08:34:30 +00:00
func rcFsInfo ( ctx context . Context , in rc . Params ) ( out rc . Params , err error ) {
2020-11-05 15:18:51 +00:00
f , err := rc . GetFs ( ctx , in )
2019-06-08 08:19:07 +00:00
if err != nil {
return nil , err
}
info := GetFsInfo ( f )
err = rc . Reshape ( & out , info )
if err != nil {
2021-11-04 10:12:57 +00:00
return nil , fmt . Errorf ( "fsinfo Reshape failed: %w" , err )
2019-06-08 08:19:07 +00:00
}
return out , nil
}
2020-04-28 12:02:19 +00:00
func init ( ) {
rc . Add ( rc . Call {
Path : "backend/command" ,
AuthRequired : true ,
Fn : rcBackend ,
Title : "Runs a backend command." ,
2021-08-16 09:30:01 +00:00
Help : ` This takes the following parameters :
2020-04-28 12:02:19 +00:00
- command - a string with the command name
2020-10-13 21:49:58 +00:00
- fs - a remote name string e . g . "drive:"
2020-04-28 12:02:19 +00:00
- arg - a list of arguments for the backend command
- opt - a map of string to string of options
2021-08-16 09:30:01 +00:00
Returns :
2020-04-28 12:02:19 +00:00
- result - result from the backend command
2021-08-16 09:30:01 +00:00
Example :
2020-04-28 12:02:19 +00:00
rclone rc backend / command command = noop fs = . - o echo = yes - o blue - a path1 - a path2
Returns
` + " ` ` ` " + `
{
"result" : {
"arg" : [
"path1" ,
"path2"
] ,
"name" : "noop" ,
"opt" : {
"blue" : "" ,
"echo" : "yes"
}
}
}
` + " ` ` ` " + `
Note that this is the direct equivalent of using this "backend"
command :
rclone backend noop . - o echo = yes - o blue path1 path2
2020-05-20 10:39:20 +00:00
Note that arguments must be preceded by the "-a" flag
2020-04-28 12:02:19 +00:00
See the [ backend ] ( / commands / rclone_backend / ) command for more information .
` ,
} )
}
// Make a public link
func rcBackend ( ctx context . Context , in rc . Params ) ( out rc . Params , err error ) {
2020-11-05 15:18:51 +00:00
f , err := rc . GetFs ( ctx , in )
2020-04-28 12:02:19 +00:00
if err != nil {
return nil , err
}
doCommand := f . Features ( ) . Command
if doCommand == nil {
2021-11-04 10:12:57 +00:00
return nil , fmt . Errorf ( "%v: doesn't support backend commands" , f )
2020-04-28 12:02:19 +00:00
}
command , err := in . GetString ( "command" )
if err != nil {
return nil , err
}
var opt = map [ string ] string { }
err = in . GetStructMissingOK ( "opt" , & opt )
if err != nil {
return nil , err
}
var arg = [ ] string { }
err = in . GetStructMissingOK ( "arg" , & arg )
if err != nil {
return nil , err
}
result , err := doCommand ( context . Background ( ) , command , arg , opt )
if err != nil {
2021-11-04 10:12:57 +00:00
return nil , fmt . Errorf ( "command %q failed: %w" , command , err )
2020-04-28 12:02:19 +00:00
}
out = make ( rc . Params )
out [ "result" ] = result
return out , nil
}