/[gentoo-projects]/portage-utils/qlop.c
Gentoo

Contents of /portage-utils/qlop.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.61 - (show annotations) (download) (as text)
Sun Oct 28 04:56:05 2012 UTC (20 months, 4 weeks ago) by vapier
Branch: MAIN
Changes since 1.60: +14 -13 lines
File MIME type: text/x-csrc
mark funcs as static to avoid duplicate prototypes

1 /*
2 * Copyright 2005-2010 Gentoo Foundation
3 * Distributed under the terms of the GNU General Public License v2
4 * $Header: /var/cvsroot/gentoo-projects/portage-utils/qlop.c,v 1.60 2012/10/28 04:16:19 vapier Exp $
5 *
6 * Copyright 2005-2010 Ned Ludd - <solar@gentoo.org>
7 * Copyright 2005-2010 Mike Frysinger - <vapier@gentoo.org>
8 */
9
10 #ifdef APPLET_qlop
11
12 #ifdef __linux__
13 # include <asm/param.h>
14 #endif
15
16 #ifdef __FreeBSD__
17 # include <kvm.h>
18 # include <sys/param.h>
19 # include <sys/sysctl.h>
20 # include <sys/user.h>
21 # include <sys/time.h>
22 #endif
23
24 #ifdef __MACH__
25 # include <stdlib.h>
26 # include <sys/types.h>
27 # include <sys/sysctl.h>
28 #endif
29
30 #define QLOP_DEFAULT_LOGFILE CONFIG_EPREFIX "var/log/emerge.log"
31
32 #define QLOP_FLAGS "gtHluscf:" COMMON_FLAGS
33 static struct option const qlop_long_opts[] = {
34 {"gauge", no_argument, NULL, 'g'},
35 {"time", no_argument, NULL, 't'},
36 {"human", no_argument, NULL, 'H'},
37 {"list", no_argument, NULL, 'l'},
38 {"unlist", no_argument, NULL, 'u'},
39 {"sync", no_argument, NULL, 's'},
40 {"current", no_argument, NULL, 'c'},
41 {"logfile", a_argument, NULL, 'f'},
42 COMMON_LONG_OPTS
43 };
44 static const char * const qlop_opts_help[] = {
45 "Gauge number of times a package has been merged",
46 "Calculate merge time for a specific package",
47 "Print seconds in human readable format (needs -t)",
48 "Show merge history",
49 "Show unmerge history",
50 "Show sync history",
51 "Show current emerging packages",
52 "Read emerge logfile instead of " QLOP_DEFAULT_LOGFILE,
53 COMMON_OPTS_HELP
54 };
55 static const char qlop_rcsid[] = "$Id: qlop.c,v 1.60 2012/10/28 04:16:19 vapier Exp $";
56 #define qlop_usage(ret) usage(ret, QLOP_FLAGS, qlop_long_opts, qlop_opts_help, lookup_applet_idx("qlop"))
57
58 #define QLOP_LIST 0x01
59 #define QLOP_UNLIST 0x02
60
61 _q_static void
62 print_seconds_for_earthlings(const unsigned long t)
63 {
64 unsigned dd, hh, mm, ss;
65 unsigned long tt = t;
66 ss = tt % 60; tt /= 60;
67 mm = tt % 60; tt /= 60;
68 hh = tt % 24; tt /= 24;
69 dd = tt;
70 if (dd) printf("%s%u%s day%s, ", GREEN, dd, NORM, (dd == 1 ? "" : "s"));
71 if (hh) printf("%s%u%s hour%s, ", GREEN, hh, NORM, (hh == 1 ? "" : "s"));
72 if (mm) printf("%s%u%s minute%s, ", GREEN, mm, NORM, (mm == 1 ? "" : "s"));
73 printf("%s%u%s second%s", GREEN, ss, NORM, (ss == 1 ? "" : "s"));
74 }
75
76 _q_static const char *
77 chop_ctime(time_t t)
78 {
79 static char ctime_out[50];
80 char *p;
81 snprintf(ctime_out, sizeof(ctime_out), "%s", ctime(&t));
82 if ((p = strchr(ctime_out, '\n')) != NULL)
83 *p = '\0';
84 return ctime_out;
85 }
86
87 _q_static unsigned long
88 show_merge_times(char *package, const char *logfile, int average, char human_readable)
89 {
90 FILE *fp;
91 char cat[126], buf[2][BUFSIZ];
92 char *pkg, *p, *q;
93 char ep[BUFSIZ];
94 unsigned long count, merge_time;
95 time_t t[2];
96 depend_atom *atom;
97 unsigned int parallel_emerge;
98
99 t[0] = t[1] = 0UL;
100 count = merge_time = 0;
101 cat[0] = 0;
102
103 if ((p = strchr(package, '/')) != NULL) {
104 pkg = p + 1;
105 strncpy(cat, package, sizeof(cat));
106 if ((p = strchr(cat, '/')) != NULL)
107 *p = 0;
108 } else {
109 pkg = package;
110 }
111
112 DBG("Searching for %s in %s\n", pkg, logfile);
113
114 if ((fp = fopen(logfile, "r")) == NULL) {
115 warnp("Could not open logfile '%s'", logfile);
116 return 1;
117 }
118
119 while (fgets(buf[0], sizeof(buf[0]), fp) != NULL) {
120 if (strstr(buf[0], pkg) == NULL)
121 continue;
122
123 if ((p = strchr(buf[0], '\n')) != NULL)
124 *p = 0;
125 if ((p = strchr(buf[0], ':')) == NULL)
126 continue;
127 *p = 0;
128 t[0] = atol(buf[0]);
129 strcpy(buf[1], p + 1);
130 rmspace(buf[1]);
131 if (strncmp(buf[1], ">>> emerge (", 12) == 0) {
132 snprintf(ep, BUFSIZ, "completed %s", &buf[1][4]);
133
134 char matched = 0;
135 if ((p = strchr(buf[1], ')')) == NULL)
136 continue;
137 *p = 0;
138 strcpy(buf[0], p + 1);
139 rmspace(buf[0]);
140 if ((p = strchr(buf[0], ' ')) == NULL)
141 continue;
142 *p = 0;
143 if ((atom = atom_explode(buf[0])) == NULL)
144 continue;
145
146 if (*cat) {
147 if ((strcmp(cat, atom->CATEGORY) == 0) && (strcmp(pkg, atom->PN) == 0))
148 matched = 1;
149 } else if (strcmp(pkg, atom->PN) == 0)
150 matched = 1;
151
152 if (matched) {
153 parallel_emerge = 0;
154 while (fgets(buf[0], sizeof(buf[0]), fp) != NULL) {
155 if ((p = strchr(buf[0], '\n')) != NULL)
156 *p = 0;
157 if ((p = strchr(buf[0], ':')) == NULL)
158 continue;
159 *p = 0;
160 t[1] = atol(buf[0]);
161 strcpy(buf[1], p + 1);
162 rmspace(buf[1]);
163
164 if (strncmp(buf[1], "Started emerge on:", 18) == 0) {
165 /* a parallel emerge was launched */
166 parallel_emerge++;
167 continue;
168 }
169
170 if (strncmp(buf[1], "*** terminating.", 16) == 0) {
171 if (parallel_emerge > 0) {
172 /* a parallel emerge has finished */
173 parallel_emerge--;
174 continue;
175 } else
176 /* the main emerge was stopped */
177 break;
178 }
179
180 /*
181 * pay attention to malformed log files (when the end of an emerge process
182 * is not indicated by the line '*** terminating'). We assume than the log is
183 * malformed when we find a parallel emerge process which is trying to
184 * emerge the same package
185 */
186 if (strncmp(buf[1], ">>> emerge (", 12) == 0 && parallel_emerge > 0) {
187 p = strchr(buf[1], ')');
188 q = strchr(ep, ')');
189 if (!p || !q)
190 continue;
191
192 if (!strcmp(p, q)) {
193 parallel_emerge--;
194 /* update the main emerge reference data */
195 snprintf(ep, BUFSIZ, "completed %s", &buf[1][4]);
196 continue;
197 }
198 }
199
200 if (strncmp(&buf[1][4], ep, BUFSIZ) == 0) {
201 if (!average) {
202 strcpy(buf[1], "");
203 if (verbose) {
204 if (atom->PR_int)
205 snprintf(buf[1], sizeof(buf[1]), "-%s-r%i", atom->PV, atom->PR_int);
206 else
207 snprintf(buf[1], sizeof(buf[1]), "-%s", atom->PV);
208 }
209 printf("%s%s%s%s: %s: ", BLUE, atom->PN, buf[1], NORM, chop_ctime(t[0]));
210 if (human_readable)
211 print_seconds_for_earthlings(t[1] - t[0]);
212 else
213 printf("%s%lu%s seconds", GREEN, (t[1] - t[0]), NORM);
214 puts("");
215 }
216 merge_time += (t[1] - t[0]);
217 count++;
218 break;
219 }
220 }
221 }
222 atom_implode(atom);
223 }
224 }
225 fclose(fp);
226 if (count == 0)
227 return 0;
228 if (average == 1) {
229 printf("%s%s%s: ", BLUE, pkg, NORM);
230 if (human_readable)
231 print_seconds_for_earthlings(merge_time / count);
232 else
233 printf("%s%lu%s seconds average", GREEN, merge_time / count, NORM);
234 printf(" for %s%lu%s merges\n", GREEN, count, NORM);
235 } else {
236 printf("%s%s%s: %s%lu%s times\n", BLUE, pkg, NORM, GREEN, count, NORM);
237 }
238 return 0;
239 }
240
241 _q_static void
242 show_emerge_history(char listflag, int argc, char **argv, const char *logfile)
243 {
244 FILE *fp;
245 size_t buflen;
246 char *buf, merged;
247 char *p, *q;
248 int i;
249 time_t t;
250
251 if ((fp = fopen(logfile, "r")) == NULL) {
252 warnp("Could not open logfile '%s'", logfile);
253 return;
254 }
255
256 buf = NULL;
257 while (getline(&buf, &buflen, fp) != -1) {
258 if (strlen(buf) < 30)
259 continue;
260
261 for (i = 0; i < argc; ++i)
262 if (strstr(buf, argv[i]) != NULL)
263 break;
264 if (argc && i == argc)
265 continue;
266
267 if ((p = strchr(buf, '\n')) != NULL)
268 *p = 0;
269 if ((p = strchr(buf, ':')) == NULL)
270 continue;
271 *p = 0;
272 q = p + 3;
273
274 t = (time_t) atol(buf);
275
276 if ((listflag & QLOP_LIST) && !strncmp(q, "::: completed emerge (", 22)) {
277 merged = 1;
278 if ((p = strchr(q, ')')) == NULL)
279 continue;
280 q = p + 2;
281 if ((p = strchr(q, ' ')) == NULL)
282 continue;
283 *p = 0;
284 } else if ((listflag & QLOP_UNLIST) && !strncmp(q, ">>> unmerge success: ", 21)) {
285 merged = 0;
286 if ((p = strchr(q, ':')) == NULL)
287 continue;
288 q = p + 2;
289 } else
290 continue;
291 if (!quiet)
292 printf("%s %s %s%s%s\n", chop_ctime(t), (merged ? ">>>" : "<<<"), (merged ? GREEN : RED), q, NORM);
293 else {
294 depend_atom *atom;
295 atom = atom_explode(q);
296 if (quiet == 1)
297 printf("%s ", chop_ctime(t));
298 if (quiet <= 2)
299 printf("%s ", (merged ? ">>>" : "<<<"));
300 printf("%s%s/%s%s\n", (merged ? GREEN : RED), atom->CATEGORY, atom->PN, NORM);
301 atom_implode(atom);
302 }
303 }
304
305 free(buf);
306 fclose(fp);
307 }
308
309 _q_static void
310 show_sync_history(const char *logfile)
311 {
312 FILE *fp;
313 size_t buflen;
314 char *buf, *p, *q;
315 time_t t;
316
317 if ((fp = fopen(logfile, "r")) == NULL) {
318 warnp("Could not open logfile '%s'", logfile);
319 return;
320 }
321
322 buf = NULL;
323 while (getline(&buf, &buflen, fp) != -1) {
324 if (strlen(buf) < 35)
325 continue;
326 if (strncmp(buf+12, "=== Sync completed with", 23) != 0)
327 continue;
328
329 if ((p = strchr(buf, '\n')) != NULL)
330 *p = 0;
331 if ((p = strchr(buf, ':')) == NULL)
332 continue;
333 *p = 0;
334 q = p+2;
335
336 t = (time_t)atol(buf);
337
338 if ((p = strstr(q, "with")) == NULL)
339 continue;
340 q = p + 5;
341
342 printf("%s >>> %s%s%s\n", chop_ctime(t), GREEN, q, NORM);
343 }
344
345 free(buf);
346 fclose(fp);
347 }
348
349 _q_static void show_current_emerge(void);
350 #ifdef __linux__
351 #include <elf.h>
352 static unsigned long hz = 0;
353 static void init_hz(void)
354 {
355 #ifdef HZ
356 hz = HZ;
357 #endif
358 /* kernel pushes elf notes onto stack */
359 unsigned long *elf_note = (unsigned long *)environ;
360 while (!*elf_note++)
361 continue;
362 while (elf_note[0]) {
363 if (elf_note[0] == AT_CLKTCK) {
364 hz = elf_note[1];
365 break;
366 }
367 elf_note += 2;
368 }
369 if (!hz)
370 hz = 100;
371 }
372
373 static char *root_readlink(const int pid)
374 {
375 static char path[_Q_PATH_MAX];
376 char buf[_Q_PATH_MAX];
377 memset(&path, 0, sizeof(path));
378 snprintf(buf, sizeof(buf), "/proc/%d/root", pid);
379 if (readlink(buf, path, sizeof(path) - 1) == -1)
380 return NULL;
381 else
382 return path;
383 }
384
385 void show_current_emerge(void)
386 {
387 DIR *proc;
388 struct dirent *de;
389 pid_t pid;
390 char buf[BUFSIZE], bufstat[300];
391 char path[50];
392 char *p, *q;
393 unsigned long long start_time = 0;
394 double uptime_secs;
395 time_t start_date;
396
397 if ((proc = opendir("/proc")) == NULL) {
398 warnp("Could not open /proc");
399 return;
400 }
401
402 if (!hz)
403 init_hz();
404
405 while ((de = readdir(proc)) != NULL) {
406
407 if ((pid = (pid_t)atol(de->d_name)) == 0)
408 continue;
409
410 /* portage renames the cmdline so the package name is first */
411 snprintf(path, sizeof(path), "/proc/%i/cmdline", pid);
412 if (!eat_file(path, buf, sizeof(buf)))
413 continue;
414
415 if (buf[0] == '[' && (p = strchr(buf, ']')) != NULL && strstr(buf, "sandbox") != NULL) {
416 *p = '\0';
417 p = buf+1;
418 q = p + strlen(p) + 1;
419
420 /* open the stat file to figure out how long we have been running */
421 snprintf(path, sizeof(path), "/proc/%i/stat", pid);
422 if (!eat_file(path, bufstat, sizeof(bufstat)))
423 continue;
424
425 /* ripped from procps/proc/readproc.c */
426 if ((q = strchr(bufstat, ')')) == NULL)
427 continue;
428 /* grab the start time */
429 sscanf(q + 2,
430 "%*c "
431 "%*d %*d %*d %*d %*d "
432 "%*u %*u %*u %*u %*u "
433 "%*u %*u %*u %*u "
434 "%*d %*d "
435 "%*d "
436 "%*d "
437 "%llu ",
438 &start_time);
439 /* get uptime */
440 if (!eat_file("/proc/uptime", bufstat, sizeof(bufstat)))
441 continue;
442 sscanf(bufstat, "%lf", &uptime_secs);
443
444 /* figure out when this thing started and then show it */
445 start_date = time(0) - (uptime_secs - (start_time / hz));
446 printf(
447 " %s*%s %s%s%s\n"
448 " started: %s%s%s\n"
449 " elapsed: ", /*%s%llu%s seconds\n",*/
450 BOLD, NORM, BLUE, p, NORM,
451 GREEN, chop_ctime(start_date), NORM);
452 print_seconds_for_earthlings(uptime_secs - (start_time / hz));
453 puts(NORM);
454 p = root_readlink(pid);
455 if (p && strcmp(p, "/"))
456 printf(" chroot: %s%s%s\n", GREEN, p, NORM);
457 }
458 }
459
460 closedir(proc);
461
462 if (start_time == 0 && verbose)
463 puts("No emerge processes located");
464 }
465 #elif defined(__FreeBSD__)
466 void show_current_emerge(void)
467 {
468 kvm_t *kd = NULL;
469 struct kinfo_proc *ip;
470 int i; int total_processes;
471 char *p, *q;
472 time_t start_date = 0;
473
474 if (! (kd = kvm_open("/dev/null", "/dev/null", "/dev/null", O_RDONLY, "kvm_open"))) {
475 warnp("Could not open kvm: %s", kvm_geterr(kd));
476 return;
477 }
478
479 ip = kvm_getprocs(kd, KERN_PROC_PROC, 0, &total_processes);
480
481 for (i = 0; i < total_processes; i++) {
482 char **proc_argv = NULL;
483 char *buf = NULL;
484
485 if (strcmp(ip[i].ki_comm, "sandbox") != 0)
486 continue;
487
488 proc_argv = kvm_getargv(kd, &(ip[i]), 0);
489
490 if (!proc_argv || (buf = xstrdup(proc_argv[0])) == NULL ||
491 buf[0] != '[' || (p = strchr(buf, ']')) == NULL) {
492 free(buf);
493 continue;
494 }
495
496 *p = '\0';
497 p = buf+1;
498 q = p + strlen(p) + 1;
499
500 printf(
501 " %s*%s %s%s%s\n"
502 " started: %s%s%s\n"
503 " elapsed: ", /*%s%llu%s seconds\n",*/
504 BOLD, NORM, BLUE, p, NORM,
505 GREEN, chop_ctime(ip[i].ki_start.tv_sec), NORM);
506 print_seconds_for_earthlings(time(0) - ip[i].ki_start.tv_sec);
507 puts(NORM);
508
509 free(buf);
510 }
511
512 if (start_date == 0 && verbose)
513 puts("No emerge processes located");
514 }
515 #elif defined(__MACH__)
516 void show_current_emerge(void)
517 {
518 int mib[3];
519 size_t size = 0;
520 struct kinfo_proc *ip, *raip;
521 int ret, total_processes, i;
522 char *p, *q;
523 time_t start_date = 0;
524 char args[512];
525
526 mib[0] = CTL_KERN;
527 mib[1] = KERN_PROC;
528 mib[2] = KERN_PROC_ALL; /* could restrict to _UID (effective uid) */
529
530 /* probe once to get the current size; estimate only, but OS tries
531 * to round up if it can predict a sudden growth, so optimise below
532 * for the optimistic case */
533 ret = sysctl(mib, 3, NULL, &size, NULL, 0);
534 ip = xmalloc(sizeof(*ip) * size);
535 while (1) {
536 ret = sysctl(mib, 3, ip, &size, NULL, 0);
537 if (ret >= 0 && errno == ENOMEM) {
538 size += size / 10; /* may be a bit overdone... */
539 raip = realloc(ip, sizeof(struct kinfo_proc) * size);
540 if (raip == NULL) {
541 free(ip);
542 warnp("Could not extend allocated block to %d bytes for process information",
543 sizeof(struct kinfo_proc) * size);
544 return;
545 }
546 ip = raip;
547 } else if (ret < 0) {
548 free(ip);
549 warnp("Could not retrieve process information");
550 return;
551 } else {
552 break;
553 }
554 }
555
556 total_processes = size / sizeof(struct kinfo_proc);
557
558 /* initialise mib for argv retrieval calls */
559 mib[0] = CTL_KERN;
560 mib[1] = KERN_PROCARGS;
561
562 for (i = 0; i < total_processes; i++) {
563 char *buf = NULL;
564 size_t argssize = sizeof(args);
565
566 if (strcmp(ip[i].kp_proc.p_comm, "sandbox") != 0)
567 continue;
568
569 mib[2] = ip[i].kp_proc.p_pid;
570 if (sysctl(mib, 3, args, &argssize, NULL, 0) != 0) {
571 free(ip);
572 return;
573 }
574
575 /* this is magic to get back up in the stack where the arguments
576 * start */
577 for (buf = args; buf < &args[argssize]; buf++)
578 if (*buf == '\0')
579 break;
580 if (buf == &args[argssize]) {
581 free(ip);
582 continue;
583 }
584 if ((buf = xstrdup(buf)) == NULL ||
585 buf[0] != '[' || (p = strchr(buf, ']')) == NULL) {
586 free(buf);
587 continue;
588 }
589
590 *p = '\0';
591 p = buf+1;
592 q = p + strlen(p) + 1;
593
594 printf(
595 " %s*%s %s%s%s\n"
596 " started: %s%s%s\n"
597 " elapsed: ", /*%s%llu%s seconds\n",*/
598 BOLD, NORM, BLUE, p, NORM,
599 GREEN, chop_ctime(ip[i].kp_proc.p_starttime.tv_sec), NORM);
600 print_seconds_for_earthlings(time(0) - ip[i].kp_proc.p_starttime.tv_sec);
601 puts(NORM);
602
603 free(buf);
604 }
605
606 free(ip);
607
608 if (start_date == 0 && verbose)
609 puts("No emerge processes located");
610 }
611 #else
612 void show_current_emerge(void)
613 {
614 errf("not supported on your OS");
615 }
616 #endif
617
618 int qlop_main(int argc, char **argv)
619 {
620 int i, average = 1;
621 char do_time, do_list, do_unlist, do_sync, do_current, do_human_readable = 0;
622 char *opt_logfile;
623 const char *logfile = QLOP_DEFAULT_LOGFILE;
624
625 DBG("argc=%d argv[0]=%s argv[1]=%s",
626 argc, argv[0], argc > 1 ? argv[1] : "NULL?");
627
628 opt_logfile = NULL;
629 do_time = do_list = do_unlist = do_sync = do_current = 0;
630
631 while ((i = GETOPT_LONG(QLOP, qlop, "")) != -1) {
632 switch (i) {
633 COMMON_GETOPTS_CASES(qlop)
634
635 case 't': do_time = 1; break;
636 case 'l': do_list = 1; break;
637 case 'u': do_unlist = 1; break;
638 case 's': do_sync = 1; break;
639 case 'c': do_current = 1; break;
640 case 'g': do_time = 1; average = 0; break;
641 case 'H': do_human_readable = 1; break;
642 case 'f':
643 if (opt_logfile) err("Only use -f once");
644 opt_logfile = xstrdup(optarg);
645 break;
646 }
647 }
648 if (!do_list && !do_unlist && !do_time && !do_sync && !do_current)
649 qlop_usage(EXIT_FAILURE);
650 if (opt_logfile != NULL)
651 logfile = opt_logfile;
652
653 argc -= optind;
654 argv += optind;
655
656 if (do_list && do_unlist)
657 show_emerge_history(QLOP_LIST | QLOP_UNLIST, argc, argv, logfile);
658 else if (do_list)
659 show_emerge_history(QLOP_LIST, argc, argv, logfile);
660 else if (do_unlist)
661 show_emerge_history(QLOP_UNLIST, argc, argv, logfile);
662 if (do_current)
663 show_current_emerge();
664 if (do_sync)
665 show_sync_history(logfile);
666
667 if (do_time) {
668 for (i = 0; i < argc; ++i)
669 show_merge_times(argv[i], logfile, average, do_human_readable);
670 }
671
672 if (opt_logfile) free(opt_logfile);
673
674 return EXIT_SUCCESS;
675 }
676
677 #else
678 DEFINE_APPLET_STUB(qlop)
679 #endif

  ViewVC Help
Powered by ViewVC 1.1.20