manager.go 18 KB

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