nicer output
[tridge/junkcode.git] / capq.c
1 /*
2  * capq - print out state of CAP queue (obtained from capqd).
3  *
4  * capq [-c] [-i interval] [-h cap-host]
5  *
6  * Author: Paul Mackerras, June 1993.
7  */
8 #include <stdio.h>
9 #include <string.h>
10 #include <stdlib.h>
11 #include <errno.h>
12 #include <math.h>
13 #include <signal.h>
14 #include <sys/types.h>
15 #include <sys/time.h>
16 #include <sys/socket.h>
17 #include <sys/termios.h>
18 #include <netinet/in.h>
19 #include <arpa/inet.h>
20 #include <netdb.h>
21 #include <termios.h>
22
23 #define CAPQD_PORT      6123    /* capqd's well-known port number */
24 #define CAP_HOST_DFLT   "cafe"  /* default value for -h option */
25
26 FILE *capqd_w, *capqd_r;        /* streams to and from capqd */
27 char *cap_host;                 /* hostname where capqd is */
28
29 /* used for terminal capabilities */
30 char termcap_entry[1024], string_caps[1024];
31
32 #define MAXLINES        100     /* max # lines for continuous display */
33 #define MAXCOLS         100     /* max # columns  */
34
35 double last_change_time;        /* cap_host's time of last queue change */
36 double our_time;                /* time according to our clock */
37 double host_time_diff;          /* cap_host's clock - our clock */
38 double timez[MAXLINES];         /* start time for each line of display */
39 int num_lines;                  /* # entries valid in timez[] */
40
41 /* Convert from a timeval to a floating-point number of seconds */
42 #define double_time(x)          ((x)->tv_sec + (x)->tv_usec / 1.0E6)
43
44 /* Termcap routines */
45 extern int tgetent();
46 extern int tgetnum();
47 extern char *tgetstr();
48 extern int tgetflag();
49 extern short ospeed;
50
51 /* Option values */
52 int continuous;                 /* => display the queue continuously */
53 double interval;                /* with updates at this interval */
54
55 /* Capability strings and terminal information from termcap */
56 int screen_lines;               /* screen has this many lines */
57 int screen_width;               /* and this many columns */
58 char *term;                     /* terminal type */
59 char *home;                     /* go to top-left corner of screen */
60 char *init_str;                 /* initialize terminal */
61 char *finish_str;               /* reset terminal before exit */
62 char *clr_screen;               /* clear whole screen */
63 char *clr_eol;                  /* clear to end of line */
64 char *clr_eos;                  /* clear to end of screen */
65
66 int size_changed;               /* set when the window size changes */
67
68 void getout();
69 void hoist();
70 int outchar();
71
72 main(argc, argv)
73     int argc;
74     char **argv;
75 {
76     int fd, c, n;
77     int queue_empty;
78     double t;
79     char *ptr;
80     extern char *optarg;
81     fd_set ready;
82     struct timeval *timo, tval;
83     struct termios tio;
84
85     /* set defaults */
86     cap_host = getenv("AP1000HOST");
87     if( cap_host == NULL )
88         cap_host = CAP_HOST_DFLT;
89     continuous = 0;
90     interval = 1.0;
91
92     /* parse command-line options */
93     while( (c = getopt(argc, argv, "c:h:")) != -1 ){
94         switch( c ){
95         case 'h':
96             cap_host = optarg;
97             break;
98         case 'c':
99           continuous = 1;
100           interval = atof(optarg);
101           break;
102         case '?':
103             fprintf(stderr, "Usage: %s [-h host]\n", argv[0]);
104             exit(1);
105         }
106     }
107
108     /* connect to the capqd process */
109     fd = connect_tcp(cap_host, CAPQD_PORT);
110     if( fd < 0 )
111         exit(1);
112     capqd_r = fdopen(fd, "r");
113     capqd_w = fdopen(fd, "w");
114     if( capqd_r == NULL || capqd_w == NULL ){
115         fprintf(stderr, "fdopen failed!\n");
116         exit(1);
117     }
118
119     /* request the current state of the queue */
120     fputs("queue\n", capqd_w);
121     fflush(capqd_w);
122
123     if( !continuous ){
124         /* just print the queue once and exit. */
125         show_queue(0, "");
126         exit(0);
127     }
128
129     /* Continuous mode - get information about the terminal. */
130     if( tcgetattr(0, &tio) == 0 )
131         ospeed = cfgetospeed(&tio);
132     screen_lines = 24;
133     screen_width = 80;
134     home = clr_screen = clr_eol = finish_str = NULL;
135     term = getenv("TERM");
136     if( term != NULL && tgetent(termcap_entry, term) > 0 ){
137         ptr = string_caps;
138         screen_lines = tgetnum("li");
139         screen_width = tgetnum("co");
140         home = tgetstr("ho", &ptr);
141         clr_screen = tgetstr("cl", &ptr);
142         clr_eol = tgetstr("ce", &ptr);
143         clr_eos = tgetstr("cd", &ptr);
144         init_str = tgetstr("ti", &ptr);
145         finish_str = tgetstr("te", &ptr);
146         if( init_str != NULL )
147             tputs(init_str, screen_lines, outchar);
148     }
149
150     /* default to the ANSI sequences */
151     if( home == NULL )
152         home = "\033[H";
153     if( clr_screen == NULL )
154         clr_screen = "\033[H\033[J";
155     if( clr_eol == NULL )
156         clr_eol = "\033[K";
157     if( clr_eos == NULL )
158         clr_eos = "\033[J";
159     if( finish_str == NULL )
160         finish_str = "";
161     tputs(clr_screen, screen_lines, outchar);
162     fflush(stdout);
163
164     if( screen_lines > MAXLINES )
165         screen_lines = MAXLINES;
166     if( screen_width > MAXCOLS )
167         screen_width = MAXCOLS;
168
169     /* fix up the terminal and exit on these signals */
170     signal(SIGINT, getout);
171     signal(SIGTERM, getout);
172     /* update the terminal size on this signal */
173     signal(SIGWINCH, hoist);
174
175     /*
176      * This loop waits for either (a) an update to arrive from capqd,
177      * or (b) for it to be time to change the time values displayed
178      * for jobs in the queue, or (c) for the terminal window size to change.
179      */
180     num_lines = 0;
181     size_changed = 0;
182     for(;;){
183         if( num_lines <= 1 )
184             /* no times to update - don't time out */
185             timo = NULL;
186
187         else {
188             /* work out how long until the first time displayed
189                should change, i.e. until it's a multiple of `interval'. */
190             gettimeofday(&tval, NULL);
191             our_time = double_time(&tval);
192             t = timez[1];
193             if( t == 0 )
194                 t = timez[1];
195             t = our_time + host_time_diff - t;
196             t = interval - fmod(t, interval);
197             tval.tv_sec = (int) floor(t);
198             tval.tv_usec = (int) floor((t - tval.tv_sec) * 1.0E6);
199             timo = &tval;
200         }
201
202         /* wait for something */
203         FD_ZERO(&ready);
204         FD_SET(fd, &ready);
205         n = select(fd+1, &ready, NULL, NULL, timo);
206         if( n < 0 && errno != EINTR ){
207             tputs(finish_str, screen_lines, outchar);
208             perror("select");
209             exit(1);
210         }
211
212         if( n > 0 ){
213             /* update display with new information, then ask for
214                another report when the queue changes. */
215             size_changed = 0;
216             show_queue();
217             fprintf(capqd_w, "queue %.2f\n", last_change_time);
218             fflush(capqd_w);
219
220         } else if( size_changed ){
221             /* ask for the queues again */
222             fprintf(capqd_w, "queue\n");
223             fflush(capqd_w);
224             size_changed = 0;
225
226         } else if( n == 0 ){
227             /* timeout - no new information from capqd */
228             update_times();
229         }
230
231         /* leave the cursor at the top left of the screen */
232         tputs(home, 0, outchar);
233         fflush(stdout);
234     }
235 }
236
237 /*
238  * Fatal signal - clean up terminal and exit.
239  */
240 void
241 getout(sig)
242     int sig;
243 {
244     tputs(finish_str, screen_lines, outchar);
245     exit(0);
246 }
247
248 /*
249  * Window size changed - winch up the new size.
250  */
251 void
252 hoist(sig)
253     int sig;
254 {
255     struct winsize winsz;
256
257     if( ioctl(fileno(stdout), TIOCGWINSZ, &winsz) == 0 ){
258         size_changed = 1;
259         screen_lines = winsz.ws_row;
260         screen_width = winsz.ws_col;
261         if( screen_lines > MAXLINES )
262             screen_lines = MAXLINES;
263         if( screen_width > MAXCOLS )
264             screen_width = MAXCOLS;
265     }
266 }
267
268 /*
269  * Output character routine for tputs to use.
270  */
271 int
272 outchar(c)
273     int c;
274 {
275     putchar(c);
276 }
277
278 /*
279  * New information from capqd - display it, and (in continuous mode)
280  * record the start times for the users shown on each line of the display.
281  */
282 int
283 show_queue()
284 {
285     int n, index, run_done, lnum;
286     double start_time;
287     char *ptr;
288     char line[80];
289     char user[64], more[8];
290     int pid[3];
291     struct timeval now;
292     char str[MAXCOLS+1];
293
294     /* initialize, print heading */
295     run_done = 0;
296     timez[0] = 0;
297     lnum = 1;
298     sprintf(str, "        CAP queue on %.50s", cap_host);
299     putstr(str, 0);
300
301     /* get lines from capqd */
302     for(;;){
303         if( fgets(line, sizeof(line), capqd_r) == NULL ){
304             /* EOF or error - capqd must have gone away */
305             if( continuous ){
306                 tputs(finish_str, screen_lines, outchar);
307                 fflush(stdout);
308             }
309             fprintf(stderr, "read error\n");
310             exit(1);
311         }
312
313         if( line[0] == 'E' )
314             /* end of queue report */
315             break;
316         if( line[0] == 'T' ){
317             /* first line of queue report: T last-change-time time-now */
318             gettimeofday(&now, NULL);
319             our_time = double_time(&now);
320             sscanf(line+2, "%lf %lf", &last_change_time, &host_time_diff);
321             host_time_diff -= our_time;
322             continue;
323         }
324
325         /* line specifying next user in queue:
326            index user start-time { pids... } */
327         n = sscanf(line, "%d %s %lf { %d %d %d %d %s", &index,
328                    user, &start_time, &pid[0], &pid[1], &pid[2],
329                    &pid[3], more);
330         if( (n -= 3) <= 0 ){
331             /* couldn't parse line - ignore it */
332 #ifdef  DEBUG
333             fprintf(stderr, "bad line %s", line);
334 #endif
335             continue;
336         }
337
338         /* accumulate a line to be printed in str */
339         ptr = str;
340         if( index == 0 ){
341             /* this is the running job */
342             sprint_time(&ptr, start_time);
343             sprintf(ptr, "  Run  %.20s  (pid %d", user, pid[0]);
344             ptr += strlen(ptr);
345             if( n > 1 ){
346                 /* print the rest of the pids which are waiting */
347                 strcpy(ptr, ", ");
348                 ptr += 2;
349                 sprint_pids_waiting(&ptr, pid, n, 1);
350                 strcpy(ptr, " waiting");
351                 ptr += 8;
352             }
353             *ptr++ = ')';
354             run_done = 1;
355
356         } else {
357             /* a user in the queue */
358             if( continuous && !run_done ){
359                 /* no running job - leave a blank line for it */
360                 str[0] = 0;
361                 putstr(str, lnum);
362                 run_done = 1;
363                 timez[lnum] = 0;
364                 ++lnum;
365             }
366             if( continuous && lnum >= screen_lines - 1 ){
367                 /* no more room on screen */
368                 if( lnum == screen_lines - 1 ){
369                     strcpy(ptr, "                (more)");
370                     ptr += strlen(ptr);
371                     start_time = 0;
372                 } else
373                     ptr = NULL; /* don't print anything */
374             } else {
375                 /* format this line into str */
376                 sprint_time(&ptr, start_time);
377                 sprintf(ptr, "  %3d  %.20s  (", index, user);
378                 ptr += strlen(ptr);
379                 sprint_pids_waiting(&ptr, pid, n, 0);
380                 *ptr++ = ')';
381             }
382         }
383         /* print out the line we've formatted */
384         if( ptr != NULL ){
385             *ptr = 0;
386             if( continuous ){
387                 putstr(str, lnum);
388                 timez[lnum] = start_time;
389                 ++lnum;
390             } else
391                 printf("%s\n", str);
392         }
393     }
394
395     if( lnum > screen_lines )
396         lnum = screen_lines;
397     num_lines = lnum;
398
399     /* clear the remainder of the screen */
400     if( continuous && lnum < screen_lines )
401         tputs(clr_eos, screen_lines - lnum + 1, outchar);
402 }
403
404 /*
405  * Output a line to the screen.  In continuous mode, truncate the
406  * line to the screen width and clear the remainder of the line.
407  */
408 putstr(str, lnum)
409     char *str;
410     int lnum;
411 {
412     if( continuous ){
413         if( lnum < screen_lines ){
414             str[screen_width] = 0;
415             fputs(str, stdout);
416             if( strlen(str) < screen_width )
417                 tputs(clr_eol, 1, outchar);
418             if( lnum < screen_lines - 1 )
419                 putchar('\n');
420         }
421     } else
422         printf("%s\n", str);
423 }
424
425 /*
426  * Format the time since time t on the cap_host into the buffer
427  * at **pp, advancing *pp past the formatted string.
428  */
429 sprint_time(pp, t)
430     char **pp;
431     double t;
432 {
433     int x;
434
435     t = floor(our_time + host_time_diff - t + 0.5);
436     if( t > 3600 ){
437         x = floor(t / 3600);
438         sprintf(*pp, "%3d:", x);
439         *pp += strlen(*pp);
440         t -= x * 3600.0;
441     } else {
442         strcpy(*pp, "    ");
443         *pp += 4;
444     }
445     x = floor(t / 60);
446     sprintf(*pp, "%.2d:%.2d", x, (int)(t - x * 60));
447     *pp += strlen(*pp);
448 }
449
450 /*
451  * Format a list of pids waiting into **pp, advancing *pp.
452  */
453 sprint_pids_waiting(pp, pid, n, i)
454     char **pp;
455     int pid[], n, i;
456 {
457     sprintf(*pp, "pid%s %d", (n > i+1? "s": ""), pid[i]);
458     *pp += strlen(*pp);
459     for( ++i; i < n && i < 4; ++i ){
460         sprintf(*pp, ", %d", pid[i]);
461         *pp += strlen(*pp);
462     }
463     if( n >= 5 ){
464         strcpy(*pp, ", ...");
465         *pp += 5;
466     }
467 }
468
469 /*
470  * Update the times displayed for each user.
471  */
472 update_times()
473 {
474     double t;
475     int i;
476     struct timeval now;
477     char *ptr, str[12];
478
479     gettimeofday(&now, NULL);
480     our_time = double_time(&now);
481     for( i = 0; i < num_lines; ++i ){
482         ptr = str;
483         if( (t = timez[i]) != 0 ){
484             sprint_time(&ptr, t);
485             if( screen_width < sizeof(str) )
486                 str[screen_width] = 0;
487             *ptr = 0;
488             printf("%s", str);
489         }
490         if( i < num_lines - 1 )
491             printf("\n");
492     }
493 }
494
495 /*
496  * Establish a TCP/IP connection with the process at the given hostname
497  * and port number.
498  */
499 int
500 connect_tcp(host, port)
501     char *host;
502     int port;
503 {
504     int fd;
505     unsigned long hnum;
506     struct hostent *hent;
507     struct sockaddr_in addr;
508
509     hnum = inet_addr(host);
510     if( hnum == -1 ){
511         hent = gethostbyname(host);
512         if( hent == NULL ){
513             fprintf(stderr, "hostname %s not recognized\n", host);
514             return -1;
515         }
516         hnum = *(unsigned long *)(hent->h_addr_list[0]);
517     }
518
519     if( (fd = socket(PF_INET, SOCK_STREAM, 0)) < 0 ){
520         perror("socket");
521         return -1;
522     }
523
524     bzero(&addr, sizeof(addr));
525     addr.sin_family = AF_INET;
526     addr.sin_port = htons(port);
527     addr.sin_addr.s_addr = hnum;
528
529     if( connect(fd, &addr, sizeof(addr)) < 0 ){
530         perror("connect");
531         return -1;
532     }
533
534     return fd;
535 }