manager.go 18 KB

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