]> git.lyx.org Git - lyx.git/blob - src/support/syscall.C
8b18c28704b51bdf7fe78431d27c2bdf49b8537e
[lyx.git] / src / support / syscall.C
1 #include <config.h>
2
3 #ifdef __GNUG__
4 #pragma implementation
5 #endif
6
7 #include <errno.h>
8 #include <sys/types.h>
9 #include <sys/wait.h>
10 #include <signal.h>
11 #include <cstdlib>
12 #include <cstdio>
13 #include <unistd.h>
14 #include "debug.h"
15 #include "syscall.h"
16 #include "syscontr.h"
17 #include "support/lstrings.h"
18
19 Systemcalls::Systemcalls() {
20         pid = (pid_t) 0; // No child yet
21 }
22
23 Systemcalls::Systemcalls(Starttype how, string const & what, Callbackfct cback)
24 {
25         start   = how;
26         command = what;
27         cbk     = cback;
28         pid     = (pid_t) 0;
29         retval  = 0;
30         startscript();
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                 ::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                 retval = system(command.c_str());
51                 callback();
52                 break;
53         case Wait:   
54                 pid = fork();
55                 if (pid>0) { // Fork succesful. Wait for child
56                         waitForChild();
57                         callback();
58                 } else
59                         retval = 1;
60                 break;
61         case DontWait:
62                 pid = fork();
63                 if (pid>0) {
64                         // Now integrate into Controller
65                         SystemcallsSingletoncontroller::Startcontroller starter;
66                         SystemcallsSingletoncontroller * contr = starter.getController();
67                         // Add this to controller
68                         contr->addCall(*this);
69                 } else
70                         retval = 1;
71                 break;
72         }
73         return retval;
74 }
75
76 void Systemcalls::kill(int tolerance) {
77         if (getpid() == 0) {
78                 lyxerr << "LyX: Can't kill non-existing process." << endl;
79                 return;
80         }
81         int ret = ::kill(getpid(), SIGHUP);
82         bool wait_for_death = true;
83         if (ret != 0) {
84                 if (errno == ESRCH) {
85                         // The process is already dead!
86                         wait_for_death = false;
87                 } else {
88                         // Something is rotten - maybe we lost permissions?
89                 }
90         }
91         if (wait_for_death) {
92                 // Here, we should add the PID to a list of
93                 // waiting processes to kill if they are not
94                 // dead without tolerance seconds
95 #ifdef WITH_WARNINGS
96 #warning Implement this using the timer of the singleton systemcontroller (Asger)
97 #endif
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                 string childcommand(command); // copy
143                 string rest = split(command, childcommand, ' ');
144                 const int MAX_ARGV = 255;
145                 char *syscmd = 0; 
146                 char *argv[MAX_ARGV];
147                 int  index = 0;
148                 bool more;
149                 do {
150                         if (syscmd == 0) {
151                                 syscmd = new char[childcommand.length() + 1];
152                                 childcommand.copy(syscmd, childcommand.length());
153                                 syscmd[childcommand.length()] = '\0';
154                         }
155                         char * tmp = new char[childcommand.length() + 1];
156                         childcommand.copy(tmp, childcommand.length());
157                         tmp[childcommand.length()] = '\0';
158                         argv[index++] = tmp;
159                         // reinit
160                         more = !rest.empty();
161                         if (more) 
162                                 rest = split(rest, childcommand, ' ');
163                 } while (more);
164                 argv[index] = 0;
165                 // replace by command. Expand using PATH-environment-var.
166                 execvp(syscmd, argv);
167                 // If something goes wrong, we end up here:
168                 lyxerr << "LyX: execvp failed: " << strerror(errno) << endl;
169         } else if (cpid < 0) { // error
170                 lyxerr << "LyX: Could not fork: " << strerror(errno) << endl;
171         } else { // parent
172                 return cpid;
173         }
174         return 0;
175 }
176
177
178 // Reuse of instance
179
180 int Systemcalls::startscript(Starttype how, string const & what, 
181                              Callbackfct cback)
182 {
183         start   = how;
184         command = what;
185         cbk     = cback;
186         pid     = (pid_t) 0; // yet no child
187         retval  = 0;
188         return startscript();
189 }
190
191
192
193 //
194 // Mini-Test-environment for script-classes
195 //
196 #ifdef TEST_MAIN
197 #include <stdio.h>
198
199
200 int SimulateTimer;
201 void back(string cmd, int retval)
202 {
203         printf("Done: %s gave %d\n", cmd.c_str(), retval);
204         SimulateTimer = 0;
205 }
206
207
208 int main(int, char**)
209 {
210         
211         SystemcallsSingletoncontroller::Startcontroller starter; 
212         SystemcallsSingletoncontroller *contr=starter.GetController();
213         
214         Systemcalls one(Systemcalls::System, "ls -ltag", back);
215         Systemcalls two(Systemcalls::Wait, "ls -ltag", back);
216         SimulateTimer = 1;
217         Systemcalls three(Systemcalls::DontWait , "ls -ltag", back);
218         // Simulation of timer
219         while (SimulateTimer)
220                 {
221                         sleep(1);
222                         contr->Timer();
223                 }
224 }
225 #endif