1*2f172c55SRobert Thurlow /* 2*2f172c55SRobert Thurlow * CDDL HEADER START 3*2f172c55SRobert Thurlow * 4*2f172c55SRobert Thurlow * The contents of this file are subject to the terms of the 5*2f172c55SRobert Thurlow * Common Development and Distribution License (the "License"). 6*2f172c55SRobert Thurlow * You may not use this file except in compliance with the License. 7*2f172c55SRobert Thurlow * 8*2f172c55SRobert Thurlow * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*2f172c55SRobert Thurlow * or http://www.opensolaris.org/os/licensing. 10*2f172c55SRobert Thurlow * See the License for the specific language governing permissions 11*2f172c55SRobert Thurlow * and limitations under the License. 12*2f172c55SRobert Thurlow * 13*2f172c55SRobert Thurlow * When distributing Covered Code, include this CDDL HEADER in each 14*2f172c55SRobert Thurlow * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*2f172c55SRobert Thurlow * If applicable, add the following below this CDDL HEADER, with the 16*2f172c55SRobert Thurlow * fields enclosed by brackets "[]" replaced with your own identifying 17*2f172c55SRobert Thurlow * information: Portions Copyright [yyyy] [name of copyright owner] 18*2f172c55SRobert Thurlow * 19*2f172c55SRobert Thurlow * CDDL HEADER END 20*2f172c55SRobert Thurlow */ 21*2f172c55SRobert Thurlow 22*2f172c55SRobert Thurlow /* 23*2f172c55SRobert Thurlow * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24*2f172c55SRobert Thurlow * Use is subject to license terms. 25*2f172c55SRobert Thurlow */ 26*2f172c55SRobert Thurlow 27*2f172c55SRobert Thurlow #include <stdio.h> 28*2f172c55SRobert Thurlow #include <unistd.h> 29*2f172c55SRobert Thurlow #include <strings.h> 30*2f172c55SRobert Thurlow #include <string.h> 31*2f172c55SRobert Thurlow #include <limits.h> 32*2f172c55SRobert Thurlow #include <libnvpair.h> 33*2f172c55SRobert Thurlow #include <locale.h> 34*2f172c55SRobert Thurlow #include <sys/stat.h> 35*2f172c55SRobert Thurlow #include <sys/fs_reparse.h> 36*2f172c55SRobert Thurlow #include <rp_plugin.h> 37*2f172c55SRobert Thurlow #include <uuid/uuid.h> 38*2f172c55SRobert Thurlow #include <sys/types.h> 39*2f172c55SRobert Thurlow #include <sys/stat.h> 40*2f172c55SRobert Thurlow #include <fcntl.h> 41*2f172c55SRobert Thurlow #include <priv.h> 42*2f172c55SRobert Thurlow #include <nfs/nfs4.h> 43*2f172c55SRobert Thurlow #include <rpcsvc/nfs4_prot.h> 44*2f172c55SRobert Thurlow #include "ref_subr.h" 45*2f172c55SRobert Thurlow 46*2f172c55SRobert Thurlow #ifndef TEXT_DOMAIN 47*2f172c55SRobert Thurlow #define TEXT_DOMAIN "SUNW_OST_OSCMD" 48*2f172c55SRobert Thurlow #endif /* TEXT_DOMAIN */ 49*2f172c55SRobert Thurlow 50*2f172c55SRobert Thurlow extern int errno; 51*2f172c55SRobert Thurlow 52*2f172c55SRobert Thurlow void 53*2f172c55SRobert Thurlow usage() 54*2f172c55SRobert Thurlow { 55*2f172c55SRobert Thurlow fprintf(stderr, gettext("Usage:\n")); 56*2f172c55SRobert Thurlow fprintf(stderr, 57*2f172c55SRobert Thurlow gettext("\tnfsref [-t type] add path location [location ...]\n")); 58*2f172c55SRobert Thurlow fprintf(stderr, gettext("\tnfsref [-t type] remove path\n")); 59*2f172c55SRobert Thurlow fprintf(stderr, gettext("\tnfsref [-t type] lookup path\n")); 60*2f172c55SRobert Thurlow } 61*2f172c55SRobert Thurlow 62*2f172c55SRobert Thurlow /* 63*2f172c55SRobert Thurlow * Copy a string from source to destination, escaping space 64*2f172c55SRobert Thurlow * with a backslash and escaping the escape character as well. 65*2f172c55SRobert Thurlow */ 66*2f172c55SRobert Thurlow int 67*2f172c55SRobert Thurlow add_escape(char *src, char *dest, int limit) 68*2f172c55SRobert Thurlow { 69*2f172c55SRobert Thurlow char *sp, *dp; 70*2f172c55SRobert Thurlow int destlen = 0; 71*2f172c55SRobert Thurlow 72*2f172c55SRobert Thurlow sp = src; 73*2f172c55SRobert Thurlow dp = dest; 74*2f172c55SRobert Thurlow 75*2f172c55SRobert Thurlow while (*sp && destlen < limit) { 76*2f172c55SRobert Thurlow if (*sp == '\\') { 77*2f172c55SRobert Thurlow *dp++ = '\\'; 78*2f172c55SRobert Thurlow *dp++ = '\\'; 79*2f172c55SRobert Thurlow destlen++; 80*2f172c55SRobert Thurlow } else if (*sp == ' ') { 81*2f172c55SRobert Thurlow *dp++ = '\\'; 82*2f172c55SRobert Thurlow *dp++ = ' '; 83*2f172c55SRobert Thurlow destlen++; 84*2f172c55SRobert Thurlow } else 85*2f172c55SRobert Thurlow *dp++ = *sp; 86*2f172c55SRobert Thurlow destlen++; 87*2f172c55SRobert Thurlow sp++; 88*2f172c55SRobert Thurlow } 89*2f172c55SRobert Thurlow if (limit <= 0) 90*2f172c55SRobert Thurlow return (-1); 91*2f172c55SRobert Thurlow return (destlen); 92*2f172c55SRobert Thurlow } 93*2f172c55SRobert Thurlow 94*2f172c55SRobert Thurlow int 95*2f172c55SRobert Thurlow addref(char *sl_path, char *svc_type, int optind, int argc, char *argv[]) 96*2f172c55SRobert Thurlow { 97*2f172c55SRobert Thurlow int err, fd, i, len, oldlen, notfound = 0; 98*2f172c55SRobert Thurlow char *text, *location; 99*2f172c55SRobert Thurlow nvlist_t *nvl = NULL; 100*2f172c55SRobert Thurlow char buf[SYMLINK_MAX]; 101*2f172c55SRobert Thurlow struct stat sbuf; 102*2f172c55SRobert Thurlow 103*2f172c55SRobert Thurlow /* Get an nvlist */ 104*2f172c55SRobert Thurlow nvl = reparse_init(); 105*2f172c55SRobert Thurlow if (nvl == NULL) 106*2f172c55SRobert Thurlow return (ENOMEM); 107*2f172c55SRobert Thurlow 108*2f172c55SRobert Thurlow /* Get the reparse point data, if the RP exists */ 109*2f172c55SRobert Thurlow err = readlink(sl_path, buf, SYMLINK_MAX); 110*2f172c55SRobert Thurlow if (err == -1) { 111*2f172c55SRobert Thurlow if (errno == ENOENT) { 112*2f172c55SRobert Thurlow notfound = 1; 113*2f172c55SRobert Thurlow err = 0; 114*2f172c55SRobert Thurlow } else { 115*2f172c55SRobert Thurlow reparse_free(nvl); 116*2f172c55SRobert Thurlow return (errno); 117*2f172c55SRobert Thurlow } 118*2f172c55SRobert Thurlow } else { 119*2f172c55SRobert Thurlow buf[err] = '\0'; 120*2f172c55SRobert Thurlow } 121*2f172c55SRobert Thurlow 122*2f172c55SRobert Thurlow /* Get any data into nvlist */ 123*2f172c55SRobert Thurlow if (notfound == 0) 124*2f172c55SRobert Thurlow err = reparse_parse(buf, nvl); 125*2f172c55SRobert Thurlow if (err != 0) { 126*2f172c55SRobert Thurlow reparse_free(nvl); 127*2f172c55SRobert Thurlow return (err); 128*2f172c55SRobert Thurlow } 129*2f172c55SRobert Thurlow 130*2f172c55SRobert Thurlow /* 131*2f172c55SRobert Thurlow * Accumulate multiple locations on the command line into 'buf' 132*2f172c55SRobert Thurlow */ 133*2f172c55SRobert Thurlow oldlen = len = 0; 134*2f172c55SRobert Thurlow location = NULL; 135*2f172c55SRobert Thurlow for (i = optind; i < argc; i++) { 136*2f172c55SRobert Thurlow bzero(buf, sizeof (buf)); 137*2f172c55SRobert Thurlow len += add_escape(argv[i], buf, SYMLINK_MAX) + 2; 138*2f172c55SRobert Thurlow location = realloc(location, len); 139*2f172c55SRobert Thurlow location[oldlen] = '\0'; 140*2f172c55SRobert Thurlow oldlen = len; 141*2f172c55SRobert Thurlow strlcat(location, buf, len); 142*2f172c55SRobert Thurlow strlcat(location, " ", len); 143*2f172c55SRobert Thurlow } 144*2f172c55SRobert Thurlow location[len - 2] = '\0'; 145*2f172c55SRobert Thurlow 146*2f172c55SRobert Thurlow /* Add to the list */ 147*2f172c55SRobert Thurlow err = reparse_add(nvl, svc_type, location); 148*2f172c55SRobert Thurlow if (err) { 149*2f172c55SRobert Thurlow reparse_free(nvl); 150*2f172c55SRobert Thurlow return (err); 151*2f172c55SRobert Thurlow } 152*2f172c55SRobert Thurlow 153*2f172c55SRobert Thurlow /* Get the new or modified symlink contents */ 154*2f172c55SRobert Thurlow err = reparse_unparse(nvl, &text); 155*2f172c55SRobert Thurlow reparse_free(nvl); 156*2f172c55SRobert Thurlow if (err) 157*2f172c55SRobert Thurlow return (err); 158*2f172c55SRobert Thurlow 159*2f172c55SRobert Thurlow /* Delete first if found */ 160*2f172c55SRobert Thurlow if (notfound == 0) { 161*2f172c55SRobert Thurlow err = reparse_delete(sl_path); 162*2f172c55SRobert Thurlow if (err) { 163*2f172c55SRobert Thurlow free(text); 164*2f172c55SRobert Thurlow return (err); 165*2f172c55SRobert Thurlow } 166*2f172c55SRobert Thurlow } 167*2f172c55SRobert Thurlow 168*2f172c55SRobert Thurlow /* Finally, write out the reparse point */ 169*2f172c55SRobert Thurlow err = reparse_create(sl_path, text); 170*2f172c55SRobert Thurlow free(text); 171*2f172c55SRobert Thurlow if (err) 172*2f172c55SRobert Thurlow return (err); 173*2f172c55SRobert Thurlow 174*2f172c55SRobert Thurlow err = lstat(sl_path, &sbuf); 175*2f172c55SRobert Thurlow if (err == 0 && strcasecmp(sbuf.st_fstype, "ZFS") != 0) 176*2f172c55SRobert Thurlow printf(gettext( 177*2f172c55SRobert Thurlow "Warning: referrals do not work on this filesystem\n")); 178*2f172c55SRobert Thurlow 179*2f172c55SRobert Thurlow if (notfound) 180*2f172c55SRobert Thurlow printf(gettext("Created reparse point %s\n"), sl_path); 181*2f172c55SRobert Thurlow else 182*2f172c55SRobert Thurlow printf(gettext("Added to reparse point %s\n"), sl_path); 183*2f172c55SRobert Thurlow 184*2f172c55SRobert Thurlow return (0); 185*2f172c55SRobert Thurlow } 186*2f172c55SRobert Thurlow 187*2f172c55SRobert Thurlow int 188*2f172c55SRobert Thurlow delref(char *sl_path, char *svc_type) 189*2f172c55SRobert Thurlow { 190*2f172c55SRobert Thurlow char *cp; 191*2f172c55SRobert Thurlow char *svc_data; 192*2f172c55SRobert Thurlow int err; 193*2f172c55SRobert Thurlow nvlist_t *nvl; 194*2f172c55SRobert Thurlow nvpair_t *curr; 195*2f172c55SRobert Thurlow char buf[SYMLINK_MAX]; 196*2f172c55SRobert Thurlow int fd, fd2; 197*2f172c55SRobert Thurlow FILE *fp, *fp2; 198*2f172c55SRobert Thurlow char uuid[UUID_PRINTABLE_STRING_LENGTH], path[256], loc[2048]; 199*2f172c55SRobert Thurlow 200*2f172c55SRobert Thurlow /* Get an nvlist */ 201*2f172c55SRobert Thurlow if (!(nvl = reparse_init())) 202*2f172c55SRobert Thurlow return (ENOMEM); 203*2f172c55SRobert Thurlow 204*2f172c55SRobert Thurlow /* Get the symlink data (should be there) */ 205*2f172c55SRobert Thurlow err = readlink(sl_path, buf, SYMLINK_MAX); 206*2f172c55SRobert Thurlow if (err == -1) { 207*2f172c55SRobert Thurlow reparse_free(nvl); 208*2f172c55SRobert Thurlow return (errno); 209*2f172c55SRobert Thurlow } 210*2f172c55SRobert Thurlow buf[err] = '\0'; 211*2f172c55SRobert Thurlow 212*2f172c55SRobert Thurlow /* Get the records into the nvlist */ 213*2f172c55SRobert Thurlow err = reparse_parse(buf, nvl); 214*2f172c55SRobert Thurlow if (err) { 215*2f172c55SRobert Thurlow reparse_free(nvl); 216*2f172c55SRobert Thurlow return (err); 217*2f172c55SRobert Thurlow } 218*2f172c55SRobert Thurlow 219*2f172c55SRobert Thurlow /* Remove from nvlist */ 220*2f172c55SRobert Thurlow err = reparse_remove(nvl, svc_type); 221*2f172c55SRobert Thurlow if (err) { 222*2f172c55SRobert Thurlow reparse_free(nvl); 223*2f172c55SRobert Thurlow return (err); 224*2f172c55SRobert Thurlow } 225*2f172c55SRobert Thurlow 226*2f172c55SRobert Thurlow /* Any list entries left? If so, turn nvlist back to string. */ 227*2f172c55SRobert Thurlow curr = nvlist_next_nvpair(nvl, NULL); 228*2f172c55SRobert Thurlow if (curr != NULL) { 229*2f172c55SRobert Thurlow err = reparse_unparse(nvl, &cp); 230*2f172c55SRobert Thurlow reparse_free(nvl); 231*2f172c55SRobert Thurlow if (err) 232*2f172c55SRobert Thurlow return (err); 233*2f172c55SRobert Thurlow } else { 234*2f172c55SRobert Thurlow reparse_free(nvl); 235*2f172c55SRobert Thurlow cp = NULL; 236*2f172c55SRobert Thurlow } 237*2f172c55SRobert Thurlow 238*2f172c55SRobert Thurlow /* Finally, delete and perhaps recreate the reparse point */ 239*2f172c55SRobert Thurlow err = reparse_delete(sl_path); 240*2f172c55SRobert Thurlow if (err) { 241*2f172c55SRobert Thurlow free(cp); 242*2f172c55SRobert Thurlow return (err); 243*2f172c55SRobert Thurlow } 244*2f172c55SRobert Thurlow 245*2f172c55SRobert Thurlow if (cp != NULL) { 246*2f172c55SRobert Thurlow err = reparse_create(sl_path, cp); 247*2f172c55SRobert Thurlow free(cp); 248*2f172c55SRobert Thurlow if (err) 249*2f172c55SRobert Thurlow return (err); 250*2f172c55SRobert Thurlow } 251*2f172c55SRobert Thurlow printf(gettext("Removed svc_type '%s' from %s\n"), svc_type, sl_path); 252*2f172c55SRobert Thurlow return (err); 253*2f172c55SRobert Thurlow } 254*2f172c55SRobert Thurlow 255*2f172c55SRobert Thurlow int 256*2f172c55SRobert Thurlow lookup(char *sl_path, char *svc_type, int type_set) 257*2f172c55SRobert Thurlow { 258*2f172c55SRobert Thurlow int err; 259*2f172c55SRobert Thurlow size_t bufsize; 260*2f172c55SRobert Thurlow char buf[1024]; 261*2f172c55SRobert Thurlow char *type, *svc_data; 262*2f172c55SRobert Thurlow nvlist_t *nvl; 263*2f172c55SRobert Thurlow nvpair_t *curr; 264*2f172c55SRobert Thurlow fs_locations4 fsl; 265*2f172c55SRobert Thurlow XDR xdr; 266*2f172c55SRobert Thurlow 267*2f172c55SRobert Thurlow if (!(nvl = reparse_init())) 268*2f172c55SRobert Thurlow return (-1); 269*2f172c55SRobert Thurlow 270*2f172c55SRobert Thurlow /* Get reparse point data */ 271*2f172c55SRobert Thurlow err = readlink(sl_path, buf, SYMLINK_MAX); 272*2f172c55SRobert Thurlow if (err == -1) 273*2f172c55SRobert Thurlow return (errno); 274*2f172c55SRobert Thurlow buf[err] = '\0'; 275*2f172c55SRobert Thurlow 276*2f172c55SRobert Thurlow /* Parse it to an nvlist */ 277*2f172c55SRobert Thurlow err = reparse_parse(buf, nvl); 278*2f172c55SRobert Thurlow if (err) { 279*2f172c55SRobert Thurlow reparse_free(nvl); 280*2f172c55SRobert Thurlow return (err); 281*2f172c55SRobert Thurlow } 282*2f172c55SRobert Thurlow 283*2f172c55SRobert Thurlow /* Look for entries of the requested service type */ 284*2f172c55SRobert Thurlow curr = NULL; 285*2f172c55SRobert Thurlow while ((curr = nvlist_next_nvpair(nvl, curr)) != NULL) { 286*2f172c55SRobert Thurlow type = nvpair_name(curr); 287*2f172c55SRobert Thurlow if (type_set && strcasecmp(type, svc_type) == 0) 288*2f172c55SRobert Thurlow break; 289*2f172c55SRobert Thurlow if (!type_set && strncasecmp(type, "nfs", 3) == 0) 290*2f172c55SRobert Thurlow break; 291*2f172c55SRobert Thurlow } 292*2f172c55SRobert Thurlow if (curr == NULL) { 293*2f172c55SRobert Thurlow reparse_free(nvl); 294*2f172c55SRobert Thurlow return (ENOENT); 295*2f172c55SRobert Thurlow } 296*2f172c55SRobert Thurlow 297*2f172c55SRobert Thurlow /* Get the service data and look it up */ 298*2f172c55SRobert Thurlow nvpair_value_string(curr, &svc_data); 299*2f172c55SRobert Thurlow 300*2f172c55SRobert Thurlow bufsize = sizeof (buf); 301*2f172c55SRobert Thurlow err = reparse_deref(type, svc_data, buf, &bufsize); 302*2f172c55SRobert Thurlow reparse_free(nvl); 303*2f172c55SRobert Thurlow if (err) 304*2f172c55SRobert Thurlow return (err); 305*2f172c55SRobert Thurlow 306*2f172c55SRobert Thurlow xdrmem_create(&xdr, buf, bufsize, XDR_DECODE); 307*2f172c55SRobert Thurlow err = xdr_fs_locations4(&xdr, &fsl); 308*2f172c55SRobert Thurlow XDR_DESTROY(&xdr); 309*2f172c55SRobert Thurlow if (err != TRUE) 310*2f172c55SRobert Thurlow return (ENOENT); 311*2f172c55SRobert Thurlow printf(gettext("%s points to: "), sl_path); 312*2f172c55SRobert Thurlow print_referral_summary(&fsl); 313*2f172c55SRobert Thurlow return (0); 314*2f172c55SRobert Thurlow } 315*2f172c55SRobert Thurlow 316*2f172c55SRobert Thurlow extern char *optarg; 317*2f172c55SRobert Thurlow extern int optind, optopt; 318*2f172c55SRobert Thurlow 319*2f172c55SRobert Thurlow int 320*2f172c55SRobert Thurlow main(int argc, char *argv[]) 321*2f172c55SRobert Thurlow { 322*2f172c55SRobert Thurlow char c, *command, *sl_path, *svc_type; 323*2f172c55SRobert Thurlow int type_set, err; 324*2f172c55SRobert Thurlow 325*2f172c55SRobert Thurlow (void) setlocale(LC_ALL, ""); 326*2f172c55SRobert Thurlow (void) textdomain(TEXT_DOMAIN); 327*2f172c55SRobert Thurlow 328*2f172c55SRobert Thurlow svc_type = "nfs-basic"; /* Default from SMF some day */ 329*2f172c55SRobert Thurlow type_set = 0; /* Lookup any nfs type */ 330*2f172c55SRobert Thurlow 331*2f172c55SRobert Thurlow /* Look for options (just the service type now) */ 332*2f172c55SRobert Thurlow while ((c = getopt(argc, argv, "t:")) != -1) { 333*2f172c55SRobert Thurlow switch (c) { 334*2f172c55SRobert Thurlow case 't': 335*2f172c55SRobert Thurlow svc_type = optarg; 336*2f172c55SRobert Thurlow type_set = 1; 337*2f172c55SRobert Thurlow break; 338*2f172c55SRobert Thurlow 339*2f172c55SRobert Thurlow default: 340*2f172c55SRobert Thurlow usage(); 341*2f172c55SRobert Thurlow exit(1); 342*2f172c55SRobert Thurlow } 343*2f172c55SRobert Thurlow } 344*2f172c55SRobert Thurlow 345*2f172c55SRobert Thurlow /* Make sure there's at least a command and one argument */ 346*2f172c55SRobert Thurlow if (optind + 1 >= argc) { 347*2f172c55SRobert Thurlow usage(); 348*2f172c55SRobert Thurlow exit(1); 349*2f172c55SRobert Thurlow } 350*2f172c55SRobert Thurlow 351*2f172c55SRobert Thurlow err = rp_plugin_init(); 352*2f172c55SRobert Thurlow switch (err) { 353*2f172c55SRobert Thurlow case RP_OK: 354*2f172c55SRobert Thurlow break; 355*2f172c55SRobert Thurlow case RP_NO_PLUGIN_DIR: 356*2f172c55SRobert Thurlow fprintf(stderr, 357*2f172c55SRobert Thurlow gettext("Warning: no plugin directory, continuing...\n")); 358*2f172c55SRobert Thurlow break; 359*2f172c55SRobert Thurlow case RP_NO_PLUGIN: 360*2f172c55SRobert Thurlow fprintf(stderr, 361*2f172c55SRobert Thurlow gettext("Warning: no plugin found, continuing...\n")); 362*2f172c55SRobert Thurlow break; 363*2f172c55SRobert Thurlow case RP_NO_MEMORY: 364*2f172c55SRobert Thurlow fprintf(stderr, 365*2f172c55SRobert Thurlow gettext("rp_plugin_init failed, no memory\n")); 366*2f172c55SRobert Thurlow exit(0); 367*2f172c55SRobert Thurlow default: 368*2f172c55SRobert Thurlow fprintf(stderr, 369*2f172c55SRobert Thurlow gettext("rp_plugin_init failed, error %d\n"), err); 370*2f172c55SRobert Thurlow exit(0); 371*2f172c55SRobert Thurlow } 372*2f172c55SRobert Thurlow 373*2f172c55SRobert Thurlow command = argv[optind++]; 374*2f172c55SRobert Thurlow sl_path = argv[optind++]; 375*2f172c55SRobert Thurlow 376*2f172c55SRobert Thurlow if (strcmp(command, "add") == 0) { 377*2f172c55SRobert Thurlow 378*2f172c55SRobert Thurlow if (optind >= argc) { 379*2f172c55SRobert Thurlow usage(); 380*2f172c55SRobert Thurlow exit(1); 381*2f172c55SRobert Thurlow } 382*2f172c55SRobert Thurlow 383*2f172c55SRobert Thurlow err = addref(sl_path, svc_type, optind, argc, argv); 384*2f172c55SRobert Thurlow 385*2f172c55SRobert Thurlow } else if (strcmp(command, "remove") == 0) { 386*2f172c55SRobert Thurlow 387*2f172c55SRobert Thurlow err = delref(sl_path, svc_type); 388*2f172c55SRobert Thurlow 389*2f172c55SRobert Thurlow } else if (strcmp(command, "lookup") == 0) { 390*2f172c55SRobert Thurlow 391*2f172c55SRobert Thurlow err = lookup(sl_path, svc_type, type_set); 392*2f172c55SRobert Thurlow 393*2f172c55SRobert Thurlow } else { 394*2f172c55SRobert Thurlow usage(); 395*2f172c55SRobert Thurlow exit(1); 396*2f172c55SRobert Thurlow } 397*2f172c55SRobert Thurlow if (err != 0) 398*2f172c55SRobert Thurlow fprintf(stderr, gettext("Command %s failed: %s\n"), command, 399*2f172c55SRobert Thurlow strerror(err)); 400*2f172c55SRobert Thurlow return (err); 401*2f172c55SRobert Thurlow } 402