1/* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright (C) 2023 Intel Corporation. 3 * All rights reserved. 4 */ 5 6package main 7 8//#cgo CFLAGS: -I../../include 9//#include "spdk/stdinc.h" 10import "C" 11import ( 12 "encoding/json" 13 "github.com/spdk/spdk/go/rpc/client" 14 "log" 15 "reflect" 16 "strings" 17 "unsafe" 18) 19 20const ( 21 InvalidParameterError = iota + 1 22 ConnectionError 23 JsonRpcCallError 24 InvalidResponseError 25) 26 27func main() { 28} 29 30//export spdk_gorpc_call 31func spdk_gorpc_call(jsonPtr *C.char, location *C.char) (*C.char, C.int) { 32 var jsonMap map[string]any 33 jsonStr := C.GoString(jsonPtr) 34 jsonDecoder := json.NewDecoder(strings.NewReader(jsonStr)) 35 jsonDecoder.UseNumber() 36 err := jsonDecoder.Decode(&jsonMap) 37 if err != nil { 38 log.Printf("error when decoding function arguments, err: %s", err.Error()) 39 return nil, InvalidParameterError 40 } 41 42 socketLocation := C.GoString(location) 43 44 rpcClient, err := client.CreateClientWithJsonCodec(client.Unix, socketLocation) 45 if err != nil { 46 log.Printf("error on client creation, err: %s", err.Error()) 47 return nil, ConnectionError 48 } 49 defer rpcClient.Close() 50 51 method := jsonMap["method"].(string) 52 params := jsonMap["params"].(map[string]any) 53 // Force Go client to skip 'params' parameter in JSON-RPC call. 54 if len(params) == 0 { 55 params = nil 56 } 57 58 resp, err := rpcClient.Call(method, params) 59 if err != nil { 60 log.Printf("error on JSON-RPC call, method: %s, params: %s, err: %s", method, params, err.Error()) 61 return nil, JsonRpcCallError 62 } 63 64 var respToEncode any 65 // This is a special case where inside JSON-RPC response 'Result' field is null ("result": null). 66 // Due to json tag 'omitempty' in *client.Response.Result this field is ignored during 67 // serialization when its value is null. General idea is to create a map containing values 68 // from response and serialize that map instead of a *client.Response we got from 69 // a Client.Call() method. 70 if resp.Result == nil && resp.Error == nil { 71 jsonMap := make(map[string]any) 72 // Get all *client.Response fields. 73 types := reflect.TypeOf(resp).Elem() 74 // Get all *client.Response values. 75 values := reflect.ValueOf(resp).Elem() 76 // Iterate over fields. 77 for i := 0; i < types.NumField(); i++ { 78 // Name of a field. 79 fieldName := types.Field(i).Name 80 // Name of *client.Response.Error field. 81 errorFieldName := reflect.TypeOf(resp.Error).Elem().Name() 82 // Add all fields with values to a map except *client.Response.Error 83 if fieldName != errorFieldName { 84 jsonMap[strings.ToLower(fieldName)] = values.Field(i).Interface() 85 } 86 } 87 respToEncode = jsonMap 88 } else { 89 respToEncode = resp 90 } 91 92 encodedResp, err := json.Marshal(respToEncode) 93 if err != nil { 94 log.Printf("error on creating json representation of response, err: %s", err.Error()) 95 return nil, InvalidResponseError 96 } 97 98 return C.CString(string(encodedResp)), 0 99} 100 101//export spdk_gorpc_free_response 102func spdk_gorpc_free_response(p *C.char) { 103 C.free(unsafe.Pointer(p)) 104} 105