File: | src/sbin/dump/tape.c |
Warning: | line 818, column 7 Although the value stored to 'nread' is used in the enclosing expression, the value is never actually read from 'nread' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: tape.c,v 1.47 2021/01/21 00:16:36 mortimer Exp $ */ |
2 | /* $NetBSD: tape.c,v 1.11 1997/06/05 11:13:26 lukem Exp $ */ |
3 | |
4 | /*- |
5 | * Copyright (c) 1980, 1991, 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 DEV_BSIZE */ |
34 | #include <sys/socket.h> |
35 | #include <sys/time.h> |
36 | #include <sys/wait.h> |
37 | #include <sys/stat.h> |
38 | #include <ufs/ffs/fs.h> |
39 | #include <ufs/ufs/dinode.h> |
40 | |
41 | #include <protocols/dumprestore.h> |
42 | |
43 | #include <errno(*__errno()).h> |
44 | #include <fcntl.h> |
45 | #include <signal.h> |
46 | #include <stdio.h> |
47 | #include <stdlib.h> |
48 | #include <string.h> |
49 | #include <time.h> |
50 | #include <unistd.h> |
51 | #include <limits.h> |
52 | |
53 | #include "dump.h" |
54 | #include "pathnames.h" |
55 | |
56 | #define MINIMUM(a, b)(((a) < (b)) ? (a) : (b)) (((a) < (b)) ? (a) : (b)) |
57 | |
58 | ino_t curino; /* current inumber; used globally */ |
59 | int newtape; /* new tape flag */ |
60 | union u_spcl u_spcl; /* mapping of variables in a control block */ |
61 | |
62 | static int tapefd; /* tape file descriptor */ |
63 | static int64_t asize; /* number of 0.1" units written on cur tape */ |
64 | static int writesize; /* size of malloc()ed buffer for tape */ |
65 | static int64_t lastspclrec = -1; /* tape block number of last written header */ |
66 | static int trecno = 0; /* next record to write in current block */ |
67 | static int64_t blocksthisvol; /* number of blocks on current output file */ |
68 | static char *nexttape; |
69 | |
70 | static ssize_t atomic(ssize_t (*)(int, void *, size_t), int, char *, int); |
71 | static void doslave(int, int); |
72 | static void enslave(void); |
73 | static void flushtape(void); |
74 | static void killall(void); |
75 | static void rollforward(void); |
76 | |
77 | void tperror(int signo); |
78 | void sigpipe(int signo); |
79 | void proceed(int signo); |
80 | |
81 | /* |
82 | * Concurrent dump mods (Caltech) - disk block reading and tape writing |
83 | * are exported to several slave processes. While one slave writes the |
84 | * tape, the others read disk blocks; they pass control of the tape in |
85 | * a ring via signals. The parent process traverses the filesystem and |
86 | * sends writeheader()'s and lists of daddr's to the slaves via pipes. |
87 | * The following structure defines the instruction packets sent to slaves. |
88 | */ |
89 | struct req { |
90 | daddr_t dblk; |
91 | int count; |
92 | }; |
93 | static int reqsiz; |
94 | |
95 | #define SLAVES3 3 /* 1 slave writing, 1 reading, 1 for slack */ |
96 | static struct slave { |
97 | int64_t tapea; /* header number at start of this chunk */ |
98 | int64_t firstrec; /* record number of this block */ |
99 | int count; /* count to next header (used for TS_TAPE */ |
100 | /* after EOT) */ |
101 | int inode; /* inode that we are currently dealing with */ |
102 | int fd; /* FD for this slave */ |
103 | pid_t pid; /* PID for this slave */ |
104 | int sent; /* 1 == we've sent this slave requests */ |
105 | char (*tblock)[TP_BSIZE1024]; /* buffer for data blocks */ |
106 | struct req *req; /* buffer for requests */ |
107 | } slaves[SLAVES3+1]; |
108 | static struct slave *slp; |
109 | |
110 | static char (*nextblock)[TP_BSIZE1024]; |
111 | |
112 | static time_t tstart_volume; /* time of volume start */ |
113 | static int64_t tapea_volume; /* value of spcl.c_tapea at volume start */ |
114 | |
115 | static pid_t master; /* pid of master, for sending error signals */ |
116 | static int tenths; /* length of tape used per block written */ |
117 | static volatile sig_atomic_t caught; /* have we caught the signal to proceed? */ |
118 | |
119 | int |
120 | alloctape(void) |
121 | { |
122 | int pgoff = getpagesize() - 1; |
123 | char *buf; |
124 | int i; |
125 | |
126 | writesize = ntrec * TP_BSIZE1024; |
127 | reqsiz = (ntrec + 1) * sizeof(struct req); |
128 | /* |
129 | * CDC 92181's and 92185's make 0.8" gaps in 1600-bpi start/stop mode |
130 | * (see DEC TU80 User's Guide). The shorter gaps of 6250-bpi require |
131 | * repositioning after stopping, i.e, streaming mode, where the gap is |
132 | * variable, 0.30" to 0.45". The gap is maximal when the tape stops. |
133 | */ |
134 | if (blocksperfile == 0 && !unlimited) |
135 | tenths = writesize / density + |
136 | (cartridge ? 16 : density == 625 ? 5 : 8); |
137 | /* |
138 | * Allocate tape buffer contiguous with the array of instruction |
139 | * packets, so flushtape() can write them together with one write(). |
140 | * Align tape buffer on page boundary to speed up tape write(). |
141 | */ |
142 | for (i = 0; i <= SLAVES3; i++) { |
143 | buf = malloc((unsigned)(reqsiz + writesize + pgoff + TP_BSIZE1024)); |
144 | if (buf == NULL((void *)0)) |
145 | return(0); |
146 | slaves[i].tblock = (char (*)[TP_BSIZE1024]) |
147 | (((long)&buf[ntrec + 1] + pgoff) &~ pgoff); |
148 | slaves[i].req = (struct req *)slaves[i].tblock - ntrec - 1; |
149 | } |
150 | slp = &slaves[0]; |
151 | slp->count = 1; |
152 | slp->tapea = 0; |
153 | slp->firstrec = 0; |
154 | nextblock = slp->tblock; |
155 | return(1); |
156 | } |
157 | |
158 | void |
159 | writerec(char *dp, int isspcl) |
160 | { |
161 | |
162 | slp->req[trecno].dblk = 0; |
163 | slp->req[trecno].count = 1; |
164 | *(union u_spcl *)(*(nextblock)++) = *(union u_spcl *)dp; |
165 | if (isspcl) |
166 | lastspclrec = spclu_spcl.s_spcl.c_tapea; |
167 | trecno++; |
168 | spclu_spcl.s_spcl.c_tapea++; |
169 | if (trecno >= ntrec) |
170 | flushtape(); |
171 | } |
172 | |
173 | void |
174 | dumpblock(daddr_t blkno, int size) |
175 | { |
176 | int avail, tpblks; |
177 | daddr_t dblkno; |
178 | |
179 | dblkno = fsbtodb(sblock, blkno)((blkno) << (sblock)->fs_fsbtodb); |
180 | tpblks = size >> tp_bshift; |
181 | while ((avail = MINIMUM(tpblks, ntrec - trecno)(((tpblks) < (ntrec - trecno)) ? (tpblks) : (ntrec - trecno ))) > 0) { |
182 | slp->req[trecno].dblk = dblkno; |
183 | slp->req[trecno].count = avail; |
184 | trecno += avail; |
185 | spclu_spcl.s_spcl.c_tapea += avail; |
186 | if (trecno >= ntrec) |
187 | flushtape(); |
188 | dblkno += avail << (tp_bshift - (ffs(DEV_BSIZE(1 << 9)) - 1)); |
189 | tpblks -= avail; |
190 | } |
191 | } |
192 | |
193 | int nogripe = 0; |
194 | |
195 | /* ARGSUSED */ |
196 | void |
197 | tperror(int signo) |
198 | { |
199 | /* XXX - signal races */ |
200 | |
201 | if (pipeout) { |
202 | msg("write error on %s\n", tape); |
203 | quit("Cannot recover\n"); |
204 | /* NOTREACHED */ |
205 | } |
206 | msg("write error %lld blocks into volume %d\n", |
207 | (long long)blocksthisvol, tapeno); |
208 | broadcast("DUMP WRITE ERROR!\n"); |
209 | if (!query("Do you want to restart?")) |
210 | dumpabort(0); |
211 | msg("Closing this volume. Prepare to restart with new media;\n"); |
212 | msg("this dump volume will be rewritten.\n"); |
213 | killall(); |
214 | nogripe = 1; |
215 | close_rewind(); |
216 | Exit(X_REWRITE2); |
217 | } |
218 | |
219 | /* ARGSUSED */ |
220 | void |
221 | sigpipe(int signo) |
222 | { |
223 | |
224 | quit("Broken pipe\n"); |
225 | } |
226 | |
227 | /* |
228 | * do_stats -- |
229 | * Update xferrate stats |
230 | */ |
231 | time_t |
232 | do_stats(void) |
233 | { |
234 | time_t tnow, ttaken; |
235 | int64_t blocks; |
236 | |
237 | (void)time(&tnow); |
238 | ttaken = tnow - tstart_volume; |
239 | blocks = spclu_spcl.s_spcl.c_tapea - tapea_volume; |
240 | msg("Volume %d completed at: %s", tapeno, ctime(&tnow)); |
241 | if (ttaken > 0) { |
242 | msg("Volume %d took %lld:%02lld:%02lld\n", tapeno, |
243 | (long long)ttaken / 3600, ((long long)ttaken % 3600) / 60, |
244 | (long long)ttaken % 60); |
245 | blocks /= ttaken; |
246 | msg("Volume %d transfer rate: %lld KB/s\n", tapeno, blocks); |
247 | xferrate += blocks; |
248 | } |
249 | return(tnow); |
250 | } |
251 | |
252 | /* |
253 | * statussig -- |
254 | * information message upon receipt of SIGINFO |
255 | * (derived from optr.c::timeest()) |
256 | * XXX not safe |
257 | */ |
258 | /* ARGSUSED */ |
259 | void |
260 | statussig(int signo) |
261 | { |
262 | time_t tnow, deltat; |
263 | int save_errno = errno(*__errno()); |
264 | |
265 | if (blockswritten < 500) |
266 | return; |
267 | (void) time(&tnow); |
268 | deltat = tstart_writing - tnow + (1.0 * (tnow - tstart_writing)) |
269 | / blockswritten * tapesize; |
270 | /* XXX not safe due to floating point printf */ |
271 | dprintf(STDERR_FILENO2, |
272 | "dump: %s %3.2f%% done at %lld KB/s, finished in %d:%02d\n", |
273 | tape, (blockswritten * 100.0) / tapesize, |
274 | (spclu_spcl.s_spcl.c_tapea - tapea_volume) / (tnow - tstart_volume), |
275 | (int)(deltat / 3600), (int)((deltat % 3600) / 60)); |
276 | errno(*__errno()) = save_errno; |
277 | } |
278 | |
279 | static void |
280 | flushtape(void) |
281 | { |
282 | int i, blks, got; |
283 | int64_t lastfirstrec; |
284 | |
285 | int siz = (char *)nextblock - (char *)slp->req; |
286 | |
287 | slp->req[trecno].count = 0; /* Sentinel */ |
288 | |
289 | if (atomic((ssize_t (*)(int, void *, size_t))write, slp->fd, |
290 | (char *)slp->req, siz) != siz) |
291 | quit("error writing command pipe: %s\n", strerror(errno(*__errno()))); |
292 | slp->sent = 1; /* we sent a request, read the response later */ |
293 | |
294 | lastfirstrec = slp->firstrec; |
295 | |
296 | if (++slp >= &slaves[SLAVES3]) |
297 | slp = &slaves[0]; |
298 | |
299 | /* Read results back from next slave */ |
300 | if (slp->sent) { |
301 | if (atomic(read, slp->fd, (char *)&got, sizeof(got)) |
302 | != sizeof(got)) { |
303 | perror(" DUMP: error reading command pipe in master"); |
304 | dumpabort(0); |
305 | } |
306 | slp->sent = 0; |
307 | |
308 | /* Check for end of tape */ |
309 | if (got < writesize) { |
310 | msg("End of tape detected\n"); |
311 | |
312 | /* |
313 | * Drain the results, don't care what the values were. |
314 | * If we read them here then trewind won't... |
315 | */ |
316 | for (i = 0; i < SLAVES3; i++) { |
317 | if (slaves[i].sent) { |
318 | if (atomic(read, slaves[i].fd, |
319 | (char *)&got, sizeof(got)) |
320 | != sizeof(got)) { |
321 | perror(" DUMP: error reading command pipe in master"); |
322 | dumpabort(0); |
323 | } |
324 | slaves[i].sent = 0; |
325 | } |
326 | } |
327 | |
328 | close_rewind(); |
329 | rollforward(); |
330 | return; |
331 | } |
332 | } |
333 | |
334 | blks = 0; |
335 | if (spclu_spcl.s_spcl.c_type != TS_END5 && spclu_spcl.s_spcl.c_type != TS_CLRI6 && |
336 | spclu_spcl.s_spcl.c_type != TS_BITS3) { |
337 | if (spclu_spcl.s_spcl.c_count > TP_NINDIR(1024/2)) |
338 | quit("c_count too large\n"); |
339 | for (i = 0; i < spclu_spcl.s_spcl.c_count; i++) |
340 | if (spclu_spcl.s_spcl.c_addr[i] != 0) |
341 | blks++; |
342 | } |
343 | slp->count = lastspclrec + blks + 1 - spclu_spcl.s_spcl.c_tapea; |
344 | slp->tapea = spclu_spcl.s_spcl.c_tapea; |
345 | slp->firstrec = lastfirstrec + ntrec; |
346 | slp->inode = curino; |
347 | nextblock = slp->tblock; |
348 | trecno = 0; |
349 | asize += tenths; |
350 | blockswritten += ntrec; |
351 | blocksthisvol += ntrec; |
352 | if (!pipeout && !unlimited && (blocksperfile ? |
353 | (blocksthisvol >= blocksperfile) : (asize > tsize))) { |
354 | close_rewind(); |
355 | startnewtape(0); |
356 | } |
357 | timeest(); |
358 | } |
359 | |
360 | void |
361 | trewind(void) |
362 | { |
363 | struct stat sb; |
364 | int f, got; |
365 | |
366 | for (f = 0; f < SLAVES3; f++) { |
367 | /* |
368 | * Drain the results, but unlike EOT we DO (or should) care |
369 | * what the return values were, since if we detect EOT after |
370 | * we think we've written the last blocks to the tape anyway, |
371 | * we have to replay those blocks with rollforward. |
372 | * |
373 | * fixme: punt for now. |
374 | */ |
375 | if (slaves[f].sent) { |
376 | if (atomic(read, slaves[f].fd, (char *)&got, sizeof(got)) |
377 | != sizeof(got)) { |
378 | perror(" DUMP: error reading command pipe in master"); |
379 | dumpabort(0); |
380 | } |
381 | slaves[f].sent = 0; |
382 | if (got != writesize) { |
383 | msg("EOT detected in last 2 tape records!\n"); |
384 | msg("Use a longer tape, decrease the size estimate\n"); |
385 | quit("or use no size estimate at all.\n"); |
386 | } |
387 | } |
388 | (void) close(slaves[f].fd); |
389 | } |
390 | while (wait((int *)NULL((void *)0)) >= 0) /* wait for any signals from slaves */ |
391 | /* void */; |
392 | |
393 | if (pipeout) |
394 | return; |
395 | |
396 | msg("Closing %s\n", tape); |
397 | |
398 | #ifdef RDUMP1 |
399 | if (host) { |
400 | rmtclose(); |
401 | while (rmtopen(tape, O_RDONLY0x0000) < 0) |
402 | sleep(10); |
403 | rmtclose(); |
404 | return; |
405 | } |
406 | #endif |
407 | /* |
408 | * st(4) says: "Bit 1 of the minor number specifies whether an eject is |
409 | * attempted when the device is closed. When it is set, the device |
410 | * will attempt to eject its media on close ...". |
411 | * |
412 | * If the tape has been ejected, looping on open() will generate 'Media |
413 | * not present' errors until a tape is loaded. Once loaded the tape |
414 | * will be immediately ejected as a result of the second close(). |
415 | * |
416 | * So if the tape will be ejected, just close and return. |
417 | */ |
418 | if ((fstat(tapefd, &sb) == 0) && (minor(sb.st_rdev)((unsigned)((sb.st_rdev) & 0xff) | (((sb.st_rdev) & 0xffff0000 ) >> 8)) & 0x02)) { |
419 | (void) close(tapefd); |
420 | return; |
421 | } |
422 | |
423 | (void) close(tapefd); |
424 | while ((f = open(tape, O_RDONLY0x0000)) == -1) |
425 | sleep (10); |
426 | (void) close(f); |
427 | } |
428 | |
429 | void |
430 | close_rewind(void) |
431 | { |
432 | trewind(); |
433 | (void)do_stats(); |
434 | if (nexttape) |
435 | return; |
436 | if (!nogripe) { |
437 | msg("Change Volumes: Mount volume #%d\n", tapeno+1); |
438 | broadcast("CHANGE DUMP VOLUMES!\7\7\n"); |
439 | } |
440 | while (!query("Is the new volume mounted and ready to go?")) |
441 | if (query("Do you want to abort?")) { |
442 | dumpabort(0); |
443 | /*NOTREACHED*/ |
444 | } |
445 | } |
446 | |
447 | void |
448 | rollforward(void) |
449 | { |
450 | struct req *p, *q, *prev; |
451 | struct slave *tslp; |
452 | int i, size, got; |
453 | int64_t savedtapea; |
454 | union u_spcl *ntb, *otb; |
455 | tslp = &slaves[SLAVES3]; |
456 | ntb = (union u_spcl *)tslp->tblock[1]; |
457 | |
458 | /* |
459 | * Each of the N slaves should have requests that need to |
460 | * be replayed on the next tape. Use the extra slave buffers |
461 | * (slaves[SLAVES]) to construct request lists to be sent to |
462 | * each slave in turn. |
463 | */ |
464 | for (i = 0; i < SLAVES3; i++) { |
465 | q = &tslp->req[1]; |
466 | otb = (union u_spcl *)slp->tblock; |
467 | |
468 | /* |
469 | * For each request in the current slave, copy it to tslp. |
470 | */ |
471 | |
472 | prev = NULL((void *)0); |
473 | for (p = slp->req; p->count > 0; p += p->count) { |
474 | *q = *p; |
475 | if (p->dblk == 0) |
476 | *ntb++ = *otb++; /* copy the datablock also */ |
477 | prev = q; |
478 | q += q->count; |
479 | } |
480 | if (prev == NULL((void *)0)) |
481 | quit("rollforward: protocol botch\n"); |
482 | if (prev->dblk != 0) |
483 | prev->count -= 1; |
484 | else |
485 | ntb--; |
486 | q -= 1; |
487 | q->count = 0; |
488 | q = &tslp->req[0]; |
489 | if (i == 0) { |
490 | q->dblk = 0; |
491 | q->count = 1; |
492 | trecno = 0; |
493 | nextblock = tslp->tblock; |
494 | savedtapea = spclu_spcl.s_spcl.c_tapea; |
495 | spclu_spcl.s_spcl.c_tapea = slp->tapea; |
496 | startnewtape(0); |
497 | spclu_spcl.s_spcl.c_tapea = savedtapea; |
498 | lastspclrec = savedtapea - 1; |
499 | } |
500 | size = (char *)ntb - (char *)q; |
501 | if (atomic((ssize_t (*)(int, void *, size_t))write, |
502 | slp->fd, (char *)q, size) != size) { |
503 | perror(" DUMP: error writing command pipe"); |
504 | dumpabort(0); |
505 | } |
506 | slp->sent = 1; |
507 | if (++slp >= &slaves[SLAVES3]) |
508 | slp = &slaves[0]; |
509 | |
510 | q->count = 1; |
511 | |
512 | if (prev->dblk != 0) { |
513 | /* |
514 | * If the last one was a disk block, make the |
515 | * first of this one be the last bit of that disk |
516 | * block... |
517 | */ |
518 | q->dblk = prev->dblk + |
519 | prev->count * (TP_BSIZE1024 / DEV_BSIZE(1 << 9)); |
520 | ntb = (union u_spcl *)tslp->tblock; |
521 | } else { |
522 | /* |
523 | * It wasn't a disk block. Copy the data to its |
524 | * new location in the buffer. |
525 | */ |
526 | q->dblk = 0; |
527 | *((union u_spcl *)tslp->tblock) = *ntb; |
528 | ntb = (union u_spcl *)tslp->tblock[1]; |
529 | } |
530 | } |
531 | slp->req[0] = *q; |
532 | nextblock = slp->tblock; |
533 | if (q->dblk == 0) |
534 | nextblock++; |
535 | trecno = 1; |
536 | |
537 | /* |
538 | * Clear the first slaves' response. One hopes that it |
539 | * worked ok, otherwise the tape is much too short! |
540 | */ |
541 | if (slp->sent) { |
542 | if (atomic(read, slp->fd, (char *)&got, sizeof(got)) |
543 | != sizeof(got)) { |
544 | perror(" DUMP: error reading command pipe in master"); |
545 | dumpabort(0); |
546 | } |
547 | slp->sent = 0; |
548 | |
549 | if (got != writesize) { |
550 | quit("EOT detected at start of the tape!\n"); |
551 | } |
552 | } |
553 | } |
554 | |
555 | /* |
556 | * We implement taking and restoring checkpoints on the tape level. |
557 | * When each tape is opened, a new process is created by forking; this |
558 | * saves all of the necessary context in the parent. The child |
559 | * continues the dump; the parent waits around, saving the context. |
560 | * If the child returns X_REWRITE, then it had problems writing that tape; |
561 | * this causes the parent to fork again, duplicating the context, and |
562 | * everything continues as if nothing had happened. |
563 | */ |
564 | void |
565 | startnewtape(int top) |
566 | { |
567 | pid_t parentpid; |
568 | pid_t childpid; |
569 | int status; |
570 | pid_t waitingpid; |
571 | char *p; |
572 | sig_t interrupt_save; |
573 | |
574 | interrupt_save = signal(SIGINT2, SIG_IGN(void (*)(int))1); |
575 | parentpid = getpid(); |
576 | tapea_volume = spclu_spcl.s_spcl.c_tapea; |
577 | (void)time(&tstart_volume); |
578 | |
579 | restore_check_point: |
580 | (void)signal(SIGINT2, interrupt_save); |
581 | /* |
582 | * All signals are inherited... |
583 | */ |
584 | childpid = fork(); |
585 | if (childpid == -1) { |
586 | msg("Context save fork fails in parent %d\n", parentpid); |
587 | Exit(X_ABORT3); |
588 | } |
589 | if (childpid != 0) { |
590 | /* |
591 | * PARENT: |
592 | * save the context by waiting |
593 | * until the child doing all of the work returns. |
594 | * don't catch the interrupt |
595 | */ |
596 | signal(SIGINT2, SIG_IGN(void (*)(int))1); |
597 | #ifdef TDEBUG |
598 | msg("Tape: %d; parent process: %d child process %d\n", |
599 | tapeno+1, parentpid, childpid); |
600 | #endif /* TDEBUG */ |
601 | while ((waitingpid = wait(&status)) != childpid) |
602 | msg("Parent %d waiting for child %d has another child %d return\n", |
603 | parentpid, childpid, waitingpid); |
604 | if (status & 0xFF) { |
605 | msg("Child %d returns LOB status %o\n", |
606 | childpid, status&0xFF); |
607 | } |
608 | status = (status >> 8) & 0xFF; |
609 | #ifdef TDEBUG |
610 | switch(status) { |
611 | case X_FINOK0: |
612 | msg("Child %d finishes X_FINOK\n", childpid); |
613 | break; |
614 | case X_ABORT3: |
615 | msg("Child %d finishes X_ABORT\n", childpid); |
616 | break; |
617 | case X_REWRITE2: |
618 | msg("Child %d finishes X_REWRITE\n", childpid); |
619 | break; |
620 | default: |
621 | msg("Child %d finishes unknown %d\n", |
622 | childpid, status); |
623 | break; |
624 | } |
625 | #endif /* TDEBUG */ |
626 | switch(status) { |
627 | case X_FINOK0: |
628 | Exit(X_FINOK0); |
629 | break; |
630 | case X_ABORT3: |
631 | Exit(X_ABORT3); |
632 | break; |
633 | case X_REWRITE2: |
634 | goto restore_check_point; |
635 | default: |
636 | msg("Bad return code from dump: %d\n", status); |
637 | Exit(X_ABORT3); |
638 | } |
639 | /*NOTREACHED*/ |
640 | } else { /* we are the child; just continue */ |
641 | #ifdef TDEBUG |
642 | sleep(4); /* allow time for parent's message to get out */ |
643 | msg("Child on Tape %d has parent %d, my pid = %d\n", |
644 | tapeno+1, parentpid, getpid()); |
645 | #endif /* TDEBUG */ |
646 | /* |
647 | * If we have a name like "/dev/rst0,/dev/rst1", |
648 | * use the name before the comma first, and save |
649 | * the remaining names for subsequent volumes. |
650 | */ |
651 | tapeno++; /* current tape sequence */ |
652 | if (nexttape || strchr(tape, ',')) { |
653 | if (nexttape && *nexttape) |
654 | tape = nexttape; |
655 | if ((p = strchr(tape, ',')) != NULL((void *)0)) { |
656 | *p = '\0'; |
657 | nexttape = p + 1; |
658 | } else |
659 | nexttape = NULL((void *)0); |
660 | msg("Dumping volume %d on %s\n", tapeno, tape); |
661 | } |
662 | #ifdef RDUMP1 |
663 | while ((tapefd = (host ? rmtopen(tape, O_WRONLY0x0001|O_CREAT0x0200) : |
664 | pipeout ? 1 : open(tape, O_WRONLY0x0001|O_CREAT0x0200, 0666))) == -1) |
665 | #else |
666 | while ((tapefd = (pipeout ? 1 : |
667 | open(tape, O_WRONLY0x0001|O_CREAT0x0200, 0666))) == -1) |
668 | #endif |
669 | { |
670 | msg("Cannot open output \"%s\".\n", tape); |
671 | if (!query("Do you want to retry the open?")) |
672 | dumpabort(0); |
673 | } |
674 | |
675 | enslave(); /* Share open tape file descriptor with slaves */ |
676 | |
677 | asize = 0; |
678 | blocksthisvol = 0; |
679 | if (top) |
680 | newtape++; /* new tape signal */ |
681 | spclu_spcl.s_spcl.c_count = slp->count; |
682 | /* |
683 | * measure firstrec in TP_BSIZE units since restore doesn't |
684 | * know the correct ntrec value... |
685 | */ |
686 | spclu_spcl.s_spcl.c_firstrec = slp->firstrec; |
687 | spclu_spcl.s_spcl.c_volume++; |
688 | spclu_spcl.s_spcl.c_type = TS_TAPE1; |
689 | if (sblock->fs_magic != FS_UFS2_MAGIC0x19540119) |
690 | spclu_spcl.s_spcl.c_flags |= DR_NEWHEADER0x0001; |
691 | writeheader((ino_t)slp->inode); |
692 | if (sblock->fs_magic != FS_UFS2_MAGIC0x19540119) |
693 | spclu_spcl.s_spcl.c_flags &=~ DR_NEWHEADER0x0001; |
694 | msg("Volume %d started at: %s", tapeno, ctime(&tstart_volume)); |
695 | if (tapeno > 1) |
696 | msg("Volume %d begins with blocks from inode %llu\n", |
697 | tapeno, (unsigned long long)slp->inode); |
698 | } |
699 | } |
700 | |
701 | /* ARGSUSED */ |
702 | void |
703 | dumpabort(int signo) |
704 | { |
705 | |
706 | if (master != 0 && master != getpid()) |
707 | /* Signals master to call dumpabort */ |
708 | (void) kill(master, SIGTERM15); |
709 | else { |
710 | killall(); |
711 | msg("The ENTIRE dump is aborted.\n"); |
712 | } |
713 | #ifdef RDUMP1 |
714 | rmtclose(); |
715 | #endif |
716 | Exit(X_ABORT3); |
717 | } |
718 | |
719 | __dead__attribute__((__noreturn__)) void |
720 | Exit(int status) |
721 | { |
722 | |
723 | #ifdef TDEBUG |
724 | msg("pid = %d exits with status %d\n", getpid(), status); |
725 | #endif /* TDEBUG */ |
726 | exit(status); |
727 | } |
728 | |
729 | /* |
730 | * proceed - handler for SIGUSR2, used to synchronize IO between the slaves. |
731 | */ |
732 | /* ARGSUSED */ |
733 | void |
734 | proceed(int signo) |
735 | { |
736 | caught++; |
737 | } |
738 | |
739 | void |
740 | enslave(void) |
741 | { |
742 | int cmd[2]; |
743 | int i, j; |
744 | |
745 | master = getpid(); |
746 | |
747 | signal(SIGTERM15, dumpabort); /* Slave sends SIGTERM on dumpabort() */ |
748 | signal(SIGPIPE13, sigpipe); |
749 | signal(SIGUSR130, tperror); /* Slave sends SIGUSR1 on tape errors */ |
750 | signal(SIGUSR231, proceed); /* Slave sends SIGUSR2 to next slave */ |
751 | |
752 | for (i = 0; i < SLAVES3; i++) { |
753 | if (i == slp - &slaves[0]) { |
754 | caught = 1; |
755 | } else { |
756 | caught = 0; |
757 | } |
758 | |
759 | if (socketpair(AF_UNIX1, SOCK_STREAM1, 0, cmd) == -1 || |
760 | (slaves[i].pid = fork()) == -1) |
761 | quit("too many slaves, %d (recompile smaller): %s\n", |
762 | i, strerror(errno(*__errno()))); |
763 | |
764 | slaves[i].fd = cmd[1]; |
765 | slaves[i].sent = 0; |
766 | if (slaves[i].pid == 0) { /* Slave starts up here */ |
767 | for (j = 0; j <= i; j++) |
768 | (void) close(slaves[j].fd); |
769 | signal(SIGINT2, SIG_IGN(void (*)(int))1); /* Master handles this */ |
770 | signal(SIGINFO29, SIG_IGN(void (*)(int))1); |
771 | doslave(cmd[0], i); |
772 | Exit(X_FINOK0); |
773 | } |
774 | } |
775 | |
776 | for (i = 0; i < SLAVES3; i++) |
777 | (void) atomic((ssize_t (*)(int, void *, size_t))write, |
778 | slaves[i].fd, (char *) &slaves[(i + 1) % SLAVES3].pid, |
779 | sizeof(slaves[0].pid)); |
780 | master = 0; |
781 | } |
782 | |
783 | void |
784 | killall(void) |
785 | { |
786 | int i; |
787 | |
788 | for (i = 0; i < SLAVES3; i++) |
789 | if (slaves[i].pid > 0) { |
790 | (void) kill(slaves[i].pid, SIGKILL9); |
791 | slaves[i].pid = 0; |
792 | } |
793 | } |
794 | |
795 | /* |
796 | * Synchronization - each process has a lockfile, and shares file |
797 | * descriptors to the following process's lockfile. When our write |
798 | * completes, we release our lock on the following process's lock- |
799 | * file, allowing the following process to lock it and proceed. We |
800 | * get the lock back for the next cycle by swapping descriptors. |
801 | */ |
802 | static void |
803 | doslave(int cmd, int slave_number) |
804 | { |
805 | int nread, nextslave, size, wrote = 0, eot_count; |
806 | sigset_t nsigset, osigset; |
807 | |
808 | /* |
809 | * Need our own seek pointer. |
810 | */ |
811 | (void) close(diskfd); |
812 | if ((diskfd = open(disk, O_RDONLY0x0000)) == -1) |
813 | quit("slave couldn't reopen disk: %s\n", strerror(errno(*__errno()))); |
814 | |
815 | /* |
816 | * Need the pid of the next slave in the loop... |
817 | */ |
818 | if ((nread = atomic(read, cmd, (char *)&nextslave, sizeof(nextslave))) |
Although the value stored to 'nread' is used in the enclosing expression, the value is never actually read from 'nread' | |
819 | != sizeof(nextslave)) { |
820 | quit("master/slave protocol botched - didn't get pid of next slave.\n"); |
821 | } |
822 | |
823 | /* |
824 | * Get list of blocks to dump, read the blocks into tape buffer |
825 | */ |
826 | while ((nread = atomic(read, cmd, (char *)slp->req, reqsiz)) == reqsiz) { |
827 | struct req *p = slp->req; |
828 | |
829 | for (trecno = 0; trecno < ntrec; |
830 | trecno += p->count, p += p->count) { |
831 | if (p->dblk) { |
832 | bread(p->dblk, slp->tblock[trecno], |
833 | p->count * TP_BSIZE1024); |
834 | } else { |
835 | if (p->count != 1 || atomic(read, cmd, |
836 | (char *)slp->tblock[trecno], |
837 | TP_BSIZE1024) != TP_BSIZE1024) |
838 | quit("master/slave protocol botched.\n"); |
839 | } |
840 | } |
841 | |
842 | sigemptyset(&nsigset); |
843 | sigaddset(&nsigset, SIGUSR231); |
844 | sigprocmask(SIG_BLOCK1, &nsigset, &osigset); |
845 | while (!caught) |
846 | sigsuspend(&osigset); |
847 | caught = 0; |
848 | sigprocmask(SIG_SETMASK3, &osigset, NULL((void *)0)); |
849 | |
850 | /* Try to write the data... */ |
851 | eot_count = 0; |
852 | size = 0; |
853 | |
854 | while (eot_count < 10 && size < writesize) { |
855 | #ifdef RDUMP1 |
856 | if (host) |
857 | wrote = rmtwrite(slp->tblock[0]+size, |
858 | writesize-size); |
859 | else |
860 | #endif |
861 | wrote = write(tapefd, slp->tblock[0]+size, |
862 | writesize-size); |
863 | #ifdef WRITEDEBUG |
864 | printf("slave %d wrote %d\n", slave_number, wrote); |
865 | #endif |
866 | if (wrote < 0) |
867 | break; |
868 | if (wrote == 0) |
869 | eot_count++; |
870 | size += wrote; |
871 | } |
872 | |
873 | #ifdef WRITEDEBUG |
874 | if (size != writesize) |
875 | printf("slave %d only wrote %d out of %d bytes and gave up.\n", |
876 | slave_number, size, writesize); |
877 | #endif |
878 | |
879 | if (eot_count > 0) |
880 | size = 0; |
881 | |
882 | /* |
883 | * Handle ENOSPC as an EOT condition |
884 | */ |
885 | if (wrote < 0 && errno(*__errno()) == ENOSPC28) { |
886 | wrote = 0; |
887 | eot_count++; |
888 | } |
889 | |
890 | if (size < 0) { |
891 | (void) kill(master, SIGUSR130); |
892 | sigemptyset(&nsigset); |
893 | for (;;) |
894 | sigsuspend(&nsigset); |
895 | } else { |
896 | /* |
897 | * pass size of write back to master |
898 | * (for EOT handling) |
899 | */ |
900 | (void) atomic((ssize_t (*)(int, void *, size_t))write, |
901 | cmd, (char *)&size, sizeof(size)); |
902 | } |
903 | |
904 | /* |
905 | * If partial write, don't want next slave to go. |
906 | * Also jolts him awake. |
907 | */ |
908 | (void) kill(nextslave, SIGUSR231); |
909 | } |
910 | if (nread != 0) |
911 | quit("error reading command pipe: %s\n", strerror(errno(*__errno()))); |
912 | } |
913 | |
914 | /* |
915 | * Since a read from a pipe may not return all we asked for, |
916 | * or a write may not write all we ask if we get a signal, |
917 | * loop until the count is satisfied (or error). |
918 | */ |
919 | static ssize_t |
920 | atomic(ssize_t (*func)(int, void *, size_t), int fd, char *buf, int count) |
921 | { |
922 | ssize_t got, need = count; |
923 | |
924 | while ((got = (*func)(fd, buf, need)) > 0 && (need -= got) > 0) |
925 | buf += got; |
926 | return (got < 0 ? got : count - need); |
927 | } |