1 /*
2  * Create a squashfs filesystem.  This is a highly compressed read only
3  * filesystem.
4  *
5  * Copyright (c) 2012, 2013, 2014
6  * Phillip Lougher <phillip@squashfs.org.uk>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2,
11  * or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21  *
22  * progressbar.c
23  */
24 
25 #include <pthread.h>
26 #include <sys/ioctl.h>
27 #include <unistd.h>
28 #include <signal.h>
29 #include <sys/time.h>
30 #include <stdio.h>
31 #include <math.h>
32 #include <stdarg.h>
33 #include <errno.h>
34 #include <stdlib.h>
35 
36 #include "error.h"
37 
38 #define FALSE 0
39 #define TRUE 1
40 
41 /* flag whether progressbar display is enabled or not */
42 int display_progress_bar = FALSE;
43 
44 /* flag whether the progress bar is temporarily disbled */
45 int temp_disabled = FALSE;
46 
47 int rotate = 0;
48 int cur_uncompressed = 0, estimated_uncompressed = 0;
49 int columns;
50 
51 pthread_t progress_thread;
52 pthread_mutex_t progress_mutex = PTHREAD_MUTEX_INITIALIZER;
53 
54 
sigwinch_handler()55 static void sigwinch_handler()
56 {
57 	struct winsize winsize;
58 
59 	if(ioctl(1, TIOCGWINSZ, &winsize) == -1) {
60 		if(isatty(STDOUT_FILENO))
61 			ERROR("TIOCGWINSZ ioctl failed, defaulting to 80 "
62 				"columns\n");
63 		columns = 80;
64 	} else
65 		columns = winsize.ws_col;
66 }
67 
68 
sigalrm_handler()69 static void sigalrm_handler()
70 {
71 	rotate = (rotate + 1) % 4;
72 }
73 
74 
inc_progress_bar()75 void inc_progress_bar()
76 {
77 	cur_uncompressed ++;
78 }
79 
80 
dec_progress_bar(int count)81 void dec_progress_bar(int count)
82 {
83 	cur_uncompressed -= count;
84 }
85 
86 
progress_bar_size(int count)87 void progress_bar_size(int count)
88 {
89 	estimated_uncompressed += count;
90 }
91 
92 
progress_bar(long long current,long long max,int columns)93 static void progress_bar(long long current, long long max, int columns)
94 {
95 	char rotate_list[] = { '|', '/', '-', '\\' };
96 	int max_digits, used, hashes, spaces;
97 	static int tty = -1;
98 
99 	if(max == 0)
100 		return;
101 
102 	max_digits = floor(log10(max)) + 1;
103 	used = max_digits * 2 + 11;
104 	hashes = (current * (columns - used)) / max;
105 	spaces = columns - used - hashes;
106 
107 	if((current > max) || (columns - used < 0))
108 		return;
109 
110 	if(tty == -1)
111 		tty = isatty(STDOUT_FILENO);
112 	if(!tty) {
113 		static long long previous = -1;
114 
115 		/* Updating much more frequently than this results in huge
116 		 * log files. */
117 		if((current % 100) != 0 && current != max)
118 			return;
119 		/* Don't update just to rotate the spinner. */
120 		if(current == previous)
121 			return;
122 		previous = current;
123 	}
124 
125 	printf("\r[");
126 
127 	while (hashes --)
128 		putchar('=');
129 
130 	putchar(rotate_list[rotate]);
131 
132 	while(spaces --)
133 		putchar(' ');
134 
135 	printf("] %*lld/%*lld", max_digits, current, max_digits, max);
136 	printf(" %3lld%%", current * 100 / max);
137 	fflush(stdout);
138 }
139 
140 
enable_progress_bar()141 void enable_progress_bar()
142 {
143 	pthread_cleanup_push((void *) pthread_mutex_unlock, &progress_mutex);
144 	pthread_mutex_lock(&progress_mutex);
145 	if(display_progress_bar)
146 		progress_bar(cur_uncompressed, estimated_uncompressed, columns);
147 	temp_disabled = FALSE;
148 	pthread_cleanup_pop(1);
149 }
150 
151 
disable_progress_bar()152 void disable_progress_bar()
153 {
154 	pthread_cleanup_push((void *) pthread_mutex_unlock, &progress_mutex);
155 	pthread_mutex_lock(&progress_mutex);
156 	if(display_progress_bar)
157 		printf("\n");
158 	temp_disabled = TRUE;
159 	pthread_cleanup_pop(1);
160 }
161 
162 
set_progressbar_state(int state)163 void set_progressbar_state(int state)
164 {
165 	pthread_cleanup_push((void *) pthread_mutex_unlock, &progress_mutex);
166 	pthread_mutex_lock(&progress_mutex);
167 	if(display_progress_bar != state) {
168 		if(display_progress_bar && !temp_disabled) {
169 			progress_bar(cur_uncompressed, estimated_uncompressed,
170 				columns);
171 			printf("\n");
172 		}
173 		display_progress_bar = state;
174 	}
175 	pthread_cleanup_pop(1);
176 }
177 
178 
progress_thrd(void * arg)179 void *progress_thrd(void *arg)
180 {
181 	struct timespec requested_time, remaining;
182 	struct itimerval itimerval;
183 	struct winsize winsize;
184 
185 	if(ioctl(1, TIOCGWINSZ, &winsize) == -1) {
186 		if(isatty(STDOUT_FILENO))
187 			ERROR("TIOCGWINSZ ioctl failed, defaulting to 80 "
188 				"columns\n");
189 		columns = 80;
190 	} else
191 		columns = winsize.ws_col;
192 	signal(SIGWINCH, sigwinch_handler);
193 	signal(SIGALRM, sigalrm_handler);
194 
195 	itimerval.it_value.tv_sec = 0;
196 	itimerval.it_value.tv_usec = 250000;
197 	itimerval.it_interval.tv_sec = 0;
198 	itimerval.it_interval.tv_usec = 250000;
199 	setitimer(ITIMER_REAL, &itimerval, NULL);
200 
201 	requested_time.tv_sec = 0;
202 	requested_time.tv_nsec = 250000000;
203 
204 	while(1) {
205 		int res = nanosleep(&requested_time, &remaining);
206 
207 		if(res == -1 && errno != EINTR)
208 			BAD_ERROR("nanosleep failed in progress thread\n");
209 
210 		pthread_mutex_lock(&progress_mutex);
211 		if(display_progress_bar && !temp_disabled)
212 			progress_bar(cur_uncompressed, estimated_uncompressed,
213 				columns);
214 		pthread_mutex_unlock(&progress_mutex);
215 	}
216 }
217 
218 
init_progress_bar()219 void init_progress_bar()
220 {
221 	pthread_create(&progress_thread, NULL, progress_thrd, NULL);
222 }
223 
224 
progressbar_error(char * fmt,...)225 void progressbar_error(char *fmt, ...)
226 {
227 	va_list ap;
228 
229 	pthread_cleanup_push((void *) pthread_mutex_unlock, &progress_mutex);
230 	pthread_mutex_lock(&progress_mutex);
231 
232 	if(display_progress_bar && !temp_disabled)
233 		fprintf(stderr, "\n");
234 
235 	va_start(ap, fmt);
236 	vfprintf(stderr, fmt, ap);
237 	va_end(ap);
238 
239 	pthread_cleanup_pop(1);
240 }
241 
242 
progressbar_info(char * fmt,...)243 void progressbar_info(char *fmt, ...)
244 {
245 	va_list ap;
246 
247 	pthread_cleanup_push((void *) pthread_mutex_unlock, &progress_mutex);
248 	pthread_mutex_lock(&progress_mutex);
249 
250 	if(display_progress_bar && !temp_disabled)
251 		printf("\n");
252 
253 	va_start(ap, fmt);
254 	vprintf(fmt, ap);
255 	va_end(ap);
256 
257 	pthread_cleanup_pop(1);
258 }
259 
260