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 (at) 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 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 69 static void sigalrm_handler() 70 { 71 rotate = (rotate + 1) % 4; 72 } 73 74 75 void inc_progress_bar() 76 { 77 cur_uncompressed ++; 78 } 79 80 81 void dec_progress_bar(int count) 82 { 83 cur_uncompressed -= count; 84 } 85 86 87 void progress_bar_size(int count) 88 { 89 estimated_uncompressed += count; 90 } 91 92 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 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 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 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 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 219 void init_progress_bar() 220 { 221 pthread_create(&progress_thread, NULL, progress_thrd, NULL); 222 } 223 224 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 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