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