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 (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
  23  * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
  24  */
  25 
  26 #include <sys/types.h>
  27 #include <sys/stat.h>
  28 #include <sys/swap.h>
  29 #include <sys/dumpadm.h>
  30 #include <sys/utsname.h>
  31 
  32 #include <unistd.h>
  33 #include <string.h>
  34 #include <stdlib.h>
  35 #include <stdio.h>
  36 #include <fcntl.h>
  37 #include <errno.h>
  38 #include <libdiskmgt.h>
  39 #include <libzfs.h>
  40 #include <uuid/uuid.h>
  41 
  42 #include "dconf.h"
  43 #include "minfree.h"
  44 #include "utils.h"
  45 #include "swap.h"
  46 
  47 typedef struct dc_token {
  48         const char *tok_name;
  49         int (*tok_parse)(dumpconf_t *, char *);
  50         int (*tok_print)(const dumpconf_t *, FILE *);
  51 } dc_token_t;
  52 
  53 
  54 static int print_device(const dumpconf_t *, FILE *);
  55 static int print_savdir(const dumpconf_t *, FILE *);
  56 static int print_content(const dumpconf_t *, FILE *);
  57 static int print_enable(const dumpconf_t *, FILE *);
  58 static int print_csave(const dumpconf_t *, FILE *);
  59 
  60 static const dc_token_t tokens[] = {
  61         { "DUMPADM_DEVICE", dconf_str2device, print_device },
  62         { "DUMPADM_SAVDIR", dconf_str2savdir, print_savdir },
  63         { "DUMPADM_CONTENT", dconf_str2content, print_content },
  64         { "DUMPADM_ENABLE", dconf_str2enable, print_enable },
  65         { "DUMPADM_CSAVE", dconf_str2csave, print_csave },
  66         { NULL, NULL, NULL }
  67 };
  68 
  69 static const char DC_STR_ON[] = "on";           /* On string */
  70 static const char DC_STR_OFF[] = "off";         /* Off string */
  71 static const char DC_STR_YES[] = "yes";         /* Enable on string */
  72 static const char DC_STR_NO[] = "no";           /* Enable off string */
  73 static const char DC_STR_SWAP[] = "swap";       /* Default dump device */
  74 static const char DC_STR_NONE[] = "none";
  75 
  76 /* The pages included in the dump */
  77 static const char DC_STR_KERNEL[] = "kernel";   /* Kernel only */
  78 static const char DC_STR_CURPROC[] = "curproc"; /* Kernel + current process */
  79 static const char DC_STR_ALL[] = "all";         /* All pages */
  80 
  81 /*
  82  * Permissions and ownership for the configuration file:
  83  */
  84 #define DC_OWNER        0                               /* Uid 0 (root) */
  85 #define DC_GROUP        1                               /* Gid 1 (other) */
  86 #define DC_PERM (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) /* Mode 0644 */
  87 
  88 static void
  89 dconf_init(dumpconf_t *dcp, int dcmode)
  90 {
  91         struct utsname ut;
  92 
  93         /*
  94          * Default device for dumps is 'swap' (appropriate swap device),
  95          * and default savecore directory is /var/crash/`uname -n`,
  96          * which is compatible with pre-dumpadm behavior.
  97          */
  98         (void) strcpy(dcp->dc_device, DC_STR_SWAP);
  99         (void) strcpy(dcp->dc_savdir, "/var/crash");
 100 
 101         if (uname(&ut) != -1) {
 102                 (void) strcat(dcp->dc_savdir, "/");
 103                 (void) strcat(dcp->dc_savdir, ut.nodename);
 104         }
 105 
 106         /*
 107          * Default is contents kernel, savecore enabled on reboot,
 108          * savecore saves compressed core files.
 109          */
 110         dcp->dc_cflags = DUMP_KERNEL;
 111         dcp->dc_enable = DC_ON;
 112         dcp->dc_csave = DC_COMPRESSED;
 113 
 114         dcp->dc_mode = dcmode;
 115         dcp->dc_conf_fp = NULL;
 116         dcp->dc_conf_fd = -1;
 117         dcp->dc_dump_fd = -1;
 118         dcp->dc_readonly = B_FALSE;
 119 }
 120 
 121 int
 122 dconf_open(dumpconf_t *dcp, const char *dpath, const char *fpath, int dcmode)
 123 {
 124         char buf[BUFSIZ];
 125         int line;
 126         const char *fpmode = "r+";
 127 
 128         dconf_init(dcp, dcmode);
 129 
 130         if ((dcp->dc_dump_fd = open(dpath, O_RDWR)) == -1) {
 131                 warn(gettext("failed to open %s"), dpath);
 132                 return (-1);
 133         }
 134 
 135         if ((dcp->dc_conf_fd = open(fpath, O_RDWR | O_CREAT, DC_PERM)) == -1) {
 136                 /*
 137                  * Attempt to open the file read-only.
 138                  */
 139                 if ((dcp->dc_conf_fd = open(fpath, O_RDONLY)) == -1) {
 140                         warn(gettext("failed to open %s"), fpath);
 141                         return (-1);
 142                 }
 143 
 144                 dcp->dc_readonly = B_TRUE;
 145                 fpmode = "r";
 146         }
 147 
 148         if ((dcp->dc_conf_fp = fdopen(dcp->dc_conf_fd, fpmode)) == NULL) {
 149                 warn(gettext("failed to open stream for %s"), fpath);
 150                 return (-1);
 151         }
 152 
 153         /*
 154          * If we're in override mode, the current kernel settings override the
 155          * default settings and anything invalid in the configuration file.
 156          */
 157         if (dcmode == DC_OVERRIDE)
 158                 (void) dconf_getdev(dcp);
 159 
 160         for (line = 1; fgets(buf, BUFSIZ, dcp->dc_conf_fp) != NULL; line++) {
 161 
 162                 char name[BUFSIZ], value[BUFSIZ];
 163                 const dc_token_t *tokp;
 164                 int len;
 165 
 166                 if (buf[0] == '#' || buf[0] == '\n')
 167                         continue;
 168 
 169                 /*
 170                  * Look for "name=value", with optional whitespace on either
 171                  * side, terminated by a newline, and consuming the whole line.
 172                  */
 173                 /* LINTED - unbounded string specifier */
 174                 if (sscanf(buf, " %[^=]=%s \n%n", name, value, &len) == 2 &&
 175                     name[0] != '\0' && value[0] != '\0' && len == strlen(buf)) {
 176                         /*
 177                          * Locate a matching token in the tokens[] table,
 178                          * and invoke its parsing function.
 179                          */
 180                         for (tokp = tokens; tokp->tok_name != NULL; tokp++) {
 181                                 if (strcmp(name, tokp->tok_name) == 0) {
 182                                         if (tokp->tok_parse(dcp, value) == -1) {
 183                                                 warn(gettext("\"%s\", line %d: "
 184                                                     "warning: invalid %s\n"),
 185                                                     fpath, line, name);
 186                                         }
 187                                         break;
 188                                 }
 189                         }
 190 
 191                         /*
 192                          * If we hit the end of the tokens[] table,
 193                          * no matching token was found.
 194                          */
 195                         if (tokp->tok_name == NULL) {
 196                                 warn(gettext("\"%s\", line %d: warning: "
 197                                     "invalid token: %s\n"), fpath, line, name);
 198                         }
 199 
 200                 } else {
 201                         warn(gettext("\"%s\", line %d: syntax error\n"),
 202                             fpath, line);
 203                 }
 204         }
 205 
 206         /*
 207          * If we're not in override mode, the current kernel settings
 208          * override the settings read from the configuration file.
 209          */
 210         if (dcmode == DC_CURRENT)
 211                 return (dconf_getdev(dcp));
 212 
 213         return (0);
 214 }
 215 
 216 int
 217 dconf_getdev(dumpconf_t *dcp)
 218 {
 219         int status = 0;
 220 
 221         if ((dcp->dc_cflags = ioctl(dcp->dc_dump_fd, DIOCGETCONF, 0)) == -1) {
 222                 warn(gettext("failed to get kernel dump settings"));
 223                 status = -1;
 224         }
 225 
 226         if (ioctl(dcp->dc_dump_fd, DIOCGETDEV, dcp->dc_device) == -1) {
 227                 if (errno != ENODEV) {
 228                         warn(gettext("failed to get dump device"));
 229                         status = -1;
 230                 } else
 231                         dcp->dc_device[0] = '\0';
 232         }
 233 
 234         return (status);
 235 }
 236 
 237 int
 238 dconf_close(dumpconf_t *dcp)
 239 {
 240         if (fclose(dcp->dc_conf_fp) == 0) {
 241                 (void) close(dcp->dc_dump_fd);
 242                 return (0);
 243         }
 244         return (-1);
 245 }
 246 
 247 int
 248 dconf_write(dumpconf_t *dcp)
 249 {
 250         const dc_token_t *tokp;
 251 
 252         if (fseeko(dcp->dc_conf_fp, (off_t)0, SEEK_SET) == -1) {
 253                 warn(gettext("failed to seek config file"));
 254                 return (-1);
 255         }
 256 
 257         if (ftruncate(dcp->dc_conf_fd, (off_t)0) == -1) {
 258                 warn(gettext("failed to truncate config file"));
 259                 return (-1);
 260         }
 261 
 262         (void) fputs("#\n# dumpadm.conf\n#\n"
 263             "# Configuration parameters for system crash dump.\n"
 264             "# Do NOT edit this file by hand -- use dumpadm(1m) instead.\n"
 265             "#\n", dcp->dc_conf_fp);
 266 
 267         for (tokp = tokens; tokp->tok_name != NULL; tokp++) {
 268                 if (fprintf(dcp->dc_conf_fp, "%s=", tokp->tok_name) == -1 ||
 269                     tokp->tok_print(dcp, dcp->dc_conf_fp) == -1) {
 270                         warn(gettext("failed to write token"));
 271                         return (-1);
 272                 }
 273         }
 274 
 275         if (fflush(dcp->dc_conf_fp) != 0)
 276                 warn(gettext("warning: failed to flush config file"));
 277 
 278         if (fsync(dcp->dc_conf_fd) == -1)
 279                 warn(gettext("warning: failed to sync config file to disk"));
 280 
 281         if (fchmod(dcp->dc_conf_fd, DC_PERM) == -1)
 282                 warn(gettext("warning: failed to reset mode on config file"));
 283 
 284         if (fchown(dcp->dc_conf_fd, DC_OWNER, DC_GROUP) == -1)
 285                 warn(gettext("warning: failed to reset owner on config file"));
 286 
 287         return (0);
 288 }
 289 
 290 static int
 291 open_stat64(const char *path, struct stat64 *stp)
 292 {
 293         int fd = open64(path, O_RDONLY);
 294 
 295         if (fd >= 0) {
 296                 int status = fstat64(fd, stp);
 297                 (void) close(fd);
 298                 return (status);
 299         }
 300 
 301         return (-1);
 302 }
 303 
 304 static int
 305 dconf_swap_compare(const swapent_t *s1, const swapent_t *s2)
 306 {
 307         struct stat64 st1, st2;
 308 
 309         int prefer_s1 = -1;     /* Return value to move s1 left (s1 < s2) */
 310         int prefer_s2 = 1;      /* Return value to move s2 left (s1 > s2) */
 311 
 312         /*
 313          * First try: open and fstat each swap entry.  If either system
 314          * call fails, arbitrarily prefer the other entry.
 315          */
 316         if (open_stat64(s1->ste_path, &st1) == -1)
 317                 return (prefer_s2);
 318 
 319         if (open_stat64(s2->ste_path, &st2) == -1)
 320                 return (prefer_s1);
 321 
 322         /*
 323          * Second try: if both entries are block devices, or if
 324          * neither is a block device, prefer the larger.
 325          */
 326         if (S_ISBLK(st1.st_mode) == S_ISBLK(st2.st_mode)) {
 327                 if (st2.st_size > st1.st_size)
 328                         return (prefer_s2);
 329                 return (prefer_s1);
 330         }
 331 
 332         /*
 333          * Third try: prefer the entry that is a block device.
 334          */
 335         if (S_ISBLK(st2.st_mode))
 336                 return (prefer_s2);
 337         return (prefer_s1);
 338 }
 339 
 340 static int
 341 dconf_dev_ioctl(dumpconf_t *dcp, int cmd)
 342 {
 343         if (ioctl(dcp->dc_dump_fd, cmd, dcp->dc_device) == 0)
 344                 return (0);
 345 
 346         switch (errno) {
 347         case ENOTSUP:
 348                 warn(gettext("dumps not supported on %s\n"), dcp->dc_device);
 349                 break;
 350         case EBUSY:
 351                 warn(gettext("device %s is already in use\n"), dcp->dc_device);
 352                 break;
 353         case EBADR:
 354                 /* ZFS pool is too fragmented to support a dump device */
 355                 warn(gettext("device %s is too fragmented to be used as "
 356                     "a dump device\n"), dcp->dc_device);
 357                 break;
 358         default:
 359                 /*
 360                  * NOTE: The stmsboot(1M) command's boot-up script parses this
 361                  * error to get the dump device name. If you change the format
 362                  * of this message, make sure that stmsboot(1M) is in sync.
 363                  */
 364                 warn(gettext("cannot use %s as dump device"), dcp->dc_device);
 365         }
 366         return (-1);
 367 }
 368 
 369 int
 370 dconf_update(dumpconf_t *dcp, int checkinuse)
 371 {
 372         int             oconf;
 373         int             error;
 374         char            *msg;
 375 
 376         error = 0;
 377 
 378         if (checkinuse && (dm_inuse(dcp->dc_device, &msg, DM_WHO_DUMP,
 379             &error) || error)) {
 380                 if (error != 0) {
 381                         warn(gettext("failed to determine if %s is"
 382                             " in use"), dcp->dc_device);
 383                 } else {
 384                         warn(msg);
 385                         free(msg);
 386                         return (-1);
 387                 }
 388         }
 389 
 390         /*
 391          * Save the existing dump configuration in case something goes wrong.
 392          */
 393         if ((oconf = ioctl(dcp->dc_dump_fd, DIOCGETCONF, 0)) == -1) {
 394                 warn(gettext("failed to get kernel dump configuration"));
 395                 return (-1);
 396         }
 397 
 398         oconf &= DUMP_CONTENT;
 399         dcp->dc_cflags &= DUMP_CONTENT;
 400 
 401         if (ioctl(dcp->dc_dump_fd, DIOCSETCONF, dcp->dc_cflags) == -1) {
 402                 warn(gettext("failed to update kernel dump configuration"));
 403                 return (-1);
 404         }
 405 
 406         if (strcmp(dcp->dc_device, DC_STR_SWAP) == 0) {
 407                 swaptbl_t *swt;
 408                 int i;
 409 
 410                 if ((swt = swap_list()) == NULL)
 411                         goto err;
 412 
 413                 if (swt->swt_n == 0) {
 414                         warn(gettext("no swap devices are available\n"));
 415                         free(swt);
 416                         goto err;
 417                 }
 418 
 419                 qsort(&swt->swt_ent[0], swt->swt_n, sizeof (swapent_t),
 420                     (int (*)(const void *, const void *))dconf_swap_compare);
 421 
 422                 /*
 423                  * Iterate through the prioritized list of swap entries,
 424                  * trying to configure one as the dump device.
 425                  */
 426                 for (i = 0; i < swt->swt_n; i++) {
 427                         if (ioctl(dcp->dc_dump_fd, DIOCSETDEV,
 428                             swt->swt_ent[i].ste_path) == 0) {
 429                                 (void) strcpy(dcp->dc_device,
 430                                     swt->swt_ent[i].ste_path);
 431                                 break;
 432                         }
 433                 }
 434 
 435                 if (i == swt->swt_n) {
 436                         warn(gettext("no swap devices could be configured "
 437                             "as the dump device\n"));
 438                         free(swt);
 439                         goto err;
 440                 }
 441                 free(swt);
 442 
 443         } else if (strcmp(dcp->dc_device, DC_STR_NONE) == 0) {
 444                 if (ioctl(dcp->dc_dump_fd, DIOCRMDEV, NULL) == -1) {
 445                         warn(gettext("failed to remove dump device"));
 446                         return (-1);
 447                 }
 448         } else if (dcp->dc_device[0] != '\0') {
 449                 /*
 450                  * If we're not in forcible update mode, then fail the change
 451                  * if the selected device cannot be used as the dump device,
 452                  * or if it is not big enough to hold the dump.
 453                  */
 454                 if (dcp->dc_mode == DC_CURRENT) {
 455                         struct stat64 st;
 456                         uint64_t d;
 457 
 458                         if (dconf_dev_ioctl(dcp, DIOCTRYDEV) == -1)
 459                                 goto err;
 460 
 461                         if (open_stat64(dcp->dc_device, &st) == -1) {
 462                                 warn(gettext("failed to access %s"),
 463                                     dcp->dc_device);
 464                                 goto err;
 465                         }
 466 
 467                         if ((error = zvol_check_dump_config(
 468                             dcp->dc_device)) > 0)
 469                                 goto err;
 470                         if (ioctl(dcp->dc_dump_fd, DIOCGETDUMPSIZE, &d) == -1) {
 471                                 warn(gettext("failed to get kernel dump size"));
 472                                 goto err;
 473                         }
 474 
 475                         if (st.st_size < d) {
 476                                 warn(gettext("dump device %s is too small to "
 477                                     "hold a system dump\ndump size %llu "
 478                                     "bytes, device size %lld bytes\n"),
 479                                     dcp->dc_device, d, st.st_size);
 480                                 goto err;
 481                         }
 482                 }
 483 
 484                 if (dconf_dev_ioctl(dcp, DIOCSETDEV) == -1)
 485                         goto err;
 486         }
 487 
 488         /*
 489          * Now that we've updated the dump device, we need to issue another
 490          * ioctl to re-read the config flags to determine whether we
 491          * obtained DUMP_EXCL access on our dump device.
 492          */
 493         if ((dcp->dc_cflags = ioctl(dcp->dc_dump_fd, DIOCGETCONF, 0)) == -1) {
 494                 warn(gettext("failed to re-read kernel dump configuration"));
 495                 return (-1);
 496         }
 497 
 498         return (0);
 499 
 500 err:
 501         (void) ioctl(dcp->dc_dump_fd, DIOCSETCONF, oconf);
 502         return (-1);
 503 }
 504 
 505 int
 506 dconf_write_uuid(dumpconf_t *dcp)
 507 {
 508         char uuidstr[36 + 1];
 509         uuid_t uu;
 510         int err;
 511 
 512         uuid_generate(uu);
 513         uuid_unparse(uu, uuidstr);
 514 
 515         err = ioctl(dcp->dc_dump_fd, DIOCSETUUID, uuidstr);
 516 
 517         if (err)
 518                 warn(gettext("kernel image uuid write failed"));
 519 
 520         return (err == 0);
 521 }
 522 
 523 int
 524 dconf_get_dumpsize(dumpconf_t *dcp)
 525 {
 526         char buf[32];
 527         uint64_t d;
 528 
 529         if (ioctl(dcp->dc_dump_fd, DIOCGETDUMPSIZE, &d) == -1) {
 530                 warn(gettext("failed to get kernel dump size"));
 531                 return (-1);
 532         }
 533 
 534         zfs_nicenum(d, buf, sizeof (buf));
 535 
 536         (void) printf(gettext("Estimated dump size: %s\n"), buf);
 537         return (0);
 538 }
 539 
 540 void
 541 dconf_print(dumpconf_t *dcp, FILE *fp)
 542 {
 543         u_longlong_t min;
 544         char *content;
 545 
 546         if (dcp->dc_cflags & DUMP_ALL)
 547                 content = gettext("all");
 548         else if (dcp->dc_cflags & DUMP_CURPROC)
 549                 content = gettext("kernel and current process");
 550         else
 551                 content = gettext("kernel");
 552 
 553         (void) fprintf(fp, gettext("      Dump content: %s pages\n"), content);
 554 
 555         if (dcp->dc_device[0] != '\0') {
 556                 (void) fprintf(fp, gettext("       Dump device: %s (%s)\n"),
 557                     dcp->dc_device, (dcp->dc_cflags & DUMP_EXCL) ?
 558                     gettext("dedicated") : gettext("swap"));
 559         } else {
 560                 (void) fprintf(fp, gettext("       Dump device: none "
 561                     "(dumps disabled)\n"));
 562         }
 563 
 564         (void) fprintf(fp, gettext("Savecore directory: %s"), dcp->dc_savdir);
 565 
 566         if (minfree_read(dcp->dc_savdir, &min) == 0) {
 567                 if (min < 1024 || (min % 1024) != 0)
 568                         (void) fprintf(fp, gettext(" (minfree = %lluKB)"), min);
 569                 else
 570                         (void) fprintf(fp, gettext(" (minfree = %lluMB)"),
 571                             min / 1024);
 572         }
 573 
 574         (void) fprintf(fp, gettext("\n"));
 575 
 576         (void) fprintf(fp, gettext("  Savecore enabled: %s\n"),
 577             (dcp->dc_enable == DC_OFF) ? gettext("no") : gettext("yes"));
 578         (void) fprintf(fp, gettext("   Save compressed: %s\n"),
 579             (dcp->dc_csave == DC_UNCOMPRESSED) ? gettext("off") :
 580             gettext("on"));
 581 }
 582 
 583 int
 584 dconf_str2device(dumpconf_t *dcp, char *buf)
 585 {
 586         if (strcasecmp(buf, DC_STR_SWAP) == 0) {
 587                 (void) strcpy(dcp->dc_device, DC_STR_SWAP);
 588                 return (0);
 589         }
 590 
 591         if (strcasecmp(buf, DC_STR_NONE) == 0) {
 592                 (void) strcpy(dcp->dc_device, DC_STR_NONE);
 593                 return (0);
 594         }
 595 
 596         if (valid_abspath(buf)) {
 597                 (void) strcpy(dcp->dc_device, buf);
 598                 return (0);
 599         }
 600 
 601         return (-1);
 602 }
 603 
 604 int
 605 dconf_str2savdir(dumpconf_t *dcp, char *buf)
 606 {
 607         if (valid_abspath(buf)) {
 608                 (void) strcpy(dcp->dc_savdir, buf);
 609                 return (0);
 610         }
 611 
 612         return (-1);
 613 }
 614 
 615 int
 616 dconf_str2content(dumpconf_t *dcp, char *buf)
 617 {
 618         if (strcasecmp(buf, DC_STR_KERNEL) == 0) {
 619                 dcp->dc_cflags = (dcp->dc_cflags & ~DUMP_CONTENT) | DUMP_KERNEL;
 620                 return (0);
 621         }
 622 
 623         if (strcasecmp(buf, DC_STR_CURPROC) == 0) {
 624                 dcp->dc_cflags = (dcp->dc_cflags & ~DUMP_CONTENT) |
 625                     DUMP_CURPROC;
 626                 return (0);
 627         }
 628 
 629         if (strcasecmp(buf, DC_STR_ALL) == 0) {
 630                 dcp->dc_cflags = (dcp->dc_cflags & ~DUMP_CONTENT) | DUMP_ALL;
 631                 return (0);
 632         }
 633 
 634         warn(gettext("invalid dump content type -- %s\n"), buf);
 635         return (-1);
 636 }
 637 
 638 int
 639 dconf_str2enable(dumpconf_t *dcp, char *buf)
 640 {
 641         if (strcasecmp(buf, DC_STR_YES) == 0) {
 642                 dcp->dc_enable = DC_ON;
 643                 return (0);
 644         }
 645 
 646         if (strcasecmp(buf, DC_STR_NO) == 0) {
 647                 dcp->dc_enable = DC_OFF;
 648                 return (0);
 649         }
 650 
 651         warn(gettext("invalid enable value -- %s\n"), buf);
 652         return (-1);
 653 }
 654 
 655 int
 656 dconf_str2csave(dumpconf_t *dcp, char *buf)
 657 {
 658         if (strcasecmp(buf, DC_STR_ON) == 0) {
 659                 dcp->dc_csave = DC_COMPRESSED;
 660                 return (0);
 661         }
 662 
 663         if (strcasecmp(buf, DC_STR_OFF) == 0) {
 664                 dcp->dc_csave = DC_UNCOMPRESSED;
 665                 return (0);
 666         }
 667 
 668         warn(gettext("invalid save compressed value -- %s\n"), buf);
 669         return (-1);
 670 }
 671 
 672 static int
 673 print_content(const dumpconf_t *dcp, FILE *fp)
 674 {
 675         const char *content;
 676 
 677         if (dcp->dc_cflags & DUMP_ALL)
 678                 content = DC_STR_ALL;
 679         else if (dcp->dc_cflags & DUMP_CURPROC)
 680                 content = DC_STR_CURPROC;
 681         else
 682                 content = DC_STR_KERNEL;
 683 
 684         return (fprintf(fp, "%s\n", content));
 685 }
 686 
 687 static int
 688 print_device(const dumpconf_t *dcp, FILE *fp)
 689 {
 690         return (fprintf(fp, "%s\n", (dcp->dc_device[0] != '\0') ?
 691             dcp->dc_device : DC_STR_SWAP));
 692 }
 693 
 694 static int
 695 print_enable(const dumpconf_t *dcp, FILE *fp)
 696 {
 697         return (fprintf(fp, "%s\n", (dcp->dc_enable == DC_OFF) ?
 698             DC_STR_NO : DC_STR_YES));
 699 }
 700 
 701 static int
 702 print_csave(const dumpconf_t *dcp, FILE *fp)
 703 {
 704         return (fprintf(fp, "%s\n", (dcp->dc_csave == DC_COMPRESSED) ?
 705             DC_STR_ON : DC_STR_OFF));
 706 }
 707 
 708 static int
 709 print_savdir(const dumpconf_t *dcp, FILE *fp)
 710 {
 711         return (fprintf(fp, "%s\n", dcp->dc_savdir));
 712 }