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>
6 * vim: tabstop=4 shiftwidth=4 noexpandtab :
15 #include <sys/types.h>
17 #include <sys/signal.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
)
31 /* parent fd is fds[0], child fd is fds[1] */
36 if ((p
->pid
= fork()) == -1)
48 execl("/bin/sh", "sh", "-c", command
, NULL
);
49 /* if execl failed, exit with code 127 */
58 int mypclose(const struct mypopen_t
*p
)
62 /* fprintf(stderr, "mypclose: pid=%lu, fd=%d\n", p->pid, p->fd); */
66 ret
= waitpid(p
->pid
, &status
, 0);
67 } while ((ret
== -1) && (errno
== EINTR
));
69 /* fprintf(stderr, "waitpid returned %d; errno = %d; "
70 "status = %d; exit = %d; signaled = %d\n",
71 ret, errno, status, WEXITSTATUS(status),
72 WIFSIGNALED(status)); */
75 return WEXITSTATUS(status
);
78 /* Callback function for "Cancel" button of 'command is running' window
81 cancel_cb(struct mypopen_t
*p
) {
84 kill(p
->pid
, SIGTERM
);
85 /* FIXME: should we send SIGKILL some time later
86 * if process still exists?
90 /* Dialog showing that command is being executed.
91 * Click on "Cancel" should kill the process which pid is p->pid
93 GtkWidget
*command_is_running(const char * command
, struct mypopen_t
*p
,
95 GtkWidget
*win
, *vbox
, *frame
, *text
, *cancel
;
97 win
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
98 gtk_window_set_title(GTK_WINDOW(win
), _("Command is running..."));
99 gtk_window_set_transient_for(GTK_WINDOW(win
), parent
);
100 gtk_window_set_decorated(GTK_WINDOW(win
), FALSE
);
101 gtk_window_set_type_hint(GTK_WINDOW(win
), GDK_WINDOW_TYPE_HINT_DIALOG
);
102 gtk_window_set_modal(GTK_WINDOW(win
), TRUE
);
103 gtk_window_set_default_size(GTK_WINDOW(win
), 250, -1);
104 gtk_window_set_position(GTK_WINDOW(win
), GTK_WIN_POS_CENTER_ON_PARENT
);
106 frame
= gtk_frame_new(NULL
);
107 gtk_frame_set_shadow_type(GTK_FRAME(frame
), GTK_SHADOW_OUT
);
108 gtk_container_add(GTK_CONTAINER(win
), frame
);
110 vbox
= gtk_vbox_new(FALSE
, 5);
111 gtk_container_set_border_width(GTK_CONTAINER(vbox
), 5);
112 gtk_container_add(GTK_CONTAINER(frame
), vbox
);
114 text
= gtk_label_new(_("Command is running..."));
115 gtk_box_pack_start(GTK_BOX(vbox
), text
, TRUE
, TRUE
, 0);
117 cancel
= gtk_button_new_from_stock(GTK_STOCK_CANCEL
);
118 gtk_box_pack_start(GTK_BOX(vbox
), cancel
, FALSE
, FALSE
, 0);
119 g_signal_connect_swapped(G_OBJECT(cancel
), "clicked",
120 G_CALLBACK(cancel_cb
), p
);
122 gtk_widget_show_all(win
);
126 /* Callback function for gtk_input_add_full(). Reads data from the
127 * file descriptor. Calls gtk_quit if there's nothing to read.
129 void pipe_read_cb(gpointer data
, gint source
, GdkInputCondition condition
) {
133 c
= read(source
, buf
, sizeof(buf
));
134 if (c
== 0) /* EOF */
137 g_string_append_len((GString
*)data
, buf
, c
);
140 GString
* get_command_output(const char *command
, GError
**err
,
148 pd
.cancelled
= FALSE
;
149 if (mypopen_r(command
, &pd
) == FALSE
) {
150 g_set_error(err
, 0, 0, _("command failed: %s"),
155 /* fprintf(stderr, "before read: pid=%lu, fd=%d\n", pd.pid, pd.fd); */
156 output
= g_string_sized_new(BUFSIZ
);
157 evid
= gtk_input_add_full(pd
.fd
, GDK_INPUT_READ
,
158 pipe_read_cb
, NULL
, output
, NULL
);
160 win
= command_is_running(command
, &pd
, parent
);
163 gtk_input_remove(evid
);
164 gtk_widget_destroy(win
);
168 g_string_free(output
, TRUE
);
171 /* Positive 'ret' values are command exit code, while negative one
172 * means execution error or termination.
173 * Let's ignore all non-zero exit codes but 127, since 127
174 * means "command not found" from sh. Ugly hack?
177 g_string_free(output
, TRUE
);
178 g_set_error(err
, 0, 0, _("command failed: %s"),
181 } else if (ret
== 127)
183 g_string_free(output
, TRUE
);
184 /* Set err->code to 127 so caller will know that command
185 * was not found. It is probably a spelling error
186 * in command string and in this case caller should not
187 * close a dialog asking for command name, but rather let
188 * the user correct it (or click "Cancel").
190 g_set_error(err
, 0, 127, _("command not found"));