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