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