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