manager.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722
  1. // Copyright 2021-2022 EMQ Technologies Co., Ltd.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. // Manage the loading of both native and portable plugins
  15. package native
  16. import (
  17. "archive/zip"
  18. "bytes"
  19. "fmt"
  20. "github.com/lf-edge/ekuiper/internal/conf"
  21. "github.com/lf-edge/ekuiper/internal/meta"
  22. "github.com/lf-edge/ekuiper/internal/pkg/filex"
  23. "github.com/lf-edge/ekuiper/internal/pkg/httpx"
  24. "github.com/lf-edge/ekuiper/internal/pkg/store"
  25. plugin2 "github.com/lf-edge/ekuiper/internal/plugin"
  26. "github.com/lf-edge/ekuiper/pkg/api"
  27. "github.com/lf-edge/ekuiper/pkg/errorx"
  28. "github.com/lf-edge/ekuiper/pkg/kv"
  29. "io/ioutil"
  30. "os"
  31. "os/exec"
  32. "path"
  33. "path/filepath"
  34. "plugin"
  35. "regexp"
  36. "strings"
  37. "sync"
  38. "time"
  39. "unicode"
  40. )
  41. // Manager Initialized in the binder
  42. var manager *Manager
  43. const DELETED = "$deleted"
  44. //Manager is append only because plugin cannot delete or reload. To delete a plugin, restart the server to reindex
  45. type Manager struct {
  46. sync.RWMutex
  47. // 3 maps for source/sink/function. In each map, key is the plugin name, value is the version
  48. plugins []map[string]string
  49. // A map from function name to its plugin file name. It is constructed during initialization by reading kv info. All functions must have at least an entry, even the function resizes in a one function plugin.
  50. symbols map[string]string
  51. // loaded symbols in current runtime
  52. runtime map[string]*plugin.Plugin
  53. // dirs
  54. pluginDir string
  55. etcDir string
  56. // the access to db
  57. db kv.KeyValue
  58. }
  59. // InitManager must only be called once
  60. func InitManager() (*Manager, error) {
  61. pluginDir, err := conf.GetPluginsLoc()
  62. if err != nil {
  63. return nil, fmt.Errorf("cannot find plugins folder: %s", err)
  64. }
  65. etcDir, err := conf.GetConfLoc()
  66. if err != nil {
  67. return nil, fmt.Errorf("cannot find etc folder: %s", err)
  68. }
  69. err, db := store.GetKV("pluginFuncs")
  70. if err != nil {
  71. return nil, fmt.Errorf("error when opening db: %v", err)
  72. }
  73. registry := &Manager{symbols: make(map[string]string), db: db, pluginDir: pluginDir, etcDir: etcDir, runtime: make(map[string]*plugin.Plugin)}
  74. manager = registry
  75. plugins := make([]map[string]string, 3)
  76. for i := range plugin2.PluginTypes {
  77. names, err := findAll(plugin2.PluginType(i), pluginDir)
  78. if err != nil {
  79. return nil, fmt.Errorf("fail to find existing plugins: %s", err)
  80. }
  81. plugins[i] = names
  82. }
  83. registry.plugins = plugins
  84. for pf := range plugins[plugin2.FUNCTION] {
  85. l := make([]string, 0)
  86. if ok, err := db.Get(pf, &l); ok {
  87. registry.storeSymbols(pf, l)
  88. } else if err != nil {
  89. return nil, fmt.Errorf("error when querying kv: %s", err)
  90. } else {
  91. registry.storeSymbols(pf, []string{pf})
  92. }
  93. }
  94. return registry, nil
  95. }
  96. func findAll(t plugin2.PluginType, pluginDir string) (result map[string]string, err error) {
  97. result = make(map[string]string)
  98. dir := path.Join(pluginDir, plugin2.PluginTypes[t])
  99. files, err := ioutil.ReadDir(dir)
  100. if err != nil {
  101. return
  102. }
  103. for _, file := range files {
  104. baseName := filepath.Base(file.Name())
  105. if strings.HasSuffix(baseName, ".so") {
  106. n, v := parseName(baseName)
  107. //load the plugins when ekuiper set up
  108. if !conf.IsTesting {
  109. if _, err := manager.loadRuntime(t, n, path.Join(dir, baseName), ""); err != nil {
  110. continue
  111. }
  112. }
  113. result[n] = v
  114. }
  115. }
  116. return
  117. }
  118. func GetManager() *Manager {
  119. return manager
  120. }
  121. func (rr *Manager) get(t plugin2.PluginType, name string) (string, bool) {
  122. rr.RLock()
  123. result := rr.plugins[t]
  124. rr.RUnlock()
  125. r, ok := result[name]
  126. return r, ok
  127. }
  128. func (rr *Manager) store(t plugin2.PluginType, name string, version string) {
  129. rr.Lock()
  130. rr.plugins[t][name] = version
  131. rr.Unlock()
  132. }
  133. func (rr *Manager) storeSymbols(name string, symbols []string) error {
  134. rr.Lock()
  135. defer rr.Unlock()
  136. for _, s := range symbols {
  137. if _, ok := rr.symbols[s]; ok {
  138. return fmt.Errorf("function name %s already exists", s)
  139. } else {
  140. rr.symbols[s] = name
  141. }
  142. }
  143. return nil
  144. }
  145. func (rr *Manager) removeSymbols(symbols []string) {
  146. rr.Lock()
  147. for _, s := range symbols {
  148. delete(rr.symbols, s)
  149. }
  150. rr.Unlock()
  151. }
  152. // API for management
  153. func (rr *Manager) List(t plugin2.PluginType) []string {
  154. rr.RLock()
  155. result := rr.plugins[t]
  156. rr.RUnlock()
  157. keys := make([]string, 0, len(result))
  158. for k := range result {
  159. keys = append(keys, k)
  160. }
  161. return keys
  162. }
  163. func (rr *Manager) ListSymbols() []string {
  164. rr.RLock()
  165. result := rr.symbols
  166. rr.RUnlock()
  167. keys := make([]string, 0, len(result))
  168. for k := range result {
  169. keys = append(keys, k)
  170. }
  171. return keys
  172. }
  173. func (rr *Manager) GetPluginVersionBySymbol(t plugin2.PluginType, symbolName string) (string, bool) {
  174. switch t {
  175. case plugin2.FUNCTION:
  176. rr.RLock()
  177. result := rr.plugins[t]
  178. name, ok := rr.symbols[symbolName]
  179. rr.RUnlock()
  180. if ok {
  181. r, nok := result[name]
  182. return r, nok
  183. } else {
  184. return "", false
  185. }
  186. default:
  187. return rr.get(t, symbolName)
  188. }
  189. }
  190. func (rr *Manager) GetPluginBySymbol(t plugin2.PluginType, symbolName string) (string, bool) {
  191. switch t {
  192. case plugin2.FUNCTION:
  193. rr.RLock()
  194. defer rr.RUnlock()
  195. name, ok := rr.symbols[symbolName]
  196. return name, ok
  197. default:
  198. return symbolName, true
  199. }
  200. }
  201. func (rr *Manager) Register(t plugin2.PluginType, j plugin2.Plugin) error {
  202. name, uri, shellParas := j.GetName(), j.GetFile(), j.GetShellParas()
  203. //Validation
  204. name = strings.Trim(name, " ")
  205. if name == "" {
  206. return fmt.Errorf("invalid name %s: should not be empty", name)
  207. }
  208. if !httpx.IsValidUrl(uri) || !strings.HasSuffix(uri, ".zip") {
  209. return fmt.Errorf("invalid uri %s", uri)
  210. }
  211. if v, ok := rr.get(t, name); ok {
  212. if v == DELETED {
  213. return fmt.Errorf("invalid name %s: the plugin is marked as deleted but Kuiper is not restarted for the change to take effect yet", name)
  214. } else {
  215. return fmt.Errorf("invalid name %s: duplicate", name)
  216. }
  217. }
  218. var err error
  219. zipPath := path.Join(rr.pluginDir, name+".zip")
  220. //clean up: delete zip file and unzip files in error
  221. defer os.Remove(zipPath)
  222. //download
  223. err = httpx.DownloadFile(zipPath, uri)
  224. if err != nil {
  225. return fmt.Errorf("fail to download file %s: %s", uri, err)
  226. }
  227. if t == plugin2.FUNCTION {
  228. if len(j.GetSymbols()) > 0 {
  229. err = rr.db.Set(name, j.GetSymbols())
  230. if err != nil {
  231. return err
  232. }
  233. err = rr.storeSymbols(name, j.GetSymbols())
  234. } else {
  235. err = rr.storeSymbols(name, []string{name})
  236. }
  237. }
  238. if err != nil {
  239. return err
  240. }
  241. //unzip and copy to destination
  242. version, err := rr.install(t, name, zipPath, shellParas)
  243. if err == nil && len(j.GetSymbols()) > 0 {
  244. err = rr.db.Set(name, j.GetSymbols())
  245. }
  246. if err != nil { //Revert for any errors
  247. if len(j.GetSymbols()) > 0 {
  248. rr.removeSymbols(j.GetSymbols())
  249. } else {
  250. rr.removeSymbols([]string{name})
  251. }
  252. return fmt.Errorf("fail to install plugin: %s", err)
  253. }
  254. rr.store(t, name, version)
  255. switch t {
  256. case plugin2.SINK:
  257. if err := meta.ReadSinkMetaFile(path.Join(rr.etcDir, plugin2.PluginTypes[t], name+`.json`), true); nil != err {
  258. conf.Log.Errorf("readSinkFile:%v", err)
  259. }
  260. case plugin2.SOURCE:
  261. if err := meta.ReadSourceMetaFile(path.Join(rr.etcDir, plugin2.PluginTypes[t], name+`.json`), true); nil != err {
  262. conf.Log.Errorf("readSourceFile:%v", err)
  263. }
  264. case plugin2.FUNCTION:
  265. if err := meta.ReadFuncMetaFile(path.Join(rr.etcDir, plugin2.PluginTypes[t], name+`.json`), true); nil != err {
  266. conf.Log.Errorf("readFuncFile:%v", err)
  267. }
  268. }
  269. return nil
  270. }
  271. // RegisterFuncs prerequisite:function plugin of name exists
  272. func (rr *Manager) RegisterFuncs(name string, functions []string) error {
  273. if len(functions) == 0 {
  274. return fmt.Errorf("property 'functions' must not be empty")
  275. }
  276. old := make([]string, 0)
  277. if ok, err := rr.db.Get(name, &old); err != nil {
  278. return err
  279. } else if ok {
  280. rr.removeSymbols(old)
  281. } else if !ok {
  282. rr.removeSymbols([]string{name})
  283. }
  284. err := rr.db.Set(name, functions)
  285. if err != nil {
  286. return err
  287. }
  288. return rr.storeSymbols(name, functions)
  289. }
  290. func (rr *Manager) Delete(t plugin2.PluginType, name string, stop bool) error {
  291. name = strings.Trim(name, " ")
  292. if name == "" {
  293. return fmt.Errorf("invalid name %s: should not be empty", name)
  294. }
  295. soPath, err := rr.getSoFilePath(t, name, true)
  296. if err != nil {
  297. return err
  298. }
  299. var results []string
  300. paths := []string{
  301. soPath,
  302. }
  303. // Find etc folder
  304. etcPath := path.Join(rr.etcDir, plugin2.PluginTypes[t], name)
  305. if fi, err := os.Stat(etcPath); err == nil {
  306. if fi.Mode().IsDir() {
  307. paths = append(paths, etcPath)
  308. }
  309. }
  310. switch t {
  311. case plugin2.SOURCE:
  312. paths = append(paths, path.Join(rr.etcDir, plugin2.PluginTypes[t], name+".yaml"))
  313. srcJsonPath := path.Join(rr.etcDir, plugin2.PluginTypes[plugin2.SOURCE], name+".json")
  314. _ = os.RemoveAll(srcJsonPath)
  315. meta.UninstallSource(name)
  316. case plugin2.SINK:
  317. sinkJsonPaths := path.Join(rr.etcDir, plugin2.PluginTypes[plugin2.SINK], name+".json")
  318. _ = os.RemoveAll(sinkJsonPaths)
  319. meta.UninstallSink(name)
  320. case plugin2.FUNCTION:
  321. funcJsonPath := path.Join(rr.etcDir, plugin2.PluginTypes[plugin2.FUNCTION], name+".json")
  322. _ = os.RemoveAll(funcJsonPath)
  323. old := make([]string, 0)
  324. if ok, err := rr.db.Get(name, &old); err != nil {
  325. return err
  326. } else if ok {
  327. rr.removeSymbols(old)
  328. err := rr.db.Delete(name)
  329. if err != nil {
  330. return err
  331. }
  332. } else if !ok {
  333. rr.removeSymbols([]string{name})
  334. }
  335. meta.UninstallFunc(name)
  336. }
  337. for _, p := range paths {
  338. _, err := os.Stat(p)
  339. if err == nil {
  340. err = os.RemoveAll(p)
  341. if err != nil {
  342. results = append(results, err.Error())
  343. }
  344. } else {
  345. results = append(results, fmt.Sprintf("can't find %s", p))
  346. }
  347. }
  348. if len(results) > 0 {
  349. return fmt.Errorf(strings.Join(results, "\n"))
  350. } else {
  351. rr.store(t, name, DELETED)
  352. if stop {
  353. go func() {
  354. time.Sleep(1 * time.Second)
  355. os.Exit(100)
  356. }()
  357. }
  358. return nil
  359. }
  360. }
  361. func (rr *Manager) GetPluginInfo(t plugin2.PluginType, name string) (map[string]interface{}, bool) {
  362. v, ok := rr.get(t, name)
  363. if strings.HasPrefix(v, "v") {
  364. v = v[1:]
  365. }
  366. if ok {
  367. r := map[string]interface{}{
  368. "name": name,
  369. "version": v,
  370. }
  371. if t == plugin2.FUNCTION {
  372. l := make([]string, 0)
  373. if ok, _ := rr.db.Get(name, &l); ok {
  374. r["functions"] = l
  375. }
  376. // ignore the error
  377. }
  378. return r, ok
  379. }
  380. return nil, false
  381. }
  382. func (rr *Manager) install(t plugin2.PluginType, name, src string, shellParas []string) (string, error) {
  383. var filenames []string
  384. var tempPath = path.Join(rr.pluginDir, "temp", plugin2.PluginTypes[t], name)
  385. defer os.RemoveAll(tempPath)
  386. r, err := zip.OpenReader(src)
  387. if err != nil {
  388. return "", err
  389. }
  390. defer r.Close()
  391. haveInstallFile := false
  392. for _, file := range r.File {
  393. fileName := file.Name
  394. if fileName == "install.sh" {
  395. haveInstallFile = true
  396. }
  397. }
  398. if len(shellParas) != 0 && !haveInstallFile {
  399. return "", fmt.Errorf("have shell parameters : %s but no install.sh file", shellParas)
  400. }
  401. soPrefix := regexp.MustCompile(fmt.Sprintf(`^((%s)|(%s))(@.*)?\.so$`, name, ucFirst(name)))
  402. var soPath string
  403. var yamlFile, yamlPath, version, soName string
  404. expFiles := 1
  405. if t == plugin2.SOURCE {
  406. yamlFile = name + ".yaml"
  407. yamlPath = path.Join(rr.etcDir, plugin2.PluginTypes[t], yamlFile)
  408. expFiles = 2
  409. }
  410. var revokeFiles []string
  411. defer func() {
  412. if err != nil {
  413. for _, f := range revokeFiles {
  414. os.RemoveAll(f)
  415. }
  416. }
  417. }()
  418. for _, file := range r.File {
  419. fileName := file.Name
  420. if yamlFile == fileName {
  421. err = filex.UnzipTo(file, yamlPath)
  422. if err != nil {
  423. return version, err
  424. }
  425. revokeFiles = append(revokeFiles, yamlPath)
  426. filenames = append(filenames, yamlPath)
  427. } else if fileName == name+".json" {
  428. jsonPath := path.Join(rr.etcDir, plugin2.PluginTypes[t], fileName)
  429. if err := filex.UnzipTo(file, jsonPath); nil != err {
  430. conf.Log.Errorf("Failed to decompress the metadata %s file", fileName)
  431. } else {
  432. revokeFiles = append(revokeFiles, jsonPath)
  433. }
  434. } else if soPrefix.Match([]byte(fileName)) {
  435. soPath = path.Join(rr.pluginDir, plugin2.PluginTypes[t], fileName)
  436. err = filex.UnzipTo(file, soPath)
  437. if err != nil {
  438. return version, err
  439. }
  440. filenames = append(filenames, soPath)
  441. revokeFiles = append(revokeFiles, soPath)
  442. soName, version = parseName(fileName)
  443. } else if strings.HasPrefix(fileName, "etc/") {
  444. err = filex.UnzipTo(file, path.Join(rr.etcDir, plugin2.PluginTypes[t], strings.Replace(fileName, "etc", name, 1)))
  445. if err != nil {
  446. return version, err
  447. }
  448. } else { //unzip other files
  449. err = filex.UnzipTo(file, path.Join(tempPath, fileName))
  450. if err != nil {
  451. return version, err
  452. }
  453. }
  454. }
  455. if len(filenames) != expFiles {
  456. err = fmt.Errorf("invalid zip file: so file or conf file is missing")
  457. return version, err
  458. } else if haveInstallFile {
  459. //run install script if there is
  460. spath := path.Join(tempPath, "install.sh")
  461. shellParas = append(shellParas, spath)
  462. if 1 != len(shellParas) {
  463. copy(shellParas[1:], shellParas[0:])
  464. shellParas[0] = spath
  465. }
  466. cmd := exec.Command("/bin/sh", shellParas...)
  467. var outb, errb bytes.Buffer
  468. cmd.Stdout = &outb
  469. cmd.Stderr = &errb
  470. err := cmd.Run()
  471. if err != nil {
  472. conf.Log.Infof(`err:%v stdout:%s stderr:%s`, err, outb.String(), errb.String())
  473. return version, err
  474. }
  475. conf.Log.Infof(`run install script:%s`, outb.String())
  476. }
  477. if !conf.IsTesting {
  478. // load the runtime first
  479. _, err = manager.loadRuntime(t, soName, soPath, "")
  480. if err != nil {
  481. return version, err
  482. }
  483. }
  484. conf.Log.Infof("install %s plugin %s", plugin2.PluginTypes[t], name)
  485. return version, nil
  486. }
  487. // binder factory implementations
  488. func (rr *Manager) Source(name string) (api.Source, error) {
  489. nf, err := rr.loadRuntime(plugin2.SOURCE, name, "", "")
  490. if err != nil {
  491. return nil, err
  492. }
  493. if nf == nil {
  494. return nil, nil
  495. }
  496. switch t := nf.(type) {
  497. case api.Source:
  498. return t, nil
  499. case func() api.Source:
  500. return t(), nil
  501. default:
  502. return nil, fmt.Errorf("exported symbol %s is not type of api.Source or function that return api.Source", t)
  503. }
  504. }
  505. func (rr *Manager) LookupSource(name string) (api.LookupSource, error) {
  506. nf, err := rr.loadRuntime(plugin2.SOURCE, name, "", ucFirst(name)+"Lookup")
  507. if err != nil {
  508. return nil, err
  509. }
  510. if nf == nil {
  511. return nil, nil
  512. }
  513. switch t := nf.(type) {
  514. case api.LookupSource:
  515. return t, nil
  516. case func() api.LookupSource:
  517. return t(), nil
  518. default:
  519. return nil, fmt.Errorf("exported symbol %s is not type of api.LookupSource or function that return api.LookupSource", t)
  520. }
  521. }
  522. func (rr *Manager) Sink(name string) (api.Sink, error) {
  523. nf, err := rr.loadRuntime(plugin2.SINK, name, "", "")
  524. if err != nil {
  525. return nil, err
  526. }
  527. if nf == nil {
  528. return nil, nil
  529. }
  530. var s api.Sink
  531. switch t := nf.(type) {
  532. case api.Sink:
  533. s = t
  534. case func() api.Sink:
  535. s = t()
  536. default:
  537. return nil, fmt.Errorf("exported symbol %s is not type of api.Sink or function that return api.Sink", t)
  538. }
  539. return s, nil
  540. }
  541. func (rr *Manager) Function(name string) (api.Function, error) {
  542. nf, err := rr.loadRuntime(plugin2.FUNCTION, name, "", "")
  543. if err != nil {
  544. return nil, err
  545. }
  546. if nf == nil {
  547. return nil, nil
  548. }
  549. var s api.Function
  550. switch t := nf.(type) {
  551. case api.Function:
  552. s = t
  553. case func() api.Function:
  554. s = t()
  555. default:
  556. return nil, fmt.Errorf("exported symbol %s is not type of api.Function or function that return api.Function", t)
  557. }
  558. return s, nil
  559. }
  560. func (rr *Manager) HasFunctionSet(name string) bool {
  561. _, ok := rr.get(plugin2.FUNCTION, name)
  562. return ok
  563. }
  564. func (rr *Manager) ConvName(name string) (string, bool) {
  565. _, err := rr.Function(name)
  566. if err == nil {
  567. return name, true
  568. }
  569. return name, false
  570. }
  571. // If not found, return nil,nil; Other errors return nil, err
  572. func (rr *Manager) loadRuntime(t plugin2.PluginType, soName, soFilepath, symbolName string) (plugin.Symbol, error) {
  573. ptype := plugin2.PluginTypes[t]
  574. key := ptype + "/" + soName
  575. var (
  576. plug *plugin.Plugin
  577. ok bool
  578. err error
  579. )
  580. rr.RLock()
  581. plug, ok = rr.runtime[key]
  582. rr.RUnlock()
  583. if !ok {
  584. var soPath string
  585. if soFilepath != "" {
  586. soPath = soFilepath
  587. } else {
  588. mod, err := rr.getSoFilePath(t, soName, false)
  589. if err != nil {
  590. conf.Log.Warnf(fmt.Sprintf("cannot find the native plugin %s in path: %v", soName, err))
  591. return nil, nil
  592. }
  593. soPath = mod
  594. }
  595. conf.Log.Debugf("Opening plugin %s", soPath)
  596. plug, err = plugin.Open(soPath)
  597. if err != nil {
  598. conf.Log.Errorf(fmt.Sprintf("plugin %s open error: %v", soName, err))
  599. return nil, fmt.Errorf("cannot open %s: %v", soPath, err)
  600. }
  601. rr.Lock()
  602. rr.runtime[key] = plug
  603. rr.Unlock()
  604. conf.Log.Debugf("Successfully open plugin %s", soPath)
  605. }
  606. if symbolName == "" {
  607. symbolName = ucFirst(soName)
  608. }
  609. conf.Log.Debugf("Loading symbol %s", symbolName)
  610. nf, err := plug.Lookup(symbolName)
  611. if err != nil {
  612. conf.Log.Warnf(fmt.Sprintf("cannot find symbol %s, please check if it is exported: %v", symbolName, err))
  613. return nil, nil
  614. }
  615. conf.Log.Debugf("Successfully look-up plugin %s", symbolName)
  616. return nf, nil
  617. }
  618. // Return the lowercase version of so name. It may be upper case in path.
  619. func (rr *Manager) getSoFilePath(t plugin2.PluginType, name string, isSoName bool) (string, error) {
  620. var (
  621. v string
  622. soname string
  623. ok bool
  624. )
  625. // We must identify plugin or symbol when deleting function plugin
  626. if isSoName {
  627. soname = name
  628. } else {
  629. soname, ok = rr.GetPluginBySymbol(t, name)
  630. if !ok {
  631. return "", errorx.NewWithCode(errorx.NOT_FOUND, fmt.Sprintf("invalid symbol name %s: not exist", name))
  632. }
  633. }
  634. v, ok = rr.get(t, soname)
  635. if !ok {
  636. return "", errorx.NewWithCode(errorx.NOT_FOUND, fmt.Sprintf("invalid name %s: not exist", soname))
  637. }
  638. soFile := soname + ".so"
  639. if v != "" {
  640. soFile = fmt.Sprintf("%s@%s.so", soname, v)
  641. }
  642. p := path.Join(rr.pluginDir, plugin2.PluginTypes[t], soFile)
  643. if _, err := os.Stat(p); err != nil {
  644. p = path.Join(rr.pluginDir, plugin2.PluginTypes[t], ucFirst(soFile))
  645. }
  646. if _, err := os.Stat(p); err != nil {
  647. return "", errorx.NewWithCode(errorx.NOT_FOUND, fmt.Sprintf("cannot find .so file for plugin %s", soname))
  648. }
  649. return p, nil
  650. }
  651. func parseName(n string) (string, string) {
  652. result := strings.Split(n, ".so")
  653. result = strings.Split(result[0], "@")
  654. name := lcFirst(result[0])
  655. if len(result) > 1 {
  656. return name, result[1]
  657. }
  658. return name, ""
  659. }
  660. func ucFirst(str string) string {
  661. for i, v := range str {
  662. return string(unicode.ToUpper(v)) + str[i+1:]
  663. }
  664. return ""
  665. }
  666. func lcFirst(str string) string {
  667. for i, v := range str {
  668. return string(unicode.ToLower(v)) + str[i+1:]
  669. }
  670. return ""
  671. }