xref: /titanic_50/usr/src/cmd/boot/bootadm/bootadm_upgrade.c (revision 551bc2a66868b5cb5be6b70ab9f55515e77a39a9)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <stdio.h>
29 #include <errno.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <limits.h>
36 #include <fcntl.h>
37 #include <strings.h>
38 
39 #include <sys/mman.h>
40 #include <sys/elf.h>
41 #include <sys/multiboot.h>
42 
43 #include "message.h"
44 #include "bootadm.h"
45 
46 direct_or_multi_t bam_direct = BAM_DIRECT_NOT_SET;
47 hv_t bam_is_hv = BAM_HV_UNKNOWN;
48 
49 error_t
50 dboot_or_multiboot(const char *root)
51 {
52 	char fname[PATH_MAX];
53 	char *image;
54 	uchar_t *ident;
55 	int fd, m;
56 	multiboot_header_t *mbh;
57 	struct stat sb;
58 
59 	if (!is_grub(root)) {
60 		/* there is no non dboot sparc new-boot */
61 		bam_direct = BAM_DIRECT_DBOOT;
62 		return (BAM_SUCCESS);
63 	}
64 
65 	(void) snprintf(fname, PATH_MAX, "%s/%s", root,
66 	    "platform/i86pc/kernel/unix");
67 	fd = open(fname, O_RDONLY);
68 	if (fd < 0) {
69 		bam_error(OPEN_FAIL, fname, strerror(errno));
70 		return (BAM_ERROR);
71 	}
72 
73 	/*
74 	 * mmap the first 8K
75 	 */
76 	image = mmap(NULL, 8192, PROT_READ, MAP_SHARED, fd, 0);
77 	if (image == MAP_FAILED) {
78 		bam_error(MMAP_FAIL, fname, strerror(errno));
79 		return (BAM_ERROR);
80 	}
81 
82 	ident = (uchar_t *)image;
83 	if (ident[EI_MAG0] != ELFMAG0 || ident[EI_MAG1] != ELFMAG1 ||
84 	    ident[EI_MAG2] != ELFMAG2 || ident[EI_MAG3] != ELFMAG3) {
85 		bam_error(NOT_ELF_FILE, fname);
86 		return (BAM_ERROR);
87 	}
88 	if (ident[EI_CLASS] != ELFCLASS32) {
89 		bam_error(WRONG_ELF_CLASS, fname, ident[EI_CLASS]);
90 		return (BAM_ERROR);
91 	}
92 
93 	/*
94 	 * The GRUB multiboot header must be 32-bit aligned and completely
95 	 * contained in the 1st 8K of the file.  If the unix binary has
96 	 * a multiboot header, then it is a 'dboot' kernel.  Otherwise,
97 	 * this kernel must be booted via multiboot -- we call this a
98 	 * 'multiboot' kernel.
99 	 */
100 	bam_direct = BAM_DIRECT_MULTIBOOT;
101 	for (m = 0; m < 8192 - sizeof (multiboot_header_t); m += 4) {
102 		mbh = (void *)(image + m);
103 		if (mbh->magic == MB_HEADER_MAGIC) {
104 			bam_direct = BAM_DIRECT_DBOOT;
105 			break;
106 		}
107 	}
108 	(void) munmap(image, 8192);
109 	(void) close(fd);
110 
111 	if (bam_direct == BAM_DIRECT_DBOOT) {
112 		(void) snprintf(fname, PATH_MAX, "%s/%s", root, XEN_32);
113 		if (stat(fname, &sb) == 0) {
114 			bam_is_hv = BAM_HV_PRESENT;
115 		} else {
116 			bam_is_hv = BAM_HV_NO;
117 		}
118 	}
119 
120 	return (BAM_SUCCESS);
121 }
122 
123 #define	INST_RELEASE	"var/sadm/system/admin/INST_RELEASE"
124 
125 /*
126  * Return true if root has been bfu'ed.  bfu will blow away
127  * var/sadm/system/admin/INST_RELEASE, so if it's still there, we can
128  * assume the system has not been bfu'ed.
129  */
130 static int
131 is_bfu_system(const char *root)
132 {
133 	static int is_bfu = -1;
134 	char path[PATH_MAX];
135 	struct stat sb;
136 
137 	if (is_bfu != -1)
138 		return (is_bfu);
139 
140 	(void) snprintf(path, sizeof (path), "%s/%s", root, INST_RELEASE);
141 	if (stat(path, &sb) != 0) {
142 		is_bfu = 1;
143 	} else {
144 		is_bfu = 0;
145 	}
146 	return (is_bfu);
147 }
148 
149 #define	MENU_URL(root)	(is_bfu_system(root) ?		\
150 	"http://www.sun.com/msg/SUNOS-8000-CF" :	\
151 	"http://www.sun.com/msg/SUNOS-8000-AK")
152 
153 /*
154  * Simply allocate a new line and copy in cmd + sep + arg
155  */
156 void
157 update_line(line_t *linep)
158 {
159 	size_t size;
160 
161 	free(linep->line);
162 	size = strlen(linep->cmd) + strlen(linep->sep) + strlen(linep->arg) + 1;
163 	linep->line = s_calloc(1, size);
164 	(void) snprintf(linep->line, size, "%s%s%s", linep->cmd, linep->sep,
165 	    linep->arg);
166 }
167 
168 /*
169  * The parse_kernel_line function examines a menu.lst kernel line.  For
170  * multiboot, this is:
171  *
172  * kernel <multiboot path> <flags1> <kernel path> <flags2>
173  *
174  * <multiboot path> is either /platform/i86pc/multiboot or /boot/multiboot
175  *
176  * <kernel path> may be missing, or may be any full or relative path to unix.
177  *	We check for it by looking for a word ending in "/unix".  If it ends
178  *	in "kernel/unix", we upgrade it to a 32-bit entry.  If it ends in
179  *	"kernel/amd64/unix", we upgrade it to the default entry.  Otherwise,
180  *	it's a custom kernel, and we skip it.
181  *
182  * <flags*> are anything that doesn't fit either of the above - these will be
183  *	copied over.
184  *
185  * For direct boot, the defaults are
186  *
187  * kernel$ <kernel path> <flags>
188  *
189  * <kernel path> is one of:
190  *	/platform/i86pc/kernel/$ISADIR/unix
191  *	/platform/i86pc/kernel/unix
192  *	/platform/i86pc/kernel/amd64/unix
193  *	/boot/platform/i86pc/kernel/unix
194  *
195  * If <kernel path> is any of the last three, the command may also be "kernel".
196  *
197  * <flags> is anything that isn't <kernel path>.
198  *
199  * This function is only called if it applies to our target boot environment.
200  * If we can't make any sense of the kernel line, an error is printed and
201  * BAM_ERROR is returned.
202  *
203  * The desired install type is given in the global variable bam_direct.
204  * If the kernel line is of a different install type, we change it to the
205  * preferred type.  If the kernel line is already of the correct install
206  * type, we do nothing.  Either way, BAM_SUCCESS is returned.
207  *
208  * For safety, we do one more check: if the kernel path starts with /boot,
209  * we verify that the new kernel exists before changing it.  This is mainly
210  * done for bfu, as it may cause the failsafe archives to be a different
211  * boot architecture from the newly bfu'ed system.
212  */
213 static error_t
214 parse_kernel_line(line_t *linep, const char *root, uint8_t *flags)
215 {
216 	char path[PATH_MAX];
217 	int len, left, total_len;
218 	struct stat sb;
219 	char *new_ptr, *new_arg, *old_ptr;
220 	menu_cmd_t which;
221 
222 	/* Used when changing a multiboot line to dboot */
223 	char *unix_ptr, *flags1_ptr, *flags2_ptr;
224 
225 	/*
226 	 * Note that BAM_ENTRY_DBOOT refers to the entry we're looking at, not
227 	 * necessarily the system type.
228 	 */
229 	if (strncmp(linep->arg, DIRECT_BOOT_32,
230 	    sizeof (DIRECT_BOOT_32) - 1) == 0) {
231 		*flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_32BIT;
232 	} else if ((strncmp(linep->arg, DIRECT_BOOT_KERNEL,
233 	    sizeof (DIRECT_BOOT_KERNEL) - 1) == 0) ||
234 	    (strncmp(linep->arg, DIRECT_BOOT_64,
235 	    sizeof (DIRECT_BOOT_64) - 1) == 0) ||
236 	    (strncmp(linep->arg, DIRECT_BOOT_FAILSAFE_KERNEL,
237 	    sizeof (DIRECT_BOOT_FAILSAFE_KERNEL) - 1) == 0)) {
238 		*flags |= BAM_ENTRY_DBOOT;
239 	} else if ((strncmp(linep->arg, MULTI_BOOT,
240 	    sizeof (MULTI_BOOT) - 1) == 0) ||
241 	    (strncmp(linep->arg, MULTI_BOOT_FAILSAFE,
242 	    sizeof (MULTI_BOOT_FAILSAFE) - 1) == 0)) {
243 		*flags &= ~BAM_ENTRY_DBOOT;
244 	} else {
245 		bam_error(NO_KERNEL_MATCH, linep->lineNum, MENU_URL(root));
246 		return (BAM_ERROR);
247 	}
248 
249 	if (((*flags & BAM_ENTRY_DBOOT) && (bam_direct == BAM_DIRECT_DBOOT)) ||
250 	    (((*flags & BAM_ENTRY_DBOOT) == 0) &&
251 	    (bam_direct == BAM_DIRECT_MULTIBOOT))) {
252 
253 		/* No action needed */
254 		return (BAM_SUCCESS);
255 	}
256 
257 	if (*flags & BAM_ENTRY_MINIROOT) {
258 		/*
259 		 * We're changing boot architectures - make sure
260 		 * the multiboot failsafe still exists.
261 		 */
262 		(void) snprintf(path, PATH_MAX, "%s%s", root,
263 		    (*flags & BAM_ENTRY_DBOOT) ? MULTI_BOOT_FAILSAFE :
264 		    DIRECT_BOOT_FAILSAFE_KERNEL);
265 		if (stat(path, &sb) != 0) {
266 			if (bam_verbose) {
267 				bam_error(FAILSAFE_MISSING, linep->lineNum);
268 			}
269 			return (BAM_SUCCESS);
270 		}
271 	}
272 
273 	/*
274 	 * Make sure we have the correct cmd - either kernel or kernel$
275 	 * The failsafe entry should always be KERNEL_CMD.
276 	 */
277 	which = ((bam_direct == BAM_DIRECT_MULTIBOOT) ||
278 	    (*flags & BAM_ENTRY_MINIROOT)) ? KERNEL_CMD : KERNEL_DOLLAR_CMD;
279 	free(linep->cmd);
280 	len = strlen(menu_cmds[which]) + 1;
281 	linep->cmd = s_calloc(1, len);
282 	(void) strncpy(linep->cmd, menu_cmds[which], len);
283 
284 	/*
285 	 * Since all arguments are copied, the new arg string should be close
286 	 * in size to the old one.  Just add 32 to cover the difference in
287 	 * the boot path.
288 	 */
289 	total_len = strlen(linep->arg) + 32;
290 	new_arg = s_calloc(1, total_len);
291 	old_ptr = strchr(linep->arg, ' ');
292 	if (old_ptr != NULL)
293 		old_ptr++;
294 
295 	/*
296 	 * Transitioning from dboot to multiboot is pretty simple.  We
297 	 * copy in multiboot and any args.
298 	 */
299 	if (bam_direct == BAM_DIRECT_MULTIBOOT) {
300 		if (old_ptr == NULL) {
301 			(void) snprintf(new_arg, total_len, "%s",
302 			    (*flags & BAM_ENTRY_MINIROOT) ?
303 			    MULTI_BOOT_FAILSAFE : MULTI_BOOT);
304 		} else {
305 			(void) snprintf(new_arg, total_len, "%s %s",
306 			    (*flags & BAM_ENTRY_MINIROOT) ?
307 			    MULTI_BOOT_FAILSAFE : MULTI_BOOT, old_ptr);
308 		}
309 		goto done;
310 	}
311 
312 	/*
313 	 * Transitioning from multiboot to directboot is a bit more
314 	 * complicated, since we may have two sets of arguments to
315 	 * copy and a unix path to parse.
316 	 *
317 	 * First, figure out if there's a unix path.
318 	 */
319 	if ((old_ptr != NULL) &&
320 	    ((unix_ptr = strstr(old_ptr, "/unix")) != NULL)) {
321 		/* See if there's anything past unix */
322 		flags2_ptr = unix_ptr + sizeof ("/unix");
323 		if (*flags2_ptr == '\0') {
324 			flags2_ptr = NULL;
325 		}
326 
327 		while ((unix_ptr > old_ptr) && (*unix_ptr != ' '))
328 			unix_ptr--;
329 
330 		if (unix_ptr == old_ptr) {
331 			flags1_ptr = NULL;
332 		} else {
333 			flags1_ptr = old_ptr;
334 		}
335 
336 		if (strstr(unix_ptr, "kernel/unix") != NULL) {
337 			*flags |= BAM_ENTRY_32BIT;
338 		} else if ((strstr(unix_ptr, "kernel/amd64/unix") == NULL) &&
339 		    (!bam_force)) {
340 			/*
341 			 * If the above strstr returns NULL, but bam_force is
342 			 * set, we'll be upgrading an Install kernel.  The
343 			 * result probably won't be what was intended, but we'll
344 			 * try it anyways.
345 			 */
346 			return (BAM_SKIP);
347 		}
348 	} else if (old_ptr != NULL) {
349 		flags1_ptr = old_ptr;
350 		unix_ptr = flags1_ptr + strlen(old_ptr);
351 		flags2_ptr = NULL;
352 	} else {
353 		unix_ptr = flags1_ptr = flags2_ptr = NULL;
354 	}
355 
356 	if (*flags & BAM_ENTRY_MINIROOT) {
357 		(void) snprintf(new_arg, total_len, "%s",
358 		    DIRECT_BOOT_FAILSAFE_KERNEL);
359 	} else if (*flags & BAM_ENTRY_32BIT) {
360 		(void) snprintf(new_arg, total_len, "%s", DIRECT_BOOT_32);
361 	} else {
362 		(void) snprintf(new_arg, total_len, "%s", DIRECT_BOOT_KERNEL);
363 	}
364 
365 	/*
366 	 * We now want to copy flags1_ptr through unix_ptr, and
367 	 * flags2_ptr through the end of the string
368 	 */
369 	if (flags1_ptr != NULL) {
370 		len = strlcat(new_arg, " ", total_len);
371 		left = total_len - len;
372 		new_ptr = new_arg + len;
373 
374 		if ((unix_ptr - flags1_ptr) < left)
375 			left = (unix_ptr - flags1_ptr) + 1;
376 		(void) strlcpy(new_ptr, flags1_ptr, left);
377 	}
378 	if (flags2_ptr != NULL) {
379 		(void) strlcat(new_arg, " ", total_len);
380 		(void) strlcat(new_arg, flags2_ptr, total_len);
381 	}
382 
383 done:
384 	free(linep->arg);
385 	linep->arg = new_arg;
386 	update_line(linep);
387 	return (BAM_SUCCESS);
388 }
389 
390 /*
391  * Similar to above, except this time we're looking at a module line,
392  * which is quite a bit simpler.
393  *
394  * Under multiboot, the archive line is:
395  *
396  * module /platform/i86pc/boot_archive
397  *
398  * Under directboot, the archive line is:
399  *
400  * module$ /platform/i86pc/$ISADIR/boot_archive
401  *
402  * which may be specified exactly as either of:
403  *
404  * module /platform/i86pc/boot_archive
405  * module /platform/i86pc/amd64/boot_archive
406  *
407  * For either dboot or multiboot, the failsafe is:
408  *
409  * module /boot/x86.miniroot-safe
410  */
411 static error_t
412 parse_module_line(line_t *linep, const char *root, uint8_t flags)
413 {
414 	int len;
415 	menu_cmd_t which;
416 	char *new;
417 
418 	/*
419 	 * If necessary, BAM_ENTRY_MINIROOT was already set in flags
420 	 * in upgrade_menu().  We re-check BAM_ENTRY_DBOOT here in here
421 	 * in case the kernel and module lines differ.
422 	 */
423 	if ((strcmp(linep->arg, DIRECT_BOOT_ARCHIVE) == 0) ||
424 	    (strcmp(linep->arg, DIRECT_BOOT_ARCHIVE_64) == 0)) {
425 		flags |= BAM_ENTRY_DBOOT;
426 	} else if ((strcmp(linep->arg, MULTI_BOOT_ARCHIVE) == 0) ||
427 	    (strcmp(linep->arg, MINIROOT) == 0)) {
428 		flags &= ~BAM_ENTRY_DBOOT;
429 	} else {
430 		bam_error(NO_MODULE_MATCH, linep->lineNum, MENU_URL(root));
431 		return (BAM_ERROR);
432 	}
433 
434 	if (((flags & BAM_ENTRY_DBOOT) && (bam_direct == BAM_DIRECT_DBOOT)) ||
435 	    (((flags & BAM_ENTRY_DBOOT) == 0) &&
436 	    (bam_direct == BAM_DIRECT_MULTIBOOT)) ||
437 	    ((flags & BAM_ENTRY_MINIROOT) &&
438 	    (strcmp(linep->cmd, menu_cmds[MODULE_CMD]) == 0))) {
439 
440 		/* No action needed */
441 		return (BAM_SUCCESS);
442 	}
443 
444 	/*
445 	 * Make sure we have the correct cmd - either module or module$
446 	 * The failsafe entry should always be MODULE_CMD.
447 	 */
448 	which = ((bam_direct == BAM_DIRECT_MULTIBOOT) ||
449 	    (flags & BAM_ENTRY_MINIROOT)) ? MODULE_CMD : MODULE_DOLLAR_CMD;
450 	free(linep->cmd);
451 	len = strlen(menu_cmds[which]) + 1;
452 	linep->cmd = s_calloc(1, len);
453 	(void) strncpy(linep->cmd, menu_cmds[which], len);
454 
455 	if (flags & BAM_ENTRY_MINIROOT) {
456 		new = MINIROOT;
457 	} else if ((bam_direct == BAM_DIRECT_DBOOT) &&
458 	    ((flags & BAM_ENTRY_32BIT) == 0)) {
459 		new = DIRECT_BOOT_ARCHIVE;
460 	} else {
461 		new = MULTI_BOOT_ARCHIVE;
462 	}
463 
464 	free(linep->arg);
465 	len = strlen(new) + 1;
466 	linep->arg = s_calloc(1, len);
467 	(void) strncpy(linep->arg, new, len);
468 	update_line(linep);
469 
470 	return (BAM_SUCCESS);
471 }
472 
473 /*ARGSUSED*/
474 error_t
475 upgrade_menu(menu_t *mp, char *root, char *opt)
476 {
477 	entry_t	*cur_entry;
478 	line_t	*cur_line;
479 	int	i, skipit, num_entries, found_hv;
480 	int	*hand_entries = NULL;
481 	boolean_t found_kernel = B_FALSE;
482 	error_t	rv;
483 	char	*rootdev, *grubdisk = NULL;
484 
485 	skipit = num_entries = found_hv = 0;
486 
487 	rootdev = get_special(root);
488 	if (rootdev) {
489 		grubdisk = os_to_grubdisk(rootdev, strlen(root) == 1);
490 		free(rootdev);
491 		rootdev = NULL;
492 	}
493 
494 	/* Loop through all OS entries in the menu.lst file */
495 	for (cur_entry = mp->entries; cur_entry != NULL;
496 	    cur_entry = cur_entry->next, skipit = 0) {
497 
498 		if ((cur_entry->flags & BAM_ENTRY_CHAINLOADER) ||
499 		    ((cur_entry->flags & BAM_ENTRY_MINIROOT) && !bam_force))
500 			continue;
501 
502 		/*
503 		 * We only change entries added by bootadm and live upgrade,
504 		 * and warn on the rest, unless the -f flag was passed.
505 		 */
506 		if ((!(cur_entry->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU))) &&
507 		    !bam_force) {
508 			if (num_entries == 0) {
509 				hand_entries = s_calloc(1, sizeof (int));
510 			} else {
511 				hand_entries = s_realloc(hand_entries,
512 				    (num_entries + 1) * sizeof (int));
513 			}
514 			hand_entries[num_entries++] = cur_entry->entryNum;
515 			continue;
516 		}
517 
518 		if (cur_entry->flags & BAM_ENTRY_HV) {
519 			found_hv = 1;
520 			continue;
521 		}
522 
523 		/*
524 		 * We make two loops through the lines.  First, we check if
525 		 * there is a root entry, and if so, whether we should be
526 		 * checking this entry.
527 		 */
528 		if ((grubdisk != NULL) && (cur_entry->flags & BAM_ENTRY_ROOT)) {
529 			for (cur_line = cur_entry->start; cur_line != NULL;
530 			    cur_line = cur_line->next) {
531 				if ((cur_line->cmd == NULL) ||
532 				    (cur_line->arg == NULL))
533 					continue;
534 
535 				if (strcmp(cur_line->cmd,
536 				    menu_cmds[ROOT_CMD]) == 0) {
537 					if (strcmp(cur_line->arg,
538 					    grubdisk) != 0) {
539 						/* A different slice */
540 						skipit = 1;
541 					}
542 					break;
543 				}
544 				if (cur_line == cur_entry->end)
545 					break;
546 			}
547 		}
548 		if (skipit)
549 			continue;
550 
551 		for (cur_line = cur_entry->start; cur_line != NULL;
552 		    cur_line = cur_line->next) {
553 
554 			/*
555 			 * We only compare for the length of KERNEL_CMD,
556 			 * so that KERNEL_DOLLAR_CMD will also match.
557 			 */
558 			if (strncmp(cur_line->cmd, menu_cmds[KERNEL_CMD],
559 			    strlen(menu_cmds[KERNEL_CMD])) == 0) {
560 				rv = parse_kernel_line(cur_line, root,
561 				    &(cur_entry->flags));
562 				if (rv == BAM_SKIP) {
563 					break;
564 				} else if (rv != BAM_SUCCESS) {
565 					return (rv);
566 				}
567 				found_kernel = B_TRUE;
568 			} else if (strncmp(cur_line->cmd,
569 			    menu_cmds[MODULE_CMD],
570 			    strlen(menu_cmds[MODULE_CMD])) == 0) {
571 				rv = parse_module_line(cur_line, root,
572 				    cur_entry->flags);
573 				if (rv != BAM_SUCCESS) {
574 					return (rv);
575 				}
576 			}
577 			if (cur_line == cur_entry->end)
578 				break;
579 		}
580 	}
581 
582 	/*
583 	 * If we're upgrading to a virtualized kernel and there are no
584 	 * hv entries in menu.lst, we need to add one.
585 	 */
586 	if ((bam_is_hv == BAM_HV_PRESENT) && (found_hv == 0)) {
587 		(void) add_boot_entry(mp, NEW_HV_ENTRY, grubdisk,
588 		    XEN_MENU, KERNEL_MODULE_LINE, DIRECT_BOOT_ARCHIVE);
589 	}
590 
591 	/*
592 	 * We only want to output one error, to avoid confusing a user.  We
593 	 * rank "No kernels changed" as a higher priority than "will not
594 	 * update hand-added entries", since the former implies the latter.
595 	 */
596 	if (found_kernel == B_FALSE) {
597 		bam_error(NO_KERNELS_FOUND, MENU_URL(root));
598 		return (BAM_ERROR);
599 	} else if (num_entries > 0) {
600 		bam_error(HAND_ADDED_ENTRY, MENU_URL(root));
601 		bam_print_stderr("Entry Number%s: ", (num_entries > 1) ?
602 		    "s" : "");
603 		for (i = 0; i < num_entries; i++) {
604 			bam_print_stderr("%d ", hand_entries[i]);
605 		}
606 		bam_print_stderr("\n");
607 	}
608 	return (BAM_WRITE);
609 }
610