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