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