1 /* logjam - a GTK client for LiveJournal.
3 * Functions to run some external program and return its output.
4 * Copyright (C) 2004, Kir Kolyshkin <kir@sacred.ru>
13 #include <sys/signal.h>
14 #include <sys/types.h>
25 /* An analogue of popen(command, "r").
26 * A bit more useful since we know child PID.
28 gboolean
mypopen_r (const char *command
, struct mypopen_t
*p
) {
30 /* parent fd is fds[0], child fd is fds[1] */
31 if (pipe(fds
) == -1) return FALSE
;
32 if ((p
->pid
= fork()) == -1) {
42 execl("/bin/sh", "sh", "-c", command
, NULL
);
43 /* if execl failed, exit with code 127 */
53 int mypclose (const struct mypopen_t
*p
) {
55 /*fprintf(stderr, "mypclose: pid=%lu, fd=%d\n", p->pid, p->fd);*/
58 ret
= waitpid(p
->pid
, &status
, 0);
59 } while ((ret
== -1) && (errno
== EINTR
));
60 /*fprintf(stderr, "waitpid returned %d; errno = %d; status = %d; exit = %d; signaled = %d\n", ret, errno, status, WEXITSTATUS(status), WIFSIGNALED(status));*/
61 if (ret
== -1) return -1;
62 return WEXITSTATUS(status
);
66 /* Callback function for "Cancel" button of 'command is running' window */
67 static void cancel_cb (struct mypopen_t
*p
) {
69 if (p
->pid
) kill(p
->pid
, SIGTERM
);
70 /* FIXME: should we send SIGKILL some time later if process still exists? */
74 /* Dialog showing that command is being executed.
75 * Click on "Cancel" should kill the process which pid is p->pid
77 GtkWidget
*command_is_running (const char *command
, struct mypopen_t
*p
, GtkWindow
*parent
) {
78 GtkWidget
*win
, *vbox
, *frame
, *text
, *cancel
;
80 win
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
81 gtk_window_set_title(GTK_WINDOW(win
), _("Command is running..."));
82 gtk_window_set_transient_for(GTK_WINDOW(win
), parent
);
83 gtk_window_set_decorated(GTK_WINDOW(win
), FALSE
);
84 gtk_window_set_type_hint(GTK_WINDOW(win
), GDK_WINDOW_TYPE_HINT_DIALOG
);
85 gtk_window_set_modal(GTK_WINDOW(win
), TRUE
);
86 gtk_window_set_default_size(GTK_WINDOW(win
), 250, -1);
87 gtk_window_set_position(GTK_WINDOW(win
), GTK_WIN_POS_CENTER_ON_PARENT
);
89 frame
= gtk_frame_new(NULL
);
90 gtk_frame_set_shadow_type(GTK_FRAME(frame
), GTK_SHADOW_OUT
);
91 gtk_container_add(GTK_CONTAINER(win
), frame
);
93 vbox
= gtk_vbox_new(FALSE
, 5);
94 gtk_container_set_border_width(GTK_CONTAINER(vbox
), 5);
95 gtk_container_add(GTK_CONTAINER(frame
), vbox
);
97 text
= gtk_label_new(_("Command is running..."));
98 gtk_box_pack_start(GTK_BOX(vbox
), text
, TRUE
, TRUE
, 0);
100 cancel
= gtk_button_new_from_stock(GTK_STOCK_CANCEL
);
101 gtk_box_pack_start(GTK_BOX(vbox
), cancel
, FALSE
, FALSE
, 0);
102 g_signal_connect_swapped(G_OBJECT(cancel
), "clicked", G_CALLBACK(cancel_cb
), p
);
104 gtk_widget_show_all(win
);
109 /* Callback function for gtk_input_add_full(). Reads data from the
110 * file descriptor. Calls gtk_quit if there's nothing to read.
112 void pipe_read_cb (gpointer data
, gint source
, GdkInputCondition condition
) {
115 c
= read(source
, buf
, sizeof(buf
));
116 if (c
== 0) gtk_main_quit(); /* EOF */
117 else g_string_append_len((GString
*)data
, buf
, c
);
121 GString
*get_command_output (const char *command
, GError
**err
, GtkWindow
*parent
) {
128 pd
.cancelled
= FALSE
;
129 if (mypopen_r(command
, &pd
) == FALSE
) {
130 g_set_error(err
, 0, 0, _("command failed: %s"), g_strerror(errno
));
134 /*fprintf(stderr, "before read: pid=%lu, fd=%d\n", pd.pid, pd.fd);*/
135 output
= g_string_sized_new(BUFSIZ
);
136 evid
= gtk_input_add_full(pd
.fd
, GDK_INPUT_READ
, pipe_read_cb
, NULL
, output
, NULL
);
138 win
= command_is_running(command
, &pd
, parent
);
141 gtk_input_remove(evid
);
142 gtk_widget_destroy(win
);
145 g_string_free(output
, TRUE
);
148 /* Positive 'ret' values are command exit code, while negative one
149 * means execution error or termination.
150 * Let's ignore all non-zero exit codes but 127, since 127
151 * means "command not found" from sh. Ugly hack?
154 g_string_free(output
, TRUE
);
155 g_set_error(err
, 0, 0, _("command failed: %s"), g_strerror(errno
));
157 } else if (ret
== 127) {
158 g_string_free(output
, TRUE
);
159 /* Set err->code to 127 so caller will know that command
160 * was not found. It is probably a spelling error
161 * in command string and in this case caller should not
162 * close a dialog asking for command name, but rather let
163 * the user correct it (or click "Cancel").
165 g_set_error(err
, 0, 127, "%s", _("command not found"));