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 */ 42int display_progress_bar = FALSE; 43 44/* flag whether the progress bar is temporarily disbled */ 45int temp_disabled = FALSE; 46 47int rotate = 0; 48int cur_uncompressed = 0, estimated_uncompressed = 0; 49int columns; 50 51pthread_t progress_thread; 52pthread_mutex_t progress_mutex = PTHREAD_MUTEX_INITIALIZER; 53 54 55static 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 69static void sigalrm_handler() 70{ 71 rotate = (rotate + 1) % 4; 72} 73 74 75void inc_progress_bar() 76{ 77 cur_uncompressed ++; 78} 79 80 81void dec_progress_bar(int count) 82{ 83 cur_uncompressed -= count; 84} 85 86 87void progress_bar_size(int count) 88{ 89 estimated_uncompressed += count; 90} 91 92 93static 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 141void 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 152void 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 163void 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 179void *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 219void init_progress_bar() 220{ 221 pthread_create(&progress_thread, NULL, progress_thrd, NULL); 222} 223 224 225void 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 243void 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