2
using TeeJee.FileSystem;
3
using TeeJee.JsonHelper;
4
using TeeJee.ProcessHelper;
8
public abstract class AsyncTask : GLib.Object{
10
private string err_line = "";
11
private string out_line = "";
12
private DataOutputStream dos_in;
13
private DataInputStream dis_out;
14
private DataInputStream dis_err;
15
protected DataOutputStream dos_log;
16
protected bool is_terminated = false;
18
private bool stdout_is_open = false;
19
private bool stderr_is_open = false;
21
protected Pid child_pid;
23
private int output_fd;
26
protected string script_file = "";
27
protected string working_dir = "";
28
protected string log_file = "";
30
public bool background_mode = false;
33
public AppStatus status;
34
public bool is_running = false;
35
public string status_line = "";
36
public int exit_code = 0;
37
public string error_msg = "";
38
public GLib.Timer timer;
39
public double progress = 0.0;
40
public double percent = 0.0;
41
public int64 prg_count = 0;
42
public int64 prg_count_total = 0;
43
public int64 prg_bytes = 0;
44
public int64 prg_bytes_total = 0;
47
public signal void stdout_line_read(string line);
48
public signal void stderr_line_read(string line);
49
public signal void task_complete();
51
public void AsyncTask(string _script_file, string _working_dir, string _log_file){
52
this.script_file = _script_file;
53
this.working_dir = _working_dir;
54
this.log_file = _log_file;
58
bool has_started = true;
59
is_terminated = false;
65
string[] spawn_args = new string[1];
66
spawn_args[0] = script_file;
68
string[] spawn_env = Environ.get();
72
timer = new GLib.Timer();
76
status = AppStatus.RUNNING;
78
// execute script file
79
Process.spawn_async_with_pipes(
80
working_dir, // working dir
82
spawn_env, // environment
83
SpawnFlags.SEARCH_PATH,
92
log_debug("AsyncTask: child_pid: %d".printf(child_pid));
94
// create stream readers
95
UnixOutputStream uos_in = new UnixOutputStream(input_fd, false);
96
UnixInputStream uis_out = new UnixInputStream(output_fd, false);
97
UnixInputStream uis_err = new UnixInputStream(error_fd, false);
98
dos_in = new DataOutputStream(uos_in);
99
dis_out = new DataInputStream(uis_out);
100
dis_err = new DataInputStream(uis_err);
101
dis_out.newline_type = DataStreamNewlineType.ANY;
102
dis_err.newline_type = DataStreamNewlineType.ANY;
105
if (log_file.length > 0){
106
var file = File.new_for_path(log_file);
107
if (file.query_exists()){
110
var file_stream = file.create (FileCreateFlags.REPLACE_DESTINATION);
111
dos_log = new DataOutputStream (file_stream);
115
//start thread for reading output stream
116
Thread.create<void> (read_stdout, true);
118
log_error ("AsyncTask.begin():create_thread:read_stdout()");
119
log_error (e.message);
123
//start thread for reading error stream
124
Thread.create<void> (read_stderr, true);
126
log_error ("AsyncTask.begin():create_thread:read_stderr()");
127
log_error (e.message);
131
log_error ("AsyncTask.begin()");
132
log_error(e.message);
135
//status = AppStatus.FINISHED;
141
private void read_stdout() {
143
stdout_is_open = true;
145
out_line = dis_out.read_line (null);
146
while (out_line != null) {
147
//log_msg("O: " + out_line);
148
if (!is_terminated && (out_line.length > 0)){
149
parse_stdout_line(out_line);
150
stdout_line_read(out_line); //signal
152
out_line = dis_out.read_line (null); //read next
155
stdout_is_open = false;
158
if ((dis_out != null) && !dis_out.is_closed()){
163
GLib.FileUtils.close(output_fd);
166
if (!stdout_is_open && !stderr_is_open){
171
log_error ("AsyncTask.read_stdout()");
172
log_error (e.message);
176
private void read_stderr() {
178
stderr_is_open = true;
180
err_line = dis_err.read_line (null);
181
while (err_line != null) {
182
if (!is_terminated && (err_line.length > 0)){
183
error_msg += "%s\n".printf(err_line);
185
parse_stderr_line(err_line);
186
stderr_line_read(err_line); //signal
188
err_line = dis_err.read_line (null); //read next
191
stderr_is_open = false;
194
if ((dis_err != null) && !dis_err.is_closed()){
199
GLib.FileUtils.close(error_fd);
202
if (!stdout_is_open && !stderr_is_open){
207
log_error ("AsyncTask.read_stderr()");
208
log_error (e.message);
212
public void write_stdin(string line){
214
if (status == AppStatus.RUNNING){
215
dos_in.put_string(line + "\n");
218
log_error ("AsyncTask.write_stdin(): NOT RUNNING");
222
log_error ("AsyncTask.write_stdin(): %s".printf(line));
223
log_error (e.message);
227
protected abstract void parse_stdout_line(string out_line);
229
protected abstract void parse_stderr_line(string err_line);
231
private void finish(){
237
log_error ("AsyncTask.finish(): dos_in.close()");
238
log_error (e.message);
242
GLib.FileUtils.close(input_fd);
244
// dispose child process
245
Process.close_pid(child_pid); //required on Windows, doesn't do anything on Unix
249
if ((dos_log != null) && !dos_log.is_closed() && !dos_log.is_closing()){
255
// error can be ignored
256
// dos_log is closed automatically when the last reference is set to null
257
// there may be pending operations which may throw an error
264
if ((status != AppStatus.CANCELLED) && (status != AppStatus.PASSWORD_REQUIRED)) {
265
status = AppStatus.FINISHED;
272
task_complete(); //signal
275
protected abstract void finish_task();
277
protected int read_exit_code(){
279
var path = file_parent(script_file) + "/status";
280
if (file_exists(path)){
281
var txt = file_read(path);
282
exit_code = int.parse(txt);
284
log_debug("exit_code: %d".printf(exit_code));
288
// public actions --------------
290
public void pause() {
292
foreach (long pid in get_process_children(child_pid)) {
293
sub_child_pid = (Pid) pid;
294
process_pause(sub_child_pid);
297
status = AppStatus.PAUSED;
300
public void resume() {
302
foreach (long pid in get_process_children(child_pid)) {
303
sub_child_pid = (Pid) pid;
304
process_resume(sub_child_pid);
307
status = AppStatus.RUNNING;
310
public void stop(AppStatus status_to_update) {
311
// we need to un-freeze the processes before we kill them
312
if (status == AppStatus.PAUSED) {
316
status = status_to_update;
318
process_quit(child_pid);
320
log_debug("process_quit: %d".printf(child_pid));
323
public void set_priority() {
324
if (background_mode){
325
set_priority_value(5);
328
set_priority_value(0);
332
public void set_priority_value(int prio) {
333
Pid app_pid = Posix.getpid();
334
process_set_priority (app_pid, prio);
336
if (status == AppStatus.RUNNING) {
337
process_set_priority (child_pid, prio);
340
foreach (long pid in get_process_children (child_pid)) {
341
sub_child_pid = (Pid) pid;
342
process_set_priority (sub_child_pid, prio);
347
public string stat_time_elapsed{
349
long elapsed = (long) timer_elapsed(timer);
350
return format_duration(elapsed);
354
public string stat_time_remaining{
357
long elapsed = (long) timer_elapsed(timer);
358
long remaining = (long)((elapsed / progress) * (1.0 - progress));
359
return format_duration(remaining);
367
public void print_app_status(){
369
case AppStatus.NOT_STARTED:
370
log_debug("status=%s".printf("NOT_STARTED"));
372
case AppStatus.RUNNING:
373
log_debug("status=%s".printf("RUNNING"));
375
case AppStatus.PAUSED:
376
log_debug("status=%s".printf("PAUSED"));
378
case AppStatus.FINISHED:
379
log_debug("status=%s".printf("FINISHED"));
381
case AppStatus.CANCELLED:
382
log_debug("status=%s".printf("CANCELLED"));
384
case AppStatus.PASSWORD_REQUIRED:
385
log_debug("status=%s".printf("PASSWORD_REQUIRED"));
391
public enum AppStatus {