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