first try to journal_store_get_latest_id() for sqlite
[k8lowj.git] / src / get_cmd_out.c
blob2331ab5ca9b1fdf2306f7497638685ccf17bc767
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>
5 */
6 #include "gtk-all.h"
8 #include <errno.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <unistd.h>
13 #include <sys/signal.h>
14 #include <sys/types.h>
15 #include <sys/wait.h>
18 struct mypopen_t {
19 int fd;
20 pid_t pid;
21 gboolean cancelled;
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) {
29 int fds[2];
30 /* parent fd is fds[0], child fd is fds[1] */
31 if (pipe(fds) == -1) return FALSE;
32 if ((p->pid = fork()) == -1) {
33 close(fds[0]);
34 close(fds[1]);
35 return FALSE;
37 if (p->pid == 0) {
38 /* Child */
39 close(fds[0]);
40 dup2(fds[1], 1);
41 close(fds[1]);
42 execl("/bin/sh", "sh", "-c", command, NULL);
43 /* if execl failed, exit with code 127 */
44 exit(127);
46 /* Parent */
47 close(fds[1]);
48 p->fd = fds[0];
49 return TRUE;
53 int mypclose (const struct mypopen_t *p) {
54 int ret, status;
55 /*fprintf(stderr, "mypclose: pid=%lu, fd=%d\n", p->pid, p->fd);*/
56 close(p->fd);
57 do {
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) {
68 p->cancelled = TRUE;
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);
105 return 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) {
113 gchar buf[BUFSIZ];
114 ssize_t c;
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) {
122 GtkWidget *win;
123 GString *output;
124 int ret;
125 guint evid;
126 struct mypopen_t pd;
128 pd.cancelled = FALSE;
129 if (mypopen_r(command, &pd) == FALSE) {
130 g_set_error(err, 0, 0, _("command failed: %s"), g_strerror(errno));
131 return NULL;
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);
139 gtk_main();
141 gtk_input_remove(evid);
142 gtk_widget_destroy(win);
143 ret = mypclose(&pd);
144 if (pd.cancelled) {
145 g_string_free(output, TRUE);
146 return NULL;
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?
153 if (ret < 0) {
154 g_string_free(output, TRUE);
155 g_set_error(err, 0, 0, _("command failed: %s"), g_strerror(errno));
156 return NULL;
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"));
166 return NULL;
168 return output;