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