mirror of
https://github.com/rclone/rclone.git
synced 2024-11-25 09:41:44 +08:00
librclone: export the rclone RC as a C library #4891
This commit is contained in:
parent
4401d180aa
commit
316e65589b
22
librclone/ctest/Makefile
Normal file
22
librclone/ctest/Makefile
Normal file
|
@ -0,0 +1,22 @@
|
|||
CFLAGS = -g -Wall
|
||||
LDFLAGS = -L. -lrclone -lpthread -ldl
|
||||
|
||||
static: ctest
|
||||
|
||||
shared:
|
||||
go build --buildmode=c-shared -o librclone.so github.com/rclone/rclone/librclone
|
||||
|
||||
ctest: ctest.o librclone.h
|
||||
$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
|
||||
|
||||
ctest.o: ctest.c librclone.h
|
||||
$(CC) $(CFLAGS) -c $^ $(LDFLAGS)
|
||||
|
||||
build:
|
||||
go build
|
||||
|
||||
librclone.h:
|
||||
go build --buildmode=c-archive -o librclone.a github.com/rclone/rclone/librclone
|
||||
|
||||
clean:
|
||||
rm -f tmp ctest *.o *.a *.h *.gch
|
32
librclone/ctest/ctest.c
Normal file
32
librclone/ctest/ctest.c
Normal file
|
@ -0,0 +1,32 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <dlfcn.h>
|
||||
#include "librclone.h"
|
||||
|
||||
// copy file using "operations/copyfile" command
|
||||
void testCopyFile() {
|
||||
struct CRPC_return res = CRPC("operations/copyfile", "{ \"srcFs\": \"/tmp\", \"srcRemote\": \"tmpfile\", \"dstFs\": \"/tmp\", \"dstRemote\": \"tmpfile2\" }");
|
||||
printf("%d\n", res.r1); // status
|
||||
printf("%s\n", res.r0); // output
|
||||
free(res.r0);
|
||||
}
|
||||
|
||||
// noop command
|
||||
void testNoOp() {
|
||||
struct CRPC_return res = CRPC("rc/noop", "{ \"p1\": [1,\"2\",null,4], \"p2\": { \"a\":1, \"b\":2 } }");
|
||||
printf("%d\n", res.r1); // status
|
||||
printf("%s\n", res.r0); // output
|
||||
free(res.r0);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
printf("c main begin\n");
|
||||
Cinit();
|
||||
|
||||
//testNoOp();
|
||||
testCopyFile();
|
||||
|
||||
Cdestroy();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
133
librclone/librclone.go
Normal file
133
librclone/librclone.go
Normal file
|
@ -0,0 +1,133 @@
|
|||
// Package exports exports function for c library
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"C"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rclone/rclone/fs"
|
||||
"github.com/rclone/rclone/fs/rc"
|
||||
"github.com/rclone/rclone/fs/rc/jobs"
|
||||
|
||||
_ "github.com/rclone/rclone/backend/all" // import all backends
|
||||
_ "github.com/rclone/rclone/cmd/all" // import all commands
|
||||
_ "github.com/rclone/rclone/lib/plugin" // import plugins
|
||||
)
|
||||
|
||||
func init() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
// call to init the library
|
||||
//export Cinit
|
||||
func Cinit() {
|
||||
// TODO: what need to be initialized manually?
|
||||
}
|
||||
|
||||
// call to destroy the whole thing
|
||||
//export Cdestroy
|
||||
func Cdestroy() {
|
||||
// TODO: how to clean up? what happens when rcserver terminates?
|
||||
// what about unfinished async jobs?
|
||||
runtime.GC()
|
||||
}
|
||||
|
||||
// Caller is responsible for freeing the memory for output
|
||||
// TODO: how to specify config file?
|
||||
//export CRPC
|
||||
func CRPC(method *C.char, input *C.char) (output *C.char, status C.int) {
|
||||
res, s := callFunctionJSON(C.GoString(method), C.GoString(input))
|
||||
return C.CString(res), C.int(s)
|
||||
}
|
||||
|
||||
// copied from rcserver.go
|
||||
// writeError writes a formatted error to the output
|
||||
func writeError(path string, in rc.Params, w io.Writer, err error, status int) {
|
||||
fs.Errorf(nil, "rc: %q: error: %v", path, err)
|
||||
// Adjust the error return for some well known errors
|
||||
errOrig := errors.Cause(err)
|
||||
switch {
|
||||
case errOrig == fs.ErrorDirNotFound || errOrig == fs.ErrorObjectNotFound:
|
||||
status = http.StatusNotFound
|
||||
case rc.IsErrParamInvalid(err) || rc.IsErrParamNotFound(err):
|
||||
status = http.StatusBadRequest
|
||||
}
|
||||
// w.WriteHeader(status)
|
||||
err = rc.WriteJSON(w, rc.Params{
|
||||
"status": status,
|
||||
"error": err.Error(),
|
||||
"input": in,
|
||||
"path": path,
|
||||
})
|
||||
if err != nil {
|
||||
// can't return the error at this point
|
||||
fs.Errorf(nil, "rc: failed to write JSON output: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// operations/uploadfile and core/command are not supported as they need request or response object
|
||||
// modified from handlePost in rcserver.go
|
||||
// call a rc function using JSON to input parameters and output the resulted JSON
|
||||
func callFunctionJSON(method string, input string) (output string, status int) {
|
||||
// create a buffer to capture the output
|
||||
buf := new(bytes.Buffer)
|
||||
in := make(rc.Params)
|
||||
err := json.NewDecoder(strings.NewReader(input)).Decode(&in)
|
||||
if err != nil {
|
||||
// TODO: handle error
|
||||
writeError(method, in, buf, errors.Wrap(err, "failed to read input JSON"), http.StatusBadRequest)
|
||||
return buf.String(), http.StatusBadRequest
|
||||
}
|
||||
|
||||
// Find the call
|
||||
call := rc.Calls.Get(method)
|
||||
if call == nil {
|
||||
writeError(method, in, buf, errors.Errorf("couldn't find method %q", method), http.StatusNotFound)
|
||||
return buf.String(), http.StatusNotFound
|
||||
}
|
||||
|
||||
// TODO: handle these cases
|
||||
if call.NeedsRequest {
|
||||
writeError(method, in, buf, errors.Errorf("method %q needs request, not supported", method), http.StatusNotFound)
|
||||
return buf.String(), http.StatusNotFound
|
||||
// Add the request to RC
|
||||
//in["_request"] = r
|
||||
}
|
||||
if call.NeedsResponse {
|
||||
writeError(method, in, buf, errors.Errorf("method %q need response, not supported", method), http.StatusNotFound)
|
||||
return buf.String(), http.StatusNotFound
|
||||
//in["_response"] = w
|
||||
}
|
||||
|
||||
fs.Debugf(nil, "rc: %q: with parameters %+v", method, in)
|
||||
// TODO: what is r.Context()? use Background() for the moment
|
||||
_, out, err := jobs.NewJob(context.Background(), call.Fn, in)
|
||||
if err != nil {
|
||||
// handle error
|
||||
writeError(method, in, buf, err, http.StatusInternalServerError)
|
||||
return buf.String(), http.StatusInternalServerError
|
||||
}
|
||||
if out == nil {
|
||||
out = make(rc.Params)
|
||||
}
|
||||
|
||||
fs.Debugf(nil, "rc: %q: reply %+v: %v", method, out, err)
|
||||
err = rc.WriteJSON(buf, out)
|
||||
if err != nil {
|
||||
writeError(method, in, buf, err, http.StatusInternalServerError)
|
||||
return buf.String(), http.StatusInternalServerError
|
||||
fs.Errorf(nil, "rc: failed to write JSON output: %v", err)
|
||||
}
|
||||
return buf.String(), http.StatusOK
|
||||
}
|
||||
|
||||
// do nothing here
|
||||
func main() {}
|
Loading…
Reference in New Issue
Block a user