]> git.lyx.org Git - lyx.git/blob - src/support/syscall.C
os:: patch from Ruurd + bindings display fix
[lyx.git] / src / support / syscall.C
1 #include <config.h>
2
3 #ifdef __GNUG__
4 #pragma implementation
5 #endif
6
7 #include <cerrno>
8 #include <sys/types.h>
9 #include <sys/wait.h>
10 #include <csignal>
11 #include <cstdlib>
12 #include <unistd.h>
13 #include "debug.h"
14 #include "syscall.h"
15 #include "syscontr.h"
16 #include "support/lstrings.h"
17 #include "support/lyxlib.h"
18 #include "support/filetools.h"
19 #include "support/os.h"
20
21 using std::endl;
22
23
24 Systemcalls::Systemcalls() {
25         pid = 0; // No child yet
26 }
27
28 Systemcalls::Systemcalls(Starttype how, string const & what, Callbackfct cback)
29 {
30         startscript(how, what, cback);
31 }
32
33 Systemcalls::~Systemcalls() {
34 #if 0
35         // If the child is alive, we have to brutally kill it
36         if (getpid() != 0) {
37                 lyx::kill(getpid(), SIGKILL);
38         }
39 #endif
40 }
41
42 // Start a childprocess
43 // 
44 // if child runs in background, add information to global controller.
45
46 int Systemcalls::startscript() {
47         retval = 0;
48         switch (start) {
49         case System: 
50         case SystemDontWait:
51                 retval = ::system(command.c_str());
52                 callback();
53                 break;
54         case Wait:   
55                 pid = fork();
56                 if (pid>0) { // Fork succesful. Wait for child
57                         waitForChild();
58                         callback();
59                 } else
60                         retval = 1;
61                 break;
62         case DontWait:
63                 pid = fork();
64                 if (pid>0) {
65                         // Now integrate into Controller
66                         SystemcallsSingletoncontroller::Startcontroller starter;
67                         SystemcallsSingletoncontroller * contr = starter.getController();
68                         // Add this to controller
69                         contr->addCall(*this);
70                 } else
71                         retval = 1;
72                 break;
73         }
74         return retval;
75 }
76
77 void Systemcalls::kill(int /*tolerance*/) {
78         if (getpid() == 0) {
79                 lyxerr << "LyX: Can't kill non-existing process." << endl;
80                 return;
81         }
82         int ret = lyx::kill(getpid(), SIGHUP);
83         bool wait_for_death = true;
84         if (ret != 0) {
85                 if (errno == ESRCH) {
86                         // The process is already dead!
87                         wait_for_death = false;
88                 } else {
89                         // Something is rotten - maybe we lost permissions?
90                 }
91         }
92         if (wait_for_death) {
93                 // Here, we should add the PID to a list of
94                 // waiting processes to kill if they are not
95                 // dead within tolerance seconds
96
97                 // CHECK Implement this using the timer of
98                 // the singleton systemcontroller (Asger)
99
100         }
101 }
102
103
104 // Wait for child process to finish. Returns returncode from child.
105 void Systemcalls::waitForChild() {
106         // We'll pretend that the child returns 1 on all errorconditions.
107         retval = 1;
108         int status;
109         bool wait = true;
110         while (wait) {
111                 pid_t waitrpid = waitpid(pid, &status, WUNTRACED);
112                 if (waitrpid == -1) {
113                         lyxerr << "LyX: Error waiting for child:" << strerror(errno) << endl;
114                         wait = false;
115                 } else if (WIFEXITED(status)) {
116                         // Child exited normally. Update return value.
117                         retval = WEXITSTATUS(status);
118                         wait = false;
119                 } else if (WIFSIGNALED(status)) {
120                         lyxerr << "LyX: Child didn't catch signal "
121                                << WTERMSIG(status) 
122                                << "and died. Too bad." << endl;
123                         wait = false;
124                 } else if (WIFSTOPPED(status)) {
125                         lyxerr << "LyX: Child (pid: " << pid 
126                                << ") stopped on signal "
127                                << WSTOPSIG(status) 
128                                << ". Waiting for child to finish." << endl;
129                 } else {
130                         lyxerr << "LyX: Something rotten happened while "
131                                 "waiting for child " << pid << endl;
132                         wait = false;
133                 }
134         }
135 }
136
137
138 // generate child in background
139
140 pid_t Systemcalls::fork()
141 {
142         #ifndef __EMX__
143         pid_t cpid= ::fork();
144         if (cpid == 0) { // child
145         #endif
146                 // TODO: Consider doing all of this before the fork, otherwise me
147                 // might have troubles with multi-threaded access. (Baruch 20010228)
148                 string childcommand(command); // copy
149                 string rest = split(command, childcommand, ' ');
150                 const int MAX_ARGV = 255;
151                 char *syscmd = 0; 
152                 char *argv[MAX_ARGV];
153                 int  index = 0;
154                 bool more = true;
155                 while (more) {
156                         childcommand = frontStrip(childcommand);
157                         if (syscmd == 0) {
158                                 syscmd = new char[childcommand.length() + 1];
159                                 childcommand.copy(syscmd, childcommand.length());
160                                 syscmd[childcommand.length()] = '\0';
161                         }
162                         if (!childcommand.empty()) {
163                         char * tmp = new char[childcommand.length() + 1];
164                         childcommand.copy(tmp, childcommand.length());
165                         tmp[childcommand.length()] = '\0';
166                         argv[index++] = tmp;
167                         }
168                         
169                         // reinit
170                         more = !rest.empty();
171                         if (more) 
172                                 rest = split(rest, childcommand, ' ');
173                 }
174                 argv[index] = 0;
175                 // replace by command. Expand using PATH-environment-var.
176 #ifndef __EMX__
177                 execvp(syscmd, argv);
178                 // If something goes wrong, we end up here:
179                 lyxerr << "LyX: execvp failed: " << strerror(errno) << endl;
180         } else if (cpid < 0) { // error
181 #else
182         pid_t cpid = spawnvp(P_SESSION|P_DEFAULT|P_MINIMIZE|P_BACKGROUND, syscmd, argv);
183         if (cpid < 0) { // error
184 #endif
185                 lyxerr << "LyX: Could not fork: " << strerror(errno) << endl;
186         } else { // parent
187                 return cpid;
188         }
189         return 0;
190 }
191
192
193 // Reuse of instance
194
195 int Systemcalls::startscript(Starttype how, string const & what, 
196                              Callbackfct cback)
197 {
198         start   = how;
199         command = what;
200         cbk     = cback;
201         pid     = static_cast<pid_t>(0); // yet no child
202         retval  = 0;
203
204         if (how == SystemDontWait) {
205                 (os::shell() == os::UNIX) ? command += " &"
206                                           : command = "start /min/n " + command;
207         }
208
209         return startscript();
210 }
211
212
213
214 //
215 // Mini-Test-environment for script-classes
216 //
217 #ifdef TEST_MAIN
218 #include <stdio.h>
219
220
221 int SimulateTimer;
222 void back(string cmd, int retval)
223 {
224         ::printf("Done: %s gave %d\n", cmd.c_str(), retval);
225         SimulateTimer = 0;
226 }
227
228
229 int main(int, char **)
230 {
231         
232         SystemcallsSingletoncontroller::Startcontroller starter; 
233         SystemcallsSingletoncontroller *contr= starter.GetController();
234         
235         Systemcalls one(Systemcalls::System, "ls -ltag", back);
236         Systemcalls two(Systemcalls::Wait, "ls -ltag", back);
237         SimulateTimer = 1;
238         Systemcalls three(Systemcalls::DontWait , "ls -ltag", back);
239         // Simulation of timer
240         while (SimulateTimer)
241                 {
242                         sleep(1);
243                         contr->Timer();
244                 }
245 }
246 #endif