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 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * RCM module providing support for swap areas 28 * during reconfiguration operations. 29 */ 30 #include <stdlib.h> 31 #include <unistd.h> 32 #include <fcntl.h> 33 #include <thread.h> 34 #include <synch.h> 35 #include <strings.h> 36 #include <assert.h> 37 #include <errno.h> 38 #include <libintl.h> 39 #include <sys/types.h> 40 #include <sys/swap.h> 41 #include <sys/stat.h> 42 #include <sys/param.h> 43 #include <sys/dumpadm.h> 44 #include <sys/wait.h> 45 #include "rcm_module.h" 46 47 /* cache flags */ 48 #define SWAP_CACHE_NEW 0x01 49 #define SWAP_CACHE_STALE 0x02 50 #define SWAP_CACHE_OFFLINED 0x04 51 52 #define SWAP_CMD "/usr/sbin/swap" 53 #define SWAP_DELETE SWAP_CMD" -d %s %ld" 54 #define SWAP_ADD SWAP_CMD" -a %s %ld %ld" 55 56 /* LP64 hard code */ 57 #define MAXOFFSET_STRLEN 20 58 59 typedef struct swap_file { 60 char path[MAXPATHLEN]; 61 int cache_flags; 62 struct swap_area *areas; 63 struct swap_file *next; 64 struct swap_file *prev; 65 } swap_file_t; 66 67 /* swap file may have multiple swap areas */ 68 typedef struct swap_area { 69 off_t start; 70 off_t len; 71 int cache_flags; 72 struct swap_area *next; 73 struct swap_area *prev; 74 } swap_area_t; 75 76 static swap_file_t *cache; 77 static mutex_t cache_lock; 78 79 static int swap_register(rcm_handle_t *); 80 static int swap_unregister(rcm_handle_t *); 81 static int swap_getinfo(rcm_handle_t *, char *, id_t, uint_t, 82 char **, char **, nvlist_t *, rcm_info_t **); 83 static int swap_suspend(rcm_handle_t *, char *, id_t, timespec_t *, 84 uint_t, char **, rcm_info_t **); 85 static int swap_resume(rcm_handle_t *, char *, id_t, uint_t, 86 char **, rcm_info_t **); 87 static int swap_offline(rcm_handle_t *, char *, id_t, uint_t, 88 char **, rcm_info_t **); 89 static int swap_online(rcm_handle_t *, char *, id_t, uint_t, 90 char **, rcm_info_t **); 91 static int swap_remove(rcm_handle_t *, char *, id_t, uint_t, 92 char **, rcm_info_t **); 93 94 static int alloc_usage(char **); 95 static void cache_insert(swap_file_t *); 96 static swap_file_t *cache_lookup(char *); 97 static void cache_remove(swap_file_t *); 98 static void free_cache(void); 99 static int get_dumpdev(char []); 100 static void log_cmd_status(int); 101 static int swap_add(swap_file_t *, char **); 102 static void swap_area_add(swap_file_t *, swap_area_t *); 103 static swap_area_t *swap_area_alloc(swapent_t *); 104 static swap_area_t *swap_area_lookup(swap_file_t *, swapent_t *); 105 static void swap_area_remove(swap_file_t *, swap_area_t *); 106 static int swap_delete(swap_file_t *, char **); 107 static swap_file_t *swap_file_alloc(char *); 108 static void swap_file_free(swap_file_t *); 109 static swaptbl_t *sys_swaptbl(void); 110 static int update_cache(rcm_handle_t *); 111 112 static struct rcm_mod_ops swap_ops = 113 { 114 RCM_MOD_OPS_VERSION, 115 swap_register, 116 swap_unregister, 117 swap_getinfo, 118 swap_suspend, 119 swap_resume, 120 swap_offline, 121 swap_online, 122 swap_remove, 123 NULL, 124 NULL, 125 NULL 126 }; 127 128 struct rcm_mod_ops * 129 rcm_mod_init() 130 { 131 return (&swap_ops); 132 } 133 134 const char * 135 rcm_mod_info() 136 { 137 return ("RCM Swap module 1.5"); 138 } 139 140 int 141 rcm_mod_fini() 142 { 143 free_cache(); 144 (void) mutex_destroy(&cache_lock); 145 146 return (RCM_SUCCESS); 147 } 148 149 static int 150 swap_register(rcm_handle_t *hdl) 151 { 152 return (update_cache(hdl)); 153 } 154 155 static int 156 swap_unregister(rcm_handle_t *hdl) 157 { 158 swap_file_t *sf; 159 160 (void) mutex_lock(&cache_lock); 161 while ((sf = cache) != NULL) { 162 cache = cache->next; 163 (void) rcm_unregister_interest(hdl, sf->path, 0); 164 swap_file_free(sf); 165 } 166 (void) mutex_unlock(&cache_lock); 167 168 return (RCM_SUCCESS); 169 } 170 171 /*ARGSUSED*/ 172 static int 173 swap_getinfo(rcm_handle_t *hdl, char *rsrcname, id_t id, uint_t flags, 174 char **infostr, char **errstr, nvlist_t *props, rcm_info_t **dependent) 175 { 176 assert(rsrcname != NULL && infostr != NULL); 177 178 (void) mutex_lock(&cache_lock); 179 if (cache_lookup(rsrcname) == NULL) { 180 rcm_log_message(RCM_ERROR, "unknown resource: %s\n", 181 rsrcname); 182 (void) mutex_unlock(&cache_lock); 183 return (RCM_FAILURE); 184 } 185 (void) mutex_unlock(&cache_lock); 186 (void) alloc_usage(infostr); 187 188 return (RCM_SUCCESS); 189 } 190 191 /* 192 * Remove swap space to maintain availability of anonymous pages 193 * during device suspension. Swap will be reconfigured upon resume. 194 * Fail if operation will unconfigure dump device. 195 */ 196 /*ARGSUSED*/ 197 static int 198 swap_suspend(rcm_handle_t *hdl, char *rsrcname, id_t id, timespec_t *interval, 199 uint_t flags, char **errstr, rcm_info_t **dependent) 200 { 201 swap_file_t *sf; 202 int rv; 203 204 assert(rsrcname != NULL && errstr != NULL); 205 206 if (flags & RCM_QUERY) 207 return (RCM_SUCCESS); 208 209 (void) mutex_lock(&cache_lock); 210 if ((sf = cache_lookup(rsrcname)) == NULL) { 211 (void) mutex_unlock(&cache_lock); 212 return (RCM_SUCCESS); 213 } 214 215 rv = swap_delete(sf, errstr); 216 (void) mutex_unlock(&cache_lock); 217 218 return (rv); 219 } 220 221 /*ARGSUSED*/ 222 static int 223 swap_resume(rcm_handle_t *hdl, char *rsrcname, id_t id, uint_t flags, 224 char **errstr, rcm_info_t **dependent) 225 { 226 swap_file_t *sf; 227 int rv; 228 229 assert(rsrcname != NULL && errstr != NULL); 230 231 (void) mutex_lock(&cache_lock); 232 if ((sf = cache_lookup(rsrcname)) == NULL) { 233 (void) mutex_unlock(&cache_lock); 234 return (RCM_SUCCESS); 235 } 236 237 rv = swap_add(sf, errstr); 238 (void) mutex_unlock(&cache_lock); 239 240 return (rv); 241 } 242 243 /* 244 * By default, reject offline request. If forced, attempt to 245 * delete swap. Fail if operation will unconfigure dump device. 246 */ 247 /*ARGSUSED*/ 248 static int 249 swap_offline(rcm_handle_t *hdl, char *rsrcname, id_t id, uint_t flags, 250 char **errstr, rcm_info_t **dependent) 251 { 252 swap_file_t *sf; 253 int rv; 254 255 assert(rsrcname != NULL && errstr != NULL); 256 257 if ((flags & RCM_FORCE) && (flags & RCM_QUERY)) 258 return (RCM_SUCCESS); 259 260 (void) mutex_lock(&cache_lock); 261 if ((sf = cache_lookup(rsrcname)) == NULL) { 262 (void) mutex_unlock(&cache_lock); 263 return (RCM_SUCCESS); 264 } 265 266 if (flags & RCM_FORCE) { 267 rv = swap_delete(sf, errstr); 268 (void) mutex_unlock(&cache_lock); 269 return (rv); 270 } 271 /* default reject */ 272 (void) mutex_unlock(&cache_lock); 273 (void) alloc_usage(errstr); 274 275 return (RCM_FAILURE); 276 } 277 278 /*ARGSUSED*/ 279 static int 280 swap_online(rcm_handle_t *hdl, char *rsrcname, id_t id, uint_t flags, 281 char **errstr, rcm_info_t **dependent) 282 { 283 swap_file_t *sf; 284 int rv; 285 286 assert(rsrcname != NULL && errstr != NULL); 287 288 (void) mutex_lock(&cache_lock); 289 if ((sf = cache_lookup(rsrcname)) == NULL) { 290 (void) mutex_unlock(&cache_lock); 291 return (RCM_SUCCESS); 292 } 293 294 rv = swap_add(sf, errstr); 295 (void) mutex_unlock(&cache_lock); 296 297 return (rv); 298 } 299 300 /*ARGSUSED*/ 301 static int 302 swap_remove(rcm_handle_t *hdl, char *rsrcname, id_t id, uint_t flags, 303 char **errstr, rcm_info_t **dependent) 304 { 305 swap_file_t *sf; 306 307 assert(rsrcname != NULL); 308 309 (void) mutex_lock(&cache_lock); 310 if ((sf = cache_lookup(rsrcname)) == NULL) { 311 (void) mutex_unlock(&cache_lock); 312 return (RCM_SUCCESS); 313 } 314 /* RCM framework handles unregistration */ 315 cache_remove(sf); 316 swap_file_free(sf); 317 (void) mutex_unlock(&cache_lock); 318 319 return (RCM_SUCCESS); 320 } 321 322 /* 323 * Delete all swap areas for swap file. 324 * Invoke swap(8) instead of swapctl(2) to 325 * handle relocation of dump device. 326 * If dump device is configured, fail if 327 * unable to relocate dump. 328 * 329 * Call with cache_lock held. 330 */ 331 static int 332 swap_delete(swap_file_t *sf, char **errstr) 333 { 334 swap_area_t *sa; 335 char cmd[sizeof (SWAP_DELETE) + MAXPATHLEN + 336 MAXOFFSET_STRLEN]; 337 char dumpdev[MAXPATHLEN]; 338 int have_dump = 1; 339 int stat; 340 int rv = RCM_SUCCESS; 341 342 if (get_dumpdev(dumpdev) == 0 && dumpdev[0] == '\0') 343 have_dump = 0; 344 345 for (sa = sf->areas; sa != NULL; sa = sa->next) { 346 /* swap(8) is not idempotent */ 347 if (sa->cache_flags & SWAP_CACHE_OFFLINED) { 348 continue; 349 } 350 351 (void) snprintf(cmd, sizeof (cmd), SWAP_DELETE, sf->path, 352 sa->start); 353 rcm_log_message(RCM_TRACE1, "%s\n", cmd); 354 if ((stat = rcm_exec_cmd(cmd)) != 0) { 355 log_cmd_status(stat); 356 *errstr = strdup(gettext("unable to delete swap")); 357 rv = RCM_FAILURE; 358 goto out; 359 } 360 sa->cache_flags |= SWAP_CACHE_OFFLINED; 361 362 /* 363 * Fail on removal of dump device. 364 */ 365 if (have_dump == 0) 366 continue; 367 368 if (get_dumpdev(dumpdev) != 0) { 369 rcm_log_message(RCM_WARNING, "unable to " 370 "check for removal of dump device\n"); 371 } else if (dumpdev[0] == '\0') { 372 rcm_log_message(RCM_DEBUG, "removed dump: " 373 "attempting recovery\n"); 374 375 /* 376 * Restore dump 377 */ 378 (void) snprintf(cmd, sizeof (cmd), SWAP_ADD, 379 sf->path, sa->start, sa->len); 380 rcm_log_message(RCM_TRACE1, "%s\n", cmd); 381 if ((stat = rcm_exec_cmd(cmd)) != 0) { 382 log_cmd_status(stat); 383 rcm_log_message(RCM_ERROR, 384 "failed to restore dump\n"); 385 } else { 386 sa->cache_flags &= ~SWAP_CACHE_OFFLINED; 387 rcm_log_message(RCM_DEBUG, "dump restored\n"); 388 } 389 *errstr = strdup(gettext("unable to relocate dump")); 390 rv = RCM_FAILURE; 391 goto out; 392 } 393 } 394 sf->cache_flags |= SWAP_CACHE_OFFLINED; 395 out: 396 return (rv); 397 } 398 399 /* 400 * Invoke swap(8) to add each registered swap area. 401 * 402 * Call with cache_lock held. 403 */ 404 static int 405 swap_add(swap_file_t *sf, char **errstr) 406 { 407 swap_area_t *sa; 408 char cmd[sizeof (SWAP_ADD) + MAXPATHLEN + 409 (2 * MAXOFFSET_STRLEN)]; 410 int stat; 411 int rv = RCM_SUCCESS; 412 413 for (sa = sf->areas; sa != NULL; sa = sa->next) { 414 /* swap(8) is not idempotent */ 415 if (!(sa->cache_flags & SWAP_CACHE_OFFLINED)) { 416 continue; 417 } 418 419 (void) snprintf(cmd, sizeof (cmd), 420 SWAP_ADD, sf->path, sa->start, sa->len); 421 rcm_log_message(RCM_TRACE1, "%s\n", cmd); 422 if ((stat = rcm_exec_cmd(cmd)) != 0) { 423 log_cmd_status(stat); 424 *errstr = strdup(gettext("unable to add swap")); 425 rv = RCM_FAILURE; 426 break; 427 } else { 428 sa->cache_flags &= ~SWAP_CACHE_OFFLINED; 429 sf->cache_flags &= ~SWAP_CACHE_OFFLINED; 430 } 431 } 432 433 return (rv); 434 } 435 436 static int 437 update_cache(rcm_handle_t *hdl) 438 { 439 swaptbl_t *swt; 440 swap_file_t *sf, *stale_sf; 441 swap_area_t *sa, *stale_sa; 442 int i; 443 int rv = RCM_SUCCESS; 444 445 if ((swt = sys_swaptbl()) == NULL) { 446 rcm_log_message(RCM_ERROR, "failed to read " 447 "current swap configuration\n"); 448 return (RCM_FAILURE); 449 } 450 451 (void) mutex_lock(&cache_lock); 452 453 /* 454 * cache pass 1 - mark everyone stale 455 */ 456 for (sf = cache; sf != NULL; sf = sf->next) { 457 sf->cache_flags |= SWAP_CACHE_STALE; 458 for (sa = sf->areas; sa != NULL; sa = sa->next) { 459 sa->cache_flags |= SWAP_CACHE_STALE; 460 } 461 } 462 463 /* 464 * add new entries 465 */ 466 for (i = 0; i < swt->swt_n; i++) { 467 if (swt->swt_ent[i].ste_flags & (ST_INDEL|ST_DOINGDEL)) { 468 continue; 469 } 470 471 /* 472 * assure swap_file_t 473 */ 474 if ((sf = cache_lookup(swt->swt_ent[i].ste_path)) == NULL) { 475 if ((sf = swap_file_alloc(swt->swt_ent[i].ste_path)) == 476 NULL) { 477 free(swt); 478 return (RCM_FAILURE); 479 } 480 sf->cache_flags |= SWAP_CACHE_NEW; 481 cache_insert(sf); 482 } else { 483 sf->cache_flags &= ~SWAP_CACHE_STALE; 484 } 485 486 /* 487 * assure swap_area_t 488 */ 489 if ((sa = swap_area_lookup(sf, &swt->swt_ent[i])) == NULL) { 490 if ((sa = swap_area_alloc(&swt->swt_ent[i])) == NULL) { 491 free(swt); 492 return (RCM_FAILURE); 493 } 494 swap_area_add(sf, sa); 495 } else { 496 sa->cache_flags &= ~SWAP_CACHE_STALE; 497 } 498 } 499 500 free(swt); 501 502 /* 503 * cache pass 2 504 * 505 * swap_file_t - skip offlined, register new, unregister/remove stale 506 * swap_area_t - skip offlined, remove stale 507 */ 508 sf = cache; 509 while (sf != NULL) { 510 sa = sf->areas; 511 while (sa != NULL) { 512 if (sa->cache_flags & SWAP_CACHE_OFFLINED) { 513 sa->cache_flags &= ~SWAP_CACHE_STALE; 514 sa = sa->next; 515 continue; 516 } 517 if (sa->cache_flags & SWAP_CACHE_STALE) { 518 stale_sa = sa; 519 sa = sa->next; 520 swap_area_remove(sf, stale_sa); 521 free(stale_sa); 522 continue; 523 } 524 sa = sa->next; 525 } 526 527 if (sf->cache_flags & SWAP_CACHE_OFFLINED) { 528 sf->cache_flags &= ~SWAP_CACHE_STALE; 529 sf = sf->next; 530 continue; 531 } 532 533 if (sf->cache_flags & SWAP_CACHE_STALE) { 534 if (rcm_unregister_interest(hdl, sf->path, 0) != 535 RCM_SUCCESS) { 536 rcm_log_message(RCM_ERROR, "failed to register " 537 "%s\n", sf->path); 538 } 539 stale_sf = sf; 540 sf = sf->next; 541 cache_remove(stale_sf); 542 swap_file_free(stale_sf); 543 continue; 544 } 545 546 if (!(sf->cache_flags & SWAP_CACHE_NEW)) { 547 sf = sf->next; 548 continue; 549 } 550 551 if (rcm_register_interest(hdl, sf->path, 0, NULL) != 552 RCM_SUCCESS) { 553 rcm_log_message(RCM_ERROR, "failed to register %s\n", 554 sf->path); 555 rv = RCM_FAILURE; 556 } else { 557 rcm_log_message(RCM_DEBUG, "registered %s\n", 558 sf->path); 559 sf->cache_flags &= ~SWAP_CACHE_NEW; 560 } 561 sf = sf->next; 562 } 563 (void) mutex_unlock(&cache_lock); 564 565 return (rv); 566 } 567 568 /* 569 * Returns system swap table. 570 */ 571 static swaptbl_t * 572 sys_swaptbl() 573 { 574 swaptbl_t *swt; 575 char *cp; 576 int i, n; 577 size_t tbl_size; 578 579 if ((n = swapctl(SC_GETNSWP, NULL)) == -1) 580 return (NULL); 581 582 tbl_size = sizeof (int) + n * sizeof (swapent_t) + n * MAXPATHLEN; 583 if ((swt = (swaptbl_t *)malloc(tbl_size)) == NULL) 584 return (NULL); 585 586 swt->swt_n = n; 587 cp = (char *)swt + (sizeof (int) + n * sizeof (swapent_t)); 588 for (i = 0; i < n; i++) { 589 swt->swt_ent[i].ste_path = cp; 590 cp += MAXPATHLEN; 591 } 592 593 if ((n = swapctl(SC_LIST, swt)) == -1) { 594 free(swt); 595 return (NULL); 596 } 597 598 if (n != swt->swt_n) { 599 /* mismatch, try again */ 600 free(swt); 601 return (sys_swaptbl()); 602 } 603 604 return (swt); 605 } 606 607 static int 608 get_dumpdev(char dumpdev[]) 609 { 610 int fd; 611 int rv = 0; 612 char *err; 613 614 if ((fd = open("/dev/dump", O_RDONLY)) == -1) { 615 rcm_log_message(RCM_ERROR, "failed to open /dev/dump\n"); 616 return (-1); 617 } 618 619 if (ioctl(fd, DIOCGETDEV, dumpdev) == -1) { 620 if (errno == ENODEV) { 621 dumpdev[0] = '\0'; 622 } else { 623 rcm_log_message(RCM_ERROR, "ioctl: %s\n", 624 ((err = strerror(errno)) == NULL) ? "" : err); 625 rv = -1; 626 } 627 } 628 (void) close(fd); 629 630 return (rv); 631 } 632 633 static void 634 free_cache(void) 635 { 636 swap_file_t *sf; 637 638 (void) mutex_lock(&cache_lock); 639 while ((sf = cache) != NULL) { 640 cache = cache->next; 641 swap_file_free(sf); 642 } 643 (void) mutex_unlock(&cache_lock); 644 645 } 646 647 /* 648 * Call with cache_lock held. 649 */ 650 static void 651 swap_file_free(swap_file_t *sf) 652 { 653 swap_area_t *sa; 654 655 assert(sf != NULL); 656 657 while ((sa = sf->areas) != NULL) { 658 sf->areas = sf->areas->next; 659 free(sa); 660 } 661 free(sf); 662 } 663 664 /* 665 * Call with cache_lock held. 666 */ 667 static void 668 cache_insert(swap_file_t *ent) 669 { 670 ent->next = cache; 671 if (ent->next) 672 ent->next->prev = ent; 673 ent->prev = NULL; 674 cache = ent; 675 } 676 677 /* 678 * Call with cache_lock held. 679 */ 680 static swap_file_t * 681 cache_lookup(char *rsrc) 682 { 683 swap_file_t *sf; 684 685 for (sf = cache; sf != NULL; sf = sf->next) { 686 if (strcmp(rsrc, sf->path) == 0) { 687 return (sf); 688 } 689 } 690 return (NULL); 691 } 692 693 /* 694 * Call with cache_lock held. 695 */ 696 static void 697 cache_remove(swap_file_t *ent) 698 { 699 assert(ent != NULL); 700 701 if (ent->next != NULL) { 702 ent->next->prev = ent->prev; 703 } 704 if (ent->prev != NULL) { 705 ent->prev->next = ent->next; 706 } else { 707 cache = ent->next; 708 } 709 ent->next = NULL; 710 ent->prev = NULL; 711 } 712 713 /* 714 * Call with cache_lock held. 715 */ 716 static void 717 swap_area_add(swap_file_t *sf, swap_area_t *sa) 718 { 719 sa->next = sf->areas; 720 if (sa->next) 721 sa->next->prev = sa; 722 sa->prev = NULL; 723 sf->areas = sa; 724 } 725 726 /* 727 * Call with cache_lock held. 728 */ 729 static void 730 swap_area_remove(swap_file_t *sf, swap_area_t *ent) 731 { 732 assert(sf != NULL && ent != NULL); 733 734 if (ent->next != NULL) { 735 ent->next->prev = ent->prev; 736 } 737 if (ent->prev != NULL) { 738 ent->prev->next = ent->next; 739 } else { 740 sf->areas = ent->next; 741 } 742 ent->next = NULL; 743 ent->prev = NULL; 744 } 745 746 static swap_file_t * 747 swap_file_alloc(char *rsrc) 748 { 749 swap_file_t *sf; 750 751 if ((sf = calloc(1, sizeof (*sf))) == NULL) { 752 rcm_log_message(RCM_ERROR, "calloc failure\n"); 753 return (NULL); 754 } 755 (void) strlcpy(sf->path, rsrc, sizeof (sf->path)); 756 757 return (sf); 758 } 759 760 static swap_area_t * 761 swap_area_alloc(swapent_t *swt_ent) 762 { 763 swap_area_t *sa; 764 765 if ((sa = calloc(1, sizeof (*sa))) == NULL) { 766 rcm_log_message(RCM_ERROR, "calloc failure\n"); 767 return (NULL); 768 } 769 sa->start = swt_ent->ste_start; 770 sa->len = swt_ent->ste_length; 771 772 return (sa); 773 } 774 775 /* 776 * Call with cache_lock held. 777 */ 778 static swap_area_t * 779 swap_area_lookup(swap_file_t *sf, swapent_t *swt_ent) 780 { 781 swap_area_t *sa; 782 783 assert(sf != NULL && swt_ent != NULL); 784 assert(strcmp(sf->path, swt_ent->ste_path) == 0); 785 786 for (sa = sf->areas; sa != NULL; sa = sa->next) { 787 if (sa->start == swt_ent->ste_start && 788 sa->len == swt_ent->ste_length) { 789 return (sa); 790 } 791 } 792 return (NULL); 793 } 794 795 /* 796 * All-purpose usage string. 797 */ 798 static int 799 alloc_usage(char **cpp) 800 { 801 if ((*cpp = strdup(gettext("swap area"))) == NULL) { 802 rcm_log_message(RCM_ERROR, "strdup failure\n"); 803 return (-1); 804 } 805 return (0); 806 } 807 808 static void 809 log_cmd_status(int stat) 810 { 811 char *err; 812 813 if (stat == -1) { 814 rcm_log_message(RCM_ERROR, "wait: %s\n", 815 ((err = strerror(errno)) == NULL) ? "" : err); 816 } else if (WIFEXITED(stat)) { 817 rcm_log_message(RCM_ERROR, "exit status: %d\n", 818 WEXITSTATUS(stat)); 819 } else { 820 rcm_log_message(RCM_ERROR, "wait status: %d\n", stat); 821 } 822 } 823