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