1/*
2 * Copyright (c) 2008, 2009 Apple Computer, Inc.  All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License").  You may not use this file except in compliance with the
9 * License.  Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22
23#include <stdio.h>
24#include <stdlib.h>
25#include <stdbool.h>
26#include <inttypes.h>
27#include <string.h>
28#include <curses.h>
29#include <unistd.h>
30#include <signal.h>
31#include <sys/select.h>
32#include <fcntl.h>
33#include <sys/ioctl.h>
34#include <termios.h>
35
36#include "libtop.h"
37#include "top.h"
38#include "log.h"
39#include "userinput.h"
40#include "preferences.h"
41#include "options.h"
42#include "logging.h"
43#include "sig.h"
44
45const libtop_tsamp_t *tsamp;
46
47static volatile sig_atomic_t resized = 1;
48
49static int cached_lines = 0, cached_columns = 0;
50
51static void init(void);
52
53enum {
54    MICROSECONDS = 1000000
55};
56
57static void event_loop(void *tinst) {
58    sigset_t sset, oldsset;
59    int samples;
60    struct timeval before, now, tlimit;
61
62    if(sigemptyset(&sset)) {
63	perror("sigemptyset");
64	exit(EXIT_FAILURE);
65    }
66
67    if(sigaddset(&sset, SIGWINCH)) {
68	perror("sigaddset");
69	exit(EXIT_FAILURE);
70    }
71
72    if(-1 == gettimeofday(&before, NULL))
73	perror("gettimeofday");
74
75    while(1) {
76	bool sleep_expired = false;
77	fd_set fset;
78	int ready;
79
80	FD_ZERO(&fset);
81	FD_SET(STDIN_FILENO, &fset);
82
83	tlimit.tv_sec = top_prefs_get_sleep();
84	tlimit.tv_usec = 0;
85
86	ready = select(STDIN_FILENO + 1, &fset, NULL, NULL, &tlimit);
87
88	if(-1 == gettimeofday(&now, NULL))
89	    perror("gettimeofday");
90
91	if(now.tv_sec >= (before.tv_sec + tlimit.tv_sec)) {
92	    /*
93	     * The sleep has expired, so we should insert new
94	     * data for all stats.  This is different than just
95	     * the case where we handle user input and the rest
96	     * of the data is awaiting a sleep interval update.
97	     */
98	    sleep_expired = true;
99	    before = now;
100	}
101
102	if(sleep_expired) {
103	    samples = top_prefs_get_samples();
104
105	    if(samples > -1) {
106		/* Samples was set in the preferences. */
107		if(0 == samples) {
108		    /* We had N samples and now it's time to exit. */
109		    endwin();
110		    exit(EXIT_SUCCESS);
111		}
112
113		top_prefs_set_samples(samples - 1);
114	    }
115	}
116
117	if(top_signal_is_exit_set()) {
118	    exit(EXIT_SUCCESS);
119	}
120
121	if(ready && FD_ISSET(STDIN_FILENO, &fset))
122	    (void)user_input_process(tinst);
123
124	if(sleep_expired)
125	    top_insert(tinst);
126
127	/* Block SIGWINCH signals while we are in a relayout. */
128	if(-1 == sigprocmask(SIG_BLOCK, &sset, &oldsset)) {
129	    perror("sigprocmask");
130	    exit(EXIT_FAILURE);
131	}
132
133	if(top_need_relayout()
134	   || resized || LINES != cached_lines || COLS != cached_columns) {
135	    cached_lines = LINES;
136	    cached_columns = COLS;
137
138	    if(top_layout(tinst)) {
139		resized = 1;
140	    } else {
141		resized = 0;
142	    }
143	}
144
145	if(-1 == sigprocmask(SIG_SETMASK, &oldsset, NULL)) {
146	    perror("sigprocmask");
147		exit(EXIT_FAILURE);
148	}
149
150	top_draw(tinst);
151    }
152}
153
154void exit_handler(void) {
155    endwin();
156}
157
158void init(void) {
159    if(NULL == initscr()) {
160	fprintf(stderr, "error: unable to initscr!\n");
161	exit(EXIT_FAILURE);
162    }
163
164    atexit(exit_handler);
165
166    if(ERR == cbreak() /* disable line buffering */
167       || ERR == noecho() /* disable echoing what the user types */
168       || ERR == nonl() /* no newline */
169       || ERR == intrflush(stdscr, FALSE)
170       || ERR == meta(stdscr, TRUE)
171       || ERR == keypad(stdscr, TRUE)) {
172	fprintf(stderr, "error: initializing curses\n");
173	exit(EXIT_FAILURE);
174    }
175
176    timeout(0);
177}
178
179int main(int argc, char *argv[]) {
180    void *tinst;
181
182    top_prefs_init();
183    top_options_init();
184
185    if(top_options_parse(argc, argv)) {
186	top_options_usage(stderr, argv[0]);
187	return EXIT_FAILURE;
188    }
189
190    if(top_prefs_get_samples() > -1)
191	top_prefs_set_logging_mode(true);
192
193    if(!top_prefs_get_logging_mode())
194	init();
195
196    top_signal_init();
197
198    if(libtop_init(NULL, NULL)) {
199	endwin();
200	fprintf(stderr, "libtop_init failed!\n");
201	return EXIT_FAILURE;
202    }
203
204    if(top_prefs_get_frameworks()
205       && libtop_set_interval(top_prefs_get_frameworks_interval())) {
206	endwin();
207	fprintf(stderr, "error: setting framework update interval.\n");
208	exit(EXIT_FAILURE);
209    }
210
211    tinst = top_create(stdscr);
212
213    if(!top_prefs_get_logging_mode()) {
214	top_insert(tinst);
215	top_layout(tinst);
216	top_draw(tinst);
217	event_loop(tinst);
218    } else {
219	top_logging_loop(tinst);
220    }
221
222    return EXIT_SUCCESS;
223}
224