1 /*
2 ** 1998-09-25 - This module deals with child processes. Cut out from the old commands module,
3 ** which really was kind of bloated.
4 ** 1998-12-15 - Rewritten. Now uses the POSIX sigaction() API, which perhaps is more portable.
5 ** I've been noticing some odd problems on Solaris, with "infinite signals" etc.
6 ** I found my (virtual) copy of the Linux Programmer's Guide, and it makes me
7 ** believe that POSIX signals are reliable by default (i.e. the handler need not
8 ** be reinstalled). New problem: possible interruption of system calls. :(
9 ** 1999-03-31 - Made this module a lot more self-contained.
10 ** 1999-04-08 - Now waitpid()s for the child after chd_kill_child(), which really is better.
11 */
12
13 #include "gentoo.h"
14
15 #if !defined _POSIX_SOURCE
16 #define _POSIX_SOURCE
17 #endif
18
19 #include <stdlib.h>
20 #include <signal.h>
21 #include <sys/types.h>
22 #include <sys/wait.h>
23
24 #include "dialog.h"
25 #include "strutil.h"
26 #include "queue.h"
27 #include "children.h"
28
29 /* ----------------------------------------------------------------------------------------- */
30
31 typedef struct { /* Information about a single child process. */
32 GPid pid;
33 guint watch; /* Glib source associated with this child. */
34 gchar prog[MAXNAMLEN]; /* Name of program running as this child (argv[0]). */
35 guint32 gflags; /* The general flags for the command. */
36 guint32 aflags; /* After-flags to associate with this child. */
37 } Child;
38
39 typedef struct { /* Holds data about child processes we have spawned. */
40 MainInfo *min; /* Very handy to have around. Keep first! */
41 GSList *child_list; /* List of running processes. */
42 GPid lock_pid; /* Pid of a command that we want to wait for. */
43 CmdSeq *running; /* If non-null, we're currently running this sequence. */
44 guint index; /* When 'running' is set, this is the next position to run. */
45 } ChildInfo;
46
47 static ChildInfo the_chi = { NULL };
48
49 /* ----------------------------------------------------------------------------------------- */
50
51 static gboolean chd_unregister(const gchar *name);
52
53 /* ----------------------------------------------------------------------------------------- */
54
55 /* 1999-04-08 - Rewritten, now simply initializes our private ChildInfo. */
chd_initialize(MainInfo * min)56 gboolean chd_initialize(MainInfo *min)
57 {
58 if(the_chi.min == NULL)
59 {
60 the_chi.min = min;
61 the_chi.child_list = NULL;
62 the_chi.lock_pid = -1;
63 the_chi.running = NULL;
64 the_chi.index = 0;
65
66 return TRUE;
67 }
68 return FALSE;
69 }
70
71 /* ----------------------------------------------------------------------------------------- */
72
73 /* 2010-03-08 - A child process has tragically died, and needs to be reaped. */
cb_watch_child(GPid pid,gint status,gpointer user)74 static void cb_watch_child(GPid pid, gint status, gpointer user)
75 {
76 ChildInfo *chi = &the_chi;
77 Child *ch = user;
78
79 /* Unlink from rest of GTK+, as soon as possible, to be safe. */
80 chi->child_list = g_slist_remove(chi->child_list, ch);
81 g_source_remove(ch->watch);
82
83 /* Now handle exit status. */
84 if(WIFEXITED(status) && WEXITSTATUS(status) == CHD_EXIT_FAILURE)
85 {
86 gchar buf[1024];
87
88 g_snprintf(buf, sizeof buf, _("Execution of \"%s\" Failed"), ch->prog);
89 dlg_dialog_sync_new_simple_wait(buf, _("Error"), _("_OK"));
90 }
91 if(chi->lock_pid == ch->pid) /* Running in foreground? */
92 que_enqueue(chi->min, QEVT_CONTINUE_CMD);
93 que_enqueue(chi->min, QEVT_END_CMD, ch->aflags);
94
95 g_free(ch);
96 }
97
98 /* ----------------------------------------------------------------------------------------- */
99
100 /* 1998-09-09 - Register a child process started by a user command. Keeps not only
101 ** the <pid>. This information is used to kill previous instance(s).
102 ** 1998-09-10 - Now takes an additional parameter, <lock>. If set, the GUI is locked.
103 ** 1998-09-18 - This routine is now exported for use by other code (the new "file" module).
104 */
chd_register(const gchar * prog,GPid pid,guint32 gflags,guint32 aflags)105 void chd_register(const gchar *prog, GPid pid, guint32 gflags, guint32 aflags)
106 {
107 ChildInfo *chi = &the_chi;
108 Child *ch;
109
110 ch = g_malloc(sizeof *ch);
111 g_strlcpy(ch->prog, prog, sizeof ch->prog);
112 ch->pid = pid;
113 ch->gflags = gflags;
114 ch->aflags = aflags;
115 chi->child_list = g_slist_append(chi->child_list, ch);
116 if(!(gflags & CGF_RUNINBG))
117 {
118 chi->lock_pid = pid;
119 gtk_widget_set_sensitive(the_chi.min->gui->window, FALSE);
120 }
121 ch->watch = g_child_watch_add(ch->pid, cb_watch_child, ch);
122 }
123
124 /* 1999-04-08 - Unregister the child named <name>. This is not meant for public consumption,
125 ** hence it's static. Returns TRUE if a child was found, else FALSE.
126 ** NOTE NOTE This function doesn't kill any processes or otherwise affect the system's
127 ** process table: it just removes the internal data.
128 */
chd_unregister(const gchar * name)129 static gboolean chd_unregister(const gchar *name)
130 {
131 GSList *iter;
132 Child *ch;
133 ChildInfo *chi = &the_chi;
134
135 for(iter = chi->child_list; iter != NULL; iter = g_slist_next(iter))
136 {
137 ch = iter->data;
138 if(strcmp(ch->prog, name) == 0)
139 {
140 chi->child_list = g_slist_remove_link(chi->child_list, iter);
141 g_slist_free_1(iter);
142 g_source_remove(ch->watch);
143 g_free(ch);
144 return TRUE;
145 }
146 }
147 return FALSE;
148 }
149
150 /* 1999-03-31 - Set the continuation information. */
chd_set_running(CmdSeq * cs,guint index)151 void chd_set_running(CmdSeq *cs, guint index)
152 {
153 the_chi.running = cs;
154 the_chi.index = index;
155 }
156
157 /* 1999-03-31 - Get the currently running command sequence. If non-NULL, <index> will
158 ** receive the index of the next row to run.
159 */
chd_get_running(guint * index)160 CmdSeq * chd_get_running(guint *index)
161 {
162 if(index != NULL)
163 *index = the_chi.index;
164 return the_chi.running;
165 }
166
167 /* 1999-03-31 - Clear continuation info (since the sequence has finished). */
chd_clear_running(void)168 void chd_clear_running(void)
169 {
170 the_chi.running = NULL;
171 the_chi.index = 0;
172 }
173
174 /* 1999-03-31 - Just clear the locking child PID (since it just died, typically). */
chd_clear_lock(void)175 void chd_clear_lock(void)
176 {
177 the_chi.lock_pid = -1;
178 }
179
180 /* 1998-09-28 - Kill (all) running instances of command named <name>. */
chd_kill_child(const gchar * name)181 void chd_kill_child(const gchar *name)
182 {
183 GSList *iter, *next;
184 Child *ch;
185 guint rcount = 0U;
186 pid_t pid;
187
188 for(iter = the_chi.child_list; iter != NULL; iter = next)
189 {
190 next = g_slist_next(iter);
191 ch = iter->data;
192 if(strcmp(ch->prog, name) == 0)
193 {
194 pid = ch->pid; /* Buffer in case sighandler g_free()s it. */
195 if(kill(pid, SIGTERM) == 0)
196 {
197 int ret;
198
199 if((ret = waitpid(pid, NULL, 0)) == pid)
200 rcount++;
201 }
202 else
203 perror("CHILDREN: kill() failed");
204 }
205 }
206 while(rcount && chd_unregister(name))
207 rcount--;
208 }
209
210 /* 1998-05-26 - Kill any child processes (left over from running asynchronous commands).
211 ** 1998-09-09 - Rewritten due a more advanced child data format.
212 ** 1998-10-11 - Moved into the children module, where it belongs.
213 */
chd_kill_children(void)214 void chd_kill_children(void)
215 {
216 GSList *iter, *next;
217 Child *ch;
218
219 for(iter = the_chi.child_list; iter != NULL; iter = next)
220 {
221 next = g_slist_next(iter);
222 ch = iter->data;
223 if(ch->gflags & CGF_SURVIVE)
224 continue;
225 g_source_remove(ch->watch);
226 if(kill(ch->pid, SIGTERM) != 0)
227 g_warning(_("Couldn't terminate child \"%s\" (pid=%d)--zombie alert"), ch->prog, (gint) ch->pid);
228 else
229 {
230 waitpid(ch->pid, NULL, 0);
231 the_chi.child_list = g_slist_remove(the_chi.child_list, ch);
232 g_free(ch);
233 }
234 }
235 }
236