os.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. package stdlib
  2. import (
  3. "fmt"
  4. "io"
  5. "io/ioutil"
  6. "os"
  7. "os/exec"
  8. "github.com/d5/tengo/v2"
  9. )
  10. var osModule = map[string]tengo.Object{
  11. "o_rdonly": tengo.Int{Value: int64(os.O_RDONLY)},
  12. "o_wronly": tengo.Int{Value: int64(os.O_WRONLY)},
  13. "o_rdwr": tengo.Int{Value: int64(os.O_RDWR)},
  14. "o_append": tengo.Int{Value: int64(os.O_APPEND)},
  15. "o_create": tengo.Int{Value: int64(os.O_CREATE)},
  16. "o_excl": tengo.Int{Value: int64(os.O_EXCL)},
  17. "o_sync": tengo.Int{Value: int64(os.O_SYNC)},
  18. "o_trunc": tengo.Int{Value: int64(os.O_TRUNC)},
  19. "mode_dir": tengo.Int{Value: int64(os.ModeDir)},
  20. "mode_append": tengo.Int{Value: int64(os.ModeAppend)},
  21. "mode_exclusive": tengo.Int{Value: int64(os.ModeExclusive)},
  22. "mode_temporary": tengo.Int{Value: int64(os.ModeTemporary)},
  23. "mode_symlink": tengo.Int{Value: int64(os.ModeSymlink)},
  24. "mode_device": tengo.Int{Value: int64(os.ModeDevice)},
  25. "mode_named_pipe": tengo.Int{Value: int64(os.ModeNamedPipe)},
  26. "mode_socket": tengo.Int{Value: int64(os.ModeSocket)},
  27. "mode_setuid": tengo.Int{Value: int64(os.ModeSetuid)},
  28. "mode_setgui": tengo.Int{Value: int64(os.ModeSetgid)},
  29. "mode_char_device": tengo.Int{Value: int64(os.ModeCharDevice)},
  30. "mode_sticky": tengo.Int{Value: int64(os.ModeSticky)},
  31. "mode_type": tengo.Int{Value: int64(os.ModeType)},
  32. "mode_perm": tengo.Int{Value: int64(os.ModePerm)},
  33. "path_separator": tengo.Char{Value: os.PathSeparator},
  34. "path_list_separator": tengo.Char{Value: os.PathListSeparator},
  35. "dev_null": &tengo.String{Value: os.DevNull},
  36. "seek_set": tengo.Int{Value: int64(io.SeekStart)},
  37. "seek_cur": tengo.Int{Value: int64(io.SeekCurrent)},
  38. "seek_end": tengo.Int{Value: int64(io.SeekEnd)},
  39. "args": &tengo.UserFunction{
  40. Name: "args",
  41. Value: osArgs,
  42. }, // args() => array(string)
  43. "chdir": &tengo.UserFunction{
  44. Name: "chdir",
  45. Value: FuncASRE(os.Chdir),
  46. }, // chdir(dir string) => error
  47. "chmod": osFuncASFmRE("chmod", os.Chmod), // chmod(name string, mode int) => error
  48. "chown": &tengo.UserFunction{
  49. Name: "chown",
  50. Value: FuncASIIRE(os.Chown),
  51. }, // chown(name string, uid int, gid int) => error
  52. "clearenv": &tengo.UserFunction{
  53. Name: "clearenv",
  54. Value: FuncAR(os.Clearenv),
  55. }, // clearenv()
  56. "environ": &tengo.UserFunction{
  57. Name: "environ",
  58. Value: FuncARSs(os.Environ),
  59. }, // environ() => array(string)
  60. "exit": &tengo.UserFunction{
  61. Name: "exit",
  62. Value: FuncAIR(os.Exit),
  63. }, // exit(code int)
  64. "expand_env": &tengo.UserFunction{
  65. Name: "expand_env",
  66. Value: osExpandEnv,
  67. }, // expand_env(s string) => string
  68. "getegid": &tengo.UserFunction{
  69. Name: "getegid",
  70. Value: FuncARI(os.Getegid),
  71. }, // getegid() => int
  72. "getenv": &tengo.UserFunction{
  73. Name: "getenv",
  74. Value: FuncASRS(os.Getenv),
  75. }, // getenv(s string) => string
  76. "geteuid": &tengo.UserFunction{
  77. Name: "geteuid",
  78. Value: FuncARI(os.Geteuid),
  79. }, // geteuid() => int
  80. "getgid": &tengo.UserFunction{
  81. Name: "getgid",
  82. Value: FuncARI(os.Getgid),
  83. }, // getgid() => int
  84. "getgroups": &tengo.UserFunction{
  85. Name: "getgroups",
  86. Value: FuncARIsE(os.Getgroups),
  87. }, // getgroups() => array(string)/error
  88. "getpagesize": &tengo.UserFunction{
  89. Name: "getpagesize",
  90. Value: FuncARI(os.Getpagesize),
  91. }, // getpagesize() => int
  92. "getpid": &tengo.UserFunction{
  93. Name: "getpid",
  94. Value: FuncARI(os.Getpid),
  95. }, // getpid() => int
  96. "getppid": &tengo.UserFunction{
  97. Name: "getppid",
  98. Value: FuncARI(os.Getppid),
  99. }, // getppid() => int
  100. "getuid": &tengo.UserFunction{
  101. Name: "getuid",
  102. Value: FuncARI(os.Getuid),
  103. }, // getuid() => int
  104. "getwd": &tengo.UserFunction{
  105. Name: "getwd",
  106. Value: FuncARSE(os.Getwd),
  107. }, // getwd() => string/error
  108. "hostname": &tengo.UserFunction{
  109. Name: "hostname",
  110. Value: FuncARSE(os.Hostname),
  111. }, // hostname() => string/error
  112. "lchown": &tengo.UserFunction{
  113. Name: "lchown",
  114. Value: FuncASIIRE(os.Lchown),
  115. }, // lchown(name string, uid int, gid int) => error
  116. "link": &tengo.UserFunction{
  117. Name: "link",
  118. Value: FuncASSRE(os.Link),
  119. }, // link(oldname string, newname string) => error
  120. "lookup_env": &tengo.UserFunction{
  121. Name: "lookup_env",
  122. Value: osLookupEnv,
  123. }, // lookup_env(key string) => string/false
  124. "mkdir": osFuncASFmRE("mkdir", os.Mkdir), // mkdir(name string, perm int) => error
  125. "mkdir_all": osFuncASFmRE("mkdir_all", os.MkdirAll), // mkdir_all(name string, perm int) => error
  126. "readlink": &tengo.UserFunction{
  127. Name: "readlink",
  128. Value: FuncASRSE(os.Readlink),
  129. }, // readlink(name string) => string/error
  130. "remove": &tengo.UserFunction{
  131. Name: "remove",
  132. Value: FuncASRE(os.Remove),
  133. }, // remove(name string) => error
  134. "remove_all": &tengo.UserFunction{
  135. Name: "remove_all",
  136. Value: FuncASRE(os.RemoveAll),
  137. }, // remove_all(name string) => error
  138. "rename": &tengo.UserFunction{
  139. Name: "rename",
  140. Value: FuncASSRE(os.Rename),
  141. }, // rename(oldpath string, newpath string) => error
  142. "setenv": &tengo.UserFunction{
  143. Name: "setenv",
  144. Value: FuncASSRE(os.Setenv),
  145. }, // setenv(key string, value string) => error
  146. "symlink": &tengo.UserFunction{
  147. Name: "symlink",
  148. Value: FuncASSRE(os.Symlink),
  149. }, // symlink(oldname string newname string) => error
  150. "temp_dir": &tengo.UserFunction{
  151. Name: "temp_dir",
  152. Value: FuncARS(os.TempDir),
  153. }, // temp_dir() => string
  154. "truncate": &tengo.UserFunction{
  155. Name: "truncate",
  156. Value: FuncASI64RE(os.Truncate),
  157. }, // truncate(name string, size int) => error
  158. "unsetenv": &tengo.UserFunction{
  159. Name: "unsetenv",
  160. Value: FuncASRE(os.Unsetenv),
  161. }, // unsetenv(key string) => error
  162. "create": &tengo.UserFunction{
  163. Name: "create",
  164. Value: osCreate,
  165. }, // create(name string) => imap(file)/error
  166. "open": &tengo.UserFunction{
  167. Name: "open",
  168. Value: osOpen,
  169. }, // open(name string) => imap(file)/error
  170. "open_file": &tengo.UserFunction{
  171. Name: "open_file",
  172. Value: osOpenFile,
  173. }, // open_file(name string, flag int, perm int) => imap(file)/error
  174. "find_process": &tengo.UserFunction{
  175. Name: "find_process",
  176. Value: osFindProcess,
  177. }, // find_process(pid int) => imap(process)/error
  178. "start_process": &tengo.UserFunction{
  179. Name: "start_process",
  180. Value: osStartProcess,
  181. }, // start_process(name string, argv array(string), dir string, env array(string)) => imap(process)/error
  182. "exec_look_path": &tengo.UserFunction{
  183. Name: "exec_look_path",
  184. Value: FuncASRSE(exec.LookPath),
  185. }, // exec_look_path(file) => string/error
  186. "exec": &tengo.UserFunction{
  187. Name: "exec",
  188. Value: osExec,
  189. }, // exec(name, args...) => command
  190. "stat": &tengo.UserFunction{
  191. Name: "stat",
  192. Value: osStat,
  193. }, // stat(name) => imap(fileinfo)/error
  194. "read_file": &tengo.UserFunction{
  195. Name: "read_file",
  196. Value: osReadFile,
  197. }, // readfile(name) => array(byte)/error
  198. }
  199. func osReadFile(args ...tengo.Object) (ret tengo.Object, err error) {
  200. if len(args) != 1 {
  201. return nil, tengo.ErrWrongNumArguments
  202. }
  203. fname, ok := tengo.ToString(args[0])
  204. if !ok {
  205. return nil, tengo.ErrInvalidArgumentType{
  206. Name: "first",
  207. Expected: "string(compatible)",
  208. Found: args[0].TypeName(),
  209. }
  210. }
  211. bytes, err := ioutil.ReadFile(fname)
  212. if err != nil {
  213. return wrapError(err), nil
  214. }
  215. if len(bytes) > tengo.MaxBytesLen {
  216. return nil, tengo.ErrBytesLimit
  217. }
  218. return &tengo.Bytes{Value: bytes}, nil
  219. }
  220. func osStat(args ...tengo.Object) (ret tengo.Object, err error) {
  221. if len(args) != 1 {
  222. return nil, tengo.ErrWrongNumArguments
  223. }
  224. fname, ok := tengo.ToString(args[0])
  225. if !ok {
  226. return nil, tengo.ErrInvalidArgumentType{
  227. Name: "first",
  228. Expected: "string(compatible)",
  229. Found: args[0].TypeName(),
  230. }
  231. }
  232. stat, err := os.Stat(fname)
  233. if err != nil {
  234. return wrapError(err), nil
  235. }
  236. fstat := &tengo.ImmutableMap{
  237. Value: map[string]tengo.Object{
  238. "name": &tengo.String{Value: stat.Name()},
  239. "mtime": &tengo.Time{Value: stat.ModTime()},
  240. "size": tengo.Int{Value: stat.Size()},
  241. "mode": tengo.Int{Value: int64(stat.Mode())},
  242. },
  243. }
  244. if stat.IsDir() {
  245. fstat.Value["directory"] = tengo.TrueValue
  246. } else {
  247. fstat.Value["directory"] = tengo.FalseValue
  248. }
  249. return fstat, nil
  250. }
  251. func osCreate(args ...tengo.Object) (tengo.Object, error) {
  252. if len(args) != 1 {
  253. return nil, tengo.ErrWrongNumArguments
  254. }
  255. s1, ok := tengo.ToString(args[0])
  256. if !ok {
  257. return nil, tengo.ErrInvalidArgumentType{
  258. Name: "first",
  259. Expected: "string(compatible)",
  260. Found: args[0].TypeName(),
  261. }
  262. }
  263. res, err := os.Create(s1)
  264. if err != nil {
  265. return wrapError(err), nil
  266. }
  267. return makeOSFile(res), nil
  268. }
  269. func osOpen(args ...tengo.Object) (tengo.Object, error) {
  270. if len(args) != 1 {
  271. return nil, tengo.ErrWrongNumArguments
  272. }
  273. s1, ok := tengo.ToString(args[0])
  274. if !ok {
  275. return nil, tengo.ErrInvalidArgumentType{
  276. Name: "first",
  277. Expected: "string(compatible)",
  278. Found: args[0].TypeName(),
  279. }
  280. }
  281. res, err := os.Open(s1)
  282. if err != nil {
  283. return wrapError(err), nil
  284. }
  285. return makeOSFile(res), nil
  286. }
  287. func osOpenFile(args ...tengo.Object) (tengo.Object, error) {
  288. if len(args) != 3 {
  289. return nil, tengo.ErrWrongNumArguments
  290. }
  291. s1, ok := tengo.ToString(args[0])
  292. if !ok {
  293. return nil, tengo.ErrInvalidArgumentType{
  294. Name: "first",
  295. Expected: "string(compatible)",
  296. Found: args[0].TypeName(),
  297. }
  298. }
  299. i2, ok := tengo.ToInt(args[1])
  300. if !ok {
  301. return nil, tengo.ErrInvalidArgumentType{
  302. Name: "second",
  303. Expected: "int(compatible)",
  304. Found: args[1].TypeName(),
  305. }
  306. }
  307. i3, ok := tengo.ToInt(args[2])
  308. if !ok {
  309. return nil, tengo.ErrInvalidArgumentType{
  310. Name: "third",
  311. Expected: "int(compatible)",
  312. Found: args[2].TypeName(),
  313. }
  314. }
  315. res, err := os.OpenFile(s1, i2, os.FileMode(i3))
  316. if err != nil {
  317. return wrapError(err), nil
  318. }
  319. return makeOSFile(res), nil
  320. }
  321. func osArgs(args ...tengo.Object) (tengo.Object, error) {
  322. if len(args) != 0 {
  323. return nil, tengo.ErrWrongNumArguments
  324. }
  325. arr := &tengo.Array{}
  326. for _, osArg := range os.Args {
  327. if len(osArg) > tengo.MaxStringLen {
  328. return nil, tengo.ErrStringLimit
  329. }
  330. arr.Value = append(arr.Value, &tengo.String{Value: osArg})
  331. }
  332. return arr, nil
  333. }
  334. func osFuncASFmRE(
  335. name string,
  336. fn func(string, os.FileMode) error,
  337. ) *tengo.UserFunction {
  338. return &tengo.UserFunction{
  339. Name: name,
  340. Value: func(args ...tengo.Object) (tengo.Object, error) {
  341. if len(args) != 2 {
  342. return nil, tengo.ErrWrongNumArguments
  343. }
  344. s1, ok := tengo.ToString(args[0])
  345. if !ok {
  346. return nil, tengo.ErrInvalidArgumentType{
  347. Name: "first",
  348. Expected: "string(compatible)",
  349. Found: args[0].TypeName(),
  350. }
  351. }
  352. i2, ok := tengo.ToInt64(args[1])
  353. if !ok {
  354. return nil, tengo.ErrInvalidArgumentType{
  355. Name: "second",
  356. Expected: "int(compatible)",
  357. Found: args[1].TypeName(),
  358. }
  359. }
  360. return wrapError(fn(s1, os.FileMode(i2))), nil
  361. },
  362. }
  363. }
  364. func osLookupEnv(args ...tengo.Object) (tengo.Object, error) {
  365. if len(args) != 1 {
  366. return nil, tengo.ErrWrongNumArguments
  367. }
  368. s1, ok := tengo.ToString(args[0])
  369. if !ok {
  370. return nil, tengo.ErrInvalidArgumentType{
  371. Name: "first",
  372. Expected: "string(compatible)",
  373. Found: args[0].TypeName(),
  374. }
  375. }
  376. res, ok := os.LookupEnv(s1)
  377. if !ok {
  378. return tengo.FalseValue, nil
  379. }
  380. if len(res) > tengo.MaxStringLen {
  381. return nil, tengo.ErrStringLimit
  382. }
  383. return &tengo.String{Value: res}, nil
  384. }
  385. func osExpandEnv(args ...tengo.Object) (tengo.Object, error) {
  386. if len(args) != 1 {
  387. return nil, tengo.ErrWrongNumArguments
  388. }
  389. s1, ok := tengo.ToString(args[0])
  390. if !ok {
  391. return nil, tengo.ErrInvalidArgumentType{
  392. Name: "first",
  393. Expected: "string(compatible)",
  394. Found: args[0].TypeName(),
  395. }
  396. }
  397. var vlen int
  398. var failed bool
  399. s := os.Expand(s1, func(k string) string {
  400. if failed {
  401. return ""
  402. }
  403. v := os.Getenv(k)
  404. // this does not count the other texts that are not being replaced
  405. // but the code checks the final length at the end
  406. vlen += len(v)
  407. if vlen > tengo.MaxStringLen {
  408. failed = true
  409. return ""
  410. }
  411. return v
  412. })
  413. if failed || len(s) > tengo.MaxStringLen {
  414. return nil, tengo.ErrStringLimit
  415. }
  416. return &tengo.String{Value: s}, nil
  417. }
  418. func osExec(args ...tengo.Object) (tengo.Object, error) {
  419. if len(args) == 0 {
  420. return nil, tengo.ErrWrongNumArguments
  421. }
  422. name, ok := tengo.ToString(args[0])
  423. if !ok {
  424. return nil, tengo.ErrInvalidArgumentType{
  425. Name: "first",
  426. Expected: "string(compatible)",
  427. Found: args[0].TypeName(),
  428. }
  429. }
  430. var execArgs []string
  431. for idx, arg := range args[1:] {
  432. execArg, ok := tengo.ToString(arg)
  433. if !ok {
  434. return nil, tengo.ErrInvalidArgumentType{
  435. Name: fmt.Sprintf("args[%d]", idx),
  436. Expected: "string(compatible)",
  437. Found: args[1+idx].TypeName(),
  438. }
  439. }
  440. execArgs = append(execArgs, execArg)
  441. }
  442. return makeOSExecCommand(exec.Command(name, execArgs...)), nil
  443. }
  444. func osFindProcess(args ...tengo.Object) (tengo.Object, error) {
  445. if len(args) != 1 {
  446. return nil, tengo.ErrWrongNumArguments
  447. }
  448. i1, ok := tengo.ToInt(args[0])
  449. if !ok {
  450. return nil, tengo.ErrInvalidArgumentType{
  451. Name: "first",
  452. Expected: "int(compatible)",
  453. Found: args[0].TypeName(),
  454. }
  455. }
  456. proc, err := os.FindProcess(i1)
  457. if err != nil {
  458. return wrapError(err), nil
  459. }
  460. return makeOSProcess(proc), nil
  461. }
  462. func osStartProcess(args ...tengo.Object) (tengo.Object, error) {
  463. if len(args) != 4 {
  464. return nil, tengo.ErrWrongNumArguments
  465. }
  466. name, ok := tengo.ToString(args[0])
  467. if !ok {
  468. return nil, tengo.ErrInvalidArgumentType{
  469. Name: "first",
  470. Expected: "string(compatible)",
  471. Found: args[0].TypeName(),
  472. }
  473. }
  474. var argv []string
  475. var err error
  476. switch arg1 := args[1].(type) {
  477. case *tengo.Array:
  478. argv, err = stringArray(arg1.Value, "second")
  479. if err != nil {
  480. return nil, err
  481. }
  482. case *tengo.ImmutableArray:
  483. argv, err = stringArray(arg1.Value, "second")
  484. if err != nil {
  485. return nil, err
  486. }
  487. default:
  488. return nil, tengo.ErrInvalidArgumentType{
  489. Name: "second",
  490. Expected: "array",
  491. Found: arg1.TypeName(),
  492. }
  493. }
  494. dir, ok := tengo.ToString(args[2])
  495. if !ok {
  496. return nil, tengo.ErrInvalidArgumentType{
  497. Name: "third",
  498. Expected: "string(compatible)",
  499. Found: args[2].TypeName(),
  500. }
  501. }
  502. var env []string
  503. switch arg3 := args[3].(type) {
  504. case *tengo.Array:
  505. env, err = stringArray(arg3.Value, "fourth")
  506. if err != nil {
  507. return nil, err
  508. }
  509. case *tengo.ImmutableArray:
  510. env, err = stringArray(arg3.Value, "fourth")
  511. if err != nil {
  512. return nil, err
  513. }
  514. default:
  515. return nil, tengo.ErrInvalidArgumentType{
  516. Name: "fourth",
  517. Expected: "array",
  518. Found: arg3.TypeName(),
  519. }
  520. }
  521. proc, err := os.StartProcess(name, argv, &os.ProcAttr{
  522. Dir: dir,
  523. Env: env,
  524. })
  525. if err != nil {
  526. return wrapError(err), nil
  527. }
  528. return makeOSProcess(proc), nil
  529. }
  530. func stringArray(arr []tengo.Object, argName string) ([]string, error) {
  531. var sarr []string
  532. for idx, elem := range arr {
  533. str, ok := elem.(*tengo.String)
  534. if !ok {
  535. return nil, tengo.ErrInvalidArgumentType{
  536. Name: fmt.Sprintf("%s[%d]", argName, idx),
  537. Expected: "string",
  538. Found: elem.TypeName(),
  539. }
  540. }
  541. sarr = append(sarr, str.Value)
  542. }
  543. return sarr, nil
  544. }