]> git.lyx.org Git - lyx.git/blob - src/support/forkedcontr.C
fix typo that put too many include paths for most people
[lyx.git] / src / support / forkedcontr.C
1 /**
2  * \file forkedcontr.C
3  * Copyright 2001 The LyX Team
4  * Read COPYING
5  *
6  * \author Asger Alstrup Nielsen
7  * \author Angus Leeming
8  *
9  * A class for the control of child processes launched using
10  * fork() and execvp().
11  */
12
13 #include <config.h>
14
15 #ifdef __GNUG__
16 #pragma implementation
17 #endif
18
19 #include "forkedcontr.h"
20 #include "forkedcall.h"
21 #include "lyxfunctional.h"
22 #include "frontends/Timeout.h"
23 #include "debug.h"
24
25 #include <cerrno>
26 #include <cstdlib>
27 #include <unistd.h>
28 #include <sys/wait.h>
29
30 using std::vector;
31 using std::endl;
32 using std::find_if;
33
34 #ifndef CXX_GLOBAL_CSTD
35 using std::strerror;
36 #endif
37
38 // Ensure, that only one controller exists inside process
39 ForkedcallsController & ForkedcallsController::get()
40 {
41         static ForkedcallsController singleton;
42         return singleton;
43 }
44
45
46 ForkedcallsController::ForkedcallsController()
47 {
48         timeout_ = new Timeout(100, Timeout::ONETIME);
49
50         timeout_->timeout
51                 .connect(SigC::slot(this, &ForkedcallsController::timer));
52 }
53
54
55 // open question: should we stop childs here?
56 // Asger says no: I like to have my xdvi open after closing LyX. Maybe
57 // I want to print or something.
58 ForkedcallsController::~ForkedcallsController()
59 {
60         for (ListType::iterator it = forkedCalls.begin();
61              it != forkedCalls.end(); ++it) {
62                 delete *it;
63         }
64
65         delete timeout_;
66 }
67
68
69 // Add child process information to the list of controlled processes
70 void ForkedcallsController::addCall(Forkedcall const &newcall)
71 {
72         if (!timeout_->running())
73                 timeout_->start();
74
75         Forkedcall * call = new Forkedcall(newcall);
76         forkedCalls.push_back(call);
77         childrenChanged.emit();
78 }
79
80
81 // Timer-call
82 // Check the list and, if there is a stopped child, emit the signal.
83 void ForkedcallsController::timer()
84 {
85         ListType::size_type start_size = forkedCalls.size();
86
87         for (ListType::iterator it = forkedCalls.begin();
88              it != forkedCalls.end(); ++it) {
89                 Forkedcall * actCall = *it;
90
91                 pid_t pid = actCall->pid();
92                 int stat_loc;
93                 pid_t const waitrpid = waitpid(pid, &stat_loc, WNOHANG);
94                 bool remove_it = false;
95
96                 if (waitrpid == -1) {
97                         lyxerr << "LyX: Error waiting for child: "
98                                << strerror(errno) << endl;
99
100                         // Child died, so pretend it returned 1
101                         actCall->setRetValue(1);
102                         remove_it = true;
103
104                 } else if (waitrpid == 0) {
105                         // Still running. Move on to the next child.
106                         continue;
107
108                 } else if (WIFEXITED(stat_loc)) {
109                         // Ok, the return value goes into retval.
110                         actCall->setRetValue(WEXITSTATUS(stat_loc));
111                         remove_it = true;
112
113                 } else if (WIFSIGNALED(stat_loc)) {
114                         // Child died, so pretend it returned 1
115                         actCall->setRetValue(1);
116                         remove_it = true;
117
118                 } else if (WIFSTOPPED(stat_loc)) {
119                         lyxerr << "LyX: Child (pid: " << pid
120                                << ") stopped on signal "
121                                << WSTOPSIG(stat_loc)
122                                << ". Waiting for child to finish." << endl;
123
124                 } else {
125                         lyxerr << "LyX: Something rotten happened while "
126                                 "waiting for child " << pid << endl;
127
128                         // Child died, so pretend it returned 1
129                         actCall->setRetValue(1);
130                         remove_it = true;
131                 }
132
133                 if (remove_it) {
134                         // Emit signal and remove the item from the list
135                         actCall->emitSignal();
136                         delete actCall;
137                         // erase returns the next iterator, so decrement it
138                         // to continue the loop.
139                         ListType::iterator prev = it;
140                         --prev;
141                         forkedCalls.erase(it);
142                         it = prev;
143                 }
144         }
145
146         if (!forkedCalls.empty()) {
147                 timeout_->start();
148         }
149
150         if (start_size != forkedCalls.size())
151                 childrenChanged.emit();
152 }
153
154
155 // Return a vector of the pids of all the controlled processes.
156 vector<pid_t> const ForkedcallsController::getPIDs() const
157 {
158         vector<pid_t> pids;
159
160         if (forkedCalls.empty())
161                 return pids;
162
163         pids.resize(forkedCalls.size());
164
165         vector<pid_t>::iterator vit = pids.begin();
166         for (ListType::const_iterator lit = forkedCalls.begin();
167              lit != forkedCalls.end(); ++lit, ++vit) {
168                 *vit = (*lit)->pid();
169         }
170
171         std::sort(pids.begin(), pids.end());
172         return pids;
173 }
174
175
176 // Get the command string of the process.
177 string const ForkedcallsController::getCommand(pid_t pid) const
178 {
179         ListType::const_iterator it =
180                 find_if(forkedCalls.begin(), forkedCalls.end(),
181                         lyx::compare_memfun(&Forkedcall::pid, pid));
182
183         if (it == forkedCalls.end())
184                 return string();
185
186         return (*it)->command();
187 }
188
189
190 // Kill the process prematurely and remove it from the list
191 // within tolerance secs
192 void ForkedcallsController::kill(pid_t pid, int tolerance)
193 {
194         ListType::iterator it =
195                 find_if(forkedCalls.begin(), forkedCalls.end(),
196                         lyx::compare_memfun(&Forkedcall::pid, pid));
197
198         if (it == forkedCalls.end())
199                 return;
200
201         (*it)->kill(tolerance);
202         forkedCalls.erase(it);
203
204         if (forkedCalls.empty()) {
205                 timeout_->stop();
206         }
207 }