optr.c revision 129556
185909Simp/*- 285909Simp * Copyright (c) 1980, 1988, 1993 3122116Sbde * The Regents of the University of California. All rights reserved. 4122116Sbde * 5122116Sbde * Redistribution and use in source and binary forms, with or without 6266349Simp * modification, are permitted provided that the following conditions 7266349Simp * are met: 8266349Simp * 1. Redistributions of source code must retain the above copyright 9266349Simp * notice, this list of conditions and the following disclaimer. 10266349Simp * 2. Redistributions in binary form must reproduce the above copyright 11266349Simp * notice, this list of conditions and the following disclaimer in the 12266349Simp * documentation and/or other materials provided with the distribution. 13266349Simp * 4. Neither the name of the University nor the names of its contributors 14266349Simp * may be used to endorse or promote products derived from this software 15266349Simp * without specific prior written permission. 16266349Simp * 17266349Simp * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18266349Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19240468Sbrooks * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20265785Simp * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21160440Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2285909Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2385909Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2485909Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2585909Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26175888Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27175888Simp * SUCH DAMAGE. 2885909Simp */ 29218538Simp 3085909Simp#ifndef lint 3191512Sobrien#if 0 32240451Snpstatic char sccsid[] = "@(#)optr.c 8.2 (Berkeley) 1/6/94"; 33116341Smarkm#endif 3485909Simpstatic const char rcsid[] = 3585909Simp "$FreeBSD: head/sbin/dump/optr.c 129556 2004-05-21 20:13:33Z imp $"; 3685909Simp#endif /* not lint */ 3785909Simp 38220863Sdim#include <sys/param.h> 39140606Sobrien#include <sys/queue.h> 40187103Sgnn#include <sys/wait.h> 41220863Sdim#include <sys/time.h> 42224882Snwhitehorn 43224882Snwhitehorn#include <ufs/ufs/dinode.h> 44224882Snwhitehorn 45140606Sobrien#include <errno.h> 46220863Sdim#include <fstab.h> 47224882Snwhitehorn#include <grp.h> 48220863Sdim#include <limits.h> 49265832Simp#include <stdio.h> 50265832Simp#include <stdlib.h> 51265832Simp#include <string.h> 52127204Sobrien#include <stdarg.h> 53228868Sdim#include <signal.h> 54228868Sdim#include <unistd.h> 55140606Sobrien 56220863Sdim#include "dump.h" 57220863Sdim#include "pathnames.h" 58124834Sru 59124834Sruvoid alarmcatch(int); 6085909Simpint datesort(const void *, const void *); 6185909Simp 6285909Simp/* 63126890Strhodes * Query the operator; This previously-fascist piece of code 6485909Simp * no longer requires an exact response. 65192901Sthompsa * It is intended to protect dump aborting by inquisitive 66126890Strhodes * people banging on the console terminal to see what is 67151605Sobrien * happening which might cause dump to croak, destroying 68151605Sobrien * a large number of hours of work. 69130416Smlaier * 70130416Smlaier * Every 2 minutes we reprint the message, alerting others 71149978Sobrien * that dump needs attention. 72149978Sobrien */ 73149978Sobrienstatic int timeout; 74149978Sobrienstatic const char *attnmessage; /* attention message */ 75149978Sobrien 76185522Ssamint 77250173Sadrianquery(const char *question) 78149978Sobrien{ 79149978Sobrien char replybuffer[64]; 80149978Sobrien int back, errcount; 81149978Sobrien FILE *mytty; 82278277Sgonzo 83278277Sgonzo if ((mytty = fopen(_PATH_TTY, "r")) == NULL) 84278277Sgonzo quit("fopen on %s fails: %s\n", _PATH_TTY, strerror(errno)); 85229353Sgjb attnmessage = question; 86149978Sobrien timeout = 0; 87149978Sobrien alarmcatch(0); 88218792Snp back = -1; 89218792Snp errcount = 0; 90183292Skmacy do { 91149978Sobrien if (fgets(replybuffer, 63, mytty) == NULL) { 92149978Sobrien clearerr(mytty); 93276496Simp if (++errcount > 30) /* XXX ugly */ 94150966Sglebius quit("excessive operator query failures\n"); 95257735Simp } else if (replybuffer[0] == 'y' || replybuffer[0] == 'Y') { 96257735Simp back = 1; 97257735Simp } else if (replybuffer[0] == 'n' || replybuffer[0] == 'N') { 98257735Simp back = 0; 99257735Simp } else { 100278913Sglebius (void) fprintf(stderr, 101265832Simp " DUMP: \"Yes\" or \"No\"?\n"); 102265832Simp (void) fprintf(stderr, 103257735Simp " DUMP: %s: (\"yes\" or \"no\") ", question); 104265832Simp } 105210311Sjmallett } while (back < 0); 106171239Speter 10785909Simp /* 10885909Simp * Turn off the alarm, and reset the signal to trap out.. 109276770Simp */ 11085909Simp (void) alarm(0); 11199923Sbde if (signal(SIGALRM, sig) == SIG_IGN) 112242715Sdim signal(SIGALRM, SIG_IGN); 113265832Simp (void) fclose(mytty); 11499932Sbde return(back); 11599932Sbde} 116242717Sdim 117265832Simpchar lastmsg[BUFSIZ]; 118242717Sdim 119242717Sdim/* 12099932Sbde * Alert the console operator, and enable the alarm clock to 121242717Sdim * sleep for 2 minutes in case nobody comes to satisfy dump 12299923Sbde */ 12399932Sbdevoid 12485909Simpalarmcatch(int sig __unused) 12591002Speter{ 12685909Simp if (notify == 0) { 12785909Simp if (timeout == 0) 12885909Simp (void) fprintf(stderr, 12985909Simp " DUMP: %s: (\"yes\" or \"no\") ", 130116341Smarkm attnmessage); 131116341Smarkm else 132116341Smarkm msgtail("\a\a"); 13391002Speter } else { 13491002Speter if (timeout) { 13591002Speter msgtail("\n"); 136105489Smux broadcast(""); /* just print last msg */ 13785909Simp } 138105462Smux (void) fprintf(stderr," DUMP: %s: (\"yes\" or \"no\") ", 139105462Smux attnmessage); 14085909Simp } 141239956Sjhb signal(SIGALRM, alarmcatch); 142239957Sjhb (void) alarm(120); 143239957Sjhb timeout = 1; 144239955Sjhb} 145233578Speter 146233578Speter/* 147253996Savg * Here if an inquisitive operator interrupts the dump program 148233578Speter */ 149233578Spetervoid 150233578Speterinterrupt(int signo __unused) 151233578Speter{ 152228158Sfjoe msg("Interrupt received.\n"); 153228140Sfjoe if (query("Do you want to abort dump?")) 154228158Sfjoe dumpabort(0); 155228158Sfjoe} 156228124Sfjoe 157228158Sfjoe/* 158228124Sfjoe * We now use wall(1) to do the actual broadcasting. 159179226Sjb */ 160116341Smarkmvoid 161116341Smarkmbroadcast(const char *message) 162219819Sjeff{ 163219819Sjeff FILE *fp; 164219819Sjeff char buf[sizeof(_PATH_WALL) + sizeof(OPGRENT) + 3]; 165278913Sglebius 166219819Sjeff if (!notify) 167219819Sjeff return; 168219819Sjeff 169219819Sjeff snprintf(buf, sizeof(buf), "%s -g %s", _PATH_WALL, OPGRENT); 170131210Simp if ((fp = popen(buf, "w")) == NULL) 171144293Sphk return; 17285909Simp 173111684Sru (void) fputs("\a\a\aMessage from the dump program to all operators\n\nDUMP: NEEDS ATTENTION: ", fp); 174111684Sru if (lastmsg[0]) 175111684Sru (void) fputs(lastmsg, fp); 176269114Ssjg if (message[0]) 177278306Sdavide (void) fputs(message, fp); 17885909Simp 17985909Simp (void) pclose(fp); 180123966Sbde} 181175888Simp 18285909Simp/* 18388893Simp * Print out an estimate of the amount of time left to do the dump 18488893Simp */ 18588893Simp 186221265Sbztime_t tschedule = 0; 187210151Simp 188261460Simpvoid 189278519Simptimeest(void) 19090789Sphk{ 19190789Sphk double percent; 19290789Sphk time_t tnow, tdone; 19388893Simp int deltat, hours, mins; 19488893Simp 19588893Simp if (blockswritten > tapesize) { 19688893Simp setproctitle("%s: 99.99%% done, finished soon", disk); 197125772Sru msg("99.99%% done, finished soon\n"); 19888893Simp } else { 199240402Sobrien (void) time(&tnow); 200258753Sandreast deltat = (blockswritten == 0) ? 0 : tstart_writing - tnow + 201240402Sobrien (double)(tnow - tstart_writing) / blockswritten * tapesize; 202240402Sobrien tdone = tnow + deltat; 203240402Sobrien percent = (blockswritten * 100.0) / tapesize; 204 hours = deltat / 3600; 205 mins = (deltat % 3600) / 60; 206 207 setproctitle( 208 "%s: pass %d: %3.2f%% done, finished in %d:%02d at %s", 209 disk, passno, percent, hours, mins, ctime(&tdone)); 210 if (tnow >= tschedule) { 211 tschedule = tnow + 300; 212 if (blockswritten < 500) 213 return; 214 msg("%3.2f%% done, finished in %d:%02d at %s", percent, 215 hours, mins, ctime(&tdone)); 216 } 217 } 218} 219 220/* 221 * Schedule a printout of the estimate in the next call to timeest(). 222 */ 223void 224infosch(int signal __unused) 225{ 226 tschedule = 0; 227} 228 229void 230msg(const char *fmt, ...) 231{ 232 va_list ap; 233 va_list ap2; 234 235 (void) fprintf(stderr," DUMP: "); 236#ifdef TDEBUG 237 (void) fprintf(stderr, "pid=%d ", getpid()); 238#endif 239 va_start(ap, fmt); 240 va_copy(ap2, ap); 241 (void) vfprintf(stderr, fmt, ap); 242 (void) fflush(stdout); 243 (void) fflush(stderr); 244 (void) vsnprintf(lastmsg, sizeof(lastmsg), fmt, ap2); 245 va_end(ap); 246 va_end(ap2); 247} 248 249void 250msgtail(const char *fmt, ...) 251{ 252 va_list ap; 253 va_start(ap, fmt); 254 (void) vfprintf(stderr, fmt, ap); 255 va_end(ap); 256} 257 258void 259quit(const char *fmt, ...) 260{ 261 va_list ap; 262 263 (void) fprintf(stderr," DUMP: "); 264#ifdef TDEBUG 265 (void) fprintf(stderr, "pid=%d ", getpid()); 266#endif 267 va_start(ap, fmt); 268 (void) vfprintf(stderr, fmt, ap); 269 va_end(ap); 270 (void) fflush(stdout); 271 (void) fflush(stderr); 272 dumpabort(0); 273} 274 275/* 276 * Tell the operator what has to be done; 277 * we don't actually do it 278 */ 279 280struct fstab * 281allocfsent(const struct fstab *fs) 282{ 283 struct fstab *new; 284 285 new = (struct fstab *)malloc(sizeof (*fs)); 286 if (new == NULL || 287 (new->fs_file = strdup(fs->fs_file)) == NULL || 288 (new->fs_type = strdup(fs->fs_type)) == NULL || 289 (new->fs_spec = strdup(fs->fs_spec)) == NULL) 290 quit("%s\n", strerror(errno)); 291 new->fs_passno = fs->fs_passno; 292 new->fs_freq = fs->fs_freq; 293 return (new); 294} 295 296struct pfstab { 297 SLIST_ENTRY(pfstab) pf_list; 298 struct fstab *pf_fstab; 299}; 300 301static SLIST_HEAD(, pfstab) table; 302 303void 304dump_getfstab(void) 305{ 306 struct fstab *fs; 307 struct pfstab *pf; 308 309 if (setfsent() == 0) { 310 msg("Can't open %s for dump table information: %s\n", 311 _PATH_FSTAB, strerror(errno)); 312 return; 313 } 314 while ((fs = getfsent()) != NULL) { 315 if (strcmp(fs->fs_type, FSTAB_RW) && 316 strcmp(fs->fs_type, FSTAB_RO) && 317 strcmp(fs->fs_type, FSTAB_RQ)) 318 continue; 319 fs = allocfsent(fs); 320 if ((pf = (struct pfstab *)malloc(sizeof (*pf))) == NULL) 321 quit("%s\n", strerror(errno)); 322 pf->pf_fstab = fs; 323 SLIST_INSERT_HEAD(&table, pf, pf_list); 324 } 325 (void) endfsent(); 326} 327 328/* 329 * Search in the fstab for a file name. 330 * This file name can be either the special or the path file name. 331 * 332 * The file name can omit the leading '/'. 333 */ 334struct fstab * 335fstabsearch(const char *key) 336{ 337 struct pfstab *pf; 338 struct fstab *fs; 339 char *rn; 340 341 SLIST_FOREACH(pf, &table, pf_list) { 342 fs = pf->pf_fstab; 343 if (strcmp(fs->fs_file, key) == 0 || 344 strcmp(fs->fs_spec, key) == 0) 345 return (fs); 346 rn = rawname(fs->fs_spec); 347 if (rn != NULL && strcmp(rn, key) == 0) 348 return (fs); 349 if (key[0] != '/') { 350 if (*fs->fs_spec == '/' && 351 strcmp(fs->fs_spec + 1, key) == 0) 352 return (fs); 353 if (*fs->fs_file == '/' && 354 strcmp(fs->fs_file + 1, key) == 0) 355 return (fs); 356 } 357 } 358 return (NULL); 359} 360 361/* 362 * Tell the operator what to do 363 */ 364void 365lastdump(int arg) /* w ==> just what to do; W ==> most recent dumps */ 366{ 367 int i; 368 struct fstab *dt; 369 struct dumpdates *dtwalk; 370 char *lastname, *date; 371 int dumpme; 372 time_t tnow; 373 struct tm *tlast; 374 375 (void) time(&tnow); 376 dump_getfstab(); /* /etc/fstab input */ 377 initdumptimes(); /* /etc/dumpdates input */ 378 qsort((char *) ddatev, nddates, sizeof(struct dumpdates *), datesort); 379 380 if (arg == 'w') 381 (void) printf("Dump these file systems:\n"); 382 else 383 (void) printf("Last dump(s) done (Dump '>' file systems):\n"); 384 lastname = "??"; 385 ITITERATE(i, dtwalk) { 386 if (strncmp(lastname, dtwalk->dd_name, 387 sizeof(dtwalk->dd_name)) == 0) 388 continue; 389 date = (char *)ctime(&dtwalk->dd_ddate); 390 date[16] = '\0'; /* blast away seconds and year */ 391 lastname = dtwalk->dd_name; 392 dt = fstabsearch(dtwalk->dd_name); 393 dumpme = (dt != NULL && dt->fs_freq != 0); 394 if (dumpme) { 395 tlast = localtime(&dtwalk->dd_ddate); 396 dumpme = tnow > (dtwalk->dd_ddate - (tlast->tm_hour * 3600) 397 - (tlast->tm_min * 60) - tlast->tm_sec 398 + (dt->fs_freq * 86400)); 399 }; 400 if (arg != 'w' || dumpme) 401 (void) printf( 402 "%c %8s\t(%6s) Last dump: Level %c, Date %s\n", 403 dumpme && (arg != 'w') ? '>' : ' ', 404 dtwalk->dd_name, 405 dt ? dt->fs_file : "", 406 dtwalk->dd_level, 407 date); 408 } 409} 410 411int 412datesort(const void *a1, const void *a2) 413{ 414 struct dumpdates *d1 = *(struct dumpdates **)a1; 415 struct dumpdates *d2 = *(struct dumpdates **)a2; 416 int diff; 417 418 diff = strncmp(d1->dd_name, d2->dd_name, sizeof(d1->dd_name)); 419 if (diff == 0) 420 return (d2->dd_ddate - d1->dd_ddate); 421 return (diff); 422} 423