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