1 /*- 2 * Copyright (c) 1980, 1988, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of the University nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * @(#)optr.c 8.2 (Berkeley) 1/6/94 30 * $FreeBSD: src/sbin/dump/optr.c,v 1.9.2.5 2002/02/23 22:32:51 iedowse Exp $ 31 * $DragonFly: src/sbin/dump/optr.c,v 1.10 2005/08/28 04:35:12 dillon Exp $ 32 */ 33 34 #include <sys/param.h> 35 #include <sys/queue.h> 36 #include <sys/wait.h> 37 #include <sys/time.h> 38 39 #include <errno.h> 40 #include <fstab.h> 41 #include <grp.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #include <stdarg.h> 46 #include <signal.h> 47 #include <unistd.h> 48 #include <utmp.h> 49 50 #include <vfs/ufs/dinode.h> 51 52 #include "dump.h" 53 #include "pathnames.h" 54 55 static void alarmcatch(int); 56 static int datesort(const void *, const void *); 57 58 /* 59 * Query the operator; This previously-fascist piece of code 60 * no longer requires an exact response. 61 * It is intended to protect dump aborting by inquisitive 62 * people banging on the console terminal to see what is 63 * happening which might cause dump to croak, destroying 64 * a large number of hours of work. 65 * 66 * Every 2 minutes we reprint the message, alerting others 67 * that dump needs attention. 68 */ 69 static int timeout; 70 static const char *attnmessage; /* attention message */ 71 72 int 73 query(const char *question) 74 { 75 char replybuffer[64]; 76 int back, errcount; 77 FILE *mytty; 78 79 if ((mytty = fopen(_PATH_TTY, "r")) == NULL) 80 quit("fopen on %s fails: %s\n", _PATH_TTY, strerror(errno)); 81 attnmessage = question; 82 timeout = 0; 83 alarmcatch(0); 84 back = -1; 85 errcount = 0; 86 do { 87 if (fgets(replybuffer, 63, mytty) == NULL) { 88 clearerr(mytty); 89 if (++errcount > 30) /* XXX ugly */ 90 quit("excessive operator query failures\n"); 91 } else if (replybuffer[0] == 'y' || replybuffer[0] == 'Y') { 92 back = 1; 93 } else if (replybuffer[0] == 'n' || replybuffer[0] == 'N') { 94 back = 0; 95 } else { 96 fprintf(stderr, " DUMP: \"Yes\" or \"No\"?\n"); 97 fprintf(stderr, " DUMP: %s: (\"yes\" or \"no\") ", 98 question); 99 } 100 } while (back < 0); 101 102 /* 103 * Turn off the alarm, and reset the signal to trap out.. 104 */ 105 alarm(0); 106 if (signal(SIGALRM, sig) == SIG_IGN) 107 signal(SIGALRM, SIG_IGN); 108 fclose(mytty); 109 return(back); 110 } 111 112 char lastmsg[BUFSIZ]; 113 114 /* 115 * Alert the console operator, and enable the alarm clock to 116 * sleep for 2 minutes in case nobody comes to satisfy dump 117 */ 118 static void 119 alarmcatch(int signo __unused) 120 { 121 if (notify == 0) { 122 if (timeout == 0) 123 fprintf(stderr, " DUMP: %s: (\"yes\" or \"no\") ", 124 attnmessage); 125 else 126 msgtail("\a\a"); 127 } else { 128 if (timeout) { 129 msgtail("\n"); 130 broadcast(""); /* just print last msg */ 131 } 132 fprintf(stderr," DUMP: %s: (\"yes\" or \"no\") ", attnmessage); 133 } 134 signal(SIGALRM, alarmcatch); 135 alarm(120); 136 timeout = 1; 137 } 138 139 /* 140 * Here if an inquisitive operator interrupts the dump program 141 */ 142 void 143 interrupt(int signo __unused) 144 { 145 msg("Interrupt received.\n"); 146 if (query("Do you want to abort dump?")) 147 dumpabort(0); 148 } 149 150 /* 151 * We now use wall(1) to do the actual broadcasting. 152 */ 153 void 154 broadcast(const char *message) 155 { 156 FILE *fp; 157 char buf[sizeof(_PATH_WALL) + sizeof(OPGRENT) + 3]; 158 159 if (!notify) 160 return; 161 162 snprintf(buf, sizeof(buf), "%s -g %s", _PATH_WALL, OPGRENT); 163 if ((fp = popen(buf, "w")) == NULL) 164 return; 165 166 fputs("\a\a\aMessage from the dump program to all operators\n\nDUMP: NEEDS ATTENTION: ", fp); 167 if (lastmsg[0]) 168 fputs(lastmsg, fp); 169 if (message[0]) 170 fputs(message, fp); 171 172 pclose(fp); 173 } 174 175 /* 176 * Print out an estimate of the amount of time left to do the dump 177 */ 178 179 time_t tschedule = 0; 180 181 void 182 timeest(void) 183 { 184 double percent; 185 time_t tnow; 186 int deltat, hours, mins; 187 188 time(&tnow); 189 deltat = (blockswritten == 0) ? 0 : tstart_writing - tnow + 190 (double)(tnow - tstart_writing) / blockswritten * tapesize; 191 percent = (blockswritten * 100.0) / tapesize; 192 hours = deltat / 3600; 193 mins = (deltat % 3600) / 60; 194 195 setproctitle("%s: pass %d: %3.2f%% done, finished in %d:%02d", 196 disk, passno, percent, hours, mins); 197 if (tnow >= tschedule) { 198 tschedule = tnow + 300; 199 if (blockswritten < 500) 200 return; 201 msg("%3.2f%% done, finished in %d:%02d\n", percent, hours, 202 mins); 203 } 204 } 205 206 /* 207 * Schedule a printout of the estimate in the next call to timeest(). 208 */ 209 void 210 infosch(int signo __unused) 211 { 212 tschedule = 0; 213 } 214 215 void 216 msg(const char *fmt, ...) 217 { 218 va_list ap; 219 220 fprintf(stderr," DUMP: "); 221 #ifdef TDEBUG 222 fprintf(stderr, "pid=%d ", getpid()); 223 #endif 224 va_start(ap, fmt); 225 vfprintf(stderr, fmt, ap); 226 va_end(ap); 227 fflush(stdout); 228 fflush(stderr); 229 va_start(ap, fmt); 230 vsnprintf(lastmsg, sizeof(lastmsg), fmt, ap); 231 va_end(ap); 232 } 233 234 void 235 msgtail(const char *fmt, ...) 236 { 237 va_list ap; 238 239 va_start(ap, fmt); 240 vfprintf(stderr, fmt, ap); 241 va_end(ap); 242 } 243 244 void 245 quit(const char *fmt, ...) 246 { 247 va_list ap; 248 249 fprintf(stderr," DUMP: "); 250 #ifdef TDEBUG 251 fprintf(stderr, "pid=%d ", getpid()); 252 #endif 253 va_start(ap, fmt); 254 vfprintf(stderr, fmt, ap); 255 va_end(ap); 256 fflush(stdout); 257 fflush(stderr); 258 dumpabort(0); 259 } 260 261 /* 262 * Tell the operator what has to be done; 263 * we don't actually do it 264 */ 265 266 static struct fstab * 267 allocfsent(struct fstab *fs) 268 { 269 struct fstab *new; 270 271 new = (struct fstab *)malloc(sizeof (*fs)); 272 if (new == NULL || 273 (new->fs_file = strdup(fs->fs_file)) == NULL || 274 (new->fs_type = strdup(fs->fs_type)) == NULL || 275 (new->fs_spec = strdup(fs->fs_spec)) == NULL) 276 quit("%s\n", strerror(errno)); 277 new->fs_passno = fs->fs_passno; 278 new->fs_freq = fs->fs_freq; 279 return (new); 280 } 281 282 struct pfstab { 283 SLIST_ENTRY(pfstab) pf_list; 284 struct fstab *pf_fstab; 285 }; 286 287 static SLIST_HEAD(, pfstab) table; 288 289 void 290 dump_getfstab(void) 291 { 292 struct fstab *fs; 293 struct pfstab *pf; 294 295 if (setfsent() == 0) { 296 msg("Can't open %s for dump table information: %s\n", 297 _PATH_FSTAB, strerror(errno)); 298 return; 299 } 300 while ((fs = getfsent()) != NULL) { 301 if (strcmp(fs->fs_type, FSTAB_RW) && 302 strcmp(fs->fs_type, FSTAB_RO) && 303 strcmp(fs->fs_type, FSTAB_RQ)) 304 continue; 305 fs = allocfsent(fs); 306 if ((pf = (struct pfstab *)malloc(sizeof (*pf))) == NULL) 307 quit("%s\n", strerror(errno)); 308 pf->pf_fstab = fs; 309 SLIST_INSERT_HEAD(&table, pf, pf_list); 310 } 311 endfsent(); 312 } 313 314 /* 315 * Search in the fstab for a file name. 316 * This file name can be either the special or the path file name. 317 * 318 * The file name can omit the leading '/'. 319 */ 320 struct fstab * 321 fstabsearch(const char *key) 322 { 323 struct pfstab *pf; 324 struct fstab *fs; 325 char *rn; 326 327 SLIST_FOREACH(pf, &table, pf_list) { 328 fs = pf->pf_fstab; 329 if (strcmp(fs->fs_file, key) == 0 || 330 strcmp(fs->fs_spec, key) == 0) 331 return (fs); 332 rn = rawname(fs->fs_spec); 333 if (rn != NULL && strcmp(rn, key) == 0) 334 return (fs); 335 if (key[0] != '/') { 336 if (*fs->fs_spec == '/' && 337 strcmp(fs->fs_spec + 1, key) == 0) 338 return (fs); 339 if (*fs->fs_file == '/' && 340 strcmp(fs->fs_file + 1, key) == 0) 341 return (fs); 342 } 343 } 344 return (NULL); 345 } 346 347 /* 348 * Tell the operator what to do 349 * 350 * char arg: w ==> just what to do; W ==> most recent dumps 351 */ 352 void 353 lastdump(int arg) 354 { 355 int i; 356 struct fstab *dt; 357 struct dumpdates *dtwalk; 358 const char *lastname; 359 char *date; 360 int dumpme; 361 time_t tnow; 362 struct tm *tlast; 363 364 time(&tnow); 365 dump_getfstab(); /* /etc/fstab input */ 366 initdumptimes(); /* /etc/dumpdates input */ 367 qsort(ddatev, nddates, sizeof(struct dumpdates *), datesort); 368 369 if (arg == 'w') 370 printf("Dump these file systems:\n"); 371 else 372 printf("Last dump(s) done (Dump '>' file systems):\n"); 373 lastname = "??"; 374 ITITERATE(i, dtwalk) { 375 if (strncmp(lastname, dtwalk->dd_name, 376 sizeof(dtwalk->dd_name)) == 0) 377 continue; 378 date = (char *)ctime(&dtwalk->dd_ddate); 379 date[16] = '\0'; /* blast away seconds and year */ 380 lastname = dtwalk->dd_name; 381 dt = fstabsearch(dtwalk->dd_name); 382 dumpme = (dt != NULL && dt->fs_freq != 0); 383 if (dumpme) { 384 tlast = localtime(&dtwalk->dd_ddate); 385 dumpme = tnow > (dtwalk->dd_ddate - (tlast->tm_hour * 3600) 386 - (tlast->tm_min * 60) - tlast->tm_sec 387 + (dt->fs_freq * 86400)); 388 }; 389 if (arg != 'w' || dumpme) 390 printf( 391 "%c %8s\t(%6s) Last dump: Level %c, Date %s\n", 392 dumpme && (arg != 'w') ? '>' : ' ', 393 dtwalk->dd_name, 394 dt ? dt->fs_file : "", 395 dtwalk->dd_level, 396 date); 397 } 398 } 399 400 static int 401 datesort(const void *a1, const void *a2) 402 { 403 const struct dumpdates *d1 = *(const struct dumpdates * const *)a1; 404 const struct dumpdates *d2 = *(const struct dumpdates * const *)a2; 405 int diff; 406 407 diff = strncmp(d1->dd_name, d2->dd_name, sizeof(d1->dd_name)); 408 if (diff == 0) 409 return (d2->dd_ddate - d1->dd_ddate); 410 return (diff); 411 } 412