1 |
commit: c9d7b9de78f1488827cb516bf61a54df28a750ab |
2 |
Author: Fabian Groffen <grobian <AT> gentoo <DOT> org> |
3 |
AuthorDate: Sun Oct 27 12:19:06 2019 +0000 |
4 |
Commit: Fabian Groffen <grobian <AT> gentoo <DOT> org> |
5 |
CommitDate: Sun Oct 27 12:19:06 2019 +0000 |
6 |
URL: https://gitweb.gentoo.org/proj/portage-utils.git/commit/?id=c9d7b9de |
7 |
|
8 |
qlop: take two at implementing currently running merges |
9 |
|
10 |
Probe /proc filesystem for running merges, and use the atoms found there |
11 |
to select the packages to list. In addition, prune any operations that |
12 |
started > 10 days ago so there will be some sort of convergence. |
13 |
|
14 |
Bug: https://bugs.gentoo.org/698196 |
15 |
Signed-off-by: Fabian Groffen <grobian <AT> gentoo.org> |
16 |
|
17 |
main.c | 3 +- |
18 |
man/qlop.1 | 4 +- |
19 |
qlop.c | 175 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- |
20 |
3 files changed, 173 insertions(+), 9 deletions(-) |
21 |
|
22 |
diff --git a/main.c b/main.c |
23 |
index 5d4c4cd..0b827fc 100644 |
24 |
--- a/main.c |
25 |
+++ b/main.c |
26 |
@@ -501,7 +501,8 @@ read_portage_env_file(const char *configroot, const char *file, env_vars vars[]) |
27 |
source_len = strlen(sfile); |
28 |
|
29 |
if (buflen <= source_len + file_path_len) |
30 |
- buf = xrealloc(buf, buflen = source_len + file_path_len + 1); |
31 |
+ buf = xrealloc(buf, |
32 |
+ buflen = source_len + file_path_len + 1); |
33 |
memmove(buf + file_path_len, buf + 7, source_len + 1); |
34 |
memcpy(buf, file, file_path_len); |
35 |
sfile = buf; |
36 |
|
37 |
diff --git a/man/qlop.1 b/man/qlop.1 |
38 |
index de1e525..58efd09 100644 |
39 |
--- a/man/qlop.1 |
40 |
+++ b/man/qlop.1 |
41 |
@@ -1,5 +1,5 @@ |
42 |
.\" generated by mkman.py, please do NOT edit! |
43 |
-.TH qlop "1" "Sep 2019" "Gentoo Foundation" "qlop" |
44 |
+.TH qlop "1" "Oct 2019" "Gentoo Foundation" "qlop" |
45 |
.SH NAME |
46 |
qlop \- emerge log analyzer |
47 |
.SH SYNOPSIS |
48 |
@@ -51,7 +51,7 @@ Print elapssed time in human readable format. This form uses |
49 |
minutes, hours and days instead of just seconds. |
50 |
.TP |
51 |
\fB\-M\fR, \fB\-\-machine\fR |
52 |
-Print elapsed time as seconds with no formatting. |
53 |
+Print start/elapsed time as seconds with no formatting. |
54 |
.TP |
55 |
\fB\-m\fR, \fB\-\-merge\fR |
56 |
Show merge history. |
57 |
|
58 |
diff --git a/qlop.c b/qlop.c |
59 |
index 7a93656..6cb2c04 100644 |
60 |
--- a/qlop.c |
61 |
+++ b/qlop.c |
62 |
@@ -16,9 +16,11 @@ |
63 |
#include <stdlib.h> |
64 |
#include <ctype.h> |
65 |
#include <limits.h> |
66 |
+#include <sys/stat.h> |
67 |
|
68 |
#include "atom.h" |
69 |
#include "eat_file.h" |
70 |
+#include "scandirat.h" |
71 |
#include "set.h" |
72 |
#include "xarray.h" |
73 |
#include "xasprintf.h" |
74 |
@@ -51,7 +53,7 @@ static const char * const qlop_opts_help[] = { |
75 |
"Print time taken to complete action", |
76 |
"Print average time taken to complete action", |
77 |
"Print elapsed time in human readable format (use with -t or -a)", |
78 |
- "Print elapsed time as seconds with no formatting", |
79 |
+ "Print start/elapsed time as seconds with no formatting", |
80 |
"Show merge history", |
81 |
"Show unmerge history", |
82 |
"Show autoclean unmerge history", |
83 |
@@ -201,10 +203,15 @@ parse_date(const char *sdate, time_t *t) |
84 |
static char _date_buf[48]; |
85 |
static char *fmt_date(struct qlop_mode *flags, time_t ts, time_t te) |
86 |
{ |
87 |
- time_t t; |
88 |
+ time_t t = flags->do_endtime ? te : ts; |
89 |
+ |
90 |
+ if (flags->do_machine) |
91 |
+ snprintf(_date_buf, sizeof(_date_buf), |
92 |
+ "%zd", (size_t)t); |
93 |
+ else |
94 |
+ strftime(_date_buf, sizeof(_date_buf), |
95 |
+ "%Y-%m-%dT%H:%M:%S", localtime(&t)); |
96 |
|
97 |
- t = flags->do_endtime ? te : ts; |
98 |
- strftime(_date_buf, sizeof(_date_buf), "%Y-%m-%dT%H:%M:%S", localtime(&t)); |
99 |
return _date_buf; |
100 |
} |
101 |
|
102 |
@@ -336,6 +343,21 @@ New format: |
103 |
1550953125: >>> unmerge success: app-admin/pwgen-2.08 |
104 |
1550953125: *** exiting successfully. |
105 |
1550953125: *** terminating. |
106 |
+ |
107 |
+ |
108 |
+Currently running merges can be found in the /proc filesystem: |
109 |
+- Linux: readlink(/proc/<pid>/fd/X) |
110 |
+- Solaris: readlink(/proc/<pid>/path/X) |
111 |
+from here a file should be there that points to the build.log file |
112 |
+$CAT/$P/work/build.log. If so, it's running for $CAT/$P. |
113 |
+This requires being the portage user though, or root. |
114 |
+ |
115 |
+Should there be no /proc, we can deduce from the log whether a package |
116 |
+is being emerged, if and only if, there are no parallel merges, and |
117 |
+portage never got interrupted in a way where it could not write its |
118 |
+interruption to the log. Unfortunately these scenarios happen a lot. |
119 |
+As such, we can try to remedy this somewhat by using a rule of thumb |
120 |
+that currently merging packages need to be withinin the last 10 days. |
121 |
*/ |
122 |
static int do_emerge_log( |
123 |
const char *log, |
124 |
@@ -463,6 +485,7 @@ static int do_emerge_log( |
125 |
tbegin = last_merge; |
126 |
tend = tstart; |
127 |
} |
128 |
+ |
129 |
/* loop over lines searching for atoms */ |
130 |
while (fgets(buf, sizeof(buf), fp) != NULL) { |
131 |
if ((p = strchr(buf, ':')) == NULL) |
132 |
@@ -821,11 +844,19 @@ static int do_emerge_log( |
133 |
} |
134 |
fclose(fp); |
135 |
if (flags->do_running) { |
136 |
+ time_t cutofftime; |
137 |
+ |
138 |
+ /* emerge.log can be interrupted, incorrect and hopelessly lost, |
139 |
+ * so to eliminate some unfinished crap from there, we just |
140 |
+ * ignore anything that's > cutofftime, 10 days for now. */ |
141 |
+ cutofftime = 10 * 24 * 60 * 60; /* when we consider entries stale */ |
142 |
+ |
143 |
/* can't report endtime for non-finished operations */ |
144 |
flags->do_endtime = 0; |
145 |
tstart = time(NULL); |
146 |
sync_time /= sync_cnt; |
147 |
- if (sync_start > 0) { |
148 |
+ if (sync_start >= tstart - cutofftime) { |
149 |
+ elapsed = tstart - sync_start; |
150 |
if (elapsed >= sync_time) |
151 |
sync_time = 0; |
152 |
if (flags->do_time) { |
153 |
@@ -847,6 +878,9 @@ static int do_emerge_log( |
154 |
time_t maxtime = 0; |
155 |
bool isMax = false; |
156 |
|
157 |
+ if (pkgw->tbegin < tstart - cutofftime) |
158 |
+ continue; |
159 |
+ |
160 |
snprintf(afmt, sizeof(afmt), "%s/%s", |
161 |
pkgw->atom->CATEGORY, pkgw->atom->PN); |
162 |
|
163 |
@@ -891,6 +925,9 @@ static int do_emerge_log( |
164 |
time_t maxtime = 0; |
165 |
bool isMax = false; |
166 |
|
167 |
+ if (pkgw->tbegin < tstart - cutofftime) |
168 |
+ continue; |
169 |
+ |
170 |
snprintf(afmt, sizeof(afmt), "%s/%s", |
171 |
pkgw->atom->CATEGORY, pkgw->atom->PN); |
172 |
|
173 |
@@ -978,6 +1015,118 @@ static int do_emerge_log( |
174 |
return 0; |
175 |
} |
176 |
|
177 |
+/* scan through /proc for running merges, this requires portage user |
178 |
+ * or root */ |
179 |
+static array_t *probe_proc(array_t *atoms) |
180 |
+{ |
181 |
+ struct dirent **procs; |
182 |
+ int procslen; |
183 |
+ int pi; |
184 |
+ struct dirent **links; |
185 |
+ int linkslen; |
186 |
+ int li; |
187 |
+ struct dirent *d; |
188 |
+ char npath[_Q_PATH_MAX * 2]; |
189 |
+ char rpath[_Q_PATH_MAX]; |
190 |
+ const char *subdir = NULL; |
191 |
+ const char *pid; |
192 |
+ ssize_t rpathlen; |
193 |
+ char *p; |
194 |
+ depend_atom *atom; |
195 |
+ DECLARE_ARRAY(ret_atoms); |
196 |
+ size_t i; |
197 |
+ |
198 |
+ /* /proc/<pid>/path/<[0-9]+link> |
199 |
+ * /proc/<pid>/fd/<[0-9]+link> */ |
200 |
+ if ((procslen = scandir("/proc", &procs, NULL, NULL)) > 0) { |
201 |
+ for (pi = 0; pi < procslen; pi++) { |
202 |
+ d = procs[pi]; |
203 |
+ /* must be [0-9]+ */ |
204 |
+ if (d->d_name[0] < '0' || d->d_name[0] > '9') |
205 |
+ continue; |
206 |
+ |
207 |
+ if (subdir == NULL) { |
208 |
+ struct stat st; |
209 |
+ |
210 |
+ snprintf(npath, sizeof(npath), "/proc/%s/path", d->d_name); |
211 |
+ if (stat(npath, &st) < 0) |
212 |
+ subdir = "fd"; |
213 |
+ else |
214 |
+ subdir = "path"; |
215 |
+ } |
216 |
+ |
217 |
+ pid = d->d_name; |
218 |
+ snprintf(npath, sizeof(npath), "/proc/%s/%s", pid, subdir); |
219 |
+ if ((linkslen = scandir(npath, &links, NULL, NULL)) > 0) { |
220 |
+ for (li = 0; li < linkslen; li++) { |
221 |
+ d = links[li]; |
222 |
+ /* must be [0-9]+ */ |
223 |
+ if (d->d_name[0] < '0' || d->d_name[0] > '9') |
224 |
+ continue; |
225 |
+ snprintf(npath, sizeof(npath), "/proc/%s/%s/%s", |
226 |
+ pid, subdir, d->d_name); |
227 |
+ rpathlen = readlink(npath, rpath, sizeof(rpath)); |
228 |
+ if (rpathlen <= 0) |
229 |
+ continue; |
230 |
+ rpath[rpathlen] = '\0'; |
231 |
+ /* check if this points to a portage build: |
232 |
+ * <somepath>/portage/<cat>/<pf>/temp/build.log */ |
233 |
+ if (strcmp(rpath + rpathlen - |
234 |
+ (sizeof("/temp/build.log") - 1), |
235 |
+ "/temp/build.log") == 0 && |
236 |
+ (p = strstr(rpath, "/portage/")) != NULL) |
237 |
+ { |
238 |
+ p += sizeof("/portage/") - 1; |
239 |
+ rpath[rpathlen - (sizeof("/temp/build.log") - 1)] = |
240 |
+ '\0'; |
241 |
+ atom = atom_explode(p); |
242 |
+ if (atom == NULL || |
243 |
+ atom->CATEGORY == NULL || atom->P == NULL) |
244 |
+ { |
245 |
+ if (atom != NULL) |
246 |
+ atom_implode(atom); |
247 |
+ continue; |
248 |
+ } |
249 |
+ xarraypush_ptr(ret_atoms, atom); |
250 |
+ } |
251 |
+ } |
252 |
+ scandir_free(links, linkslen); |
253 |
+ } |
254 |
+ } |
255 |
+ scandir_free(procs, procslen); |
256 |
+ } else { |
257 |
+ /* flag /proc doesn't exist */ |
258 |
+ return NULL; |
259 |
+ } |
260 |
+ |
261 |
+ if (array_cnt(atoms) > 0) { |
262 |
+ size_t j; |
263 |
+ depend_atom *atomr; |
264 |
+ |
265 |
+ /* calculate intersection */ |
266 |
+ array_for_each(atoms, i, atom) { |
267 |
+ array_for_each(ret_atoms, j, atomr) { |
268 |
+ if (atom_compare(atomr, atom) != EQUAL) { |
269 |
+ xarraydelete_ptr(ret_atoms, j); |
270 |
+ atom_implode(atomr); |
271 |
+ break; |
272 |
+ } |
273 |
+ } |
274 |
+ atom_implode(atom); |
275 |
+ } |
276 |
+ xarrayfree_int(atoms); |
277 |
+ } |
278 |
+ |
279 |
+ /* ret_atoms is allocated on the stack, so copy into atoms which is |
280 |
+ * empty at this point */ |
281 |
+ array_for_each(ret_atoms, i, atom) |
282 |
+ xarraypush_ptr(atoms, atom); |
283 |
+ |
284 |
+ xarrayfree_int(ret_atoms); |
285 |
+ |
286 |
+ return atoms; |
287 |
+} |
288 |
+ |
289 |
int qlop_main(int argc, char **argv) |
290 |
{ |
291 |
size_t i; |
292 |
@@ -1160,7 +1309,21 @@ int qlop_main(int argc, char **argv) |
293 |
m.fmt = "%[CATEGORY]%[PN]"; |
294 |
} |
295 |
|
296 |
- do_emerge_log(logfile, &m, atoms, start_time, end_time); |
297 |
+ if (m.do_running) { |
298 |
+ array_t *new_atoms = probe_proc(atoms); |
299 |
+ |
300 |
+ if (new_atoms != NULL && array_cnt(new_atoms) == 0) { |
301 |
+ /* proc supported, found nothing running */ |
302 |
+ start_time = LONG_MAX; |
303 |
+ } else { |
304 |
+ warn("/proc not available, deducing running " |
305 |
+ "merges from emerge.log"); |
306 |
+ } |
307 |
+ |
308 |
+ } |
309 |
+ |
310 |
+ if (start_time < LONG_MAX) |
311 |
+ do_emerge_log(logfile, &m, atoms, start_time, end_time); |
312 |
|
313 |
array_for_each(atoms, i, atom) |
314 |
atom_implode(atom); |