1 /*
2 **
3 ** process.c
4 **
5 ** Copyright (C) 1996, 1997 Johannes Plass
6 ** Copyright (C) 2004 Jose E. Marchesi
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 3 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 GNU gv; see the file COPYING.  If not, write to
20 ** the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 ** Boston, MA 02111-1307, USA.
22 **
23 ** Author:   Johannes Plass (plass@thep.physik.uni-mainz.de)
24 **           Department of Physics
25 **           Johannes Gutenberg-University
26 **           Mainz, Germany
27 **
28 **           Jose E. Marchesi (jemarch@gnu.org)
29 **           GNU Project
30 **
31 */
32 #include "ac_config.h"
33 
34 /*
35 #define MESSAGES
36 */
37 #include "message.h"
38 
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <ctype.h>
42 
43 
44 #   include <sys/types.h>
45 #   include <sys/wait.h>
46 #   include <unistd.h>
47 #   include <signal.h>
48 #   include <gv_signal.h>
49 
50 #include "paths.h"
51 #include INC_X11(Intrinsic.h)
52 #include INC_X11(StringDefs.h)
53 #include INC_XAW(MenuButton.h)
54 #include INC_XAW(SimpleMenu.h)
55 #include INC_XAW(SmeBSB.h)
56 #include INC_X11(IntrinsicP.h)
57 #include INC_X11(ShellP.h)
58 
59 #include "types.h"
60 #include "config.h"
61 #include "callbacks.h"
62 #include "process.h"
63 #include "main_resources.h"
64 #include "main_globals.h"
65 #include "misc_private.h"
66 
67 #include "resource.h"
68 
69 #define CHECK_PERIOD 500
70 #define ADD_TIMEOUT(widget,interval,data)       \
71         XtAppAddTimeOut (                       \
72                   app_con,                      \
73                   ((unsigned long)(interval)),  \
74                   process_notify,               \
75                   ((XtPointer)(data))           \
76                 )
77 
78 #define DISABLED	((XtIntervalId) 0)
79 #define DESTROY_TIMER(aaa)			\
80 	if ((aaa)) {				\
81 		XtRemoveTimeOut((aaa));		\
82 		aaa = DISABLED;			\
83 	}
84 
85 static ProcessData gpd = NULL;
86 
87 /*------------------------------------------------------------*/
88 /* process_get_pd */
89 /*------------------------------------------------------------*/
90 
process_get_pd(void)91 static ProcessData process_get_pd(void)
92 {
93    ProcessData pd;
94    Cardinal size = sizeof(ProcessDataStruct);
95    BEGINMESSAGE(process_get_pd)
96 #  ifdef MESSAGES
97       if (!gpd) { INFMESSAGE(no processes registered yet) }
98 #  endif
99    pd = (ProcessData) XtMalloc(size);
100    memset((void*) pd ,0,(size_t)size);
101    if (!gpd) gpd = pd;
102    else {
103       ProcessData tmppd;
104       tmppd = gpd; while (tmppd->next) tmppd = tmppd->next;
105       tmppd->next=pd;
106    }
107    ENDMESSAGE(process_get_pd)
108    return(pd);
109 }
110 
111 /*------------------------------------------------------------*/
112 /* process_remove_pd */
113 /*------------------------------------------------------------*/
114 
process_remove_pd(ProcessData pd)115 static void process_remove_pd(ProcessData pd)
116 {
117    BEGINMESSAGE(process_remove_pd)
118    if (gpd == pd) gpd = pd->next;
119    else {
120       ProcessData tmppd = gpd;
121       while (tmppd->next != pd) tmppd=tmppd->next;
122       tmppd->next = pd->next;
123    }
124    process_menu(pd,PROCESS_MENU_DEL_ENTRY);
125    DESTROY_TIMER(pd->timer)
126    XtFree(pd->name);
127    XtFree((XtPointer)pd);
128 #  ifdef MESSAGES
129       if (!gpd) { INFMESSAGE(no more processes registered) }
130 #  endif
131    ENDMESSAGE(process_remove_pd)
132 }
133 
134 /*------------------------------------------------------------*/
135 /* process_child_status */
136 /*------------------------------------------------------------*/
137 
138 #define CHILD_UNKNOWN_STATUS -2
139 #define CHILD_ERROR          -1
140 #define CHILD_OKAY            0
141 #define CHILD_EXITED          1
142 
process_child_status(ProcessData pd)143 static int process_child_status(ProcessData pd)
144 {
145    int status;
146    pid_t child_pid;
147 
148    BEGINMESSAGE(process_child_status)
149 
150       child_pid = waitpid(pd->pid,NULL,WNOHANG);
151       if      (child_pid==pd->pid) { status=CHILD_EXITED;         INFMESSAGE(CHILD_EXITED) }
152       else if (child_pid==0)       { status=CHILD_OKAY;           INFMESSAGE(CHILD_OKAY) }
153       else if (child_pid==-1)      { status=CHILD_ERROR;          INFMESSAGE(CHILD_ERROR) }
154       else                         { status=CHILD_UNKNOWN_STATUS; INFMESSAGE(CHILD_UNKNOWN_STATUS) }
155    ENDMESSAGE(process_child_status)
156    return(status);
157 }
158 
159 
160 /*##############################################################*/
161 /* cb_processKillProcess */
162 /*##############################################################*/
163 
164 void
cb_processKillProcess(Widget w _GL_UNUSED,XtPointer client_data,XtPointer call_data _GL_UNUSED)165 cb_processKillProcess(Widget w _GL_UNUSED, XtPointer client_data, XtPointer call_data _GL_UNUSED)
166 {
167    ProcessData pd;
168    BEGINMESSAGE(cb_processKillProcess)
169    pd = (ProcessData)client_data;
170    process_kill_process(pd);
171    ENDMESSAGE(cb_processKillProcess)
172 }
173 
174 /*##############################################################*/
175 /* process_kill_process */
176 /*##############################################################*/
177 
process_kill_process(ProcessData pd)178 void process_kill_process(ProcessData pd)
179 {
180    int status;
181    BEGINMESSAGE(process_kill_process)
182    kill(pd->pid, SIGTERM);
183    status=process_child_status(pd);
184    if (status==CHILD_OKAY) {
185       INFMESSAGE(waiting for child to exit);
186       wait(NULL);
187    }
188    (*(pd->notify_proc))(pd->data,PROCESS_KILL);
189    process_remove_pd(pd);
190    ENDMESSAGE(process_kill_process)
191 }
192 
193 /*##############################################################*/
194 /* process_kill_all_processes */
195 /*##############################################################*/
196 
process_kill_all_processes(void)197 void process_kill_all_processes(void)
198 {
199    BEGINMESSAGE(process_kill_all_processes)
200    while (gpd) process_kill_process(gpd);
201    ENDMESSAGE(process_kill_all_processes)
202 }
203 
204 /*------------------------------------------------------------*/
205 /* process_notify */
206 /*------------------------------------------------------------*/
207 
process_notify(XtPointer client_data,XtIntervalId * idp _GL_UNUSED)208 static void process_notify(XtPointer client_data, XtIntervalId *idp _GL_UNUSED)
209 {
210    pid_t child_pid;
211    ProcessData pd = (ProcessData) client_data;
212 
213    BEGINMESSAGE(process_notify)
214    child_pid = process_child_status(pd);
215    if (child_pid==0) {
216       INFMESSAGE(child did not exit yet)
217       pd->timer = ADD_TIMEOUT(toplevel,CHECK_PERIOD,client_data);
218       process_menu(pd,PROCESS_MENU_PROGRESS);
219    } else {
220       INFMESSAGE(calling notify procedure)
221       (*(pd->notify_proc))(pd->data,PROCESS_NOTIFY);
222       process_remove_pd(pd);
223    }
224    ENDMESSAGE(process_notify)
225 }
226 
227 /*##############################################################*/
228 /* process_fork */
229 /*##############################################################*/
230 
process_fork(String name,String command,ProcessNotifyProc notify_proc,XtPointer data)231 ProcessData process_fork(String name, String command, ProcessNotifyProc notify_proc, XtPointer data)
232 {
233    ProcessData pd;
234    pid_t       pid;
235 
236    BEGINMESSAGE(process_fork)
237 
238    pd  = process_get_pd();
239 
240    pid = fork();
241 
242    if (pid == 0) { /* child */
243       char *argv[4];
244       char *c;
245 
246       INFMESSAGE(child process)
247       c = command;
248       SMESSAGE(c)
249 
250 
251       argv[0] = "sh";
252       argv[1] = "-c";
253       argv[2] = c;
254       argv[3] = NULL;
255 
256       INFMESSAGE(spawning conversion process)
257 /*
258       if (!freopen("/dev/null", "w", stdout)) perror("/dev/null");
259 */
260       if (!freopen("/dev/null", "r", stdin))  perror("/dev/null");
261 
262       if (gv_gs_safeDir) {
263         if (chdir(gv_safe_gs_workdir) != 0) {
264 	  char buf[512];
265           sprintf(buf, "Chdir to %s failed", gv_safe_gs_workdir);
266 	  perror(buf);
267 	  _exit(EXIT_STATUS_ERROR);
268 	}
269       }
270       execvp(argv[0], argv);
271 
272       {
273          char tmp[512];
274          sprintf(tmp, execOfFailedLabel, argv[0]);
275          perror(tmp);
276          _exit(EXIT_STATUS_ERROR);
277       }
278    }
279    INFMESSAGE(parent process)
280    pd->name        = XtNewString(name);
281    pd->notify_proc = notify_proc;
282    pd->data        = data;
283    pd->pid         = pid;
284    pd->timer       = ADD_TIMEOUT(toplevel,CHECK_PERIOD,pd);
285 
286    process_menu(pd,PROCESS_MENU_ADD_ENTRY);
287 
288    ENDMESSAGE(process_fork)
289    return(pd);
290 }
291 
292 /*------------------------------------------------------------*/
293 /* process_set_shell_resize */
294 /*------------------------------------------------------------*/
295 
process_set_shell_resize(Boolean allow_resize)296 static Boolean process_set_shell_resize(Boolean allow_resize)
297 {
298    Boolean old_allow_resize;
299    ShellWidget sw = (ShellWidget)toplevel;
300    old_allow_resize = sw->shell.allow_shell_resize;
301    sw->shell.allow_shell_resize=allow_resize;
302    return(old_allow_resize);
303 }
304 
305 /*##############################################################*/
306 /* process_menu */
307 /*##############################################################*/
308 
process_menu(ProcessData pd,int action)309 void process_menu(ProcessData pd, int action)
310 {
311 
312    Arg args[5];
313    Cardinal n;
314    static int visible=1;
315    static int progress=0;
316 
317    if (action==PROCESS_MENU_HIDE) {
318       INFMESSAGE(PROCESS_MENU_HIDE)
319       if (visible) {
320          Boolean allow_resize;
321 
322          allow_resize=process_set_shell_resize(False);
323    					        n=0;
324          XtSetArg(args[n], XtNwidth,   0);	n++;
325          XtSetArg(args[n], XtNheight,  0);	n++;
326          XtSetValues(processButton,args,n);
327 
328          process_set_shell_resize(allow_resize);
329 
330          visible=0;
331          progress=0;
332       }
333    }
334    else if (action==PROCESS_MENU_SHOW) {
335       INFMESSAGE(PROCESS_MENU_SHOW)
336       if (!visible) {
337          String label;
338          Boolean allow_resize;
339 
340          allow_resize=process_set_shell_resize(False);
341 
342                                                 n=0;
343          XtSetArg(args[n], XtNlabel,  &label);	n++;
344          XtGetValues(processButton,args,n);
345          label=XtNewString(label);
346          SMESSAGE(label)
347 
348                                                 n=0;
349          XtSetArg(args[n], XtNresize,  True);	n++;
350          XtSetArg(args[n], XtNlabel,   "");	n++;
351          XtSetValues(processButton,args,n);
352 
353      					        n=0;
354          XtSetArg(args[n], XtNlabel,   label);	n++;
355          XtSetValues(processButton,args,n);
356    					        n=0;
357          XtSetArg(args[n], XtNresize,  False);	n++;
358          XtSetValues(processButton,args,n);
359          XtFree(label);
360 
361          process_set_shell_resize(allow_resize);
362          visible=1;
363          progress=0;
364       }
365    }
366    else if (action==PROCESS_MENU_ADD_ENTRY) {
367       Widget entry;
368       char label[512];
369       INFMESSAGE(PROCESS_MENU_ADD_ENTRY)
370       if (!processMenu) {
371                                                 n=0;
372          processMenu = XtCreatePopupShell("menu",
373                        simpleMenuWidgetClass,processButton,args,n);
374       }
375       sprintf(label,"Stop %s",pd->name);
376 
377           					n=0;
378          XtSetArg(args[n], XtNlabel, label);    n++;
379       entry = XtCreateManagedWidget("aaa", smeBSBObjectClass,processMenu,args,n);
380       XtAddCallback(entry, XtNcallback, cb_processKillProcess, (XtPointer)pd);
381       pd->menuentry  = entry;
382       process_menu(NULL,PROCESS_MENU_SHOW);
383       process_menu(pd,PROCESS_MENU_PROGRESS);
384    }
385    else if (action==PROCESS_MENU_DEL_ENTRY) {
386       INFMESSAGE(PROCESS_MENU_DEL_ENTRY)
387       if (!gv_exiting) {
388          if (!gpd) {
389             INFMESSAGE(destroying processMenu)
390             XtDestroyWidget(processMenu);
391             processMenu=NULL;
392             process_menu(NULL,PROCESS_MENU_HIDE);
393          }
394          else {
395             INFMESSAGE(destroying menu entry)
396             XtDestroyWidget(pd->menuentry);
397          }
398       }
399    }
400    else if (action==PROCESS_MENU_PROGRESS) {
401       INFMESSAGE(PROCESS_MENU_PROGRESS)
402       if (visible) {
403          char *label;
404          char *tmp;
405          size_t len;
406                                                 n=0;
407          XtSetArg(args[n], XtNlabel,  &label);	n++;
408          XtGetValues(processButton,args,n);
409          len = strlen(label);
410          tmp = (char*) XtMalloc(len*sizeof(char)+1);
411          strcpy(tmp,&(label[progress]));
412          if (progress) {
413             if (progress>(int)len) progress=(int)len;
414             strncpy(&(tmp[(int)len-progress]),label,(size_t)progress);
415          }
416          tmp[len]='\0';
417          progress++;
418          if (progress==(int)len+1) progress=0;
419          update_label(processButton,tmp);
420          XtFree(tmp);
421       }
422    }
423 }
424 
425 /*##############################################################*/
426 /* process_disallow_quit */
427 /*##############################################################*/
428 
process_disallow_quit(void)429 char *process_disallow_quit(void)
430 {
431 # define MAX_DISALLOW_QUIT_MESSAGE 512
432   static char message[MAX_DISALLOW_QUIT_MESSAGE];
433   ProcessData pd;
434   int l;
435 
436   BEGINMESSAGE(process_disallow_quit)
437   if (!gpd) {
438     ENDMESSAGE(process_disallow_quit)
439     return NULL;
440   }
441   strcpy(message,stillInProgressLabel);
442   l = strlen(message);
443   for (pd = gpd; pd ; pd = pd->next) {
444       l = l + strlen(pd->name) + 1;
445       if (l + 10 < MAX_DISALLOW_QUIT_MESSAGE) {
446         strcat(message,"\n");
447 	strcat(message,pd->name);
448       } else break;
449   };
450   if (pd) strcat(message,"\n...");
451   ENDMESSAGE(process_disallow_quit)
452   return message;
453 }
454 
455