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