1 /* 2 * Copyright (c) 1998 by Sun Microsystems, Inc. 3 * All rights reserved. 4 */ 5 6 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 7 /* All Rights Reserved */ 8 9 /* 10 * Copyright (c) 1980 Regents of the University of California. 11 * All rights reserved. The Berkeley software License Agreement 12 * specifies the terms and conditions for redistribution. 13 */ 14 15 #pragma ident "%Z%%M% %I% %E% SMI" 16 17 #include <errno.h> 18 #include "dump.h" 19 20 static unsigned int timeout; /* current timeout */ 21 static char *attnmessage, *saveattn; /* attention message */ 22 23 #ifdef __STDC__ 24 static void alarmcatch(); 25 static int idatesort(const void *, const void *); 26 #else /* !__STDC__ */ 27 static void alarmcatch(); 28 static int idatesort(); 29 #endif 30 31 #ifdef DEBUG 32 extern int xflag; 33 #endif 34 35 /* 36 * Query the operator; This fascist piece of code requires 37 * an exact response. 38 * It is intended to protect dump aborting by inquisitive 39 * people banging on the console terminal to see what is 40 * happening which might cause dump to croak, destroying 41 * a large number of hours of work. 42 * 43 * Every time += 2 minutes we reprint the message, alerting others 44 * that dump needs attention. 45 */ 46 int 47 query(question) 48 char *question; 49 { 50 int def = -1; 51 52 while (def == -1) 53 def = query_once(question, -1); 54 return (def); 55 } 56 57 static int in_query_once; 58 static jmp_buf sjalarmbuf; 59 60 /* real simple check-sum */ 61 static int 62 addem(s) 63 char *s; 64 { 65 int total = 0; 66 67 if (s == (char *)NULL) 68 return (total); 69 while (*s) 70 total += *s++; 71 return (total); 72 } 73 74 int 75 query_once(question, def) 76 char *question; 77 int def; 78 { 79 static char *lastmsg; 80 static int lastmsgsum; 81 int msgsum; 82 char replybuffer[BUFSIZ]; 83 int back; 84 time32_t timeclockstate; 85 pollfd_t pollset; 86 struct sigvec sv; 87 88 /* special hook to flush timeout cache */ 89 if (question == NULL) { 90 lastmsg = (char *)NULL; 91 lastmsgsum = 0; 92 return (0); 93 } 94 95 attnmessage = question; 96 /* 97 * Only reset the state if the message changed somehow 98 */ 99 msgsum = addem(question); 100 if (lastmsg != question || lastmsgsum != msgsum) { 101 timeout = 0; 102 if (telapsed && tstart_writing) 103 *telapsed += time((time_t *)0) - *tstart_writing; 104 lastmsg = question; 105 lastmsgsum = msgsum; 106 } 107 timeclockstate = timeclock((time_t)0); 108 if (setjmp(sjalarmbuf) != 0) { 109 if (def != -1) { 110 if (def) 111 msgtail(gettext("YES\n")); 112 else 113 msgtail(gettext("NO\n")); 114 } 115 back = def; 116 goto done; 117 } 118 alarmcatch(); 119 in_query_once = 1; 120 pollset.fd = -1; 121 pollset.events = 0; 122 pollset.revents = 0; 123 if (isatty(fileno(stdin))) { 124 pollset.fd = fileno(stdin); 125 pollset.events = POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND; 126 } else { 127 dumpabort(); 128 /*NOTREACHED*/ 129 } 130 for (;;) { 131 if (poll(&pollset, 1, -1) < 0) { 132 if (errno == EINTR) 133 continue; 134 perror("poll(stdin)"); 135 dumpabort(); 136 /*NOTREACHED*/ 137 } 138 if (pollset.revents == 0) 139 continue; /* sanity check */ 140 if (fgets(replybuffer, sizeof (replybuffer), stdin) == NULL) { 141 if (ferror(stdin)) { 142 clearerr(stdin); 143 continue; 144 } else { 145 dumpabort(); 146 /*NOTREACHED*/ 147 } 148 } 149 timeout = 0; 150 if (strcasecmp(replybuffer, gettext("yes\n")) == 0) { 151 back = 1; 152 lastmsg = (char *)NULL; 153 lastmsgsum = 0; 154 goto done; 155 } else if (strcasecmp(replybuffer, gettext("no\n")) == 0) { 156 back = 0; 157 lastmsg = (char *)NULL; 158 lastmsgsum = 0; 159 goto done; 160 } else { 161 msg(gettext("\"yes\" or \"no\"?\n")); 162 in_query_once = 0; 163 alarmcatch(); 164 in_query_once = 1; 165 } 166 } 167 done: 168 /* 169 * Turn off the alarm, and reset the signal to trap out.. 170 */ 171 (void) alarm(0); 172 attnmessage = NULL; 173 sv.sv_handler = sigAbort; 174 sv.sv_flags = SA_RESTART; 175 (void) sigemptyset(&sv.sa_mask); 176 (void) sigvec(SIGALRM, &sv, (struct sigvec *)0); 177 if (tstart_writing) 178 (void) time(tstart_writing); 179 (void) timeclock(timeclockstate); 180 in_query_once = 0; 181 return (back); 182 } 183 /* 184 * Alert the console operator, and enable the alarm clock to 185 * sleep for time += 2 minutes in case nobody comes to satisfy dump 186 * If the alarm goes off while in the query_once for loop, we just 187 * longjmp back there and return the default answer. 188 */ 189 static void 190 #ifdef __STDC__ 191 alarmcatch(void) 192 #else 193 alarmcatch() 194 #endif 195 { 196 struct sigvec sv; 197 198 if (in_query_once) { 199 longjmp(sjalarmbuf, 1); 200 } 201 if (timeout) { 202 msgtail("\n"); 203 } 204 205 timeout += 120; 206 msg(gettext("NEEDS ATTENTION: %s"), attnmessage); 207 sv.sv_handler = alarmcatch; 208 sv.sv_flags = SA_RESTART; 209 (void) sigemptyset(&sv.sa_mask); 210 (void) sigvec(SIGALRM, &sv, (struct sigvec *)0); 211 (void) alarm(timeout); 212 } 213 214 /* 215 * Here if an inquisitive operator interrupts the dump program 216 */ 217 /*ARGSUSED*/ 218 void 219 interrupt(sig) 220 int sig; 221 { 222 if (!saveattn) { 223 saveattn = attnmessage; 224 } 225 msg(gettext("Interrupt received.\n")); 226 if (query(gettext( 227 "Do you want to abort dump?: (\"yes\" or \"no\") "))) { 228 dumpabort(); 229 /*NOTREACHED*/ 230 } 231 if (saveattn) { 232 attnmessage = saveattn; 233 saveattn = NULL; 234 alarmcatch(); 235 } 236 } 237 238 /* 239 * We use wall(1) to do the actual broadcasting, so 240 * that we don't have to worry about duplicated code 241 * only getting fixed in one place. This also saves 242 * us from having to worry about process groups, 243 * controlling terminals, and the like. 244 */ 245 void 246 broadcast(message) 247 char *message; 248 { 249 time_t clock; 250 pid_t pid; 251 int saverr; 252 int fildes[2]; 253 FILE *wall; 254 struct tm *localclock; 255 256 if (!notify) 257 return; 258 259 if (pipe(fildes) < 0) { 260 saverr = errno; 261 msg(gettext("pipe: %s\n"), strerror(saverr)); 262 return; 263 } 264 265 switch (pid = fork()) { 266 case -1: 267 return; 268 case 0: 269 close(fildes[0]); 270 if (dup2(fildes[1], 0) < 0) { 271 saverr = errno; 272 msg(gettext("dup2: %s\n"), strerror(saverr)); 273 exit(1); 274 } 275 execl("/usr/sbin/wall", "wall", "-g", OPGRENT, (char *)NULL); 276 saverr = errno; 277 msg(gettext("execl: %s\n"), strerror(saverr)); 278 exit(1); 279 default: 280 break; /* parent */ 281 } 282 283 close(fildes[1]); 284 wall = fdopen(fildes[0], "r+"); 285 if (wall == (FILE *)NULL) { 286 saverr = errno; 287 msg(gettext("fdopen: %s\n"), strerror(saverr)); 288 return; 289 } 290 291 clock = time((time_t *)0); 292 localclock = localtime(&clock); 293 294 (void) fprintf(wall, gettext( 295 "\n\007\007\007Message from the dump program to all operators at \ 296 %d:%02d ...\n\n%s"), 297 localclock->tm_hour, localclock->tm_min, message); 298 fclose(wall); 299 300 while (wait((int *)0) != pid) { 301 continue; 302 /*LINTED [empty loop body]*/ 303 } 304 } 305 306 /* 307 * print out an estimate of the amount of time left to do the dump 308 */ 309 #define EST_SEC 600 /* every 10 minutes */ 310 void 311 timeest(force, blkswritten) 312 int force; 313 int blkswritten; 314 { 315 time_t tnow, deltat; 316 char *msgp; 317 318 if (tschedule == NULL) 319 return; 320 if (*tschedule == 0) 321 *tschedule = time((time_t *)0) + EST_SEC; 322 (void) time(&tnow); 323 if ((force || tnow >= *tschedule) && blkswritten) { 324 *tschedule = tnow + EST_SEC; 325 if (!force && blkswritten < 50 * ntrec) 326 return; 327 deltat = (*telapsed + (tnow - *tstart_writing)) 328 * ((double)esize / blkswritten - 1.0); 329 msgp = gettext("%3.2f%% done, finished in %d:%02d\n"); 330 msg(msgp, (blkswritten*100.0)/esize, 331 deltat/3600, (deltat%3600)/60); 332 } 333 } 334 335 #include <stdarg.h> 336 337 /* VARARGS1 */ 338 void 339 msg(const char *fmt, ...) 340 { 341 char buf[1024], *cp; 342 size_t size; 343 va_list args; 344 345 va_start(args, fmt); 346 (void) strcpy(buf, " DUMP: "); 347 cp = &buf[strlen(buf)]; 348 #ifdef TDEBUG 349 (void) sprintf(cp, "pid=%d ", getpid()); 350 cp = &buf[strlen(buf)]; 351 #endif 352 /* don't need -1, vsnprintf does it right */ 353 /* LINTED pointer arithmetic result fits in size_t */ 354 size = ((size_t)sizeof (buf)) - (size_t)(cp - buf); 355 (void) vsnprintf(cp, size, fmt, args); 356 (void) fputs(buf, stderr); 357 (void) fflush(stdout); 358 (void) fflush(stderr); 359 va_end(args); 360 } 361 362 /* VARARGS1 */ 363 void 364 msgtail(const char *fmt, ...) 365 { 366 va_list args; 367 368 va_start(args, fmt); 369 (void) vfprintf(stderr, fmt, args); 370 va_end(args); 371 } 372 373 #define MINUTES(x) ((x) * 60) 374 375 /* 376 * Tell the operator what has to be done; 377 * we don't actually do it 378 */ 379 void 380 lastdump(arg) /* w ==> just what to do; W ==> most recent dumps */ 381 int arg; 382 { 383 char *lastname; 384 char *date; 385 int i; 386 time_t tnow, ddate; 387 struct mntent *dt; 388 int dumpme = 0; 389 struct idates *itwalk; 390 391 (void) time(&tnow); 392 mnttabread(); /* /etc/fstab input */ 393 inititimes(); /* /etc/dumpdates input */ 394 395 /* Don't use msg(), this isn't a tell-the-world kind of thing */ 396 if (arg == 'w') 397 (void) fprintf(stdout, gettext("Dump these file systems:\n")); 398 else 399 (void) fprintf(stdout, gettext( 400 "Last dump(s) done (Dump '>' file systems):\n")); 401 402 if (idatev != NULL) { 403 qsort((char *)idatev, nidates, sizeof (*idatev), idatesort); 404 lastname = "??"; 405 ITITERATE(i, itwalk) { 406 if (strncmp(lastname, itwalk->id_name, 407 sizeof (itwalk->id_name)) == 0) 408 continue; 409 /* must be ctime(), per ufsdump(4) */ 410 ddate = itwalk->id_ddate; 411 date = (char *)ctime(&ddate); 412 date[16] = '\0'; /* blow away seconds and year */ 413 lastname = itwalk->id_name; 414 dt = mnttabsearch(itwalk->id_name, 0); 415 if ((time_t)(itwalk->id_ddate) < (tnow - DAY)) { 416 dumpme = 1; 417 } 418 419 if ((arg == 'w') && dumpme) { 420 /* 421 * Handle the w option: print out file systems 422 * which haven't been backed up within a day. 423 */ 424 (void) printf(gettext("%8s\t(%6s)\n"), 425 itwalk->id_name, dt ? dt->mnt_dir : ""); 426 } 427 if (arg == 'W') { 428 /* 429 * Handle the W option: print out ALL 430 * filesystems including recent dump dates and 431 * dump levels. Mark the backup-needing 432 * filesystems with a >. 433 */ 434 (void) printf(gettext( 435 "%c %8s\t(%6s) Last dump: Level %c, Date %s\n"), 436 dumpme ? '>' : ' ', 437 itwalk->id_name, 438 dt ? dt->mnt_dir : "", 439 (uchar_t)itwalk->id_incno, 440 date); 441 } 442 dumpme = 0; 443 } 444 } 445 } 446 447 static int 448 idatesort(v1, v2) 449 #ifdef __STDC__ 450 const void *v1; 451 const void *v2; 452 #else 453 void *v1; 454 void *v2; 455 #endif 456 { 457 struct idates **p1 = (struct idates **)v1; 458 struct idates **p2 = (struct idates **)v2; 459 int diff; 460 461 diff = strcoll((*p1)->id_name, (*p2)->id_name); 462 if (diff == 0) { 463 /* 464 * Time may eventually become unsigned, so can't 465 * rely on subtraction to give a useful result. 466 * Note that we are sorting dates into reverse 467 * order, so that we will report based on the 468 * most-recent record for a particular filesystem. 469 */ 470 if ((*p1)->id_ddate > (*p2)->id_ddate) 471 diff = -1; 472 else if ((*p1)->id_ddate < (*p2)->id_ddate) 473 diff = 1; 474 } 475 return (diff); 476 } 477