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