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 51static int autofs_init(); 52static void autofs_fini(); 53static int autofs_validate_property(sa_handle_t, sa_property_t, sa_optionset_t); 54static int autofs_set_proto_prop(sa_property_t); 55static sa_protocol_properties_t autofs_get_proto_set(); 56static char *autofs_get_status(); 57static uint64_t autofs_features(); 58 59static int initautofsprotofromsmf(); 60static int true_false_validator(int index, char *value); 61static int strlen_validator(int index, char *value); 62static 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 */ 68struct 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 99static 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 */ 109struct 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 155static void 156add_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 193static int 194autofs_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 215static void 216free_protoprops() 217{ 218 if (protoset != NULL) { 219 xmlFreeNode(protoset); 220 protoset = NULL; 221 } 222} 223 224static void 225autofs_fini() 226{ 227 free_protoprops(); 228} 229 230static int 231findprotoopt(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 241static int 242autofs_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 */ 302static int 303service_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 317static void 318restart_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 358static int 359is_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 386static void 387fixcaselower(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 */ 401static char * 402skipwhitespace(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 */ 416static int 417extractprop(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 440static int 441initautofsprotofromsmf(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 468static int 469range_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 488static int 489true_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 508static int 509strlen_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 525static int 526autofs_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 534static int 535autofs_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 582static sa_protocol_properties_t 583autofs_get_proto_set(void) 584{ 585 return (protoset); 586} 587 588static uint64_t 589autofs_features(void) 590{ 591 return (0); 592} 593 594static char * 595autofs_get_status(void) 596{ 597 char *state = NULL; 598 state = smf_get_state(AUTOFS_DEFAULT_FMRI); 599 return (state != NULL ? state : "-"); 600} 601