1 /*- 2 * Copyright (c) 1980, 1988 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 static char sccsid[] = "@(#)optr.c 5.16 (Berkeley) 05/11/93"; 10 #endif /* not lint */ 11 12 #include <sys/param.h> 13 #include <sys/wait.h> 14 #include <sys/time.h> 15 16 #include <errno.h> 17 #include <fstab.h> 18 #include <grp.h> 19 #include <signal.h> 20 #include <stdio.h> 21 #ifdef __STDC__ 22 #include <stdlib.h> 23 #include <string.h> 24 #include <stdarg.h> 25 #endif 26 #include <tzfile.h> 27 #ifdef __STDC__ 28 #include <unistd.h> 29 #endif 30 #include <utmp.h> 31 #ifndef __STDC__ 32 #include <varargs.h> 33 #endif 34 35 #include "dump.h" 36 #include "pathnames.h" 37 38 void alarmcatch __P((/* int, int */)); 39 int datesort __P((const void *, const void *)); 40 static void sendmes __P((char *, char *)); 41 42 /* 43 * Query the operator; This previously-fascist piece of code 44 * no longer requires an exact response. 45 * It is intended to protect dump aborting by inquisitive 46 * people banging on the console terminal to see what is 47 * happening which might cause dump to croak, destroying 48 * a large number of hours of work. 49 * 50 * Every 2 minutes we reprint the message, alerting others 51 * that dump needs attention. 52 */ 53 static int timeout; 54 static char *attnmessage; /* attention message */ 55 56 int 57 query(question) 58 char *question; 59 { 60 char replybuffer[64]; 61 int back, errcount; 62 FILE *mytty; 63 64 if ((mytty = fopen(_PATH_TTY, "r")) == NULL) 65 quit("fopen on %s fails: %s\n", _PATH_TTY, strerror(errno)); 66 attnmessage = question; 67 timeout = 0; 68 alarmcatch(); 69 back = -1; 70 errcount = 0; 71 do { 72 if (fgets(replybuffer, 63, mytty) == NULL) { 73 clearerr(mytty); 74 if (++errcount > 30) /* XXX ugly */ 75 quit("excessive operator query failures\n"); 76 } else if (replybuffer[0] == 'y' || replybuffer[0] == 'Y') { 77 back = 1; 78 } else if (replybuffer[0] == 'n' || replybuffer[0] == 'N') { 79 back = 0; 80 } else { 81 (void) fprintf(stderr, 82 " DUMP: \"Yes\" or \"No\"?\n"); 83 (void) fprintf(stderr, 84 " DUMP: %s: (\"yes\" or \"no\") ", question); 85 } 86 } while (back < 0); 87 88 /* 89 * Turn off the alarm, and reset the signal to trap out.. 90 */ 91 (void) alarm(0); 92 if (signal(SIGALRM, sig) == SIG_IGN) 93 signal(SIGALRM, SIG_IGN); 94 (void) fclose(mytty); 95 return(back); 96 } 97 98 char lastmsg[100]; 99 100 /* 101 * Alert the console operator, and enable the alarm clock to 102 * sleep for 2 minutes in case nobody comes to satisfy dump 103 */ 104 void 105 alarmcatch() 106 { 107 if (notify == 0) { 108 if (timeout == 0) 109 (void) fprintf(stderr, 110 " DUMP: %s: (\"yes\" or \"no\") ", 111 attnmessage); 112 else 113 msgtail("\7\7"); 114 } else { 115 if (timeout) { 116 msgtail("\n"); 117 broadcast(""); /* just print last msg */ 118 } 119 (void) fprintf(stderr," DUMP: %s: (\"yes\" or \"no\") ", 120 attnmessage); 121 } 122 signal(SIGALRM, alarmcatch); 123 (void) alarm(120); 124 timeout = 1; 125 } 126 127 /* 128 * Here if an inquisitive operator interrupts the dump program 129 */ 130 void 131 interrupt(signo) 132 int signo; 133 { 134 msg("Interrupt received.\n"); 135 if (query("Do you want to abort dump?")) 136 dumpabort(0); 137 } 138 139 /* 140 * The following variables and routines manage alerting 141 * operators to the status of dump. 142 * This works much like wall(1) does. 143 */ 144 struct group *gp; 145 146 /* 147 * Get the names from the group entry "operator" to notify. 148 */ 149 void 150 set_operators() 151 { 152 if (!notify) /*not going to notify*/ 153 return; 154 gp = getgrnam(OPGRENT); 155 (void) endgrent(); 156 if (gp == NULL) { 157 msg("No group entry for %s.\n", OPGRENT); 158 notify = 0; 159 return; 160 } 161 } 162 163 struct tm *localclock; 164 165 /* 166 * We fork a child to do the actual broadcasting, so 167 * that the process control groups are not messed up 168 */ 169 void 170 broadcast(message) 171 char *message; 172 { 173 time_t clock; 174 FILE *f_utmp; 175 struct utmp utmp; 176 char **np; 177 int pid, s; 178 179 if (!notify || gp == NULL) 180 return; 181 182 switch (pid = fork()) { 183 case -1: 184 return; 185 case 0: 186 break; 187 default: 188 while (wait(&s) != pid) 189 continue; 190 return; 191 } 192 193 clock = time((time_t *)0); 194 localclock = localtime(&clock); 195 196 if ((f_utmp = fopen(_PATH_UTMP, "r")) == NULL) { 197 msg("Cannot open %s: %s\n", _PATH_UTMP, strerror(errno)); 198 return; 199 } 200 201 while (!feof(f_utmp)) { 202 if (fread((char *) &utmp, sizeof (struct utmp), 1, f_utmp) != 1) 203 break; 204 if (utmp.ut_name[0] == 0) 205 continue; 206 for (np = gp->gr_mem; *np; np++) { 207 if (strncmp(*np, utmp.ut_name, sizeof(utmp.ut_name)) != 0) 208 continue; 209 /* 210 * Do not send messages to operators on dialups 211 */ 212 if (strncmp(utmp.ut_line, DIALUP, strlen(DIALUP)) == 0) 213 continue; 214 #ifdef DEBUG 215 msg("Message to %s at %s\n", *np, utmp.ut_line); 216 #endif 217 sendmes(utmp.ut_line, message); 218 } 219 } 220 (void) fclose(f_utmp); 221 Exit(0); /* the wait in this same routine will catch this */ 222 /* NOTREACHED */ 223 } 224 225 static void 226 sendmes(tty, message) 227 char *tty, *message; 228 { 229 char t[50], buf[BUFSIZ]; 230 register char *cp; 231 int lmsg = 1; 232 FILE *f_tty; 233 234 (void) strcpy(t, _PATH_DEV); 235 (void) strcat(t, tty); 236 237 if ((f_tty = fopen(t, "w")) != NULL) { 238 setbuf(f_tty, buf); 239 (void) fprintf(f_tty, 240 "\n\ 241 \7\7\7Message from the dump program to all operators at %d:%02d ...\r\n\n\ 242 DUMP: NEEDS ATTENTION: ", 243 localclock->tm_hour, localclock->tm_min); 244 for (cp = lastmsg; ; cp++) { 245 if (*cp == '\0') { 246 if (lmsg) { 247 cp = message; 248 if (*cp == '\0') 249 break; 250 lmsg = 0; 251 } else 252 break; 253 } 254 if (*cp == '\n') 255 (void) putc('\r', f_tty); 256 (void) putc(*cp, f_tty); 257 } 258 (void) fclose(f_tty); 259 } 260 } 261 262 /* 263 * print out an estimate of the amount of time left to do the dump 264 */ 265 266 time_t tschedule = 0; 267 268 void 269 timeest() 270 { 271 time_t tnow, deltat; 272 273 (void) time((time_t *) &tnow); 274 if (tnow >= tschedule) { 275 tschedule = tnow + 300; 276 if (blockswritten < 500) 277 return; 278 deltat = tstart_writing - tnow + 279 (1.0 * (tnow - tstart_writing)) 280 / blockswritten * tapesize; 281 msg("%3.2f%% done, finished in %d:%02d\n", 282 (blockswritten * 100.0) / tapesize, 283 deltat / 3600, (deltat % 3600) / 60); 284 } 285 } 286 287 void 288 #if __STDC__ 289 msg(const char *fmt, ...) 290 #else 291 msg(fmt, va_alist) 292 char *fmt; 293 va_dcl 294 #endif 295 { 296 va_list ap; 297 298 (void) fprintf(stderr," DUMP: "); 299 #ifdef TDEBUG 300 (void) fprintf(stderr, "pid=%d ", getpid()); 301 #endif 302 #if __STDC__ 303 va_start(ap, fmt); 304 #else 305 va_start(ap); 306 #endif 307 (void) vfprintf(stderr, fmt, ap); 308 (void) fflush(stdout); 309 (void) fflush(stderr); 310 (void) vsprintf(lastmsg, fmt, ap); 311 va_end(ap); 312 } 313 314 void 315 #if __STDC__ 316 msgtail(const char *fmt, ...) 317 #else 318 msgtail(fmt, va_alist) 319 char *fmt; 320 va_dcl 321 #endif 322 { 323 va_list ap; 324 #if __STDC__ 325 va_start(ap, fmt); 326 #else 327 va_start(ap); 328 #endif 329 (void) vfprintf(stderr, fmt, ap); 330 va_end(ap); 331 } 332 333 void 334 #if __STDC__ 335 quit(const char *fmt, ...) 336 #else 337 quit(fmt, va_alist) 338 char *fmt; 339 va_dcl 340 #endif 341 { 342 va_list ap; 343 344 (void) fprintf(stderr," DUMP: "); 345 #ifdef TDEBUG 346 (void) fprintf(stderr, "pid=%d ", getpid()); 347 #endif 348 #if __STDC__ 349 va_start(ap, fmt); 350 #else 351 va_start(ap); 352 #endif 353 (void) vfprintf(stderr, fmt, ap); 354 va_end(ap); 355 (void) fflush(stdout); 356 (void) fflush(stderr); 357 dumpabort(0); 358 } 359 360 /* 361 * Tell the operator what has to be done; 362 * we don't actually do it 363 */ 364 365 struct fstab * 366 allocfsent(fs) 367 register struct fstab *fs; 368 { 369 register struct fstab *new; 370 371 new = (struct fstab *)malloc(sizeof (*fs)); 372 if (new == NULL || 373 (new->fs_file = strdup(fs->fs_file)) == NULL || 374 (new->fs_type = strdup(fs->fs_type)) == NULL || 375 (new->fs_spec = strdup(fs->fs_spec)) == NULL) 376 quit("%s\n", strerror(errno)); 377 new->fs_passno = fs->fs_passno; 378 new->fs_freq = fs->fs_freq; 379 return (new); 380 } 381 382 struct pfstab { 383 struct pfstab *pf_next; 384 struct fstab *pf_fstab; 385 }; 386 387 static struct pfstab *table; 388 389 void 390 getfstab() 391 { 392 register struct fstab *fs; 393 register struct pfstab *pf; 394 395 if (setfsent() == 0) { 396 msg("Can't open %s for dump table information: %s\n", 397 _PATH_FSTAB, strerror(errno)); 398 return; 399 } 400 while ((fs = getfsent()) != NULL) { 401 if (strcmp(fs->fs_type, FSTAB_RW) && 402 strcmp(fs->fs_type, FSTAB_RO) && 403 strcmp(fs->fs_type, FSTAB_RQ)) 404 continue; 405 fs = allocfsent(fs); 406 if ((pf = (struct pfstab *)malloc(sizeof (*pf))) == NULL) 407 quit("%s\n", strerror(errno)); 408 pf->pf_fstab = fs; 409 pf->pf_next = table; 410 table = pf; 411 } 412 (void) endfsent(); 413 } 414 415 /* 416 * Search in the fstab for a file name. 417 * This file name can be either the special or the path file name. 418 * 419 * The entries in the fstab are the BLOCK special names, not the 420 * character special names. 421 * The caller of fstabsearch assures that the character device 422 * is dumped (that is much faster) 423 * 424 * The file name can omit the leading '/'. 425 */ 426 struct fstab * 427 fstabsearch(key) 428 char *key; 429 { 430 register struct pfstab *pf; 431 register struct fstab *fs; 432 433 for (pf = table; pf != NULL; pf = pf->pf_next) { 434 fs = pf->pf_fstab; 435 if (strcmp(fs->fs_file, key) == 0 || 436 strcmp(fs->fs_spec, key) == 0 || 437 strcmp(rawname(fs->fs_spec), key) == 0) 438 return (fs); 439 if (key[0] != '/') { 440 if (*fs->fs_spec == '/' && 441 strcmp(fs->fs_spec + 1, key) == 0) 442 return (fs); 443 if (*fs->fs_file == '/' && 444 strcmp(fs->fs_file + 1, key) == 0) 445 return (fs); 446 } 447 } 448 return (NULL); 449 } 450 451 /* 452 * Tell the operator what to do 453 */ 454 void 455 lastdump(arg) 456 char arg; /* w ==> just what to do; W ==> most recent dumps */ 457 { 458 register int i; 459 register struct fstab *dt; 460 register struct dumpdates *dtwalk; 461 char *lastname, *date; 462 int dumpme; 463 time_t tnow; 464 465 (void) time(&tnow); 466 getfstab(); /* /etc/fstab input */ 467 initdumptimes(); /* /etc/dumpdates input */ 468 qsort((char *) ddatev, nddates, sizeof(struct dumpdates *), datesort); 469 470 if (arg == 'w') 471 (void) printf("Dump these file systems:\n"); 472 else 473 (void) printf("Last dump(s) done (Dump '>' file systems):\n"); 474 lastname = "??"; 475 ITITERATE(i, dtwalk) { 476 if (strncmp(lastname, dtwalk->dd_name, 477 sizeof(dtwalk->dd_name)) == 0) 478 continue; 479 date = (char *)ctime(&dtwalk->dd_ddate); 480 date[16] = '\0'; /* blast away seconds and year */ 481 lastname = dtwalk->dd_name; 482 dt = fstabsearch(dtwalk->dd_name); 483 dumpme = (dt != NULL && 484 dt->fs_freq != 0 && 485 dtwalk->dd_ddate < tnow - (dt->fs_freq * SECSPERDAY)); 486 if (arg != 'w' || dumpme) 487 (void) printf( 488 "%c %8s\t(%6s) Last dump: Level %c, Date %s\n", 489 dumpme && (arg != 'w') ? '>' : ' ', 490 dtwalk->dd_name, 491 dt ? dt->fs_file : "", 492 dtwalk->dd_level, 493 date); 494 } 495 } 496 497 int 498 datesort(a1, a2) 499 const void *a1, *a2; 500 { 501 struct dumpdates *d1 = *(struct dumpdates **)a1; 502 struct dumpdates *d2 = *(struct dumpdates **)a2; 503 int diff; 504 505 diff = strncmp(d1->dd_name, d2->dd_name, sizeof(d1->dd_name)); 506 if (diff == 0) 507 return (d2->dd_ddate - d1->dd_ddate); 508 return (diff); 509 } 510