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 /* 23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 /* 27 * AUTOMOUNT specific functions 28 */ 29 #include <stdio.h> 30 #include <string.h> 31 #include <ctype.h> 32 #include <stdlib.h> 33 #include <unistd.h> 34 #include <zone.h> 35 #include <errno.h> 36 #include <locale.h> 37 #include <fcntl.h> 38 #include <sys/types.h> 39 #include <sys/stat.h> 40 #include <syslog.h> 41 #include "libshare.h" 42 #include "libshare_impl.h" 43 #include <pwd.h> 44 #include <limits.h> 45 #include <libscf.h> 46 #include <strings.h> 47 #include <libdlpi.h> 48 #include "smfcfg.h" 49 50 51 static int autofs_init(); 52 static void autofs_fini(); 53 static int autofs_validate_property(sa_handle_t, sa_property_t, sa_optionset_t); 54 static int autofs_set_proto_prop(sa_property_t); 55 static sa_protocol_properties_t autofs_get_proto_set(); 56 static char *autofs_get_status(); 57 static uint64_t autofs_features(); 58 59 static int initautofsprotofromsmf(); 60 static int true_false_validator(int index, char *value); 61 static int strlen_validator(int index, char *value); 62 static int range_check_validator(int index, char *value); 63 64 /* 65 * ops vector that provides the protocol specific info and operations 66 * for share management. 67 */ 68 struct sa_plugin_ops sa_plugin_ops = { 69 SA_PLUGIN_VERSION, 70 "autofs", 71 autofs_init, /* Init autofs */ 72 autofs_fini, /* Fini autofs */ 73 NULL, /* Start Sharing */ 74 NULL, /* stop sharing */ 75 autofs_validate_property, 76 NULL, /* valid_space */ 77 NULL, /* security_prop */ 78 NULL, /* parse optstring */ 79 NULL, /* format optstring */ 80 autofs_set_proto_prop, /* Set properties */ 81 autofs_get_proto_set, /* get properties */ 82 autofs_get_status, /* get status */ 83 NULL, /* space_alias */ 84 NULL, /* update_legacy */ 85 NULL, /* delete_legacy */ 86 NULL, /* change notify */ 87 NULL, /* enable resource */ 88 NULL, /* disable resource */ 89 autofs_features, /* features */ 90 NULL, /* transient shares */ 91 NULL, /* notify resource */ 92 NULL, /* rename resource */ 93 NULL, /* run_command */ 94 NULL, /* command_help */ 95 NULL /* delete_proto_section */ 96 }; 97 98 99 static sa_protocol_properties_t protoset; 100 101 #define AUTOMOUNT_VERBOSE_DEFAULT 0 102 #define AUTOMOUNTD_VERBOSE_DEFAULT 0 103 #define AUTOMOUNT_NOBROWSE_DEFAULT 0 104 #define AUTOMOUNT_TIMEOUT_DEFAULT 600 105 #define AUTOMOUNT_TRACE_DEFAULT 0 106 /* 107 * Protocol Management functions 108 */ 109 struct proto_option_defs { 110 char *tag; 111 char *name; /* display name -- remove protocol identifier */ 112 int index; 113 scf_type_t type; 114 union { 115 int intval; 116 char *string; 117 } defvalue; 118 int32_t minval; 119 int32_t maxval; 120 int (*check)(int, char *); 121 } proto_options[] = { 122 #define PROTO_OPT_AUTOMOUNT_TIMEOUT 0 123 { "timeout", 124 "timeout", PROTO_OPT_AUTOMOUNT_TIMEOUT, 125 SCF_TYPE_INTEGER, AUTOMOUNT_TIMEOUT_DEFAULT, 126 1, INT32_MAX, range_check_validator}, 127 #define PROTO_OPT_AUTOMOUNT_VERBOSE 1 128 { "automount_verbose", 129 "automount_verbose", PROTO_OPT_AUTOMOUNT_VERBOSE, 130 SCF_TYPE_BOOLEAN, AUTOMOUNT_VERBOSE_DEFAULT, 0, 1, 131 true_false_validator}, 132 #define PROTO_OPT_AUTOMOUNTD_VERBOSE 2 133 { "automountd_verbose", 134 "automountd_verbose", PROTO_OPT_AUTOMOUNTD_VERBOSE, 135 SCF_TYPE_BOOLEAN, AUTOMOUNTD_VERBOSE_DEFAULT, 0, 1, 136 true_false_validator}, 137 #define PROTO_OPT_AUTOMOUNTD_NOBROWSE 3 138 { "nobrowse", 139 "nobrowse", PROTO_OPT_AUTOMOUNTD_NOBROWSE, SCF_TYPE_BOOLEAN, 140 AUTOMOUNT_NOBROWSE_DEFAULT, 0, 1, true_false_validator}, 141 #define PROTO_OPT_AUTOMOUNTD_TRACE 4 142 { "trace", 143 "trace", PROTO_OPT_AUTOMOUNTD_TRACE, 144 SCF_TYPE_INTEGER, AUTOMOUNT_TRACE_DEFAULT, 145 0, 20, range_check_validator}, 146 #define PROTO_OPT_AUTOMOUNTD_ENV 5 147 { "environment", 148 "environment", PROTO_OPT_AUTOMOUNTD_ENV, SCF_TYPE_ASTRING, 149 NULL, 0, 1024, strlen_validator}, 150 {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL} 151 }; 152 153 #define AUTOFS_PROP_MAX (sizeof (proto_options) / sizeof (proto_options[0])) 154 155 static void 156 add_defaults() 157 { 158 int i; 159 char number[MAXDIGITS]; 160 161 for (i = 0; proto_options[i].tag != NULL; i++) { 162 sa_property_t prop; 163 prop = sa_get_protocol_property(protoset, 164 proto_options[i].name); 165 if (prop == NULL) { 166 /* add the default value */ 167 switch (proto_options[i].type) { 168 case SCF_TYPE_INTEGER: 169 (void) snprintf(number, sizeof (number), "%d", 170 proto_options[i].defvalue.intval); 171 prop = sa_create_property(proto_options[i].name, 172 number); 173 break; 174 175 case SCF_TYPE_BOOLEAN: 176 prop = sa_create_property(proto_options[i].name, 177 proto_options[i].defvalue.intval ? 178 "true" : "false"); 179 break; 180 181 default: 182 /* treat as strings of zero length */ 183 prop = sa_create_property(proto_options[i].name, 184 ""); 185 break; 186 } 187 if (prop != NULL) 188 (void) sa_add_protocol_property(protoset, prop); 189 } 190 } 191 } 192 193 static int 194 autofs_init() 195 { 196 int ret = SA_OK; 197 198 if (sa_plugin_ops.sa_init != autofs_init) { 199 (void) printf(dgettext(TEXT_DOMAIN, 200 "AUTOFS plugin not installed properly\n")); 201 return (SA_CONFIG_ERR); 202 } 203 204 ret = initautofsprotofromsmf(); 205 if (ret != SA_OK) { 206 (void) printf(dgettext(TEXT_DOMAIN, 207 "AUTOFS plugin problem with SMF properties: %s\n"), 208 sa_errorstr(ret)); 209 ret = SA_OK; 210 } 211 add_defaults(); 212 return (ret); 213 } 214 215 static void 216 free_protoprops() 217 { 218 if (protoset != NULL) { 219 xmlFreeNode(protoset); 220 protoset = NULL; 221 } 222 } 223 224 static void 225 autofs_fini() 226 { 227 free_protoprops(); 228 } 229 230 static int 231 findprotoopt(char *propname) 232 { 233 int i; 234 235 for (i = 0; proto_options[i].tag != NULL; i++) 236 if (strcmp(proto_options[i].name, propname) == 0) 237 return (i); 238 return (-1); 239 } 240 241 static int 242 autofs_validate_property(sa_handle_t handle, sa_property_t property, 243 sa_optionset_t parent) 244 { 245 int ret = SA_OK; 246 char *propname; 247 int optionindex; 248 char *value; 249 250 #ifdef lint 251 handle = handle; 252 parent = parent; 253 #endif 254 propname = sa_get_property(property, "type"); 255 if (propname == NULL) 256 return (SA_NO_SUCH_PROP); 257 258 if ((optionindex = findprotoopt(propname)) < 0) 259 ret = SA_NO_SUCH_PROP; 260 261 if (ret != SA_OK) { 262 if (propname != NULL) 263 sa_free_attr_string(propname); 264 return (ret); 265 } 266 267 value = sa_get_property_attr(property, "value"); 268 if (value != NULL) { 269 /* 270 * If any property is added to AUTOFS, which is a different 271 * type than the below list, a case needs to be added for that 272 * to check the values. For now AUTOFS type are just integers, 273 * string and boolean properties. Just taking care of them. 274 */ 275 switch (proto_options[optionindex].type) { 276 case SCF_TYPE_INTEGER: 277 case SCF_TYPE_BOOLEAN: 278 case SCF_TYPE_ASTRING: 279 ret = proto_options[optionindex].check(optionindex, 280 value); 281 break; 282 default: 283 break; 284 } 285 } 286 287 /* Free the value */ 288 if (value != NULL) 289 sa_free_attr_string(value); 290 if (propname != NULL) 291 sa_free_attr_string(propname); 292 return (ret); 293 } 294 295 /* 296 * service_in_state(service, chkstate) 297 * 298 * Want to know if the specified service is in the desired state 299 * (chkstate) or not. Return true (1) if it is and false (0) if it 300 * isn't. 301 */ 302 static int 303 service_in_state(char *service, const char *chkstate) 304 { 305 char *state; 306 int ret = B_FALSE; 307 308 state = smf_get_state(service); 309 if (state != NULL) { 310 /* got the state so get the equality for the return value */ 311 ret = strcmp(state, chkstate) == 0 ? B_TRUE : B_FALSE; 312 free(state); 313 } 314 return (ret); 315 } 316 317 static void 318 restart_service(char *service) 319 { 320 int ret = -1; 321 322 /* 323 * Only attempt to restart the service if it is 324 * currently running. In the future, it may be 325 * desirable to use smf_refresh_instance if the AUTOFS 326 * services ever implement the refresh method. 327 */ 328 if (service_in_state(service, SCF_STATE_STRING_ONLINE)) { 329 ret = smf_restart_instance(service); 330 /* 331 * There are only a few SMF errors at this point, but 332 * it is also possible that a bad value may have put 333 * the service into maintenance if there wasn't an 334 * SMF level error. 335 */ 336 if (ret != 0) { 337 (void) fprintf(stderr, 338 dgettext(TEXT_DOMAIN, 339 "%s failed to restart: %s\n"), 340 scf_strerror(scf_error())); 341 } else { 342 /* 343 * Check whether it has gone to "maintenance" 344 * mode or not. Maintenance implies something 345 * went wrong. 346 */ 347 if (service_in_state(service, 348 SCF_STATE_STRING_MAINT)) { 349 (void) fprintf(stderr, 350 dgettext(TEXT_DOMAIN, 351 "%s failed to restart\n"), 352 service); 353 } 354 } 355 } 356 } 357 358 static int 359 is_a_number(char *number) 360 { 361 int ret = 1; 362 int hex = 0; 363 364 if (strncmp(number, "0x", 2) == 0) { 365 number += 2; 366 hex = 1; 367 } else if (*number == '-') { 368 number++; /* skip the minus */ 369 } 370 while (ret == 1 && *number != '\0') { 371 if (hex) { 372 ret = isxdigit(*number++); 373 } else { 374 ret = isdigit(*number++); 375 } 376 } 377 return (ret); 378 } 379 380 /* 381 * fixcaselower(str) 382 * 383 * convert a string to lower case (inplace). 384 */ 385 386 static void 387 fixcaselower(char *str) 388 { 389 while (*str) { 390 *str = tolower(*str); 391 str++; 392 } 393 } 394 395 /* 396 * skipwhitespace(str) 397 * 398 * Skip leading white space. It is assumed that it is called with a 399 * valid pointer. 400 */ 401 static char * 402 skipwhitespace(char *str) 403 { 404 while (*str && isspace(*str)) 405 str++; 406 407 return (str); 408 } 409 410 /* 411 * extractprop() 412 * 413 * Extract the property and value out of the line and create the 414 * property in the optionset. 415 */ 416 static int 417 extractprop(char *name, char *value) 418 { 419 sa_property_t prop; 420 int index; 421 int ret = SA_OK; 422 /* 423 * Remove any leading 424 * white space. 425 */ 426 name = skipwhitespace(name); 427 428 index = findprotoopt(name); 429 if (index >= 0) { 430 fixcaselower(name); 431 prop = sa_create_property(proto_options[index].name, value); 432 if (prop != NULL) 433 ret = sa_add_protocol_property(protoset, prop); 434 else 435 ret = SA_NO_MEMORY; 436 } 437 return (ret); 438 } 439 440 static int 441 initautofsprotofromsmf(void) 442 { 443 char name[PATH_MAX]; 444 char value[PATH_MAX]; 445 int ret = SA_OK, bufsz = 0, i; 446 char *instance = NULL; 447 scf_type_t sctype; 448 449 protoset = sa_create_protocol_properties("autofs"); 450 if (protoset != NULL) { 451 for (i = 0; proto_options[i].tag != NULL; i++) { 452 bzero(value, PATH_MAX); 453 (void) strncpy(name, proto_options[i].name, PATH_MAX); 454 sctype = proto_options[i].type; 455 bufsz = PATH_MAX; 456 ret = autofs_smf_get_prop(name, value, 457 instance, sctype, AUTOFS_FMRI, &bufsz); 458 if (ret == SA_OK) { 459 ret = extractprop(name, value); 460 } 461 } 462 } else { 463 ret = SA_NO_MEMORY; 464 } 465 return (ret); 466 } 467 468 static int 469 range_check_validator(int index, char *value) 470 { 471 int ret = SA_OK; 472 if (!is_a_number(value)) { 473 ret = SA_BAD_VALUE; 474 } else { 475 int val; 476 errno = 0; 477 val = strtoul(value, NULL, 0); 478 if (errno != 0) 479 return (SA_BAD_VALUE); 480 481 if (val < proto_options[index].minval || 482 val > proto_options[index].maxval) 483 ret = SA_BAD_VALUE; 484 } 485 return (ret); 486 } 487 488 static int 489 true_false_validator(int index, char *value) 490 { 491 492 #ifdef lint 493 index = index; 494 #endif 495 if ((strcasecmp(value, "true") == 0) || 496 (strcasecmp(value, "on") == 0) || 497 (strcasecmp(value, "yes") == 0) || 498 (strcmp(value, "1") == 0) || 499 (strcasecmp(value, "false") == 0) || 500 (strcasecmp(value, "off") == 0) || 501 (strcasecmp(value, "no") == 0) || 502 (strcmp(value, "0") == 0)) { 503 return (SA_OK); 504 } 505 return (SA_BAD_VALUE); 506 } 507 508 static int 509 strlen_validator(int index, char *value) 510 { 511 int ret = SA_OK; 512 if (value == NULL) { 513 if (proto_options[index].minval == 0) { 514 return (ret); 515 } else { 516 return (SA_BAD_VALUE); 517 } 518 } 519 if (strlen(value) > proto_options[index].maxval || 520 strlen(value) < proto_options[index].minval) 521 ret = SA_BAD_VALUE; 522 return (ret); 523 } 524 525 static int 526 autofs_validate_proto_prop(int index, char *name, char *value) 527 { 528 #ifdef lint 529 name = name; 530 #endif 531 return (proto_options[index].check(index, value)); 532 } 533 534 static int 535 autofs_set_proto_prop(sa_property_t prop) 536 { 537 int ret = SA_OK; 538 char *name; 539 char *value, *instance = NULL; 540 scf_type_t sctype; 541 542 name = sa_get_property_attr(prop, "type"); 543 value = sa_get_property_attr(prop, "value"); 544 if (name != NULL && value != NULL) { 545 int index = findprotoopt(name); 546 if (index >= 0) { 547 ret = autofs_validate_proto_prop(index, name, value); 548 if (ret == SA_OK) { 549 sctype = proto_options[index].type; 550 if (sctype == SCF_TYPE_BOOLEAN) { 551 if (value != NULL) 552 sa_free_attr_string(value); 553 if (string_to_boolean(value) == 0) 554 value = strdup("0"); 555 else 556 value = strdup("1"); 557 } 558 ret = autofs_smf_set_prop(name, value, 559 instance, sctype, AUTOFS_FMRI); 560 /* 561 * Make an instance based FMRI. 562 * For now its DEFAULT_AUTOFS_FMRI. 563 */ 564 if (ret == SA_OK) 565 restart_service(AUTOFS_DEFAULT_FMRI); 566 } 567 } else { 568 ret = SA_NO_SUCH_PROP; 569 } 570 } else { 571 ret = SA_CONFIG_ERR; 572 } 573 574 if (name != NULL) 575 sa_free_attr_string(name); 576 if (value != NULL) 577 sa_free_attr_string(value); 578 return (ret); 579 } 580 581 582 static sa_protocol_properties_t 583 autofs_get_proto_set(void) 584 { 585 return (protoset); 586 } 587 588 static uint64_t 589 autofs_features(void) 590 { 591 return (0); 592 } 593 594 static char * 595 autofs_get_status(void) 596 { 597 char *state = NULL; 598 state = smf_get_state(AUTOFS_DEFAULT_FMRI); 599 return (state != NULL ? state : "-"); 600 } 601