1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /*
4 * File-Roller
5 *
6 * Copyright (C) 2001, 2003, 2008, 2012 Free Software Foundation, Inc.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include <config.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <stdio.h>
26 #include <signal.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/types.h>
30 #include <sys/wait.h>
31 #include <unistd.h>
32 #include <glib.h>
33 #include "file-utils.h"
34 #include "fr-process.h"
35 #include "glib-utils.h"
36
37 #define REFRESH_RATE 20
38 #define BUFFER_SIZE 16384
39
40
41 /* -- FrCommandInfo -- */
42
43
44 typedef struct {
45 GList *args; /* command to execute */
46 char *dir; /* working directory */
47 guint sticky : 1; /* whether the command must be
48 * executed even if a previous
49 * command has failed. */
50 guint ignore_error : 1; /* whether to continue to execute
51 * other commands if this command
52 * fails. */
53 ContinueFunc continue_func;
54 gpointer continue_data;
55 ProcFunc begin_func;
56 gpointer begin_data;
57 ProcFunc end_func;
58 gpointer end_data;
59 } FrCommandInfo;
60
61
62 static FrCommandInfo *
fr_command_info_new(void)63 fr_command_info_new (void)
64 {
65 FrCommandInfo *info;
66
67 info = g_new0 (FrCommandInfo, 1);
68 info->args = NULL;
69 info->dir = NULL;
70 info->sticky = FALSE;
71 info->ignore_error = FALSE;
72
73 return info;
74 }
75
76
77 static void
fr_command_info_free(FrCommandInfo * info)78 fr_command_info_free (FrCommandInfo *info)
79 {
80 if (info == NULL)
81 return;
82
83 if (info->args != NULL) {
84 g_list_foreach (info->args, (GFunc) g_free, NULL);
85 g_list_free (info->args);
86 info->args = NULL;
87 }
88
89 if (info->dir != NULL) {
90 g_free (info->dir);
91 info->dir = NULL;
92 }
93
94 g_free (info);
95 }
96
97
98 /* -- FrChannelData -- */
99
100
101 static void
fr_channel_data_init(FrChannelData * channel)102 fr_channel_data_init (FrChannelData *channel)
103 {
104 channel->source = NULL;
105 channel->raw = NULL;
106 channel->status = G_IO_STATUS_NORMAL;
107 channel->error = NULL;
108 }
109
110
111 static void
fr_channel_data_close_source(FrChannelData * channel)112 fr_channel_data_close_source (FrChannelData *channel)
113 {
114 if (channel->source != NULL) {
115 g_io_channel_shutdown (channel->source, FALSE, NULL);
116 g_io_channel_unref (channel->source);
117 channel->source = NULL;
118 }
119 }
120
121
122 static GIOStatus
fr_channel_data_read(FrChannelData * channel)123 fr_channel_data_read (FrChannelData *channel)
124 {
125 char *line;
126 gsize length;
127 gsize terminator_pos;
128
129 channel->status = G_IO_STATUS_NORMAL;
130 g_clear_error (&channel->error);
131
132 while ((channel->status = g_io_channel_read_line (channel->source,
133 &line,
134 &length,
135 &terminator_pos,
136 &channel->error)) == G_IO_STATUS_NORMAL)
137 {
138 line[terminator_pos] = 0;
139 channel->raw = g_list_prepend (channel->raw, line);
140 if (channel->line_func != NULL)
141 (*channel->line_func) (line, channel->line_data);
142 }
143
144 return channel->status;
145 }
146
147
148 static GIOStatus
fr_channel_data_flush(FrChannelData * channel)149 fr_channel_data_flush (FrChannelData *channel)
150 {
151 GIOStatus status;
152
153 while (((status = fr_channel_data_read (channel)) != G_IO_STATUS_ERROR) && (status != G_IO_STATUS_EOF))
154 /* void */;
155 fr_channel_data_close_source (channel);
156
157 return status;
158 }
159
160
161 static void
fr_channel_data_reset(FrChannelData * channel)162 fr_channel_data_reset (FrChannelData *channel)
163 {
164 fr_channel_data_close_source (channel);
165
166 if (channel->raw != NULL) {
167 g_list_foreach (channel->raw, (GFunc) g_free, NULL);
168 g_list_free (channel->raw);
169 channel->raw = NULL;
170 }
171 }
172
173
174 static void
fr_channel_data_free(FrChannelData * channel)175 fr_channel_data_free (FrChannelData *channel)
176 {
177 fr_channel_data_reset (channel);
178 }
179
180
181 static void
fr_channel_data_set_fd(FrChannelData * channel,int fd,const char * charset)182 fr_channel_data_set_fd (FrChannelData *channel,
183 int fd,
184 const char *charset)
185 {
186 fr_channel_data_reset (channel);
187
188 channel->source = g_io_channel_unix_new (fd);
189 g_io_channel_set_flags (channel->source, G_IO_FLAG_NONBLOCK, NULL);
190 g_io_channel_set_buffer_size (channel->source, BUFFER_SIZE);
191 if (charset != NULL)
192 g_io_channel_set_encoding (channel->source, charset, NULL);
193 }
194
195
196 /* -- ExecData -- */
197
198
199 typedef struct {
200 FrProcess *process;
201 GCancellable *cancellable;
202 GSimpleAsyncResult *result;
203 gulong cancel_id;
204 int error_command; /* command that caused an error. */
205 FrError *error;
206 FrError *first_error;
207 GList *first_error_stdout;
208 GList *first_error_stderr;
209 } ExecuteData;
210
211
212 static void
execute_data_free(ExecuteData * exec_data)213 execute_data_free (ExecuteData *exec_data)
214 {
215 if (exec_data == NULL)
216 return;
217
218 if (exec_data->cancel_id != 0)
219 g_cancellable_disconnect (exec_data->cancellable, exec_data->cancel_id);
220
221 _g_object_unref (exec_data->process);
222 _g_object_unref (exec_data->cancellable);
223 _g_object_unref (exec_data->result);
224 fr_error_free (exec_data->error);
225 fr_error_free (exec_data->first_error);
226 _g_string_list_free (exec_data->first_error_stdout);
227 _g_string_list_free (exec_data->first_error_stderr);
228 g_free (exec_data);
229 }
230
231
232 /* -- FrProcess -- */
233
234
235 enum {
236 STICKY_ONLY,
237 LAST_SIGNAL
238 };
239
240
241 static guint fr_process_signals[LAST_SIGNAL] = { 0 };
242 static const char *try_charsets[] = { "UTF-8", "ISO-8859-1", "WINDOWS-1252" };
243 static int n_charsets = G_N_ELEMENTS (try_charsets);
244
245
246 struct _FrProcessPrivate {
247 GPtrArray *comm; /* FrCommandInfo elements. */
248 gint n_comm; /* total number of commands */
249 gint current_comm; /* currently editing command. */
250
251 GPid command_pid;
252 guint check_timeout;
253
254 gboolean running;
255 gboolean stopping;
256 gint current_command;
257
258 gboolean use_standard_locale;
259 gboolean sticky_only; /* whether to execute only sticky
260 * commands. */
261 int current_charset;
262
263 ExecuteData *exec_data;
264 };
265
G_DEFINE_TYPE_WITH_PRIVATE(FrProcess,fr_process,G_TYPE_OBJECT)266 G_DEFINE_TYPE_WITH_PRIVATE (FrProcess, fr_process, G_TYPE_OBJECT)
267
268
269 static void
270 fr_process_finalize (GObject *object)
271 {
272 FrProcess *process;
273
274 g_return_if_fail (object != NULL);
275 g_return_if_fail (FR_IS_PROCESS (object));
276
277 process = FR_PROCESS (object);
278
279 execute_data_free (process->priv->exec_data);
280 fr_process_clear (process);
281 g_ptr_array_free (process->priv->comm, FALSE);
282 fr_channel_data_free (&process->out);
283 fr_channel_data_free (&process->err);
284
285 if (G_OBJECT_CLASS (fr_process_parent_class)->finalize)
286 G_OBJECT_CLASS (fr_process_parent_class)->finalize (object);
287 }
288
289
290 static void
fr_process_class_init(FrProcessClass * klass)291 fr_process_class_init (FrProcessClass *klass)
292 {
293 GObjectClass *gobject_class;
294
295 fr_process_parent_class = g_type_class_peek_parent (klass);
296
297 fr_process_signals[STICKY_ONLY] =
298 g_signal_new ("sticky_only",
299 G_TYPE_FROM_CLASS (klass),
300 G_SIGNAL_RUN_LAST,
301 G_STRUCT_OFFSET (FrProcessClass, sticky_only),
302 NULL, NULL,
303 g_cclosure_marshal_VOID__VOID,
304 G_TYPE_NONE, 0);
305
306 gobject_class = G_OBJECT_CLASS (klass);
307 gobject_class->finalize = fr_process_finalize;
308 }
309
310
311 static void
fr_process_init(FrProcess * process)312 fr_process_init (FrProcess *process)
313 {
314 process->priv = fr_process_get_instance_private (process);
315
316 process->priv->comm = g_ptr_array_new ();
317 process->priv->n_comm = -1;
318 process->priv->current_comm = -1;
319
320 process->priv->command_pid = 0;
321 fr_channel_data_init (&process->out);
322 fr_channel_data_init (&process->err);
323
324 process->priv->check_timeout = 0;
325 process->priv->running = FALSE;
326 process->priv->stopping = FALSE;
327 process->restart = FALSE;
328
329 process->priv->current_charset = -1;
330 process->priv->use_standard_locale = FALSE;
331 process->priv->exec_data = NULL;
332 }
333
334
335 FrProcess *
fr_process_new(void)336 fr_process_new (void)
337 {
338 return FR_PROCESS (g_object_new (FR_TYPE_PROCESS, NULL));
339 }
340
341
342 void
fr_process_clear(FrProcess * process)343 fr_process_clear (FrProcess *process)
344 {
345 gint i;
346
347 g_return_if_fail (process != NULL);
348
349 for (i = 0; i <= process->priv->n_comm; i++) {
350 FrCommandInfo *info;
351
352 info = g_ptr_array_index (process->priv->comm, i);
353 fr_command_info_free (info);
354 g_ptr_array_index (process->priv->comm, i) = NULL;
355 }
356
357 for (i = 0; i <= process->priv->n_comm; i++)
358 g_ptr_array_remove_index_fast (process->priv->comm, 0);
359
360 process->priv->n_comm = -1;
361 process->priv->current_comm = -1;
362 }
363
364
365 void
fr_process_begin_command(FrProcess * process,const char * arg)366 fr_process_begin_command (FrProcess *process,
367 const char *arg)
368 {
369 FrCommandInfo *info;
370
371 g_return_if_fail (process != NULL);
372
373 info = fr_command_info_new ();
374 info->args = g_list_prepend (NULL, g_strdup (arg));
375
376 g_ptr_array_add (process->priv->comm, info);
377
378 process->priv->n_comm++;
379 process->priv->current_comm = process->priv->n_comm;
380 }
381
382
383 void
fr_process_begin_command_at(FrProcess * process,const char * arg,int index)384 fr_process_begin_command_at (FrProcess *process,
385 const char *arg,
386 int index)
387 {
388 FrCommandInfo *info, *old_c_info;
389
390 g_return_if_fail (process != NULL);
391 g_return_if_fail (index >= 0 && index <= process->priv->n_comm);
392
393 process->priv->current_comm = index;
394
395 old_c_info = g_ptr_array_index (process->priv->comm, index);
396
397 if (old_c_info != NULL)
398 fr_command_info_free (old_c_info);
399
400 info = fr_command_info_new ();
401 info->args = g_list_prepend (NULL, g_strdup (arg));
402
403 g_ptr_array_index (process->priv->comm, index) = info;
404 }
405
406
407 void
fr_process_set_working_dir(FrProcess * process,const char * dir)408 fr_process_set_working_dir (FrProcess *process,
409 const char *dir)
410 {
411 FrCommandInfo *info;
412
413 g_return_if_fail (process != NULL);
414 g_return_if_fail (process->priv->current_comm >= 0);
415
416 info = g_ptr_array_index (process->priv->comm, process->priv->current_comm);
417 if (info->dir != NULL)
418 g_free (info->dir);
419 info->dir = g_strdup (dir);
420 }
421
422
423 void
fr_process_set_working_dir_file(FrProcess * process,GFile * folder)424 fr_process_set_working_dir_file (FrProcess *process,
425 GFile *folder)
426 {
427 char *path;
428
429 path = g_file_get_path (folder);
430 fr_process_set_working_dir (process, path);
431
432 g_free (path);
433 }
434
435 void
fr_process_set_sticky(FrProcess * process,gboolean sticky)436 fr_process_set_sticky (FrProcess *process,
437 gboolean sticky)
438 {
439 FrCommandInfo *info;
440
441 g_return_if_fail (process != NULL);
442 g_return_if_fail (process->priv->current_comm >= 0);
443
444 info = g_ptr_array_index (process->priv->comm, process->priv->current_comm);
445 info->sticky = sticky;
446 }
447
448
449 void
fr_process_set_ignore_error(FrProcess * process,gboolean ignore_error)450 fr_process_set_ignore_error (FrProcess *process,
451 gboolean ignore_error)
452 {
453 FrCommandInfo *info;
454
455 g_return_if_fail (process != NULL);
456 g_return_if_fail (process->priv->current_comm >= 0);
457
458 info = g_ptr_array_index (process->priv->comm, process->priv->current_comm);
459 info->ignore_error = ignore_error;
460 }
461
462
463 void
fr_process_add_arg(FrProcess * process,const char * arg)464 fr_process_add_arg (FrProcess *process,
465 const char *arg)
466 {
467 FrCommandInfo *info;
468
469 g_return_if_fail (process != NULL);
470 g_return_if_fail (process->priv->current_comm >= 0);
471
472 info = g_ptr_array_index (process->priv->comm, process->priv->current_comm);
473 info->args = g_list_prepend (info->args, g_strdup (arg));
474 }
475
476
477 void
fr_process_add_arg_concat(FrProcess * process,const char * arg1,...)478 fr_process_add_arg_concat (FrProcess *process,
479 const char *arg1,
480 ...)
481 {
482 GString *arg;
483 va_list args;
484 char *s;
485
486 arg = g_string_new (arg1);
487
488 va_start (args, arg1);
489 while ((s = va_arg (args, char*)) != NULL)
490 g_string_append (arg, s);
491 va_end (args);
492
493 fr_process_add_arg (process, arg->str);
494 g_string_free (arg, TRUE);
495 }
496
497
498 void
fr_process_add_arg_printf(FrProcess * fr_proc,const char * format,...)499 fr_process_add_arg_printf (FrProcess *fr_proc,
500 const char *format,
501 ...)
502 {
503 va_list args;
504 char *arg;
505
506 va_start (args, format);
507 arg = g_strdup_vprintf (format, args);
508 va_end (args);
509
510 fr_process_add_arg (fr_proc, arg);
511
512 g_free (arg);
513 }
514
515
516 void
fr_process_add_arg_file(FrProcess * process,GFile * file)517 fr_process_add_arg_file (FrProcess *process,
518 GFile *file)
519 {
520 char *path;
521
522 path = g_file_get_path (file);
523 fr_process_add_arg (process, path);
524
525 g_free (path);
526 }
527
528
529 void
fr_process_set_arg_at(FrProcess * process,int n_comm,int n_arg,const char * arg_value)530 fr_process_set_arg_at (FrProcess *process,
531 int n_comm,
532 int n_arg,
533 const char *arg_value)
534 {
535 FrCommandInfo *info;
536 GList *arg;
537
538 g_return_if_fail (process != NULL);
539
540 info = g_ptr_array_index (process->priv->comm, n_comm);
541 arg = g_list_nth (info->args, n_arg);
542 g_return_if_fail (arg != NULL);
543
544 g_free (arg->data);
545 arg->data = g_strdup (arg_value);
546 }
547
548
549 void
fr_process_set_begin_func(FrProcess * process,ProcFunc func,gpointer func_data)550 fr_process_set_begin_func (FrProcess *process,
551 ProcFunc func,
552 gpointer func_data)
553 {
554 FrCommandInfo *info;
555
556 g_return_if_fail (process != NULL);
557
558 info = g_ptr_array_index (process->priv->comm, process->priv->current_comm);
559 info->begin_func = func;
560 info->begin_data = func_data;
561 }
562
563
564 void
fr_process_set_end_func(FrProcess * process,ProcFunc func,gpointer func_data)565 fr_process_set_end_func (FrProcess *process,
566 ProcFunc func,
567 gpointer func_data)
568 {
569 FrCommandInfo *info;
570
571 g_return_if_fail (process != NULL);
572
573 info = g_ptr_array_index (process->priv->comm, process->priv->current_comm);
574 info->end_func = func;
575 info->end_data = func_data;
576 }
577
578
579 void
fr_process_set_continue_func(FrProcess * process,ContinueFunc func,gpointer func_data)580 fr_process_set_continue_func (FrProcess *process,
581 ContinueFunc func,
582 gpointer func_data)
583 {
584 FrCommandInfo *info;
585
586 g_return_if_fail (process != NULL);
587
588 if (process->priv->current_comm < 0)
589 return;
590
591 info = g_ptr_array_index (process->priv->comm, process->priv->current_comm);
592 info->continue_func = func;
593 info->continue_data = func_data;
594 }
595
596
597 void
fr_process_end_command(FrProcess * process)598 fr_process_end_command (FrProcess *process)
599 {
600 FrCommandInfo *info;
601
602 g_return_if_fail (process != NULL);
603
604 info = g_ptr_array_index (process->priv->comm, process->priv->current_comm);
605 info->args = g_list_reverse (info->args);
606 }
607
608
609 void
fr_process_use_standard_locale(FrProcess * process,gboolean use_stand_locale)610 fr_process_use_standard_locale (FrProcess *process,
611 gboolean use_stand_locale)
612 {
613 g_return_if_fail (process != NULL);
614 process->priv->use_standard_locale = use_stand_locale;
615 }
616
617
618 void
fr_process_set_out_line_func(FrProcess * process,LineFunc func,gpointer data)619 fr_process_set_out_line_func (FrProcess *process,
620 LineFunc func,
621 gpointer data)
622 {
623 g_return_if_fail (process != NULL);
624
625 process->out.line_func = func;
626 process->out.line_data = data;
627 }
628
629
630 void
fr_process_set_err_line_func(FrProcess * process,LineFunc func,gpointer data)631 fr_process_set_err_line_func (FrProcess *process,
632 LineFunc func,
633 gpointer data)
634 {
635 g_return_if_fail (process != NULL);
636
637 process->err.line_func = func;
638 process->err.line_data = data;
639 }
640
641
642 /* fr_process_execute */
643
644
645 static gboolean
command_is_sticky(FrProcess * process,int i)646 command_is_sticky (FrProcess *process,
647 int i)
648 {
649 FrCommandInfo *info;
650
651 info = g_ptr_array_index (process->priv->comm, i);
652 return info->sticky;
653 }
654
655
656 static void
allow_sticky_processes_only(ExecuteData * exec_data)657 allow_sticky_processes_only (ExecuteData *exec_data)
658 {
659 FrProcess *process = exec_data->process;
660
661 if (! process->priv->sticky_only) {
662 /* Remember the first error. */
663
664 exec_data->error_command = process->priv->current_command;
665 exec_data->first_error = fr_error_copy (exec_data->error);
666 exec_data->first_error_stdout = g_list_reverse (_g_string_list_dup (process->out.raw));
667 exec_data->first_error_stderr = g_list_reverse (_g_string_list_dup (process->err.raw));
668 }
669
670 process->priv->sticky_only = TRUE;
671
672 if (! process->priv->stopping)
673 g_signal_emit (G_OBJECT (process),
674 fr_process_signals[STICKY_ONLY],
675 0);
676 }
677
678
679 static void
execute_cancelled_cb(GCancellable * cancellable,gpointer user_data)680 execute_cancelled_cb (GCancellable *cancellable,
681 gpointer user_data)
682 {
683 ExecuteData *exec_data = user_data;
684 FrProcess *process = exec_data->process;
685
686 if (! process->priv->running)
687 return;
688
689 if (process->priv->stopping)
690 return;
691
692 process->priv->stopping = TRUE;
693 exec_data->error = fr_error_new (FR_ERROR_STOPPED, 0, NULL);
694
695 if (command_is_sticky (process, process->priv->current_command))
696 allow_sticky_processes_only (exec_data);
697
698 else if (process->priv->command_pid > 0)
699 killpg (process->priv->command_pid, SIGTERM);
700
701 else {
702 if (process->priv->check_timeout != 0) {
703 g_source_remove (process->priv->check_timeout);
704 process->priv->check_timeout = 0;
705 }
706
707 process->priv->command_pid = 0;
708 fr_channel_data_close_source (&process->out);
709 fr_channel_data_close_source (&process->err);
710
711 process->priv->running = FALSE;
712
713 if (exec_data->cancel_id != 0) {
714 g_signal_handler_disconnect (exec_data->cancellable, exec_data->cancel_id);
715 exec_data->cancel_id = 0;
716 }
717 g_simple_async_result_complete_in_idle (exec_data->result);
718 }
719 }
720
721
722 static void _fr_process_start (ExecuteData *exec_data);
723
724
725 static void
_fr_process_restart(ExecuteData * exec_data)726 _fr_process_restart (ExecuteData *exec_data)
727 {
728 exec_data->process->restart = TRUE;
729 _fr_process_start (exec_data);
730 }
731
732
733 static void execute_current_command (ExecuteData *exec_data);
734
735
736 static void
child_setup(gpointer user_data)737 child_setup (gpointer user_data)
738 {
739 FrProcess *process = user_data;
740
741 if (process->priv->use_standard_locale)
742 putenv ("LC_MESSAGES=C");
743
744 /* detach from the tty */
745
746 setsid ();
747
748 /* create a process group to kill all the child processes when
749 * canceling the operation. */
750
751 setpgid (0, 0);
752 }
753
754
755 static const char *
_fr_process_get_charset(FrProcess * process)756 _fr_process_get_charset (FrProcess *process)
757 {
758 const char *charset = NULL;
759
760 if (process->priv->current_charset >= 0)
761 charset = try_charsets[process->priv->current_charset];
762 else if (g_get_charset (&charset))
763 charset = NULL;
764
765 return charset;
766 }
767
768
769 static void
_fr_process_execute_complete_in_idle(ExecuteData * exec_data)770 _fr_process_execute_complete_in_idle (ExecuteData *exec_data)
771 {
772 if (exec_data->cancel_id != 0) {
773 g_cancellable_disconnect (exec_data->cancellable, exec_data->cancel_id);
774 exec_data->cancel_id = 0;
775 }
776 g_simple_async_result_complete_in_idle (exec_data->result);
777 }
778
779
780 static gint
check_child(gpointer data)781 check_child (gpointer data)
782 {
783 ExecuteData *exec_data = data;
784 FrProcess *process = exec_data->process;
785 FrCommandInfo *info;
786 pid_t pid;
787 int status;
788 gboolean continue_process;
789
790 info = g_ptr_array_index (process->priv->comm, process->priv->current_command);
791
792 /* Remove check. */
793
794 g_source_remove (process->priv->check_timeout);
795 process->priv->check_timeout = 0;
796
797 if (fr_channel_data_read (&process->out) == G_IO_STATUS_ERROR) {
798 exec_data->error = fr_error_new (FR_ERROR_IO_CHANNEL, 0, process->out.error);
799 }
800 else if (fr_channel_data_read (&process->err) == G_IO_STATUS_ERROR) {
801 exec_data->error = fr_error_new (FR_ERROR_IO_CHANNEL, 0, process->err.error);
802 }
803 else {
804 pid = waitpid (process->priv->command_pid, &status, WNOHANG);
805 if (pid != process->priv->command_pid) {
806 /* Add check again. */
807 process->priv->check_timeout = g_timeout_add (REFRESH_RATE,
808 check_child,
809 exec_data);
810 return FALSE;
811 }
812 }
813
814 if (info->ignore_error && (exec_data->error != NULL)) {
815 #ifdef DEBUG
816 {
817 GList *scan;
818
819 g_print ("** ERROR **\n");
820 g_print ("%s\n", exec_data->error->gerror->message);
821 for (scan = process->err.raw; scan; scan = scan->next)
822 g_print ("%s\n", (char *)scan->data);
823 }
824 #endif
825 fr_clear_error (&exec_data->error);
826 debug (DEBUG_INFO, "[error ignored]\n");
827 }
828 else if (exec_data->error == NULL) {
829 if (WIFEXITED (status)) {
830 if (WEXITSTATUS (status) == 255) {
831 exec_data->error = fr_error_new (FR_ERROR_COMMAND_NOT_FOUND, 0, NULL);
832 }
833 else if (WEXITSTATUS (status) != 0) {
834 exec_data->error = fr_error_new (FR_ERROR_COMMAND_ERROR, WEXITSTATUS (status), NULL);
835 }
836 }
837 else {
838 exec_data->error = fr_error_new (FR_ERROR_EXITED_ABNORMALLY, 255, NULL);
839 }
840 }
841
842 process->priv->command_pid = 0;
843
844 if (exec_data->error == NULL) {
845 if (fr_channel_data_flush (&process->out) == G_IO_STATUS_ERROR)
846 exec_data->error = fr_error_new (FR_ERROR_IO_CHANNEL, 0, process->out.error);
847 else if (fr_channel_data_flush (&process->err) == G_IO_STATUS_ERROR)
848 exec_data->error = fr_error_new (FR_ERROR_IO_CHANNEL, 0, process->err.error);
849 }
850
851 if (info->end_func != NULL)
852 (*info->end_func) (info->end_data);
853
854 /**/
855
856 if ((exec_data->error != NULL)
857 && (exec_data->error->type == FR_ERROR_IO_CHANNEL)
858 && g_error_matches (exec_data->error->gerror, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
859 {
860 if (process->priv->current_charset < n_charsets - 1) {
861 /* try with another charset */
862 process->priv->current_charset++;
863 _fr_process_restart (exec_data);
864 return FALSE;
865 }
866 fr_error_free (exec_data->error);
867 exec_data->error = fr_error_new (FR_ERROR_BAD_CHARSET, 0, exec_data->error->gerror);
868 }
869
870 /* Check whether to continue or stop the process */
871
872 continue_process = TRUE;
873 if (info->continue_func != NULL)
874 continue_process = (*info->continue_func) (&exec_data->error, info->continue_data);
875
876 /* Execute the next command. */
877 if (continue_process) {
878 if (exec_data->error != NULL) {
879 allow_sticky_processes_only (exec_data);
880 #ifdef DEBUG
881 {
882 GList *scan;
883
884 g_print ("** ERROR **\n");
885 for (scan = process->err.raw; scan; scan = scan->next)
886 g_print ("%s\n", (char *)scan->data);
887 }
888 #endif
889 }
890
891 if (process->priv->sticky_only) {
892 do {
893 process->priv->current_command++;
894 }
895 while ((process->priv->current_command <= process->priv->n_comm)
896 && ! command_is_sticky (process, process->priv->current_command));
897 }
898 else
899 process->priv->current_command++;
900
901 if (process->priv->current_command <= process->priv->n_comm) {
902 execute_current_command (exec_data);
903 return FALSE;
904 }
905 }
906
907 /* Done */
908
909 process->priv->current_command = -1;
910 process->priv->use_standard_locale = FALSE;
911
912 if (process->out.raw != NULL)
913 process->out.raw = g_list_reverse (process->out.raw);
914 if (process->err.raw != NULL)
915 process->err.raw = g_list_reverse (process->err.raw);
916
917 process->priv->running = FALSE;
918 process->priv->stopping = FALSE;
919
920 if (process->priv->sticky_only) {
921 /* Restore the first error. */
922
923 fr_error_free (exec_data->error);
924 exec_data->error = fr_error_copy (exec_data->first_error);
925
926 /* Restore the first error output as well. */
927
928 _g_string_list_free (process->out.raw);
929 process->out.raw = exec_data->first_error_stdout;
930 exec_data->first_error_stdout = NULL;
931
932 _g_string_list_free (process->err.raw);
933 process->err.raw = exec_data->first_error_stderr;
934 exec_data->first_error_stderr = NULL;
935 }
936
937 _fr_process_execute_complete_in_idle (exec_data);
938
939 return FALSE;
940 }
941
942
943 static void
execute_current_command(ExecuteData * exec_data)944 execute_current_command (ExecuteData *exec_data)
945 {
946 FrProcess *process = exec_data->process;
947 FrCommandInfo *info;
948 GList *scan;
949 char **argv;
950 int out_fd, err_fd;
951 int i = 0;
952 GError *error = NULL;
953
954 debug (DEBUG_INFO, "%d/%d) ", process->priv->current_command, process->priv->n_comm);
955
956 info = g_ptr_array_index (process->priv->comm, process->priv->current_command);
957
958 argv = g_new (char *, g_list_length (info->args) + 1);
959 for (scan = info->args; scan; scan = scan->next)
960 argv[i++] = scan->data;
961 argv[i] = NULL;
962
963 #ifdef DEBUG
964 {
965 int j;
966
967 if (process->priv->use_standard_locale)
968 g_print ("\tLC_MESSAGES=C\n");
969
970 if (info->dir != NULL)
971 g_print ("\tcd %s\n", info->dir);
972
973 if (info->ignore_error)
974 g_print ("\t[ignore error]\n");
975
976 g_print ("\t");
977 for (j = 0; j < i; j++)
978 g_print ("%s ", argv[j]);
979 g_print ("\n");
980 }
981 #endif
982
983 if (info->begin_func != NULL)
984 (*info->begin_func) (info->begin_data);
985
986 if (! g_spawn_async_with_pipes (info->dir,
987 argv,
988 NULL,
989 (G_SPAWN_LEAVE_DESCRIPTORS_OPEN
990 | G_SPAWN_SEARCH_PATH
991 | G_SPAWN_DO_NOT_REAP_CHILD),
992 child_setup,
993 process,
994 &process->priv->command_pid,
995 NULL,
996 &out_fd,
997 &err_fd,
998 &error))
999 {
1000 exec_data->error = fr_error_new (FR_ERROR_SPAWN, 0, error);
1001 _fr_process_execute_complete_in_idle (exec_data);
1002
1003 g_error_free (error);
1004 g_free (argv);
1005 return;
1006 }
1007
1008 g_free (argv);
1009
1010 fr_channel_data_set_fd (&process->out, out_fd, _fr_process_get_charset (process));
1011 fr_channel_data_set_fd (&process->err, err_fd, _fr_process_get_charset (process));
1012
1013 process->priv->check_timeout = g_timeout_add (REFRESH_RATE,
1014 check_child,
1015 exec_data);
1016 }
1017
1018
1019 static void
_fr_process_start(ExecuteData * exec_data)1020 _fr_process_start (ExecuteData *exec_data)
1021 {
1022 FrProcess *process = exec_data->process;
1023
1024 _g_string_list_free (exec_data->first_error_stdout);
1025 exec_data->first_error_stdout = NULL;
1026
1027 _g_string_list_free (exec_data->first_error_stderr);
1028 exec_data->first_error_stderr = NULL;
1029
1030 fr_error_free (exec_data->error);
1031 exec_data->error = NULL;
1032
1033 fr_channel_data_reset (&process->out);
1034 fr_channel_data_reset (&process->err);
1035
1036 process->priv->sticky_only = FALSE;
1037 process->priv->current_command = 0;
1038 process->priv->stopping = FALSE;
1039
1040 if (process->priv->n_comm == -1) {
1041 process->priv->running = FALSE;
1042 _fr_process_execute_complete_in_idle (exec_data);
1043 }
1044 else {
1045 process->priv->running = TRUE;
1046 execute_current_command (exec_data);
1047 }
1048 }
1049
1050
1051 void
fr_process_execute(FrProcess * process,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1052 fr_process_execute (FrProcess *process,
1053 GCancellable *cancellable,
1054 GAsyncReadyCallback callback,
1055 gpointer user_data)
1056 {
1057 ExecuteData *exec_data;
1058
1059 g_return_if_fail (! process->priv->running);
1060
1061 execute_data_free (process->priv->exec_data);
1062
1063 process->priv->exec_data = exec_data = g_new0 (ExecuteData, 1);
1064 exec_data->process = g_object_ref (process);
1065 exec_data->cancellable = _g_object_ref (cancellable);
1066 exec_data->cancel_id = 0;
1067 exec_data->result = g_simple_async_result_new (G_OBJECT (process),
1068 callback,
1069 user_data,
1070 fr_process_execute);
1071
1072 g_simple_async_result_set_op_res_gpointer (exec_data->result, exec_data, NULL);
1073
1074 if (! process->restart)
1075 process->priv->current_charset = -1;
1076
1077 if (cancellable != NULL) {
1078 GError *error = NULL;
1079
1080 if (g_cancellable_set_error_if_cancelled (cancellable, &error)) {
1081 exec_data->error = fr_error_new (FR_ERROR_STOPPED, 0, error);
1082 _fr_process_execute_complete_in_idle (exec_data);
1083
1084 g_error_free (error);
1085 return;
1086 }
1087
1088 exec_data->cancel_id = g_cancellable_connect (cancellable,
1089 G_CALLBACK (execute_cancelled_cb),
1090 exec_data,
1091 NULL);
1092 }
1093
1094 _fr_process_start (exec_data);
1095 }
1096
1097
1098 gboolean
fr_process_execute_finish(FrProcess * process,GAsyncResult * result,FrError ** error)1099 fr_process_execute_finish (FrProcess *process,
1100 GAsyncResult *result,
1101 FrError **error)
1102 {
1103 ExecuteData *exec_data;
1104
1105 g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (process), fr_process_execute), FALSE);
1106
1107 exec_data = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
1108 if (exec_data->error == NULL)
1109 return TRUE;
1110
1111 if (error != NULL) {
1112 if (exec_data->error->gerror == NULL)
1113 exec_data->error->gerror = g_error_new_literal (FR_ERROR, exec_data->error->type, "");
1114 *error = fr_error_copy (exec_data->error);
1115 }
1116
1117 return FALSE;
1118 }
1119
1120
1121 void
fr_process_restart(FrProcess * process)1122 fr_process_restart (FrProcess *process)
1123 {
1124 if (process->priv->exec_data != NULL)
1125 _fr_process_start (process->priv->exec_data);
1126 }
1127
1128
1129 void
fr_process_cancel(FrProcess * process)1130 fr_process_cancel (FrProcess *process)
1131 {
1132 if (! process->priv->running)
1133 return;
1134 if (process->priv->exec_data == NULL)
1135 return;
1136 if (process->priv->exec_data->cancellable == NULL)
1137 return;
1138 g_cancellable_cancel (process->priv->exec_data->cancellable);
1139 }
1140