]> git.lyx.org Git - lyx.git/blob - src/support/syscall.C
adjust to be able to complile with gcc 3.0, put selection vars into own struct, remov...
[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:"
114                                << std::strerror(errno) << endl;
115                         wait = false;
116                 } else if (WIFEXITED(status)) {
117                         // Child exited normally. Update return value.
118                         retval = WEXITSTATUS(status);
119                         wait = false;
120                 } else if (WIFSIGNALED(status)) {
121                         lyxerr << "LyX: Child didn't catch signal "
122                                << WTERMSIG(status) 
123                                << "and died. Too bad." << endl;
124                         wait = false;
125                 } else if (WIFSTOPPED(status)) {
126                         lyxerr << "LyX: Child (pid: " << pid 
127                                << ") stopped on signal "
128                                << WSTOPSIG(status) 
129                                << ". Waiting for child to finish." << endl;
130                 } else {
131                         lyxerr << "LyX: Something rotten happened while "
132                                 "waiting for child " << pid << endl;
133                         wait = false;
134                 }
135         }
136 }
137
138
139 // generate child in background
140
141 pid_t Systemcalls::fork()
142 {
143         #ifndef __EMX__
144         pid_t cpid= ::fork();
145         if (cpid == 0) { // child
146         #endif
147                 // TODO: Consider doing all of this before the fork, otherwise me
148                 // might have troubles with multi-threaded access. (Baruch 20010228)
149                 string childcommand(command); // copy
150                 string rest = split(command, childcommand, ' ');
151                 const int MAX_ARGV = 255;
152                 char *syscmd = 0; 
153                 char *argv[MAX_ARGV];
154                 int  index = 0;
155                 bool more = true;
156                 while (more) {
157                         childcommand = frontStrip(childcommand);
158                         if (syscmd == 0) {
159                                 syscmd = new char[childcommand.length() + 1];
160                                 childcommand.copy(syscmd, childcommand.length());
161                                 syscmd[childcommand.length()] = '\0';
162                         }
163                         if (!childcommand.empty()) {
164                         char * tmp = new char[childcommand.length() + 1];
165                         childcommand.copy(tmp, childcommand.length());
166                         tmp[childcommand.length()] = '\0';
167                         argv[index++] = tmp;
168                         }
169                         
170                         // reinit
171                         more = !rest.empty();
172                         if (more) 
173                                 rest = split(rest, childcommand, ' ');
174                 }
175                 argv[index] = 0;
176                 // replace by command. Expand using PATH-environment-var.
177 #ifndef __EMX__
178                 execvp(syscmd, argv);
179                 // If something goes wrong, we end up here:
180                 lyxerr << "LyX: execvp failed: "
181                        << std::strerror(errno) << endl;
182         } else if (cpid < 0) { // error
183 #else
184         pid_t cpid = spawnvp(P_SESSION|P_DEFAULT|P_MINIMIZE|P_BACKGROUND, syscmd, argv);
185         if (cpid < 0) { // error
186 #endif
187                 lyxerr << "LyX: Could not fork: "
188                        << std::strerror(errno) << endl;
189         } else { // parent
190                 return cpid;
191         }
192         return 0;
193 }
194
195
196 // Reuse of instance
197
198 int Systemcalls::startscript(Starttype how, string const & what, 
199                              Callbackfct cback)
200 {
201         start   = how;
202         command = what;
203         cbk     = cback;
204         pid     = static_cast<pid_t>(0); // yet no child
205         retval  = 0;
206
207         if (how == SystemDontWait) {
208                 (os::shell() == os::UNIX) ? command += " &"
209                                           : command = "start /min/n " + command;
210         }
211
212         return startscript();
213 }
214
215
216
217 //
218 // Mini-Test-environment for script-classes
219 //
220 #ifdef TEST_MAIN
221 #include <stdio.h>
222
223
224 int SimulateTimer;
225 void back(string cmd, int retval)
226 {
227         ::printf("Done: %s gave %d\n", cmd.c_str(), retval);
228         SimulateTimer = 0;
229 }
230
231
232 int main(int, char **)
233 {
234         
235         SystemcallsSingletoncontroller::Startcontroller starter; 
236         SystemcallsSingletoncontroller *contr= starter.GetController();
237         
238         Systemcalls one(Systemcalls::System, "ls -ltag", back);
239         Systemcalls two(Systemcalls::Wait, "ls -ltag", back);
240         SimulateTimer = 1;
241         Systemcalls three(Systemcalls::DontWait , "ls -ltag", back);
242         // Simulation of timer
243         while (SimulateTimer)
244                 {
245                         sleep(1);
246                         contr->Timer();
247                 }
248 }
249 #endif