]> git.lyx.org Git - lyx.git/blob - src/support/syscall.C
some new (not extensive) changes, some fixes, will probably reverto to .la libs later...
[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
18 Systemcalls::Systemcalls() {
19         pid = 0; // No child yet
20 }
21
22 Systemcalls::Systemcalls(Starttype how, string const & what, Callbackfct cback)
23 {
24         start   = how;
25         command = what;
26         cbk     = cback;
27         pid     = static_cast<pid_t>(0);
28         retval  = 0;
29         startscript();
30 }
31
32 Systemcalls::~Systemcalls() {
33 #if 0
34         // If the child is alive, we have to brutally kill it
35         if (getpid() != 0) {
36                 ::kill(getpid(), SIGKILL);
37         }
38 #endif
39 }
40
41 // Start a childprocess
42 // 
43 // if child runs in background, add information to global controller.
44
45 int Systemcalls::startscript() {
46         retval = 0;
47         switch (start) {
48         case System: 
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 = ::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 without tolerance seconds
94 #ifdef WITH_WARNINGS
95 #warning Implement this using the timer of the singleton systemcontroller (Asger)
96 #endif
97         }
98 }
99
100
101 // Wait for child process to finish. Returns returncode from child.
102 void Systemcalls::waitForChild() {
103         // We'll pretend that the child returns 1 on all errorconditions.
104         retval = 1;
105         int status;
106         bool wait = true;
107         while (wait) {
108                 pid_t waitrpid = waitpid(pid, &status, WUNTRACED);
109                 if (waitrpid == -1) {
110                         lyxerr << "LyX: Error waiting for child:" << strerror(errno) << endl;
111                         wait = false;
112                 } else if (WIFEXITED(status)) {
113                         // Child exited normally. Update return value.
114                         retval = WEXITSTATUS(status);
115                         wait = false;
116                 } else if (WIFSIGNALED(status)) {
117                         lyxerr << "LyX: Child didn't catch signal "
118                                << WTERMSIG(status) 
119                                << "and died. Too bad." << endl;
120                         wait = false;
121                 } else if (WIFSTOPPED(status)) {
122                         lyxerr << "LyX: Child (pid: " << pid 
123                                << ") stopped on signal "
124                                << WSTOPSIG(status) 
125                                << ". Waiting for child to finish." << endl;
126                 } else {
127                         lyxerr << "LyX: Something rotten happened while "
128                                 "waiting for child " << pid << endl;
129                         wait = false;
130                 }
131         }
132 }
133
134
135 // generate child in background
136
137 pid_t Systemcalls::fork()
138 {
139         pid_t cpid= ::fork();
140         if (cpid == 0) { // child
141                 string childcommand(command); // copy
142                 string rest = split(command, childcommand, ' ');
143                 const int MAX_ARGV = 255;
144                 char *syscmd = 0; 
145                 char *argv[MAX_ARGV];
146                 int  index = 0;
147                 bool more;
148                 do {
149                         if (syscmd == 0) {
150                                 syscmd = new char[childcommand.length() + 1];
151                                 childcommand.copy(syscmd, childcommand.length());
152                                 syscmd[childcommand.length()] = '\0';
153                         }
154                         char * tmp = new char[childcommand.length() + 1];
155                         childcommand.copy(tmp, childcommand.length());
156                         tmp[childcommand.length()] = '\0';
157                         argv[index++] = tmp;
158                         // reinit
159                         more = !rest.empty();
160                         if (more) 
161                                 rest = split(rest, childcommand, ' ');
162                 } while (more);
163                 argv[index] = 0;
164                 // replace by command. Expand using PATH-environment-var.
165                 execvp(syscmd, argv);
166                 // If something goes wrong, we end up here:
167                 lyxerr << "LyX: execvp failed: " << strerror(errno) << endl;
168         } else if (cpid < 0) { // error
169                 lyxerr << "LyX: Could not fork: " << strerror(errno) << endl;
170         } else { // parent
171                 return cpid;
172         }
173         return 0;
174 }
175
176
177 // Reuse of instance
178
179 int Systemcalls::startscript(Starttype how, string const & what, 
180                              Callbackfct cback)
181 {
182         start   = how;
183         command = what;
184         cbk     = cback;
185         pid     = static_cast<pid_t>(0); // yet no child
186         retval  = 0;
187         return startscript();
188 }
189
190
191
192 //
193 // Mini-Test-environment for script-classes
194 //
195 #ifdef TEST_MAIN
196 #include <stdio.h>
197
198
199 int SimulateTimer;
200 void back(string cmd, int retval)
201 {
202         printf("Done: %s gave %d\n", cmd.c_str(), retval);
203         SimulateTimer = 0;
204 }
205
206
207 int main(int, char**)
208 {
209         
210         SystemcallsSingletoncontroller::Startcontroller starter; 
211         SystemcallsSingletoncontroller *contr= starter.GetController();
212         
213         Systemcalls one(Systemcalls::System, "ls -ltag", back);
214         Systemcalls two(Systemcalls::Wait, "ls -ltag", back);
215         SimulateTimer = 1;
216         Systemcalls three(Systemcalls::DontWait , "ls -ltag", back);
217         // Simulation of timer
218         while (SimulateTimer)
219                 {
220                         sleep(1);
221                         contr->Timer();
222                 }
223 }
224 #endif