utilexecmd.js 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. /*
  2. Copyright (C) 2017 Borsato Ivano
  3. The JavaScript code in this page is free software: you can
  4. redistribute it and/or modify it under the terms of the GNU
  5. General Public License (GNU GPL) as published by the Free Software
  6. Foundation, either version 3 of the License, or (at your option)
  7. any later version. The code is distributed WITHOUT ANY WARRANTY;
  8. without even the implied warranty of MERCHANTABILITY or FITNESS
  9. FOR A PARTICULAR PURPOSE. See the GNU GPL for more details.
  10. */
  11. 'use strict';
  12. import GObject from 'gi://GObject';
  13. import GLib from 'gi://GLib';
  14. import Gio from 'gi://Gio';
  15. import * as Lib from './convenience.js';
  16. /**
  17. * @type {ExecuteStuff}
  18. */
  19. export const ExecuteStuff = GObject.registerClass({
  20. GTypeName: 'EasyScreenCast_ExecuteStuff',
  21. }, class ExecuteStuff extends GObject.Object {
  22. /**
  23. * @param {EasyScreenCastSettingsWidget|EasyScreenCastIndicator} scope the scope for executing callback methods
  24. * @private
  25. */
  26. constructor(scope) {
  27. super();
  28. Lib.TalkativeLog(`-¶-init scope:${scope}`);
  29. this.Scope = scope;
  30. this.Callback = null;
  31. }
  32. /**
  33. * @param {string} cmd the command to be executed
  34. * @returns {*[]}
  35. * @private
  36. */
  37. _parseCmd(cmd) {
  38. let successP, argv;
  39. try {
  40. [successP, argv] = GLib.shell_parse_argv(cmd);
  41. } catch (err) {
  42. Lib.TalkativeLog('-¶-ERROR PARSE');
  43. successP = false;
  44. }
  45. if (successP) {
  46. Lib.TalkativeLog(`-¶-parse: ${successP} argv: ${argv}`);
  47. return [successP, argv];
  48. } else {
  49. return [successP, null];
  50. }
  51. }
  52. /**
  53. * Result callback.
  54. *
  55. * @callback ExecuteStuff~resultCallback
  56. * @param {boolean} result whether the executed command exited successfully or not
  57. * @param {string} stdout (optional) output of the result, if it was executed synchronously
  58. */
  59. /**
  60. * Line output callback for asynchronous commands.
  61. *
  62. * @callback ExecuteStuff~lineCallback
  63. * @param {string} line a single line of output
  64. */
  65. /**
  66. * @param {string} cmd the command to be executed
  67. * @param {boolean} sync execute synchronously (wait for return) or fork a process
  68. * @param {ExecuteStuff~resultCallback} resCb callback after the command is finished
  69. * @param {ExecuteStuff~lineCallback} lineCb callback for stdout when command is executed asynchronosly
  70. * @class
  71. */
  72. Execute(cmd, sync, resCb, lineCb) {
  73. Lib.TalkativeLog(`-¶-execute: ${cmd}`);
  74. this.CommandString = cmd;
  75. if (
  76. resCb === undefined &&
  77. resCb === null &&
  78. typeof resCb !== 'function'
  79. ) {
  80. Lib.TalkativeLog('-¶-resCallback NEED to be a function');
  81. this.Callback = null;
  82. } else {
  83. this.Callback = resCb;
  84. }
  85. if (sync === true) {
  86. Lib.TalkativeLog('-¶-sync execute (wait for return)');
  87. this._syncCmd(this.CommandString);
  88. } else {
  89. Lib.TalkativeLog('-¶-async execute (fork process)');
  90. if (
  91. lineCb === undefined &&
  92. lineCb === null &&
  93. typeof lineCb !== 'function'
  94. ) {
  95. Lib.TalkativeLog('-¶-lineCallback NEED to be a function');
  96. this.lineCallback = null;
  97. } else {
  98. this.lineCallback = lineCb;
  99. }
  100. this._asyncCmd(this.CommandString);
  101. }
  102. }
  103. /**
  104. * @param {string} cmd the command to be executed
  105. * @returns {*}
  106. * @class
  107. */
  108. Spawn(cmd) {
  109. let [successP, argv] = this._parseCmd(cmd);
  110. if (successP) {
  111. let successS, pid;
  112. try {
  113. [successS, pid] = GLib.spawn_async(
  114. null,
  115. argv,
  116. null,
  117. GLib.SpawnFlags.SEARCH_PATH |
  118. GLib.SpawnFlags.DO_NOT_REAP_CHILD,
  119. null
  120. );
  121. } catch (err) {
  122. Lib.TalkativeLog(
  123. `-¶-ERROR SPAWN err:${err.message.toString()}`
  124. );
  125. successS = false;
  126. }
  127. if (successS) {
  128. Lib.TalkativeLog(`-¶-spawn: ${successS} pid: ${pid}`);
  129. return true;
  130. } else {
  131. Lib.TalkativeLog('-¶-spawn ERROR');
  132. return null;
  133. }
  134. }
  135. return null;
  136. }
  137. /**
  138. * @param {string} cmd the command to be executed
  139. * @private
  140. */
  141. _syncCmd(cmd) {
  142. let [successP, argv] = this._parseCmd(cmd);
  143. let decoder = new TextDecoder();
  144. if (successP) {
  145. Lib.TalkativeLog(`-¶-argv: ${argv}`);
  146. let successS, stdOut, stdErr, exit;
  147. try {
  148. [successS, stdOut, stdErr, exit] = GLib.spawn_sync(
  149. null,
  150. argv,
  151. null,
  152. GLib.SpawnFlags.SEARCH_PATH,
  153. () => {}
  154. );
  155. } catch (err) {
  156. Lib.TalkativeLog('-¶-ERROR SPAWN');
  157. successS = false;
  158. }
  159. if (successS) {
  160. Lib.TalkativeLog(`-¶-argv: ${argv}`);
  161. Lib.TalkativeLog(`-¶-stdOut: ${decoder.decode(stdOut)}`);
  162. Lib.TalkativeLog(`-¶-stdErr: ${decoder.decode(stdErr)}`);
  163. Lib.TalkativeLog(`-¶-exit: ${exit}`);
  164. Lib.TalkativeLog('-¶-exe RC');
  165. if (this.Callback !== null) {
  166. this.Callback.apply(this.Scope, [
  167. true,
  168. decoder.decode(stdOut),
  169. ]);
  170. }
  171. } else {
  172. Lib.TalkativeLog(`-¶-ERROR exe WC - exit status: ${exit}`);
  173. if (this.Callback !== null)
  174. this.Callback.apply(this.Scope, [false]);
  175. }
  176. }
  177. }
  178. /**
  179. * @param {string} cmd the command to be executed
  180. * @private
  181. */
  182. _asyncCmd(cmd) {
  183. let [successP, argv] = this._parseCmd(cmd);
  184. let decoder = new TextDecoder();
  185. if (successP) {
  186. Lib.TalkativeLog(`-¶-argv: ${argv}`);
  187. let successS, pid, stdIn, stdOut, stdErr;
  188. try {
  189. [
  190. successS,
  191. pid,
  192. stdIn,
  193. stdOut,
  194. stdErr,
  195. ] = GLib.spawn_async_with_pipes(
  196. null, // working directory
  197. argv, // argv
  198. null, // envp
  199. GLib.SpawnFlags.SEARCH_PATH, // flags
  200. () => {} // child_setup
  201. );
  202. } catch (err) {
  203. Lib.TalkativeLog('-¶-ERROR SPAWN');
  204. successS = false;
  205. }
  206. if (successS) {
  207. Lib.TalkativeLog(`-¶-argv: ${argv}`);
  208. Lib.TalkativeLog(`-¶-pid: ${pid}`);
  209. Lib.TalkativeLog(`-¶-stdIn: ${stdIn}`);
  210. Lib.TalkativeLog(`-¶-stdOut: ${stdOut}`);
  211. Lib.TalkativeLog(`-¶-stdErr: ${stdErr}`);
  212. let outReader = new Gio.DataInputStream({
  213. base_stream: new Gio.UnixInputStream({
  214. fd: stdOut,
  215. }),
  216. });
  217. let inWriter = new Gio.DataOutputStream({
  218. base_stream: new Gio.UnixOutputStream({
  219. fd: stdIn,
  220. }),
  221. });
  222. inWriter.close(null);
  223. let [out] = outReader.read_line(null);
  224. while (out !== null) {
  225. if (this.lineCallback !== null)
  226. this.lineCallback.apply(this.Scope, [decoder.decode(out)]);
  227. [out] = outReader.read_line(null);
  228. }
  229. if (this.Callback !== null) {
  230. Lib.TalkativeLog('-¶-exe RC');
  231. this.Callback.apply(this.Scope, [true]);
  232. }
  233. } else {
  234. Lib.TalkativeLog('-¶-ERROR exe WC');
  235. if (this.Callback !== null)
  236. this.Callback.apply(this.Scope, [false]);
  237. }
  238. }
  239. }
  240. });