Line data Source code
1 : // Copyright (c) 2013-2022 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/eventdispatcher
4 : // contact@m2osw.com
5 : //
6 : // This program is free software; you can redistribute it and/or modify
7 : // it under the terms of the GNU General Public License as published by
8 : // the Free Software Foundation; either version 2 of the License, or
9 : // (at your option) any later version.
10 : //
11 : // This program is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 : //
16 : // You should have received a copy of the GNU General Public License along
17 : // with this program; if not, write to the Free Software Foundation, Inc.,
18 : // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 :
20 : /** \file
21 : * \brief Implementation of the process_list loader.
22 : *
23 : * This object loads the complete list of process identifiers (pid_t)
24 : * found under /proc. You can further query about many of the parameters
25 : * of a process using the resulting process_info objects.
26 : *
27 : * If interested by a specific process for which you know the pid, you can
28 : * instead use a process_info object directly.
29 : */
30 :
31 : // self
32 : //
33 : #include <cppprocess/process_list.h>
34 :
35 :
36 : // snapdev lib
37 : //
38 : #include <snapdev/glob_to_list.h>
39 : #include <snapdev/map_keyset.h>
40 :
41 :
42 : // snaplogger lib
43 : //
44 : #include <snaplogger/message.h>
45 :
46 :
47 : // C++ lib
48 : //
49 : #include <list>
50 :
51 :
52 : // C lib
53 : //
54 : #include <signal.h>
55 :
56 :
57 : // last include
58 : //
59 : #include <snapdev/poison.h>
60 :
61 :
62 :
63 : namespace cppprocess
64 : {
65 :
66 :
67 :
68 : /** \brief The process list constructor determines the list of processes.
69 : *
70 : * The constructor builds the list of existing processes from /proc with
71 : * all the file names that are only digits. Those are the process PIDs.
72 : * The resulting list is saved in the process_list as proc_info objects
73 : * which can be quiered for additional information.
74 : *
75 : * The additional information is loaded at the time it gets queried which
76 : * means you may actually get an invalid result (i.e. if the process
77 : * died before you were able to query its data).
78 : *
79 : * You can refresh the list of processes by calling the refresh() function.
80 : * It will re-read the list of available processes by reading their pid
81 : * from the `/proc/...` folder.
82 : */
83 0 : process_list::process_list()
84 : {
85 0 : refresh();
86 0 : }
87 :
88 :
89 : /** \brief Refresh the list of processes.
90 : *
91 : * If you want to keep a process_list object around for a while, it is
92 : * going to decay over time (i.e. many processes die and new onces get
93 : * created).
94 : *
95 : * This function refreshes the list of process_info objects defined in
96 : * this process_list.
97 : */
98 0 : void process_list::refresh()
99 : {
100 : // Keep a copy of the existing keys; if still in that set at the end
101 : // of the following loop, delete those from the map
102 : //
103 0 : std::set<pid_t> keys;
104 0 : snapdev::map_keyset(keys, *this);
105 :
106 : // gather current set of processes (pid_t)
107 : //
108 : typedef std::list<std::string> list_t;
109 0 : snapdev::glob_to_list<list_t> filenames;
110 : filenames.read_path<
111 : snapdev::glob_to_list_flag_t::GLOB_FLAG_IGNORE_ERRORS
112 0 : , snapdev::glob_to_list_flag_t::GLOB_FLAG_ONLY_DIRECTORIES>("/proc/[0-9]*");
113 :
114 0 : for(auto f : filenames)
115 : {
116 0 : if(f.length() < 7) // strlen("/proc/[0-9]") == 7
117 : {
118 0 : continue;
119 : }
120 :
121 : // convert string to integer
122 : //
123 0 : pid_t pid(0);
124 0 : char const * s(f.c_str() + 6);
125 0 : for(; *s >= '0' && *s <= '9'; ++s)
126 : {
127 0 : pid *= 10;
128 0 : pid += *s - '0';
129 : }
130 0 : if(*s != '\0'
131 0 : || pid == 0)
132 : {
133 : // invalid character or number ("0" is not valid)
134 : //
135 0 : continue;
136 : }
137 :
138 : // got a pid considered valid, use it
139 : //
140 0 : keys.erase(pid);
141 0 : auto it(map::find(pid));
142 0 : if(it == end())
143 : {
144 0 : insert({pid, std::make_shared<process_info>(pid)});
145 : }
146 : }
147 :
148 : // delete processes from our list if they died
149 : //
150 0 : for(auto k : keys)
151 : {
152 0 : auto it(map::find(k));
153 0 : if(it != end())
154 : {
155 0 : erase(it);
156 : }
157 : }
158 0 : }
159 :
160 :
161 : /** \brief Find a process_info object by pid.
162 : *
163 : * This function returns the process info object searching by its process
164 : * identifier.
165 : *
166 : * \return A pointer to the process_info or null.
167 : */
168 0 : process_info::pointer_t process_list::find(pid_t pid)
169 : {
170 0 : auto it(map::find(pid));
171 0 : if(it == end())
172 : {
173 0 : return process_info::pointer_t();
174 : }
175 :
176 0 : return it->second;
177 : }
178 :
179 :
180 : /** \brief Find a process_info object by its process basename.
181 : *
182 : * This function goes through the map of processes and returns the
183 : * first one which name matches the specified \p basename.
184 : *
185 : * \return A pointer to a process_info or null.
186 : */
187 0 : process_info::pointer_t process_list::find(std::string const & basename)
188 : {
189 0 : for(auto & p : *this)
190 : {
191 0 : if(p.second->get_basename() == basename)
192 : {
193 0 : return p.second;
194 : }
195 : }
196 :
197 0 : return process_info::pointer_t();
198 : }
199 :
200 :
201 : /** \brief Check whether a process is running.
202 : *
203 : * When sending a STOP message to a process, or a SIGINT/SIGTERM signal,
204 : * the process may not stop right away. This function allows you to wait
205 : * and see that a process ends within a given amount of time.
206 : *
207 : * When \p timeout is set to 0, the function returns immediately after
208 : * checking whether the process is running or not.
209 : *
210 : * The \p sig parameter allows you to send a specific signal the first
211 : * time the kill() function is called. If set to 0, the process is not
212 : * sent any signal. The function only uses 0 to determine whether the
213 : * process is running or not.
214 : *
215 : * \param[in] pid The process identifier to wait on.
216 : * \param[in] sig Send that signal the first time.
217 : * \param[in] timeout The amount of time to wait for the process to quit.
218 : *
219 : * \return true if the process is still running after \p timeout.
220 : */
221 0 : bool is_running(pid_t pid, int sig, unsigned int timeout)
222 : {
223 0 : bool const result(kill(pid, sig) == 0);
224 0 : if(timeout == 0 || !result)
225 : {
226 0 : return result;
227 : }
228 :
229 0 : time_t const deadline(time(nullptr) + timeout);
230 0 : do
231 : {
232 : // the kill() function returns immediately so we have to
233 : // sleep otherwise it would loop very quickly...
234 : //
235 : // (I do not know of a way to poll() on a dying process
236 : // unless it is your direct child or we have a lock...)
237 : //
238 0 : sleep(1);
239 :
240 0 : if(kill(pid, 0) != 0)
241 : {
242 : // the process is dead now
243 : //
244 0 : return false;
245 : }
246 : }
247 0 : while(deadline > time(nullptr));
248 :
249 : // still running
250 : //
251 0 : return true;
252 : }
253 :
254 :
255 6 : } // namespace cppprocess
256 : // vim: ts=4 sw=4 et
|