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