Newer
Older
/*
* Top users/processes display for Unix
* Version 3
*
* This program may be freely redistributed,
* but this entire comment MUST remain intact.
*
* Copyright (c) 1984, 1989, William LeFebvre, Rice University
* Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
*/
/*
* This file contains the routines that implement some of the interactive
* mode commands. Note that some of the commands are implemented in-line
* in "main". This is necessary because they change the global state of
* "top" (i.e.: changing the number of processes to display).
*/
#include "os.h"
#include <ctype.h>
#include <signal.h>
#include <errno.h>
#ifdef HAVE_SYS_RESOURCE_H
#include <sys/resource.h>
#endif
#include <unistd.h>
#include "pg_top.h"
#include "boolean.h"
#include "utils.h"
#include "version.h"
#include "machine.h"
#include "help.h"
#include "display.h"
#include "pg.h"
#include "commands.h"
extern int errno;
extern char *copyright;
/* imported from screen.c */
extern int overstrike;
#define BEGIN "BEGIN;"
#define ROLLBACK "ROLLBACK;"
struct cmd cmd_map[] = {
{'\014', cmd_redraw},
{' ', cmd_update},
{'?', cmd_help},
{'c', cmd_cmdline},
#ifdef ENABLE_COLOR
{'C', cmd_color},
{'d', cmd_displays},
{'E', cmd_explain},
{'i', cmd_idletog},
{'I', cmd_io},
{'L', cmd_locks},
{'n', cmd_number},
{'o', cmd_order},
{'q', cmd_quit},
{'Q', cmd_current_query},
{'s', cmd_delay},
{'u', cmd_user},
int
cmd_activity(struct pg_top_context *pgtctx)
{
pgtctx->mode = MODE_PROCESSES;
pgtctx->header_text =
pgtctx->header_options[pgtctx->mode_remote][pgtctx->mode];
reset_display(pgtctx);
return No;
}
#ifdef ENABLE_COLOR
int
cmd_color(struct pg_top_context *pgtctx)
{
reset_display(pgtctx);
if (pgtctx->color_on)
{
pgtctx->color_on = 0;
display_resize(); /* To realloc screenbuf */
new_message(MT_standout | MT_delayed, " Color off");
}
else
{
if (!smart_terminal)
{
new_message(MT_standout | MT_delayed,
" Sorry, cannot do colors on this terminal type");
}
else
{
pgtctx->color_on = 1;
new_message(MT_standout | MT_delayed, " Color on");
}
}
return No;
}
int
cmd_cmdline(struct pg_top_context *pgtctx)
{
if (pgtctx->statics.flags.fullcmds)
{
pgtctx->ps.fullcmd = (pgtctx->ps.fullcmd + 1) % 3;
switch (pgtctx->ps.fullcmd)
{
case 2:
new_message(MT_standout | MT_delayed, " Displaying current query.");
break;
case 1:
new_message(MT_standout | MT_delayed,
" Displaying full command lines.");
break;
case 0:
default:
new_message(MT_standout | MT_delayed,
" Not displaying full command lines.");
}
}
else
{
new_message(MT_standout, " Full command display not supported.");
/* no_command = Yes; */
}
putchar('\r');
return No;
}
int
cmd_current_query(struct pg_top_context *pgtctx)
{
new_message(MT_standout, "Current query of process: ");
newval = readline(tempbuf1, 8, Yes);
reset_display(pgtctx);
display_pagerstart();
show_current_query(&pgtctx->conninfo, newval);
display_pagerend();
return No;
}
int
cmd_delay(struct pg_top_context *pgtctx)
{
new_message(MT_standout, "Seconds to delay: ");
if ((i = readline(tempbuf, 8, Yes)) > -1)
{
if ((pgtctx->delay = i) == 0 && getuid() != 0)
{
pgtctx->delay = 1;
}
}
clear_message();
return No;
}
int
cmd_displays(struct pg_top_context *pgtctx)
{
new_message(MT_standout, "Displays to show (currently %s): ",
pgtctx->displays == -1 ? "infinite" : itoa(pgtctx->displays));
if ((i = readline(tempbuf, 10, Yes)) > 0)
{
pgtctx->displays = i;
}
else if (i == 0)
{
quit(0);
}
clear_message();
return No;
}
int
cmd_explain(struct pg_top_context *pgtctx)
{
new_message(MT_standout, "Re-determine execution plan: ");
newval = readline(tempbuf1, 8, Yes);
reset_display(pgtctx);
display_pagerstart();
show_explain(&pgtctx->conninfo, newval, EXPLAIN);
display_pagerend();
return No;
}
int
cmd_explain_analyze(struct pg_top_context *pgtctx)
{
new_message(MT_standout, "Re-run SQL for analysis: ");
newval = readline(tempbuf1, 8, Yes);
reset_display(pgtctx);
display_pagerstart();
show_explain(&pgtctx->conninfo, newval, EXPLAIN_ANALYZE);
display_pagerend();
return No;
}
int
cmd_help(struct pg_top_context *pgtctx)
{
reset_display(pgtctx);
display_pagerstart();
show_help(&pgtctx->statics);
display_pagerend();
return No;
}
int
cmd_idletog(struct pg_top_context *pgtctx)
{
pgtctx->ps.idle = !pgtctx->ps.idle;
new_message(MT_standout | MT_delayed, " %sisplaying idle processes.",
putchar('\r');
return No;
}
int
cmd_io(struct pg_top_context *pgtctx)
{
pgtctx->mode = MODE_IO_STATS;
pgtctx->header_text =
pgtctx->header_options[pgtctx->mode_remote][pgtctx->mode];
reset_display(pgtctx);
return No;
}
int
cmd_locks(struct pg_top_context *pgtctx)
{
new_message(MT_standout, "Show locks held by process: ");
newval = readline(tempbuf1, 8, Yes);
reset_display(pgtctx);
display_pagerstart();
show_locks(&pgtctx->conninfo, newval);
display_pagerend();
return No;
}
int
cmd_number(struct pg_top_context *pgtctx)
{
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
new_message(MT_standout, "Number of processes to show: ");
newval = readline(tempbuf, 8, Yes);
if (newval > -1)
{
if (newval > max_topn)
{
new_message(MT_standout | MT_delayed,
" This terminal can only display %d processes.",
max_topn);
putchar('\r');
}
if (newval == 0)
{
/* inhibit the header */
display_header(No);
}
else if (newval > pgtctx->topn && pgtctx->topn == 0)
{
/* redraw the header */
display_header(Yes);
pgtctx->d_header = i_header;
}
pgtctx->topn = newval;
}
return No;
}
int
cmd_quit(struct pg_top_context *pgtctx)
{
quit(0);
/* NOT REACHED */
return No;
}
int
cmd_replication(struct pg_top_context *pgtctx)
{
pgtctx->mode = MODE_REPLICATION;
pgtctx->header_text =
pgtctx->header_options[pgtctx->mode_remote][pgtctx->mode];
reset_display(pgtctx);
return No;
}
int
cmd_order(struct pg_top_context *pgtctx)
{
int i;
int no_command = No;
char tempbuf[50];
if (pgtctx->statics.order_names == NULL)
{
new_message(MT_standout, " Ordering not supported.");
putchar('\r');
no_command = Yes;
}
else
{
new_message(MT_standout, "Order to sort: ");
if (readline(tempbuf, sizeof(tempbuf), No) > 0)
{
i = string_index(tempbuf, pgtctx->statics.order_names);
if (i == -1)
{
new_message(MT_standout, " %s: unrecognized sorting order",
tempbuf);
no_command = Yes;
}
else
{
}
putchar('\r');
}
else
{
}
}
return no_command;
}
int
cmd_order_cpu(struct pg_top_context *pgtctx)
{
if ((i = string_index("cpu", pgtctx->statics.order_names)) == -1)
{
new_message(MT_standout, " Unrecognized sorting order");
putchar('\r');
/* no_command = Yes; */
}
else
{
pgtctx->order_index = i;
}
return No;
}
int
cmd_order_mem(struct pg_top_context *pgtctx)
{
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
if ((i = string_index("size", pgtctx->statics.order_names)) == -1)
{
new_message(MT_standout, " Unrecognized sorting order");
putchar('\r');
return Yes;
}
else
{
pgtctx->order_index = i;
}
return No;
}
int
cmd_redraw(struct pg_top_context *pgtctx)
{
reset_display(pgtctx);
return No;
}
int
cmd_update(struct pg_top_context *pgtctx)
{
/* go home for visual feedback */
go_home();
fflush(stdout);
return No;
}
int
cmd_user(struct pg_top_context *pgtctx)
{
new_message(MT_standout, "Username to show: ");
if (readline(pgtctx->ps.usename, sizeof(pgtctx->ps.usename), No) > 0)
{
putchar('\r');
}
else
{
clear_message();
}
int
execute_command(struct pg_top_context *pgtctx, char ch)
{
struct cmd *cmap;
cmap = cmd_map;
while (cmap->func != NULL)
{
if (cmap->ch == ch)
{
}
++cmap;
}
return No;
}
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
/*
* str_adderr(str, len, err) - add an explanation of error "err" to
* the string "str".
*/
int
str_adderr(char *str, int len, int err)
{
register char *msg;
register int msglen;
msg = err == 0 ? "Not a number" : errmsg(err);
msglen = strlen(msg) + 2;
if (len <= msglen)
{
return (0);
}
(void) strcat(str, ": ");
(void) strcat(str, msg);
return (len - msglen);
}
/*
* str_addarg(str, len, arg, first) - add the string argument "arg" to
* the string "str". This is the first in the group when "first"
* is set (indicating that a comma should NOT be added to the front).
*/
int
str_addarg(char *str, int len, char *arg, int first)
{
register int arglen;
arglen = strlen(arg);
if (!first)
{
arglen += 2;
}
if (len <= arglen)
{
return (0);
}
if (!first)
{
(void) strcat(str, ", ");
}
(void) strcat(str, arg);
return (len - arglen);
}
/*
* show_help() - display the help screen; invoked in response to
* either 'h' or '?'.
*/
void
{
static char *fullhelp;
char *p = NULL;
if (fullhelp == NULL)
{
/* set it up first time thru */
if (stp->order_names != NULL)
{
p = string_list(stp->order_names);
}
if (p == NULL)
{
p = "not supported";
}
fullhelp = (char *) malloc(strlen(help_text) + strlen(p) + 2);
sprintf(fullhelp, help_text, p);
display_pager("pg_top version ");
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
display_pager(version_string());
display_pager(", ");
display_pager(copyright);
display_pager("\n");
display_pager(fullhelp);
}
/*
* Utility routines that help with some of the commands.
*/
char *
next_field(char *str)
{
if ((str = strchr(str, ' ')) == NULL)
{
return (NULL);
}
*str = '\0';
while (*++str == ' ') /* loop */ ;
/* if there is nothing left of the string, return NULL */
/* This fix is dedicated to Greg Earle */
return (*str == '\0' ? NULL : str);
}
int
scanint(char *str, int *intp)
{
register int val = 0;
register char ch;
/* if there is nothing left of the string, flag it as an error */
/* This fix is dedicated to Greg Earle */
if (*str == '\0')
{
return (-1);
}
while ((ch = *str++) != '\0')
{
if (isdigit(ch))
{
val = val * 10 + (ch - '0');
}
else if (isspace(ch))
{
break;
}
else
{
return (-1);
}
}
*intp = val;
return (0);
}
void
show_current_query(struct pg_conninfo_ctx *conninfo, int procpid)
{
int i;
int rows;
char info[64];
PGresult *pgresult = NULL;
sprintf(info, "Current query for procpid %d:\n\n", procpid);
display_pager(info);
/* Get the currently running query. */
connect_to_db(conninfo);
if (conninfo->connection != NULL)
pgresult = pg_query(conninfo->connection, procpid);
rows = PQntuples(pgresult);
}
else
{
rows = 0;
}
for (i = 0; i < rows; i++)
{
display_pager(PQgetvalue(pgresult, i, 0));
}
display_pager("\n\n");
if (pgresult != NULL)
PQclear(pgresult);
show_explain(struct pg_conninfo_ctx *conninfo, int procpid, int analyze)
{
int i,
j;
int rows,
r;
char sql[4096];
char info[1024];
PGresult *pgresult_query = NULL;
PGresult *pgresult_explain = NULL;
sprintf(info,
"Current query plan for procpid %d:\n\n Statement:\n\n",
procpid);
display_pager(info);
/* Get the currently running query. */
connect_to_db(conninfo);
if (conninfo->connection != NULL)
pgresult_query = pg_query(conninfo->connection, procpid);
rows = PQntuples(pgresult_query);
}
else
{
rows = 0;
}
for (i = 0; i < rows; i++)
{
/* Display the query before the query plan. */
display_pager(PQgetvalue(pgresult_query, i, 0));
/* Execute the EXPLAIN. */
if (analyze == EXPLAIN_ANALYZE)
{
sprintf(sql, "EXPLAIN (ANALYZE, VERBOSE, BUFFERS)\n%s",
PQgetvalue(pgresult_query, i, 0));
}
else
{
sprintf(sql, "EXPLAIN\n%s", PQgetvalue(pgresult_query, i, 0));
}
PQexec(conninfo->connection, BEGIN);
pgresult_explain = PQexec(conninfo->connection, sql);
PQexec(conninfo->connection, ROLLBACK);
r = PQntuples(pgresult_explain);
/* This will display an error if the EXPLAIN fails. */
display_pager("\n\nQuery Plan:\n\n");
display_pager(PQresultErrorMessage(pgresult_explain));
for (j = 0; j < r; j++)
{
display_pager(PQgetvalue(pgresult_explain, j, 0));
display_pager("\n");
}
if (pgresult_explain != NULL)
PQclear(pgresult_explain);
}
display_pager("\n\n");
if (pgresult_query != NULL)
PQclear(pgresult_query);
show_locks(struct pg_conninfo_ctx *conninfo, int procpid)
{
int i,
j,
k;
int rows;
char info[64];
int width[7] = {1, 8, 6, 5, 5, 4, 7};
PGresult *pgresult = NULL;
char header_format[1024];
char line_format[1024];
char prefix[21]; /* Should hold any 64 bit integer. */
char line[1024];
sprintf(info, "Locks held by procpid %d:\n\n", procpid);
display_pager(info);
/* Get the locks helf by the process. */
connect_to_db(conninfo);
if (conninfo->connection == NULL)
pgresult = pg_locks(conninfo->connection, procpid);
rows = PQntuples(pgresult);
/* Determine column sizes. */
sprintf(prefix, "%d", rows);
width[0] = strlen(prefix);
for (i = 0; i < rows; i++)
{
if (strlen(PQgetvalue(pgresult, i, 0)) > width[1])
width[1] = strlen(PQgetvalue(pgresult, i, 0));
if (strlen(PQgetvalue(pgresult, i, 1)) > width[2])
width[2] = strlen(PQgetvalue(pgresult, i, 1));
if (strlen(PQgetvalue(pgresult, i, 2)) > width[3])
width[3] = strlen(PQgetvalue(pgresult, i, 2));
if (strlen(PQgetvalue(pgresult, i, 3)) > width[4])
width[4] = strlen(PQgetvalue(pgresult, i, 3));
if (strlen(PQgetvalue(pgresult, i, 4)) > width[5])
width[5] = strlen(PQgetvalue(pgresult, i, 4));
if (strlen(PQgetvalue(pgresult, i, 5)) > width[6])
width[6] = strlen(PQgetvalue(pgresult, i, 5));
}
sprintf(header_format,
"%%-%ds | %%-%ds | %%-%ds | %%-%ds | %%-%ds | %%-%ds | %%-%ds\n",
width[0], width[1], width[2], width[3], width[4], width[5],
width[6]);
sprintf(line_format,
"%%%dd | %%-%ds | %%-%ds | %%-%ds | %%-%ds | %%-%ds | %%-%ds\n",
width[0], width[1], width[2], width[3], width[4], width[5],
width[6]);
sprintf(line, header_format, "", "database", "schema", "table", "index",
"type", "granted");
{
for (j = 0; j < width[i]; j++, k++)
{
line[k] = '-';
}
line[k++] = '-';
line[k++] = '+';
line[k++] = '-';
}
line[k - 3] = '\n';
line[k - 2] = '\0';
display_pager(line);
/* Display data. */
for (i = 0; i < rows; i++)
{
sprintf(line, line_format, i + 1, PQgetvalue(pgresult, i, 0),
PQgetvalue(pgresult, i, 1), PQgetvalue(pgresult, i, 2),
PQgetvalue(pgresult, i, 3), PQgetvalue(pgresult, i, 4),
PQgetvalue(pgresult, i, 5));
display_pager(line);
}
display_pager("\n");
PQclear(pgresult);