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