manager.go 18 KB

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