aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFabian Groffen <grobian@gentoo.org>2019-10-27 13:19:06 +0100
committerFabian Groffen <grobian@gentoo.org>2019-10-27 13:19:06 +0100
commitc9d7b9de78f1488827cb516bf61a54df28a750ab (patch)
treecd9a14b85e102484d954b4d8cb832f440fbd19f8
parentTODO: add entry for qlop -r using /proc (diff)
downloadportage-utils-c9d7b9de.tar.gz
portage-utils-c9d7b9de.tar.bz2
portage-utils-c9d7b9de.zip
qlop: take two at implementing currently running merges
Probe /proc filesystem for running merges, and use the atoms found there to select the packages to list. In addition, prune any operations that started > 10 days ago so there will be some sort of convergence. Bug: https://bugs.gentoo.org/698196 Signed-off-by: Fabian Groffen <grobian@gentoo.org>
-rw-r--r--main.c3
-rw-r--r--man/qlop.14
-rw-r--r--qlop.c175
3 files changed, 173 insertions, 9 deletions
diff --git a/main.c b/main.c
index 5d4c4cd..0b827fc 100644
--- a/main.c
+++ b/main.c
@@ -501,7 +501,8 @@ read_portage_env_file(const char *configroot, const char *file, env_vars vars[])
source_len = strlen(sfile);
if (buflen <= source_len + file_path_len)
- buf = xrealloc(buf, buflen = source_len + file_path_len + 1);
+ buf = xrealloc(buf,
+ buflen = source_len + file_path_len + 1);
memmove(buf + file_path_len, buf + 7, source_len + 1);
memcpy(buf, file, file_path_len);
sfile = buf;
diff --git a/man/qlop.1 b/man/qlop.1
index de1e525..58efd09 100644
--- a/man/qlop.1
+++ b/man/qlop.1
@@ -1,5 +1,5 @@
.\" generated by mkman.py, please do NOT edit!
-.TH qlop "1" "Sep 2019" "Gentoo Foundation" "qlop"
+.TH qlop "1" "Oct 2019" "Gentoo Foundation" "qlop"
.SH NAME
qlop \- emerge log analyzer
.SH SYNOPSIS
@@ -51,7 +51,7 @@ Print elapssed time in human readable format. This form uses
minutes, hours and days instead of just seconds.
.TP
\fB\-M\fR, \fB\-\-machine\fR
-Print elapsed time as seconds with no formatting.
+Print start/elapsed time as seconds with no formatting.
.TP
\fB\-m\fR, \fB\-\-merge\fR
Show merge history.
diff --git a/qlop.c b/qlop.c
index 7a93656..6cb2c04 100644
--- a/qlop.c
+++ b/qlop.c
@@ -16,9 +16,11 @@
#include <stdlib.h>
#include <ctype.h>
#include <limits.h>
+#include <sys/stat.h>
#include "atom.h"
#include "eat_file.h"
+#include "scandirat.h"
#include "set.h"
#include "xarray.h"
#include "xasprintf.h"
@@ -51,7 +53,7 @@ static const char * const qlop_opts_help[] = {
"Print time taken to complete action",
"Print average time taken to complete action",
"Print elapsed time in human readable format (use with -t or -a)",
- "Print elapsed time as seconds with no formatting",
+ "Print start/elapsed time as seconds with no formatting",
"Show merge history",
"Show unmerge history",
"Show autoclean unmerge history",
@@ -201,10 +203,15 @@ parse_date(const char *sdate, time_t *t)
static char _date_buf[48];
static char *fmt_date(struct qlop_mode *flags, time_t ts, time_t te)
{
- time_t t;
+ time_t t = flags->do_endtime ? te : ts;
+
+ if (flags->do_machine)
+ snprintf(_date_buf, sizeof(_date_buf),
+ "%zd", (size_t)t);
+ else
+ strftime(_date_buf, sizeof(_date_buf),
+ "%Y-%m-%dT%H:%M:%S", localtime(&t));
- t = flags->do_endtime ? te : ts;
- strftime(_date_buf, sizeof(_date_buf), "%Y-%m-%dT%H:%M:%S", localtime(&t));
return _date_buf;
}
@@ -336,6 +343,21 @@ New format:
1550953125: >>> unmerge success: app-admin/pwgen-2.08
1550953125: *** exiting successfully.
1550953125: *** terminating.
+
+
+Currently running merges can be found in the /proc filesystem:
+- Linux: readlink(/proc/<pid>/fd/X)
+- Solaris: readlink(/proc/<pid>/path/X)
+from here a file should be there that points to the build.log file
+$CAT/$P/work/build.log. If so, it's running for $CAT/$P.
+This requires being the portage user though, or root.
+
+Should there be no /proc, we can deduce from the log whether a package
+is being emerged, if and only if, there are no parallel merges, and
+portage never got interrupted in a way where it could not write its
+interruption to the log. Unfortunately these scenarios happen a lot.
+As such, we can try to remedy this somewhat by using a rule of thumb
+that currently merging packages need to be withinin the last 10 days.
*/
static int do_emerge_log(
const char *log,
@@ -463,6 +485,7 @@ static int do_emerge_log(
tbegin = last_merge;
tend = tstart;
}
+
/* loop over lines searching for atoms */
while (fgets(buf, sizeof(buf), fp) != NULL) {
if ((p = strchr(buf, ':')) == NULL)
@@ -821,11 +844,19 @@ static int do_emerge_log(
}
fclose(fp);
if (flags->do_running) {
+ time_t cutofftime;
+
+ /* emerge.log can be interrupted, incorrect and hopelessly lost,
+ * so to eliminate some unfinished crap from there, we just
+ * ignore anything that's > cutofftime, 10 days for now. */
+ cutofftime = 10 * 24 * 60 * 60; /* when we consider entries stale */
+
/* can't report endtime for non-finished operations */
flags->do_endtime = 0;
tstart = time(NULL);
sync_time /= sync_cnt;
- if (sync_start > 0) {
+ if (sync_start >= tstart - cutofftime) {
+ elapsed = tstart - sync_start;
if (elapsed >= sync_time)
sync_time = 0;
if (flags->do_time) {
@@ -847,6 +878,9 @@ static int do_emerge_log(
time_t maxtime = 0;
bool isMax = false;
+ if (pkgw->tbegin < tstart - cutofftime)
+ continue;
+
snprintf(afmt, sizeof(afmt), "%s/%s",
pkgw->atom->CATEGORY, pkgw->atom->PN);
@@ -891,6 +925,9 @@ static int do_emerge_log(
time_t maxtime = 0;
bool isMax = false;
+ if (pkgw->tbegin < tstart - cutofftime)
+ continue;
+
snprintf(afmt, sizeof(afmt), "%s/%s",
pkgw->atom->CATEGORY, pkgw->atom->PN);
@@ -978,6 +1015,118 @@ static int do_emerge_log(
return 0;
}
+/* scan through /proc for running merges, this requires portage user
+ * or root */
+static array_t *probe_proc(array_t *atoms)
+{
+ struct dirent **procs;
+ int procslen;
+ int pi;
+ struct dirent **links;
+ int linkslen;
+ int li;
+ struct dirent *d;
+ char npath[_Q_PATH_MAX * 2];
+ char rpath[_Q_PATH_MAX];
+ const char *subdir = NULL;
+ const char *pid;
+ ssize_t rpathlen;
+ char *p;
+ depend_atom *atom;
+ DECLARE_ARRAY(ret_atoms);
+ size_t i;
+
+ /* /proc/<pid>/path/<[0-9]+link>
+ * /proc/<pid>/fd/<[0-9]+link> */
+ if ((procslen = scandir("/proc", &procs, NULL, NULL)) > 0) {
+ for (pi = 0; pi < procslen; pi++) {
+ d = procs[pi];
+ /* must be [0-9]+ */
+ if (d->d_name[0] < '0' || d->d_name[0] > '9')
+ continue;
+
+ if (subdir == NULL) {
+ struct stat st;
+
+ snprintf(npath, sizeof(npath), "/proc/%s/path", d->d_name);
+ if (stat(npath, &st) < 0)
+ subdir = "fd";
+ else
+ subdir = "path";
+ }
+
+ pid = d->d_name;
+ snprintf(npath, sizeof(npath), "/proc/%s/%s", pid, subdir);
+ if ((linkslen = scandir(npath, &links, NULL, NULL)) > 0) {
+ for (li = 0; li < linkslen; li++) {
+ d = links[li];
+ /* must be [0-9]+ */
+ if (d->d_name[0] < '0' || d->d_name[0] > '9')
+ continue;
+ snprintf(npath, sizeof(npath), "/proc/%s/%s/%s",
+ pid, subdir, d->d_name);
+ rpathlen = readlink(npath, rpath, sizeof(rpath));
+ if (rpathlen <= 0)
+ continue;
+ rpath[rpathlen] = '\0';
+ /* check if this points to a portage build:
+ * <somepath>/portage/<cat>/<pf>/temp/build.log */
+ if (strcmp(rpath + rpathlen -
+ (sizeof("/temp/build.log") - 1),
+ "/temp/build.log") == 0 &&
+ (p = strstr(rpath, "/portage/")) != NULL)
+ {
+ p += sizeof("/portage/") - 1;
+ rpath[rpathlen - (sizeof("/temp/build.log") - 1)] =
+ '\0';
+ atom = atom_explode(p);
+ if (atom == NULL ||
+ atom->CATEGORY == NULL || atom->P == NULL)
+ {
+ if (atom != NULL)
+ atom_implode(atom);
+ continue;
+ }
+ xarraypush_ptr(ret_atoms, atom);
+ }
+ }
+ scandir_free(links, linkslen);
+ }
+ }
+ scandir_free(procs, procslen);
+ } else {
+ /* flag /proc doesn't exist */
+ return NULL;
+ }
+
+ if (array_cnt(atoms) > 0) {
+ size_t j;
+ depend_atom *atomr;
+
+ /* calculate intersection */
+ array_for_each(atoms, i, atom) {
+ array_for_each(ret_atoms, j, atomr) {
+ if (atom_compare(atomr, atom) != EQUAL) {
+ xarraydelete_ptr(ret_atoms, j);
+ atom_implode(atomr);
+ break;
+ }
+ }
+ atom_implode(atom);
+ }
+ xarrayfree_int(atoms);
+ }
+
+ /* ret_atoms is allocated on the stack, so copy into atoms which is
+ * empty at this point */
+ array_for_each(ret_atoms, i, atom)
+ xarraypush_ptr(atoms, atom);
+
+ xarrayfree_int(ret_atoms);
+
+ return atoms;
+}
+
int qlop_main(int argc, char **argv)
{
size_t i;
@@ -1160,7 +1309,21 @@ int qlop_main(int argc, char **argv)
m.fmt = "%[CATEGORY]%[PN]";
}
- do_emerge_log(logfile, &m, atoms, start_time, end_time);
+ if (m.do_running) {
+ array_t *new_atoms = probe_proc(atoms);
+
+ if (new_atoms != NULL && array_cnt(new_atoms) == 0) {
+ /* proc supported, found nothing running */
+ start_time = LONG_MAX;
+ } else {
+ warn("/proc not available, deducing running "
+ "merges from emerge.log");
+ }
+
+ }
+
+ if (start_time < LONG_MAX)
+ do_emerge_log(logfile, &m, atoms, start_time, end_time);
array_for_each(atoms, i, atom)
atom_implode(atom);